star-3d

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

commit 1a2901628e769475ad079776a5812c2fb205277a
parent 2bb9edf0c7fbe273d57c69b48af7b4ef39f46ddf
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon, 29 May 2017 15:05:11 +0200

Merge branch 'release-0.4'

Diffstat:
MREADME.md | 41++++++++++++++++++++++++++++++++---------
Mcmake/CMakeLists.txt | 8+++++++-
Msrc/s3d.h | 161+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/s3d_backend.h | 2+-
Msrc/s3d_buffer.h | 2+-
Msrc/s3d_c.h | 2+-
Msrc/s3d_device.c | 3++-
Msrc/s3d_device_c.h | 2+-
Msrc/s3d_geometry.c | 2+-
Msrc/s3d_geometry.h | 13++++++++++++-
Msrc/s3d_instance.c | 21+--------------------
Msrc/s3d_instance.h | 14++++++--------
Msrc/s3d_mesh.c | 47+++--------------------------------------------
Msrc/s3d_mesh.h | 9+--------
Msrc/s3d_primitive.c | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/s3d_scene.c | 1305++++---------------------------------------------------------------------------
Msrc/s3d_scene_c.h | 71+++++++++++++----------------------------------------------------------
Asrc/s3d_scene_view.c | 1473+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/s3d_scene_view_c.h | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/s3d_shape.c | 21++++-----------------
Msrc/s3d_shape_c.h | 3+--
Asrc/test_s3d_camera.h | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_s3d_cbox.h | 4++--
Msrc/test_s3d_device.c | 2+-
Msrc/test_s3d_primitive.c | 94++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/test_s3d_sampler.c | 84+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/test_s3d_scene.c | 184++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Asrc/test_s3d_scene_view.c | 1048+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_s3d_seams.c | 192+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_s3d_shape.c | 12+-----------
Msrc/test_s3d_trace_ray.c | 212++++++++++++++++++++++++++++---------------------------------------------------
Asrc/test_s3d_trace_ray_instance.c | 344+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_s3d_utils.h | 2+-
33 files changed, 3874 insertions(+), 1813 deletions(-)

