star-2d

Contour structuring for efficient 2D geometric queries
git clone git://git.meso-star.fr/star-2d.git
Log | Files | Refs | README | LICENSE

commit 5e60e840f66b6842d6579775b64518555765f1c4
parent 82efe24bb04436872fd72cde3811fd46c5f961f0
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 22 Jun 2016 16:10:04 +0200

Implement and test the s2d_scene session API

Diffstat:
Msrc/s2d_line_segments.c | 224++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/s2d_line_segments.h | 26+++++++++++++-------------
Msrc/s2d_scene.c | 404++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/s2d_shape.c | 30+++++++++++++++---------------
Msrc/s2d_shape_c.h | 2+-
Msrc/test_s2d_scene.c | 36++++++++++++++++++++++++++++++++++++
6 files changed, 568 insertions(+), 154 deletions(-)

diff --git a/src/s2d_line_segments.c b/src/s2d_line_segments.c @@ -37,7 +37,7 @@ ******************************************************************************/ static void line_setup_indices - (struct line_segments* line, + (struct line_segments* lines, const unsigned nsegments, void (*get_indices)(const unsigned isegment, unsigned ids[2], void*), const unsigned nverts, @@ -48,34 +48,34 @@ line_setup_indices unsigned nsegments_prev; unsigned nverts_new; res_T res = RES_OK; - ASSERT(line && nsegments && nverts); + ASSERT(lines && nsegments && nverts); - nsegments_prev = (unsigned)line_segments_get_nsegments(line); + nsegments_prev = (unsigned)line_segments_get_nsegments(lines); ASSERT(get_indices != S2D_KEEP || nsegments == nsegments_prev); if(get_indices == S2D_KEEP) return; if(nsegments == nsegments_prev) { - line->update_mask |= (INDEX_BUFFER & !line->resize_mask); + lines->update_mask |= (INDEX_BUFFER & !lines->resize_mask); } else { - line->resize_mask |= INDEX_BUFFER; - line->update_mask &= !INDEX_BUFFER; + lines->resize_mask |= INDEX_BUFFER; + lines->update_mask &= !INDEX_BUFFER; } - if(line->indices) { /* Release the old index buffer */ - index_buffer_ref_put(line->indices); - line->indices = NULL; + if(lines->indices) { /* Release the old index buffer */ + index_buffer_ref_put(lines->indices); + lines->indices = NULL; } /* Allocate the new index buffer */ - res = index_buffer_create(line->dev->allocator, &line->indices); + res = index_buffer_create(lines->dev->allocator, &lines->indices); if(res != RES_OK) FATAL("Insufficient memory\n"); - res = darray_u32_resize(&line->indices->data, nsegments * 2/*# segmet ids*/); + res = darray_u32_resize(&lines->indices->data, nsegments * 2/*# segmet ids*/); if(res != RES_OK) FATAL("Insufficient memory\n"); - /* Setup the line indices */ - indices = line_segments_get_ids(line); + /* Setup the lines indices */ + indices = line_segments_get_ids(lines); nverts_new = 0; FOR_EACH(isegment, 0, nsegments) { uint32_t* ids = indices + isegment*2/*#ids per segment*/; @@ -91,7 +91,7 @@ line_setup_indices static void line_setup_positions - (struct line_segments* line, + (struct line_segments* lines, const unsigned nverts, struct s2d_vertex_data* attr, void* data) @@ -99,37 +99,37 @@ line_setup_positions float* positions; unsigned ivert, nverts_prev; res_T res; - ASSERT(line && nverts && attr && attr->usage == S2D_POSITION); + ASSERT(lines && nverts && attr && attr->usage == S2D_POSITION); if(attr->get == S2D_KEEP) { - ASSERT(line->attribs[S2D_POSITION]); - ASSERT(darray_float_size_get(&line->attribs[S2D_POSITION]->data) == nverts*2); + ASSERT(lines->attribs[S2D_POSITION]); + ASSERT(darray_float_size_get(&lines->attribs[S2D_POSITION]->data) == nverts*2); return; } - nverts_prev = (unsigned)line_segments_get_nverts(line); + nverts_prev = (unsigned)line_segments_get_nverts(lines); if(nverts == nverts_prev) { - line->update_mask |= (VERTEX_BUFFER & ~line->resize_mask); + lines->update_mask |= (VERTEX_BUFFER & ~lines->resize_mask); } else { - line->resize_mask |= VERTEX_BUFFER; - line->update_mask &= !VERTEX_BUFFER; + lines->resize_mask |= VERTEX_BUFFER; + lines->update_mask &= !VERTEX_BUFFER; } /* Release the old vertex buffer */ - if(line->attribs[S2D_POSITION]) { - vertex_buffer_ref_put(line->attribs[S2D_POSITION]); - line->attribs[S2D_POSITION] = NULL; + if(lines->attribs[S2D_POSITION]) { + vertex_buffer_ref_put(lines->attribs[S2D_POSITION]); + lines->attribs[S2D_POSITION] = NULL; } /* Allocate the vertex positions */ - res = vertex_buffer_create(line->dev->allocator, &line->attribs[S2D_POSITION]); + res = vertex_buffer_create(lines->dev->allocator, &lines->attribs[S2D_POSITION]); if(res != RES_OK) FATAL("Insufficient memory\n"); - res = darray_float_resize(&line->attribs[S2D_POSITION]->data, nverts*2); + res = darray_float_resize(&lines->attribs[S2D_POSITION]->data, nverts*2); if(res != RES_OK) FATAL("Insufficient memory\n"); - line->attribs_type[S2D_POSITION] = S2D_FLOAT2; + lines->attribs_type[S2D_POSITION] = S2D_FLOAT2; /* Setup the vertex positions */ - positions = darray_float_data_get(&line->attribs[S2D_POSITION]->data); + positions = darray_float_data_get(&lines->attribs[S2D_POSITION]->data); if(attr->type == S2D_FLOAT2) { FOR_EACH(ivert, 0, nverts) { attr->get(ivert, positions + ivert*2/*# coords per vertex*/, data); @@ -160,7 +160,7 @@ line_setup_positions static void line_setup_attribs - (struct line_segments* line, + (struct line_segments* lines, const unsigned nverts, const struct s2d_vertex_data* attr, void* data) @@ -169,30 +169,30 @@ line_setup_attribs unsigned dim; unsigned ivert; res_T res; - ASSERT(line && nverts && attr); + ASSERT(lines && nverts && attr); ASSERT(attr->usage!=S2D_POSITION && (unsigned)attr->usage<S2D_ATTRIBS_COUNT__); dim = s2d_type_get_dimension(attr->type); if(attr->get == S2D_KEEP) { - ASSERT(line->attribs_type[attr->usage] == attr->type); - ASSERT(line->attribs[attr->usage]); - ASSERT(darray_float_size_get(&line->attribs[attr->usage]->data) == nverts*dim); + ASSERT(lines->attribs_type[attr->usage] == attr->type); + ASSERT(lines->attribs[attr->usage]); + ASSERT(darray_float_size_get(&lines->attribs[attr->usage]->data) == nverts*dim); return; } - if(line->attribs[attr->usage]) { /* Release the previous vertex buffer */ - vertex_buffer_ref_put(line->attribs[attr->usage]); - line->attribs[attr->usage] = NULL; + if(lines->attribs[attr->usage]) { /* Release the previous vertex buffer */ + vertex_buffer_ref_put(lines->attribs[attr->usage]); + lines->attribs[attr->usage] = NULL; } /* Allocate the new vertex buffer */ - res = vertex_buffer_create(line->dev->allocator, &line->attribs[attr->usage]); + res = vertex_buffer_create(lines->dev->allocator, &lines->attribs[attr->usage]); if(res != RES_OK) FATAL("Insufficient memory\n"); - res = darray_float_resize(&line->attribs[attr->usage]->data, nverts * dim); + res = darray_float_resize(&lines->attribs[attr->usage]->data, nverts * dim); if(res != RES_OK) FATAL("Insufficient memory\n"); /* Setup the vertex attrib */ - attr_data = darray_float_data_get(&line->attribs[attr->usage]->data); + attr_data = darray_float_data_get(&lines->attribs[attr->usage]->data); FOR_EACH(ivert, 0, nverts) { attr->get(ivert, attr_data, data); attr_data += dim; @@ -202,14 +202,14 @@ line_setup_attribs static void line_release(ref_T* ref) { - struct line_segments* line; + struct line_segments* lines; struct s2d_device* dev; ASSERT(ref); - line = CONTAINER_OF(ref, struct line_segments, ref); - line_segments_clear(line); - dev = line->dev; - MEM_RM(dev->allocator, line); + lines = CONTAINER_OF(ref, struct line_segments, ref); + line_segments_clear(lines); + dev = lines->dev; + MEM_RM(dev->allocator, lines); S2D(device_ref_put(dev)); } @@ -219,128 +219,128 @@ line_release(ref_T* ref) res_T line_segments_create(struct s2d_device* dev, struct line_segments** out_lines) { - struct line_segments* line = NULL; + struct line_segments* lines = NULL; res_T res = RES_OK; ASSERT(dev && out_lines); - line = (struct line_segments*)MEM_CALLOC + lines = (struct line_segments*)MEM_CALLOC (dev->allocator, 1, sizeof(struct line_segments)); - if(!line) { + if(!lines) { res = RES_MEM_ERR; goto error; } - ref_init(&line->ref); + ref_init(&lines->ref); S2D(device_ref_get(dev)); - line->dev = dev; + lines->dev = dev; exit: - *out_lines = line; + *out_lines = lines; return res; error: - if(line) { - line_segments_ref_put(line); - line = NULL; + if(lines) { + line_segments_ref_put(lines); + lines = NULL; } goto exit; } void -line_segments_ref_get(struct line_segments* line) +line_segments_ref_get(struct line_segments* lines) { - ASSERT(line); - ref_get(&line->ref); + ASSERT(lines); + ref_get(&lines->ref); } void -line_segments_ref_put(struct line_segments* line) +line_segments_ref_put(struct line_segments* lines) { - ASSERT(line); - ref_put(&line->ref, line_release); + ASSERT(lines); + ref_put(&lines->ref, line_release); } void -line_segments_clear(struct line_segments* line) +line_segments_clear(struct line_segments* lines) { size_t iattr; - ASSERT(line); - if(line->indices) { - index_buffer_ref_put(line->indices); - line->indices = NULL; + ASSERT(lines); + if(lines->indices) { + index_buffer_ref_put(lines->indices); + lines->indices = NULL; } FOR_EACH(iattr, 0, S2D_ATTRIBS_COUNT__) { - if(line->attribs[iattr]) { - vertex_buffer_ref_put(line->attribs[iattr]); - line->attribs[iattr] = NULL; + if(lines->attribs[iattr]) { + vertex_buffer_ref_put(lines->attribs[iattr]); + lines->attribs[iattr] = NULL; } } - line->resize_mask = 0; - line->update_mask = 0; + lines->resize_mask = 0; + lines->update_mask = 0; } size_t -line_segments_get_nsegments(const struct line_segments* line) +line_segments_get_nsegments(const struct line_segments* lines) { size_t nids; - ASSERT(line); - if(!line->indices) + ASSERT(lines); + if(!lines->indices) return 0; - nids = darray_u32_size_get(&line->indices->data); + nids = darray_u32_size_get(&lines->indices->data); ASSERT(nids % 2 == 0); /* 2 vertices per segment */ return nids / 2/* #vertices per segement */; } size_t -line_segments_get_nverts(const struct line_segments* line) +line_segments_get_nverts(const struct line_segments* lines) { size_t ncoords; - ASSERT(line); - if(!line->attribs[S2D_POSITION]) + ASSERT(lines); + if(!lines->attribs[S2D_POSITION]) return 0; - ASSERT(line->attribs_type[S2D_POSITION] == S2D_FLOAT2); - ncoords = darray_float_size_get(&line->attribs[S2D_POSITION]->data); + ASSERT(lines->attribs_type[S2D_POSITION] == S2D_FLOAT2); + ncoords = darray_float_size_get(&lines->attribs[S2D_POSITION]->data); ASSERT(ncoords % 2 == 0); return ncoords / 2/* #coords per vertices */; } uint32_t* -line_segments_get_ids(struct line_segments* line) +line_segments_get_ids(struct line_segments* lines) { - ASSERT(line && line->indices); - return darray_u32_data_get(&line->indices->data); + ASSERT(lines && lines->indices); + return darray_u32_data_get(&lines->indices->data); } float* -line_segments_get_pos(struct line_segments* line) +line_segments_get_pos(struct line_segments* lines) { - ASSERT(line && line->attribs[S2D_POSITION]); - ASSERT(line->attribs_type[S2D_POSITION] == S2D_FLOAT2); - return darray_float_data_get(&line->attribs[S2D_POSITION]->data); + ASSERT(lines && lines->attribs[S2D_POSITION]); + ASSERT(lines->attribs_type[S2D_POSITION] == S2D_FLOAT2); + return darray_float_data_get(&lines->attribs[S2D_POSITION]->data); } float* line_segments_get_attr - (struct line_segments* line, + (struct line_segments* lines, const enum s2d_attrib_usage usage) { - ASSERT(line && usage < S2D_ATTRIBS_COUNT__ && line->attribs[usage]); - return darray_float_data_get(&line->attribs[usage]->data); + ASSERT(lines && usage < S2D_ATTRIBS_COUNT__ && lines->attribs[usage]); + return darray_float_data_get(&lines->attribs[usage]->data); } float -line_segments_compute_length(struct line_segments* line) +line_segments_compute_length(struct line_segments* lines) { const uint32_t* ids; const float* pos; size_t iseg, nsegs; float tmp[2]; float length = 0.f; - ASSERT(line); + ASSERT(lines); - nsegs = line_segments_get_nsegments(line); + nsegs = line_segments_get_nsegments(lines); if(!nsegs) return 0.f; - ids = line_segments_get_ids(line); - pos = line_segments_get_pos(line); + ids = line_segments_get_ids(lines); + pos = line_segments_get_pos(lines); FOR_EACH(iseg, 0, nsegs) { const size_t id = iseg * 2/*#ids per segment*/; @@ -353,20 +353,20 @@ line_segments_compute_length(struct line_segments* line) float line_segments_compute_area - (struct line_segments* line, + (struct line_segments* lines, const char flip_contour) { const uint32_t* ids; const float* pos; size_t iseg, nsegs; double area2 = 0.f; - ASSERT(line); + ASSERT(lines); - nsegs = line_segments_get_nsegments(line); + nsegs = line_segments_get_nsegments(lines); if(!nsegs) return 0.f; - ids = line_segments_get_ids(line); - pos = line_segments_get_pos(line); + ids = line_segments_get_ids(lines); + pos = line_segments_get_pos(lines); /* Build a triangle whose base is the contour segment and its appex is the * origin. Then compute the area of the triangle and add or sub it from the @@ -399,7 +399,7 @@ line_segments_compute_area res_T line_segments_setup_indexed_vertices - (struct line_segments* line, + (struct line_segments* lines, const unsigned nsegments, void (*get_indices)(const unsigned isegment, unsigned ids[2], void* ctx), const unsigned nverts, @@ -410,7 +410,7 @@ line_segments_setup_indexed_vertices unsigned iattr = 0; int has_position = 0; res_T res = RES_OK; - ASSERT(line); + ASSERT(lines); if(!nsegments || !nverts || !attribs || !nattribs) { res = RES_BAD_ARG; @@ -419,11 +419,11 @@ line_segments_setup_indexed_vertices /* Check indices description */ if(get_indices == S2D_KEEP) { - if(!line->indices) { /* No indice was previously set */ + if(!lines->indices) { /* No indice was previously set */ res = RES_BAD_ARG; goto error; } else { - const size_t nsegments_prev = line_segments_get_nsegments(line); + const size_t nsegments_prev = line_segments_get_nsegments(lines); if(nsegments_prev != nsegments) { /* Inconsistant data */ res = RES_BAD_ARG; goto error; @@ -442,12 +442,12 @@ line_segments_setup_indexed_vertices if(attribs[iattr].get == S2D_KEEP) { const enum s2d_attrib_usage attr_usage = attribs[iattr].usage; const enum s2d_type type = attribs[iattr].type; - if(!line->attribs[attr_usage]) { /* The vertex attrib was not set */ + if(!lines->attribs[attr_usage]) { /* The vertex attrib was not set */ res = RES_BAD_ARG; goto error; } else { - const enum s2d_type type_prev = line->attribs_type[attr_usage]; - const struct darray_float* attr = &line->attribs[attr_usage]->data; + const enum s2d_type type_prev = lines->attribs_type[attr_usage]; + const struct darray_float* attr = &lines->attribs[attr_usage]->data; size_t nverts_prev = darray_float_size_get(attr); nverts_prev /= s2d_type_get_dimension(type_prev); if(type_prev != type || nverts_prev != nverts) { /* Inconsistant data */ @@ -465,14 +465,14 @@ line_segments_setup_indexed_vertices goto error; } - line_setup_indices(line, nsegments, get_indices, nverts, data); + line_setup_indices(lines, nsegments, get_indices, nverts, data); /* Setup vertex data */ FOR_EACH(iattr, 0, nattribs) { if(attribs[iattr].usage == S2D_POSITION) { - line_setup_positions(line, nverts, attribs + iattr, data); + line_setup_positions(lines, nverts, attribs + iattr, data); } else { - line_setup_attribs(line, nverts, attribs + iattr, data); + line_setup_attribs(lines, nverts, attribs + iattr, data); } } @@ -484,21 +484,21 @@ error: void line_segments_compute_aabb - (struct line_segments* line, + (struct line_segments* lines, float lower[2], float upper[2]) { float* pos; size_t ivert, nverts; - ASSERT(line && lower && upper); + ASSERT(lines && lower && upper); f2_splat(lower, FLT_MAX); f2_splat(upper,-FLT_MAX); - nverts = line_segments_get_nverts(line); + nverts = line_segments_get_nverts(lines); if(!nverts) return; - pos = line_segments_get_pos(line); + pos = line_segments_get_pos(lines); FOR_EACH(ivert, 0, nverts) { const size_t ipos = ivert * 2/*#coords per vertex*/; f2_min(lower, lower, pos + ipos); diff --git a/src/s2d_line_segments.h b/src/s2d_line_segments.h @@ -73,53 +73,53 @@ struct line_segments { /* Segmented contour */ extern LOCAL_SYM res_T line_segments_create (struct s2d_device* dev, - struct line_segments** line); + struct line_segments** lines); extern LOCAL_SYM void line_segments_ref_get - (struct line_segments* line); + (struct line_segments* lines); extern LOCAL_SYM void line_segments_ref_put - (struct line_segments* line); + (struct line_segments* lines); extern LOCAL_SYM void line_segments_clear - (struct line_segments* line); + (struct line_segments* lines); extern LOCAL_SYM size_t line_segments_get_nsegments - (const struct line_segments* line); + (const struct line_segments* lines); extern LOCAL_SYM size_t line_segments_get_nverts - (const struct line_segments* line); + (const struct line_segments* lines); extern LOCAL_SYM uint32_t* line_segments_get_ids - (struct line_segments* line); + (struct line_segments* lines); extern LOCAL_SYM float* line_segments_get_pos - (struct line_segments* line); + (struct line_segments* lines); extern LOCAL_SYM float* line_segments_get_attr - (struct line_segments* line, + (struct line_segments* lines, const enum s2d_attrib_usage usage); extern LOCAL_SYM float line_segments_compute_length - (struct line_segments* line); + (struct line_segments* lines); extern LOCAL_SYM float line_segments_compute_area - (struct line_segments* line, + (struct line_segments* lines, const char flip_contour); extern LOCAL_SYM res_T line_segments_setup_indexed_vertices - (struct line_segments* line, + (struct line_segments* lines, const unsigned ntris, void (*get_indices)(const unsigned isegment, unsigned ids[2], void* ctx), const unsigned nverts, @@ -129,7 +129,7 @@ line_segments_setup_indexed_vertices extern LOCAL_SYM void line_segments_compute_aabb - (struct line_segments* line, + (struct line_segments* lines, float lower[2], float upper[2]); diff --git a/src/s2d_scene.c b/src/s2d_scene.c @@ -28,6 +28,7 @@ #include "s2d.h" #include "s2d_device_c.h" +#include "s2d_line_segments.h" #include "s2d_geometry.h" #include "s2d_scene_c.h" #include "s2d_shape_c.h" @@ -36,9 +37,265 @@ #include <rsys/float2.h> #include <rsys/mem_allocator.h> +struct ray_extended : public RTCRay { + struct s2d_scene* scene; + void* data; /* User defined data */ +}; + /******************************************************************************* * Helper functions ******************************************************************************/ +static INLINE void +hit_setup(struct s2d_scene* scn, const RTCRay* ray, struct s2d_hit* hit) +{ + (void)scn, (void)ray, (void)hit; + /* TODO */ +} + +static void +filter_wrapper(void* user_ptr, RTCRay& ray) +{ + struct s2d_hit hit; + struct hit_filter* filter = (struct hit_filter*)user_ptr; + struct ray_extended* ray_ex = static_cast<struct ray_extended*>(&ray); + + hit_setup(ray_ex->scene, &ray, &hit); + if(filter->func(&hit, ray_ex->org, ray_ex->dir, ray_ex->data, filter->data)) { + /* Discard the intersection */ + ray.geomID = RTC_INVALID_GEOMETRY_ID; + } +} + +static void +scene_geometry_flush_enable_state + (struct s2d_scene* scn, + struct geometry* geom, + const struct s2d_shape* shape) +{ + ASSERT(scn && geom && shape && geom->irtc != RTC_INVALID_GEOMETRY_ID); + if(geom->is_enabled == shape->is_enabled) + return; + + geom->is_enabled = shape->is_enabled; + if(geom->is_enabled) { + rtcEnable(scn->rtc_scn, geom->irtc); + } else { + rtcDisable(scn->rtc_scn, geom->irtc); + } + scn->is_rtc_scn_outdated = 1; +} + +static void +scene_geometry_flush_filter_function + (struct s2d_scene* scn, + struct geometry* geom, + const struct s2d_shape* shape) +{ + ASSERT(scn && geom && shape && geom->irtc != RTC_INVALID_GEOMETRY_ID); + + if(geom->lines->filter.func == shape->lines->filter.func + && geom->lines->filter.data == shape->lines->filter.data) + return; /* Up to date */ + + geom->lines->filter = shape->lines->filter; + if(!geom->lines->filter.func) { + rtcSetIntersectionFilterFunction(scn->rtc_scn, geom->irtc, NULL); + } else { + rtcSetIntersectionFilterFunction(scn->rtc_scn, geom->irtc, filter_wrapper); + rtcSetUserData(scn->rtc_scn, geom->irtc, &geom->lines->filter); + } + scn->is_rtc_scn_outdated = 1; +} + + +static res_T +scene_register_geometry(struct s2d_scene* scn, struct geometry* geom) +{ + ASSERT(scn && geom); + + /* Create the embree geometry if it is not valid */ + if(geom->irtc == RTC_INVALID_GEOMETRY_ID) { + geom->irtc = rtcNewQuadMesh + (scn->rtc_scn, + RTC_GEOMETRY_DYNAMIC, + line_segments_get_nsegments(geom->lines), + line_segments_get_nverts(geom->lines)*2/*Lines are extruded as quads*/); + if(geom->irtc == RTC_INVALID_GEOMETRY_ID) + return RES_UNKNOWN_ERR; + scn->is_rtc_scn_outdated = 1; + } + if(geom->irtc >= darray_geom_size_get(&scn->embree2geoms)) { + const res_T res = darray_geom_resize(&scn->embree2geoms, geom->irtc + 1); + if(res != RES_OK) { + rtcDeleteGeometry(scn->rtc_scn, geom->irtc); + geom->irtc = RTC_INVALID_GEOMETRY_ID; + return res; + } + } + geometry_ref_get(geom); + darray_geom_data_get(&scn->embree2geoms)[geom->irtc] = geom; + return RES_OK; +} + +static void +scene_geometry_flush_positions(struct s2d_scene* scn, struct geometry* geom) +{ + size_t nverts; + size_t i; + float* verts; + float* rtc_verts; + ASSERT(scn && geom && geom->irtc != RTC_INVALID_GEOMETRY_ID); + + /* Extrude segment vertices as quad whose Z is [-1, 1] */ + nverts = line_segments_get_nverts(geom->lines); + verts = line_segments_get_attr(geom->lines, S2D_POSITION); + rtc_verts = (float*)rtcMapBuffer(scn->rtc_scn, geom->irtc, RTC_VERTEX_BUFFER); + FOR_EACH(i, 0, nverts) { + size_t ivert = i*2/*#coords per vertex*/; + size_t rtc_ivert = i*4/*#coords + padding*/ * 2/*#rtc vertices per line vertex*/; + + rtc_verts[rtc_ivert + 0] = verts[ivert + 0]; + rtc_verts[rtc_ivert + 1] = verts[ivert + 1]; + rtc_verts[rtc_ivert + 2] = 1.f; + rtc_verts[rtc_ivert + 3] = 0.f; /* Padding */ + + rtc_verts[rtc_ivert + 4] = verts[ivert + 0]; + rtc_verts[rtc_ivert + 5] = verts[ivert + 1]; + rtc_verts[rtc_ivert + 6] = -1.f; + rtc_verts[rtc_ivert + 7] = 0.f; /* Padding */ + } + rtcUnmapBuffer(scn->rtc_scn, geom->irtc, RTC_VERTEX_BUFFER); +} + +static void +scene_geometry_flush_indices(struct s2d_scene* scn, struct geometry* geom) +{ + size_t nsegs; + size_t i; + uint32_t* ids; + uint32_t* rtc_ids; + ASSERT(scn && geom && geom->irtc != RTC_INVALID_GEOMETRY_ID); + + /* Define the index of the extruded line segments */ + nsegs = line_segments_get_nsegments(geom->lines); + ids = line_segments_get_ids(geom->lines); + rtc_ids = (uint32_t*)rtcMapBuffer(scn->rtc_scn, geom->irtc, RTC_INDEX_BUFFER); + FOR_EACH(i, 0, nsegs) { + size_t id = i*2/*#ids per segment*/; + size_t rtc_id = i*4/*#ids per quad*/; + + rtc_ids[rtc_id + 0] = ids[id + 0] * 2/*#rtc vertices per line vertex*/ + 0; + rtc_ids[rtc_id + 1] = ids[id + 0] * 2/*#rtc vertices per line vertex*/ + 1; + rtc_ids[rtc_id + 2] = ids[id + 1] * 2/*#rtc vertices per line vertex*/ + 1; + rtc_ids[rtc_id + 3] = ids[id + 1] * 2/*#rtc vertices per line vertex*/ + 0; + } + rtcUnmapBuffer(scn->rtc_scn, geom->irtc, RTC_INDEX_BUFFER); +} + +static res_T +scene_register_line_segments + (struct s2d_scene* scn, + struct s2d_shape* shape) +{ + struct geometry** pgeom = NULL; + struct geometry* geom = NULL; + size_t iattr; + char upd_pos, upd_ids; + + res_T res = RES_OK; + ASSERT(scn && shape); + + /* Retrieved the cached geometry */ + pgeom = htable_geom_find(&scn->cached_geoms, &shape); + if(pgeom) { + geom = *pgeom; + } else { + res = geometry_create(scn->dev, &geom); + if(res != RES_OK) goto error; + res = line_segments_create(scn->dev, &geom->lines); + if(res != RES_OK) goto error; + res = htable_geom_set(&scn->cached_geoms, &shape, &geom); + if(res != RES_OK) goto error; + geom->name = shape->id.index; + } + + /* Discard the geometries that are not geometrically valid */ + if(!shape->lines->indices || !shape->lines->attribs[S2D_POSITION]) { + if(geom->irtc != RTC_INVALID_GEOMETRY_ID) { + rtcDeleteGeometry(scn->rtc_scn, geom->irtc); + geom->irtc = RTC_INVALID_GEOMETRY_ID; + scn->is_rtc_scn_outdated = 1; + } + line_segments_clear(geom->lines); + goto exit; + } + + /* Define which geometry buffers were updated */ + upd_ids = geom->lines->indices != shape->lines->indices + || ((shape->lines->update_mask & INDEX_BUFFER) != 0); + upd_pos = geom->lines->attribs[S2D_POSITION] != shape->lines->attribs[S2D_POSITION] + || ((shape->lines->update_mask & VERTEX_BUFFER) != 0); + + /* Get a reference onto the shape lines segments indices */ + if(geom->lines->indices != shape->lines->indices) { + if(geom->lines->indices) { /* Release previous indices of the geometry */ + index_buffer_ref_put(geom->lines->indices); + geom->lines->indices = NULL; + } + ASSERT(shape->lines->indices); + index_buffer_ref_get(shape->lines->indices); + geom->lines->indices = shape->lines->indices; + } + + /* Get a reference onto the shape line segments attribs */ + FOR_EACH(iattr, 0, S2D_ATTRIBS_COUNT__) { + if(geom->lines->attribs[iattr] == shape->lines->attribs[iattr]) + continue; + + if(geom->lines->attribs[iattr]) { /* Release the previous geometry attribs */ + vertex_buffer_ref_put(geom->lines->attribs[iattr]); + geom->lines->attribs[iattr] = NULL; + } + + if(!shape->lines->attribs[iattr]) continue; + + vertex_buffer_ref_get(shape->lines->attribs[iattr]); + geom->lines->attribs[iattr] = shape->lines->attribs[iattr]; + geom->lines->attribs_type[iattr] = shape->lines->attribs_type[iattr]; + } + + /* The line segments were resize => the Embree geometry is no more valid */ + if(shape->lines->resize_mask && geom->irtc != RTC_INVALID_GEOMETRY_ID) { + rtcDeleteGeometry(scn->rtc_scn, geom->irtc); + geom->irtc = RTC_INVALID_GEOMETRY_ID; + scn->is_rtc_scn_outdated = 1; + } + + res = scene_register_geometry(scn, geom); + if(res != RES_OK) goto error; + + if(upd_pos) { /* Update the Embree vertex buffer if necessary */ + scene_geometry_flush_positions(scn, geom); + } + if(upd_ids) { /* Update the Embree index buffer if necessary */ + scene_geometry_flush_indices(scn, geom); + } + + /* Flush the remaining geometry states */ + scene_geometry_flush_enable_state(scn, geom, shape); + scene_geometry_flush_filter_function(scn, geom, shape); + geom->flip_contour = shape->flip_contour; + + /* Flush the shape line segments states */ + shape->lines->resize_mask = 0; + shape->lines->update_mask = 0; + +exit: + return res; +error: + goto exit; +} + static void scene_session_clear(struct s2d_scene* scn) { @@ -56,16 +313,25 @@ scene_session_clear(struct s2d_scene* scn) scn->session_mask = 0; } -static void -scene_detach_shape(struct s2d_scene* scn, struct s2d_shape* shape) +static res_T +scene_detach_shape + (struct s2d_scene* scn, struct s2d_shape* shape, const char* caller_name) { struct geometry** pgeom; ASSERT(scn && shape && !is_list_empty(&shape->scene_attachment)); - ASSERT(scn->session_mask == 0); pgeom = htable_geom_find(&scn->cached_geoms, &shape); if(pgeom) { /* Remove the cached geometry */ struct geometry* geom = *pgeom; + + if(scn->session_mask != 0) { + if(scn->dev->verbose) { + logger_print(scn->dev->logger, LOG_ERROR, + "%s: Cannot detach a shape currently in used in a scene session.\n", + caller_name); + } + return RES_BAD_OP; + } if(geom->irtc != RTC_INVALID_GEOMETRY_ID) { rtcDeleteGeometry(scn->rtc_scn, geom->irtc); geom->irtc = RTC_INVALID_GEOMETRY_ID; @@ -77,6 +343,77 @@ scene_detach_shape(struct s2d_scene* scn, struct s2d_shape* shape) list_del(&shape->scene_attachment); S2D(shape_ref_put(shape)); + return RES_OK; +} + +static void +scene_compute_aabb(struct s2d_scene* scn) +{ + struct list_node* node; + struct s2d_shape* shape; + struct geometry** pgeom; + struct geometry* geom; + float lower[2], upper[2]; + + f2_splat(scn->lower, FLT_MAX); + f2_splat(scn->upper,-FLT_MAX); + + LIST_FOR_EACH(node, &scn->shapes) { + shape = CONTAINER_OF(node, struct s2d_shape, scene_attachment); + pgeom = htable_geom_find(&scn->cached_geoms, &shape); + ASSERT(pgeom != NULL); + geom = *pgeom; + + if(!geom->is_enabled) continue; + + line_segments_compute_aabb(geom->lines, lower, upper); + f2_min(scn->lower, scn->lower, lower); + f2_max(scn->upper, scn->upper, upper); + } +} + +static res_T +scene_sync + (struct s2d_scene* scn, const int session_mask, const char* caller_name) +{ + struct list_node* node; + struct s2d_shape* shape; + res_T res = RES_OK; + ASSERT(scn); + + if(scn->session_mask != 0) { + if(scn->dev->verbose) { + logger_print(scn->dev->logger, LOG_ERROR, + "%s: Invalid operation. The scene cannot be synced several times.\n", + caller_name); + } + res = RES_BAD_OP; + goto error; + } + + LIST_FOR_EACH(node, &scn->shapes) { + shape = CONTAINER_OF(node, struct s2d_shape, scene_attachment); + res = scene_register_line_segments(scn, shape); + if(res != RES_OK) goto error; + } + + if((session_mask & S2D_SAMPLE) != 0) { + FATAL("The S2D_SAMPLE session is not implemented yet!\n"); + } + if((session_mask & S2D_GET_PRIMITIVE) != 0) { + FATAL("The S2D_GET_PRIMITIVE session is not implemented yet!\n"); + } + if((session_mask & S2D_TRACE) != 0 && scn->is_rtc_scn_outdated) { + rtcCommit(scn->rtc_scn); + scn->is_rtc_scn_outdated = 0; + } + scn->session_mask = session_mask; + scene_compute_aabb(scn); + +exit: + return res; +error: + goto exit; } static void @@ -186,16 +523,11 @@ s2d_scene_attach_shape(struct s2d_scene* scn, struct s2d_shape* shape) res_T s2d_scene_detach_shape(struct s2d_scene* scn, struct s2d_shape* shape) { + res_T res = RES_OK; char is_attached; + + if(!scn || !shape) return RES_BAD_ARG; - if(scn->session_mask != 0) { - if(scn->dev->verbose) { - logger_print(scn->dev->logger, LOG_ERROR, - "%s: Cannot detach a shape while a session is active onto the scene.\n", - __FUNCTION__); - } - return RES_BAD_OP; - } if(!(S2D(shape_is_attached(shape, &is_attached)), is_attached)) { if(scn->dev->verbose) { logger_print(scn->dev->logger, LOG_ERROR, @@ -217,7 +549,9 @@ s2d_scene_detach_shape(struct s2d_scene* scn, struct s2d_shape* shape) ASSERT(is_found); } #endif - scene_detach_shape(scn, shape); + res = scene_detach_shape(scn, shape, __FUNCTION__); + if(res != RES_OK) return res; + return RES_OK; } @@ -237,8 +571,52 @@ s2d_scene_clear(struct s2d_scene* scn) LIST_FOR_EACH_SAFE(node, tmp, &scn->shapes) { struct s2d_shape* shape = CONTAINER_OF (node, struct s2d_shape, scene_attachment); - scene_detach_shape(scn, shape); + res_T res = scene_detach_shape(scn, shape, __FUNCTION__); + ASSERT(res == RES_OK); (void)res; + } + return RES_OK; +} + +res_T +s2d_scene_begin_session(struct s2d_scene* scn, const int session_mask) +{ + if(!scn) return RES_BAD_ARG; + if(!(session_mask & S2D_TRACE) + && !(session_mask & S2D_SAMPLE) + && !(session_mask & S2D_GET_PRIMITIVE)) { + if(scn->dev->verbose) { + logger_print(scn->dev->logger, LOG_ERROR, + "%s: Invalid session mask. No valid session is defined.\n", + __FUNCTION__); + } + return RES_BAD_ARG; } + return scene_sync(scn, session_mask, __FUNCTION__); +} + +res_T +s2d_scene_end_session(struct s2d_scene* scn) +{ + if(!scn) + return RES_BAD_ARG; + if(!scn->session_mask) { + if(scn->dev->verbose) { + logger_print(scn->dev->logger, LOG_ERROR, + "%s: Cannot end session. No session is currently active onto the scene.\n", + __FUNCTION__); + } + return RES_BAD_ARG; + } + scene_session_clear(scn); + return RES_OK; +} + +res_T +s2d_scene_get_session_mask(struct s2d_scene* scn, int* session_mask) +{ + if(!scn || !session_mask) + return RES_BAD_ARG; + *session_mask = scn->session_mask; return RES_OK; } diff --git a/src/s2d_shape.c b/src/s2d_shape.c @@ -48,7 +48,7 @@ shape_release(ref_T* ref) /* The shape should not be attached */ ASSERT(is_list_empty(&shape->scene_attachment)); - line_segments_ref_put(shape->line); + line_segments_ref_put(shape->lines); MEM_RM(dev->allocator, shape); S2D(device_ref_put(dev)); } @@ -81,7 +81,7 @@ s2d_shape_create_line_segments shape->is_enabled = 1; shape->flip_contour = 0; - res = line_segments_create(dev, &shape->line); + res = line_segments_create(dev, &shape->lines); if(res != RES_OK) goto error; exit: @@ -164,7 +164,7 @@ s2d_line_segments_setup_indexed_vertices { if(!shape) return RES_BAD_ARG; return line_segments_setup_indexed_vertices - (shape->line, nsegments, get_indices, nverts, attribs, nattribs, data); + (shape->lines, nsegments, get_indices, nverts, attribs, nattribs, data); } res_T @@ -174,7 +174,7 @@ s2d_line_segments_copy(const struct s2d_shape* src, struct s2d_shape* dst) if(src == dst) return RES_OK; dst->flip_contour = src->flip_contour; dst->is_enabled = src->is_enabled; - line_segments_copy_indexed_vertices(src->line, dst->line); + line_segments_copy_indexed_vertices(src->lines, dst->lines); return RES_OK; } @@ -183,7 +183,7 @@ s2d_line_segments_get_vertices_count (const struct s2d_shape* shape, unsigned* nverts) { if(!shape || !nverts) return RES_BAD_ARG; - *nverts = (unsigned)line_segments_get_nverts(shape->line); + *nverts = (unsigned)line_segments_get_nverts(shape->lines); return RES_OK; } @@ -199,16 +199,16 @@ s2d_line_segments_get_vertex_attrib if(!shape || (unsigned)usage >= S2D_ATTRIBS_COUNT__ - || !shape->line->attribs[usage] + || !shape->lines->attribs[usage] || !attrib - || ivert >= (unsigned)line_segments_get_nverts(shape->line)) + || ivert >= (unsigned)line_segments_get_nverts(shape->lines)) return RES_BAD_ARG; attrib->usage = usage; - attrib->type = shape->line->attribs_type[usage]; + attrib->type = shape->lines->attribs_type[usage]; dim = s2d_type_get_dimension(attrib->type); - data = line_segments_get_attr(shape->line, usage) + ivert * dim; + data = line_segments_get_attr(shape->lines, usage) + ivert * dim; FOR_EACH(i, 0, dim) attrib->value[i] = data[i]; return RES_OK; } @@ -218,7 +218,7 @@ s2d_line_segments_get_segments_count (const struct s2d_shape* shape, unsigned* nsegments) { if(!shape || !nsegments) return RES_BAD_ARG; - *nsegments = (unsigned)line_segments_get_nsegments(shape->line); + *nsegments = (unsigned)line_segments_get_nsegments(shape->lines); return RES_OK; } @@ -231,10 +231,10 @@ s2d_line_segments_get_segment_indices const unsigned* data; if(!shape || !ids - || isegment >= (unsigned)line_segments_get_nsegments(shape->line)) + || isegment >= (unsigned)line_segments_get_nsegments(shape->lines)) return RES_BAD_ARG; - data = line_segments_get_ids(shape->line) + isegment * 2/*#ids per segment*/; + data = line_segments_get_ids(shape->lines) + isegment * 2/*#ids per segment*/; ids[0] = data[0]; ids[1] = data[1]; return RES_OK; @@ -247,8 +247,8 @@ s2d_line_segments_set_hit_filter_function void* data) { if(!shape) return RES_BAD_ARG; - shape->line->filter.func = func; - shape->line->filter.data = data; + shape->lines->filter.func = func; + shape->lines->filter.data = data; return RES_OK; } @@ -256,7 +256,7 @@ res_T s2d_line_segments_get_hit_filter_data(struct s2d_shape* shape, void** data) { if(!shape || !data) return RES_BAD_ARG; - *data = shape->line->filter.data; + *data = shape->lines->filter.data; return RES_OK; } diff --git a/src/s2d_shape_c.h b/src/s2d_shape_c.h @@ -42,7 +42,7 @@ struct s2d_shape { char flip_contour; char is_enabled; - struct line_segments* line; + struct line_segments* lines; struct s2d_device* dev; ref_T ref; }; diff --git a/src/test_s2d_scene.c b/src/test_s2d_scene.c @@ -33,9 +33,11 @@ int main(int argc, char** argv) { struct mem_allocator allocator; + struct s2d_vertex_data attrib; struct s2d_device* dev; struct s2d_scene* scn; struct s2d_shape* shape; + int mask; (void)argc, (void)argv; mem_init_proxy_allocator(&allocator, &mem_default_allocator); @@ -74,6 +76,40 @@ main(int argc, char** argv) CHECK(s2d_scene_attach_shape(scn, shape), RES_OK); CHECK(s2d_scene_clear(scn), RES_OK); + CHECK(s2d_scene_begin_session(NULL, 0), RES_BAD_ARG); + CHECK(s2d_scene_begin_session(scn, 0), RES_BAD_ARG); + CHECK(s2d_scene_begin_session(NULL, S2D_TRACE), RES_BAD_ARG); + CHECK(s2d_scene_begin_session(scn, S2D_TRACE), RES_OK); + + CHECK(s2d_scene_get_session_mask(NULL, NULL), RES_BAD_ARG); + CHECK(s2d_scene_get_session_mask(scn, NULL), RES_BAD_ARG); + CHECK(s2d_scene_get_session_mask(NULL, &mask), RES_BAD_ARG); + CHECK(s2d_scene_get_session_mask(scn, &mask), RES_OK); + CHECK(mask, S2D_TRACE); + + CHECK(s2d_scene_attach_shape(scn, shape), RES_OK); + CHECK(s2d_scene_clear(scn), RES_BAD_OP); + CHECK(s2d_scene_detach_shape(scn, shape), RES_OK); + + CHECK(s2d_scene_end_session(NULL), RES_BAD_ARG); + CHECK(s2d_scene_end_session(scn), RES_OK); + CHECK(s2d_scene_end_session(scn), RES_BAD_ARG); + CHECK(s2d_scene_get_session_mask(scn, &mask), RES_OK); + CHECK(mask, 0); + + + attrib.type = S2D_FLOAT2; + attrib.usage = S2D_POSITION; + attrib.get = box_get_position; + CHECK(s2d_line_segments_setup_indexed_vertices + (shape, box_nsegs, box_get_ids, box_nverts, &attrib, 1, (void*)&box_desc), + RES_OK); + + CHECK(s2d_scene_attach_shape(scn, shape), RES_OK); + CHECK(s2d_scene_begin_session(scn, S2D_TRACE), RES_OK); + CHECK(s2d_scene_detach_shape(scn, shape), RES_BAD_OP); + CHECK(s2d_scene_end_session(scn), RES_OK); + CHECK(s2d_shape_ref_put(shape), RES_OK); CHECK(s2d_scene_ref_put(scn), RES_OK); CHECK(s2d_device_ref_put(dev), RES_OK);