diff --git a/README.md b/README.md @@ -19,18 +19,19 @@ The main concept exposed by the Star-3D C API are *shapes*. A *shape* represents a 3D object whose data is defined by the user and can be updated at any time. A 3D environment is built by attaching one or several *shapes* to a *scene*. To access the *scene* data through sampling, ray-tracing or indexing, -one have to enable a *scene session* that commits the current *scene* geometry -as the geometry to use. A *scene* can also be instantiated into one or several -*shapes*, each with its own attributes (e.g. position, orientation, etc.). -Since the *scene* geometry is stored once even though it is instantiated -several times, this feature can be used to create extremely complex environment -with a low memory footprint. +one have to create a *scene view* that commits the current *scene* geometry +as the geometry of the *view*. A *scene* can also be instantiated into one or +several *shapes*, each with its own attributes (e.g. position, orientation, +etc.). Since the *scene* geometry is stored once even though it is +instantiated several times, this feature can be used to create extremely +complex environment with a low memory footprint. Star-3D is currently used in several softwares dealing with complex arbitrary 3D contents, ranging from graphics applications and thermal simulations to electromagnetism. Please refer to these projects for informations on their purpose. + * [Star-4V/S](https://gitlab.com/meso-star/star-4v_s.git) * [Star-Display](https://gitlab.com/meso-star/star-display.git) * [Star-GebhartFactor](https://gitlab.com/meso-star/star-gf.git) * [Star-Schiff](https://gitlab.com/meso-star/star-schiff.git) @@ -80,12 +81,11 @@ installed. Once installed, the Star-3D library and its associated headers are deployed, providing the whole environment required to develop C/C++ applications with -Star-3D. The `<STAR3D_INSTALL_DIR>/include/star/s3d.h` header defines the +Star-3D. The `<STAR3D_INSTALL_DIR>/include/star/s3d.h` header defines the Star-3D Application Programming Interface (API). Refer to this [file](https://gitlab.com/meso-star/star-3d/blob/master/src/s3d.h) for the API reference documentation. - A Star-3D [CMake package](https://cmake.org/cmake/help/v3.5/manual/cmake-packages.7.html) is also installed to facilitate the use of Star-3D in projects relying on the @@ -111,9 +111,32 @@ instance, on a GNU/Linux system: with `<STAR3D_INSTALL_DIR>` the install directory of Star-3D and `<MY_PROJECT_DIR>` the location of the `CMakeLists.txt` file. +## Release notes + +### Version 0.4 + +- Implement the `s3d_scene_view` API; it replaces the + `s3d_scene_<begin|end>_session` functions that were removed. A view registers + the state of the scene from which it is created. It is used to retrieve the + scene data through ray-tracing, sampling or indexing. Several views can be + created on the same scene. +- Add the possibility to attach a same shape to several scenes. +- Fix a memory overconsumption with instantiated shapes: the instantiated + back-end data were copied rather than shared. +- Add the `s3d_scene_shapes_count` function that returns the overall number of + shapes in the scene. +- Add the `s3d_instance_set_transform` and `s3d_instance_transform` functions + that sets or gets the transformation matrix of the instance, respectively. +- Add the `s3d_primitive_get_transform` function that gets the transformation + matrix of a primitive. +- Add the `s3d_primitive_has_attrib` function that returns if a primitive has a + specific attribute or not. +- Add the `s3d_triangle_get_vertex_attrib` function that retrieves the + vertex attributes of a triangular primitive. + ## License -Star-3D is Copyright (C) |Meso|Star> 2015-2016 (<contact@meso-star.com>). It +Star-3D is Copyright (C) |Meso|Star> 2015-2017 (<contact@meso-star.com>). It is a free software released under the [OSI](http://opensource.org)-approved CeCILL license. You are welcome to redistribute it under certain conditions; refer to the COPYING files for details. diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +# Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) # # This software is a computer program whose purpose is to generate files used # to build the Star-3D library. @@ -68,6 +68,7 @@ set(S3D_FILES_SRC s3d_mesh.c s3d_primitive.c s3d_scene.c + s3d_scene_view.c s3d_shape.c) set(S3D_FILES_INC_API s3d.h) set(S3D_FILES_INC @@ -78,6 +79,8 @@ set(S3D_FILES_INC s3d_geometry.h s3d_instance.h s3d_mesh.h + s3d_scene_c.h + s3d_scene_view_c.h s3d_shape_c.h) set(S3D_FILES_DOC COPYING.fr COPYING.en README.md) @@ -139,7 +142,10 @@ if(NOT NO_TEST) new_test(test_s3d_primitive) new_test(test_s3d_sampler) new_test(test_s3d_scene) + new_test(test_s3d_scene_view) new_test(test_s3d_shape) + new_test(test_s3d_trace_ray_instance) + new_test(test_s3d_seams) build_test(test_s3d_trace_ray) register_test(test_s3d_trace_ray_legacy test_s3d_trace_ray) diff --git a/src/s3d.h b/src/s3d.h @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -105,8 +105,8 @@ struct s3d_primitive { void* inst__; }; -#define S3D_PRIMITIVE_NULL__ {\ - S3D_INVALID_ID, S3D_INVALID_ID, S3D_INVALID_ID, S3D_INVALID_ID, NULL, NULL\ +#define S3D_PRIMITIVE_NULL__ { \ + S3D_INVALID_ID, S3D_INVALID_ID, S3D_INVALID_ID, S3D_INVALID_ID, NULL, NULL \ } static const struct s3d_primitive S3D_PRIMITIVE_NULL = S3D_PRIMITIVE_NULL__; @@ -150,16 +150,16 @@ struct s3d_hit { }; /* Constant defining a NULL intersection. Should be used to initialize a hit */ -#define S3D_HIT_NULL__ {\ - { S3D_INVALID_ID, S3D_INVALID_ID, S3D_INVALID_ID, S3D_INVALID_ID, NULL, NULL },\ - { 0.f, 0.f, 0.f },\ - { 0.f, 0.f },\ - FLT_MAX\ +#define S3D_HIT_NULL__ { \ + {S3D_INVALID_ID, S3D_INVALID_ID, S3D_INVALID_ID, S3D_INVALID_ID, NULL, NULL},\ + {0.f, 0.f, 0.f}, \ + {0.f, 0.f}, \ + FLT_MAX \ } static const struct s3d_hit S3D_HIT_NULL = S3D_HIT_NULL__; -enum s3d_session_flag { +enum s3d_scene_view_flag { S3D_TRACE = BIT(0), S3D_SAMPLE = BIT(1), S3D_GET_PRIMITIVE = BIT(2) @@ -175,16 +175,17 @@ enum s3d_session_flag { * discarded and a value not equal to zero otherwise. */ typedef int (*s3d_hit_filter_function_T) - (const struct s3d_hit* hit, - const float ray_org[3], - const float ray_dir[3], - void* ray_data, /* User data submitted on trace ray(s) invocation */ - void* filter_data); /* Data defined on the setup of the filter function */ + (const struct s3d_hit* hit, + const float ray_org[3], + const float ray_dir[3], + void* ray_data, /* User data submitted on trace ray(s) invocation */ + void* filter_data); /* Data defined on the setup of the filter function */ /* Forward declaration of s3d opaque data types */ struct s3d_device; /* Entry point of the library */ struct s3d_scene; /* Collection of shapes */ -struct s3d_shape; /* Untyped geometry */ +struct s3d_scene_view; /* Scene state */ +struct s3d_shape; /* Surfacic geometry */ /* Forward declaration of external data types */ struct logger; @@ -242,9 +243,8 @@ s3d_scene_instantiate (struct s3d_scene* scn, struct s3d_shape** shape); -/* Attach the shape to the scene. If the shape is already attached to the same - * or another scene, nothing is performed and a RES_BAD_ARG error is returned. - * On success, the scene gets a reference onto the attached shape */ +/* Attach the shape to the scene. On success, the scene gets a reference onto + * the attached shape */ S3D_API res_T s3d_scene_attach_shape (struct s3d_scene* scn, @@ -263,49 +263,56 @@ S3D_API res_T s3d_scene_clear (struct s3d_scene* scn); -/* Synchronize the scene geometry with the geometry of its attached shapes. If - * a s3d_scene_begin_session is already active on `scn' or one of its attached - * instance a RES_BAD_OP error is returned. On success neither another begin - * session nor a clear or shape_detach can be invoked on `scn' and its attached - * instances until s3d_scene_end_session is called. */ S3D_API res_T -s3d_scene_begin_session +s3d_scene_get_device (struct s3d_scene* scn, - const int session_mask); /* Combination of s3d_session_flag */ + struct s3d_device** dev); -/* End the session on the `scn' */ S3D_API res_T -s3d_scene_end_session - (struct s3d_scene* scn); +s3d_scene_get_shapes_count + (struct s3d_scene* scn, + size_t* nshapes); +/******************************************************************************* + * Scene view API - State of the scene geometry + ******************************************************************************/ S3D_API res_T -s3d_scene_get_session_mask +s3d_scene_view_create (struct s3d_scene* scn, + const int mask, /* Combination of s3d_scene_view_flag */ + struct s3d_scene_view** scnview); + +S3D_API res_T +s3d_scene_view_ref_get + (struct s3d_scene_view* scnview); + +S3D_API res_T +s3d_scene_view_ref_put + (struct s3d_scene_view* scnview); + +S3D_API res_T +s3d_scene_view_get_mask + (struct s3d_scene_view* scnview, int* mask); -/* Trace a ray into the `scn' and return the closest intersection. The ray is - * defined by `origin' + t*`direction' = 0 with t in [`range[0]', `range[1]'). - * Note that if range is degenerated (i.e. `range[0]' >= `range[1]') then the - * ray is not traced and `hit' is set to S3D_HIT_NULL. Can be called only if an - * S3D_TRACE session is active on `scn' */ S3D_API res_T -s3d_scene_trace_ray - (struct s3d_scene* scn, +s3d_scene_view_trace_ray + (struct s3d_scene_view* scnview, const float origin[3], /* Ray origin */ const float direction[3], /* Ray direction. Must be normalized */ const float range[2], /* In [0, INF)^2 */ void* ray_data, /* User ray data sent to the hit filter func. May be NULL */ struct s3d_hit* hit); -/* Trace a bundle of rays into `scn' and return the closest intersection along - * them. The rays are defined by `origin' + t*`direction' = 0 with t in +/* Trace a bundle of rays into the scene and return the closest intersection + * along them. The rays are defined by `origin' + t*`direction' = 0 with t in * [`range[0]', `range[1]'). Note that if a range is degenerated (i.e. * `range[0]' >= `range[1]') then its associated ray is not traced and `hit' is - * set to S3D_HIT_NULL. Can be called only if an S3D_TRACE session is active on - * `scn' */ + * set to S3D_HIT_NULL. Can be called only if the scnview was created with the + * S3D_TRACE flag. */ S3D_API res_T -s3d_scene_trace_rays - (struct s3d_scene* scn, +s3d_scene_view_trace_rays + (struct s3d_scene_view* scnview, const size_t nrays, /* # rays */ const int mask, /* Combination of s3d_ray_flag */ const float* origins, /* List of 3D ray origins */ @@ -315,59 +322,50 @@ s3d_scene_trace_rays const size_t sizeof_ray_data, /* Size in Bytes of *one* ray data */ struct s3d_hit* hits); -/* Uniformly sample the scene and returned the sampled primitive and its sample - * uv position. Can be called only if a S3D_SAMPLE session is active on `scn'*/ +/* Uniformly sample the scene and return the sampled primitive and its sample + * uv position. Can be called only if the scnview was created with the + * S3D_SAMPLE flag */ S3D_API res_T -s3d_scene_sample - (struct s3d_scene* scn, +s3d_scene_view_sample + (struct s3d_scene_view* scnview, const float u, const float v, const float w, /* Random numbers in [0, 1) */ struct s3d_primitive* primitive, /* Sampled primitive */ float st[2]); /* Sampled parametric coordinates on the primitive */ -/* Retrieve a primitive from the scene. Can be called only if a - * S3D_GET_PRIMITIVE session is active on `scn' */ +/* Retrieve a primitive from the scene. Can be called only if the scnview was + * created with the S3D_GET_PRIMITIVE flag */ S3D_API res_T -s3d_scene_get_primitive - (struct s3d_scene* scn, +s3d_scene_view_get_primitive + (struct s3d_scene_view* scnview, const unsigned iprim, /* in [0, #prims) */ struct s3d_primitive* prim); -/* Retrieve the number of scene primitives. Can be called only if a sessio is - * active on `scn' */ +/* Return the overall number of scene primitives */ S3D_API res_T -s3d_scene_primitives_count - (struct s3d_scene* scn, +s3d_scene_view_primitives_count + (struct s3d_scene_view* scnview, size_t* primitives_count); -/* Compute the overall scene surface area. Can be called only if a session is - * active on `scn' */ +/* Compute the overall scene surface area */ S3D_API res_T -s3d_scene_compute_area - (struct s3d_scene* scn, +s3d_scene_view_compute_area + (struct s3d_scene_view* scnview, float* area); /* This function assumes that the scene defines a closed volume and that the - * normals point into the volume. Can be called only if a session is active on - * `scn' */ + * normals point into the volume. */ S3D_API res_T -s3d_scene_compute_volume - (struct s3d_scene* scn, +s3d_scene_view_compute_volume + (struct s3d_scene_view* scnview, float* volume); -/* Retrieve the Axis Aligned Bounding Box of the scene. Can be called only if a - * session is active on `scn' */ +/* Retrieve the Axis Aligned Bounding Box of the scene */ S3D_API res_T -s3d_scene_get_aabb - (struct s3d_scene* scn, +s3d_scene_view_get_aabb + (struct s3d_scene_view* scnview, float lower[3], /* AABB lower bound */ float upper[3]); /* AABB upper bound */ -/* Retrieve the device from which the scene was created */ -S3D_API res_T -s3d_scene_get_device - (struct s3d_scene* scn, - struct s3d_device** dev); - /******************************************************************************* * Shape API - A shape defines a geometry that can be attached to *one* scene. ******************************************************************************/ @@ -389,7 +387,7 @@ s3d_shape_ref_put * representation of the caller with a simple dynamic array */ S3D_API res_T s3d_shape_get_id - (struct s3d_shape* shape, + (const struct s3d_shape* shape, unsigned* id); /* Enable/disable the shape, i.e. it cannot be hit when its associated scene is @@ -402,15 +400,9 @@ s3d_shape_enable /* Return whether or not the shape is enabled, i.e. ray-traced. Default is 1 */ S3D_API res_T s3d_shape_is_enabled - (struct s3d_shape* shape, + (const struct s3d_shape* shape, char* is_enabled); -/* Define whether the shape is attached or not */ -S3D_API res_T -s3d_shape_is_attached - (struct s3d_shape* shape, - char* is_attached); - /* Flip the surface orientation, i.e. flip the geometric normal of the surface */ S3D_API res_T s3d_shape_flip_surface @@ -452,6 +444,13 @@ s3d_primitive_get_transform (const struct s3d_primitive* prim, float transform[12]); /* 3x4 column major matrix */ +S3D_API res_T +s3d_triangle_get_vertex_attrib + (const struct s3d_primitive* prim, + const size_t ivertex, /* in [0..3[ */ + const enum s3d_attrib_usage usage, + struct s3d_attrib* attrib); + /******************************************************************************* * Mesh API - Manage a triangular meshes ******************************************************************************/ @@ -517,7 +516,7 @@ s3d_mesh_get_hit_filter_data * Instance API - An instance is a shape that encapsulates a scene and that * supports a local to world transformation. Since the scene geometry is stored * only a single time even though it is instantiated in several positions, one - * can use this feature to create extremely large scene + * can use this feature to create extremely large scene. ******************************************************************************/ S3D_API res_T s3d_instance_set_position diff --git a/src/s3d_backend.h b/src/s3d_backend.h @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly diff --git a/src/s3d_buffer.h b/src/s3d_buffer.h @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly diff --git a/src/s3d_c.h b/src/s3d_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly diff --git a/src/s3d_device.c b/src/s3d_device.c @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -61,6 +61,7 @@ device_release(ref_T* ref) struct s3d_device* dev; ASSERT(ref); dev = CONTAINER_OF(ref, struct s3d_device, ref); + ASSERT(flist_name_is_empty(&dev->names) == 1); flist_name_release(&dev->names); rtcDeleteDevice(dev->rtc); MEM_RM(dev->allocator, dev); diff --git a/src/s3d_device_c.h b/src/s3d_device_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly diff --git a/src/s3d_geometry.c b/src/s3d_geometry.c @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly diff --git a/src/s3d_geometry.h b/src/s3d_geometry.h @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -44,11 +44,22 @@ enum geometry_type { GEOM_NONE = GEOM_TYPES_COUNT__ }; +enum embree_attrib { + EMBREE_ENABLE = BIT(0), + EMBREE_FILTER_FUNCTION = BIT(1), + EMBREE_INDICES = BIT(2), + EMBREE_TRANSFORM = BIT(4), + EMBREE_VERTICES = BIT(5) +}; + /* Backend geometry */ struct geometry { unsigned name; /* Client side identifier */ unsigned irtc; /* Embree identifier */ unsigned scene_prim_id_offset; /* Offset from local to scene prim_id */ + + int embree_outdated_mask; /* Combination of embree_attrib */ + char flip_surface; /* Is the geometry surface flipped? */ char is_enabled; /* Is the geometry enabled? */ diff --git a/src/s3d_instance.c b/src/s3d_instance.c @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -74,7 +74,6 @@ instance_create } f33_set_identity(inst->transform); /* rotation */ f3_splat(inst->transform + 9, 0.f); /* Translation */ - inst->update_transform = 0; ref_init(&inst->ref); S3D(scene_ref_get(scn)); inst->scene = scn; @@ -103,21 +102,3 @@ instance_ref_put(struct instance* inst) ref_put(&inst->ref, instance_release); } -float -instance_compute_volume(struct instance* inst, const char flip_surface) -{ - struct list_node* node; - float volume = 0.f; - ASSERT(inst); - - /* TODO take into account the scale factor of the instance */ - LIST_FOR_EACH(node, &inst->scene->shapes) { - struct s3d_shape* shape = CONTAINER_OF - (node, struct s3d_shape, scene_attachment); - const char flip = flip_surface ^ shape->flip_surface; - ASSERT(shape->type == GEOM_MESH); /* One instancing level is supported */ - volume += mesh_compute_volume(shape->data.mesh, flip); - } - return volume; -} - diff --git a/src/s3d_instance.h b/src/s3d_instance.h @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -39,8 +39,11 @@ struct instance { float transform[12]; /* local to world 3x4 column major matrix */ - char update_transform; - struct s3d_scene* scene; + struct s3d_scene* scene; /* Instantiated scene */ + /* Current view of the instantiated scene. Note that the instance does not + * own the scnview; the instance scnview lifetime is managed by the scnview + * into which the instance lies */ + struct s3d_scene_view* scnview; ref_T ref; }; @@ -57,10 +60,5 @@ extern LOCAL_SYM void instance_ref_put (struct instance* inst); -extern LOCAL_SYM float -instance_compute_volume - (struct instance* inst, - const char flip_surface); - #endif /* S3D_INSTANCE_H */ diff --git a/src/s3d_mesh.c b/src/s3d_mesh.c @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -56,17 +56,11 @@ mesh_setup_indices ntris_prev = (unsigned)mesh_get_ntris(mesh); ASSERT(get_indices != S3D_KEEP || ntris == ntris_prev); + (void)ntris_prev; if(get_indices == S3D_KEEP) return; - if(ntris == ntris_prev) { - mesh->update_mask |= (INDEX_BUFFER & !mesh->resize_mask); - } else { - mesh->resize_mask |= INDEX_BUFFER; - mesh->update_mask &= !INDEX_BUFFER; - } - if(mesh->indices) { /* Release the old index buffer */ index_buffer_ref_put(mesh->indices); mesh->indices = NULL; @@ -102,7 +96,7 @@ mesh_setup_positions void* data) { float* positions; - unsigned ivert, nverts_prev; + unsigned ivert; res_T res; ASSERT(mesh && nverts && attr && attr->usage == S3D_POSITION); @@ -112,14 +106,6 @@ mesh_setup_positions return; } - nverts_prev = (unsigned)mesh_get_nverts(mesh); - if(nverts == nverts_prev) { - mesh->update_mask |= (VERTEX_BUFFER & ~mesh->resize_mask); - } else { - mesh->resize_mask |= VERTEX_BUFFER; - mesh->update_mask &= !VERTEX_BUFFER; - } - if(mesh->attribs[S3D_POSITION]) { /* Release the old vertex buffer */ vertex_buffer_ref_put(mesh->attribs[S3D_POSITION]); mesh->attribs[S3D_POSITION] = NULL; @@ -304,8 +290,6 @@ mesh_clear(struct mesh* mesh) mesh->attribs[iattr] = NULL; } } - mesh->resize_mask = 0; - mesh->update_mask = 0; darray_float_clear(&mesh->cdf); } @@ -549,26 +533,9 @@ mesh_compute_aabb(struct mesh* mesh, float lower[3], float upper[3]) void mesh_copy_indexed_vertices(const struct mesh* src, struct mesh* dst) { - size_t ntris_src; - size_t ntris_dst; - size_t nverts_src; - size_t nverts_dst; int i; ASSERT(src && dst && src != dst); - ntris_src = mesh_get_ntris(src); - ntris_dst = mesh_get_ntris(dst); - nverts_src = mesh_get_nverts(src); - nverts_dst = mesh_get_nverts(dst); - - /* Setup the index buffer masks */ - if(ntris_src == ntris_dst) { - dst->update_mask = (INDEX_BUFFER & !dst->resize_mask); - } else { - dst->resize_mask |= INDEX_BUFFER; - dst->update_mask &= !INDEX_BUFFER; - } - /* Release the previous index buffer of dst */ if(dst->indices) { index_buffer_ref_put(dst->indices); @@ -580,14 +547,6 @@ mesh_copy_indexed_vertices(const struct mesh* src, struct mesh* dst) dst->indices = src->indices; } - /* Setup the vertex buffer masks */ - if(nverts_src == nverts_dst) { - dst->update_mask = (VERTEX_BUFFER & ~dst->resize_mask); - } else { - dst->resize_mask |= VERTEX_BUFFER; - dst->update_mask &= !VERTEX_BUFFER; - } - FOR_EACH(i, 0, S3D_ATTRIBS_COUNT__) { /* Release the previous vertex buffers of dst */ if(dst->attribs[i]) { diff --git a/src/s3d_mesh.h b/src/s3d_mesh.h @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -52,11 +52,6 @@ #define BUFFER_DARRAY darray_float #include "s3d_buffer.h" -enum buffer_type { - INDEX_BUFFER = BIT(0), - VERTEX_BUFFER = BIT(1) -}; - /* Filter function and its associated user defined data */ struct hit_filter { s3d_hit_filter_function_T func; @@ -70,8 +65,6 @@ struct mesh { /* Triangular mesh */ struct darray_float cdf; struct hit_filter filter; - int resize_mask; /* Combination of buffer_type */ - int update_mask; /* Combination of buffer_type */ struct s3d_device* dev; ref_T ref; }; diff --git a/src/s3d_primitive.c b/src/s3d_primitive.c @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -38,6 +38,19 @@ #include <rsys/float33.h> /******************************************************************************* + * Helper functions + ******************************************************************************/ +static int +check_primitive(const struct s3d_primitive* prim) +{ + return prim + && prim->geom_id != S3D_INVALID_ID + && prim->prim_id != S3D_INVALID_ID + && prim->mesh__ != NULL + && (prim->inst_id != S3D_INVALID_ID || prim->inst__ == NULL); +} + +/******************************************************************************* * Exported functions ******************************************************************************/ res_T @@ -54,15 +67,8 @@ s3d_primitive_get_attrib float w; res_T res = RES_OK; - if(!prim || usage == S3D_ATTRIBS_COUNT__ || !uv || !attrib) - return RES_BAD_ARG; - - if(prim->geom_id == S3D_INVALID_ID - || prim->prim_id == S3D_INVALID_ID - || prim->mesh__ == NULL - || (prim->inst_id != S3D_INVALID_ID && prim->inst__ == NULL)) { + if(!check_primitive(prim) || usage == S3D_ATTRIBS_COUNT__ || !uv || !attrib) return RES_BAD_ARG; - } /* Unormalized barycentric coordinates */ w = CLAMP(1.f - uv[0] - uv[1], 0.f, 1.f); @@ -163,8 +169,7 @@ s3d_primitive_has_attrib const enum s3d_attrib_usage attr, char* has_attrib) { - if(!prim - || !has_attrib + if(!check_primitive(prim) || !has_attrib || (attr != S3D_GEOMETRY_NORMAL && (unsigned)attr >= S3D_ATTRIBS_COUNT__)) return RES_BAD_ARG; @@ -187,7 +192,7 @@ s3d_primitive_sample { double sqrt_u; - if(!prim || S3D_PRIMITIVE_EQ(prim, &S3D_PRIMITIVE_NULL) || !st) + if(!check_primitive(prim) || !st) return RES_BAD_ARG; /* Expecting canonic numbers */ @@ -211,7 +216,7 @@ s3d_primitive_compute_area(const struct s3d_primitive* prim, float* area) float E0[3], E1[3], N[3]; struct geometry* geom; - if(!prim || !area || S3D_PRIMITIVE_EQ(prim, &S3D_PRIMITIVE_NULL)) + if(!check_primitive(prim) || !area) return RES_BAD_ARG; geom = (struct geometry*)prim->mesh__; @@ -230,7 +235,7 @@ res_T s3d_primitive_get_transform (const struct s3d_primitive* prim, float transform[12]) { - if(!prim || !transform) + if(!check_primitive(prim) || !transform) return RES_BAD_ARG; if(!prim->inst__) { @@ -249,3 +254,65 @@ s3d_primitive_get_transform return RES_OK; } +res_T +s3d_triangle_get_vertex_attrib + (const struct s3d_primitive* prim, + const size_t ivertex, + const enum s3d_attrib_usage usage, + struct s3d_attrib* attrib) +{ + struct geometry* geom_mesh = NULL; + const float* transform = NULL; + const uint32_t* ids; + + if(!check_primitive(prim) || ivertex > 2 + || (unsigned)usage >= S3D_ATTRIBS_COUNT__ + || !attrib) { + return RES_BAD_ARG; + } + + geom_mesh = (struct geometry*)prim->mesh__; + ASSERT(prim->geom_id == geom_mesh->name); + ASSERT(geom_mesh->type == GEOM_MESH); + + if(prim->inst__ != NULL) { + const struct geometry* geom_inst = (const struct geometry*)prim->inst__; + ASSERT(geom_inst->type == GEOM_INSTANCE); + ASSERT(prim->inst_id == geom_inst->name); + transform = geom_inst->data.instance->transform; + } + + /* The mesh haven't the required mesh attrib */ + if(!geom_mesh->data.mesh->attribs[usage]) { + return RES_BAD_ARG; + } + + /* Out of bound primitive index */ + if(prim->prim_id >= mesh_get_ntris(geom_mesh->data.mesh)) { + return RES_BAD_ARG; + } + ids = mesh_get_ids(geom_mesh->data.mesh) + prim->prim_id * 3/*#triangle ids*/; + attrib->usage = usage; + + if(usage != S3D_POSITION) { + const float* attr; + unsigned i, dim; + attrib->type = geom_mesh->data.mesh->attribs_type[usage]; + /* Fetch attrib data */ + dim = s3d_type_get_dimension(attrib->type); + attr = mesh_get_attr(geom_mesh->data.mesh, usage) + ids[ivertex] * dim; + FOR_EACH(i, 0, dim) attrib->value[i] = attr[i]; + } else { + const float* pos; + attrib->type = S3D_FLOAT3; + /* Fetch data */ + pos = mesh_get_pos(geom_mesh->data.mesh) + ids[ivertex] * 3; + f3_set(attrib->value, pos); + if(transform) { /* Transform the position from local to world space */ + f33_mulf3(attrib->value, transform, attrib->value); /* Rotation */ + f3_add(attrib->value, attrib->value, transform + 9); /* Translation */ + } + } + return RES_OK; +} + diff --git a/src/s3d_scene.c b/src/s3d_scene.c @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -33,707 +33,31 @@ #include "s3d.h" #include "s3d_device_c.h" #include "s3d_scene_c.h" +#include "s3d_scene_view_c.h" #include "s3d_shape_c.h" -#include <rsys/float3.h> -#include <rsys/float33.h> +#include <rsys/list.h> #include <rsys/mem_allocator.h> -#include <rsys/mutex.h> - -#include <algorithm> - -/* Flag used to define session enabled on instantiated scene */ -#define S3D_INSTANCE (BIT(sizeof(int)*8 - 1)) - -struct ray_extended : public RTCRay { - struct s3d_scene* scene; - void* data; /* User defined data */ -}; /******************************************************************************* * Helper functions ******************************************************************************/ -static INLINE void -hit_setup(struct s3d_scene* scn, const RTCRay* ray, struct s3d_hit* hit) -{ - float w; - char flip_surface = 0; - - ASSERT(scn && hit && ray); - - if((unsigned)ray->geomID == RTC_INVALID_GEOMETRY_ID) { /* No hit */ - *hit = S3D_HIT_NULL; - return; - } - - f3_set(hit->normal, ray->Ng); - hit->distance = ray->tfar; - - hit->uv[0] = ray->u; - hit->uv[1] = ray->v; - w = 1.f - hit->uv[0] - hit->uv[1]; - ASSERT(w <= 1.f); /* This may not occurs */ - if(w < 0.f) { /* Handle precision error */ - if(hit->uv[0] > hit->uv[1]) hit->uv[0] += w; - else hit->uv[1] += w; - w = 0.f; - } - - /* Embree stores on the u and v ray parameters the barycentric coordinates of - * the hit with respect to the second and third triangle vertices, - * respectively. The following code computes the barycentric coordinates of - * the hit for the first and second triangle vertices */ - hit->uv[1] = hit->uv[0]; - hit->uv[0] = w; - - if((unsigned)ray->instID == RTC_INVALID_GEOMETRY_ID) { - struct geometry* geom_mesh; - ASSERT((unsigned)ray->geomID < darray_geom_size_get(&scn->embree2geoms)); - geom_mesh = darray_geom_data_get(&scn->embree2geoms)[ray->geomID]; - hit->prim.mesh__ = geom_mesh; - hit->prim.inst__ = NULL; - hit->prim.prim_id = ray->primID; - hit->prim.geom_id = geom_mesh->name; - hit->prim.inst_id = S3D_INVALID_ID; - hit->prim.scene_prim_id = /* Compute the "scene space" primitive id */ - hit->prim.prim_id /* Mesh space */ - + geom_mesh->scene_prim_id_offset; /* Scene space */ - - } else { /* The hit shape is instantiated */ - /* Retrieve the hit instance */ - struct geometry* geom_inst; - struct geometry* geom_mesh; - ASSERT((unsigned)ray->instID < darray_geom_size_get(&scn->embree2geoms)); - geom_inst = darray_geom_data_get(&scn->embree2geoms)[ray->instID]; - geom_mesh = scene_get_mesh(geom_inst->data.instance->scene, ray->geomID); - hit->prim.mesh__ = geom_mesh; - hit->prim.inst__ = geom_inst; - hit->prim.prim_id = ray->primID; - hit->prim.geom_id = geom_mesh->name; - hit->prim.inst_id = geom_inst->name; - hit->prim.scene_prim_id = /* Compute the "scene space" */ - hit->prim.prim_id /* Mesh space */ - + geom_mesh->scene_prim_id_offset /* Inst space */ - + geom_inst->scene_prim_id_offset; /* Scene space */ - - flip_surface = geom_inst->flip_surface; - ASSERT(hit->prim.inst__); - ASSERT(((struct geometry*)hit->prim.inst__)->type == GEOM_INSTANCE); - } - ASSERT(hit->prim.mesh__); - ASSERT(((struct geometry*)hit->prim.mesh__)->type == GEOM_MESH); - - /* Flip geometric normal with respect to the flip surface flag */ - flip_surface ^= ((struct geometry*)hit->prim.mesh__)->flip_surface; - if(flip_surface) f3_minus(hit->normal, hit->normal); -} - -/* Wrapper between an Embree and a Star-3D filter function */ -static void -filter_wrapper(void* user_ptr, RTCRay& ray) -{ - struct s3d_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 res_T -scene_sync - (struct s3d_scene* scn, - const int mask);/* combination of s3d_session_flag & S3D_INSTANCE */ - -static INLINE void -scene_geometry_flush_enable_state - (struct s3d_scene* scn, - struct geometry* geom, /* Cached geometry */ - const struct s3d_shape* shape) /* New shape */ -{ - ASSERT(scn && geom && shape); - 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 INLINE void -scene_geometry_flush_filter_function - (struct s3d_scene* scn, - struct geometry* geom, /* Cached geometry */ - const struct s3d_shape* shape) -{ - ASSERT(scn && geom && shape && geom->irtc != RTC_INVALID_GEOMETRY_ID); - ASSERT(shape->type == GEOM_MESH); - - if(geom->data.mesh->filter.func == shape->data.mesh->filter.func - && geom->data.mesh->filter.data == shape->data.mesh->filter.data) - return; /* Up to date */ - - geom->data.mesh->filter = shape->data.mesh->filter; - if(!geom->data.mesh->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->data.mesh->filter); - } - scn->is_rtc_scn_outdated = 1; -} - -static res_T -scene_register_embree_geometry(struct s3d_scene* scn, struct geometry* geom) -{ - ASSERT(scn && geom && (geom->type==GEOM_MESH || geom->type==GEOM_INSTANCE)); - - /* Create the Embree geometry if it is not valid */ - if(geom->irtc == RTC_INVALID_GEOMETRY_ID) { - switch(geom->type) { - case GEOM_MESH: - geom->irtc = rtcNewTriangleMesh(scn->rtc_scn, RTC_GEOMETRY_DYNAMIC, - mesh_get_ntris(geom->data.mesh), mesh_get_nverts(geom->data.mesh)); - break; - case GEOM_INSTANCE: - geom->irtc = rtcNewInstance - (scn->rtc_scn, geom->data.instance->scene->rtc_scn); - break; - default: FATAL("Unreachable code\n"); break; - } - 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 INLINE void -scene_geometry_flush_positions(struct s3d_scene* scn, struct geometry* geom) -{ - ASSERT(scn && geom && geom->type == GEOM_MESH); - ASSERT(geom->irtc != RTC_INVALID_GEOMETRY_ID); - rtcSetBuffer(scn->rtc_scn, geom->irtc, RTC_VERTEX_BUFFER, - mesh_get_pos(geom->data.mesh), 0, sizeof(float[3])); - rtcUpdateBuffer(scn->rtc_scn, geom->irtc, RTC_VERTEX_BUFFER); - scn->is_rtc_scn_outdated = 1; -} - -static INLINE void -scene_geometry_flush_indices(struct s3d_scene* scn, struct geometry* geom) -{ - ASSERT(scn && geom && geom->type == GEOM_MESH); - ASSERT(geom->irtc != RTC_INVALID_GEOMETRY_ID); - rtcSetBuffer(scn->rtc_scn, geom->irtc, RTC_INDEX_BUFFER, - mesh_get_ids(geom->data.mesh), 0, sizeof(uint32_t[3])); - rtcUpdateBuffer(scn->rtc_scn, geom->irtc, RTC_INDEX_BUFFER); - scn->is_rtc_scn_outdated = 1; -} - -static void -scene_session_clear(struct s3d_scene* scn) -{ - struct geometry** geoms; - size_t ngeoms; - size_t i; - ASSERT(scn); - - ngeoms = darray_geom_size_get(&scn->embree2geoms); - geoms = darray_geom_data_get(&scn->embree2geoms); - FOR_EACH(i, 0, ngeoms) { - if(!geoms[i]) continue; - - if(geoms[i]->type == GEOM_INSTANCE) - scene_session_clear(geoms[i]->data.instance->scene); - - geometry_ref_put(geoms[i]); - } - darray_geom_clear(&scn->embree2geoms); - scn->session_mask = 0; -} - -static res_T -scene_register_mesh - (struct s3d_scene* scn, - struct s3d_shape* shape) -{ - struct geometry** pgeom = NULL; - struct geometry* geom = NULL; - size_t iattr; - char upd_pos, upd_ids; - - res_T res = RES_OK; - ASSERT(shape && shape->type == GEOM_MESH); - - /* Retrieve 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 = mesh_create(scn->dev, &geom->data.mesh); - if(res != RES_OK) goto error; - geom->type = GEOM_MESH; - res = htable_geom_set(&scn->cached_geoms, &shape, &geom); - if(res != RES_OK) goto error; - geom->name = shape->id.index; - } - - /* Discard the geometry that is not geometrically valid */ - if(!shape->data.mesh->indices || !shape->data.mesh->attribs[S3D_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; - } - mesh_clear(geom->data.mesh); - goto exit; - } - - /* Define which geometry buffers were updated */ - upd_ids = geom->data.mesh->indices != shape->data.mesh->indices - || ((shape->data.mesh->update_mask & INDEX_BUFFER) != 0); - upd_pos = geom->data.mesh->attribs[S3D_POSITION] != shape->data.mesh->attribs[S3D_POSITION] - || ((shape->data.mesh->update_mask & VERTEX_BUFFER) != 0); - - /* Get a reference onto the shape mesh indices */ - if(geom->data.mesh->indices != shape->data.mesh->indices) { - if(geom->data.mesh->indices) { - index_buffer_ref_put(geom->data.mesh->indices); - geom->data.mesh->indices = NULL; - } - ASSERT(shape->data.mesh->indices); - index_buffer_ref_get(shape->data.mesh->indices); - geom->data.mesh->indices = shape->data.mesh->indices; - } - - /* Get a reference onto the shape mesh attribs */ - FOR_EACH(iattr, 0, S3D_ATTRIBS_COUNT__) { - if(geom->data.mesh->attribs[iattr] == shape->data.mesh->attribs[iattr]) - continue; - - if(geom->data.mesh->attribs[iattr]) { - vertex_buffer_ref_put(geom->data.mesh->attribs[iattr]); - geom->data.mesh->attribs[iattr] = NULL; - } - if(!shape->data.mesh->attribs[iattr]) - continue; - - vertex_buffer_ref_get(shape->data.mesh->attribs[iattr]); - geom->data.mesh->attribs[iattr] = shape->data.mesh->attribs[iattr]; - geom->data.mesh->attribs_type[iattr] = shape->data.mesh->attribs_type[iattr]; - } - - /* The shape mesh was resize => the Embree geometry is no more valid */ - if(shape->data.mesh->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_embree_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_surface = shape->flip_surface; - - /* Flush the shape mesh states */ - shape->data.mesh->resize_mask = 0; - shape->data.mesh->update_mask = 0; - -exit: - return res; -error: - goto exit; -} - -static res_T -scene_register_instance - (struct s3d_scene* scn, - struct s3d_shape* shape, - const int session_mask) -{ - struct geometry** pgeom = NULL; - struct geometry* geom = NULL; - res_T res = RES_OK; - ASSERT(scn && shape && shape->type == GEOM_INSTANCE); - - /* Recursuvely update the scene */ - res = scene_sync(shape->data.instance->scene, - session_mask|S3D_INSTANCE); - if(res != RES_OK) goto error; - - pgeom = htable_geom_find(&scn->cached_geoms, &shape); - /* Create the scene instance of the geometry if necessary */ - if(pgeom) { - geom = *pgeom; - } else { - res = geometry_create(scn->dev, &geom); - if(res != RES_OK) goto error; - geom->type = GEOM_INSTANCE; - res = instance_create(shape->data.instance->scene, &geom->data.instance); - 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; - } - /* Update the cached instance states */ - ASSERT(geom->data.instance->scene == shape->data.instance->scene); - f33_set(geom->data.instance->transform, shape->data.instance->transform); - f3_set(geom->data.instance->transform + 9, shape->data.instance->transform + 9); - geom->flip_surface = shape->flip_surface; - - /* The instance cannot contain instances, i.e. one instancing level is - * supported */ - if(geom->data.instance->scene->instances_count != 0) { - res = RES_BAD_ARG; - goto error; - } - - /* Create the Embree instance */ - res = scene_register_embree_geometry(scn, geom); - if(res != RES_OK) goto error; - - /* Update the Embree instance transformation */ - if(shape->data.instance->update_transform) { - rtcSetTransform - (scn->rtc_scn, - geom->irtc, - RTC_MATRIX_COLUMN_MAJOR, - geom->data.instance->transform); - scn->is_rtc_scn_outdated = 1; - } - - scene_geometry_flush_enable_state(scn, geom, shape); - shape->data.instance->update_transform = 0; /* Flush instance state */ - -exit: - return res; -error: - goto exit; -} - -static res_T -scene_detach_shape - (struct s3d_scene* scn, struct s3d_shape* shape, const char* caller_name) -{ - struct geometry** pgeom; - ASSERT(scn && shape && !is_list_empty(&shape->scene_attachment)); - ASSERT(shape->type == GEOM_MESH || shape->type == GEOM_INSTANCE); - - pgeom = htable_geom_find(&scn->cached_geoms, &shape); - if(pgeom) { /* Remove the cached shape mesh */ - struct geometry* geom = *pgeom; - if(scn->session_mask != 0) { - log_error(scn->dev, - "%s: the shape is currently 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; - scn->is_rtc_scn_outdated = 1; - } - geometry_ref_put(geom); - htable_geom_erase(&scn->cached_geoms, &shape); - } - list_del(&shape->scene_attachment); - - if(shape->type == GEOM_INSTANCE) { - ASSERT(scn->instances_count != 0); - --scn->instances_count; - } - - S3D(shape_ref_put(shape)); - return RES_OK; -} - -static res_T -scene_compute_cdf(struct s3d_scene* scn) -{ - struct list_node* node; - struct s3d_shape* shape; - struct geometry** pgeom; - struct geometry* geom; - size_t len; - float area = 0.f; - res_T res = RES_OK; - ASSERT(scn); - - darray_fltui_clear(&scn->cdf); - - LIST_FOR_EACH(node, &scn->shapes) { - shape = CONTAINER_OF(node, struct s3d_shape, scene_attachment); - pgeom = htable_geom_find(&scn->cached_geoms, &shape); - ASSERT(pgeom != NULL); - geom = *pgeom; - struct fltui fltui; - - if(!geom->is_enabled) continue; - - switch(geom->type) { - case GEOM_MESH: - res = mesh_compute_cdf(geom->data.mesh); - if(res != RES_OK) goto error; - len = darray_float_size_get(&geom->data.mesh->cdf); - if(len) { - area += darray_float_cdata_get(&geom->data.mesh->cdf)[len - 1]; - } - break; - case GEOM_INSTANCE: - res = scene_compute_cdf(geom->data.instance->scene); - if(res != RES_OK) goto error; - len = darray_fltui_size_get(&geom->data.instance->scene->cdf); - if(len) { - area += darray_fltui_cdata_get - (&geom->data.instance->scene->cdf)[len - 1].flt; - } - break; - default: FATAL("Unreachable code\n"); break; - } - fltui.ui = geom->irtc; - fltui.flt = area; - if(len) { - res = darray_fltui_push_back(&scn->cdf, &fltui); - if(res != RES_OK) goto error; - } - } -exit: - return res; -error: - darray_fltui_clear(&scn->cdf); - goto exit; -} - -static void -scene_compute_aabb(struct s3d_scene* scn) -{ - struct list_node* node; - struct s3d_shape* shape; - struct geometry** pgeom; - struct geometry* geom; - struct instance* inst; - float lower[3], upper[3]; - - f3_splat(scn->lower, FLT_MAX); - f3_splat(scn->upper,-FLT_MAX); - - LIST_FOR_EACH(node, &scn->shapes) { - shape = CONTAINER_OF(node, struct s3d_shape, scene_attachment); - pgeom = htable_geom_find(&scn->cached_geoms, &shape); - ASSERT(pgeom != NULL); - geom = *pgeom; - - if(!geom->is_enabled) continue; - - switch(geom->type) { - case GEOM_MESH: mesh_compute_aabb(geom->data.mesh, lower, upper); break; - case GEOM_INSTANCE: - inst = geom->data.instance; - scene_compute_aabb(inst->scene); - /* Transform local scene AABB in world space */ - f33_mulf3(lower, inst->transform, inst->scene->lower); - f33_mulf3(upper, inst->transform, inst->scene->upper); - f3_add(lower, inst->transform + 9, lower); - f3_add(upper, inst->transform + 9, upper); - break; - default: FATAL("Unreachable code\n"); break; - } - f3_min(scn->lower, scn->lower, lower); - f3_max(scn->upper, scn->upper, upper); - } -} - -static FINLINE bool -operator < (const struct fltui& it, const float val) -{ - /* This operator is used by the std::lower_bound algorithm that returns an - * iterator to the first element that is not less than val while one expect - * an iterator on the first element that is not less *or equal* than val. - * That's why we use <= rather than < */ - return it.flt <= val; -} - -static res_T -scene_compute_nprims_cdf - (struct s3d_scene* scn, - const char store_cdf) -{ - struct list_node* node; - struct s3d_shape* shape; - struct geometry** pgeom; - struct geometry* geom; - size_t len; - unsigned nprims; - res_T res = RES_OK; - ASSERT(scn); - - darray_nprims_cdf_clear(&scn->nprims_cdf); - - nprims = 0; - LIST_FOR_EACH(node, &scn->shapes) { - shape = CONTAINER_OF(node, struct s3d_shape, scene_attachment); - pgeom = htable_geom_find(&scn->cached_geoms, &shape); - ASSERT(pgeom != NULL); - geom = *pgeom; - struct nprims_cdf cdf; - - if(!geom->is_enabled) continue; - - geom->scene_prim_id_offset = nprims; - switch(geom->type) { - case GEOM_MESH: - len = mesh_get_ntris(geom->data.mesh); - nprims += (unsigned)len; - break; - case GEOM_INSTANCE: - res = scene_compute_nprims_cdf(geom->data.instance->scene, store_cdf); - if(res != RES_OK) goto error; - len = darray_nprims_cdf_size_get(&geom->data.instance->scene->nprims_cdf); - if(len) { - nprims += darray_nprims_cdf_cdata_get - (&geom->data.instance->scene->nprims_cdf)[len - 1].nprims; - } - break; - default: FATAL("Unreachable code\n"); break; - } - - cdf.nprims = nprims; - cdf.irtc = geom->irtc; - if(store_cdf && len) { - res = darray_nprims_cdf_push_back(&scn->nprims_cdf, &cdf); - if(res != RES_OK) goto error; - } - } -exit: - return res; -error: - darray_nprims_cdf_clear(&scn->nprims_cdf); - goto exit; -} - -static FINLINE bool -operator < (const struct nprims_cdf& it, const size_t iprim) -{ - /* This operator is used by the std::lower_bound algorithm that returns an - * iterator to the first element that is not less than iprim while one expect - * an iterator on the first element that is not less *or equal* than iprim. - * That's why we use <= rather than < */ - return it.nprims <= iprim; -} - -static res_T -scene_sync(struct s3d_scene* scn, const int session_mask) -{ - struct list_node* node; - struct s3d_shape* shape; - res_T res = RES_OK; - ASSERT(scn); - - if((session_mask & S3D_INSTANCE) != 0 - && (scn->session_mask & S3D_INSTANCE) != 0) { - /* The scene was already synced as an instance. Discard sync process */ - return RES_OK; - } else if(scn->session_mask != 0) { - /* The scene cannot be synced several times exepted if it is instantiated */ - res = RES_BAD_OP; - goto error; - } - - if((session_mask & S3D_INSTANCE) == 0 && scn->session_mask != 0) { - res = RES_BAD_OP; - goto error; - } - - LIST_FOR_EACH(node, &scn->shapes) { - shape = CONTAINER_OF(node, struct s3d_shape, scene_attachment); - switch(shape->type) { - case GEOM_INSTANCE: - /* One instancing level is supported */ - if((session_mask & S3D_INSTANCE) != 0) { - res = RES_BAD_ARG; - goto error; - } - res = scene_register_instance(scn, shape, session_mask); - break; - case GEOM_MESH: - res = scene_register_mesh(scn, shape); - break; - default: FATAL("Unreachable code\n"); break; - } - if(res != RES_OK) - goto error; - } - if((session_mask & S3D_SAMPLE) != 0) { - res = scene_compute_cdf(scn); - if(res != RES_OK) goto error; - } - if((session_mask & S3D_GET_PRIMITIVE) != 0) { - res = scene_compute_nprims_cdf(scn, 1); - } else { - res = scene_compute_nprims_cdf(scn, 0); - } - if(res != RES_OK) goto error; - - if((session_mask & S3D_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 scene_release(ref_T* ref) { struct s3d_scene* scn; struct s3d_device* dev; + struct list_node* node; + struct list_node* tmp; + ASSERT(ref); scn = CONTAINER_OF(ref, struct s3d_scene, ref); - S3D(scene_clear(scn)); dev = scn->dev; - scene_session_clear(scn); - if(scn->rtc_scn) rtcDeleteScene(scn->rtc_scn); - htable_geom_release(&scn->cached_geoms); - darray_geom_release(&scn->embree2geoms); - darray_fltui_release(&scn->cdf); - darray_nprims_cdf_release(&scn->nprims_cdf); + LIST_FOR_EACH_SAFE(node, tmp, &scn->scnviews) { + scene_view_destroy(CONTAINER_OF(node, struct s3d_scene_view, node)); + } + S3D(scene_clear(scn)); + htable_shape_release(&scn->shapes); MEM_RM(dev->allocator, scn); S3D(device_ref_put(dev)); } @@ -757,24 +81,12 @@ s3d_scene_create(struct s3d_device* dev, struct s3d_scene** out_scn) res = RES_MEM_ERR; goto error; } - list_init(&scn->shapes); - htable_geom_init(dev->allocator, &scn->cached_geoms); - darray_geom_init(dev->allocator, &scn->embree2geoms); - darray_fltui_init(dev->allocator, &scn->cdf); - darray_nprims_cdf_init(dev->allocator, &scn->nprims_cdf); + htable_shape_init(dev->allocator, &scn->shapes); + SIG_INIT(&scn->sig_shape_detach); + list_init(&scn->scnviews); ref_init(&scn->ref); S3D(device_ref_get(dev)); scn->dev = dev; - scn->session_mask = 0; - scn->rtc_scn = rtcDeviceNewScene - (dev->rtc, RTC_SCENE_DYNAMIC | RTC_SCENE_INCOHERENT | RTC_SCENE_ROBUST, - RTC_INTERSECT1 | RTC_INTERSECT4); - if(!scn->rtc_scn) { - res = RES_MEM_ERR; - goto error; - } - f3_splat(scn->lower, FLT_MAX); - f3_splat(scn->upper,-FLT_MAX); exit: if(out_scn) *out_scn = scn; @@ -838,587 +150,77 @@ error: res_T s3d_scene_attach_shape(struct s3d_scene* scn, struct s3d_shape* shape) { + unsigned shape_id; + res_T res = RES_OK; + if(!scn || !shape) return RES_BAD_ARG; - if(!is_list_empty(&shape->scene_attachment)) { - log_error(scn->dev, - "%s: the shape is already attached to a scene.\n", FUNC_NAME); - return RES_BAD_ARG; - } if(shape->type == GEOM_INSTANCE && shape->data.instance->scene == scn) { log_error(scn->dev, "%s: the instantiated scene cannot be attached to itself.\n", FUNC_NAME); return RES_BAD_ARG; } - list_add_tail(&scn->shapes, &shape->scene_attachment); - S3D(shape_ref_get(shape)); - scn->instances_count += shape->type == GEOM_INSTANCE; - return RES_OK; -} - -res_T -s3d_scene_detach_shape(struct s3d_scene* scn, struct s3d_shape* shape) -{ - res_T res = RES_OK; - char is_attached; - - if(!scn || !shape) return RES_BAD_ARG; - if(!(S3D(shape_is_attached(shape, &is_attached)), is_attached)) { - log_error(scn->dev, - "%s: the shape is not attached to a scene.\n", FUNC_NAME); - return RES_BAD_ARG; - } -#ifndef NDEBUG - { /* Check that the shape is attached to `scn' */ - struct list_node* node; - char is_found = 0; - LIST_FOR_EACH(node, &scn->shapes) { - if(node == &shape->scene_attachment) { - is_found = 1; - break; - } - } - ASSERT(is_found); + S3D(shape_get_id(shape, &shape_id)); + if(htable_shape_find(&scn->shapes, &shape_id) != NULL) { + log_warning(scn->dev, + "%s: the shape is already attached to the scene.\n", FUNC_NAME); + return RES_OK; } -#endif - res = scene_detach_shape(scn, shape, FUNC_NAME); - if(res != RES_OK) return res; - return RES_OK; -} -res_T -s3d_scene_clear(struct s3d_scene* scn) -{ - struct list_node* node, *tmp; - if(!scn) return RES_BAD_ARG; - if(scn->session_mask != 0) { + res = htable_shape_set(&scn->shapes, &shape_id, &shape); + if(res != RES_OK) { log_error(scn->dev, - "%s: cannot clear a scene with an active session.\n", FUNC_NAME); - return RES_BAD_OP; - } - LIST_FOR_EACH_SAFE(node, tmp, &scn->shapes) { - struct s3d_shape* shape = CONTAINER_OF - (node, struct s3d_shape, scene_attachment); - const res_T res = scene_detach_shape(scn, shape, FUNC_NAME); - ASSERT(res == RES_OK); (void)res; + "%s: cannot attach the shape to the scene.\n", FUNC_NAME); + return RES_OK; } + S3D(shape_ref_get(shape)); + scn->instances_count += shape->type == GEOM_INSTANCE; return RES_OK; } res_T -s3d_scene_begin_session(struct s3d_scene* scn, const int session_mask) -{ - if(!scn) - return RES_BAD_ARG; - if(!(session_mask&S3D_TRACE) - && !(session_mask&S3D_SAMPLE) - && !(session_mask&S3D_GET_PRIMITIVE)) { - log_error(scn->dev, "%s: no valid session is defined.\n", FUNC_NAME); - return RES_BAD_ARG; - } - return scene_sync(scn, session_mask); -} - -res_T -s3d_scene_end_session(struct s3d_scene* scn) +s3d_scene_detach_shape(struct s3d_scene* scn, struct s3d_shape* shape) { - if(!scn) - return RES_BAD_ARG; - if(scn->session_mask & S3D_INSTANCE) { - log_error(scn->dev, - "%s: the scene session was enabled through scene instantiation.\n", - FUNC_NAME); - return RES_BAD_OP; - } - if(!scn->session_mask) { - log_error(scn->dev, "%s: the scene has no active session.\n", FUNC_NAME); - return RES_BAD_OP; - } - scene_session_clear(scn); - return RES_OK; -} + size_t n; + unsigned shape_id; -res_T -s3d_scene_get_session_mask(struct s3d_scene* scn, int* session_mask) -{ - if(!scn || !session_mask) - return RES_BAD_ARG; - *session_mask = scn->session_mask & (~S3D_INSTANCE); - return RES_OK; -} + if(!scn || !shape) return RES_BAD_ARG; -res_T -s3d_scene_trace_ray - (struct s3d_scene* scn, - const float org[3], - const float dir[3], - const float range[2], - void* ray_data, - struct s3d_hit* hit) -{ - struct ray_extended ray_ex; - if(!scn || !org || !dir || !range || !hit) - return RES_BAD_ARG; - if(!f3_is_normalized(dir)) { + S3D(shape_get_id(shape, &shape_id)); + if(htable_shape_find(&scn->shapes, &shape_id) == NULL) { log_error(scn->dev, - "%s: unnormalized ray direction {%g, %g, %g}.\n", - FUNC_NAME, SPLIT3(dir)); + "%s: the shape is not attached to the scene.\n", FUNC_NAME); return RES_BAD_ARG; } - if((scn->session_mask & S3D_TRACE) == 0) { - log_error(scn->dev, - "%s: no active S3D_TRACE session on the submitted scene.\n", FUNC_NAME); - return RES_BAD_OP; - } - if(range[0] > range[1]) { /* Degenerated range <=> disabled ray */ - *hit = S3D_HIT_NULL; - return RES_OK; - } - f3_set(ray_ex.org, org); - f3_set(ray_ex.dir, dir); - ray_ex.tnear = range[0]; - ray_ex.tfar = range[1]; - ray_ex.geomID = RTC_INVALID_GEOMETRY_ID; - ray_ex.primID = RTC_INVALID_GEOMETRY_ID; - ray_ex.instID = RTC_INVALID_GEOMETRY_ID; - ray_ex.mask = 0xFFFFFFFF; - ray_ex.time = 0.f; - ray_ex.scene = scn; - ray_ex.data = ray_data; + n = htable_shape_erase(&scn->shapes, &shape_id); + ASSERT(n == 1); (void)n; - rtcIntersect(scn->rtc_scn, ray_ex); - - hit_setup(scn, &ray_ex, hit); + SIG_BROADCAST(&scn->sig_shape_detach, scene_shape_cb_T, ARG2(scn, shape)); + S3D(shape_ref_put(shape)); return RES_OK; } res_T -s3d_scene_trace_rays - (struct s3d_scene* scn, - const size_t nrays, - const int mask, - const float* origins, - const float* directions, - const float* ranges, - void* rays_data, - const size_t sizeof_ray_data, - struct s3d_hit* hits) +s3d_scene_clear(struct s3d_scene* scn) { - size_t iray; - size_t iorg, idir, irange, idata; - size_t org_step, dir_step, range_step, data_step; - res_T res = RES_OK; + struct htable_shape_iterator it, end; if(!scn) return RES_BAD_ARG; - if(!nrays) return RES_OK; - - org_step = mask & S3D_RAYS_SINGLE_ORIGIN ? 0 : 3; - dir_step = mask & S3D_RAYS_SINGLE_DIRECTION ? 0 : 3; - range_step = mask & S3D_RAYS_SINGLE_RANGE ? 0 : 2; - data_step = (mask & S3D_RAYS_SINGLE_DATA) || !rays_data ? 0 : sizeof_ray_data; - iorg = idir = irange = idata = 0; - - FOR_EACH(iray, 0, nrays) { - res = s3d_scene_trace_ray(scn, origins+iorg, directions+idir, - ranges+irange, (char*)rays_data+idata, hits+iray); - if(UNLIKELY(res != RES_OK)) break; - iorg += org_step; - idir += dir_step; - irange += range_step; - idata += data_step; - } - return res; -} -res_T -s3d_scene_sample - (struct s3d_scene* scn, - const float u, - const float v, - const float w, - struct s3d_primitive* primitive, /* sampled primitive */ - float st[2]) -{ - struct geometry* geom; - const struct fltui* fltui_begin, *fltui_end, *fltui_found; - const float* flt_begin, *flt_end, *flt_found; - size_t igeom; - float f; - res_T res = RES_OK; - - if(!scn || !primitive || !st) { - res = RES_BAD_ARG; - goto error; - } - /* Expecting canonic numbers */ - if(u < 0.f || u >= 1.f || v < 0.f || v >= 1.f || w < 0.f || w >= 1.f) { - log_error(scn->dev, - "%s: the submitted numbers are not canonical, i.e. they are not in [0, 1[.\n", - FUNC_NAME); - res = RES_BAD_ARG; - goto error; - } - if((scn->session_mask & S3D_SAMPLE) == 0) { - log_error(scn->dev, - "%s: no active S3D_SAMPLE session on the submitted scene.\n", - FUNC_NAME); - res = RES_BAD_OP; - goto error; - } - - /* Find the sampled geometry */ - if(darray_fltui_size_get(&scn->cdf) == 0) { - /* No geometry to sample */ - *primitive = S3D_PRIMITIVE_NULL; - goto exit; - } else if(darray_fltui_size_get(&scn->cdf) == 1) { - igeom = darray_fltui_cdata_get(&scn->cdf)[0].ui; - f = u * darray_fltui_cdata_get(&scn->cdf)[0].flt;/* Map u to the CDF bounds */ - } else { - fltui_begin = darray_fltui_cdata_get(&scn->cdf); - fltui_end = fltui_begin + darray_fltui_size_get(&scn->cdf); - f = u * fltui_end[-1].flt; /* Map u to the CDF bounds */ - fltui_found = std::lower_bound(fltui_begin, fltui_end, f); - ASSERT(fltui_found != fltui_end); - igeom = fltui_found->ui; - - /* Transform u to the geometry CDF bounds */ - if(fltui_found != fltui_begin) - f -= fltui_found[-1].flt; - } - geom = darray_geom_data_get(&scn->embree2geoms)[igeom]; - ASSERT(geom); - - if(geom->type == GEOM_MESH) { - primitive->inst__ = NULL; - primitive->inst_id = S3D_INVALID_ID; - primitive->scene_prim_id = 0; - } else { - /* Find the sampled instantiated geometry */ - ASSERT(geom->type == GEOM_INSTANCE); - primitive->inst__ = geom; - primitive->inst_id = geom->name; - primitive->scene_prim_id = geom->scene_prim_id_offset; - if(darray_fltui_size_get(&geom->data.instance->scene->cdf) == 1) { - igeom = darray_fltui_cdata_get(&geom->data.instance->scene->cdf)[0].ui; - } else { - fltui_begin = darray_fltui_cdata_get(&geom->data.instance->scene->cdf); - fltui_end = fltui_begin + darray_fltui_size_get(&geom->data.instance->scene->cdf); - fltui_found = std::lower_bound(fltui_begin, fltui_end, f); - ASSERT(fltui_found != fltui_end); - igeom = fltui_found->ui; - - /* Transform u to the geometry CDF bounds */ - if(fltui_found != fltui_begin) - f -= fltui_found[-1].flt; - } - geom = darray_geom_data_get - (&geom->data.instance->scene->embree2geoms)[igeom]; - ASSERT(geom); - } - ASSERT(geom->type == GEOM_MESH); - - /* Find the sampled triangle */ - primitive->mesh__ = geom; - primitive->geom_id = geom->name; - primitive->scene_prim_id += geom->scene_prim_id_offset; - flt_begin = darray_float_cdata_get(&geom->data.mesh->cdf); - flt_end = flt_begin + darray_float_size_get(&geom->data.mesh->cdf); - flt_found = std::lower_bound(flt_begin, flt_end, f); - ASSERT(flt_found != flt_end); - - primitive->prim_id = (unsigned)(flt_found - flt_begin); - primitive->scene_prim_id += primitive->prim_id; - S3D(primitive_sample(primitive, v, w, st)); - -exit: - return res; -error: - goto exit; -} - -res_T -s3d_scene_get_primitive - (struct s3d_scene* scn, const unsigned iprim, struct s3d_primitive* prim) -{ - struct geometry* geom; - const struct nprims_cdf* begin, *end, *found; - size_t nprims; - size_t igeom; - size_t i; - res_T res = RES_OK; - - if(!scn || !prim) { - res = RES_BAD_ARG; - goto error; - } - if((scn->session_mask & S3D_GET_PRIMITIVE) == 0) { - log_error(scn->dev, - "%s: no active S3D_GET_PRIMITIVE session on the submitted scene.\n", - FUNC_NAME); - res = RES_BAD_OP; - goto error; - } - S3D(scene_primitives_count(scn, &nprims)); - if(iprim >= nprims) { - log_error(scn->dev, - "%s: the primitive index %u exceeds the number of scene primitives %u.\n", - FUNC_NAME, iprim, (unsigned)nprims); - res = RES_BAD_ARG; - goto error; - } - - i = iprim; - if(darray_nprims_cdf_size_get(&scn->nprims_cdf) == 1) { - igeom = darray_nprims_cdf_cdata_get(&scn->nprims_cdf)[0].irtc; - } else { - begin = darray_nprims_cdf_cdata_get(&scn->nprims_cdf); - end = begin + darray_nprims_cdf_size_get(&scn->nprims_cdf); - found = std::lower_bound(begin, end, i); - ASSERT(found != end); - igeom = found->irtc; - if(found != begin) { - ASSERT(i >= found[-1].nprims); - i -= found[-1].nprims; - } - } - geom = darray_geom_data_get(&scn->embree2geoms)[igeom]; - ASSERT(geom); - - if(geom->type == GEOM_MESH) { - prim->inst__ = NULL; - prim->inst_id = S3D_INVALID_ID; - prim->scene_prim_id = 0; - } else { - ASSERT(geom->type == GEOM_INSTANCE); - prim->inst__ = geom; - prim->inst_id = geom->name; - prim->scene_prim_id = geom->scene_prim_id_offset; - if(darray_nprims_cdf_size_get(&geom->data.instance->scene->nprims_cdf)==1) { - igeom = darray_nprims_cdf_cdata_get - (&geom->data.instance->scene->nprims_cdf)[0].irtc; - } else { - begin = darray_nprims_cdf_cdata_get - (&geom->data.instance->scene->nprims_cdf); - end = begin + darray_nprims_cdf_size_get - (&geom->data.instance->scene->nprims_cdf); - found = std::lower_bound(begin, end, i); - ASSERT(found != end); - igeom = found->irtc; - if(found != begin) { - ASSERT(i >= found[-1].nprims); - i -= found[-1].nprims; - } - } - geom = darray_geom_data_get - (&geom->data.instance->scene->embree2geoms)[igeom]; - ASSERT(geom); - } - ASSERT(geom->type == GEOM_MESH); - ASSERT(i < mesh_get_ntris(geom->data.mesh)); - prim->mesh__ = geom; - prim->geom_id = geom->name; - prim->prim_id = (unsigned)i; - prim->scene_prim_id += geom->scene_prim_id_offset; - prim->scene_prim_id += prim->prim_id; - -exit: - return res; -error: - goto exit; -} - -res_T -s3d_scene_primitives_count(struct s3d_scene* scn, size_t* prims_count) -{ - res_T res = RES_OK; - - if(!scn || !prims_count) { - res = RES_BAD_ARG; - goto error; - } - if(!scn->session_mask) { - log_error(scn->dev, - "%s: no active session on the submitted scene.\n", FUNC_NAME); - res = RES_BAD_OP; - goto error; - } - if((scn->session_mask & S3D_GET_PRIMITIVE) != 0) { - const size_t len = darray_nprims_cdf_size_get(&scn->nprims_cdf); - if(!len) { - *prims_count = 0; - } else { - *prims_count = darray_nprims_cdf_cdata_get(&scn->nprims_cdf)[len - 1].nprims; - } - } else { - struct list_node* node; - struct s3d_shape* shape; - struct geometry** pgeom; - struct geometry* geom; - size_t inst_count; - *prims_count = 0; - LIST_FOR_EACH(node, &scn->shapes) { - shape = CONTAINER_OF(node, struct s3d_shape, scene_attachment); - pgeom = htable_geom_find(&scn->cached_geoms, &shape); - ASSERT(pgeom != NULL); - geom = *pgeom; - - if(!geom->is_enabled) continue; - - switch(geom->type) { - case GEOM_MESH: - *prims_count += mesh_get_ntris(geom->data.mesh); - break; - case GEOM_INSTANCE: - S3D(scene_primitives_count(geom->data.instance->scene, &inst_count)); - *prims_count += inst_count; - break; - default: FATAL("Unreachable code\n"); break; - } - } - } -exit: - return res; -error: - goto exit; -} - -res_T -s3d_scene_compute_area(struct s3d_scene* scn, float* out_area) -{ - float area; - res_T res = RES_OK; - - if(!scn || !out_area) { - res = RES_BAD_ARG; - goto error; - } - if(!scn->session_mask) { - log_error(scn->dev, - "%s: no active session on the submitted scene.\n", FUNC_NAME); - res = RES_BAD_OP; - goto error; - } - - if((scn->session_mask & S3D_SAMPLE) != 0) { - /* Retrieve the overall scene area from the scene cumulative distribution - * function. Note that the CDF stores the cumulative triangle area - * multiplied by 2; the real scene area is thus the CDF upper bound / 2 */ - size_t len = darray_fltui_size_get(&scn->cdf); - if(!len) { - area = 0.f; - } else { - area = darray_fltui_cdata_get(&scn->cdf)[len - 1].flt * 0.5f; - } - } else { - struct list_node* node; - struct s3d_shape* shape; - struct geometry** pgeom; - struct geometry* geom; - float inst_area; - - area = 0.f; - LIST_FOR_EACH(node, &scn->shapes) { - shape = CONTAINER_OF(node, struct s3d_shape, scene_attachment); - pgeom = htable_geom_find(&scn->cached_geoms, &shape); - ASSERT(pgeom != NULL); - geom = *pgeom; - - if(!geom->is_enabled) continue; - - switch(geom->type) { - case GEOM_MESH: - area += mesh_compute_area(geom->data.mesh); - break; - case GEOM_INSTANCE: - /* TODO take into account the instance scale factor */ - S3D(scene_compute_area(geom->data.instance->scene, &inst_area)); - area += inst_area; - break; - default: FATAL("Unreachable code\n"); break; - } - } - } - -exit: - if(out_area) *out_area = area; - return res; -error: - area = -1.f; - goto exit; -} - -res_T -s3d_scene_compute_volume(struct s3d_scene* scn, float* out_volume) -{ - struct list_node* node; - struct s3d_shape* shape; - struct geometry** pgeom; - struct geometry* geom; - float volume; - res_T res = RES_OK; - - if(!scn || !out_volume) { - res = RES_BAD_ARG; - goto error; - } - if(!scn->session_mask) { - log_error(scn->dev, - "%s: no active session on the submitted scene.\n", FUNC_NAME); - res = RES_BAD_OP; - goto error; - } - - volume = 0.f; - LIST_FOR_EACH(node, &scn->shapes) { - shape = CONTAINER_OF(node, struct s3d_shape, scene_attachment); - pgeom = htable_geom_find(&scn->cached_geoms, &shape); - ASSERT(pgeom != NULL); - geom = *pgeom; - - if(!geom->is_enabled) continue; - - switch(geom->type) { - case GEOM_MESH: - volume += mesh_compute_volume(geom->data.mesh, geom->flip_surface); - break; - case GEOM_INSTANCE: - volume += instance_compute_volume(geom->data.instance, geom->flip_surface); - break; - default: FATAL("Unreachable code\n"); break; - } - } - - if(volume < 0.f) { - log_warning(scn->dev, -"%s:\n" -"\tthe volume is negative. The scene shapes might not represent closed 2D\n" -"\tmanifold volumes, or their surface normal might not point inward the volume.\n", - FUNC_NAME); + htable_shape_begin(&scn->shapes, &it); + htable_shape_end(&scn->shapes, &end); + while(!htable_shape_iterator_eq(&it, &end)) { + struct s3d_shape** pshape = htable_shape_iterator_data_get(&it); + struct s3d_shape* shape = *pshape; + SIG_BROADCAST(&scn->sig_shape_detach, scene_shape_cb_T, ARG2(scn, shape)); + S3D(shape_ref_put(shape)); + htable_shape_iterator_next(&it); } + htable_shape_clear(&scn->shapes); -exit: - if(out_volume) *out_volume = volume; - return res; -error: - volume = -1.f; - goto exit; -} - -res_T -s3d_scene_get_aabb(struct s3d_scene* scn, float lower[3], float upper[3]) -{ - if(!scn || !lower || !upper) return RES_BAD_ARG; - if(!scn->session_mask) { - log_error(scn->dev, - "%s: no active session on the submitted scene.\n", FUNC_NAME); - return RES_BAD_OP; - } - f3_set(lower, scn->lower); - f3_set(upper, scn->upper); return RES_OK; } @@ -1430,3 +232,10 @@ s3d_scene_get_device(struct s3d_scene* scn, struct s3d_device** dev) return RES_OK; } +res_T +s3d_scene_get_shapes_count(struct s3d_scene* scn, size_t* nshapes) +{ + if(!scn || !nshapes) return RES_BAD_ARG; + *nshapes = htable_shape_size_get(&scn->shapes); + return RES_OK; +} diff --git a/src/s3d_scene_c.h b/src/s3d_scene_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -33,77 +33,32 @@ #ifndef S3D_SCENE_C_H #define S3D_SCENE_C_H -#include "s3d_backend.h" - -#include <rsys/dynamic_array.h> #include <rsys/hash_table.h> #include <rsys/list.h> #include <rsys/ref_count.h> +#include <rsys/signal.h> -/* - * The geometry pointers must be initialized to NULL in order to define - * which pointers are valid or not - */ -static FINLINE void -geom_ptr_init__(struct mem_allocator* alloc, struct geometry** geom) -{ - (void)alloc; *geom = NULL; -} - -/* Generate the darray_geom dynamic array */ -#define DARRAY_NAME geom -#define DARRAY_DATA struct geometry* -#define DARRAY_FUNCTOR_INIT geom_ptr_init__ -#include <rsys/dynamic_array.h> - -/* Generate the htable_geom hash table */ -#define HTABLE_NAME geom -#define HTABLE_DATA struct geometry* -#define HTABLE_KEY struct s3d_shape* +/* Generate the htable_shape hash table */ +#define HTABLE_NAME shape +#define HTABLE_DATA struct s3d_shape* +#define HTABLE_KEY unsigned /* Id of the shape */ #include <rsys/hash_table.h> -/* Generate the darray_fltui dynamic array */ -struct fltui { float flt; unsigned ui; }; -#define DARRAY_NAME fltui -#define DARRAY_DATA struct fltui -#include <rsys/dynamic_array.h> - -/* Generate the darray_geom_nprims array */ -struct nprims_cdf { unsigned nprims, irtc; }; -#define DARRAY_NAME nprims_cdf -#define DARRAY_DATA struct nprims_cdf -#include <rsys/dynamic_array.h> +/* Declare the scene_shape_cb_T callback data type */ +CLBK(scene_shape_cb_T, ARG2 + (const struct s3d_scene* scn, + const struct s3d_shape* shape)); struct s3d_scene { - struct list_node shapes; /* List of attached shapes */ - struct htable_geom cached_geoms; /* Cached shape geometries */ - struct darray_geom embree2geoms; /* Shape geometries index by embree id */ - struct darray_fltui cdf; /* Unormalized CDF */ - struct darray_nprims_cdf nprims_cdf; - - float lower[3], upper[3]; /* AABB of the scene */ - + struct htable_shape shapes; /* List of attached shapes */ size_t instances_count; /* # instances in the scene */ + struct list_node scnviews; /* Pool of available s3d_scene_view */ - RTCScene rtc_scn; /* Embree scene */ - char is_rtc_scn_outdated; /* Must the embree scene rebuild */ - - int session_mask; /* Combination of enum s3d_session_flag */ + signal_T sig_shape_detach; struct s3d_device* dev; ref_T ref; }; -static FINLINE struct geometry* -scene_get_mesh(struct s3d_scene* scn, const unsigned igeom) -{ - struct geometry* geom; - ASSERT(scn && igeom != RTC_INVALID_GEOMETRY_ID); - ASSERT(igeom < darray_geom_size_get(&scn->embree2geoms)); - geom = darray_geom_data_get(&scn->embree2geoms)[igeom]; - ASSERT(geom); - return geom; -} - #endif /* S3D_SCENE_C_H */ diff --git a/src/s3d_scene_view.c b/src/s3d_scene_view.c @@ -0,0 +1,1473 @@ +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to describe a + * virtual 3D environment that can be ray-traced and sampled both robustly + * and efficiently. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "s3d.h" +#include "s3d_device_c.h" +#include "s3d_scene_c.h" +#include "s3d_scene_view_c.h" +#include "s3d_shape_c.h" + +#include <rsys/float3.h> +#include <rsys/float33.h> +#include <rsys/mem_allocator.h> + +#include <algorithm> + +struct ray_extended : public RTCRay { + struct s3d_scene_view* scnview; + float ws_org[3]; /* World space ray origin */ + float ws_dir[3]; /* World space ray direction */ + void* data; /* User defined data */ +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static FINLINE int +aabb_is_degenerated(const float lower[3], const float upper[3]) +{ + ASSERT(lower && upper); + return lower[0] > upper[0] || lower[1] > upper[1] || lower[2] > upper[2]; +} + +static FINLINE bool +operator < (const struct fltui& it, const float val) +{ + /* This operator is used by the std::lower_bound algorithm that returns an + * iterator to the first element that is not less than val while one expect + * an iterator on the first element that is not less *or equal* than val. + * That's why we use <= rather than < */ + return it.flt <= val; +} + +static FINLINE bool +operator < (const struct nprims_cdf& it, const size_t iprim) +{ + /* This operator is used by the std::lower_bound algorithm that returns an + * iterator to the first element that is not less than iprim while one expect + * an iterator on the first element that is not less *or equal* than iprim. + * That's why we use <= rather than < */ + return it.nprims <= iprim; +} + +static INLINE void +scene_view_destroy_geometry(struct s3d_scene_view* scnview, struct geometry* geom) +{ + ASSERT(geom); + if(geom->irtc != RTC_INVALID_GEOMETRY_ID) { + rtcDeleteGeometry(scnview->rtc_scn, geom->irtc); + geom->irtc = RTC_INVALID_GEOMETRY_ID; + scnview->rtc_scn_update = 1; /* Notify the scene upd */ + } + geometry_ref_put(geom); +} + +static void +on_shape_detach + (const struct s3d_scene* scn, + const struct s3d_shape* shape, + void* data) +{ + struct geometry** pgeom; + struct geometry* geom; + struct s3d_scene_view* scnview = (struct s3d_scene_view*)data; + unsigned shape_id; + ASSERT(scn && shape && data); + (void)scn; + + S3D(shape_get_id(shape, &shape_id)); + pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id); + + /* The scnview did not register a geometry for this shape. Ignore the signal */ + if(!pgeom) return; + + geom = *pgeom; + if(scnview->mask == 0) { + /* The scnview is NOT in use. Directly rm the cached geometry */ + size_t n; (void)n; + scene_view_destroy_geometry(scnview, geom); + n = htable_geom_erase(&scnview->cached_geoms, &shape_id); + ASSERT(n == 1); + } else { + /* The scnview is in use. Delay the deletion of the cached geometry */ + res_T res = darray_uint_push_back(&scnview->detached_shapes, &shape_id); + if(res != RES_OK) FATAL("Insufficient memory.\n"); + } +} + +static INLINE void +hit_setup(struct s3d_scene_view* scnview, const RTCRay* ray, struct s3d_hit* hit) +{ + float w; + char flip_surface = 0; + + ASSERT(scnview && hit && ray); + + if((unsigned)ray->geomID == RTC_INVALID_GEOMETRY_ID) { /* No hit */ + *hit = S3D_HIT_NULL; + return; + } + + f3_set(hit->normal, ray->Ng); + hit->distance = ray->tfar; + + hit->uv[0] = ray->u; + hit->uv[1] = ray->v; + w = 1.f - hit->uv[0] - hit->uv[1]; + ASSERT(w <= 1.f); /* This may not occurs */ + if(w < 0.f) { /* Handle precision error */ + if(hit->uv[0] > hit->uv[1]) hit->uv[0] += w; + else hit->uv[1] += w; + w = 0.f; + } + + /* Embree stores on the u and v ray parameters the barycentric coordinates of + * the hit with respect to the second and third triangle vertices, + * respectively. The following code computes the barycentric coordinates of + * the hit for the first and second triangle vertices */ + hit->uv[1] = hit->uv[0]; + hit->uv[0] = w; + + if((unsigned)ray->instID == RTC_INVALID_GEOMETRY_ID) { + struct geometry* geom_mesh; + ASSERT((unsigned)ray->geomID < darray_geom_size_get(&scnview->embree2geoms)); + geom_mesh = scene_view_geometry_from_embree_id(scnview, ray->geomID); + hit->prim.mesh__ = geom_mesh; + hit->prim.inst__ = NULL; + hit->prim.prim_id = ray->primID; + hit->prim.geom_id = geom_mesh->name; + hit->prim.inst_id = S3D_INVALID_ID; + hit->prim.scene_prim_id = /* Compute the "scene space" primitive id */ + hit->prim.prim_id /* Mesh space */ + + geom_mesh->scene_prim_id_offset; /* Scene space */ + + } else { /* The hit shape is instantiated */ + /* Retrieve the hit instance */ + struct geometry* geom_inst; + struct geometry* geom_mesh; + float transform[9]; + ASSERT((unsigned)ray->instID < darray_geom_size_get(&scnview->embree2geoms)); + geom_inst = scene_view_geometry_from_embree_id(scnview, ray->instID); + geom_mesh = scene_view_geometry_from_embree_id + (geom_inst->data.instance->scnview, ray->geomID); + hit->prim.mesh__ = geom_mesh; + hit->prim.inst__ = geom_inst; + hit->prim.prim_id = ray->primID; + hit->prim.geom_id = geom_mesh->name; + hit->prim.inst_id = geom_inst->name; + hit->prim.scene_prim_id = /* Compute the "scene space" */ + hit->prim.prim_id /* Mesh space */ + + geom_mesh->scene_prim_id_offset /* Inst space */ + + geom_inst->scene_prim_id_offset; /* Scene space */ + + flip_surface = geom_inst->flip_surface; + ASSERT(hit->prim.inst__); + ASSERT(((struct geometry*)hit->prim.inst__)->type == GEOM_INSTANCE); + + /* Transform the normal in world space */ + f33_invtrans(transform, geom_inst->data.instance->transform); + f33_mulf3(hit->normal, transform, hit->normal); + } + ASSERT(hit->prim.mesh__); + ASSERT(((struct geometry*)hit->prim.mesh__)->type == GEOM_MESH); + + /* Flip geometric normal with respect to the flip surface flag */ + flip_surface ^= ((struct geometry*)hit->prim.mesh__)->flip_surface; + if(flip_surface) f3_minus(hit->normal, hit->normal); +} + +/* Wrapper between an Embree and a Star-3D filter function */ +static void +filter_wrapper(void* user_ptr, RTCRay& ray) +{ + struct s3d_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->scnview, &ray, &hit); + if(filter->func(&hit, ray_ex->ws_org, ray_ex->ws_dir, ray_ex->data, filter->data)) { + /* Discard the intersection */ + ray.geomID = RTC_INVALID_GEOMETRY_ID; + } +} + +static res_T +embree_geometry_register + (struct s3d_scene_view* scnview, + struct geometry* geom) +{ + ASSERT(scnview && geom && (geom->type==GEOM_MESH || geom->type==GEOM_INSTANCE)); + + /* Create the Embree geometry if it is not valid */ + if(geom->irtc != RTC_INVALID_GEOMETRY_ID) { + if(geom->type == GEOM_INSTANCE) { + /* If the geometry is an instance one have to update it if the + * instantiated geometry was updated. Currently, we have no simple way to + * know if the geometry was upd or not so we simply force the update. */ + rtcUpdate(scnview->rtc_scn, geom->irtc); + } + } else { + switch(geom->type) { + case GEOM_MESH: + geom->irtc = rtcNewTriangleMesh(scnview->rtc_scn, RTC_GEOMETRY_DYNAMIC, + mesh_get_ntris(geom->data.mesh), mesh_get_nverts(geom->data.mesh)); + break; + case GEOM_INSTANCE: + geom->irtc = rtcNewInstance2 + (scnview->rtc_scn, geom->data.instance->scnview->rtc_scn); + break; + default: FATAL("Unreachable code\n"); break; + } + if(geom->irtc == RTC_INVALID_GEOMETRY_ID) + return RES_UNKNOWN_ERR; + + scnview->rtc_scn_update = 1; + } + + /* Register the embree geometry in the embree2geoms associative array */ + if(geom->irtc >= darray_geom_size_get(&scnview->embree2geoms)) { + const res_T res = darray_geom_resize(&scnview->embree2geoms, geom->irtc+1); + if(res != RES_OK) { + rtcDeleteGeometry(scnview->rtc_scn, geom->irtc); + geom->irtc = RTC_INVALID_GEOMETRY_ID; + return res; + } + } + darray_geom_data_get(&scnview->embree2geoms)[geom->irtc] = geom; + return RES_OK; +} + +static INLINE void +embree_geometry_setup_positions + (struct s3d_scene_view* scnview, struct geometry* geom) +{ + ASSERT(scnview && geom && geom->type == GEOM_MESH); + ASSERT(geom->irtc != RTC_INVALID_GEOMETRY_ID); + rtcSetBuffer(scnview->rtc_scn, geom->irtc, RTC_VERTEX_BUFFER, + mesh_get_pos(geom->data.mesh), 0, sizeof(float[3])); + rtcUpdateBuffer(scnview->rtc_scn, geom->irtc, RTC_VERTEX_BUFFER); +} + +static INLINE void +embree_geometry_setup_indices + (struct s3d_scene_view* scnview, struct geometry* geom) +{ + ASSERT(scnview && geom && geom->type == GEOM_MESH); + ASSERT(geom->irtc != RTC_INVALID_GEOMETRY_ID); + rtcSetBuffer(scnview->rtc_scn, geom->irtc, RTC_INDEX_BUFFER, + mesh_get_ids(geom->data.mesh), 0, sizeof(uint32_t[3])); + rtcUpdateBuffer(scnview->rtc_scn, geom->irtc, RTC_INDEX_BUFFER); +} + +static INLINE void +embree_geometry_setup_enable_state + (struct s3d_scene_view* scnview, struct geometry* geom) +{ + ASSERT(scnview && geom); + if(geom->is_enabled) { + rtcEnable(scnview->rtc_scn, geom->irtc); + } else { + rtcDisable(scnview->rtc_scn, geom->irtc); + } +} + +static INLINE void +embree_geometry_setup_filter_function + (struct s3d_scene_view* scnview, struct geometry* geom) +{ + ASSERT(scnview && geom && geom->irtc != RTC_INVALID_GEOMETRY_ID); + ASSERT(geom->type == GEOM_MESH); + + if(!geom->data.mesh->filter.func) { + rtcSetIntersectionFilterFunction(scnview->rtc_scn, geom->irtc, NULL); + } else { + rtcSetIntersectionFilterFunction(scnview->rtc_scn, geom->irtc, filter_wrapper); + rtcSetUserData(scnview->rtc_scn, geom->irtc, &geom->data.mesh->filter); + } +} + +static INLINE void +embree_geometry_setup_transform + (struct s3d_scene_view* scnview, struct geometry* geom) +{ + ASSERT(scnview && geom && geom->irtc != RTC_INVALID_GEOMETRY_ID); + ASSERT(geom->type == GEOM_INSTANCE); + rtcSetTransform2 + (scnview->rtc_scn, + geom->irtc, + RTC_MATRIX_COLUMN_MAJOR, + geom->data.instance->transform); + rtcUpdate(scnview->rtc_scn, geom->irtc); +} + +static INLINE res_T +scene_view_setup_embree(struct s3d_scene_view* scnview) +{ + struct htable_geom_iterator it, end; + const RTCSceneFlags rtc_scene_mask = + RTC_SCENE_DYNAMIC + | RTC_SCENE_INCOHERENT + | RTC_SCENE_ROBUST; + const RTCAlgorithmFlags rtc_algorithm_mask = + RTC_INTERSECT1 + | RTC_INTERSECT4; + int rtc_outdated = 0; + res_T res = RES_OK; + ASSERT(scnview); + + /* The rtc_scn could be already allocated since the scene views are cached */ + if(!scnview->rtc_scn) { + scnview->rtc_scn = rtcDeviceNewScene + (scnview->scn->dev->rtc, rtc_scene_mask, rtc_algorithm_mask); + if(!scnview->rtc_scn) { + res = RES_MEM_ERR; + goto error; + } + } + + htable_geom_begin(&scnview->cached_geoms, &it); + htable_geom_end(&scnview->cached_geoms, &end); + + while(!htable_geom_iterator_eq(&it, &end)) { + struct geometry** pgeom = htable_geom_iterator_data_get(&it); + struct geometry* geom = *pgeom; + + htable_geom_iterator_next(&it); + + /* Define whether or not the embree scene is outdated */ + if(geom->embree_outdated_mask) rtc_outdated = 1; + + /* Register the embree geometry */ + res = embree_geometry_register(scnview, geom); + if(res != RES_OK) goto error; + + /* Flush the embree geometry states */ + if((geom->embree_outdated_mask & EMBREE_VERTICES) != 0) + embree_geometry_setup_positions(scnview, geom); + if((geom->embree_outdated_mask & EMBREE_INDICES) != 0) + embree_geometry_setup_indices(scnview, geom); + if((geom->embree_outdated_mask & EMBREE_ENABLE) != 0) + embree_geometry_setup_enable_state(scnview, geom); + if((geom->embree_outdated_mask & EMBREE_FILTER_FUNCTION) != 0) + embree_geometry_setup_filter_function(scnview, geom); + if((geom->embree_outdated_mask & EMBREE_TRANSFORM) != 0) + embree_geometry_setup_transform(scnview, geom); + + geom->embree_outdated_mask = 0; + } + + rtc_outdated = rtc_outdated || scnview->rtc_scn_update; + + /* Commit the embree changes */ + if(rtc_outdated) { + rtcCommit(scnview->rtc_scn); + scnview->rtc_scn_update = 0; + } + +exit: + return res; +error: + if(scnview->rtc_scn) { + scnview->rtc_scn = NULL; + rtcDeleteScene(scnview->rtc_scn); + } + darray_geom_clear(&scnview->embree2geoms); + goto exit; +} + +static res_T +scene_view_register_mesh + (struct s3d_scene_view* scnview, + struct s3d_shape* shape) +{ + struct geometry** pgeom = NULL; + struct geometry* geom = NULL; + size_t iattr; + unsigned shape_id; + + res_T res = RES_OK; + ASSERT(scnview && shape && shape->type == GEOM_MESH); + + /* Retrieve the cached geometry */ + S3D(shape_get_id(shape, &shape_id)); + pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id); + if(pgeom) { + geom = *pgeom; + } else { + res = geometry_create(scnview->scn->dev, &geom); + if(res != RES_OK) goto error; + res = mesh_create(scnview->scn->dev, &geom->data.mesh); + if(res != RES_OK) goto error; + geom->type = GEOM_MESH; + res = htable_geom_set(&scnview->cached_geoms, &shape_id, &geom); + if(res != RES_OK) goto error; + geom->name = shape->id.index; + } + + /* Discard the geometry that is not geometrically valid */ + if(!shape->data.mesh->indices || !shape->data.mesh->attribs[S3D_POSITION]) { + if(geom->irtc != RTC_INVALID_GEOMETRY_ID) { + rtcDeleteGeometry(scnview->rtc_scn, geom->irtc); + geom->irtc = RTC_INVALID_GEOMETRY_ID; + } + mesh_clear(geom->data.mesh); + goto exit; + } + + /* Get a reference onto the shape mesh indices */ + if(geom->data.mesh->indices != shape->data.mesh->indices) { + geom->embree_outdated_mask |= EMBREE_INDICES; + if(geom->data.mesh->indices) { /* Release the previous index buffer */ + index_buffer_ref_put(geom->data.mesh->indices); + geom->data.mesh->indices = NULL; + } + ASSERT(shape->data.mesh->indices); + index_buffer_ref_get(shape->data.mesh->indices); + geom->data.mesh->indices = shape->data.mesh->indices; + } + + /* Get a reference onto the shape mesh attribs */ + FOR_EACH(iattr, 0, S3D_ATTRIBS_COUNT__) { + geom->embree_outdated_mask |= EMBREE_VERTICES; + if(geom->data.mesh->attribs[iattr] == shape->data.mesh->attribs[iattr]) + continue; + + if(geom->data.mesh->attribs[iattr]) { /* Release the previous buffer */ + vertex_buffer_ref_put(geom->data.mesh->attribs[iattr]); + geom->data.mesh->attribs[iattr] = NULL; + } + if(!shape->data.mesh->attribs[iattr]) + continue; + + vertex_buffer_ref_get(shape->data.mesh->attribs[iattr]); + geom->data.mesh->attribs[iattr] = shape->data.mesh->attribs[iattr]; + geom->data.mesh->attribs_type[iattr] = shape->data.mesh->attribs_type[iattr]; + } + + /* Update the enable flag */ + if(geom->is_enabled != shape->is_enabled) { + geom->is_enabled = shape->is_enabled; + geom->embree_outdated_mask |= EMBREE_ENABLE; + } + + /* Update the filter function */ + if(geom->data.mesh->filter.func != shape->data.mesh->filter.func + || geom->data.mesh->filter.data != shape->data.mesh->filter.data) { + geom->data.mesh->filter = shape->data.mesh->filter; + geom->embree_outdated_mask |= EMBREE_FILTER_FUNCTION; + } + + if(geom->irtc != RTC_INVALID_GEOMETRY_ID) { + struct index_buffer* shape_ids = shape->data.mesh->indices; + struct index_buffer* geom_ids = geom->data.mesh->indices; + struct vertex_buffer* shape_verts = shape->data.mesh->attribs[S3D_POSITION]; + struct vertex_buffer* geom_verts = geom->data.mesh->attribs[S3D_POSITION]; + const size_t shape_nids = darray_u32_size_get(&shape_ids->data); + const size_t geom_nids = darray_u32_size_get(&geom_ids->data); + const size_t shape_nverts = darray_float_size_get(&shape_verts->data); + const size_t geom_nverts = darray_float_size_get(&geom_verts->data); + + /* The shape mesh was resize => the Embree geometry is no more valid */ + if(shape_nids != geom_nids || shape_nverts != geom_nverts) { + rtcDeleteGeometry(scnview->rtc_scn, geom->irtc); + geom->irtc = RTC_INVALID_GEOMETRY_ID; + } + } + + geom->flip_surface = shape->flip_surface; + +exit: + return res; +error: + goto exit; +} + +static res_T +scene_view_register_instance + (struct s3d_scene_view* scnview, + struct s3d_shape* shape, + const int mask) +{ + struct geometry** pgeom = NULL; + struct geometry* geom = NULL; + struct s3d_scene_view** pview = NULL; + struct s3d_scene_view* view = NULL; + unsigned shape_id; + res_T res = RES_OK; + ASSERT(scnview && shape && shape->type == GEOM_INSTANCE); + + /* The instance cannot contain instances, i.e. one instancing level is + * supported */ + if(shape->data.instance->scene->instances_count != 0) { + res = RES_BAD_ARG; + goto error; + } + + /* Recursively create scnview on the scene to instantiate if necessary */ + pview = htable_instview_find + (&scnview->instviews, &shape->data.instance->scene); + if(pview) { + view = *pview; + } else { + res = s3d_scene_view_create + (shape->data.instance->scene, mask, &view); + if(res != RES_OK) goto error; + res = htable_instview_set + (&scnview->instviews, &shape->data.instance->scene, &view); + if(res != RES_OK) goto error; + } + + /* Create the scene instance of the geometry if necessary */ + S3D(shape_get_id(shape, &shape_id)); + pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id); + if(pgeom) { + geom = *pgeom; + } else { + res = geometry_create(scnview->scn->dev, &geom); + if(res != RES_OK) goto error; + geom->type = GEOM_INSTANCE; + res = instance_create(shape->data.instance->scene, &geom->data.instance); + if(res != RES_OK) goto error; + res = htable_geom_set(&scnview->cached_geoms, &shape_id, &geom); + if(res != RES_OK) goto error; + geom->name = shape->id.index; + } + ASSERT(geom->data.instance->scene == shape->data.instance->scene); + geom->data.instance->scnview = view; + + /* Update the Embree instance transformation if necessary */ + if(!f33_eq(shape->data.instance->transform, geom->data.instance->transform) + || !f3_eq(shape->data.instance->transform+9, geom->data.instance->transform+9)) { + geom->embree_outdated_mask |= EMBREE_TRANSFORM; + f33_set(geom->data.instance->transform, shape->data.instance->transform); + f3_set(geom->data.instance->transform+9, shape->data.instance->transform+9); + } + + /* Update the enable flag */ + if(geom->is_enabled != shape->is_enabled) { + geom->is_enabled = shape->is_enabled; + geom->embree_outdated_mask |= EMBREE_TRANSFORM; + } + + geom->flip_surface = shape->flip_surface; + +exit: + return res; +error: + goto exit; +} + +static res_T +scene_view_compute_cdf(struct s3d_scene_view* scnview) +{ + struct htable_geom_iterator it, end; + size_t len; + float area = 0.f; + res_T res = RES_OK; + ASSERT(scnview); + ASSERT(darray_fltui_size_get(&scnview->cdf) == 0); + + htable_geom_begin(&scnview->cached_geoms, &it); + htable_geom_end(&scnview->cached_geoms, &end); + + while(!htable_geom_iterator_eq(&it, &end)) { + const unsigned* shape_id = htable_geom_iterator_key_get(&it); + struct geometry* geom = *htable_geom_iterator_data_get(&it); + struct fltui fltui; + + htable_geom_iterator_next(&it); + + if(!geom->is_enabled) continue; + + switch(geom->type) { + case GEOM_MESH: + res = mesh_compute_cdf(geom->data.mesh); + if(res != RES_OK) goto error; + len = darray_float_size_get(&geom->data.mesh->cdf); + if(len) { + area += darray_float_cdata_get(&geom->data.mesh->cdf)[len - 1]; + } + break; + case GEOM_INSTANCE: + /* The instance CDF was computed during its scnview synchronisation */ + len = darray_fltui_size_get(&geom->data.instance->scnview->cdf); + if(len) { + area += darray_fltui_cdata_get + (&geom->data.instance->scnview->cdf)[len - 1].flt; + } + break; + default: FATAL("Unreachable code\n"); break; + } + fltui.ui = *shape_id; + fltui.flt = area; + if(len) { + res = darray_fltui_push_back(&scnview->cdf, &fltui); + if(res != RES_OK) goto error; + } + } +exit: + return res; +error: + darray_fltui_clear(&scnview->cdf); + goto exit; +} + +static res_T +scene_view_compute_nprims_cdf + (struct s3d_scene_view* scnview, + const char store_cdf) +{ + struct htable_geom_iterator it, end; + size_t len; + unsigned nprims; + res_T res = RES_OK; + ASSERT(scnview); + ASSERT(darray_nprims_cdf_size_get(&scnview->nprims_cdf) == 0); + + htable_geom_begin(&scnview->cached_geoms, &it); + htable_geom_end(&scnview->cached_geoms, &end); + + nprims = 0; + while(!htable_geom_iterator_eq(&it, &end)) { + const unsigned* shape_id = htable_geom_iterator_key_get(&it); + struct geometry* geom = *htable_geom_iterator_data_get(&it); + struct nprims_cdf cdf; + + htable_geom_iterator_next(&it); + + if(!geom->is_enabled) continue; + + geom->scene_prim_id_offset = nprims; + switch(geom->type) { + case GEOM_MESH: + len = mesh_get_ntris(geom->data.mesh); + nprims += (unsigned)len; + break; + case GEOM_INSTANCE: + /* The instance CDF was computed during its scnview synchronisation */ + len = darray_nprims_cdf_size_get + (&geom->data.instance->scnview->nprims_cdf); + if(len) { + nprims += darray_nprims_cdf_cdata_get + (&geom->data.instance->scnview->nprims_cdf)[len - 1].nprims; + } + break; + default: FATAL("Unreachable code\n"); break; + } + + cdf.nprims = nprims; + cdf.ishape = *shape_id; + if(store_cdf && len) { + res = darray_nprims_cdf_push_back(&scnview->nprims_cdf, &cdf); + if(res != RES_OK) goto error; + } + } +exit: + return res; +error: + darray_nprims_cdf_clear(&scnview->nprims_cdf); + goto exit; +} + +static void +scene_view_compute_scene_aabb(struct s3d_scene_view* scnview) +{ + struct htable_geom_iterator it, end; + float lower[3], upper[3]; + + ASSERT(scnview->lower[0] == FLT_MAX && scnview->upper[0] == -FLT_MAX); + ASSERT(scnview->lower[1] == FLT_MAX && scnview->upper[1] == -FLT_MAX); + ASSERT(scnview->lower[2] == FLT_MAX && scnview->upper[2] == -FLT_MAX); + + htable_geom_begin(&scnview->cached_geoms, &it); + htable_geom_end(&scnview->cached_geoms, &end); + + while(!htable_geom_iterator_eq(&it, &end)) { + struct instance* inst; + struct geometry* geom = *htable_geom_iterator_data_get(&it); + + htable_geom_iterator_next(&it); + + if(!geom->is_enabled) continue; + + switch(geom->type) { + case GEOM_MESH: mesh_compute_aabb(geom->data.mesh, lower, upper); break; + case GEOM_INSTANCE: + /* Note that the instance AABB was computed during its scnview + * synchronisation. */ + inst = geom->data.instance; + if(aabb_is_degenerated(inst->scnview->lower, inst->scnview->upper)) { + /* Empty scene */ + f3_splat(lower, FLT_MAX); + f3_splat(upper,-FLT_MAX); + } else { + /* Transform local scene AABB bounds in world space */ + f33_mulf3(lower, inst->transform, inst->scnview->lower); + f33_mulf3(upper, inst->transform, inst->scnview->upper); + f3_add(lower, inst->transform + 9, lower); + f3_add(upper, inst->transform + 9, upper); + /* Define the world space AABB of the transformed local scene AABB */ + if(lower[0] > upper[0]) SWAP(float, lower[0], upper[0]); + if(lower[1] > upper[1]) SWAP(float, lower[1], upper[1]); + if(lower[2] > upper[2]) SWAP(float, lower[2], upper[2]); + } + break; + default: FATAL("Unreachable code\n"); break; + } + f3_min(scnview->lower, scnview->lower, lower); + f3_max(scnview->upper, scnview->upper, upper); + } +} + +float +scene_view_compute_volume + (struct s3d_scene_view* scnview, + const char flip_surface) +{ + struct htable_geom_iterator it, end; + float volume; + + ASSERT(scnview); + htable_geom_begin(&scnview->cached_geoms, &it); + htable_geom_end(&scnview->cached_geoms, &end); + + volume = 0.f; + while(!htable_geom_iterator_eq(&it, &end)) { + struct geometry* geom = *htable_geom_iterator_data_get(&it); + const char flip = geom->flip_surface ^ flip_surface; + + htable_geom_iterator_next(&it); + + if(!geom->is_enabled) continue; + + switch(geom->type) { + case GEOM_MESH: + volume += mesh_compute_volume(geom->data.mesh, flip); + break; + case GEOM_INSTANCE: + volume += scene_view_compute_volume(geom->data.instance->scnview, flip); + break; + default: FATAL("Unreachable code\n"); break; + } + } + + if(volume < 0.f) { + log_warning(scnview->scn->dev, +"%s:\n" +"\tthe volume is negative. The scene shapes might not represent closed 2D\n" +"\tmanifold volumes, or their surface normal might not point inward the volume.\n", + FUNC_NAME); + } + + return volume; +} + + +static res_T +scene_view_sync + (struct s3d_scene_view* scnview, + const int mask) +{ + struct htable_shape_iterator it, end; + res_T res = RES_OK; + + ASSERT(scnview); + ASSERT((mask & (S3D_TRACE|S3D_SAMPLE|S3D_GET_PRIMITIVE)) != 0); + + /* Commit the scene shape to the scnview */ + htable_shape_begin(&scnview->scn->shapes, &it); + htable_shape_end(&scnview->scn->shapes, &end); + while(!htable_shape_iterator_eq(&it, &end)) { + struct s3d_shape** pshape = htable_shape_iterator_data_get(&it); + struct s3d_shape* shape = *pshape; + + switch(shape->type) { + case GEOM_INSTANCE: + res = scene_view_register_instance(scnview, shape, mask); + break; + case GEOM_MESH: + res = scene_view_register_mesh(scnview, shape); + break; + default: FATAL("Unreachable code\n"); break; + } + if(res != RES_OK) goto error; + htable_shape_iterator_next(&it); + } + + scene_view_compute_scene_aabb(scnview); + + /* Setup the scene for the S3D_TRACE scnview */ + if((mask & S3D_TRACE) != 0) { + res = scene_view_setup_embree(scnview); + if(res != RES_OK) goto error; + } + /* Setup the scene for the S3D_SAMPLE scnview */ + if((mask & S3D_SAMPLE) != 0) { + res = scene_view_compute_cdf(scnview); + if(res != RES_OK) goto error; + } + /* Setup the scene for the scene_primitive_id/S3D_GET_PRIMITIVE scnview */ + res = scene_view_compute_nprims_cdf(scnview, (mask & S3D_GET_PRIMITIVE)!=0); + if(res != RES_OK) goto error; + + scnview->mask = mask; + +exit: + return res; +error: + goto exit; +} + +static res_T +scene_view_create(struct s3d_scene* scn, struct s3d_scene_view** out_scnview) +{ + struct s3d_scene_view* scnview = NULL; + res_T res = RES_OK; + ASSERT(scn && out_scnview); + + if(!is_list_empty(&scn->scnviews)) { + /* Retrieve an already allocated scnview */ + scnview = CONTAINER_OF(list_head(&scn->scnviews), struct s3d_scene_view, node); + list_del(&scnview->node); + ref_get(&scnview->ref); + } else { + scnview = (struct s3d_scene_view*)MEM_CALLOC + (scn->dev->allocator, 1, sizeof(struct s3d_scene_view)); + if(!scnview) { + res = RES_MEM_ERR; + goto error; + } + list_init(&scnview->node); + htable_geom_init(scn->dev->allocator, &scnview->cached_geoms); + darray_geom_init(scn->dev->allocator, &scnview->embree2geoms); + darray_fltui_init(scn->dev->allocator, &scnview->cdf); + darray_nprims_cdf_init(scn->dev->allocator, &scnview->nprims_cdf); + htable_instview_init(scn->dev->allocator, &scnview->instviews); + darray_uint_init(scn->dev->allocator, &scnview->detached_shapes); + f3_splat(scnview->lower, FLT_MAX); + f3_splat(scnview->upper,-FLT_MAX); + ref_init(&scnview->ref); + + CLBK_INIT(&scnview->on_shape_detach_cb); + CLBK_SETUP(&scnview->on_shape_detach_cb, on_shape_detach, scnview); + SIG_CONNECT_CLBK(&scn->sig_shape_detach, &scnview->on_shape_detach_cb); + } + S3D(scene_ref_get(scn)); + scnview->scn = scn; +exit: + *out_scnview = scnview; + return res; +error: + if(scnview) { + S3D(scene_view_ref_put(scnview)); + scnview = NULL; + } + goto exit; +} + +static void +scene_view_release(ref_T* ref) +{ + struct htable_instview_iterator it_view, end_view; + struct htable_geom_iterator it_geom, end_geom; + struct s3d_scene_view* scnview = CONTAINER_OF(ref, struct s3d_scene_view, ref); + size_t i, n; + ASSERT(ref); + + /* Release the scnview of the instances */ + htable_instview_begin(&scnview->instviews, &it_view); + htable_instview_end(&scnview->instviews, &end_view); + while(!htable_instview_iterator_eq(&it_view, &end_view)) { + struct s3d_scene_view* view = *htable_instview_iterator_data_get(&it_view); + htable_instview_iterator_next(&it_view); + S3D(scene_view_ref_put(view)); + } + htable_instview_clear(&scnview->instviews); + + /* Reset the scnview of the cached instance. Note that this step is actually + * not necessary but this reset is "appreciated" in debug */ + htable_geom_begin(&scnview->cached_geoms, &it_geom); + htable_geom_end(&scnview->cached_geoms, &end_geom); + while(!htable_geom_iterator_eq(&it_geom, &end_geom)) { + struct geometry* geom = *htable_geom_iterator_data_get(&it_geom); + htable_geom_iterator_next(&it_geom); + if(geom->type != GEOM_INSTANCE) continue; + geom->data.instance->scnview = NULL; + } + + /* Remove the geometry of the shapes detached while the scnview was active */ + n = darray_uint_size_get(&scnview->detached_shapes); + FOR_EACH(i, 0, n) { + const unsigned shape_id = darray_uint_cdata_get(&scnview->detached_shapes)[i]; + struct geometry** pgeom = htable_geom_find(&scnview->cached_geoms, &shape_id); + struct geometry* geom = *pgeom; + size_t tmp; (void)tmp; + scene_view_destroy_geometry(scnview, geom); + tmp = htable_geom_erase(&scnview->cached_geoms, &shape_id); + ASSERT(tmp == 1); + } + darray_uint_clear(&scnview->detached_shapes); + + /* Clear the scnview data structures excepted the cache of geometries that + * will be used to speed up the future scnview creation */ + darray_geom_clear(&scnview->embree2geoms); + darray_fltui_clear(&scnview->cdf); + darray_nprims_cdf_clear(&scnview->nprims_cdf); + f3_splat(scnview->lower, FLT_MAX); + f3_splat(scnview->upper,-FLT_MAX); + scnview->mask = 0; + + /* Do not physically release the memory space of the scnview. Add it to the + * available scnviews pool of the scene */ + list_add(&scnview->scn->scnviews, &scnview->node); + S3D(scene_ref_put(scnview->scn)); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +s3d_scene_view_create + (struct s3d_scene* scn, + const int mask, + struct s3d_scene_view** out_scnview) +{ + struct s3d_scene_view* scnview = NULL; + res_T res = RES_OK; + + if(!scn || !out_scnview) { + res = RES_BAD_ARG; + goto error; + } + + if(!(mask & S3D_TRACE) + && !(mask & S3D_SAMPLE) + && !(mask & S3D_GET_PRIMITIVE)) { + log_error(scn->dev, "%s: no valid scene view mask is defined.\n", FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + + res = scene_view_create(scn, &scnview); + if(res != RES_OK) goto error; + + res = scene_view_sync(scnview, mask); + if(res != RES_OK) goto error; + +exit: + if(out_scnview) *out_scnview = scnview; + return res; +error: + if(scnview) { + S3D(scene_view_ref_put(scnview)); + scnview = NULL; + } + goto exit; +} + +res_T +s3d_scene_view_ref_get(struct s3d_scene_view* scnview) +{ + if(!scnview) return RES_BAD_ARG; + ref_get(&scnview->ref); + return RES_OK; +} + +res_T +s3d_scene_view_ref_put(struct s3d_scene_view* scnview) +{ + if(!scnview) return RES_BAD_ARG; + ref_put(&scnview->ref, scene_view_release); + return RES_OK; +} + +res_T +s3d_scene_view_get_mask(struct s3d_scene_view* scnview, int* mask) +{ + if(!scnview || !mask) return RES_BAD_ARG; + *mask = scnview->mask; + return RES_OK; +} + +res_T +s3d_scene_view_trace_ray + (struct s3d_scene_view* scnview, + const float org[3], + const float dir[3], + const float range[2], + void* ray_data, + struct s3d_hit* hit) +{ + struct ray_extended ray_ex; + if(!scnview || !org || !dir || !range || !hit) + return RES_BAD_ARG; + if(!f3_is_normalized(dir)) { + log_error(scnview->scn->dev, + "%s: unnormalized ray direction {%g, %g, %g}.\n", + FUNC_NAME, SPLIT3(dir)); + return RES_BAD_ARG; + } + if((scnview->mask & S3D_TRACE) == 0) { + log_error(scnview->scn->dev, + "%s: no active S3D_TRACE scnview on the submitted scnview.\n", FUNC_NAME); + return RES_BAD_OP; + } + if(range[0] > range[1]) { /* Degenerated range <=> disabled ray */ + *hit = S3D_HIT_NULL; + return RES_OK; + } + + f3_set(ray_ex.org, f3_set(ray_ex.ws_org, org)); + f3_set(ray_ex.dir, f3_set(ray_ex.ws_dir, dir)); + ray_ex.tnear = range[0]; + ray_ex.tfar = range[1]; + ray_ex.geomID = RTC_INVALID_GEOMETRY_ID; + ray_ex.primID = RTC_INVALID_GEOMETRY_ID; + ray_ex.instID = RTC_INVALID_GEOMETRY_ID; + ray_ex.mask = 0xFFFFFFFF; + ray_ex.time = 0.f; + ray_ex.scnview = scnview; + ray_ex.data = ray_data; + + rtcIntersect(scnview->rtc_scn, ray_ex); + + hit_setup(scnview, &ray_ex, hit); + + return RES_OK; +} + +res_T +s3d_scene_view_trace_rays + (struct s3d_scene_view* scnview, + const size_t nrays, + const int mask, + const float* origins, + const float* directions, + const float* ranges, + void* rays_data, + const size_t sizeof_ray_data, + struct s3d_hit* hits) +{ + size_t iray; + size_t iorg, idir, irange, idata; + size_t org_step, dir_step, range_step, data_step; + res_T res = RES_OK; + + if(!scnview) return RES_BAD_ARG; + if(!nrays) return RES_OK; + + org_step = mask & S3D_RAYS_SINGLE_ORIGIN ? 0 : 3; + dir_step = mask & S3D_RAYS_SINGLE_DIRECTION ? 0 : 3; + range_step = mask & S3D_RAYS_SINGLE_RANGE ? 0 : 2; + data_step = (mask & S3D_RAYS_SINGLE_DATA) || !rays_data ? 0 : sizeof_ray_data; + iorg = idir = irange = idata = 0; + + FOR_EACH(iray, 0, nrays) { + res = s3d_scene_view_trace_ray(scnview, origins+iorg, directions+idir, + ranges+irange, (char*)rays_data+idata, hits+iray); + if(UNLIKELY(res != RES_OK)) break; + iorg += org_step; + idir += dir_step; + irange += range_step; + idata += data_step; + } + return res; +} + +res_T +s3d_scene_view_sample + (struct s3d_scene_view* scnview, + const float u, + const float v, + const float w, + struct s3d_primitive* primitive, /* sampled primitive */ + float st[2]) +{ + struct geometry** pgeom; + struct geometry* geom; + const struct fltui* fltui_begin, *fltui_end, *fltui_found; + const float* flt_begin, *flt_end, *flt_found; + unsigned ishape; + float f; + res_T res = RES_OK; + + if(!scnview || !primitive || !st) { + res = RES_BAD_ARG; + goto error; + } + /* Expecting canonic numbers */ + if(u < 0.f || u >= 1.f || v < 0.f || v >= 1.f || w < 0.f || w >= 1.f) { + log_error(scnview->scn->dev, + "%s: the submitted numbers are not canonical, i.e. they are not in [0, 1[.\n", + FUNC_NAME); + res = RES_BAD_ARG; + goto error; + } + if((scnview->mask & S3D_SAMPLE) == 0) { + log_error(scnview->scn->dev, + "%s: no active S3D_SAMPLE scnview on the submitted scene.\n", + FUNC_NAME); + res = RES_BAD_OP; + goto error; + } + + /* Find the sampled geometry */ + if(darray_fltui_size_get(&scnview->cdf) == 0) { + /* No geometry to sample */ + *primitive = S3D_PRIMITIVE_NULL; + goto exit; + } else if(darray_fltui_size_get(&scnview->cdf) == 1) { + ishape = darray_fltui_cdata_get(&scnview->cdf)[0].ui; + /* Map u to the CDF bounds */ + f = u * darray_fltui_cdata_get(&scnview->cdf)[0].flt; + } else { + fltui_begin = darray_fltui_cdata_get(&scnview->cdf); + fltui_end = fltui_begin + darray_fltui_size_get(&scnview->cdf); + f = u * fltui_end[-1].flt; /* Map u to the CDF bounds */ + fltui_found = std::lower_bound(fltui_begin, fltui_end, f); + ASSERT(fltui_found != fltui_end); + ishape = fltui_found->ui; + + /* Transform u to the geometry CDF bounds */ + if(fltui_found != fltui_begin) + f -= fltui_found[-1].flt; + } + pgeom = htable_geom_find(&scnview->cached_geoms, &ishape); + ASSERT(pgeom); + geom = *pgeom; + + if(geom->type == GEOM_MESH) { + primitive->inst__ = NULL; + primitive->inst_id = S3D_INVALID_ID; + primitive->scene_prim_id = 0; + } else { + /* Find the sampled instantiated geometry */ + ASSERT(geom->type == GEOM_INSTANCE); + primitive->inst__ = geom; + primitive->inst_id = geom->name; + primitive->scene_prim_id = geom->scene_prim_id_offset; + if(darray_fltui_size_get(&geom->data.instance->scnview->cdf) == 1) { + ishape = darray_fltui_cdata_get(&geom->data.instance->scnview->cdf)[0].ui; + } else { + fltui_begin = darray_fltui_cdata_get(&geom->data.instance->scnview->cdf); + fltui_end = fltui_begin + darray_fltui_size_get(&geom->data.instance->scnview->cdf); + fltui_found = std::lower_bound(fltui_begin, fltui_end, f); + ASSERT(fltui_found != fltui_end); + ishape = fltui_found->ui; + + /* Transform u to the geometry CDF bounds */ + if(fltui_found != fltui_begin) + f -= fltui_found[-1].flt; + } + pgeom = htable_geom_find(&geom->data.instance->scnview->cached_geoms, &ishape); + ASSERT(pgeom); + geom = *pgeom; + } + ASSERT(geom->type == GEOM_MESH); + + /* Find the sampled triangle */ + primitive->mesh__ = geom; + primitive->geom_id = geom->name; + primitive->scene_prim_id += geom->scene_prim_id_offset; + flt_begin = darray_float_cdata_get(&geom->data.mesh->cdf); + flt_end = flt_begin + darray_float_size_get(&geom->data.mesh->cdf); + flt_found = std::lower_bound(flt_begin, flt_end, f); + ASSERT(flt_found != flt_end); + + primitive->prim_id = (unsigned)(flt_found - flt_begin); + primitive->scene_prim_id += primitive->prim_id; + S3D(primitive_sample(primitive, v, w, st)); + +exit: + return res; +error: + goto exit; +} + +res_T +s3d_scene_view_get_primitive + (struct s3d_scene_view* scnview, + const unsigned iprim, + struct s3d_primitive* prim) +{ + struct geometry** pgeom; + struct geometry* geom; + const struct nprims_cdf* begin, *end, *found; + size_t nprims; + unsigned ishape; + size_t i; + res_T res = RES_OK; + + if(!scnview || !prim) { + res = RES_BAD_ARG; + goto error; + } + if((scnview->mask & S3D_GET_PRIMITIVE) == 0) { + log_error(scnview->scn->dev, + "%s: no active S3D_GET_PRIMITIVE scnview on the submitted scene.\n", + FUNC_NAME); + res = RES_BAD_OP; + goto error; + } + S3D(scene_view_primitives_count(scnview, &nprims)); + if(iprim >= nprims) { + log_error(scnview->scn->dev, + "%s: the primitive index %u exceeds the number of scene primitives %u.\n", + FUNC_NAME, iprim, (unsigned)nprims); + res = RES_BAD_ARG; + goto error; + } + + i = iprim; + if(darray_nprims_cdf_size_get(&scnview->nprims_cdf) == 1) { + ishape = darray_nprims_cdf_cdata_get(&scnview->nprims_cdf)[0].ishape; + } else { + begin = darray_nprims_cdf_cdata_get(&scnview->nprims_cdf); + end = begin + darray_nprims_cdf_size_get(&scnview->nprims_cdf); + found = std::lower_bound(begin, end, i); + ASSERT(found != end); + ishape = found->ishape; + if(found != begin) { + ASSERT(i >= found[-1].nprims); + i -= found[-1].nprims; + } + } + pgeom = htable_geom_find(&scnview->cached_geoms, &ishape); + ASSERT(pgeom); + geom = *pgeom; + + if(geom->type == GEOM_MESH) { + prim->inst__ = NULL; + prim->inst_id = S3D_INVALID_ID; + prim->scene_prim_id = 0; + } else { + ASSERT(geom->type == GEOM_INSTANCE); + prim->inst__ = geom; + prim->inst_id = geom->name; + prim->scene_prim_id = geom->scene_prim_id_offset; + if(darray_nprims_cdf_size_get(&geom->data.instance->scnview->nprims_cdf)==1) { + ishape = darray_nprims_cdf_cdata_get + (&geom->data.instance->scnview->nprims_cdf)[0].ishape; + } else { + begin = darray_nprims_cdf_cdata_get + (&geom->data.instance->scnview->nprims_cdf); + end = begin + darray_nprims_cdf_size_get + (&geom->data.instance->scnview->nprims_cdf); + found = std::lower_bound(begin, end, i); + ASSERT(found != end); + ishape = found->ishape; + if(found != begin) { + ASSERT(i >= found[-1].nprims); + i -= found[-1].nprims; + } + } + pgeom = htable_geom_find(&geom->data.instance->scnview->cached_geoms, &ishape); + ASSERT(pgeom); + geom = *pgeom; + } + ASSERT(geom->type == GEOM_MESH); + ASSERT(i < mesh_get_ntris(geom->data.mesh)); + prim->mesh__ = geom; + prim->geom_id = geom->name; + prim->prim_id = (unsigned)i; + prim->scene_prim_id += geom->scene_prim_id_offset; + prim->scene_prim_id += prim->prim_id; + +exit: + return res; +error: + goto exit; +} + +res_T +s3d_scene_view_primitives_count(struct s3d_scene_view* scnview, size_t* prims_count) +{ + res_T res = RES_OK; + + if(!scnview || !prims_count) { + res = RES_BAD_ARG; + goto error; + } + if((scnview->mask & S3D_GET_PRIMITIVE) != 0) { + const size_t len = darray_nprims_cdf_size_get(&scnview->nprims_cdf); + if(!len) { + *prims_count = 0; + } else { + *prims_count = darray_nprims_cdf_cdata_get + (&scnview->nprims_cdf)[len - 1].nprims; + } + } else { + struct htable_geom_iterator it, end; + size_t inst_count; + + htable_geom_begin(&scnview->cached_geoms, &it); + htable_geom_end(&scnview->cached_geoms, &end); + *prims_count = 0; + while(!htable_geom_iterator_eq(&it, &end)) { + struct geometry** pgeom = htable_geom_iterator_data_get(&it); + struct geometry* geom = *pgeom; + htable_geom_iterator_next(&it); + + if(!geom->is_enabled) continue; + + switch(geom->type) { + case GEOM_MESH: + *prims_count += mesh_get_ntris(geom->data.mesh); + break; + case GEOM_INSTANCE: + S3D(scene_view_primitives_count(geom->data.instance->scnview, &inst_count)); + *prims_count += inst_count; + break; + default: FATAL("Unreachable code\n"); break; + } + } + } +exit: + return res; +error: + goto exit; +} + +res_T +s3d_scene_view_compute_area(struct s3d_scene_view* scnview, float* out_area) +{ + float area; + res_T res = RES_OK; + + if(!scnview || !out_area) { + res = RES_BAD_ARG; + goto error; + } + if((scnview->mask & S3D_SAMPLE) != 0) { + /* Retrieve the overall scene area from the scene cumulative distribution + * function. Note that the CDF stores the cumulative triangle area + * multiplied by 2; the real scene area is thus the CDF upper bound / 2 */ + size_t len = darray_fltui_size_get(&scnview->cdf); + if(!len) { + area = 0.f; + } else { + area = darray_fltui_cdata_get(&scnview->cdf)[len - 1].flt * 0.5f; + } + } else { + struct htable_geom_iterator it, end; + float inst_area; + + htable_geom_begin(&scnview->cached_geoms, &it); + htable_geom_end(&scnview->cached_geoms, &end); + + area = 0.f; + while(!htable_geom_iterator_eq(&it, &end)) { + struct geometry** pgeom = htable_geom_iterator_data_get(&it); + struct geometry* geom = *pgeom; + + htable_geom_iterator_next(&it); + + if(!geom->is_enabled) continue; + + switch(geom->type) { + case GEOM_MESH: + area += mesh_compute_area(geom->data.mesh); + break; + case GEOM_INSTANCE: + /* TODO take into account the instance scale factor */ + S3D(scene_view_compute_area(geom->data.instance->scnview, &inst_area)); + area += inst_area; + break; + default: FATAL("Unreachable code\n"); break; + } + } + } + +exit: + if(out_area) *out_area = area; + return res; +error: + area = -1.f; + goto exit; +} + +res_T +s3d_scene_view_compute_volume(struct s3d_scene_view* scnview, float* out_volume) +{ + if(!scnview || !out_volume) return RES_BAD_ARG; + *out_volume = scene_view_compute_volume(scnview, 0/*No initial flip_surface*/); + return RES_OK; +} + +res_T +s3d_scene_view_get_aabb(struct s3d_scene_view* scnview, float lower[3], float upper[3]) +{ + if(!scnview || !lower || !upper) return RES_BAD_ARG; + f3_set(lower, scnview->lower); + f3_set(upper, scnview->upper); + return RES_OK; +} + +/******************************************************************************* + * Local functions + ******************************************************************************/ +void +scene_view_destroy(struct s3d_scene_view* scnview) +{ + htable_geom_iterator it, end; + ASSERT(scnview && !is_list_empty(&scnview->node)/*Not in use*/); + ASSERT(scnview->mask == 0); + + /* Delete the cached geometries */ + htable_geom_begin(&scnview->cached_geoms, &it); + htable_geom_end(&scnview->cached_geoms, &end); + while(!htable_geom_iterator_eq(&it, &end)) { + struct geometry** pgeom = htable_geom_iterator_data_get(&it); + struct geometry* geom = *pgeom; + scene_view_destroy_geometry(scnview, geom); + htable_geom_iterator_next(&it); + } + + /* Delete the back-end scene */ + if(scnview->rtc_scn) rtcDeleteScene(scnview->rtc_scn); + + /* Release internal data structure */ + htable_geom_release(&scnview->cached_geoms); + darray_geom_release(&scnview->embree2geoms); + darray_fltui_release(&scnview->cdf); + darray_nprims_cdf_release(&scnview->nprims_cdf); + htable_instview_release(&scnview->instviews); + darray_uint_release(&scnview->detached_shapes); + + /* Remove the scnview from its pool */ + list_del(&scnview->node); + CLBK_DISCONNECT(&scnview->on_shape_detach_cb); + + /* Free the scnview memory space */ + MEM_RM(scnview->scn->dev->allocator, scnview); +} + diff --git a/src/s3d_scene_view_c.h b/src/s3d_scene_view_c.h @@ -0,0 +1,133 @@ +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to describe a + * virtual 3D environment that can be ray-traced and sampled both robustly + * and efficiently. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#ifndef S3D_SCENE_VIEW_C_H +#define S3D_SCENE_VIEW_C_H + +#include "s3d_backend.h" +#include "s3d_scene_c.h" + +#include <rsys/dynamic_array.h> +#include <rsys/dynamic_array_uint.h> +#include <rsys/hash_table.h> +#include <rsys/list.h> +#include <rsys/ref_count.h> + +/* Forward declarations */ +struct s3d_scene_view; + +/* + * The geometry pointers must be initialized to NULL in order to define + * which pointers are valid or not + */ +static FINLINE void +geom_ptr_init__(struct mem_allocator* alloc, struct geometry** geom) +{ + (void)alloc; *geom = NULL; +} + +/* Generate the darray_geom dynamic array */ +#define DARRAY_NAME geom +#define DARRAY_DATA struct geometry* +#define DARRAY_FUNCTOR_INIT geom_ptr_init__ +#include <rsys/dynamic_array.h> + +/* Generate the htable_geom hash table */ +#define HTABLE_NAME geom +#define HTABLE_DATA struct geometry* +#define HTABLE_KEY unsigned /* Id of the shape */ +#include <rsys/hash_table.h> + +/* Generate the darray_fltui dynamic array */ +struct fltui { float flt; unsigned ui; }; +#define DARRAY_NAME fltui +#define DARRAY_DATA struct fltui +#include <rsys/dynamic_array.h> + +/* Generate the darray_geom_nprims array */ +struct nprims_cdf { unsigned nprims, ishape; }; +#define DARRAY_NAME nprims_cdf +#define DARRAY_DATA struct nprims_cdf +#include <rsys/dynamic_array.h> + +/* Generate the htable_instview hash table */ +#define HTABLE_NAME instview +#define HTABLE_DATA struct s3d_scene_view* +#define HTABLE_KEY struct s3d_scene* +#include <rsys/hash_table.h> + +struct s3d_scene_view { + struct list_node node; /* Attachment point to the scene scene_views pool */ + + struct htable_geom cached_geoms; /* Cached shape geometries */ + struct darray_geom embree2geoms; /* Embree index to geometry */ + struct darray_fltui cdf; /* Unormalized CDF */ + struct darray_nprims_cdf nprims_cdf; + + /* Map an instantiated scene to its scene view */ + struct htable_instview instviews; + + /* Id of Shapes detached while the scnview is active */ + struct darray_uint detached_shapes; + + float lower[3], upper[3]; /* AABB of the scene */ + + /* Callback attached to the sig_shape_detach signal of scn */ + scene_shape_cb_T on_shape_detach_cb; + + int mask; /* Combination of enum s3d_scene_view_flag */ + int rtc_scn_update; /* Define if Embree geometries were deleted/added */ + RTCScene rtc_scn; /* Embree scene */ + + ref_T ref; + struct s3d_scene* scn; +}; + +extern LOCAL_SYM void +scene_view_destroy + (struct s3d_scene_view* scnview); + +static FINLINE struct geometry* +scene_view_geometry_from_embree_id + (struct s3d_scene_view* scnview, + const unsigned irtc) +{ + struct geometry* geom; + ASSERT(scnview && irtc != RTC_INVALID_GEOMETRY_ID); + ASSERT(irtc < darray_geom_size_get(&scnview->embree2geoms)); + geom = darray_geom_data_get(&scnview->embree2geoms)[irtc]; + ASSERT(geom); + return geom; +} + +#endif /* S3D_SCENE_VIEW_C_H */ + diff --git a/src/s3d_shape.c b/src/s3d_shape.c @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -52,7 +52,6 @@ shape_release(ref_T* ref) dev = shape->dev; /* The shape should not be attached */ - ASSERT(is_list_empty(&shape->scene_attachment)); if(shape->type != GEOM_NONE) { switch(shape->type) { case GEOM_MESH: @@ -64,6 +63,7 @@ shape_release(ref_T* ref) default: FATAL("Unreachable code \n"); break; } } + flist_name_del(&dev->names, shape->id); MEM_RM(dev->allocator, shape); S3D(device_ref_put(dev)); } @@ -87,7 +87,6 @@ shape_create(struct s3d_device* dev, struct s3d_shape** out_shape) res = RES_MEM_ERR; goto error; } - list_init(&shape->scene_attachment); S3D(device_ref_get(dev)); shape->dev = dev; ref_init(&shape->ref); @@ -160,7 +159,7 @@ s3d_shape_ref_put(struct s3d_shape* shape) } res_T -s3d_shape_get_id(struct s3d_shape* shape, unsigned* id) +s3d_shape_get_id(const struct s3d_shape* shape, unsigned* id) { if(!shape || !id) return RES_BAD_ARG; *id = shape->id.index; @@ -176,7 +175,7 @@ s3d_shape_enable(struct s3d_shape* shape, const char enable) } res_T -s3d_shape_is_enabled(struct s3d_shape* shape, char* is_enabled) +s3d_shape_is_enabled(const struct s3d_shape* shape, char* is_enabled) { if(!shape || !is_enabled) return RES_BAD_ARG; *is_enabled = shape->is_enabled; @@ -184,14 +183,6 @@ s3d_shape_is_enabled(struct s3d_shape* shape, char* is_enabled) } res_T -s3d_shape_is_attached(struct s3d_shape* shape, char* is_attached) -{ - if(!shape || !is_attached) return RES_BAD_ARG; - *is_attached = !is_list_empty(&shape->scene_attachment); - return RES_OK; -} - -res_T s3d_shape_flip_surface(struct s3d_shape* shape) { if(!shape) return RES_BAD_ARG; @@ -212,7 +203,6 @@ s3d_instance_set_position f3_dot(f33_row(axis, shape->data.instance->transform, 1), position); shape->data.instance->transform[11] = f3_dot(f33_row(axis, shape->data.instance->transform, 2), position); - shape->data.instance->update_transform = 1; return RES_OK; } @@ -231,13 +221,11 @@ s3d_instance_translate (shape->data.instance->transform + 9, shape->data.instance->transform + 9, vec); - shape->data.instance->update_transform = 1; } else if(space == S3D_WORLD_TRANSFORM) { f3_add (shape->data.instance->transform + 9, shape->data.instance->transform + 9, translation); - shape->data.instance->update_transform = 1; } else { return RES_BAD_ARG; } @@ -388,4 +376,3 @@ s3d_mesh_get_hit_filter_data(struct s3d_shape* shape, void** data) return RES_OK; } - diff --git a/src/s3d_shape_c.h b/src/s3d_shape_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -45,7 +45,6 @@ #include <limits.h> struct s3d_shape { - struct list_node scene_attachment; struct fid id; char flip_surface; diff --git a/src/test_s3d_camera.h b/src/test_s3d_camera.h @@ -0,0 +1,81 @@ +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to describe a + * virtual 3D environment that can be ray-traced and sampled both robustly + * and efficiently. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#ifndef TEST_S3D_CAMERA_H +#define TEST_S3D_CAMERA_H + +#include <rsys/float3.h> + +struct camera { + float pos[3]; + float x[3], y[3], z[3]; /* Basis */ +}; + +static INLINE void +camera_init + (struct camera* cam, + const float pos[3], + const float tgt[3], + const float up[3], + const float fov_x, + const float proj_ratio) +{ + float f = 0.f; + ASSERT(cam); + + f3_set(cam->pos, pos); + f = f3_normalize(cam->z, f3_sub(cam->z, tgt, pos)); NCHECK(f, 0); + f = f3_normalize(cam->x, f3_cross(cam->x, cam->z, up)); NCHECK(f, 0); + f = f3_normalize(cam->y, f3_cross(cam->y, cam->z, cam->x)); NCHECK(f, 0); + f3_divf(cam->z, cam->z, (float)tan(fov_x*0.5f)); + f3_divf(cam->y, cam->y, proj_ratio); +} + +static INLINE void +camera_ray + (const struct camera* cam, + const float pixel[2], + float org[3], + float dir[3]) +{ + float x[3], y[3], f; + ASSERT(cam && pixel && org && dir); + + f3_mulf(x, cam->x, pixel[0]*2.f - 1.f); + f3_mulf(y, cam->y, pixel[1]*2.f - 1.f); + f3_add(dir, f3_add(dir, x, y), cam->z); + f = f3_normalize(dir, dir); NCHECK(f, 0); + f3_set(org, cam->pos); +} + +#endif /* TEST_S3D_CAMERA_H */ + diff --git a/src/test_s3d_cbox.h b/src/test_s3d_cbox.h @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -36,7 +36,7 @@ #include <rsys/rsys.h> #include <stdint.h> -struct cbox_desc{ +struct cbox_desc { const float* vertices; const unsigned* indices; }; diff --git a/src/test_s3d_device.c b/src/test_s3d_device.c @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly diff --git a/src/test_s3d_primitive.c b/src/test_s3d_primitive.c @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -34,6 +34,7 @@ #include "test_s3d_cbox.h" #include "test_s3d_utils.h" +#include <rsys/float2.h> #include <rsys/float3.h> #include <rsys/math.h> @@ -80,17 +81,29 @@ plane_get_pos(const unsigned ivert, float pos[3], void* data) pos[2] = plane_verts[i + 2]; } +static void +plane_get_uv(const unsigned ivert, float uv[2], void* data) +{ + const unsigned i = ivert*3; + (void)data; + NCHECK(uv, NULL); + CHECK(ivert < plane_nverts, 1); + uv[0] = -plane_verts[i + 0]; + uv[1] = -plane_verts[i + 1]; +} + int main(int argc, char** argv) { struct mem_allocator allocator; struct s3d_device* dev; struct s3d_scene* scn; + struct s3d_scene_view* scnview; struct s3d_shape* walls; struct s3d_shape* plane; struct s3d_attrib attr; struct s3d_primitive prim = S3D_PRIMITIVE_NULL; - struct s3d_vertex_data attribs; + struct s3d_vertex_data attribs[2]; struct cbox_desc desc; size_t nprims; size_t i; @@ -112,24 +125,28 @@ main(int argc, char** argv) CHECK(s3d_shape_get_id(walls, &walls_id), RES_OK); CHECK(s3d_scene_attach_shape(scn, walls), RES_OK); - attribs.usage = S3D_POSITION; - attribs.type = S3D_FLOAT3; - attribs.get = cbox_get_position; + attribs[1].usage = S3D_ATTRIB_0; + attribs[1].type = S3D_FLOAT2; + attribs[1].get = plane_get_uv; + + attribs[0].usage = S3D_POSITION; + attribs[0].type = S3D_FLOAT3; + attribs[0].get = cbox_get_position; ntris = cbox_walls_ntris; nverts = cbox_walls_nverts; desc.vertices = cbox_walls; desc.indices = cbox_walls_ids; CHECK(s3d_mesh_setup_indexed_vertices - (walls, ntris, cbox_get_ids, nverts, &attribs, 1, &desc), RES_OK); + (walls, ntris, cbox_get_ids, nverts, attribs, 1, &desc), RES_OK); - attribs.get = plane_get_pos; + attribs[0].get = plane_get_pos; CHECK(s3d_mesh_setup_indexed_vertices - (plane, plane_ntris, plane_get_ids, plane_nverts, &attribs, 1, NULL), RES_OK); + (plane, plane_ntris, plane_get_ids, plane_nverts, attribs, 2, NULL), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_SAMPLE), RES_OK); - CHECK(s3d_scene_sample(scn, 0, 0, 0, &prim, uv), RES_OK); - CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); + CHECK(s3d_scene_view_sample(scnview, 0, 0, 0, &prim, uv), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); CHECK(s3d_primitive_get_attrib(NULL, S3D_ATTRIBS_COUNT__, NULL, NULL), RES_BAD_ARG); CHECK(s3d_primitive_get_attrib(&prim, S3D_ATTRIBS_COUNT__, NULL, NULL), RES_BAD_ARG); @@ -181,12 +198,18 @@ main(int argc, char** argv) CHECK(s3d_scene_clear(scn), RES_OK); CHECK(s3d_scene_attach_shape(scn, plane), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_GET_PRIMITIVE), RES_OK); - CHECK(s3d_scene_primitives_count(scn, &nprims), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_GET_PRIMITIVE, &scnview), RES_OK); + CHECK(s3d_scene_view_primitives_count(scnview, &nprims), RES_OK); CHECK(nprims, 2); - CHECK(s3d_scene_get_primitive(scn, 0, &prim), RES_OK); + CHECK(s3d_scene_view_get_primitive(scnview, 0, &prim), RES_OK); CHECK(S3D_PRIMITIVE_EQ(&prim, &S3D_PRIMITIVE_NULL), 0); - CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + + CHECK(s3d_primitive_has_attrib(&prim, S3D_ATTRIB_0, &b), RES_OK); + CHECK(b, 1); + CHECK(s3d_primitive_has_attrib(&prim, S3D_ATTRIB_1, &b), RES_OK); + CHECK(b, 0); CHECK(s3d_primitive_compute_area(NULL, NULL), RES_BAD_ARG); CHECK(s3d_primitive_compute_area(&prim, NULL), RES_BAD_ARG); @@ -220,6 +243,47 @@ main(int argc, char** argv) CHECK(uv[1] <= 1.f, 1); } + #define GET_VERTEX_ATTR s3d_triangle_get_vertex_attrib + CHECK(GET_VERTEX_ATTR(NULL, 3, S3D_GEOMETRY_NORMAL, NULL), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(&prim, 3, S3D_GEOMETRY_NORMAL, NULL), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(NULL, 0, S3D_GEOMETRY_NORMAL, NULL), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(&prim, 0, S3D_GEOMETRY_NORMAL, NULL), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(NULL, 3, S3D_POSITION, NULL), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(&prim, 3, S3D_POSITION, NULL), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(NULL, 0, S3D_POSITION, NULL), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(&prim, 0, S3D_POSITION, NULL), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(NULL, 3, S3D_GEOMETRY_NORMAL, &attr), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(&prim, 3, S3D_GEOMETRY_NORMAL, &attr), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(NULL, 0, S3D_GEOMETRY_NORMAL, &attr), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(&prim, 0, S3D_GEOMETRY_NORMAL, &attr), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(NULL, 3, S3D_POSITION, &attr), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(&prim, 3, S3D_POSITION, &attr), RES_BAD_ARG); + CHECK(GET_VERTEX_ATTR(NULL, 0, S3D_POSITION, &attr), RES_BAD_ARG); + + CHECK(GET_VERTEX_ATTR(&prim, 0, S3D_POSITION, &attr), RES_OK); + CHECK(attr.type, S3D_FLOAT3); + CHECK(f3_eq_eps(attr.value, plane_verts + plane_ids[0]*3, 1.e-6f), 1); + CHECK(GET_VERTEX_ATTR(&prim, 1, S3D_POSITION, &attr), RES_OK); + CHECK(attr.type, S3D_FLOAT3); + CHECK(f3_eq_eps(attr.value, plane_verts + plane_ids[1]*3, 1.e-6f), 1); + CHECK(GET_VERTEX_ATTR(&prim, 2, S3D_POSITION, &attr), RES_OK); + CHECK(attr.type, S3D_FLOAT3); + CHECK(f3_eq_eps(attr.value, plane_verts + plane_ids[2]*3, 1.e-6f), 1); + + CHECK(GET_VERTEX_ATTR(&prim, 0, S3D_ATTRIB_0, &attr), RES_OK); + CHECK(attr.type, S3D_FLOAT2); + f2_minus(uv, plane_verts + plane_ids[0]*3); + CHECK(f2_eq_eps(attr.value, uv, 1.e-6f), 1); + CHECK(GET_VERTEX_ATTR(&prim, 1, S3D_ATTRIB_0, &attr), RES_OK); + CHECK(attr.type, S3D_FLOAT2); + f2_minus(uv, plane_verts + plane_ids[1]*3); + CHECK(f2_eq_eps(attr.value, uv, 1.e-6f), 1); + CHECK(GET_VERTEX_ATTR(&prim, 2, S3D_ATTRIB_0, &attr), RES_OK); + CHECK(attr.type, S3D_FLOAT2); + f2_minus(uv, plane_verts + plane_ids[2]*3); + CHECK(f2_eq_eps(attr.value, uv, 1.e-6f), 1); + #undef GET_VERTEX_ATTR + CHECK(s3d_device_ref_put(dev), RES_OK); CHECK(s3d_scene_ref_put(scn), RES_OK); CHECK(s3d_shape_ref_put(walls), RES_OK); diff --git a/src/test_s3d_sampler.c b/src/test_s3d_sampler.c @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -53,6 +53,7 @@ main(int argc, char** argv) struct s3d_device* dev; struct s3d_scene* scn; struct s3d_scene* scn2; + struct s3d_scene_view* scnview; struct s3d_shape* cbox; struct s3d_shape* walls; struct s3d_shape* short_block; @@ -87,24 +88,24 @@ main(int argc, char** argv) CHECK(s3d_shape_get_id(short_block, &short_block_id), RES_OK); CHECK(s3d_shape_get_id(tall_block, &tall_block_id), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_SAMPLE), RES_OK); - CHECK(s3d_scene_sample(NULL, 0, 0, 0, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_sample(scn, 0, 0, 0, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_sample(NULL, 0, 0, 0, &prim, NULL), RES_BAD_ARG); - CHECK(s3d_scene_sample(scn, 0, 0, 0, &prim, NULL), RES_BAD_ARG); - CHECK(s3d_scene_sample(NULL, 0, 0, 0, NULL, uv), RES_BAD_ARG); - CHECK(s3d_scene_sample(scn, 0, 0, 0, NULL, uv), RES_BAD_ARG); - CHECK(s3d_scene_sample(NULL, 0, 0, 0, &prim, uv), RES_BAD_ARG); - CHECK(s3d_scene_sample(scn, 0, 0, 0, &prim, uv), RES_OK); - CHECK(s3d_scene_sample(scn, -1, 0, 0, &prim, uv), RES_BAD_ARG); - CHECK(s3d_scene_sample(scn, 0, -1, 0, &prim, uv), RES_BAD_ARG); - CHECK(s3d_scene_sample(scn, 0, 0, -1, &prim, uv), RES_BAD_ARG); - CHECK(s3d_scene_sample(scn, 1, 0, 0, &prim, uv), RES_BAD_ARG); - CHECK(s3d_scene_sample(scn, 0, 1, 0, &prim, uv), RES_BAD_ARG); - CHECK(s3d_scene_sample(scn, 0, 0, 1, &prim, uv), RES_BAD_ARG); - CHECK(s3d_scene_sample(scn, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); + CHECK(s3d_scene_view_sample(NULL, 0, 0, 0, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_sample(scnview, 0, 0, 0, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_sample(NULL, 0, 0, 0, &prim, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_sample(scnview, 0, 0, 0, &prim, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_sample(NULL, 0, 0, 0, NULL, uv), RES_BAD_ARG); + CHECK(s3d_scene_view_sample(scnview, 0, 0, 0, NULL, uv), RES_BAD_ARG); + CHECK(s3d_scene_view_sample(NULL, 0, 0, 0, &prim, uv), RES_BAD_ARG); + CHECK(s3d_scene_view_sample(scnview, 0, 0, 0, &prim, uv), RES_OK); + CHECK(s3d_scene_view_sample(scnview, -1, 0, 0, &prim, uv), RES_BAD_ARG); + CHECK(s3d_scene_view_sample(scnview, 0, -1, 0, &prim, uv), RES_BAD_ARG); + CHECK(s3d_scene_view_sample(scnview, 0, 0, -1, &prim, uv), RES_BAD_ARG); + CHECK(s3d_scene_view_sample(scnview, 1, 0, 0, &prim, uv), RES_BAD_ARG); + CHECK(s3d_scene_view_sample(scnview, 0, 1, 0, &prim, uv), RES_BAD_ARG); + CHECK(s3d_scene_view_sample(scnview, 0, 0, 1, &prim, uv), RES_BAD_ARG); + CHECK(s3d_scene_view_sample(scnview, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); CHECK(S3D_PRIMITIVE_EQ(&prim, &S3D_PRIMITIVE_NULL), 1); - CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); attribs.usage = S3D_POSITION; attribs.type = S3D_FLOAT3; @@ -119,12 +120,11 @@ main(int argc, char** argv) CHECK(s3d_scene_attach_shape(scn, walls), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_SAMPLE), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_SAMPLE), RES_BAD_OP); + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); - CHECK(s3d_scene_sample(scn, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); + CHECK(s3d_scene_view_sample(scnview, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); CHECK(s3d_primitive_get_attrib(&prim, S3D_POSITION, uv, &attr0), RES_OK); - CHECK(s3d_scene_sample(scn, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); + CHECK(s3d_scene_view_sample(scnview, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); CHECK(s3d_primitive_get_attrib(&prim, S3D_POSITION, uv, &attr1), RES_OK); prim1 = prim; @@ -143,21 +143,19 @@ main(int argc, char** argv) CHECK(attr1.type, S3D_FLOAT3); CHECK(f3_eq_eps(attr0.value, attr1.value, 1.e-6f), 1); - CHECK(s3d_scene_sample(scn, 0.3f, 0.1f, 0.2f, &prim, uv), RES_OK); + CHECK(s3d_scene_view_sample(scnview, 0.3f, 0.1f, 0.2f, &prim, uv), RES_OK); CHECK(s3d_primitive_get_attrib(&prim, S3D_POSITION, uv, &attr1), RES_OK); NCHECK(f3_eq_eps(attr0.value, attr1.value, 1.e-6f), 1); - CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); CHECK(s3d_shape_enable(walls, 0), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_SAMPLE), RES_OK); - CHECK(s3d_scene_sample(scn, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); + CHECK(s3d_scene_view_sample(scnview, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); CHECK(S3D_PRIMITIVE_EQ(&prim, &S3D_PRIMITIVE_NULL), 1); - CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); CHECK(s3d_shape_enable(walls, 1), RES_OK); - CHECK(s3d_scene_sample(scn, 0.5f, 0.5f, 0.5f, &prim, uv), RES_BAD_OP); - ntris = sizeof(cbox_block_ids)/sizeof(unsigned[3]); nverts = sizeof(cbox_short_block)/sizeof(float[3]); desc.vertices = cbox_short_block; @@ -168,35 +166,35 @@ main(int argc, char** argv) CHECK(s3d_scene_attach_shape(scn, short_block), RES_OK); CHECK(s3d_scene_attach_shape(scn, tall_block), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_SAMPLE), RES_OK); - CHECK(s3d_scene_sample(scn, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); + CHECK(s3d_scene_view_sample(scnview, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); CHECK(s3d_primitive_get_attrib(&prim, S3D_POSITION, uv, &attr0), RES_OK); desc.vertices = cbox_tall_block; CHECK(s3d_mesh_setup_indexed_vertices (tall_block, ntris, cbox_get_ids, nverts, &attribs, 1, &desc), RES_OK); - CHECK(s3d_scene_sample(scn, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); + CHECK(s3d_scene_view_sample(scnview, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); CHECK(s3d_primitive_get_attrib(&prim, S3D_POSITION, uv, &attr1), RES_OK); CHECK(f3_eq_eps(attr0.value, attr1.value, 1.e-6f), 1); - CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_SAMPLE), RES_OK); - CHECK(s3d_scene_sample(scn, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); + CHECK(s3d_scene_view_sample(scnview, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); CHECK(s3d_primitive_get_attrib(&prim, S3D_POSITION, uv, &attr1), RES_OK); NCHECK(f3_eq_eps(attr0.value, attr1.value, 1.e-6f), 1); - CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); CHECK(s3d_shape_enable(cbox, 0), RES_OK); - CHECK(s3d_scene_begin_session(scn2, S3D_SAMPLE|S3D_TRACE), RES_OK); - CHECK(s3d_scene_sample(scn2, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); + CHECK(s3d_scene_view_create(scn2, S3D_SAMPLE|S3D_TRACE, &scnview), RES_OK); + CHECK(s3d_scene_view_sample(scnview, 0.5f, 0.5f, 0.5f, &prim, uv), RES_OK); CHECK(S3D_PRIMITIVE_EQ(&prim, &S3D_PRIMITIVE_NULL), 1); - CHECK(s3d_scene_end_session(scn2), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); CHECK(s3d_shape_enable(cbox, 1), RES_OK); - CHECK(s3d_scene_begin_session(scn2, S3D_SAMPLE|S3D_TRACE), RES_OK); + CHECK(s3d_scene_view_create(scn2, S3D_SAMPLE|S3D_TRACE, &scnview), RES_OK); FOR_EACH(i, 0, NSAMPS) { const float u = rand_canonic(); const float v = rand_canonic(); const float w = rand_canonic(); - CHECK(s3d_scene_sample(scn2, u, v, w, &prim, uv), RES_OK); + CHECK(s3d_scene_view_sample(scnview, u, v, w, &prim, uv), RES_OK); CHECK(s3d_primitive_get_attrib(&prim, S3D_POSITION, uv, &attr0), RES_OK); CHECK(prim.inst_id, cbox_id); @@ -206,9 +204,9 @@ main(int argc, char** argv) CHECK(prim.prim_id < 10, 1); CHECK(prim.scene_prim_id >= prim.prim_id, 1); CHECK(prim.scene_prim_id < 30, 1); -/* printf("%f %f %f\n", SPLIT3(attr0.value)); */ + printf("%f %f %f\n", SPLIT3(attr0.value)); } - CHECK(s3d_scene_end_session(scn2), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); CHECK(s3d_device_ref_put(dev), RES_OK); CHECK(s3d_scene_ref_put(scn), RES_OK); diff --git a/src/test_s3d_scene.c b/src/test_s3d_scene.c @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -92,11 +92,14 @@ main(int argc, char** argv) struct s3d_scene* scn; struct s3d_scene* scn2; struct s3d_scene* scn3; + struct s3d_scene_view* scnview; + struct s3d_scene_view* scnview2; + struct s3d_scene_view* scnview3; struct s3d_vertex_data attribs; struct s3d_shape* shapes[4]; const size_t nshapes = sizeof(shapes)/sizeof(struct s3d_shape*); void* data = (void*)&cbox_walls_desc; - size_t i; + size_t i, n; size_t nprims; float area, volume, lower[3], upper[3]; unsigned id; @@ -116,22 +119,36 @@ main(int argc, char** argv) CHECK(s3d_scene_create(dev, &scn2), RES_OK); CHECK(s3d_scene_create(dev, &scn3), RES_OK); + CHECK(s3d_scene_get_shapes_count(NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_get_shapes_count(scn, NULL), RES_BAD_ARG); + CHECK(s3d_scene_get_shapes_count(NULL, &n), RES_BAD_ARG); + CHECK(s3d_scene_get_shapes_count(scn, &n), RES_OK); + CHECK(n, 0); + CHECK(s3d_scene_attach_shape(NULL, NULL), RES_BAD_ARG); CHECK(s3d_scene_attach_shape(scn, NULL), RES_BAD_ARG); CHECK(s3d_scene_attach_shape(NULL, shapes[0]), RES_BAD_ARG); CHECK(s3d_scene_attach_shape(scn, shapes[0]), RES_OK); - CHECK(s3d_scene_attach_shape(scn, shapes[0]), RES_BAD_ARG); + CHECK(s3d_scene_get_shapes_count(scn, &n), RES_OK); + CHECK(n, 1); + CHECK(s3d_scene_attach_shape(scn, shapes[0]), RES_OK); + CHECK(s3d_scene_get_shapes_count(scn, &n), RES_OK); + CHECK(n, 1); CHECK(s3d_scene_detach_shape(NULL, NULL), RES_BAD_ARG); CHECK(s3d_scene_detach_shape(scn, NULL), RES_BAD_ARG); CHECK(s3d_scene_detach_shape(NULL, shapes[0]), RES_BAD_ARG); CHECK(s3d_scene_detach_shape(scn, shapes[0]), RES_OK); CHECK(s3d_scene_detach_shape(scn, shapes[0]), RES_BAD_ARG); + CHECK(s3d_scene_get_shapes_count(scn, &n), RES_OK); + CHECK(n, 0); FOR_EACH(i, 1, nshapes) { CHECK(s3d_scene_attach_shape(scn, shapes[i]), RES_OK); CHECK(s3d_shape_ref_put(shapes[i]), RES_OK); } + CHECK(s3d_scene_get_shapes_count(scn, &n), RES_OK); + CHECK(n, nshapes - 1); CHECK(s3d_scene_instantiate(NULL, NULL), RES_BAD_ARG); CHECK(s3d_scene_instantiate(scn, NULL), RES_BAD_ARG); @@ -146,88 +163,85 @@ main(int argc, char** argv) CHECK(s3d_scene_clear(NULL), RES_BAD_ARG); CHECK(s3d_scene_clear(scn), RES_OK); + CHECK(s3d_scene_get_shapes_count(scn, &n), RES_OK); + CHECK(n, 0); CHECK(s3d_scene_clear(scn), RES_OK); CHECK(s3d_scene_instantiate(scn, shapes + 2), RES_OK); CHECK(s3d_scene_attach_shape(scn, shapes[2]), RES_BAD_ARG); - CHECK(s3d_scene_get_session_mask(NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_get_session_mask(scn, NULL), RES_BAD_ARG); - CHECK(s3d_scene_get_session_mask(NULL, &mask), RES_BAD_ARG); - CHECK(s3d_scene_get_session_mask(scn, &mask), RES_OK); - CHECK(mask, 0); - CHECK(s3d_scene_attach_shape(scn, shapes[0]), RES_OK); - CHECK(s3d_scene_begin_session(NULL, 0), RES_BAD_ARG); - CHECK(s3d_scene_begin_session(scn, 0), RES_BAD_ARG); - CHECK(s3d_scene_begin_session(scn, S3D_TRACE|S3D_GET_PRIMITIVE), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_TRACE), RES_BAD_OP); + CHECK(s3d_scene_get_shapes_count(scn, &n), RES_OK); + CHECK(n, 1); - CHECK(s3d_scene_get_session_mask(scn, &mask), RES_OK); - CHECK(mask & S3D_TRACE, S3D_TRACE); - CHECK(mask & S3D_SAMPLE, 0); - - CHECK(s3d_scene_clear(scn), RES_BAD_OP); - CHECK(s3d_scene_detach_shape(scn, shapes[0]), RES_BAD_OP); - - CHECK(s3d_scene_end_session(NULL), RES_BAD_ARG); - CHECK(s3d_scene_end_session(scn), RES_OK); - CHECK(s3d_scene_end_session(scn), RES_BAD_OP); + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview), RES_OK); + CHECK(s3d_scene_view_get_mask(scnview, &mask), RES_OK); + CHECK(mask, S3D_TRACE); + CHECK(s3d_scene_detach_shape(scn, shapes[0]), RES_OK); + CHECK(s3d_scene_clear(scn), RES_OK); - CHECK(s3d_scene_get_session_mask(scn, &mask), RES_OK); - CHECK(mask, 0); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); - CHECK(s3d_scene_detach_shape(scn, shapes[0]), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_TRACE), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview), RES_OK); CHECK(s3d_scene_attach_shape(scn, shapes[0]), RES_OK); CHECK(s3d_scene_detach_shape(scn, shapes[0]), RES_OK); CHECK(s3d_scene_attach_shape(scn, shapes[0]), RES_OK); - CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); CHECK(s3d_scene_attach_shape(scn2, shapes[1]), RES_OK); CHECK(s3d_scene_attach_shape(scn2, shapes[2]), RES_OK); - CHECK(s3d_scene_begin_session(scn2, S3D_SAMPLE|S3D_TRACE), RES_OK); - - CHECK(s3d_scene_compute_area(NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_compute_area(scn2, NULL), RES_BAD_ARG); - CHECK(s3d_scene_compute_area(NULL, &area), RES_BAD_ARG); - CHECK(s3d_scene_compute_area(scn2, &area), RES_OK); + CHECK(s3d_scene_attach_shape(scn3, shapes[1]), RES_OK); + + CHECK(s3d_scene_get_shapes_count(scn, &n), RES_OK); + CHECK(n, 1); + CHECK(s3d_scene_get_shapes_count(scn2, &n), RES_OK); + CHECK(n, 2); + CHECK(s3d_scene_get_shapes_count(scn3, &n), RES_OK); + CHECK(n, 1); + + CHECK(s3d_scene_view_create(scn2, S3D_SAMPLE|S3D_TRACE, &scnview2), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); + CHECK(s3d_scene_view_create(scn3, S3D_SAMPLE, &scnview3), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview3), RES_OK); + + CHECK(s3d_scene_view_compute_area(NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_compute_area(scnview2, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_compute_area(NULL, &area), RES_BAD_ARG); + CHECK(s3d_scene_view_compute_area(scnview2, &area), RES_OK); CHECK(area, 0.f); - CHECK(s3d_scene_compute_volume(NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_compute_volume(scn2, NULL), RES_BAD_ARG); - CHECK(s3d_scene_compute_volume(NULL, &volume), RES_BAD_ARG); - CHECK(s3d_scene_compute_volume(scn2, &volume), RES_OK); + CHECK(s3d_scene_view_compute_volume(NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_compute_volume(scnview2, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_compute_volume(NULL, &volume), RES_BAD_ARG); + CHECK(s3d_scene_view_compute_volume(scnview2, &volume), RES_OK); CHECK(volume, 0.f); - CHECK(s3d_scene_primitives_count(NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_primitives_count(scn2, NULL), RES_BAD_ARG); - CHECK(s3d_scene_primitives_count(NULL, &nprims), RES_BAD_ARG); - CHECK(s3d_scene_primitives_count(scn2, &nprims), RES_OK); + CHECK(s3d_scene_view_primitives_count(NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_primitives_count(scnview2, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_primitives_count(NULL, &nprims), RES_BAD_ARG); + CHECK(s3d_scene_view_primitives_count(scnview2, &nprims), RES_OK); CHECK(nprims, 0); - CHECK(s3d_scene_get_aabb(NULL, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_get_aabb(scn2, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_get_aabb(NULL, lower, NULL), RES_BAD_ARG); - CHECK(s3d_scene_get_aabb(scn2, lower, NULL), RES_BAD_ARG); - CHECK(s3d_scene_get_aabb(NULL, NULL, upper), RES_BAD_ARG); - CHECK(s3d_scene_get_aabb(scn2, NULL, upper), RES_BAD_ARG); - CHECK(s3d_scene_get_aabb(NULL, lower, upper), RES_BAD_ARG); - CHECK(s3d_scene_get_aabb(scn2, lower, upper), RES_OK); + CHECK(s3d_scene_view_get_aabb(NULL, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_get_aabb(scnview2, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_get_aabb(NULL, lower, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_get_aabb(scnview2, lower, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_get_aabb(NULL, NULL, upper), RES_BAD_ARG); + CHECK(s3d_scene_view_get_aabb(scnview2, NULL, upper), RES_BAD_ARG); + CHECK(s3d_scene_view_get_aabb(NULL, lower, upper), RES_BAD_ARG); + CHECK(s3d_scene_view_get_aabb(scnview2, lower, upper), RES_OK); CHECK(lower[0] > upper[0], 1); CHECK(lower[1] > upper[1], 1); CHECK(lower[2] > upper[2], 1); - CHECK(s3d_scene_end_session(scn2), RES_OK); - - CHECK(s3d_scene_compute_area(scn2, &area), RES_BAD_OP); - CHECK(s3d_scene_compute_volume(scn2, &volume), RES_BAD_OP); - CHECK(s3d_scene_primitives_count(scn2, &nprims), RES_BAD_OP); - CHECK(s3d_scene_get_aabb(scn2, lower, upper), RES_BAD_OP); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview2), RES_OK); CHECK(s3d_scene_instantiate(scn2, shapes + 3), RES_OK); CHECK(s3d_scene_attach_shape(scn3, shapes[3]), RES_OK); - CHECK(s3d_scene_begin_session(scn3, S3D_SAMPLE|S3D_TRACE), RES_BAD_ARG); + CHECK(s3d_scene_get_shapes_count(scn3, &n), RES_OK); + CHECK(n, 2); + CHECK(s3d_scene_view_create(scn3, S3D_SAMPLE|S3D_TRACE, &scnview3), RES_BAD_ARG); CHECK(s3d_scene_detach_shape(scn, shapes[0]), RES_OK); @@ -255,60 +269,56 @@ main(int argc, char** argv) cbox_get_ids, cbox_walls_nverts, &attribs, 1, data), RES_OK); CHECK(s3d_scene_attach_shape(scn, shapes[0]), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_TRACE), RES_OK); - CHECK(s3d_scene_compute_area(scn, &area), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview), RES_OK); + CHECK(s3d_scene_view_compute_area(scnview, &area), RES_OK); CHECK(eq_epsf(area, 1532296.f, 1.e-6f), 1); - CHECK(s3d_scene_primitives_count(scn, &nprims), RES_OK); + CHECK(s3d_scene_view_primitives_count(scnview, &nprims), RES_OK); CHECK(nprims, 10); - CHECK(s3d_scene_get_aabb(scn, lower, upper), RES_OK); + CHECK(s3d_scene_view_get_aabb(scnview, lower, upper), RES_OK); CHECK(eq_epsf(lower[0], 0.f, 1.e-6f), 1); CHECK(eq_epsf(lower[1], 0.f, 1.e-6f), 1); CHECK(eq_epsf(lower[2], 0.f, 1.e-6f), 1); CHECK(eq_epsf(upper[0], 552.f, 1.e-6f), 1); CHECK(eq_epsf(upper[1], 559.f, 1.e-6f), 1); CHECK(eq_epsf(upper[2], 548.f, 1.e-6f), 1); - CHECK(s3d_scene_end_session(scn), RES_OK); CHECK(s3d_scene_instantiate(scn, shapes + 1), RES_OK); CHECK(s3d_scene_attach_shape(scn2, shapes[1]), RES_OK); - CHECK(s3d_scene_begin_session(scn2, S3D_TRACE), RES_OK); - CHECK(s3d_scene_compute_area(scn2, &area), RES_OK); + CHECK(s3d_scene_view_create(scn2, S3D_GET_PRIMITIVE, &scnview2), RES_OK); + CHECK(s3d_scene_view_compute_area(scnview2, &area), RES_OK); CHECK(eq_epsf(area, 1532296.f, 1.e-6f), 1); - CHECK(s3d_scene_primitives_count(scn, &nprims), RES_OK); + CHECK(s3d_scene_view_primitives_count(scnview2, &nprims), RES_OK); CHECK(nprims, 10); - CHECK(s3d_scene_get_aabb(scn, lower, upper), RES_OK); + CHECK(s3d_scene_view_get_aabb(scnview2, lower, upper), RES_OK); CHECK(eq_epsf(lower[0], 0.f, 1.e-6f), 1); CHECK(eq_epsf(lower[1], 0.f, 1.e-6f), 1); CHECK(eq_epsf(lower[2], 0.f, 1.e-6f), 1); CHECK(eq_epsf(upper[0], 552.f, 1.e-6f), 1); CHECK(eq_epsf(upper[1], 559.f, 1.e-6f), 1); CHECK(eq_epsf(upper[2], 548.f, 1.e-6f), 1); - CHECK(s3d_scene_end_session(scn2), RES_OK); - CHECK(s3d_scene_begin_session(scn2, S3D_GET_PRIMITIVE), RES_OK); - CHECK(s3d_scene_compute_area(scn2, &area), RES_OK); + CHECK(s3d_scene_view_compute_area(scnview, &area), RES_OK); CHECK(eq_epsf(area, 1532296.f, 1.e-6f), 1); - CHECK(s3d_scene_primitives_count(scn, &nprims), RES_OK); - CHECK(nprims, 10); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); - CHECK(s3d_scene_get_primitive(NULL, 11, NULL), RES_BAD_ARG); - CHECK(s3d_scene_get_primitive(scn2, 11, NULL), RES_BAD_ARG); - CHECK(s3d_scene_get_primitive(NULL, 0, NULL), RES_BAD_ARG); - CHECK(s3d_scene_get_primitive(scn2, 0, NULL), RES_BAD_ARG); - CHECK(s3d_scene_get_primitive(NULL, 11, prims + 0), RES_BAD_ARG); - CHECK(s3d_scene_get_primitive(scn2, 11, prims + 0), RES_BAD_ARG); - CHECK(s3d_scene_get_primitive(NULL, 0, prims + 0), RES_BAD_ARG); + CHECK(s3d_scene_view_get_primitive(NULL, 11, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_get_primitive(scnview2, 11, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_get_primitive(NULL, 0, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_get_primitive(scnview2, 0, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_get_primitive(NULL, 11, prims + 0), RES_BAD_ARG); + CHECK(s3d_scene_view_get_primitive(scnview2, 11, prims + 0), RES_BAD_ARG); + CHECK(s3d_scene_view_get_primitive(NULL, 0, prims + 0), RES_BAD_ARG); FOR_EACH(i, 0, nprims) { size_t j; - CHECK(s3d_scene_get_primitive(scn2, (unsigned)i, prims + i), RES_OK); + CHECK(s3d_scene_view_get_primitive(scnview2, (unsigned)i, prims + i), RES_OK); CHECK(S3D_PRIMITIVE_EQ(prims + i, &S3D_PRIMITIVE_NULL), 0); CHECK(prims[i].scene_prim_id, i); FOR_EACH(j, 0, i) CHECK(S3D_PRIMITIVE_EQ(prims + i, prims + j), 0); } - CHECK(s3d_scene_end_session(scn2), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview2), RES_OK); attribs.type = S3D_FLOAT3; attribs.usage = S3D_POSITION; @@ -316,18 +326,18 @@ main(int argc, char** argv) CHECK(s3d_mesh_setup_indexed_vertices (shapes[0], cube_ntris, cube_get_ids, cube_nverts, &attribs, 1, NULL), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_TRACE), RES_OK); - CHECK(s3d_scene_compute_area(scn, &area), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview), RES_OK); + CHECK(s3d_scene_view_compute_area(scnview, &area), RES_OK); CHECK(eq_epsf(area, 6.f, 1.e-6f), 1); - CHECK(s3d_scene_compute_volume(scn, &volume), RES_OK); + CHECK(s3d_scene_view_compute_volume(scnview, &volume), RES_OK); CHECK(eq_epsf(volume, 1.f, 1.e-6f), 1); - CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); CHECK(s3d_shape_flip_surface(shapes[0]), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_GET_PRIMITIVE), RES_OK); - CHECK(s3d_scene_compute_volume(scn, &volume), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_GET_PRIMITIVE, &scnview), RES_OK); + CHECK(s3d_scene_view_compute_volume(scnview, &volume), RES_OK); CHECK(eq_epsf(volume, -1.f, 1.e-6f), 1); - CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); CHECK(s3d_scene_get_device(NULL, NULL), RES_BAD_ARG); CHECK(s3d_scene_get_device(scn, NULL), RES_BAD_ARG); diff --git a/src/test_s3d_scene_view.c b/src/test_s3d_scene_view.c @@ -0,0 +1,1048 @@ +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to describe a + * virtual 3D environment that can be ray-traced and sampled both robustly + * and efficiently. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "s3d.h" +#include "test_s3d_utils.h" + +#include <rsys/float3.h> +#include <rsys/float2.h> + +#include <string.h> + +struct mesh_context { + const float* verts; + const unsigned* ids; +}; + +static int +filter + (const struct s3d_hit* hit, + const float org[3], + const float dir[3], + void* ray_data, + void* filter_data) +{ + (void)org, (void)dir, (void)ray_data, (void)filter_data; + CHECK(S3D_HIT_NONE(hit), 0); + return hit->prim.prim_id % 2 == 0; +} + +/******************************************************************************* + * Cube data + ******************************************************************************/ +static const float cube_verts[] = { + 0.f, 0.f, 0.f, + 1.f, 0.f, 0.f, + 0.f, 1.f, 0.f, + 1.f, 1.f, 0.f, + 0.f, 0.f, 1.f, + 1.f, 0.f, 1.f, + 0.f, 1.f, 1.f, + 1.f, 1.f, 1.f +}; +static const unsigned cube_nverts = sizeof(cube_verts) / sizeof(float[3]); + +/* Front faces are CW. The normals point into the cube */ +static const unsigned cube_ids[] = { + 0, 2, 1, 1, 2, 3, /* Front */ + 0, 4, 2, 2, 4, 6, /* Left */ + 4, 5, 6, 6, 5, 7, /* Back */ + 3, 7, 1, 1, 7, 5, /* Right */ + 2, 6, 3, 3, 6, 7, /* Top */ + 0, 1, 4, 4, 1, 5 /* Bottom */ +}; +static const unsigned cube_ntris = sizeof(cube_ids) / sizeof(unsigned[3]); + +/******************************************************************************* + * Plane data + ******************************************************************************/ +static const float plane_verts[] = { + 0.f, 0.f, 0.5f, + 1.f, 0.f, 0.5f, + 1.f, 1.f, 0.5f, + 0.f, 1.f, 0.5f +}; +static const unsigned plane_nverts = sizeof(plane_verts) / sizeof(float[3]); + +static const unsigned plane_ids[] = { 0, 1, 2, 2, 3, 0 }; +static const unsigned plane_ntris = sizeof(plane_ids) / sizeof(unsigned[3]); + +/******************************************************************************* + * helper function + ******************************************************************************/ +static float +rand_canonic(void) +{ + int r; + while((r = rand()) == RAND_MAX); + return (float)r / (float)RAND_MAX; +} + +static void +get_ids(const unsigned itri, unsigned ids[3], void* data) +{ + const unsigned id = itri * 3; + const struct mesh_context* ctx = data; + NCHECK(ctx, NULL); + NCHECK(ids, NULL); + ids[0] = ctx->ids[id + 0]; + ids[1] = ctx->ids[id + 1]; + ids[2] = ctx->ids[id + 2]; +} + +static void +get_pos(const unsigned ivert, float pos[3], void* data) +{ + const unsigned i = ivert*3; + const struct mesh_context* ctx = data; + NCHECK(ctx, NULL); + NCHECK(pos, NULL); + pos[0] = ctx->verts[i + 0]; + pos[1] = ctx->verts[i + 1]; + pos[2] = ctx->verts[i + 2]; +} + +static void +test_miscellaneous + (struct s3d_device* dev, + struct s3d_shape* cube, + struct s3d_shape* plane) +{ + struct s3d_scene* scn; + struct s3d_scene_view* scnview; + int mask; + + CHECK(s3d_scene_create(dev, &scn), RES_OK); + CHECK(s3d_scene_attach_shape(scn, cube), RES_OK); + CHECK(s3d_scene_attach_shape(scn, plane), RES_OK); + + CHECK(s3d_scene_view_create(NULL, 0, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_create(scn, 0, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_create(NULL, S3D_SAMPLE, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_create(NULL, 0, &scnview), RES_BAD_ARG); + CHECK(s3d_scene_view_create(scn, 0, &scnview), RES_BAD_ARG); + CHECK(s3d_scene_view_create(NULL, S3D_SAMPLE, &scnview), RES_BAD_ARG); + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); + + CHECK(s3d_scene_view_get_mask(NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_get_mask(scnview, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_get_mask(NULL, &mask), RES_BAD_ARG); + CHECK(s3d_scene_view_get_mask(scnview, &mask), RES_OK); + CHECK(mask, S3D_SAMPLE); + + CHECK(s3d_scene_view_ref_get(NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_ref_get(scnview), RES_OK); + CHECK(s3d_scene_view_ref_put(NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_TRACE|S3D_GET_PRIMITIVE, &scnview), RES_OK); + CHECK(s3d_scene_view_get_mask(scnview, &mask), RES_OK); + CHECK(mask & S3D_TRACE, S3D_TRACE); + CHECK(mask & S3D_GET_PRIMITIVE, S3D_GET_PRIMITIVE); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE|S3D_GET_PRIMITIVE, &scnview), RES_OK); + CHECK(s3d_scene_view_get_mask(scnview, &mask), RES_OK); + CHECK(mask & S3D_SAMPLE, S3D_SAMPLE); + CHECK(mask & S3D_GET_PRIMITIVE, S3D_GET_PRIMITIVE); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + + CHECK(s3d_scene_ref_put(scn), RES_OK); +} + +static void +test_trace_ray + (struct s3d_device* dev, + struct s3d_shape* cube, + struct s3d_shape* plane) +{ + struct s3d_scene* scn; + struct s3d_scene* scn2; + struct s3d_scene* scn3; + struct s3d_scene_view* scnview; + struct s3d_scene_view* scnview2; + struct s3d_scene_view* scnview3; + struct s3d_shape* inst0; + struct s3d_shape* inst1; + struct s3d_hit hit, hit2; + float org[3], dir[3], range[2]; + unsigned icube; + unsigned iplane; + unsigned iinst0; + unsigned iinst1; + + CHECK(s3d_shape_get_id(cube, &icube), RES_OK); + CHECK(s3d_shape_get_id(plane, &iplane), RES_OK); + + CHECK(s3d_scene_create(dev, &scn), RES_OK); + CHECK(s3d_scene_create(dev, &scn2), RES_OK); + CHECK(s3d_scene_create(dev, &scn3), RES_OK); + CHECK(s3d_scene_attach_shape(scn, plane), RES_OK); + CHECK(s3d_scene_attach_shape(scn, cube), RES_OK); + CHECK(s3d_scene_attach_shape(scn2, cube), RES_OK); + CHECK(s3d_scene_attach_shape(scn2, plane), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE|S3D_GET_PRIMITIVE, &scnview), RES_OK); + + f3(org, 0.5f, 0.25f, 0.25f); + f3(dir, 0.f, 0.f, 1.f); + f2(range, 0.f, FLT_MAX); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_BAD_OP); + + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview2), RES_OK); + CHECK(s3d_scene_view_trace_ray(scnview2, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 0); + CHECK(hit.prim.inst_id, S3D_INVALID_ID); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 0); + + f3(dir, 0.f, 0.f, -1.f); + CHECK(s3d_scene_view_trace_ray(scnview2, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 0); + CHECK(hit.prim.inst_id, S3D_INVALID_ID); + CHECK(hit.prim.geom_id, icube); + CHECK(hit.prim.prim_id, 0); + f3(dir, 0.f, 0.f, 1.f); + + CHECK(s3d_shape_enable(plane, 0), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview), RES_OK); + + CHECK(s3d_scene_view_trace_ray(scnview2, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 0); + CHECK(hit.prim.inst_id, S3D_INVALID_ID); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 0); + + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 0); + CHECK(hit.prim.inst_id, S3D_INVALID_ID); + CHECK(hit.prim.geom_id, icube); + CHECK(hit.prim.prim_id, 4); + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview2), RES_OK); + CHECK(s3d_shape_enable(plane, 1), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview), RES_OK); + CHECK(s3d_scene_view_create(scn2, S3D_TRACE, &scnview2), RES_OK); + + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(s3d_scene_view_trace_ray(scnview2, org, dir, range, NULL, &hit2), RES_OK); + CHECK(f3_eq(hit.normal, hit2.normal), 1); + CHECK(f2_eq(hit.uv, hit2.uv), 1); + CHECK(hit.distance, hit2.distance); + CHECK(S3D_PRIMITIVE_EQ(&hit.prim, &hit2.prim), 1); + CHECK(hit.prim.inst_id, S3D_INVALID_ID); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 0); + + CHECK(s3d_scene_detach_shape(scn2, plane), RES_OK); + CHECK(s3d_scene_view_trace_ray(scnview2, org, dir, range, NULL, &hit2), RES_OK); + CHECK(f3_eq(hit.normal, hit2.normal), 1); + CHECK(f2_eq(hit.uv, hit2.uv), 1); + CHECK(hit.distance, hit2.distance); + CHECK(S3D_PRIMITIVE_EQ(&hit.prim, &hit2.prim), 1); + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview2), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview), RES_OK); + CHECK(s3d_scene_view_create(scn2, S3D_TRACE, &scnview2), RES_OK); + + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(f3_eq(hit.normal, hit2.normal), 1); + CHECK(f2_eq(hit.uv, hit2.uv), 1); + CHECK(hit.distance, hit2.distance); + CHECK(S3D_PRIMITIVE_EQ(&hit.prim, &hit2.prim), 1); + CHECK(hit.prim.inst_id, S3D_INVALID_ID); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 0); + + CHECK(s3d_scene_view_trace_ray(scnview2, org, dir, range, NULL, &hit2), RES_OK); + CHECK(hit2.prim.inst_id, S3D_INVALID_ID); + CHECK(hit2.prim.geom_id, icube); + CHECK(hit2.prim.prim_id, 4); + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview2), RES_OK); + + CHECK(s3d_scene_instantiate(scn2, &inst0), RES_OK); + CHECK(s3d_scene_instantiate(scn2, &inst1), RES_OK); + CHECK(s3d_shape_get_id(inst0, &iinst0), RES_OK); + CHECK(s3d_shape_get_id(inst1, &iinst1), RES_OK); + CHECK(s3d_instance_translate + (inst0, S3D_WORLD_TRANSFORM, f3(org,-2.f, 0.f, 0.f)), RES_OK); + CHECK(s3d_instance_translate + (inst1, S3D_WORLD_TRANSFORM, f3(org, 2.f, 0.f, 0.f)), RES_OK); + + CHECK(s3d_scene_attach_shape(scn3, inst0), RES_OK); + CHECK(s3d_scene_attach_shape(scn3, inst1), RES_OK); + CHECK(s3d_scene_attach_shape(scn, inst0), RES_OK); + CHECK(s3d_scene_attach_shape(scn, inst1), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview), RES_OK); + CHECK(s3d_scene_view_create(scn3, S3D_TRACE, &scnview3), RES_OK); + + f3(org, 0.5f, 0.25f, 0.25f); + f3(dir, 0.f, 0.f, 1.f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, S3D_INVALID_ID); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 0); + + CHECK(s3d_scene_view_trace_ray(scnview3, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + + f3(org, -1.5f, 0.25f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, iinst0); + CHECK(hit.prim.geom_id, icube); + CHECK(hit.prim.prim_id, 4); + + CHECK(s3d_scene_view_trace_ray(scnview3, org, dir, range, NULL, &hit2), RES_OK); + CHECK(hit2.prim.inst_id, iinst0); + CHECK(hit2.prim.geom_id, icube); + CHECK(hit2.prim.prim_id, 4); + + CHECK(f3_eq(hit.normal, hit2.normal), 1); + CHECK(f2_eq(hit.uv, hit2.uv), 1); + CHECK(hit.distance, hit2.distance); + + CHECK(s3d_scene_clear(scn2), RES_OK); + + f3(org, 2.5f, 0.25f, 0.25f); + + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, iinst1); + CHECK(hit.prim.geom_id, icube); + CHECK(hit.prim.prim_id, 4); + + CHECK(s3d_scene_view_trace_ray(scnview3, org, dir, range, NULL, &hit2), RES_OK); + CHECK(hit2.prim.inst_id, iinst1); + CHECK(hit2.prim.geom_id, icube); + CHECK(hit2.prim.prim_id, 4); + + CHECK(f3_eq(hit.normal, hit2.normal), 1); + CHECK(f2_eq(hit.uv, hit2.uv), 1); + CHECK(hit.distance, hit2.distance); + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview), RES_OK); + + f3(org, -1.5f, 0.25f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + CHECK(s3d_scene_view_trace_ray(scnview3, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, iinst0); + CHECK(hit.prim.geom_id, icube); + CHECK(hit.prim.prim_id, 4); + + f3(org, 2.5f, 0.25f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + CHECK(s3d_scene_view_trace_ray(scnview3, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, iinst1); + CHECK(hit.prim.geom_id, icube); + CHECK(hit.prim.prim_id, 4); + + f3(org, 0.5f, 0.25f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, S3D_INVALID_ID); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 0); + CHECK(s3d_scene_view_trace_ray(scnview3, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + + CHECK(s3d_scene_attach_shape(scn2, plane), RES_OK); + CHECK(s3d_mesh_set_hit_filter_function(plane, filter, NULL), RES_OK); + + f3(org, 0.5f, 0.25f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, S3D_INVALID_ID); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 0); + + CHECK(s3d_scene_view_ref_put(scnview3), RES_OK); + CHECK(s3d_scene_view_create(scn3, S3D_TRACE, &scnview3), RES_OK); + + f3(org, -1.5f, 0.25f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + CHECK(s3d_scene_view_trace_ray(scnview3, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + + f3(org, -1.5f, 0.75f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + CHECK(s3d_scene_view_trace_ray(scnview3, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, iinst0); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 1); + + f3(org, 2.5f, 0.25f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + CHECK(s3d_scene_view_trace_ray(scnview3, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + + f3(org, 2.5f, 0.75f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + CHECK(s3d_scene_view_trace_ray(scnview3, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, iinst1); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 1); + + f3(org, 0.5f, 0.25f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, S3D_INVALID_ID); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 0); + CHECK(s3d_scene_view_trace_ray(scnview3, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + + f3(org, 0.5f, 0.75f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, S3D_INVALID_ID); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 1); + CHECK(s3d_scene_view_trace_ray(scnview3, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview), RES_OK); + f3(org, 0.5f, 0.25f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, S3D_INVALID_ID); + CHECK(hit.prim.geom_id, icube); + CHECK(hit.prim.prim_id, 4); + f3(org, 0.5f, 0.75f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, S3D_INVALID_ID); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 1); + + f3(org, -1.5f, 0.25f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + f3(org, -1.5f, 0.75f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, iinst0); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 1); + + f3(org, 2.5f, 0.25f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(S3D_HIT_NONE(&hit), 1); + f3(org, 2.5f, 0.75f, 0.25f); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(hit.prim.inst_id, iinst1); + CHECK(hit.prim.geom_id, iplane); + CHECK(hit.prim.prim_id, 1); + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview3), RES_OK); + + CHECK(s3d_shape_ref_put(inst0), RES_OK); + CHECK(s3d_shape_ref_put(inst1), RES_OK); + CHECK(s3d_scene_ref_put(scn), RES_OK); + CHECK(s3d_scene_ref_put(scn2), RES_OK); + CHECK(s3d_scene_ref_put(scn3), RES_OK); +} + +static void +test_sample + (struct s3d_device* dev, + struct s3d_shape* cube, + struct s3d_shape* plane) +{ + #define NSAMPS 512 + struct s3d_scene* scn; + struct s3d_scene* scn2; + struct s3d_scene* scn3; + struct s3d_scene_view* scnview; + struct s3d_scene_view* scnview3; + struct s3d_shape* inst0; + struct s3d_shape* inst1; + struct s3d_primitive prims[NSAMPS]; + float u, v, w, st[2]; + float pos[3]; + unsigned icube; + unsigned iplane; + unsigned iinst0; + unsigned iinst1; + int nsamps_cube; + int nsamps_plane; + int nsamps_inst0; + int nsamps_inst1; + int i; + + CHECK(s3d_scene_create(dev, &scn), RES_OK); + CHECK(s3d_scene_create(dev, &scn2), RES_OK); + CHECK(s3d_scene_create(dev, &scn3), RES_OK); + CHECK(s3d_scene_attach_shape(scn, cube), RES_OK); + CHECK(s3d_scene_attach_shape(scn, plane), RES_OK); + CHECK(s3d_shape_get_id(cube, &icube), RES_OK); + CHECK(s3d_shape_get_id(plane, &iplane), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview), RES_OK); + CHECK(s3d_scene_view_sample(scnview, 0.f, 0.f, 0.f, &prims[0], st), RES_BAD_OP); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); + CHECK(s3d_scene_view_sample(scnview, 0.f, 0.f, 0.f, &prims[0], st), RES_OK); + CHECK(prims[0].inst_id, S3D_INVALID_ID); + CHECK(prims[0].geom_id == icube || prims[0].geom_id == iplane, 1); + + nsamps_cube = 0; + nsamps_plane = 0; + srand(0); + FOR_EACH(i, 0, NSAMPS) { + u = rand_canonic(), v = rand_canonic(), w = rand_canonic(); + CHECK(s3d_scene_view_sample(scnview, u, v, w, &prims[i], st), RES_OK); + CHECK(prims[i].inst_id, S3D_INVALID_ID); + if(prims[i].geom_id == icube) { + ++nsamps_cube; + } else { + CHECK(prims[i].geom_id, iplane); + ++nsamps_plane; + } + } + NCHECK(nsamps_cube, 0); + NCHECK(nsamps_plane, 0); + + CHECK(s3d_shape_enable(cube, 0), RES_OK); + srand(0); + FOR_EACH(i, 0, NSAMPS) { + struct s3d_primitive prim; + u = rand_canonic(), v = rand_canonic(), w = rand_canonic(); + CHECK(s3d_scene_view_sample(scnview, u, v, w, &prim, st), RES_OK); + CHECK(S3D_PRIMITIVE_EQ(&prim, &prims[i]), 1); + } + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); + + srand(0); + FOR_EACH(i, 0, NSAMPS) { + u = rand_canonic(), v = rand_canonic(), w = rand_canonic(); + CHECK(s3d_scene_view_sample(scnview, u, v, w, &prims[i], st), RES_OK); + CHECK(prims[i].inst_id, S3D_INVALID_ID); + CHECK(prims[i].geom_id, iplane); + } + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + + CHECK(s3d_shape_enable(cube, 1), RES_OK); + CHECK(s3d_scene_attach_shape(scn2, cube), RES_OK); + CHECK(s3d_scene_instantiate(scn2, &inst0), RES_OK); + CHECK(s3d_scene_instantiate(scn2, &inst1), RES_OK); + CHECK(s3d_shape_get_id(inst0, &iinst0), RES_OK); + CHECK(s3d_shape_get_id(inst1, &iinst1), RES_OK); + CHECK(s3d_instance_translate + (inst0, S3D_WORLD_TRANSFORM, f3(pos,-2.f, 0.f, 0.f)), RES_OK); + CHECK(s3d_instance_translate + (inst1, S3D_WORLD_TRANSFORM, f3(pos, 2.f, 0.f, 0.f)), RES_OK); + + CHECK(s3d_scene_attach_shape(scn, inst0), RES_OK); + CHECK(s3d_scene_attach_shape(scn, inst1), RES_OK); + CHECK(s3d_scene_attach_shape(scn3, inst0), RES_OK); + CHECK(s3d_scene_attach_shape(scn3, inst1), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); + CHECK(s3d_scene_view_create(scn3, S3D_SAMPLE, &scnview3), RES_OK); + + CHECK(s3d_scene_detach_shape(scn2, cube), RES_OK); + + nsamps_cube = 0; + nsamps_inst0 = 0; + nsamps_inst1 = 0; + nsamps_plane = 0; + srand(0); + FOR_EACH(i, 0, NSAMPS) { + u = rand_canonic(), v = rand_canonic(), w = rand_canonic(); + CHECK(s3d_scene_view_sample(scnview, u, v, w, &prims[i], st), RES_OK); + if(prims[i].inst_id != S3D_INVALID_ID) { + CHECK(prims[i].geom_id, icube); + if(prims[i].inst_id == iinst0) { + ++nsamps_inst0; + } else { + CHECK(prims[i].inst_id, iinst1); + ++nsamps_inst1; + } + } else { + if(prims[i].geom_id == icube) { + ++nsamps_cube; + } else { + CHECK(prims[i].geom_id, iplane); + ++nsamps_plane; + } + } + + } + NCHECK(nsamps_cube, 0); + NCHECK(nsamps_inst0, 0); + NCHECK(nsamps_inst1, 0); + NCHECK(nsamps_plane, 0); + + nsamps_inst0 = 0; + nsamps_inst1 = 0; + srand(0); + FOR_EACH(i, 0, NSAMPS) { + u = rand_canonic(), v = rand_canonic(), w = rand_canonic(); + CHECK(s3d_scene_view_sample(scnview3, u, v, w, &prims[i], st), RES_OK); + CHECK(prims[i].geom_id, icube); + if(prims[i].inst_id == iinst0) { + ++nsamps_inst0; + } else { + CHECK(prims[i].inst_id, iinst1); + ++nsamps_inst1; + } + } + NCHECK(nsamps_inst0, 0); + NCHECK(nsamps_inst1, 0); + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); + + nsamps_cube = 0; + nsamps_plane = 0; + srand(0); + FOR_EACH(i, 0, NSAMPS) { + u = rand_canonic(), v = rand_canonic(), w = rand_canonic(); + CHECK(s3d_scene_view_sample(scnview, u, v, w, &prims[i], st), RES_OK); + CHECK(prims[i].inst_id, S3D_INVALID_ID); + if(prims[i].geom_id == icube) { + ++nsamps_cube; + } else { + CHECK(prims[i].geom_id, iplane); + ++nsamps_plane; + } + } + NCHECK(nsamps_cube, 0); + NCHECK(nsamps_plane, 0); + + nsamps_inst0 = 0; + nsamps_inst1 = 0; + srand(0); + FOR_EACH(i, 0, NSAMPS) { + u = rand_canonic(), v = rand_canonic(), w = rand_canonic(); + CHECK(s3d_scene_view_sample(scnview3, u, v, w, &prims[i], st), RES_OK); + CHECK(prims[i].geom_id, icube); + if(prims[i].inst_id == iinst0) { + ++nsamps_inst0; + } else { + CHECK(prims[i].inst_id, iinst1); + ++nsamps_inst1; + } + } + NCHECK(nsamps_inst0, 0); + NCHECK(nsamps_inst1, 0); + + CHECK(s3d_scene_attach_shape(scn2, plane), RES_OK); + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); + + FOR_EACH(i, 0, NSAMPS) { + u = rand_canonic(), v = rand_canonic(), w = rand_canonic(); + CHECK(s3d_scene_view_sample(scnview, u, v, w, &prims[i], st), RES_OK); + if(prims[i].inst_id != S3D_INVALID_ID) { + CHECK(prims[i].geom_id, iplane); + if(prims[i].inst_id == iinst0) { + ++nsamps_inst0; + } else { + CHECK(prims[i].inst_id, iinst1); + ++nsamps_inst1; + } + } else { + if(prims[i].geom_id == icube) { + ++nsamps_cube; + } else { + CHECK(prims[i].geom_id, iplane); + ++nsamps_plane; + } + } + + } + NCHECK(nsamps_cube, 0); + NCHECK(nsamps_inst0, 0); + NCHECK(nsamps_inst1, 0); + NCHECK(nsamps_plane, 0); + + nsamps_inst0 = 0; + nsamps_inst1 = 0; + srand(0); + FOR_EACH(i, 0, NSAMPS) { + u = rand_canonic(), v = rand_canonic(), w = rand_canonic(); + CHECK(s3d_scene_view_sample(scnview3, u, v, w, &prims[i], st), RES_OK); + CHECK(prims[i].geom_id, icube); + if(prims[i].inst_id == iinst0) { + ++nsamps_inst0; + } else { + CHECK(prims[i].inst_id, iinst1); + ++nsamps_inst1; + } + } + NCHECK(nsamps_inst0, 0); + NCHECK(nsamps_inst1, 0); + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview3), RES_OK); + + CHECK(s3d_scene_view_create(scn3, S3D_SAMPLE, &scnview3), RES_OK); + nsamps_inst0 = 0; + nsamps_inst1 = 0; + srand(0); + FOR_EACH(i, 0, NSAMPS) { + u = rand_canonic(), v = rand_canonic(), w = rand_canonic(); + CHECK(s3d_scene_view_sample(scnview3, u, v, w, &prims[i], st), RES_OK); + CHECK(prims[i].geom_id, iplane); + if(prims[i].inst_id == iinst0) { + ++nsamps_inst0; + } else { + CHECK(prims[i].inst_id, iinst1); + ++nsamps_inst1; + } + } + NCHECK(nsamps_inst0, 0); + NCHECK(nsamps_inst1, 0); + + CHECK(s3d_scene_view_ref_put(scnview3), RES_OK); + + CHECK(s3d_scene_ref_put(scn), RES_OK); + CHECK(s3d_scene_ref_put(scn2), RES_OK); + CHECK(s3d_scene_ref_put(scn3), RES_OK); + CHECK(s3d_shape_ref_put(inst0), RES_OK); + CHECK(s3d_shape_ref_put(inst1), RES_OK); +} + +static void +test_get_primitive + (struct s3d_device* dev, + struct s3d_shape* cube, + struct s3d_shape* plane) +{ + struct s3d_scene* scn; + struct s3d_scene* scn2; + struct s3d_scene* scn3; + struct s3d_scene_view* scnview; + struct s3d_scene_view* scnview3; + struct s3d_shape* inst0; + struct s3d_shape* inst1; + struct s3d_primitive prim; + size_t nprims; + unsigned i; + unsigned icube; + unsigned iplane; + unsigned iinst0; + unsigned iinst1; + float pos[3]; + int cube_prims[12]; + int plane_prims[2]; + int inst0_prims[12]; + int inst1_prims[12]; + + CHECK(s3d_scene_create(dev, &scn), RES_OK); + CHECK(s3d_scene_create(dev, &scn2), RES_OK); + CHECK(s3d_scene_create(dev, &scn3), RES_OK); + CHECK(s3d_scene_attach_shape(scn, cube), RES_OK); + CHECK(s3d_scene_attach_shape(scn, plane), RES_OK); + CHECK(s3d_shape_get_id(cube, &icube), RES_OK); + CHECK(s3d_shape_get_id(plane, &iplane), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview), RES_OK); + CHECK(s3d_scene_view_get_primitive(scnview, 0, &prim), RES_BAD_OP); + CHECK(s3d_scene_view_primitives_count(scnview, &nprims), RES_OK); + CHECK(nprims, 14); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_GET_PRIMITIVE, &scnview), RES_OK); + CHECK(s3d_scene_view_primitives_count(scnview, &nprims), RES_OK); + CHECK(nprims, 14); + + memset(cube_prims, 0, sizeof(cube_prims)); + memset(plane_prims, 0, sizeof(plane_prims)); + FOR_EACH(i, 0, nprims) { + CHECK(s3d_scene_view_get_primitive(scnview, i, &prim), RES_OK); + CHECK(prim.inst_id, S3D_INVALID_ID); + CHECK(prim.scene_prim_id, i); + if(prim.geom_id == icube) { + cube_prims[prim.prim_id] = 1; + } else { + CHECK(prim.geom_id, iplane); + plane_prims[prim.prim_id] = 1; + } + } + FOR_EACH(i, 0, 12) CHECK(cube_prims[i], 1); + FOR_EACH(i, 0, 2) CHECK(plane_prims[i], 1); + + CHECK(s3d_scene_detach_shape(scn, cube), RES_OK); + CHECK(s3d_scene_view_primitives_count(scnview, &nprims), RES_OK); + CHECK(nprims, 14); + memset(cube_prims, 0, sizeof(cube_prims)); + memset(plane_prims, 0, sizeof(plane_prims)); + FOR_EACH(i, 0, nprims) { + CHECK(s3d_scene_view_get_primitive(scnview, i, &prim), RES_OK); + CHECK(prim.inst_id, S3D_INVALID_ID); + CHECK(prim.scene_prim_id, i); + if(prim.geom_id == icube) { + cube_prims[prim.prim_id] = 1; + } else { + CHECK(prim.geom_id, iplane); + plane_prims[prim.prim_id] = 1; + } + } + FOR_EACH(i, 0, 12) CHECK(cube_prims[i], 1); + FOR_EACH(i, 0, 2) CHECK(plane_prims[i], 1); + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_GET_PRIMITIVE, &scnview), RES_OK); + CHECK(s3d_scene_view_primitives_count(scnview, &nprims), RES_OK); + CHECK(nprims, 2); + memset(plane_prims, 0, sizeof(plane_prims)); + FOR_EACH(i, 0, nprims) { + CHECK(s3d_scene_view_get_primitive(scnview, i, &prim), RES_OK); + CHECK(prim.inst_id, S3D_INVALID_ID); + CHECK(prim.scene_prim_id, i); + CHECK(prim.geom_id, iplane); + plane_prims[prim.prim_id] = 1; + } + FOR_EACH(i, 0, 2) CHECK(plane_prims[i], 1); + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + + CHECK(s3d_scene_attach_shape(scn, cube), RES_OK); + + CHECK(s3d_scene_attach_shape(scn2, plane), RES_OK); + CHECK(s3d_scene_instantiate(scn2, &inst0), RES_OK); + CHECK(s3d_scene_instantiate(scn2, &inst1), RES_OK); + CHECK(s3d_shape_get_id(inst0, &iinst0), RES_OK); + CHECK(s3d_shape_get_id(inst1, &iinst1), RES_OK); + CHECK(s3d_instance_translate + (inst0, S3D_WORLD_TRANSFORM, f3(pos,-2.f, 0.f, 0.f)), RES_OK); + CHECK(s3d_instance_translate + (inst1, S3D_WORLD_TRANSFORM, f3(pos, 2.f, 0.f, 0.f)), RES_OK); + + CHECK(s3d_scene_attach_shape(scn, inst0), RES_OK); + CHECK(s3d_scene_attach_shape(scn3, inst0), RES_OK); + CHECK(s3d_scene_attach_shape(scn3, inst1), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_GET_PRIMITIVE, &scnview), RES_OK); + CHECK(s3d_scene_view_create(scn3, S3D_GET_PRIMITIVE, &scnview3), RES_OK); + + CHECK(s3d_scene_clear(scn2), RES_OK); + + CHECK(s3d_scene_view_primitives_count(scnview, &nprims), RES_OK); + CHECK(nprims, 16); + memset(plane_prims, 0, sizeof(plane_prims)); + memset(cube_prims, 0, sizeof(cube_prims)); + memset(inst0_prims, 0, sizeof(inst0_prims)); + FOR_EACH(i, 0, nprims) { + CHECK(s3d_scene_view_get_primitive(scnview, i, &prim), RES_OK); + if(prim.inst_id != S3D_INVALID_ID) { + CHECK(prim.inst_id, iinst0); + CHECK(prim.geom_id, iplane); + inst0_prims[prim.prim_id] = 1; + } else { + if(prim.geom_id == icube) { + cube_prims[prim.prim_id] = 1; + } else { + CHECK(prim.geom_id, iplane); + plane_prims[prim.prim_id] = 1; + } + } + } + FOR_EACH(i, 0, 12) CHECK(cube_prims[i], 1); + FOR_EACH(i, 0, 2) CHECK(plane_prims[i], 1); + FOR_EACH(i, 0, 2) CHECK(inst0_prims[i], 1); + + CHECK(s3d_scene_view_primitives_count(scnview3, &nprims), RES_OK); + CHECK(nprims, 4); + memset(inst0_prims, 0, sizeof(inst0_prims)); + memset(inst1_prims, 0, sizeof(inst1_prims)); + FOR_EACH(i, 0, nprims) { + CHECK(s3d_scene_view_get_primitive(scnview3, i, &prim), RES_OK); + NCHECK(prim.inst_id, S3D_INVALID_ID); + CHECK(prim.geom_id, iplane); + if(prim.inst_id == iinst0) { + inst0_prims[prim.prim_id] = 1; + } else { + CHECK(prim.inst_id, iinst1); + inst1_prims[prim.prim_id] = 1; + } + } + FOR_EACH(i, 0, 2) CHECK(inst1_prims[i], 1); + FOR_EACH(i, 0, 2) CHECK(inst1_prims[i], 1); + + CHECK(s3d_scene_view_ref_put(scnview3), RES_OK); + + CHECK(s3d_scene_view_create(scn3, S3D_GET_PRIMITIVE, &scnview3), RES_OK); + CHECK(s3d_scene_view_primitives_count(scnview3, &nprims), RES_OK); + CHECK(nprims, 0); + CHECK(s3d_scene_view_ref_put(scnview3), RES_OK); + + CHECK(s3d_scene_attach_shape(scn2, cube), RES_OK); + CHECK(s3d_scene_view_create(scn3, S3D_GET_PRIMITIVE, &scnview3), RES_OK); + + CHECK(s3d_scene_view_primitives_count(scnview, &nprims), RES_OK); + CHECK(nprims, 16); + memset(plane_prims, 0, sizeof(plane_prims)); + memset(cube_prims, 0, sizeof(cube_prims)); + memset(inst0_prims, 0, sizeof(inst0_prims)); + FOR_EACH(i, 0, nprims) { + CHECK(s3d_scene_view_get_primitive(scnview, i, &prim), RES_OK); + if(prim.inst_id != S3D_INVALID_ID) { + CHECK(prim.inst_id, iinst0); + CHECK(prim.geom_id, iplane); + inst0_prims[prim.prim_id] = 1; + } else { + if(prim.geom_id == icube) { + cube_prims[prim.prim_id] = 1; + } else { + CHECK(prim.geom_id, iplane); + plane_prims[prim.prim_id] = 1; + } + } + } + FOR_EACH(i, 0, 12) CHECK(cube_prims[i], 1); + FOR_EACH(i, 0, 2) CHECK(plane_prims[i], 1); + FOR_EACH(i, 0, 2) CHECK(inst0_prims[i], 1); + + CHECK(s3d_scene_view_primitives_count(scnview3, &nprims), RES_OK); + CHECK(nprims, 24); + memset(inst0_prims, 0, sizeof(inst0_prims)); + memset(inst1_prims, 0, sizeof(inst1_prims)); + FOR_EACH(i, 0, nprims) { + CHECK(s3d_scene_view_get_primitive(scnview3, i, &prim), RES_OK); + NCHECK(prim.inst_id, S3D_INVALID_ID); + CHECK(prim.geom_id, icube); + if(prim.inst_id == iinst0) { + inst0_prims[prim.prim_id] = 1; + } else { + CHECK(prim.inst_id, iinst1); + inst1_prims[prim.prim_id] = 1; + } + } + FOR_EACH(i, 0, 12) CHECK(inst1_prims[i], 1); + FOR_EACH(i, 0, 12) CHECK(inst1_prims[i], 1); + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview3), RES_OK); + + CHECK(s3d_scene_view_create(scn, S3D_GET_PRIMITIVE, &scnview), RES_OK); + + CHECK(s3d_scene_view_primitives_count(scnview, &nprims), RES_OK); + CHECK(nprims, 26); + memset(plane_prims, 0, sizeof(plane_prims)); + memset(cube_prims, 0, sizeof(cube_prims)); + memset(inst0_prims, 0, sizeof(inst0_prims)); + FOR_EACH(i, 0, nprims) { + CHECK(s3d_scene_view_get_primitive(scnview, i, &prim), RES_OK); + if(prim.inst_id != S3D_INVALID_ID) { + CHECK(prim.inst_id, iinst0); + CHECK(prim.geom_id, icube); + inst0_prims[prim.prim_id] = 1; + } else { + if(prim.geom_id == icube) { + cube_prims[prim.prim_id] = 1; + } else { + CHECK(prim.geom_id, iplane); + plane_prims[prim.prim_id] = 1; + } + } + } + FOR_EACH(i, 0, 12) CHECK(cube_prims[i], 1); + FOR_EACH(i, 0, 2) CHECK(plane_prims[i], 1); + FOR_EACH(i, 0, 12) CHECK(inst0_prims[i], 1); + + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + + CHECK(s3d_shape_ref_put(inst0), RES_OK); + CHECK(s3d_shape_ref_put(inst1), RES_OK); + CHECK(s3d_scene_ref_put(scn), RES_OK); + CHECK(s3d_scene_ref_put(scn2), RES_OK); + CHECK(s3d_scene_ref_put(scn3), RES_OK); +} + +/******************************************************************************* + * Main test function + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct mesh_context ctx; + struct s3d_device* dev; + struct s3d_shape* cube; + struct s3d_shape* plane; + struct s3d_vertex_data vdata; + (void)argc, (void)argv; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(s3d_device_create(NULL, &allocator, 1, &dev), RES_OK); + + vdata.type = S3D_FLOAT3; + vdata.usage = S3D_POSITION; + vdata.get = get_pos; + + ctx.ids = cube_ids; + ctx.verts = cube_verts; + CHECK(s3d_shape_create_mesh(dev, &cube), RES_OK); + CHECK(s3d_mesh_setup_indexed_vertices + (cube, cube_ntris, get_ids, cube_nverts, &vdata, 1, &ctx), RES_OK); + + ctx.ids = plane_ids; + ctx.verts = plane_verts; + CHECK(s3d_shape_create_mesh(dev, &plane), RES_OK); + CHECK(s3d_mesh_setup_indexed_vertices + (plane, plane_ntris, get_ids, plane_nverts, &vdata, 1, &ctx), RES_OK); + + test_miscellaneous(dev, cube, plane); + test_trace_ray(dev, cube, plane); + test_sample(dev, cube, plane); + test_get_primitive(dev, cube, plane); + + CHECK(s3d_shape_ref_put(cube), RES_OK); + CHECK(s3d_shape_ref_put(plane), RES_OK); + CHECK(s3d_device_ref_put(dev), RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + return 0; +} + diff --git a/src/test_s3d_seams.c b/src/test_s3d_seams.c @@ -0,0 +1,192 @@ +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to describe a + * virtual 3D environment that can be ray-traced and sampled both robustly + * and efficiently. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "s3d.h" +#include "test_s3d_utils.h" + +#include <rsys/mem_allocator.h> +#include <rsys/float33.h> +#include <rsys/double33.h> + +struct desc { + const float* vertices; + const unsigned* indices; +}; + +/******************************************************************************* + * Callbacks + ******************************************************************************/ +static INLINE void +get_ids(const unsigned itri, unsigned ids[3], void* data) +{ + const unsigned id = itri * 3; + struct desc* desc = data; + NCHECK(desc, NULL); + NCHECK(ids, NULL); + ids[0] = desc->indices[id + 0]; + ids[1] = desc->indices[id + 1]; + ids[2] = desc->indices[id + 2]; +} + +static INLINE void +get_position(const unsigned ivert, float position[3], void* data) +{ + struct desc* desc = data; + NCHECK(desc, NULL); + NCHECK(position, NULL); + position[0] = desc->vertices[ivert * 3 + 0]; + position[1] = desc->vertices[ivert * 3 + 1]; + position[2] = desc->vertices[ivert * 3 + 2]; +} + +static INLINE void +get_normal(const unsigned ivert, float normal[3], void* data) +{ + (void) ivert, (void) data; + NCHECK(normal, NULL); + normal[0] = 1.f; + normal[1] = 0.f; + normal[2] = 0.f; +} + +static INLINE void +get_uv(const unsigned ivert, float uv[2], void* data) +{ + (void) ivert, (void) data; + NCHECK(uv, NULL); + uv[0] = -1.f; + uv[1] = 1.f; +} + +static INLINE void +get_polygon_vertices(const size_t ivert, double position[2], void* ctx) +{ + const double* verts = ctx; + NCHECK(position, NULL); + NCHECK(ctx, NULL); + position[0] = verts[ivert * 2 + 0]; + position[1] = verts[ivert * 2 + 1]; +} + +static const float SQUARE_EDGES__ [] = { + -0.1f, -0.1f, 0.f, + 0.1f, -0.1f, 0.f, + 0.1f, 0.1f, 0.f, + -0.1f, 0.1f, 0.f +}; +static const unsigned SQUARE_NVERTS__ = sizeof(SQUARE_EDGES__) / sizeof(float[3]); +static const unsigned SQUARE_TRG_IDS__ [] = { 0, 2, 1, 2, 0, 3 }; +static const unsigned SQUARE_NTRIS__ = sizeof(SQUARE_TRG_IDS__) / sizeof(unsigned[3]); +static const struct desc SQUARE_DESC__ = { SQUARE_EDGES__, SQUARE_TRG_IDS__ }; + +static int +check_ray(int use_double) +{ + struct mem_allocator allocator; + struct s3d_device* dev; + struct s3d_hit hit; + struct s3d_scene* scn; + struct s3d_scene* scn2; + struct s3d_scene_view* scnview; + struct s3d_shape* square; + struct s3d_shape* inst; + struct s3d_vertex_data attribs; + float transformf[12]; + double transform[12]; + float range[2] = { 0.f, FLT_MAX }; + float org[3] = { + 3.3492994308471680f, -9.7470426559448242f, 2.6555661803570274f + }; + float dir[3] = { + -0.26465030351986046f, 0.77017831656345948f, 0.58033229924097962f + }; + float pos[3]; + + if(use_double) { + d33_rotation_pitch(transform, PI); + f33_set_d33(transformf, transform); + } else { + f33_rotation_pitch(transformf, (float)PI); + } + f3_splat(transformf + 9, 0); + transformf[11] = 10; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(s3d_device_create(NULL, &allocator, 1, &dev), RES_OK); + CHECK(s3d_scene_create(dev, &scn), RES_OK); + CHECK(s3d_scene_create(dev, &scn2), RES_OK); + + attribs.usage = S3D_POSITION; + attribs.type = S3D_FLOAT3; + attribs.get = get_position; + + CHECK(s3d_shape_create_mesh(dev, &square), RES_OK); + CHECK(s3d_mesh_setup_indexed_vertices(square, SQUARE_NTRIS__, get_ids, + SQUARE_NVERTS__, &attribs, 1, (void*) &SQUARE_DESC__), RES_OK); + CHECK(s3d_scene_attach_shape(scn, square), RES_OK); + s3d_scene_instantiate(scn, &inst); + CHECK(s3d_instance_set_transform(inst, transformf), RES_OK); + CHECK(s3d_scene_attach_shape(scn2, inst), RES_OK); + + CHECK(s3d_scene_view_create(scn2, S3D_TRACE, &scnview), RES_OK); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + printf("\nRaytrace using %s: ", use_double ? "double" : "float"); + if (!S3D_HIT_NONE(&hit)) { + f3_add(pos, org, f3_mulf(pos, dir, hit.distance)); + printf("Hit at [%g %g %g]\n",SPLIT3(pos)); + } else { + printf("No hit\n"); + } + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(s3d_scene_ref_put(scn), RES_OK); + CHECK(s3d_scene_ref_put(scn2), RES_OK); + CHECK(s3d_device_ref_put(dev), RES_OK); + CHECK(s3d_shape_ref_put(square), RES_OK); + CHECK(s3d_shape_ref_put(inst), RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + + return S3D_HIT_NONE(&hit) ? RES_UNKNOWN_ERR : RES_OK; +} + +int +main(int argc, char** argv) +{ + (void)argc, (void)argv; + CHECK(check_ray(1), RES_OK); + CHECK(check_ray(0), RES_OK); + return 0; +} + diff --git a/src/test_s3d_shape.c b/src/test_s3d_shape.c @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -87,25 +87,15 @@ main(int argc, char** argv) CHECK(s3d_shape_get_id(shape, &id), RES_OK); NCHECK(id, S3D_INVALID_ID); - CHECK(s3d_shape_is_attached(NULL, NULL), RES_BAD_ARG); - CHECK(s3d_shape_is_attached(shape, NULL), RES_BAD_ARG); - CHECK(s3d_shape_is_attached(NULL, &c), RES_BAD_ARG); - CHECK(s3d_shape_is_attached(shape, &c), RES_OK); - CHECK(c, 0); - CHECK(s3d_scene_attach_shape(NULL, NULL), RES_BAD_ARG); CHECK(s3d_scene_attach_shape(scn, NULL), RES_BAD_ARG); CHECK(s3d_scene_attach_shape(NULL, shape), RES_BAD_ARG); CHECK(s3d_scene_attach_shape(scn, shape), RES_OK); - CHECK(s3d_shape_is_attached(shape, &c), RES_OK); - NCHECK(c, 0); CHECK(s3d_scene_detach_shape(NULL, NULL), RES_BAD_ARG); CHECK(s3d_scene_detach_shape(scn, NULL), RES_BAD_ARG); CHECK(s3d_scene_detach_shape(NULL, shape), RES_BAD_ARG); CHECK(s3d_scene_detach_shape(scn, shape), RES_OK); - CHECK(s3d_shape_is_attached(shape, &c), RES_OK); - CHECK(c, 0); attribs[0].type = S3D_FLOAT3; attribs[0].usage = S3D_POSITION; diff --git a/src/test_s3d_trace_ray.c b/src/test_s3d_trace_ray.c @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly @@ -31,6 +31,7 @@ * knowledge of the CeCILL license and that you accept its terms. */ #include "s3d.h" +#include "test_s3d_camera.h" #include "test_s3d_cbox.h" #include "test_s3d_utils.h" @@ -42,47 +43,6 @@ #define IMG_WIDTH 640 #define IMG_HEIGHT 480 -struct camera { - float pos[3]; - float x[3], y[3], z[3]; /* Basis */ -}; - -static void -camera_init(struct camera* cam) -{ - const float pos[3] = { 178.f, -1000.f, 273.f }; - const float tgt[3] = { 178.f, 0.f, 273.f }; - const float up[3] = { 0.f, 0.f, 1.f }; - const float proj_ratio = (float)IMG_WIDTH/(float)IMG_HEIGHT; - const float fov_x = (float)PI * 0.25f; - float f = 0.f; - ASSERT(cam); - - f3_set(cam->pos, pos); - f = f3_normalize(cam->z, f3_sub(cam->z, tgt, pos)); NCHECK(f, 0); - f = f3_normalize(cam->x, f3_cross(cam->x, cam->z, up)); NCHECK(f, 0); - f = f3_normalize(cam->y, f3_cross(cam->y, cam->z, cam->x)); NCHECK(f, 0); - f3_divf(cam->z, cam->z, (float)tan(fov_x*0.5f)); - f3_divf(cam->y, cam->y, proj_ratio); -} - -static void -camera_ray - (const struct camera* cam, - const float pixel[2], - float org[3], - float dir[3]) -{ - float x[3], y[3], f; - ASSERT(cam && pixel && org && dir); - - f3_mulf(x, cam->x, pixel[0]*2.f - 1.f); - f3_mulf(y, cam->y, pixel[1]*2.f - 1.f); - f3_add(dir, f3_add(dir, x, y), cam->z); - f = f3_normalize(dir, dir); NCHECK(f, 0); - f3_set(org, cam->pos); -} - static int filter_func (const struct s3d_hit* hit, @@ -104,10 +64,12 @@ int main(int argc, char** argv) { struct mem_allocator allocator; + struct image img; struct s3d_device* dev; struct s3d_hit hit; struct s3d_scene* scn; struct s3d_scene* scn2; + struct s3d_scene_view* scnview; struct s3d_shape* inst; struct s3d_shape* walls; struct s3d_shape* walls_copy; @@ -117,13 +79,13 @@ main(int argc, char** argv) struct s3d_primitive prims[30]; struct camera cam; struct cbox_desc desc; - unsigned char* img = NULL; unsigned ntris, nverts; size_t nprims; size_t ix, iy; float transform[12]; float vec[3]; float lower[3], upper[3]; + float pos[3], tgt[3], up[3]; float org[3] = { 0.f, 0.f, 0.f }; float dir[3] = { 0.f, 1.f, 0.f }; float range[2] = { 0.f, FLT_MAX }; @@ -133,26 +95,18 @@ main(int argc, char** argv) unsigned short_block_id; size_t i; char filter = 0; - char* img_name = NULL; mem_init_proxy_allocator(&allocator, &mem_default_allocator); - if(argc > 1) { - if(!strcmp(argv[1], "filter")) { - filter = 1; - } else { - img_name = argv[1]; - } - } - if(!img_name && argc > 2) { - img_name = argv[2]; - } - if(img_name) { - img = MEM_ALLOC(&allocator, 3 * IMG_WIDTH * IMG_HEIGHT); - NCHECK(img, NULL); + if(argc > 1 && !strcmp(argv[1], "filter")) { + filter = 1; } - CHECK(s3d_device_create(NULL, &allocator, 1, &dev), RES_OK); + image_init(&allocator, &img); + CHECK(image_setup + (&img, IMG_WIDTH, IMG_HEIGHT, IMG_WIDTH*3, IMAGE_RGB8, NULL), RES_OK); + + CHECK(s3d_device_create(NULL, &allocator, 0, &dev), RES_OK); CHECK(s3d_scene_create(dev, &scn), RES_OK); attribs.usage = S3D_POSITION; @@ -169,46 +123,45 @@ main(int argc, char** argv) CHECK(s3d_scene_attach_shape(scn, walls), RES_OK); CHECK(s3d_shape_ref_put(walls), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_TRACE), RES_OK); - CHECK(s3d_scene_trace_ray(NULL, NULL, NULL, NULL, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, NULL, NULL, NULL, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, org, NULL, NULL, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, org, NULL, NULL, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, NULL, dir, NULL, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, NULL, dir, NULL, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, org, dir, NULL, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, org, dir, NULL, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, NULL, NULL, range, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, NULL, NULL, range, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, org, NULL, range, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, org, NULL, range, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, NULL, dir, range, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, NULL, dir, range, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, org, dir, range, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, org, dir, range, NULL, NULL), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, NULL, NULL, NULL, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, NULL, NULL, NULL, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, org, NULL, NULL, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, org, NULL, NULL, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, NULL, dir, NULL, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, NULL, dir, NULL, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, org, dir, NULL, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, org, dir, NULL, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, NULL, NULL, range, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, NULL, NULL, range, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, org, NULL, range, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, org, NULL, range, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, NULL, dir, range, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, NULL, dir, range, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(NULL, org, dir, range, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_trace_ray(scn, org, dir, range, NULL, &hit), RES_OK); - CHECK(s3d_scene_trace_ray(scn, org, dir, range, NULL, &hit), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &scnview), RES_OK); + CHECK(s3d_scene_view_trace_ray(NULL, NULL, NULL, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, NULL, NULL, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, org, NULL, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, org, NULL, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, NULL, dir, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, NULL, dir, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, org, dir, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, NULL, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, NULL, NULL, range, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, NULL, NULL, range, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, org, NULL, range, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, org, NULL, range, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, NULL, dir, range, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, NULL, dir, range, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, org, dir, range, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, NULL), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, NULL, NULL, NULL, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, NULL, NULL, NULL, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, org, NULL, NULL, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, org, NULL, NULL, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, NULL, dir, NULL, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, NULL, dir, NULL, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, org, dir, NULL, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, NULL, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, NULL, NULL, range, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, NULL, NULL, range, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, org, NULL, range, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, org, NULL, range, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, NULL, dir, range, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, NULL, dir, range, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(NULL, org, dir, range, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_OK); f3(dir, 1.f, 1.f, 1.f); - CHECK(s3d_scene_trace_ray(scn, org, dir, range, NULL, &hit), RES_BAD_ARG); - CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_view_trace_ray(scnview, org, dir, range, NULL, &hit), RES_BAD_ARG); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); f3(dir, 0.f, 1.f, 0.f); - CHECK(s3d_scene_trace_ray(scn, org, dir, range, NULL, &hit), RES_BAD_OP); CHECK(s3d_scene_clear(scn), RES_OK); /* Update the inst with the CBox tall block mesh */ @@ -237,10 +190,10 @@ main(int argc, char** argv) /* Instantiate the scene */ CHECK(s3d_scene_instantiate(scn, &inst), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_SAMPLE), RES_OK); - CHECK(s3d_scene_primitives_count(scn, &nprims), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_SAMPLE, &scnview), RES_OK); + CHECK(s3d_scene_view_primitives_count(scnview, &nprims), RES_OK); CHECK(nprims, 20); - CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); CHECK(s3d_shape_get_id(inst, &inst_id), RES_OK); /* Create the CBox walls */ @@ -266,21 +219,12 @@ main(int argc, char** argv) CHECK(s3d_instance_set_position(inst, org), RES_OK); CHECK(s3d_shape_enable(inst, 0), RES_OK); + CHECK(s3d_scene_view_create(scn2, S3D_TRACE|S3D_SAMPLE, &scnview), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_TRACE), RES_OK); - CHECK(s3d_scene_begin_session(scn2, S3D_TRACE), RES_BAD_OP); - CHECK(s3d_scene_end_session(scn), RES_OK); - CHECK(s3d_scene_begin_session(scn2, S3D_TRACE), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_TRACE), RES_BAD_OP); - CHECK(s3d_scene_end_session(scn), RES_BAD_OP); - - CHECK(s3d_scene_clear(scn2), RES_BAD_OP); - CHECK(s3d_scene_clear(scn), RES_BAD_OP); - - CHECK(s3d_scene_end_session(scn2), RES_OK); CHECK(s3d_shape_enable(inst, 1), RES_OK); - CHECK(s3d_scene_begin_session(scn2, S3D_TRACE), RES_OK); - CHECK(s3d_scene_end_session(scn2), RES_OK); + CHECK(s3d_scene_view_create(scn2, S3D_TRACE, &scnview), RES_OK); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); CHECK(s3d_shape_create_mesh(dev, &walls_copy), RES_OK); CHECK(s3d_mesh_copy(walls, walls_copy), RES_OK); @@ -296,11 +240,11 @@ main(int argc, char** argv) CHECK(s3d_scene_attach_shape(scn, short_block), RES_OK); CHECK(s3d_scene_attach_shape(scn, tall_block), RES_OK); - CHECK(s3d_scene_begin_session(scn2, S3D_TRACE|S3D_GET_PRIMITIVE), RES_OK); - CHECK(s3d_scene_primitives_count(scn2, &nprims), RES_OK); + CHECK(s3d_scene_view_create(scn2, S3D_TRACE|S3D_GET_PRIMITIVE, &scnview), RES_OK); + CHECK(s3d_scene_view_primitives_count(scnview, &nprims), RES_OK); CHECK(nprims, 30); - CHECK(s3d_scene_get_aabb(scn2, lower, upper), RES_OK); + CHECK(s3d_scene_view_get_aabb(scnview, lower, upper), RES_OK); CHECK(eq_epsf(lower[0], -100.f, 1.e-6f), 1); CHECK(eq_epsf(lower[1], 0.f, 1.e-6f), 1); CHECK(eq_epsf(lower[2], -2.f, 1.e-6f), 1); @@ -310,7 +254,7 @@ main(int argc, char** argv) FOR_EACH(i, 0, nprims) { size_t j; - CHECK(s3d_scene_get_primitive(scn2, (unsigned)i, prims + i), RES_OK); + CHECK(s3d_scene_view_get_primitive(scnview, (unsigned)i, prims + i), RES_OK); CHECK(S3D_PRIMITIVE_EQ(prims + i, &S3D_PRIMITIVE_NULL), 0); FOR_EACH(j, 0, i) { CHECK(S3D_PRIMITIVE_EQ(prims + i, prims + j), 0); @@ -323,7 +267,11 @@ main(int argc, char** argv) CHECK(f3_eq(transform + 9, f3(vec, -100.f, 0.f, -2.f)), 1); } - camera_init(&cam); + f3(pos, 178.f, -1000.f, 273.f); + f3(tgt, 178.f, 0.f, 273.f); + f3(up, 0.f, 0.f, 1.f); + camera_init(&cam, pos, tgt, up, (float)PI*0.25f, + (float)IMG_WIDTH/(float)IMG_HEIGHT); FOR_EACH(iy, 0, IMG_HEIGHT) { float pixel[2]; @@ -333,17 +281,16 @@ main(int argc, char** argv) pixel[0] = (float)ix/(float)IMG_WIDTH; camera_ray(&cam, pixel, org, dir); - CHECK(s3d_scene_trace_ray - (scn2, org, dir, range, (void*)(uintptr_t)0xDEADBEEF, &hit), RES_OK); + CHECK(s3d_scene_view_trace_ray + (scnview, org, dir, range, (void*)(uintptr_t)0xDEADBEEF, &hit), RES_OK); if(S3D_HIT_NONE(&hit)) { - if(img) { - img[ipix+0] = img[ipix+1] = img[ipix+2] = 0; - } + ((uint8_t*)img.pixels)[ipix+0] = 0; + ((uint8_t*)img.pixels)[ipix+1] = 0; + ((uint8_t*)img.pixels)[ipix+2] = 0; } else { float N[3], len, dot, col[3] = { 1.f, 1.f, 1.f }; struct s3d_attrib attr; - float pos[3]; CHECK(hit.prim.inst_id, inst_id); CHECK(hit.prim.geom_id == walls_id @@ -378,9 +325,6 @@ main(int argc, char** argv) CHECK(hit.prim.scene_prim_id >= hit.prim.prim_id, 1); CHECK(hit.prim.scene_prim_id < 30, 1); - if(!img) - continue; - if(hit.prim.geom_id == walls_id) { if(hit.prim.prim_id == 4 || hit.prim.prim_id == 5) { col[0] = 1.f, col[1] = 0.f, col[2] = 0.f; @@ -393,21 +337,17 @@ main(int argc, char** argv) if(dot < 0.f) dot = f3_dot(f3_minus(N, N), dir); - img[ipix+0] = (unsigned char)(dot * col[0] * 255.f); - img[ipix+1] = (unsigned char)(dot * col[1] * 255.f); - img[ipix+2] = (unsigned char)(dot * col[2] * 255.f); + ((uint8_t*)img.pixels)[ipix+0] = (unsigned char)(dot * col[0] * 255.f); + ((uint8_t*)img.pixels)[ipix+1] = (unsigned char)(dot * col[1] * 255.f); + ((uint8_t*)img.pixels)[ipix+2] = (unsigned char)(dot * col[2] * 255.f); } } } - CHECK(s3d_scene_end_session(scn2), RES_OK); - - if(img_name) { - CHECK(image_ppm_write(img_name, IMG_WIDTH, IMG_HEIGHT, 3, img), RES_OK); - } - - if(img) - MEM_RM(&allocator, img); + CHECK(s3d_scene_view_ref_put(scnview), RES_OK); + CHECK(image_write_ppm_stream(&img, 0, stdout), RES_OK); + image_release(&img); + CHECK(s3d_device_ref_put(dev), RES_OK); CHECK(s3d_shape_ref_put(inst), RES_OK); CHECK(s3d_shape_ref_put(short_block), RES_OK); diff --git a/src/test_s3d_trace_ray_instance.c b/src/test_s3d_trace_ray_instance.c @@ -0,0 +1,344 @@ +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to describe a + * virtual 3D environment that can be ray-traced and sampled both robustly + * and efficiently. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "s3d.h" +#include "test_s3d_camera.h" +#include "test_s3d_cbox.h" +#include "test_s3d_utils.h" + +#include <rsys/float2.h> +#include <rsys/float3.h> +#include <rsys/float33.h> +#include <rsys/image.h> + +static const float quad_verts[] = { + -1.f, -1.f, 0.f, + -1.f, 1.f, 0.f, + 1.f, 1.f, 0.f, + 1.f, -1.f, 0.f +}; +static const unsigned quad_nverts = sizeof(quad_verts)/sizeof(float[3]); +static const unsigned quad_ids[] = { 0, 1, 3, 3, 1, 2 }; +static const unsigned quad_ntris = sizeof(quad_ids)/sizeof(unsigned[3]); + +struct ray { + float org[3]; + float dir[3]; +}; + +static int +filter(const struct s3d_hit* hit, + const float ray_org[3], + const float ray_dir[3], + void* ray_data, + void* filter_data) +{ + struct ray* ray = ray_data; + + NCHECK(hit, NULL); + NCHECK(ray_org, NULL); + NCHECK(ray_dir, NULL); + NCHECK(ray_data, NULL); + CHECK(filter_data, NULL); + CHECK(f3_eq(ray_org, ray->org), 1); + CHECK(f3_eq(ray_dir, ray->dir), 1); + return 0; +} + +static void +quad_get_ids(const unsigned itri, unsigned ids[3], void* data) +{ + const unsigned id = itri * 3; + NCHECK(ids, NULL); + CHECK(itri < quad_ntris, 1); + (void)data; + ids[0] = quad_ids[id + 0]; + ids[1] = quad_ids[id + 1]; + ids[2] = quad_ids[id + 2]; +} + +static void +quad_get_pos(const unsigned ivert, float pos[3], void* data) +{ + const unsigned i = ivert*3; + NCHECK(pos, NULL); + CHECK(ivert < quad_nverts, 1); + (void)data; + pos[0] = quad_verts[i+0]; + pos[1] = quad_verts[i+1]; + pos[2] = quad_verts[i+2]; +} + +static void +test_quad(struct s3d_device* dev) +{ + struct ray ray; + struct s3d_attrib attr; + struct s3d_hit hit[2]; + struct s3d_scene* scn; + struct s3d_scene_view* view[2]; + struct s3d_shape* quad; + struct s3d_shape* quad_inst; + struct s3d_vertex_data vdata; + unsigned quad_id; + unsigned quad_inst_id; + float transform[12]; + float dir[3]; + float range[2]; + + f33_rotation_pitch(transform, (float)PI); + f3_splat(transform+9, 0); + + vdata.type = S3D_FLOAT3; + vdata.usage = S3D_POSITION; + vdata.get = quad_get_pos; + CHECK(s3d_shape_create_mesh(dev, &quad), RES_OK); + CHECK(s3d_mesh_set_hit_filter_function(quad, filter, NULL), RES_OK); + CHECK(s3d_mesh_setup_indexed_vertices + (quad, quad_ntris, quad_get_ids, quad_nverts, &vdata, 1, NULL), RES_OK); + + CHECK(s3d_scene_create(dev, &scn), RES_OK); + CHECK(s3d_scene_attach_shape(scn, quad), RES_OK); + CHECK(s3d_scene_instantiate(scn, &quad_inst), RES_OK); + CHECK(s3d_instance_set_transform(quad_inst, transform), RES_OK); + CHECK(s3d_scene_ref_put(scn), RES_OK); + + CHECK(s3d_scene_create(dev, &scn), RES_OK); + CHECK(s3d_scene_attach_shape(scn, quad_inst), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &view[0]), RES_OK); + + CHECK(s3d_scene_clear(scn), RES_OK); + CHECK(s3d_scene_attach_shape(scn, quad), RES_OK); + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &view[1]), RES_OK); + + CHECK(s3d_shape_get_id(quad, &quad_id), RES_OK); + CHECK(s3d_shape_get_id(quad_inst, &quad_inst_id), RES_OK); + + f3(ray.org, 0.f, 0.5f, -1.f); + f3(ray.dir, 0.f, 0.f, 1.f); + f2(range, 0.f, FLT_MAX); + CHECK(s3d_scene_view_trace_ray + (view[0], ray.org, ray.dir, range, &ray, &hit[0]), RES_OK); + CHECK(s3d_scene_view_trace_ray + (view[1], ray.org, ray.dir, range, &ray, &hit[1]), RES_OK); + + CHECK(hit[0].prim.prim_id, 0); + CHECK(hit[1].prim.prim_id, 1); + CHECK(hit[0].prim.geom_id, quad_id); + CHECK(hit[1].prim.geom_id, quad_id); + CHECK(hit[0].prim.inst_id, quad_inst_id); + CHECK(hit[1].prim.inst_id, S3D_INVALID_ID); + CHECK(f3_eq_eps(hit[0].normal, f3_minus(dir, hit[1].normal), 1.e-6f), 1); + CHECK(eq_epsf(hit[0].distance, hit[1].distance, 1.e-6f), 1); + + CHECK(s3d_primitive_get_attrib + (&hit[0].prim, S3D_GEOMETRY_NORMAL, hit[0].uv, &attr), RES_OK); + f3_normalize(attr.value, attr.value); + f3_normalize(hit[0].normal, hit[0].normal); + CHECK(f3_eq_eps(hit[0].normal, attr.value, 1.e-6f), 1); + + CHECK(s3d_primitive_get_attrib + (&hit[1].prim, S3D_GEOMETRY_NORMAL, hit[1].uv, &attr), RES_OK); + f3_normalize(attr.value, attr.value); + f3_normalize(hit[1].normal, hit[1].normal); + CHECK(f3_eq_eps(hit[1].normal, attr.value, 1.e-6f), 1); + + CHECK(s3d_scene_view_ref_put(view[0]), RES_OK); + CHECK(s3d_scene_view_ref_put(view[1]), RES_OK); + + CHECK(s3d_shape_ref_put(quad_inst), RES_OK); + CHECK(s3d_shape_ref_put(quad), RES_OK); + CHECK(s3d_scene_ref_put(scn), RES_OK); +} + +static void +test_cbox(struct s3d_device* dev) +{ + struct image img; + struct camera cam; + struct cbox_desc cbox_desc; + struct s3d_scene* scn; + struct s3d_scene* cbox; + struct s3d_shape* shape; + struct s3d_vertex_data vdata; + struct s3d_scene_view* view; + float lower[3], upper[3], extend[3]; + float size[2]; + float pos[3], tgt[3], up[3]; + float org[3], dir[3], range[2]; + float proj_ratio; + unsigned walls_id; + const size_t img_sz[2] = { 640, 480 }; + const size_t N = 8; + size_t x, y; + + CHECK(s3d_scene_create(dev, &cbox), RES_OK); + CHECK(s3d_scene_create(dev, &scn), RES_OK); + + vdata.usage = S3D_POSITION; + vdata.type = S3D_FLOAT3; + vdata.get = cbox_get_position; + + /* Walls */ + cbox_desc.vertices = cbox_walls; + cbox_desc.indices = cbox_walls_ids; + CHECK(s3d_shape_create_mesh(dev, &shape), RES_OK); + CHECK(s3d_mesh_setup_indexed_vertices(shape, cbox_walls_ntris, cbox_get_ids, + cbox_walls_nverts, &vdata, 1, &cbox_desc), RES_OK); + CHECK(s3d_scene_attach_shape(cbox, shape), RES_OK); + CHECK(s3d_shape_get_id(shape, &walls_id), RES_OK); + CHECK(s3d_shape_ref_put(shape), RES_OK); + + /* Short block */ + cbox_desc.vertices = cbox_short_block; + cbox_desc.indices = cbox_block_ids; + CHECK(s3d_shape_create_mesh(dev, &shape), RES_OK); + CHECK(s3d_mesh_setup_indexed_vertices(shape, cbox_block_ntris, cbox_get_ids, + cbox_block_nverts, &vdata, 1, &cbox_desc), RES_OK); + CHECK(s3d_scene_attach_shape(cbox, shape), RES_OK); + CHECK(s3d_shape_ref_put(shape), RES_OK); + + /* Tall block */ + cbox_desc.vertices = cbox_tall_block; + cbox_desc.indices = cbox_block_ids; + CHECK(s3d_shape_create_mesh(dev, &shape), RES_OK); + CHECK(s3d_mesh_setup_indexed_vertices(shape, cbox_block_ntris, cbox_get_ids, + cbox_block_nverts, &vdata, 1, &cbox_desc), RES_OK); + CHECK(s3d_scene_attach_shape(cbox, shape), RES_OK); + CHECK(s3d_shape_ref_put(shape), RES_OK); + + /* Compute the cbox extends */ + CHECK(s3d_scene_view_create(cbox, S3D_GET_PRIMITIVE, &view), RES_OK); + CHECK(s3d_scene_view_get_aabb(view, lower, upper), RES_OK); + CHECK(s3d_scene_view_ref_put(view), RES_OK); + f3_sub(extend, upper, lower); + + /* Create instances */ + size[0] = extend[0]*(float)N + (extend[0]*0.05f) * (float)(N-1); + size[1] = extend[2]*(float)N + (extend[2]*0.05f) * (float)(N-1); + pos[0] = -size[0] * 0.5f; + pos[1] = 0; + FOR_EACH(x, 0, N) { + pos[2] = -size[1] * 0.5f; + FOR_EACH(y, 0, N) { + CHECK(s3d_scene_instantiate(cbox, &shape), RES_OK); + CHECK(s3d_instance_set_position(shape, pos), RES_OK); + CHECK(s3d_scene_attach_shape(scn, shape), RES_OK); + CHECK(s3d_shape_ref_put(shape), RES_OK); + pos[2] += extend[2] * 1.05f; + } + pos[0] += extend[0] * 1.05f; + } + + /* Setup point of view */ + f3(pos, 0.f, -3000.f, 0.f); + f3(tgt, 0.f, 0.f, 0.f); + f3(up, 0.f, 0.f, 1.f); + proj_ratio = (float)img_sz[0] / (float)img_sz[1]; + camera_init(&cam, pos, tgt, up, (float)PI*0.5f, proj_ratio); + + image_init(NULL, &img); + CHECK(image_setup + (&img, img_sz[0], img_sz[1], img_sz[0]*3, IMAGE_RGB8, NULL), RES_OK); + + /* Trace rays */ + CHECK(s3d_scene_view_create(scn, S3D_TRACE, &view), RES_OK); + range[0] = 0.f; + range[1] = FLT_MAX; + FOR_EACH(y, 0, img_sz[1]) { + float pixel[2]; + pixel[1] = (float)y / (float)img_sz[1]; + FOR_EACH(x, 0, img_sz[0]) { + const size_t ipix = (y*img_sz[0] + x)*3/*RGB*/; + struct s3d_hit hit; + + pixel[0] = (float)x/(float)img_sz[0]; + camera_ray(&cam, pixel, org, dir); + CHECK(s3d_scene_view_trace_ray(view, org, dir, range, NULL, &hit), RES_OK); + + if(S3D_HIT_NONE(&hit)) { + ((uint8_t*)img.pixels)[ipix+0] = 0; + ((uint8_t*)img.pixels)[ipix+1] = 0; + ((uint8_t*)img.pixels)[ipix+2] = 0; + } else { + float normal[3] = {0.f, 0.f, 0.f}; + float col[3], dot; + float f = (float)hit.prim.inst_id / (float)(N*N); + f3(col, f, MMAX(0.f, 1.f-f), MMAX(0.f, 1.f-f)); + + if(hit.prim.geom_id == walls_id) { + if(hit.prim.prim_id == 4 || hit.prim.prim_id == 5) { + f3(col, col[0], 0.f, 0.f); + } else if(hit.prim.prim_id == 6 || hit.prim.prim_id == 7) { + f3(col, 0.f, col[1], 0.f); + } + } + + f3_normalize(normal, hit.normal); + dot = absf(f3_dot(normal, dir)); + ((uint8_t*)img.pixels)[ipix+0] = (uint8_t)(dot * col[0] * 255.f); + ((uint8_t*)img.pixels)[ipix+1] = (uint8_t)(dot * col[1] * 255.f); + ((uint8_t*)img.pixels)[ipix+2] = (uint8_t)(dot * col[2] * 255.f); + } + } + } + CHECK(s3d_scene_view_ref_put(view), RES_OK); + + /* Write image */ + CHECK(image_write_ppm_stream(&img, 0, stdout), RES_OK); + image_release(&img); + + /* Release data */ + CHECK(s3d_scene_ref_put(cbox), RES_OK); + CHECK(s3d_scene_ref_put(scn), RES_OK); +} + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct s3d_device* dev; + (void)argc, (void)argv; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + CHECK(s3d_device_create(NULL, &allocator, 0, &dev), RES_OK); + + test_quad(dev); + test_cbox(dev); + + CHECK(s3d_device_ref_put(dev), RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + return 0; +} diff --git a/src/test_s3d_utils.h b/src/test_s3d_utils.h @@ -1,4 +1,4 @@ -/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) +/* Copyright (C) |Meso|Star> 2015-2017 (contact@meso-star.com) * * This software is a computer program whose purpose is to describe a * virtual 3D environment that can be ray-traced and sampled both robustly