diff --git a/.gitignore b/.gitignore index 85501469d..be4a573f7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,6 @@ SetupWriteRead5.3mf v093reout.3mf .vs .idea -cmake-build-* \ No newline at end of file +cmake-build-* +TODO.md +Testing diff --git a/Autogenerated/Bindings/C/lib3mf.h b/Autogenerated/Bindings/C/lib3mf.h index 84ccf1d2b..a084d2709 100644 --- a/Autogenerated/Bindings/C/lib3mf.h +++ b/Autogenerated/Bindings/C/lib3mf.h @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated plain C Header file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -497,6 +497,19 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_meshobjectiterator_getcurrentmeshobject(Lib3 */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_componentsobjectiterator_getcurrentcomponentsobject(Lib3MF_ComponentsObjectIterator pComponentsObjectIterator, Lib3MF_ComponentsObject * pResource); +/************************************************************************************************************************* + Class definition for BooleanObjectIterator +**************************************************************************************************************************/ + +/** +* Returns the BooleanObject the iterator points at. +* +* @param[in] pBooleanObjectIterator - BooleanObjectIterator instance. +* @param[out] pResource - returns the BooleanObject instance. +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobjectiterator_getcurrentbooleanobject(Lib3MF_BooleanObjectIterator pBooleanObjectIterator, Lib3MF_BooleanObject * pResource); + /************************************************************************************************************************* Class definition for Texture2DIterator **************************************************************************************************************************/ @@ -1011,6 +1024,15 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_object_iscomponentsobject(Lib3MF_Object pObj */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_object_islevelsetobject(Lib3MF_Object pObject, bool * pIsLevelSetObject); +/** +* Retrieves, if an object is a boolean object +* +* @param[in] pObject - Object instance. +* @param[out] pIsBooleanObject - returns, whether the object is a boolean object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_object_isbooleanobject(Lib3MF_Object pObject, bool * pIsBooleanObject); + /** * Retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. * @@ -1561,6 +1583,131 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_levelset_getvolumedata(Lib3MF_LevelSet pLeve */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_levelset_setvolumedata(Lib3MF_LevelSet pLevelSet, Lib3MF_VolumeData pTheVolumeData); +/************************************************************************************************************************* + Class definition for BooleanObject +**************************************************************************************************************************/ + +/** +* Sets the base object and transform for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pBaseObject - base object of the boolean shape +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setbaseobject(Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object pBaseObject, const sLib3MFTransform * pTransform); + +/** +* Returns the base object of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pBaseObject - base object of the boolean shape +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getbaseobject(Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object * pBaseObject); + +/** +* Sets the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setbasetransform(Lib3MF_BooleanObject pBooleanObject, const sLib3MFTransform * pTransform); + +/** +* Returns the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getbasetransform(Lib3MF_BooleanObject pBooleanObject, sLib3MFTransform * pTransform); + +/** +* Sets the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] eOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setoperation(Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation eOperation); + +/** +* Returns the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getoperation(Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation * pOperation); + +/** +* Enables or disables CSG field evaluation for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] bCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setcsgmodeenabled(Lib3MF_BooleanObject pBooleanObject, bool bCSGModeEnabled); + +/** +* Returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getcsgmodeenabled(Lib3MF_BooleanObject pBooleanObject, bool * pCSGModeEnabled); + +/** +* Sets the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setextractiongridresolution(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nGridResolution); + +/** +* Returns the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getextractiongridresolution(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pGridResolution); + +/** +* Returns the number of operands in the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCount - number of operands in the boolean sequence +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getoperandcount(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pCount); + +/** +* Adds an operand object to the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pOperandObject - mesh object used as operand +* @param[in] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_addoperand(Lib3MF_BooleanObject pBooleanObject, Lib3MF_MeshObject pOperandObject, const sLib3MFTransform * pTransform); + +/** +* Returns one operand object and transform from the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nIndex - index of the operand in the boolean sequence +* @param[out] pOperandObject - mesh object used as operand +* @param[out] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getoperand(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nIndex, Lib3MF_MeshObject * pOperandObject, sLib3MFTransform * pTransform); + /************************************************************************************************************************* Class definition for BeamLattice **************************************************************************************************************************/ @@ -6178,6 +6325,16 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getmeshobjectbyid(Lib3MF_Model pModel, */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getcomponentsobjectbyid(Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* finds a boolean object by its UniqueResourceID +* +* @param[in] pModel - Model instance. +* @param[in] nUniqueResourceID - UniqueResourceID +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getbooleanobjectbyid(Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * finds a model color group by its UniqueResourceID * @@ -6283,6 +6440,15 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getmeshobjects(Lib3MF_Model pModel, Li */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getcomponentsobjects(Lib3MF_Model pModel, Lib3MF_ComponentsObjectIterator * pResourceIterator); +/** +* creates a resource iterator instance with all boolean object resources. +* +* @param[in] pModel - Model instance. +* @param[out] pResourceIterator - returns the iterator instance. +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getbooleanobjects(Lib3MF_Model pModel, Lib3MF_BooleanObjectIterator * pResourceIterator); + /** * creates a Texture2DIterator instance with all texture2d resources. * @@ -6391,6 +6557,15 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_addmeshobject(Lib3MF_Model pModel, Lib */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_addcomponentsobject(Lib3MF_Model pModel, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* adds an empty boolean object to the model. +* +* @param[in] pModel - Model instance. +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_addbooleanobject(Lib3MF_Model pModel, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * creates a new model slicestack by its id * diff --git a/Autogenerated/Bindings/C/lib3mf_types.h b/Autogenerated/Bindings/C/lib3mf_types.h index d87847a5b..b9e4aec27 100644 --- a/Autogenerated/Bindings/C/lib3mf_types.h +++ b/Autogenerated/Bindings/C/lib3mf_types.h @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated plain C Header file with basic types in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -84,7 +84,7 @@ typedef void * Lib3MF_pvoid; **************************************************************************************************************************/ #define LIB3MF_VERSION_MAJOR 2 -#define LIB3MF_VERSION_MINOR 5 +#define LIB3MF_VERSION_MINOR 6 #define LIB3MF_VERSION_MICRO 0 #define LIB3MF_VERSION_PRERELEASEINFO "" #define LIB3MF_VERSION_BUILDINFO "" @@ -220,6 +220,7 @@ typedef Lib3MFHandle Lib3MF_SliceStackIterator; typedef Lib3MFHandle Lib3MF_ObjectIterator; typedef Lib3MFHandle Lib3MF_MeshObjectIterator; typedef Lib3MFHandle Lib3MF_ComponentsObjectIterator; +typedef Lib3MFHandle Lib3MF_BooleanObjectIterator; typedef Lib3MFHandle Lib3MF_Texture2DIterator; typedef Lib3MFHandle Lib3MF_BaseMaterialGroupIterator; typedef Lib3MFHandle Lib3MF_ColorGroupIterator; @@ -235,6 +236,7 @@ typedef Lib3MFHandle Lib3MF_TriangleSet; typedef Lib3MFHandle Lib3MF_Object; typedef Lib3MFHandle Lib3MF_MeshObject; typedef Lib3MFHandle Lib3MF_LevelSet; +typedef Lib3MFHandle Lib3MF_BooleanObject; typedef Lib3MFHandle Lib3MF_BeamLattice; typedef Lib3MFHandle Lib3MF_FunctionReference; typedef Lib3MFHandle Lib3MF_VolumeDataColor; @@ -365,6 +367,12 @@ typedef enum eLib3MFObjectType { eObjectTypeSurface = 4 } eLib3MFObjectType; +typedef enum eLib3MFBooleanOperation { + eBooleanOperationUnion = 0, + eBooleanOperationDifference = 1, + eBooleanOperationIntersection = 2 +} eLib3MFBooleanOperation; + typedef enum eLib3MFTextureType { eTextureTypeUnknown = 0, eTextureTypePNG = 1, @@ -585,6 +593,11 @@ typedef union { int m_code; } structEnumLib3MFObjectType; +typedef union { + eLib3MFBooleanOperation m_enum; + int m_code; +} structEnumLib3MFBooleanOperation; + typedef union { eLib3MFTextureType m_enum; int m_code; diff --git a/Autogenerated/Bindings/CDynamic/lib3mf_dynamic.cc b/Autogenerated/Bindings/CDynamic/lib3mf_dynamic.cc index 238da6b18..553825932 100644 --- a/Autogenerated/Bindings/CDynamic/lib3mf_dynamic.cc +++ b/Autogenerated/Bindings/CDynamic/lib3mf_dynamic.cc @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated plain C Header file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -90,6 +90,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_ObjectIterator_GetCurrentObject = NULL; pWrapperTable->m_MeshObjectIterator_GetCurrentMeshObject = NULL; pWrapperTable->m_ComponentsObjectIterator_GetCurrentComponentsObject = NULL; + pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject = NULL; pWrapperTable->m_Texture2DIterator_GetCurrentTexture2D = NULL; pWrapperTable->m_BaseMaterialGroupIterator_GetCurrentBaseMaterialGroup = NULL; pWrapperTable->m_ColorGroupIterator_GetCurrentColorGroup = NULL; @@ -138,6 +139,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_Object_IsMeshObject = NULL; pWrapperTable->m_Object_IsComponentsObject = NULL; pWrapperTable->m_Object_IsLevelSetObject = NULL; + pWrapperTable->m_Object_IsBooleanObject = NULL; pWrapperTable->m_Object_IsValid = NULL; pWrapperTable->m_Object_SetAttachmentAsThumbnail = NULL; pWrapperTable->m_Object_GetThumbnailAttachment = NULL; @@ -195,6 +197,19 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_LevelSet_GetMesh = NULL; pWrapperTable->m_LevelSet_GetVolumeData = NULL; pWrapperTable->m_LevelSet_SetVolumeData = NULL; + pWrapperTable->m_BooleanObject_SetBaseObject = NULL; + pWrapperTable->m_BooleanObject_GetBaseObject = NULL; + pWrapperTable->m_BooleanObject_SetBaseTransform = NULL; + pWrapperTable->m_BooleanObject_GetBaseTransform = NULL; + pWrapperTable->m_BooleanObject_SetOperation = NULL; + pWrapperTable->m_BooleanObject_GetOperation = NULL; + pWrapperTable->m_BooleanObject_SetCSGModeEnabled = NULL; + pWrapperTable->m_BooleanObject_GetCSGModeEnabled = NULL; + pWrapperTable->m_BooleanObject_SetExtractionGridResolution = NULL; + pWrapperTable->m_BooleanObject_GetExtractionGridResolution = NULL; + pWrapperTable->m_BooleanObject_GetOperandCount = NULL; + pWrapperTable->m_BooleanObject_AddOperand = NULL; + pWrapperTable->m_BooleanObject_GetOperand = NULL; pWrapperTable->m_BeamLattice_GetMinLength = NULL; pWrapperTable->m_BeamLattice_SetMinLength = NULL; pWrapperTable->m_BeamLattice_GetClipping = NULL; @@ -620,6 +635,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_Model_GetMultiPropertyGroupByID = NULL; pWrapperTable->m_Model_GetMeshObjectByID = NULL; pWrapperTable->m_Model_GetComponentsObjectByID = NULL; + pWrapperTable->m_Model_GetBooleanObjectByID = NULL; pWrapperTable->m_Model_GetColorGroupByID = NULL; pWrapperTable->m_Model_GetSliceStackByID = NULL; pWrapperTable->m_Model_GetLevelSetByID = NULL; @@ -631,6 +647,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_Model_GetObjects = NULL; pWrapperTable->m_Model_GetMeshObjects = NULL; pWrapperTable->m_Model_GetComponentsObjects = NULL; + pWrapperTable->m_Model_GetBooleanObjects = NULL; pWrapperTable->m_Model_GetTexture2Ds = NULL; pWrapperTable->m_Model_GetBaseMaterialGroups = NULL; pWrapperTable->m_Model_GetColorGroups = NULL; @@ -643,6 +660,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_Model_MergeFromModel = NULL; pWrapperTable->m_Model_AddMeshObject = NULL; pWrapperTable->m_Model_AddComponentsObject = NULL; + pWrapperTable->m_Model_AddBooleanObject = NULL; pWrapperTable->m_Model_AddSliceStack = NULL; pWrapperTable->m_Model_AddTexture2DFromAttachment = NULL; pWrapperTable->m_Model_AddBaseMaterialGroup = NULL; @@ -1126,6 +1144,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_ComponentsObjectIterator_GetCurrentComponentsObject == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject = (PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr) GetProcAddress(hLibrary, "lib3mf_booleanobjectiterator_getcurrentbooleanobject"); + #else // _WIN32 + pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject = (PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr) dlsym(hLibrary, "lib3mf_booleanobjectiterator_getcurrentbooleanobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Texture2DIterator_GetCurrentTexture2D = (PLib3MFTexture2DIterator_GetCurrentTexture2DPtr) GetProcAddress(hLibrary, "lib3mf_texture2diterator_getcurrenttexture2d"); #else // _WIN32 @@ -1558,6 +1585,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_Object_IsLevelSetObject == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Object_IsBooleanObject = (PLib3MFObject_IsBooleanObjectPtr) GetProcAddress(hLibrary, "lib3mf_object_isbooleanobject"); + #else // _WIN32 + pWrapperTable->m_Object_IsBooleanObject = (PLib3MFObject_IsBooleanObjectPtr) dlsym(hLibrary, "lib3mf_object_isbooleanobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Object_IsBooleanObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Object_IsValid = (PLib3MFObject_IsValidPtr) GetProcAddress(hLibrary, "lib3mf_object_isvalid"); #else // _WIN32 @@ -2071,6 +2107,123 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_LevelSet_SetVolumeData == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetBaseObject = (PLib3MFBooleanObject_SetBaseObjectPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setbaseobject"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetBaseObject = (PLib3MFBooleanObject_SetBaseObjectPtr) dlsym(hLibrary, "lib3mf_booleanobject_setbaseobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetBaseObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetBaseObject = (PLib3MFBooleanObject_GetBaseObjectPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getbaseobject"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetBaseObject = (PLib3MFBooleanObject_GetBaseObjectPtr) dlsym(hLibrary, "lib3mf_booleanobject_getbaseobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetBaseObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetBaseTransform = (PLib3MFBooleanObject_SetBaseTransformPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setbasetransform"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetBaseTransform = (PLib3MFBooleanObject_SetBaseTransformPtr) dlsym(hLibrary, "lib3mf_booleanobject_setbasetransform"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetBaseTransform == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetBaseTransform = (PLib3MFBooleanObject_GetBaseTransformPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getbasetransform"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetBaseTransform = (PLib3MFBooleanObject_GetBaseTransformPtr) dlsym(hLibrary, "lib3mf_booleanobject_getbasetransform"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetBaseTransform == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetOperation = (PLib3MFBooleanObject_SetOperationPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setoperation"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetOperation = (PLib3MFBooleanObject_SetOperationPtr) dlsym(hLibrary, "lib3mf_booleanobject_setoperation"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetOperation == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetOperation = (PLib3MFBooleanObject_GetOperationPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getoperation"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetOperation = (PLib3MFBooleanObject_GetOperationPtr) dlsym(hLibrary, "lib3mf_booleanobject_getoperation"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetOperation == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetCSGModeEnabled = (PLib3MFBooleanObject_SetCSGModeEnabledPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setcsgmodeenabled"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetCSGModeEnabled = (PLib3MFBooleanObject_SetCSGModeEnabledPtr) dlsym(hLibrary, "lib3mf_booleanobject_setcsgmodeenabled"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetCSGModeEnabled == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetCSGModeEnabled = (PLib3MFBooleanObject_GetCSGModeEnabledPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getcsgmodeenabled"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetCSGModeEnabled = (PLib3MFBooleanObject_GetCSGModeEnabledPtr) dlsym(hLibrary, "lib3mf_booleanobject_getcsgmodeenabled"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetCSGModeEnabled == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetExtractionGridResolution = (PLib3MFBooleanObject_SetExtractionGridResolutionPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setextractiongridresolution"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetExtractionGridResolution = (PLib3MFBooleanObject_SetExtractionGridResolutionPtr) dlsym(hLibrary, "lib3mf_booleanobject_setextractiongridresolution"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetExtractionGridResolution == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetExtractionGridResolution = (PLib3MFBooleanObject_GetExtractionGridResolutionPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getextractiongridresolution"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetExtractionGridResolution = (PLib3MFBooleanObject_GetExtractionGridResolutionPtr) dlsym(hLibrary, "lib3mf_booleanobject_getextractiongridresolution"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetExtractionGridResolution == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetOperandCount = (PLib3MFBooleanObject_GetOperandCountPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getoperandcount"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetOperandCount = (PLib3MFBooleanObject_GetOperandCountPtr) dlsym(hLibrary, "lib3mf_booleanobject_getoperandcount"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetOperandCount == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_AddOperand = (PLib3MFBooleanObject_AddOperandPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_addoperand"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_AddOperand = (PLib3MFBooleanObject_AddOperandPtr) dlsym(hLibrary, "lib3mf_booleanobject_addoperand"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_AddOperand == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetOperand = (PLib3MFBooleanObject_GetOperandPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getoperand"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetOperand = (PLib3MFBooleanObject_GetOperandPtr) dlsym(hLibrary, "lib3mf_booleanobject_getoperand"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetOperand == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_BeamLattice_GetMinLength = (PLib3MFBeamLattice_GetMinLengthPtr) GetProcAddress(hLibrary, "lib3mf_beamlattice_getminlength"); #else // _WIN32 @@ -5896,6 +6049,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_Model_GetComponentsObjectByID == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Model_GetBooleanObjectByID = (PLib3MFModel_GetBooleanObjectByIDPtr) GetProcAddress(hLibrary, "lib3mf_model_getbooleanobjectbyid"); + #else // _WIN32 + pWrapperTable->m_Model_GetBooleanObjectByID = (PLib3MFModel_GetBooleanObjectByIDPtr) dlsym(hLibrary, "lib3mf_model_getbooleanobjectbyid"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Model_GetBooleanObjectByID == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Model_GetColorGroupByID = (PLib3MFModel_GetColorGroupByIDPtr) GetProcAddress(hLibrary, "lib3mf_model_getcolorgroupbyid"); #else // _WIN32 @@ -5995,6 +6157,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_Model_GetComponentsObjects == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Model_GetBooleanObjects = (PLib3MFModel_GetBooleanObjectsPtr) GetProcAddress(hLibrary, "lib3mf_model_getbooleanobjects"); + #else // _WIN32 + pWrapperTable->m_Model_GetBooleanObjects = (PLib3MFModel_GetBooleanObjectsPtr) dlsym(hLibrary, "lib3mf_model_getbooleanobjects"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Model_GetBooleanObjects == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Model_GetTexture2Ds = (PLib3MFModel_GetTexture2DsPtr) GetProcAddress(hLibrary, "lib3mf_model_gettexture2ds"); #else // _WIN32 @@ -6103,6 +6274,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_Model_AddComponentsObject == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Model_AddBooleanObject = (PLib3MFModel_AddBooleanObjectPtr) GetProcAddress(hLibrary, "lib3mf_model_addbooleanobject"); + #else // _WIN32 + pWrapperTable->m_Model_AddBooleanObject = (PLib3MFModel_AddBooleanObjectPtr) dlsym(hLibrary, "lib3mf_model_addbooleanobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Model_AddBooleanObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Model_AddSliceStack = (PLib3MFModel_AddSliceStackPtr) GetProcAddress(hLibrary, "lib3mf_model_addslicestack"); #else // _WIN32 diff --git a/Autogenerated/Bindings/CDynamic/lib3mf_dynamic.h b/Autogenerated/Bindings/CDynamic/lib3mf_dynamic.h index cc8d934bd..778010c7c 100644 --- a/Autogenerated/Bindings/CDynamic/lib3mf_dynamic.h +++ b/Autogenerated/Bindings/CDynamic/lib3mf_dynamic.h @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated plain C Header file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -484,6 +484,19 @@ typedef Lib3MFResult (*PLib3MFMeshObjectIterator_GetCurrentMeshObjectPtr) (Lib3M */ typedef Lib3MFResult (*PLib3MFComponentsObjectIterator_GetCurrentComponentsObjectPtr) (Lib3MF_ComponentsObjectIterator pComponentsObjectIterator, Lib3MF_ComponentsObject * pResource); +/************************************************************************************************************************* + Class definition for BooleanObjectIterator +**************************************************************************************************************************/ + +/** +* Returns the BooleanObject the iterator points at. +* +* @param[in] pBooleanObjectIterator - BooleanObjectIterator instance. +* @param[out] pResource - returns the BooleanObject instance. +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr) (Lib3MF_BooleanObjectIterator pBooleanObjectIterator, Lib3MF_BooleanObject * pResource); + /************************************************************************************************************************* Class definition for Texture2DIterator **************************************************************************************************************************/ @@ -998,6 +1011,15 @@ typedef Lib3MFResult (*PLib3MFObject_IsComponentsObjectPtr) (Lib3MF_Object pObje */ typedef Lib3MFResult (*PLib3MFObject_IsLevelSetObjectPtr) (Lib3MF_Object pObject, bool * pIsLevelSetObject); +/** +* Retrieves, if an object is a boolean object +* +* @param[in] pObject - Object instance. +* @param[out] pIsBooleanObject - returns, whether the object is a boolean object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFObject_IsBooleanObjectPtr) (Lib3MF_Object pObject, bool * pIsBooleanObject); + /** * Retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. * @@ -1548,6 +1570,131 @@ typedef Lib3MFResult (*PLib3MFLevelSet_GetVolumeDataPtr) (Lib3MF_LevelSet pLevel */ typedef Lib3MFResult (*PLib3MFLevelSet_SetVolumeDataPtr) (Lib3MF_LevelSet pLevelSet, Lib3MF_VolumeData pTheVolumeData); +/************************************************************************************************************************* + Class definition for BooleanObject +**************************************************************************************************************************/ + +/** +* Sets the base object and transform for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pBaseObject - base object of the boolean shape +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetBaseObjectPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object pBaseObject, const sLib3MFTransform * pTransform); + +/** +* Returns the base object of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pBaseObject - base object of the boolean shape +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetBaseObjectPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object * pBaseObject); + +/** +* Sets the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetBaseTransformPtr) (Lib3MF_BooleanObject pBooleanObject, const sLib3MFTransform * pTransform); + +/** +* Returns the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetBaseTransformPtr) (Lib3MF_BooleanObject pBooleanObject, sLib3MFTransform * pTransform); + +/** +* Sets the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] eOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetOperationPtr) (Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation eOperation); + +/** +* Returns the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetOperationPtr) (Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation * pOperation); + +/** +* Enables or disables CSG field evaluation for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] bCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetCSGModeEnabledPtr) (Lib3MF_BooleanObject pBooleanObject, bool bCSGModeEnabled); + +/** +* Returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetCSGModeEnabledPtr) (Lib3MF_BooleanObject pBooleanObject, bool * pCSGModeEnabled); + +/** +* Sets the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetExtractionGridResolutionPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nGridResolution); + +/** +* Returns the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetExtractionGridResolutionPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pGridResolution); + +/** +* Returns the number of operands in the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCount - number of operands in the boolean sequence +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetOperandCountPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pCount); + +/** +* Adds an operand object to the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pOperandObject - mesh object used as operand +* @param[in] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_AddOperandPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_MeshObject pOperandObject, const sLib3MFTransform * pTransform); + +/** +* Returns one operand object and transform from the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nIndex - index of the operand in the boolean sequence +* @param[out] pOperandObject - mesh object used as operand +* @param[out] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetOperandPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nIndex, Lib3MF_MeshObject * pOperandObject, sLib3MFTransform * pTransform); + /************************************************************************************************************************* Class definition for BeamLattice **************************************************************************************************************************/ @@ -6165,6 +6312,16 @@ typedef Lib3MFResult (*PLib3MFModel_GetMeshObjectByIDPtr) (Lib3MF_Model pModel, */ typedef Lib3MFResult (*PLib3MFModel_GetComponentsObjectByIDPtr) (Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* finds a boolean object by its UniqueResourceID +* +* @param[in] pModel - Model instance. +* @param[in] nUniqueResourceID - UniqueResourceID +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFModel_GetBooleanObjectByIDPtr) (Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * finds a model color group by its UniqueResourceID * @@ -6270,6 +6427,15 @@ typedef Lib3MFResult (*PLib3MFModel_GetMeshObjectsPtr) (Lib3MF_Model pModel, Lib */ typedef Lib3MFResult (*PLib3MFModel_GetComponentsObjectsPtr) (Lib3MF_Model pModel, Lib3MF_ComponentsObjectIterator * pResourceIterator); +/** +* creates a resource iterator instance with all boolean object resources. +* +* @param[in] pModel - Model instance. +* @param[out] pResourceIterator - returns the iterator instance. +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFModel_GetBooleanObjectsPtr) (Lib3MF_Model pModel, Lib3MF_BooleanObjectIterator * pResourceIterator); + /** * creates a Texture2DIterator instance with all texture2d resources. * @@ -6378,6 +6544,15 @@ typedef Lib3MFResult (*PLib3MFModel_AddMeshObjectPtr) (Lib3MF_Model pModel, Lib3 */ typedef Lib3MFResult (*PLib3MFModel_AddComponentsObjectPtr) (Lib3MF_Model pModel, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* adds an empty boolean object to the model. +* +* @param[in] pModel - Model instance. +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFModel_AddBooleanObjectPtr) (Lib3MF_Model pModel, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * creates a new model slicestack by its id * @@ -6928,6 +7103,7 @@ typedef struct { PLib3MFObjectIterator_GetCurrentObjectPtr m_ObjectIterator_GetCurrentObject; PLib3MFMeshObjectIterator_GetCurrentMeshObjectPtr m_MeshObjectIterator_GetCurrentMeshObject; PLib3MFComponentsObjectIterator_GetCurrentComponentsObjectPtr m_ComponentsObjectIterator_GetCurrentComponentsObject; + PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr m_BooleanObjectIterator_GetCurrentBooleanObject; PLib3MFTexture2DIterator_GetCurrentTexture2DPtr m_Texture2DIterator_GetCurrentTexture2D; PLib3MFBaseMaterialGroupIterator_GetCurrentBaseMaterialGroupPtr m_BaseMaterialGroupIterator_GetCurrentBaseMaterialGroup; PLib3MFColorGroupIterator_GetCurrentColorGroupPtr m_ColorGroupIterator_GetCurrentColorGroup; @@ -6976,6 +7152,7 @@ typedef struct { PLib3MFObject_IsMeshObjectPtr m_Object_IsMeshObject; PLib3MFObject_IsComponentsObjectPtr m_Object_IsComponentsObject; PLib3MFObject_IsLevelSetObjectPtr m_Object_IsLevelSetObject; + PLib3MFObject_IsBooleanObjectPtr m_Object_IsBooleanObject; PLib3MFObject_IsValidPtr m_Object_IsValid; PLib3MFObject_SetAttachmentAsThumbnailPtr m_Object_SetAttachmentAsThumbnail; PLib3MFObject_GetThumbnailAttachmentPtr m_Object_GetThumbnailAttachment; @@ -7033,6 +7210,19 @@ typedef struct { PLib3MFLevelSet_GetMeshPtr m_LevelSet_GetMesh; PLib3MFLevelSet_GetVolumeDataPtr m_LevelSet_GetVolumeData; PLib3MFLevelSet_SetVolumeDataPtr m_LevelSet_SetVolumeData; + PLib3MFBooleanObject_SetBaseObjectPtr m_BooleanObject_SetBaseObject; + PLib3MFBooleanObject_GetBaseObjectPtr m_BooleanObject_GetBaseObject; + PLib3MFBooleanObject_SetBaseTransformPtr m_BooleanObject_SetBaseTransform; + PLib3MFBooleanObject_GetBaseTransformPtr m_BooleanObject_GetBaseTransform; + PLib3MFBooleanObject_SetOperationPtr m_BooleanObject_SetOperation; + PLib3MFBooleanObject_GetOperationPtr m_BooleanObject_GetOperation; + PLib3MFBooleanObject_SetCSGModeEnabledPtr m_BooleanObject_SetCSGModeEnabled; + PLib3MFBooleanObject_GetCSGModeEnabledPtr m_BooleanObject_GetCSGModeEnabled; + PLib3MFBooleanObject_SetExtractionGridResolutionPtr m_BooleanObject_SetExtractionGridResolution; + PLib3MFBooleanObject_GetExtractionGridResolutionPtr m_BooleanObject_GetExtractionGridResolution; + PLib3MFBooleanObject_GetOperandCountPtr m_BooleanObject_GetOperandCount; + PLib3MFBooleanObject_AddOperandPtr m_BooleanObject_AddOperand; + PLib3MFBooleanObject_GetOperandPtr m_BooleanObject_GetOperand; PLib3MFBeamLattice_GetMinLengthPtr m_BeamLattice_GetMinLength; PLib3MFBeamLattice_SetMinLengthPtr m_BeamLattice_SetMinLength; PLib3MFBeamLattice_GetClippingPtr m_BeamLattice_GetClipping; @@ -7458,6 +7648,7 @@ typedef struct { PLib3MFModel_GetMultiPropertyGroupByIDPtr m_Model_GetMultiPropertyGroupByID; PLib3MFModel_GetMeshObjectByIDPtr m_Model_GetMeshObjectByID; PLib3MFModel_GetComponentsObjectByIDPtr m_Model_GetComponentsObjectByID; + PLib3MFModel_GetBooleanObjectByIDPtr m_Model_GetBooleanObjectByID; PLib3MFModel_GetColorGroupByIDPtr m_Model_GetColorGroupByID; PLib3MFModel_GetSliceStackByIDPtr m_Model_GetSliceStackByID; PLib3MFModel_GetLevelSetByIDPtr m_Model_GetLevelSetByID; @@ -7469,6 +7660,7 @@ typedef struct { PLib3MFModel_GetObjectsPtr m_Model_GetObjects; PLib3MFModel_GetMeshObjectsPtr m_Model_GetMeshObjects; PLib3MFModel_GetComponentsObjectsPtr m_Model_GetComponentsObjects; + PLib3MFModel_GetBooleanObjectsPtr m_Model_GetBooleanObjects; PLib3MFModel_GetTexture2DsPtr m_Model_GetTexture2Ds; PLib3MFModel_GetBaseMaterialGroupsPtr m_Model_GetBaseMaterialGroups; PLib3MFModel_GetColorGroupsPtr m_Model_GetColorGroups; @@ -7481,6 +7673,7 @@ typedef struct { PLib3MFModel_MergeFromModelPtr m_Model_MergeFromModel; PLib3MFModel_AddMeshObjectPtr m_Model_AddMeshObject; PLib3MFModel_AddComponentsObjectPtr m_Model_AddComponentsObject; + PLib3MFModel_AddBooleanObjectPtr m_Model_AddBooleanObject; PLib3MFModel_AddSliceStackPtr m_Model_AddSliceStack; PLib3MFModel_AddTexture2DFromAttachmentPtr m_Model_AddTexture2DFromAttachment; PLib3MFModel_AddBaseMaterialGroupPtr m_Model_AddBaseMaterialGroup; diff --git a/Autogenerated/Bindings/CDynamic/lib3mf_types.h b/Autogenerated/Bindings/CDynamic/lib3mf_types.h index d87847a5b..b9e4aec27 100644 --- a/Autogenerated/Bindings/CDynamic/lib3mf_types.h +++ b/Autogenerated/Bindings/CDynamic/lib3mf_types.h @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated plain C Header file with basic types in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -84,7 +84,7 @@ typedef void * Lib3MF_pvoid; **************************************************************************************************************************/ #define LIB3MF_VERSION_MAJOR 2 -#define LIB3MF_VERSION_MINOR 5 +#define LIB3MF_VERSION_MINOR 6 #define LIB3MF_VERSION_MICRO 0 #define LIB3MF_VERSION_PRERELEASEINFO "" #define LIB3MF_VERSION_BUILDINFO "" @@ -220,6 +220,7 @@ typedef Lib3MFHandle Lib3MF_SliceStackIterator; typedef Lib3MFHandle Lib3MF_ObjectIterator; typedef Lib3MFHandle Lib3MF_MeshObjectIterator; typedef Lib3MFHandle Lib3MF_ComponentsObjectIterator; +typedef Lib3MFHandle Lib3MF_BooleanObjectIterator; typedef Lib3MFHandle Lib3MF_Texture2DIterator; typedef Lib3MFHandle Lib3MF_BaseMaterialGroupIterator; typedef Lib3MFHandle Lib3MF_ColorGroupIterator; @@ -235,6 +236,7 @@ typedef Lib3MFHandle Lib3MF_TriangleSet; typedef Lib3MFHandle Lib3MF_Object; typedef Lib3MFHandle Lib3MF_MeshObject; typedef Lib3MFHandle Lib3MF_LevelSet; +typedef Lib3MFHandle Lib3MF_BooleanObject; typedef Lib3MFHandle Lib3MF_BeamLattice; typedef Lib3MFHandle Lib3MF_FunctionReference; typedef Lib3MFHandle Lib3MF_VolumeDataColor; @@ -365,6 +367,12 @@ typedef enum eLib3MFObjectType { eObjectTypeSurface = 4 } eLib3MFObjectType; +typedef enum eLib3MFBooleanOperation { + eBooleanOperationUnion = 0, + eBooleanOperationDifference = 1, + eBooleanOperationIntersection = 2 +} eLib3MFBooleanOperation; + typedef enum eLib3MFTextureType { eTextureTypeUnknown = 0, eTextureTypePNG = 1, @@ -585,6 +593,11 @@ typedef union { int m_code; } structEnumLib3MFObjectType; +typedef union { + eLib3MFBooleanOperation m_enum; + int m_code; +} structEnumLib3MFBooleanOperation; + typedef union { eLib3MFTextureType m_enum; int m_code; diff --git a/Autogenerated/Bindings/CSharp/Lib3MF.cs b/Autogenerated/Bindings/CSharp/Lib3MF.cs index facf4c002..162fa5c0f 100644 --- a/Autogenerated/Bindings/CSharp/Lib3MF.cs +++ b/Autogenerated/Bindings/CSharp/Lib3MF.cs @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated CSharp file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -358,6 +358,12 @@ public enum eObjectType { Surface = 4 }; + public enum eBooleanOperation { + Union = 0, + Difference = 1, + Intersection = 2 + }; + public enum eTextureType { Unknown = 0, PNG = 1, @@ -853,6 +859,9 @@ public class Lib3MFWrapper [DllImport("lib3mf.dll", EntryPoint = "lib3mf_componentsobjectiterator_getcurrentcomponentsobject", CallingConvention=CallingConvention.Cdecl)] public unsafe extern static Int32 ComponentsObjectIterator_GetCurrentComponentsObject (IntPtr Handle, out IntPtr AResource); + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobjectiterator_getcurrentbooleanobject", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObjectIterator_GetCurrentBooleanObject (IntPtr Handle, out IntPtr AResource); + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_texture2diterator_getcurrenttexture2d", CallingConvention=CallingConvention.Cdecl)] public unsafe extern static Int32 Texture2DIterator_GetCurrentTexture2D (IntPtr Handle, out IntPtr AResource); @@ -997,6 +1006,9 @@ public class Lib3MFWrapper [DllImport("lib3mf.dll", EntryPoint = "lib3mf_object_islevelsetobject", CallingConvention=CallingConvention.Cdecl)] public unsafe extern static Int32 Object_IsLevelSetObject (IntPtr Handle, out Byte AIsLevelSetObject); + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_object_isbooleanobject", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 Object_IsBooleanObject (IntPtr Handle, out Byte AIsBooleanObject); + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_object_isvalid", CallingConvention=CallingConvention.Cdecl)] public unsafe extern static Int32 Object_IsValid (IntPtr Handle, out Byte AIsValid); @@ -1168,6 +1180,45 @@ public class Lib3MFWrapper [DllImport("lib3mf.dll", EntryPoint = "lib3mf_levelset_setvolumedata", CallingConvention=CallingConvention.Cdecl)] public unsafe extern static Int32 LevelSet_SetVolumeData (IntPtr Handle, IntPtr ATheVolumeData); + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobject_setbaseobject", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObject_SetBaseObject (IntPtr Handle, IntPtr ABaseObject, ref InternalTransform ATransform); + + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobject_getbaseobject", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObject_GetBaseObject (IntPtr Handle, out IntPtr ABaseObject); + + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobject_setbasetransform", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObject_SetBaseTransform (IntPtr Handle, ref InternalTransform ATransform); + + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobject_getbasetransform", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObject_GetBaseTransform (IntPtr Handle, out InternalTransform ATransform); + + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobject_setoperation", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObject_SetOperation (IntPtr Handle, Int32 AOperation); + + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobject_getoperation", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObject_GetOperation (IntPtr Handle, out Int32 AOperation); + + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobject_setcsgmodeenabled", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObject_SetCSGModeEnabled (IntPtr Handle, Byte ACSGModeEnabled); + + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobject_getcsgmodeenabled", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObject_GetCSGModeEnabled (IntPtr Handle, out Byte ACSGModeEnabled); + + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobject_setextractiongridresolution", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObject_SetExtractionGridResolution (IntPtr Handle, UInt32 AGridResolution); + + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobject_getextractiongridresolution", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObject_GetExtractionGridResolution (IntPtr Handle, out UInt32 AGridResolution); + + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobject_getoperandcount", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObject_GetOperandCount (IntPtr Handle, out UInt32 ACount); + + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobject_addoperand", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObject_AddOperand (IntPtr Handle, IntPtr AOperandObject, ref InternalTransform ATransform); + + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_booleanobject_getoperand", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 BooleanObject_GetOperand (IntPtr Handle, UInt32 AIndex, out IntPtr AOperandObject, out InternalTransform ATransform); + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_beamlattice_getminlength", CallingConvention=CallingConvention.Cdecl)] public unsafe extern static Int32 BeamLattice_GetMinLength (IntPtr Handle, out Double AMinLength); @@ -2443,6 +2494,9 @@ public class Lib3MFWrapper [DllImport("lib3mf.dll", EntryPoint = "lib3mf_model_getcomponentsobjectbyid", CallingConvention=CallingConvention.Cdecl)] public unsafe extern static Int32 Model_GetComponentsObjectByID (IntPtr Handle, UInt32 AUniqueResourceID, out IntPtr AComponentsObjectInstance); + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_model_getbooleanobjectbyid", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 Model_GetBooleanObjectByID (IntPtr Handle, UInt32 AUniqueResourceID, out IntPtr ABooleanObjectInstance); + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_model_getcolorgroupbyid", CallingConvention=CallingConvention.Cdecl)] public unsafe extern static Int32 Model_GetColorGroupByID (IntPtr Handle, UInt32 AUniqueResourceID, out IntPtr AColorGroupInstance); @@ -2476,6 +2530,9 @@ public class Lib3MFWrapper [DllImport("lib3mf.dll", EntryPoint = "lib3mf_model_getcomponentsobjects", CallingConvention=CallingConvention.Cdecl)] public unsafe extern static Int32 Model_GetComponentsObjects (IntPtr Handle, out IntPtr AResourceIterator); + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_model_getbooleanobjects", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 Model_GetBooleanObjects (IntPtr Handle, out IntPtr AResourceIterator); + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_model_gettexture2ds", CallingConvention=CallingConvention.Cdecl)] public unsafe extern static Int32 Model_GetTexture2Ds (IntPtr Handle, out IntPtr AResourceIterator); @@ -2512,6 +2569,9 @@ public class Lib3MFWrapper [DllImport("lib3mf.dll", EntryPoint = "lib3mf_model_addcomponentsobject", CallingConvention=CallingConvention.Cdecl)] public unsafe extern static Int32 Model_AddComponentsObject (IntPtr Handle, out IntPtr AComponentsObjectInstance); + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_model_addbooleanobject", CallingConvention=CallingConvention.Cdecl)] + public unsafe extern static Int32 Model_AddBooleanObject (IntPtr Handle, out IntPtr ABooleanObjectInstance); + [DllImport("lib3mf.dll", EntryPoint = "lib3mf_model_addslicestack", CallingConvention=CallingConvention.Cdecl)] public unsafe extern static Int32 Model_AddSliceStack (IntPtr Handle, Double AZBottom, out IntPtr ASliceStackInstance); @@ -3030,6 +3090,7 @@ public static T PolymorphicFactory(IntPtr Handle) where T : class case 0xDE92510BD2112288: Object = new CObjectIterator(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::ObjectIterator" case 0xF4196034E2B9FDE6: Object = new CMeshObjectIterator(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::MeshObjectIterator" case 0x564DE4217ED7614A: Object = new CComponentsObjectIterator(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::ComponentsObjectIterator" + case 0xAFF01F512E1FF6AE: Object = new CBooleanObjectIterator(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObjectIterator" case 0x4BD32B4870FFC03B: Object = new CTexture2DIterator(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::Texture2DIterator" case 0x65E6EDD9362C79CB: Object = new CBaseMaterialGroupIterator(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::BaseMaterialGroupIterator" case 0x10274A1757C729C0: Object = new CColorGroupIterator(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::ColorGroupIterator" @@ -3045,6 +3106,7 @@ public static T PolymorphicFactory(IntPtr Handle) where T : class case 0x2DA2136F577A779C: Object = new CObject(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::Object" case 0x3B3A6DC6EC610497: Object = new CMeshObject(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::MeshObject" case 0xE8A7D9C192EFD0E2: Object = new CLevelSet(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::LevelSet" + case 0x85FA0E8806B6C357: Object = new CBooleanObject(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObject" case 0x63B3B461B30B4BA5: Object = new CBeamLattice(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::BeamLattice" case 0x4DF17E76926221C2: Object = new CFunctionReference(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::FunctionReference" case 0xD85B5B6143E787E3: Object = new CVolumeDataColor(Handle) as T; break; // First 64 bits of SHA1 of a string: "Lib3MF::VolumeDataColor" @@ -3600,6 +3662,22 @@ public CComponentsObject GetCurrentComponentsObject () } + public class CBooleanObjectIterator : CResourceIterator + { + public CBooleanObjectIterator (IntPtr NewHandle) : base (NewHandle) + { + } + + public CBooleanObject GetCurrentBooleanObject () + { + IntPtr newResource = IntPtr.Zero; + + CheckError(Internal.Lib3MFWrapper.BooleanObjectIterator_GetCurrentBooleanObject (Handle, out newResource)); + return Internal.Lib3MFWrapper.PolymorphicFactory(newResource); + } + + } + public class CTexture2DIterator : CResourceIterator { public CTexture2DIterator (IntPtr NewHandle) : base (NewHandle) @@ -4133,6 +4211,14 @@ public bool IsLevelSetObject () return (resultIsLevelSetObject != 0); } + public bool IsBooleanObject () + { + Byte resultIsBooleanObject = 0; + + CheckError(Internal.Lib3MFWrapper.Object_IsBooleanObject (Handle, out resultIsBooleanObject)); + return (resultIsBooleanObject != 0); + } + public bool IsValid () { Byte resultIsValid = 0; @@ -4647,6 +4733,118 @@ public void SetVolumeData (CVolumeData ATheVolumeData) } + public class CBooleanObject : CObject + { + public CBooleanObject (IntPtr NewHandle) : base (NewHandle) + { + } + + public void SetBaseObject (CObject ABaseObject, sTransform ATransform) + { + IntPtr ABaseObjectHandle = IntPtr.Zero; + if (ABaseObject != null) + ABaseObjectHandle = ABaseObject.GetHandle(); + Internal.InternalTransform intTransform = Internal.Lib3MFWrapper.convertStructToInternal_Transform (ATransform); + + CheckError(Internal.Lib3MFWrapper.BooleanObject_SetBaseObject (Handle, ABaseObjectHandle, ref intTransform)); + } + + public CObject GetBaseObject () + { + IntPtr newBaseObject = IntPtr.Zero; + + CheckError(Internal.Lib3MFWrapper.BooleanObject_GetBaseObject (Handle, out newBaseObject)); + return Internal.Lib3MFWrapper.PolymorphicFactory(newBaseObject); + } + + public void SetBaseTransform (sTransform ATransform) + { + Internal.InternalTransform intTransform = Internal.Lib3MFWrapper.convertStructToInternal_Transform (ATransform); + + CheckError(Internal.Lib3MFWrapper.BooleanObject_SetBaseTransform (Handle, ref intTransform)); + } + + public sTransform GetBaseTransform () + { + Internal.InternalTransform intresultTransform; + + CheckError(Internal.Lib3MFWrapper.BooleanObject_GetBaseTransform (Handle, out intresultTransform)); + return Internal.Lib3MFWrapper.convertInternalToStruct_Transform (intresultTransform); + } + + public void SetOperation (eBooleanOperation AOperation) + { + Int32 enumOperation = (Int32) AOperation; + + CheckError(Internal.Lib3MFWrapper.BooleanObject_SetOperation (Handle, enumOperation)); + } + + public eBooleanOperation GetOperation () + { + Int32 resultOperation = 0; + + CheckError(Internal.Lib3MFWrapper.BooleanObject_GetOperation (Handle, out resultOperation)); + return (eBooleanOperation) (resultOperation); + } + + public void SetCSGModeEnabled (bool ACSGModeEnabled) + { + + CheckError(Internal.Lib3MFWrapper.BooleanObject_SetCSGModeEnabled (Handle, (Byte)( ACSGModeEnabled ? 1 : 0 ))); + } + + public bool GetCSGModeEnabled () + { + Byte resultCSGModeEnabled = 0; + + CheckError(Internal.Lib3MFWrapper.BooleanObject_GetCSGModeEnabled (Handle, out resultCSGModeEnabled)); + return (resultCSGModeEnabled != 0); + } + + public void SetExtractionGridResolution (UInt32 AGridResolution) + { + + CheckError(Internal.Lib3MFWrapper.BooleanObject_SetExtractionGridResolution (Handle, AGridResolution)); + } + + public UInt32 GetExtractionGridResolution () + { + UInt32 resultGridResolution = 0; + + CheckError(Internal.Lib3MFWrapper.BooleanObject_GetExtractionGridResolution (Handle, out resultGridResolution)); + return resultGridResolution; + } + + public UInt32 GetOperandCount () + { + UInt32 resultCount = 0; + + CheckError(Internal.Lib3MFWrapper.BooleanObject_GetOperandCount (Handle, out resultCount)); + return resultCount; + } + + public void AddOperand (CMeshObject AOperandObject, sTransform ATransform) + { + IntPtr AOperandObjectHandle = IntPtr.Zero; + if (AOperandObject != null) + AOperandObjectHandle = AOperandObject.GetHandle(); + Internal.InternalTransform intTransform = Internal.Lib3MFWrapper.convertStructToInternal_Transform (ATransform); + + CheckError(Internal.Lib3MFWrapper.BooleanObject_AddOperand (Handle, AOperandObjectHandle, ref intTransform)); + } + + public sTransform GetOperand (UInt32 AIndex, out CMeshObject AOperandObject) + { + IntPtr newOperandObject = IntPtr.Zero; + Internal.InternalTransform intresultTransform; + + CheckError(Internal.Lib3MFWrapper.BooleanObject_GetOperand (Handle, AIndex, out newOperandObject, out intresultTransform)); + AOperandObject = Internal.Lib3MFWrapper.PolymorphicFactory(newOperandObject); + return Internal.Lib3MFWrapper.convertInternalToStruct_Transform (intresultTransform); + } + + } + public class CBeamLattice : CBase { public CBeamLattice (IntPtr NewHandle) : base (NewHandle) @@ -9306,6 +9504,14 @@ public CComponentsObject GetComponentsObjectByID (UInt32 AUniqueResourceID) return Internal.Lib3MFWrapper.PolymorphicFactory(newComponentsObjectInstance); } + public CBooleanObject GetBooleanObjectByID (UInt32 AUniqueResourceID) + { + IntPtr newBooleanObjectInstance = IntPtr.Zero; + + CheckError(Internal.Lib3MFWrapper.Model_GetBooleanObjectByID (Handle, AUniqueResourceID, out newBooleanObjectInstance)); + return Internal.Lib3MFWrapper.PolymorphicFactory(newBooleanObjectInstance); + } + public CColorGroup GetColorGroupByID (UInt32 AUniqueResourceID) { IntPtr newColorGroupInstance = IntPtr.Zero; @@ -9401,6 +9607,14 @@ public CComponentsObjectIterator GetComponentsObjects () return Internal.Lib3MFWrapper.PolymorphicFactory(newResourceIterator); } + public CBooleanObjectIterator GetBooleanObjects () + { + IntPtr newResourceIterator = IntPtr.Zero; + + CheckError(Internal.Lib3MFWrapper.Model_GetBooleanObjects (Handle, out newResourceIterator)); + return Internal.Lib3MFWrapper.PolymorphicFactory(newResourceIterator); + } + public CTexture2DIterator GetTexture2Ds () { IntPtr newResourceIterator = IntPtr.Zero; @@ -9498,6 +9712,14 @@ public CComponentsObject AddComponentsObject () return Internal.Lib3MFWrapper.PolymorphicFactory(newComponentsObjectInstance); } + public CBooleanObject AddBooleanObject () + { + IntPtr newBooleanObjectInstance = IntPtr.Zero; + + CheckError(Internal.Lib3MFWrapper.Model_AddBooleanObject (Handle, out newBooleanObjectInstance)); + return Internal.Lib3MFWrapper.PolymorphicFactory(newBooleanObjectInstance); + } + public CSliceStack AddSliceStack (Double AZBottom) { IntPtr newSliceStackInstance = IntPtr.Zero; diff --git a/Autogenerated/Bindings/Cpp/lib3mf_abi.hpp b/Autogenerated/Bindings/Cpp/lib3mf_abi.hpp index 0c558039c..d8ee45244 100644 --- a/Autogenerated/Bindings/Cpp/lib3mf_abi.hpp +++ b/Autogenerated/Bindings/Cpp/lib3mf_abi.hpp @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++-Header file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -497,6 +497,19 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_meshobjectiterator_getcurrentmeshobject(Lib3 */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_componentsobjectiterator_getcurrentcomponentsobject(Lib3MF_ComponentsObjectIterator pComponentsObjectIterator, Lib3MF_ComponentsObject * pResource); +/************************************************************************************************************************* + Class definition for BooleanObjectIterator +**************************************************************************************************************************/ + +/** +* Returns the BooleanObject the iterator points at. +* +* @param[in] pBooleanObjectIterator - BooleanObjectIterator instance. +* @param[out] pResource - returns the BooleanObject instance. +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobjectiterator_getcurrentbooleanobject(Lib3MF_BooleanObjectIterator pBooleanObjectIterator, Lib3MF_BooleanObject * pResource); + /************************************************************************************************************************* Class definition for Texture2DIterator **************************************************************************************************************************/ @@ -1011,6 +1024,15 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_object_iscomponentsobject(Lib3MF_Object pObj */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_object_islevelsetobject(Lib3MF_Object pObject, bool * pIsLevelSetObject); +/** +* Retrieves, if an object is a boolean object +* +* @param[in] pObject - Object instance. +* @param[out] pIsBooleanObject - returns, whether the object is a boolean object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_object_isbooleanobject(Lib3MF_Object pObject, bool * pIsBooleanObject); + /** * Retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. * @@ -1561,6 +1583,131 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_levelset_getvolumedata(Lib3MF_LevelSet pLeve */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_levelset_setvolumedata(Lib3MF_LevelSet pLevelSet, Lib3MF_VolumeData pTheVolumeData); +/************************************************************************************************************************* + Class definition for BooleanObject +**************************************************************************************************************************/ + +/** +* Sets the base object and transform for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pBaseObject - base object of the boolean shape +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setbaseobject(Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object pBaseObject, const Lib3MF::sTransform * pTransform); + +/** +* Returns the base object of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pBaseObject - base object of the boolean shape +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getbaseobject(Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object * pBaseObject); + +/** +* Sets the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setbasetransform(Lib3MF_BooleanObject pBooleanObject, const Lib3MF::sTransform * pTransform); + +/** +* Returns the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getbasetransform(Lib3MF_BooleanObject pBooleanObject, Lib3MF::sTransform * pTransform); + +/** +* Sets the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] eOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setoperation(Lib3MF_BooleanObject pBooleanObject, Lib3MF::eBooleanOperation eOperation); + +/** +* Returns the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getoperation(Lib3MF_BooleanObject pBooleanObject, Lib3MF::eBooleanOperation * pOperation); + +/** +* Enables or disables CSG field evaluation for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] bCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setcsgmodeenabled(Lib3MF_BooleanObject pBooleanObject, bool bCSGModeEnabled); + +/** +* Returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getcsgmodeenabled(Lib3MF_BooleanObject pBooleanObject, bool * pCSGModeEnabled); + +/** +* Sets the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setextractiongridresolution(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nGridResolution); + +/** +* Returns the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getextractiongridresolution(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pGridResolution); + +/** +* Returns the number of operands in the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCount - number of operands in the boolean sequence +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getoperandcount(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pCount); + +/** +* Adds an operand object to the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pOperandObject - mesh object used as operand +* @param[in] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_addoperand(Lib3MF_BooleanObject pBooleanObject, Lib3MF_MeshObject pOperandObject, const Lib3MF::sTransform * pTransform); + +/** +* Returns one operand object and transform from the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nIndex - index of the operand in the boolean sequence +* @param[out] pOperandObject - mesh object used as operand +* @param[out] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getoperand(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nIndex, Lib3MF_MeshObject * pOperandObject, Lib3MF::sTransform * pTransform); + /************************************************************************************************************************* Class definition for BeamLattice **************************************************************************************************************************/ @@ -6178,6 +6325,16 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getmeshobjectbyid(Lib3MF_Model pModel, */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getcomponentsobjectbyid(Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* finds a boolean object by its UniqueResourceID +* +* @param[in] pModel - Model instance. +* @param[in] nUniqueResourceID - UniqueResourceID +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getbooleanobjectbyid(Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * finds a model color group by its UniqueResourceID * @@ -6283,6 +6440,15 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getmeshobjects(Lib3MF_Model pModel, Li */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getcomponentsobjects(Lib3MF_Model pModel, Lib3MF_ComponentsObjectIterator * pResourceIterator); +/** +* creates a resource iterator instance with all boolean object resources. +* +* @param[in] pModel - Model instance. +* @param[out] pResourceIterator - returns the iterator instance. +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getbooleanobjects(Lib3MF_Model pModel, Lib3MF_BooleanObjectIterator * pResourceIterator); + /** * creates a Texture2DIterator instance with all texture2d resources. * @@ -6391,6 +6557,15 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_addmeshobject(Lib3MF_Model pModel, Lib */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_addcomponentsobject(Lib3MF_Model pModel, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* adds an empty boolean object to the model. +* +* @param[in] pModel - Model instance. +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_addbooleanobject(Lib3MF_Model pModel, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * creates a new model slicestack by its id * diff --git a/Autogenerated/Bindings/Cpp/lib3mf_implicit.hpp b/Autogenerated/Bindings/Cpp/lib3mf_implicit.hpp index d422116e3..155fca4b1 100644 --- a/Autogenerated/Bindings/Cpp/lib3mf_implicit.hpp +++ b/Autogenerated/Bindings/Cpp/lib3mf_implicit.hpp @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++-Header file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -67,6 +67,7 @@ class CSliceStackIterator; class CObjectIterator; class CMeshObjectIterator; class CComponentsObjectIterator; +class CBooleanObjectIterator; class CTexture2DIterator; class CBaseMaterialGroupIterator; class CColorGroupIterator; @@ -82,6 +83,7 @@ class CTriangleSet; class CObject; class CMeshObject; class CLevelSet; +class CBooleanObject; class CBeamLattice; class CFunctionReference; class CVolumeDataColor; @@ -191,6 +193,7 @@ typedef CSliceStackIterator CLib3MFSliceStackIterator; typedef CObjectIterator CLib3MFObjectIterator; typedef CMeshObjectIterator CLib3MFMeshObjectIterator; typedef CComponentsObjectIterator CLib3MFComponentsObjectIterator; +typedef CBooleanObjectIterator CLib3MFBooleanObjectIterator; typedef CTexture2DIterator CLib3MFTexture2DIterator; typedef CBaseMaterialGroupIterator CLib3MFBaseMaterialGroupIterator; typedef CColorGroupIterator CLib3MFColorGroupIterator; @@ -206,6 +209,7 @@ typedef CTriangleSet CLib3MFTriangleSet; typedef CObject CLib3MFObject; typedef CMeshObject CLib3MFMeshObject; typedef CLevelSet CLib3MFLevelSet; +typedef CBooleanObject CLib3MFBooleanObject; typedef CBeamLattice CLib3MFBeamLattice; typedef CFunctionReference CLib3MFFunctionReference; typedef CVolumeDataColor CLib3MFVolumeDataColor; @@ -315,6 +319,7 @@ typedef std::shared_ptr PSliceStackIterator; typedef std::shared_ptr PObjectIterator; typedef std::shared_ptr PMeshObjectIterator; typedef std::shared_ptr PComponentsObjectIterator; +typedef std::shared_ptr PBooleanObjectIterator; typedef std::shared_ptr PTexture2DIterator; typedef std::shared_ptr PBaseMaterialGroupIterator; typedef std::shared_ptr PColorGroupIterator; @@ -330,6 +335,7 @@ typedef std::shared_ptr PTriangleSet; typedef std::shared_ptr PObject; typedef std::shared_ptr PMeshObject; typedef std::shared_ptr PLevelSet; +typedef std::shared_ptr PBooleanObject; typedef std::shared_ptr PBeamLattice; typedef std::shared_ptr PFunctionReference; typedef std::shared_ptr PVolumeDataColor; @@ -439,6 +445,7 @@ typedef PSliceStackIterator PLib3MFSliceStackIterator; typedef PObjectIterator PLib3MFObjectIterator; typedef PMeshObjectIterator PLib3MFMeshObjectIterator; typedef PComponentsObjectIterator PLib3MFComponentsObjectIterator; +typedef PBooleanObjectIterator PLib3MFBooleanObjectIterator; typedef PTexture2DIterator PLib3MFTexture2DIterator; typedef PBaseMaterialGroupIterator PLib3MFBaseMaterialGroupIterator; typedef PColorGroupIterator PLib3MFColorGroupIterator; @@ -454,6 +461,7 @@ typedef PTriangleSet PLib3MFTriangleSet; typedef PObject PLib3MFObject; typedef PMeshObject PLib3MFMeshObject; typedef PLevelSet PLib3MFLevelSet; +typedef PBooleanObject PLib3MFBooleanObject; typedef PBeamLattice PLib3MFBeamLattice; typedef PFunctionReference PLib3MFFunctionReference; typedef PVolumeDataColor PLib3MFVolumeDataColor; @@ -855,6 +863,7 @@ class CWrapper { friend class CObjectIterator; friend class CMeshObjectIterator; friend class CComponentsObjectIterator; + friend class CBooleanObjectIterator; friend class CTexture2DIterator; friend class CBaseMaterialGroupIterator; friend class CColorGroupIterator; @@ -870,6 +879,7 @@ class CWrapper { friend class CObject; friend class CMeshObject; friend class CLevelSet; + friend class CBooleanObject; friend class CBeamLattice; friend class CFunctionReference; friend class CVolumeDataColor; @@ -1212,6 +1222,23 @@ class CComponentsObjectIterator : public CResourceIterator { inline PComponentsObject GetCurrentComponentsObject(); }; +/************************************************************************************************************************* + Class CBooleanObjectIterator +**************************************************************************************************************************/ +class CBooleanObjectIterator : public CResourceIterator { +public: + + /** + * CBooleanObjectIterator::CBooleanObjectIterator - Constructor for BooleanObjectIterator class. + */ + CBooleanObjectIterator(CWrapper* pWrapper, Lib3MFHandle pHandle) + : CResourceIterator(pWrapper, pHandle) + { + } + + inline PBooleanObject GetCurrentBooleanObject(); +}; + /************************************************************************************************************************* Class CTexture2DIterator **************************************************************************************************************************/ @@ -1466,6 +1493,7 @@ class CObject : public CResource { inline bool IsMeshObject(); inline bool IsComponentsObject(); inline bool IsLevelSetObject(); + inline bool IsBooleanObject(); inline bool IsValid(); inline void SetAttachmentAsThumbnail(classParam pAttachment); inline PAttachment GetThumbnailAttachment(); @@ -1557,6 +1585,35 @@ class CLevelSet : public CObject { inline void SetVolumeData(classParam pTheVolumeData); }; +/************************************************************************************************************************* + Class CBooleanObject +**************************************************************************************************************************/ +class CBooleanObject : public CObject { +public: + + /** + * CBooleanObject::CBooleanObject - Constructor for BooleanObject class. + */ + CBooleanObject(CWrapper* pWrapper, Lib3MFHandle pHandle) + : CObject(pWrapper, pHandle) + { + } + + inline void SetBaseObject(classParam pBaseObject, const sTransform & Transform); + inline PObject GetBaseObject(); + inline void SetBaseTransform(const sTransform & Transform); + inline sTransform GetBaseTransform(); + inline void SetOperation(const eBooleanOperation eOperation); + inline eBooleanOperation GetOperation(); + inline void SetCSGModeEnabled(const bool bCSGModeEnabled); + inline bool GetCSGModeEnabled(); + inline void SetExtractionGridResolution(const Lib3MF_uint32 nGridResolution); + inline Lib3MF_uint32 GetExtractionGridResolution(); + inline Lib3MF_uint32 GetOperandCount(); + inline void AddOperand(classParam pOperandObject, const sTransform & Transform); + inline sTransform GetOperand(const Lib3MF_uint32 nIndex, PMeshObject & pOperandObject); +}; + /************************************************************************************************************************* Class CBeamLattice **************************************************************************************************************************/ @@ -3484,6 +3541,7 @@ class CModel : public CBase { inline PMultiPropertyGroup GetMultiPropertyGroupByID(const Lib3MF_uint32 nUniqueResourceID); inline PMeshObject GetMeshObjectByID(const Lib3MF_uint32 nUniqueResourceID); inline PComponentsObject GetComponentsObjectByID(const Lib3MF_uint32 nUniqueResourceID); + inline PBooleanObject GetBooleanObjectByID(const Lib3MF_uint32 nUniqueResourceID); inline PColorGroup GetColorGroupByID(const Lib3MF_uint32 nUniqueResourceID); inline PSliceStack GetSliceStackByID(const Lib3MF_uint32 nUniqueResourceID); inline PLevelSet GetLevelSetByID(const Lib3MF_uint32 nUniqueResourceID); @@ -3495,6 +3553,7 @@ class CModel : public CBase { inline PObjectIterator GetObjects(); inline PMeshObjectIterator GetMeshObjects(); inline PComponentsObjectIterator GetComponentsObjects(); + inline PBooleanObjectIterator GetBooleanObjects(); inline PTexture2DIterator GetTexture2Ds(); inline PBaseMaterialGroupIterator GetBaseMaterialGroups(); inline PColorGroupIterator GetColorGroups(); @@ -3507,6 +3566,7 @@ class CModel : public CBase { inline void MergeFromModel(classParam pModelInstance); inline PMeshObject AddMeshObject(); inline PComponentsObject AddComponentsObject(); + inline PBooleanObject AddBooleanObject(); inline PSliceStack AddSliceStack(const Lib3MF_double dZBottom); inline PTexture2D AddTexture2DFromAttachment(classParam pTextureAttachment); inline PBaseMaterialGroup AddBaseMaterialGroup(); @@ -3567,6 +3627,7 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) case 0xDE92510BD2112288UL: return new CObjectIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::ObjectIterator" case 0xF4196034E2B9FDE6UL: return new CMeshObjectIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::MeshObjectIterator" case 0x564DE4217ED7614AUL: return new CComponentsObjectIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::ComponentsObjectIterator" + case 0xAFF01F512E1FF6AEUL: return new CBooleanObjectIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObjectIterator" case 0x4BD32B4870FFC03BUL: return new CTexture2DIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::Texture2DIterator" case 0x65E6EDD9362C79CBUL: return new CBaseMaterialGroupIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::BaseMaterialGroupIterator" case 0x10274A1757C729C0UL: return new CColorGroupIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::ColorGroupIterator" @@ -3582,6 +3643,7 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) case 0x2DA2136F577A779CUL: return new CObject(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::Object" case 0x3B3A6DC6EC610497UL: return new CMeshObject(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::MeshObject" case 0xE8A7D9C192EFD0E2UL: return new CLevelSet(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::LevelSet" + case 0x85FA0E8806B6C357UL: return new CBooleanObject(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObject" case 0x63B3B461B30B4BA5UL: return new CBeamLattice(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::BeamLattice" case 0x4DF17E76926221C2UL: return new CFunctionReference(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::FunctionReference" case 0xD85B5B6143E787E3UL: return new CVolumeDataColor(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::VolumeDataColor" @@ -4497,6 +4559,25 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hResource))); } + /** + * Method definitions for class CBooleanObjectIterator + */ + + /** + * CBooleanObjectIterator::GetCurrentBooleanObject - Returns the BooleanObject the iterator points at. + * @return returns the BooleanObject instance. + */ + PBooleanObject CBooleanObjectIterator::GetCurrentBooleanObject() + { + Lib3MFHandle hResource = (Lib3MFHandle)nullptr; + CheckError(lib3mf_booleanobjectiterator_getcurrentbooleanobject(m_pHandle, &hResource)); + + if (!hResource) { + CheckError(LIB3MF_ERROR_INVALIDPARAM); + } + return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hResource))); + } + /** * Method definitions for class CTexture2DIterator */ @@ -5147,6 +5228,18 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) return resultIsLevelSetObject; } + /** + * CObject::IsBooleanObject - Retrieves, if an object is a boolean object + * @return returns, whether the object is a boolean object + */ + bool CObject::IsBooleanObject() + { + bool resultIsBooleanObject = 0; + CheckError(lib3mf_object_isbooleanobject(m_pHandle, &resultIsBooleanObject)); + + return resultIsBooleanObject; + } + /** * CObject::IsValid - Retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. * @return returns whether the object is a valid object description @@ -5842,6 +5935,163 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) CheckError(lib3mf_levelset_setvolumedata(m_pHandle, hTheVolumeData)); } + /** + * Method definitions for class CBooleanObject + */ + + /** + * CBooleanObject::SetBaseObject - Sets the base object and transform for the boolean shape. + * @param[in] pBaseObject - base object of the boolean shape + * @param[in] Transform - transform applied to the base object + */ + void CBooleanObject::SetBaseObject(classParam pBaseObject, const sTransform & Transform) + { + Lib3MFHandle hBaseObject = pBaseObject.GetHandle(); + CheckError(lib3mf_booleanobject_setbaseobject(m_pHandle, hBaseObject, &Transform)); + } + + /** + * CBooleanObject::GetBaseObject - Returns the base object of the boolean shape. + * @return base object of the boolean shape + */ + PObject CBooleanObject::GetBaseObject() + { + Lib3MFHandle hBaseObject = (Lib3MFHandle)nullptr; + CheckError(lib3mf_booleanobject_getbaseobject(m_pHandle, &hBaseObject)); + + if (!hBaseObject) { + CheckError(LIB3MF_ERROR_INVALIDPARAM); + } + return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hBaseObject))); + } + + /** + * CBooleanObject::SetBaseTransform - Sets the base transform of the boolean shape. + * @param[in] Transform - transform applied to the base object + */ + void CBooleanObject::SetBaseTransform(const sTransform & Transform) + { + CheckError(lib3mf_booleanobject_setbasetransform(m_pHandle, &Transform)); + } + + /** + * CBooleanObject::GetBaseTransform - Returns the base transform of the boolean shape. + * @return transform applied to the base object + */ + sTransform CBooleanObject::GetBaseTransform() + { + sTransform resultTransform; + CheckError(lib3mf_booleanobject_getbasetransform(m_pHandle, &resultTransform)); + + return resultTransform; + } + + /** + * CBooleanObject::SetOperation - Sets the boolean operation used for the boolean shape. + * @param[in] eOperation - boolean operation used for the shape + */ + void CBooleanObject::SetOperation(const eBooleanOperation eOperation) + { + CheckError(lib3mf_booleanobject_setoperation(m_pHandle, eOperation)); + } + + /** + * CBooleanObject::GetOperation - Returns the boolean operation used for the boolean shape. + * @return boolean operation used for the shape + */ + eBooleanOperation CBooleanObject::GetOperation() + { + eBooleanOperation resultOperation = (eBooleanOperation) 0; + CheckError(lib3mf_booleanobject_getoperation(m_pHandle, &resultOperation)); + + return resultOperation; + } + + /** + * CBooleanObject::SetCSGModeEnabled - Enables or disables CSG field evaluation for boolean-to-mesh materialization. + * @param[in] bCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback + */ + void CBooleanObject::SetCSGModeEnabled(const bool bCSGModeEnabled) + { + CheckError(lib3mf_booleanobject_setcsgmodeenabled(m_pHandle, bCSGModeEnabled)); + } + + /** + * CBooleanObject::GetCSGModeEnabled - Returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. + * @return if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback + */ + bool CBooleanObject::GetCSGModeEnabled() + { + bool resultCSGModeEnabled = 0; + CheckError(lib3mf_booleanobject_getcsgmodeenabled(m_pHandle, &resultCSGModeEnabled)); + + return resultCSGModeEnabled; + } + + /** + * CBooleanObject::SetExtractionGridResolution - Sets the extraction grid resolution used for boolean-to-mesh materialization. + * @param[in] nGridResolution - extraction grid resolution for boolean surface extraction + */ + void CBooleanObject::SetExtractionGridResolution(const Lib3MF_uint32 nGridResolution) + { + CheckError(lib3mf_booleanobject_setextractiongridresolution(m_pHandle, nGridResolution)); + } + + /** + * CBooleanObject::GetExtractionGridResolution - Returns the extraction grid resolution used for boolean-to-mesh materialization. + * @return extraction grid resolution for boolean surface extraction + */ + Lib3MF_uint32 CBooleanObject::GetExtractionGridResolution() + { + Lib3MF_uint32 resultGridResolution = 0; + CheckError(lib3mf_booleanobject_getextractiongridresolution(m_pHandle, &resultGridResolution)); + + return resultGridResolution; + } + + /** + * CBooleanObject::GetOperandCount - Returns the number of operands in the boolean sequence. + * @return number of operands in the boolean sequence + */ + Lib3MF_uint32 CBooleanObject::GetOperandCount() + { + Lib3MF_uint32 resultCount = 0; + CheckError(lib3mf_booleanobject_getoperandcount(m_pHandle, &resultCount)); + + return resultCount; + } + + /** + * CBooleanObject::AddOperand - Adds an operand object to the boolean sequence. + * @param[in] pOperandObject - mesh object used as operand + * @param[in] Transform - transform applied to the operand object + */ + void CBooleanObject::AddOperand(classParam pOperandObject, const sTransform & Transform) + { + Lib3MFHandle hOperandObject = pOperandObject.GetHandle(); + CheckError(lib3mf_booleanobject_addoperand(m_pHandle, hOperandObject, &Transform)); + } + + /** + * CBooleanObject::GetOperand - Returns one operand object and transform from the boolean sequence. + * @param[in] nIndex - index of the operand in the boolean sequence + * @param[out] pOperandObject - mesh object used as operand + * @return transform applied to the operand object + */ + sTransform CBooleanObject::GetOperand(const Lib3MF_uint32 nIndex, PMeshObject & pOperandObject) + { + Lib3MFHandle hOperandObject = (Lib3MFHandle)nullptr; + sTransform resultTransform; + CheckError(lib3mf_booleanobject_getoperand(m_pHandle, nIndex, &hOperandObject, &resultTransform)); + if (!hOperandObject) { + CheckError(LIB3MF_ERROR_INVALIDPARAM); + } else { + pOperandObject = std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hOperandObject))); + } + + return resultTransform; + } + /** * Method definitions for class CBeamLattice */ @@ -12054,6 +12304,22 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hComponentsObjectInstance))); } + /** + * CModel::GetBooleanObjectByID - finds a boolean object by its UniqueResourceID + * @param[in] nUniqueResourceID - UniqueResourceID + * @return returns the boolean object instance + */ + PBooleanObject CModel::GetBooleanObjectByID(const Lib3MF_uint32 nUniqueResourceID) + { + Lib3MFHandle hBooleanObjectInstance = (Lib3MFHandle)nullptr; + CheckError(lib3mf_model_getbooleanobjectbyid(m_pHandle, nUniqueResourceID, &hBooleanObjectInstance)); + + if (!hBooleanObjectInstance) { + CheckError(LIB3MF_ERROR_INVALIDPARAM); + } + return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hBooleanObjectInstance))); + } + /** * CModel::GetColorGroupByID - finds a model color group by its UniqueResourceID * @param[in] nUniqueResourceID - UniqueResourceID @@ -12214,6 +12480,21 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hResourceIterator))); } + /** + * CModel::GetBooleanObjects - creates a resource iterator instance with all boolean object resources. + * @return returns the iterator instance. + */ + PBooleanObjectIterator CModel::GetBooleanObjects() + { + Lib3MFHandle hResourceIterator = (Lib3MFHandle)nullptr; + CheckError(lib3mf_model_getbooleanobjects(m_pHandle, &hResourceIterator)); + + if (!hResourceIterator) { + CheckError(LIB3MF_ERROR_INVALIDPARAM); + } + return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hResourceIterator))); + } + /** * CModel::GetTexture2Ds - creates a Texture2DIterator instance with all texture2d resources. * @return returns the iterator instance. @@ -12389,6 +12670,21 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hComponentsObjectInstance))); } + /** + * CModel::AddBooleanObject - adds an empty boolean object to the model. + * @return returns the boolean object instance + */ + PBooleanObject CModel::AddBooleanObject() + { + Lib3MFHandle hBooleanObjectInstance = (Lib3MFHandle)nullptr; + CheckError(lib3mf_model_addbooleanobject(m_pHandle, &hBooleanObjectInstance)); + + if (!hBooleanObjectInstance) { + CheckError(LIB3MF_ERROR_INVALIDPARAM); + } + return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hBooleanObjectInstance))); + } + /** * CModel::AddSliceStack - creates a new model slicestack by its id * @param[in] dZBottom - Bottom Z value of the slicestack diff --git a/Autogenerated/Bindings/Cpp/lib3mf_types.hpp b/Autogenerated/Bindings/Cpp/lib3mf_types.hpp index 2ae400ad6..09f6a3441 100644 --- a/Autogenerated/Bindings/Cpp/lib3mf_types.hpp +++ b/Autogenerated/Bindings/Cpp/lib3mf_types.hpp @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++-Header file with basic types in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -83,7 +83,7 @@ typedef void * Lib3MF_pvoid; **************************************************************************************************************************/ #define LIB3MF_VERSION_MAJOR 2 -#define LIB3MF_VERSION_MINOR 5 +#define LIB3MF_VERSION_MINOR 6 #define LIB3MF_VERSION_MICRO 0 #define LIB3MF_VERSION_PRERELEASEINFO "" #define LIB3MF_VERSION_BUILDINFO "" @@ -219,6 +219,7 @@ typedef Lib3MFHandle Lib3MF_SliceStackIterator; typedef Lib3MFHandle Lib3MF_ObjectIterator; typedef Lib3MFHandle Lib3MF_MeshObjectIterator; typedef Lib3MFHandle Lib3MF_ComponentsObjectIterator; +typedef Lib3MFHandle Lib3MF_BooleanObjectIterator; typedef Lib3MFHandle Lib3MF_Texture2DIterator; typedef Lib3MFHandle Lib3MF_BaseMaterialGroupIterator; typedef Lib3MFHandle Lib3MF_ColorGroupIterator; @@ -234,6 +235,7 @@ typedef Lib3MFHandle Lib3MF_TriangleSet; typedef Lib3MFHandle Lib3MF_Object; typedef Lib3MFHandle Lib3MF_MeshObject; typedef Lib3MFHandle Lib3MF_LevelSet; +typedef Lib3MFHandle Lib3MF_BooleanObject; typedef Lib3MFHandle Lib3MF_BeamLattice; typedef Lib3MFHandle Lib3MF_FunctionReference; typedef Lib3MFHandle Lib3MF_VolumeDataColor; @@ -366,6 +368,12 @@ namespace Lib3MF { Surface = 4 }; + enum class eBooleanOperation : Lib3MF_int32 { + Union = 0, + Difference = 1, + Intersection = 2 + }; + enum class eTextureType : Lib3MF_int32 { Unknown = 0, PNG = 1, @@ -722,6 +730,7 @@ typedef Lib3MF::ePropertyType eLib3MFPropertyType; typedef Lib3MF::eSlicesMeshResolution eLib3MFSlicesMeshResolution; typedef Lib3MF::eModelUnit eLib3MFModelUnit; typedef Lib3MF::eObjectType eLib3MFObjectType; +typedef Lib3MF::eBooleanOperation eLib3MFBooleanOperation; typedef Lib3MF::eTextureType eLib3MFTextureType; typedef Lib3MF::eTextureTileStyle eLib3MFTextureTileStyle; typedef Lib3MF::eTextureFilter eLib3MFTextureFilter; diff --git a/Autogenerated/Bindings/CppDynamic/lib3mf_dynamic.h b/Autogenerated/Bindings/CppDynamic/lib3mf_dynamic.h index a4f0edca8..ecbea3fcb 100644 --- a/Autogenerated/Bindings/CppDynamic/lib3mf_dynamic.h +++ b/Autogenerated/Bindings/CppDynamic/lib3mf_dynamic.h @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++-Header file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -484,6 +484,19 @@ typedef Lib3MFResult (*PLib3MFMeshObjectIterator_GetCurrentMeshObjectPtr) (Lib3M */ typedef Lib3MFResult (*PLib3MFComponentsObjectIterator_GetCurrentComponentsObjectPtr) (Lib3MF_ComponentsObjectIterator pComponentsObjectIterator, Lib3MF_ComponentsObject * pResource); +/************************************************************************************************************************* + Class definition for BooleanObjectIterator +**************************************************************************************************************************/ + +/** +* Returns the BooleanObject the iterator points at. +* +* @param[in] pBooleanObjectIterator - BooleanObjectIterator instance. +* @param[out] pResource - returns the BooleanObject instance. +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr) (Lib3MF_BooleanObjectIterator pBooleanObjectIterator, Lib3MF_BooleanObject * pResource); + /************************************************************************************************************************* Class definition for Texture2DIterator **************************************************************************************************************************/ @@ -998,6 +1011,15 @@ typedef Lib3MFResult (*PLib3MFObject_IsComponentsObjectPtr) (Lib3MF_Object pObje */ typedef Lib3MFResult (*PLib3MFObject_IsLevelSetObjectPtr) (Lib3MF_Object pObject, bool * pIsLevelSetObject); +/** +* Retrieves, if an object is a boolean object +* +* @param[in] pObject - Object instance. +* @param[out] pIsBooleanObject - returns, whether the object is a boolean object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFObject_IsBooleanObjectPtr) (Lib3MF_Object pObject, bool * pIsBooleanObject); + /** * Retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. * @@ -1548,6 +1570,131 @@ typedef Lib3MFResult (*PLib3MFLevelSet_GetVolumeDataPtr) (Lib3MF_LevelSet pLevel */ typedef Lib3MFResult (*PLib3MFLevelSet_SetVolumeDataPtr) (Lib3MF_LevelSet pLevelSet, Lib3MF_VolumeData pTheVolumeData); +/************************************************************************************************************************* + Class definition for BooleanObject +**************************************************************************************************************************/ + +/** +* Sets the base object and transform for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pBaseObject - base object of the boolean shape +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetBaseObjectPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object pBaseObject, const Lib3MF::sTransform * pTransform); + +/** +* Returns the base object of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pBaseObject - base object of the boolean shape +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetBaseObjectPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object * pBaseObject); + +/** +* Sets the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetBaseTransformPtr) (Lib3MF_BooleanObject pBooleanObject, const Lib3MF::sTransform * pTransform); + +/** +* Returns the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetBaseTransformPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF::sTransform * pTransform); + +/** +* Sets the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] eOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetOperationPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF::eBooleanOperation eOperation); + +/** +* Returns the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetOperationPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF::eBooleanOperation * pOperation); + +/** +* Enables or disables CSG field evaluation for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] bCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetCSGModeEnabledPtr) (Lib3MF_BooleanObject pBooleanObject, bool bCSGModeEnabled); + +/** +* Returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetCSGModeEnabledPtr) (Lib3MF_BooleanObject pBooleanObject, bool * pCSGModeEnabled); + +/** +* Sets the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetExtractionGridResolutionPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nGridResolution); + +/** +* Returns the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetExtractionGridResolutionPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pGridResolution); + +/** +* Returns the number of operands in the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCount - number of operands in the boolean sequence +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetOperandCountPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pCount); + +/** +* Adds an operand object to the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pOperandObject - mesh object used as operand +* @param[in] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_AddOperandPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_MeshObject pOperandObject, const Lib3MF::sTransform * pTransform); + +/** +* Returns one operand object and transform from the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nIndex - index of the operand in the boolean sequence +* @param[out] pOperandObject - mesh object used as operand +* @param[out] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetOperandPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nIndex, Lib3MF_MeshObject * pOperandObject, Lib3MF::sTransform * pTransform); + /************************************************************************************************************************* Class definition for BeamLattice **************************************************************************************************************************/ @@ -6165,6 +6312,16 @@ typedef Lib3MFResult (*PLib3MFModel_GetMeshObjectByIDPtr) (Lib3MF_Model pModel, */ typedef Lib3MFResult (*PLib3MFModel_GetComponentsObjectByIDPtr) (Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* finds a boolean object by its UniqueResourceID +* +* @param[in] pModel - Model instance. +* @param[in] nUniqueResourceID - UniqueResourceID +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFModel_GetBooleanObjectByIDPtr) (Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * finds a model color group by its UniqueResourceID * @@ -6270,6 +6427,15 @@ typedef Lib3MFResult (*PLib3MFModel_GetMeshObjectsPtr) (Lib3MF_Model pModel, Lib */ typedef Lib3MFResult (*PLib3MFModel_GetComponentsObjectsPtr) (Lib3MF_Model pModel, Lib3MF_ComponentsObjectIterator * pResourceIterator); +/** +* creates a resource iterator instance with all boolean object resources. +* +* @param[in] pModel - Model instance. +* @param[out] pResourceIterator - returns the iterator instance. +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFModel_GetBooleanObjectsPtr) (Lib3MF_Model pModel, Lib3MF_BooleanObjectIterator * pResourceIterator); + /** * creates a Texture2DIterator instance with all texture2d resources. * @@ -6378,6 +6544,15 @@ typedef Lib3MFResult (*PLib3MFModel_AddMeshObjectPtr) (Lib3MF_Model pModel, Lib3 */ typedef Lib3MFResult (*PLib3MFModel_AddComponentsObjectPtr) (Lib3MF_Model pModel, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* adds an empty boolean object to the model. +* +* @param[in] pModel - Model instance. +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFModel_AddBooleanObjectPtr) (Lib3MF_Model pModel, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * creates a new model slicestack by its id * @@ -6928,6 +7103,7 @@ typedef struct { PLib3MFObjectIterator_GetCurrentObjectPtr m_ObjectIterator_GetCurrentObject; PLib3MFMeshObjectIterator_GetCurrentMeshObjectPtr m_MeshObjectIterator_GetCurrentMeshObject; PLib3MFComponentsObjectIterator_GetCurrentComponentsObjectPtr m_ComponentsObjectIterator_GetCurrentComponentsObject; + PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr m_BooleanObjectIterator_GetCurrentBooleanObject; PLib3MFTexture2DIterator_GetCurrentTexture2DPtr m_Texture2DIterator_GetCurrentTexture2D; PLib3MFBaseMaterialGroupIterator_GetCurrentBaseMaterialGroupPtr m_BaseMaterialGroupIterator_GetCurrentBaseMaterialGroup; PLib3MFColorGroupIterator_GetCurrentColorGroupPtr m_ColorGroupIterator_GetCurrentColorGroup; @@ -6976,6 +7152,7 @@ typedef struct { PLib3MFObject_IsMeshObjectPtr m_Object_IsMeshObject; PLib3MFObject_IsComponentsObjectPtr m_Object_IsComponentsObject; PLib3MFObject_IsLevelSetObjectPtr m_Object_IsLevelSetObject; + PLib3MFObject_IsBooleanObjectPtr m_Object_IsBooleanObject; PLib3MFObject_IsValidPtr m_Object_IsValid; PLib3MFObject_SetAttachmentAsThumbnailPtr m_Object_SetAttachmentAsThumbnail; PLib3MFObject_GetThumbnailAttachmentPtr m_Object_GetThumbnailAttachment; @@ -7033,6 +7210,19 @@ typedef struct { PLib3MFLevelSet_GetMeshPtr m_LevelSet_GetMesh; PLib3MFLevelSet_GetVolumeDataPtr m_LevelSet_GetVolumeData; PLib3MFLevelSet_SetVolumeDataPtr m_LevelSet_SetVolumeData; + PLib3MFBooleanObject_SetBaseObjectPtr m_BooleanObject_SetBaseObject; + PLib3MFBooleanObject_GetBaseObjectPtr m_BooleanObject_GetBaseObject; + PLib3MFBooleanObject_SetBaseTransformPtr m_BooleanObject_SetBaseTransform; + PLib3MFBooleanObject_GetBaseTransformPtr m_BooleanObject_GetBaseTransform; + PLib3MFBooleanObject_SetOperationPtr m_BooleanObject_SetOperation; + PLib3MFBooleanObject_GetOperationPtr m_BooleanObject_GetOperation; + PLib3MFBooleanObject_SetCSGModeEnabledPtr m_BooleanObject_SetCSGModeEnabled; + PLib3MFBooleanObject_GetCSGModeEnabledPtr m_BooleanObject_GetCSGModeEnabled; + PLib3MFBooleanObject_SetExtractionGridResolutionPtr m_BooleanObject_SetExtractionGridResolution; + PLib3MFBooleanObject_GetExtractionGridResolutionPtr m_BooleanObject_GetExtractionGridResolution; + PLib3MFBooleanObject_GetOperandCountPtr m_BooleanObject_GetOperandCount; + PLib3MFBooleanObject_AddOperandPtr m_BooleanObject_AddOperand; + PLib3MFBooleanObject_GetOperandPtr m_BooleanObject_GetOperand; PLib3MFBeamLattice_GetMinLengthPtr m_BeamLattice_GetMinLength; PLib3MFBeamLattice_SetMinLengthPtr m_BeamLattice_SetMinLength; PLib3MFBeamLattice_GetClippingPtr m_BeamLattice_GetClipping; @@ -7458,6 +7648,7 @@ typedef struct { PLib3MFModel_GetMultiPropertyGroupByIDPtr m_Model_GetMultiPropertyGroupByID; PLib3MFModel_GetMeshObjectByIDPtr m_Model_GetMeshObjectByID; PLib3MFModel_GetComponentsObjectByIDPtr m_Model_GetComponentsObjectByID; + PLib3MFModel_GetBooleanObjectByIDPtr m_Model_GetBooleanObjectByID; PLib3MFModel_GetColorGroupByIDPtr m_Model_GetColorGroupByID; PLib3MFModel_GetSliceStackByIDPtr m_Model_GetSliceStackByID; PLib3MFModel_GetLevelSetByIDPtr m_Model_GetLevelSetByID; @@ -7469,6 +7660,7 @@ typedef struct { PLib3MFModel_GetObjectsPtr m_Model_GetObjects; PLib3MFModel_GetMeshObjectsPtr m_Model_GetMeshObjects; PLib3MFModel_GetComponentsObjectsPtr m_Model_GetComponentsObjects; + PLib3MFModel_GetBooleanObjectsPtr m_Model_GetBooleanObjects; PLib3MFModel_GetTexture2DsPtr m_Model_GetTexture2Ds; PLib3MFModel_GetBaseMaterialGroupsPtr m_Model_GetBaseMaterialGroups; PLib3MFModel_GetColorGroupsPtr m_Model_GetColorGroups; @@ -7481,6 +7673,7 @@ typedef struct { PLib3MFModel_MergeFromModelPtr m_Model_MergeFromModel; PLib3MFModel_AddMeshObjectPtr m_Model_AddMeshObject; PLib3MFModel_AddComponentsObjectPtr m_Model_AddComponentsObject; + PLib3MFModel_AddBooleanObjectPtr m_Model_AddBooleanObject; PLib3MFModel_AddSliceStackPtr m_Model_AddSliceStack; PLib3MFModel_AddTexture2DFromAttachmentPtr m_Model_AddTexture2DFromAttachment; PLib3MFModel_AddBaseMaterialGroupPtr m_Model_AddBaseMaterialGroup; diff --git a/Autogenerated/Bindings/CppDynamic/lib3mf_dynamic.hpp b/Autogenerated/Bindings/CppDynamic/lib3mf_dynamic.hpp index ad14a9504..e6484c37a 100644 --- a/Autogenerated/Bindings/CppDynamic/lib3mf_dynamic.hpp +++ b/Autogenerated/Bindings/CppDynamic/lib3mf_dynamic.hpp @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++-Header file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -67,6 +67,7 @@ class CSliceStackIterator; class CObjectIterator; class CMeshObjectIterator; class CComponentsObjectIterator; +class CBooleanObjectIterator; class CTexture2DIterator; class CBaseMaterialGroupIterator; class CColorGroupIterator; @@ -82,6 +83,7 @@ class CTriangleSet; class CObject; class CMeshObject; class CLevelSet; +class CBooleanObject; class CBeamLattice; class CFunctionReference; class CVolumeDataColor; @@ -191,6 +193,7 @@ typedef CSliceStackIterator CLib3MFSliceStackIterator; typedef CObjectIterator CLib3MFObjectIterator; typedef CMeshObjectIterator CLib3MFMeshObjectIterator; typedef CComponentsObjectIterator CLib3MFComponentsObjectIterator; +typedef CBooleanObjectIterator CLib3MFBooleanObjectIterator; typedef CTexture2DIterator CLib3MFTexture2DIterator; typedef CBaseMaterialGroupIterator CLib3MFBaseMaterialGroupIterator; typedef CColorGroupIterator CLib3MFColorGroupIterator; @@ -206,6 +209,7 @@ typedef CTriangleSet CLib3MFTriangleSet; typedef CObject CLib3MFObject; typedef CMeshObject CLib3MFMeshObject; typedef CLevelSet CLib3MFLevelSet; +typedef CBooleanObject CLib3MFBooleanObject; typedef CBeamLattice CLib3MFBeamLattice; typedef CFunctionReference CLib3MFFunctionReference; typedef CVolumeDataColor CLib3MFVolumeDataColor; @@ -315,6 +319,7 @@ typedef std::shared_ptr PSliceStackIterator; typedef std::shared_ptr PObjectIterator; typedef std::shared_ptr PMeshObjectIterator; typedef std::shared_ptr PComponentsObjectIterator; +typedef std::shared_ptr PBooleanObjectIterator; typedef std::shared_ptr PTexture2DIterator; typedef std::shared_ptr PBaseMaterialGroupIterator; typedef std::shared_ptr PColorGroupIterator; @@ -330,6 +335,7 @@ typedef std::shared_ptr PTriangleSet; typedef std::shared_ptr PObject; typedef std::shared_ptr PMeshObject; typedef std::shared_ptr PLevelSet; +typedef std::shared_ptr PBooleanObject; typedef std::shared_ptr PBeamLattice; typedef std::shared_ptr PFunctionReference; typedef std::shared_ptr PVolumeDataColor; @@ -439,6 +445,7 @@ typedef PSliceStackIterator PLib3MFSliceStackIterator; typedef PObjectIterator PLib3MFObjectIterator; typedef PMeshObjectIterator PLib3MFMeshObjectIterator; typedef PComponentsObjectIterator PLib3MFComponentsObjectIterator; +typedef PBooleanObjectIterator PLib3MFBooleanObjectIterator; typedef PTexture2DIterator PLib3MFTexture2DIterator; typedef PBaseMaterialGroupIterator PLib3MFBaseMaterialGroupIterator; typedef PColorGroupIterator PLib3MFColorGroupIterator; @@ -454,6 +461,7 @@ typedef PTriangleSet PLib3MFTriangleSet; typedef PObject PLib3MFObject; typedef PMeshObject PLib3MFMeshObject; typedef PLevelSet PLib3MFLevelSet; +typedef PBooleanObject PLib3MFBooleanObject; typedef PBeamLattice PLib3MFBeamLattice; typedef PFunctionReference PLib3MFFunctionReference; typedef PVolumeDataColor PLib3MFVolumeDataColor; @@ -879,6 +887,7 @@ class CWrapper { friend class CObjectIterator; friend class CMeshObjectIterator; friend class CComponentsObjectIterator; + friend class CBooleanObjectIterator; friend class CTexture2DIterator; friend class CBaseMaterialGroupIterator; friend class CColorGroupIterator; @@ -894,6 +903,7 @@ class CWrapper { friend class CObject; friend class CMeshObject; friend class CLevelSet; + friend class CBooleanObject; friend class CBeamLattice; friend class CFunctionReference; friend class CVolumeDataColor; @@ -1236,6 +1246,23 @@ class CComponentsObjectIterator : public CResourceIterator { inline PComponentsObject GetCurrentComponentsObject(); }; +/************************************************************************************************************************* + Class CBooleanObjectIterator +**************************************************************************************************************************/ +class CBooleanObjectIterator : public CResourceIterator { +public: + + /** + * CBooleanObjectIterator::CBooleanObjectIterator - Constructor for BooleanObjectIterator class. + */ + CBooleanObjectIterator(CWrapper* pWrapper, Lib3MFHandle pHandle) + : CResourceIterator(pWrapper, pHandle) + { + } + + inline PBooleanObject GetCurrentBooleanObject(); +}; + /************************************************************************************************************************* Class CTexture2DIterator **************************************************************************************************************************/ @@ -1490,6 +1517,7 @@ class CObject : public CResource { inline bool IsMeshObject(); inline bool IsComponentsObject(); inline bool IsLevelSetObject(); + inline bool IsBooleanObject(); inline bool IsValid(); inline void SetAttachmentAsThumbnail(classParam pAttachment); inline PAttachment GetThumbnailAttachment(); @@ -1581,6 +1609,35 @@ class CLevelSet : public CObject { inline void SetVolumeData(classParam pTheVolumeData); }; +/************************************************************************************************************************* + Class CBooleanObject +**************************************************************************************************************************/ +class CBooleanObject : public CObject { +public: + + /** + * CBooleanObject::CBooleanObject - Constructor for BooleanObject class. + */ + CBooleanObject(CWrapper* pWrapper, Lib3MFHandle pHandle) + : CObject(pWrapper, pHandle) + { + } + + inline void SetBaseObject(classParam pBaseObject, const sTransform & Transform); + inline PObject GetBaseObject(); + inline void SetBaseTransform(const sTransform & Transform); + inline sTransform GetBaseTransform(); + inline void SetOperation(const eBooleanOperation eOperation); + inline eBooleanOperation GetOperation(); + inline void SetCSGModeEnabled(const bool bCSGModeEnabled); + inline bool GetCSGModeEnabled(); + inline void SetExtractionGridResolution(const Lib3MF_uint32 nGridResolution); + inline Lib3MF_uint32 GetExtractionGridResolution(); + inline Lib3MF_uint32 GetOperandCount(); + inline void AddOperand(classParam pOperandObject, const sTransform & Transform); + inline sTransform GetOperand(const Lib3MF_uint32 nIndex, PMeshObject & pOperandObject); +}; + /************************************************************************************************************************* Class CBeamLattice **************************************************************************************************************************/ @@ -3508,6 +3565,7 @@ class CModel : public CBase { inline PMultiPropertyGroup GetMultiPropertyGroupByID(const Lib3MF_uint32 nUniqueResourceID); inline PMeshObject GetMeshObjectByID(const Lib3MF_uint32 nUniqueResourceID); inline PComponentsObject GetComponentsObjectByID(const Lib3MF_uint32 nUniqueResourceID); + inline PBooleanObject GetBooleanObjectByID(const Lib3MF_uint32 nUniqueResourceID); inline PColorGroup GetColorGroupByID(const Lib3MF_uint32 nUniqueResourceID); inline PSliceStack GetSliceStackByID(const Lib3MF_uint32 nUniqueResourceID); inline PLevelSet GetLevelSetByID(const Lib3MF_uint32 nUniqueResourceID); @@ -3519,6 +3577,7 @@ class CModel : public CBase { inline PObjectIterator GetObjects(); inline PMeshObjectIterator GetMeshObjects(); inline PComponentsObjectIterator GetComponentsObjects(); + inline PBooleanObjectIterator GetBooleanObjects(); inline PTexture2DIterator GetTexture2Ds(); inline PBaseMaterialGroupIterator GetBaseMaterialGroups(); inline PColorGroupIterator GetColorGroups(); @@ -3531,6 +3590,7 @@ class CModel : public CBase { inline void MergeFromModel(classParam pModelInstance); inline PMeshObject AddMeshObject(); inline PComponentsObject AddComponentsObject(); + inline PBooleanObject AddBooleanObject(); inline PSliceStack AddSliceStack(const Lib3MF_double dZBottom); inline PTexture2D AddTexture2DFromAttachment(classParam pTextureAttachment); inline PBaseMaterialGroup AddBaseMaterialGroup(); @@ -3591,6 +3651,7 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) case 0xDE92510BD2112288UL: return new CObjectIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::ObjectIterator" case 0xF4196034E2B9FDE6UL: return new CMeshObjectIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::MeshObjectIterator" case 0x564DE4217ED7614AUL: return new CComponentsObjectIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::ComponentsObjectIterator" + case 0xAFF01F512E1FF6AEUL: return new CBooleanObjectIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObjectIterator" case 0x4BD32B4870FFC03BUL: return new CTexture2DIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::Texture2DIterator" case 0x65E6EDD9362C79CBUL: return new CBaseMaterialGroupIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::BaseMaterialGroupIterator" case 0x10274A1757C729C0UL: return new CColorGroupIterator(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::ColorGroupIterator" @@ -3606,6 +3667,7 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) case 0x2DA2136F577A779CUL: return new CObject(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::Object" case 0x3B3A6DC6EC610497UL: return new CMeshObject(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::MeshObject" case 0xE8A7D9C192EFD0E2UL: return new CLevelSet(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::LevelSet" + case 0x85FA0E8806B6C357UL: return new CBooleanObject(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObject" case 0x63B3B461B30B4BA5UL: return new CBeamLattice(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::BeamLattice" case 0x4DF17E76926221C2UL: return new CFunctionReference(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::FunctionReference" case 0xD85B5B6143E787E3UL: return new CVolumeDataColor(this, pHandle); break; // First 64 bits of SHA1 of a string: "Lib3MF::VolumeDataColor" @@ -4028,6 +4090,7 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) pWrapperTable->m_ObjectIterator_GetCurrentObject = nullptr; pWrapperTable->m_MeshObjectIterator_GetCurrentMeshObject = nullptr; pWrapperTable->m_ComponentsObjectIterator_GetCurrentComponentsObject = nullptr; + pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject = nullptr; pWrapperTable->m_Texture2DIterator_GetCurrentTexture2D = nullptr; pWrapperTable->m_BaseMaterialGroupIterator_GetCurrentBaseMaterialGroup = nullptr; pWrapperTable->m_ColorGroupIterator_GetCurrentColorGroup = nullptr; @@ -4076,6 +4139,7 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) pWrapperTable->m_Object_IsMeshObject = nullptr; pWrapperTable->m_Object_IsComponentsObject = nullptr; pWrapperTable->m_Object_IsLevelSetObject = nullptr; + pWrapperTable->m_Object_IsBooleanObject = nullptr; pWrapperTable->m_Object_IsValid = nullptr; pWrapperTable->m_Object_SetAttachmentAsThumbnail = nullptr; pWrapperTable->m_Object_GetThumbnailAttachment = nullptr; @@ -4133,6 +4197,19 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) pWrapperTable->m_LevelSet_GetMesh = nullptr; pWrapperTable->m_LevelSet_GetVolumeData = nullptr; pWrapperTable->m_LevelSet_SetVolumeData = nullptr; + pWrapperTable->m_BooleanObject_SetBaseObject = nullptr; + pWrapperTable->m_BooleanObject_GetBaseObject = nullptr; + pWrapperTable->m_BooleanObject_SetBaseTransform = nullptr; + pWrapperTable->m_BooleanObject_GetBaseTransform = nullptr; + pWrapperTable->m_BooleanObject_SetOperation = nullptr; + pWrapperTable->m_BooleanObject_GetOperation = nullptr; + pWrapperTable->m_BooleanObject_SetCSGModeEnabled = nullptr; + pWrapperTable->m_BooleanObject_GetCSGModeEnabled = nullptr; + pWrapperTable->m_BooleanObject_SetExtractionGridResolution = nullptr; + pWrapperTable->m_BooleanObject_GetExtractionGridResolution = nullptr; + pWrapperTable->m_BooleanObject_GetOperandCount = nullptr; + pWrapperTable->m_BooleanObject_AddOperand = nullptr; + pWrapperTable->m_BooleanObject_GetOperand = nullptr; pWrapperTable->m_BeamLattice_GetMinLength = nullptr; pWrapperTable->m_BeamLattice_SetMinLength = nullptr; pWrapperTable->m_BeamLattice_GetClipping = nullptr; @@ -4558,6 +4635,7 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) pWrapperTable->m_Model_GetMultiPropertyGroupByID = nullptr; pWrapperTable->m_Model_GetMeshObjectByID = nullptr; pWrapperTable->m_Model_GetComponentsObjectByID = nullptr; + pWrapperTable->m_Model_GetBooleanObjectByID = nullptr; pWrapperTable->m_Model_GetColorGroupByID = nullptr; pWrapperTable->m_Model_GetSliceStackByID = nullptr; pWrapperTable->m_Model_GetLevelSetByID = nullptr; @@ -4569,6 +4647,7 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) pWrapperTable->m_Model_GetObjects = nullptr; pWrapperTable->m_Model_GetMeshObjects = nullptr; pWrapperTable->m_Model_GetComponentsObjects = nullptr; + pWrapperTable->m_Model_GetBooleanObjects = nullptr; pWrapperTable->m_Model_GetTexture2Ds = nullptr; pWrapperTable->m_Model_GetBaseMaterialGroups = nullptr; pWrapperTable->m_Model_GetColorGroups = nullptr; @@ -4581,6 +4660,7 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) pWrapperTable->m_Model_MergeFromModel = nullptr; pWrapperTable->m_Model_AddMeshObject = nullptr; pWrapperTable->m_Model_AddComponentsObject = nullptr; + pWrapperTable->m_Model_AddBooleanObject = nullptr; pWrapperTable->m_Model_AddSliceStack = nullptr; pWrapperTable->m_Model_AddTexture2DFromAttachment = nullptr; pWrapperTable->m_Model_AddBaseMaterialGroup = nullptr; @@ -5060,6 +5140,15 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) if (pWrapperTable->m_ComponentsObjectIterator_GetCurrentComponentsObject == nullptr) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject = (PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr) GetProcAddress(hLibrary, "lib3mf_booleanobjectiterator_getcurrentbooleanobject"); + #else // _WIN32 + pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject = (PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr) dlsym(hLibrary, "lib3mf_booleanobjectiterator_getcurrentbooleanobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Texture2DIterator_GetCurrentTexture2D = (PLib3MFTexture2DIterator_GetCurrentTexture2DPtr) GetProcAddress(hLibrary, "lib3mf_texture2diterator_getcurrenttexture2d"); #else // _WIN32 @@ -5492,6 +5581,15 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) if (pWrapperTable->m_Object_IsLevelSetObject == nullptr) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Object_IsBooleanObject = (PLib3MFObject_IsBooleanObjectPtr) GetProcAddress(hLibrary, "lib3mf_object_isbooleanobject"); + #else // _WIN32 + pWrapperTable->m_Object_IsBooleanObject = (PLib3MFObject_IsBooleanObjectPtr) dlsym(hLibrary, "lib3mf_object_isbooleanobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Object_IsBooleanObject == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Object_IsValid = (PLib3MFObject_IsValidPtr) GetProcAddress(hLibrary, "lib3mf_object_isvalid"); #else // _WIN32 @@ -6005,6 +6103,123 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) if (pWrapperTable->m_LevelSet_SetVolumeData == nullptr) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetBaseObject = (PLib3MFBooleanObject_SetBaseObjectPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setbaseobject"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetBaseObject = (PLib3MFBooleanObject_SetBaseObjectPtr) dlsym(hLibrary, "lib3mf_booleanobject_setbaseobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetBaseObject == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetBaseObject = (PLib3MFBooleanObject_GetBaseObjectPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getbaseobject"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetBaseObject = (PLib3MFBooleanObject_GetBaseObjectPtr) dlsym(hLibrary, "lib3mf_booleanobject_getbaseobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetBaseObject == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetBaseTransform = (PLib3MFBooleanObject_SetBaseTransformPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setbasetransform"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetBaseTransform = (PLib3MFBooleanObject_SetBaseTransformPtr) dlsym(hLibrary, "lib3mf_booleanobject_setbasetransform"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetBaseTransform == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetBaseTransform = (PLib3MFBooleanObject_GetBaseTransformPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getbasetransform"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetBaseTransform = (PLib3MFBooleanObject_GetBaseTransformPtr) dlsym(hLibrary, "lib3mf_booleanobject_getbasetransform"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetBaseTransform == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetOperation = (PLib3MFBooleanObject_SetOperationPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setoperation"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetOperation = (PLib3MFBooleanObject_SetOperationPtr) dlsym(hLibrary, "lib3mf_booleanobject_setoperation"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetOperation == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetOperation = (PLib3MFBooleanObject_GetOperationPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getoperation"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetOperation = (PLib3MFBooleanObject_GetOperationPtr) dlsym(hLibrary, "lib3mf_booleanobject_getoperation"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetOperation == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetCSGModeEnabled = (PLib3MFBooleanObject_SetCSGModeEnabledPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setcsgmodeenabled"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetCSGModeEnabled = (PLib3MFBooleanObject_SetCSGModeEnabledPtr) dlsym(hLibrary, "lib3mf_booleanobject_setcsgmodeenabled"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetCSGModeEnabled == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetCSGModeEnabled = (PLib3MFBooleanObject_GetCSGModeEnabledPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getcsgmodeenabled"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetCSGModeEnabled = (PLib3MFBooleanObject_GetCSGModeEnabledPtr) dlsym(hLibrary, "lib3mf_booleanobject_getcsgmodeenabled"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetCSGModeEnabled == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetExtractionGridResolution = (PLib3MFBooleanObject_SetExtractionGridResolutionPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setextractiongridresolution"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetExtractionGridResolution = (PLib3MFBooleanObject_SetExtractionGridResolutionPtr) dlsym(hLibrary, "lib3mf_booleanobject_setextractiongridresolution"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetExtractionGridResolution == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetExtractionGridResolution = (PLib3MFBooleanObject_GetExtractionGridResolutionPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getextractiongridresolution"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetExtractionGridResolution = (PLib3MFBooleanObject_GetExtractionGridResolutionPtr) dlsym(hLibrary, "lib3mf_booleanobject_getextractiongridresolution"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetExtractionGridResolution == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetOperandCount = (PLib3MFBooleanObject_GetOperandCountPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getoperandcount"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetOperandCount = (PLib3MFBooleanObject_GetOperandCountPtr) dlsym(hLibrary, "lib3mf_booleanobject_getoperandcount"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetOperandCount == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_AddOperand = (PLib3MFBooleanObject_AddOperandPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_addoperand"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_AddOperand = (PLib3MFBooleanObject_AddOperandPtr) dlsym(hLibrary, "lib3mf_booleanobject_addoperand"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_AddOperand == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetOperand = (PLib3MFBooleanObject_GetOperandPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getoperand"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetOperand = (PLib3MFBooleanObject_GetOperandPtr) dlsym(hLibrary, "lib3mf_booleanobject_getoperand"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetOperand == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_BeamLattice_GetMinLength = (PLib3MFBeamLattice_GetMinLengthPtr) GetProcAddress(hLibrary, "lib3mf_beamlattice_getminlength"); #else // _WIN32 @@ -9830,6 +10045,15 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) if (pWrapperTable->m_Model_GetComponentsObjectByID == nullptr) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Model_GetBooleanObjectByID = (PLib3MFModel_GetBooleanObjectByIDPtr) GetProcAddress(hLibrary, "lib3mf_model_getbooleanobjectbyid"); + #else // _WIN32 + pWrapperTable->m_Model_GetBooleanObjectByID = (PLib3MFModel_GetBooleanObjectByIDPtr) dlsym(hLibrary, "lib3mf_model_getbooleanobjectbyid"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Model_GetBooleanObjectByID == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Model_GetColorGroupByID = (PLib3MFModel_GetColorGroupByIDPtr) GetProcAddress(hLibrary, "lib3mf_model_getcolorgroupbyid"); #else // _WIN32 @@ -9929,6 +10153,15 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) if (pWrapperTable->m_Model_GetComponentsObjects == nullptr) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Model_GetBooleanObjects = (PLib3MFModel_GetBooleanObjectsPtr) GetProcAddress(hLibrary, "lib3mf_model_getbooleanobjects"); + #else // _WIN32 + pWrapperTable->m_Model_GetBooleanObjects = (PLib3MFModel_GetBooleanObjectsPtr) dlsym(hLibrary, "lib3mf_model_getbooleanobjects"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Model_GetBooleanObjects == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Model_GetTexture2Ds = (PLib3MFModel_GetTexture2DsPtr) GetProcAddress(hLibrary, "lib3mf_model_gettexture2ds"); #else // _WIN32 @@ -10037,6 +10270,15 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) if (pWrapperTable->m_Model_AddComponentsObject == nullptr) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Model_AddBooleanObject = (PLib3MFModel_AddBooleanObjectPtr) GetProcAddress(hLibrary, "lib3mf_model_addbooleanobject"); + #else // _WIN32 + pWrapperTable->m_Model_AddBooleanObject = (PLib3MFModel_AddBooleanObjectPtr) dlsym(hLibrary, "lib3mf_model_addbooleanobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Model_AddBooleanObject == nullptr) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Model_AddSliceStack = (PLib3MFModel_AddSliceStackPtr) GetProcAddress(hLibrary, "lib3mf_model_addslicestack"); #else // _WIN32 @@ -10680,6 +10922,10 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) if ( (eLookupError != 0) || (pWrapperTable->m_ComponentsObjectIterator_GetCurrentComponentsObject == nullptr) ) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("lib3mf_booleanobjectiterator_getcurrentbooleanobject", (void**)&(pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("lib3mf_texture2diterator_getcurrenttexture2d", (void**)&(pWrapperTable->m_Texture2DIterator_GetCurrentTexture2D)); if ( (eLookupError != 0) || (pWrapperTable->m_Texture2DIterator_GetCurrentTexture2D == nullptr) ) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; @@ -10872,6 +11118,10 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) if ( (eLookupError != 0) || (pWrapperTable->m_Object_IsLevelSetObject == nullptr) ) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("lib3mf_object_isbooleanobject", (void**)&(pWrapperTable->m_Object_IsBooleanObject)); + if ( (eLookupError != 0) || (pWrapperTable->m_Object_IsBooleanObject == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("lib3mf_object_isvalid", (void**)&(pWrapperTable->m_Object_IsValid)); if ( (eLookupError != 0) || (pWrapperTable->m_Object_IsValid == nullptr) ) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; @@ -11100,6 +11350,58 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) if ( (eLookupError != 0) || (pWrapperTable->m_LevelSet_SetVolumeData == nullptr) ) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("lib3mf_booleanobject_setbaseobject", (void**)&(pWrapperTable->m_BooleanObject_SetBaseObject)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObject_SetBaseObject == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("lib3mf_booleanobject_getbaseobject", (void**)&(pWrapperTable->m_BooleanObject_GetBaseObject)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObject_GetBaseObject == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("lib3mf_booleanobject_setbasetransform", (void**)&(pWrapperTable->m_BooleanObject_SetBaseTransform)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObject_SetBaseTransform == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("lib3mf_booleanobject_getbasetransform", (void**)&(pWrapperTable->m_BooleanObject_GetBaseTransform)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObject_GetBaseTransform == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("lib3mf_booleanobject_setoperation", (void**)&(pWrapperTable->m_BooleanObject_SetOperation)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObject_SetOperation == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("lib3mf_booleanobject_getoperation", (void**)&(pWrapperTable->m_BooleanObject_GetOperation)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObject_GetOperation == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("lib3mf_booleanobject_setcsgmodeenabled", (void**)&(pWrapperTable->m_BooleanObject_SetCSGModeEnabled)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObject_SetCSGModeEnabled == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("lib3mf_booleanobject_getcsgmodeenabled", (void**)&(pWrapperTable->m_BooleanObject_GetCSGModeEnabled)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObject_GetCSGModeEnabled == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("lib3mf_booleanobject_setextractiongridresolution", (void**)&(pWrapperTable->m_BooleanObject_SetExtractionGridResolution)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObject_SetExtractionGridResolution == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("lib3mf_booleanobject_getextractiongridresolution", (void**)&(pWrapperTable->m_BooleanObject_GetExtractionGridResolution)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObject_GetExtractionGridResolution == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("lib3mf_booleanobject_getoperandcount", (void**)&(pWrapperTable->m_BooleanObject_GetOperandCount)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObject_GetOperandCount == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("lib3mf_booleanobject_addoperand", (void**)&(pWrapperTable->m_BooleanObject_AddOperand)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObject_AddOperand == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("lib3mf_booleanobject_getoperand", (void**)&(pWrapperTable->m_BooleanObject_GetOperand)); + if ( (eLookupError != 0) || (pWrapperTable->m_BooleanObject_GetOperand == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("lib3mf_beamlattice_getminlength", (void**)&(pWrapperTable->m_BeamLattice_GetMinLength)); if ( (eLookupError != 0) || (pWrapperTable->m_BeamLattice_GetMinLength == nullptr) ) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; @@ -12800,6 +13102,10 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) if ( (eLookupError != 0) || (pWrapperTable->m_Model_GetComponentsObjectByID == nullptr) ) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("lib3mf_model_getbooleanobjectbyid", (void**)&(pWrapperTable->m_Model_GetBooleanObjectByID)); + if ( (eLookupError != 0) || (pWrapperTable->m_Model_GetBooleanObjectByID == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("lib3mf_model_getcolorgroupbyid", (void**)&(pWrapperTable->m_Model_GetColorGroupByID)); if ( (eLookupError != 0) || (pWrapperTable->m_Model_GetColorGroupByID == nullptr) ) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; @@ -12844,6 +13150,10 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) if ( (eLookupError != 0) || (pWrapperTable->m_Model_GetComponentsObjects == nullptr) ) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("lib3mf_model_getbooleanobjects", (void**)&(pWrapperTable->m_Model_GetBooleanObjects)); + if ( (eLookupError != 0) || (pWrapperTable->m_Model_GetBooleanObjects == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("lib3mf_model_gettexture2ds", (void**)&(pWrapperTable->m_Model_GetTexture2Ds)); if ( (eLookupError != 0) || (pWrapperTable->m_Model_GetTexture2Ds == nullptr) ) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; @@ -12892,6 +13202,10 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) if ( (eLookupError != 0) || (pWrapperTable->m_Model_AddComponentsObject == nullptr) ) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("lib3mf_model_addbooleanobject", (void**)&(pWrapperTable->m_Model_AddBooleanObject)); + if ( (eLookupError != 0) || (pWrapperTable->m_Model_AddBooleanObject == nullptr) ) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("lib3mf_model_addslicestack", (void**)&(pWrapperTable->m_Model_AddSliceStack)); if ( (eLookupError != 0) || (pWrapperTable->m_Model_AddSliceStack == nullptr) ) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; @@ -13640,6 +13954,25 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hResource))); } + /** + * Method definitions for class CBooleanObjectIterator + */ + + /** + * CBooleanObjectIterator::GetCurrentBooleanObject - Returns the BooleanObject the iterator points at. + * @return returns the BooleanObject instance. + */ + PBooleanObject CBooleanObjectIterator::GetCurrentBooleanObject() + { + Lib3MFHandle hResource = (Lib3MFHandle)nullptr; + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObjectIterator_GetCurrentBooleanObject(m_pHandle, &hResource)); + + if (!hResource) { + CheckError(LIB3MF_ERROR_INVALIDPARAM); + } + return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hResource))); + } + /** * Method definitions for class CTexture2DIterator */ @@ -14290,6 +14623,18 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) return resultIsLevelSetObject; } + /** + * CObject::IsBooleanObject - Retrieves, if an object is a boolean object + * @return returns, whether the object is a boolean object + */ + bool CObject::IsBooleanObject() + { + bool resultIsBooleanObject = 0; + CheckError(m_pWrapper->m_WrapperTable.m_Object_IsBooleanObject(m_pHandle, &resultIsBooleanObject)); + + return resultIsBooleanObject; + } + /** * CObject::IsValid - Retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. * @return returns whether the object is a valid object description @@ -14985,6 +15330,163 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) CheckError(m_pWrapper->m_WrapperTable.m_LevelSet_SetVolumeData(m_pHandle, hTheVolumeData)); } + /** + * Method definitions for class CBooleanObject + */ + + /** + * CBooleanObject::SetBaseObject - Sets the base object and transform for the boolean shape. + * @param[in] pBaseObject - base object of the boolean shape + * @param[in] Transform - transform applied to the base object + */ + void CBooleanObject::SetBaseObject(classParam pBaseObject, const sTransform & Transform) + { + Lib3MFHandle hBaseObject = pBaseObject.GetHandle(); + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObject_SetBaseObject(m_pHandle, hBaseObject, &Transform)); + } + + /** + * CBooleanObject::GetBaseObject - Returns the base object of the boolean shape. + * @return base object of the boolean shape + */ + PObject CBooleanObject::GetBaseObject() + { + Lib3MFHandle hBaseObject = (Lib3MFHandle)nullptr; + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObject_GetBaseObject(m_pHandle, &hBaseObject)); + + if (!hBaseObject) { + CheckError(LIB3MF_ERROR_INVALIDPARAM); + } + return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hBaseObject))); + } + + /** + * CBooleanObject::SetBaseTransform - Sets the base transform of the boolean shape. + * @param[in] Transform - transform applied to the base object + */ + void CBooleanObject::SetBaseTransform(const sTransform & Transform) + { + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObject_SetBaseTransform(m_pHandle, &Transform)); + } + + /** + * CBooleanObject::GetBaseTransform - Returns the base transform of the boolean shape. + * @return transform applied to the base object + */ + sTransform CBooleanObject::GetBaseTransform() + { + sTransform resultTransform; + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObject_GetBaseTransform(m_pHandle, &resultTransform)); + + return resultTransform; + } + + /** + * CBooleanObject::SetOperation - Sets the boolean operation used for the boolean shape. + * @param[in] eOperation - boolean operation used for the shape + */ + void CBooleanObject::SetOperation(const eBooleanOperation eOperation) + { + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObject_SetOperation(m_pHandle, eOperation)); + } + + /** + * CBooleanObject::GetOperation - Returns the boolean operation used for the boolean shape. + * @return boolean operation used for the shape + */ + eBooleanOperation CBooleanObject::GetOperation() + { + eBooleanOperation resultOperation = (eBooleanOperation) 0; + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObject_GetOperation(m_pHandle, &resultOperation)); + + return resultOperation; + } + + /** + * CBooleanObject::SetCSGModeEnabled - Enables or disables CSG field evaluation for boolean-to-mesh materialization. + * @param[in] bCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback + */ + void CBooleanObject::SetCSGModeEnabled(const bool bCSGModeEnabled) + { + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObject_SetCSGModeEnabled(m_pHandle, bCSGModeEnabled)); + } + + /** + * CBooleanObject::GetCSGModeEnabled - Returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. + * @return if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback + */ + bool CBooleanObject::GetCSGModeEnabled() + { + bool resultCSGModeEnabled = 0; + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObject_GetCSGModeEnabled(m_pHandle, &resultCSGModeEnabled)); + + return resultCSGModeEnabled; + } + + /** + * CBooleanObject::SetExtractionGridResolution - Sets the extraction grid resolution used for boolean-to-mesh materialization. + * @param[in] nGridResolution - extraction grid resolution for boolean surface extraction + */ + void CBooleanObject::SetExtractionGridResolution(const Lib3MF_uint32 nGridResolution) + { + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObject_SetExtractionGridResolution(m_pHandle, nGridResolution)); + } + + /** + * CBooleanObject::GetExtractionGridResolution - Returns the extraction grid resolution used for boolean-to-mesh materialization. + * @return extraction grid resolution for boolean surface extraction + */ + Lib3MF_uint32 CBooleanObject::GetExtractionGridResolution() + { + Lib3MF_uint32 resultGridResolution = 0; + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObject_GetExtractionGridResolution(m_pHandle, &resultGridResolution)); + + return resultGridResolution; + } + + /** + * CBooleanObject::GetOperandCount - Returns the number of operands in the boolean sequence. + * @return number of operands in the boolean sequence + */ + Lib3MF_uint32 CBooleanObject::GetOperandCount() + { + Lib3MF_uint32 resultCount = 0; + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObject_GetOperandCount(m_pHandle, &resultCount)); + + return resultCount; + } + + /** + * CBooleanObject::AddOperand - Adds an operand object to the boolean sequence. + * @param[in] pOperandObject - mesh object used as operand + * @param[in] Transform - transform applied to the operand object + */ + void CBooleanObject::AddOperand(classParam pOperandObject, const sTransform & Transform) + { + Lib3MFHandle hOperandObject = pOperandObject.GetHandle(); + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObject_AddOperand(m_pHandle, hOperandObject, &Transform)); + } + + /** + * CBooleanObject::GetOperand - Returns one operand object and transform from the boolean sequence. + * @param[in] nIndex - index of the operand in the boolean sequence + * @param[out] pOperandObject - mesh object used as operand + * @return transform applied to the operand object + */ + sTransform CBooleanObject::GetOperand(const Lib3MF_uint32 nIndex, PMeshObject & pOperandObject) + { + Lib3MFHandle hOperandObject = (Lib3MFHandle)nullptr; + sTransform resultTransform; + CheckError(m_pWrapper->m_WrapperTable.m_BooleanObject_GetOperand(m_pHandle, nIndex, &hOperandObject, &resultTransform)); + if (!hOperandObject) { + CheckError(LIB3MF_ERROR_INVALIDPARAM); + } else { + pOperandObject = std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hOperandObject))); + } + + return resultTransform; + } + /** * Method definitions for class CBeamLattice */ @@ -21197,6 +21699,22 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hComponentsObjectInstance))); } + /** + * CModel::GetBooleanObjectByID - finds a boolean object by its UniqueResourceID + * @param[in] nUniqueResourceID - UniqueResourceID + * @return returns the boolean object instance + */ + PBooleanObject CModel::GetBooleanObjectByID(const Lib3MF_uint32 nUniqueResourceID) + { + Lib3MFHandle hBooleanObjectInstance = (Lib3MFHandle)nullptr; + CheckError(m_pWrapper->m_WrapperTable.m_Model_GetBooleanObjectByID(m_pHandle, nUniqueResourceID, &hBooleanObjectInstance)); + + if (!hBooleanObjectInstance) { + CheckError(LIB3MF_ERROR_INVALIDPARAM); + } + return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hBooleanObjectInstance))); + } + /** * CModel::GetColorGroupByID - finds a model color group by its UniqueResourceID * @param[in] nUniqueResourceID - UniqueResourceID @@ -21357,6 +21875,21 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hResourceIterator))); } + /** + * CModel::GetBooleanObjects - creates a resource iterator instance with all boolean object resources. + * @return returns the iterator instance. + */ + PBooleanObjectIterator CModel::GetBooleanObjects() + { + Lib3MFHandle hResourceIterator = (Lib3MFHandle)nullptr; + CheckError(m_pWrapper->m_WrapperTable.m_Model_GetBooleanObjects(m_pHandle, &hResourceIterator)); + + if (!hResourceIterator) { + CheckError(LIB3MF_ERROR_INVALIDPARAM); + } + return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hResourceIterator))); + } + /** * CModel::GetTexture2Ds - creates a Texture2DIterator instance with all texture2d resources. * @return returns the iterator instance. @@ -21532,6 +22065,21 @@ inline CBase* CWrapper::polymorphicFactory(Lib3MFHandle pHandle) return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hComponentsObjectInstance))); } + /** + * CModel::AddBooleanObject - adds an empty boolean object to the model. + * @return returns the boolean object instance + */ + PBooleanObject CModel::AddBooleanObject() + { + Lib3MFHandle hBooleanObjectInstance = (Lib3MFHandle)nullptr; + CheckError(m_pWrapper->m_WrapperTable.m_Model_AddBooleanObject(m_pHandle, &hBooleanObjectInstance)); + + if (!hBooleanObjectInstance) { + CheckError(LIB3MF_ERROR_INVALIDPARAM); + } + return std::shared_ptr(dynamic_cast(m_pWrapper->polymorphicFactory(hBooleanObjectInstance))); + } + /** * CModel::AddSliceStack - creates a new model slicestack by its id * @param[in] dZBottom - Bottom Z value of the slicestack diff --git a/Autogenerated/Bindings/CppDynamic/lib3mf_types.hpp b/Autogenerated/Bindings/CppDynamic/lib3mf_types.hpp index 2ae400ad6..09f6a3441 100644 --- a/Autogenerated/Bindings/CppDynamic/lib3mf_types.hpp +++ b/Autogenerated/Bindings/CppDynamic/lib3mf_types.hpp @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++-Header file with basic types in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -83,7 +83,7 @@ typedef void * Lib3MF_pvoid; **************************************************************************************************************************/ #define LIB3MF_VERSION_MAJOR 2 -#define LIB3MF_VERSION_MINOR 5 +#define LIB3MF_VERSION_MINOR 6 #define LIB3MF_VERSION_MICRO 0 #define LIB3MF_VERSION_PRERELEASEINFO "" #define LIB3MF_VERSION_BUILDINFO "" @@ -219,6 +219,7 @@ typedef Lib3MFHandle Lib3MF_SliceStackIterator; typedef Lib3MFHandle Lib3MF_ObjectIterator; typedef Lib3MFHandle Lib3MF_MeshObjectIterator; typedef Lib3MFHandle Lib3MF_ComponentsObjectIterator; +typedef Lib3MFHandle Lib3MF_BooleanObjectIterator; typedef Lib3MFHandle Lib3MF_Texture2DIterator; typedef Lib3MFHandle Lib3MF_BaseMaterialGroupIterator; typedef Lib3MFHandle Lib3MF_ColorGroupIterator; @@ -234,6 +235,7 @@ typedef Lib3MFHandle Lib3MF_TriangleSet; typedef Lib3MFHandle Lib3MF_Object; typedef Lib3MFHandle Lib3MF_MeshObject; typedef Lib3MFHandle Lib3MF_LevelSet; +typedef Lib3MFHandle Lib3MF_BooleanObject; typedef Lib3MFHandle Lib3MF_BeamLattice; typedef Lib3MFHandle Lib3MF_FunctionReference; typedef Lib3MFHandle Lib3MF_VolumeDataColor; @@ -366,6 +368,12 @@ namespace Lib3MF { Surface = 4 }; + enum class eBooleanOperation : Lib3MF_int32 { + Union = 0, + Difference = 1, + Intersection = 2 + }; + enum class eTextureType : Lib3MF_int32 { Unknown = 0, PNG = 1, @@ -722,6 +730,7 @@ typedef Lib3MF::ePropertyType eLib3MFPropertyType; typedef Lib3MF::eSlicesMeshResolution eLib3MFSlicesMeshResolution; typedef Lib3MF::eModelUnit eLib3MFModelUnit; typedef Lib3MF::eObjectType eLib3MFObjectType; +typedef Lib3MF::eBooleanOperation eLib3MFBooleanOperation; typedef Lib3MF::eTextureType eLib3MFTextureType; typedef Lib3MF::eTextureTileStyle eLib3MFTextureTileStyle; typedef Lib3MF::eTextureFilter eLib3MFTextureFilter; diff --git a/Autogenerated/Bindings/Go/cfunc.go b/Autogenerated/Bindings/Go/cfunc.go index 6bbe16a03..db74d6965 100644 --- a/Autogenerated/Bindings/Go/cfunc.go +++ b/Autogenerated/Bindings/Go/cfunc.go @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Go wrapper file in order to allow an easy use of the 3MF Library. -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Go/lib3mf.go b/Autogenerated/Bindings/Go/lib3mf.go index 008b4294d..96acdfb76 100644 --- a/Autogenerated/Bindings/Go/lib3mf.go +++ b/Autogenerated/Bindings/Go/lib3mf.go @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Go wrapper file in order to allow an easy use of the 3MF Library. -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -96,6 +96,15 @@ const ( ObjectType_Surface = 4 ) +// BooleanOperation represents a Lib3MF enum. +type BooleanOperation int + +const ( + BooleanOperation_Union = 0 + BooleanOperation_Difference = 1 + BooleanOperation_Intersection = 2 +) + // TextureType represents a Lib3MF enum. type TextureType int @@ -1253,6 +1262,26 @@ func (inst ComponentsObjectIterator) GetCurrentComponentsObject() (ComponentsObj } +// BooleanObjectIterator represents a Lib3MF class. +type BooleanObjectIterator struct { + ResourceIterator +} + +func (wrapper Wrapper) NewBooleanObjectIterator(r ref) BooleanObjectIterator { + return BooleanObjectIterator{wrapper.NewResourceIterator(r)} +} + +// GetCurrentBooleanObject returns the BooleanObject the iterator points at. +func (inst BooleanObjectIterator) GetCurrentBooleanObject() (BooleanObject, error) { + var resource ref + ret := C.CCall_lib3mf_booleanobjectiterator_getcurrentbooleanobject(inst.wrapperRef.LibraryHandle, inst.Ref, &resource) + if ret != 0 { + return BooleanObject{}, makeError(uint32(ret)) + } + return inst.wrapperRef.NewBooleanObject(resource), nil +} + + // Texture2DIterator represents a Lib3MF class. type Texture2DIterator struct { ResourceIterator @@ -1913,6 +1942,16 @@ func (inst Object) IsLevelSetObject() (bool, error) { return bool(isLevelSetObject), nil } +// IsBooleanObject retrieves, if an object is a boolean object. +func (inst Object) IsBooleanObject() (bool, error) { + var isBooleanObject C.bool + ret := C.CCall_lib3mf_object_isbooleanobject(inst.wrapperRef.LibraryHandle, inst.Ref, &isBooleanObject) + if ret != 0 { + return false, makeError(uint32(ret)) + } + return bool(isBooleanObject), nil +} + // IsValid retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. func (inst Object) IsValid() (bool, error) { var isValid C.bool @@ -2545,6 +2584,141 @@ func (inst LevelSet) SetVolumeData(theVolumeData VolumeData) error { } +// BooleanObject represents a Lib3MF class. +type BooleanObject struct { + Object +} + +func (wrapper Wrapper) NewBooleanObject(r ref) BooleanObject { + return BooleanObject{wrapper.NewObject(r)} +} + +// SetBaseObject sets the base object and transform for the boolean shape. +func (inst BooleanObject) SetBaseObject(baseObject Object, transform Transform) error { + ret := C.CCall_lib3mf_booleanobject_setbaseobject(inst.wrapperRef.LibraryHandle, inst.Ref, baseObject.Ref, (*C.sLib3MFTransform)(unsafe.Pointer(&transform))) + if ret != 0 { + return makeError(uint32(ret)) + } + return nil +} + +// GetBaseObject returns the base object of the boolean shape. +func (inst BooleanObject) GetBaseObject() (Object, error) { + var baseObject ref + ret := C.CCall_lib3mf_booleanobject_getbaseobject(inst.wrapperRef.LibraryHandle, inst.Ref, &baseObject) + if ret != 0 { + return Object{}, makeError(uint32(ret)) + } + return inst.wrapperRef.NewObject(baseObject), nil +} + +// SetBaseTransform sets the base transform of the boolean shape. +func (inst BooleanObject) SetBaseTransform(transform Transform) error { + ret := C.CCall_lib3mf_booleanobject_setbasetransform(inst.wrapperRef.LibraryHandle, inst.Ref, (*C.sLib3MFTransform)(unsafe.Pointer(&transform))) + if ret != 0 { + return makeError(uint32(ret)) + } + return nil +} + +// GetBaseTransform returns the base transform of the boolean shape. +func (inst BooleanObject) GetBaseTransform() (Transform, error) { + var transform C.sLib3MFTransform + ret := C.CCall_lib3mf_booleanobject_getbasetransform(inst.wrapperRef.LibraryHandle, inst.Ref, &transform) + if ret != 0 { + return Transform{}, makeError(uint32(ret)) + } + return *(*Transform)(unsafe.Pointer(&transform)), nil +} + +// SetOperation sets the boolean operation used for the boolean shape. +func (inst BooleanObject) SetOperation(operation BooleanOperation) error { + ret := C.CCall_lib3mf_booleanobject_setoperation(inst.wrapperRef.LibraryHandle, inst.Ref, C.eLib3MFBooleanOperation(operation)) + if ret != 0 { + return makeError(uint32(ret)) + } + return nil +} + +// GetOperation returns the boolean operation used for the boolean shape. +func (inst BooleanObject) GetOperation() (BooleanOperation, error) { + var operation C.eLib3MFBooleanOperation + ret := C.CCall_lib3mf_booleanobject_getoperation(inst.wrapperRef.LibraryHandle, inst.Ref, &operation) + if ret != 0 { + return 0, makeError(uint32(ret)) + } + return BooleanOperation(operation), nil +} + +// SetCSGModeEnabled enables or disables CSG field evaluation for boolean-to-mesh materialization. +func (inst BooleanObject) SetCSGModeEnabled(cSGModeEnabled bool) error { + ret := C.CCall_lib3mf_booleanobject_setcsgmodeenabled(inst.wrapperRef.LibraryHandle, inst.Ref, C.bool(cSGModeEnabled)) + if ret != 0 { + return makeError(uint32(ret)) + } + return nil +} + +// GetCSGModeEnabled returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. +func (inst BooleanObject) GetCSGModeEnabled() (bool, error) { + var cSGModeEnabled C.bool + ret := C.CCall_lib3mf_booleanobject_getcsgmodeenabled(inst.wrapperRef.LibraryHandle, inst.Ref, &cSGModeEnabled) + if ret != 0 { + return false, makeError(uint32(ret)) + } + return bool(cSGModeEnabled), nil +} + +// SetExtractionGridResolution sets the extraction grid resolution used for boolean-to-mesh materialization. +func (inst BooleanObject) SetExtractionGridResolution(gridResolution uint32) error { + ret := C.CCall_lib3mf_booleanobject_setextractiongridresolution(inst.wrapperRef.LibraryHandle, inst.Ref, C.uint32_t(gridResolution)) + if ret != 0 { + return makeError(uint32(ret)) + } + return nil +} + +// GetExtractionGridResolution returns the extraction grid resolution used for boolean-to-mesh materialization. +func (inst BooleanObject) GetExtractionGridResolution() (uint32, error) { + var gridResolution C.uint32_t + ret := C.CCall_lib3mf_booleanobject_getextractiongridresolution(inst.wrapperRef.LibraryHandle, inst.Ref, &gridResolution) + if ret != 0 { + return 0, makeError(uint32(ret)) + } + return uint32(gridResolution), nil +} + +// GetOperandCount returns the number of operands in the boolean sequence. +func (inst BooleanObject) GetOperandCount() (uint32, error) { + var count C.uint32_t + ret := C.CCall_lib3mf_booleanobject_getoperandcount(inst.wrapperRef.LibraryHandle, inst.Ref, &count) + if ret != 0 { + return 0, makeError(uint32(ret)) + } + return uint32(count), nil +} + +// AddOperand adds an operand object to the boolean sequence. +func (inst BooleanObject) AddOperand(operandObject MeshObject, transform Transform) error { + ret := C.CCall_lib3mf_booleanobject_addoperand(inst.wrapperRef.LibraryHandle, inst.Ref, operandObject.Ref, (*C.sLib3MFTransform)(unsafe.Pointer(&transform))) + if ret != 0 { + return makeError(uint32(ret)) + } + return nil +} + +// GetOperand returns one operand object and transform from the boolean sequence. +func (inst BooleanObject) GetOperand(index uint32) (MeshObject, Transform, error) { + var operandObject ref + var transform C.sLib3MFTransform + ret := C.CCall_lib3mf_booleanobject_getoperand(inst.wrapperRef.LibraryHandle, inst.Ref, C.uint32_t(index), &operandObject, &transform) + if ret != 0 { + return MeshObject{}, Transform{}, makeError(uint32(ret)) + } + return inst.wrapperRef.NewMeshObject(operandObject), *(*Transform)(unsafe.Pointer(&transform)), nil +} + + // BeamLattice represents a Lib3MF class. type BeamLattice struct { Base @@ -8040,6 +8214,16 @@ func (inst Model) GetComponentsObjectByID(uniqueResourceID uint32) (ComponentsOb return inst.wrapperRef.NewComponentsObject(componentsObjectInstance), nil } +// GetBooleanObjectByID finds a boolean object by its UniqueResourceID. +func (inst Model) GetBooleanObjectByID(uniqueResourceID uint32) (BooleanObject, error) { + var booleanObjectInstance ref + ret := C.CCall_lib3mf_model_getbooleanobjectbyid(inst.wrapperRef.LibraryHandle, inst.Ref, C.uint32_t(uniqueResourceID), &booleanObjectInstance) + if ret != 0 { + return BooleanObject{}, makeError(uint32(ret)) + } + return inst.wrapperRef.NewBooleanObject(booleanObjectInstance), nil +} + // GetColorGroupByID finds a model color group by its UniqueResourceID. func (inst Model) GetColorGroupByID(uniqueResourceID uint32) (ColorGroup, error) { var colorGroupInstance ref @@ -8157,6 +8341,16 @@ func (inst Model) GetComponentsObjects() (ComponentsObjectIterator, error) { return inst.wrapperRef.NewComponentsObjectIterator(resourceIterator), nil } +// GetBooleanObjects creates a resource iterator instance with all boolean object resources. +func (inst Model) GetBooleanObjects() (BooleanObjectIterator, error) { + var resourceIterator ref + ret := C.CCall_lib3mf_model_getbooleanobjects(inst.wrapperRef.LibraryHandle, inst.Ref, &resourceIterator) + if ret != 0 { + return BooleanObjectIterator{}, makeError(uint32(ret)) + } + return inst.wrapperRef.NewBooleanObjectIterator(resourceIterator), nil +} + // GetTexture2Ds creates a Texture2DIterator instance with all texture2d resources. func (inst Model) GetTexture2Ds() (Texture2DIterator, error) { var resourceIterator ref @@ -8276,6 +8470,16 @@ func (inst Model) AddComponentsObject() (ComponentsObject, error) { return inst.wrapperRef.NewComponentsObject(componentsObjectInstance), nil } +// AddBooleanObject adds an empty boolean object to the model. +func (inst Model) AddBooleanObject() (BooleanObject, error) { + var booleanObjectInstance ref + ret := C.CCall_lib3mf_model_addbooleanobject(inst.wrapperRef.LibraryHandle, inst.Ref, &booleanObjectInstance) + if ret != 0 { + return BooleanObject{}, makeError(uint32(ret)) + } + return inst.wrapperRef.NewBooleanObject(booleanObjectInstance), nil +} + // AddSliceStack creates a new model slicestack by its id. func (inst Model) AddSliceStack(zBottom float64) (SliceStack, error) { var sliceStackInstance ref @@ -8834,7 +9038,7 @@ func (wrapper Wrapper) releaseC(r *ref) error { func (wrapper Wrapper) CheckBinaryVersion() error { var nBindingMajor uint32 = 2; - var nBindingMinor uint32 = 5; + var nBindingMinor uint32 = 6; nMajor, nMinor, _, err := wrapper.GetLibraryVersion() if err != nil { return err; diff --git a/Autogenerated/Bindings/Go/lib3mf_dynamic.c b/Autogenerated/Bindings/Go/lib3mf_dynamic.c index 427a2efe9..a03226a4d 100644 --- a/Autogenerated/Bindings/Go/lib3mf_dynamic.c +++ b/Autogenerated/Bindings/Go/lib3mf_dynamic.c @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated plain C Header file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -90,6 +90,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_ObjectIterator_GetCurrentObject = NULL; pWrapperTable->m_MeshObjectIterator_GetCurrentMeshObject = NULL; pWrapperTable->m_ComponentsObjectIterator_GetCurrentComponentsObject = NULL; + pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject = NULL; pWrapperTable->m_Texture2DIterator_GetCurrentTexture2D = NULL; pWrapperTable->m_BaseMaterialGroupIterator_GetCurrentBaseMaterialGroup = NULL; pWrapperTable->m_ColorGroupIterator_GetCurrentColorGroup = NULL; @@ -138,6 +139,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_Object_IsMeshObject = NULL; pWrapperTable->m_Object_IsComponentsObject = NULL; pWrapperTable->m_Object_IsLevelSetObject = NULL; + pWrapperTable->m_Object_IsBooleanObject = NULL; pWrapperTable->m_Object_IsValid = NULL; pWrapperTable->m_Object_SetAttachmentAsThumbnail = NULL; pWrapperTable->m_Object_GetThumbnailAttachment = NULL; @@ -195,6 +197,19 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_LevelSet_GetMesh = NULL; pWrapperTable->m_LevelSet_GetVolumeData = NULL; pWrapperTable->m_LevelSet_SetVolumeData = NULL; + pWrapperTable->m_BooleanObject_SetBaseObject = NULL; + pWrapperTable->m_BooleanObject_GetBaseObject = NULL; + pWrapperTable->m_BooleanObject_SetBaseTransform = NULL; + pWrapperTable->m_BooleanObject_GetBaseTransform = NULL; + pWrapperTable->m_BooleanObject_SetOperation = NULL; + pWrapperTable->m_BooleanObject_GetOperation = NULL; + pWrapperTable->m_BooleanObject_SetCSGModeEnabled = NULL; + pWrapperTable->m_BooleanObject_GetCSGModeEnabled = NULL; + pWrapperTable->m_BooleanObject_SetExtractionGridResolution = NULL; + pWrapperTable->m_BooleanObject_GetExtractionGridResolution = NULL; + pWrapperTable->m_BooleanObject_GetOperandCount = NULL; + pWrapperTable->m_BooleanObject_AddOperand = NULL; + pWrapperTable->m_BooleanObject_GetOperand = NULL; pWrapperTable->m_BeamLattice_GetMinLength = NULL; pWrapperTable->m_BeamLattice_SetMinLength = NULL; pWrapperTable->m_BeamLattice_GetClipping = NULL; @@ -620,6 +635,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_Model_GetMultiPropertyGroupByID = NULL; pWrapperTable->m_Model_GetMeshObjectByID = NULL; pWrapperTable->m_Model_GetComponentsObjectByID = NULL; + pWrapperTable->m_Model_GetBooleanObjectByID = NULL; pWrapperTable->m_Model_GetColorGroupByID = NULL; pWrapperTable->m_Model_GetSliceStackByID = NULL; pWrapperTable->m_Model_GetLevelSetByID = NULL; @@ -631,6 +647,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_Model_GetObjects = NULL; pWrapperTable->m_Model_GetMeshObjects = NULL; pWrapperTable->m_Model_GetComponentsObjects = NULL; + pWrapperTable->m_Model_GetBooleanObjects = NULL; pWrapperTable->m_Model_GetTexture2Ds = NULL; pWrapperTable->m_Model_GetBaseMaterialGroups = NULL; pWrapperTable->m_Model_GetColorGroups = NULL; @@ -643,6 +660,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_Model_MergeFromModel = NULL; pWrapperTable->m_Model_AddMeshObject = NULL; pWrapperTable->m_Model_AddComponentsObject = NULL; + pWrapperTable->m_Model_AddBooleanObject = NULL; pWrapperTable->m_Model_AddSliceStack = NULL; pWrapperTable->m_Model_AddTexture2DFromAttachment = NULL; pWrapperTable->m_Model_AddBaseMaterialGroup = NULL; @@ -1126,6 +1144,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_ComponentsObjectIterator_GetCurrentComponentsObject == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject = (PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr) GetProcAddress(hLibrary, "lib3mf_booleanobjectiterator_getcurrentbooleanobject"); + #else // _WIN32 + pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject = (PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr) dlsym(hLibrary, "lib3mf_booleanobjectiterator_getcurrentbooleanobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Texture2DIterator_GetCurrentTexture2D = (PLib3MFTexture2DIterator_GetCurrentTexture2DPtr) GetProcAddress(hLibrary, "lib3mf_texture2diterator_getcurrenttexture2d"); #else // _WIN32 @@ -1558,6 +1585,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_Object_IsLevelSetObject == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Object_IsBooleanObject = (PLib3MFObject_IsBooleanObjectPtr) GetProcAddress(hLibrary, "lib3mf_object_isbooleanobject"); + #else // _WIN32 + pWrapperTable->m_Object_IsBooleanObject = (PLib3MFObject_IsBooleanObjectPtr) dlsym(hLibrary, "lib3mf_object_isbooleanobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Object_IsBooleanObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Object_IsValid = (PLib3MFObject_IsValidPtr) GetProcAddress(hLibrary, "lib3mf_object_isvalid"); #else // _WIN32 @@ -2071,6 +2107,123 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_LevelSet_SetVolumeData == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetBaseObject = (PLib3MFBooleanObject_SetBaseObjectPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setbaseobject"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetBaseObject = (PLib3MFBooleanObject_SetBaseObjectPtr) dlsym(hLibrary, "lib3mf_booleanobject_setbaseobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetBaseObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetBaseObject = (PLib3MFBooleanObject_GetBaseObjectPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getbaseobject"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetBaseObject = (PLib3MFBooleanObject_GetBaseObjectPtr) dlsym(hLibrary, "lib3mf_booleanobject_getbaseobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetBaseObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetBaseTransform = (PLib3MFBooleanObject_SetBaseTransformPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setbasetransform"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetBaseTransform = (PLib3MFBooleanObject_SetBaseTransformPtr) dlsym(hLibrary, "lib3mf_booleanobject_setbasetransform"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetBaseTransform == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetBaseTransform = (PLib3MFBooleanObject_GetBaseTransformPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getbasetransform"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetBaseTransform = (PLib3MFBooleanObject_GetBaseTransformPtr) dlsym(hLibrary, "lib3mf_booleanobject_getbasetransform"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetBaseTransform == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetOperation = (PLib3MFBooleanObject_SetOperationPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setoperation"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetOperation = (PLib3MFBooleanObject_SetOperationPtr) dlsym(hLibrary, "lib3mf_booleanobject_setoperation"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetOperation == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetOperation = (PLib3MFBooleanObject_GetOperationPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getoperation"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetOperation = (PLib3MFBooleanObject_GetOperationPtr) dlsym(hLibrary, "lib3mf_booleanobject_getoperation"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetOperation == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetCSGModeEnabled = (PLib3MFBooleanObject_SetCSGModeEnabledPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setcsgmodeenabled"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetCSGModeEnabled = (PLib3MFBooleanObject_SetCSGModeEnabledPtr) dlsym(hLibrary, "lib3mf_booleanobject_setcsgmodeenabled"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetCSGModeEnabled == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetCSGModeEnabled = (PLib3MFBooleanObject_GetCSGModeEnabledPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getcsgmodeenabled"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetCSGModeEnabled = (PLib3MFBooleanObject_GetCSGModeEnabledPtr) dlsym(hLibrary, "lib3mf_booleanobject_getcsgmodeenabled"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetCSGModeEnabled == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetExtractionGridResolution = (PLib3MFBooleanObject_SetExtractionGridResolutionPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setextractiongridresolution"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetExtractionGridResolution = (PLib3MFBooleanObject_SetExtractionGridResolutionPtr) dlsym(hLibrary, "lib3mf_booleanobject_setextractiongridresolution"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetExtractionGridResolution == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetExtractionGridResolution = (PLib3MFBooleanObject_GetExtractionGridResolutionPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getextractiongridresolution"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetExtractionGridResolution = (PLib3MFBooleanObject_GetExtractionGridResolutionPtr) dlsym(hLibrary, "lib3mf_booleanobject_getextractiongridresolution"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetExtractionGridResolution == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetOperandCount = (PLib3MFBooleanObject_GetOperandCountPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getoperandcount"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetOperandCount = (PLib3MFBooleanObject_GetOperandCountPtr) dlsym(hLibrary, "lib3mf_booleanobject_getoperandcount"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetOperandCount == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_AddOperand = (PLib3MFBooleanObject_AddOperandPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_addoperand"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_AddOperand = (PLib3MFBooleanObject_AddOperandPtr) dlsym(hLibrary, "lib3mf_booleanobject_addoperand"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_AddOperand == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetOperand = (PLib3MFBooleanObject_GetOperandPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getoperand"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetOperand = (PLib3MFBooleanObject_GetOperandPtr) dlsym(hLibrary, "lib3mf_booleanobject_getoperand"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetOperand == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_BeamLattice_GetMinLength = (PLib3MFBeamLattice_GetMinLengthPtr) GetProcAddress(hLibrary, "lib3mf_beamlattice_getminlength"); #else // _WIN32 @@ -5896,6 +6049,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_Model_GetComponentsObjectByID == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Model_GetBooleanObjectByID = (PLib3MFModel_GetBooleanObjectByIDPtr) GetProcAddress(hLibrary, "lib3mf_model_getbooleanobjectbyid"); + #else // _WIN32 + pWrapperTable->m_Model_GetBooleanObjectByID = (PLib3MFModel_GetBooleanObjectByIDPtr) dlsym(hLibrary, "lib3mf_model_getbooleanobjectbyid"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Model_GetBooleanObjectByID == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Model_GetColorGroupByID = (PLib3MFModel_GetColorGroupByIDPtr) GetProcAddress(hLibrary, "lib3mf_model_getcolorgroupbyid"); #else // _WIN32 @@ -5995,6 +6157,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_Model_GetComponentsObjects == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Model_GetBooleanObjects = (PLib3MFModel_GetBooleanObjectsPtr) GetProcAddress(hLibrary, "lib3mf_model_getbooleanobjects"); + #else // _WIN32 + pWrapperTable->m_Model_GetBooleanObjects = (PLib3MFModel_GetBooleanObjectsPtr) dlsym(hLibrary, "lib3mf_model_getbooleanobjects"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Model_GetBooleanObjects == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Model_GetTexture2Ds = (PLib3MFModel_GetTexture2DsPtr) GetProcAddress(hLibrary, "lib3mf_model_gettexture2ds"); #else // _WIN32 @@ -6103,6 +6274,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_Model_AddComponentsObject == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Model_AddBooleanObject = (PLib3MFModel_AddBooleanObjectPtr) GetProcAddress(hLibrary, "lib3mf_model_addbooleanobject"); + #else // _WIN32 + pWrapperTable->m_Model_AddBooleanObject = (PLib3MFModel_AddBooleanObjectPtr) dlsym(hLibrary, "lib3mf_model_addbooleanobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Model_AddBooleanObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Model_AddSliceStack = (PLib3MFModel_AddSliceStackPtr) GetProcAddress(hLibrary, "lib3mf_model_addslicestack"); #else // _WIN32 @@ -6975,6 +7155,15 @@ Lib3MFResult CCall_lib3mf_componentsobjectiterator_getcurrentcomponentsobject(Li } +Lib3MFResult CCall_lib3mf_booleanobjectiterator_getcurrentbooleanobject(Lib3MFHandle libraryHandle, Lib3MF_BooleanObjectIterator pBooleanObjectIterator, Lib3MF_BooleanObject * pResource) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject (pBooleanObjectIterator, pResource); +} + + Lib3MFResult CCall_lib3mf_texture2diterator_getcurrenttexture2d(Lib3MFHandle libraryHandle, Lib3MF_Texture2DIterator pTexture2DIterator, Lib3MF_Texture2D * pResource) { if (libraryHandle == 0) @@ -7407,6 +7596,15 @@ Lib3MFResult CCall_lib3mf_object_islevelsetobject(Lib3MFHandle libraryHandle, Li } +Lib3MFResult CCall_lib3mf_object_isbooleanobject(Lib3MFHandle libraryHandle, Lib3MF_Object pObject, bool * pIsBooleanObject) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_Object_IsBooleanObject (pObject, pIsBooleanObject); +} + + Lib3MFResult CCall_lib3mf_object_isvalid(Lib3MFHandle libraryHandle, Lib3MF_Object pObject, bool * pIsValid) { if (libraryHandle == 0) @@ -7920,6 +8118,123 @@ Lib3MFResult CCall_lib3mf_levelset_setvolumedata(Lib3MFHandle libraryHandle, Lib } +Lib3MFResult CCall_lib3mf_booleanobject_setbaseobject(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object pBaseObject, const sLib3MFTransform * pTransform) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObject_SetBaseObject (pBooleanObject, pBaseObject, pTransform); +} + + +Lib3MFResult CCall_lib3mf_booleanobject_getbaseobject(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object * pBaseObject) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObject_GetBaseObject (pBooleanObject, pBaseObject); +} + + +Lib3MFResult CCall_lib3mf_booleanobject_setbasetransform(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, const sLib3MFTransform * pTransform) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObject_SetBaseTransform (pBooleanObject, pTransform); +} + + +Lib3MFResult CCall_lib3mf_booleanobject_getbasetransform(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, sLib3MFTransform * pTransform) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObject_GetBaseTransform (pBooleanObject, pTransform); +} + + +Lib3MFResult CCall_lib3mf_booleanobject_setoperation(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation eOperation) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObject_SetOperation (pBooleanObject, eOperation); +} + + +Lib3MFResult CCall_lib3mf_booleanobject_getoperation(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation * pOperation) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObject_GetOperation (pBooleanObject, pOperation); +} + + +Lib3MFResult CCall_lib3mf_booleanobject_setcsgmodeenabled(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, bool bCSGModeEnabled) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObject_SetCSGModeEnabled (pBooleanObject, bCSGModeEnabled); +} + + +Lib3MFResult CCall_lib3mf_booleanobject_getcsgmodeenabled(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, bool * pCSGModeEnabled) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObject_GetCSGModeEnabled (pBooleanObject, pCSGModeEnabled); +} + + +Lib3MFResult CCall_lib3mf_booleanobject_setextractiongridresolution(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nGridResolution) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObject_SetExtractionGridResolution (pBooleanObject, nGridResolution); +} + + +Lib3MFResult CCall_lib3mf_booleanobject_getextractiongridresolution(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pGridResolution) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObject_GetExtractionGridResolution (pBooleanObject, pGridResolution); +} + + +Lib3MFResult CCall_lib3mf_booleanobject_getoperandcount(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pCount) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObject_GetOperandCount (pBooleanObject, pCount); +} + + +Lib3MFResult CCall_lib3mf_booleanobject_addoperand(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_MeshObject pOperandObject, const sLib3MFTransform * pTransform) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObject_AddOperand (pBooleanObject, pOperandObject, pTransform); +} + + +Lib3MFResult CCall_lib3mf_booleanobject_getoperand(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nIndex, Lib3MF_MeshObject * pOperandObject, sLib3MFTransform * pTransform) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_BooleanObject_GetOperand (pBooleanObject, nIndex, pOperandObject, pTransform); +} + + Lib3MFResult CCall_lib3mf_beamlattice_getminlength(Lib3MFHandle libraryHandle, Lib3MF_BeamLattice pBeamLattice, Lib3MF_double * pMinLength) { if (libraryHandle == 0) @@ -11745,6 +12060,15 @@ Lib3MFResult CCall_lib3mf_model_getcomponentsobjectbyid(Lib3MFHandle libraryHand } +Lib3MFResult CCall_lib3mf_model_getbooleanobjectbyid(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_BooleanObject * pBooleanObjectInstance) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_Model_GetBooleanObjectByID (pModel, nUniqueResourceID, pBooleanObjectInstance); +} + + Lib3MFResult CCall_lib3mf_model_getcolorgroupbyid(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_ColorGroup * pColorGroupInstance) { if (libraryHandle == 0) @@ -11844,6 +12168,15 @@ Lib3MFResult CCall_lib3mf_model_getcomponentsobjects(Lib3MFHandle libraryHandle, } +Lib3MFResult CCall_lib3mf_model_getbooleanobjects(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_BooleanObjectIterator * pResourceIterator) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_Model_GetBooleanObjects (pModel, pResourceIterator); +} + + Lib3MFResult CCall_lib3mf_model_gettexture2ds(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_Texture2DIterator * pResourceIterator) { if (libraryHandle == 0) @@ -11952,6 +12285,15 @@ Lib3MFResult CCall_lib3mf_model_addcomponentsobject(Lib3MFHandle libraryHandle, } +Lib3MFResult CCall_lib3mf_model_addbooleanobject(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_BooleanObject * pBooleanObjectInstance) +{ + if (libraryHandle == 0) + return LIB3MF_ERROR_INVALIDCAST; + sLib3MFDynamicWrapperTable * wrapperTable = (sLib3MFDynamicWrapperTable *) libraryHandle; + return wrapperTable->m_Model_AddBooleanObject (pModel, pBooleanObjectInstance); +} + + Lib3MFResult CCall_lib3mf_model_addslicestack(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_double dZBottom, Lib3MF_SliceStack * pSliceStackInstance) { if (libraryHandle == 0) diff --git a/Autogenerated/Bindings/Go/lib3mf_dynamic.h b/Autogenerated/Bindings/Go/lib3mf_dynamic.h index 70597403b..a7a4de878 100644 --- a/Autogenerated/Bindings/Go/lib3mf_dynamic.h +++ b/Autogenerated/Bindings/Go/lib3mf_dynamic.h @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated plain C Header file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -484,6 +484,19 @@ typedef Lib3MFResult (*PLib3MFMeshObjectIterator_GetCurrentMeshObjectPtr) (Lib3M */ typedef Lib3MFResult (*PLib3MFComponentsObjectIterator_GetCurrentComponentsObjectPtr) (Lib3MF_ComponentsObjectIterator pComponentsObjectIterator, Lib3MF_ComponentsObject * pResource); +/************************************************************************************************************************* + Class definition for BooleanObjectIterator +**************************************************************************************************************************/ + +/** +* Returns the BooleanObject the iterator points at. +* +* @param[in] pBooleanObjectIterator - BooleanObjectIterator instance. +* @param[out] pResource - returns the BooleanObject instance. +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr) (Lib3MF_BooleanObjectIterator pBooleanObjectIterator, Lib3MF_BooleanObject * pResource); + /************************************************************************************************************************* Class definition for Texture2DIterator **************************************************************************************************************************/ @@ -998,6 +1011,15 @@ typedef Lib3MFResult (*PLib3MFObject_IsComponentsObjectPtr) (Lib3MF_Object pObje */ typedef Lib3MFResult (*PLib3MFObject_IsLevelSetObjectPtr) (Lib3MF_Object pObject, bool * pIsLevelSetObject); +/** +* Retrieves, if an object is a boolean object +* +* @param[in] pObject - Object instance. +* @param[out] pIsBooleanObject - returns, whether the object is a boolean object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFObject_IsBooleanObjectPtr) (Lib3MF_Object pObject, bool * pIsBooleanObject); + /** * Retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. * @@ -1548,6 +1570,131 @@ typedef Lib3MFResult (*PLib3MFLevelSet_GetVolumeDataPtr) (Lib3MF_LevelSet pLevel */ typedef Lib3MFResult (*PLib3MFLevelSet_SetVolumeDataPtr) (Lib3MF_LevelSet pLevelSet, Lib3MF_VolumeData pTheVolumeData); +/************************************************************************************************************************* + Class definition for BooleanObject +**************************************************************************************************************************/ + +/** +* Sets the base object and transform for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pBaseObject - base object of the boolean shape +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetBaseObjectPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object pBaseObject, const sLib3MFTransform * pTransform); + +/** +* Returns the base object of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pBaseObject - base object of the boolean shape +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetBaseObjectPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object * pBaseObject); + +/** +* Sets the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetBaseTransformPtr) (Lib3MF_BooleanObject pBooleanObject, const sLib3MFTransform * pTransform); + +/** +* Returns the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetBaseTransformPtr) (Lib3MF_BooleanObject pBooleanObject, sLib3MFTransform * pTransform); + +/** +* Sets the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] eOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetOperationPtr) (Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation eOperation); + +/** +* Returns the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetOperationPtr) (Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation * pOperation); + +/** +* Enables or disables CSG field evaluation for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] bCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetCSGModeEnabledPtr) (Lib3MF_BooleanObject pBooleanObject, bool bCSGModeEnabled); + +/** +* Returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetCSGModeEnabledPtr) (Lib3MF_BooleanObject pBooleanObject, bool * pCSGModeEnabled); + +/** +* Sets the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetExtractionGridResolutionPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nGridResolution); + +/** +* Returns the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetExtractionGridResolutionPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pGridResolution); + +/** +* Returns the number of operands in the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCount - number of operands in the boolean sequence +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetOperandCountPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pCount); + +/** +* Adds an operand object to the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pOperandObject - mesh object used as operand +* @param[in] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_AddOperandPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_MeshObject pOperandObject, const sLib3MFTransform * pTransform); + +/** +* Returns one operand object and transform from the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nIndex - index of the operand in the boolean sequence +* @param[out] pOperandObject - mesh object used as operand +* @param[out] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetOperandPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nIndex, Lib3MF_MeshObject * pOperandObject, sLib3MFTransform * pTransform); + /************************************************************************************************************************* Class definition for BeamLattice **************************************************************************************************************************/ @@ -6165,6 +6312,16 @@ typedef Lib3MFResult (*PLib3MFModel_GetMeshObjectByIDPtr) (Lib3MF_Model pModel, */ typedef Lib3MFResult (*PLib3MFModel_GetComponentsObjectByIDPtr) (Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* finds a boolean object by its UniqueResourceID +* +* @param[in] pModel - Model instance. +* @param[in] nUniqueResourceID - UniqueResourceID +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFModel_GetBooleanObjectByIDPtr) (Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * finds a model color group by its UniqueResourceID * @@ -6270,6 +6427,15 @@ typedef Lib3MFResult (*PLib3MFModel_GetMeshObjectsPtr) (Lib3MF_Model pModel, Lib */ typedef Lib3MFResult (*PLib3MFModel_GetComponentsObjectsPtr) (Lib3MF_Model pModel, Lib3MF_ComponentsObjectIterator * pResourceIterator); +/** +* creates a resource iterator instance with all boolean object resources. +* +* @param[in] pModel - Model instance. +* @param[out] pResourceIterator - returns the iterator instance. +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFModel_GetBooleanObjectsPtr) (Lib3MF_Model pModel, Lib3MF_BooleanObjectIterator * pResourceIterator); + /** * creates a Texture2DIterator instance with all texture2d resources. * @@ -6378,6 +6544,15 @@ typedef Lib3MFResult (*PLib3MFModel_AddMeshObjectPtr) (Lib3MF_Model pModel, Lib3 */ typedef Lib3MFResult (*PLib3MFModel_AddComponentsObjectPtr) (Lib3MF_Model pModel, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* adds an empty boolean object to the model. +* +* @param[in] pModel - Model instance. +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFModel_AddBooleanObjectPtr) (Lib3MF_Model pModel, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * creates a new model slicestack by its id * @@ -6928,6 +7103,7 @@ typedef struct { PLib3MFObjectIterator_GetCurrentObjectPtr m_ObjectIterator_GetCurrentObject; PLib3MFMeshObjectIterator_GetCurrentMeshObjectPtr m_MeshObjectIterator_GetCurrentMeshObject; PLib3MFComponentsObjectIterator_GetCurrentComponentsObjectPtr m_ComponentsObjectIterator_GetCurrentComponentsObject; + PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr m_BooleanObjectIterator_GetCurrentBooleanObject; PLib3MFTexture2DIterator_GetCurrentTexture2DPtr m_Texture2DIterator_GetCurrentTexture2D; PLib3MFBaseMaterialGroupIterator_GetCurrentBaseMaterialGroupPtr m_BaseMaterialGroupIterator_GetCurrentBaseMaterialGroup; PLib3MFColorGroupIterator_GetCurrentColorGroupPtr m_ColorGroupIterator_GetCurrentColorGroup; @@ -6976,6 +7152,7 @@ typedef struct { PLib3MFObject_IsMeshObjectPtr m_Object_IsMeshObject; PLib3MFObject_IsComponentsObjectPtr m_Object_IsComponentsObject; PLib3MFObject_IsLevelSetObjectPtr m_Object_IsLevelSetObject; + PLib3MFObject_IsBooleanObjectPtr m_Object_IsBooleanObject; PLib3MFObject_IsValidPtr m_Object_IsValid; PLib3MFObject_SetAttachmentAsThumbnailPtr m_Object_SetAttachmentAsThumbnail; PLib3MFObject_GetThumbnailAttachmentPtr m_Object_GetThumbnailAttachment; @@ -7033,6 +7210,19 @@ typedef struct { PLib3MFLevelSet_GetMeshPtr m_LevelSet_GetMesh; PLib3MFLevelSet_GetVolumeDataPtr m_LevelSet_GetVolumeData; PLib3MFLevelSet_SetVolumeDataPtr m_LevelSet_SetVolumeData; + PLib3MFBooleanObject_SetBaseObjectPtr m_BooleanObject_SetBaseObject; + PLib3MFBooleanObject_GetBaseObjectPtr m_BooleanObject_GetBaseObject; + PLib3MFBooleanObject_SetBaseTransformPtr m_BooleanObject_SetBaseTransform; + PLib3MFBooleanObject_GetBaseTransformPtr m_BooleanObject_GetBaseTransform; + PLib3MFBooleanObject_SetOperationPtr m_BooleanObject_SetOperation; + PLib3MFBooleanObject_GetOperationPtr m_BooleanObject_GetOperation; + PLib3MFBooleanObject_SetCSGModeEnabledPtr m_BooleanObject_SetCSGModeEnabled; + PLib3MFBooleanObject_GetCSGModeEnabledPtr m_BooleanObject_GetCSGModeEnabled; + PLib3MFBooleanObject_SetExtractionGridResolutionPtr m_BooleanObject_SetExtractionGridResolution; + PLib3MFBooleanObject_GetExtractionGridResolutionPtr m_BooleanObject_GetExtractionGridResolution; + PLib3MFBooleanObject_GetOperandCountPtr m_BooleanObject_GetOperandCount; + PLib3MFBooleanObject_AddOperandPtr m_BooleanObject_AddOperand; + PLib3MFBooleanObject_GetOperandPtr m_BooleanObject_GetOperand; PLib3MFBeamLattice_GetMinLengthPtr m_BeamLattice_GetMinLength; PLib3MFBeamLattice_SetMinLengthPtr m_BeamLattice_SetMinLength; PLib3MFBeamLattice_GetClippingPtr m_BeamLattice_GetClipping; @@ -7458,6 +7648,7 @@ typedef struct { PLib3MFModel_GetMultiPropertyGroupByIDPtr m_Model_GetMultiPropertyGroupByID; PLib3MFModel_GetMeshObjectByIDPtr m_Model_GetMeshObjectByID; PLib3MFModel_GetComponentsObjectByIDPtr m_Model_GetComponentsObjectByID; + PLib3MFModel_GetBooleanObjectByIDPtr m_Model_GetBooleanObjectByID; PLib3MFModel_GetColorGroupByIDPtr m_Model_GetColorGroupByID; PLib3MFModel_GetSliceStackByIDPtr m_Model_GetSliceStackByID; PLib3MFModel_GetLevelSetByIDPtr m_Model_GetLevelSetByID; @@ -7469,6 +7660,7 @@ typedef struct { PLib3MFModel_GetObjectsPtr m_Model_GetObjects; PLib3MFModel_GetMeshObjectsPtr m_Model_GetMeshObjects; PLib3MFModel_GetComponentsObjectsPtr m_Model_GetComponentsObjects; + PLib3MFModel_GetBooleanObjectsPtr m_Model_GetBooleanObjects; PLib3MFModel_GetTexture2DsPtr m_Model_GetTexture2Ds; PLib3MFModel_GetBaseMaterialGroupsPtr m_Model_GetBaseMaterialGroups; PLib3MFModel_GetColorGroupsPtr m_Model_GetColorGroups; @@ -7481,6 +7673,7 @@ typedef struct { PLib3MFModel_MergeFromModelPtr m_Model_MergeFromModel; PLib3MFModel_AddMeshObjectPtr m_Model_AddMeshObject; PLib3MFModel_AddComponentsObjectPtr m_Model_AddComponentsObject; + PLib3MFModel_AddBooleanObjectPtr m_Model_AddBooleanObject; PLib3MFModel_AddSliceStackPtr m_Model_AddSliceStack; PLib3MFModel_AddTexture2DFromAttachmentPtr m_Model_AddTexture2DFromAttachment; PLib3MFModel_AddBaseMaterialGroupPtr m_Model_AddBaseMaterialGroup; @@ -7673,6 +7866,9 @@ Lib3MFResult CCall_lib3mf_meshobjectiterator_getcurrentmeshobject(Lib3MFHandle l Lib3MFResult CCall_lib3mf_componentsobjectiterator_getcurrentcomponentsobject(Lib3MFHandle libraryHandle, Lib3MF_ComponentsObjectIterator pComponentsObjectIterator, Lib3MF_ComponentsObject * pResource); +Lib3MFResult CCall_lib3mf_booleanobjectiterator_getcurrentbooleanobject(Lib3MFHandle libraryHandle, Lib3MF_BooleanObjectIterator pBooleanObjectIterator, Lib3MF_BooleanObject * pResource); + + Lib3MFResult CCall_lib3mf_texture2diterator_getcurrenttexture2d(Lib3MFHandle libraryHandle, Lib3MF_Texture2DIterator pTexture2DIterator, Lib3MF_Texture2D * pResource); @@ -7817,6 +8013,9 @@ Lib3MFResult CCall_lib3mf_object_iscomponentsobject(Lib3MFHandle libraryHandle, Lib3MFResult CCall_lib3mf_object_islevelsetobject(Lib3MFHandle libraryHandle, Lib3MF_Object pObject, bool * pIsLevelSetObject); +Lib3MFResult CCall_lib3mf_object_isbooleanobject(Lib3MFHandle libraryHandle, Lib3MF_Object pObject, bool * pIsBooleanObject); + + Lib3MFResult CCall_lib3mf_object_isvalid(Lib3MFHandle libraryHandle, Lib3MF_Object pObject, bool * pIsValid); @@ -7988,6 +8187,45 @@ Lib3MFResult CCall_lib3mf_levelset_getvolumedata(Lib3MFHandle libraryHandle, Lib Lib3MFResult CCall_lib3mf_levelset_setvolumedata(Lib3MFHandle libraryHandle, Lib3MF_LevelSet pLevelSet, Lib3MF_VolumeData pTheVolumeData); +Lib3MFResult CCall_lib3mf_booleanobject_setbaseobject(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object pBaseObject, const sLib3MFTransform * pTransform); + + +Lib3MFResult CCall_lib3mf_booleanobject_getbaseobject(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object * pBaseObject); + + +Lib3MFResult CCall_lib3mf_booleanobject_setbasetransform(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, const sLib3MFTransform * pTransform); + + +Lib3MFResult CCall_lib3mf_booleanobject_getbasetransform(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, sLib3MFTransform * pTransform); + + +Lib3MFResult CCall_lib3mf_booleanobject_setoperation(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation eOperation); + + +Lib3MFResult CCall_lib3mf_booleanobject_getoperation(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation * pOperation); + + +Lib3MFResult CCall_lib3mf_booleanobject_setcsgmodeenabled(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, bool bCSGModeEnabled); + + +Lib3MFResult CCall_lib3mf_booleanobject_getcsgmodeenabled(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, bool * pCSGModeEnabled); + + +Lib3MFResult CCall_lib3mf_booleanobject_setextractiongridresolution(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nGridResolution); + + +Lib3MFResult CCall_lib3mf_booleanobject_getextractiongridresolution(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pGridResolution); + + +Lib3MFResult CCall_lib3mf_booleanobject_getoperandcount(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pCount); + + +Lib3MFResult CCall_lib3mf_booleanobject_addoperand(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_MeshObject pOperandObject, const sLib3MFTransform * pTransform); + + +Lib3MFResult CCall_lib3mf_booleanobject_getoperand(Lib3MFHandle libraryHandle, Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nIndex, Lib3MF_MeshObject * pOperandObject, sLib3MFTransform * pTransform); + + Lib3MFResult CCall_lib3mf_beamlattice_getminlength(Lib3MFHandle libraryHandle, Lib3MF_BeamLattice pBeamLattice, Lib3MF_double * pMinLength); @@ -9263,6 +9501,9 @@ Lib3MFResult CCall_lib3mf_model_getmeshobjectbyid(Lib3MFHandle libraryHandle, Li Lib3MFResult CCall_lib3mf_model_getcomponentsobjectbyid(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_ComponentsObject * pComponentsObjectInstance); +Lib3MFResult CCall_lib3mf_model_getbooleanobjectbyid(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_BooleanObject * pBooleanObjectInstance); + + Lib3MFResult CCall_lib3mf_model_getcolorgroupbyid(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_ColorGroup * pColorGroupInstance); @@ -9296,6 +9537,9 @@ Lib3MFResult CCall_lib3mf_model_getmeshobjects(Lib3MFHandle libraryHandle, Lib3M Lib3MFResult CCall_lib3mf_model_getcomponentsobjects(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_ComponentsObjectIterator * pResourceIterator); +Lib3MFResult CCall_lib3mf_model_getbooleanobjects(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_BooleanObjectIterator * pResourceIterator); + + Lib3MFResult CCall_lib3mf_model_gettexture2ds(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_Texture2DIterator * pResourceIterator); @@ -9332,6 +9576,9 @@ Lib3MFResult CCall_lib3mf_model_addmeshobject(Lib3MFHandle libraryHandle, Lib3MF Lib3MFResult CCall_lib3mf_model_addcomponentsobject(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_ComponentsObject * pComponentsObjectInstance); +Lib3MFResult CCall_lib3mf_model_addbooleanobject(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_BooleanObject * pBooleanObjectInstance); + + Lib3MFResult CCall_lib3mf_model_addslicestack(Lib3MFHandle libraryHandle, Lib3MF_Model pModel, Lib3MF_double dZBottom, Lib3MF_SliceStack * pSliceStackInstance); diff --git a/Autogenerated/Bindings/Go/lib3mf_types.h b/Autogenerated/Bindings/Go/lib3mf_types.h index d87847a5b..b9e4aec27 100644 --- a/Autogenerated/Bindings/Go/lib3mf_types.h +++ b/Autogenerated/Bindings/Go/lib3mf_types.h @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated plain C Header file with basic types in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -84,7 +84,7 @@ typedef void * Lib3MF_pvoid; **************************************************************************************************************************/ #define LIB3MF_VERSION_MAJOR 2 -#define LIB3MF_VERSION_MINOR 5 +#define LIB3MF_VERSION_MINOR 6 #define LIB3MF_VERSION_MICRO 0 #define LIB3MF_VERSION_PRERELEASEINFO "" #define LIB3MF_VERSION_BUILDINFO "" @@ -220,6 +220,7 @@ typedef Lib3MFHandle Lib3MF_SliceStackIterator; typedef Lib3MFHandle Lib3MF_ObjectIterator; typedef Lib3MFHandle Lib3MF_MeshObjectIterator; typedef Lib3MFHandle Lib3MF_ComponentsObjectIterator; +typedef Lib3MFHandle Lib3MF_BooleanObjectIterator; typedef Lib3MFHandle Lib3MF_Texture2DIterator; typedef Lib3MFHandle Lib3MF_BaseMaterialGroupIterator; typedef Lib3MFHandle Lib3MF_ColorGroupIterator; @@ -235,6 +236,7 @@ typedef Lib3MFHandle Lib3MF_TriangleSet; typedef Lib3MFHandle Lib3MF_Object; typedef Lib3MFHandle Lib3MF_MeshObject; typedef Lib3MFHandle Lib3MF_LevelSet; +typedef Lib3MFHandle Lib3MF_BooleanObject; typedef Lib3MFHandle Lib3MF_BeamLattice; typedef Lib3MFHandle Lib3MF_FunctionReference; typedef Lib3MFHandle Lib3MF_VolumeDataColor; @@ -365,6 +367,12 @@ typedef enum eLib3MFObjectType { eObjectTypeSurface = 4 } eLib3MFObjectType; +typedef enum eLib3MFBooleanOperation { + eBooleanOperationUnion = 0, + eBooleanOperationDifference = 1, + eBooleanOperationIntersection = 2 +} eLib3MFBooleanOperation; + typedef enum eLib3MFTextureType { eTextureTypeUnknown = 0, eTextureTypePNG = 1, @@ -585,6 +593,11 @@ typedef union { int m_code; } structEnumLib3MFObjectType; +typedef union { + eLib3MFBooleanOperation m_enum; + int m_code; +} structEnumLib3MFBooleanOperation; + typedef union { eLib3MFTextureType m_enum; int m_code; diff --git a/Autogenerated/Bindings/Java8/build_jar.sh b/Autogenerated/Bindings/Java8/build_jar.sh index 02819608f..8bd9d4094 100644 --- a/Autogenerated/Bindings/Java8/build_jar.sh +++ b/Autogenerated/Bindings/Java8/build_jar.sh @@ -9,4 +9,4 @@ echo "Compile Java Bindings" javac -classpath *.jar lib3mf/* echo "Create JAR" -jar cvf lib3mf-2.5.0.jar lib3mf +jar cvf lib3mf-2.6.0.jar lib3mf diff --git a/Autogenerated/Bindings/Java8/lib3mf/AbsNode.java b/Autogenerated/Bindings/Java8/lib3mf/AbsNode.java index 048755683..0d6fd7cec 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/AbsNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/AbsNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/AccessRight.java b/Autogenerated/Bindings/Java8/lib3mf/AccessRight.java index a5fa54964..8f0c00f15 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/AccessRight.java +++ b/Autogenerated/Bindings/Java8/lib3mf/AccessRight.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/AdditionNode.java b/Autogenerated/Bindings/Java8/lib3mf/AdditionNode.java index 79b8c4f85..4a7dae3a2 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/AdditionNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/AdditionNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ArcCosNode.java b/Autogenerated/Bindings/Java8/lib3mf/ArcCosNode.java index b06fe14a4..ff5373b94 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ArcCosNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ArcCosNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ArcSinNode.java b/Autogenerated/Bindings/Java8/lib3mf/ArcSinNode.java index 8e2d874b0..d856a6d6b 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ArcSinNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ArcSinNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ArcTan2Node.java b/Autogenerated/Bindings/Java8/lib3mf/ArcTan2Node.java index b8faa14c1..c3c69655e 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ArcTan2Node.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ArcTan2Node.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ArcTanNode.java b/Autogenerated/Bindings/Java8/lib3mf/ArcTanNode.java index d5bef49b6..8139cd557 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ArcTanNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ArcTanNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Attachment.java b/Autogenerated/Bindings/Java8/lib3mf/Attachment.java index 825f204c7..d5c0471d3 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Attachment.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Attachment.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Ball.java b/Autogenerated/Bindings/Java8/lib3mf/Ball.java index c01b96185..3862d162f 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Ball.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Ball.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Base.java b/Autogenerated/Bindings/Java8/lib3mf/Base.java index af354ada6..b6c3e23b9 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Base.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Base.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/BaseMaterialGroup.java b/Autogenerated/Bindings/Java8/lib3mf/BaseMaterialGroup.java index 9c4bb88a1..e4af1dae0 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/BaseMaterialGroup.java +++ b/Autogenerated/Bindings/Java8/lib3mf/BaseMaterialGroup.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/BaseMaterialGroupIterator.java b/Autogenerated/Bindings/Java8/lib3mf/BaseMaterialGroupIterator.java index de0cfea77..22708aaf6 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/BaseMaterialGroupIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/BaseMaterialGroupIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Beam.java b/Autogenerated/Bindings/Java8/lib3mf/Beam.java index 40cbe6400..21817705d 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Beam.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Beam.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/BeamLattice.java b/Autogenerated/Bindings/Java8/lib3mf/BeamLattice.java index 8328f24f3..662d7e1f9 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/BeamLattice.java +++ b/Autogenerated/Bindings/Java8/lib3mf/BeamLattice.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/BeamLatticeNode.java b/Autogenerated/Bindings/Java8/lib3mf/BeamLatticeNode.java index 59f2122f6..d993e5da1 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/BeamLatticeNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/BeamLatticeNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/BeamSet.java b/Autogenerated/Bindings/Java8/lib3mf/BeamSet.java index 4e3337151..247f99487 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/BeamSet.java +++ b/Autogenerated/Bindings/Java8/lib3mf/BeamSet.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/BooleanObject.java b/Autogenerated/Bindings/Java8/lib3mf/BooleanObject.java new file mode 100644 index 000000000..79d619b64 --- /dev/null +++ b/Autogenerated/Bindings/Java8/lib3mf/BooleanObject.java @@ -0,0 +1,253 @@ +/*++ + +Copyright (C) 2024 3MF Consortium (Original Author) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated Java file in order to allow an easy + use of the 3MF Library + +Interface version: 2.6.0 + +*/ + +package lib3mf; + +import com.sun.jna.Library; +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; + + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; + +public class BooleanObject extends Object { + + public BooleanObject(Lib3MFWrapper wrapper, Pointer handle) { + super(wrapper, handle); + } + + /** + * Sets the base object and transform for the boolean shape. + * + * @param baseObject base object of the boolean shape + * @param transform transform applied to the base object + * @throws Lib3MFException + */ + public void setBaseObject(Object baseObject, Transform transform) throws Lib3MFException { + Pointer baseObjectHandle = null; + if (baseObject != null) { + baseObjectHandle = baseObject.getHandle(); + } else { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "BaseObject is a null value."); + } + Pointer bufferTransform = new Memory(Transform.SIZE); + transform.writeToPointer(bufferTransform, 0); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_setbaseobject.invokeInt(new java.lang.Object[]{mHandle, baseObjectHandle, bufferTransform})); + } + + /** + * Returns the base object of the boolean shape. + * + * @return base object of the boolean shape + * @throws Lib3MFException + */ + public Object getBaseObject() throws Lib3MFException { + Pointer bufferBaseObject = new Memory(8); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getbaseobject.invokeInt(new java.lang.Object[]{mHandle, bufferBaseObject})); + Pointer valueBaseObject = bufferBaseObject.getPointer(0); + Object baseObject = null; + if (valueBaseObject == Pointer.NULL) { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "BaseObject was a null pointer"); + } + baseObject = mWrapper.PolymorphicFactory(valueBaseObject, Object.class); + return baseObject; + } + + /** + * Sets the base transform of the boolean shape. + * + * @param transform transform applied to the base object + * @throws Lib3MFException + */ + public void setBaseTransform(Transform transform) throws Lib3MFException { + Pointer bufferTransform = new Memory(Transform.SIZE); + transform.writeToPointer(bufferTransform, 0); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_setbasetransform.invokeInt(new java.lang.Object[]{mHandle, bufferTransform})); + } + + /** + * Returns the base transform of the boolean shape. + * + * @return transform applied to the base object + * @throws Lib3MFException + */ + public Transform getBaseTransform() throws Lib3MFException { + Pointer bufferTransform = new Memory(Transform.SIZE); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getbasetransform.invokeInt(new java.lang.Object[]{mHandle, bufferTransform})); + Transform transform = new Transform(); + transform.readFromPointer(bufferTransform, 0); + return transform; + } + + /** + * Sets the boolean operation used for the boolean shape. + * + * @param operation boolean operation used for the shape + * @throws Lib3MFException + */ + public void setOperation(Lib3MFWrapper.BooleanOperation operation) throws Lib3MFException { + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_setoperation.invokeInt(new java.lang.Object[]{mHandle, Lib3MFWrapper.EnumConversion.convertBooleanOperationToConst(operation)})); + } + + /** + * Returns the boolean operation used for the boolean shape. + * + * @return boolean operation used for the shape + * @throws Lib3MFException + */ + public Lib3MFWrapper.BooleanOperation getOperation() throws Lib3MFException { + Pointer bufferOperation = new Memory(4); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getoperation.invokeInt(new java.lang.Object[]{mHandle, bufferOperation})); + return Lib3MFWrapper.EnumConversion.convertConstToBooleanOperation(bufferOperation.getInt(0)); + } + + /** + * Enables or disables CSG field evaluation for boolean-to-mesh materialization. + * + * @param cSGModeEnabled if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback + * @throws Lib3MFException + */ + public void setCSGModeEnabled(boolean cSGModeEnabled) throws Lib3MFException { + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_setcsgmodeenabled.invokeInt(new java.lang.Object[]{mHandle, cSGModeEnabled})); + } + + /** + * Returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. + * + * @return if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback + * @throws Lib3MFException + */ + public boolean getCSGModeEnabled() throws Lib3MFException { + Pointer bufferCSGModeEnabled = new Memory(1); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getcsgmodeenabled.invokeInt(new java.lang.Object[]{mHandle, bufferCSGModeEnabled})); + return bufferCSGModeEnabled.getByte(0) != 0; + } + + /** + * Sets the extraction grid resolution used for boolean-to-mesh materialization. + * + * @param gridResolution extraction grid resolution for boolean surface extraction + * @throws Lib3MFException + */ + public void setExtractionGridResolution(int gridResolution) throws Lib3MFException { + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_setextractiongridresolution.invokeInt(new java.lang.Object[]{mHandle, gridResolution})); + } + + /** + * Returns the extraction grid resolution used for boolean-to-mesh materialization. + * + * @return extraction grid resolution for boolean surface extraction + * @throws Lib3MFException + */ + public int getExtractionGridResolution() throws Lib3MFException { + Pointer bufferGridResolution = new Memory(4); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getextractiongridresolution.invokeInt(new java.lang.Object[]{mHandle, bufferGridResolution})); + return bufferGridResolution.getInt(0); + } + + /** + * Returns the number of operands in the boolean sequence. + * + * @return number of operands in the boolean sequence + * @throws Lib3MFException + */ + public int getOperandCount() throws Lib3MFException { + Pointer bufferCount = new Memory(4); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getoperandcount.invokeInt(new java.lang.Object[]{mHandle, bufferCount})); + return bufferCount.getInt(0); + } + + /** + * Adds an operand object to the boolean sequence. + * + * @param operandObject mesh object used as operand + * @param transform transform applied to the operand object + * @throws Lib3MFException + */ + public void addOperand(MeshObject operandObject, Transform transform) throws Lib3MFException { + Pointer operandObjectHandle = null; + if (operandObject != null) { + operandObjectHandle = operandObject.getHandle(); + } else { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "OperandObject is a null value."); + } + Pointer bufferTransform = new Memory(Transform.SIZE); + transform.writeToPointer(bufferTransform, 0); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_addoperand.invokeInt(new java.lang.Object[]{mHandle, operandObjectHandle, bufferTransform})); + } + + /** + * Returns one operand object and transform from the boolean sequence. + * + * @param index index of the operand in the boolean sequence + * @return GetOperand Result Tuple + * @throws Lib3MFException + */ + public GetOperandResult getOperand(int index) throws Lib3MFException { + Pointer bufferOperandObject = new Memory(8); + Pointer bufferTransform = new Memory(Transform.SIZE); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getoperand.invokeInt(new java.lang.Object[]{mHandle, index, bufferOperandObject, bufferTransform})); + Pointer valueOperandObject = bufferOperandObject.getPointer(0); + MeshObject operandObject = null; + if (valueOperandObject == Pointer.NULL) { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "OperandObject was a null pointer"); + } + operandObject = mWrapper.PolymorphicFactory(valueOperandObject, MeshObject.class); + Transform transform = new Transform(); + transform.readFromPointer(bufferTransform, 0); + GetOperandResult returnTuple = new GetOperandResult(); + returnTuple.OperandObject = operandObject; + returnTuple.Transform = transform; + return returnTuple; + } + + public static class GetOperandResult { + /** + * mesh object used as operand + */ + public MeshObject OperandObject; + + /** + * transform applied to the operand object + */ + public Transform Transform; + + } + +} + diff --git a/Autogenerated/Bindings/Java8/lib3mf/BooleanObjectIterator.java b/Autogenerated/Bindings/Java8/lib3mf/BooleanObjectIterator.java new file mode 100644 index 000000000..badb9bcf6 --- /dev/null +++ b/Autogenerated/Bindings/Java8/lib3mf/BooleanObjectIterator.java @@ -0,0 +1,74 @@ +/*++ + +Copyright (C) 2024 3MF Consortium (Original Author) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated Java file in order to allow an easy + use of the 3MF Library + +Interface version: 2.6.0 + +*/ + +package lib3mf; + +import com.sun.jna.Library; +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; + + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; + +public class BooleanObjectIterator extends ResourceIterator { + + public BooleanObjectIterator(Lib3MFWrapper wrapper, Pointer handle) { + super(wrapper, handle); + } + + /** + * Returns the BooleanObject the iterator points at. + * + * @return returns the BooleanObject instance. + * @throws Lib3MFException + */ + public BooleanObject getCurrentBooleanObject() throws Lib3MFException { + Pointer bufferResource = new Memory(8); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobjectiterator_getcurrentbooleanobject.invokeInt(new java.lang.Object[]{mHandle, bufferResource})); + Pointer valueResource = bufferResource.getPointer(0); + BooleanObject resource = null; + if (valueResource == Pointer.NULL) { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "Resource was a null pointer"); + } + resource = mWrapper.PolymorphicFactory(valueResource, BooleanObject.class); + return resource; + } + + +} + diff --git a/Autogenerated/Bindings/Java8/lib3mf/Box.java b/Autogenerated/Bindings/Java8/lib3mf/Box.java index 70a3484e0..e07728552 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Box.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Box.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/BuildItem.java b/Autogenerated/Bindings/Java8/lib3mf/BuildItem.java index 94d552c6f..5f2cbfd8c 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/BuildItem.java +++ b/Autogenerated/Bindings/Java8/lib3mf/BuildItem.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/BuildItemIterator.java b/Autogenerated/Bindings/Java8/lib3mf/BuildItemIterator.java index ce259bbc6..16a098c61 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/BuildItemIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/BuildItemIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/CeilNode.java b/Autogenerated/Bindings/Java8/lib3mf/CeilNode.java index aac96b448..bc450c7b2 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/CeilNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/CeilNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ClampNode.java b/Autogenerated/Bindings/Java8/lib3mf/ClampNode.java index e22fc3446..8d625cc15 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ClampNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ClampNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Color.java b/Autogenerated/Bindings/Java8/lib3mf/Color.java index 66de0245e..2530c2382 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Color.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Color.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ColorGroup.java b/Autogenerated/Bindings/Java8/lib3mf/ColorGroup.java index f2018a23a..8fab9d276 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ColorGroup.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ColorGroup.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ColorGroupIterator.java b/Autogenerated/Bindings/Java8/lib3mf/ColorGroupIterator.java index ad58d7092..53d401836 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ColorGroupIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ColorGroupIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Component.java b/Autogenerated/Bindings/Java8/lib3mf/Component.java index 8817b0a1a..c81e41b19 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Component.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Component.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ComponentsObject.java b/Autogenerated/Bindings/Java8/lib3mf/ComponentsObject.java index 52a5a8e16..0bef522b1 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ComponentsObject.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ComponentsObject.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ComponentsObjectIterator.java b/Autogenerated/Bindings/Java8/lib3mf/ComponentsObjectIterator.java index 8c2a5050d..b5da35646 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ComponentsObjectIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ComponentsObjectIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ComposeMatrixNode.java b/Autogenerated/Bindings/Java8/lib3mf/ComposeMatrixNode.java index f74e83338..5d4a4272e 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ComposeMatrixNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ComposeMatrixNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ComposeVectorNode.java b/Autogenerated/Bindings/Java8/lib3mf/ComposeVectorNode.java index 2fcc5aa31..ed987c770 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ComposeVectorNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ComposeVectorNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/CompositeConstituent.java b/Autogenerated/Bindings/Java8/lib3mf/CompositeConstituent.java index 8a799bd84..a472fb4f1 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/CompositeConstituent.java +++ b/Autogenerated/Bindings/Java8/lib3mf/CompositeConstituent.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/CompositeMaterials.java b/Autogenerated/Bindings/Java8/lib3mf/CompositeMaterials.java index 2be3800cc..f233f7001 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/CompositeMaterials.java +++ b/Autogenerated/Bindings/Java8/lib3mf/CompositeMaterials.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/CompositeMaterialsIterator.java b/Autogenerated/Bindings/Java8/lib3mf/CompositeMaterialsIterator.java index e761f69cf..877d38d30 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/CompositeMaterialsIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/CompositeMaterialsIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ConstMatNode.java b/Autogenerated/Bindings/Java8/lib3mf/ConstMatNode.java index 7de80bd61..267bfea30 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ConstMatNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ConstMatNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ConstVecNode.java b/Autogenerated/Bindings/Java8/lib3mf/ConstVecNode.java index 9458b56bb..6d513e737 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ConstVecNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ConstVecNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ConstantNode.java b/Autogenerated/Bindings/Java8/lib3mf/ConstantNode.java index 58ddce61b..594054662 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ConstantNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ConstantNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Consumer.java b/Autogenerated/Bindings/Java8/lib3mf/Consumer.java index 5de66765f..d8b3e80eb 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Consumer.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Consumer.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ContentEncryptionParams.java b/Autogenerated/Bindings/Java8/lib3mf/ContentEncryptionParams.java index 74f1e2fdc..6f243f722 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ContentEncryptionParams.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ContentEncryptionParams.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/CosNode.java b/Autogenerated/Bindings/Java8/lib3mf/CosNode.java index ad499ed27..9980980fd 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/CosNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/CosNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/CoshNode.java b/Autogenerated/Bindings/Java8/lib3mf/CoshNode.java index 78475175a..67e78fdbc 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/CoshNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/CoshNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/CrossNode.java b/Autogenerated/Bindings/Java8/lib3mf/CrossNode.java index 59a61b517..53c927b0b 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/CrossNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/CrossNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/DecomposeVectorNode.java b/Autogenerated/Bindings/Java8/lib3mf/DecomposeVectorNode.java index 6cf2d49db..9410a166a 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/DecomposeVectorNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/DecomposeVectorNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/DivisionNode.java b/Autogenerated/Bindings/Java8/lib3mf/DivisionNode.java index d4731a1c1..cc8a4ad21 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/DivisionNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/DivisionNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/DotNode.java b/Autogenerated/Bindings/Java8/lib3mf/DotNode.java index 43aeef1f6..2ce83c843 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/DotNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/DotNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ExpNode.java b/Autogenerated/Bindings/Java8/lib3mf/ExpNode.java index 33f07a2d9..16eff1e15 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ExpNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ExpNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/FloorNode.java b/Autogenerated/Bindings/Java8/lib3mf/FloorNode.java index 02f584802..396940c56 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/FloorNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/FloorNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/FmodNode.java b/Autogenerated/Bindings/Java8/lib3mf/FmodNode.java index b49001fad..69ee610fb 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/FmodNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/FmodNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/FractNode.java b/Autogenerated/Bindings/Java8/lib3mf/FractNode.java index 18a88fa7c..6c711f62e 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/FractNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/FractNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Function.java b/Autogenerated/Bindings/Java8/lib3mf/Function.java index 8f33a7c6b..e76033507 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Function.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Function.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/FunctionCallNode.java b/Autogenerated/Bindings/Java8/lib3mf/FunctionCallNode.java index f849b396e..7f65f66a9 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/FunctionCallNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/FunctionCallNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/FunctionFromImage3D.java b/Autogenerated/Bindings/Java8/lib3mf/FunctionFromImage3D.java index dcafeb36a..133d6ea79 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/FunctionFromImage3D.java +++ b/Autogenerated/Bindings/Java8/lib3mf/FunctionFromImage3D.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/FunctionGradientNode.java b/Autogenerated/Bindings/Java8/lib3mf/FunctionGradientNode.java index c648464fc..5b0cdb6d8 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/FunctionGradientNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/FunctionGradientNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/FunctionIterator.java b/Autogenerated/Bindings/Java8/lib3mf/FunctionIterator.java index 6d6849fa3..c32c277a0 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/FunctionIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/FunctionIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/FunctionReference.java b/Autogenerated/Bindings/Java8/lib3mf/FunctionReference.java index 89ed264b9..f10cb3068 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/FunctionReference.java +++ b/Autogenerated/Bindings/Java8/lib3mf/FunctionReference.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Image3D.java b/Autogenerated/Bindings/Java8/lib3mf/Image3D.java index 90afdecd2..57d546906 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Image3D.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Image3D.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Image3DIterator.java b/Autogenerated/Bindings/Java8/lib3mf/Image3DIterator.java index f9d567464..176be9f35 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Image3DIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Image3DIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ImageStack.java b/Autogenerated/Bindings/Java8/lib3mf/ImageStack.java index 1c6b101f7..38040917b 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ImageStack.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ImageStack.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ImplicitFunction.java b/Autogenerated/Bindings/Java8/lib3mf/ImplicitFunction.java index bb328cc78..1e9ce2690 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ImplicitFunction.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ImplicitFunction.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ImplicitNode.java b/Autogenerated/Bindings/Java8/lib3mf/ImplicitNode.java index 6776b5634..c107d62b2 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ImplicitNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ImplicitNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ImplicitPort.java b/Autogenerated/Bindings/Java8/lib3mf/ImplicitPort.java index 7aba9cf61..9914c1d34 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ImplicitPort.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ImplicitPort.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ImplicitPortIterator.java b/Autogenerated/Bindings/Java8/lib3mf/ImplicitPortIterator.java index f116ff59f..7db97344c 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ImplicitPortIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ImplicitPortIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/InverseNode.java b/Autogenerated/Bindings/Java8/lib3mf/InverseNode.java index 336e71174..df50d9c85 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/InverseNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/InverseNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Iterator.java b/Autogenerated/Bindings/Java8/lib3mf/Iterator.java index e6197ae5a..98feff349 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Iterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Iterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/KeyStore.java b/Autogenerated/Bindings/Java8/lib3mf/KeyStore.java index 5143f5390..bd0ca4b8f 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/KeyStore.java +++ b/Autogenerated/Bindings/Java8/lib3mf/KeyStore.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/LengthNode.java b/Autogenerated/Bindings/Java8/lib3mf/LengthNode.java index 5eb7b7455..751e8665a 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/LengthNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/LengthNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/LevelSet.java b/Autogenerated/Bindings/Java8/lib3mf/LevelSet.java index fa7149c1e..7b90acc83 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/LevelSet.java +++ b/Autogenerated/Bindings/Java8/lib3mf/LevelSet.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/LevelSetIterator.java b/Autogenerated/Bindings/Java8/lib3mf/LevelSetIterator.java index 2e58d27d4..90436f8f7 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/LevelSetIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/LevelSetIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Lib3MFException.java b/Autogenerated/Bindings/Java8/lib3mf/Lib3MFException.java index c6f0fffb0..55f2739fe 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Lib3MFException.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Lib3MFException.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Lib3MFWrapper.java b/Autogenerated/Bindings/Java8/lib3mf/Lib3MFWrapper.java index 346c0b820..780c8ef54 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Lib3MFWrapper.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Lib3MFWrapper.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -96,6 +96,16 @@ public enum ObjectType { eSurface } + public static final int BOOLEANOPERATION_UNION = 0; + public static final int BOOLEANOPERATION_DIFFERENCE = 1; + public static final int BOOLEANOPERATION_INTERSECTION = 2; + + public enum BooleanOperation { + eUnion, + eDifference, + eIntersection + } + public static final int TEXTURETYPE_UNKNOWN = 0; public static final int TEXTURETYPE_PNG = 1; public static final int TEXTURETYPE_JPEG = 2; @@ -519,6 +529,24 @@ public static ObjectType convertConstToObjectType (int value) throws Lib3MFExcep } } + public static int convertBooleanOperationToConst (BooleanOperation value) throws Lib3MFException { + switch (value) { + case eUnion: return BOOLEANOPERATION_UNION; + case eDifference: return BOOLEANOPERATION_DIFFERENCE; + case eIntersection: return BOOLEANOPERATION_INTERSECTION; + default: throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "Unknown enum value : " + value); + } + } + + public static BooleanOperation convertConstToBooleanOperation (int value) throws Lib3MFException { + switch (value) { + case BOOLEANOPERATION_UNION: return BooleanOperation.eUnion; + case BOOLEANOPERATION_DIFFERENCE: return BooleanOperation.eDifference; + case BOOLEANOPERATION_INTERSECTION: return BooleanOperation.eIntersection; + default: throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "Unknown enum const : " + value); + } + } + public static int convertTextureTypeToConst (TextureType value) throws Lib3MFException { switch (value) { case eUnknown: return TEXTURETYPE_UNKNOWN; @@ -1112,6 +1140,7 @@ public interface ContentEncryptionCallback extends Callback { protected com.sun.jna.Function lib3mf_objectiterator_getcurrentobject; protected com.sun.jna.Function lib3mf_meshobjectiterator_getcurrentmeshobject; protected com.sun.jna.Function lib3mf_componentsobjectiterator_getcurrentcomponentsobject; + protected com.sun.jna.Function lib3mf_booleanobjectiterator_getcurrentbooleanobject; protected com.sun.jna.Function lib3mf_texture2diterator_getcurrenttexture2d; protected com.sun.jna.Function lib3mf_basematerialgroupiterator_getcurrentbasematerialgroup; protected com.sun.jna.Function lib3mf_colorgroupiterator_getcurrentcolorgroup; @@ -1160,6 +1189,7 @@ public interface ContentEncryptionCallback extends Callback { protected com.sun.jna.Function lib3mf_object_ismeshobject; protected com.sun.jna.Function lib3mf_object_iscomponentsobject; protected com.sun.jna.Function lib3mf_object_islevelsetobject; + protected com.sun.jna.Function lib3mf_object_isbooleanobject; protected com.sun.jna.Function lib3mf_object_isvalid; protected com.sun.jna.Function lib3mf_object_setattachmentasthumbnail; protected com.sun.jna.Function lib3mf_object_getthumbnailattachment; @@ -1217,6 +1247,19 @@ public interface ContentEncryptionCallback extends Callback { protected com.sun.jna.Function lib3mf_levelset_getmesh; protected com.sun.jna.Function lib3mf_levelset_getvolumedata; protected com.sun.jna.Function lib3mf_levelset_setvolumedata; + protected com.sun.jna.Function lib3mf_booleanobject_setbaseobject; + protected com.sun.jna.Function lib3mf_booleanobject_getbaseobject; + protected com.sun.jna.Function lib3mf_booleanobject_setbasetransform; + protected com.sun.jna.Function lib3mf_booleanobject_getbasetransform; + protected com.sun.jna.Function lib3mf_booleanobject_setoperation; + protected com.sun.jna.Function lib3mf_booleanobject_getoperation; + protected com.sun.jna.Function lib3mf_booleanobject_setcsgmodeenabled; + protected com.sun.jna.Function lib3mf_booleanobject_getcsgmodeenabled; + protected com.sun.jna.Function lib3mf_booleanobject_setextractiongridresolution; + protected com.sun.jna.Function lib3mf_booleanobject_getextractiongridresolution; + protected com.sun.jna.Function lib3mf_booleanobject_getoperandcount; + protected com.sun.jna.Function lib3mf_booleanobject_addoperand; + protected com.sun.jna.Function lib3mf_booleanobject_getoperand; protected com.sun.jna.Function lib3mf_beamlattice_getminlength; protected com.sun.jna.Function lib3mf_beamlattice_setminlength; protected com.sun.jna.Function lib3mf_beamlattice_getclipping; @@ -1642,6 +1685,7 @@ public interface ContentEncryptionCallback extends Callback { protected com.sun.jna.Function lib3mf_model_getmultipropertygroupbyid; protected com.sun.jna.Function lib3mf_model_getmeshobjectbyid; protected com.sun.jna.Function lib3mf_model_getcomponentsobjectbyid; + protected com.sun.jna.Function lib3mf_model_getbooleanobjectbyid; protected com.sun.jna.Function lib3mf_model_getcolorgroupbyid; protected com.sun.jna.Function lib3mf_model_getslicestackbyid; protected com.sun.jna.Function lib3mf_model_getlevelsetbyid; @@ -1653,6 +1697,7 @@ public interface ContentEncryptionCallback extends Callback { protected com.sun.jna.Function lib3mf_model_getobjects; protected com.sun.jna.Function lib3mf_model_getmeshobjects; protected com.sun.jna.Function lib3mf_model_getcomponentsobjects; + protected com.sun.jna.Function lib3mf_model_getbooleanobjects; protected com.sun.jna.Function lib3mf_model_gettexture2ds; protected com.sun.jna.Function lib3mf_model_getbasematerialgroups; protected com.sun.jna.Function lib3mf_model_getcolorgroups; @@ -1665,6 +1710,7 @@ public interface ContentEncryptionCallback extends Callback { protected com.sun.jna.Function lib3mf_model_mergefrommodel; protected com.sun.jna.Function lib3mf_model_addmeshobject; protected com.sun.jna.Function lib3mf_model_addcomponentsobject; + protected com.sun.jna.Function lib3mf_model_addbooleanobject; protected com.sun.jna.Function lib3mf_model_addslicestack; protected com.sun.jna.Function lib3mf_model_addtexture2dfromattachment; protected com.sun.jna.Function lib3mf_model_addbasematerialgroup; @@ -1763,6 +1809,7 @@ public Lib3MFWrapper(String libraryPath) { lib3mf_objectiterator_getcurrentobject = mLibrary.getFunction("lib3mf_objectiterator_getcurrentobject"); lib3mf_meshobjectiterator_getcurrentmeshobject = mLibrary.getFunction("lib3mf_meshobjectiterator_getcurrentmeshobject"); lib3mf_componentsobjectiterator_getcurrentcomponentsobject = mLibrary.getFunction("lib3mf_componentsobjectiterator_getcurrentcomponentsobject"); + lib3mf_booleanobjectiterator_getcurrentbooleanobject = mLibrary.getFunction("lib3mf_booleanobjectiterator_getcurrentbooleanobject"); lib3mf_texture2diterator_getcurrenttexture2d = mLibrary.getFunction("lib3mf_texture2diterator_getcurrenttexture2d"); lib3mf_basematerialgroupiterator_getcurrentbasematerialgroup = mLibrary.getFunction("lib3mf_basematerialgroupiterator_getcurrentbasematerialgroup"); lib3mf_colorgroupiterator_getcurrentcolorgroup = mLibrary.getFunction("lib3mf_colorgroupiterator_getcurrentcolorgroup"); @@ -1811,6 +1858,7 @@ public Lib3MFWrapper(String libraryPath) { lib3mf_object_ismeshobject = mLibrary.getFunction("lib3mf_object_ismeshobject"); lib3mf_object_iscomponentsobject = mLibrary.getFunction("lib3mf_object_iscomponentsobject"); lib3mf_object_islevelsetobject = mLibrary.getFunction("lib3mf_object_islevelsetobject"); + lib3mf_object_isbooleanobject = mLibrary.getFunction("lib3mf_object_isbooleanobject"); lib3mf_object_isvalid = mLibrary.getFunction("lib3mf_object_isvalid"); lib3mf_object_setattachmentasthumbnail = mLibrary.getFunction("lib3mf_object_setattachmentasthumbnail"); lib3mf_object_getthumbnailattachment = mLibrary.getFunction("lib3mf_object_getthumbnailattachment"); @@ -1868,6 +1916,19 @@ public Lib3MFWrapper(String libraryPath) { lib3mf_levelset_getmesh = mLibrary.getFunction("lib3mf_levelset_getmesh"); lib3mf_levelset_getvolumedata = mLibrary.getFunction("lib3mf_levelset_getvolumedata"); lib3mf_levelset_setvolumedata = mLibrary.getFunction("lib3mf_levelset_setvolumedata"); + lib3mf_booleanobject_setbaseobject = mLibrary.getFunction("lib3mf_booleanobject_setbaseobject"); + lib3mf_booleanobject_getbaseobject = mLibrary.getFunction("lib3mf_booleanobject_getbaseobject"); + lib3mf_booleanobject_setbasetransform = mLibrary.getFunction("lib3mf_booleanobject_setbasetransform"); + lib3mf_booleanobject_getbasetransform = mLibrary.getFunction("lib3mf_booleanobject_getbasetransform"); + lib3mf_booleanobject_setoperation = mLibrary.getFunction("lib3mf_booleanobject_setoperation"); + lib3mf_booleanobject_getoperation = mLibrary.getFunction("lib3mf_booleanobject_getoperation"); + lib3mf_booleanobject_setcsgmodeenabled = mLibrary.getFunction("lib3mf_booleanobject_setcsgmodeenabled"); + lib3mf_booleanobject_getcsgmodeenabled = mLibrary.getFunction("lib3mf_booleanobject_getcsgmodeenabled"); + lib3mf_booleanobject_setextractiongridresolution = mLibrary.getFunction("lib3mf_booleanobject_setextractiongridresolution"); + lib3mf_booleanobject_getextractiongridresolution = mLibrary.getFunction("lib3mf_booleanobject_getextractiongridresolution"); + lib3mf_booleanobject_getoperandcount = mLibrary.getFunction("lib3mf_booleanobject_getoperandcount"); + lib3mf_booleanobject_addoperand = mLibrary.getFunction("lib3mf_booleanobject_addoperand"); + lib3mf_booleanobject_getoperand = mLibrary.getFunction("lib3mf_booleanobject_getoperand"); lib3mf_beamlattice_getminlength = mLibrary.getFunction("lib3mf_beamlattice_getminlength"); lib3mf_beamlattice_setminlength = mLibrary.getFunction("lib3mf_beamlattice_setminlength"); lib3mf_beamlattice_getclipping = mLibrary.getFunction("lib3mf_beamlattice_getclipping"); @@ -2293,6 +2354,7 @@ public Lib3MFWrapper(String libraryPath) { lib3mf_model_getmultipropertygroupbyid = mLibrary.getFunction("lib3mf_model_getmultipropertygroupbyid"); lib3mf_model_getmeshobjectbyid = mLibrary.getFunction("lib3mf_model_getmeshobjectbyid"); lib3mf_model_getcomponentsobjectbyid = mLibrary.getFunction("lib3mf_model_getcomponentsobjectbyid"); + lib3mf_model_getbooleanobjectbyid = mLibrary.getFunction("lib3mf_model_getbooleanobjectbyid"); lib3mf_model_getcolorgroupbyid = mLibrary.getFunction("lib3mf_model_getcolorgroupbyid"); lib3mf_model_getslicestackbyid = mLibrary.getFunction("lib3mf_model_getslicestackbyid"); lib3mf_model_getlevelsetbyid = mLibrary.getFunction("lib3mf_model_getlevelsetbyid"); @@ -2304,6 +2366,7 @@ public Lib3MFWrapper(String libraryPath) { lib3mf_model_getobjects = mLibrary.getFunction("lib3mf_model_getobjects"); lib3mf_model_getmeshobjects = mLibrary.getFunction("lib3mf_model_getmeshobjects"); lib3mf_model_getcomponentsobjects = mLibrary.getFunction("lib3mf_model_getcomponentsobjects"); + lib3mf_model_getbooleanobjects = mLibrary.getFunction("lib3mf_model_getbooleanobjects"); lib3mf_model_gettexture2ds = mLibrary.getFunction("lib3mf_model_gettexture2ds"); lib3mf_model_getbasematerialgroups = mLibrary.getFunction("lib3mf_model_getbasematerialgroups"); lib3mf_model_getcolorgroups = mLibrary.getFunction("lib3mf_model_getcolorgroups"); @@ -2316,6 +2379,7 @@ public Lib3MFWrapper(String libraryPath) { lib3mf_model_mergefrommodel = mLibrary.getFunction("lib3mf_model_mergefrommodel"); lib3mf_model_addmeshobject = mLibrary.getFunction("lib3mf_model_addmeshobject"); lib3mf_model_addcomponentsobject = mLibrary.getFunction("lib3mf_model_addcomponentsobject"); + lib3mf_model_addbooleanobject = mLibrary.getFunction("lib3mf_model_addbooleanobject"); lib3mf_model_addslicestack = mLibrary.getFunction("lib3mf_model_addslicestack"); lib3mf_model_addtexture2dfromattachment = mLibrary.getFunction("lib3mf_model_addtexture2dfromattachment"); lib3mf_model_addbasematerialgroup = mLibrary.getFunction("lib3mf_model_addbasematerialgroup"); @@ -2413,6 +2477,7 @@ public Lib3MFWrapper(Pointer lookupPointer) throws Lib3MFException { lib3mf_objectiterator_getcurrentobject = loadFunctionByLookup(lookupMethod, "lib3mf_objectiterator_getcurrentobject"); lib3mf_meshobjectiterator_getcurrentmeshobject = loadFunctionByLookup(lookupMethod, "lib3mf_meshobjectiterator_getcurrentmeshobject"); lib3mf_componentsobjectiterator_getcurrentcomponentsobject = loadFunctionByLookup(lookupMethod, "lib3mf_componentsobjectiterator_getcurrentcomponentsobject"); + lib3mf_booleanobjectiterator_getcurrentbooleanobject = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobjectiterator_getcurrentbooleanobject"); lib3mf_texture2diterator_getcurrenttexture2d = loadFunctionByLookup(lookupMethod, "lib3mf_texture2diterator_getcurrenttexture2d"); lib3mf_basematerialgroupiterator_getcurrentbasematerialgroup = loadFunctionByLookup(lookupMethod, "lib3mf_basematerialgroupiterator_getcurrentbasematerialgroup"); lib3mf_colorgroupiterator_getcurrentcolorgroup = loadFunctionByLookup(lookupMethod, "lib3mf_colorgroupiterator_getcurrentcolorgroup"); @@ -2461,6 +2526,7 @@ public Lib3MFWrapper(Pointer lookupPointer) throws Lib3MFException { lib3mf_object_ismeshobject = loadFunctionByLookup(lookupMethod, "lib3mf_object_ismeshobject"); lib3mf_object_iscomponentsobject = loadFunctionByLookup(lookupMethod, "lib3mf_object_iscomponentsobject"); lib3mf_object_islevelsetobject = loadFunctionByLookup(lookupMethod, "lib3mf_object_islevelsetobject"); + lib3mf_object_isbooleanobject = loadFunctionByLookup(lookupMethod, "lib3mf_object_isbooleanobject"); lib3mf_object_isvalid = loadFunctionByLookup(lookupMethod, "lib3mf_object_isvalid"); lib3mf_object_setattachmentasthumbnail = loadFunctionByLookup(lookupMethod, "lib3mf_object_setattachmentasthumbnail"); lib3mf_object_getthumbnailattachment = loadFunctionByLookup(lookupMethod, "lib3mf_object_getthumbnailattachment"); @@ -2518,6 +2584,19 @@ public Lib3MFWrapper(Pointer lookupPointer) throws Lib3MFException { lib3mf_levelset_getmesh = loadFunctionByLookup(lookupMethod, "lib3mf_levelset_getmesh"); lib3mf_levelset_getvolumedata = loadFunctionByLookup(lookupMethod, "lib3mf_levelset_getvolumedata"); lib3mf_levelset_setvolumedata = loadFunctionByLookup(lookupMethod, "lib3mf_levelset_setvolumedata"); + lib3mf_booleanobject_setbaseobject = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_setbaseobject"); + lib3mf_booleanobject_getbaseobject = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getbaseobject"); + lib3mf_booleanobject_setbasetransform = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_setbasetransform"); + lib3mf_booleanobject_getbasetransform = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getbasetransform"); + lib3mf_booleanobject_setoperation = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_setoperation"); + lib3mf_booleanobject_getoperation = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getoperation"); + lib3mf_booleanobject_setcsgmodeenabled = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_setcsgmodeenabled"); + lib3mf_booleanobject_getcsgmodeenabled = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getcsgmodeenabled"); + lib3mf_booleanobject_setextractiongridresolution = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_setextractiongridresolution"); + lib3mf_booleanobject_getextractiongridresolution = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getextractiongridresolution"); + lib3mf_booleanobject_getoperandcount = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getoperandcount"); + lib3mf_booleanobject_addoperand = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_addoperand"); + lib3mf_booleanobject_getoperand = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getoperand"); lib3mf_beamlattice_getminlength = loadFunctionByLookup(lookupMethod, "lib3mf_beamlattice_getminlength"); lib3mf_beamlattice_setminlength = loadFunctionByLookup(lookupMethod, "lib3mf_beamlattice_setminlength"); lib3mf_beamlattice_getclipping = loadFunctionByLookup(lookupMethod, "lib3mf_beamlattice_getclipping"); @@ -2943,6 +3022,7 @@ public Lib3MFWrapper(Pointer lookupPointer) throws Lib3MFException { lib3mf_model_getmultipropertygroupbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getmultipropertygroupbyid"); lib3mf_model_getmeshobjectbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getmeshobjectbyid"); lib3mf_model_getcomponentsobjectbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getcomponentsobjectbyid"); + lib3mf_model_getbooleanobjectbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getbooleanobjectbyid"); lib3mf_model_getcolorgroupbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getcolorgroupbyid"); lib3mf_model_getslicestackbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getslicestackbyid"); lib3mf_model_getlevelsetbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getlevelsetbyid"); @@ -2954,6 +3034,7 @@ public Lib3MFWrapper(Pointer lookupPointer) throws Lib3MFException { lib3mf_model_getobjects = loadFunctionByLookup(lookupMethod, "lib3mf_model_getobjects"); lib3mf_model_getmeshobjects = loadFunctionByLookup(lookupMethod, "lib3mf_model_getmeshobjects"); lib3mf_model_getcomponentsobjects = loadFunctionByLookup(lookupMethod, "lib3mf_model_getcomponentsobjects"); + lib3mf_model_getbooleanobjects = loadFunctionByLookup(lookupMethod, "lib3mf_model_getbooleanobjects"); lib3mf_model_gettexture2ds = loadFunctionByLookup(lookupMethod, "lib3mf_model_gettexture2ds"); lib3mf_model_getbasematerialgroups = loadFunctionByLookup(lookupMethod, "lib3mf_model_getbasematerialgroups"); lib3mf_model_getcolorgroups = loadFunctionByLookup(lookupMethod, "lib3mf_model_getcolorgroups"); @@ -2966,6 +3047,7 @@ public Lib3MFWrapper(Pointer lookupPointer) throws Lib3MFException { lib3mf_model_mergefrommodel = loadFunctionByLookup(lookupMethod, "lib3mf_model_mergefrommodel"); lib3mf_model_addmeshobject = loadFunctionByLookup(lookupMethod, "lib3mf_model_addmeshobject"); lib3mf_model_addcomponentsobject = loadFunctionByLookup(lookupMethod, "lib3mf_model_addcomponentsobject"); + lib3mf_model_addbooleanobject = loadFunctionByLookup(lookupMethod, "lib3mf_model_addbooleanobject"); lib3mf_model_addslicestack = loadFunctionByLookup(lookupMethod, "lib3mf_model_addslicestack"); lib3mf_model_addtexture2dfromattachment = loadFunctionByLookup(lookupMethod, "lib3mf_model_addtexture2dfromattachment"); lib3mf_model_addbasematerialgroup = loadFunctionByLookup(lookupMethod, "lib3mf_model_addbasematerialgroup"); @@ -3831,6 +3913,11 @@ public T PolymorphicFactory(Pointer handle, Class cls) { case 0xBAF1D8B7: obj = (T)(new Base(this, handle)); break; // First 64 bits of SHA1 of a string: "Lib3MF::Base" } break; + case 0x85FA0E88: + switch(lsbId) { + case 0x06B6C357: obj = (T)(new BooleanObject(this, handle)); break; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObject" + } + break; case 0x87740AD5: switch(lsbId) { case 0x3454E0DF: obj = (T)(new Log10Node(this, handle)); break; // First 64 bits of SHA1 of a string: "Lib3MF::Log10Node" @@ -3906,6 +3993,11 @@ public T PolymorphicFactory(Pointer handle, Class cls) { case 0xF70FB6F9: obj = (T)(new CompositeMaterialsIterator(this, handle)); break; // First 64 bits of SHA1 of a string: "Lib3MF::CompositeMaterialsIterator" } break; + case 0xAFF01F51: + switch(lsbId) { + case 0x2E1FF6AE: obj = (T)(new BooleanObjectIterator(this, handle)); break; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObjectIterator" + } + break; case 0xB19B9FDA: switch(lsbId) { case 0x94B0A5E7: obj = (T)(new OneInputNode(this, handle)); break; // First 64 bits of SHA1 of a string: "Lib3MF::OneInputNode" diff --git a/Autogenerated/Bindings/Java8/lib3mf/Log10Node.java b/Autogenerated/Bindings/Java8/lib3mf/Log10Node.java index 77a62cb9b..eacfc4723 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Log10Node.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Log10Node.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Log2Node.java b/Autogenerated/Bindings/Java8/lib3mf/Log2Node.java index 453a51517..b2ca38596 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Log2Node.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Log2Node.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/LogNode.java b/Autogenerated/Bindings/Java8/lib3mf/LogNode.java index 1daacaf68..d6fdecdb0 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/LogNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/LogNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MatVecMultiplicationNode.java b/Autogenerated/Bindings/Java8/lib3mf/MatVecMultiplicationNode.java index 7b90b2f4f..b303d30b9 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MatVecMultiplicationNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MatVecMultiplicationNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MaterialMapping.java b/Autogenerated/Bindings/Java8/lib3mf/MaterialMapping.java index 61b63d5a2..9f37389b3 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MaterialMapping.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MaterialMapping.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Matrix4x4.java b/Autogenerated/Bindings/Java8/lib3mf/Matrix4x4.java index f2edb51bb..6d6619b25 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Matrix4x4.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Matrix4x4.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MatrixFromColumnsNode.java b/Autogenerated/Bindings/Java8/lib3mf/MatrixFromColumnsNode.java index 58f946632..5edd99328 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MatrixFromColumnsNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MatrixFromColumnsNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MatrixFromRowsNode.java b/Autogenerated/Bindings/Java8/lib3mf/MatrixFromRowsNode.java index ad36b26f5..9b344b600 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MatrixFromRowsNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MatrixFromRowsNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MaxNode.java b/Autogenerated/Bindings/Java8/lib3mf/MaxNode.java index 738668d2d..04ecb2290 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MaxNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MaxNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MeshNode.java b/Autogenerated/Bindings/Java8/lib3mf/MeshNode.java index c11c76b6e..7d2ac8801 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MeshNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MeshNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MeshObject.java b/Autogenerated/Bindings/Java8/lib3mf/MeshObject.java index 8f26f939b..bb8913f5a 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MeshObject.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MeshObject.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MeshObjectIterator.java b/Autogenerated/Bindings/Java8/lib3mf/MeshObjectIterator.java index 16d8ab997..df13b641c 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MeshObjectIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MeshObjectIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MetaData.java b/Autogenerated/Bindings/Java8/lib3mf/MetaData.java index d2b22481b..a47864bb9 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MetaData.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MetaData.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MetaDataGroup.java b/Autogenerated/Bindings/Java8/lib3mf/MetaDataGroup.java index 14b82be67..607809d7e 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MetaDataGroup.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MetaDataGroup.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MinNode.java b/Autogenerated/Bindings/Java8/lib3mf/MinNode.java index 214edb242..1d19cb10e 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MinNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MinNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ModNode.java b/Autogenerated/Bindings/Java8/lib3mf/ModNode.java index c2807274e..c949f08da 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ModNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ModNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Model.java b/Autogenerated/Bindings/Java8/lib3mf/Model.java index 44c413fc2..7a12e82c9 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Model.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Model.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -354,6 +354,25 @@ public ComponentsObject getComponentsObjectByID(int uniqueResourceID) throws Lib return componentsObjectInstance; } + /** + * finds a boolean object by its UniqueResourceID + * + * @param uniqueResourceID UniqueResourceID + * @return returns the boolean object instance + * @throws Lib3MFException + */ + public BooleanObject getBooleanObjectByID(int uniqueResourceID) throws Lib3MFException { + Pointer bufferBooleanObjectInstance = new Memory(8); + mWrapper.checkError(this, mWrapper.lib3mf_model_getbooleanobjectbyid.invokeInt(new java.lang.Object[]{mHandle, uniqueResourceID, bufferBooleanObjectInstance})); + Pointer valueBooleanObjectInstance = bufferBooleanObjectInstance.getPointer(0); + BooleanObject booleanObjectInstance = null; + if (valueBooleanObjectInstance == Pointer.NULL) { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "BooleanObjectInstance was a null pointer"); + } + booleanObjectInstance = mWrapper.PolymorphicFactory(valueBooleanObjectInstance, BooleanObject.class); + return booleanObjectInstance; + } + /** * finds a model color group by its UniqueResourceID * @@ -560,6 +579,24 @@ public ComponentsObjectIterator getComponentsObjects() throws Lib3MFException { return resourceIterator; } + /** + * creates a resource iterator instance with all boolean object resources. + * + * @return returns the iterator instance. + * @throws Lib3MFException + */ + public BooleanObjectIterator getBooleanObjects() throws Lib3MFException { + Pointer bufferResourceIterator = new Memory(8); + mWrapper.checkError(this, mWrapper.lib3mf_model_getbooleanobjects.invokeInt(new java.lang.Object[]{mHandle, bufferResourceIterator})); + Pointer valueResourceIterator = bufferResourceIterator.getPointer(0); + BooleanObjectIterator resourceIterator = null; + if (valueResourceIterator == Pointer.NULL) { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "ResourceIterator was a null pointer"); + } + resourceIterator = mWrapper.PolymorphicFactory(valueResourceIterator, BooleanObjectIterator.class); + return resourceIterator; + } + /** * creates a Texture2DIterator instance with all texture2d resources. * @@ -774,6 +811,24 @@ public ComponentsObject addComponentsObject() throws Lib3MFException { return componentsObjectInstance; } + /** + * adds an empty boolean object to the model. + * + * @return returns the boolean object instance + * @throws Lib3MFException + */ + public BooleanObject addBooleanObject() throws Lib3MFException { + Pointer bufferBooleanObjectInstance = new Memory(8); + mWrapper.checkError(this, mWrapper.lib3mf_model_addbooleanobject.invokeInt(new java.lang.Object[]{mHandle, bufferBooleanObjectInstance})); + Pointer valueBooleanObjectInstance = bufferBooleanObjectInstance.getPointer(0); + BooleanObject booleanObjectInstance = null; + if (valueBooleanObjectInstance == Pointer.NULL) { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "BooleanObjectInstance was a null pointer"); + } + booleanObjectInstance = mWrapper.PolymorphicFactory(valueBooleanObjectInstance, BooleanObject.class); + return booleanObjectInstance; + } + /** * creates a new model slicestack by its id * diff --git a/Autogenerated/Bindings/Java8/lib3mf/MultiPropertyGroup.java b/Autogenerated/Bindings/Java8/lib3mf/MultiPropertyGroup.java index 3a7457478..ceba02099 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MultiPropertyGroup.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MultiPropertyGroup.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MultiPropertyGroupIterator.java b/Autogenerated/Bindings/Java8/lib3mf/MultiPropertyGroupIterator.java index 54014b3fd..732fad0c1 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MultiPropertyGroupIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MultiPropertyGroupIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MultiPropertyLayer.java b/Autogenerated/Bindings/Java8/lib3mf/MultiPropertyLayer.java index cc349b19a..fd6e38a45 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MultiPropertyLayer.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MultiPropertyLayer.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/MultiplicationNode.java b/Autogenerated/Bindings/Java8/lib3mf/MultiplicationNode.java index d289041cc..2510a8c59 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/MultiplicationNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/MultiplicationNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/NodeIterator.java b/Autogenerated/Bindings/Java8/lib3mf/NodeIterator.java index 989a4b153..9ac51b5fe 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/NodeIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/NodeIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/NormalizeDistanceNode.java b/Autogenerated/Bindings/Java8/lib3mf/NormalizeDistanceNode.java index 525b85458..336daf875 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/NormalizeDistanceNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/NormalizeDistanceNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Object.java b/Autogenerated/Bindings/Java8/lib3mf/Object.java index 0d365df4e..ca875fe42 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Object.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Object.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -167,6 +167,18 @@ public boolean isLevelSetObject() throws Lib3MFException { return bufferIsLevelSetObject.getByte(0) != 0; } + /** + * Retrieves, if an object is a boolean object + * + * @return returns, whether the object is a boolean object + * @throws Lib3MFException + */ + public boolean isBooleanObject() throws Lib3MFException { + Pointer bufferIsBooleanObject = new Memory(1); + mWrapper.checkError(this, mWrapper.lib3mf_object_isbooleanobject.invokeInt(new java.lang.Object[]{mHandle, bufferIsBooleanObject})); + return bufferIsBooleanObject.getByte(0) != 0; + } + /** * Retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. * diff --git a/Autogenerated/Bindings/Java8/lib3mf/ObjectIterator.java b/Autogenerated/Bindings/Java8/lib3mf/ObjectIterator.java index d70f6c75d..79ee9159d 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ObjectIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ObjectIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/OneInputNode.java b/Autogenerated/Bindings/Java8/lib3mf/OneInputNode.java index e8c564862..e6697f333 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/OneInputNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/OneInputNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/PackagePart.java b/Autogenerated/Bindings/Java8/lib3mf/PackagePart.java index 68f15d610..b8fa5c2f5 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/PackagePart.java +++ b/Autogenerated/Bindings/Java8/lib3mf/PackagePart.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Position.java b/Autogenerated/Bindings/Java8/lib3mf/Position.java index 8ca7b7f64..ebf1c4043 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Position.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Position.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Position2D.java b/Autogenerated/Bindings/Java8/lib3mf/Position2D.java index 65c9cab65..58d86aa67 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Position2D.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Position2D.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/PowNode.java b/Autogenerated/Bindings/Java8/lib3mf/PowNode.java index 1f6e0736d..6a1165999 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/PowNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/PowNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Reader.java b/Autogenerated/Bindings/Java8/lib3mf/Reader.java index e326b9b9c..54bd22870 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Reader.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Reader.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Resource.java b/Autogenerated/Bindings/Java8/lib3mf/Resource.java index 20629c800..1bcffe183 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Resource.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Resource.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ResourceData.java b/Autogenerated/Bindings/Java8/lib3mf/ResourceData.java index c69fb353d..ab80b4c51 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ResourceData.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ResourceData.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ResourceDataGroup.java b/Autogenerated/Bindings/Java8/lib3mf/ResourceDataGroup.java index 9ee79549d..2b70707d4 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ResourceDataGroup.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ResourceDataGroup.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ResourceIdNode.java b/Autogenerated/Bindings/Java8/lib3mf/ResourceIdNode.java index c7ba5bc39..810447b69 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ResourceIdNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ResourceIdNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/ResourceIterator.java b/Autogenerated/Bindings/Java8/lib3mf/ResourceIterator.java index 6b8f8fe99..5330c6ed7 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/ResourceIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/ResourceIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/RoundNode.java b/Autogenerated/Bindings/Java8/lib3mf/RoundNode.java index 2bede99ff..c0ec1ffad 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/RoundNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/RoundNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/SelectNode.java b/Autogenerated/Bindings/Java8/lib3mf/SelectNode.java index 9423a3da4..1917b9620 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/SelectNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/SelectNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/SignNode.java b/Autogenerated/Bindings/Java8/lib3mf/SignNode.java index 07c6d7a25..58c0d3e8f 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/SignNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/SignNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/SinNode.java b/Autogenerated/Bindings/Java8/lib3mf/SinNode.java index d195e1804..0c165eeb7 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/SinNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/SinNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/SinhNode.java b/Autogenerated/Bindings/Java8/lib3mf/SinhNode.java index 602b71076..3acbcc163 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/SinhNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/SinhNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Slice.java b/Autogenerated/Bindings/Java8/lib3mf/Slice.java index 61b5e0c10..f82b09c53 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Slice.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Slice.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/SliceStack.java b/Autogenerated/Bindings/Java8/lib3mf/SliceStack.java index 9d409fec3..ee8728bd8 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/SliceStack.java +++ b/Autogenerated/Bindings/Java8/lib3mf/SliceStack.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/SliceStackIterator.java b/Autogenerated/Bindings/Java8/lib3mf/SliceStackIterator.java index f2334a347..14303f6bc 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/SliceStackIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/SliceStackIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/SqrtNode.java b/Autogenerated/Bindings/Java8/lib3mf/SqrtNode.java index 595fa4799..5db4eb965 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/SqrtNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/SqrtNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/SubtractionNode.java b/Autogenerated/Bindings/Java8/lib3mf/SubtractionNode.java index 13fea4988..b8dfd1b4f 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/SubtractionNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/SubtractionNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/TanNode.java b/Autogenerated/Bindings/Java8/lib3mf/TanNode.java index bb282765d..3886d14b8 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/TanNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/TanNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/TanhNode.java b/Autogenerated/Bindings/Java8/lib3mf/TanhNode.java index 11079eb84..ca04c88d2 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/TanhNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/TanhNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Tex2Coord.java b/Autogenerated/Bindings/Java8/lib3mf/Tex2Coord.java index 183717a5d..6fafa91a2 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Tex2Coord.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Tex2Coord.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Texture2D.java b/Autogenerated/Bindings/Java8/lib3mf/Texture2D.java index 726f30df4..b008d23b1 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Texture2D.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Texture2D.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Texture2DGroup.java b/Autogenerated/Bindings/Java8/lib3mf/Texture2DGroup.java index d5775e26c..2efff6a95 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Texture2DGroup.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Texture2DGroup.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Texture2DGroupIterator.java b/Autogenerated/Bindings/Java8/lib3mf/Texture2DGroupIterator.java index f7e5cd330..5170b5232 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Texture2DGroupIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Texture2DGroupIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Texture2DIterator.java b/Autogenerated/Bindings/Java8/lib3mf/Texture2DIterator.java index 2b8ed0e27..8511ca4f8 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Texture2DIterator.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Texture2DIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Transform.java b/Autogenerated/Bindings/Java8/lib3mf/Transform.java index ed02ea877..795496b75 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Transform.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Transform.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/TransposeNode.java b/Autogenerated/Bindings/Java8/lib3mf/TransposeNode.java index 0ab7a82d9..ec3c53a7b 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/TransposeNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/TransposeNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Triangle.java b/Autogenerated/Bindings/Java8/lib3mf/Triangle.java index c561cd299..007400619 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Triangle.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Triangle.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/TriangleProperties.java b/Autogenerated/Bindings/Java8/lib3mf/TriangleProperties.java index 7328dc17f..0e44da4e8 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/TriangleProperties.java +++ b/Autogenerated/Bindings/Java8/lib3mf/TriangleProperties.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/TriangleSet.java b/Autogenerated/Bindings/Java8/lib3mf/TriangleSet.java index 5b682aa0e..0363a9803 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/TriangleSet.java +++ b/Autogenerated/Bindings/Java8/lib3mf/TriangleSet.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/TwoInputNode.java b/Autogenerated/Bindings/Java8/lib3mf/TwoInputNode.java index 50bd8349a..7ad69146e 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/TwoInputNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/TwoInputNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/UnsignedMeshNode.java b/Autogenerated/Bindings/Java8/lib3mf/UnsignedMeshNode.java index d792a6ad1..844811c48 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/UnsignedMeshNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/UnsignedMeshNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Vector.java b/Autogenerated/Bindings/Java8/lib3mf/Vector.java index ab8e4f415..4a47a9847 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Vector.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Vector.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/VectorFromScalarNode.java b/Autogenerated/Bindings/Java8/lib3mf/VectorFromScalarNode.java index 6c84b6e68..43793619e 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/VectorFromScalarNode.java +++ b/Autogenerated/Bindings/Java8/lib3mf/VectorFromScalarNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/VolumeData.java b/Autogenerated/Bindings/Java8/lib3mf/VolumeData.java index 39865a786..9eeaedf3f 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/VolumeData.java +++ b/Autogenerated/Bindings/Java8/lib3mf/VolumeData.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/VolumeDataColor.java b/Autogenerated/Bindings/Java8/lib3mf/VolumeDataColor.java index fba325e90..c361c342d 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/VolumeDataColor.java +++ b/Autogenerated/Bindings/Java8/lib3mf/VolumeDataColor.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/VolumeDataComposite.java b/Autogenerated/Bindings/Java8/lib3mf/VolumeDataComposite.java index 3acbe3489..1a60c8aac 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/VolumeDataComposite.java +++ b/Autogenerated/Bindings/Java8/lib3mf/VolumeDataComposite.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/VolumeDataProperty.java b/Autogenerated/Bindings/Java8/lib3mf/VolumeDataProperty.java index 1e58a5578..ae66d4746 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/VolumeDataProperty.java +++ b/Autogenerated/Bindings/Java8/lib3mf/VolumeDataProperty.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java8/lib3mf/Writer.java b/Autogenerated/Bindings/Java8/lib3mf/Writer.java index 15d75b68b..bb4920518 100644 --- a/Autogenerated/Bindings/Java8/lib3mf/Writer.java +++ b/Autogenerated/Bindings/Java8/lib3mf/Writer.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/build_jar.sh b/Autogenerated/Bindings/Java9/build_jar.sh index 02819608f..8bd9d4094 100644 --- a/Autogenerated/Bindings/Java9/build_jar.sh +++ b/Autogenerated/Bindings/Java9/build_jar.sh @@ -9,4 +9,4 @@ echo "Compile Java Bindings" javac -classpath *.jar lib3mf/* echo "Create JAR" -jar cvf lib3mf-2.5.0.jar lib3mf +jar cvf lib3mf-2.6.0.jar lib3mf diff --git a/Autogenerated/Bindings/Java9/lib3mf/AbsNode.java b/Autogenerated/Bindings/Java9/lib3mf/AbsNode.java index 957af31fd..5869da07c 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/AbsNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/AbsNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/AccessRight.java b/Autogenerated/Bindings/Java9/lib3mf/AccessRight.java index e2ede36da..1053f5d45 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/AccessRight.java +++ b/Autogenerated/Bindings/Java9/lib3mf/AccessRight.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/AdditionNode.java b/Autogenerated/Bindings/Java9/lib3mf/AdditionNode.java index 8598e6a39..1a4f9d37a 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/AdditionNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/AdditionNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ArcCosNode.java b/Autogenerated/Bindings/Java9/lib3mf/ArcCosNode.java index e5155737f..50f4deca3 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ArcCosNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ArcCosNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ArcSinNode.java b/Autogenerated/Bindings/Java9/lib3mf/ArcSinNode.java index 8ac1ebe2a..f80a2982a 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ArcSinNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ArcSinNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ArcTan2Node.java b/Autogenerated/Bindings/Java9/lib3mf/ArcTan2Node.java index 3ef4aa2f3..f23e8fd5b 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ArcTan2Node.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ArcTan2Node.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ArcTanNode.java b/Autogenerated/Bindings/Java9/lib3mf/ArcTanNode.java index e7fcec449..dda3e3ab6 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ArcTanNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ArcTanNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Attachment.java b/Autogenerated/Bindings/Java9/lib3mf/Attachment.java index 6054d35d6..30995efdb 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Attachment.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Attachment.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Ball.java b/Autogenerated/Bindings/Java9/lib3mf/Ball.java index c01b96185..3862d162f 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Ball.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Ball.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Base.java b/Autogenerated/Bindings/Java9/lib3mf/Base.java index 4c4e5b2e2..6522a1367 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Base.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Base.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/BaseMaterialGroup.java b/Autogenerated/Bindings/Java9/lib3mf/BaseMaterialGroup.java index d90668096..83132fa0f 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/BaseMaterialGroup.java +++ b/Autogenerated/Bindings/Java9/lib3mf/BaseMaterialGroup.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/BaseMaterialGroupIterator.java b/Autogenerated/Bindings/Java9/lib3mf/BaseMaterialGroupIterator.java index 71fd65b9e..08bf85e52 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/BaseMaterialGroupIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/BaseMaterialGroupIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Beam.java b/Autogenerated/Bindings/Java9/lib3mf/Beam.java index 40cbe6400..21817705d 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Beam.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Beam.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/BeamLattice.java b/Autogenerated/Bindings/Java9/lib3mf/BeamLattice.java index bd3313a3f..cd6fc3624 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/BeamLattice.java +++ b/Autogenerated/Bindings/Java9/lib3mf/BeamLattice.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/BeamLatticeNode.java b/Autogenerated/Bindings/Java9/lib3mf/BeamLatticeNode.java index 80904d2e2..932000ef2 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/BeamLatticeNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/BeamLatticeNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/BeamSet.java b/Autogenerated/Bindings/Java9/lib3mf/BeamSet.java index db06c5e0a..b7214786b 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/BeamSet.java +++ b/Autogenerated/Bindings/Java9/lib3mf/BeamSet.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/BooleanObject.java b/Autogenerated/Bindings/Java9/lib3mf/BooleanObject.java new file mode 100644 index 000000000..386fc4073 --- /dev/null +++ b/Autogenerated/Bindings/Java9/lib3mf/BooleanObject.java @@ -0,0 +1,254 @@ +/*++ + +Copyright (C) 2024 3MF Consortium (Original Author) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated Java file in order to allow an easy + use of the 3MF Library + +Interface version: 2.6.0 + +*/ + +package lib3mf; + +import com.sun.jna.Library; +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import java.lang.ref.Cleaner; + + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; + +public class BooleanObject extends Object { + + public BooleanObject(Lib3MFWrapper wrapper, Pointer handle) { + super(wrapper, handle); + } + + /** + * Sets the base object and transform for the boolean shape. + * + * @param baseObject base object of the boolean shape + * @param transform transform applied to the base object + * @throws Lib3MFException + */ + public void setBaseObject(Object baseObject, Transform transform) throws Lib3MFException { + Pointer baseObjectHandle = null; + if (baseObject != null) { + baseObjectHandle = baseObject.getHandle(); + } else { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "BaseObject is a null value."); + } + Pointer bufferTransform = new Memory(Transform.SIZE); + transform.writeToPointer(bufferTransform, 0); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_setbaseobject.invokeInt(new java.lang.Object[]{mHandle, baseObjectHandle, bufferTransform})); + } + + /** + * Returns the base object of the boolean shape. + * + * @return base object of the boolean shape + * @throws Lib3MFException + */ + public Object getBaseObject() throws Lib3MFException { + Pointer bufferBaseObject = new Memory(8); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getbaseobject.invokeInt(new java.lang.Object[]{mHandle, bufferBaseObject})); + Pointer valueBaseObject = bufferBaseObject.getPointer(0); + Object baseObject = null; + if (valueBaseObject == Pointer.NULL) { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "BaseObject was a null pointer"); + } + baseObject = mWrapper.PolymorphicFactory(valueBaseObject, Object.class); + return baseObject; + } + + /** + * Sets the base transform of the boolean shape. + * + * @param transform transform applied to the base object + * @throws Lib3MFException + */ + public void setBaseTransform(Transform transform) throws Lib3MFException { + Pointer bufferTransform = new Memory(Transform.SIZE); + transform.writeToPointer(bufferTransform, 0); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_setbasetransform.invokeInt(new java.lang.Object[]{mHandle, bufferTransform})); + } + + /** + * Returns the base transform of the boolean shape. + * + * @return transform applied to the base object + * @throws Lib3MFException + */ + public Transform getBaseTransform() throws Lib3MFException { + Pointer bufferTransform = new Memory(Transform.SIZE); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getbasetransform.invokeInt(new java.lang.Object[]{mHandle, bufferTransform})); + Transform transform = new Transform(); + transform.readFromPointer(bufferTransform, 0); + return transform; + } + + /** + * Sets the boolean operation used for the boolean shape. + * + * @param operation boolean operation used for the shape + * @throws Lib3MFException + */ + public void setOperation(Lib3MFWrapper.BooleanOperation operation) throws Lib3MFException { + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_setoperation.invokeInt(new java.lang.Object[]{mHandle, Lib3MFWrapper.EnumConversion.convertBooleanOperationToConst(operation)})); + } + + /** + * Returns the boolean operation used for the boolean shape. + * + * @return boolean operation used for the shape + * @throws Lib3MFException + */ + public Lib3MFWrapper.BooleanOperation getOperation() throws Lib3MFException { + Pointer bufferOperation = new Memory(4); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getoperation.invokeInt(new java.lang.Object[]{mHandle, bufferOperation})); + return Lib3MFWrapper.EnumConversion.convertConstToBooleanOperation(bufferOperation.getInt(0)); + } + + /** + * Enables or disables CSG field evaluation for boolean-to-mesh materialization. + * + * @param cSGModeEnabled if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback + * @throws Lib3MFException + */ + public void setCSGModeEnabled(boolean cSGModeEnabled) throws Lib3MFException { + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_setcsgmodeenabled.invokeInt(new java.lang.Object[]{mHandle, cSGModeEnabled})); + } + + /** + * Returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. + * + * @return if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback + * @throws Lib3MFException + */ + public boolean getCSGModeEnabled() throws Lib3MFException { + Pointer bufferCSGModeEnabled = new Memory(1); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getcsgmodeenabled.invokeInt(new java.lang.Object[]{mHandle, bufferCSGModeEnabled})); + return bufferCSGModeEnabled.getByte(0) != 0; + } + + /** + * Sets the extraction grid resolution used for boolean-to-mesh materialization. + * + * @param gridResolution extraction grid resolution for boolean surface extraction + * @throws Lib3MFException + */ + public void setExtractionGridResolution(int gridResolution) throws Lib3MFException { + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_setextractiongridresolution.invokeInt(new java.lang.Object[]{mHandle, gridResolution})); + } + + /** + * Returns the extraction grid resolution used for boolean-to-mesh materialization. + * + * @return extraction grid resolution for boolean surface extraction + * @throws Lib3MFException + */ + public int getExtractionGridResolution() throws Lib3MFException { + Pointer bufferGridResolution = new Memory(4); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getextractiongridresolution.invokeInt(new java.lang.Object[]{mHandle, bufferGridResolution})); + return bufferGridResolution.getInt(0); + } + + /** + * Returns the number of operands in the boolean sequence. + * + * @return number of operands in the boolean sequence + * @throws Lib3MFException + */ + public int getOperandCount() throws Lib3MFException { + Pointer bufferCount = new Memory(4); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getoperandcount.invokeInt(new java.lang.Object[]{mHandle, bufferCount})); + return bufferCount.getInt(0); + } + + /** + * Adds an operand object to the boolean sequence. + * + * @param operandObject mesh object used as operand + * @param transform transform applied to the operand object + * @throws Lib3MFException + */ + public void addOperand(MeshObject operandObject, Transform transform) throws Lib3MFException { + Pointer operandObjectHandle = null; + if (operandObject != null) { + operandObjectHandle = operandObject.getHandle(); + } else { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "OperandObject is a null value."); + } + Pointer bufferTransform = new Memory(Transform.SIZE); + transform.writeToPointer(bufferTransform, 0); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_addoperand.invokeInt(new java.lang.Object[]{mHandle, operandObjectHandle, bufferTransform})); + } + + /** + * Returns one operand object and transform from the boolean sequence. + * + * @param index index of the operand in the boolean sequence + * @return GetOperand Result Tuple + * @throws Lib3MFException + */ + public GetOperandResult getOperand(int index) throws Lib3MFException { + Pointer bufferOperandObject = new Memory(8); + Pointer bufferTransform = new Memory(Transform.SIZE); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobject_getoperand.invokeInt(new java.lang.Object[]{mHandle, index, bufferOperandObject, bufferTransform})); + Pointer valueOperandObject = bufferOperandObject.getPointer(0); + MeshObject operandObject = null; + if (valueOperandObject == Pointer.NULL) { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "OperandObject was a null pointer"); + } + operandObject = mWrapper.PolymorphicFactory(valueOperandObject, MeshObject.class); + Transform transform = new Transform(); + transform.readFromPointer(bufferTransform, 0); + GetOperandResult returnTuple = new GetOperandResult(); + returnTuple.OperandObject = operandObject; + returnTuple.Transform = transform; + return returnTuple; + } + + public static class GetOperandResult { + /** + * mesh object used as operand + */ + public MeshObject OperandObject; + + /** + * transform applied to the operand object + */ + public Transform Transform; + + } + +} + diff --git a/Autogenerated/Bindings/Java9/lib3mf/BooleanObjectIterator.java b/Autogenerated/Bindings/Java9/lib3mf/BooleanObjectIterator.java new file mode 100644 index 000000000..ce880a8c2 --- /dev/null +++ b/Autogenerated/Bindings/Java9/lib3mf/BooleanObjectIterator.java @@ -0,0 +1,75 @@ +/*++ + +Copyright (C) 2024 3MF Consortium (Original Author) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.1-develop. + +Abstract: This is an autogenerated Java file in order to allow an easy + use of the 3MF Library + +Interface version: 2.6.0 + +*/ + +package lib3mf; + +import com.sun.jna.Library; +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import java.lang.ref.Cleaner; + + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; + +public class BooleanObjectIterator extends ResourceIterator { + + public BooleanObjectIterator(Lib3MFWrapper wrapper, Pointer handle) { + super(wrapper, handle); + } + + /** + * Returns the BooleanObject the iterator points at. + * + * @return returns the BooleanObject instance. + * @throws Lib3MFException + */ + public BooleanObject getCurrentBooleanObject() throws Lib3MFException { + Pointer bufferResource = new Memory(8); + mWrapper.checkError(this, mWrapper.lib3mf_booleanobjectiterator_getcurrentbooleanobject.invokeInt(new java.lang.Object[]{mHandle, bufferResource})); + Pointer valueResource = bufferResource.getPointer(0); + BooleanObject resource = null; + if (valueResource == Pointer.NULL) { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "Resource was a null pointer"); + } + resource = mWrapper.PolymorphicFactory(valueResource, BooleanObject.class); + return resource; + } + + +} + diff --git a/Autogenerated/Bindings/Java9/lib3mf/Box.java b/Autogenerated/Bindings/Java9/lib3mf/Box.java index 70a3484e0..e07728552 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Box.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Box.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/BuildItem.java b/Autogenerated/Bindings/Java9/lib3mf/BuildItem.java index 40afd4c09..6f0394b6a 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/BuildItem.java +++ b/Autogenerated/Bindings/Java9/lib3mf/BuildItem.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/BuildItemIterator.java b/Autogenerated/Bindings/Java9/lib3mf/BuildItemIterator.java index 2388b4369..250abd1b4 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/BuildItemIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/BuildItemIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/CeilNode.java b/Autogenerated/Bindings/Java9/lib3mf/CeilNode.java index d2061211f..fdc91a3e3 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/CeilNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/CeilNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ClampNode.java b/Autogenerated/Bindings/Java9/lib3mf/ClampNode.java index 9e800eafd..9798c1f40 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ClampNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ClampNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Color.java b/Autogenerated/Bindings/Java9/lib3mf/Color.java index 66de0245e..2530c2382 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Color.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Color.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ColorGroup.java b/Autogenerated/Bindings/Java9/lib3mf/ColorGroup.java index e15e294da..118f595d6 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ColorGroup.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ColorGroup.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ColorGroupIterator.java b/Autogenerated/Bindings/Java9/lib3mf/ColorGroupIterator.java index 80ca2cc6d..16d0da86e 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ColorGroupIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ColorGroupIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Component.java b/Autogenerated/Bindings/Java9/lib3mf/Component.java index e1a7a62ff..4d44b5ebb 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Component.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Component.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ComponentsObject.java b/Autogenerated/Bindings/Java9/lib3mf/ComponentsObject.java index e85be17de..bdec60e2b 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ComponentsObject.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ComponentsObject.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ComponentsObjectIterator.java b/Autogenerated/Bindings/Java9/lib3mf/ComponentsObjectIterator.java index 78261987c..200ae2c72 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ComponentsObjectIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ComponentsObjectIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ComposeMatrixNode.java b/Autogenerated/Bindings/Java9/lib3mf/ComposeMatrixNode.java index e1c5abe50..d5a2a2b58 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ComposeMatrixNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ComposeMatrixNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ComposeVectorNode.java b/Autogenerated/Bindings/Java9/lib3mf/ComposeVectorNode.java index dd80c11a2..f16462afc 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ComposeVectorNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ComposeVectorNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/CompositeConstituent.java b/Autogenerated/Bindings/Java9/lib3mf/CompositeConstituent.java index 8a799bd84..a472fb4f1 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/CompositeConstituent.java +++ b/Autogenerated/Bindings/Java9/lib3mf/CompositeConstituent.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/CompositeMaterials.java b/Autogenerated/Bindings/Java9/lib3mf/CompositeMaterials.java index c9110a1f9..7b7a6f46b 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/CompositeMaterials.java +++ b/Autogenerated/Bindings/Java9/lib3mf/CompositeMaterials.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/CompositeMaterialsIterator.java b/Autogenerated/Bindings/Java9/lib3mf/CompositeMaterialsIterator.java index 6e2bb33f6..76b6f7458 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/CompositeMaterialsIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/CompositeMaterialsIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ConstMatNode.java b/Autogenerated/Bindings/Java9/lib3mf/ConstMatNode.java index 806365a98..0a520979c 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ConstMatNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ConstMatNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ConstVecNode.java b/Autogenerated/Bindings/Java9/lib3mf/ConstVecNode.java index b360d1631..b7675634c 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ConstVecNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ConstVecNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ConstantNode.java b/Autogenerated/Bindings/Java9/lib3mf/ConstantNode.java index 15fa87dac..e273163ec 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ConstantNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ConstantNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Consumer.java b/Autogenerated/Bindings/Java9/lib3mf/Consumer.java index 0dfb0252c..aa5a00231 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Consumer.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Consumer.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ContentEncryptionParams.java b/Autogenerated/Bindings/Java9/lib3mf/ContentEncryptionParams.java index e3297535d..546689c49 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ContentEncryptionParams.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ContentEncryptionParams.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/CosNode.java b/Autogenerated/Bindings/Java9/lib3mf/CosNode.java index ae8d4871f..f9aa1a119 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/CosNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/CosNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/CoshNode.java b/Autogenerated/Bindings/Java9/lib3mf/CoshNode.java index fbc37e690..4b552510e 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/CoshNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/CoshNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/CrossNode.java b/Autogenerated/Bindings/Java9/lib3mf/CrossNode.java index c5fc4dbbb..5b1457c5d 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/CrossNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/CrossNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/DecomposeVectorNode.java b/Autogenerated/Bindings/Java9/lib3mf/DecomposeVectorNode.java index 9ee6ab9f9..0ed8ea010 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/DecomposeVectorNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/DecomposeVectorNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/DivisionNode.java b/Autogenerated/Bindings/Java9/lib3mf/DivisionNode.java index d2b444822..1b44f99fc 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/DivisionNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/DivisionNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/DotNode.java b/Autogenerated/Bindings/Java9/lib3mf/DotNode.java index 1de2776a9..53a5fbb59 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/DotNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/DotNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ExpNode.java b/Autogenerated/Bindings/Java9/lib3mf/ExpNode.java index 15c7b06ff..91a2179ad 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ExpNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ExpNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/FloorNode.java b/Autogenerated/Bindings/Java9/lib3mf/FloorNode.java index 630e11abe..585490b2c 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/FloorNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/FloorNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/FmodNode.java b/Autogenerated/Bindings/Java9/lib3mf/FmodNode.java index d1b545091..edb1b0051 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/FmodNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/FmodNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/FractNode.java b/Autogenerated/Bindings/Java9/lib3mf/FractNode.java index 6ab1368f7..e4299ba2b 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/FractNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/FractNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Function.java b/Autogenerated/Bindings/Java9/lib3mf/Function.java index fd5f332a5..a15ba3029 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Function.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Function.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/FunctionCallNode.java b/Autogenerated/Bindings/Java9/lib3mf/FunctionCallNode.java index 57d14a149..4e67ed807 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/FunctionCallNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/FunctionCallNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/FunctionFromImage3D.java b/Autogenerated/Bindings/Java9/lib3mf/FunctionFromImage3D.java index c8aa233c3..c4971991a 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/FunctionFromImage3D.java +++ b/Autogenerated/Bindings/Java9/lib3mf/FunctionFromImage3D.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/FunctionGradientNode.java b/Autogenerated/Bindings/Java9/lib3mf/FunctionGradientNode.java index 73410cb97..7f0889c99 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/FunctionGradientNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/FunctionGradientNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/FunctionIterator.java b/Autogenerated/Bindings/Java9/lib3mf/FunctionIterator.java index 763b7e40e..034d58454 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/FunctionIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/FunctionIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/FunctionReference.java b/Autogenerated/Bindings/Java9/lib3mf/FunctionReference.java index 974d3d6fd..54cb9fa5d 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/FunctionReference.java +++ b/Autogenerated/Bindings/Java9/lib3mf/FunctionReference.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Image3D.java b/Autogenerated/Bindings/Java9/lib3mf/Image3D.java index 62b722891..661e165c5 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Image3D.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Image3D.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Image3DIterator.java b/Autogenerated/Bindings/Java9/lib3mf/Image3DIterator.java index 28e747078..cd8dfdf55 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Image3DIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Image3DIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ImageStack.java b/Autogenerated/Bindings/Java9/lib3mf/ImageStack.java index fc38fbc4f..e1aa6ea12 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ImageStack.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ImageStack.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ImplicitFunction.java b/Autogenerated/Bindings/Java9/lib3mf/ImplicitFunction.java index 6e885097a..c3405e7fa 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ImplicitFunction.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ImplicitFunction.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ImplicitNode.java b/Autogenerated/Bindings/Java9/lib3mf/ImplicitNode.java index fd11cfa02..357b2b5a5 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ImplicitNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ImplicitNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ImplicitPort.java b/Autogenerated/Bindings/Java9/lib3mf/ImplicitPort.java index 76e76a43f..fc376d6e4 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ImplicitPort.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ImplicitPort.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ImplicitPortIterator.java b/Autogenerated/Bindings/Java9/lib3mf/ImplicitPortIterator.java index cd95d03fd..18b71da2d 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ImplicitPortIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ImplicitPortIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/InverseNode.java b/Autogenerated/Bindings/Java9/lib3mf/InverseNode.java index 3f65fd48c..1d554817a 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/InverseNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/InverseNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Iterator.java b/Autogenerated/Bindings/Java9/lib3mf/Iterator.java index b073b0954..fab8636c2 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Iterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Iterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/KeyStore.java b/Autogenerated/Bindings/Java9/lib3mf/KeyStore.java index f77c807d4..e71b5f9ee 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/KeyStore.java +++ b/Autogenerated/Bindings/Java9/lib3mf/KeyStore.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/LengthNode.java b/Autogenerated/Bindings/Java9/lib3mf/LengthNode.java index 2e1251a8c..8d017be47 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/LengthNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/LengthNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/LevelSet.java b/Autogenerated/Bindings/Java9/lib3mf/LevelSet.java index 6c99f35d7..4e40d140f 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/LevelSet.java +++ b/Autogenerated/Bindings/Java9/lib3mf/LevelSet.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/LevelSetIterator.java b/Autogenerated/Bindings/Java9/lib3mf/LevelSetIterator.java index 18612fee1..05cc5ae90 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/LevelSetIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/LevelSetIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Lib3MFException.java b/Autogenerated/Bindings/Java9/lib3mf/Lib3MFException.java index c6f0fffb0..55f2739fe 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Lib3MFException.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Lib3MFException.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Lib3MFWrapper.java b/Autogenerated/Bindings/Java9/lib3mf/Lib3MFWrapper.java index 346c0b820..780c8ef54 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Lib3MFWrapper.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Lib3MFWrapper.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -96,6 +96,16 @@ public enum ObjectType { eSurface } + public static final int BOOLEANOPERATION_UNION = 0; + public static final int BOOLEANOPERATION_DIFFERENCE = 1; + public static final int BOOLEANOPERATION_INTERSECTION = 2; + + public enum BooleanOperation { + eUnion, + eDifference, + eIntersection + } + public static final int TEXTURETYPE_UNKNOWN = 0; public static final int TEXTURETYPE_PNG = 1; public static final int TEXTURETYPE_JPEG = 2; @@ -519,6 +529,24 @@ public static ObjectType convertConstToObjectType (int value) throws Lib3MFExcep } } + public static int convertBooleanOperationToConst (BooleanOperation value) throws Lib3MFException { + switch (value) { + case eUnion: return BOOLEANOPERATION_UNION; + case eDifference: return BOOLEANOPERATION_DIFFERENCE; + case eIntersection: return BOOLEANOPERATION_INTERSECTION; + default: throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "Unknown enum value : " + value); + } + } + + public static BooleanOperation convertConstToBooleanOperation (int value) throws Lib3MFException { + switch (value) { + case BOOLEANOPERATION_UNION: return BooleanOperation.eUnion; + case BOOLEANOPERATION_DIFFERENCE: return BooleanOperation.eDifference; + case BOOLEANOPERATION_INTERSECTION: return BooleanOperation.eIntersection; + default: throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "Unknown enum const : " + value); + } + } + public static int convertTextureTypeToConst (TextureType value) throws Lib3MFException { switch (value) { case eUnknown: return TEXTURETYPE_UNKNOWN; @@ -1112,6 +1140,7 @@ public interface ContentEncryptionCallback extends Callback { protected com.sun.jna.Function lib3mf_objectiterator_getcurrentobject; protected com.sun.jna.Function lib3mf_meshobjectiterator_getcurrentmeshobject; protected com.sun.jna.Function lib3mf_componentsobjectiterator_getcurrentcomponentsobject; + protected com.sun.jna.Function lib3mf_booleanobjectiterator_getcurrentbooleanobject; protected com.sun.jna.Function lib3mf_texture2diterator_getcurrenttexture2d; protected com.sun.jna.Function lib3mf_basematerialgroupiterator_getcurrentbasematerialgroup; protected com.sun.jna.Function lib3mf_colorgroupiterator_getcurrentcolorgroup; @@ -1160,6 +1189,7 @@ public interface ContentEncryptionCallback extends Callback { protected com.sun.jna.Function lib3mf_object_ismeshobject; protected com.sun.jna.Function lib3mf_object_iscomponentsobject; protected com.sun.jna.Function lib3mf_object_islevelsetobject; + protected com.sun.jna.Function lib3mf_object_isbooleanobject; protected com.sun.jna.Function lib3mf_object_isvalid; protected com.sun.jna.Function lib3mf_object_setattachmentasthumbnail; protected com.sun.jna.Function lib3mf_object_getthumbnailattachment; @@ -1217,6 +1247,19 @@ public interface ContentEncryptionCallback extends Callback { protected com.sun.jna.Function lib3mf_levelset_getmesh; protected com.sun.jna.Function lib3mf_levelset_getvolumedata; protected com.sun.jna.Function lib3mf_levelset_setvolumedata; + protected com.sun.jna.Function lib3mf_booleanobject_setbaseobject; + protected com.sun.jna.Function lib3mf_booleanobject_getbaseobject; + protected com.sun.jna.Function lib3mf_booleanobject_setbasetransform; + protected com.sun.jna.Function lib3mf_booleanobject_getbasetransform; + protected com.sun.jna.Function lib3mf_booleanobject_setoperation; + protected com.sun.jna.Function lib3mf_booleanobject_getoperation; + protected com.sun.jna.Function lib3mf_booleanobject_setcsgmodeenabled; + protected com.sun.jna.Function lib3mf_booleanobject_getcsgmodeenabled; + protected com.sun.jna.Function lib3mf_booleanobject_setextractiongridresolution; + protected com.sun.jna.Function lib3mf_booleanobject_getextractiongridresolution; + protected com.sun.jna.Function lib3mf_booleanobject_getoperandcount; + protected com.sun.jna.Function lib3mf_booleanobject_addoperand; + protected com.sun.jna.Function lib3mf_booleanobject_getoperand; protected com.sun.jna.Function lib3mf_beamlattice_getminlength; protected com.sun.jna.Function lib3mf_beamlattice_setminlength; protected com.sun.jna.Function lib3mf_beamlattice_getclipping; @@ -1642,6 +1685,7 @@ public interface ContentEncryptionCallback extends Callback { protected com.sun.jna.Function lib3mf_model_getmultipropertygroupbyid; protected com.sun.jna.Function lib3mf_model_getmeshobjectbyid; protected com.sun.jna.Function lib3mf_model_getcomponentsobjectbyid; + protected com.sun.jna.Function lib3mf_model_getbooleanobjectbyid; protected com.sun.jna.Function lib3mf_model_getcolorgroupbyid; protected com.sun.jna.Function lib3mf_model_getslicestackbyid; protected com.sun.jna.Function lib3mf_model_getlevelsetbyid; @@ -1653,6 +1697,7 @@ public interface ContentEncryptionCallback extends Callback { protected com.sun.jna.Function lib3mf_model_getobjects; protected com.sun.jna.Function lib3mf_model_getmeshobjects; protected com.sun.jna.Function lib3mf_model_getcomponentsobjects; + protected com.sun.jna.Function lib3mf_model_getbooleanobjects; protected com.sun.jna.Function lib3mf_model_gettexture2ds; protected com.sun.jna.Function lib3mf_model_getbasematerialgroups; protected com.sun.jna.Function lib3mf_model_getcolorgroups; @@ -1665,6 +1710,7 @@ public interface ContentEncryptionCallback extends Callback { protected com.sun.jna.Function lib3mf_model_mergefrommodel; protected com.sun.jna.Function lib3mf_model_addmeshobject; protected com.sun.jna.Function lib3mf_model_addcomponentsobject; + protected com.sun.jna.Function lib3mf_model_addbooleanobject; protected com.sun.jna.Function lib3mf_model_addslicestack; protected com.sun.jna.Function lib3mf_model_addtexture2dfromattachment; protected com.sun.jna.Function lib3mf_model_addbasematerialgroup; @@ -1763,6 +1809,7 @@ public Lib3MFWrapper(String libraryPath) { lib3mf_objectiterator_getcurrentobject = mLibrary.getFunction("lib3mf_objectiterator_getcurrentobject"); lib3mf_meshobjectiterator_getcurrentmeshobject = mLibrary.getFunction("lib3mf_meshobjectiterator_getcurrentmeshobject"); lib3mf_componentsobjectiterator_getcurrentcomponentsobject = mLibrary.getFunction("lib3mf_componentsobjectiterator_getcurrentcomponentsobject"); + lib3mf_booleanobjectiterator_getcurrentbooleanobject = mLibrary.getFunction("lib3mf_booleanobjectiterator_getcurrentbooleanobject"); lib3mf_texture2diterator_getcurrenttexture2d = mLibrary.getFunction("lib3mf_texture2diterator_getcurrenttexture2d"); lib3mf_basematerialgroupiterator_getcurrentbasematerialgroup = mLibrary.getFunction("lib3mf_basematerialgroupiterator_getcurrentbasematerialgroup"); lib3mf_colorgroupiterator_getcurrentcolorgroup = mLibrary.getFunction("lib3mf_colorgroupiterator_getcurrentcolorgroup"); @@ -1811,6 +1858,7 @@ public Lib3MFWrapper(String libraryPath) { lib3mf_object_ismeshobject = mLibrary.getFunction("lib3mf_object_ismeshobject"); lib3mf_object_iscomponentsobject = mLibrary.getFunction("lib3mf_object_iscomponentsobject"); lib3mf_object_islevelsetobject = mLibrary.getFunction("lib3mf_object_islevelsetobject"); + lib3mf_object_isbooleanobject = mLibrary.getFunction("lib3mf_object_isbooleanobject"); lib3mf_object_isvalid = mLibrary.getFunction("lib3mf_object_isvalid"); lib3mf_object_setattachmentasthumbnail = mLibrary.getFunction("lib3mf_object_setattachmentasthumbnail"); lib3mf_object_getthumbnailattachment = mLibrary.getFunction("lib3mf_object_getthumbnailattachment"); @@ -1868,6 +1916,19 @@ public Lib3MFWrapper(String libraryPath) { lib3mf_levelset_getmesh = mLibrary.getFunction("lib3mf_levelset_getmesh"); lib3mf_levelset_getvolumedata = mLibrary.getFunction("lib3mf_levelset_getvolumedata"); lib3mf_levelset_setvolumedata = mLibrary.getFunction("lib3mf_levelset_setvolumedata"); + lib3mf_booleanobject_setbaseobject = mLibrary.getFunction("lib3mf_booleanobject_setbaseobject"); + lib3mf_booleanobject_getbaseobject = mLibrary.getFunction("lib3mf_booleanobject_getbaseobject"); + lib3mf_booleanobject_setbasetransform = mLibrary.getFunction("lib3mf_booleanobject_setbasetransform"); + lib3mf_booleanobject_getbasetransform = mLibrary.getFunction("lib3mf_booleanobject_getbasetransform"); + lib3mf_booleanobject_setoperation = mLibrary.getFunction("lib3mf_booleanobject_setoperation"); + lib3mf_booleanobject_getoperation = mLibrary.getFunction("lib3mf_booleanobject_getoperation"); + lib3mf_booleanobject_setcsgmodeenabled = mLibrary.getFunction("lib3mf_booleanobject_setcsgmodeenabled"); + lib3mf_booleanobject_getcsgmodeenabled = mLibrary.getFunction("lib3mf_booleanobject_getcsgmodeenabled"); + lib3mf_booleanobject_setextractiongridresolution = mLibrary.getFunction("lib3mf_booleanobject_setextractiongridresolution"); + lib3mf_booleanobject_getextractiongridresolution = mLibrary.getFunction("lib3mf_booleanobject_getextractiongridresolution"); + lib3mf_booleanobject_getoperandcount = mLibrary.getFunction("lib3mf_booleanobject_getoperandcount"); + lib3mf_booleanobject_addoperand = mLibrary.getFunction("lib3mf_booleanobject_addoperand"); + lib3mf_booleanobject_getoperand = mLibrary.getFunction("lib3mf_booleanobject_getoperand"); lib3mf_beamlattice_getminlength = mLibrary.getFunction("lib3mf_beamlattice_getminlength"); lib3mf_beamlattice_setminlength = mLibrary.getFunction("lib3mf_beamlattice_setminlength"); lib3mf_beamlattice_getclipping = mLibrary.getFunction("lib3mf_beamlattice_getclipping"); @@ -2293,6 +2354,7 @@ public Lib3MFWrapper(String libraryPath) { lib3mf_model_getmultipropertygroupbyid = mLibrary.getFunction("lib3mf_model_getmultipropertygroupbyid"); lib3mf_model_getmeshobjectbyid = mLibrary.getFunction("lib3mf_model_getmeshobjectbyid"); lib3mf_model_getcomponentsobjectbyid = mLibrary.getFunction("lib3mf_model_getcomponentsobjectbyid"); + lib3mf_model_getbooleanobjectbyid = mLibrary.getFunction("lib3mf_model_getbooleanobjectbyid"); lib3mf_model_getcolorgroupbyid = mLibrary.getFunction("lib3mf_model_getcolorgroupbyid"); lib3mf_model_getslicestackbyid = mLibrary.getFunction("lib3mf_model_getslicestackbyid"); lib3mf_model_getlevelsetbyid = mLibrary.getFunction("lib3mf_model_getlevelsetbyid"); @@ -2304,6 +2366,7 @@ public Lib3MFWrapper(String libraryPath) { lib3mf_model_getobjects = mLibrary.getFunction("lib3mf_model_getobjects"); lib3mf_model_getmeshobjects = mLibrary.getFunction("lib3mf_model_getmeshobjects"); lib3mf_model_getcomponentsobjects = mLibrary.getFunction("lib3mf_model_getcomponentsobjects"); + lib3mf_model_getbooleanobjects = mLibrary.getFunction("lib3mf_model_getbooleanobjects"); lib3mf_model_gettexture2ds = mLibrary.getFunction("lib3mf_model_gettexture2ds"); lib3mf_model_getbasematerialgroups = mLibrary.getFunction("lib3mf_model_getbasematerialgroups"); lib3mf_model_getcolorgroups = mLibrary.getFunction("lib3mf_model_getcolorgroups"); @@ -2316,6 +2379,7 @@ public Lib3MFWrapper(String libraryPath) { lib3mf_model_mergefrommodel = mLibrary.getFunction("lib3mf_model_mergefrommodel"); lib3mf_model_addmeshobject = mLibrary.getFunction("lib3mf_model_addmeshobject"); lib3mf_model_addcomponentsobject = mLibrary.getFunction("lib3mf_model_addcomponentsobject"); + lib3mf_model_addbooleanobject = mLibrary.getFunction("lib3mf_model_addbooleanobject"); lib3mf_model_addslicestack = mLibrary.getFunction("lib3mf_model_addslicestack"); lib3mf_model_addtexture2dfromattachment = mLibrary.getFunction("lib3mf_model_addtexture2dfromattachment"); lib3mf_model_addbasematerialgroup = mLibrary.getFunction("lib3mf_model_addbasematerialgroup"); @@ -2413,6 +2477,7 @@ public Lib3MFWrapper(Pointer lookupPointer) throws Lib3MFException { lib3mf_objectiterator_getcurrentobject = loadFunctionByLookup(lookupMethod, "lib3mf_objectiterator_getcurrentobject"); lib3mf_meshobjectiterator_getcurrentmeshobject = loadFunctionByLookup(lookupMethod, "lib3mf_meshobjectiterator_getcurrentmeshobject"); lib3mf_componentsobjectiterator_getcurrentcomponentsobject = loadFunctionByLookup(lookupMethod, "lib3mf_componentsobjectiterator_getcurrentcomponentsobject"); + lib3mf_booleanobjectiterator_getcurrentbooleanobject = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobjectiterator_getcurrentbooleanobject"); lib3mf_texture2diterator_getcurrenttexture2d = loadFunctionByLookup(lookupMethod, "lib3mf_texture2diterator_getcurrenttexture2d"); lib3mf_basematerialgroupiterator_getcurrentbasematerialgroup = loadFunctionByLookup(lookupMethod, "lib3mf_basematerialgroupiterator_getcurrentbasematerialgroup"); lib3mf_colorgroupiterator_getcurrentcolorgroup = loadFunctionByLookup(lookupMethod, "lib3mf_colorgroupiterator_getcurrentcolorgroup"); @@ -2461,6 +2526,7 @@ public Lib3MFWrapper(Pointer lookupPointer) throws Lib3MFException { lib3mf_object_ismeshobject = loadFunctionByLookup(lookupMethod, "lib3mf_object_ismeshobject"); lib3mf_object_iscomponentsobject = loadFunctionByLookup(lookupMethod, "lib3mf_object_iscomponentsobject"); lib3mf_object_islevelsetobject = loadFunctionByLookup(lookupMethod, "lib3mf_object_islevelsetobject"); + lib3mf_object_isbooleanobject = loadFunctionByLookup(lookupMethod, "lib3mf_object_isbooleanobject"); lib3mf_object_isvalid = loadFunctionByLookup(lookupMethod, "lib3mf_object_isvalid"); lib3mf_object_setattachmentasthumbnail = loadFunctionByLookup(lookupMethod, "lib3mf_object_setattachmentasthumbnail"); lib3mf_object_getthumbnailattachment = loadFunctionByLookup(lookupMethod, "lib3mf_object_getthumbnailattachment"); @@ -2518,6 +2584,19 @@ public Lib3MFWrapper(Pointer lookupPointer) throws Lib3MFException { lib3mf_levelset_getmesh = loadFunctionByLookup(lookupMethod, "lib3mf_levelset_getmesh"); lib3mf_levelset_getvolumedata = loadFunctionByLookup(lookupMethod, "lib3mf_levelset_getvolumedata"); lib3mf_levelset_setvolumedata = loadFunctionByLookup(lookupMethod, "lib3mf_levelset_setvolumedata"); + lib3mf_booleanobject_setbaseobject = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_setbaseobject"); + lib3mf_booleanobject_getbaseobject = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getbaseobject"); + lib3mf_booleanobject_setbasetransform = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_setbasetransform"); + lib3mf_booleanobject_getbasetransform = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getbasetransform"); + lib3mf_booleanobject_setoperation = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_setoperation"); + lib3mf_booleanobject_getoperation = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getoperation"); + lib3mf_booleanobject_setcsgmodeenabled = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_setcsgmodeenabled"); + lib3mf_booleanobject_getcsgmodeenabled = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getcsgmodeenabled"); + lib3mf_booleanobject_setextractiongridresolution = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_setextractiongridresolution"); + lib3mf_booleanobject_getextractiongridresolution = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getextractiongridresolution"); + lib3mf_booleanobject_getoperandcount = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getoperandcount"); + lib3mf_booleanobject_addoperand = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_addoperand"); + lib3mf_booleanobject_getoperand = loadFunctionByLookup(lookupMethod, "lib3mf_booleanobject_getoperand"); lib3mf_beamlattice_getminlength = loadFunctionByLookup(lookupMethod, "lib3mf_beamlattice_getminlength"); lib3mf_beamlattice_setminlength = loadFunctionByLookup(lookupMethod, "lib3mf_beamlattice_setminlength"); lib3mf_beamlattice_getclipping = loadFunctionByLookup(lookupMethod, "lib3mf_beamlattice_getclipping"); @@ -2943,6 +3022,7 @@ public Lib3MFWrapper(Pointer lookupPointer) throws Lib3MFException { lib3mf_model_getmultipropertygroupbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getmultipropertygroupbyid"); lib3mf_model_getmeshobjectbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getmeshobjectbyid"); lib3mf_model_getcomponentsobjectbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getcomponentsobjectbyid"); + lib3mf_model_getbooleanobjectbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getbooleanobjectbyid"); lib3mf_model_getcolorgroupbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getcolorgroupbyid"); lib3mf_model_getslicestackbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getslicestackbyid"); lib3mf_model_getlevelsetbyid = loadFunctionByLookup(lookupMethod, "lib3mf_model_getlevelsetbyid"); @@ -2954,6 +3034,7 @@ public Lib3MFWrapper(Pointer lookupPointer) throws Lib3MFException { lib3mf_model_getobjects = loadFunctionByLookup(lookupMethod, "lib3mf_model_getobjects"); lib3mf_model_getmeshobjects = loadFunctionByLookup(lookupMethod, "lib3mf_model_getmeshobjects"); lib3mf_model_getcomponentsobjects = loadFunctionByLookup(lookupMethod, "lib3mf_model_getcomponentsobjects"); + lib3mf_model_getbooleanobjects = loadFunctionByLookup(lookupMethod, "lib3mf_model_getbooleanobjects"); lib3mf_model_gettexture2ds = loadFunctionByLookup(lookupMethod, "lib3mf_model_gettexture2ds"); lib3mf_model_getbasematerialgroups = loadFunctionByLookup(lookupMethod, "lib3mf_model_getbasematerialgroups"); lib3mf_model_getcolorgroups = loadFunctionByLookup(lookupMethod, "lib3mf_model_getcolorgroups"); @@ -2966,6 +3047,7 @@ public Lib3MFWrapper(Pointer lookupPointer) throws Lib3MFException { lib3mf_model_mergefrommodel = loadFunctionByLookup(lookupMethod, "lib3mf_model_mergefrommodel"); lib3mf_model_addmeshobject = loadFunctionByLookup(lookupMethod, "lib3mf_model_addmeshobject"); lib3mf_model_addcomponentsobject = loadFunctionByLookup(lookupMethod, "lib3mf_model_addcomponentsobject"); + lib3mf_model_addbooleanobject = loadFunctionByLookup(lookupMethod, "lib3mf_model_addbooleanobject"); lib3mf_model_addslicestack = loadFunctionByLookup(lookupMethod, "lib3mf_model_addslicestack"); lib3mf_model_addtexture2dfromattachment = loadFunctionByLookup(lookupMethod, "lib3mf_model_addtexture2dfromattachment"); lib3mf_model_addbasematerialgroup = loadFunctionByLookup(lookupMethod, "lib3mf_model_addbasematerialgroup"); @@ -3831,6 +3913,11 @@ public T PolymorphicFactory(Pointer handle, Class cls) { case 0xBAF1D8B7: obj = (T)(new Base(this, handle)); break; // First 64 bits of SHA1 of a string: "Lib3MF::Base" } break; + case 0x85FA0E88: + switch(lsbId) { + case 0x06B6C357: obj = (T)(new BooleanObject(this, handle)); break; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObject" + } + break; case 0x87740AD5: switch(lsbId) { case 0x3454E0DF: obj = (T)(new Log10Node(this, handle)); break; // First 64 bits of SHA1 of a string: "Lib3MF::Log10Node" @@ -3906,6 +3993,11 @@ public T PolymorphicFactory(Pointer handle, Class cls) { case 0xF70FB6F9: obj = (T)(new CompositeMaterialsIterator(this, handle)); break; // First 64 bits of SHA1 of a string: "Lib3MF::CompositeMaterialsIterator" } break; + case 0xAFF01F51: + switch(lsbId) { + case 0x2E1FF6AE: obj = (T)(new BooleanObjectIterator(this, handle)); break; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObjectIterator" + } + break; case 0xB19B9FDA: switch(lsbId) { case 0x94B0A5E7: obj = (T)(new OneInputNode(this, handle)); break; // First 64 bits of SHA1 of a string: "Lib3MF::OneInputNode" diff --git a/Autogenerated/Bindings/Java9/lib3mf/Log10Node.java b/Autogenerated/Bindings/Java9/lib3mf/Log10Node.java index 8c051ae41..55a643f21 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Log10Node.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Log10Node.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Log2Node.java b/Autogenerated/Bindings/Java9/lib3mf/Log2Node.java index a940b14ba..92441cf21 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Log2Node.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Log2Node.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/LogNode.java b/Autogenerated/Bindings/Java9/lib3mf/LogNode.java index dd1e84757..aecc51d9f 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/LogNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/LogNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MatVecMultiplicationNode.java b/Autogenerated/Bindings/Java9/lib3mf/MatVecMultiplicationNode.java index dc0ae5de2..93c24e283 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MatVecMultiplicationNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MatVecMultiplicationNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MaterialMapping.java b/Autogenerated/Bindings/Java9/lib3mf/MaterialMapping.java index c7a1c135e..53fc29b43 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MaterialMapping.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MaterialMapping.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Matrix4x4.java b/Autogenerated/Bindings/Java9/lib3mf/Matrix4x4.java index f2edb51bb..6d6619b25 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Matrix4x4.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Matrix4x4.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MatrixFromColumnsNode.java b/Autogenerated/Bindings/Java9/lib3mf/MatrixFromColumnsNode.java index 176632286..00ce7b82f 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MatrixFromColumnsNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MatrixFromColumnsNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MatrixFromRowsNode.java b/Autogenerated/Bindings/Java9/lib3mf/MatrixFromRowsNode.java index ea205e31b..a75634870 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MatrixFromRowsNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MatrixFromRowsNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MaxNode.java b/Autogenerated/Bindings/Java9/lib3mf/MaxNode.java index 8466a0c53..24f097140 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MaxNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MaxNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MeshNode.java b/Autogenerated/Bindings/Java9/lib3mf/MeshNode.java index 45304bdb5..3abafe80e 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MeshNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MeshNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MeshObject.java b/Autogenerated/Bindings/Java9/lib3mf/MeshObject.java index 5eb159be5..a5c29d9dc 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MeshObject.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MeshObject.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MeshObjectIterator.java b/Autogenerated/Bindings/Java9/lib3mf/MeshObjectIterator.java index 27da63207..6320032e9 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MeshObjectIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MeshObjectIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MetaData.java b/Autogenerated/Bindings/Java9/lib3mf/MetaData.java index 8dd99d573..188b1efdc 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MetaData.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MetaData.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MetaDataGroup.java b/Autogenerated/Bindings/Java9/lib3mf/MetaDataGroup.java index 4b7618326..dd713b50a 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MetaDataGroup.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MetaDataGroup.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MinNode.java b/Autogenerated/Bindings/Java9/lib3mf/MinNode.java index 5cf44fa24..6774e2867 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MinNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MinNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ModNode.java b/Autogenerated/Bindings/Java9/lib3mf/ModNode.java index 608b38a8a..3a555dfa8 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ModNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ModNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Model.java b/Autogenerated/Bindings/Java9/lib3mf/Model.java index 5e4cc26bb..a4bc985c4 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Model.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Model.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -355,6 +355,25 @@ public ComponentsObject getComponentsObjectByID(int uniqueResourceID) throws Lib return componentsObjectInstance; } + /** + * finds a boolean object by its UniqueResourceID + * + * @param uniqueResourceID UniqueResourceID + * @return returns the boolean object instance + * @throws Lib3MFException + */ + public BooleanObject getBooleanObjectByID(int uniqueResourceID) throws Lib3MFException { + Pointer bufferBooleanObjectInstance = new Memory(8); + mWrapper.checkError(this, mWrapper.lib3mf_model_getbooleanobjectbyid.invokeInt(new java.lang.Object[]{mHandle, uniqueResourceID, bufferBooleanObjectInstance})); + Pointer valueBooleanObjectInstance = bufferBooleanObjectInstance.getPointer(0); + BooleanObject booleanObjectInstance = null; + if (valueBooleanObjectInstance == Pointer.NULL) { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "BooleanObjectInstance was a null pointer"); + } + booleanObjectInstance = mWrapper.PolymorphicFactory(valueBooleanObjectInstance, BooleanObject.class); + return booleanObjectInstance; + } + /** * finds a model color group by its UniqueResourceID * @@ -561,6 +580,24 @@ public ComponentsObjectIterator getComponentsObjects() throws Lib3MFException { return resourceIterator; } + /** + * creates a resource iterator instance with all boolean object resources. + * + * @return returns the iterator instance. + * @throws Lib3MFException + */ + public BooleanObjectIterator getBooleanObjects() throws Lib3MFException { + Pointer bufferResourceIterator = new Memory(8); + mWrapper.checkError(this, mWrapper.lib3mf_model_getbooleanobjects.invokeInt(new java.lang.Object[]{mHandle, bufferResourceIterator})); + Pointer valueResourceIterator = bufferResourceIterator.getPointer(0); + BooleanObjectIterator resourceIterator = null; + if (valueResourceIterator == Pointer.NULL) { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "ResourceIterator was a null pointer"); + } + resourceIterator = mWrapper.PolymorphicFactory(valueResourceIterator, BooleanObjectIterator.class); + return resourceIterator; + } + /** * creates a Texture2DIterator instance with all texture2d resources. * @@ -775,6 +812,24 @@ public ComponentsObject addComponentsObject() throws Lib3MFException { return componentsObjectInstance; } + /** + * adds an empty boolean object to the model. + * + * @return returns the boolean object instance + * @throws Lib3MFException + */ + public BooleanObject addBooleanObject() throws Lib3MFException { + Pointer bufferBooleanObjectInstance = new Memory(8); + mWrapper.checkError(this, mWrapper.lib3mf_model_addbooleanobject.invokeInt(new java.lang.Object[]{mHandle, bufferBooleanObjectInstance})); + Pointer valueBooleanObjectInstance = bufferBooleanObjectInstance.getPointer(0); + BooleanObject booleanObjectInstance = null; + if (valueBooleanObjectInstance == Pointer.NULL) { + throw new Lib3MFException(Lib3MFException.LIB3MF_ERROR_INVALIDPARAM, "BooleanObjectInstance was a null pointer"); + } + booleanObjectInstance = mWrapper.PolymorphicFactory(valueBooleanObjectInstance, BooleanObject.class); + return booleanObjectInstance; + } + /** * creates a new model slicestack by its id * diff --git a/Autogenerated/Bindings/Java9/lib3mf/MultiPropertyGroup.java b/Autogenerated/Bindings/Java9/lib3mf/MultiPropertyGroup.java index b32a8b8d9..d6287f451 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MultiPropertyGroup.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MultiPropertyGroup.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MultiPropertyGroupIterator.java b/Autogenerated/Bindings/Java9/lib3mf/MultiPropertyGroupIterator.java index 3dc4d0f93..ca596f4e7 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MultiPropertyGroupIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MultiPropertyGroupIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MultiPropertyLayer.java b/Autogenerated/Bindings/Java9/lib3mf/MultiPropertyLayer.java index cc349b19a..fd6e38a45 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MultiPropertyLayer.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MultiPropertyLayer.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/MultiplicationNode.java b/Autogenerated/Bindings/Java9/lib3mf/MultiplicationNode.java index eeba2a29f..9fbbf3843 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/MultiplicationNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/MultiplicationNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/NodeIterator.java b/Autogenerated/Bindings/Java9/lib3mf/NodeIterator.java index 6cefef57c..d1bf57277 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/NodeIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/NodeIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/NormalizeDistanceNode.java b/Autogenerated/Bindings/Java9/lib3mf/NormalizeDistanceNode.java index 8ba115a9f..da41811e9 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/NormalizeDistanceNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/NormalizeDistanceNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Object.java b/Autogenerated/Bindings/Java9/lib3mf/Object.java index 10a187e85..2f80fac8b 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Object.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Object.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -168,6 +168,18 @@ public boolean isLevelSetObject() throws Lib3MFException { return bufferIsLevelSetObject.getByte(0) != 0; } + /** + * Retrieves, if an object is a boolean object + * + * @return returns, whether the object is a boolean object + * @throws Lib3MFException + */ + public boolean isBooleanObject() throws Lib3MFException { + Pointer bufferIsBooleanObject = new Memory(1); + mWrapper.checkError(this, mWrapper.lib3mf_object_isbooleanobject.invokeInt(new java.lang.Object[]{mHandle, bufferIsBooleanObject})); + return bufferIsBooleanObject.getByte(0) != 0; + } + /** * Retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. * diff --git a/Autogenerated/Bindings/Java9/lib3mf/ObjectIterator.java b/Autogenerated/Bindings/Java9/lib3mf/ObjectIterator.java index e9ee4dbc4..3dd3faddf 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ObjectIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ObjectIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/OneInputNode.java b/Autogenerated/Bindings/Java9/lib3mf/OneInputNode.java index 3cd0f8f66..0949e7694 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/OneInputNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/OneInputNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/PackagePart.java b/Autogenerated/Bindings/Java9/lib3mf/PackagePart.java index c22d4ae95..3b487b29a 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/PackagePart.java +++ b/Autogenerated/Bindings/Java9/lib3mf/PackagePart.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Position.java b/Autogenerated/Bindings/Java9/lib3mf/Position.java index 8ca7b7f64..ebf1c4043 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Position.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Position.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Position2D.java b/Autogenerated/Bindings/Java9/lib3mf/Position2D.java index 65c9cab65..58d86aa67 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Position2D.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Position2D.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/PowNode.java b/Autogenerated/Bindings/Java9/lib3mf/PowNode.java index 640300644..21a01f209 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/PowNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/PowNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Reader.java b/Autogenerated/Bindings/Java9/lib3mf/Reader.java index 6ecbc20c7..eb0a6c304 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Reader.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Reader.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Resource.java b/Autogenerated/Bindings/Java9/lib3mf/Resource.java index 0c889bbd5..383ceca2b 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Resource.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Resource.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ResourceData.java b/Autogenerated/Bindings/Java9/lib3mf/ResourceData.java index 8e0fc54dd..1420342a8 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ResourceData.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ResourceData.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ResourceDataGroup.java b/Autogenerated/Bindings/Java9/lib3mf/ResourceDataGroup.java index 4f13266bc..4422dad39 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ResourceDataGroup.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ResourceDataGroup.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ResourceIdNode.java b/Autogenerated/Bindings/Java9/lib3mf/ResourceIdNode.java index 32dde6ea5..827d38020 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ResourceIdNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ResourceIdNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/ResourceIterator.java b/Autogenerated/Bindings/Java9/lib3mf/ResourceIterator.java index d1d847a2e..495019b11 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/ResourceIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/ResourceIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/RoundNode.java b/Autogenerated/Bindings/Java9/lib3mf/RoundNode.java index 0351d5512..c5ff42f74 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/RoundNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/RoundNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/SelectNode.java b/Autogenerated/Bindings/Java9/lib3mf/SelectNode.java index d9fe99006..16ba55b85 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/SelectNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/SelectNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/SignNode.java b/Autogenerated/Bindings/Java9/lib3mf/SignNode.java index d796d736e..1a203133f 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/SignNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/SignNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/SinNode.java b/Autogenerated/Bindings/Java9/lib3mf/SinNode.java index 674eee2bd..23919d8ae 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/SinNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/SinNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/SinhNode.java b/Autogenerated/Bindings/Java9/lib3mf/SinhNode.java index 767541bc9..c58d244ed 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/SinhNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/SinhNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Slice.java b/Autogenerated/Bindings/Java9/lib3mf/Slice.java index 0d8a6d037..a0c262cfb 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Slice.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Slice.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/SliceStack.java b/Autogenerated/Bindings/Java9/lib3mf/SliceStack.java index aafb46703..02e241fb3 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/SliceStack.java +++ b/Autogenerated/Bindings/Java9/lib3mf/SliceStack.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/SliceStackIterator.java b/Autogenerated/Bindings/Java9/lib3mf/SliceStackIterator.java index ce6e6a026..f51751352 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/SliceStackIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/SliceStackIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/SqrtNode.java b/Autogenerated/Bindings/Java9/lib3mf/SqrtNode.java index df7dcabfb..d0ce5e516 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/SqrtNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/SqrtNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/SubtractionNode.java b/Autogenerated/Bindings/Java9/lib3mf/SubtractionNode.java index 808defec7..ddcb659bd 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/SubtractionNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/SubtractionNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/TanNode.java b/Autogenerated/Bindings/Java9/lib3mf/TanNode.java index 1d40332fc..c10d32de2 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/TanNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/TanNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/TanhNode.java b/Autogenerated/Bindings/Java9/lib3mf/TanhNode.java index 94562e998..970797efc 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/TanhNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/TanhNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Tex2Coord.java b/Autogenerated/Bindings/Java9/lib3mf/Tex2Coord.java index 183717a5d..6fafa91a2 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Tex2Coord.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Tex2Coord.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Texture2D.java b/Autogenerated/Bindings/Java9/lib3mf/Texture2D.java index 24a9c523e..ad0ad7aa5 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Texture2D.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Texture2D.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Texture2DGroup.java b/Autogenerated/Bindings/Java9/lib3mf/Texture2DGroup.java index e80a5907e..39592625c 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Texture2DGroup.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Texture2DGroup.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Texture2DGroupIterator.java b/Autogenerated/Bindings/Java9/lib3mf/Texture2DGroupIterator.java index 185f318be..d25de0d9e 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Texture2DGroupIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Texture2DGroupIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Texture2DIterator.java b/Autogenerated/Bindings/Java9/lib3mf/Texture2DIterator.java index 85789f6a4..42d289e6c 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Texture2DIterator.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Texture2DIterator.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Transform.java b/Autogenerated/Bindings/Java9/lib3mf/Transform.java index ed02ea877..795496b75 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Transform.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Transform.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/TransposeNode.java b/Autogenerated/Bindings/Java9/lib3mf/TransposeNode.java index 2dfd32153..2513b7dd0 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/TransposeNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/TransposeNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Triangle.java b/Autogenerated/Bindings/Java9/lib3mf/Triangle.java index c561cd299..007400619 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Triangle.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Triangle.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/TriangleProperties.java b/Autogenerated/Bindings/Java9/lib3mf/TriangleProperties.java index 7328dc17f..0e44da4e8 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/TriangleProperties.java +++ b/Autogenerated/Bindings/Java9/lib3mf/TriangleProperties.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/TriangleSet.java b/Autogenerated/Bindings/Java9/lib3mf/TriangleSet.java index 7d41ac48e..7718b8563 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/TriangleSet.java +++ b/Autogenerated/Bindings/Java9/lib3mf/TriangleSet.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/TwoInputNode.java b/Autogenerated/Bindings/Java9/lib3mf/TwoInputNode.java index c3ff0d9af..2f26f7c18 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/TwoInputNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/TwoInputNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/UnsignedMeshNode.java b/Autogenerated/Bindings/Java9/lib3mf/UnsignedMeshNode.java index 95ccdadce..3b1c9ff91 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/UnsignedMeshNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/UnsignedMeshNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Vector.java b/Autogenerated/Bindings/Java9/lib3mf/Vector.java index ab8e4f415..4a47a9847 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Vector.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Vector.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/VectorFromScalarNode.java b/Autogenerated/Bindings/Java9/lib3mf/VectorFromScalarNode.java index 69e2e8bef..7527de675 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/VectorFromScalarNode.java +++ b/Autogenerated/Bindings/Java9/lib3mf/VectorFromScalarNode.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/VolumeData.java b/Autogenerated/Bindings/Java9/lib3mf/VolumeData.java index 87153b0ab..26678e11d 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/VolumeData.java +++ b/Autogenerated/Bindings/Java9/lib3mf/VolumeData.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/VolumeDataColor.java b/Autogenerated/Bindings/Java9/lib3mf/VolumeDataColor.java index dd587d263..41b3fb590 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/VolumeDataColor.java +++ b/Autogenerated/Bindings/Java9/lib3mf/VolumeDataColor.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/VolumeDataComposite.java b/Autogenerated/Bindings/Java9/lib3mf/VolumeDataComposite.java index c0bc094bb..002e1acdc 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/VolumeDataComposite.java +++ b/Autogenerated/Bindings/Java9/lib3mf/VolumeDataComposite.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/VolumeDataProperty.java b/Autogenerated/Bindings/Java9/lib3mf/VolumeDataProperty.java index 33ba58256..9a7014a97 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/VolumeDataProperty.java +++ b/Autogenerated/Bindings/Java9/lib3mf/VolumeDataProperty.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/Java9/lib3mf/Writer.java b/Autogenerated/Bindings/Java9/lib3mf/Writer.java index 45bda9efc..91e91ff70 100644 --- a/Autogenerated/Bindings/Java9/lib3mf/Writer.java +++ b/Autogenerated/Bindings/Java9/lib3mf/Writer.java @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated Java file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Bindings/NodeJS/lib3mf_dynamic.cc b/Autogenerated/Bindings/NodeJS/lib3mf_dynamic.cc index 238da6b18..553825932 100644 --- a/Autogenerated/Bindings/NodeJS/lib3mf_dynamic.cc +++ b/Autogenerated/Bindings/NodeJS/lib3mf_dynamic.cc @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated plain C Header file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -90,6 +90,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_ObjectIterator_GetCurrentObject = NULL; pWrapperTable->m_MeshObjectIterator_GetCurrentMeshObject = NULL; pWrapperTable->m_ComponentsObjectIterator_GetCurrentComponentsObject = NULL; + pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject = NULL; pWrapperTable->m_Texture2DIterator_GetCurrentTexture2D = NULL; pWrapperTable->m_BaseMaterialGroupIterator_GetCurrentBaseMaterialGroup = NULL; pWrapperTable->m_ColorGroupIterator_GetCurrentColorGroup = NULL; @@ -138,6 +139,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_Object_IsMeshObject = NULL; pWrapperTable->m_Object_IsComponentsObject = NULL; pWrapperTable->m_Object_IsLevelSetObject = NULL; + pWrapperTable->m_Object_IsBooleanObject = NULL; pWrapperTable->m_Object_IsValid = NULL; pWrapperTable->m_Object_SetAttachmentAsThumbnail = NULL; pWrapperTable->m_Object_GetThumbnailAttachment = NULL; @@ -195,6 +197,19 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_LevelSet_GetMesh = NULL; pWrapperTable->m_LevelSet_GetVolumeData = NULL; pWrapperTable->m_LevelSet_SetVolumeData = NULL; + pWrapperTable->m_BooleanObject_SetBaseObject = NULL; + pWrapperTable->m_BooleanObject_GetBaseObject = NULL; + pWrapperTable->m_BooleanObject_SetBaseTransform = NULL; + pWrapperTable->m_BooleanObject_GetBaseTransform = NULL; + pWrapperTable->m_BooleanObject_SetOperation = NULL; + pWrapperTable->m_BooleanObject_GetOperation = NULL; + pWrapperTable->m_BooleanObject_SetCSGModeEnabled = NULL; + pWrapperTable->m_BooleanObject_GetCSGModeEnabled = NULL; + pWrapperTable->m_BooleanObject_SetExtractionGridResolution = NULL; + pWrapperTable->m_BooleanObject_GetExtractionGridResolution = NULL; + pWrapperTable->m_BooleanObject_GetOperandCount = NULL; + pWrapperTable->m_BooleanObject_AddOperand = NULL; + pWrapperTable->m_BooleanObject_GetOperand = NULL; pWrapperTable->m_BeamLattice_GetMinLength = NULL; pWrapperTable->m_BeamLattice_SetMinLength = NULL; pWrapperTable->m_BeamLattice_GetClipping = NULL; @@ -620,6 +635,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_Model_GetMultiPropertyGroupByID = NULL; pWrapperTable->m_Model_GetMeshObjectByID = NULL; pWrapperTable->m_Model_GetComponentsObjectByID = NULL; + pWrapperTable->m_Model_GetBooleanObjectByID = NULL; pWrapperTable->m_Model_GetColorGroupByID = NULL; pWrapperTable->m_Model_GetSliceStackByID = NULL; pWrapperTable->m_Model_GetLevelSetByID = NULL; @@ -631,6 +647,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_Model_GetObjects = NULL; pWrapperTable->m_Model_GetMeshObjects = NULL; pWrapperTable->m_Model_GetComponentsObjects = NULL; + pWrapperTable->m_Model_GetBooleanObjects = NULL; pWrapperTable->m_Model_GetTexture2Ds = NULL; pWrapperTable->m_Model_GetBaseMaterialGroups = NULL; pWrapperTable->m_Model_GetColorGroups = NULL; @@ -643,6 +660,7 @@ Lib3MFResult InitLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable) pWrapperTable->m_Model_MergeFromModel = NULL; pWrapperTable->m_Model_AddMeshObject = NULL; pWrapperTable->m_Model_AddComponentsObject = NULL; + pWrapperTable->m_Model_AddBooleanObject = NULL; pWrapperTable->m_Model_AddSliceStack = NULL; pWrapperTable->m_Model_AddTexture2DFromAttachment = NULL; pWrapperTable->m_Model_AddBaseMaterialGroup = NULL; @@ -1126,6 +1144,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_ComponentsObjectIterator_GetCurrentComponentsObject == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject = (PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr) GetProcAddress(hLibrary, "lib3mf_booleanobjectiterator_getcurrentbooleanobject"); + #else // _WIN32 + pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject = (PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr) dlsym(hLibrary, "lib3mf_booleanobjectiterator_getcurrentbooleanobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Texture2DIterator_GetCurrentTexture2D = (PLib3MFTexture2DIterator_GetCurrentTexture2DPtr) GetProcAddress(hLibrary, "lib3mf_texture2diterator_getcurrenttexture2d"); #else // _WIN32 @@ -1558,6 +1585,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_Object_IsLevelSetObject == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Object_IsBooleanObject = (PLib3MFObject_IsBooleanObjectPtr) GetProcAddress(hLibrary, "lib3mf_object_isbooleanobject"); + #else // _WIN32 + pWrapperTable->m_Object_IsBooleanObject = (PLib3MFObject_IsBooleanObjectPtr) dlsym(hLibrary, "lib3mf_object_isbooleanobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Object_IsBooleanObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Object_IsValid = (PLib3MFObject_IsValidPtr) GetProcAddress(hLibrary, "lib3mf_object_isvalid"); #else // _WIN32 @@ -2071,6 +2107,123 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_LevelSet_SetVolumeData == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetBaseObject = (PLib3MFBooleanObject_SetBaseObjectPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setbaseobject"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetBaseObject = (PLib3MFBooleanObject_SetBaseObjectPtr) dlsym(hLibrary, "lib3mf_booleanobject_setbaseobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetBaseObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetBaseObject = (PLib3MFBooleanObject_GetBaseObjectPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getbaseobject"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetBaseObject = (PLib3MFBooleanObject_GetBaseObjectPtr) dlsym(hLibrary, "lib3mf_booleanobject_getbaseobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetBaseObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetBaseTransform = (PLib3MFBooleanObject_SetBaseTransformPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setbasetransform"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetBaseTransform = (PLib3MFBooleanObject_SetBaseTransformPtr) dlsym(hLibrary, "lib3mf_booleanobject_setbasetransform"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetBaseTransform == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetBaseTransform = (PLib3MFBooleanObject_GetBaseTransformPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getbasetransform"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetBaseTransform = (PLib3MFBooleanObject_GetBaseTransformPtr) dlsym(hLibrary, "lib3mf_booleanobject_getbasetransform"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetBaseTransform == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetOperation = (PLib3MFBooleanObject_SetOperationPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setoperation"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetOperation = (PLib3MFBooleanObject_SetOperationPtr) dlsym(hLibrary, "lib3mf_booleanobject_setoperation"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetOperation == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetOperation = (PLib3MFBooleanObject_GetOperationPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getoperation"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetOperation = (PLib3MFBooleanObject_GetOperationPtr) dlsym(hLibrary, "lib3mf_booleanobject_getoperation"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetOperation == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetCSGModeEnabled = (PLib3MFBooleanObject_SetCSGModeEnabledPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setcsgmodeenabled"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetCSGModeEnabled = (PLib3MFBooleanObject_SetCSGModeEnabledPtr) dlsym(hLibrary, "lib3mf_booleanobject_setcsgmodeenabled"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetCSGModeEnabled == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetCSGModeEnabled = (PLib3MFBooleanObject_GetCSGModeEnabledPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getcsgmodeenabled"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetCSGModeEnabled = (PLib3MFBooleanObject_GetCSGModeEnabledPtr) dlsym(hLibrary, "lib3mf_booleanobject_getcsgmodeenabled"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetCSGModeEnabled == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_SetExtractionGridResolution = (PLib3MFBooleanObject_SetExtractionGridResolutionPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_setextractiongridresolution"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_SetExtractionGridResolution = (PLib3MFBooleanObject_SetExtractionGridResolutionPtr) dlsym(hLibrary, "lib3mf_booleanobject_setextractiongridresolution"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_SetExtractionGridResolution == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetExtractionGridResolution = (PLib3MFBooleanObject_GetExtractionGridResolutionPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getextractiongridresolution"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetExtractionGridResolution = (PLib3MFBooleanObject_GetExtractionGridResolutionPtr) dlsym(hLibrary, "lib3mf_booleanobject_getextractiongridresolution"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetExtractionGridResolution == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetOperandCount = (PLib3MFBooleanObject_GetOperandCountPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getoperandcount"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetOperandCount = (PLib3MFBooleanObject_GetOperandCountPtr) dlsym(hLibrary, "lib3mf_booleanobject_getoperandcount"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetOperandCount == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_AddOperand = (PLib3MFBooleanObject_AddOperandPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_addoperand"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_AddOperand = (PLib3MFBooleanObject_AddOperandPtr) dlsym(hLibrary, "lib3mf_booleanobject_addoperand"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_AddOperand == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_BooleanObject_GetOperand = (PLib3MFBooleanObject_GetOperandPtr) GetProcAddress(hLibrary, "lib3mf_booleanobject_getoperand"); + #else // _WIN32 + pWrapperTable->m_BooleanObject_GetOperand = (PLib3MFBooleanObject_GetOperandPtr) dlsym(hLibrary, "lib3mf_booleanobject_getoperand"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_BooleanObject_GetOperand == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_BeamLattice_GetMinLength = (PLib3MFBeamLattice_GetMinLengthPtr) GetProcAddress(hLibrary, "lib3mf_beamlattice_getminlength"); #else // _WIN32 @@ -5896,6 +6049,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_Model_GetComponentsObjectByID == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Model_GetBooleanObjectByID = (PLib3MFModel_GetBooleanObjectByIDPtr) GetProcAddress(hLibrary, "lib3mf_model_getbooleanobjectbyid"); + #else // _WIN32 + pWrapperTable->m_Model_GetBooleanObjectByID = (PLib3MFModel_GetBooleanObjectByIDPtr) dlsym(hLibrary, "lib3mf_model_getbooleanobjectbyid"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Model_GetBooleanObjectByID == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Model_GetColorGroupByID = (PLib3MFModel_GetColorGroupByIDPtr) GetProcAddress(hLibrary, "lib3mf_model_getcolorgroupbyid"); #else // _WIN32 @@ -5995,6 +6157,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_Model_GetComponentsObjects == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Model_GetBooleanObjects = (PLib3MFModel_GetBooleanObjectsPtr) GetProcAddress(hLibrary, "lib3mf_model_getbooleanobjects"); + #else // _WIN32 + pWrapperTable->m_Model_GetBooleanObjects = (PLib3MFModel_GetBooleanObjectsPtr) dlsym(hLibrary, "lib3mf_model_getbooleanobjects"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Model_GetBooleanObjects == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Model_GetTexture2Ds = (PLib3MFModel_GetTexture2DsPtr) GetProcAddress(hLibrary, "lib3mf_model_gettexture2ds"); #else // _WIN32 @@ -6103,6 +6274,15 @@ Lib3MFResult LoadLib3MFWrapperTable(sLib3MFDynamicWrapperTable * pWrapperTable, if (pWrapperTable->m_Model_AddComponentsObject == NULL) return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_Model_AddBooleanObject = (PLib3MFModel_AddBooleanObjectPtr) GetProcAddress(hLibrary, "lib3mf_model_addbooleanobject"); + #else // _WIN32 + pWrapperTable->m_Model_AddBooleanObject = (PLib3MFModel_AddBooleanObjectPtr) dlsym(hLibrary, "lib3mf_model_addbooleanobject"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_Model_AddBooleanObject == NULL) + return LIB3MF_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_Model_AddSliceStack = (PLib3MFModel_AddSliceStackPtr) GetProcAddress(hLibrary, "lib3mf_model_addslicestack"); #else // _WIN32 diff --git a/Autogenerated/Bindings/NodeJS/lib3mf_dynamic.h b/Autogenerated/Bindings/NodeJS/lib3mf_dynamic.h index cc8d934bd..778010c7c 100644 --- a/Autogenerated/Bindings/NodeJS/lib3mf_dynamic.h +++ b/Autogenerated/Bindings/NodeJS/lib3mf_dynamic.h @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated plain C Header file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -484,6 +484,19 @@ typedef Lib3MFResult (*PLib3MFMeshObjectIterator_GetCurrentMeshObjectPtr) (Lib3M */ typedef Lib3MFResult (*PLib3MFComponentsObjectIterator_GetCurrentComponentsObjectPtr) (Lib3MF_ComponentsObjectIterator pComponentsObjectIterator, Lib3MF_ComponentsObject * pResource); +/************************************************************************************************************************* + Class definition for BooleanObjectIterator +**************************************************************************************************************************/ + +/** +* Returns the BooleanObject the iterator points at. +* +* @param[in] pBooleanObjectIterator - BooleanObjectIterator instance. +* @param[out] pResource - returns the BooleanObject instance. +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr) (Lib3MF_BooleanObjectIterator pBooleanObjectIterator, Lib3MF_BooleanObject * pResource); + /************************************************************************************************************************* Class definition for Texture2DIterator **************************************************************************************************************************/ @@ -998,6 +1011,15 @@ typedef Lib3MFResult (*PLib3MFObject_IsComponentsObjectPtr) (Lib3MF_Object pObje */ typedef Lib3MFResult (*PLib3MFObject_IsLevelSetObjectPtr) (Lib3MF_Object pObject, bool * pIsLevelSetObject); +/** +* Retrieves, if an object is a boolean object +* +* @param[in] pObject - Object instance. +* @param[out] pIsBooleanObject - returns, whether the object is a boolean object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFObject_IsBooleanObjectPtr) (Lib3MF_Object pObject, bool * pIsBooleanObject); + /** * Retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. * @@ -1548,6 +1570,131 @@ typedef Lib3MFResult (*PLib3MFLevelSet_GetVolumeDataPtr) (Lib3MF_LevelSet pLevel */ typedef Lib3MFResult (*PLib3MFLevelSet_SetVolumeDataPtr) (Lib3MF_LevelSet pLevelSet, Lib3MF_VolumeData pTheVolumeData); +/************************************************************************************************************************* + Class definition for BooleanObject +**************************************************************************************************************************/ + +/** +* Sets the base object and transform for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pBaseObject - base object of the boolean shape +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetBaseObjectPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object pBaseObject, const sLib3MFTransform * pTransform); + +/** +* Returns the base object of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pBaseObject - base object of the boolean shape +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetBaseObjectPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object * pBaseObject); + +/** +* Sets the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetBaseTransformPtr) (Lib3MF_BooleanObject pBooleanObject, const sLib3MFTransform * pTransform); + +/** +* Returns the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetBaseTransformPtr) (Lib3MF_BooleanObject pBooleanObject, sLib3MFTransform * pTransform); + +/** +* Sets the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] eOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetOperationPtr) (Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation eOperation); + +/** +* Returns the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetOperationPtr) (Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation * pOperation); + +/** +* Enables or disables CSG field evaluation for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] bCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetCSGModeEnabledPtr) (Lib3MF_BooleanObject pBooleanObject, bool bCSGModeEnabled); + +/** +* Returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetCSGModeEnabledPtr) (Lib3MF_BooleanObject pBooleanObject, bool * pCSGModeEnabled); + +/** +* Sets the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_SetExtractionGridResolutionPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nGridResolution); + +/** +* Returns the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetExtractionGridResolutionPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pGridResolution); + +/** +* Returns the number of operands in the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCount - number of operands in the boolean sequence +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetOperandCountPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pCount); + +/** +* Adds an operand object to the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pOperandObject - mesh object used as operand +* @param[in] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_AddOperandPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_MeshObject pOperandObject, const sLib3MFTransform * pTransform); + +/** +* Returns one operand object and transform from the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nIndex - index of the operand in the boolean sequence +* @param[out] pOperandObject - mesh object used as operand +* @param[out] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFBooleanObject_GetOperandPtr) (Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nIndex, Lib3MF_MeshObject * pOperandObject, sLib3MFTransform * pTransform); + /************************************************************************************************************************* Class definition for BeamLattice **************************************************************************************************************************/ @@ -6165,6 +6312,16 @@ typedef Lib3MFResult (*PLib3MFModel_GetMeshObjectByIDPtr) (Lib3MF_Model pModel, */ typedef Lib3MFResult (*PLib3MFModel_GetComponentsObjectByIDPtr) (Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* finds a boolean object by its UniqueResourceID +* +* @param[in] pModel - Model instance. +* @param[in] nUniqueResourceID - UniqueResourceID +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFModel_GetBooleanObjectByIDPtr) (Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * finds a model color group by its UniqueResourceID * @@ -6270,6 +6427,15 @@ typedef Lib3MFResult (*PLib3MFModel_GetMeshObjectsPtr) (Lib3MF_Model pModel, Lib */ typedef Lib3MFResult (*PLib3MFModel_GetComponentsObjectsPtr) (Lib3MF_Model pModel, Lib3MF_ComponentsObjectIterator * pResourceIterator); +/** +* creates a resource iterator instance with all boolean object resources. +* +* @param[in] pModel - Model instance. +* @param[out] pResourceIterator - returns the iterator instance. +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFModel_GetBooleanObjectsPtr) (Lib3MF_Model pModel, Lib3MF_BooleanObjectIterator * pResourceIterator); + /** * creates a Texture2DIterator instance with all texture2d resources. * @@ -6378,6 +6544,15 @@ typedef Lib3MFResult (*PLib3MFModel_AddMeshObjectPtr) (Lib3MF_Model pModel, Lib3 */ typedef Lib3MFResult (*PLib3MFModel_AddComponentsObjectPtr) (Lib3MF_Model pModel, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* adds an empty boolean object to the model. +* +* @param[in] pModel - Model instance. +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +typedef Lib3MFResult (*PLib3MFModel_AddBooleanObjectPtr) (Lib3MF_Model pModel, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * creates a new model slicestack by its id * @@ -6928,6 +7103,7 @@ typedef struct { PLib3MFObjectIterator_GetCurrentObjectPtr m_ObjectIterator_GetCurrentObject; PLib3MFMeshObjectIterator_GetCurrentMeshObjectPtr m_MeshObjectIterator_GetCurrentMeshObject; PLib3MFComponentsObjectIterator_GetCurrentComponentsObjectPtr m_ComponentsObjectIterator_GetCurrentComponentsObject; + PLib3MFBooleanObjectIterator_GetCurrentBooleanObjectPtr m_BooleanObjectIterator_GetCurrentBooleanObject; PLib3MFTexture2DIterator_GetCurrentTexture2DPtr m_Texture2DIterator_GetCurrentTexture2D; PLib3MFBaseMaterialGroupIterator_GetCurrentBaseMaterialGroupPtr m_BaseMaterialGroupIterator_GetCurrentBaseMaterialGroup; PLib3MFColorGroupIterator_GetCurrentColorGroupPtr m_ColorGroupIterator_GetCurrentColorGroup; @@ -6976,6 +7152,7 @@ typedef struct { PLib3MFObject_IsMeshObjectPtr m_Object_IsMeshObject; PLib3MFObject_IsComponentsObjectPtr m_Object_IsComponentsObject; PLib3MFObject_IsLevelSetObjectPtr m_Object_IsLevelSetObject; + PLib3MFObject_IsBooleanObjectPtr m_Object_IsBooleanObject; PLib3MFObject_IsValidPtr m_Object_IsValid; PLib3MFObject_SetAttachmentAsThumbnailPtr m_Object_SetAttachmentAsThumbnail; PLib3MFObject_GetThumbnailAttachmentPtr m_Object_GetThumbnailAttachment; @@ -7033,6 +7210,19 @@ typedef struct { PLib3MFLevelSet_GetMeshPtr m_LevelSet_GetMesh; PLib3MFLevelSet_GetVolumeDataPtr m_LevelSet_GetVolumeData; PLib3MFLevelSet_SetVolumeDataPtr m_LevelSet_SetVolumeData; + PLib3MFBooleanObject_SetBaseObjectPtr m_BooleanObject_SetBaseObject; + PLib3MFBooleanObject_GetBaseObjectPtr m_BooleanObject_GetBaseObject; + PLib3MFBooleanObject_SetBaseTransformPtr m_BooleanObject_SetBaseTransform; + PLib3MFBooleanObject_GetBaseTransformPtr m_BooleanObject_GetBaseTransform; + PLib3MFBooleanObject_SetOperationPtr m_BooleanObject_SetOperation; + PLib3MFBooleanObject_GetOperationPtr m_BooleanObject_GetOperation; + PLib3MFBooleanObject_SetCSGModeEnabledPtr m_BooleanObject_SetCSGModeEnabled; + PLib3MFBooleanObject_GetCSGModeEnabledPtr m_BooleanObject_GetCSGModeEnabled; + PLib3MFBooleanObject_SetExtractionGridResolutionPtr m_BooleanObject_SetExtractionGridResolution; + PLib3MFBooleanObject_GetExtractionGridResolutionPtr m_BooleanObject_GetExtractionGridResolution; + PLib3MFBooleanObject_GetOperandCountPtr m_BooleanObject_GetOperandCount; + PLib3MFBooleanObject_AddOperandPtr m_BooleanObject_AddOperand; + PLib3MFBooleanObject_GetOperandPtr m_BooleanObject_GetOperand; PLib3MFBeamLattice_GetMinLengthPtr m_BeamLattice_GetMinLength; PLib3MFBeamLattice_SetMinLengthPtr m_BeamLattice_SetMinLength; PLib3MFBeamLattice_GetClippingPtr m_BeamLattice_GetClipping; @@ -7458,6 +7648,7 @@ typedef struct { PLib3MFModel_GetMultiPropertyGroupByIDPtr m_Model_GetMultiPropertyGroupByID; PLib3MFModel_GetMeshObjectByIDPtr m_Model_GetMeshObjectByID; PLib3MFModel_GetComponentsObjectByIDPtr m_Model_GetComponentsObjectByID; + PLib3MFModel_GetBooleanObjectByIDPtr m_Model_GetBooleanObjectByID; PLib3MFModel_GetColorGroupByIDPtr m_Model_GetColorGroupByID; PLib3MFModel_GetSliceStackByIDPtr m_Model_GetSliceStackByID; PLib3MFModel_GetLevelSetByIDPtr m_Model_GetLevelSetByID; @@ -7469,6 +7660,7 @@ typedef struct { PLib3MFModel_GetObjectsPtr m_Model_GetObjects; PLib3MFModel_GetMeshObjectsPtr m_Model_GetMeshObjects; PLib3MFModel_GetComponentsObjectsPtr m_Model_GetComponentsObjects; + PLib3MFModel_GetBooleanObjectsPtr m_Model_GetBooleanObjects; PLib3MFModel_GetTexture2DsPtr m_Model_GetTexture2Ds; PLib3MFModel_GetBaseMaterialGroupsPtr m_Model_GetBaseMaterialGroups; PLib3MFModel_GetColorGroupsPtr m_Model_GetColorGroups; @@ -7481,6 +7673,7 @@ typedef struct { PLib3MFModel_MergeFromModelPtr m_Model_MergeFromModel; PLib3MFModel_AddMeshObjectPtr m_Model_AddMeshObject; PLib3MFModel_AddComponentsObjectPtr m_Model_AddComponentsObject; + PLib3MFModel_AddBooleanObjectPtr m_Model_AddBooleanObject; PLib3MFModel_AddSliceStackPtr m_Model_AddSliceStack; PLib3MFModel_AddTexture2DFromAttachmentPtr m_Model_AddTexture2DFromAttachment; PLib3MFModel_AddBaseMaterialGroupPtr m_Model_AddBaseMaterialGroup; diff --git a/Autogenerated/Bindings/NodeJS/lib3mf_nodeaddon.cc b/Autogenerated/Bindings/NodeJS/lib3mf_nodeaddon.cc index b1b71d3fc..9bf593ea5 100644 --- a/Autogenerated/Bindings/NodeJS/lib3mf_nodeaddon.cc +++ b/Autogenerated/Bindings/NodeJS/lib3mf_nodeaddon.cc @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++ Implementation file for the Node addon class of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -58,6 +58,7 @@ void InitAll(v8::Local exports, v8::Local module) CLib3MFObjectIterator::Init(); CLib3MFMeshObjectIterator::Init(); CLib3MFComponentsObjectIterator::Init(); + CLib3MFBooleanObjectIterator::Init(); CLib3MFTexture2DIterator::Init(); CLib3MFBaseMaterialGroupIterator::Init(); CLib3MFColorGroupIterator::Init(); @@ -73,6 +74,7 @@ void InitAll(v8::Local exports, v8::Local module) CLib3MFObject::Init(); CLib3MFMeshObject::Init(); CLib3MFLevelSet::Init(); + CLib3MFBooleanObject::Init(); CLib3MFBeamLattice::Init(); CLib3MFFunctionReference::Init(); CLib3MFVolumeDataColor::Init(); diff --git a/Autogenerated/Bindings/NodeJS/lib3mf_nodewrapper.cc b/Autogenerated/Bindings/NodeJS/lib3mf_nodewrapper.cc index 3bda55ccc..0abfe25f3 100644 --- a/Autogenerated/Bindings/NodeJS/lib3mf_nodewrapper.cc +++ b/Autogenerated/Bindings/NodeJS/lib3mf_nodewrapper.cc @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++ Implementation file for the Node wrapper class of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -51,6 +51,7 @@ Persistent CLib3MFSliceStackIterator::constructor; Persistent CLib3MFObjectIterator::constructor; Persistent CLib3MFMeshObjectIterator::constructor; Persistent CLib3MFComponentsObjectIterator::constructor; +Persistent CLib3MFBooleanObjectIterator::constructor; Persistent CLib3MFTexture2DIterator::constructor; Persistent CLib3MFBaseMaterialGroupIterator::constructor; Persistent CLib3MFColorGroupIterator::constructor; @@ -66,6 +67,7 @@ Persistent CLib3MFTriangleSet::constructor; Persistent CLib3MFObject::constructor; Persistent CLib3MFMeshObject::constructor; Persistent CLib3MFLevelSet::constructor; +Persistent CLib3MFBooleanObject::constructor; Persistent CLib3MFBeamLattice::constructor; Persistent CLib3MFFunctionReference::constructor; Persistent CLib3MFVolumeDataColor::constructor; @@ -3011,6 +3013,85 @@ void CLib3MFComponentsObjectIterator::GetCurrentComponentsObject(const FunctionC } } +/************************************************************************************************************************* + Class CLib3MFBooleanObjectIterator Implementation +**************************************************************************************************************************/ + +CLib3MFBooleanObjectIterator::CLib3MFBooleanObjectIterator() + : CLib3MFBaseClass() +{ +} + +CLib3MFBooleanObjectIterator::~CLib3MFBooleanObjectIterator() +{ +} + +void CLib3MFBooleanObjectIterator::Init() +{ + Isolate* isolate = Isolate::GetCurrent(); + + // Prepare constructor template + Local tpl = FunctionTemplate::New(isolate, New); + tpl->SetClassName(String::NewFromUtf8(isolate, "Lib3MFBooleanObjectIterator")); + tpl->InstanceTemplate()->SetInternalFieldCount(NODEWRAPPER_FIELDCOUNT); + + // Prototype + NODE_SET_PROTOTYPE_METHOD(tpl, "GetCurrentBooleanObject", GetCurrentBooleanObject); + constructor.Reset(isolate, tpl->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()); + +} + +void CLib3MFBooleanObjectIterator::New(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.IsConstructCall()) { + CLib3MFBaseClass * holderObj = ObjectWrap::Unwrap(args.Holder()); + CLib3MFBooleanObjectIterator * booleanobjectiteratorInstance = new CLib3MFBooleanObjectIterator(); + booleanobjectiteratorInstance->Wrap(args.This()); + args.GetReturnValue().Set(args.This()); + } else { + RaiseError(isolate, "Lib3MFBooleanObjectIterator: Invalid call to Constructor"); + } +} + +Local CLib3MFBooleanObjectIterator::NewInstance(Local pParent, Lib3MFHandle pHandle) +{ + Isolate* isolate = Isolate::GetCurrent(); + HandleScope scope(isolate); + Local cons = Local::New(isolate, constructor); + Local instance; + if (cons->NewInstance(isolate->GetCurrentContext()).ToLocal(&instance)) { + instance->SetInternalField(NODEWRAPPER_TABLEINDEX, External::New(isolate, CLib3MFBaseClass::getDynamicWrapperTable(pParent))); + instance->SetInternalField(NODEWRAPPER_HANDLEINDEX, External::New(isolate, pHandle)); + } + return instance; +} + + +void CLib3MFBooleanObjectIterator::GetCurrentBooleanObject(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + Lib3MFHandle hReturnResource = nullptr; + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method GetCurrentBooleanObject."); + if (wrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObjectIterator::GetCurrentBooleanObject."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObjectIterator_GetCurrentBooleanObject(instanceHandle, &hReturnResource); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + Local instanceObjResource = CLib3MFBooleanObject::NewInstance(args.Holder(), hReturnResource); + args.GetReturnValue().Set(instanceObjResource); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + /************************************************************************************************************************* Class CLib3MFTexture2DIterator Implementation **************************************************************************************************************************/ @@ -4723,6 +4804,7 @@ void CLib3MFObject::Init() NODE_SET_PROTOTYPE_METHOD(tpl, "IsMeshObject", IsMeshObject); NODE_SET_PROTOTYPE_METHOD(tpl, "IsComponentsObject", IsComponentsObject); NODE_SET_PROTOTYPE_METHOD(tpl, "IsLevelSetObject", IsLevelSetObject); + NODE_SET_PROTOTYPE_METHOD(tpl, "IsBooleanObject", IsBooleanObject); NODE_SET_PROTOTYPE_METHOD(tpl, "IsValid", IsValid); NODE_SET_PROTOTYPE_METHOD(tpl, "SetAttachmentAsThumbnail", SetAttachmentAsThumbnail); NODE_SET_PROTOTYPE_METHOD(tpl, "GetThumbnailAttachment", GetThumbnailAttachment); @@ -4986,6 +5068,28 @@ void CLib3MFObject::IsLevelSetObject(const FunctionCallbackInfo& args) } +void CLib3MFObject::IsBooleanObject(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + bool bReturnIsBooleanObject = false; + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method IsBooleanObject."); + if (wrapperTable->m_Object_IsBooleanObject == nullptr) + throw std::runtime_error("Could not call Lib3MF method Object::IsBooleanObject."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_Object_IsBooleanObject(instanceHandle, &bReturnIsBooleanObject); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + args.GetReturnValue().Set(Boolean::New(isolate, bReturnIsBooleanObject)); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + void CLib3MFObject::IsValid(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); @@ -6531,6 +6635,398 @@ void CLib3MFLevelSet::SetVolumeData(const FunctionCallbackInfo& args) } } +/************************************************************************************************************************* + Class CLib3MFBooleanObject Implementation +**************************************************************************************************************************/ + +CLib3MFBooleanObject::CLib3MFBooleanObject() + : CLib3MFBaseClass() +{ +} + +CLib3MFBooleanObject::~CLib3MFBooleanObject() +{ +} + +void CLib3MFBooleanObject::Init() +{ + Isolate* isolate = Isolate::GetCurrent(); + + // Prepare constructor template + Local tpl = FunctionTemplate::New(isolate, New); + tpl->SetClassName(String::NewFromUtf8(isolate, "Lib3MFBooleanObject")); + tpl->InstanceTemplate()->SetInternalFieldCount(NODEWRAPPER_FIELDCOUNT); + + // Prototype + NODE_SET_PROTOTYPE_METHOD(tpl, "SetBaseObject", SetBaseObject); + NODE_SET_PROTOTYPE_METHOD(tpl, "GetBaseObject", GetBaseObject); + NODE_SET_PROTOTYPE_METHOD(tpl, "SetBaseTransform", SetBaseTransform); + NODE_SET_PROTOTYPE_METHOD(tpl, "GetBaseTransform", GetBaseTransform); + NODE_SET_PROTOTYPE_METHOD(tpl, "SetOperation", SetOperation); + NODE_SET_PROTOTYPE_METHOD(tpl, "GetOperation", GetOperation); + NODE_SET_PROTOTYPE_METHOD(tpl, "SetCSGModeEnabled", SetCSGModeEnabled); + NODE_SET_PROTOTYPE_METHOD(tpl, "GetCSGModeEnabled", GetCSGModeEnabled); + NODE_SET_PROTOTYPE_METHOD(tpl, "SetExtractionGridResolution", SetExtractionGridResolution); + NODE_SET_PROTOTYPE_METHOD(tpl, "GetExtractionGridResolution", GetExtractionGridResolution); + NODE_SET_PROTOTYPE_METHOD(tpl, "GetOperandCount", GetOperandCount); + NODE_SET_PROTOTYPE_METHOD(tpl, "AddOperand", AddOperand); + NODE_SET_PROTOTYPE_METHOD(tpl, "GetOperand", GetOperand); + constructor.Reset(isolate, tpl->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()); + +} + +void CLib3MFBooleanObject::New(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + + if (args.IsConstructCall()) { + CLib3MFBaseClass * holderObj = ObjectWrap::Unwrap(args.Holder()); + CLib3MFBooleanObject * booleanobjectInstance = new CLib3MFBooleanObject(); + booleanobjectInstance->Wrap(args.This()); + args.GetReturnValue().Set(args.This()); + } else { + RaiseError(isolate, "Lib3MFBooleanObject: Invalid call to Constructor"); + } +} + +Local CLib3MFBooleanObject::NewInstance(Local pParent, Lib3MFHandle pHandle) +{ + Isolate* isolate = Isolate::GetCurrent(); + HandleScope scope(isolate); + Local cons = Local::New(isolate, constructor); + Local instance; + if (cons->NewInstance(isolate->GetCurrentContext()).ToLocal(&instance)) { + instance->SetInternalField(NODEWRAPPER_TABLEINDEX, External::New(isolate, CLib3MFBaseClass::getDynamicWrapperTable(pParent))); + instance->SetInternalField(NODEWRAPPER_HANDLEINDEX, External::New(isolate, pHandle)); + } + return instance; +} + + +void CLib3MFBooleanObject::SetBaseObject(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + if (!args[0]->IsObject()) { + throw std::runtime_error("Expected class parameter 0 (BaseObject)"); + } + if (!args[1]->IsObject()) { + throw std::runtime_error("Expected struct parameter 1 (Transform)"); + } + Local objBaseObject = args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked(); + CLib3MFObject * instanceBaseObject = ObjectWrap::Unwrap(objBaseObject); + if (instanceBaseObject == nullptr) + throw std::runtime_error("Invalid Object parameter 0 (BaseObject)"); + Lib3MFHandle hBaseObject = instanceBaseObject->getHandle( objBaseObject ); + sLib3MFTransform sTransform = convertObjectToLib3MFTransform(isolate, args[1]); + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method SetBaseObject."); + if (wrapperTable->m_BooleanObject_SetBaseObject == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObject::SetBaseObject."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObject_SetBaseObject(instanceHandle, hBaseObject, &sTransform); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + +void CLib3MFBooleanObject::GetBaseObject(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + Lib3MFHandle hReturnBaseObject = nullptr; + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method GetBaseObject."); + if (wrapperTable->m_BooleanObject_GetBaseObject == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObject::GetBaseObject."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObject_GetBaseObject(instanceHandle, &hReturnBaseObject); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + Local instanceObjBaseObject = CLib3MFObject::NewInstance(args.Holder(), hReturnBaseObject); + args.GetReturnValue().Set(instanceObjBaseObject); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + +void CLib3MFBooleanObject::SetBaseTransform(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + if (!args[0]->IsObject()) { + throw std::runtime_error("Expected struct parameter 0 (Transform)"); + } + sLib3MFTransform sTransform = convertObjectToLib3MFTransform(isolate, args[0]); + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method SetBaseTransform."); + if (wrapperTable->m_BooleanObject_SetBaseTransform == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObject::SetBaseTransform."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObject_SetBaseTransform(instanceHandle, &sTransform); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + +void CLib3MFBooleanObject::GetBaseTransform(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + sLib3MFTransform sReturnTransform; + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method GetBaseTransform."); + if (wrapperTable->m_BooleanObject_GetBaseTransform == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObject::GetBaseTransform."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObject_GetBaseTransform(instanceHandle, &sReturnTransform); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + args.GetReturnValue().Set(convertLib3MFTransformToObject(isolate, sReturnTransform)); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + +void CLib3MFBooleanObject::SetOperation(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + if (!args[0]->IsUint32()) { + throw std::runtime_error("Expected enum parameter 0 (Operation)"); + } + unsigned int eOperation = (unsigned int) args[0]->IntegerValue(isolate->GetCurrentContext()).ToChecked(); + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method SetOperation."); + if (wrapperTable->m_BooleanObject_SetOperation == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObject::SetOperation."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObject_SetOperation(instanceHandle, (eLib3MFBooleanOperation) eOperation); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + +void CLib3MFBooleanObject::GetOperation(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + eLib3MFBooleanOperation eReturnOperation; + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method GetOperation."); + if (wrapperTable->m_BooleanObject_GetOperation == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObject::GetOperation."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObject_GetOperation(instanceHandle, &eReturnOperation); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + args.GetReturnValue().Set(Integer::New(isolate, (int)eReturnOperation)); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + +void CLib3MFBooleanObject::SetCSGModeEnabled(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + if (!args[0]->IsBoolean()) { + throw std::runtime_error("Expected bool parameter 0 (CSGModeEnabled)"); + } + bool bCSGModeEnabled = args[0]->BooleanValue(isolate->GetCurrentContext()).ToChecked(); + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method SetCSGModeEnabled."); + if (wrapperTable->m_BooleanObject_SetCSGModeEnabled == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObject::SetCSGModeEnabled."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObject_SetCSGModeEnabled(instanceHandle, bCSGModeEnabled); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + +void CLib3MFBooleanObject::GetCSGModeEnabled(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + bool bReturnCSGModeEnabled = false; + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method GetCSGModeEnabled."); + if (wrapperTable->m_BooleanObject_GetCSGModeEnabled == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObject::GetCSGModeEnabled."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObject_GetCSGModeEnabled(instanceHandle, &bReturnCSGModeEnabled); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + args.GetReturnValue().Set(Boolean::New(isolate, bReturnCSGModeEnabled)); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + +void CLib3MFBooleanObject::SetExtractionGridResolution(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + if (!args[0]->IsUint32()) { + throw std::runtime_error("Expected uint32 parameter 0 (GridResolution)"); + } + unsigned int nGridResolution = (unsigned int) args[0]->IntegerValue(isolate->GetCurrentContext()).ToChecked(); + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method SetExtractionGridResolution."); + if (wrapperTable->m_BooleanObject_SetExtractionGridResolution == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObject::SetExtractionGridResolution."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObject_SetExtractionGridResolution(instanceHandle, nGridResolution); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + +void CLib3MFBooleanObject::GetExtractionGridResolution(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + unsigned int nReturnGridResolution = 0; + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method GetExtractionGridResolution."); + if (wrapperTable->m_BooleanObject_GetExtractionGridResolution == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObject::GetExtractionGridResolution."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObject_GetExtractionGridResolution(instanceHandle, &nReturnGridResolution); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + args.GetReturnValue().Set(Integer::NewFromUnsigned(isolate, nReturnGridResolution)); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + +void CLib3MFBooleanObject::GetOperandCount(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + unsigned int nReturnCount = 0; + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method GetOperandCount."); + if (wrapperTable->m_BooleanObject_GetOperandCount == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObject::GetOperandCount."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObject_GetOperandCount(instanceHandle, &nReturnCount); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + args.GetReturnValue().Set(Integer::NewFromUnsigned(isolate, nReturnCount)); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + +void CLib3MFBooleanObject::AddOperand(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + if (!args[0]->IsObject()) { + throw std::runtime_error("Expected class parameter 0 (OperandObject)"); + } + if (!args[1]->IsObject()) { + throw std::runtime_error("Expected struct parameter 1 (Transform)"); + } + Local objOperandObject = args[0]->ToObject(isolate->GetCurrentContext()).ToLocalChecked(); + CLib3MFMeshObject * instanceOperandObject = ObjectWrap::Unwrap(objOperandObject); + if (instanceOperandObject == nullptr) + throw std::runtime_error("Invalid Object parameter 0 (OperandObject)"); + Lib3MFHandle hOperandObject = instanceOperandObject->getHandle( objOperandObject ); + sLib3MFTransform sTransform = convertObjectToLib3MFTransform(isolate, args[1]); + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method AddOperand."); + if (wrapperTable->m_BooleanObject_AddOperand == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObject::AddOperand."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObject_AddOperand(instanceHandle, hOperandObject, &sTransform); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + +void CLib3MFBooleanObject::GetOperand(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + if (!args[0]->IsUint32()) { + throw std::runtime_error("Expected uint32 parameter 0 (Index)"); + } + Local outObject = Object::New(isolate); + unsigned int nIndex = (unsigned int) args[0]->IntegerValue(isolate->GetCurrentContext()).ToChecked(); + Lib3MFHandle hReturnOperandObject = nullptr; + sLib3MFTransform sReturnTransform; + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method GetOperand."); + if (wrapperTable->m_BooleanObject_GetOperand == nullptr) + throw std::runtime_error("Could not call Lib3MF method BooleanObject::GetOperand."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_BooleanObject_GetOperand(instanceHandle, nIndex, &hReturnOperandObject, &sReturnTransform); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + Local instanceObjOperandObject = CLib3MFMeshObject::NewInstance(args.Holder(), hReturnOperandObject); + outObject->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "OperandObject"), instanceObjOperandObject); + outObject->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "Transform"), convertLib3MFTransformToObject(isolate, sReturnTransform)); + args.GetReturnValue().Set(outObject); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + /************************************************************************************************************************* Class CLib3MFBeamLattice Implementation **************************************************************************************************************************/ @@ -22966,6 +23462,7 @@ void CLib3MFModel::Init() NODE_SET_PROTOTYPE_METHOD(tpl, "GetMultiPropertyGroupByID", GetMultiPropertyGroupByID); NODE_SET_PROTOTYPE_METHOD(tpl, "GetMeshObjectByID", GetMeshObjectByID); NODE_SET_PROTOTYPE_METHOD(tpl, "GetComponentsObjectByID", GetComponentsObjectByID); + NODE_SET_PROTOTYPE_METHOD(tpl, "GetBooleanObjectByID", GetBooleanObjectByID); NODE_SET_PROTOTYPE_METHOD(tpl, "GetColorGroupByID", GetColorGroupByID); NODE_SET_PROTOTYPE_METHOD(tpl, "GetSliceStackByID", GetSliceStackByID); NODE_SET_PROTOTYPE_METHOD(tpl, "GetLevelSetByID", GetLevelSetByID); @@ -22977,6 +23474,7 @@ void CLib3MFModel::Init() NODE_SET_PROTOTYPE_METHOD(tpl, "GetObjects", GetObjects); NODE_SET_PROTOTYPE_METHOD(tpl, "GetMeshObjects", GetMeshObjects); NODE_SET_PROTOTYPE_METHOD(tpl, "GetComponentsObjects", GetComponentsObjects); + NODE_SET_PROTOTYPE_METHOD(tpl, "GetBooleanObjects", GetBooleanObjects); NODE_SET_PROTOTYPE_METHOD(tpl, "GetTexture2Ds", GetTexture2Ds); NODE_SET_PROTOTYPE_METHOD(tpl, "GetBaseMaterialGroups", GetBaseMaterialGroups); NODE_SET_PROTOTYPE_METHOD(tpl, "GetColorGroups", GetColorGroups); @@ -22989,6 +23487,7 @@ void CLib3MFModel::Init() NODE_SET_PROTOTYPE_METHOD(tpl, "MergeFromModel", MergeFromModel); NODE_SET_PROTOTYPE_METHOD(tpl, "AddMeshObject", AddMeshObject); NODE_SET_PROTOTYPE_METHOD(tpl, "AddComponentsObject", AddComponentsObject); + NODE_SET_PROTOTYPE_METHOD(tpl, "AddBooleanObject", AddBooleanObject); NODE_SET_PROTOTYPE_METHOD(tpl, "AddSliceStack", AddSliceStack); NODE_SET_PROTOTYPE_METHOD(tpl, "AddTexture2DFromAttachment", AddTexture2DFromAttachment); NODE_SET_PROTOTYPE_METHOD(tpl, "AddBaseMaterialGroup", AddBaseMaterialGroup); @@ -23501,6 +24000,33 @@ void CLib3MFModel::GetComponentsObjectByID(const FunctionCallbackInfo& ar } +void CLib3MFModel::GetBooleanObjectByID(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + if (!args[0]->IsUint32()) { + throw std::runtime_error("Expected uint32 parameter 0 (UniqueResourceID)"); + } + unsigned int nUniqueResourceID = (unsigned int) args[0]->IntegerValue(isolate->GetCurrentContext()).ToChecked(); + Lib3MFHandle hReturnBooleanObjectInstance = nullptr; + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method GetBooleanObjectByID."); + if (wrapperTable->m_Model_GetBooleanObjectByID == nullptr) + throw std::runtime_error("Could not call Lib3MF method Model::GetBooleanObjectByID."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_Model_GetBooleanObjectByID(instanceHandle, nUniqueResourceID, &hReturnBooleanObjectInstance); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + Local instanceObjBooleanObjectInstance = CLib3MFBooleanObject::NewInstance(args.Holder(), hReturnBooleanObjectInstance); + args.GetReturnValue().Set(instanceObjBooleanObjectInstance); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + void CLib3MFModel::GetColorGroupByID(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); @@ -23775,6 +24301,29 @@ void CLib3MFModel::GetComponentsObjects(const FunctionCallbackInfo& args) } +void CLib3MFModel::GetBooleanObjects(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + Lib3MFHandle hReturnResourceIterator = nullptr; + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method GetBooleanObjects."); + if (wrapperTable->m_Model_GetBooleanObjects == nullptr) + throw std::runtime_error("Could not call Lib3MF method Model::GetBooleanObjects."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_Model_GetBooleanObjects(instanceHandle, &hReturnResourceIterator); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + Local instanceObjResourceIterator = CLib3MFBooleanObjectIterator::NewInstance(args.Holder(), hReturnResourceIterator); + args.GetReturnValue().Set(instanceObjResourceIterator); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + void CLib3MFModel::GetTexture2Ds(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); @@ -24056,6 +24605,29 @@ void CLib3MFModel::AddComponentsObject(const FunctionCallbackInfo& args) } +void CLib3MFModel::AddBooleanObject(const FunctionCallbackInfo& args) +{ + Isolate* isolate = args.GetIsolate(); + HandleScope scope(isolate); + try { + Lib3MFHandle hReturnBooleanObjectInstance = nullptr; + sLib3MFDynamicWrapperTable * wrapperTable = CLib3MFBaseClass::getDynamicWrapperTable(args.Holder()); + if (wrapperTable == nullptr) + throw std::runtime_error("Could not get wrapper table for Lib3MF method AddBooleanObject."); + if (wrapperTable->m_Model_AddBooleanObject == nullptr) + throw std::runtime_error("Could not call Lib3MF method Model::AddBooleanObject."); + Lib3MFHandle instanceHandle = CLib3MFBaseClass::getHandle(args.Holder()); + Lib3MFResult errorCode = wrapperTable->m_Model_AddBooleanObject(instanceHandle, &hReturnBooleanObjectInstance); + CheckError(isolate, wrapperTable, instanceHandle, errorCode); + Local instanceObjBooleanObjectInstance = CLib3MFBooleanObject::NewInstance(args.Holder(), hReturnBooleanObjectInstance); + args.GetReturnValue().Set(instanceObjBooleanObjectInstance); + + } catch (std::exception & E) { + RaiseError(isolate, E.what()); + } +} + + void CLib3MFModel::AddSliceStack(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); @@ -24986,6 +25558,9 @@ void CLib3MFWrapper::New(const FunctionCallbackInfo& args) newObject->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "eObjectType_Support"), Integer::New(isolate, 2)); newObject->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "eObjectType_SolidSupport"), Integer::New(isolate, 3)); newObject->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "eObjectType_Surface"), Integer::New(isolate, 4)); + newObject->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "eBooleanOperation_Union"), Integer::New(isolate, 0)); + newObject->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "eBooleanOperation_Difference"), Integer::New(isolate, 1)); + newObject->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "eBooleanOperation_Intersection"), Integer::New(isolate, 2)); newObject->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "eTextureType_Unknown"), Integer::New(isolate, 0)); newObject->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "eTextureType_PNG"), Integer::New(isolate, 1)); newObject->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "eTextureType_JPEG"), Integer::New(isolate, 2)); diff --git a/Autogenerated/Bindings/NodeJS/lib3mf_nodewrapper.h b/Autogenerated/Bindings/NodeJS/lib3mf_nodewrapper.h index a65c4134b..4e2abed2c 100644 --- a/Autogenerated/Bindings/NodeJS/lib3mf_nodewrapper.h +++ b/Autogenerated/Bindings/NodeJS/lib3mf_nodewrapper.h @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++ Header file for the Node wrapper class of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -281,6 +281,24 @@ class CLib3MFComponentsObjectIterator : public CLib3MFBaseClass { }; +/************************************************************************************************************************* + Class CLib3MFBooleanObjectIterator +**************************************************************************************************************************/ +class CLib3MFBooleanObjectIterator : public CLib3MFBaseClass { +private: + static void New(const v8::FunctionCallbackInfo& args); + static v8::Persistent constructor; + static void GetCurrentBooleanObject(const v8::FunctionCallbackInfo& args); + +public: + CLib3MFBooleanObjectIterator(); + ~CLib3MFBooleanObjectIterator(); + + static void Init(); + static v8::Local NewInstance(v8::Local, Lib3MFHandle pHandle); + +}; + /************************************************************************************************************************* Class CLib3MFTexture2DIterator **************************************************************************************************************************/ @@ -540,6 +558,7 @@ class CLib3MFObject : public CLib3MFBaseClass { static void IsMeshObject(const v8::FunctionCallbackInfo& args); static void IsComponentsObject(const v8::FunctionCallbackInfo& args); static void IsLevelSetObject(const v8::FunctionCallbackInfo& args); + static void IsBooleanObject(const v8::FunctionCallbackInfo& args); static void IsValid(const v8::FunctionCallbackInfo& args); static void SetAttachmentAsThumbnail(const v8::FunctionCallbackInfo& args); static void GetThumbnailAttachment(const v8::FunctionCallbackInfo& args); @@ -641,6 +660,36 @@ class CLib3MFLevelSet : public CLib3MFBaseClass { }; +/************************************************************************************************************************* + Class CLib3MFBooleanObject +**************************************************************************************************************************/ +class CLib3MFBooleanObject : public CLib3MFBaseClass { +private: + static void New(const v8::FunctionCallbackInfo& args); + static v8::Persistent constructor; + static void SetBaseObject(const v8::FunctionCallbackInfo& args); + static void GetBaseObject(const v8::FunctionCallbackInfo& args); + static void SetBaseTransform(const v8::FunctionCallbackInfo& args); + static void GetBaseTransform(const v8::FunctionCallbackInfo& args); + static void SetOperation(const v8::FunctionCallbackInfo& args); + static void GetOperation(const v8::FunctionCallbackInfo& args); + static void SetCSGModeEnabled(const v8::FunctionCallbackInfo& args); + static void GetCSGModeEnabled(const v8::FunctionCallbackInfo& args); + static void SetExtractionGridResolution(const v8::FunctionCallbackInfo& args); + static void GetExtractionGridResolution(const v8::FunctionCallbackInfo& args); + static void GetOperandCount(const v8::FunctionCallbackInfo& args); + static void AddOperand(const v8::FunctionCallbackInfo& args); + static void GetOperand(const v8::FunctionCallbackInfo& args); + +public: + CLib3MFBooleanObject(); + ~CLib3MFBooleanObject(); + + static void Init(); + static v8::Local NewInstance(v8::Local, Lib3MFHandle pHandle); + +}; + /************************************************************************************************************************* Class CLib3MFBeamLattice **************************************************************************************************************************/ @@ -2654,6 +2703,7 @@ class CLib3MFModel : public CLib3MFBaseClass { static void GetMultiPropertyGroupByID(const v8::FunctionCallbackInfo& args); static void GetMeshObjectByID(const v8::FunctionCallbackInfo& args); static void GetComponentsObjectByID(const v8::FunctionCallbackInfo& args); + static void GetBooleanObjectByID(const v8::FunctionCallbackInfo& args); static void GetColorGroupByID(const v8::FunctionCallbackInfo& args); static void GetSliceStackByID(const v8::FunctionCallbackInfo& args); static void GetLevelSetByID(const v8::FunctionCallbackInfo& args); @@ -2665,6 +2715,7 @@ class CLib3MFModel : public CLib3MFBaseClass { static void GetObjects(const v8::FunctionCallbackInfo& args); static void GetMeshObjects(const v8::FunctionCallbackInfo& args); static void GetComponentsObjects(const v8::FunctionCallbackInfo& args); + static void GetBooleanObjects(const v8::FunctionCallbackInfo& args); static void GetTexture2Ds(const v8::FunctionCallbackInfo& args); static void GetBaseMaterialGroups(const v8::FunctionCallbackInfo& args); static void GetColorGroups(const v8::FunctionCallbackInfo& args); @@ -2677,6 +2728,7 @@ class CLib3MFModel : public CLib3MFBaseClass { static void MergeFromModel(const v8::FunctionCallbackInfo& args); static void AddMeshObject(const v8::FunctionCallbackInfo& args); static void AddComponentsObject(const v8::FunctionCallbackInfo& args); + static void AddBooleanObject(const v8::FunctionCallbackInfo& args); static void AddSliceStack(const v8::FunctionCallbackInfo& args); static void AddTexture2DFromAttachment(const v8::FunctionCallbackInfo& args); static void AddBaseMaterialGroup(const v8::FunctionCallbackInfo& args); diff --git a/Autogenerated/Bindings/NodeJS/lib3mf_types.h b/Autogenerated/Bindings/NodeJS/lib3mf_types.h index d87847a5b..b9e4aec27 100644 --- a/Autogenerated/Bindings/NodeJS/lib3mf_types.h +++ b/Autogenerated/Bindings/NodeJS/lib3mf_types.h @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated plain C Header file with basic types in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -84,7 +84,7 @@ typedef void * Lib3MF_pvoid; **************************************************************************************************************************/ #define LIB3MF_VERSION_MAJOR 2 -#define LIB3MF_VERSION_MINOR 5 +#define LIB3MF_VERSION_MINOR 6 #define LIB3MF_VERSION_MICRO 0 #define LIB3MF_VERSION_PRERELEASEINFO "" #define LIB3MF_VERSION_BUILDINFO "" @@ -220,6 +220,7 @@ typedef Lib3MFHandle Lib3MF_SliceStackIterator; typedef Lib3MFHandle Lib3MF_ObjectIterator; typedef Lib3MFHandle Lib3MF_MeshObjectIterator; typedef Lib3MFHandle Lib3MF_ComponentsObjectIterator; +typedef Lib3MFHandle Lib3MF_BooleanObjectIterator; typedef Lib3MFHandle Lib3MF_Texture2DIterator; typedef Lib3MFHandle Lib3MF_BaseMaterialGroupIterator; typedef Lib3MFHandle Lib3MF_ColorGroupIterator; @@ -235,6 +236,7 @@ typedef Lib3MFHandle Lib3MF_TriangleSet; typedef Lib3MFHandle Lib3MF_Object; typedef Lib3MFHandle Lib3MF_MeshObject; typedef Lib3MFHandle Lib3MF_LevelSet; +typedef Lib3MFHandle Lib3MF_BooleanObject; typedef Lib3MFHandle Lib3MF_BeamLattice; typedef Lib3MFHandle Lib3MF_FunctionReference; typedef Lib3MFHandle Lib3MF_VolumeDataColor; @@ -365,6 +367,12 @@ typedef enum eLib3MFObjectType { eObjectTypeSurface = 4 } eLib3MFObjectType; +typedef enum eLib3MFBooleanOperation { + eBooleanOperationUnion = 0, + eBooleanOperationDifference = 1, + eBooleanOperationIntersection = 2 +} eLib3MFBooleanOperation; + typedef enum eLib3MFTextureType { eTextureTypeUnknown = 0, eTextureTypePNG = 1, @@ -585,6 +593,11 @@ typedef union { int m_code; } structEnumLib3MFObjectType; +typedef union { + eLib3MFBooleanOperation m_enum; + int m_code; +} structEnumLib3MFBooleanOperation; + typedef union { eLib3MFTextureType m_enum; int m_code; diff --git a/Autogenerated/Bindings/Pascal/Unit_Lib3MF.pas b/Autogenerated/Bindings/Pascal/Unit_Lib3MF.pas index 3c72ffd9d..18fc878e8 100644 --- a/Autogenerated/Bindings/Pascal/Unit_Lib3MF.pas +++ b/Autogenerated/Bindings/Pascal/Unit_Lib3MF.pas @@ -30,7 +30,7 @@ Abstract: This is an autogenerated Pascal Header file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 *) @@ -54,7 +54,7 @@ interface const LIB3MF_VERSION_MAJOR = 2; - LIB3MF_VERSION_MINOR = 5; + LIB3MF_VERSION_MINOR = 6; LIB3MF_VERSION_MICRO = 0; LIB3MF_VERSION_PRERELEASEINFO = ''; LIB3MF_VERSION_BUILDINFO = ''; @@ -165,6 +165,12 @@ interface eObjectTypeSurface ); + TLib3MFBooleanOperation = ( + eBooleanOperationUnion, + eBooleanOperationDifference, + eBooleanOperationIntersection + ); + TLib3MFTextureType = ( eTextureTypeUnknown, eTextureTypePNG, @@ -489,6 +495,7 @@ TLib3MFSliceStackIterator = class; TLib3MFObjectIterator = class; TLib3MFMeshObjectIterator = class; TLib3MFComponentsObjectIterator = class; + TLib3MFBooleanObjectIterator = class; TLib3MFTexture2DIterator = class; TLib3MFBaseMaterialGroupIterator = class; TLib3MFColorGroupIterator = class; @@ -504,6 +511,7 @@ TLib3MFTriangleSet = class; TLib3MFObject = class; TLib3MFMeshObject = class; TLib3MFLevelSet = class; + TLib3MFBooleanObject = class; TLib3MFBeamLattice = class; TLib3MFFunctionReference = class; TLib3MFVolumeDataColor = class; @@ -1054,6 +1062,20 @@ TLib3MFModel = class; TLib3MFComponentsObjectIterator_GetCurrentComponentsObjectFunc = function(pComponentsObjectIterator: TLib3MFHandle; out pResource: TLib3MFHandle): TLib3MFResult; cdecl; +(************************************************************************************************************************* + Function type definitions for BooleanObjectIterator +**************************************************************************************************************************) + + (** + * Returns the BooleanObject the iterator points at. + * + * @param[in] pBooleanObjectIterator - BooleanObjectIterator instance. + * @param[out] pResource - returns the BooleanObject instance. + * @return error code or 0 (success) + *) + TLib3MFBooleanObjectIterator_GetCurrentBooleanObjectFunc = function(pBooleanObjectIterator: TLib3MFHandle; out pResource: TLib3MFHandle): TLib3MFResult; cdecl; + + (************************************************************************************************************************* Function type definitions for Texture2DIterator **************************************************************************************************************************) @@ -1580,6 +1602,15 @@ TLib3MFModel = class; *) TLib3MFObject_IsLevelSetObjectFunc = function(pObject: TLib3MFHandle; out pIsLevelSetObject: Byte): TLib3MFResult; cdecl; + (** + * Retrieves, if an object is a boolean object + * + * @param[in] pObject - Object instance. + * @param[out] pIsBooleanObject - returns, whether the object is a boolean object + * @return error code or 0 (success) + *) + TLib3MFObject_IsBooleanObjectFunc = function(pObject: TLib3MFHandle; out pIsBooleanObject: Byte): TLib3MFResult; cdecl; + (** * Retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. * @@ -2133,6 +2164,132 @@ TLib3MFModel = class; TLib3MFLevelSet_SetVolumeDataFunc = function(pLevelSet: TLib3MFHandle; const pTheVolumeData: TLib3MFHandle): TLib3MFResult; cdecl; +(************************************************************************************************************************* + Function type definitions for BooleanObject +**************************************************************************************************************************) + + (** + * Sets the base object and transform for the boolean shape. + * + * @param[in] pBooleanObject - BooleanObject instance. + * @param[in] pBaseObject - base object of the boolean shape + * @param[in] pTransform - transform applied to the base object + * @return error code or 0 (success) + *) + TLib3MFBooleanObject_SetBaseObjectFunc = function(pBooleanObject: TLib3MFHandle; const pBaseObject: TLib3MFHandle; const pTransform: PLib3MFTransform): TLib3MFResult; cdecl; + + (** + * Returns the base object of the boolean shape. + * + * @param[in] pBooleanObject - BooleanObject instance. + * @param[out] pBaseObject - base object of the boolean shape + * @return error code or 0 (success) + *) + TLib3MFBooleanObject_GetBaseObjectFunc = function(pBooleanObject: TLib3MFHandle; out pBaseObject: TLib3MFHandle): TLib3MFResult; cdecl; + + (** + * Sets the base transform of the boolean shape. + * + * @param[in] pBooleanObject - BooleanObject instance. + * @param[in] pTransform - transform applied to the base object + * @return error code or 0 (success) + *) + TLib3MFBooleanObject_SetBaseTransformFunc = function(pBooleanObject: TLib3MFHandle; const pTransform: PLib3MFTransform): TLib3MFResult; cdecl; + + (** + * Returns the base transform of the boolean shape. + * + * @param[in] pBooleanObject - BooleanObject instance. + * @param[out] pTransform - transform applied to the base object + * @return error code or 0 (success) + *) + TLib3MFBooleanObject_GetBaseTransformFunc = function(pBooleanObject: TLib3MFHandle; pTransform: PLib3MFTransform): TLib3MFResult; cdecl; + + (** + * Sets the boolean operation used for the boolean shape. + * + * @param[in] pBooleanObject - BooleanObject instance. + * @param[in] eOperation - boolean operation used for the shape + * @return error code or 0 (success) + *) + TLib3MFBooleanObject_SetOperationFunc = function(pBooleanObject: TLib3MFHandle; const eOperation: Integer): TLib3MFResult; cdecl; + + (** + * Returns the boolean operation used for the boolean shape. + * + * @param[in] pBooleanObject - BooleanObject instance. + * @param[out] pOperation - boolean operation used for the shape + * @return error code or 0 (success) + *) + TLib3MFBooleanObject_GetOperationFunc = function(pBooleanObject: TLib3MFHandle; out pOperation: Integer): TLib3MFResult; cdecl; + + (** + * Enables or disables CSG field evaluation for boolean-to-mesh materialization. + * + * @param[in] pBooleanObject - BooleanObject instance. + * @param[in] bCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback + * @return error code or 0 (success) + *) + TLib3MFBooleanObject_SetCSGModeEnabledFunc = function(pBooleanObject: TLib3MFHandle; const bCSGModeEnabled: Byte): TLib3MFResult; cdecl; + + (** + * Returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. + * + * @param[in] pBooleanObject - BooleanObject instance. + * @param[out] pCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback + * @return error code or 0 (success) + *) + TLib3MFBooleanObject_GetCSGModeEnabledFunc = function(pBooleanObject: TLib3MFHandle; out pCSGModeEnabled: Byte): TLib3MFResult; cdecl; + + (** + * Sets the extraction grid resolution used for boolean-to-mesh materialization. + * + * @param[in] pBooleanObject - BooleanObject instance. + * @param[in] nGridResolution - extraction grid resolution for boolean surface extraction + * @return error code or 0 (success) + *) + TLib3MFBooleanObject_SetExtractionGridResolutionFunc = function(pBooleanObject: TLib3MFHandle; const nGridResolution: Cardinal): TLib3MFResult; cdecl; + + (** + * Returns the extraction grid resolution used for boolean-to-mesh materialization. + * + * @param[in] pBooleanObject - BooleanObject instance. + * @param[out] pGridResolution - extraction grid resolution for boolean surface extraction + * @return error code or 0 (success) + *) + TLib3MFBooleanObject_GetExtractionGridResolutionFunc = function(pBooleanObject: TLib3MFHandle; out pGridResolution: Cardinal): TLib3MFResult; cdecl; + + (** + * Returns the number of operands in the boolean sequence. + * + * @param[in] pBooleanObject - BooleanObject instance. + * @param[out] pCount - number of operands in the boolean sequence + * @return error code or 0 (success) + *) + TLib3MFBooleanObject_GetOperandCountFunc = function(pBooleanObject: TLib3MFHandle; out pCount: Cardinal): TLib3MFResult; cdecl; + + (** + * Adds an operand object to the boolean sequence. + * + * @param[in] pBooleanObject - BooleanObject instance. + * @param[in] pOperandObject - mesh object used as operand + * @param[in] pTransform - transform applied to the operand object + * @return error code or 0 (success) + *) + TLib3MFBooleanObject_AddOperandFunc = function(pBooleanObject: TLib3MFHandle; const pOperandObject: TLib3MFHandle; const pTransform: PLib3MFTransform): TLib3MFResult; cdecl; + + (** + * Returns one operand object and transform from the boolean sequence. + * + * @param[in] pBooleanObject - BooleanObject instance. + * @param[in] nIndex - index of the operand in the boolean sequence + * @param[out] pOperandObject - mesh object used as operand + * @param[out] pTransform - transform applied to the operand object + * @return error code or 0 (success) + *) + TLib3MFBooleanObject_GetOperandFunc = function(pBooleanObject: TLib3MFHandle; const nIndex: Cardinal; out pOperandObject: TLib3MFHandle; pTransform: PLib3MFTransform): TLib3MFResult; cdecl; + + (************************************************************************************************************************* Function type definitions for BeamLattice **************************************************************************************************************************) @@ -6843,6 +7000,16 @@ TLib3MFModel = class; *) TLib3MFModel_GetComponentsObjectByIDFunc = function(pModel: TLib3MFHandle; const nUniqueResourceID: Cardinal; out pComponentsObjectInstance: TLib3MFHandle): TLib3MFResult; cdecl; + (** + * finds a boolean object by its UniqueResourceID + * + * @param[in] pModel - Model instance. + * @param[in] nUniqueResourceID - UniqueResourceID + * @param[out] pBooleanObjectInstance - returns the boolean object instance + * @return error code or 0 (success) + *) + TLib3MFModel_GetBooleanObjectByIDFunc = function(pModel: TLib3MFHandle; const nUniqueResourceID: Cardinal; out pBooleanObjectInstance: TLib3MFHandle): TLib3MFResult; cdecl; + (** * finds a model color group by its UniqueResourceID * @@ -6948,6 +7115,15 @@ TLib3MFModel = class; *) TLib3MFModel_GetComponentsObjectsFunc = function(pModel: TLib3MFHandle; out pResourceIterator: TLib3MFHandle): TLib3MFResult; cdecl; + (** + * creates a resource iterator instance with all boolean object resources. + * + * @param[in] pModel - Model instance. + * @param[out] pResourceIterator - returns the iterator instance. + * @return error code or 0 (success) + *) + TLib3MFModel_GetBooleanObjectsFunc = function(pModel: TLib3MFHandle; out pResourceIterator: TLib3MFHandle): TLib3MFResult; cdecl; + (** * creates a Texture2DIterator instance with all texture2d resources. * @@ -7056,6 +7232,15 @@ TLib3MFModel = class; *) TLib3MFModel_AddComponentsObjectFunc = function(pModel: TLib3MFHandle; out pComponentsObjectInstance: TLib3MFHandle): TLib3MFResult; cdecl; + (** + * adds an empty boolean object to the model. + * + * @param[in] pModel - Model instance. + * @param[out] pBooleanObjectInstance - returns the boolean object instance + * @return error code or 0 (success) + *) + TLib3MFModel_AddBooleanObjectFunc = function(pModel: TLib3MFHandle; out pBooleanObjectInstance: TLib3MFHandle): TLib3MFResult; cdecl; + (** * creates a new model slicestack by its id * @@ -7736,6 +7921,18 @@ TLib3MFComponentsObjectIterator = class(TLib3MFResourceIterator) end; +(************************************************************************************************************************* + Class definition for BooleanObjectIterator +**************************************************************************************************************************) + + TLib3MFBooleanObjectIterator = class(TLib3MFResourceIterator) + public + constructor Create(AWrapper: TLib3MFWrapper; AHandle: TLib3MFHandle); + destructor Destroy; override; + function GetCurrentBooleanObject(): TLib3MFBooleanObject; + end; + + (************************************************************************************************************************* Class definition for Texture2DIterator **************************************************************************************************************************) @@ -7924,6 +8121,7 @@ TLib3MFObject = class(TLib3MFResource) function IsMeshObject(): Boolean; function IsComponentsObject(): Boolean; function IsLevelSetObject(): Boolean; + function IsBooleanObject(): Boolean; function IsValid(): Boolean; procedure SetAttachmentAsThumbnail(const AAttachment: TLib3MFAttachment); function GetThumbnailAttachment(): TLib3MFAttachment; @@ -8006,6 +8204,30 @@ TLib3MFLevelSet = class(TLib3MFObject) end; +(************************************************************************************************************************* + Class definition for BooleanObject +**************************************************************************************************************************) + + TLib3MFBooleanObject = class(TLib3MFObject) + public + constructor Create(AWrapper: TLib3MFWrapper; AHandle: TLib3MFHandle); + destructor Destroy; override; + procedure SetBaseObject(const ABaseObject: TLib3MFObject; const ATransform: TLib3MFTransform); + function GetBaseObject(): TLib3MFObject; + procedure SetBaseTransform(const ATransform: TLib3MFTransform); + function GetBaseTransform(): TLib3MFTransform; + procedure SetOperation(const AOperation: TLib3MFBooleanOperation); + function GetOperation(): TLib3MFBooleanOperation; + procedure SetCSGModeEnabled(const ACSGModeEnabled: Boolean); + function GetCSGModeEnabled(): Boolean; + procedure SetExtractionGridResolution(const AGridResolution: Cardinal); + function GetExtractionGridResolution(): Cardinal; + function GetOperandCount(): Cardinal; + procedure AddOperand(const AOperandObject: TLib3MFMeshObject; const ATransform: TLib3MFTransform); + function GetOperand(const AIndex: Cardinal; out AOperandObject: TLib3MFMeshObject): TLib3MFTransform; + end; + + (************************************************************************************************************************* Class definition for BeamLattice **************************************************************************************************************************) @@ -9462,6 +9684,7 @@ TLib3MFModel = class(TLib3MFBase) function GetMultiPropertyGroupByID(const AUniqueResourceID: Cardinal): TLib3MFMultiPropertyGroup; function GetMeshObjectByID(const AUniqueResourceID: Cardinal): TLib3MFMeshObject; function GetComponentsObjectByID(const AUniqueResourceID: Cardinal): TLib3MFComponentsObject; + function GetBooleanObjectByID(const AUniqueResourceID: Cardinal): TLib3MFBooleanObject; function GetColorGroupByID(const AUniqueResourceID: Cardinal): TLib3MFColorGroup; function GetSliceStackByID(const AUniqueResourceID: Cardinal): TLib3MFSliceStack; function GetLevelSetByID(const AUniqueResourceID: Cardinal): TLib3MFLevelSet; @@ -9473,6 +9696,7 @@ TLib3MFModel = class(TLib3MFBase) function GetObjects(): TLib3MFObjectIterator; function GetMeshObjects(): TLib3MFMeshObjectIterator; function GetComponentsObjects(): TLib3MFComponentsObjectIterator; + function GetBooleanObjects(): TLib3MFBooleanObjectIterator; function GetTexture2Ds(): TLib3MFTexture2DIterator; function GetBaseMaterialGroups(): TLib3MFBaseMaterialGroupIterator; function GetColorGroups(): TLib3MFColorGroupIterator; @@ -9485,6 +9709,7 @@ TLib3MFModel = class(TLib3MFBase) procedure MergeFromModel(const AModelInstance: TLib3MFModel); function AddMeshObject(): TLib3MFMeshObject; function AddComponentsObject(): TLib3MFComponentsObject; + function AddBooleanObject(): TLib3MFBooleanObject; function AddSliceStack(const AZBottom: Double): TLib3MFSliceStack; function AddTexture2DFromAttachment(const ATextureAttachment: TLib3MFAttachment): TLib3MFTexture2D; function AddBaseMaterialGroup(): TLib3MFBaseMaterialGroup; @@ -9568,6 +9793,7 @@ TLib3MFWrapper = class(TObject) FLib3MFObjectIterator_GetCurrentObjectFunc: TLib3MFObjectIterator_GetCurrentObjectFunc; FLib3MFMeshObjectIterator_GetCurrentMeshObjectFunc: TLib3MFMeshObjectIterator_GetCurrentMeshObjectFunc; FLib3MFComponentsObjectIterator_GetCurrentComponentsObjectFunc: TLib3MFComponentsObjectIterator_GetCurrentComponentsObjectFunc; + FLib3MFBooleanObjectIterator_GetCurrentBooleanObjectFunc: TLib3MFBooleanObjectIterator_GetCurrentBooleanObjectFunc; FLib3MFTexture2DIterator_GetCurrentTexture2DFunc: TLib3MFTexture2DIterator_GetCurrentTexture2DFunc; FLib3MFBaseMaterialGroupIterator_GetCurrentBaseMaterialGroupFunc: TLib3MFBaseMaterialGroupIterator_GetCurrentBaseMaterialGroupFunc; FLib3MFColorGroupIterator_GetCurrentColorGroupFunc: TLib3MFColorGroupIterator_GetCurrentColorGroupFunc; @@ -9616,6 +9842,7 @@ TLib3MFWrapper = class(TObject) FLib3MFObject_IsMeshObjectFunc: TLib3MFObject_IsMeshObjectFunc; FLib3MFObject_IsComponentsObjectFunc: TLib3MFObject_IsComponentsObjectFunc; FLib3MFObject_IsLevelSetObjectFunc: TLib3MFObject_IsLevelSetObjectFunc; + FLib3MFObject_IsBooleanObjectFunc: TLib3MFObject_IsBooleanObjectFunc; FLib3MFObject_IsValidFunc: TLib3MFObject_IsValidFunc; FLib3MFObject_SetAttachmentAsThumbnailFunc: TLib3MFObject_SetAttachmentAsThumbnailFunc; FLib3MFObject_GetThumbnailAttachmentFunc: TLib3MFObject_GetThumbnailAttachmentFunc; @@ -9673,6 +9900,19 @@ TLib3MFWrapper = class(TObject) FLib3MFLevelSet_GetMeshFunc: TLib3MFLevelSet_GetMeshFunc; FLib3MFLevelSet_GetVolumeDataFunc: TLib3MFLevelSet_GetVolumeDataFunc; FLib3MFLevelSet_SetVolumeDataFunc: TLib3MFLevelSet_SetVolumeDataFunc; + FLib3MFBooleanObject_SetBaseObjectFunc: TLib3MFBooleanObject_SetBaseObjectFunc; + FLib3MFBooleanObject_GetBaseObjectFunc: TLib3MFBooleanObject_GetBaseObjectFunc; + FLib3MFBooleanObject_SetBaseTransformFunc: TLib3MFBooleanObject_SetBaseTransformFunc; + FLib3MFBooleanObject_GetBaseTransformFunc: TLib3MFBooleanObject_GetBaseTransformFunc; + FLib3MFBooleanObject_SetOperationFunc: TLib3MFBooleanObject_SetOperationFunc; + FLib3MFBooleanObject_GetOperationFunc: TLib3MFBooleanObject_GetOperationFunc; + FLib3MFBooleanObject_SetCSGModeEnabledFunc: TLib3MFBooleanObject_SetCSGModeEnabledFunc; + FLib3MFBooleanObject_GetCSGModeEnabledFunc: TLib3MFBooleanObject_GetCSGModeEnabledFunc; + FLib3MFBooleanObject_SetExtractionGridResolutionFunc: TLib3MFBooleanObject_SetExtractionGridResolutionFunc; + FLib3MFBooleanObject_GetExtractionGridResolutionFunc: TLib3MFBooleanObject_GetExtractionGridResolutionFunc; + FLib3MFBooleanObject_GetOperandCountFunc: TLib3MFBooleanObject_GetOperandCountFunc; + FLib3MFBooleanObject_AddOperandFunc: TLib3MFBooleanObject_AddOperandFunc; + FLib3MFBooleanObject_GetOperandFunc: TLib3MFBooleanObject_GetOperandFunc; FLib3MFBeamLattice_GetMinLengthFunc: TLib3MFBeamLattice_GetMinLengthFunc; FLib3MFBeamLattice_SetMinLengthFunc: TLib3MFBeamLattice_SetMinLengthFunc; FLib3MFBeamLattice_GetClippingFunc: TLib3MFBeamLattice_GetClippingFunc; @@ -10098,6 +10338,7 @@ TLib3MFWrapper = class(TObject) FLib3MFModel_GetMultiPropertyGroupByIDFunc: TLib3MFModel_GetMultiPropertyGroupByIDFunc; FLib3MFModel_GetMeshObjectByIDFunc: TLib3MFModel_GetMeshObjectByIDFunc; FLib3MFModel_GetComponentsObjectByIDFunc: TLib3MFModel_GetComponentsObjectByIDFunc; + FLib3MFModel_GetBooleanObjectByIDFunc: TLib3MFModel_GetBooleanObjectByIDFunc; FLib3MFModel_GetColorGroupByIDFunc: TLib3MFModel_GetColorGroupByIDFunc; FLib3MFModel_GetSliceStackByIDFunc: TLib3MFModel_GetSliceStackByIDFunc; FLib3MFModel_GetLevelSetByIDFunc: TLib3MFModel_GetLevelSetByIDFunc; @@ -10109,6 +10350,7 @@ TLib3MFWrapper = class(TObject) FLib3MFModel_GetObjectsFunc: TLib3MFModel_GetObjectsFunc; FLib3MFModel_GetMeshObjectsFunc: TLib3MFModel_GetMeshObjectsFunc; FLib3MFModel_GetComponentsObjectsFunc: TLib3MFModel_GetComponentsObjectsFunc; + FLib3MFModel_GetBooleanObjectsFunc: TLib3MFModel_GetBooleanObjectsFunc; FLib3MFModel_GetTexture2DsFunc: TLib3MFModel_GetTexture2DsFunc; FLib3MFModel_GetBaseMaterialGroupsFunc: TLib3MFModel_GetBaseMaterialGroupsFunc; FLib3MFModel_GetColorGroupsFunc: TLib3MFModel_GetColorGroupsFunc; @@ -10121,6 +10363,7 @@ TLib3MFWrapper = class(TObject) FLib3MFModel_MergeFromModelFunc: TLib3MFModel_MergeFromModelFunc; FLib3MFModel_AddMeshObjectFunc: TLib3MFModel_AddMeshObjectFunc; FLib3MFModel_AddComponentsObjectFunc: TLib3MFModel_AddComponentsObjectFunc; + FLib3MFModel_AddBooleanObjectFunc: TLib3MFModel_AddBooleanObjectFunc; FLib3MFModel_AddSliceStackFunc: TLib3MFModel_AddSliceStackFunc; FLib3MFModel_AddTexture2DFromAttachmentFunc: TLib3MFModel_AddTexture2DFromAttachmentFunc; FLib3MFModel_AddBaseMaterialGroupFunc: TLib3MFModel_AddBaseMaterialGroupFunc; @@ -10224,6 +10467,7 @@ TLib3MFWrapper = class(TObject) property Lib3MFObjectIterator_GetCurrentObjectFunc: TLib3MFObjectIterator_GetCurrentObjectFunc read FLib3MFObjectIterator_GetCurrentObjectFunc; property Lib3MFMeshObjectIterator_GetCurrentMeshObjectFunc: TLib3MFMeshObjectIterator_GetCurrentMeshObjectFunc read FLib3MFMeshObjectIterator_GetCurrentMeshObjectFunc; property Lib3MFComponentsObjectIterator_GetCurrentComponentsObjectFunc: TLib3MFComponentsObjectIterator_GetCurrentComponentsObjectFunc read FLib3MFComponentsObjectIterator_GetCurrentComponentsObjectFunc; + property Lib3MFBooleanObjectIterator_GetCurrentBooleanObjectFunc: TLib3MFBooleanObjectIterator_GetCurrentBooleanObjectFunc read FLib3MFBooleanObjectIterator_GetCurrentBooleanObjectFunc; property Lib3MFTexture2DIterator_GetCurrentTexture2DFunc: TLib3MFTexture2DIterator_GetCurrentTexture2DFunc read FLib3MFTexture2DIterator_GetCurrentTexture2DFunc; property Lib3MFBaseMaterialGroupIterator_GetCurrentBaseMaterialGroupFunc: TLib3MFBaseMaterialGroupIterator_GetCurrentBaseMaterialGroupFunc read FLib3MFBaseMaterialGroupIterator_GetCurrentBaseMaterialGroupFunc; property Lib3MFColorGroupIterator_GetCurrentColorGroupFunc: TLib3MFColorGroupIterator_GetCurrentColorGroupFunc read FLib3MFColorGroupIterator_GetCurrentColorGroupFunc; @@ -10272,6 +10516,7 @@ TLib3MFWrapper = class(TObject) property Lib3MFObject_IsMeshObjectFunc: TLib3MFObject_IsMeshObjectFunc read FLib3MFObject_IsMeshObjectFunc; property Lib3MFObject_IsComponentsObjectFunc: TLib3MFObject_IsComponentsObjectFunc read FLib3MFObject_IsComponentsObjectFunc; property Lib3MFObject_IsLevelSetObjectFunc: TLib3MFObject_IsLevelSetObjectFunc read FLib3MFObject_IsLevelSetObjectFunc; + property Lib3MFObject_IsBooleanObjectFunc: TLib3MFObject_IsBooleanObjectFunc read FLib3MFObject_IsBooleanObjectFunc; property Lib3MFObject_IsValidFunc: TLib3MFObject_IsValidFunc read FLib3MFObject_IsValidFunc; property Lib3MFObject_SetAttachmentAsThumbnailFunc: TLib3MFObject_SetAttachmentAsThumbnailFunc read FLib3MFObject_SetAttachmentAsThumbnailFunc; property Lib3MFObject_GetThumbnailAttachmentFunc: TLib3MFObject_GetThumbnailAttachmentFunc read FLib3MFObject_GetThumbnailAttachmentFunc; @@ -10329,6 +10574,19 @@ TLib3MFWrapper = class(TObject) property Lib3MFLevelSet_GetMeshFunc: TLib3MFLevelSet_GetMeshFunc read FLib3MFLevelSet_GetMeshFunc; property Lib3MFLevelSet_GetVolumeDataFunc: TLib3MFLevelSet_GetVolumeDataFunc read FLib3MFLevelSet_GetVolumeDataFunc; property Lib3MFLevelSet_SetVolumeDataFunc: TLib3MFLevelSet_SetVolumeDataFunc read FLib3MFLevelSet_SetVolumeDataFunc; + property Lib3MFBooleanObject_SetBaseObjectFunc: TLib3MFBooleanObject_SetBaseObjectFunc read FLib3MFBooleanObject_SetBaseObjectFunc; + property Lib3MFBooleanObject_GetBaseObjectFunc: TLib3MFBooleanObject_GetBaseObjectFunc read FLib3MFBooleanObject_GetBaseObjectFunc; + property Lib3MFBooleanObject_SetBaseTransformFunc: TLib3MFBooleanObject_SetBaseTransformFunc read FLib3MFBooleanObject_SetBaseTransformFunc; + property Lib3MFBooleanObject_GetBaseTransformFunc: TLib3MFBooleanObject_GetBaseTransformFunc read FLib3MFBooleanObject_GetBaseTransformFunc; + property Lib3MFBooleanObject_SetOperationFunc: TLib3MFBooleanObject_SetOperationFunc read FLib3MFBooleanObject_SetOperationFunc; + property Lib3MFBooleanObject_GetOperationFunc: TLib3MFBooleanObject_GetOperationFunc read FLib3MFBooleanObject_GetOperationFunc; + property Lib3MFBooleanObject_SetCSGModeEnabledFunc: TLib3MFBooleanObject_SetCSGModeEnabledFunc read FLib3MFBooleanObject_SetCSGModeEnabledFunc; + property Lib3MFBooleanObject_GetCSGModeEnabledFunc: TLib3MFBooleanObject_GetCSGModeEnabledFunc read FLib3MFBooleanObject_GetCSGModeEnabledFunc; + property Lib3MFBooleanObject_SetExtractionGridResolutionFunc: TLib3MFBooleanObject_SetExtractionGridResolutionFunc read FLib3MFBooleanObject_SetExtractionGridResolutionFunc; + property Lib3MFBooleanObject_GetExtractionGridResolutionFunc: TLib3MFBooleanObject_GetExtractionGridResolutionFunc read FLib3MFBooleanObject_GetExtractionGridResolutionFunc; + property Lib3MFBooleanObject_GetOperandCountFunc: TLib3MFBooleanObject_GetOperandCountFunc read FLib3MFBooleanObject_GetOperandCountFunc; + property Lib3MFBooleanObject_AddOperandFunc: TLib3MFBooleanObject_AddOperandFunc read FLib3MFBooleanObject_AddOperandFunc; + property Lib3MFBooleanObject_GetOperandFunc: TLib3MFBooleanObject_GetOperandFunc read FLib3MFBooleanObject_GetOperandFunc; property Lib3MFBeamLattice_GetMinLengthFunc: TLib3MFBeamLattice_GetMinLengthFunc read FLib3MFBeamLattice_GetMinLengthFunc; property Lib3MFBeamLattice_SetMinLengthFunc: TLib3MFBeamLattice_SetMinLengthFunc read FLib3MFBeamLattice_SetMinLengthFunc; property Lib3MFBeamLattice_GetClippingFunc: TLib3MFBeamLattice_GetClippingFunc read FLib3MFBeamLattice_GetClippingFunc; @@ -10754,6 +11012,7 @@ TLib3MFWrapper = class(TObject) property Lib3MFModel_GetMultiPropertyGroupByIDFunc: TLib3MFModel_GetMultiPropertyGroupByIDFunc read FLib3MFModel_GetMultiPropertyGroupByIDFunc; property Lib3MFModel_GetMeshObjectByIDFunc: TLib3MFModel_GetMeshObjectByIDFunc read FLib3MFModel_GetMeshObjectByIDFunc; property Lib3MFModel_GetComponentsObjectByIDFunc: TLib3MFModel_GetComponentsObjectByIDFunc read FLib3MFModel_GetComponentsObjectByIDFunc; + property Lib3MFModel_GetBooleanObjectByIDFunc: TLib3MFModel_GetBooleanObjectByIDFunc read FLib3MFModel_GetBooleanObjectByIDFunc; property Lib3MFModel_GetColorGroupByIDFunc: TLib3MFModel_GetColorGroupByIDFunc read FLib3MFModel_GetColorGroupByIDFunc; property Lib3MFModel_GetSliceStackByIDFunc: TLib3MFModel_GetSliceStackByIDFunc read FLib3MFModel_GetSliceStackByIDFunc; property Lib3MFModel_GetLevelSetByIDFunc: TLib3MFModel_GetLevelSetByIDFunc read FLib3MFModel_GetLevelSetByIDFunc; @@ -10765,6 +11024,7 @@ TLib3MFWrapper = class(TObject) property Lib3MFModel_GetObjectsFunc: TLib3MFModel_GetObjectsFunc read FLib3MFModel_GetObjectsFunc; property Lib3MFModel_GetMeshObjectsFunc: TLib3MFModel_GetMeshObjectsFunc read FLib3MFModel_GetMeshObjectsFunc; property Lib3MFModel_GetComponentsObjectsFunc: TLib3MFModel_GetComponentsObjectsFunc read FLib3MFModel_GetComponentsObjectsFunc; + property Lib3MFModel_GetBooleanObjectsFunc: TLib3MFModel_GetBooleanObjectsFunc read FLib3MFModel_GetBooleanObjectsFunc; property Lib3MFModel_GetTexture2DsFunc: TLib3MFModel_GetTexture2DsFunc read FLib3MFModel_GetTexture2DsFunc; property Lib3MFModel_GetBaseMaterialGroupsFunc: TLib3MFModel_GetBaseMaterialGroupsFunc read FLib3MFModel_GetBaseMaterialGroupsFunc; property Lib3MFModel_GetColorGroupsFunc: TLib3MFModel_GetColorGroupsFunc read FLib3MFModel_GetColorGroupsFunc; @@ -10777,6 +11037,7 @@ TLib3MFWrapper = class(TObject) property Lib3MFModel_MergeFromModelFunc: TLib3MFModel_MergeFromModelFunc read FLib3MFModel_MergeFromModelFunc; property Lib3MFModel_AddMeshObjectFunc: TLib3MFModel_AddMeshObjectFunc read FLib3MFModel_AddMeshObjectFunc; property Lib3MFModel_AddComponentsObjectFunc: TLib3MFModel_AddComponentsObjectFunc read FLib3MFModel_AddComponentsObjectFunc; + property Lib3MFModel_AddBooleanObjectFunc: TLib3MFModel_AddBooleanObjectFunc read FLib3MFModel_AddBooleanObjectFunc; property Lib3MFModel_AddSliceStackFunc: TLib3MFModel_AddSliceStackFunc read FLib3MFModel_AddSliceStackFunc; property Lib3MFModel_AddTexture2DFromAttachmentFunc: TLib3MFModel_AddTexture2DFromAttachmentFunc read FLib3MFModel_AddTexture2DFromAttachmentFunc; property Lib3MFModel_AddBaseMaterialGroupFunc: TLib3MFModel_AddBaseMaterialGroupFunc read FLib3MFModel_AddBaseMaterialGroupFunc; @@ -10867,6 +11128,7 @@ TLib3MFPolymorphicFactory<_T:class; _B> = record function TLib3MFPolymorphicFactoryMakeObjectIterator(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFObjectIterator; function TLib3MFPolymorphicFactoryMakeMeshObjectIterator(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFMeshObjectIterator; function TLib3MFPolymorphicFactoryMakeComponentsObjectIterator(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFComponentsObjectIterator; + function TLib3MFPolymorphicFactoryMakeBooleanObjectIterator(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFBooleanObjectIterator; function TLib3MFPolymorphicFactoryMakeTexture2DIterator(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFTexture2DIterator; function TLib3MFPolymorphicFactoryMakeBaseMaterialGroupIterator(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFBaseMaterialGroupIterator; function TLib3MFPolymorphicFactoryMakeColorGroupIterator(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFColorGroupIterator; @@ -10882,6 +11144,7 @@ TLib3MFPolymorphicFactory<_T:class; _B> = record function TLib3MFPolymorphicFactoryMakeObject(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFObject; function TLib3MFPolymorphicFactoryMakeMeshObject(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFMeshObject; function TLib3MFPolymorphicFactoryMakeLevelSet(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFLevelSet; + function TLib3MFPolymorphicFactoryMakeBooleanObject(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFBooleanObject; function TLib3MFPolymorphicFactoryMakeBeamLattice(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFBeamLattice; function TLib3MFPolymorphicFactoryMakeFunctionReference(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFFunctionReference; function TLib3MFPolymorphicFactoryMakeVolumeDataColor(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFVolumeDataColor; @@ -10988,6 +11251,8 @@ TLib3MFPolymorphicFactory<_T:class; _B> = record function convertConstToModelUnit(const AValue: Integer): TLib3MFModelUnit; function convertObjectTypeToConst(const AValue: TLib3MFObjectType): Integer; function convertConstToObjectType(const AValue: Integer): TLib3MFObjectType; + function convertBooleanOperationToConst(const AValue: TLib3MFBooleanOperation): Integer; + function convertConstToBooleanOperation(const AValue: Integer): TLib3MFBooleanOperation; function convertTextureTypeToConst(const AValue: TLib3MFTextureType): Integer; function convertConstToTextureType(const AValue: Integer): TLib3MFTextureType; function convertTextureTileStyleToConst(const AValue: TLib3MFTextureTileStyle): Integer; @@ -11140,6 +11405,29 @@ implementation end; + function convertBooleanOperationToConst(const AValue: TLib3MFBooleanOperation): Integer; + begin + case AValue of + eBooleanOperationUnion: Result := 0; + eBooleanOperationDifference: Result := 1; + eBooleanOperationIntersection: Result := 2; + else + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_INVALIDPARAM, 'invalid enum value'); + end; + end; + + function convertConstToBooleanOperation(const AValue: Integer): TLib3MFBooleanOperation; + begin + case AValue of + 0: Result := eBooleanOperationUnion; + 1: Result := eBooleanOperationDifference; + 2: Result := eBooleanOperationIntersection; + else + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_INVALIDPARAM, 'invalid enum constant'); + end; + end; + + function convertTextureTypeToConst(const AValue: TLib3MFTextureType): Integer; begin case AValue of @@ -11754,6 +12042,7 @@ implementation QWord($DE92510BD2112288): begin Obj := TLIB3MFObjectIterator.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::ObjectIterator" QWord($F4196034E2B9FDE6): begin Obj := TLIB3MFMeshObjectIterator.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::MeshObjectIterator" QWord($564DE4217ED7614A): begin Obj := TLIB3MFComponentsObjectIterator.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::ComponentsObjectIterator" + QWord($AFF01F512E1FF6AE): begin Obj := TLIB3MFBooleanObjectIterator.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObjectIterator" QWord($4BD32B4870FFC03B): begin Obj := TLIB3MFTexture2DIterator.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::Texture2DIterator" QWord($65E6EDD9362C79CB): begin Obj := TLIB3MFBaseMaterialGroupIterator.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::BaseMaterialGroupIterator" QWord($10274A1757C729C0): begin Obj := TLIB3MFColorGroupIterator.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::ColorGroupIterator" @@ -11769,6 +12058,7 @@ implementation QWord($2DA2136F577A779C): begin Obj := TLIB3MFObject.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::Object" QWord($3B3A6DC6EC610497): begin Obj := TLIB3MFMeshObject.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::MeshObject" QWord($E8A7D9C192EFD0E2): begin Obj := TLIB3MFLevelSet.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::LevelSet" + QWord($85FA0E8806B6C357): begin Obj := TLIB3MFBooleanObject.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObject" QWord($63B3B461B30B4BA5): begin Obj := TLIB3MFBeamLattice.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::BeamLattice" QWord($4DF17E76926221C2): begin Obj := TLIB3MFFunctionReference.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::FunctionReference" QWord($D85B5B6143E787E3): begin Obj := TLIB3MFVolumeDataColor.Create(Wrapper, Handle); if Obj.inheritsFrom(_T) then Result := Obj as _T; end; // First 64 bits of SHA1 of a string: "Lib3MF::VolumeDataColor" @@ -11906,6 +12196,10 @@ implementation begin Result := TLib3MFPolymorphicFactory.Make(Wrapper, Handle); end; + function TLib3MFPolymorphicFactoryMakeBooleanObjectIterator(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFBooleanObjectIterator; + begin + Result := TLib3MFPolymorphicFactory.Make(Wrapper, Handle); + end; function TLib3MFPolymorphicFactoryMakeTexture2DIterator(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFTexture2DIterator; begin Result := TLib3MFPolymorphicFactory.Make(Wrapper, Handle); @@ -11966,6 +12260,10 @@ implementation begin Result := TLib3MFPolymorphicFactory.Make(Wrapper, Handle); end; + function TLib3MFPolymorphicFactoryMakeBooleanObject(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFBooleanObject; + begin + Result := TLib3MFPolymorphicFactory.Make(Wrapper, Handle); + end; function TLib3MFPolymorphicFactoryMakeBeamLattice(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): TLIB3MFBeamLattice; begin Result := TLib3MFPolymorphicFactory.Make(Wrapper, Handle); @@ -12904,6 +13202,31 @@ implementation Result := TLib3MFPolymorphicFactory.Make(FWrapper, HResource); end; +(************************************************************************************************************************* + Class implementation for BooleanObjectIterator +**************************************************************************************************************************) + + constructor TLib3MFBooleanObjectIterator.Create(AWrapper: TLib3MFWrapper; AHandle: TLib3MFHandle); + begin + inherited Create(AWrapper, AHandle); + end; + + destructor TLib3MFBooleanObjectIterator.Destroy; + begin + inherited; + end; + + function TLib3MFBooleanObjectIterator.GetCurrentBooleanObject(): TLib3MFBooleanObject; + var + HResource: TLib3MFHandle; + begin + Result := nil; + HResource := nil; + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObjectIterator_GetCurrentBooleanObjectFunc(FHandle, HResource)); + if Assigned(HResource) then + Result := TLib3MFPolymorphicFactory.Make(FWrapper, HResource); + end; + (************************************************************************************************************************* Class implementation for Texture2DIterator **************************************************************************************************************************) @@ -13546,6 +13869,15 @@ implementation Result := (ResultIsLevelSetObject <> 0); end; + function TLib3MFObject.IsBooleanObject(): Boolean; + var + ResultIsBooleanObject: Byte; + begin + ResultIsBooleanObject := 0; + FWrapper.CheckError(Self, FWrapper.Lib3MFObject_IsBooleanObjectFunc(FHandle, ResultIsBooleanObject)); + Result := (ResultIsBooleanObject <> 0); + end; + function TLib3MFObject.IsValid(): Boolean; var ResultIsValid: Byte; @@ -14063,6 +14395,117 @@ implementation FWrapper.CheckError(Self, FWrapper.Lib3MFLevelSet_SetVolumeDataFunc(FHandle, ATheVolumeDataHandle)); end; +(************************************************************************************************************************* + Class implementation for BooleanObject +**************************************************************************************************************************) + + constructor TLib3MFBooleanObject.Create(AWrapper: TLib3MFWrapper; AHandle: TLib3MFHandle); + begin + inherited Create(AWrapper, AHandle); + end; + + destructor TLib3MFBooleanObject.Destroy; + begin + inherited; + end; + + procedure TLib3MFBooleanObject.SetBaseObject(const ABaseObject: TLib3MFObject; const ATransform: TLib3MFTransform); + var + ABaseObjectHandle: TLib3MFHandle; + begin + if Assigned(ABaseObject) then + ABaseObjectHandle := ABaseObject.TheHandle + else + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_INVALIDPARAM, 'ABaseObject is a nil value.'); + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObject_SetBaseObjectFunc(FHandle, ABaseObjectHandle, @ATransform)); + end; + + function TLib3MFBooleanObject.GetBaseObject(): TLib3MFObject; + var + HBaseObject: TLib3MFHandle; + begin + Result := nil; + HBaseObject := nil; + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObject_GetBaseObjectFunc(FHandle, HBaseObject)); + if Assigned(HBaseObject) then + Result := TLib3MFPolymorphicFactory.Make(FWrapper, HBaseObject); + end; + + procedure TLib3MFBooleanObject.SetBaseTransform(const ATransform: TLib3MFTransform); + begin + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObject_SetBaseTransformFunc(FHandle, @ATransform)); + end; + + function TLib3MFBooleanObject.GetBaseTransform(): TLib3MFTransform; + begin + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObject_GetBaseTransformFunc(FHandle, @Result)); + end; + + procedure TLib3MFBooleanObject.SetOperation(const AOperation: TLib3MFBooleanOperation); + begin + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObject_SetOperationFunc(FHandle, convertBooleanOperationToConst(AOperation))); + end; + + function TLib3MFBooleanObject.GetOperation(): TLib3MFBooleanOperation; + var + ResultOperation: Integer; + begin + ResultOperation := 0; + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObject_GetOperationFunc(FHandle, ResultOperation)); + Result := convertConstToBooleanOperation(ResultOperation); + end; + + procedure TLib3MFBooleanObject.SetCSGModeEnabled(const ACSGModeEnabled: Boolean); + begin + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObject_SetCSGModeEnabledFunc(FHandle, Ord(ACSGModeEnabled))); + end; + + function TLib3MFBooleanObject.GetCSGModeEnabled(): Boolean; + var + ResultCSGModeEnabled: Byte; + begin + ResultCSGModeEnabled := 0; + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObject_GetCSGModeEnabledFunc(FHandle, ResultCSGModeEnabled)); + Result := (ResultCSGModeEnabled <> 0); + end; + + procedure TLib3MFBooleanObject.SetExtractionGridResolution(const AGridResolution: Cardinal); + begin + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObject_SetExtractionGridResolutionFunc(FHandle, AGridResolution)); + end; + + function TLib3MFBooleanObject.GetExtractionGridResolution(): Cardinal; + begin + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObject_GetExtractionGridResolutionFunc(FHandle, Result)); + end; + + function TLib3MFBooleanObject.GetOperandCount(): Cardinal; + begin + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObject_GetOperandCountFunc(FHandle, Result)); + end; + + procedure TLib3MFBooleanObject.AddOperand(const AOperandObject: TLib3MFMeshObject; const ATransform: TLib3MFTransform); + var + AOperandObjectHandle: TLib3MFHandle; + begin + if Assigned(AOperandObject) then + AOperandObjectHandle := AOperandObject.TheHandle + else + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_INVALIDPARAM, 'AOperandObject is a nil value.'); + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObject_AddOperandFunc(FHandle, AOperandObjectHandle, @ATransform)); + end; + + function TLib3MFBooleanObject.GetOperand(const AIndex: Cardinal; out AOperandObject: TLib3MFMeshObject): TLib3MFTransform; + var + HOperandObject: TLib3MFHandle; + begin + AOperandObject := nil; + HOperandObject := nil; + FWrapper.CheckError(Self, FWrapper.Lib3MFBooleanObject_GetOperandFunc(FHandle, AIndex, HOperandObject, @Result)); + if Assigned(HOperandObject) then + AOperandObject := TLib3MFMeshObject.Create(FWrapper, HOperandObject); + end; + (************************************************************************************************************************* Class implementation for BeamLattice **************************************************************************************************************************) @@ -19478,6 +19921,17 @@ implementation Result := TLib3MFPolymorphicFactory.Make(FWrapper, HComponentsObjectInstance); end; + function TLib3MFModel.GetBooleanObjectByID(const AUniqueResourceID: Cardinal): TLib3MFBooleanObject; + var + HBooleanObjectInstance: TLib3MFHandle; + begin + Result := nil; + HBooleanObjectInstance := nil; + FWrapper.CheckError(Self, FWrapper.Lib3MFModel_GetBooleanObjectByIDFunc(FHandle, AUniqueResourceID, HBooleanObjectInstance)); + if Assigned(HBooleanObjectInstance) then + Result := TLib3MFPolymorphicFactory.Make(FWrapper, HBooleanObjectInstance); + end; + function TLib3MFModel.GetColorGroupByID(const AUniqueResourceID: Cardinal): TLib3MFColorGroup; var HColorGroupInstance: TLib3MFHandle; @@ -19593,6 +20047,17 @@ implementation Result := TLib3MFPolymorphicFactory.Make(FWrapper, HResourceIterator); end; + function TLib3MFModel.GetBooleanObjects(): TLib3MFBooleanObjectIterator; + var + HResourceIterator: TLib3MFHandle; + begin + Result := nil; + HResourceIterator := nil; + FWrapper.CheckError(Self, FWrapper.Lib3MFModel_GetBooleanObjectsFunc(FHandle, HResourceIterator)); + if Assigned(HResourceIterator) then + Result := TLib3MFPolymorphicFactory.Make(FWrapper, HResourceIterator); + end; + function TLib3MFModel.GetTexture2Ds(): TLib3MFTexture2DIterator; var HResourceIterator: TLib3MFHandle; @@ -19725,6 +20190,17 @@ implementation Result := TLib3MFPolymorphicFactory.Make(FWrapper, HComponentsObjectInstance); end; + function TLib3MFModel.AddBooleanObject(): TLib3MFBooleanObject; + var + HBooleanObjectInstance: TLib3MFHandle; + begin + Result := nil; + HBooleanObjectInstance := nil; + FWrapper.CheckError(Self, FWrapper.Lib3MFModel_AddBooleanObjectFunc(FHandle, HBooleanObjectInstance)); + if Assigned(HBooleanObjectInstance) then + Result := TLib3MFPolymorphicFactory.Make(FWrapper, HBooleanObjectInstance); + end; + function TLib3MFModel.AddSliceStack(const AZBottom: Double): TLib3MFSliceStack; var HSliceStackInstance: TLib3MFHandle; @@ -20136,6 +20612,7 @@ implementation FLib3MFObjectIterator_GetCurrentObjectFunc := LoadFunction('lib3mf_objectiterator_getcurrentobject'); FLib3MFMeshObjectIterator_GetCurrentMeshObjectFunc := LoadFunction('lib3mf_meshobjectiterator_getcurrentmeshobject'); FLib3MFComponentsObjectIterator_GetCurrentComponentsObjectFunc := LoadFunction('lib3mf_componentsobjectiterator_getcurrentcomponentsobject'); + FLib3MFBooleanObjectIterator_GetCurrentBooleanObjectFunc := LoadFunction('lib3mf_booleanobjectiterator_getcurrentbooleanobject'); FLib3MFTexture2DIterator_GetCurrentTexture2DFunc := LoadFunction('lib3mf_texture2diterator_getcurrenttexture2d'); FLib3MFBaseMaterialGroupIterator_GetCurrentBaseMaterialGroupFunc := LoadFunction('lib3mf_basematerialgroupiterator_getcurrentbasematerialgroup'); FLib3MFColorGroupIterator_GetCurrentColorGroupFunc := LoadFunction('lib3mf_colorgroupiterator_getcurrentcolorgroup'); @@ -20184,6 +20661,7 @@ implementation FLib3MFObject_IsMeshObjectFunc := LoadFunction('lib3mf_object_ismeshobject'); FLib3MFObject_IsComponentsObjectFunc := LoadFunction('lib3mf_object_iscomponentsobject'); FLib3MFObject_IsLevelSetObjectFunc := LoadFunction('lib3mf_object_islevelsetobject'); + FLib3MFObject_IsBooleanObjectFunc := LoadFunction('lib3mf_object_isbooleanobject'); FLib3MFObject_IsValidFunc := LoadFunction('lib3mf_object_isvalid'); FLib3MFObject_SetAttachmentAsThumbnailFunc := LoadFunction('lib3mf_object_setattachmentasthumbnail'); FLib3MFObject_GetThumbnailAttachmentFunc := LoadFunction('lib3mf_object_getthumbnailattachment'); @@ -20241,6 +20719,19 @@ implementation FLib3MFLevelSet_GetMeshFunc := LoadFunction('lib3mf_levelset_getmesh'); FLib3MFLevelSet_GetVolumeDataFunc := LoadFunction('lib3mf_levelset_getvolumedata'); FLib3MFLevelSet_SetVolumeDataFunc := LoadFunction('lib3mf_levelset_setvolumedata'); + FLib3MFBooleanObject_SetBaseObjectFunc := LoadFunction('lib3mf_booleanobject_setbaseobject'); + FLib3MFBooleanObject_GetBaseObjectFunc := LoadFunction('lib3mf_booleanobject_getbaseobject'); + FLib3MFBooleanObject_SetBaseTransformFunc := LoadFunction('lib3mf_booleanobject_setbasetransform'); + FLib3MFBooleanObject_GetBaseTransformFunc := LoadFunction('lib3mf_booleanobject_getbasetransform'); + FLib3MFBooleanObject_SetOperationFunc := LoadFunction('lib3mf_booleanobject_setoperation'); + FLib3MFBooleanObject_GetOperationFunc := LoadFunction('lib3mf_booleanobject_getoperation'); + FLib3MFBooleanObject_SetCSGModeEnabledFunc := LoadFunction('lib3mf_booleanobject_setcsgmodeenabled'); + FLib3MFBooleanObject_GetCSGModeEnabledFunc := LoadFunction('lib3mf_booleanobject_getcsgmodeenabled'); + FLib3MFBooleanObject_SetExtractionGridResolutionFunc := LoadFunction('lib3mf_booleanobject_setextractiongridresolution'); + FLib3MFBooleanObject_GetExtractionGridResolutionFunc := LoadFunction('lib3mf_booleanobject_getextractiongridresolution'); + FLib3MFBooleanObject_GetOperandCountFunc := LoadFunction('lib3mf_booleanobject_getoperandcount'); + FLib3MFBooleanObject_AddOperandFunc := LoadFunction('lib3mf_booleanobject_addoperand'); + FLib3MFBooleanObject_GetOperandFunc := LoadFunction('lib3mf_booleanobject_getoperand'); FLib3MFBeamLattice_GetMinLengthFunc := LoadFunction('lib3mf_beamlattice_getminlength'); FLib3MFBeamLattice_SetMinLengthFunc := LoadFunction('lib3mf_beamlattice_setminlength'); FLib3MFBeamLattice_GetClippingFunc := LoadFunction('lib3mf_beamlattice_getclipping'); @@ -20666,6 +21157,7 @@ implementation FLib3MFModel_GetMultiPropertyGroupByIDFunc := LoadFunction('lib3mf_model_getmultipropertygroupbyid'); FLib3MFModel_GetMeshObjectByIDFunc := LoadFunction('lib3mf_model_getmeshobjectbyid'); FLib3MFModel_GetComponentsObjectByIDFunc := LoadFunction('lib3mf_model_getcomponentsobjectbyid'); + FLib3MFModel_GetBooleanObjectByIDFunc := LoadFunction('lib3mf_model_getbooleanobjectbyid'); FLib3MFModel_GetColorGroupByIDFunc := LoadFunction('lib3mf_model_getcolorgroupbyid'); FLib3MFModel_GetSliceStackByIDFunc := LoadFunction('lib3mf_model_getslicestackbyid'); FLib3MFModel_GetLevelSetByIDFunc := LoadFunction('lib3mf_model_getlevelsetbyid'); @@ -20677,6 +21169,7 @@ implementation FLib3MFModel_GetObjectsFunc := LoadFunction('lib3mf_model_getobjects'); FLib3MFModel_GetMeshObjectsFunc := LoadFunction('lib3mf_model_getmeshobjects'); FLib3MFModel_GetComponentsObjectsFunc := LoadFunction('lib3mf_model_getcomponentsobjects'); + FLib3MFModel_GetBooleanObjectsFunc := LoadFunction('lib3mf_model_getbooleanobjects'); FLib3MFModel_GetTexture2DsFunc := LoadFunction('lib3mf_model_gettexture2ds'); FLib3MFModel_GetBaseMaterialGroupsFunc := LoadFunction('lib3mf_model_getbasematerialgroups'); FLib3MFModel_GetColorGroupsFunc := LoadFunction('lib3mf_model_getcolorgroups'); @@ -20689,6 +21182,7 @@ implementation FLib3MFModel_MergeFromModelFunc := LoadFunction('lib3mf_model_mergefrommodel'); FLib3MFModel_AddMeshObjectFunc := LoadFunction('lib3mf_model_addmeshobject'); FLib3MFModel_AddComponentsObjectFunc := LoadFunction('lib3mf_model_addcomponentsobject'); + FLib3MFModel_AddBooleanObjectFunc := LoadFunction('lib3mf_model_addbooleanobject'); FLib3MFModel_AddSliceStackFunc := LoadFunction('lib3mf_model_addslicestack'); FLib3MFModel_AddTexture2DFromAttachmentFunc := LoadFunction('lib3mf_model_addtexture2dfromattachment'); FLib3MFModel_AddBaseMaterialGroupFunc := LoadFunction('lib3mf_model_addbasematerialgroup'); @@ -20875,6 +21369,9 @@ implementation if AResult <> LIB3MF_SUCCESS then raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); AResult := ALookupMethod(PAnsiChar('lib3mf_componentsobjectiterator_getcurrentcomponentsobject'), @FLib3MFComponentsObjectIterator_GetCurrentComponentsObjectFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobjectiterator_getcurrentbooleanobject'), @FLib3MFBooleanObjectIterator_GetCurrentBooleanObjectFunc); if AResult <> LIB3MF_SUCCESS then raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); AResult := ALookupMethod(PAnsiChar('lib3mf_texture2diterator_getcurrenttexture2d'), @FLib3MFTexture2DIterator_GetCurrentTexture2DFunc); @@ -21019,6 +21516,9 @@ implementation if AResult <> LIB3MF_SUCCESS then raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); AResult := ALookupMethod(PAnsiChar('lib3mf_object_islevelsetobject'), @FLib3MFObject_IsLevelSetObjectFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_object_isbooleanobject'), @FLib3MFObject_IsBooleanObjectFunc); if AResult <> LIB3MF_SUCCESS then raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); AResult := ALookupMethod(PAnsiChar('lib3mf_object_isvalid'), @FLib3MFObject_IsValidFunc); @@ -21190,6 +21690,45 @@ implementation if AResult <> LIB3MF_SUCCESS then raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); AResult := ALookupMethod(PAnsiChar('lib3mf_levelset_setvolumedata'), @FLib3MFLevelSet_SetVolumeDataFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobject_setbaseobject'), @FLib3MFBooleanObject_SetBaseObjectFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobject_getbaseobject'), @FLib3MFBooleanObject_GetBaseObjectFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobject_setbasetransform'), @FLib3MFBooleanObject_SetBaseTransformFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobject_getbasetransform'), @FLib3MFBooleanObject_GetBaseTransformFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobject_setoperation'), @FLib3MFBooleanObject_SetOperationFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobject_getoperation'), @FLib3MFBooleanObject_GetOperationFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobject_setcsgmodeenabled'), @FLib3MFBooleanObject_SetCSGModeEnabledFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobject_getcsgmodeenabled'), @FLib3MFBooleanObject_GetCSGModeEnabledFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobject_setextractiongridresolution'), @FLib3MFBooleanObject_SetExtractionGridResolutionFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobject_getextractiongridresolution'), @FLib3MFBooleanObject_GetExtractionGridResolutionFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobject_getoperandcount'), @FLib3MFBooleanObject_GetOperandCountFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobject_addoperand'), @FLib3MFBooleanObject_AddOperandFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_booleanobject_getoperand'), @FLib3MFBooleanObject_GetOperandFunc); if AResult <> LIB3MF_SUCCESS then raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); AResult := ALookupMethod(PAnsiChar('lib3mf_beamlattice_getminlength'), @FLib3MFBeamLattice_GetMinLengthFunc); @@ -22465,6 +23004,9 @@ implementation if AResult <> LIB3MF_SUCCESS then raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); AResult := ALookupMethod(PAnsiChar('lib3mf_model_getcomponentsobjectbyid'), @FLib3MFModel_GetComponentsObjectByIDFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_model_getbooleanobjectbyid'), @FLib3MFModel_GetBooleanObjectByIDFunc); if AResult <> LIB3MF_SUCCESS then raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); AResult := ALookupMethod(PAnsiChar('lib3mf_model_getcolorgroupbyid'), @FLib3MFModel_GetColorGroupByIDFunc); @@ -22498,6 +23040,9 @@ implementation if AResult <> LIB3MF_SUCCESS then raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); AResult := ALookupMethod(PAnsiChar('lib3mf_model_getcomponentsobjects'), @FLib3MFModel_GetComponentsObjectsFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_model_getbooleanobjects'), @FLib3MFModel_GetBooleanObjectsFunc); if AResult <> LIB3MF_SUCCESS then raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); AResult := ALookupMethod(PAnsiChar('lib3mf_model_gettexture2ds'), @FLib3MFModel_GetTexture2DsFunc); @@ -22534,6 +23079,9 @@ implementation if AResult <> LIB3MF_SUCCESS then raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); AResult := ALookupMethod(PAnsiChar('lib3mf_model_addcomponentsobject'), @FLib3MFModel_AddComponentsObjectFunc); + if AResult <> LIB3MF_SUCCESS then + raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); + AResult := ALookupMethod(PAnsiChar('lib3mf_model_addbooleanobject'), @FLib3MFModel_AddBooleanObjectFunc); if AResult <> LIB3MF_SUCCESS then raise ELib3MFException.CreateCustomMessage(LIB3MF_ERROR_COULDNOTLOADLIBRARY, ''); AResult := ALookupMethod(PAnsiChar('lib3mf_model_addslicestack'), @FLib3MFModel_AddSliceStackFunc); diff --git a/Autogenerated/Bindings/Python/Lib3MF.py b/Autogenerated/Bindings/Python/Lib3MF.py index 560105e0d..caeed505b 100644 --- a/Autogenerated/Bindings/Python/Lib3MF.py +++ b/Autogenerated/Bindings/Python/Lib3MF.py @@ -29,7 +29,7 @@ Abstract: This is an autogenerated Python file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 ''' @@ -299,7 +299,7 @@ def error_description(self): ''' class BindingVersion(enum.IntEnum): MAJOR = 2 - MINOR = 5 + MINOR = 6 MICRO = 0 '''Definition Error Codes @@ -421,6 +421,7 @@ class FunctionTable: lib3mf_objectiterator_getcurrentobject = None lib3mf_meshobjectiterator_getcurrentmeshobject = None lib3mf_componentsobjectiterator_getcurrentcomponentsobject = None + lib3mf_booleanobjectiterator_getcurrentbooleanobject = None lib3mf_texture2diterator_getcurrenttexture2d = None lib3mf_basematerialgroupiterator_getcurrentbasematerialgroup = None lib3mf_colorgroupiterator_getcurrentcolorgroup = None @@ -469,6 +470,7 @@ class FunctionTable: lib3mf_object_ismeshobject = None lib3mf_object_iscomponentsobject = None lib3mf_object_islevelsetobject = None + lib3mf_object_isbooleanobject = None lib3mf_object_isvalid = None lib3mf_object_setattachmentasthumbnail = None lib3mf_object_getthumbnailattachment = None @@ -526,6 +528,19 @@ class FunctionTable: lib3mf_levelset_getmesh = None lib3mf_levelset_getvolumedata = None lib3mf_levelset_setvolumedata = None + lib3mf_booleanobject_setbaseobject = None + lib3mf_booleanobject_getbaseobject = None + lib3mf_booleanobject_setbasetransform = None + lib3mf_booleanobject_getbasetransform = None + lib3mf_booleanobject_setoperation = None + lib3mf_booleanobject_getoperation = None + lib3mf_booleanobject_setcsgmodeenabled = None + lib3mf_booleanobject_getcsgmodeenabled = None + lib3mf_booleanobject_setextractiongridresolution = None + lib3mf_booleanobject_getextractiongridresolution = None + lib3mf_booleanobject_getoperandcount = None + lib3mf_booleanobject_addoperand = None + lib3mf_booleanobject_getoperand = None lib3mf_beamlattice_getminlength = None lib3mf_beamlattice_setminlength = None lib3mf_beamlattice_getclipping = None @@ -951,6 +966,7 @@ class FunctionTable: lib3mf_model_getmultipropertygroupbyid = None lib3mf_model_getmeshobjectbyid = None lib3mf_model_getcomponentsobjectbyid = None + lib3mf_model_getbooleanobjectbyid = None lib3mf_model_getcolorgroupbyid = None lib3mf_model_getslicestackbyid = None lib3mf_model_getlevelsetbyid = None @@ -962,6 +978,7 @@ class FunctionTable: lib3mf_model_getobjects = None lib3mf_model_getmeshobjects = None lib3mf_model_getcomponentsobjects = None + lib3mf_model_getbooleanobjects = None lib3mf_model_gettexture2ds = None lib3mf_model_getbasematerialgroups = None lib3mf_model_getcolorgroups = None @@ -974,6 +991,7 @@ class FunctionTable: lib3mf_model_mergefrommodel = None lib3mf_model_addmeshobject = None lib3mf_model_addcomponentsobject = None + lib3mf_model_addbooleanobject = None lib3mf_model_addslicestack = None lib3mf_model_addtexture2dfromattachment = None lib3mf_model_addbasematerialgroup = None @@ -1047,6 +1065,12 @@ class ObjectType(CTypesEnum): Support = 2 SolidSupport = 3 Surface = 4 +'''Definition of BooleanOperation +''' +class BooleanOperation(CTypesEnum): + Union = 0 + Difference = 1 + Intersection = 2 '''Definition of TextureType ''' class TextureType(CTypesEnum): @@ -1786,6 +1810,12 @@ def _loadFunctionTableFromMethod(self, symbolLookupMethodAddress): methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)) self.lib.lib3mf_componentsobjectiterator_getcurrentcomponentsobject = methodType(int(methodAddress.value)) + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobjectiterator_getcurrentbooleanobject")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)) + self.lib.lib3mf_booleanobjectiterator_getcurrentbooleanobject = methodType(int(methodAddress.value)) + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_texture2diterator_getcurrenttexture2d")), methodAddress) if err != 0: raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) @@ -2074,6 +2104,12 @@ def _loadFunctionTableFromMethod(self, symbolLookupMethodAddress): methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_bool)) self.lib.lib3mf_object_islevelsetobject = methodType(int(methodAddress.value)) + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_object_isbooleanobject")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_bool)) + self.lib.lib3mf_object_isbooleanobject = methodType(int(methodAddress.value)) + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_object_isvalid")), methodAddress) if err != 0: raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) @@ -2416,6 +2452,84 @@ def _loadFunctionTableFromMethod(self, symbolLookupMethodAddress): methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.c_void_p) self.lib.lib3mf_levelset_setvolumedata = methodType(int(methodAddress.value)) + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobject_setbaseobject")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.c_void_p, ctypes.POINTER(Transform)) + self.lib.lib3mf_booleanobject_setbaseobject = methodType(int(methodAddress.value)) + + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobject_getbaseobject")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)) + self.lib.lib3mf_booleanobject_getbaseobject = methodType(int(methodAddress.value)) + + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobject_setbasetransform")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(Transform)) + self.lib.lib3mf_booleanobject_setbasetransform = methodType(int(methodAddress.value)) + + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobject_getbasetransform")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(Transform)) + self.lib.lib3mf_booleanobject_getbasetransform = methodType(int(methodAddress.value)) + + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobject_setoperation")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, BooleanOperation) + self.lib.lib3mf_booleanobject_setoperation = methodType(int(methodAddress.value)) + + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobject_getoperation")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_int32)) + self.lib.lib3mf_booleanobject_getoperation = methodType(int(methodAddress.value)) + + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobject_setcsgmodeenabled")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.c_bool) + self.lib.lib3mf_booleanobject_setcsgmodeenabled = methodType(int(methodAddress.value)) + + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobject_getcsgmodeenabled")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_bool)) + self.lib.lib3mf_booleanobject_getcsgmodeenabled = methodType(int(methodAddress.value)) + + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobject_setextractiongridresolution")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.c_uint32) + self.lib.lib3mf_booleanobject_setextractiongridresolution = methodType(int(methodAddress.value)) + + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobject_getextractiongridresolution")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint32)) + self.lib.lib3mf_booleanobject_getextractiongridresolution = methodType(int(methodAddress.value)) + + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobject_getoperandcount")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint32)) + self.lib.lib3mf_booleanobject_getoperandcount = methodType(int(methodAddress.value)) + + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobject_addoperand")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.c_void_p, ctypes.POINTER(Transform)) + self.lib.lib3mf_booleanobject_addoperand = methodType(int(methodAddress.value)) + + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_booleanobject_getoperand")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(Transform)) + self.lib.lib3mf_booleanobject_getoperand = methodType(int(methodAddress.value)) + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_beamlattice_getminlength")), methodAddress) if err != 0: raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) @@ -4966,6 +5080,12 @@ def _loadFunctionTableFromMethod(self, symbolLookupMethodAddress): methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_void_p)) self.lib.lib3mf_model_getcomponentsobjectbyid = methodType(int(methodAddress.value)) + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_model_getbooleanobjectbyid")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_void_p)) + self.lib.lib3mf_model_getbooleanobjectbyid = methodType(int(methodAddress.value)) + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_model_getcolorgroupbyid")), methodAddress) if err != 0: raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) @@ -5032,6 +5152,12 @@ def _loadFunctionTableFromMethod(self, symbolLookupMethodAddress): methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)) self.lib.lib3mf_model_getcomponentsobjects = methodType(int(methodAddress.value)) + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_model_getbooleanobjects")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)) + self.lib.lib3mf_model_getbooleanobjects = methodType(int(methodAddress.value)) + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_model_gettexture2ds")), methodAddress) if err != 0: raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) @@ -5104,6 +5230,12 @@ def _loadFunctionTableFromMethod(self, symbolLookupMethodAddress): methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)) self.lib.lib3mf_model_addcomponentsobject = methodType(int(methodAddress.value)) + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_model_addbooleanobject")), methodAddress) + if err != 0: + raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) + methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)) + self.lib.lib3mf_model_addbooleanobject = methodType(int(methodAddress.value)) + err = symbolLookupMethod(ctypes.c_char_p(str.encode("lib3mf_model_addslicestack")), methodAddress) if err != 0: raise ELib3MFException(ErrorCodes.COULDNOTLOADLIBRARY, str(err)) @@ -5484,6 +5616,9 @@ def _loadFunctionTable(self): self.lib.lib3mf_componentsobjectiterator_getcurrentcomponentsobject.restype = ctypes.c_int32 self.lib.lib3mf_componentsobjectiterator_getcurrentcomponentsobject.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)] + self.lib.lib3mf_booleanobjectiterator_getcurrentbooleanobject.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobjectiterator_getcurrentbooleanobject.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)] + self.lib.lib3mf_texture2diterator_getcurrenttexture2d.restype = ctypes.c_int32 self.lib.lib3mf_texture2diterator_getcurrenttexture2d.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)] @@ -5628,6 +5763,9 @@ def _loadFunctionTable(self): self.lib.lib3mf_object_islevelsetobject.restype = ctypes.c_int32 self.lib.lib3mf_object_islevelsetobject.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_bool)] + self.lib.lib3mf_object_isbooleanobject.restype = ctypes.c_int32 + self.lib.lib3mf_object_isbooleanobject.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_bool)] + self.lib.lib3mf_object_isvalid.restype = ctypes.c_int32 self.lib.lib3mf_object_isvalid.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_bool)] @@ -5799,6 +5937,45 @@ def _loadFunctionTable(self): self.lib.lib3mf_levelset_setvolumedata.restype = ctypes.c_int32 self.lib.lib3mf_levelset_setvolumedata.argtypes = [ctypes.c_void_p, ctypes.c_void_p] + self.lib.lib3mf_booleanobject_setbaseobject.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobject_setbaseobject.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.POINTER(Transform)] + + self.lib.lib3mf_booleanobject_getbaseobject.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobject_getbaseobject.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)] + + self.lib.lib3mf_booleanobject_setbasetransform.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobject_setbasetransform.argtypes = [ctypes.c_void_p, ctypes.POINTER(Transform)] + + self.lib.lib3mf_booleanobject_getbasetransform.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobject_getbasetransform.argtypes = [ctypes.c_void_p, ctypes.POINTER(Transform)] + + self.lib.lib3mf_booleanobject_setoperation.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobject_setoperation.argtypes = [ctypes.c_void_p, BooleanOperation] + + self.lib.lib3mf_booleanobject_getoperation.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobject_getoperation.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_int32)] + + self.lib.lib3mf_booleanobject_setcsgmodeenabled.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobject_setcsgmodeenabled.argtypes = [ctypes.c_void_p, ctypes.c_bool] + + self.lib.lib3mf_booleanobject_getcsgmodeenabled.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobject_getcsgmodeenabled.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_bool)] + + self.lib.lib3mf_booleanobject_setextractiongridresolution.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobject_setextractiongridresolution.argtypes = [ctypes.c_void_p, ctypes.c_uint32] + + self.lib.lib3mf_booleanobject_getextractiongridresolution.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobject_getextractiongridresolution.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint32)] + + self.lib.lib3mf_booleanobject_getoperandcount.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobject_getoperandcount.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint32)] + + self.lib.lib3mf_booleanobject_addoperand.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobject_addoperand.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.POINTER(Transform)] + + self.lib.lib3mf_booleanobject_getoperand.restype = ctypes.c_int32 + self.lib.lib3mf_booleanobject_getoperand.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(Transform)] + self.lib.lib3mf_beamlattice_getminlength.restype = ctypes.c_int32 self.lib.lib3mf_beamlattice_getminlength.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_double)] @@ -7074,6 +7251,9 @@ def _loadFunctionTable(self): self.lib.lib3mf_model_getcomponentsobjectbyid.restype = ctypes.c_int32 self.lib.lib3mf_model_getcomponentsobjectbyid.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_void_p)] + self.lib.lib3mf_model_getbooleanobjectbyid.restype = ctypes.c_int32 + self.lib.lib3mf_model_getbooleanobjectbyid.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_void_p)] + self.lib.lib3mf_model_getcolorgroupbyid.restype = ctypes.c_int32 self.lib.lib3mf_model_getcolorgroupbyid.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_void_p)] @@ -7107,6 +7287,9 @@ def _loadFunctionTable(self): self.lib.lib3mf_model_getcomponentsobjects.restype = ctypes.c_int32 self.lib.lib3mf_model_getcomponentsobjects.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)] + self.lib.lib3mf_model_getbooleanobjects.restype = ctypes.c_int32 + self.lib.lib3mf_model_getbooleanobjects.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)] + self.lib.lib3mf_model_gettexture2ds.restype = ctypes.c_int32 self.lib.lib3mf_model_gettexture2ds.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)] @@ -7143,6 +7326,9 @@ def _loadFunctionTable(self): self.lib.lib3mf_model_addcomponentsobject.restype = ctypes.c_int32 self.lib.lib3mf_model_addcomponentsobject.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)] + self.lib.lib3mf_model_addbooleanobject.restype = ctypes.c_int32 + self.lib.lib3mf_model_addbooleanobject.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)] + self.lib.lib3mf_model_addslicestack.restype = ctypes.c_int32 self.lib.lib3mf_model_addslicestack.argtypes = [ctypes.c_void_p, ctypes.c_double, ctypes.POINTER(ctypes.c_void_p)] @@ -7469,6 +7655,8 @@ def getObjectById_F4196034E2B9FDE6(self, handle, wrapper): # First 64 bits of SH return MeshObjectIterator(handle, wrapper) def getObjectById_564DE4217ED7614A(self, handle, wrapper): # First 64 bits of SHA1 of a string: "Lib3MF::ComponentsObjectIterator" return ComponentsObjectIterator(handle, wrapper) + def getObjectById_AFF01F512E1FF6AE(self, handle, wrapper): # First 64 bits of SHA1 of a string: "Lib3MF::BooleanObjectIterator" + return BooleanObjectIterator(handle, wrapper) def getObjectById_4BD32B4870FFC03B(self, handle, wrapper): # First 64 bits of SHA1 of a string: "Lib3MF::Texture2DIterator" return Texture2DIterator(handle, wrapper) def getObjectById_65E6EDD9362C79CB(self, handle, wrapper): # First 64 bits of SHA1 of a string: "Lib3MF::BaseMaterialGroupIterator" @@ -7499,6 +7687,8 @@ def getObjectById_3B3A6DC6EC610497(self, handle, wrapper): # First 64 bits of SH return MeshObject(handle, wrapper) def getObjectById_E8A7D9C192EFD0E2(self, handle, wrapper): # First 64 bits of SHA1 of a string: "Lib3MF::LevelSet" return LevelSet(handle, wrapper) + def getObjectById_85FA0E8806B6C357(self, handle, wrapper): # First 64 bits of SHA1 of a string: "Lib3MF::BooleanObject" + return BooleanObject(handle, wrapper) def getObjectById_63B3B461B30B4BA5(self, handle, wrapper): # First 64 bits of SHA1 of a string: "Lib3MF::BeamLattice" return BeamLattice(handle, wrapper) def getObjectById_4DF17E76926221C2(self, handle, wrapper): # First 64 bits of SHA1 of a string: "Lib3MF::FunctionReference" @@ -8067,6 +8257,23 @@ def GetCurrentComponentsObject(self): +''' Class Implementation for BooleanObjectIterator +''' +class BooleanObjectIterator(ResourceIterator): + def __init__(self, handle, wrapper): + ResourceIterator.__init__(self, handle, wrapper) + def GetCurrentBooleanObject(self): + ResourceHandle = ctypes.c_void_p() + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobjectiterator_getcurrentbooleanobject(self._handle, ResourceHandle)) + if ResourceHandle: + ResourceObject = self._wrapper._polymorphicFactory(ResourceHandle) + else: + raise ELib3MFException(ErrorCodes.INVALIDCAST, 'Invalid return/output value') + + return ResourceObject + + + ''' Class Implementation for Texture2DIterator ''' class Texture2DIterator(ResourceIterator): @@ -8544,6 +8751,12 @@ def IsLevelSetObject(self): return pIsLevelSetObject.value + def IsBooleanObject(self): + pIsBooleanObject = ctypes.c_bool() + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_object_isbooleanobject(self._handle, pIsBooleanObject)) + + return pIsBooleanObject.value + def IsValid(self): pIsValid = ctypes.c_bool() self._wrapper.checkError(self, self._wrapper.lib.lib3mf_object_isvalid(self._handle, pIsValid)) @@ -8985,6 +9198,105 @@ def SetVolumeData(self, TheVolumeDataObject): +''' Class Implementation for BooleanObject +''' +class BooleanObject(Object): + def __init__(self, handle, wrapper): + Object.__init__(self, handle, wrapper) + def SetBaseObject(self, BaseObjectObject, Transform): + BaseObjectHandle = None + if BaseObjectObject: + BaseObjectHandle = BaseObjectObject._handle + else: + raise ELib3MFException(ErrorCodes.INVALIDPARAM, 'Invalid return/output value') + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobject_setbaseobject(self._handle, BaseObjectHandle, Transform)) + + + def GetBaseObject(self): + BaseObjectHandle = ctypes.c_void_p() + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobject_getbaseobject(self._handle, BaseObjectHandle)) + if BaseObjectHandle: + BaseObjectObject = self._wrapper._polymorphicFactory(BaseObjectHandle) + else: + raise ELib3MFException(ErrorCodes.INVALIDCAST, 'Invalid return/output value') + + return BaseObjectObject + + def SetBaseTransform(self, Transform): + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobject_setbasetransform(self._handle, Transform)) + + + def GetBaseTransform(self): + pTransform = Transform() + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobject_getbasetransform(self._handle, pTransform)) + + return pTransform + + def SetOperation(self, Operation): + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobject_setoperation(self._handle, Operation)) + + + def GetOperation(self): + pOperation = ctypes.c_int32() + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobject_getoperation(self._handle, pOperation)) + + return BooleanOperation(pOperation.value) + + def SetCSGModeEnabled(self, CSGModeEnabled): + bCSGModeEnabled = ctypes.c_bool(CSGModeEnabled) + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobject_setcsgmodeenabled(self._handle, bCSGModeEnabled)) + + + def GetCSGModeEnabled(self): + pCSGModeEnabled = ctypes.c_bool() + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobject_getcsgmodeenabled(self._handle, pCSGModeEnabled)) + + return pCSGModeEnabled.value + + def SetExtractionGridResolution(self, GridResolution): + nGridResolution = ctypes.c_uint32(GridResolution) + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobject_setextractiongridresolution(self._handle, nGridResolution)) + + + def GetExtractionGridResolution(self): + pGridResolution = ctypes.c_uint32() + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobject_getextractiongridresolution(self._handle, pGridResolution)) + + return pGridResolution.value + + def GetOperandCount(self): + pCount = ctypes.c_uint32() + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobject_getoperandcount(self._handle, pCount)) + + return pCount.value + + def AddOperand(self, OperandObjectObject, Transform): + OperandObjectHandle = None + if OperandObjectObject: + OperandObjectHandle = OperandObjectObject._handle + else: + raise ELib3MFException(ErrorCodes.INVALIDPARAM, 'Invalid return/output value') + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobject_addoperand(self._handle, OperandObjectHandle, Transform)) + + + def GetOperand(self, Index, OperandObjectObject = None): + nIndex = ctypes.c_uint32(Index) + OperandObjectHandle = ctypes.c_void_p() + if OperandObjectObject is not None: + OperandObjectHandle = ctypes.c_void_p(OperandObjectObject._handle) + else: + OperandObjectHandle = ctypes.c_void_p() + pTransform = Transform() + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_booleanobject_getoperand(self._handle, nIndex, OperandObjectHandle, pTransform)) + if OperandObjectHandle.value: + OperandObjectObject = self._wrapper._polymorphicFactory(OperandObjectHandle.value) + else: + OperandObjectObject = None + + return OperandObjectObject, pTransform + + + ''' Class Implementation for BeamLattice ''' class BeamLattice(Base): @@ -13451,6 +13763,17 @@ def GetComponentsObjectByID(self, UniqueResourceID): return ComponentsObjectInstanceObject + def GetBooleanObjectByID(self, UniqueResourceID): + nUniqueResourceID = ctypes.c_uint32(UniqueResourceID) + BooleanObjectInstanceHandle = ctypes.c_void_p() + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_model_getbooleanobjectbyid(self._handle, nUniqueResourceID, BooleanObjectInstanceHandle)) + if BooleanObjectInstanceHandle: + BooleanObjectInstanceObject = self._wrapper._polymorphicFactory(BooleanObjectInstanceHandle) + else: + raise ELib3MFException(ErrorCodes.INVALIDCAST, 'Invalid return/output value') + + return BooleanObjectInstanceObject + def GetColorGroupByID(self, UniqueResourceID): nUniqueResourceID = ctypes.c_uint32(UniqueResourceID) ColorGroupInstanceHandle = ctypes.c_void_p() @@ -13557,6 +13880,16 @@ def GetComponentsObjects(self): return ResourceIteratorObject + def GetBooleanObjects(self): + ResourceIteratorHandle = ctypes.c_void_p() + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_model_getbooleanobjects(self._handle, ResourceIteratorHandle)) + if ResourceIteratorHandle: + ResourceIteratorObject = self._wrapper._polymorphicFactory(ResourceIteratorHandle) + else: + raise ELib3MFException(ErrorCodes.INVALIDCAST, 'Invalid return/output value') + + return ResourceIteratorObject + def GetTexture2Ds(self): ResourceIteratorHandle = ctypes.c_void_p() self._wrapper.checkError(self, self._wrapper.lib.lib3mf_model_gettexture2ds(self._handle, ResourceIteratorHandle)) @@ -13676,6 +14009,16 @@ def AddComponentsObject(self): return ComponentsObjectInstanceObject + def AddBooleanObject(self): + BooleanObjectInstanceHandle = ctypes.c_void_p() + self._wrapper.checkError(self, self._wrapper.lib.lib3mf_model_addbooleanobject(self._handle, BooleanObjectInstanceHandle)) + if BooleanObjectInstanceHandle: + BooleanObjectInstanceObject = self._wrapper._polymorphicFactory(BooleanObjectInstanceHandle) + else: + raise ELib3MFException(ErrorCodes.INVALIDCAST, 'Invalid return/output value') + + return BooleanObjectInstanceObject + def AddSliceStack(self, ZBottom): dZBottom = ctypes.c_double(ZBottom) SliceStackInstanceHandle = ctypes.c_void_p() diff --git a/Autogenerated/Bindings/WASM/lib3mf_bindings.cpp b/Autogenerated/Bindings/WASM/lib3mf_bindings.cpp index d5e8fa3c5..67e8f0e01 100644 --- a/Autogenerated/Bindings/WASM/lib3mf_bindings.cpp +++ b/Autogenerated/Bindings/WASM/lib3mf_bindings.cpp @@ -28,7 +28,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: C++ Emscripten wrapper for WebAssembly -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -504,6 +504,25 @@ static void wrap_LevelSet_SetVolumeData(CLevelSet &self, PVolumeData& TheVolumeD self.SetVolumeData(classParam(TheVolumeData)); } +static void wrap_BooleanObject_SetBaseObject(CBooleanObject &self, PObject& BaseObject, const sTransformWrapper& Transform) { + self.SetBaseObject(classParam(BaseObject), Transform.toStruct()); +} + +static void wrap_BooleanObject_SetBaseTransform(CBooleanObject &self, const sTransformWrapper& Transform) { + self.SetBaseTransform(Transform.toStruct()); +} + +static sTransformWrapper wrap_BooleanObject_GetBaseTransform(CBooleanObject &self) { + auto result = self.GetBaseTransform(); + sTransformWrapper wrapper; + wrapper.value = result; + return wrapper; +} + +static void wrap_BooleanObject_AddOperand(CBooleanObject &self, PMeshObject& OperandObject, const sTransformWrapper& Transform) { + self.AddOperand(classParam(OperandObject), Transform.toStruct()); +} + static sBeamWrapper wrap_BeamLattice_GetBeam(CBeamLattice &self, const Lib3MF_uint32& Index) { auto result = self.GetBeam(Index); sBeamWrapper wrapper; @@ -929,7 +948,10 @@ static emscripten::val wrap_MeshObject_GetVertices(CMeshObject &self) { emscripten::val output = emscripten::val::object(); std::vector Vertices; self.GetVertices(Vertices); - output.set("Vertices", Vertices); + std::vector wrapped_Vertices; + wrapped_Vertices.reserve(Vertices.size()); + for (const auto& v : Vertices) wrapped_Vertices.push_back(sPositionWrapper{v}); + output.set("Vertices", wrapped_Vertices); return output; } @@ -937,7 +959,10 @@ static emscripten::val wrap_MeshObject_GetTriangleIndices(CMeshObject &self) { emscripten::val output = emscripten::val::object(); std::vector Indices; self.GetTriangleIndices(Indices); - output.set("Indices", Indices); + std::vector wrapped_Indices; + wrapped_Indices.reserve(Indices.size()); + for (const auto& v : Indices) wrapped_Indices.push_back(sTriangleWrapper{v}); + output.set("Indices", wrapped_Indices); return output; } @@ -964,7 +989,20 @@ static emscripten::val wrap_MeshObject_GetAllTriangleProperties(CMeshObject &sel emscripten::val output = emscripten::val::object(); std::vector PropertiesArray; self.GetAllTriangleProperties(PropertiesArray); - output.set("PropertiesArray", PropertiesArray); + std::vector wrapped_PropertiesArray; + wrapped_PropertiesArray.reserve(PropertiesArray.size()); + for (const auto& v : PropertiesArray) wrapped_PropertiesArray.push_back(sTrianglePropertiesWrapper{v}); + output.set("PropertiesArray", wrapped_PropertiesArray); + return output; +} + +static emscripten::val wrap_BooleanObject_GetOperand(CBooleanObject &self, const Lib3MF_uint32& Index) { + emscripten::val output = emscripten::val::object(); + PMeshObject OperandObject; + sTransformWrapper return_value; + return_value.value = self.GetOperand(Index, OperandObject); + output.set("return", return_value); + output.set("OperandObject", OperandObject); return output; } @@ -1001,7 +1039,10 @@ static emscripten::val wrap_BeamLattice_GetBeams(CBeamLattice &self) { emscripten::val output = emscripten::val::object(); std::vector BeamInfo; self.GetBeams(BeamInfo); - output.set("BeamInfo", BeamInfo); + std::vector wrapped_BeamInfo; + wrapped_BeamInfo.reserve(BeamInfo.size()); + for (const auto& v : BeamInfo) wrapped_BeamInfo.push_back(sBeamWrapper{v}); + output.set("BeamInfo", wrapped_BeamInfo); return output; } @@ -1009,7 +1050,10 @@ static emscripten::val wrap_BeamLattice_GetBalls(CBeamLattice &self) { emscripten::val output = emscripten::val::object(); std::vector BallInfo; self.GetBalls(BallInfo); - output.set("BallInfo", BallInfo); + std::vector wrapped_BallInfo; + wrapped_BallInfo.reserve(BallInfo.size()); + for (const auto& v : BallInfo) wrapped_BallInfo.push_back(sBallWrapper{v}); + output.set("BallInfo", wrapped_BallInfo); return output; } @@ -1074,7 +1118,10 @@ static emscripten::val wrap_CompositeMaterials_GetComposite(CCompositeMaterials emscripten::val output = emscripten::val::object(); std::vector Composite; self.GetComposite(PropertyID, Composite); - output.set("Composite", Composite); + std::vector wrapped_Composite; + wrapped_Composite.reserve(Composite.size()); + for (const auto& v : Composite) wrapped_Composite.push_back(sCompositeConstituentWrapper{v}); + output.set("Composite", wrapped_Composite); return output; } @@ -1137,7 +1184,10 @@ static emscripten::val wrap_Slice_GetVertices(CSlice &self) { emscripten::val output = emscripten::val::object(); std::vector Vertices; self.GetVertices(Vertices); - output.set("Vertices", Vertices); + std::vector wrapped_Vertices; + wrapped_Vertices.reserve(Vertices.size()); + for (const auto& v : Vertices) wrapped_Vertices.push_back(sPosition2DWrapper{v}); + output.set("Vertices", wrapped_Vertices); return output; } @@ -1326,6 +1376,11 @@ EMSCRIPTEN_BINDINGS(Lib3MF) { .value("SolidSupport", eObjectType::SolidSupport) .value("Surface", eObjectType::Surface) ; + enum_("eBooleanOperation") + .value("Union", eBooleanOperation::Union) + .value("Difference", eBooleanOperation::Difference) + .value("Intersection", eBooleanOperation::Intersection) + ; enum_("eTextureType") .value("Unknown", eTextureType::Unknown) .value("PNG", eTextureType::PNG) @@ -1776,6 +1831,10 @@ EMSCRIPTEN_BINDINGS(Lib3MF) { .smart_ptr>("shared_ptr") .function("GetCurrentComponentsObject", &CComponentsObjectIterator::GetCurrentComponentsObject) ; + class_>("CBooleanObjectIterator") + .smart_ptr>("shared_ptr") + .function("GetCurrentBooleanObject", &CBooleanObjectIterator::GetCurrentBooleanObject) + ; class_>("CTexture2DIterator") .smart_ptr>("shared_ptr") .function("GetCurrentTexture2D", &CTexture2DIterator::GetCurrentTexture2D) @@ -1862,6 +1921,7 @@ EMSCRIPTEN_BINDINGS(Lib3MF) { .function("IsMeshObject", &CObject::IsMeshObject) .function("IsComponentsObject", &CObject::IsComponentsObject) .function("IsLevelSetObject", &CObject::IsLevelSetObject) + .function("IsBooleanObject", &CObject::IsBooleanObject) .function("IsValid", &CObject::IsValid) .function("SetAttachmentAsThumbnail", &wrap_Object_SetAttachmentAsThumbnail) .function("GetThumbnailAttachment", &CObject::GetThumbnailAttachment) @@ -1926,6 +1986,22 @@ EMSCRIPTEN_BINDINGS(Lib3MF) { .function("GetVolumeData", &CLevelSet::GetVolumeData) .function("SetVolumeData", &wrap_LevelSet_SetVolumeData) ; + class_>("CBooleanObject") + .smart_ptr>("shared_ptr") + .function("SetBaseObject", &wrap_BooleanObject_SetBaseObject) + .function("GetBaseObject", &CBooleanObject::GetBaseObject) + .function("SetBaseTransform", &wrap_BooleanObject_SetBaseTransform) + .function("GetBaseTransform", &wrap_BooleanObject_GetBaseTransform) + .function("SetOperation", &CBooleanObject::SetOperation) + .function("GetOperation", &CBooleanObject::GetOperation) + .function("SetCSGModeEnabled", &CBooleanObject::SetCSGModeEnabled) + .function("GetCSGModeEnabled", &CBooleanObject::GetCSGModeEnabled) + .function("SetExtractionGridResolution", &CBooleanObject::SetExtractionGridResolution) + .function("GetExtractionGridResolution", &CBooleanObject::GetExtractionGridResolution) + .function("GetOperandCount", &CBooleanObject::GetOperandCount) + .function("AddOperand", &wrap_BooleanObject_AddOperand) + .function("GetOperand", &wrap_BooleanObject_GetOperand) + ; class_>("CBeamLattice") .smart_ptr>("shared_ptr") .function("GetMinLength", &CBeamLattice::GetMinLength) @@ -2632,6 +2708,7 @@ EMSCRIPTEN_BINDINGS(Lib3MF) { .function("GetMultiPropertyGroupByID", &CModel::GetMultiPropertyGroupByID) .function("GetMeshObjectByID", &CModel::GetMeshObjectByID) .function("GetComponentsObjectByID", &CModel::GetComponentsObjectByID) + .function("GetBooleanObjectByID", &CModel::GetBooleanObjectByID) .function("GetColorGroupByID", &CModel::GetColorGroupByID) .function("GetSliceStackByID", &CModel::GetSliceStackByID) .function("GetLevelSetByID", &CModel::GetLevelSetByID) @@ -2643,6 +2720,7 @@ EMSCRIPTEN_BINDINGS(Lib3MF) { .function("GetObjects", &CModel::GetObjects) .function("GetMeshObjects", &CModel::GetMeshObjects) .function("GetComponentsObjects", &CModel::GetComponentsObjects) + .function("GetBooleanObjects", &CModel::GetBooleanObjects) .function("GetTexture2Ds", &CModel::GetTexture2Ds) .function("GetBaseMaterialGroups", &CModel::GetBaseMaterialGroups) .function("GetColorGroups", &CModel::GetColorGroups) @@ -2655,6 +2733,7 @@ EMSCRIPTEN_BINDINGS(Lib3MF) { .function("MergeFromModel", &wrap_Model_MergeFromModel) .function("AddMeshObject", &CModel::AddMeshObject) .function("AddComponentsObject", &CModel::AddComponentsObject) + .function("AddBooleanObject", &CModel::AddBooleanObject) .function("AddSliceStack", &CModel::AddSliceStack) .function("AddTexture2DFromAttachment", &wrap_Model_AddTexture2DFromAttachment) .function("AddBaseMaterialGroup", &CModel::AddBaseMaterialGroup) diff --git a/Autogenerated/Source/lib3mf_abi.hpp b/Autogenerated/Source/lib3mf_abi.hpp index 0c558039c..d8ee45244 100644 --- a/Autogenerated/Source/lib3mf_abi.hpp +++ b/Autogenerated/Source/lib3mf_abi.hpp @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++-Header file in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -497,6 +497,19 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_meshobjectiterator_getcurrentmeshobject(Lib3 */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_componentsobjectiterator_getcurrentcomponentsobject(Lib3MF_ComponentsObjectIterator pComponentsObjectIterator, Lib3MF_ComponentsObject * pResource); +/************************************************************************************************************************* + Class definition for BooleanObjectIterator +**************************************************************************************************************************/ + +/** +* Returns the BooleanObject the iterator points at. +* +* @param[in] pBooleanObjectIterator - BooleanObjectIterator instance. +* @param[out] pResource - returns the BooleanObject instance. +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobjectiterator_getcurrentbooleanobject(Lib3MF_BooleanObjectIterator pBooleanObjectIterator, Lib3MF_BooleanObject * pResource); + /************************************************************************************************************************* Class definition for Texture2DIterator **************************************************************************************************************************/ @@ -1011,6 +1024,15 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_object_iscomponentsobject(Lib3MF_Object pObj */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_object_islevelsetobject(Lib3MF_Object pObject, bool * pIsLevelSetObject); +/** +* Retrieves, if an object is a boolean object +* +* @param[in] pObject - Object instance. +* @param[out] pIsBooleanObject - returns, whether the object is a boolean object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_object_isbooleanobject(Lib3MF_Object pObject, bool * pIsBooleanObject); + /** * Retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. * @@ -1561,6 +1583,131 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_levelset_getvolumedata(Lib3MF_LevelSet pLeve */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_levelset_setvolumedata(Lib3MF_LevelSet pLevelSet, Lib3MF_VolumeData pTheVolumeData); +/************************************************************************************************************************* + Class definition for BooleanObject +**************************************************************************************************************************/ + +/** +* Sets the base object and transform for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pBaseObject - base object of the boolean shape +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setbaseobject(Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object pBaseObject, const Lib3MF::sTransform * pTransform); + +/** +* Returns the base object of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pBaseObject - base object of the boolean shape +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getbaseobject(Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object * pBaseObject); + +/** +* Sets the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setbasetransform(Lib3MF_BooleanObject pBooleanObject, const Lib3MF::sTransform * pTransform); + +/** +* Returns the base transform of the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pTransform - transform applied to the base object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getbasetransform(Lib3MF_BooleanObject pBooleanObject, Lib3MF::sTransform * pTransform); + +/** +* Sets the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] eOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setoperation(Lib3MF_BooleanObject pBooleanObject, Lib3MF::eBooleanOperation eOperation); + +/** +* Returns the boolean operation used for the boolean shape. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pOperation - boolean operation used for the shape +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getoperation(Lib3MF_BooleanObject pBooleanObject, Lib3MF::eBooleanOperation * pOperation); + +/** +* Enables or disables CSG field evaluation for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] bCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setcsgmodeenabled(Lib3MF_BooleanObject pBooleanObject, bool bCSGModeEnabled); + +/** +* Returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getcsgmodeenabled(Lib3MF_BooleanObject pBooleanObject, bool * pCSGModeEnabled); + +/** +* Sets the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_setextractiongridresolution(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nGridResolution); + +/** +* Returns the extraction grid resolution used for boolean-to-mesh materialization. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pGridResolution - extraction grid resolution for boolean surface extraction +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getextractiongridresolution(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pGridResolution); + +/** +* Returns the number of operands in the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[out] pCount - number of operands in the boolean sequence +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getoperandcount(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pCount); + +/** +* Adds an operand object to the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] pOperandObject - mesh object used as operand +* @param[in] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_addoperand(Lib3MF_BooleanObject pBooleanObject, Lib3MF_MeshObject pOperandObject, const Lib3MF::sTransform * pTransform); + +/** +* Returns one operand object and transform from the boolean sequence. +* +* @param[in] pBooleanObject - BooleanObject instance. +* @param[in] nIndex - index of the operand in the boolean sequence +* @param[out] pOperandObject - mesh object used as operand +* @param[out] pTransform - transform applied to the operand object +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_booleanobject_getoperand(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nIndex, Lib3MF_MeshObject * pOperandObject, Lib3MF::sTransform * pTransform); + /************************************************************************************************************************* Class definition for BeamLattice **************************************************************************************************************************/ @@ -6178,6 +6325,16 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getmeshobjectbyid(Lib3MF_Model pModel, */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getcomponentsobjectbyid(Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* finds a boolean object by its UniqueResourceID +* +* @param[in] pModel - Model instance. +* @param[in] nUniqueResourceID - UniqueResourceID +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getbooleanobjectbyid(Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * finds a model color group by its UniqueResourceID * @@ -6283,6 +6440,15 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getmeshobjects(Lib3MF_Model pModel, Li */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getcomponentsobjects(Lib3MF_Model pModel, Lib3MF_ComponentsObjectIterator * pResourceIterator); +/** +* creates a resource iterator instance with all boolean object resources. +* +* @param[in] pModel - Model instance. +* @param[out] pResourceIterator - returns the iterator instance. +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_getbooleanobjects(Lib3MF_Model pModel, Lib3MF_BooleanObjectIterator * pResourceIterator); + /** * creates a Texture2DIterator instance with all texture2d resources. * @@ -6391,6 +6557,15 @@ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_addmeshobject(Lib3MF_Model pModel, Lib */ LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_addcomponentsobject(Lib3MF_Model pModel, Lib3MF_ComponentsObject * pComponentsObjectInstance); +/** +* adds an empty boolean object to the model. +* +* @param[in] pModel - Model instance. +* @param[out] pBooleanObjectInstance - returns the boolean object instance +* @return error code or 0 (success) +*/ +LIB3MF_DECLSPEC Lib3MFResult lib3mf_model_addbooleanobject(Lib3MF_Model pModel, Lib3MF_BooleanObject * pBooleanObjectInstance); + /** * creates a new model slicestack by its id * diff --git a/Autogenerated/Source/lib3mf_interfaceexception.cpp b/Autogenerated/Source/lib3mf_interfaceexception.cpp index 2a9b0d94c..29b472b15 100644 --- a/Autogenerated/Source/lib3mf_interfaceexception.cpp +++ b/Autogenerated/Source/lib3mf_interfaceexception.cpp @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++ Implementation file with the basic internal exception type in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Source/lib3mf_interfaceexception.hpp b/Autogenerated/Source/lib3mf_interfaceexception.hpp index 51201c246..e84ae8dba 100644 --- a/Autogenerated/Source/lib3mf_interfaceexception.hpp +++ b/Autogenerated/Source/lib3mf_interfaceexception.hpp @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++ Header file with the basic internal exception type in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Source/lib3mf_interfacejournal.cpp b/Autogenerated/Source/lib3mf_interfacejournal.cpp index d793b240c..352b7774d 100644 --- a/Autogenerated/Source/lib3mf_interfacejournal.cpp +++ b/Autogenerated/Source/lib3mf_interfacejournal.cpp @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++ implementation file in order to allow easy development of the 3MF Library. It provides an automatic Journaling mechanism for the library implementation. -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -286,7 +286,7 @@ CLib3MFInterfaceJournal::CLib3MFInterfaceJournal (const std::string & sFileName) m_StartTime = std::chrono::high_resolution_clock::now(); m_Stream.open (sFileName, std::ios::out); m_Stream << "\n"; - m_Stream << "\n"; + m_Stream << "\n"; m_Stream << "\n"; } diff --git a/Autogenerated/Source/lib3mf_interfacejournal.hpp b/Autogenerated/Source/lib3mf_interfacejournal.hpp index ce6e58958..22faf6631 100644 --- a/Autogenerated/Source/lib3mf_interfacejournal.hpp +++ b/Autogenerated/Source/lib3mf_interfacejournal.hpp @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++ header file in order to allow easy development of the 3MF Library. It provides an automatic Journaling mechanism for the library implementation. -Interface version: 2.5.0 +Interface version: 2.6.0 */ diff --git a/Autogenerated/Source/lib3mf_interfaces.hpp b/Autogenerated/Source/lib3mf_interfaces.hpp index 186774844..c536bb421 100644 --- a/Autogenerated/Source/lib3mf_interfaces.hpp +++ b/Autogenerated/Source/lib3mf_interfaces.hpp @@ -30,7 +30,7 @@ Abstract: This is an autogenerated C++ header file in order to allow easy development of the 3MF Library. The implementer of the 3MF Library needs to derive concrete classes from the abstract classes in this header. -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -61,6 +61,7 @@ class ISliceStackIterator; class IObjectIterator; class IMeshObjectIterator; class IComponentsObjectIterator; +class IBooleanObjectIterator; class ITexture2DIterator; class IBaseMaterialGroupIterator; class IColorGroupIterator; @@ -76,6 +77,7 @@ class ITriangleSet; class IObject; class IMeshObject; class ILevelSet; +class IBooleanObject; class IBeamLattice; class IFunctionReference; class IVolumeDataColor; @@ -822,6 +824,32 @@ class IComponentsObjectIterator : public virtual IResourceIterator { typedef IBaseSharedPtr PIComponentsObjectIterator; +/************************************************************************************************************************* + Class interface for BooleanObjectIterator +**************************************************************************************************************************/ + +class IBooleanObjectIterator : public virtual IResourceIterator { +public: + /** + * IBooleanObjectIterator::ClassTypeId - Get Class Type Id + * @return Class type as a 64 bits integer + */ + Lib3MF_uint64 ClassTypeId() override + { + return 0xAFF01F512E1FF6AEUL; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObjectIterator" + } + + /** + * IBooleanObjectIterator::GetCurrentBooleanObject - Returns the BooleanObject the iterator points at. + * @return returns the BooleanObject instance. + */ + virtual IBooleanObject * GetCurrentBooleanObject() = 0; + +}; + +typedef IBaseSharedPtr PIBooleanObjectIterator; + + /************************************************************************************************************************* Class interface for Texture2DIterator **************************************************************************************************************************/ @@ -1377,6 +1405,12 @@ class IObject : public virtual IResource { */ virtual bool IsLevelSetObject() = 0; + /** + * IObject::IsBooleanObject - Retrieves, if an object is a boolean object + * @return returns, whether the object is a boolean object + */ + virtual bool IsBooleanObject() = 0; + /** * IObject::IsValid - Retrieves, if the object is valid according to the core spec. For mesh objects, we distinguish between the type attribute of the object:In case of object type other, this always means false.In case of object type model or solidsupport, this means, if the mesh suffices all requirements of the core spec chapter 4.1.In case of object type support or surface, this always means true.A component objects is valid if and only if it contains at least one component and all child components are valid objects. * @return returns whether the object is a valid object description @@ -1789,6 +1823,108 @@ class ILevelSet : public virtual IObject { typedef IBaseSharedPtr PILevelSet; +/************************************************************************************************************************* + Class interface for BooleanObject +**************************************************************************************************************************/ + +class IBooleanObject : public virtual IObject { +public: + /** + * IBooleanObject::ClassTypeId - Get Class Type Id + * @return Class type as a 64 bits integer + */ + Lib3MF_uint64 ClassTypeId() override + { + return 0x85FA0E8806B6C357UL; // First 64 bits of SHA1 of a string: "Lib3MF::BooleanObject" + } + + /** + * IBooleanObject::SetBaseObject - Sets the base object and transform for the boolean shape. + * @param[in] pBaseObject - base object of the boolean shape + * @param[in] Transform - transform applied to the base object + */ + virtual void SetBaseObject(IObject* pBaseObject, const Lib3MF::sTransform Transform) = 0; + + /** + * IBooleanObject::GetBaseObject - Returns the base object of the boolean shape. + * @return base object of the boolean shape + */ + virtual IObject * GetBaseObject() = 0; + + /** + * IBooleanObject::SetBaseTransform - Sets the base transform of the boolean shape. + * @param[in] Transform - transform applied to the base object + */ + virtual void SetBaseTransform(const Lib3MF::sTransform Transform) = 0; + + /** + * IBooleanObject::GetBaseTransform - Returns the base transform of the boolean shape. + * @return transform applied to the base object + */ + virtual Lib3MF::sTransform GetBaseTransform() = 0; + + /** + * IBooleanObject::SetOperation - Sets the boolean operation used for the boolean shape. + * @param[in] eOperation - boolean operation used for the shape + */ + virtual void SetOperation(const Lib3MF::eBooleanOperation eOperation) = 0; + + /** + * IBooleanObject::GetOperation - Returns the boolean operation used for the boolean shape. + * @return boolean operation used for the shape + */ + virtual Lib3MF::eBooleanOperation GetOperation() = 0; + + /** + * IBooleanObject::SetCSGModeEnabled - Enables or disables CSG field evaluation for boolean-to-mesh materialization. + * @param[in] bCSGModeEnabled - if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback + */ + virtual void SetCSGModeEnabled(const bool bCSGModeEnabled) = 0; + + /** + * IBooleanObject::GetCSGModeEnabled - Returns whether CSG field evaluation is enabled for boolean-to-mesh materialization. + * @return if true, boolean materialization uses CSG field evaluation; otherwise, uses flattening fallback + */ + virtual bool GetCSGModeEnabled() = 0; + + /** + * IBooleanObject::SetExtractionGridResolution - Sets the extraction grid resolution used for boolean-to-mesh materialization. + * @param[in] nGridResolution - extraction grid resolution for boolean surface extraction + */ + virtual void SetExtractionGridResolution(const Lib3MF_uint32 nGridResolution) = 0; + + /** + * IBooleanObject::GetExtractionGridResolution - Returns the extraction grid resolution used for boolean-to-mesh materialization. + * @return extraction grid resolution for boolean surface extraction + */ + virtual Lib3MF_uint32 GetExtractionGridResolution() = 0; + + /** + * IBooleanObject::GetOperandCount - Returns the number of operands in the boolean sequence. + * @return number of operands in the boolean sequence + */ + virtual Lib3MF_uint32 GetOperandCount() = 0; + + /** + * IBooleanObject::AddOperand - Adds an operand object to the boolean sequence. + * @param[in] pOperandObject - mesh object used as operand + * @param[in] Transform - transform applied to the operand object + */ + virtual void AddOperand(IMeshObject* pOperandObject, const Lib3MF::sTransform Transform) = 0; + + /** + * IBooleanObject::GetOperand - Returns one operand object and transform from the boolean sequence. + * @param[in] nIndex - index of the operand in the boolean sequence + * @param[out] pOperandObject - mesh object used as operand + * @return transform applied to the operand object + */ + virtual Lib3MF::sTransform GetOperand(const Lib3MF_uint32 nIndex, IMeshObject*& pOperandObject) = 0; + +}; + +typedef IBaseSharedPtr PIBooleanObject; + + /************************************************************************************************************************* Class interface for BeamLattice **************************************************************************************************************************/ @@ -6568,6 +6704,13 @@ class IModel : public virtual IBase { */ virtual IComponentsObject * GetComponentsObjectByID(const Lib3MF_uint32 nUniqueResourceID) = 0; + /** + * IModel::GetBooleanObjectByID - finds a boolean object by its UniqueResourceID + * @param[in] nUniqueResourceID - UniqueResourceID + * @return returns the boolean object instance + */ + virtual IBooleanObject * GetBooleanObjectByID(const Lib3MF_uint32 nUniqueResourceID) = 0; + /** * IModel::GetColorGroupByID - finds a model color group by its UniqueResourceID * @param[in] nUniqueResourceID - UniqueResourceID @@ -6638,6 +6781,12 @@ class IModel : public virtual IBase { */ virtual IComponentsObjectIterator * GetComponentsObjects() = 0; + /** + * IModel::GetBooleanObjects - creates a resource iterator instance with all boolean object resources. + * @return returns the iterator instance. + */ + virtual IBooleanObjectIterator * GetBooleanObjects() = 0; + /** * IModel::GetTexture2Ds - creates a Texture2DIterator instance with all texture2d resources. * @return returns the iterator instance. @@ -6710,6 +6859,12 @@ class IModel : public virtual IBase { */ virtual IComponentsObject * AddComponentsObject() = 0; + /** + * IModel::AddBooleanObject - adds an empty boolean object to the model. + * @return returns the boolean object instance + */ + virtual IBooleanObject * AddBooleanObject() = 0; + /** * IModel::AddSliceStack - creates a new model slicestack by its id * @param[in] dZBottom - Bottom Z value of the slicestack diff --git a/Autogenerated/Source/lib3mf_interfacewrapper.cpp b/Autogenerated/Source/lib3mf_interfacewrapper.cpp index 4f2e65f45..dfcb17556 100644 --- a/Autogenerated/Source/lib3mf_interfacewrapper.cpp +++ b/Autogenerated/Source/lib3mf_interfacewrapper.cpp @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++ implementation file in order to allow easy development of the 3MF Library. The functions in this file need to be implemented. It needs to be generated only once. -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -1634,6 +1634,46 @@ Lib3MFResult lib3mf_componentsobjectiterator_getcurrentcomponentsobject(Lib3MF_C } +/************************************************************************************************************************* + Class implementation for BooleanObjectIterator +**************************************************************************************************************************/ +Lib3MFResult lib3mf_booleanobjectiterator_getcurrentbooleanobject(Lib3MF_BooleanObjectIterator pBooleanObjectIterator, Lib3MF_BooleanObject * pResource) +{ + IBase* pIBaseClass = (IBase *)pBooleanObjectIterator; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObjectIterator, "BooleanObjectIterator", "GetCurrentBooleanObject"); + } + if (pResource == nullptr) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDPARAM); + IBase* pBaseResource(nullptr); + IBooleanObjectIterator* pIBooleanObjectIterator = dynamic_cast(pIBaseClass); + if (!pIBooleanObjectIterator) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + pBaseResource = pIBooleanObjectIterator->GetCurrentBooleanObject(); + + *pResource = (IBase*)(pBaseResource); + if (pJournalEntry.get() != nullptr) { + pJournalEntry->addHandleResult("Resource", *pResource); + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + + /************************************************************************************************************************* Class implementation for Texture2DIterator **************************************************************************************************************************/ @@ -3567,6 +3607,40 @@ Lib3MFResult lib3mf_object_islevelsetobject(Lib3MF_Object pObject, bool * pIsLev } } +Lib3MFResult lib3mf_object_isbooleanobject(Lib3MF_Object pObject, bool * pIsBooleanObject) +{ + IBase* pIBaseClass = (IBase *)pObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pObject, "Object", "IsBooleanObject"); + } + if (pIsBooleanObject == nullptr) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDPARAM); + IObject* pIObject = dynamic_cast(pIBaseClass); + if (!pIObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + *pIsBooleanObject = pIObject->IsBooleanObject(); + + if (pJournalEntry.get() != nullptr) { + pJournalEntry->addBooleanResult("IsBooleanObject", *pIsBooleanObject); + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + Lib3MFResult lib3mf_object_isvalid(Lib3MF_Object pObject, bool * pIsValid) { IBase* pIBaseClass = (IBase *)pObject; @@ -5595,6 +5669,455 @@ Lib3MFResult lib3mf_levelset_setvolumedata(Lib3MF_LevelSet pLevelSet, Lib3MF_Vol } +/************************************************************************************************************************* + Class implementation for BooleanObject +**************************************************************************************************************************/ +Lib3MFResult lib3mf_booleanobject_setbaseobject(Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object pBaseObject, const sLib3MFTransform * pTransform) +{ + IBase* pIBaseClass = (IBase *)pBooleanObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObject, "BooleanObject", "SetBaseObject"); + pJournalEntry->addHandleParameter("BaseObject", pBaseObject); + } + IBase* pIBaseClassBaseObject = (IBase *)pBaseObject; + IObject* pIBaseObject = dynamic_cast(pIBaseClassBaseObject); + if (!pIBaseObject) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDCAST); + + IBooleanObject* pIBooleanObject = dynamic_cast(pIBaseClass); + if (!pIBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + pIBooleanObject->SetBaseObject(pIBaseObject, *pTransform); + + if (pJournalEntry.get() != nullptr) { + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + +Lib3MFResult lib3mf_booleanobject_getbaseobject(Lib3MF_BooleanObject pBooleanObject, Lib3MF_Object * pBaseObject) +{ + IBase* pIBaseClass = (IBase *)pBooleanObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObject, "BooleanObject", "GetBaseObject"); + } + if (pBaseObject == nullptr) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDPARAM); + IBase* pBaseBaseObject(nullptr); + IBooleanObject* pIBooleanObject = dynamic_cast(pIBaseClass); + if (!pIBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + pBaseBaseObject = pIBooleanObject->GetBaseObject(); + + *pBaseObject = (IBase*)(pBaseBaseObject); + if (pJournalEntry.get() != nullptr) { + pJournalEntry->addHandleResult("BaseObject", *pBaseObject); + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + +Lib3MFResult lib3mf_booleanobject_setbasetransform(Lib3MF_BooleanObject pBooleanObject, const sLib3MFTransform * pTransform) +{ + IBase* pIBaseClass = (IBase *)pBooleanObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObject, "BooleanObject", "SetBaseTransform"); + } + IBooleanObject* pIBooleanObject = dynamic_cast(pIBaseClass); + if (!pIBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + pIBooleanObject->SetBaseTransform(*pTransform); + + if (pJournalEntry.get() != nullptr) { + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + +Lib3MFResult lib3mf_booleanobject_getbasetransform(Lib3MF_BooleanObject pBooleanObject, sLib3MFTransform * pTransform) +{ + IBase* pIBaseClass = (IBase *)pBooleanObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObject, "BooleanObject", "GetBaseTransform"); + } + if (pTransform == nullptr) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDPARAM); + IBooleanObject* pIBooleanObject = dynamic_cast(pIBaseClass); + if (!pIBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + *pTransform = pIBooleanObject->GetBaseTransform(); + + if (pJournalEntry.get() != nullptr) { + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + +Lib3MFResult lib3mf_booleanobject_setoperation(Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation eOperation) +{ + IBase* pIBaseClass = (IBase *)pBooleanObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObject, "BooleanObject", "SetOperation"); + pJournalEntry->addEnumParameter("Operation", "BooleanOperation", (Lib3MF_int32)(eOperation)); + } + IBooleanObject* pIBooleanObject = dynamic_cast(pIBaseClass); + if (!pIBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + pIBooleanObject->SetOperation(eOperation); + + if (pJournalEntry.get() != nullptr) { + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + +Lib3MFResult lib3mf_booleanobject_getoperation(Lib3MF_BooleanObject pBooleanObject, eLib3MFBooleanOperation * pOperation) +{ + IBase* pIBaseClass = (IBase *)pBooleanObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObject, "BooleanObject", "GetOperation"); + } + if (pOperation == nullptr) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDPARAM); + IBooleanObject* pIBooleanObject = dynamic_cast(pIBaseClass); + if (!pIBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + *pOperation = pIBooleanObject->GetOperation(); + + if (pJournalEntry.get() != nullptr) { + pJournalEntry->addEnumResult("Operation", "BooleanOperation", (Lib3MF_int32)(*pOperation)); + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + +Lib3MFResult lib3mf_booleanobject_setcsgmodeenabled(Lib3MF_BooleanObject pBooleanObject, bool bCSGModeEnabled) +{ + IBase* pIBaseClass = (IBase *)pBooleanObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObject, "BooleanObject", "SetCSGModeEnabled"); + pJournalEntry->addBooleanParameter("CSGModeEnabled", bCSGModeEnabled); + } + IBooleanObject* pIBooleanObject = dynamic_cast(pIBaseClass); + if (!pIBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + pIBooleanObject->SetCSGModeEnabled(bCSGModeEnabled); + + if (pJournalEntry.get() != nullptr) { + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + +Lib3MFResult lib3mf_booleanobject_getcsgmodeenabled(Lib3MF_BooleanObject pBooleanObject, bool * pCSGModeEnabled) +{ + IBase* pIBaseClass = (IBase *)pBooleanObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObject, "BooleanObject", "GetCSGModeEnabled"); + } + if (pCSGModeEnabled == nullptr) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDPARAM); + IBooleanObject* pIBooleanObject = dynamic_cast(pIBaseClass); + if (!pIBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + *pCSGModeEnabled = pIBooleanObject->GetCSGModeEnabled(); + + if (pJournalEntry.get() != nullptr) { + pJournalEntry->addBooleanResult("CSGModeEnabled", *pCSGModeEnabled); + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + +Lib3MFResult lib3mf_booleanobject_setextractiongridresolution(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nGridResolution) +{ + IBase* pIBaseClass = (IBase *)pBooleanObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObject, "BooleanObject", "SetExtractionGridResolution"); + pJournalEntry->addUInt32Parameter("GridResolution", nGridResolution); + } + IBooleanObject* pIBooleanObject = dynamic_cast(pIBaseClass); + if (!pIBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + pIBooleanObject->SetExtractionGridResolution(nGridResolution); + + if (pJournalEntry.get() != nullptr) { + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + +Lib3MFResult lib3mf_booleanobject_getextractiongridresolution(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pGridResolution) +{ + IBase* pIBaseClass = (IBase *)pBooleanObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObject, "BooleanObject", "GetExtractionGridResolution"); + } + if (pGridResolution == nullptr) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDPARAM); + IBooleanObject* pIBooleanObject = dynamic_cast(pIBaseClass); + if (!pIBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + *pGridResolution = pIBooleanObject->GetExtractionGridResolution(); + + if (pJournalEntry.get() != nullptr) { + pJournalEntry->addUInt32Result("GridResolution", *pGridResolution); + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + +Lib3MFResult lib3mf_booleanobject_getoperandcount(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 * pCount) +{ + IBase* pIBaseClass = (IBase *)pBooleanObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObject, "BooleanObject", "GetOperandCount"); + } + if (pCount == nullptr) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDPARAM); + IBooleanObject* pIBooleanObject = dynamic_cast(pIBaseClass); + if (!pIBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + *pCount = pIBooleanObject->GetOperandCount(); + + if (pJournalEntry.get() != nullptr) { + pJournalEntry->addUInt32Result("Count", *pCount); + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + +Lib3MFResult lib3mf_booleanobject_addoperand(Lib3MF_BooleanObject pBooleanObject, Lib3MF_MeshObject pOperandObject, const sLib3MFTransform * pTransform) +{ + IBase* pIBaseClass = (IBase *)pBooleanObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObject, "BooleanObject", "AddOperand"); + pJournalEntry->addHandleParameter("OperandObject", pOperandObject); + } + IBase* pIBaseClassOperandObject = (IBase *)pOperandObject; + IMeshObject* pIOperandObject = dynamic_cast(pIBaseClassOperandObject); + if (!pIOperandObject) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDCAST); + + IBooleanObject* pIBooleanObject = dynamic_cast(pIBaseClass); + if (!pIBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + pIBooleanObject->AddOperand(pIOperandObject, *pTransform); + + if (pJournalEntry.get() != nullptr) { + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + +Lib3MFResult lib3mf_booleanobject_getoperand(Lib3MF_BooleanObject pBooleanObject, Lib3MF_uint32 nIndex, Lib3MF_MeshObject * pOperandObject, sLib3MFTransform * pTransform) +{ + IBase* pIBaseClass = (IBase *)pBooleanObject; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pBooleanObject, "BooleanObject", "GetOperand"); + pJournalEntry->addUInt32Parameter("Index", nIndex); + } + if (pOperandObject == nullptr) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDPARAM); + if (pTransform == nullptr) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDPARAM); + IMeshObject* pBaseOperandObject(nullptr); + IBooleanObject* pIBooleanObject = dynamic_cast(pIBaseClass); + if (!pIBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + *pTransform = pIBooleanObject->GetOperand(nIndex, pBaseOperandObject); + + *pOperandObject = (IBase*)(pBaseOperandObject); + if (pJournalEntry.get() != nullptr) { + pJournalEntry->addHandleResult("OperandObject", *pOperandObject); + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + + /************************************************************************************************************************* Class implementation for BeamLattice **************************************************************************************************************************/ @@ -22337,6 +22860,43 @@ Lib3MFResult lib3mf_model_getcomponentsobjectbyid(Lib3MF_Model pModel, Lib3MF_ui } } +Lib3MFResult lib3mf_model_getbooleanobjectbyid(Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_BooleanObject * pBooleanObjectInstance) +{ + IBase* pIBaseClass = (IBase *)pModel; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pModel, "Model", "GetBooleanObjectByID"); + pJournalEntry->addUInt32Parameter("UniqueResourceID", nUniqueResourceID); + } + if (pBooleanObjectInstance == nullptr) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDPARAM); + IBase* pBaseBooleanObjectInstance(nullptr); + IModel* pIModel = dynamic_cast(pIBaseClass); + if (!pIModel) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + pBaseBooleanObjectInstance = pIModel->GetBooleanObjectByID(nUniqueResourceID); + + *pBooleanObjectInstance = (IBase*)(pBaseBooleanObjectInstance); + if (pJournalEntry.get() != nullptr) { + pJournalEntry->addHandleResult("BooleanObjectInstance", *pBooleanObjectInstance); + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + Lib3MFResult lib3mf_model_getcolorgroupbyid(Lib3MF_Model pModel, Lib3MF_uint32 nUniqueResourceID, Lib3MF_ColorGroup * pColorGroupInstance) { IBase* pIBaseClass = (IBase *)pModel; @@ -22755,6 +23315,42 @@ Lib3MFResult lib3mf_model_getcomponentsobjects(Lib3MF_Model pModel, Lib3MF_Compo } } +Lib3MFResult lib3mf_model_getbooleanobjects(Lib3MF_Model pModel, Lib3MF_BooleanObjectIterator * pResourceIterator) +{ + IBase* pIBaseClass = (IBase *)pModel; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pModel, "Model", "GetBooleanObjects"); + } + if (pResourceIterator == nullptr) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDPARAM); + IBase* pBaseResourceIterator(nullptr); + IModel* pIModel = dynamic_cast(pIBaseClass); + if (!pIModel) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + pBaseResourceIterator = pIModel->GetBooleanObjects(); + + *pResourceIterator = (IBase*)(pBaseResourceIterator); + if (pJournalEntry.get() != nullptr) { + pJournalEntry->addHandleResult("ResourceIterator", *pResourceIterator); + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + Lib3MFResult lib3mf_model_gettexture2ds(Lib3MF_Model pModel, Lib3MF_Texture2DIterator * pResourceIterator) { IBase* pIBaseClass = (IBase *)pModel; @@ -23188,6 +23784,42 @@ Lib3MFResult lib3mf_model_addcomponentsobject(Lib3MF_Model pModel, Lib3MF_Compon } } +Lib3MFResult lib3mf_model_addbooleanobject(Lib3MF_Model pModel, Lib3MF_BooleanObject * pBooleanObjectInstance) +{ + IBase* pIBaseClass = (IBase *)pModel; + + PLib3MFInterfaceJournalEntry pJournalEntry; + try { + if (m_GlobalJournal.get() != nullptr) { + pJournalEntry = m_GlobalJournal->beginClassMethod(pModel, "Model", "AddBooleanObject"); + } + if (pBooleanObjectInstance == nullptr) + throw ELib3MFInterfaceException (LIB3MF_ERROR_INVALIDPARAM); + IBase* pBaseBooleanObjectInstance(nullptr); + IModel* pIModel = dynamic_cast(pIBaseClass); + if (!pIModel) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCAST); + + pBaseBooleanObjectInstance = pIModel->AddBooleanObject(); + + *pBooleanObjectInstance = (IBase*)(pBaseBooleanObjectInstance); + if (pJournalEntry.get() != nullptr) { + pJournalEntry->addHandleResult("BooleanObjectInstance", *pBooleanObjectInstance); + pJournalEntry->writeSuccess(); + } + return LIB3MF_SUCCESS; + } + catch (ELib3MFInterfaceException & Exception) { + return handleLib3MFException(pIBaseClass, Exception, pJournalEntry.get()); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException, pJournalEntry.get()); + } + catch (...) { + return handleUnhandledException(pIBaseClass, pJournalEntry.get()); + } +} + Lib3MFResult lib3mf_model_addslicestack(Lib3MF_Model pModel, Lib3MF_double dZBottom, Lib3MF_SliceStack * pSliceStackInstance) { IBase* pIBaseClass = (IBase *)pModel; @@ -24479,6 +25111,8 @@ Lib3MFResult Lib3MF::Impl::Lib3MF_GetProcAddress (const char * pProcName, void * *ppProcAddress = (void*) &lib3mf_meshobjectiterator_getcurrentmeshobject; if (sProcName == "lib3mf_componentsobjectiterator_getcurrentcomponentsobject") *ppProcAddress = (void*) &lib3mf_componentsobjectiterator_getcurrentcomponentsobject; + if (sProcName == "lib3mf_booleanobjectiterator_getcurrentbooleanobject") + *ppProcAddress = (void*) &lib3mf_booleanobjectiterator_getcurrentbooleanobject; if (sProcName == "lib3mf_texture2diterator_getcurrenttexture2d") *ppProcAddress = (void*) &lib3mf_texture2diterator_getcurrenttexture2d; if (sProcName == "lib3mf_basematerialgroupiterator_getcurrentbasematerialgroup") @@ -24575,6 +25209,8 @@ Lib3MFResult Lib3MF::Impl::Lib3MF_GetProcAddress (const char * pProcName, void * *ppProcAddress = (void*) &lib3mf_object_iscomponentsobject; if (sProcName == "lib3mf_object_islevelsetobject") *ppProcAddress = (void*) &lib3mf_object_islevelsetobject; + if (sProcName == "lib3mf_object_isbooleanobject") + *ppProcAddress = (void*) &lib3mf_object_isbooleanobject; if (sProcName == "lib3mf_object_isvalid") *ppProcAddress = (void*) &lib3mf_object_isvalid; if (sProcName == "lib3mf_object_setattachmentasthumbnail") @@ -24689,6 +25325,32 @@ Lib3MFResult Lib3MF::Impl::Lib3MF_GetProcAddress (const char * pProcName, void * *ppProcAddress = (void*) &lib3mf_levelset_getvolumedata; if (sProcName == "lib3mf_levelset_setvolumedata") *ppProcAddress = (void*) &lib3mf_levelset_setvolumedata; + if (sProcName == "lib3mf_booleanobject_setbaseobject") + *ppProcAddress = (void*) &lib3mf_booleanobject_setbaseobject; + if (sProcName == "lib3mf_booleanobject_getbaseobject") + *ppProcAddress = (void*) &lib3mf_booleanobject_getbaseobject; + if (sProcName == "lib3mf_booleanobject_setbasetransform") + *ppProcAddress = (void*) &lib3mf_booleanobject_setbasetransform; + if (sProcName == "lib3mf_booleanobject_getbasetransform") + *ppProcAddress = (void*) &lib3mf_booleanobject_getbasetransform; + if (sProcName == "lib3mf_booleanobject_setoperation") + *ppProcAddress = (void*) &lib3mf_booleanobject_setoperation; + if (sProcName == "lib3mf_booleanobject_getoperation") + *ppProcAddress = (void*) &lib3mf_booleanobject_getoperation; + if (sProcName == "lib3mf_booleanobject_setcsgmodeenabled") + *ppProcAddress = (void*) &lib3mf_booleanobject_setcsgmodeenabled; + if (sProcName == "lib3mf_booleanobject_getcsgmodeenabled") + *ppProcAddress = (void*) &lib3mf_booleanobject_getcsgmodeenabled; + if (sProcName == "lib3mf_booleanobject_setextractiongridresolution") + *ppProcAddress = (void*) &lib3mf_booleanobject_setextractiongridresolution; + if (sProcName == "lib3mf_booleanobject_getextractiongridresolution") + *ppProcAddress = (void*) &lib3mf_booleanobject_getextractiongridresolution; + if (sProcName == "lib3mf_booleanobject_getoperandcount") + *ppProcAddress = (void*) &lib3mf_booleanobject_getoperandcount; + if (sProcName == "lib3mf_booleanobject_addoperand") + *ppProcAddress = (void*) &lib3mf_booleanobject_addoperand; + if (sProcName == "lib3mf_booleanobject_getoperand") + *ppProcAddress = (void*) &lib3mf_booleanobject_getoperand; if (sProcName == "lib3mf_beamlattice_getminlength") *ppProcAddress = (void*) &lib3mf_beamlattice_getminlength; if (sProcName == "lib3mf_beamlattice_setminlength") @@ -25539,6 +26201,8 @@ Lib3MFResult Lib3MF::Impl::Lib3MF_GetProcAddress (const char * pProcName, void * *ppProcAddress = (void*) &lib3mf_model_getmeshobjectbyid; if (sProcName == "lib3mf_model_getcomponentsobjectbyid") *ppProcAddress = (void*) &lib3mf_model_getcomponentsobjectbyid; + if (sProcName == "lib3mf_model_getbooleanobjectbyid") + *ppProcAddress = (void*) &lib3mf_model_getbooleanobjectbyid; if (sProcName == "lib3mf_model_getcolorgroupbyid") *ppProcAddress = (void*) &lib3mf_model_getcolorgroupbyid; if (sProcName == "lib3mf_model_getslicestackbyid") @@ -25561,6 +26225,8 @@ Lib3MFResult Lib3MF::Impl::Lib3MF_GetProcAddress (const char * pProcName, void * *ppProcAddress = (void*) &lib3mf_model_getmeshobjects; if (sProcName == "lib3mf_model_getcomponentsobjects") *ppProcAddress = (void*) &lib3mf_model_getcomponentsobjects; + if (sProcName == "lib3mf_model_getbooleanobjects") + *ppProcAddress = (void*) &lib3mf_model_getbooleanobjects; if (sProcName == "lib3mf_model_gettexture2ds") *ppProcAddress = (void*) &lib3mf_model_gettexture2ds; if (sProcName == "lib3mf_model_getbasematerialgroups") @@ -25585,6 +26251,8 @@ Lib3MFResult Lib3MF::Impl::Lib3MF_GetProcAddress (const char * pProcName, void * *ppProcAddress = (void*) &lib3mf_model_addmeshobject; if (sProcName == "lib3mf_model_addcomponentsobject") *ppProcAddress = (void*) &lib3mf_model_addcomponentsobject; + if (sProcName == "lib3mf_model_addbooleanobject") + *ppProcAddress = (void*) &lib3mf_model_addbooleanobject; if (sProcName == "lib3mf_model_addslicestack") *ppProcAddress = (void*) &lib3mf_model_addslicestack; if (sProcName == "lib3mf_model_addtexture2dfromattachment") diff --git a/Autogenerated/Source/lib3mf_types.hpp b/Autogenerated/Source/lib3mf_types.hpp index 2ae400ad6..09f6a3441 100644 --- a/Autogenerated/Source/lib3mf_types.hpp +++ b/Autogenerated/Source/lib3mf_types.hpp @@ -29,7 +29,7 @@ This file has been generated by the Automatic Component Toolkit (ACT) version 1. Abstract: This is an autogenerated C++-Header file with basic types in order to allow an easy use of the 3MF Library -Interface version: 2.5.0 +Interface version: 2.6.0 */ @@ -83,7 +83,7 @@ typedef void * Lib3MF_pvoid; **************************************************************************************************************************/ #define LIB3MF_VERSION_MAJOR 2 -#define LIB3MF_VERSION_MINOR 5 +#define LIB3MF_VERSION_MINOR 6 #define LIB3MF_VERSION_MICRO 0 #define LIB3MF_VERSION_PRERELEASEINFO "" #define LIB3MF_VERSION_BUILDINFO "" @@ -219,6 +219,7 @@ typedef Lib3MFHandle Lib3MF_SliceStackIterator; typedef Lib3MFHandle Lib3MF_ObjectIterator; typedef Lib3MFHandle Lib3MF_MeshObjectIterator; typedef Lib3MFHandle Lib3MF_ComponentsObjectIterator; +typedef Lib3MFHandle Lib3MF_BooleanObjectIterator; typedef Lib3MFHandle Lib3MF_Texture2DIterator; typedef Lib3MFHandle Lib3MF_BaseMaterialGroupIterator; typedef Lib3MFHandle Lib3MF_ColorGroupIterator; @@ -234,6 +235,7 @@ typedef Lib3MFHandle Lib3MF_TriangleSet; typedef Lib3MFHandle Lib3MF_Object; typedef Lib3MFHandle Lib3MF_MeshObject; typedef Lib3MFHandle Lib3MF_LevelSet; +typedef Lib3MFHandle Lib3MF_BooleanObject; typedef Lib3MFHandle Lib3MF_BeamLattice; typedef Lib3MFHandle Lib3MF_FunctionReference; typedef Lib3MFHandle Lib3MF_VolumeDataColor; @@ -366,6 +368,12 @@ namespace Lib3MF { Surface = 4 }; + enum class eBooleanOperation : Lib3MF_int32 { + Union = 0, + Difference = 1, + Intersection = 2 + }; + enum class eTextureType : Lib3MF_int32 { Unknown = 0, PNG = 1, @@ -722,6 +730,7 @@ typedef Lib3MF::ePropertyType eLib3MFPropertyType; typedef Lib3MF::eSlicesMeshResolution eLib3MFSlicesMeshResolution; typedef Lib3MF::eModelUnit eLib3MFModelUnit; typedef Lib3MF::eObjectType eLib3MFObjectType; +typedef Lib3MF::eBooleanOperation eLib3MFBooleanOperation; typedef Lib3MF::eTextureType eLib3MFTextureType; typedef Lib3MF::eTextureTileStyle eLib3MFTextureTileStyle; typedef Lib3MF::eTextureFilter eLib3MFTextureFilter; diff --git a/AutomaticComponentToolkit/generateWrapperAndBindingsLinux64.sh b/AutomaticComponentToolkit/generateWrapperAndBindingsLinux64.sh index 4b9751618..902cb4fab 100755 --- a/AutomaticComponentToolkit/generateWrapperAndBindingsLinux64.sh +++ b/AutomaticComponentToolkit/generateWrapperAndBindingsLinux64.sh @@ -1,8 +1,9 @@ #!/bin/sh ./act.linux64 lib3mf.xml -bindings ../Autogenerated/Bindings -interfaces ../Autogenerated/Source -stubs ../Source/API -suppresslicense -suppressexamples ./act.linux64 lib3mf.xml -stubs ../Include/API -suppresslicense -suppressexamples -suppressbindings -suppressinterfaces +python3 ../patch_wasm_bindings.py # remove header form ../Source/API find ../Source/API -name "*.hpp" -type f -delete # remove cpp from ../Include/API -find ../Include/API -name "*.cpp" -type f -delete \ No newline at end of file +find ../Include/API -name "*.cpp" -type f -delete diff --git a/AutomaticComponentToolkit/lib3mf.xml b/AutomaticComponentToolkit/lib3mf.xml index 4bde4291d..07ee18ddb 100644 --- a/AutomaticComponentToolkit/lib3mf.xml +++ b/AutomaticComponentToolkit/lib3mf.xml @@ -1,5 +1,5 @@ - + @@ -155,6 +155,12 @@ \ No newline at end of file + diff --git a/CMakeLists.txt b/CMakeLists.txt index eca17957b..0e0e4607d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ include(CMakePackageConfigHelpers) # Define Version set(LIB3MF_VERSION_MAJOR 2) # increase on every backward-compatibility breaking change of the API -set(LIB3MF_VERSION_MINOR 5) # increase on every backward compatible change of the API +set(LIB3MF_VERSION_MINOR 6) # increase on every backward compatible change of the API set(LIB3MF_VERSION_MICRO 0) # increase on on every change that does not alter the API set(LIB3MF_VERSION_PRERELEASE "") # denotes pre-release information of a version of lib3mf set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") @@ -52,6 +52,13 @@ option(USE_INCLUDED_FASTFLOAT "Use included fast_float headers" ON) option(BUILD_FOR_CODECOVERAGE "Build for code coverage analysis" OFF) option(STRIP_BINARIES "Strip binaries (on non-apple)" ON) option(USE_PLATFORM_UUID "Use UUID geneator that is provided by the OS (always ON for Windows)" OFF) +option(LIB3MF_ENABLE_EXPENSIVE_TESTS "Enable expensive/long-running tests" OFF) +set(_lib3mf_tinybvh_threaded_builds_default ON) +if (MINGW OR EMSCRIPTEN) + set(_lib3mf_tinybvh_threaded_builds_default OFF) +endif() +option(LIB3MF_TINYBVH_THREADED_BUILDS "Enable threaded tinybvh build path" ${_lib3mf_tinybvh_threaded_builds_default}) +unset(_lib3mf_tinybvh_threaded_builds_default) if (LIB3MF_BUILD_WASM) if (NOT EMSCRIPTEN) @@ -194,6 +201,10 @@ target_include_directories(${PROJECT_NAME} PRIVATE ${LIBS_INCLUDE}) # allow FASTFLOAT_ALLOWS_LEADING_PLUS add_definitions(-DFASTFLOAT_ALLOWS_LEADING_PLUS=1) +if (NOT LIB3MF_TINYBVH_THREADED_BUILDS) + target_compile_definitions(${PROJECT_NAME} PRIVATE NO_THREADED_BUILDS=1) +endif() + target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR_AUTOGENERATED}/Source) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Include/API) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Include) @@ -414,7 +425,24 @@ if (LIB3MF_BUILD_WASM) message(FATAL_ERROR "Missing ACT-generated WASM bindings source: ${LIB3MF_WASM_BINDINGS_SOURCE}") endif() + find_package(Python3 COMPONENTS Interpreter QUIET) + if (NOT Python3_Interpreter_FOUND) + message(FATAL_ERROR "LIB3MF_BUILD_WASM requires Python3 to run patch_wasm_bindings.py") + endif() + + set(LIB3MF_WASM_BINDINGS_PATCH_STAMP "${CMAKE_CURRENT_BINARY_DIR}/lib3mf_wasm_bindings_patch.stamp") + add_custom_command( + OUTPUT "${LIB3MF_WASM_BINDINGS_PATCH_STAMP}" + COMMAND "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/patch_wasm_bindings.py" "${LIB3MF_WASM_BINDINGS_SOURCE}" + COMMAND "${CMAKE_COMMAND}" -E touch "${LIB3MF_WASM_BINDINGS_PATCH_STAMP}" + DEPENDS "${LIB3MF_WASM_BINDINGS_SOURCE}" "${CMAKE_CURRENT_SOURCE_DIR}/patch_wasm_bindings.py" + COMMENT "Patching ACT-generated WASM bindings for struct-return/vector wrapper compatibility" + VERBATIM + ) + add_custom_target(lib3mf_wasm_bindings_patch DEPENDS "${LIB3MF_WASM_BINDINGS_PATCH_STAMP}") + add_executable(lib3mf_wasm ${LIB3MF_WASM_BINDINGS_SOURCE}) + add_dependencies(lib3mf_wasm lib3mf_wasm_bindings_patch) target_include_directories(lib3mf_wasm PRIVATE ${CMAKE_CURRENT_SOURCE_DIR_AUTOGENERATED}/Bindings) target_link_libraries(lib3mf_wasm PRIVATE ${PROJECT_NAME}) set_target_properties(lib3mf_wasm PROPERTIES OUTPUT_NAME "lib3mf" SUFFIX ".js") diff --git a/Include/API/lib3mf_booleanobject.hpp b/Include/API/lib3mf_booleanobject.hpp new file mode 100644 index 000000000..ae07c40a3 --- /dev/null +++ b/Include/API/lib3mf_booleanobject.hpp @@ -0,0 +1,123 @@ +/*++ + +Copyright (C) 2024 3MF Consortium (Original Author) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: This is the class declaration of CBooleanObject + +*/ + + +#ifndef __LIB3MF_BOOLEANOBJECT +#define __LIB3MF_BOOLEANOBJECT + +#include "lib3mf_interfaces.hpp" + +// Parent classes +#include "lib3mf_object.hpp" +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4250) +#endif + +// Include custom headers here. +#include "Model/Classes/NMR_ModelBooleanObject.h" + +namespace Lib3MF { +namespace Impl { + + +/************************************************************************************************************************* + Class declaration of CBooleanObject +**************************************************************************************************************************/ + +class CBooleanObject : public virtual IBooleanObject, public virtual CObject { +private: + + /** + * Put private members here. + */ + +protected: + + /** + * Put protected members here. + */ + +public: + + /** + * Put additional public members here. They will not be visible in the external API. + */ + + + CBooleanObject() = delete; + CBooleanObject(NMR::PModelResource pResource); + + NMR::PModelBooleanObject booleanObject(); + + /** + * Public member functions to implement. + */ + + bool IsBooleanObject() override; + bool IsMeshObject() override; + bool IsComponentsObject() override; + bool IsLevelSetObject() override; + + void SetBaseObject(IObject* pBaseObject, const Lib3MF::sTransform Transform) override; + + IObject * GetBaseObject() override; + + void SetBaseTransform(const Lib3MF::sTransform Transform) override; + + Lib3MF::sTransform GetBaseTransform() override; + + void SetOperation(const Lib3MF::eBooleanOperation eOperation) override; + + Lib3MF::eBooleanOperation GetOperation() override; + + void SetCSGModeEnabled(const bool bCSGModeEnabled) override; + + bool GetCSGModeEnabled() override; + + void SetExtractionGridResolution(const Lib3MF_uint32 nGridResolution) override; + + Lib3MF_uint32 GetExtractionGridResolution() override; + + Lib3MF_uint32 GetOperandCount() override; + + void AddOperand(IMeshObject* pOperandObject, const Lib3MF::sTransform Transform) override; + + Lib3MF::sTransform GetOperand(const Lib3MF_uint32 nIndex, IMeshObject*& pOperandObject) override; + +}; + +} // namespace Impl +} // namespace Lib3MF + +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#endif // __LIB3MF_BOOLEANOBJECT diff --git a/Include/API/lib3mf_booleanobjectiterator.hpp b/Include/API/lib3mf_booleanobjectiterator.hpp new file mode 100644 index 000000000..077473799 --- /dev/null +++ b/Include/API/lib3mf_booleanobjectiterator.hpp @@ -0,0 +1,89 @@ +/*++ + +Copyright (C) 2024 3MF Consortium (Original Author) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: This is the class declaration of CBooleanObjectIterator + +*/ + + +#ifndef __LIB3MF_BOOLEANOBJECTITERATOR +#define __LIB3MF_BOOLEANOBJECTITERATOR + +#include "lib3mf_interfaces.hpp" + +// Parent classes +#include "lib3mf_resourceiterator.hpp" +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4250) +#endif + +// Include custom headers here. + + +namespace Lib3MF { +namespace Impl { + + +/************************************************************************************************************************* + Class declaration of CBooleanObjectIterator +**************************************************************************************************************************/ + +class CBooleanObjectIterator : public virtual IBooleanObjectIterator, public virtual CResourceIterator { +private: + + /** + * Put private members here. + */ + +protected: + + /** + * Put protected members here. + */ + +public: + + /** + * Put additional public members here. They will not be visible in the external API. + */ + + + /** + * Public member functions to implement. + */ + + IBooleanObject * GetCurrentBooleanObject() override; + +}; + +} // namespace Impl +} // namespace Lib3MF + +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#endif // __LIB3MF_BOOLEANOBJECTITERATOR diff --git a/Include/API/lib3mf_componentsobject.hpp b/Include/API/lib3mf_componentsobject.hpp index 2206a2540..0af611d8b 100644 --- a/Include/API/lib3mf_componentsobject.hpp +++ b/Include/API/lib3mf_componentsobject.hpp @@ -87,6 +87,8 @@ class CComponentsObject : public virtual IComponentsObject, public virtual CObje bool IsComponentsObject(); bool IsLevelSetObject() override; + + bool IsBooleanObject() override; }; } diff --git a/Include/API/lib3mf_levelset.hpp b/Include/API/lib3mf_levelset.hpp index be1c11e4a..9241dbca1 100644 --- a/Include/API/lib3mf_levelset.hpp +++ b/Include/API/lib3mf_levelset.hpp @@ -183,6 +183,8 @@ class CLevelSet : public virtual ILevelSet, public virtual CObject { bool IsComponentsObject() override; bool IsLevelSetObject() override; + + bool IsBooleanObject() override; }; } // namespace Impl diff --git a/Include/API/lib3mf_meshobject.hpp b/Include/API/lib3mf_meshobject.hpp index c2d4c4b6f..92f2dc459 100644 --- a/Include/API/lib3mf_meshobject.hpp +++ b/Include/API/lib3mf_meshobject.hpp @@ -112,6 +112,8 @@ class CMeshObject : public virtual IMeshObject, public virtual CObject { bool IsLevelSetObject() override; + bool IsBooleanObject() override; + bool IsValid(); virtual IBeamLattice * BeamLattice() override; diff --git a/Include/API/lib3mf_model.hpp b/Include/API/lib3mf_model.hpp index 6326c1227..0f7784395 100644 --- a/Include/API/lib3mf_model.hpp +++ b/Include/API/lib3mf_model.hpp @@ -44,6 +44,8 @@ Abstract: This is the class declaration of CModel // Include custom headers here. #include "Model/Classes/NMR_Model.h" #include "Model/Classes/NMR_KeyStore.h" +#include "lib3mf_booleanobject.hpp" +#include "lib3mf_booleanobjectiterator.hpp" namespace Lib3MF { namespace Impl { @@ -109,6 +111,8 @@ class CModel : public virtual IModel, public virtual CBase { IComponentsObject * GetComponentsObjectByID(const Lib3MF_uint32 nUniqueResourceID) override; + IBooleanObject * GetBooleanObjectByID(const Lib3MF_uint32 nUniqueResourceID) override; + IColorGroup * GetColorGroupByID(const Lib3MF_uint32 nUniqueResourceID) override; ITexture2DGroup * GetTexture2DGroupByID(const Lib3MF_uint32 nUniqueResourceID) override; @@ -135,6 +139,8 @@ class CModel : public virtual IModel, public virtual CBase { IComponentsObjectIterator * GetComponentsObjects() override; + IBooleanObjectIterator * GetBooleanObjects() override; + ITexture2DIterator * GetTexture2Ds() override; IBaseMaterialGroupIterator * GetBaseMaterialGroups() override; @@ -157,6 +163,8 @@ class CModel : public virtual IModel, public virtual CBase { IComponentsObject * AddComponentsObject() override; + IBooleanObject * AddBooleanObject() override; + ISliceStack * AddSliceStack(const Lib3MF_double dZBottom) override; ITexture2D * AddTexture2DFromAttachment(IAttachment* pTextureAttachment) override; diff --git a/Include/API/lib3mf_object.hpp b/Include/API/lib3mf_object.hpp index 1b92133ff..259c6d018 100644 --- a/Include/API/lib3mf_object.hpp +++ b/Include/API/lib3mf_object.hpp @@ -94,6 +94,8 @@ class CObject : public virtual IObject, public virtual CResource { virtual bool IsLevelSetObject (); + virtual bool IsBooleanObject (); + virtual IMeshObject * AsMeshObject(); virtual IComponentsObject * AsComponentsObject(); diff --git a/Include/Common/Boolean/NMR_BooleanDistanceField.h b/Include/Common/Boolean/NMR_BooleanDistanceField.h new file mode 100644 index 000000000..5f91f464f --- /dev/null +++ b/Include/Common/Boolean/NMR_BooleanDistanceField.h @@ -0,0 +1,64 @@ +/*++ + +Copyright (C) 2026 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +Signed-distance field generation for boolean CSG evaluation. + +--*/ + +#ifndef __NMR_BOOLEANDISTANCEFIELD +#define __NMR_BOOLEANDISTANCEFIELD + +#include "Common/Mesh/NMR_Mesh.h" +#include "Model/Classes/NMR_ModelBooleanObject.h" + +#include + +namespace NMR::Boolean { + + struct sBooleanVec3d { + double x = 0.0; + double y = 0.0; + double z = 0.0; + }; + + struct sBooleanFieldData { + std::vector values; + int resolution = 0; + sBooleanVec3d minCorner; + sBooleanVec3d maxCorner; + }; + + sBooleanFieldData buildCSGField( + _In_ CMesh * pBaseMesh, + _In_ const std::vector & operandMeshes, + _In_ eModelBooleanOperation operation, + _In_ nfUint32 nGridResolution); + +} + +#endif // __NMR_BOOLEANDISTANCEFIELD diff --git a/Include/Common/Boolean/NMR_BooleanEngine.h b/Include/Common/Boolean/NMR_BooleanEngine.h new file mode 100644 index 000000000..7b76aca2a --- /dev/null +++ b/Include/Common/Boolean/NMR_BooleanEngine.h @@ -0,0 +1,53 @@ +/*++ + +Copyright (C) 2026 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +Winding-number based boolean engine with volumetric iso-surface extraction. + +--*/ + +#ifndef __NMR_BOOLEANENGINE +#define __NMR_BOOLEANENGINE + +#include "Common/Mesh/NMR_Mesh.h" +#include "Model/Classes/NMR_ModelBooleanObject.h" + +namespace NMR { + + class CBooleanEngine { + public: + static void evaluate( + _In_ CMesh * pBaseMesh, + _In_ const std::vector & operandMeshes, + _In_ eModelBooleanOperation operation, + _In_ CMesh * pResultMesh, + _In_ nfUint32 nGridResolution = 40); + }; + +} + +#endif // __NMR_BOOLEANENGINE diff --git a/Include/Common/Boolean/NMR_BooleanSurfacePostProcess.h b/Include/Common/Boolean/NMR_BooleanSurfacePostProcess.h new file mode 100644 index 000000000..f2158dd3b --- /dev/null +++ b/Include/Common/Boolean/NMR_BooleanSurfacePostProcess.h @@ -0,0 +1,46 @@ +/*++ + +Copyright (C) 2026 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +Surface smoothing and zero-level-set projection for extracted boolean meshes. + +--*/ + +#ifndef __NMR_BOOLEANSURFACEPOSTPROCESS +#define __NMR_BOOLEANSURFACEPOSTPROCESS + +#include "Common/Boolean/NMR_BooleanDistanceField.h" + +namespace NMR::Boolean { + + void smoothAndProjectExtractedSurface( + _In_ CMesh * pMesh, + _In_ const sBooleanFieldData & fieldData); + +} + +#endif // __NMR_BOOLEANSURFACEPOSTPROCESS diff --git a/Include/Common/Boolean/NMR_MarchingCubes.h b/Include/Common/Boolean/NMR_MarchingCubes.h new file mode 100644 index 000000000..6fe90d53e --- /dev/null +++ b/Include/Common/Boolean/NMR_MarchingCubes.h @@ -0,0 +1,52 @@ +/*++ + +Copyright (C) 2026 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +Marching-cubes iso-surface extraction utilities for boolean field materialization. + +--*/ + +#ifndef __NMR_MARCHINGCUBES +#define __NMR_MARCHINGCUBES + +#include "Common/Mesh/NMR_Mesh.h" + +#include +#include + +namespace NMR::Boolean { + + void extractIsoSurfaceMarchingCubes( + _In_ CMesh * pResultMesh, + _In_ const std::vector & fieldValues, + _In_ int resolution, + _In_ const std::array & minCorner, + _In_ const std::array & maxCorner); + +} + +#endif // __NMR_MARCHINGCUBES diff --git a/Include/Common/NMR_ErrorConst.h b/Include/Common/NMR_ErrorConst.h index 007b4f7e7..e15d7b95a 100644 --- a/Include/Common/NMR_ErrorConst.h +++ b/Include/Common/NMR_ErrorConst.h @@ -1250,6 +1250,9 @@ Model error codes (0x8XXX) // A keystore element is not base64 encoded #define NMR_ERROR_KEYSTOREINVALIDENCODING 0x810E +// Boolean-object must not have an Object-Level PID +#define NMR_ERROR_OBJECTLEVELPID_ON_BOOLEANOBJECT 0x810F + // errors for the volumetric extension 0x88xx // Invalid Image3D Size diff --git a/Include/Common/Winding/NMR_WindingNumber.h b/Include/Common/Winding/NMR_WindingNumber.h new file mode 100644 index 000000000..2e5dfbc54 --- /dev/null +++ b/Include/Common/Winding/NMR_WindingNumber.h @@ -0,0 +1,96 @@ +#pragma once + +/*++ + +Copyright (C) 2026 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +Internal generalized winding number helper used by boolean processing. +Copyright remains with 3MF Consortium; this file is licensed under +the BSD terms above for inclusion in lib3mf. + +--*/ + +#include +#include +#include + +namespace tg { + struct windingSettings { + double accuracy = 2.0; + int taylorOrder = 2; + unsigned int threads = 0; + int kNeighbors = 12; + }; + + class windingNumber { + public: + using Point = std::array; + using Triangle = std::array; + using Settings = windingSettings; + + windingNumber(const std::vector &vertices, + const std::vector &triangles, + Settings s = {}); + + explicit windingNumber(const std::vector &points, Settings s = {}); + + windingNumber(const std::vector &points, + const std::vector &normals, + Settings s = {}); + + windingNumber(const std::vector &points, + const std::vector &normals, + const std::vector &areas, + Settings s = {}); + + // For closed, consistently oriented shapes: + // query(...) ~ 1 => inside, ~ 0 => outside. + // Practical thresholds: + // w > 0.5 => inside + // w < 0.5 => outside + // Optional conservative bands often used in practice: + // w > 0.75 => confidently inside + // w < 0.25 => confidently outside + double query(const Point &p) const; + + std::vector query(const std::vector &points) const; + + ~windingNumber(); + + windingNumber(windingNumber &&) noexcept; + + windingNumber &operator=(windingNumber &&) noexcept; + + windingNumber(const windingNumber &) = delete; + + windingNumber &operator=(const windingNumber &) = delete; + + private: + struct Impl; + std::unique_ptr impl_; + }; +} // namespace tg diff --git a/Include/Model/Classes/NMR_ModelBooleanObject.h b/Include/Model/Classes/NMR_ModelBooleanObject.h new file mode 100644 index 000000000..bdb2d3128 --- /dev/null +++ b/Include/Model/Classes/NMR_ModelBooleanObject.h @@ -0,0 +1,90 @@ +/*++ + +Copyright (C) 2026 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +Minimal in-memory representation of a boolean object. + +--*/ + +#ifndef __NMR_MODELBOOLEANOBJECT +#define __NMR_MODELBOOLEANOBJECT + +#include "Model/Classes/NMR_ModelObject.h" +#include "Model/Classes/NMR_ModelComponent.h" + +namespace NMR { + + enum class eModelBooleanOperation { + Union = 0, + Difference = 1, + Intersection = 2 + }; + + class CModelBooleanObject : public CModelObject { + private: + PModelComponent m_pBaseObject; + std::vector m_Operands; + eModelBooleanOperation m_eOperation; + nfBool m_bCSGModeEnabled; + nfUint32 m_nExtractionGridResolution; + + public: + CModelBooleanObject() = delete; + CModelBooleanObject(_In_ const ModelResourceID sID, _In_ CModel * pModel); + ~CModelBooleanObject() override; + + void setBaseObject(_In_ CModelObject * pObject, _In_ const NMATRIX3 & mTransform); + CModelObject * getBaseObject() const; + void setBaseTransform(_In_ const NMATRIX3 & mTransform); + NMATRIX3 getBaseTransform() const; + + void setOperation(_In_ eModelBooleanOperation eOperation); + eModelBooleanOperation getOperation() const; + std::string getOperationString() const; + nfBool setOperationString(_In_ const std::string & sOperation, _In_ nfBool bRaiseException); + void setCSGModeEnabled(_In_ nfBool bEnabled); + nfBool getCSGModeEnabled() const; + void setExtractionGridResolution(_In_ nfUint32 nGridResolution); + nfUint32 getExtractionGridResolution() const; + + void addOperand(_In_ CModelObject * pObject, _In_ const NMATRIX3 & mTransform); + nfUint32 getOperandCount() const; + PModelComponent getOperand(_In_ nfUint32 nIdx) const; + + void mergeToMesh(_In_ CMesh * pMesh, _In_ const NMATRIX3 mMatrix) override; + nfBool isValid() override; + void calculateComponentDepthLevel(nfUint32 nLevel) override; + nfBool hasSlices(nfBool bRecursive) override; + nfBool isValidForSlices(const NMATRIX3& totalParentMatrix) override; + void extendOutbox(_Out_ NOUTBOX3& vOutBox, _In_ const NMATRIX3 mAccumulatedMatrix) override; + ResourceDependencies getDependencies() override; + }; + + typedef std::shared_ptr PModelBooleanObject; +} + +#endif diff --git a/Include/Model/Classes/NMR_ModelConstants.h b/Include/Model/Classes/NMR_ModelConstants.h index 7403f0e5e..ce77e4484 100644 --- a/Include/Model/Classes/NMR_ModelConstants.h +++ b/Include/Model/Classes/NMR_ModelConstants.h @@ -88,6 +88,7 @@ These are given by the 3MF Standard #define XML_3MF_NAMESPACE_CIPHERVALUESPEC "http://www.w3.org/2001/04/xmlenc#" #define XML_3MF_NAMESPACE_VOLUMETRICSPEC "http://schemas.3mf.io/3dmanufacturing/volumetric/2022/01" #define XML_3MF_NAMESPACE_IMPLICITSPEC "http://schemas.3mf.io/3dmanufacturing/implicit/2023/12" +#define XML_3MF_NAMESPACE_BOOLEANSPEC "http://schemas.3mf.io/3dmanufacturing/booleanoperations/2023/07" #define XML_3MF_NAMESPACEPREFIX_MATERIAL "m" #define XML_3MF_NAMESPACEPREFIX_PRODUCTION "p" @@ -98,6 +99,7 @@ These are given by the 3MF Standard #define XML_3MF_NAMESPACEPREFIX_VOLUMETRIC "v" #define XML_3MF_NAMESPACEPREFIX_IMPLICIT "i" #define XML_3MF_NAMESPACEPREFIX_TRIANGLESETS "t" +#define XML_3MF_NAMESPACEPREFIX_BOOLEAN "bo" #define XML_3MF_ATTRIBUTE_XMLNS "xmlns" #define XML_3MF_ATTRIBUTE_PREFIX_XML "xml" @@ -315,6 +317,8 @@ These are given by the 3MF Standard #define XML_3MF_ELEMENT_BOUNDARY_SHAPE "levelset" +#define XML_3MF_ELEMENT_BOOLEANSHAPE "booleanshape" +#define XML_3MF_ELEMENT_BOOLEAN "boolean" #define XML_3MF_ATTRIBUTE_LEVELSET_VOLUMEDATA "volumeid" #define XML_3MF_ATTRIBUTE_BOUNDARY_SHAPE_ID "id" #define XML_3MF_ATTRIBUTE_BOUNDARY_SHAPE_FUNCTION_ID "functionid" @@ -355,6 +359,15 @@ These are given by the 3MF Standard #define XML_3MF_ATTRIBUTE_COMPONENT_OBJECTID "objectid" #define XML_3MF_ATTRIBUTE_COMPONENT_TRANSFORM "transform" +#define XML_3MF_ATTRIBUTE_BOOLEAN_OBJECTID "objectid" +#define XML_3MF_ATTRIBUTE_BOOLEAN_OPERATION "operation" +#define XML_3MF_ATTRIBUTE_BOOLEAN_TRANSFORM "transform" +#define XML_3MF_ATTRIBUTE_BOOLEAN_PATH "path" + +#define XML_3MF_VALUE_BOOLEAN_OPERATION_UNION "union" +#define XML_3MF_VALUE_BOOLEAN_OPERATION_DIFFERENCE "difference" +#define XML_3MF_VALUE_BOOLEAN_OPERATION_INTERSECTION "intersection" + // Color resource group #define XML_3MF_ELEMENT_COLORGROUP "colorgroup" #define XML_3MF_ATTRIBUTE_COLORS_COLOR "color" diff --git a/Include/Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_Boolean.h b/Include/Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_Boolean.h new file mode 100644 index 000000000..e88168df2 --- /dev/null +++ b/Include/Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_Boolean.h @@ -0,0 +1,34 @@ +/*++ +--*/ +#ifndef __NMR_MODELREADERNODE_BOOLEAN2307_BOOLEAN +#define __NMR_MODELREADERNODE_BOOLEAN2307_BOOLEAN + +#include "Model/Reader/NMR_ModelReaderNode.h" +#include "Model/Classes/NMR_ModelObject.h" + +namespace NMR { + class CModelReaderNode_Boolean2307_Boolean : public CModelReaderNode { + private: + CModel * m_pModel; + ModelResourceID m_nObjectID; + std::string m_sPath; + nfBool m_bHasObjectID; + nfBool m_bHasTransform; + nfBool m_bHasPath; + NMATRIX3 m_mTransform; + + public: + CModelReaderNode_Boolean2307_Boolean() = delete; + CModelReaderNode_Boolean2307_Boolean(_In_ CModel * pModel, _In_ PModelWarnings pWarnings); + + void parseXML(_In_ CXmlReader * pXMLReader) override; + CModelObject * getObject(); + NMATRIX3 getTransform() const; + +protected: + void OnAttribute(_In_z_ const nfChar * pAttributeName, _In_z_ const nfChar * pAttributeValue) override; + void OnNSAttribute(_In_z_ const nfChar * pAttributeName, _In_z_ const nfChar * pAttributeValue, _In_z_ const nfChar * pNameSpace) override; + }; +} + +#endif diff --git a/Include/Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_BooleanShape.h b/Include/Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_BooleanShape.h new file mode 100644 index 000000000..2d8897b5e --- /dev/null +++ b/Include/Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_BooleanShape.h @@ -0,0 +1,34 @@ +/*++ +--*/ +#ifndef __NMR_MODELREADERNODE_BOOLEAN2307_BOOLEANSHAPE +#define __NMR_MODELREADERNODE_BOOLEAN2307_BOOLEANSHAPE + +#include "Model/Reader/NMR_ModelReaderNode.h" +#include "Model/Classes/NMR_ModelBooleanObject.h" + +namespace NMR { + class CModelReaderNode_Boolean2307_BooleanShape : public CModelReaderNode { + private: + CModel * m_pModel; + PModelBooleanObject m_pBooleanObject; + ModelResourceID m_nObjectID; + std::string m_sPath; + nfBool m_bHasObjectID; + nfBool m_bHasTransform; + nfBool m_bHasPath; + NMATRIX3 m_mTransform; + + public: + CModelReaderNode_Boolean2307_BooleanShape() = delete; + CModelReaderNode_Boolean2307_BooleanShape(_In_ CModel * pModel, _In_ PModelBooleanObject pBooleanObject, _In_ PModelWarnings pWarnings); + + void parseXML(_In_ CXmlReader * pXMLReader) override; + + protected: + void OnAttribute(_In_z_ const nfChar * pAttributeName, _In_z_ const nfChar * pAttributeValue) override; + void OnNSAttribute(_In_z_ const nfChar * pAttributeName, _In_z_ const nfChar * pAttributeValue, _In_z_ const nfChar * pNameSpace) override; + void OnNSChildElement(_In_z_ const nfChar * pChildName, _In_z_ const nfChar * pNameSpace, _In_ CXmlReader * pXMLReader) override; + }; +} + +#endif diff --git a/Include/Model/Writer/v100/NMR_ModelWriterNode100_Model.h b/Include/Model/Writer/v100/NMR_ModelWriterNode100_Model.h index 5c716c209..4f99222d9 100644 --- a/Include/Model/Writer/v100/NMR_ModelWriterNode100_Model.h +++ b/Include/Model/Writer/v100/NMR_ModelWriterNode100_Model.h @@ -37,6 +37,7 @@ This is the class for exporting the 3mf model stream root node. #include "Model/Classes/NMR_Model.h" #include "Model/Writer/NMR_ModelWriterNode_ModelBase.h" #include "Model/Classes/NMR_ModelComponentsObject.h" +#include "Model/Classes/NMR_ModelBooleanObject.h" #include "Model/Classes/NMR_ModelMeshObject.h" #include "Common/Platform/NMR_XmlWriter.h" @@ -65,6 +66,7 @@ namespace NMR { nfBool m_bWriteCustomNamespaces; nfBool m_bWriteVolumetricExtension; nfBool m_bWriteImplicitExtension; + nfBool m_bWriteBooleanExtension; void writeModelMetaData(); void writeMetaData(_In_ PModelMetaData pMetaData); @@ -103,6 +105,7 @@ namespace NMR { void writeObjects(); void writeObject(CModelObject & pObject); + void writeBooleanObject(_In_ CModelBooleanObject * pBooleanObject); void writeBuild(); void writeSliceStacks(); diff --git a/Include/NMR_Spec_Version.h b/Include/NMR_Spec_Version.h index 87caaa255..e0c17d452 100644 --- a/Include/NMR_Spec_Version.h +++ b/Include/NMR_Spec_Version.h @@ -71,4 +71,8 @@ NMR_Spec_Version.h defines the current implementation version. #define NMR_SPECVERSION_IMPLICIT_MINOR 8 #define NMR_SPECVERSION_IMPLICIT_MICRO 0 +#define NMR_SPECVERSION_BOOLEAN_MAJOR 1 +#define NMR_SPECVERSION_BOOLEAN_MINOR 1 +#define NMR_SPECVERSION_BOOLEAN_MICRO 1 + #endif // __NMR_SPECVERSION diff --git a/Libraries/nanoflann/Include/nanoflann.hpp b/Libraries/nanoflann/Include/nanoflann.hpp new file mode 100644 index 000000000..dc297bc86 --- /dev/null +++ b/Libraries/nanoflann/Include/nanoflann.hpp @@ -0,0 +1,2813 @@ +/*********************************************************************** + * Software License Agreement (BSD License) + * + * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. + * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. + * Copyright 2011-2025 Jose Luis Blanco (joseluisblancoc@gmail.com). + * All rights reserved. + * + * THE BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *************************************************************************/ + +/** \mainpage nanoflann C++ API documentation + * nanoflann is a C++ header-only library for building KD-Trees, mostly + * optimized for 2D or 3D point clouds. + * + * nanoflann does not require compiling or installing, just an + * #include in your code. + * + * Macros that are observed in this file: + * - NANOFLANN_NO_THREADS: If defined, single thread will be enforced. + * - NANOFLANN_FIRST_MATCH: If defined, in case of a tie in distances the item with the smallest + * index will be returned. + * - NANOFLANN_NODE_ALIGNMENT: The memory alignment, in bytes, for kd-tree nodes. Default: 16 + * + * See: + * - [Online README](https://github.com/jlblancoc/nanoflann) + * - [C++ API documentation](https://jlblancoc.github.io/nanoflann/) + */ + +#pragma once + +#include +#include +#include +#include +#include // for abs() +#include +#include // for abs() +#include // std::reference_wrapper +#include +#include +#include // std::numeric_limits +#include +#include +#include +#include +#include + +/** Library version: 0xMmP (M=Major,m=minor,P=patch) */ +#define NANOFLANN_VERSION 0x190 + +// Avoid conflicting declaration of min/max macros in Windows headers +#if !defined(NOMINMAX) && (defined(_WIN32) || defined(_WIN32_) || defined(WIN32) || defined(_WIN64)) +#define NOMINMAX +#ifdef max +#undef max +#undef min +#endif +#endif +// Avoid conflicts with X11 headers +#ifdef None +#undef None +#endif + +// Handle restricted pointers +#if defined(__GNUC__) || defined(__clang__) +#define NANOFLANN_RESTRICT __restrict__ +#elif defined(_MSC_VER) +#define NANOFLANN_RESTRICT __restrict +#else +#define NANOFLANN_RESTRICT +#endif + +// Memory alignment of KD-tree nodes: +#ifndef NANOFLANN_NODE_ALIGNMENT +#define NANOFLANN_NODE_ALIGNMENT 16 +#endif + +namespace nanoflann +{ +/** @addtogroup nanoflann_grp nanoflann C++ library for KD-trees + * @{ */ + +/** the PI constant (required to avoid MSVC missing symbols) */ +template +constexpr T pi_const() +{ + return static_cast(3.14159265358979323846); +} + +/** + * Traits if object is resizable and assignable (typically has a resize | assign + * method) + */ +template +struct has_resize : std::false_type +{ +}; + +template +struct has_resize().resize(1), 0)> : std::true_type +{ +}; + +template +struct has_assign : std::false_type +{ +}; + +template +struct has_assign().assign(1, 0), 0)> : std::true_type +{ +}; + +/** + * Free function to resize a resizable object + */ +template +inline typename std::enable_if::value, void>::type resize( + Container& c, const size_t nElements) +{ + c.resize(nElements); +} + +/** + * Free function that has no effects on non resizable containers (e.g. + * std::array) It raises an exception if the expected size does not match + */ +template +inline typename std::enable_if::value, void>::type resize( + Container& c, const size_t nElements) +{ + if (nElements != c.size()) throw std::logic_error("Attempt to resize a fixed size container."); +} + +/** + * Free function to assign to a container + */ +template +inline typename std::enable_if::value, void>::type assign( + Container& c, const size_t nElements, const T& value) +{ + c.assign(nElements, value); +} + +/** + * Free function to assign to a std::array + */ +template +inline typename std::enable_if::value, void>::type assign( + Container& c, const size_t nElements, const T& value) +{ + for (size_t i = 0; i < nElements; i++) c[i] = value; +} + +/** operator "<" for std::sort() */ +struct IndexDist_Sorter +{ + /** PairType will be typically: ResultItem */ + template + bool operator()(const PairType& p1, const PairType& p2) const + { + return p1.second < p2.second; + } +}; + +/** + * Each result element in RadiusResultSet. Note that distances and indices + * are named `first` and `second` to keep backward-compatibility with the + * `std::pair<>` type used in the past. In contrast, this structure is ensured + * to be `std::is_standard_layout` so it can be used in wrappers to other + * languages. + * See: https://github.com/jlblancoc/nanoflann/issues/166 + */ +template +struct ResultItem +{ + ResultItem() = default; + ResultItem(const IndexType index, const DistanceType distance) : first(index), second(distance) + { + } + + IndexType first; //!< Index of the sample in the dataset + DistanceType second; //!< Distance from sample to query point +}; + +/** @addtogroup result_sets_grp Result set classes + * @{ */ + +/** Result set for KNN searches (N-closest neighbors) */ +template +class KNNResultSet +{ + public: + using DistanceType = _DistanceType; + using IndexType = _IndexType; + using CountType = _CountType; + + private: + IndexType* indices; + DistanceType* dists; + CountType capacity; + CountType count; + + public: + explicit KNNResultSet(CountType capacity_) + : indices(nullptr), dists(nullptr), capacity(capacity_), count(0) + { + } + + void init(IndexType* indices_, DistanceType* dists_) + { + indices = indices_; + dists = dists_; + count = 0; + } + + CountType size() const noexcept { return count; } + bool empty() const noexcept { return count == 0; } + bool full() const noexcept { return count == capacity; } + + /** + * Called during search to add an element matching the criteria. + * @return true if the search should be continued, false if the results are + * sufficient + */ + bool addPoint(DistanceType dist, IndexType index) + { + CountType i; + for (i = count; i > 0; --i) + { + /** If defined and two points have the same distance, the one with + * the lowest-index will be returned first. */ +#ifdef NANOFLANN_FIRST_MATCH + if ((dists[i - 1] > dist) || ((dist == dists[i - 1]) && (indices[i - 1] > index))) + { +#else + if (dists[i - 1] > dist) + { +#endif + if (i < capacity) + { + dists[i] = dists[i - 1]; + indices[i] = indices[i - 1]; + } + } + else + break; + } + if (i < capacity) + { + dists[i] = dist; + indices[i] = index; + } + if (count < capacity) count++; + + // tell caller that the search shall continue + return true; + } + + //! Returns the worst distance among found solutions if the search result is + //! full, or the maximum possible distance, if not full yet. + DistanceType worstDist() const noexcept + { + return (count < capacity || !count) ? std::numeric_limits::max() + : dists[count - 1]; + } + + void sort() + { + // already sorted + } +}; + +/** Result set for RKNN searches (N-closest neighbors with a maximum radius) */ +template +class RKNNResultSet +{ + public: + using DistanceType = _DistanceType; + using IndexType = _IndexType; + using CountType = _CountType; + + private: + IndexType* indices; + DistanceType* dists; + CountType capacity; + CountType count; + DistanceType maximumSearchDistanceSquared; + + public: + explicit RKNNResultSet(CountType capacity_, DistanceType maximumSearchDistanceSquared_) + : indices(nullptr), + dists(nullptr), + capacity(capacity_), + count(0), + maximumSearchDistanceSquared(maximumSearchDistanceSquared_) + { + } + + void init(IndexType* indices_, DistanceType* dists_) + { + indices = indices_; + dists = dists_; + count = 0; + if (capacity) dists[capacity - 1] = maximumSearchDistanceSquared; + } + + CountType size() const noexcept { return count; } + bool empty() const noexcept { return count == 0; } + bool full() const noexcept { return count == capacity; } + + /** + * Called during search to add an element matching the criteria. + * @return true if the search should be continued, false if the results are + * sufficient + */ + bool addPoint(DistanceType dist, IndexType index) + { + CountType i; + for (i = count; i > 0; --i) + { + /** If defined and two points have the same distance, the one with + * the lowest-index will be returned first. */ +#ifdef NANOFLANN_FIRST_MATCH + if ((dists[i - 1] > dist) || ((dist == dists[i - 1]) && (indices[i - 1] > index))) + { +#else + if (dists[i - 1] > dist) + { +#endif + if (i < capacity) + { + dists[i] = dists[i - 1]; + indices[i] = indices[i - 1]; + } + } + else + break; + } + if (i < capacity) + { + dists[i] = dist; + indices[i] = index; + } + if (count < capacity) count++; + + // tell caller that the search shall continue + return true; + } + + //! Returns the worst distance among found solutions if the search result is + //! full, or the maximum possible distance, if not full yet. + DistanceType worstDist() const noexcept + { + return (count < capacity || !count) ? maximumSearchDistanceSquared : dists[count - 1]; + } + + void sort() + { + // already sorted + } +}; + +/** + * A result-set class used when performing a radius based search. + */ +template +class RadiusResultSet +{ + public: + using DistanceType = _DistanceType; + using IndexType = _IndexType; + + public: + const DistanceType radius; + + std::vector>& m_indices_dists; + + explicit RadiusResultSet( + DistanceType radius_, std::vector>& indices_dists) + : radius(radius_), m_indices_dists(indices_dists) + { + init(); + } + + void init() { clear(); } + void clear() { m_indices_dists.clear(); } + + size_t size() const noexcept { return m_indices_dists.size(); } + bool empty() const noexcept { return m_indices_dists.empty(); } + bool full() const noexcept { return true; } + + /** + * Called during search to add an element matching the criteria. + * @return true if the search should be continued, false if the results are + * sufficient + */ + bool addPoint(DistanceType dist, IndexType index) + { + if (dist < radius) m_indices_dists.emplace_back(index, dist); + return true; + } + + DistanceType worstDist() const noexcept { return radius; } + + /** + * Find the worst result (farthest neighbor) without copying or sorting + * Pre-conditions: size() > 0 + */ + ResultItem worst_item() const + { + if (m_indices_dists.empty()) + throw std::runtime_error( + "Cannot invoke RadiusResultSet::worst_item() on " + "an empty list of results."); + auto it = + std::max_element(m_indices_dists.begin(), m_indices_dists.end(), IndexDist_Sorter()); + return *it; + } + + void sort() { std::sort(m_indices_dists.begin(), m_indices_dists.end(), IndexDist_Sorter()); } +}; + +/** @} */ + +/** @addtogroup loadsave_grp Load/save auxiliary functions + * @{ */ +template +void save_value(std::ostream& stream, const T& value) +{ + stream.write(reinterpret_cast(&value), sizeof(T)); +} + +template +void save_value(std::ostream& stream, const std::vector& value) +{ + size_t size = value.size(); + stream.write(reinterpret_cast(&size), sizeof(size_t)); + stream.write(reinterpret_cast(value.data()), sizeof(T) * size); +} + +template +void load_value(std::istream& stream, T& value) +{ + stream.read(reinterpret_cast(&value), sizeof(T)); +} + +template +void load_value(std::istream& stream, std::vector& value) +{ + size_t size; + stream.read(reinterpret_cast(&size), sizeof(size_t)); + value.resize(size); + stream.read(reinterpret_cast(value.data()), sizeof(T) * size); +} +/** @} */ + +/** @addtogroup metric_grp Metric (distance) classes + * @{ */ + +struct Metric +{ +}; + +/** Manhattan distance functor (generic version, optimized for + * high-dimensionality data sets). Corresponding distance traits: + * nanoflann::metric_L1 + * + * \tparam T Type of the elements (e.g. double, float, uint8_t) + * \tparam DataSource Source of the data, i.e. where the vectors are stored + * \tparam _DistanceType Type of distance variables (must be signed) + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template +struct L1_Adaptor +{ + using ElementType = T; + using DistanceType = _DistanceType; + + const DataSource& data_source; + + L1_Adaptor(const DataSource& _data_source) : data_source(_data_source) {} + + inline DistanceType evalMetric( + const T* NANOFLANN_RESTRICT a, const IndexType b_idx, size_t size, + DistanceType worst_dist = -1) const + { + DistanceType result = DistanceType(); + const size_t multof4 = (size >> 2) << 2; // largest multiple of 4, i.e. 1 << 2 + size_t d; + + /* Process 4 items with each loop for efficiency. */ + if (worst_dist <= 0) + { + /* No checks with worst_dist. */ + for (d = 0; d < multof4; d += 4) + { + const DistanceType diff0 = + std::abs(a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0)); + const DistanceType diff1 = + std::abs(a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1)); + const DistanceType diff2 = + std::abs(a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2)); + const DistanceType diff3 = + std::abs(a[d + 3] - data_source.kdtree_get_pt(b_idx, d + 3)); + /* Parentheses break dependency chain: */ + result += (diff0 + diff1) + (diff2 + diff3); + } + } + else + { + /* Check with worst_dist. */ + for (d = 0; d < multof4; d += 4) + { + const DistanceType diff0 = + std::abs(a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0)); + const DistanceType diff1 = + std::abs(a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1)); + const DistanceType diff2 = + std::abs(a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2)); + const DistanceType diff3 = + std::abs(a[d + 3] - data_source.kdtree_get_pt(b_idx, d + 3)); + /* Parentheses break dependency chain: */ + result += (diff0 + diff1) + (diff2 + diff3); + if (result > worst_dist) + { + return result; + } + } + } + /* Process last 0-3 components. Unrolled loop with fall-through switch. + */ + switch (size - multof4) + { + case 3: + result += std::abs(a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2)); + case 2: + result += std::abs(a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1)); + case 1: + result += std::abs(a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0)); + case 0: + break; + } + return result; + } + + template + inline DistanceType accum_dist(const U a, const V b, const size_t) const + { + return std::abs(a - b); + } +}; + +/** **Squared** Euclidean distance functor (generic version, optimized for + * high-dimensionality data sets). Corresponding distance traits: + * nanoflann::metric_L2 + * + * \tparam T Type of the elements (e.g. double, float, uint8_t) + * \tparam DataSource Source of the data, i.e. where the vectors are stored + * \tparam _DistanceType Type of distance variables (must be signed) + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template +struct L2_Adaptor +{ + using ElementType = T; + using DistanceType = _DistanceType; + + const DataSource& data_source; + + L2_Adaptor(const DataSource& _data_source) : data_source(_data_source) {} + + inline DistanceType evalMetric( + const T* NANOFLANN_RESTRICT a, const IndexType b_idx, size_t size, + DistanceType worst_dist = -1) const + { + DistanceType result = DistanceType(); + const size_t multof4 = (size >> 2) << 2; // largest multiple of 4, i.e. 1 << 2 + size_t d; + + /* Process 4 items with each loop for efficiency. */ + if (worst_dist <= 0) + { + /* No checks with worst_dist. */ + for (d = 0; d < multof4; d += 4) + { + const DistanceType diff0 = a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0); + const DistanceType diff1 = a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1); + const DistanceType diff2 = a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2); + const DistanceType diff3 = a[d + 3] - data_source.kdtree_get_pt(b_idx, d + 3); + /* Parentheses break dependency chain: */ + result += (diff0 * diff0 + diff1 * diff1) + (diff2 * diff2 + diff3 * diff3); + } + } + else + { + /* Check with worst_dist. */ + for (d = 0; d < multof4; d += 4) + { + const DistanceType diff0 = a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0); + const DistanceType diff1 = a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1); + const DistanceType diff2 = a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2); + const DistanceType diff3 = a[d + 3] - data_source.kdtree_get_pt(b_idx, d + 3); + /* Parentheses break dependency chain: */ + result += (diff0 * diff0 + diff1 * diff1) + (diff2 * diff2 + diff3 * diff3); + if (result > worst_dist) + { + return result; + } + } + } + /* Process last 0-3 components. Unrolled loop with fall-through switch. + */ + DistanceType diff; + switch (size - multof4) + { + case 3: + diff = a[d + 2] - data_source.kdtree_get_pt(b_idx, d + 2); + result += diff * diff; + case 2: + diff = a[d + 1] - data_source.kdtree_get_pt(b_idx, d + 1); + result += diff * diff; + case 1: + diff = a[d + 0] - data_source.kdtree_get_pt(b_idx, d + 0); + result += diff * diff; + case 0: + break; + } + return result; + } + + template + inline DistanceType accum_dist(const U a, const V b, const size_t) const + { + auto diff = a - b; + return diff * diff; + } +}; + +/** **Squared** Euclidean (L2) distance functor (suitable for low-dimensionality + * datasets, like 2D or 3D point clouds) Corresponding distance traits: + * nanoflann::metric_L2_Simple + * + * \tparam T Type of the elements (e.g. double, float, uint8_t) + * \tparam DataSource Source of the data, i.e. where the vectors are stored + * \tparam _DistanceType Type of distance variables (must be signed) + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template +struct L2_Simple_Adaptor +{ + using ElementType = T; + using DistanceType = _DistanceType; + + const DataSource& data_source; + + L2_Simple_Adaptor(const DataSource& _data_source) : data_source(_data_source) {} + + inline DistanceType evalMetric(const T* a, const IndexType b_idx, size_t size) const + { + DistanceType result = DistanceType(); + for (size_t i = 0; i < size; ++i) + { + const DistanceType diff = a[i] - data_source.kdtree_get_pt(b_idx, i); + result += diff * diff; + } + return result; + } + + template + inline DistanceType accum_dist(const U a, const V b, const size_t) const + { + auto diff = a - b; + return diff * diff; + } +}; + +/** SO2 distance functor + * Corresponding distance traits: nanoflann::metric_SO2 + * + * \tparam T Type of the elements (e.g. double, float, uint8_t) + * \tparam DataSource Source of the data, i.e. where the vectors are stored + * \tparam _DistanceType Type of distance variables (must be signed) (e.g. + * float, double) orientation is constrained to be in [-pi, pi] + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template +struct SO2_Adaptor +{ + using ElementType = T; + using DistanceType = _DistanceType; + + const DataSource& data_source; + + SO2_Adaptor(const DataSource& _data_source) : data_source(_data_source) {} + + inline DistanceType evalMetric(const T* a, const IndexType b_idx, size_t size) const + { + return accum_dist(a[size - 1], data_source.kdtree_get_pt(b_idx, size - 1), size - 1); + } + + /** Note: this assumes that input angles are already in the range [-pi,pi] + */ + template + inline DistanceType accum_dist(const U a, const V b, const size_t) const + { + DistanceType result = DistanceType(); + DistanceType PI = pi_const(); + result = b - a; + if (result > PI) + result -= 2 * PI; + else if (result < -PI) + result += 2 * PI; + return result; + } +}; + +/** SO3 distance functor (Uses L2_Simple) + * Corresponding distance traits: nanoflann::metric_SO3 + * + * \tparam T Type of the elements (e.g. double, float, uint8_t) + * \tparam DataSource Source of the data, i.e. where the vectors are stored + * \tparam _DistanceType Type of distance variables (must be signed) (e.g. + * float, double) + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template +struct SO3_Adaptor +{ + using ElementType = T; + using DistanceType = _DistanceType; + + L2_Simple_Adaptor distance_L2_Simple; + + SO3_Adaptor(const DataSource& _data_source) : distance_L2_Simple(_data_source) {} + + inline DistanceType evalMetric(const T* a, const IndexType b_idx, size_t size) const + { + return distance_L2_Simple.evalMetric(a, b_idx, size); + } + + template + inline DistanceType accum_dist(const U a, const V b, const size_t idx) const + { + return distance_L2_Simple.accum_dist(a, b, idx); + } +}; + +/** Metaprogramming helper traits class for the L1 (Manhattan) metric */ +struct metric_L1 : public Metric +{ + template + struct traits + { + using distance_t = L1_Adaptor; + }; +}; +/** Metaprogramming helper traits class for the L2 (Euclidean) **squared** + * distance metric */ +struct metric_L2 : public Metric +{ + template + struct traits + { + using distance_t = L2_Adaptor; + }; +}; +/** Metaprogramming helper traits class for the L2_simple (Euclidean) + * **squared** distance metric */ +struct metric_L2_Simple : public Metric +{ + template + struct traits + { + using distance_t = L2_Simple_Adaptor; + }; +}; +/** Metaprogramming helper traits class for the SO3_InnerProdQuat metric */ +struct metric_SO2 : public Metric +{ + template + struct traits + { + using distance_t = SO2_Adaptor; + }; +}; +/** Metaprogramming helper traits class for the SO3_InnerProdQuat metric */ +struct metric_SO3 : public Metric +{ + template + struct traits + { + using distance_t = SO3_Adaptor; + }; +}; + +/** @} */ + +/** @addtogroup param_grp Parameter structs + * @{ */ + +enum class KDTreeSingleIndexAdaptorFlags +{ + None = 0, + SkipInitialBuildIndex = 1 +}; + +inline std::underlying_type::type operator&( + KDTreeSingleIndexAdaptorFlags lhs, KDTreeSingleIndexAdaptorFlags rhs) +{ + using underlying = typename std::underlying_type::type; + return static_cast(lhs) & static_cast(rhs); +} + +/** Parameters (see README.md) */ +struct KDTreeSingleIndexAdaptorParams +{ + KDTreeSingleIndexAdaptorParams( + size_t _leaf_max_size = 10, + KDTreeSingleIndexAdaptorFlags _flags = KDTreeSingleIndexAdaptorFlags::None, + unsigned int _n_thread_build = 1) + : leaf_max_size(_leaf_max_size), flags(_flags), n_thread_build(_n_thread_build) + { + } + + size_t leaf_max_size; + KDTreeSingleIndexAdaptorFlags flags; + unsigned int n_thread_build; +}; + +/** Search options for KDTreeSingleIndexAdaptor::findNeighbors() */ +struct SearchParameters +{ + SearchParameters(float eps_ = 0, bool sorted_ = true) : eps(eps_), sorted(sorted_) {} + + float eps; //!< search for eps-approximate neighbours (default: 0) + bool sorted; //!< only for radius search, require neighbours sorted by + //!< distance (default: true) +}; +/** @} */ + +/** @addtogroup memalloc_grp Memory allocation + * @{ */ + +/** + * Pooled storage allocator + * + * The following routines allow for the efficient allocation of storage in + * small chunks from a specified pool. Rather than allowing each structure + * to be freed individually, an entire pool of storage is freed at once. + * This method has two advantages over just using malloc() and free(). First, + * it is far more efficient for allocating small objects, as there is + * no overhead for remembering all the information needed to free each + * object or consolidating fragmented memory. Second, the decision about + * how long to keep an object is made at the time of allocation, and there + * is no need to track down all the objects to free them. + * + */ +class PooledAllocator +{ + static constexpr size_t WORDSIZE = 16; // WORDSIZE must >= 8 + static constexpr size_t BLOCKSIZE = 8192; + + /* We maintain memory alignment to word boundaries by requiring that all + allocations be in multiples of the machine wordsize. */ + /* Size of machine word in bytes. Must be power of 2. */ + /* Minimum number of bytes requested at a time from the system. Must be + * multiple of WORDSIZE. */ + + using Size = size_t; + + Size remaining_ = 0; //!< Number of bytes left in current block of storage + void* base_ = nullptr; //!< Pointer to base of current block of storage + void* loc_ = nullptr; //!< Current location in block to next allocate + + void internal_init() + { + remaining_ = 0; + base_ = nullptr; + usedMemory = 0; + wastedMemory = 0; + } + + public: + Size usedMemory = 0; + Size wastedMemory = 0; + + /** + Default constructor. Initializes a new pool. + */ + PooledAllocator() { internal_init(); } + + /** + * Destructor. Frees all the memory allocated in this pool. + */ + ~PooledAllocator() { free_all(); } + + /** Frees all allocated memory chunks */ + void free_all() + { + while (base_ != nullptr) + { + // Get pointer to prev block + void* prev = *(static_cast(base_)); + ::free(base_); + base_ = prev; + } + internal_init(); + } + + /** + * Returns a pointer to a piece of new memory of the given size in bytes + * allocated from the pool. + */ + void* malloc(const size_t req_size) + { + /* Round size up to a multiple of wordsize. The following expression + only works for WORDSIZE that is a power of 2, by masking last bits + of incremented size to zero. + */ + const Size size = (req_size + (WORDSIZE - 1)) & ~(WORDSIZE - 1); + + /* Check whether a new block must be allocated. Note that the first + word of a block is reserved for a pointer to the previous block. + */ + if (size > remaining_) + { + wastedMemory += remaining_; + + /* Allocate new storage. */ + const Size blocksize = size > BLOCKSIZE ? size + WORDSIZE : BLOCKSIZE + WORDSIZE; + + // use the standard C malloc to allocate memory + void* m = ::malloc(blocksize); + if (!m) + { + throw std::bad_alloc(); + } + + /* Fill first word of new block with pointer to previous block. */ + static_cast(m)[0] = base_; + base_ = m; + + remaining_ = blocksize - WORDSIZE; + loc_ = static_cast(m) + WORDSIZE; + } + void* rloc = loc_; + loc_ = static_cast(loc_) + size; + remaining_ -= size; + + usedMemory += size; + + return rloc; + } + + /** + * Allocates (using this pool) a generic type T. + * + * Params: + * count = number of instances to allocate. + * Returns: pointer (of type T*) to memory buffer + */ + template + T* allocate(const size_t count = 1) + { + T* mem = static_cast(this->malloc(sizeof(T) * count)); + return mem; + } +}; +/** @} */ + +/** @addtogroup nanoflann_metaprog_grp Auxiliary metaprogramming stuff + * @{ */ + +/** Used to declare fixed-size arrays when DIM>0, dynamically-allocated vectors + * when DIM=-1. Fixed size version for a generic DIM: + */ +template +struct array_or_vector +{ + using type = std::array; +}; +/** Dynamic size version */ +template +struct array_or_vector<-1, T> +{ + using type = std::vector; +}; + +/** @} */ + +/** kd-tree base-class + * + * Contains the member functions common to the classes KDTreeSingleIndexAdaptor + * and KDTreeSingleIndexDynamicAdaptor_. + * + * \tparam Derived The name of the class which inherits this class. + * \tparam DatasetAdaptor The user-provided adaptor, which must be ensured to + * have a lifetime equal or longer than the instance of this class. + * \tparam Distance The distance metric to use, these are all classes derived + * from nanoflann::Metric + * \tparam DIM Dimensionality of data points (e.g. 3 for 3D points) + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template < + class Derived, typename Distance, class DatasetAdaptor, int32_t DIM = -1, + typename index_t = uint32_t> +class KDTreeBaseClass +{ + public: + /** Frees the previously-built index. Automatically called within + * buildIndex(). */ + void freeIndex(Derived& obj) + { + obj.pool_.free_all(); + obj.root_node_ = nullptr; + obj.size_at_index_build_ = 0; + } + + using ElementType = typename Distance::ElementType; + using DistanceType = typename Distance::DistanceType; + using IndexType = index_t; + + /** + * Array of indices to vectors in the dataset_. + */ + std::vector vAcc_; + + using Offset = typename decltype(vAcc_)::size_type; + using Size = typename decltype(vAcc_)::size_type; + using Dimension = int32_t; + + /*------------------------------------------------------------------- + * Internal Data Structures + * + * "Node" below can be declared with alignas(N) to improve + * cache friendliness and SIMD load/store performance. + * + * The optimal N depends on the underlying hardware: + * + Intel x86-64: 16 for SSE, 32 for AVX/AVX2 and 64 for AVX-512 + * + NVIDIA Jetson: 16 for ARM + NEON and CUDA float4/ + * To avoid unnecessary padding, the smallest alignment + * compatible with a platform's vector width should be chosen. + * ------------------------------------------------------------------*/ + struct alignas(NANOFLANN_NODE_ALIGNMENT) Node + { + /** Union used because a node can be either a LEAF node or a non-leaf + * node, so both data fields are never used simultaneously */ + union + { + struct leaf + { + Offset left, right; //!< Indices of points in leaf node + } lr; + struct nonleaf + { + Dimension divfeat; //!< Dimension used for subdivision. + /// The values used for subdivision. + DistanceType divlow, divhigh; + } sub; + } node_type; + + /** Child nodes (both=nullptr mean its a leaf node) */ + Node *child1 = nullptr, *child2 = nullptr; + }; + + using NodePtr = Node*; + using NodeConstPtr = const Node*; + + struct Interval + { + ElementType low, high; + }; + + NodePtr root_node_ = nullptr; + + Size leaf_max_size_ = 0; + + /// Number of thread for concurrent tree build + Size n_thread_build_ = 1; + /// Number of current points in the dataset + Size size_ = 0; + /// Number of points in the dataset when the index was built + Size size_at_index_build_ = 0; + Dimension dim_ = 0; //!< Dimensionality of each data point + + /** Define "BoundingBox" as a fixed-size or variable-size container + * depending on "DIM" */ + using BoundingBox = typename array_or_vector::type; + + /** Define "distance_vector_t" as a fixed-size or variable-size container + * depending on "DIM" */ + using distance_vector_t = typename array_or_vector::type; + + /** The KD-tree used to find neighbours */ + BoundingBox root_bbox_; + + /** + * Pooled memory allocator. + * + * Using a pooled memory allocator is more efficient + * than allocating memory directly when there is a large + * number small of memory allocations. + */ + PooledAllocator pool_; + + /** Returns number of points in dataset */ + Size size(const Derived& obj) const { return obj.size_; } + + /** Returns the length of each point in the dataset */ + Size veclen(const Derived& obj) const { return DIM > 0 ? DIM : obj.dim_; } + + /// Helper accessor to the dataset points: + ElementType dataset_get(const Derived& obj, IndexType element, Dimension component) const + { + return obj.dataset_.kdtree_get_pt(element, component); + } + + /** + * Computes the index memory usage + * Returns: memory used by the index + */ + Size usedMemory(const Derived& obj) const + { + return obj.pool_.usedMemory + obj.pool_.wastedMemory + + obj.dataset_.kdtree_get_point_count() * + sizeof(IndexType); // pool memory and vind array memory + } + + /** + * Compute the minimum and maximum element values in the specified dimension + */ + void computeMinMax( + const Derived& obj, Offset ind, Size count, Dimension element, ElementType& min_elem, + ElementType& max_elem) const + { + min_elem = dataset_get(obj, vAcc_[ind], element); + max_elem = min_elem; + for (Offset i = 1; i < count; ++i) + { + ElementType val = dataset_get(obj, vAcc_[ind + i], element); + if (val < min_elem) min_elem = val; + if (val > max_elem) max_elem = val; + } + } + + /** + * Create a tree node that subdivides the list of vecs from vind[first] + * to vind[last]. The routine is called recursively on each sublist. + * + * @param left index of the first vector + * @param right index of the last vector + * @param bbox bounding box used as input for splitting and output for + * parent node + */ + NodePtr divideTree(Derived& obj, const Offset left, const Offset right, BoundingBox& bbox) + { + assert(left < obj.dataset_.kdtree_get_point_count()); + + NodePtr node = obj.pool_.template allocate(); // allocate memory + const auto dims = (DIM > 0 ? DIM : obj.dim_); + + /* If too few exemplars remain, then make this a leaf node. */ + if ((right - left) <= static_cast(obj.leaf_max_size_)) + { + node->child1 = node->child2 = nullptr; /* Mark as leaf node. */ + node->node_type.lr.left = left; + node->node_type.lr.right = right; + + // compute bounding-box of leaf points + for (Dimension i = 0; i < dims; ++i) + { + bbox[i].low = dataset_get(obj, obj.vAcc_[left], i); + bbox[i].high = dataset_get(obj, obj.vAcc_[left], i); + } + for (Offset k = left + 1; k < right; ++k) + { + for (Dimension i = 0; i < dims; ++i) + { + const auto val = dataset_get(obj, obj.vAcc_[k], i); + if (bbox[i].low > val) bbox[i].low = val; + if (bbox[i].high < val) bbox[i].high = val; + } + } + } + else + { + /* Determine the index, dimension and value for split plane */ + Offset idx; + Dimension cutfeat; + DistanceType cutval; + middleSplit_(obj, left, right - left, idx, cutfeat, cutval, bbox); + + node->node_type.sub.divfeat = cutfeat; + + /* Recurse on left */ + BoundingBox left_bbox(bbox); + left_bbox[cutfeat].high = cutval; + node->child1 = this->divideTree(obj, left, left + idx, left_bbox); + + /* Recurse on right */ + BoundingBox right_bbox(bbox); + right_bbox[cutfeat].low = cutval; + node->child2 = this->divideTree(obj, left + idx, right, right_bbox); + + node->node_type.sub.divlow = left_bbox[cutfeat].high; + node->node_type.sub.divhigh = right_bbox[cutfeat].low; + + for (Dimension i = 0; i < dims; ++i) + { + bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low); + bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high); + } + } + + return node; + } + + /** + * Create a tree node that subdivides the list of vecs from vind[first] to + * vind[last] concurrently. The routine is called recursively on each + * sublist. + * + * @param left index of the first vector + * @param right index of the last vector + * @param bbox bounding box used as input for splitting and output for + * parent node + * @param thread_count count of std::async threads + * @param mutex mutex for mempool allocation + */ + NodePtr divideTreeConcurrent( + Derived& obj, const Offset left, const Offset right, BoundingBox& bbox, + std::atomic& thread_count, std::mutex& mutex) + { + std::unique_lock lock(mutex); + NodePtr node = obj.pool_.template allocate(); // allocate memory + lock.unlock(); + + const auto dims = (DIM > 0 ? DIM : obj.dim_); + + /* If too few exemplars remain, then make this a leaf node. */ + if ((right - left) <= static_cast(obj.leaf_max_size_)) + { + node->child1 = node->child2 = nullptr; /* Mark as leaf node. */ + node->node_type.lr.left = left; + node->node_type.lr.right = right; + + // compute bounding-box of leaf points + for (Dimension i = 0; i < dims; ++i) + { + bbox[i].low = dataset_get(obj, obj.vAcc_[left], i); + bbox[i].high = dataset_get(obj, obj.vAcc_[left], i); + } + for (Offset k = left + 1; k < right; ++k) + { + for (Dimension i = 0; i < dims; ++i) + { + const auto val = dataset_get(obj, obj.vAcc_[k], i); + if (bbox[i].low > val) bbox[i].low = val; + if (bbox[i].high < val) bbox[i].high = val; + } + } + } + else + { + /* Determine the index, dimension and value for split plane */ + Offset idx; + Dimension cutfeat; + DistanceType cutval; + middleSplit_(obj, left, right - left, idx, cutfeat, cutval, bbox); + + node->node_type.sub.divfeat = cutfeat; + + std::future right_future; + + /* Recurse on right concurrently, if possible */ + + BoundingBox right_bbox(bbox); + right_bbox[cutfeat].low = cutval; + if (++thread_count < n_thread_build_) + { + /* Concurrent thread for right recursion */ + + right_future = std::async( + std::launch::async, &KDTreeBaseClass::divideTreeConcurrent, this, std::ref(obj), + left + idx, right, std::ref(right_bbox), std::ref(thread_count), + std::ref(mutex)); + } + else + { + --thread_count; + } + + /* Recurse on left in this thread */ + + BoundingBox left_bbox(bbox); + left_bbox[cutfeat].high = cutval; + node->child1 = + this->divideTreeConcurrent(obj, left, left + idx, left_bbox, thread_count, mutex); + + if (right_future.valid()) + { + /* Block and wait for concurrent right from above */ + + node->child2 = right_future.get(); + --thread_count; + } + else + { + /* Otherwise, recurse on right in this thread */ + + node->child2 = this->divideTreeConcurrent( + obj, left + idx, right, right_bbox, thread_count, mutex); + } + + node->node_type.sub.divlow = left_bbox[cutfeat].high; + node->node_type.sub.divhigh = right_bbox[cutfeat].low; + + for (Dimension i = 0; i < dims; ++i) + { + bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low); + bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high); + } + } + + return node; + } + + void middleSplit_( + const Derived& obj, const Offset ind, const Size count, Offset& index, Dimension& cutfeat, + DistanceType& cutval, const BoundingBox& bbox) + { + const auto dims = (DIM > 0 ? DIM : obj.dim_); + const auto EPS = static_cast(0.00001); + + // Pre-compute max_span once + ElementType max_span = bbox[0].high - bbox[0].low; + for (Dimension i = 1; i < dims; ++i) + { + ElementType span = bbox[i].high - bbox[i].low; + if (span > max_span) max_span = span; + } + + // Single-pass min/max computation for candidate dimensions + cutfeat = 0; + ElementType max_spread = -1; + ElementType min_elem = 0, max_elem = 0; + + // Only check dimensions within (1-EPS) of max_span + std::vector candidates; + candidates.reserve(dims); + for (Dimension i = 0; i < dims; ++i) + { + if (bbox[i].high - bbox[i].low >= (1 - EPS) * max_span) + { + candidates.push_back(i); + } + } + + // Vectorized min/max for candidates + for (Dimension dim : candidates) + { + ElementType local_min = dataset_get(obj, vAcc_[ind], dim); + ElementType local_max = local_min; + + // Unrolled loop for better performance + constexpr size_t UNROLL = 4; + Offset k = 1; + for (; k + UNROLL <= count; k += UNROLL) + { + ElementType v0 = dataset_get(obj, vAcc_[ind + k], dim); + ElementType v1 = dataset_get(obj, vAcc_[ind + k + 1], dim); + ElementType v2 = dataset_get(obj, vAcc_[ind + k + 2], dim); + ElementType v3 = dataset_get(obj, vAcc_[ind + k + 3], dim); + + local_min = std::min({local_min, v0, v1, v2, v3}); + local_max = std::max({local_max, v0, v1, v2, v3}); + } + + // Handle remainder + for (; k < count; ++k) + { + ElementType val = dataset_get(obj, vAcc_[ind + k], dim); + local_min = std::min(local_min, val); + local_max = std::max(local_max, val); + } + + ElementType spread = local_max - local_min; + if (spread > max_spread) + { + cutfeat = dim; + max_spread = spread; + min_elem = local_min; + max_elem = local_max; + } + } + + // Median-of-three for better balance + DistanceType split_val = (bbox[cutfeat].low + bbox[cutfeat].high) / 2; + if (split_val < min_elem) split_val = min_elem; + if (split_val > max_elem) split_val = max_elem; + + cutval = split_val; + + // Optimized partitioning + Offset lim1, lim2; + planeSplit(obj, ind, count, cutfeat, cutval, lim1, lim2); + + index = (lim1 > count / 2) ? lim1 : (lim2 < count / 2) ? lim2 : count / 2; + } + + /** + * Subdivide the list of points by a plane perpendicular on the axis + * corresponding to the 'cutfeat' dimension at 'cutval' position. + * + * On return: + * dataset[ind[0..lim1-1]][cutfeat] < cutval + * dataset[ind[lim1..lim2-1]][cutfeat] == cutval + * dataset[ind[lim2..count]][cutfeat] > cutval + */ + void planeSplit( + const Derived& obj, const Offset ind, const Size count, const Dimension cutfeat, + const DistanceType& cutval, Offset& lim1, Offset& lim2) + { + // Dutch National Flag algorithm for three-way partitioning + Offset left = 0; + Offset mid = 0; + Offset right = count - 1; + + while (mid <= right) + { + ElementType val = dataset_get(obj, vAcc_[ind + mid], cutfeat); + + if (val < cutval) + { + std::swap(vAcc_[ind + left], vAcc_[ind + mid]); + left++; + mid++; + } + else if (val > cutval) + { + std::swap(vAcc_[ind + mid], vAcc_[ind + right]); + right--; + } + else + { + mid++; + } + } + + lim1 = left; + lim2 = mid; + } + + DistanceType computeInitialDistances( + const Derived& obj, const ElementType* vec, distance_vector_t& dists) const + { + assert(vec); + DistanceType dist = DistanceType(); + + for (Dimension i = 0; i < (DIM > 0 ? DIM : obj.dim_); ++i) + { + if (vec[i] < obj.root_bbox_[i].low) + { + dists[i] = obj.distance_.accum_dist(vec[i], obj.root_bbox_[i].low, i); + dist += dists[i]; + } + if (vec[i] > obj.root_bbox_[i].high) + { + dists[i] = obj.distance_.accum_dist(vec[i], obj.root_bbox_[i].high, i); + dist += dists[i]; + } + } + return dist; + } + + static void save_tree(const Derived& obj, std::ostream& stream, const NodeConstPtr tree) + { + save_value(stream, *tree); + if (tree->child1 != nullptr) + { + save_tree(obj, stream, tree->child1); + } + if (tree->child2 != nullptr) + { + save_tree(obj, stream, tree->child2); + } + } + + static void load_tree(Derived& obj, std::istream& stream, NodePtr& tree) + { + tree = obj.pool_.template allocate(); + load_value(stream, *tree); + if (tree->child1 != nullptr) + { + load_tree(obj, stream, tree->child1); + } + if (tree->child2 != nullptr) + { + load_tree(obj, stream, tree->child2); + } + } + + /** Stores the index in a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so + * when loading the index object it must be constructed associated to the + * same source of data points used while building it. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void saveIndex(const Derived& obj, std::ostream& stream) const + { + save_value(stream, obj.size_); + save_value(stream, obj.dim_); + save_value(stream, obj.root_bbox_); + save_value(stream, obj.leaf_max_size_); + save_value(stream, obj.vAcc_); + if (obj.root_node_) save_tree(obj, stream, obj.root_node_); + } + + /** Loads a previous index from a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so + * the index object must be constructed associated to the same source of + * data points used while building the index. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void loadIndex(Derived& obj, std::istream& stream) + { + load_value(stream, obj.size_); + load_value(stream, obj.dim_); + load_value(stream, obj.root_bbox_); + load_value(stream, obj.leaf_max_size_); + load_value(stream, obj.vAcc_); + load_tree(obj, stream, obj.root_node_); + } +}; + +/** @addtogroup kdtrees_grp KD-tree classes and adaptors + * @{ */ + +/** kd-tree static index + * + * Contains the k-d trees and other information for indexing a set of points + * for nearest-neighbor matching. + * + * The class "DatasetAdaptor" must provide the following interface (can be + * non-virtual, inlined methods): + * + * \code + * // Must return the number of data poins + * size_t kdtree_get_point_count() const { ... } + * + * + * // Must return the dim'th component of the idx'th point in the class: + * T kdtree_get_pt(const size_t idx, const size_t dim) const { ... } + * + * // Optional bounding-box computation: return false to default to a standard + * bbox computation loop. + * // Return true if the BBOX was already computed by the class and returned + * in "bb" so it can be avoided to redo it again. + * // Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 + * for point clouds) template bool kdtree_get_bbox(BBOX &bb) const + * { + * bb[0].low = ...; bb[0].high = ...; // 0th dimension limits + * bb[1].low = ...; bb[1].high = ...; // 1st dimension limits + * ... + * return true; + * } + * + * \endcode + * + * \tparam DatasetAdaptor The user-provided adaptor, which must be ensured to + * have a lifetime equal or longer than the instance of this class. + * \tparam Distance The distance metric to use: nanoflann::metric_L1, + * nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. \tparam DIM + * Dimensionality of data points (e.g. 3 for 3D points) \tparam IndexType Will + * be typically size_t or int + */ +template +class KDTreeSingleIndexAdaptor + : public KDTreeBaseClass< + KDTreeSingleIndexAdaptor, Distance, + DatasetAdaptor, DIM, index_t> +{ + public: + /** Deleted copy constructor*/ + explicit KDTreeSingleIndexAdaptor( + const KDTreeSingleIndexAdaptor&) = delete; + + /** The data source used by this index */ + const DatasetAdaptor& dataset_; + + const KDTreeSingleIndexAdaptorParams indexParams; + + Distance distance_; + + using Base = typename nanoflann::KDTreeBaseClass< + nanoflann::KDTreeSingleIndexAdaptor, Distance, + DatasetAdaptor, DIM, index_t>; + + using Offset = typename Base::Offset; + using Size = typename Base::Size; + using Dimension = typename Base::Dimension; + + using ElementType = typename Base::ElementType; + using DistanceType = typename Base::DistanceType; + using IndexType = typename Base::IndexType; + + using Node = typename Base::Node; + using NodePtr = Node*; + + using Interval = typename Base::Interval; + + /** Define "BoundingBox" as a fixed-size or variable-size container + * depending on "DIM" */ + using BoundingBox = typename Base::BoundingBox; + + /** Define "distance_vector_t" as a fixed-size or variable-size container + * depending on "DIM" */ + using distance_vector_t = typename Base::distance_vector_t; + + /** + * KDTree constructor + * + * Refer to docs in README.md or online in + * https://github.com/jlblancoc/nanoflann + * + * The KD-Tree point dimension (the length of each point in the datase, e.g. + * 3 for 3D points) is determined by means of: + * - The \a DIM template parameter if >0 (highest priority) + * - Otherwise, the \a dimensionality parameter of this constructor. + * + * @param inputData Dataset with the input features. Its lifetime must be + * equal or longer than that of the instance of this class. + * @param params Basically, the maximum leaf node size + * + * Note that there is a variable number of optional additional parameters + * which will be forwarded to the metric class constructor. Refer to example + * `examples/pointcloud_custom_metric.cpp` for a use case. + * + */ + template + explicit KDTreeSingleIndexAdaptor( + const Dimension dimensionality, const DatasetAdaptor& inputData, + const KDTreeSingleIndexAdaptorParams& params, Args&&... args) + : dataset_(inputData), + indexParams(params), + distance_(inputData, std::forward(args)...) + { + init(dimensionality, params); + } + + explicit KDTreeSingleIndexAdaptor( + const Dimension dimensionality, const DatasetAdaptor& inputData, + const KDTreeSingleIndexAdaptorParams& params = {}) + : dataset_(inputData), indexParams(params), distance_(inputData) + { + init(dimensionality, params); + } + + private: + void init(const Dimension dimensionality, const KDTreeSingleIndexAdaptorParams& params) + { + Base::size_ = dataset_.kdtree_get_point_count(); + Base::size_at_index_build_ = Base::size_; + Base::dim_ = dimensionality; + if (DIM > 0) Base::dim_ = DIM; + Base::leaf_max_size_ = params.leaf_max_size; + if (params.n_thread_build > 0) + { + Base::n_thread_build_ = params.n_thread_build; + } + else + { + Base::n_thread_build_ = std::max(std::thread::hardware_concurrency(), 1u); + } + + if (!(params.flags & KDTreeSingleIndexAdaptorFlags::SkipInitialBuildIndex)) + { + // Build KD-tree: + buildIndex(); + } + } + + public: + /** + * Builds the index + */ + void buildIndex() + { + Base::size_ = dataset_.kdtree_get_point_count(); + Base::size_at_index_build_ = Base::size_; + init_vind(); + this->freeIndex(*this); + Base::size_at_index_build_ = Base::size_; + if (Base::size_ == 0) return; + computeBoundingBox(Base::root_bbox_); + // construct the tree + if (Base::n_thread_build_ == 1) + { + Base::root_node_ = this->divideTree(*this, 0, Base::size_, Base::root_bbox_); + } + else + { +#ifndef NANOFLANN_NO_THREADS + std::atomic thread_count(0u); + std::mutex mutex; + Base::root_node_ = this->divideTreeConcurrent( + *this, 0, Base::size_, Base::root_bbox_, thread_count, mutex); +#else /* NANOFLANN_NO_THREADS */ + throw std::runtime_error("Multithreading is disabled"); +#endif /* NANOFLANN_NO_THREADS */ + } + } + + /** \name Query methods + * @{ */ + + /** + * Find set of nearest neighbors to vec[0:dim-1]. Their indices are stored + * inside the result object. + * + * Params: + * result = the result object in which the indices of the + * nearest-neighbors are stored vec = the vector for which to search the + * nearest neighbors + * + * \tparam RESULTSET Should be any ResultSet + * \return True if the requested neighbors could be found. + * \sa knnSearch, radiusSearch + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + */ + template + bool findNeighbors( + RESULTSET& result, const ElementType* vec, const SearchParameters& searchParams = {}) const + { + assert(vec); + if (this->size(*this) == 0) return false; + if (!Base::root_node_) + throw std::runtime_error( + "[nanoflann] findNeighbors() called before building the " + "index."); + float epsError = 1 + searchParams.eps; + + // fixed or variable-sized container (depending on DIM) + distance_vector_t dists; + // Fill it with zeros. + auto zero = static_cast(0); + assign(dists, (DIM > 0 ? DIM : Base::dim_), zero); + DistanceType dist = this->computeInitialDistances(*this, vec, dists); + searchLevel(result, vec, Base::root_node_, dist, dists, epsError); + + if (searchParams.sorted) result.sort(); + + return result.full(); + } + + /** + * Find all points contained within the specified bounding box. Their + * indices are stored inside the result object. + * + * Params: + * result = the result object in which the indices of the points + * within the bounding box are stored + * bbox = the bounding box defining the search region + * + * \tparam RESULTSET Should be any ResultSet + * \return Number of points found within the bounding box. + * \sa findNeighbors, knnSearch, radiusSearch + * + * \note The search is inclusive - points on the boundary are included. + */ + template + Size findWithinBox(RESULTSET& result, const BoundingBox& bbox) const + { + if (this->size(*this) == 0) return 0; + if (!Base::root_node_) + throw std::runtime_error( + "[nanoflann] findWithinBox() called before building the " + "index."); + + std::stack stack; + stack.push(Base::root_node_); + + while (!stack.empty()) + { + const NodePtr node = stack.top(); + stack.pop(); + + // If this is a leaf node, then do check and return. + if (!node->child1) // (if one node is nullptr, both are) + { + for (Offset i = node->node_type.lr.left; i < node->node_type.lr.right; ++i) + { + if (contains(bbox, Base::vAcc_[i])) + { + if (!result.addPoint(0, Base::vAcc_[i])) + { + // the resultset doesn't want to receive any more + // points, we're done searching! + return result.size(); + } + } + } + } + else + { + const int idx = node->node_type.sub.divfeat; + const auto low_bound = node->node_type.sub.divlow; + const auto high_bound = node->node_type.sub.divhigh; + + if (bbox[idx].low <= low_bound) stack.push(node->child1); + if (bbox[idx].high >= high_bound) stack.push(node->child2); + } + } + + return result.size(); + } + + /** + * Find the "num_closest" nearest neighbors to the \a query_point[0:dim-1]. + * Their indices and distances are stored in the provided pointers to + * array/vector. + * + * \sa radiusSearch, findNeighbors + * \return Number `N` of valid points in the result set. + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + * + * \note Only the first `N` entries in `out_indices` and `out_distances` + * will be valid. Return is less than `num_closest` only if the + * number of elements in the tree is less than `num_closest`. + */ + Size knnSearch( + const ElementType* query_point, const Size num_closest, IndexType* out_indices, + DistanceType* out_distances) const + { + nanoflann::KNNResultSet resultSet(num_closest); + resultSet.init(out_indices, out_distances); + findNeighbors(resultSet, query_point); + return resultSet.size(); + } + + /** + * Find all the neighbors to \a query_point[0:dim-1] within a maximum + * radius. The output is given as a vector of pairs, of which the first + * element is a point index and the second the corresponding distance. + * Previous contents of \a IndicesDists are cleared. + * + * If searchParams.sorted==true, the output list is sorted by ascending + * distances. + * + * For a better performance, it is advisable to do a .reserve() on the + * vector if you have any wild guess about the number of expected matches. + * + * \sa knnSearch, findNeighbors, radiusSearchCustomCallback + * \return The number of points within the given radius (i.e. indices.size() + * or dists.size() ) + * + * \note If L2 norms are used, search radius and all returned distances + * are actually squared distances. + */ + Size radiusSearch( + const ElementType* query_point, const DistanceType& radius, + std::vector>& IndicesDists, + const SearchParameters& searchParams = {}) const + { + RadiusResultSet resultSet(radius, IndicesDists); + const Size nFound = radiusSearchCustomCallback(query_point, resultSet, searchParams); + return nFound; + } + + /** + * Just like radiusSearch() but with a custom callback class for each point + * found in the radius of the query. See the source of RadiusResultSet<> as + * a start point for your own classes. \sa radiusSearch + */ + template + Size radiusSearchCustomCallback( + const ElementType* query_point, SEARCH_CALLBACK& resultSet, + const SearchParameters& searchParams = {}) const + { + findNeighbors(resultSet, query_point, searchParams); + return resultSet.size(); + } + + /** + * Find the first N neighbors to \a query_point[0:dim-1] within a maximum + * radius. The output is given as a vector of pairs, of which the first + * element is a point index and the second the corresponding distance. + * Previous contents of \a IndicesDists are cleared. + * + * \sa radiusSearch, findNeighbors + * \return Number `N` of valid points in the result set. + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + * + * \note Only the first `N` entries in `out_indices` and `out_distances` + * will be valid. Return is less than `num_closest` only if the + * number of elements in the tree is less than `num_closest`. + */ + Size rknnSearch( + const ElementType* query_point, const Size num_closest, IndexType* out_indices, + DistanceType* out_distances, const DistanceType& radius) const + { + nanoflann::RKNNResultSet resultSet(num_closest, radius); + resultSet.init(out_indices, out_distances); + findNeighbors(resultSet, query_point); + return resultSet.size(); + } + + /** @} */ + + public: + /** Make sure the auxiliary list \a vind has the same size as the + * current dataset, and re-generate if size has changed. */ + void init_vind() + { + // Create a permutable array of indices to the input vectors. + Base::size_ = dataset_.kdtree_get_point_count(); + if (Base::vAcc_.size() != Base::size_) Base::vAcc_.resize(Base::size_); + for (IndexType i = 0; i < static_cast(Base::size_); i++) Base::vAcc_[i] = i; + } + + void computeBoundingBox(BoundingBox& bbox) + { + const auto dims = (DIM > 0 ? DIM : Base::dim_); + resize(bbox, dims); + if (dataset_.kdtree_get_bbox(bbox)) + { + // Done! It was implemented in derived class + } + else + { + const Size N = dataset_.kdtree_get_point_count(); + if (!N) + throw std::runtime_error( + "[nanoflann] computeBoundingBox() called but " + "no data points found."); + for (Dimension i = 0; i < dims; ++i) + { + bbox[i].low = bbox[i].high = this->dataset_get(*this, Base::vAcc_[0], i); + } + for (Offset k = 1; k < N; ++k) + { + for (Dimension i = 0; i < dims; ++i) + { + const auto val = this->dataset_get(*this, Base::vAcc_[k], i); + if (val < bbox[i].low) bbox[i].low = val; + if (val > bbox[i].high) bbox[i].high = val; + } + } + } + } + + bool contains(const BoundingBox& bbox, IndexType idx) const + { + const auto dims = (DIM > 0 ? DIM : Base::dim_); + for (Dimension i = 0; i < dims; ++i) + { + const auto point = this->dataset_.kdtree_get_pt(idx, i); + if (point < bbox[i].low || point > bbox[i].high) return false; + } + return true; + } + + /** + * Performs an exact search in the tree starting from a node. + * \tparam RESULTSET Should be any ResultSet + * \return true if the search should be continued, false if the results are + * sufficient + */ + template + bool searchLevel( + RESULTSET& result_set, const ElementType* vec, const NodePtr node, DistanceType mindist, + distance_vector_t& dists, const float epsError) const + { + // If this is a leaf node, then do check and return. + if (!node->child1) // (if one node is nullptr, both are) + { + for (Offset i = node->node_type.lr.left; i < node->node_type.lr.right; ++i) + { + const IndexType accessor = Base::vAcc_[i]; // reorder... : i; + DistanceType dist = + distance_.evalMetric(vec, accessor, (DIM > 0 ? DIM : Base::dim_)); + if (dist < result_set.worstDist()) + { + if (!result_set.addPoint(dist, Base::vAcc_[i])) + { + // the resultset doesn't want to receive any more + // points, we're done searching! + return false; + } + } + } + return true; + } + + /* Which child branch should be taken first? */ + Dimension idx = node->node_type.sub.divfeat; + ElementType val = vec[idx]; + DistanceType diff1 = val - node->node_type.sub.divlow; + DistanceType diff2 = val - node->node_type.sub.divhigh; + + NodePtr bestChild; + NodePtr otherChild; + DistanceType cut_dist; + if ((diff1 + diff2) < 0) + { + bestChild = node->child1; + otherChild = node->child2; + cut_dist = distance_.accum_dist(val, node->node_type.sub.divhigh, idx); + } + else + { + bestChild = node->child2; + otherChild = node->child1; + cut_dist = distance_.accum_dist(val, node->node_type.sub.divlow, idx); + } + + /* Call recursively to search next level down. */ + if (!searchLevel(result_set, vec, bestChild, mindist, dists, epsError)) + { + // the resultset doesn't want to receive any more points, we're done + // searching! + return false; + } + + DistanceType dst = dists[idx]; + mindist = mindist + cut_dist - dst; + dists[idx] = cut_dist; + if (mindist * epsError <= result_set.worstDist()) + { + if (!searchLevel(result_set, vec, otherChild, mindist, dists, epsError)) + { + // the resultset doesn't want to receive any more points, we're + // done searching! + return false; + } + } + dists[idx] = dst; + return true; + } + + public: + /** Stores the index in a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so + * when loading the index object it must be constructed associated to the + * same source of data points used while building it. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void saveIndex(std::ostream& stream) const { Base::saveIndex(*this, stream); } + + /** Loads a previous index from a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so + * the index object must be constructed associated to the same source of + * data points used while building the index. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void loadIndex(std::istream& stream) { Base::loadIndex(*this, stream); } + +}; // class KDTree + +/** kd-tree dynamic index + * + * Contains the k-d trees and other information for indexing a set of points + * for nearest-neighbor matching. + * + * The class "DatasetAdaptor" must provide the following interface (can be + * non-virtual, inlined methods): + * + * \code + * // Must return the number of data poins + * size_t kdtree_get_point_count() const { ... } + * + * // Must return the dim'th component of the idx'th point in the class: + * T kdtree_get_pt(const size_t idx, const size_t dim) const { ... } + * + * // Optional bounding-box computation: return false to default to a standard + * bbox computation loop. + * // Return true if the BBOX was already computed by the class and returned + * in "bb" so it can be avoided to redo it again. + * // Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 + * for point clouds) template bool kdtree_get_bbox(BBOX &bb) const + * { + * bb[0].low = ...; bb[0].high = ...; // 0th dimension limits + * bb[1].low = ...; bb[1].high = ...; // 1st dimension limits + * ... + * return true; + * } + * + * \endcode + * + * \tparam DatasetAdaptor The user-provided adaptor (see comments above). + * \tparam Distance The distance metric to use: nanoflann::metric_L1, + * nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. + * \tparam DIM Dimensionality of data points (e.g. 3 for 3D points) + * \tparam IndexType Type of the arguments with which the data can be + * accessed (e.g. float, double, int64_t, T*) + */ +template +class KDTreeSingleIndexDynamicAdaptor_ + : public KDTreeBaseClass< + KDTreeSingleIndexDynamicAdaptor_, Distance, + DatasetAdaptor, DIM, IndexType> +{ + public: + /** + * The dataset used by this index + */ + const DatasetAdaptor& dataset_; //!< The source of our data + + KDTreeSingleIndexAdaptorParams index_params_; + + std::vector& treeIndex_; + + Distance distance_; + + using Base = typename nanoflann::KDTreeBaseClass< + nanoflann::KDTreeSingleIndexDynamicAdaptor_, + Distance, DatasetAdaptor, DIM, IndexType>; + + using ElementType = typename Base::ElementType; + using DistanceType = typename Base::DistanceType; + + using Offset = typename Base::Offset; + using Size = typename Base::Size; + using Dimension = typename Base::Dimension; + + using Node = typename Base::Node; + using NodePtr = Node*; + + using Interval = typename Base::Interval; + /** Define "BoundingBox" as a fixed-size or variable-size container + * depending on "DIM" */ + using BoundingBox = typename Base::BoundingBox; + + /** Define "distance_vector_t" as a fixed-size or variable-size container + * depending on "DIM" */ + using distance_vector_t = typename Base::distance_vector_t; + + /** + * KDTree constructor + * + * Refer to docs in README.md or online in + * https://github.com/jlblancoc/nanoflann + * + * The KD-Tree point dimension (the length of each point in the datase, e.g. + * 3 for 3D points) is determined by means of: + * - The \a DIM template parameter if >0 (highest priority) + * - Otherwise, the \a dimensionality parameter of this constructor. + * + * @param inputData Dataset with the input features. Its lifetime must be + * equal or longer than that of the instance of this class. + * @param params Basically, the maximum leaf node size + */ + KDTreeSingleIndexDynamicAdaptor_( + const Dimension dimensionality, const DatasetAdaptor& inputData, + std::vector& treeIndex, + const KDTreeSingleIndexAdaptorParams& params = KDTreeSingleIndexAdaptorParams()) + : dataset_(inputData), index_params_(params), treeIndex_(treeIndex), distance_(inputData) + { + Base::size_ = 0; + Base::size_at_index_build_ = 0; + for (auto& v : Base::root_bbox_) v = {}; + Base::dim_ = dimensionality; + if (DIM > 0) Base::dim_ = DIM; + Base::leaf_max_size_ = params.leaf_max_size; + if (params.n_thread_build > 0) + { + Base::n_thread_build_ = params.n_thread_build; + } + else + { + Base::n_thread_build_ = std::max(std::thread::hardware_concurrency(), 1u); + } + } + + /** Explicitly default the copy constructor */ + KDTreeSingleIndexDynamicAdaptor_(const KDTreeSingleIndexDynamicAdaptor_& rhs) = default; + + /** Assignment operator definiton */ + KDTreeSingleIndexDynamicAdaptor_ operator=(const KDTreeSingleIndexDynamicAdaptor_& rhs) + { + KDTreeSingleIndexDynamicAdaptor_ tmp(rhs); + std::swap(Base::vAcc_, tmp.Base::vAcc_); + std::swap(Base::leaf_max_size_, tmp.Base::leaf_max_size_); + std::swap(index_params_, tmp.index_params_); + std::swap(treeIndex_, tmp.treeIndex_); + std::swap(Base::size_, tmp.Base::size_); + std::swap(Base::size_at_index_build_, tmp.Base::size_at_index_build_); + std::swap(Base::root_node_, tmp.Base::root_node_); + std::swap(Base::root_bbox_, tmp.Base::root_bbox_); + std::swap(Base::pool_, tmp.Base::pool_); + return *this; + } + + /** + * Builds the index + */ + void buildIndex() + { + Base::size_ = Base::vAcc_.size(); + this->freeIndex(*this); + Base::size_at_index_build_ = Base::size_; + if (Base::size_ == 0) return; + computeBoundingBox(Base::root_bbox_); + // construct the tree + if (Base::n_thread_build_ == 1) + { + Base::root_node_ = this->divideTree(*this, 0, Base::size_, Base::root_bbox_); + } + else + { +#ifndef NANOFLANN_NO_THREADS + std::atomic thread_count(0u); + std::mutex mutex; + Base::root_node_ = this->divideTreeConcurrent( + *this, 0, Base::size_, Base::root_bbox_, thread_count, mutex); +#else /* NANOFLANN_NO_THREADS */ + throw std::runtime_error("Multithreading is disabled"); +#endif /* NANOFLANN_NO_THREADS */ + } + } + + /** \name Query methods + * @{ */ + + /** + * Find set of nearest neighbors to vec[0:dim-1]. Their indices are stored + * inside the result object. + * This is the core search function, all others are wrappers around this + * one. + * + * \param result The result object in which the indices of the + * nearest-neighbors are stored. + * \param vec The vector of the query point for which to search the + * nearest neighbors. + * \param searchParams Optional parameters for the search. + * + * \tparam RESULTSET Should be any ResultSet + * \return True if the requested neighbors could be found. + * + * \sa knnSearch(), radiusSearch(), radiusSearchCustomCallback() + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + */ + template + bool findNeighbors( + RESULTSET& result, const ElementType* vec, const SearchParameters& searchParams = {}) const + { + assert(vec); + if (this->size(*this) == 0) return false; + if (!Base::root_node_) return false; + float epsError = 1 + searchParams.eps; + + // fixed or variable-sized container (depending on DIM) + distance_vector_t dists; + // Fill it with zeros. + assign( + dists, (DIM > 0 ? DIM : Base::dim_), + static_cast(0)); + DistanceType dist = this->computeInitialDistances(*this, vec, dists); + searchLevel(result, vec, Base::root_node_, dist, dists, epsError); + + if (searchParams.sorted) result.sort(); + + return result.full(); + } + + /** + * Find the "num_closest" nearest neighbors to the \a query_point[0:dim-1]. + * Their indices are stored inside the result object. \sa radiusSearch, + * findNeighbors + * \return Number `N` of valid points in + * the result set. + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + * + * \note Only the first `N` entries in `out_indices` and `out_distances` + * will be valid. Return may be less than `num_closest` only if the + * number of elements in the tree is less than `num_closest`. + */ + Size knnSearch( + const ElementType* query_point, const Size num_closest, IndexType* out_indices, + DistanceType* out_distances, const SearchParameters& searchParams = {}) const + { + nanoflann::KNNResultSet resultSet(num_closest); + resultSet.init(out_indices, out_distances); + findNeighbors(resultSet, query_point, searchParams); + return resultSet.size(); + } + + /** + * Find all the neighbors to \a query_point[0:dim-1] within a maximum + * radius. The output is given as a vector of pairs, of which the first + * element is a point index and the second the corresponding distance. + * Previous contents of \a IndicesDists are cleared. + * + * If searchParams.sorted==true, the output list is sorted by ascending + * distances. + * + * For a better performance, it is advisable to do a .reserve() on the + * vector if you have any wild guess about the number of expected matches. + * + * \sa knnSearch, findNeighbors, radiusSearchCustomCallback + * \return The number of points within the given radius (i.e. indices.size() + * or dists.size() ) + * + * \note If L2 norms are used, search radius and all returned distances + * are actually squared distances. + */ + Size radiusSearch( + const ElementType* query_point, const DistanceType& radius, + std::vector>& IndicesDists, + const SearchParameters& searchParams = {}) const + { + RadiusResultSet resultSet(radius, IndicesDists); + const size_t nFound = radiusSearchCustomCallback(query_point, resultSet, searchParams); + return nFound; + } + + /** + * Just like radiusSearch() but with a custom callback class for each point + * found in the radius of the query. See the source of RadiusResultSet<> as + * a start point for your own classes. \sa radiusSearch + */ + template + Size radiusSearchCustomCallback( + const ElementType* query_point, SEARCH_CALLBACK& resultSet, + const SearchParameters& searchParams = {}) const + { + findNeighbors(resultSet, query_point, searchParams); + return resultSet.size(); + } + + /** @} */ + + public: + void computeBoundingBox(BoundingBox& bbox) + { + const auto dims = (DIM > 0 ? DIM : Base::dim_); + resize(bbox, dims); + + if (dataset_.kdtree_get_bbox(bbox)) + { + // Done! It was implemented in derived class + } + else + { + const Size N = Base::size_; + if (!N) + throw std::runtime_error( + "[nanoflann] computeBoundingBox() called but " + "no data points found."); + for (Dimension i = 0; i < dims; ++i) + { + bbox[i].low = bbox[i].high = this->dataset_get(*this, Base::vAcc_[0], i); + } + for (Offset k = 1; k < N; ++k) + { + for (Dimension i = 0; i < dims; ++i) + { + const auto val = this->dataset_get(*this, Base::vAcc_[k], i); + if (val < bbox[i].low) bbox[i].low = val; + if (val > bbox[i].high) bbox[i].high = val; + } + } + } + } + + /** + * Performs an exact search in the tree starting from a node. + * \tparam RESULTSET Should be any ResultSet + */ + template + void searchLevel( + RESULTSET& result_set, const ElementType* vec, const NodePtr node, DistanceType mindist, + distance_vector_t& dists, const float epsError) const + { + // If this is a leaf node, then do check and return. + if (!node->child1) // (if one node is nullptr, both are) + { + for (Offset i = node->node_type.lr.left; i < node->node_type.lr.right; ++i) + { + const IndexType index = Base::vAcc_[i]; // reorder... : i; + if (treeIndex_[index] == -1) continue; + DistanceType dist = distance_.evalMetric(vec, index, (DIM > 0 ? DIM : Base::dim_)); + if (dist < result_set.worstDist()) + { + if (!result_set.addPoint( + static_cast(dist), + static_cast(Base::vAcc_[i]))) + { + // the resultset doesn't want to receive any more + // points, we're done searching! + return; // false; + } + } + } + return; + } + + /* Which child branch should be taken first? */ + Dimension idx = node->node_type.sub.divfeat; + ElementType val = vec[idx]; + DistanceType diff1 = val - node->node_type.sub.divlow; + DistanceType diff2 = val - node->node_type.sub.divhigh; + + NodePtr bestChild; + NodePtr otherChild; + DistanceType cut_dist; + if ((diff1 + diff2) < 0) + { + bestChild = node->child1; + otherChild = node->child2; + cut_dist = distance_.accum_dist(val, node->node_type.sub.divhigh, idx); + } + else + { + bestChild = node->child2; + otherChild = node->child1; + cut_dist = distance_.accum_dist(val, node->node_type.sub.divlow, idx); + } + + /* Call recursively to search next level down. */ + searchLevel(result_set, vec, bestChild, mindist, dists, epsError); + + DistanceType dst = dists[idx]; + mindist = mindist + cut_dist - dst; + dists[idx] = cut_dist; + if (mindist * epsError <= result_set.worstDist()) + { + searchLevel(result_set, vec, otherChild, mindist, dists, epsError); + } + dists[idx] = dst; + } + + public: + /** Stores the index in a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so + * when loading the index object it must be constructed associated to the + * same source of data points used while building it. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void saveIndex(std::ostream& stream) { saveIndex(*this, stream); } + + /** Loads a previous index from a binary file. + * IMPORTANT NOTE: The set of data points is NOT stored in the file, so + * the index object must be constructed associated to the same source of + * data points used while building the index. See the example: + * examples/saveload_example.cpp \sa loadIndex */ + void loadIndex(std::istream& stream) { loadIndex(*this, stream); } +}; + +/** kd-tree dynaimic index + * + * class to create multiple static index and merge their results to behave as + * single dynamic index as proposed in Logarithmic Approach. + * + * Example of usage: + * examples/dynamic_pointcloud_example.cpp + * + * \tparam DatasetAdaptor The user-provided adaptor (see comments above). + * \tparam Distance The distance metric to use: nanoflann::metric_L1, + * nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. \tparam DIM + * Dimensionality of data points (e.g. 3 for 3D points) \tparam IndexType + * Will be typically size_t or int + */ +template +class KDTreeSingleIndexDynamicAdaptor +{ + public: + using ElementType = typename Distance::ElementType; + using DistanceType = typename Distance::DistanceType; + + using Offset = typename KDTreeSingleIndexDynamicAdaptor_::Offset; + using Size = typename KDTreeSingleIndexDynamicAdaptor_::Size; + using Dimension = + typename KDTreeSingleIndexDynamicAdaptor_::Dimension; + + protected: + Size leaf_max_size_; + Size treeCount_; + Size pointCount_; + + /** + * The dataset used by this index + */ + const DatasetAdaptor& dataset_; //!< The source of our data + + /** treeIndex[idx] is the index of tree in which point at idx is stored. + * treeIndex[idx]=-1 means that point has been removed. */ + std::vector treeIndex_; + std::unordered_set removedPoints_; + + KDTreeSingleIndexAdaptorParams index_params_; + + Dimension dim_; //!< Dimensionality of each data point + + using index_container_t = + KDTreeSingleIndexDynamicAdaptor_; + std::vector index_; + + public: + /** Get a const ref to the internal list of indices; the number of indices + * is adapted dynamically as the dataset grows in size. */ + const std::vector& getAllIndices() const { return index_; } + + private: + /** finds position of least significant unset bit */ + int First0Bit(IndexType num) + { + int pos = 0; + while (num & 1) + { + num = num >> 1; + pos++; + } + return pos; + } + + /** Creates multiple empty trees to handle dynamic support */ + void init() + { + using my_kd_tree_t = + KDTreeSingleIndexDynamicAdaptor_; + std::vector index( + treeCount_, my_kd_tree_t(dim_ /*dim*/, dataset_, treeIndex_, index_params_)); + index_ = index; + } + + public: + Distance distance_; + + /** + * KDTree constructor + * + * Refer to docs in README.md or online in + * https://github.com/jlblancoc/nanoflann + * + * The KD-Tree point dimension (the length of each point in the datase, e.g. + * 3 for 3D points) is determined by means of: + * - The \a DIM template parameter if >0 (highest priority) + * - Otherwise, the \a dimensionality parameter of this constructor. + * + * @param inputData Dataset with the input features. Its lifetime must be + * equal or longer than that of the instance of this class. + * @param params Basically, the maximum leaf node size + */ + explicit KDTreeSingleIndexDynamicAdaptor( + const int dimensionality, const DatasetAdaptor& inputData, + const KDTreeSingleIndexAdaptorParams& params = KDTreeSingleIndexAdaptorParams(), + const size_t maximumPointCount = 1000000000U) + : dataset_(inputData), index_params_(params), distance_(inputData) + { + treeCount_ = static_cast(std::log2(maximumPointCount)) + 1; + pointCount_ = 0U; + dim_ = dimensionality; + treeIndex_.clear(); + if (DIM > 0) dim_ = DIM; + leaf_max_size_ = params.leaf_max_size; + init(); + const size_t num_initial_points = dataset_.kdtree_get_point_count(); + if (num_initial_points > 0) + { + addPoints(0, num_initial_points - 1); + } + } + + /** Deleted copy constructor*/ + explicit KDTreeSingleIndexDynamicAdaptor( + const KDTreeSingleIndexDynamicAdaptor&) = delete; + + /** Add points to the set, Inserts all points from [start, end] */ + void addPoints(IndexType start, IndexType end) + { + const Size count = end - start + 1; + int maxIndex = 0; + treeIndex_.resize(treeIndex_.size() + count); + for (IndexType idx = start; idx <= end; idx++) + { + const int pos = First0Bit(pointCount_); + maxIndex = std::max(pos, maxIndex); + treeIndex_[pointCount_] = pos; + + const auto it = removedPoints_.find(idx); + if (it != removedPoints_.end()) + { + removedPoints_.erase(it); + treeIndex_[idx] = pos; + } + + for (int i = 0; i < pos; i++) + { + for (int j = 0; j < static_cast(index_[i].vAcc_.size()); j++) + { + index_[pos].vAcc_.push_back(index_[i].vAcc_[j]); + if (treeIndex_[index_[i].vAcc_[j]] != -1) treeIndex_[index_[i].vAcc_[j]] = pos; + } + index_[i].vAcc_.clear(); + } + index_[pos].vAcc_.push_back(idx); + pointCount_++; + } + + for (int i = 0; i <= maxIndex; ++i) + { + index_[i].freeIndex(index_[i]); + if (!index_[i].vAcc_.empty()) index_[i].buildIndex(); + } + } + + /** Remove a point from the set (Lazy Deletion) */ + void removePoint(size_t idx) + { + if (idx >= pointCount_) return; + removedPoints_.insert(idx); + treeIndex_[idx] = -1; + } + + /** + * Find set of nearest neighbors to vec[0:dim-1]. Their indices are stored + * inside the result object. + * + * Params: + * result = the result object in which the indices of the + * nearest-neighbors are stored vec = the vector for which to search the + * nearest neighbors + * + * \tparam RESULTSET Should be any ResultSet + * \return True if the requested neighbors could be found. + * \sa knnSearch, radiusSearch + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + */ + template + bool findNeighbors( + RESULTSET& result, const ElementType* vec, const SearchParameters& searchParams = {}) const + { + for (size_t i = 0; i < treeCount_; i++) + { + index_[i].findNeighbors(result, &vec[0], searchParams); + } + return result.full(); + } +}; + +/** An L2-metric KD-tree adaptor for working with data directly stored in an + * Eigen Matrix, without duplicating the data storage. You can select whether a + * row or column in the matrix represents a point in the state space. + * + * Example of usage: + * \code + * Eigen::Matrix mat; + * + * // Fill out "mat"... + * using my_kd_tree_t = nanoflann::KDTreeEigenMatrixAdaptor< + * Eigen::Matrix>; + * + * const int max_leaf = 10; + * my_kd_tree_t mat_index(mat, max_leaf); + * mat_index.index->... + * \endcode + * + * \tparam DIM If set to >0, it specifies a compile-time fixed dimensionality + * for the points in the data set, allowing more compiler optimizations. + * \tparam Distance The distance metric to use: nanoflann::metric_L1, + * nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc. + * \tparam row_major If set to true the rows of the matrix are used as the + * points, if set to false the columns of the matrix are used as the + * points. + */ +template < + class MatrixType, int32_t DIM = -1, class Distance = nanoflann::metric_L2, + bool row_major = true> +struct KDTreeEigenMatrixAdaptor +{ + using self_t = KDTreeEigenMatrixAdaptor; + using num_t = typename MatrixType::Scalar; + using IndexType = typename MatrixType::Index; + using metric_t = typename Distance::template traits::distance_t; + + using index_t = KDTreeSingleIndexAdaptor< + metric_t, self_t, row_major ? MatrixType::ColsAtCompileTime : MatrixType::RowsAtCompileTime, + IndexType>; + + index_t* index_; //! The kd-tree index for the user to call its methods as + //! usual with any other FLANN index. + + using Offset = typename index_t::Offset; + using Size = typename index_t::Size; + using Dimension = typename index_t::Dimension; + + /// Constructor: takes a const ref to the matrix object with the data points + explicit KDTreeEigenMatrixAdaptor( + const Dimension dimensionality, const std::reference_wrapper& mat, + const int leaf_max_size = 10, const unsigned int n_thread_build = 1) + : m_data_matrix(mat) + { + const auto dims = row_major ? mat.get().cols() : mat.get().rows(); + if (static_cast(dims) != dimensionality) + throw std::runtime_error( + "Error: 'dimensionality' must match column count in data " + "matrix"); + if (DIM > 0 && static_cast(dims) != DIM) + throw std::runtime_error( + "Data set dimensionality does not match the 'DIM' template " + "argument"); + index_ = new index_t( + dims, *this /* adaptor */, + nanoflann::KDTreeSingleIndexAdaptorParams( + leaf_max_size, nanoflann::KDTreeSingleIndexAdaptorFlags::None, n_thread_build)); + } + + public: + /** Deleted copy constructor */ + KDTreeEigenMatrixAdaptor(const self_t&) = delete; + + ~KDTreeEigenMatrixAdaptor() { delete index_; } + + const std::reference_wrapper m_data_matrix; + + /** Query for the \a num_closest closest points to a given point (entered as + * query_point[0:dim-1]). Note that this is a short-cut method for + * index->findNeighbors(). The user can also call index->... methods as + * desired. + * + * \note If L2 norms are used, all returned distances are actually squared + * distances. + */ + void query( + const num_t* query_point, const Size num_closest, IndexType* out_indices, + num_t* out_distances) const + { + nanoflann::KNNResultSet resultSet(num_closest); + resultSet.init(out_indices, out_distances); + index_->findNeighbors(resultSet, query_point); + } + + /** @name Interface expected by KDTreeSingleIndexAdaptor + * @{ */ + + inline const self_t& derived() const noexcept { return *this; } + inline self_t& derived() noexcept { return *this; } + + // Must return the number of data points + inline Size kdtree_get_point_count() const + { + if (row_major) + return m_data_matrix.get().rows(); + else + return m_data_matrix.get().cols(); + } + + // Returns the dim'th component of the idx'th point in the class: + inline num_t kdtree_get_pt(const IndexType idx, size_t dim) const + { + if (row_major) + return m_data_matrix.get().coeff(idx, IndexType(dim)); + else + return m_data_matrix.get().coeff(IndexType(dim), idx); + } + + // Optional bounding-box computation: return false to default to a standard + // bbox computation loop. + // Return true if the BBOX was already computed by the class and returned + // in "bb" so it can be avoided to redo it again. Look at bb.size() to + // find out the expected dimensionality (e.g. 2 or 3 for point clouds) + template + inline bool kdtree_get_bbox(BBOX& /*bb*/) const + { + return false; + } + + /** @} */ + +}; // end of KDTreeEigenMatrixAdaptor +/** @} */ + +/** @} */ // end of grouping +} // namespace nanoflann + +#undef NANOFLANN_RESTRICT diff --git a/Libraries/nanoflann/README.md b/Libraries/nanoflann/README.md new file mode 100644 index 000000000..5830ad992 --- /dev/null +++ b/Libraries/nanoflann/README.md @@ -0,0 +1,10 @@ +Vendored dependency: `nanoflann` + +- File: `Include/nanoflann.hpp` +- Upstream project: https://github.com/jlblancoc/nanoflann +- Imported from the winding-number integration source snapshot +- License: BSD-style (license header retained in vendored file) + +Notes: +- This is vendored for boolean/winding integration in lib3mf. +- Future work may switch this dependency to package-manager resolution (e.g. vcpkg). diff --git a/Libraries/tinybvh/Include/tiny_bvh.h b/Libraries/tinybvh/Include/tiny_bvh.h new file mode 100644 index 000000000..c56346d7a --- /dev/null +++ b/Libraries/tinybvh/Include/tiny_bvh.h @@ -0,0 +1,8852 @@ +/* +The MIT License (MIT) + +Copyright (c) 2024-2025, Jacco Bikker / Breda University of Applied Sciences. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// How to use: +// +// Use this in *one* .c or .cpp +// #define TINYBVH_IMPLEMENTATION +// #include "tiny_bvh.h" +// Instantiate a BVH and build it for a list of triangles: +// BVH bvh; +// bvh.Build( (bvhvec4*)myVerts, numTriangles ); +// Ray ray( bvhvec3( 0, 0, 0 ), bvhvec3( 0, 0, 1 ), 1e30f ); +// bvh.Intersect( ray ); +// After this, intersection information is in ray.hit. + +// tinybvh can use custom vector types by defining TINYBVH_USE_CUSTOM_VECTOR_TYPES once before inclusion. +// To define custom vector types create a tinybvh namespace with the appropriate using directives, e.g.: +// namespace tinybvh +// { +// using bvhint2 = math::int2; +// using bvhint3 = math::int3; +// using bvhuint2 = math::uint2; +// using bvhuint3 = math::uint3; +// using bvhuint4 = math::uint4; +// using bvhvec2 = math::float2; +// using bvhvec3 = math::float3; +// using bvhvec4 = math::float4; +// using bvhdbl3 = math::double3; +// using bvhmat4 = math::mat4x4; +// } +// +// #define TINYBVH_USE_CUSTOM_VECTOR_TYPES +// #include + +// tinybvh can be further configured using #defines, to be specified before the #include: +// #define BVHBINS 8 - the number of bins to use in regular BVH construction. Default is 8. +// #define HQBVHBINS 32 - the number of bins to use in SBVH construction. Default is 8. +// #define INST_IDX_BITS 10 - the number of bits to use for the instance index. Default is 32, +// which stores the bits in a separate field in tinybvh::Intersection. +// #define C_INT 1 - the estimated cost of a primitive intersection test. Default is 1. +// #define C_TRAV 1 - the estimated cost of a traversal step. Default is 1. + +// See tiny_bvh_test.cpp for basic usage. In short: +// instantiate a BVH: tinybvh::BVH bvh; +// build it: bvh.Build( (tinybvh::bvhvec4*)triangleData, TRIANGLE_COUNT ); +// ..where triangleData is an array of four-component float vectors: +// - For a single triangle, provide 3 vertices, +// - For each vertex provide x, y and z. +// The fourth float in each vertex is a dummy value and exists purely for +// a more efficient layout of the data in memory. + +// More information about the BVH data structure: +// https://jacco.ompf2.com/2022/04/13/how-to-build-a-bvh-part-1-basics + +// Further references: See README.md + +// Author and contributors: +// Jacco Bikker: BVH code and examples +// Eddy L O Jansson: g++ / clang support +// Aras Pranckevičius: non-Intel architecture support +// Jefferson Amstutz: CMake support +// Christian Oliveros: WASM / EMSCRIPTEN support +// Thierry Cantenot: user-defined alloc & free +// David Peicho: slices & Rust bindings, API advice +// Aytek Aman: C++11 threading implementation + +#ifndef TINY_BVH_H_ +#define TINY_BVH_H_ + +// Library version: +#define TINY_BVH_VERSION_MAJOR 1 +#define TINY_BVH_VERSION_MINOR 6 +#define TINY_BVH_VERSION_SUB 7 + +// Cached BVH file version - increases only when file layout changes. +#define TINY_BVH_CACHE_VERSION 166 + +// Run-time checks / debuggin. +// #define PARANOID // checks out-of-bound access of slices +// #define SLICEDUMP // dumps the slice used for building to a file - debug feature. + +// Binned BVH building: bin count. +#ifndef BVHBINS +#define BVHBINS 8 +#endif +#ifndef HQBVHBINS +#define HQBVHBINS 8 +#define MAXHQBINS 256 +#endif +#define AVXBINS 8 // must stay at 8. + +// TLAS setting +// Note: Except when INST_IDX_BITS is set to 32, the instance index is encoded in +// the top bits of the prim idx field. +// Max number of instances in TLAS: 2 ^ INST_IDX_BITS +// Max number of primitives per BLAS: 2 ^ (32 - INST_IDX_BITS) +#ifndef INST_IDX_BITS +#define INST_IDX_BITS 32 // Use 4..~12 to use prim field bits for instance id, or set to 32 to store index in separate field. +#endif + +// SAH BVH building: Heuristic parameters +// CPU traversal: C_INT = 1, C_TRAV = 1 seems optimal. +// These are defaults, which initialize the public members c_int and c_trav in +// BVHBase (and thus each BVH instance). +#ifndef C_INT +#define C_INT 1 +#endif +#ifndef C_TRAV +#define C_TRAV 1 +#endif +#ifndef W_EPO +#define W_EPO 0.71f +#endif + +// SBVH: "Unsplitting" +#define SBVH_UNSPLITTING +#define RDH_MAX_WEIGHT 0.8f + +// Triangle intersection: "Watertight" +// #define WATERTIGHT_TRITEST + +// 'Infinity' values +#define BVH_FAR 1e30f // actual valid ieee range: 3.40282347E+38 +#define BVH_DBL_FAR 1e300 // actual valid ieee range: 1.797693134862315E+308 + +// Threaded BuildAVX +#ifndef MT_BUILD_THRESHOLD +#define MT_BUILD_THRESHOLD 50000 // single-threaded builds below this triangle count +#endif + +// Features +#ifndef NO_DOUBLE_PRECISION_SUPPORT +#define DOUBLE_PRECISION_SUPPORT +#endif +// #define TINYBVH_USE_CUSTOM_VECTOR_TYPES +// #define TINYBVH_NO_SIMD +#ifndef NO_INDEXED_GEOMETRY +#define ENABLE_INDEXED_GEOMETRY +#endif +#ifndef NO_CUSTOM_GEOMETRY +#define ENABLE_CUSTOM_GEOMETRY +#endif +#ifndef NO_THREADED_BUILDS +#define ENABLE_THREADED_BUILDS +#endif + +// Experimental / WIP features + +// CWBVH triangle format - doesn't seem to help on GPU? +// #define CWBVH_COMPRESSED_TRIS +// BVH4 triangle format +// #define BVH4_GPU_COMPRESSED_TRIS + +// BVH8_CPU align to big boundaries - experimental. +#define BVH8_ALIGN_4K +// #define BVH8_ALIGN_32K + +// ============================================================================ +// +// P R E L I M I N A R I E S +// +// ============================================================================ + +// needful includes +#ifdef _MSC_VER // Visual Studio / C11 +#include // for alloc/free +#include // for fprintf +#include // for sqrtf, fabs +#include // for memset +#include // for exit(1) +#else // Emscripten / gcc / clang +#include +#include +#include +#include +#endif +#include +#include // for SBVH builds +#include +#include // for threaded builds + +// Platform-independent compile-time warnings. +#define EMIT_COMPILER_WARNING_STRINGIFY0(x) #x +#define EMIT_COMPILER_WARNING_STRINGIFY1(x) EMIT_COMPILER_WARNING_STRINGIFY0(x) +#ifdef __GNUC__ +#define EMIT_COMPILER_WARNING_COMPOSE(x) GCC warning x +#else +#define EMIT_COMPILER_MESSAGE_PREFACE(type) \ +__FILE__ "(" EMIT_COMPILER_WARNING_STRINGIFY1(__LINE__) "): " type ": " +#define EMIT_COMPILER_WARNING_COMPOSE(x) message(EMIT_COMPILER_MESSAGE_PREFACE("warning C0000") x) +#endif +#define WARNING(x) _Pragma(EMIT_COMPILER_WARNING_STRINGIFY1(EMIT_COMPILER_WARNING_COMPOSE(x))) + +// SSE/AVX/AVX2/NEON support. +// SSE4.2 availability: Since Nehalem (2008) +// AVX1 availability: Since Sandy Bridge (2011) +// AVX2 availability: Since Haswell (2013) +#ifndef TINYBVH_NO_SIMD +#if defined __x86_64__ || defined _M_X64 || defined __wasm_simd128__ || defined __wasm_relaxed_simd__ +#if !defined __SSE4_2__ && !defined _MSC_VER +WARNING( "SSE4.2 not enabled in compilation." ) +#else +#define BVH_USESSE +#ifndef __SSE4_2__ +#define __SSE4_2__ // msvc doesn't set the SSE flag +#endif +#endif +#if !defined __AVX__ +WARNING( "AVX not enabled in compilation." ) +#define TINYBVH_NO_SIMD +#else +#define BVH_USEAVX // required for BuildAVX, BVH_SoA and others +#define BVH_USESSE +#endif +#if !defined __AVX2__ || (!defined __FMA__ && !defined _MSC_VER) +WARNING( "AVX2 and FMA not enabled in compilation." ) +#define TINYBVH_NO_SIMD +#else +#define BVH_USEAVX2 // required for BVH8_CPU +#define BVH_USEAVX +#define BVH_USESSE +#endif +#include "immintrin.h" // for __m128 and __m256 +#elif defined __aarch64__ || defined _M_ARM64 +#if !defined __ARM_NEON && !defined __APPLE__ +WARNING( "NEON not enabled in compilation." ) +#define TINYBVH_NO_SIMD +#else +#define BVH_USENEON +#include "arm_neon.h" +#endif +#endif +#endif // TINYBVH_NO_SIMD + +// aligned memory allocation +// note: formally, size needs to be a multiple of 'alignment', see: +// https://en.cppreference.com/w/c/memory/aligned_alloc. +// EMSCRIPTEN enforces this. +// Copy of the same construct in tinyocl, in a different namespace. +namespace tinybvh { +inline size_t make_multiple_of( size_t x, size_t alignment ) { return (x + (alignment - 1)) & ~(alignment - 1); } +#ifdef _MSC_VER // Visual Studio / C11 +#define ALIGNED( x ) __declspec( align( x ) ) +#define _ALIGNED_ALLOC(alignment,size) _aligned_malloc( make_multiple_of( size, alignment ), alignment ); +#define _ALIGNED_FREE(ptr) _aligned_free( ptr ); +#else // EMSCRIPTEN / gcc / clang +#define ALIGNED( x ) __attribute__( ( aligned( x ) ) ) +#if !defined TINYBVH_NO_SIMD && (defined __x86_64__ || defined _M_X64 || defined __wasm_simd128__ || defined __wasm_relaxed_simd__) +#include +#define _ALIGNED_ALLOC(alignment,size) _mm_malloc( make_multiple_of( size, alignment ), alignment ); +#define _ALIGNED_FREE(ptr) _mm_free( ptr ); +#else +#if defined __APPLE__ || defined __aarch64__ || (defined __ANDROID_API__ && (__ANDROID_API__ >= 28)) +#define _ALIGNED_ALLOC(alignment,size) aligned_alloc( alignment, make_multiple_of( size, alignment ) ); +#elif defined __GNUC__ +#if defined __linux__ || defined __EMSCRIPTEN__ +#define _ALIGNED_ALLOC(alignment,size) aligned_alloc( alignment, make_multiple_of( size, alignment ) ); +#else +#define _ALIGNED_ALLOC(alignment,size) _aligned_malloc( alignment, make_multiple_of( size, alignment ) ); +#endif +#endif +#define _ALIGNED_FREE(ptr) free( ptr ); +#endif +#endif +inline void* malloc64( size_t size, void* = nullptr ) { return size == 0 ? 0 : _ALIGNED_ALLOC( 64, size ); } +inline void* malloc4k( size_t size, void* = nullptr ) { return size == 0 ? 0 : _ALIGNED_ALLOC( 4096, size ); } +inline void* malloc32k( size_t size, void* = nullptr ) { return size == 0 ? 0 : _ALIGNED_ALLOC( 32768, size ); } +inline void free64( void* ptr, void* = nullptr ) { _ALIGNED_FREE( ptr ); } +inline void free4k( void* ptr, void* = nullptr ) { _ALIGNED_FREE( ptr ); } +inline void free32k( void* ptr, void* = nullptr ) { _ALIGNED_FREE( ptr ); } +}; // namespace tiybvh + +// Derived TLAS things; for convenience. +#define INST_IDX_SHFT (32 - INST_IDX_BITS) +#if INST_IDX_BITS == 32 +#define PRIM_IDX_MASK 0xffffffff // instance index stored separately. +#else +#define PRIM_IDX_MASK ((1 << INST_IDX_SHFT) - 1) // instance index stored in top bits of hit.prim. +#endif + +// Forced inlining. +#ifdef _MSC_VER +#define __FORCEINLINE __forceinline +#else +#define __FORCEINLINE __attribute__((always_inline)) inline +#endif + +namespace tinybvh { + +// We'll use this whenever a layout has no specialized shadow ray query. +#define FALLBACK_SHADOW_QUERY( s ) { Ray r = s; float d = s.hit.t; Intersect( r ); return r.hit.t < d; } + +#ifdef _MSC_VER +// Suppress a warning caused by the union of x,y,.. and cell[..] in vectors. +// We need this union to address vector components either by name or by index. +// The warning is re-enabled right after the definition of the data types. +#pragma warning ( push ) +#pragma warning ( disable: 4201 /* nameless struct / union */ ) +#endif + +#ifndef TINYBVH_USE_CUSTOM_VECTOR_TYPES + +struct bvhvec3; +struct ALIGNED( 16 ) bvhvec4 +{ + // vector naming is designed to not cause any name clashes. + bvhvec4() = default; + bvhvec4( const float a, const float b, const float c, const float d ) : x( a ), y( b ), z( c ), w( d ) {} + bvhvec4( const float a ) : x( a ), y( a ), z( a ), w( a ) {} + bvhvec4( const bvhvec3 & a ); + bvhvec4( const bvhvec3 & a, float b ); + float& operator [] ( const int32_t i ) { return cell[i]; } + const float& operator [] ( const int32_t i ) const { return cell[i]; } + union { struct { float x, y, z, w; }; float cell[4]; }; +}; + +struct ALIGNED( 8 ) bvhvec2 +{ + bvhvec2() = default; + bvhvec2( const float a, const float b ) : x( a ), y( b ) {} + bvhvec2( const float a ) : x( a ), y( a ) {} + bvhvec2( const bvhvec4 a ) : x( a.x ), y( a.y ) {} + float& operator [] ( const int32_t i ) { return cell[i]; } + const float& operator [] ( const int32_t i ) const { return cell[i]; } + union { struct { float x, y; }; float cell[2]; }; +}; + +struct bvhvec3 +{ + bvhvec3() = default; + bvhvec3( const float a, const float b, const float c ) : x( a ), y( b ), z( c ) {} + bvhvec3( const float a ) : x( a ), y( a ), z( a ) {} + bvhvec3( const bvhvec4 a ) : x( a.x ), y( a.y ), z( a.z ) {} + float& operator [] ( const int32_t i ) { return cell[i]; } + const float& operator [] ( const int32_t i ) const { return cell[i]; } + union { struct { float x, y, z; }; float cell[3]; }; +}; + +struct bvhint3 +{ + bvhint3() = default; + bvhint3( const int32_t a, const int32_t b, const int32_t c ) : x( a ), y( b ), z( c ) {} + bvhint3( const int32_t a ) : x( a ), y( a ), z( a ) {} + bvhint3( const bvhvec3& a ) { x = (int32_t)a.x, y = (int32_t)a.y, z = (int32_t)a.z; } + int32_t& operator [] ( const int32_t i ) { return cell[i]; } + const int32_t& operator [] ( const int32_t i ) const { return cell[i]; } + union { struct { int32_t x, y, z; }; int32_t cell[3]; }; +}; + +struct bvhint2 +{ + bvhint2() = default; + bvhint2( const int32_t a, const int32_t b ) : x( a ), y( b ) {} + bvhint2( const int32_t a ) : x( a ), y( a ) {} + int32_t& operator [] ( const int32_t i ) { return cell[i]; } + const int32_t& operator [] ( const int32_t i ) const { return cell[i]; } + union { struct { int32_t x, y; }; int32_t cell[2]; }; +}; + +struct bvhuint2 +{ + bvhuint2() = default; + bvhuint2( const uint32_t a, const uint32_t b ) : x( a ), y( b ) {} + bvhuint2( const uint32_t a ) : x( a ), y( a ) {} + uint32_t& operator [] ( const int32_t i ) { return cell[i]; } + const uint32_t& operator [] ( const int32_t i ) const { return cell[i]; } + union { struct { uint32_t x, y; }; uint32_t cell[2]; }; +}; + +struct bvhuint3 +{ + bvhuint3() = default; + bvhuint3( const uint32_t a, const uint32_t b, const uint32_t c ) : x( a ), y( b ), z( c ) {} + bvhuint3( const uint32_t a ) : x( a ), y( a ), z( a ) {} + uint32_t& operator [] ( const int32_t i ) { return cell[i]; } + const uint32_t& operator [] ( const int32_t i ) const { return cell[i]; } + union { struct { uint32_t x, y, z; }; uint32_t cell[3]; }; +}; + +struct bvhuint4 +{ + bvhuint4() = default; + bvhuint4( const uint32_t a, const uint32_t b, const uint32_t c, const uint32_t d ) : x( a ), y( b ), z( c ), w( d ) {} + bvhuint4( const uint32_t a ) : x( a ), y( a ), z( a ), w( a ) {} + uint32_t& operator [] ( const int32_t i ) { return cell[i]; } + const uint32_t& operator [] ( const int32_t i ) const { return cell[i]; } + union { struct { uint32_t x, y, z, w; }; uint32_t cell[4]; }; +}; + +struct bvhmat4 // exists only so we can use tinybvh types conveniently in tinyscene. +{ + bvhmat4() = default; + float& operator [] ( const int32_t i ) { return cell[i]; } + const float& operator [] ( const int32_t i ) const { return cell[i]; } + bvhmat4& operator += ( const bvhmat4& a ) { for (int i = 0; i < 16; i++) cell[i] += a.cell[i]; return *this; } + float cell[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; +}; + +#endif // TINYBVH_USE_CUSTOM_VECTOR_TYPES + +struct ALIGNED( 32 ) bvhaabb +{ + bvhvec3 minBounds; uint32_t dummy1; + bvhvec3 maxBounds; uint32_t dummy2; +}; + +struct bvhvec4slice +{ + bvhvec4slice() = default; + bvhvec4slice( const bvhvec4* data, uint32_t count, uint32_t stride = sizeof( bvhvec4 ) ); + operator bool() const { return !!data; } + const bvhvec4& operator [] ( size_t i ) const; + const int8_t* data = nullptr; + uint32_t count, stride; +}; + +// Math operations. +// Note: Since this header file is expected to be included in a source file +// of a separate project, the static keyword doesn't provide sufficient +// isolation; hence the tinybvh_ prefix. +inline float tinybvh_safercp( const float x ) { if (x > 1e-12f || x < -1e-12f) return 1.0f / x; else return x >= 0 ? BVH_FAR : -BVH_FAR; } +inline bvhvec3 tinybvh_safercp( const bvhvec3 a ) { return bvhvec3( tinybvh_safercp( a.x ), tinybvh_safercp( a.y ), tinybvh_safercp( a.z ) ); } +inline bvhvec3 tinybvh_rcp( const bvhvec3 a ) { return tinybvh_safercp( a ); /* bvhvec3( 1.0f / a.x, 1.0f / a.y, 1.0f / a.z ); */ } +inline float tinybvh_min( const float a, const float b ) { return a < b ? a : b; } +inline float tinybvh_max( const float a, const float b ) { return a > b ? a : b; } +inline double tinybvh_min( const double a, const double b ) { return a < b ? a : b; } +inline double tinybvh_max( const double a, const double b ) { return a > b ? a : b; } +inline int32_t tinybvh_min( const int32_t a, const int32_t b ) { return a < b ? a : b; } +inline int32_t tinybvh_max( const int32_t a, const int32_t b ) { return a > b ? a : b; } +inline uint32_t tinybvh_min( const uint32_t a, const uint32_t b ) { return a < b ? a : b; } +inline uint32_t tinybvh_max( const uint32_t a, const uint32_t b ) { return a > b ? a : b; } +inline bvhvec3 tinybvh_min( const bvhvec3& a, const bvhvec3& b ) { return bvhvec3( tinybvh_min( a.x, b.x ), tinybvh_min( a.y, b.y ), tinybvh_min( a.z, b.z ) ); } +inline bvhvec4 tinybvh_min( const bvhvec4& a, const bvhvec4& b ) { return bvhvec4( tinybvh_min( a.x, b.x ), tinybvh_min( a.y, b.y ), tinybvh_min( a.z, b.z ), tinybvh_min( a.w, b.w ) ); } +inline bvhvec3 tinybvh_max( const bvhvec3& a, const bvhvec3& b ) { return bvhvec3( tinybvh_max( a.x, b.x ), tinybvh_max( a.y, b.y ), tinybvh_max( a.z, b.z ) ); } +inline bvhvec4 tinybvh_max( const bvhvec4& a, const bvhvec4& b ) { return bvhvec4( tinybvh_max( a.x, b.x ), tinybvh_max( a.y, b.y ), tinybvh_max( a.z, b.z ), tinybvh_max( a.w, b.w ) ); } +inline float tinybvh_clamp( const float x, const float a, const float b ) { return x > a ? (x < b ? x : b) : a; /* NaN safe */ } +inline int32_t tinybvh_clamp( const int32_t x, const int32_t a, const int32_t b ) { return x > a ? (x < b ? x : b) : a; /* NaN safe */ } +template inline static void tinybvh_swap( T& a, T& b ) { T t = a; a = b; b = t; } +inline float tinybvh_half_area( const bvhvec3& v ) { return v.x < -BVH_FAR ? 0 : (v.x * v.y + v.y * v.z + v.z * v.x); } // for SAH calculations +inline uint32_t tinybvh_maxdim( const bvhvec3& v ) { uint32_t r = fabs( v.x ) > fabs( v.y ) ? 0 : 1; return fabs( v.z ) > fabs( v[r] ) ? 2 : r; } + +// Operator overloads. +// Only a minimal set is provided. +#ifndef TINYBVH_USE_CUSTOM_VECTOR_TYPES + +inline bvhvec2 operator-( const bvhvec2& a ) { return bvhvec2( -a.x, -a.y ); } +inline bvhvec3 operator-( const bvhvec3& a ) { return bvhvec3( -a.x, -a.y, -a.z ); } +inline bvhvec4 operator-( const bvhvec4& a ) { return bvhvec4( -a.x, -a.y, -a.z, -a.w ); } +inline bvhvec2 operator+( const bvhvec2& a, const bvhvec2& b ) { return bvhvec2( a.x + b.x, a.y + b.y ); } +inline bvhvec3 operator+( const bvhvec3& a, const bvhvec3& b ) { return bvhvec3( a.x + b.x, a.y + b.y, a.z + b.z ); } +inline bvhvec4 operator+( const bvhvec4& a, const bvhvec4& b ) { return bvhvec4( a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w ); } +inline bvhvec4 operator+( const bvhvec4& a, const bvhvec3& b ) { return bvhvec4( a.x + b.x, a.y + b.y, a.z + b.z, a.w ); } +inline bvhvec2 operator-( const bvhvec2& a, const bvhvec2& b ) { return bvhvec2( a.x - b.x, a.y - b.y ); } +inline bvhvec3 operator-( const bvhvec3& a, const bvhvec3& b ) { return bvhvec3( a.x - b.x, a.y - b.y, a.z - b.z ); } +inline bvhvec4 operator-( const bvhvec4& a, const bvhvec4& b ) { return bvhvec4( a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w ); } +inline void operator+=( bvhvec2& a, const bvhvec2& b ) { a.x += b.x; a.y += b.y; } +inline void operator+=( bvhvec3& a, const bvhvec3& b ) { a.x += b.x; a.y += b.y; a.z += b.z; } +inline void operator+=( bvhvec4& a, const bvhvec4& b ) { a.x += b.x; a.y += b.y; a.z += b.z; a.w += b.w; } +inline bvhvec2 operator*( const bvhvec2& a, const bvhvec2& b ) { return bvhvec2( a.x * b.x, a.y * b.y ); } +inline bvhvec3 operator*( const bvhvec3& a, const bvhvec3& b ) { return bvhvec3( a.x * b.x, a.y * b.y, a.z * b.z ); } +inline bvhvec4 operator*( const bvhvec4& a, const bvhvec4& b ) { return bvhvec4( a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w ); } +inline bvhvec2 operator*( const bvhvec2& a, float b ) { return bvhvec2( a.x * b, a.y * b ); } +inline bvhvec3 operator*( const bvhvec3& a, float b ) { return bvhvec3( a.x * b, a.y * b, a.z * b ); } +inline bvhvec4 operator*( const bvhvec4& a, float b ) { return bvhvec4( a.x * b, a.y * b, a.z * b, a.w * b ); } +inline bvhvec2 operator*( float b, const bvhvec2& a ) { return bvhvec2( b * a.x, b * a.y ); } +inline bvhvec3 operator*( float b, const bvhvec3& a ) { return bvhvec3( b * a.x, b * a.y, b * a.z ); } +inline bvhvec4 operator*( float b, const bvhvec4& a ) { return bvhvec4( b * a.x, b * a.y, b * a.z, b * a.w ); } +inline bvhvec2 operator/( float b, const bvhvec2& a ) { return bvhvec2( b / a.x, b / a.y ); } +inline bvhvec3 operator/( float b, const bvhvec3& a ) { return bvhvec3( b / a.x, b / a.y, b / a.z ); } +inline bvhvec4 operator/( float b, const bvhvec4& a ) { return bvhvec4( b / a.x, b / a.y, b / a.z, b / a.w ); } +inline bvhvec3 operator/( bvhvec3 b, const bvhvec3& a ) { return bvhvec3( b.x / a.x, b.y / a.y, b.z / a.z ); } +inline void operator*=( bvhvec3& a, const float b ) { a.x *= b; a.y *= b; a.z *= b; } + +#endif // TINYBVH_USE_CUSTOM_VECTOR_TYPES + +// Vector math: cross and dot. +inline bvhvec3 tinybvh_cross( const bvhvec3& a, const bvhvec3& b ) +{ + return bvhvec3( a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x ); +} +inline float tinybvh_dot( const bvhvec2& a, const bvhvec2& b ) { return a.x * b.x + a.y * b.y; } +inline float tinybvh_dot( const bvhvec3& a, const bvhvec3& b ) { return a.x * b.x + a.y * b.y + a.z * b.z; } +inline float tinybvh_dot( const bvhvec4& a, const bvhvec4& b ) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; } + +// Vector math: common operations. +inline float tinybvh_length( const bvhvec3& a ) { return sqrtf( a.x * a.x + a.y * a.y + a.z * a.z ); } +inline bvhvec3 tinybvh_normalize( const bvhvec3& a ) +{ + float l = tinybvh_length( a ), rl = l == 0 ? 0 : (1.0f / l); + return a * rl; +} +inline bvhvec3 tinybvh_transform_point( const bvhvec3& v, const bvhmat4& T ) +{ + const float* Tcell = (const float*)&T; + const bvhvec3 res( + Tcell[0] * v.x + Tcell[1] * v.y + Tcell[2] * v.z + Tcell[3], + Tcell[4] * v.x + Tcell[5] * v.y + Tcell[6] * v.z + Tcell[7], + Tcell[8] * v.x + Tcell[9] * v.y + Tcell[10] * v.z + Tcell[11] ); + const float w = Tcell[12] * v.x + Tcell[13] * v.y + Tcell[14] * v.z + Tcell[15]; + if (w == 1) return res; else return res * (1.f / w); +} +inline bvhvec3 tinybvh_transform_vector( const bvhvec3& v, const bvhmat4& T ) +{ + const float* Tcell = (const float*)&T; + return bvhvec3( Tcell[0] * v.x + Tcell[1] * v.y + Tcell[2] * v.z, Tcell[4] * v.x + + Tcell[5] * v.y + Tcell[6] * v.z, Tcell[8] * v.x + Tcell[9] * v.y + Tcell[10] * v.z ); +} + +#ifdef DOUBLE_PRECISION_SUPPORT +// Double-precision math + +#ifndef TINYBVH_USE_CUSTOM_VECTOR_TYPES + +struct bvhdbl3 +{ + bvhdbl3() = default; + bvhdbl3( const double a, const double b, const double c ) : x( a ), y( b ), z( c ) {} + bvhdbl3( const double a ) : x( a ), y( a ), z( a ) {} + bvhdbl3( const bvhvec3 a ) : x( (double)a.x ), y( (double)a.y ), z( (double)a.z ) {} + double& operator [] ( const int32_t i ) { return cell[i]; } + const double& operator [] ( const int32_t i ) const { return cell[i]; } + union { struct { double x, y, z; }; double cell[3]; }; +}; + +#endif // TINYBVH_USE_CUSTOM_VECTOR_TYPES + +#ifdef _MSC_VER +#pragma warning ( pop ) +#endif + +inline bvhdbl3 tinybvh_min( const bvhdbl3& a, const bvhdbl3& b ) { return bvhdbl3( tinybvh_min( a.x, b.x ), tinybvh_min( a.y, b.y ), tinybvh_min( a.z, b.z ) ); } +inline bvhdbl3 tinybvh_max( const bvhdbl3& a, const bvhdbl3& b ) { return bvhdbl3( tinybvh_max( a.x, b.x ), tinybvh_max( a.y, b.y ), tinybvh_max( a.z, b.z ) ); } + +#ifndef TINYBVH_USE_CUSTOM_VECTOR_TYPES + +inline bvhdbl3 operator-( const bvhdbl3& a ) { return bvhdbl3( -a.x, -a.y, -a.z ); } +inline bvhdbl3 operator+( const bvhdbl3& a, const bvhdbl3& b ) { return bvhdbl3( a.x + b.x, a.y + b.y, a.z + b.z ); } +inline bvhdbl3 operator-( const bvhdbl3& a, const bvhdbl3& b ) { return bvhdbl3( a.x - b.x, a.y - b.y, a.z - b.z ); } +inline void operator+=( bvhdbl3& a, const bvhdbl3& b ) { a.x += b.x; a.y += b.y; a.z += b.z; } +inline bvhdbl3 operator*( const bvhdbl3& a, const bvhdbl3& b ) { return bvhdbl3( a.x * b.x, a.y * b.y, a.z * b.z ); } +inline bvhdbl3 operator*( const bvhdbl3& a, double b ) { return bvhdbl3( a.x * b, a.y * b, a.z * b ); } +inline bvhdbl3 operator*( double b, const bvhdbl3& a ) { return bvhdbl3( b * a.x, b * a.y, b * a.z ); } +inline bvhdbl3 operator/( double b, const bvhdbl3& a ) { return bvhdbl3( b / a.x, b / a.y, b / a.z ); } +inline bvhdbl3 operator/( bvhdbl3 b, const bvhdbl3& a ) { return bvhdbl3( b.x / a.x, b.y / a.y, b.z / a.z ); } +inline bvhdbl3 operator*=( bvhdbl3& a, const double b ) { return bvhdbl3( a.x * b, a.y * b, a.z * b ); } + +#endif // TINYBVH_USE_CUSTOM_VECTOR_TYPES + +inline double tinybvh_length( const bvhdbl3& a ) { return sqrt( a.x * a.x + a.y * a.y + a.z * a.z ); } +inline bvhdbl3 tinybvh_normalize( const bvhdbl3& a ) +{ + double l = tinybvh_length( a ), rl = l == 0 ? 0 : (1.0 / l); + return a * rl; +} +inline bvhdbl3 tinybvh_transform_point( const bvhdbl3& v, const double* T ) +{ + const bvhdbl3 res( + T[0] * v.x + T[1] * v.y + T[2] * v.z + T[3], + T[4] * v.x + T[5] * v.y + T[6] * v.z + T[7], + T[8] * v.x + T[9] * v.y + T[10] * v.z + T[11] ); + const double w = T[12] * v.x + T[13] * v.y + T[14] * v.z + T[15]; + if (w == 1) return res; else return res * (1. / w); +} +inline bvhdbl3 tinybvh_transform_vector( const bvhdbl3& v, const double* T ) +{ + return bvhdbl3( T[0] * v.x + T[1] * v.y + T[2] * v.z, T[4] * v.x + + T[5] * v.y + T[6] * v.z, T[8] * v.x + T[9] * v.y + T[10] * v.z ); +} + +inline bvhdbl3 tinybvh_cross( const bvhdbl3& a, const bvhdbl3& b ) +{ + return bvhdbl3( a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x ); +} +inline double tinybvh_dot( const bvhdbl3& a, const bvhdbl3& b ) { return a.x * b.x + a.y * b.y + a.z * b.z; } + +inline double tinybvh_half_area( const bvhdbl3& v ) { return v.x < -BVH_FAR ? 0 : (v.x * v.y + v.y * v.z + v.z * v.x); } // for SAH calculations + +#endif // DOUBLE_PRECISION_SUPPORT + +// SIMD typedef, helps keeping the interface generic +#if defined BVH_USESSE +typedef __m128 SIMDVEC4; +typedef __m128i SIMDIVEC4; +#define SIMD_SETVEC(a,b,c,d) _mm_set_ps( a, b, c, d ) +#define SIMD_SETRVEC(a,b,c,d) _mm_set_ps( d, c, b, a ) +#elif defined BVH_USENEON +typedef float32x4_t SIMDVEC4; +typedef int32x4_t SIMDIVEC4; +inline float32x4_t SIMD_SETVEC( float w, float z, float y, float x ) +{ + ALIGNED( 64 ) float data[4] = { x, y, z, w }; + return vld1q_f32( data ); +} +inline float32x4_t SIMD_SETRVEC( float x, float y, float z, float w ) +{ + ALIGNED( 64 ) float data[4] = { x, y, z, w }; + return vld1q_f32( data ); +} +inline uint32x4_t SIMD_SETRVECU( uint32_t x, uint32_t y, uint32_t z, uint32_t w ) +{ + ALIGNED( 64 ) uint32_t data[4] = { x, y, z, w }; + return vld1q_u32( data ); +} +inline int32x4_t SIMD_SETRVECS( int32_t x, int32_t y, int32_t z, int32_t w ) +{ + ALIGNED( 64 ) int32_t data[4] = { x, y, z, w }; + return vld1q_s32( data ); +} +#else +typedef bvhvec4 SIMDVEC4; +typedef struct { int x, y, z, w; } SIMDIVEC4; +#define SIMD_SETVEC(a,b,c,d) bvhvec4( d, c, b, a ) +#define SIMD_SETRVEC(a,b,c,d) bvhvec4( a, b, c, d ) +#endif +#ifdef BVH_USEAVX +typedef __m256 SIMDVEC8; +typedef __m256i SIMDIVEC8; +#elif defined BVH_USENEON +typedef float32x4x2_t SIMDVEC8; +typedef int32x4x2_t SIMDIVEC8; +#else +typedef struct { float v0, v1, v2, v3, v4, v5, v6, v7; } SIMDVEC8; +typedef struct { int v0, v1, v2, v3, v4, v5, v6, v7; } SIMDIVEC8; +#endif + +// ============================================================================ +// +// T I N Y _ B V H I N T E R F A C E +// +// ============================================================================ + +#if defined _MSC_VER || defined __GNUC__ +#pragma pack(push, 4) // is there a good alternative for Clang / EMSCRIPTEN? +#endif +struct Intersection +{ + // An intersection result is designed to fit in no more than + // four 32-bit values. This allows efficient storage of a result in + // GPU code. The obvious missing result is an instance id; consider + // squeezing this in the 'prim' field in some way. + // Using this data and the original triangle data, all other info for + // shading (such as normal, texture color etc.) can be reconstructed. +#if INST_IDX_BITS == 32 + uint32_t inst; // instance index. Stored in top bits of prim if INST_IDX_BITS != 32. +#endif + float t, u, v; // distance along ray & barycentric coordinates of the intersection + uint32_t prim; // primitive index + // 64 byte of custom data - + // assuming struct Ray is aligned, this starts at a cache line boundary. + void* auxData; + union + { + unsigned char userChar[56]; + float userFloat[14]; + uint32_t userInt32[14]; + double userDouble[7]; + uint64_t userInt64[7]; + }; +}; +#if defined _MSC_VER || defined __GNUC__ +#pragma pack(pop) // is there a good alternative for Clang / EMSCRIPTEN? +#endif + +// 16 bits of mask to tell which BLASInstance to intersect or not. +// By default the mask will be initialized to intersect all instances. +#define RAY_MASK_INTERSECT_ALL 0xFFFF + +struct ALIGNED( 64 ) Ray +{ + // Basic ray class. Note: For single blas traversal it is expected + // that Ray::rD is properly initialized. For tlas/blas traversal this + // field is typically updated for each blas. + Ray() = default; + Ray( bvhvec3 origin, bvhvec3 direction, float t = BVH_FAR, uint32_t rayMask = RAY_MASK_INTERSECT_ALL ) + { + memset( this, 0, sizeof( Ray ) ); + O = origin, D = tinybvh_normalize( direction ), rD = tinybvh_rcp( D ); + hit.t = t; + mask = rayMask & RAY_MASK_INTERSECT_ALL; + } + ALIGNED( 16 ) bvhvec3 O; uint32_t mask = RAY_MASK_INTERSECT_ALL; + ALIGNED( 16 ) bvhvec3 D; uint32_t instIdx = 0; + ALIGNED( 16 ) bvhvec3 rD; +#if INST_IDX_BITS != 32 + uint32_t dummy2; // align to 16 bytes if field 'hit' is 16 bytes; otherwise don't. +#endif + Intersection hit; +}; + +inline float tinybvh_intersect_aabb( Ray& ray, const bvhvec3& aabbMin, const bvhvec3& aabbMax ) +{ + // "slab test" ray/AABB intersection + float tx1 = (aabbMin.x - ray.O.x) * ray.rD.x, tx2 = (aabbMax.x - ray.O.x) * ray.rD.x; + float tmin = tinybvh_min( tx1, tx2 ), tmax = tinybvh_max( tx1, tx2 ); + float ty1 = (aabbMin.y - ray.O.y) * ray.rD.y, ty2 = (aabbMax.y - ray.O.y) * ray.rD.y; + tmin = tinybvh_max( tmin, tinybvh_min( ty1, ty2 ) ); + tmax = tinybvh_min( tmax, tinybvh_max( ty1, ty2 ) ); + float tz1 = (aabbMin.z - ray.O.z) * ray.rD.z, tz2 = (aabbMax.z - ray.O.z) * ray.rD.z; + tmin = tinybvh_max( tmin, tinybvh_min( tz1, tz2 ) ); + tmax = tinybvh_min( tmax, tinybvh_max( tz1, tz2 ) ); + if (tmax >= tmin && tmin < ray.hit.t && tmax >= 0) return tmin; else return BVH_FAR; +} + +inline bool tinybvh_aabbs_overlap( const bvhvec3& bmin1, const bvhvec3& bmax1, const bvhvec3& bmin2, const bvhvec3& bmax2 ) +{ + return bmin1.x <= bmax2.x && bmax1.x >= bmin2.x && bmin1.y <= bmax2.y && + bmax1.y >= bmin2.y && bmin1.z <= bmax2.z && bmax1.z >= bmin2.z; +} + +#ifdef DOUBLE_PRECISION_SUPPORT + +struct IntersectionEx +{ + // Double-precision hit record. + double t, u, v; // distance along ray & barycentric coordinates of the intersection + uint64_t inst, prim; // instance and primitive index +}; + +struct RayEx +{ + // Double-precision ray definition. + RayEx() = default; + RayEx( bvhdbl3 origin, bvhdbl3 direction, double tmax = BVH_DBL_FAR, uint32_t rayMask = RAY_MASK_INTERSECT_ALL ) + { + memset( this, 0, sizeof( RayEx ) ); + O = origin, D = direction; + double rl = 1.0 / sqrt( D.x * D.x + D.y * D.y + D.z * D.z ); + D.x *= rl, D.y *= rl, D.z *= rl; + rD.x = 1.0 / D.x, rD.y = 1.0 / D.y, rD.z = 1.0 / D.z; + hit.u = hit.v = 0, hit.t = tmax; + instIdx = 0; + mask = rayMask & RAY_MASK_INTERSECT_ALL; + } + bvhdbl3 O, D, rD; + IntersectionEx hit; + uint64_t instIdx; + uint64_t mask; +}; + +#endif + +struct BVHContext +{ + void* (*malloc)(size_t size, void* userdata) = malloc64; + void (*free)(void* ptr, void* userdata) = free64; + void* userdata = nullptr; +}; + +class BVHBase +{ +public: + enum BVHType : uint32_t + { + // Every BVHJ class is derived from BVHBase, but we don't use virtual functions, for + // performance reasons. For a TLAS over a mix of BVH layouts we do however need this + // kind of behavior when transitioning from a TLAS leaf to a BLAS root node. + UNDEFINED = 0, + LAYOUT_BVH = 1, + LAYOUT_BVH_VERBOSE, + LAYOUT_BVH_DOUBLE, + LAYOUT_BVH_SOA, + LAYOUT_BVH_GPU, + LAYOUT_MBVH, + LAYOUT_BVH4_CPU, + LAYOUT_BVH4_GPU, + LAYOUT_MBVH8, + LAYOUT_CWBVH, + LAYOUT_BVH8_AVX2, + LAYOUT_VOXELSET + }; + struct ALIGNED( 32 ) Fragment + { + // A fragment stores the bounds of an input primitive. The name 'Fragment' is from + // "Parallel Spatial Splits in Bounding Volume Hierarchies", 2016, Fuetterling et al., + // and refers to the potential splitting of these boxes for SBVH construction. + bvhvec3 bmin; // AABB min x, y and z + uint32_t primIdx; // index of the original primitive + bvhvec3 bmax; // AABB max x, y and z + uint32_t clipped = 0; // Fragment is the result of clipping if > 0. + bool validBox() { return bmin.x < BVH_FAR; } + }; + // BVH flags, maintainted by tiny_bvh. + bool rebuildable = true; // rebuilds are safe only if a tree has not been converted. + bool refittable = true; // refits are safe only if the tree has no spatial splits. + bool may_have_holes = false; // threaded builds and MergeLeafs produce BVHs with unused nodes. + bool bvh_over_aabbs = false; // a BVH over AABBs is useful for e.g. TLAS traversal. + bool bvh_over_indices = false; // a BVH over indices cannot translate primitive index to vertex index. + BVHContext context; // context used to provide user-defined allocation functions. + BVHType layout = UNDEFINED; // BVH layout identifier. + // Keep track of allocated buffer size to avoid repeated allocation during layout conversion. + uint32_t allocatedNodes = 0; // number of nodes allocated for the BVH. + uint32_t usedNodes = 0; // number of nodes used for the BVH. + uint32_t triCount = 0; // number of primitives in the BVH. + uint32_t idxCount = 0; // number of primitive indices; can exceed triCount for SBVH. + float c_trav = C_TRAV; // cost of a traversal step, used to steer SAH construction. + float c_int = C_INT; // cost of a primitive intersection, used to steer SAH construction. + bool l_quads = false; // some layouts have 4 prims in each leaf; adjust SAH cost for this. + uint32_t hqbvhbins = HQBVHBINS; // number of bins to use in SBVH construction. + bool hqbvhoddeven = false; // if true, odd levels will use one extra bin during construction. + bvhvec3 aabbMin, aabbMax; // bounds of the root node of the BVH. + // Opacity maps support. + uint32_t opmapN = 0; // opacity micro map subdivision: 0 = no maps. + uint32_t* opmap = 0; // opacity micro maps; opmapN^2 bits per triangle. + bool hasOpacityMicroMaps() const { return opmapN > 0; } + void SetOpacityMicroMaps( uint32_t* mapData, uint32_t N ) { opmap = mapData, opmapN = N; } + // Custom memory allocation + void* AlignedAlloc( size_t size ); + void AlignedFree( void* ptr ); + // Common methods + void CopyBasePropertiesFrom( const BVHBase& original ); // copy flags from one BVH to another +protected: + ~BVHBase() {} + __FORCEINLINE void IntersectTri( Ray& ray, const uint32_t idx, const bvhvec4slice& verts, const uint32_t i0, const uint32_t i1, const uint32_t i2 ) const; + __FORCEINLINE bool TriOccludes( const Ray& ray, const bvhvec4slice& verts, const uint32_t triIdx, const uint32_t i0, const uint32_t i1, const uint32_t i2 ) const; + static void PrecomputeTriangle( const bvhvec4slice& vert, const uint32_t ti0, const uint32_t ti1, const uint32_t ti2, float* T ); + static float SA( const bvhvec3& aabbMin, const bvhvec3& aabbMax ); +}; + +class BLASInstance; +class BVH_Verbose; +class BVH : public BVHBase +{ +public: + friend class BVH_GPU; + friend class BVH_SoA; + friend class BVH4_CPU; + friend class BVH8_CPU; + friend class BVH8_CWBVH; + template friend class MBVH; + struct SubdivTask { uint32_t node, sliceStart, sliceEnd, depth; }; + enum BuildFlags : uint32_t + { + NONE = 0, // Default building behavior (binned, SAH-driven). + FULLSPLIT = 1 // Split as far as possible, even when SAH doesn't agree. + }; + struct BVHNode + { + // 'Traditional' 32-byte BVH node layout, as proposed by Ingo Wald. + // When aligned to a cache line boundary, two of these fit together. + bvhvec3 aabbMin; uint32_t leftFirst; // 16 bytes + bvhvec3 aabbMax; uint32_t triCount; // 16 bytes, total: 32 bytes + bool isLeaf() const { return triCount > 0; /* empty BVH leaves do not exist */ } + bool Intersect( const bvhvec3& bmin, const bvhvec3& bmax ) const; + float SurfaceArea() const { return BVH::SA( aabbMin, aabbMax ); } + }; + BVH( BVHContext ctx = {} ) { layout = LAYOUT_BVH; context = ctx; } + BVH( const BVH_Verbose& original ) { layout = LAYOUT_BVH; ConvertFrom( original ); } + BVH( const bvhvec4* vertices, const uint32_t primCount ) { layout = LAYOUT_BVH; Build( vertices, primCount ); } + BVH( const bvhvec4slice& vertices ) { layout = LAYOUT_BVH; Build( vertices ); } + ~BVH(); + void ConvertFrom( const BVH_Verbose& original, bool compact = true ); + void SplitLeafs( const uint32_t maxPrims ); + float SAHCost( const uint32_t nodeIdx = 0 ) const; + float EPOCost( const uint32_t nodeIdx = 0 ) const; + int32_t NodeCount() const; + int32_t LeafCount() const; + int32_t PrimCount( const uint32_t nodeIdx = 0 ) const; + void Compact(); + void Save( const char* fileName ); + bool Load( const char* fileName, const bvhvec4* vertices, const uint32_t primCount ); + bool Load( const char* fileName, const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + bool Load( const char* fileName, const bvhvec4slice& vertices, const uint32_t* indices = 0, const uint32_t primCount = 0 ); + void BuildQuick( const bvhvec4* vertices, const uint32_t primCount ); + void BuildQuick( const bvhvec4slice& vertices ); + void Build( const bvhvec4* vertices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices ); + void Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void Build( BLASInstance* instances, const uint32_t instCount, BVHBase** blasses, const uint32_t blasCount ); + void Build( void (*customGetAABB)(const unsigned, bvhvec3&, bvhvec3&), const uint32_t primCount ); + void BuildHQ( const bvhvec4* vertices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices ); + void BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildAVX( const bvhvec4* vertices, const uint32_t primCount ); + void BuildAVX( const bvhvec4slice& vertices ); + void BuildAVX( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildAVX( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); +#ifdef BVH_USENEON + void BuildNEON( const bvhvec4* vertices, const uint32_t primCount ); + void BuildNEON( const bvhvec4slice& vertices ); + void BuildNEON( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildNEON( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void PrepareNEONBuild( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildNEON(); +#endif + void Refit( const uint32_t nodeIdx = 0 ); + void Optimize( const uint32_t iterations = 25, bool extreme = false, bool stochastic = false ); + uint32_t CombineLeafs( const uint32_t primCount, uint32_t& firstIdx, uint32_t nodeIdx = 0 ); + void CombineLeafs( const uint32_t nodeIdx = 0 ); + int32_t Intersect( Ray& ray ) const; + bool IntersectSphere( const bvhvec3& pos, const float r ) const; + bool IsOccluded( const Ray& ray ) const; + void Intersect256Rays( Ray* first ) const; + void Intersect256RaysSSE( Ray* packet ) const; // requires BVH_USEAVX + // private: + void PrepareBuild( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void Build( uint32_t nodeIdx = 0, uint32_t depth = 0 ); + void BuildFullSweep( uint32_t nodeIdx = 0, uint32_t depth = 0 ); + bool IsOccludedTLAS( const Ray& ray ) const; + int32_t IntersectTLAS( Ray& ray ) const; + void PrepareAVXBuild( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildAVXSubtree( uint32_t nodeIdx = 0, uint32_t depth = 0 ); + void PrepareHQBuild( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t prims ); + void BuildHQ(); + void BuildHQTask( uint32_t nodeIdx, uint32_t depth, const uint32_t maxDepth, uint32_t sliceStart, uint32_t sliceEnd, uint32_t* triIdxB ); + bool ClipFrag( const Fragment& orig, Fragment& newFrag, bvhvec3 bmin, bvhvec3 bmax, bvhvec3 minDim, const uint32_t splitAxis ) const; + void SplitFrag( const Fragment& orig, Fragment& left, Fragment& right, const bvhvec3& minDim, const uint32_t splitAxis, const float splitPos, bool& leftOK, bool& rightOK ) const; +protected: + template int32_t Intersect( Ray& ray ) const; + template int32_t IntersectTLAS( Ray& ray ) const; + template bool IsOccluded( const Ray& ray ) const; + template bool IsOccludedTLAS( const Ray& ray ) const; + void BuildDefault( const bvhvec4* vertices, const uint32_t primCount ); + void BuildDefault( const bvhvec4slice& vertices ); + void BuildDefault( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildDefault( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + // Helpers + inline float SplitCostSAH( const float rAparent, const float Aleft, const int Nleft, const float Aright, const int Nright ) const; + inline float NoSplitCostSAH( const int Nparent ) const; + float EPOArea( const uint32_t subtreeRoot, const uint32_t nodeIdx = 0 ) const; + float PrimArea( const uint32_t p ) const; +public: + // BVH type identification + bool isTLAS() const { return instList != 0; } + bool isBLAS() const { return instList == 0; } + bool isIndexed() const { return vertIdx != 0; } + bool hasCustomGeom() const { return customIntersect != 0; } + // Basic BVH data + bvhvec4slice verts = {}; // pointer to input primitive array: 3x16 bytes per tri. + uint32_t* vertIdx = 0; // vertex indices, only used in case the BVH is built over indexed prims. + uint32_t* primIdx = 0; // primitive index array. + uint32_t* rrsHits = 0; // for RDH: ray hit count per triangle. + BLASInstance* instList = 0; // instance array, for top-level acceleration structure. + BVHBase** blasList = 0; // blas array, for TLAS traversal. + uint32_t blasCount = 0; // number of blasses in blasList. + BVHNode* bvhNode = 0; // BVH node pool, Wald 32-byte format. Root is always in node 0. + uint32_t newNodePtr = 0; // used during build to keep track of next free node in pool. + uint32_t nextFrag = 0; // used during SBVH build to keep track of next free fragment. + Fragment* fragment = 0; // input primitive bounding boxes. + bool useFullSweep = false; // for experiments only; full-sweep SAH builder. + bool threadedBuild = true; // will be disabled for small meshes. + // Custom geometry intersection callback + bool (*customIntersect)(Ray&, const unsigned) = 0; + bool (*customIsOccluded)(const Ray&, const unsigned) = 0; +private: + // Atomic counters for threaded builds + std::atomic* atomicNewNodePtr = 0; + std::atomic* atomicNextFrag = 0; + // data for full sweep builder + uint8_t* flag = 0; + uint32_t* tmp = 0, *sortedIdx[3] = { 0 }; + float* SARs = 0; +#ifdef BVH_USEAVX + // static AVX data members + static __m128 half4, two4, min1, mask3, binmul3; + static __m128i maxbin4; + static __m256 max8, mask6, signFlip8; +public: + // helper for AVX binning + void BuildAVXBinTask( const uint32_t first, const uint32_t last, __m256* binbox, __m256* orig, + uint32_t* count, const __m128& nmin4, const __m128& rpd4 ); +#endif +}; + +class VoxelSet : public BVHBase // just so it can be attached conveniently in a TLAS +{ +public: + struct DDAState + { + uint32_t X, Y, Z; // 12 bytes + float dummy1 = 0; // 16 bytes + bvhvec3 tmax; + float dummy2 = 0; // 16 values, 64 bytes in total + }; + VoxelSet(); + void Set( const uint32_t x, const uint32_t y, const uint32_t z, const uint32_t v ); + void UpdateTopGrid(); + bvhvec3 GetNormal( const Ray& ray ) const; + int32_t Intersect( Ray& ray ) const; + bool IsOccluded( const Ray& ray ) const; +private: + bool Setup3DDDA( const Ray& ray, const bvhvec3& Dsign, DDAState& state, const bvhint3& step, bvhvec3& tdelta, float& t ) const; + // lowest level: 1 32-bit value per voxel +public: + static constexpr int objectDim = 256; // 64, 128 or 256. +private: + static constexpr int objectSize = objectDim * objectDim * objectDim; + // grid level: collection of bricks + static constexpr int brickDim = 8; + static constexpr int brickSize = brickDim * brickDim * brickDim; + static constexpr int gridDim = objectDim / brickDim; + static constexpr int gridSize = gridDim * gridDim * gridDim; + // topgrid level: 1 bit for each group of bricks + static constexpr int groupDim = 4; + static constexpr int groupSize = groupDim * groupDim * groupDim; + static constexpr int topGridDim = gridDim / groupDim; + static constexpr int topGridSize = topGridDim * topGridDim * topGridDim; + // masks + static constexpr uint32_t topResMask = gridDim - groupDim; + static constexpr uint32_t superMask = topResMask + topResMask * gridDim + topResMask * gridDim * gridDim; + // non-static data + uint32_t* grid = 0; + uint32_t* brick = 0; + uint32_t brickCount = (objectDim * objectDim) / 16; // will grow as needed; scales roughly quadratically with objectDim + uint32_t freeBrickPtr = 1; // skip 1, as 0 denotes an empty brick in the topgrid. + uint32_t* topGrid = 0; +}; + +#ifdef DOUBLE_PRECISION_SUPPORT + +class BLASInstanceEx; +class BVH_Double : public BVHBase +{ +public: + struct BVHNode + { + // Double precision 'traditional' BVH node layout. + // Compared to the default BVHNode, child node indices and triangle indices + // are also expanded to 64bit values to support massive scenes. + bvhdbl3 aabbMin, aabbMax; // 2x24 bytes + uint64_t leftFirst; // 8 bytes + uint64_t triCount; // 8 bytes, total: 64 bytes + bool isLeaf() const { return triCount > 0; /* empty BVH leaves do not exist */ } + double Intersect( const RayEx& ray ) const; + double SurfaceArea() const; + }; + struct Fragment + { + // Double-precision version of the fragment sruct. + bvhdbl3 bmin, bmax; // AABB + uint64_t primIdx; // index of the original primitive + }; + BVH_Double( BVHContext ctx = {} ) { layout = LAYOUT_BVH_DOUBLE; context = ctx; } + ~BVH_Double(); + void Build( const bvhdbl3* vertices, const uint64_t primCount ); + void Build( BLASInstanceEx* bvhs, const uint64_t instCount, BVH_Double** blasses, const uint64_t blasCount ); + void Build( void (*customGetAABB)(const uint64_t, bvhdbl3&, bvhdbl3&), const uint64_t primCount ); + void PrepareBuild( const bvhdbl3* vertices, const uint64_t primCount ); + void Build( uint64_t nodeIdx = 0, uint32_t depth = 0 ); + double SAHCost( const uint64_t nodeIdx = 0 ) const; + int32_t Intersect( RayEx& ray ) const; + bool IsOccluded( const RayEx& ray ) const; + bool IsOccludedTLAS( const RayEx& ray ) const; + int32_t IntersectTLAS( RayEx& ray ) const; + bvhdbl3* verts = 0; // pointer to input primitive array, double-precision, 3x24 bytes per tri. + Fragment* fragment = 0; // input primitive bounding boxes, double-precision. + BVHNode* bvhNode = 0; // BVH node, double precision format. + uint64_t* primIdx = 0; // primitive index array for double-precision bvh. + BLASInstanceEx* instList = 0; // instance array, for top-level acceleration structure. + BVH_Double** blasList = 0; // blas array, for TLAS traversal. + uint64_t blasCount = 0; // number of blasses in blasList. + // 64-bit base overrides + uint64_t newNodePtr = 0; // next free bvh pool entry to allocate + uint64_t usedNodes = 0; // number of nodes used for the BVH. + uint64_t allocatedNodes = 0; // number of nodes allocated for the BVH. + uint64_t triCount = 0; // number of primitives in the BVH. + uint64_t idxCount = 0; // number of primitive indices. + bvhdbl3 aabbMin, aabbMax; // bounds of the root node of the BVH. + // Custom geometry intersection callback + bool (*customIntersect)(RayEx&, uint64_t) = 0; + bool (*customIsOccluded)(const RayEx&, uint64_t) = 0; +private: + // Atomic counter for threaded builds + std::atomic* atomicNewNodePtr = 0; +}; + +#endif // DOUBLE_PRECISION_SUPPORT + +class BVH_GPU : public BVHBase +{ +public: + struct BVHNode + { + // Alternative 64-byte BVH node layout, which specifies the bounds of + // the children rather than the node itself. This layout is used by + // Aila and Laine in their seminal GPU ray tracing paper. + bvhvec3 lmin; uint32_t left; + bvhvec3 lmax; uint32_t right; + bvhvec3 rmin; uint32_t triCount; + bvhvec3 rmax; uint32_t firstTri; // total: 64 bytes + bool isLeaf() const { return triCount > 0; } + }; + BVH_GPU( BVHContext ctx = {} ) { layout = LAYOUT_BVH_GPU; context = ctx; } + BVH_GPU( const BVH& original ) { /* DEPRICATED */ ConvertFrom( original ); } + ~BVH_GPU(); + void Build( const bvhvec4* vertices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices ); + void Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void Build( BLASInstance* instances, const uint32_t instCount, BVHBase** blasses, const uint32_t blasCount ); + void BuildHQ( const bvhvec4* vertices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices ); + void BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void Optimize( const uint32_t iterations = 25, bool extreme = false ); + float SAHCost( const uint32_t nodeIdx = 0 ) const { return bvh.SAHCost( nodeIdx ); } + void ConvertFrom( const BVH& original, bool compact = true ); + int32_t Intersect( Ray& ray ) const; + bool IsOccluded( const Ray& ray ) const { FALLBACK_SHADOW_QUERY( ray ); } + // BVH data + BVHNode* bvhNode = 0; // BVH node in Aila & Laine format. + BVH bvh; // BVH4 is created from BVH and uses its data. + bool ownBVH = true; // False when ConvertFrom receives an external bvh. +}; + +class BVH_SoA : public BVHBase +{ +public: + struct BVHNode + { + // Second alternative 64-byte BVH node layout, same as BVHAilaLaine but + // with child AABBs stored in SoA order. + SIMDVEC4 xxxx, yyyy, zzzz; + uint32_t left, right, triCount, firstTri; // total: 64 bytes + bool isLeaf() const { return triCount > 0; } + }; + BVH_SoA( BVHContext ctx = {} ) { layout = LAYOUT_BVH_SOA; context = ctx; } + BVH_SoA( const BVH& original ) { /* DEPRICATED */ layout = LAYOUT_BVH_SOA; ConvertFrom( original ); } + ~BVH_SoA(); + void Build( const bvhvec4* vertices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices ); + void Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildHQ( const bvhvec4* vertices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices ); + void BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void Optimize( const uint32_t iterations = 25, bool extreme = false ); + float SAHCost( const uint32_t nodeIdx = 0 ) const { return bvh.SAHCost( nodeIdx ); } + void Save( const char* fileName ); + bool Load( const char* fileName, const bvhvec4* vertices, const uint32_t primCount ); + bool Load( const char* fileName, const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + bool Load( const char* fileName, const bvhvec4slice& vertices, const uint32_t* indices = 0, const uint32_t primCount = 0 ); + void ConvertFrom( const BVH& original, bool compact = true ); + int32_t Intersect( Ray& ray ) const; + bool IsOccluded( const Ray& ray ) const; + // BVH data + BVHNode* bvhNode = 0; // BVH node in 'structure of arrays' format. + BVH bvh; // BVH_SoA is created from BVH and uses its data. + bool ownBVH = true; // False when ConvertFrom receives an external bvh. +}; + +class BVH_Verbose : public BVHBase +{ +public: + struct BVHNode + { + // This node layout has some extra data per node: It stores left and right + // child node indices explicitly, and stores the index of the parent node. + // This format exists primarily for the BVH optimizer. + bvhvec3 aabbMin; uint32_t left; + bvhvec3 aabbMax; uint32_t right; + uint32_t triCount, firstTri, parent; + float dummy[5]; // total: 64 bytes. + bool isLeaf() const { return triCount > 0; } + float SA() const { return BVH::SA( aabbMin, aabbMax ); } + }; + BVH_Verbose( BVHContext ctx = {} ) { layout = LAYOUT_BVH_VERBOSE; context = ctx; } + BVH_Verbose( const BVH& original ) { /* DEPRECATED */ layout = LAYOUT_BVH_VERBOSE; ConvertFrom( original ); } + ~BVH_Verbose() { AlignedFree( bvhNode ); } + void ConvertFrom( const BVH& original, bool compact = true ); + float SAHCost( const uint32_t nodeIdx = 0 ) const; + int32_t NodeCount() const; + int32_t PrimCount( const uint32_t nodeIdx = 0 ) const; + void Refit( const uint32_t nodeIdx = 0, bool skipLeafs = false ); + void CheckFit( const uint32_t nodeIdx = 0, bool skipLeafs = false ); + void Compact(); + void SortIndices(); + void SplitLeafs( const uint32_t maxPrims = 1 ); + void MergeLeafs(); + void Optimize( const uint32_t iterations = 25, bool extreme = false, bool stochastic = false ); +private: + struct SortItem { uint32_t idx; float cost; }; + void RefitUp( uint32_t nodeIdx ); + float SAHCostUp( uint32_t nodeIdx ) const; + uint32_t FindBestNewPosition( const uint32_t Lid ) const; + uint32_t CountSubtreeTris( const uint32_t nodeIdx, uint32_t* counters ); + void MergeSubtree( const uint32_t nodeIdx, uint32_t* newIdx, uint32_t& newIdxPtr ); +public: + // BVH data + bvhvec4slice verts = {}; // pointer to input primitive array: 3x16 bytes per tri. + Fragment* fragment = 0; // input primitive bounding boxes, double-precision. + uint32_t* primIdx = 0; // primitive index array - pointer copied from original. + BVHNode* bvhNode = 0; // BVH node with additional info, for BVH optimizer. +}; + +template class MBVH : public BVHBase +{ +public: + struct MBVHNode + { + // M-wide (aka 'shallow') BVH layout. + bvhvec3 aabbMin; uint32_t firstTri; + bvhvec3 aabbMax; uint32_t triCount; + uint32_t child[M]; + uint32_t childCount; + uint32_t dummy[((30 - M) & 3) + 1]; // dummies are for alignment. + bool isLeaf() const { return triCount > 0; } + }; + MBVH( BVHContext ctx = {} ) { layout = LAYOUT_MBVH; context = ctx; } + MBVH( const BVH& original ) { /* DEPRECATED */ layout = LAYOUT_MBVH; ConvertFrom( original ); } + ~MBVH(); + void Build( const bvhvec4* vertices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices ); + void Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildHQ( const bvhvec4* vertices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices ); + void BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void Optimize( const uint32_t iterations = 25, bool extreme = false ); + void Refit( const uint32_t nodeIdx = 0 ); + uint32_t LeafCount( const uint32_t nodeIdx = 0 ) const; + float SAHCost( const uint32_t nodeIdx = 0 ) const; + void ConvertFrom( const BVH& original, bool compact = true ); + // BVH data + MBVHNode* mbvhNode = 0; // BVH node for M-wide BVH. + BVH bvh; // MBVH is created from BVH and uses its data. + bool ownBVH = true; // False when ConvertFrom receives an external bvh. +}; + +class BVH4_GPU : public BVHBase +{ +public: + struct BVHNode // actual struct is unused; left here to show structure of data in bvh4Data. + { + // 4-way BVH node, optimized for GPU rendering + struct aabb8 { uint8_t xmin, ymin, zmin, xmax, ymax, zmax; }; // quantized + bvhvec3 aabbMin; uint32_t c0Info; // 16 + bvhvec3 aabbExt; uint32_t c1Info; // 16 + aabb8 c0bounds, c1bounds; uint32_t c2Info; // 16 + aabb8 c2bounds, c3bounds; uint32_t c3Info; // 16; total: 64 bytes + // childInfo, 32bit: + // msb: 0=interior, 1=leaf + // leaf: 16 bits: relative start of triangle data, 15 bits: triangle count. + // interior: 31 bits: child node address, in float4s from BVH data start. + // Triangle data: directly follows nodes with leaves. Per tri: + // - bvhvec4 vert0, vert1, vert2 + // - uint vert0.w stores original triangle index. + // We can make the node smaller by storing child nodes sequentially, but + // there is no way we can shave off a full 16 bytes, unless aabbExt is stored + // as chars as well, as in CWBVH. + }; + BVH4_GPU( BVHContext ctx = {} ) { layout = LAYOUT_BVH4_GPU; context = ctx; } + BVH4_GPU( const MBVH<4>& bvh4 ) { /* DEPRECATED */ layout = LAYOUT_BVH4_GPU; ConvertFrom( bvh4 ); } + ~BVH4_GPU(); + void Build( const bvhvec4* vertices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices ); + void Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildHQ( const bvhvec4* vertices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices ); + void BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void Optimize( const uint32_t iterations = 25, bool extreme = false ); + void ConvertFrom( const MBVH<4>& original, bool compact = true ); + float SAHCost( const uint32_t nodeIdx = 0 ) const { return bvh4.SAHCost( nodeIdx ); } + int32_t Intersect( Ray& ray ) const; + bool IsOccluded( const Ray& ray ) const { FALLBACK_SHADOW_QUERY( ray ); } + // BVH data + bvhvec4* bvh4Data = 0; // 64-byte 4-wide BVH node for efficient GPU rendering. + uint32_t allocatedBlocks = 0; // node data and triangles are stored in 16-byte blocks. + uint32_t usedBlocks = 0; // actually used storage. + MBVH<4> bvh4; // BVH4_GPU is created from BVH4 and uses its data. + bool ownBVH4 = true; // False when ConvertFrom receives an external bvh. +}; + +class BVH4_CPU : public BVHBase +{ +public: + enum { EMPTY_BIT = 1 << 31, LEAF_BIT = 1 << 30 }; + struct BVHNode + { + // 4-way BVH node, optimized for CPU rendering. + // SSE version. + SIMDVEC4 xmin4, xmax4; + SIMDVEC4 ymin4, ymax4; + SIMDVEC4 zmin4, zmax4; + SIMDIVEC4 child4, perm4; // total 128 bytes. + }; + struct CacheLine { SIMDVEC4 a, b, c, d; }; + BVH4_CPU( BVHContext ctx = {} ) { layout = LAYOUT_BVH4_CPU; context = ctx; c_int = 2; l_quads = true; } + ~BVH4_CPU(); + void Save( const char* fileName ); + bool Load( const char* fileName, const uint32_t expectedTris ); + void Build( const bvhvec4* vertices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices ); + void Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ); + void Build( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ); + void BuildHQ( const bvhvec4* vertices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices ); + void BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ); + void BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ); + void Optimize( const uint32_t iterations, bool extreme ); + void Refit(); + float SAHCost( const uint32_t nodeIdx ) const; + void ConvertFrom( MBVH<4>& original ); + int32_t Intersect( Ray& ray ) const; + bool IsOccluded( const Ray& ray ) const; + // Intersect / IsOccluded specialize for ray octant using templated functions. + template int32_t Intersect( Ray& ray ) const; + template bool IsOccluded( const Ray& ray ) const; + // BVH8 data + CacheLine* bvh4Data = 0; // Interleaved interior (128b) and leaf (192b) data. + MBVH<4> bvh4; // BVH4_CPU is created from BVH4 and uses its data. + bool ownBVH4 = true; // false when ConvertFrom receives an external bvh4. + uint32_t allocatedBlocks = 0; // node data and triangles are stored in 16-byte blocks. + uint32_t usedBlocks = 0; // the amount of data actually used. +}; + +class BVH8_CWBVH : public BVHBase +{ +public: + BVH8_CWBVH( BVHContext ctx = {} ) { layout = LAYOUT_CWBVH; context = ctx; } + BVH8_CWBVH( MBVH<8>& bvh8 ) { /* DEPRECATED */ layout = LAYOUT_CWBVH; ConvertFrom( bvh8 ); } + ~BVH8_CWBVH(); + void Save( const char* fileName ); + bool Load( const char* fileName, const uint32_t expectedTris ); + void Build( const bvhvec4* vertices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices ); + void Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildHQ( const bvhvec4* vertices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices ); + void BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ); + void Optimize( const uint32_t iterations = 25, bool extreme = false ); + void ConvertFrom( MBVH<8>& original, bool compact = true ); + float SAHCost( const uint32_t nodeIdx = 0 ) const; + int32_t Intersect( Ray& ray ) const; + bool IsOccluded( const Ray& ray ) const { FALLBACK_SHADOW_QUERY( ray ); } + // BVH8 data + bvhvec4* bvh8Data = 0; // nodes in CWBVH format. + bvhvec4* bvh8Tris = 0; // triangle data for CWBVH nodes. + uint32_t allocatedBlocks = 0; // node data is stored in blocks of 16 byte. + uint32_t usedBlocks = 0; // actually used blocks. + MBVH<8> bvh8; // BVH8_CWBVH is created from BVH8 and uses its data. + bool ownBVH8 = true; // false when ConvertFrom receives an external bvh8. +}; + +// Storage for up to four triangles, in SoA layout, for BVH8_CPU. +struct BVHTri4Leaf +{ + SIMDVEC4 v0x4, v0y4, v0z4; + SIMDVEC4 e1x4, e1y4, e1z4; + SIMDVEC4 e2x4, e2y4, e2z4; + uint32_t primIdx[4]; // total: 160 bytes. + SIMDVEC4 dummy0, dummy1; // pad to 3 full cachelines. + inline void SetData( const bvhvec3& v0, const bvhvec3& e1, const bvhvec3& e2, const uint32_t pidx, const uint32_t slot ) + { + ((float*)&v0x4)[slot] = v0.x, ((float*)&v0y4)[slot] = v0.y, ((float*)&v0z4)[slot] = v0.z; + ((float*)&e1x4)[slot] = e1.x, ((float*)&e1y4)[slot] = e1.y, ((float*)&e1z4)[slot] = e1.z; + ((float*)&e2x4)[slot] = e2.x, ((float*)&e2y4)[slot] = e2.y, ((float*)&e2z4)[slot] = e2.z, primIdx[slot] = pidx; + } +}; + +// Storage for a single triangle, for BVH8_CPU. +struct BVHTri1Leaf +{ + bvhvec3 v0, e1, e2; + uint32_t primIdx; // total: 40 bytes +}; + +class BVH8_CPU : public BVHBase +{ +public: + enum { EMPTY_BIT = 1 << 31, LEAF_BIT = 1 << 30 }; + struct BVHNode + { + // 8-way BVH node, optimized for CPU rendering. + // Based on: "Accelerated Single Ray Tracing for Wide Vector Units", Fuetterling et al., 2017, + // and the implementation by Mathijs Molenaar, https://github.com/mathijs727/pandora + SIMDVEC8 xmin8, xmax8; + SIMDVEC8 ymin8, ymax8; + SIMDVEC8 zmin8, zmax8; + SIMDIVEC8 child8; // bits: 31..29 = flags, 28..0: node index. + SIMDIVEC8 perm8; + // flag bits: 000 is an empty node, 010 is an interior node. 1xx is leaf; xx = tricount. + }; + struct BVHNodeCompact + { + // Novel 128-byte 8-way BVH node. + SIMDIVEC8 child8, perm8; // 64 + float d0, bminx, bminy, bminz, d1, bextx, bexty, bextz; // 32 + SIMDIVEC4 cbminmaxx8, cbminmaxy8; // 32, total: 128 + }; + struct CacheLine { SIMDVEC8 a, b; }; + BVH8_CPU( BVHContext ctx = {} ) { layout = LAYOUT_BVH8_AVX2; context = ctx; c_int = 2; l_quads = true; } + ~BVH8_CPU(); + void Save( const char* fileName ); + bool Load( const char* fileName, const uint32_t expectedTris ); + void Build( const bvhvec4* vertices, const uint32_t primCount ); + void Build( const bvhvec4slice& vertices ); + void Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ); + void Build( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ); + void BuildHQ( const bvhvec4* vertices, const uint32_t primCount ); + void BuildHQ( const bvhvec4slice& vertices ); + void BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ); + void BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ); + void Optimize( const uint32_t iterations, bool extreme ); + void Refit(); + float SAHCost( const uint32_t nodeIdx ) const; + void ConvertFrom( MBVH<8>& original ); + int32_t Intersect( Ray& ray ) const; + bool IsOccluded( const Ray& ray ) const; + // Intersect / IsOccluded specialize for ray octant using templated functions. + template int32_t Intersect( Ray& ray ) const; + template bool IsOccluded( const Ray& ray ) const; + // BVH8 data + CacheLine* bvh8Data = 0; // Interleaved interior (256b) and leaf (192b) data. + MBVH<8> bvh8; // BVH8_CPU is created from BVH8 and uses its data. + bool ownBVH8 = true; // false when ConvertFrom receives an external bvh8. + uint32_t allocatedBlocks = 0; // node data and triangles are stored in 16-byte blocks. + uint32_t usedBlocks = 0; // the amount of data actually used. +}; + +// BLASInstance: A TLAS is built over BLAS instances, where a single BLAS can be +// used with multiple transforms, and multiple BLASses can be combined in a complex +// scene. The TLAS is built over the world-space AABBs of the BLAS root nodes. +class ALIGNED( 64 ) BLASInstance +{ +public: + BLASInstance() = default; + BLASInstance( uint32_t idx ) : blasIdx( idx ) {} + bvhmat4 transform; // defaults to identity + bvhmat4 invTransform; // defaults to identity + bvhvec3 aabbMin = bvhvec3( BVH_FAR ); + uint32_t blasIdx = 0; + bvhvec3 aabbMax = bvhvec3( -BVH_FAR ); + uint32_t mask = (uint32_t)RAY_MASK_INTERSECT_ALL; // set to intersect with all rays by default + uint32_t dummy[8]; // pad struct to 64 byte + void Update( BVHBase * blas ); + void InvertTransform(); +}; + +#ifdef DOUBLE_PRECISION_SUPPORT + +// BLASInstanceEx: Double-precision version of BLASInstance. +class BLASInstanceEx +{ +public: + BLASInstanceEx() = default; + BLASInstanceEx( uint64_t idx ) : blasIdx( idx ) {} + double transform[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; // identity + double invTransform[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; // identity + bvhdbl3 aabbMin = bvhdbl3( BVH_DBL_FAR ); + uint64_t blasIdx = 0; + bvhdbl3 aabbMax = bvhdbl3( -BVH_DBL_FAR ); + uint64_t mask = RAY_MASK_INTERSECT_ALL; // set to intersect with all rays by default + void Update( BVH_Double* blas ); + void InvertTransform(); +}; + +#endif + +} // namespace tinybvh + +#endif // TINY_BVH_H_ + +// ============================================================================ +// +// I M P L E M E N T A T I O N +// +// ============================================================================ + +#ifdef TINYBVH_IMPLEMENTATION + +#include // for assert +#ifdef _MSC_VER +#include // for __lzcnt +#endif +#include // fstream + +// job manager includes +#include +#include +#include +#include +#include + +// We need quite a bit of type reinterpretation, so we'll +// turn off the gcc warning here until the end of the file. +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif + +// Some constexpr stuff to produce nice-looking branches in +// *::Intersect with proper dead code elinimation. +#ifdef ENABLE_INDEXED_GEOMETRY +static constexpr bool indexedEnabled = true; +#else +static constexpr bool indexedEnabled = false; +#endif +#ifdef ENABLE_CUSTOM_GEOMETRY +static constexpr bool customEnabled = true; +#else +static constexpr bool customEnabled = false; +#endif + +namespace tinybvh { + +#if defined BVH_USESSE || defined BVH_USENEON +inline uint32_t __bfind( uint32_t x ) // https://github.com/mackron/refcode/blob/master/lzcnt.c +{ +#if defined _MSC_VER && !defined __clang__ + return 31 - __lzcnt( x ); +#elif defined __EMSCRIPTEN__ + return 31 - __builtin_clz( x ); +#elif defined __GNUC__ || defined __clang__ +#if defined __APPLE__ || __has_builtin(__builtin_clz) + return 31 - __builtin_clz( x ); +#else + uint32_t r; + __asm__ __volatile__( "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" ); + return 31 - r; +#endif +#endif +} +#endif + +// array element counting; https://stackoverflow.com/questions/12784136 +#define BVH_NUM_ELEMS(a) (sizeof(a)/sizeof 0[a]) + +// random numbers +uint32_t tinybvh_rnduint( uint32_t& s ) { s ^= s << 13, s ^= s >> 17, s ^= s << 5; return s; } +float tinybvh_rndfloat( uint32_t& s ) { return tinybvh_rnduint( s ) * 2.3283064365387e-10f; } + +// random unit vector +bvhvec3 tinybvh_rndvec3( uint32_t& s ) +{ + bvhvec3 R; +loop: R = bvhvec3( tinybvh_rndfloat( s ) - 0.5f, tinybvh_rndfloat( s ) * 0.5f, tinybvh_rndfloat( s ) * 0.5f ); + if (tinybvh_dot( R, R ) > 0.25f) goto loop; + return tinybvh_normalize( R ); +} + +// radix sort +static inline unsigned FloatToKey( const float value ) +{ + // Integer comparisons between numbers returned from this function behave + // as if the original float values where compared. + // Simple reinterpretation works only for [0, ...], but this also handles negatives + const unsigned f = *(unsigned*)&value, mask = (unsigned)((int)f >> 31 | (1 << 31)); + return f ^ mask; +} +template static inline void RadixSort( T* input, T* output, int len, Func getKey ) +{ + // http://stereopsis.com/radix.html - Beats std::sort unless for small inputs (say len <= ~64) + const int radixSize = 11, binSize = 1 << radixSize, mask = binSize - 1, passes = 3; + int prefixSum[binSize * passes] = { 0 }; + auto getPrefixSumRef = [=, &prefixSum]( unsigned key, int pass ) -> int& { + const unsigned radix = (key >> (pass * radixSize)) & mask; + int& offset = prefixSum[radix + pass * binSize]; + return offset; + }; + // Compute histogram for all passes + for (int i = 0; i < len; i++) + { + const unsigned key = getKey( input[i] ); + getPrefixSumRef( key, 0 )++, getPrefixSumRef( key, 1 )++, getPrefixSumRef( key, 2 )++; + } + // Compute prefix sum for all passes + int sum0 = 0, sum1 = 0, sum2 = 0; + for (int i = 0; i < binSize; i++) + { + const int temp0 = prefixSum[i + 0 * binSize]; + const int temp1 = prefixSum[i + 1 * binSize]; + const int temp2 = prefixSum[i + 2 * binSize]; + prefixSum[i + 0 * binSize] = sum0; + prefixSum[i + 1 * binSize] = sum1; + prefixSum[i + 2 * binSize] = sum2; + sum0 += temp0, sum1 += temp1, sum2 += temp2; + } + // Sort from LSB to MSB in radix-sized steps + for (int i = 0; i < passes; i++) + { + for (int j = 0; j < len; j++) + { + const T element = input[j]; + const unsigned key0 = getKey( element ); + output[getPrefixSumRef( key0, i )++] = element; + } + tinybvh_swap( input, output ); + } +} + +// error handling +#ifdef _WINDOWS_ // windows.h has been included +#define BVH_FATAL_ERROR_IF(c,s) if (c) { char t[512]; sprintf( t, \ + "Fatal error in tiny_bvh.h, line %i:\n%s\n", __LINE__, s ); \ + MessageBox( NULL, t, "Fatal error", MB_OK ); exit( 1 ); } +#else +#define BVH_FATAL_ERROR_IF(c,s) if (c) { fprintf( stderr, \ + "Fatal error in tiny_bvh.h, line %i:\n%s\n", __LINE__, s ); exit( 1 ); } +#endif +#define BVH_FATAL_ERROR(s) BVH_FATAL_ERROR_IF(1,s) + +// Fallbacks to be used in the absence of HW SIMD support. +#if !defined BVH_USESSE || defined BVH_USENEON +int32_t BVH4_CPU::Intersect( Ray& ) const { BVH_FATAL_ERROR( "BVH4_CPU::Intersect requires SSE. " ); } +bool BVH4_CPU::IsOccluded( const Ray& ) const { BVH_FATAL_ERROR( "BVH4_CPU::IsOccluded requires SSE. " ); } +#endif +#if !defined BVH_USEAVX || defined BVH_USENEON +void BVH::BuildAVX( const bvhvec4*, const uint32_t ) { BVH_FATAL_ERROR( "BVH::BuildAVX requires AVX." ); } +void BVH::BuildAVX( const bvhvec4slice& ) { BVH_FATAL_ERROR( "BVH::BuildAVX requires AVX." ); } +void BVH::BuildAVX( const bvhvec4*, const uint32_t*, const uint32_t ) { BVH_FATAL_ERROR( "BVH::BuildAVX requires AVX." ); } +void BVH::BuildAVX( const bvhvec4slice&, const uint32_t*, const uint32_t ) { BVH_FATAL_ERROR( "BVH::BuildAVX requires AVX." ); } +int32_t BVH8_CWBVH::Intersect( Ray& ) const { BVH_FATAL_ERROR( "BVH8_CWBVH::Intersect requires AVX." ); } +#endif // BVH_USEAVX +#if !defined BVH_USEAVX2 || defined BVH_USENEON +int32_t BVH8_CPU::Intersect( Ray& ) const { BVH_FATAL_ERROR( "BVH8_CPU::Intersect requires AVX2 and FMA." ); } +bool BVH8_CPU::IsOccluded( const Ray& ) const { BVH_FATAL_ERROR( "BVH8_CPU::IsOccluded requires AVX2 and FMA." ); } +#endif // BVH_USEAVX2 +#if !defined BVH_USEAVX && !defined BVH_USENEON +int32_t BVH_SoA::Intersect( Ray& ) const { BVH_FATAL_ERROR( "BVH_SoA::Intersect requires AVX or NEON." ); } +bool BVH_SoA::IsOccluded( const Ray& ) const { BVH_FATAL_ERROR( "BVH_SoA::IsOccluded requires AVX or NEON." ); } +#endif // !(BVH_USEAVX && BVH_USENEON) + +// code compaction: Moeller-Trumbore ray/tri test. +#define MOLLER_TRUMBORE_TEST( tmax, exit ) \ + const bvhvec3 h = tinybvh_cross( ray.D, e2 ); \ + const float a = tinybvh_dot( e1, h ); \ + if (fabs( a ) < 0.000001f) exit; \ + const float f = 1 / a; \ + const bvhvec3 s = ray.O - v0; \ + const float u = f * tinybvh_dot( s, h ); \ + const bvhvec3 q = tinybvh_cross( s, e1 ); \ + const float v = f * tinybvh_dot( ray.D, q ); \ + const bool miss = u < 0 || v < 0 || u + v > 1; \ + if (miss) exit; \ + const float t = f * tinybvh_dot( e2, q ); \ + if (t < 0 || t > tmax) exit; + +// code compaction: fetching triangle vertices, with or without indices. +#define GET_PRIM_INDICES_I0_I1_I2( bvh, idx ) if (indexedEnabled && bvh.vertIdx != 0) \ + i0 = bvh.vertIdx[idx * 3], i1 = bvh.vertIdx[idx * 3 + 1], i2 = bvh.vertIdx[idx * 3 + 2]; \ + else i0 = idx * 3, i1 = idx * 3 + 1, i2 = idx * 3 + 2; + +// ray validation: throw an error if the input ray contains nans. +#define VALIDATE_RAY(r) { float test = r.D.x + r.D.y + r.D.z + ray.hit.t + r.O.x \ + + r.O.y + r.O.z; BVH_FATAL_ERROR_IF( std::isnan( test ), "Input ray contains NaNs." ); } + +#ifndef TINYBVH_USE_CUSTOM_VECTOR_TYPES + +bvhmat4 operator*( const float s, const bvhmat4& a ) +{ + bvhmat4 r; + for (uint32_t i = 0; i < 16; i++) r[i] = a[i] * s; + return r; +} +bvhmat4 operator*( const bvhmat4& a, const bvhmat4& b ) +{ + bvhmat4 r; + for (uint32_t i = 0; i < 16; i += 4) for (uint32_t j = 0; j < 4; ++j) + r[i + j] = (a[i + 0] * b[j + 0]) + (a[i + 1] * b[j + 4]) + + (a[i + 2] * b[j + 8]) + (a[i + 3] * b[j + 12]); + return r; +} +bvhvec4::bvhvec4( const bvhvec3& a ) { x = a.x; y = a.y; z = a.z; w = 0; } +bvhvec4::bvhvec4( const bvhvec3& a, float b ) { x = a.x; y = a.y; z = a.z; w = b; } + +#endif + +bvhvec4slice::bvhvec4slice( const bvhvec4* data, uint32_t count, uint32_t stride ) : + data{ reinterpret_cast(data) }, + count{ count }, stride{ stride } { +} + +const bvhvec4& bvhvec4slice::operator[]( size_t i ) const +{ +#ifdef PARANOID + BVH_FATAL_ERROR_IF( i >= count, "bvhvec4slice::[..], Reading outside slice." ); +#endif + return *reinterpret_cast(data + stride * i); +} + +void* BVHBase::AlignedAlloc( size_t size ) +{ + return context.malloc ? context.malloc( size, context.userdata ) : nullptr; +} + +void BVHBase::AlignedFree( void* ptr ) +{ + if (context.free) + context.free( ptr, context.userdata ); +} + +void BVHBase::CopyBasePropertiesFrom( const BVHBase& original ) +{ + this->rebuildable = original.rebuildable; + this->refittable = original.refittable; + this->may_have_holes = original.may_have_holes; + this->bvh_over_aabbs = original.bvh_over_aabbs; + this->bvh_over_indices = original.bvh_over_indices; + this->context = original.context; + this->triCount = original.triCount; + this->idxCount = original.idxCount; + this->c_int = original.c_int, this->c_trav = original.c_trav; + this->aabbMin = original.aabbMin, this->aabbMax = original.aabbMax; +} + +// BVH implementation +// ---------------------------------------------------------------------------- + +// static variable declarations +#ifdef BVH_USEAVX +__m128 BVH::half4 = _mm_set1_ps( 0.5f ); +__m128 BVH::two4 = _mm_set1_ps( 2.0f ), BVH::min1 = _mm_set1_ps( -1 ); +__m128i BVH::maxbin4 = _mm_set1_epi32( 7 ); +__m128 BVH::mask3 = _mm_cmpeq_ps( _mm_setr_ps( 0, 0, 0, 1 ), _mm_setzero_ps() ); +__m128 BVH::binmul3 = _mm_set1_ps( AVXBINS * 0.49999f ); +__m256 BVH::max8 = _mm256_set1_ps( -BVH_FAR ), BVH::mask6 = _mm256_set_m128( mask3, mask3 ); +__m256 BVH::signFlip8 = _mm256_setr_ps( -0.0f, -0.0f, -0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f ); +#endif + +BVH::~BVH() +{ + AlignedFree( bvhNode ); + AlignedFree( primIdx ); + AlignedFree( fragment ); +} + +void BVH::Save( const char* fileName ) +{ + // saving is easy, it's the loadingn that will be complex. + std::fstream s{ fileName, s.binary | s.out }; + uint32_t header = TINY_BVH_CACHE_VERSION /* 16 bit */ + (layout << 24); + s.write( (char*)&header, sizeof( uint32_t ) ); + s.write( (char*)&triCount, sizeof( uint32_t ) ); + s.write( (char*)this, sizeof( BVH ) ); + s.write( (char*)bvhNode, usedNodes * sizeof( BVHNode ) ); + s.write( (char*)primIdx, idxCount * sizeof( uint32_t ) ); +} + +bool BVH::Load( const char* fileName, const bvhvec4* vertices, const uint32_t primCount ) +{ + return Load( fileName, bvhvec4slice{ vertices, primCount * 3, sizeof( bvhvec4 ) } ); +} + +bool BVH::Load( const char* fileName, const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ) +{ + return Load( fileName, bvhvec4slice{ vertices, primCount * 3, sizeof( bvhvec4 ) }, indices, primCount ); +} + +bool BVH::Load( const char* fileName, const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ) +{ + // open file and check contents + std::fstream s{ fileName, s.binary | s.in }; + if (!s) return false; + BVHContext tmp = context; + bool expectIndexed = (indices != nullptr), saveNewVersion = false; + uint32_t header, fileTriCount; + s.read( (char*)&header, sizeof( uint32_t ) ); + if ((header >> 24) != layout) return false; + if ((header & 0xffffff) != TINY_BVH_CACHE_VERSION) return false; + s.read( (char*)&fileTriCount, sizeof( uint32_t ) ); + if (expectIndexed && fileTriCount != primCount) return false; + if (!expectIndexed && fileTriCount != vertices.count / 3) return false; + // all checks passed; safe to overwrite *this + s.read( (char*)this, sizeof( BVH ) ); + bool fileIsIndexed = vertIdx != nullptr; + if (expectIndexed != fileIsIndexed) return false; // not what we expected. + if (blasList != nullptr || instList != nullptr) return false; // can't load/save TLAS. + context = tmp; // can't load context; function pointers will differ. + bvhNode = (BVHNode*)AlignedAlloc( allocatedNodes * sizeof( BVHNode ) ); + primIdx = (uint32_t*)AlignedAlloc( idxCount * sizeof( uint32_t ) ); + fragment = 0; // no need for this in a BVH that can't be rebuilt. + s.read( (char*)bvhNode, usedNodes * sizeof( BVHNode ) ); + s.read( (char*)primIdx, idxCount * sizeof( uint32_t ) ); + verts = vertices; // we can't load vertices since the BVH doesn't own this data. + vertIdx = (uint32_t*)indices; + // all ok. + if (saveNewVersion) Save( fileName ); + return true; +} + +void BVH::BuildDefault( const bvhvec4* vertices, const uint32_t primCount ) +{ + // access point for builds over a raw list of vertices. The stride of + // the vertex data must be 16 bytes. + // The list will be encapsulated in a 'slice'. The slice can also be used + // directly to provide data with a different stride, which enables use of + // vertex buffers created for rasterization. + BuildDefault( bvhvec4slice{ vertices, primCount * 3, sizeof( bvhvec4 ) } ); +} + +void BVH::BuildDefault( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ) +{ + // access point for builders over an indexed list of raw vertices. + BuildDefault( bvhvec4slice{ vertices, primCount * 3, sizeof( bvhvec4 ) }, indices, primCount ); +} + +void BVH::BuildDefault( const bvhvec4slice& vertices ) +{ + // default builder: used internally when constructing a BVH layout requires + // a regular BVH. Currently, this is the case for all of them. +#if defined BVH_USEAVX + // if AVX is supported, BuildAVX is the optimal option. Tree quality is + // identical to the reference builder, but speed is much better. + BuildAVX( vertices ); +#elif defined BVH_USENEON + BuildNEON( vertices ); +#else + // fallback option, in case neither AVX or NEON is not supported: the reference + // builder, which should work on all platforms. + Build( vertices ); +#endif +} + +void BVH::BuildDefault( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ) +{ + // default builder for indexed vertices. See notes above. +#if defined BVH_USEAVX + BuildAVX( vertices, indices, primCount ); +#elif defined BVH_USENEON + BuildNEON( vertices, indices, primCount ); +#else + Build( vertices, indices, primCount ); +#endif +} + +void BVH::ConvertFrom( const BVH_Verbose& original, bool compact ) +{ + // allocate space + const uint32_t spaceNeeded = compact ? original.usedNodes : original.allocatedNodes; + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + bvhNode = (BVHNode*)AlignedAlloc( triCount * 2 * sizeof( BVHNode ) ); + allocatedNodes = spaceNeeded; + } + memset( bvhNode, 0, sizeof( BVHNode ) * spaceNeeded ); + CopyBasePropertiesFrom( original ); + this->verts = original.verts; + this->primIdx = original.primIdx; + // start conversion + uint32_t srcNodeIdx = 0, dstNodeIdx = 0; + newNodePtr = 2; + uint32_t srcStack[1024], dstStack[1024], stackPtr = 0; + while (1) + { + const BVH_Verbose::BVHNode& orig = original.bvhNode[srcNodeIdx]; + bvhNode[dstNodeIdx].aabbMin = orig.aabbMin; + bvhNode[dstNodeIdx].aabbMax = orig.aabbMax; + if (orig.isLeaf()) + { + bvhNode[dstNodeIdx].triCount = orig.triCount; + bvhNode[dstNodeIdx].leftFirst = orig.firstTri; + if (stackPtr == 0) break; + srcNodeIdx = srcStack[--stackPtr]; + dstNodeIdx = dstStack[stackPtr]; + } + else + { + bvhNode[dstNodeIdx].leftFirst = newNodePtr; + uint32_t srcRightIdx = orig.right; + srcNodeIdx = orig.left, dstNodeIdx = newNodePtr++; + srcStack[stackPtr] = srcRightIdx; + dstStack[stackPtr++] = newNodePtr++; + } + } + usedNodes = original.usedNodes; +} + +float BVH::SAHCost( const uint32_t nodeIdx ) const +{ + // Determine the SAH cost of the tree. This provides an indication + // of the quality of the BVH: Lower is better. + const BVHNode& n = bvhNode[nodeIdx]; + if (n.isLeaf()) return c_int * n.SurfaceArea() * n.triCount; + float cost = c_trav * n.SurfaceArea() + SAHCost( n.leftFirst ) + SAHCost( n.leftFirst + 1 ); + return nodeIdx == 0 ? (cost / n.SurfaceArea()) : cost; +} + +float BVH::PrimArea( const uint32_t p ) const +{ + uint32_t vidx = primIdx[p] * 3; + bvhvec3 v0, v1, v2; + if (vertIdx) v0 = verts[vertIdx[vidx]], v1 = verts[vertIdx[vidx + 1]], v2 = verts[vertIdx[vidx + 2]]; + else v0 = verts[vidx], v1 = verts[vidx + 1], v2 = verts[vidx + 2]; + return 0.5f * tinybvh_length( tinybvh_cross( v1 - v0, v2 - v0 ) ); +} + +float BVH::EPOArea( const uint32_t subtreeRoot, const uint32_t nodeIdx ) const +{ + // abort if we reached the subtree + if (nodeIdx == subtreeRoot) return 0; + const BVHNode& n = bvhNode[nodeIdx]; + const BVHNode& subtree = bvhNode[subtreeRoot]; + // handle case where n is a leaf node + float area = 0; + if (n.isLeaf()) + { + // clip triangles to AABB of subtreeRoot and sum resulting areas + const bvhvec3 bmin = subtree.aabbMin, bmax = subtree.aabbMax; + for (unsigned i = 0; i < n.triCount; i++) + { + // Sutherland-Hodgeman against six bounding planes + uint32_t Nin = 3, vidx = primIdx[n.leftFirst + i] * 3; + bvhvec3 vin[10], vout[10], C; + if (vertIdx) + vin[0] = verts[vertIdx[vidx]], vin[1] = verts[vertIdx[vidx + 1]], vin[2] = verts[vertIdx[vidx + 2]]; + else + vin[0] = verts[vidx], vin[1] = verts[vidx + 1], vin[2] = verts[vidx + 2]; + for (uint32_t a = 0; a < 3; a++) + { + uint32_t Nout = 0; + const float l = bmin[a], r = bmax[a]; + for (uint32_t v = 0; v < Nin; v++) + { + bvhvec3 v0 = vin[v], v1 = vin[(v + 1) % Nin]; + const bool v0in = v0[a] >= l, v1in = v1[a] >= l; + if (!(v0in || v1in)) continue; else if (v0in ^ v1in) + C = v0 + (l - v0[a]) / (v1[a] - v0[a]) * (v1 - v0), + C[a] = l /* accurate */, vout[Nout++] = C; + if (v1in) vout[Nout++] = v1; + } + Nin = 0; + for (uint32_t v = 0; v < Nout; v++) + { + bvhvec3 v0 = vout[v], v1 = vout[(v + 1) % Nout]; + const bool v0in = v0[a] <= r, v1in = v1[a] <= r; + if (!(v0in || v1in)) continue; else if (v0in ^ v1in) + C = v0 + (r - v0[a]) / (v1[a] - v0[a]) * (v1 - v0), + C[a] = r /* accurate */, vin[Nin++] = C; + if (v1in) vin[Nin++] = v1; + } + } + if (Nin < 3) continue; + // calculate area of remaining convex shape in vin + const uint32_t tris = Nin - 2; + bvhvec3 v0 = vin[0], v1, v2; + for (uint32_t j = 0; j < tris; j++) + v1 = vin[j + 1], v2 = vin[j + 2], + area += 0.5f * tinybvh_length( tinybvh_cross( v1 - v0, v2 - v0 ) ); + } + return area; + } + // recurse if n is an inner node + BVHNode& left = bvhNode[n.leftFirst], & right = bvhNode[n.leftFirst + 1]; + if (tinybvh_aabbs_overlap( left.aabbMin, left.aabbMax, subtree.aabbMin, subtree.aabbMax )) + area += EPOArea( subtreeRoot, n.leftFirst ); + if (tinybvh_aabbs_overlap( right.aabbMin, right.aabbMax, subtree.aabbMin, subtree.aabbMax )) + area += EPOArea( subtreeRoot, n.leftFirst + 1 ); + return area; +} + +float BVH::EPOCost( const uint32_t nodeIdx ) const +{ + // Determine the EPO cost of the tree. See: + // "On Quality Metrics of Bounding Volume Hierarchies", Aila et al., 2013. + const BVHNode& n = bvhNode[nodeIdx]; + float area = EPOArea( nodeIdx ); + float cost = (n.isLeaf() ? (c_int * n.triCount) : c_trav) * area; + if (!n.isLeaf()) cost += EPOCost( n.leftFirst ) + EPOCost( n.leftFirst + 1 ); + if (nodeIdx > 0) return cost; + // recursion ends with node 0: Finalize EPO calculation + float totalArea = 0; + for (unsigned i = 0; i < triCount; i++) totalArea += PrimArea( i ); + cost /= totalArea; + return (1.0f - W_EPO) * SAHCost( 0 ) + W_EPO * cost; +} + +void BVH::SplitLeafs( const uint32_t maxPrims ) +{ + uint32_t stack[64], stackPtr = 0, nodeIdx = 0; + while (1) + { + BVHNode& node = bvhNode[nodeIdx]; + if (node.isLeaf()) + { + if (node.triCount > maxPrims) + { + BVHNode& left = bvhNode[newNodePtr], & right = bvhNode[newNodePtr + 1]; + left = node, right = node; + right.leftFirst = node.leftFirst + maxPrims; + right.triCount = node.triCount - maxPrims; + left.triCount = maxPrims, node.leftFirst = newNodePtr, node.triCount = 0, newNodePtr += 2; + } + else + { + if (!stackPtr) break; + nodeIdx = stack[--stackPtr]; + } + } + else + { + nodeIdx = node.leftFirst; + stack[stackPtr++] = node.leftFirst + 1; + } + } + usedNodes = newNodePtr; +} + +int32_t BVH::PrimCount( const uint32_t nodeIdx ) const +{ + // Determine the total number of primitives / fragments in leaf nodes. + const BVHNode& n = bvhNode[nodeIdx]; + return n.isLeaf() ? n.triCount : (PrimCount( n.leftFirst ) + PrimCount( n.leftFirst + 1 )); +} + +// Basic single-function BVH builder, using mid-point splits. +// This builder yields a correct BVH in little time, but the quality of the +// structure will be low. Use this only if build time is the bottleneck in +// your application, e.g., when you need to trace few rays. +void BVH::BuildQuick( const bvhvec4* vertices, const uint32_t primCount ) +{ + // build the BVH with a continuous array of bvhvec4 vertices: + // in this case, the stride for the slice is 16 bytes. + BuildQuick( bvhvec4slice{ vertices, primCount * 3, sizeof( bvhvec4 ) } ); +} + +void BVH::BuildQuick( const bvhvec4slice& vertices ) +{ + BVH_FATAL_ERROR_IF( vertices.count == 0, "BVH::BuildQuick( .. ), primCount == 0." ); + // allocate on first build + const uint32_t primCount = vertices.count / 3; + const uint32_t spaceNeeded = primCount * 2; // upper limit + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + AlignedFree( primIdx ); + AlignedFree( fragment ); + bvhNode = (BVHNode*)AlignedAlloc( spaceNeeded * sizeof( BVHNode ) ); + allocatedNodes = spaceNeeded; + memset( &bvhNode[1], 0, 32 ); // node 1 remains unused, for cache line alignment. + primIdx = (uint32_t*)AlignedAlloc( primCount * sizeof( uint32_t ) ); + fragment = (Fragment*)AlignedAlloc( primCount * sizeof( Fragment ) ); + } + else BVH_FATAL_ERROR_IF( !rebuildable, "BVH::BuildQuick( .. ), bvh not rebuildable." ); + verts = vertices; // note: we're not copying this data; don't delete. + idxCount = triCount = primCount; + // reset node pool + newNodePtr = 2; + // assign all triangles to the root node + BVHNode& root = bvhNode[0]; + root.leftFirst = 0, root.triCount = triCount, root.aabbMin = bvhvec3( BVH_FAR ), root.aabbMax = bvhvec3( -BVH_FAR ); + // initialize fragments and initialize root node bounds + for (uint32_t i = 0; i < triCount; i++) + { + fragment[i].bmin = tinybvh_min( tinybvh_min( verts[i * 3], verts[i * 3 + 1] ), verts[i * 3 + 2] ); + fragment[i].bmax = tinybvh_max( tinybvh_max( verts[i * 3], verts[i * 3 + 1] ), verts[i * 3 + 2] ); + root.aabbMin = tinybvh_min( root.aabbMin, fragment[i].bmin ); + root.aabbMax = tinybvh_max( root.aabbMax, fragment[i].bmax ), primIdx[i] = i; + } + // subdivide recursively + uint32_t task[256], taskCount = 0, nodeIdx = 0; + while (1) + { + while (1) + { + BVHNode& node = bvhNode[nodeIdx]; + // in-place partition against midpoint on longest axis + uint32_t j = node.leftFirst + node.triCount, src = node.leftFirst; + bvhvec3 extent = node.aabbMax - node.aabbMin; + uint32_t axis = 0; + if (extent.y > extent.x && extent.y > extent.z) axis = 1; + if (extent.z > extent.x && extent.z > extent.y) axis = 2; + float splitPos = node.aabbMin[axis] + extent[axis] * 0.5f, centroid; + bvhvec3 lbmin( BVH_FAR ), lbmax( -BVH_FAR ), rbmin( BVH_FAR ), rbmax( -BVH_FAR ), fmin, fmax; + for (uint32_t fi, i = 0; i < node.triCount; i++) + { + fi = primIdx[src], fmin = fragment[fi].bmin, fmax = fragment[fi].bmax; + centroid = (fmin[axis] + fmax[axis]) * 0.5f; + if (centroid < splitPos) + lbmin = tinybvh_min( lbmin, fmin ), lbmax = tinybvh_max( lbmax, fmax ), src++; + else + { + rbmin = tinybvh_min( rbmin, fmin ), rbmax = tinybvh_max( rbmax, fmax ); + tinybvh_swap( primIdx[src], primIdx[--j] ); + } + } + // create child nodes + const uint32_t leftCount = src - node.leftFirst, rightCount = node.triCount - leftCount; + if (leftCount == 0 || rightCount == 0 || taskCount == BVH_NUM_ELEMS( task )) break; // split did not work out. + const int32_t lci = newNodePtr++, rci = newNodePtr++; + bvhNode[lci].aabbMin = lbmin, bvhNode[lci].aabbMax = lbmax; + bvhNode[lci].leftFirst = node.leftFirst, bvhNode[lci].triCount = leftCount; + bvhNode[rci].aabbMin = rbmin, bvhNode[rci].aabbMax = rbmax; + bvhNode[rci].leftFirst = j, bvhNode[rci].triCount = rightCount; + node.leftFirst = lci, node.triCount = 0; + // recurse + task[taskCount++] = rci, nodeIdx = lci; + } + // fetch subdivision task from stack + if (taskCount == 0) break; else nodeIdx = task[--taskCount]; + } + // all done. + aabbMin = bvhNode[0].aabbMin, aabbMax = bvhNode[0].aabbMax; + refittable = true; // not using spatial splits: can refit this BVH + may_have_holes = false; // the reference builder produces a continuous list of nodes + usedNodes = newNodePtr; +} + +// Basic single-function binned-SAH-builder. +// This is the reference builder; it yields a decent tree suitable for ray tracing on the CPU. +// This code uses no SIMD instructions. Faster code, using SSE/AVX, is available for x64 CPUs. +// For GPU rendering: The resulting BVH should be converted to a more optimal +// format after construction, e.g. BVH_GPU, BVH4_GPU or BVH8_CWBVH. +void BVH::Build( const bvhvec4* vertices, const uint32_t prims ) +{ + // build the BVH with a continuous array of bvhvec4 vertices: + // in this case, the stride for the slice is 16 bytes. + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) } ); +} + +void BVH::Build( const bvhvec4slice& vertices ) +{ + // build the BVH from vertices stored in a slice. + PrepareBuild( vertices, 0, 0 /* empty index list; primcount is derived from slice */ ); + if (useFullSweep) BuildFullSweep(); else Build(); +} + +void BVH::Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + // build the BVH with a continuous array of bvhvec4 vertices, indexed by 'indices'. + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH::Build( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + // build the BVH from vertices stored in a slice, indexed by 'indices'. + PrepareBuild( vertices, indices, prims ); + if (useFullSweep) BuildFullSweep(); else Build(); +} + +void BVH::Build( void (*customGetAABB)(const unsigned, bvhvec3&, bvhvec3&), const uint32_t primCount ) +{ + BVH_FATAL_ERROR_IF( primCount == 0, "BVH::Build( void (*customGetAABB)( .. ), instCount ), instCount == 0." ); + triCount = idxCount = primCount; + const uint32_t spaceNeeded = primCount * 2; // upper limit + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + AlignedFree( primIdx ); + AlignedFree( fragment ); + bvhNode = (BVHNode*)AlignedAlloc( spaceNeeded * sizeof( BVHNode ) ); + allocatedNodes = spaceNeeded; + memset( &bvhNode[1], 0, 32 ); // node 1 remains unused, for cache line alignment. + primIdx = (uint32_t*)AlignedAlloc( primCount * sizeof( uint32_t ) ); + fragment = (Fragment*)AlignedAlloc( primCount * sizeof( Fragment ) ); + } + // copy relevant data from instance array + BVHNode& root = bvhNode[0]; + root.leftFirst = 0, root.triCount = primCount, root.aabbMin = bvhvec3( BVH_FAR ), root.aabbMax = bvhvec3( -BVH_FAR ); + for (uint32_t i = 0; i < primCount; i++) + { + customGetAABB( i, fragment[i].bmin, fragment[i].bmax ); + fragment[i].primIdx = i, fragment[i].clipped = 0, primIdx[i] = i; + root.aabbMin = tinybvh_min( root.aabbMin, fragment[i].bmin ); + root.aabbMax = tinybvh_max( root.aabbMax, fragment[i].bmax ); + } + // start build + newNodePtr = 2; + Build(); // or BuildAVX, for large TLAS. +} + +void BVH::Build( BLASInstance* instances, const uint32_t instCount, BVHBase** blasses, const uint32_t bCount ) +{ + BVH_FATAL_ERROR_IF( instCount == 0, "BVH::Build( BLASInstance*, instCount ), instCount == 0." ); + triCount = idxCount = instCount; + const uint32_t spaceNeeded = instCount * 2; // upper limit + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + AlignedFree( primIdx ); + AlignedFree( fragment ); + bvhNode = (BVHNode*)AlignedAlloc( spaceNeeded * sizeof( BVHNode ) ); + allocatedNodes = spaceNeeded; + memset( &bvhNode[1], 0, 32 ); // node 1 remains unused, for cache line alignment. + primIdx = (uint32_t*)AlignedAlloc( instCount * sizeof( uint32_t ) ); + fragment = (Fragment*)AlignedAlloc( instCount * sizeof( Fragment ) ); + } + instList = instances; + blasList = blasses; + blasCount = bCount; + // copy relevant data from instance array + BVHNode& root = bvhNode[0]; + root.leftFirst = 0, root.triCount = instCount, root.aabbMin = bvhvec3( BVH_FAR ), root.aabbMax = bvhvec3( -BVH_FAR ); + for (uint32_t i = 0; i < instCount; i++) + { + if (blasList) // if a null pointer is passed, we'll assume the BLASInstances have been updated elsewhere. + { + uint32_t blasIdx = instList[i].blasIdx; + BVH* blas = (BVH*)blasList[blasIdx]; + instList[i].Update( blas ); + } + fragment[i].bmin = instList[i].aabbMin, fragment[i].primIdx = i; + fragment[i].bmax = instList[i].aabbMax, fragment[i].clipped = 0; + root.aabbMin = tinybvh_min( root.aabbMin, instList[i].aabbMin ); + root.aabbMax = tinybvh_max( root.aabbMax, instList[i].aabbMax ), primIdx[i] = i; + } + // start build + newNodePtr = 2; + Build(); // or BuildAVX, for large TLAS. +} + +void BVH::PrepareBuild( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t prims ) +{ +#ifdef SLICEDUMP + // this code dumps the passed geometry data to a file - for debugging only. + std::fstream df{ "dump.bin", df.binary | df.out }; + uint32_t vcount = vertices.count, indexed = indices == 0 ? 0 : 1, stride = vertices.stride; + uint32_t pcount = indices ? prims : (vertices.count / 3); + df.write( (char*)&pcount, 4 ); + df.write( (char*)&vcount, 4 ); + df.write( (char*)&stride, 4 ); + df.write( (char*)&indexed, sizeof( uint32_t ) ); + df.write( (char*)vertices.data, vertices.stride * vertices.count ); + if (indexed) df.write( (char*)indices, prims * 3 * 4 ); +#endif + uint32_t primCount = prims > 0 ? prims : vertices.count / 3; + const uint32_t spaceNeeded = primCount * 2; // upper limit + // allocate memory on first build + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + AlignedFree( primIdx ); + AlignedFree( fragment ); + bvhNode = (BVHNode*)AlignedAlloc( spaceNeeded * sizeof( BVHNode ) ); + allocatedNodes = spaceNeeded; + memset( &bvhNode[1], 0, 32 ); // node 1 remains unused, for cache line alignment. + primIdx = (uint32_t*)AlignedAlloc( primCount * sizeof( uint32_t ) ); + if (vertices) fragment = (Fragment*)AlignedAlloc( primCount * sizeof( Fragment ) ); + else BVH_FATAL_ERROR_IF( fragment == 0, "BVH::PrepareBuild( 0, .. ), not called from ::Build( aabb )." ); + } + else BVH_FATAL_ERROR_IF( !rebuildable, "BVH::PrepareBuild( .. ), bvh not rebuildable." ); + verts = vertices, idxCount = triCount = primCount, vertIdx = (uint32_t*)indices; + // prepare fragments + BVH_FATAL_ERROR_IF( vertices.count == 0, "BVH::PrepareBuild( .. ), empty vertex slice." ); + BVHNode& root = bvhNode[0]; + root.leftFirst = 0, root.triCount = triCount, root.aabbMin = bvhvec3( BVH_FAR ), root.aabbMax = bvhvec3( -BVH_FAR ); + if (!indices) + { + BVH_FATAL_ERROR_IF( prims != 0, "BVH::PrepareBuild( .. ), indices == 0." ); + // building a BVH over triangles specified as three 16-byte vertices each. + for (uint32_t i = 0; i < triCount; i++) + { + const bvhvec4 v0 = verts[i * 3], v1 = verts[i * 3 + 1], v2 = verts[i * 3 + 2]; + const bvhvec4 fmin = tinybvh_min( v0, tinybvh_min( v1, v2 ) ); + const bvhvec4 fmax = tinybvh_max( v0, tinybvh_max( v1, v2 ) ); + fragment[i].bmin = fmin, fragment[i].bmax = fmax, fragment[i].primIdx = i; + root.aabbMin = tinybvh_min( root.aabbMin, fragment[i].bmin ); + root.aabbMax = tinybvh_max( root.aabbMax, fragment[i].bmax ), primIdx[i] = i; + } + } + else + { + BVH_FATAL_ERROR_IF( prims == 0, "BVH::PrepareBuild( .. ), prims == 0." ); + // building a BVH over triangles consisting of vertices indexed by 'indices'. + for (uint32_t i = 0; i < triCount; i++) + { + const uint32_t i0 = indices[i * 3], i1 = indices[i * 3 + 1], i2 = indices[i * 3 + 2]; + const bvhvec4 v0 = verts[i0], v1 = verts[i1], v2 = verts[i2]; + const bvhvec4 fmin = tinybvh_min( v0, tinybvh_min( v1, v2 ) ); + const bvhvec4 fmax = tinybvh_max( v0, tinybvh_max( v1, v2 ) ); + fragment[i].bmin = fmin, fragment[i].bmax = fmax, fragment[i].primIdx = i; + root.aabbMin = tinybvh_min( root.aabbMin, fragment[i].bmin ); + root.aabbMax = tinybvh_max( root.aabbMax, fragment[i].bmax ), primIdx[i] = i; + } + } + // reset node pool + newNodePtr = 2; + bvh_over_indices = indices != nullptr; + // all set; actual build happens in BVH::Build. +} + +void Build_( uint32_t nodeIdx, uint32_t depth, BVH* bvh ) { bvh->Build( nodeIdx, depth ); } +void BVH::Build( uint32_t nodeIdx, uint32_t depth ) +{ + // avoid threaded building for small meshes: not efficient; build multiple in parallel instead. + if (depth == 0) + { + #ifdef NO_THREADED_BUILDS + threadedBuild = false; + #else + if (triCount < MT_BUILD_THRESHOLD) threadedBuild = false; else + { + atomicNewNodePtr = new std::atomic( newNodePtr ); + } + #endif + } + // subdivide root node recursively + uint32_t task[256], taskCount = 0; + BVHNode& root = bvhNode[0]; + bvhvec3 minDim = (root.aabbMax - root.aabbMin) * 1e-20f; + bvhvec3 bestLMin( 0 ), bestLMax( 0 ), bestRMin( 0 ), bestRMax( 0 ); + while (1) + { + while (1) + { + BVHNode& node = bvhNode[nodeIdx]; + // find optimal object split + bvhvec3 binMin[3][BVHBINS], binMax[3][BVHBINS]; + for (uint32_t a = 0; a < 3; a++) for (uint32_t i = 0; i < BVHBINS; i++) + binMin[a][i] = bvhvec3( BVH_FAR ), binMax[a][i] = bvhvec3( -BVH_FAR ); + uint32_t count[3][BVHBINS]; + memset( count, 0, BVHBINS * 3 * sizeof( uint32_t ) ); + const bvhvec3 rpd3 = bvhvec3( bvhvec3( BVHBINS ) / (node.aabbMax - node.aabbMin) ), nmin3 = node.aabbMin; + for (uint32_t i = 0; i < node.triCount; i++) // process all tris for x,y and z at once + { + const uint32_t fi = primIdx[node.leftFirst + i]; + bvhint3 bi = bvhint3( ((fragment[fi].bmin + fragment[fi].bmax) * 0.5f - nmin3) * rpd3 ); + bi.x = tinybvh_clamp( bi.x, 0, BVHBINS - 1 ); + bi.y = tinybvh_clamp( bi.y, 0, BVHBINS - 1 ); + bi.z = tinybvh_clamp( bi.z, 0, BVHBINS - 1 ); + binMin[0][bi.x] = tinybvh_min( binMin[0][bi.x], fragment[fi].bmin ); + binMax[0][bi.x] = tinybvh_max( binMax[0][bi.x], fragment[fi].bmax ), count[0][bi.x]++; + binMin[1][bi.y] = tinybvh_min( binMin[1][bi.y], fragment[fi].bmin ); + binMax[1][bi.y] = tinybvh_max( binMax[1][bi.y], fragment[fi].bmax ), count[1][bi.y]++; + binMin[2][bi.z] = tinybvh_min( binMin[2][bi.z], fragment[fi].bmin ); + binMax[2][bi.z] = tinybvh_max( binMax[2][bi.z], fragment[fi].bmax ), count[2][bi.z]++; + } + // calculate per-split totals + float splitCost = BVH_FAR, rSAV = 1.0f / node.SurfaceArea(); + uint32_t bestAxis = 0, bestPos = 0; + for (int32_t a = 0; a < 3; a++) if ((node.aabbMax[a] - node.aabbMin[a]) > minDim[a]) + { + bvhvec3 lBMin[BVHBINS - 1], rBMin[BVHBINS - 1], l1( BVH_FAR ), l2( -BVH_FAR ); + bvhvec3 lBMax[BVHBINS - 1], rBMax[BVHBINS - 1], r1( BVH_FAR ), r2( -BVH_FAR ); + float ANL[BVHBINS - 1], ANR[BVHBINS - 1]; + for (uint32_t lN = 0, rN = 0, i = 0; i < BVHBINS - 1; i++) + { + lBMin[i] = l1 = tinybvh_min( l1, binMin[a][i] ); + rBMin[BVHBINS - 2 - i] = r1 = tinybvh_min( r1, binMin[a][BVHBINS - 1 - i] ); + lBMax[i] = l2 = tinybvh_max( l2, binMax[a][i] ); + rBMax[BVHBINS - 2 - i] = r2 = tinybvh_max( r2, binMax[a][BVHBINS - 1 - i] ); + lN += count[a][i], rN += count[a][BVHBINS - 1 - i]; + ANL[i] = lN == 0 ? BVH_FAR : (tinybvh_half_area( l2 - l1 ) * (float)lN); + ANR[BVHBINS - 2 - i] = rN == 0 ? BVH_FAR : (tinybvh_half_area( r2 - r1 ) * (float)rN); + } + // evaluate bin totals to find best position for object split + for (uint32_t i = 0; i < BVHBINS - 1; i++) + { + const float C = ANL[i] + ANR[i]; + if (C < splitCost) + { + splitCost = C, bestAxis = a, bestPos = i; + bestLMin = lBMin[i], bestRMin = rBMin[i], bestLMax = lBMax[i], bestRMax = rBMax[i]; + } + } + } + splitCost = c_trav + c_int * rSAV * splitCost; + float noSplitCost = (float)node.triCount * c_int; + if (splitCost >= noSplitCost) + { + if (node.triCount > 512) printf( "Warning: failed to split large node (%i tris).\n", node.triCount ); + break; // not splitting is better. + } + // in-place partition + uint32_t j = node.leftFirst + node.triCount, src = node.leftFirst; + const float rpd = rpd3[bestAxis], nmin = nmin3[bestAxis]; + for (uint32_t i = 0; i < node.triCount; i++) + { + const uint32_t fi = primIdx[src]; + int32_t bi = (uint32_t)(((fragment[fi].bmin[bestAxis] + fragment[fi].bmax[bestAxis]) * 0.5f - nmin) * rpd); + bi = tinybvh_clamp( bi, 0, BVHBINS - 1 ); + if ((uint32_t)bi <= bestPos) src++; else tinybvh_swap( primIdx[src], primIdx[--j] ); + } + // create child nodes + uint32_t leftCount = src - node.leftFirst, rightCount = node.triCount - leftCount; + if (leftCount == 0 || rightCount == 0 || taskCount == BVH_NUM_ELEMS( task )) break; // should not happen. + int32_t n; + if (threadedBuild) n = atomicNewNodePtr->fetch_add( 2 ); else n = newNodePtr, newNodePtr += 2; + bvhNode[n].aabbMin = bestLMin, bvhNode[n].aabbMax = bestLMax; + bvhNode[n].leftFirst = node.leftFirst, bvhNode[n].triCount = leftCount; + bvhNode[n + 1].aabbMin = bestRMin, bvhNode[n + 1].aabbMax = bestRMax; + bvhNode[n + 1].leftFirst = j, bvhNode[n + 1].triCount = rightCount; + node.leftFirst = n, node.triCount = 0; + if (depth < 5 && threadedBuild) + { + std::thread t1( &Build_, n, depth + 1, this ); + std::thread t2( &Build_, n + 1, depth + 1, this ); + t1.join(); + t2.join(); // TODO: join is only needed in the 'all done' section below. + break; + } + task[taskCount++] = n + 1, nodeIdx = n; + } + // fetch subdivision task from stack + if (taskCount == 0) break; else nodeIdx = task[--taskCount]; + } + // all done. + if (depth == 0) + { + if (threadedBuild) + { + newNodePtr = atomicNewNodePtr->load(); + delete atomicNewNodePtr; + atomicNewNodePtr = 0; + } + usedNodes = newNodePtr; + aabbMin = bvhNode[0].aabbMin, aabbMax = bvhNode[0].aabbMax; + refittable = true; // not using spatial splits: can refit this BVH + may_have_holes = false; // the reference builder produces a continuous list of nodes + bvh_over_aabbs = (verts == 0); // bvh over aabbs is suitable as TLAS + } +} + +// Full-sweep SAH builder. +// Instead of using binning, this builder evaluates all possible split plane +// candidates for each axis. Not efficient; e.g. sorting for each split can +// be prevented. +void BuildFullSweep_( uint32_t n, uint32_t d, BVH* bvh ) { bvh->BuildFullSweep( n, d ); } +void BVH::BuildFullSweep( uint32_t nodeIdx, uint32_t depth ) +{ + if (depth == 0) + { + // allocate data for O(N) stable partition + flag = (uint8_t*)AlignedAlloc( triCount ); + tmp = (uint32_t*)AlignedAlloc( triCount * 4 ); + for (int a = 0; a < 3; a++) sortedIdx[a] = (uint32_t*)AlignedAlloc( triCount * 4 ); + BVH* thisBVH = this; + for (uint32_t a = 0; a < 3; a++) + RadixSort( primIdx, sortedIdx[a], triCount, [=]( int index ) { + return FloatToKey( thisBVH->fragment[index].bmin[a] + thisBVH->fragment[index].bmax[a] ); + } ); + // allocate space for right sweep + SARs = (float*)AlignedAlloc( triCount * sizeof( float ) ); + // prepare threading + #ifdef NO_THREADED_BUILDS + threadedBuild = false; + #else + if (triCount < MT_BUILD_THRESHOLD) threadedBuild = false; else + { + atomicNewNodePtr = new std::atomic( newNodePtr ); + } + #endif + } + // subdivide root node recursively + uint32_t task[256], taskCount = 0; + bvhvec3 minDim = (bvhNode->aabbMax - bvhNode->aabbMin) * 1e-20f; + while (1) + { + while (1) + { + BVHNode& node = bvhNode[nodeIdx]; + // update node bounds + node.aabbMin = bvhvec3( BVH_FAR ), node.aabbMax = bvhvec3( -BVH_FAR ); + for (uint32_t i = 0; i < node.triCount; i++) + { + const uint32_t fi = sortedIdx[0][node.leftFirst + i]; + node.aabbMin = tinybvh_min( node.aabbMin, fragment[fi].bmin ); + node.aabbMax = tinybvh_max( node.aabbMax, fragment[fi].bmax ); + } + if (node.triCount == 1) break; // can't split one triangle. + const float rSAV = 1.0f / node.SurfaceArea(); + const bvhvec3 extent = node.aabbMax - node.aabbMin; + // iterate over x,y,z + float splitCost = (float)node.triCount; + uint32_t splitAxis = 0, splitPos = 0; + for (uint32_t a = 0; a < 3; a++) if (extent[a] > minDim[a]) + { + // sweep from right to left + bvhvec3 Rmin( BVH_FAR ), Rmax( -BVH_FAR ); + uint32_t firstRightTri = 1; + for (uint32_t i = 0; i < node.triCount; i++) + { + const uint32_t fi = sortedIdx[a][node.leftFirst + node.triCount - i - 1]; + const float SAR = (float)i * tinybvh_half_area( Rmax - Rmin ) * rSAV; + SARs[node.leftFirst + node.triCount - i - 1] = SAR; + Rmin = tinybvh_min( Rmin, fragment[fi].bmin ); + Rmax = tinybvh_max( Rmax, fragment[fi].bmax ); + if (SAR >= splitCost) + { + // Right side's cost is already greater than lowest cost and will only increase. Stop early + firstRightTri = node.triCount - i; + break; + } + } + // sweep from left to right + bvhvec3 Lmin( BVH_FAR ), Lmax( -BVH_FAR ); + for (uint32_t i = 0; i < firstRightTri - 1; i++) + { + const uint32_t fi = sortedIdx[a][node.leftFirst + i]; + Lmin = tinybvh_min( Lmin, fragment[fi].bmin ); + Lmax = tinybvh_max( Lmax, fragment[fi].bmax ); + } + for (uint32_t i = firstRightTri - 1; i < node.triCount - 1; i++) + { + const uint32_t fi = sortedIdx[a][node.leftFirst + i]; + Lmin = tinybvh_min( Lmin, fragment[fi].bmin ); + Lmax = tinybvh_max( Lmax, fragment[fi].bmax ); + const float SAL = (float)(i + 1) * tinybvh_half_area( Lmax - Lmin ) * rSAV; + const float C = SAL + SARs[node.leftFirst + i]; + if (C < splitCost) splitCost = C, splitPos = i + 1, splitAxis = a; + else if (SAL >= splitCost) break; + } + } + float noSplitCost = c_int * (float)node.triCount; + splitCost = c_trav + (c_int * splitCost); + if (splitCost >= noSplitCost) break; // not splitting turns out to be better. + // partition + for (uint32_t i = 0; i < splitPos; i++) flag[sortedIdx[splitAxis][node.leftFirst + i]] = 0; // "left" + for (uint32_t i = splitPos; i < node.triCount; i++) flag[sortedIdx[splitAxis][node.leftFirst + i]] = 1; // "right" + for (uint32_t a = 0; a < 3; a++) if (a != splitAxis) + { + int p0 = 0, p1 = 0; + for (uint32_t i = 0; i < node.triCount; i++) + { + const uint32_t fi = sortedIdx[a][node.leftFirst + i]; + if (flag[fi]) tmp[node.leftFirst + p1++] = fi; else sortedIdx[a][node.leftFirst + p0++] = fi; + } + memcpy( &sortedIdx[a][node.leftFirst + p0], tmp + node.leftFirst, p1 * 4 ); + } + // create child nodes + uint32_t leftCount = splitPos, rightCount = node.triCount - leftCount; + if (leftCount >= node.triCount || rightCount >= node.triCount || taskCount == BVH_NUM_ELEMS( task )) break; + memcpy( primIdx + node.leftFirst, sortedIdx[splitAxis] + node.leftFirst, node.triCount * 4 ); + int32_t n; + if (threadedBuild) n = atomicNewNodePtr->fetch_add( 2 ); else n = newNodePtr, newNodePtr += 2; + bvhNode[n].leftFirst = node.leftFirst; + bvhNode[n].triCount = leftCount; + bvhNode[n + 1].leftFirst = node.leftFirst + leftCount; + bvhNode[n + 1].triCount = rightCount; + node.leftFirst = n, node.triCount = 0; + if (leftCount + rightCount > 2000 && depth < 5 && threadedBuild) + { + std::thread t1( &BuildFullSweep_, n, depth + 1, this ); + std::thread t2( &BuildFullSweep_, n + 1, depth + 1, this ); + t1.join(); + t2.join(); // TODO: join is only needed in the 'all done' section below. + break; + } + // recurse + task[taskCount++] = n + 1, nodeIdx = n; + } + // fetch subdivision task from stack + if (taskCount == 0) break; else nodeIdx = task[--taskCount]; + } + // cleanup allocated buffers when done + if (depth == 0) + { + for (int a = 0; a < 3; a++) AlignedFree( sortedIdx[a] ); + AlignedFree( SARs ); + AlignedFree( flag ); + AlignedFree( tmp ); + aabbMin = bvhNode[0].aabbMin, aabbMax = bvhNode[0].aabbMax; + refittable = true; // not using spatial splits: can refit this BVH + may_have_holes = false; // this builder produces a continuous list of nodes + bvh_over_aabbs = (verts == 0); // bvh over aabbs is suitable as TLAS + if (threadedBuild) + { + newNodePtr = atomicNewNodePtr->load(); + delete atomicNewNodePtr; + atomicNewNodePtr = 0; + } + usedNodes = newNodePtr; + } +} + +// SBVH builder. +// Besides the regular object splits used in the reference builder, the SBVH +// algorithm also considers spatial splits, where primitives may be cut in +// multiple parts. This increases primitive count but may reduce overlap of +// BVH nodes. The cost of each option is considered per split. +// For typical geometry, SBVH yields a tree that can be traversed 25% faster. +// This comes at greatly increased construction cost, making the SBVH +// primarily useful for static geometry. +void BVH::BuildHQ( const bvhvec4* vertices, const uint32_t primCount ) +{ + BuildHQ( bvhvec4slice{ vertices, primCount * 3, sizeof( bvhvec4 ) } ); +} + +void BVH::BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + // build the BVH with a continuous array of bvhvec4 vertices, indexed by 'indices'. + BuildHQ( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH::BuildHQ( const bvhvec4slice& vertices ) +{ + PrepareHQBuild( vertices, 0, 0 ); + BuildHQ(); +} + +void BVH::BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + // build the BVH from vertices stored in a slice, indexed by 'indices'. + PrepareHQBuild( vertices, indices, prims ); + BuildHQ(); +} + +void BVH::PrepareHQBuild( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t prims ) +{ + uint32_t primCount = prims > 0 ? prims : vertices.count / 3; + const uint32_t slack = primCount >> 1; // for split prims + const uint32_t spaceNeeded = primCount * 3; + // allocate memory on first build + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + AlignedFree( primIdx ); + AlignedFree( fragment ); + bvhNode = (BVHNode*)AlignedAlloc( spaceNeeded * sizeof( BVHNode ) ); + allocatedNodes = spaceNeeded; + memset( &bvhNode[1], 0, 32 ); // node 1 remains unused, for cache line alignment. + primIdx = (uint32_t*)AlignedAlloc( (primCount + slack) * sizeof( uint32_t ) ); + fragment = (Fragment*)AlignedAlloc( (primCount + slack) * sizeof( Fragment ) ); + } + else BVH_FATAL_ERROR_IF( !rebuildable, "BVH::PrepareHQBuild( .. ), bvh not rebuildable." ); + verts = vertices; // note: we're not copying this data; don't delete. + idxCount = primCount + slack, triCount = primCount, vertIdx = (uint32_t*)indices; + // prepare fragments + BVHNode& root = bvhNode[0]; + root.leftFirst = 0, root.triCount = triCount, root.aabbMin = bvhvec3( BVH_FAR ), root.aabbMax = bvhvec3( -BVH_FAR ); + if (!indices) + { + BVH_FATAL_ERROR_IF( vertices.count == 0, "BVH::PrepareHQBuild( .. ), primCount == 0." ); + BVH_FATAL_ERROR_IF( prims != 0, "BVH::PrepareHQBuild( .. ), indices == 0." ); + // building a BVH over triangles specified as three 16-byte vertices each. + for (uint32_t i = 0; i < triCount; i++) + { + const bvhvec4 v0 = verts[i * 3], v1 = verts[i * 3 + 1], v2 = verts[i * 3 + 2]; + const bvhvec4 fmin = tinybvh_min( v0, tinybvh_min( v1, v2 ) ); + const bvhvec4 fmax = tinybvh_max( v0, tinybvh_max( v1, v2 ) ); + fragment[i].bmin = fmin, fragment[i].bmax = fmax, fragment[i].primIdx = i, fragment[i].clipped = 0; + root.aabbMin = tinybvh_min( root.aabbMin, fragment[i].bmin ); + root.aabbMax = tinybvh_max( root.aabbMax, fragment[i].bmax ), primIdx[i] = i; + } + } + else + { + BVH_FATAL_ERROR_IF( vertices.count == 0, "BVH::PrepareHQBuild( .. ), empty vertex slice." ); + BVH_FATAL_ERROR_IF( prims == 0, "BVH::PrepareHQBuild( .. ), prims == 0." ); + // building a BVH over triangles consisting of vertices indexed by 'indices'. + for (uint32_t i = 0; i < triCount; i++) + { + const uint32_t i0 = indices[i * 3], i1 = indices[i * 3 + 1], i2 = indices[i * 3 + 2]; + const bvhvec4 v0 = verts[i0], v1 = verts[i1], v2 = verts[i2]; + const bvhvec4 fmin = tinybvh_min( v0, tinybvh_min( v1, v2 ) ); + const bvhvec4 fmax = tinybvh_max( v0, tinybvh_max( v1, v2 ) ); + fragment[i].bmin = fmin, fragment[i].bmax = fmax, fragment[i].primIdx = i, fragment[i].clipped = 0; + root.aabbMin = tinybvh_min( root.aabbMin, fragment[i].bmin ); + root.aabbMax = tinybvh_max( root.aabbMax, fragment[i].bmax ), primIdx[i] = i; + } + } + // clear remainder of index array + memset( primIdx + triCount, 0, slack * 4 ); + bvh_over_indices = indices != nullptr; + // threading +#ifdef NO_THREADED_BUILDS + threadedBuild = false; +#else + if (primCount < MT_BUILD_THRESHOLD) threadedBuild = false; +#endif + // all set; actual build happens in BVH::BuildHQ. +} + +float BVH::SplitCostSAH( const float rAparent, const float Aleft, const int Nleft, const float Aright, const int Nright ) const +{ + const int lN = l_quads ? (((Nleft + 3) >> 2) * 4) : Nleft; + const int rN = l_quads ? (((Nright + 3) >> 2) * 4) : Nright; + return c_trav + c_int * rAparent * (Aleft * (float)lN + Aright * (float)rN); +} + +float BVH::NoSplitCostSAH( const int Nparent ) const +{ + return (float)(l_quads ? (((Nparent + 3) >> 2) * 4) : Nparent) * c_int; +} + +void BuildHQTask_( + uint32_t nodeIdx, uint32_t depth, const uint32_t maxDepth, + uint32_t sliceStart, uint32_t sliceEnd, uint32_t* idxTmp, BVH* bvh +) +{ + bvh->BuildHQTask( nodeIdx, depth, maxDepth, sliceStart, sliceEnd, idxTmp ); +} +void BVH::BuildHQTask( + uint32_t nodeIdx, uint32_t depth, const uint32_t maxDepth, + uint32_t sliceStart, uint32_t sliceEnd, uint32_t* idxTmp +) +{ + // prepare subdivision + ALIGNED( 64 ) SubdivTask localTask[512]; + uint32_t localTasks = 0; + bvhvec3 bestLMin( 0 ), bestLMax( 0 ), bestRMin( 0 ), bestRMax( 0 ); + BVHNode& root = bvhNode[0]; + const float rootArea = tinybvh_half_area( root.aabbMax - root.aabbMin ); + const bvhvec3 minDim = (root.aabbMax - root.aabbMin) * 1e-7f /* don't touch, carefully picked */; + // subdivide + uint32_t binCount = hqbvhbins; + while (1) + { + while (1) + { + // fetch node to subdivide + BVHNode& node = bvhNode[nodeIdx]; + // alternating bin counts for optimizer. + if (hqbvhoddeven) binCount = hqbvhbins + (depth & 1); // odd levels get one more + // find optimal object split + bvhvec3 binMin[3][MAXHQBINS], binMax[3][MAXHQBINS]; + for (uint32_t a = 0; a < 3; a++) for (uint32_t i = 0; i < binCount; i++) + binMin[a][i] = bvhvec3( BVH_FAR ), binMax[a][i] = bvhvec3( -BVH_FAR ); + uint32_t count[3][MAXHQBINS]; + for (uint32_t i = 0; i < 3; i++) memset( count[i], 0, binCount * 4 ); + const bvhvec3 rpd3 = bvhvec3( bvhvec3( (float)binCount ) / (node.aabbMax - node.aabbMin) ), nmin3 = node.aabbMin; + for (uint32_t i = 0; i < node.triCount; i++) // process all tris for x,y and z at once + { + const uint32_t fi = primIdx[node.leftFirst + i]; + bvhint3 bi = bvhint3( ((fragment[fi].bmin + fragment[fi].bmax) * 0.5f - nmin3) * rpd3 ); + bi.x = tinybvh_clamp( bi.x, 0, binCount - 1 ); + bi.y = tinybvh_clamp( bi.y, 0, binCount - 1 ); + bi.z = tinybvh_clamp( bi.z, 0, binCount - 1 ); + binMin[0][bi.x] = tinybvh_min( binMin[0][bi.x], fragment[fi].bmin ); + binMax[0][bi.x] = tinybvh_max( binMax[0][bi.x], fragment[fi].bmax ), count[0][bi.x]++; + binMin[1][bi.y] = tinybvh_min( binMin[1][bi.y], fragment[fi].bmin ); + binMax[1][bi.y] = tinybvh_max( binMax[1][bi.y], fragment[fi].bmax ), count[1][bi.y]++; + binMin[2][bi.z] = tinybvh_min( binMin[2][bi.z], fragment[fi].bmin ); + binMax[2][bi.z] = tinybvh_max( binMax[2][bi.z], fragment[fi].bmax ), count[2][bi.z]++; + } + // calculate per-split totals + float noSplitCost = NoSplitCostSAH( node.triCount ); + float splitCost = noSplitCost, rSAV = 1.0f / node.SurfaceArea(); + uint32_t bestAxis = 0, bestPos = 0; + for (int32_t a = 0; a < 3; a++) if ((node.aabbMax[a] - node.aabbMin[a]) > minDim[a]) + { + bvhvec3 lBMin[MAXHQBINS - 1], rBMin[MAXHQBINS - 1], l1( BVH_FAR ), l2( -BVH_FAR ); + bvhvec3 lBMax[MAXHQBINS - 1], rBMax[MAXHQBINS - 1], r1( BVH_FAR ), r2( -BVH_FAR ); + float AL[MAXHQBINS - 1], AR[MAXHQBINS - 1]; // left and right area per split plane + int NL[MAXHQBINS - 1], NR[MAXHQBINS - 1]; // summed left and right tricount + for (uint32_t lN = 0, rN = 0, i = 0; i < binCount - 1; i++) + { + lBMin[i] = l1 = tinybvh_min( l1, binMin[a][i] ); + rBMin[binCount - 2 - i] = r1 = tinybvh_min( r1, binMin[a][binCount - 1 - i] ); + lBMax[i] = l2 = tinybvh_max( l2, binMax[a][i] ); + rBMax[binCount - 2 - i] = r2 = tinybvh_max( r2, binMax[a][binCount - 1 - i] ); + lN += count[a][i], rN += count[a][binCount - 1 - i]; + NL[i] = lN, NR[binCount - 2 - i] = rN; + AL[i] = lN == 0 ? BVH_FAR : tinybvh_half_area( l2 - l1 ); + AR[binCount - 2 - i] = rN == 0 ? BVH_FAR : tinybvh_half_area( r2 - r1 ); + } + // evaluate bin totals to find best position for object split + for (uint32_t i = 0; i < binCount - 1; i++) + { + const float C = SplitCostSAH( rSAV, AL[i], NL[i], AR[i], NR[i] ); + if (C >= splitCost) continue; + splitCost = C, bestAxis = a, bestPos = i; + bestLMin = lBMin[i], bestRMin = rBMin[i], bestLMax = lBMax[i], bestRMax = rBMax[i]; + } + } + // consider a spatial split + bool spatial = false; + int bestNL = 0, bestNR = 0, budget = (int)(sliceEnd - sliceStart); + bvhvec3 spatialUnion = bestLMax - bestRMin; + float spatialOverlap = (tinybvh_half_area( spatialUnion )) / rootArea; + if (budget > (int)node.triCount && (spatialOverlap > 1e-4f || splitCost >= noSplitCost)) + { + float minSplitCost = splitCost * 0.985f; // don't accept a spatial split for minimal gain + for (int a = 0; a < 3; a++) if ((node.aabbMax[a] - node.aabbMin[a]) > minDim[a]) + { + // setup bins + bvhvec3 sbinMin[MAXHQBINS], sbinMax[MAXHQBINS]; + int countIn[MAXHQBINS], countOut[MAXHQBINS]; + memset( countIn, 0, binCount * 4 ); + memset( countOut, 0, binCount * 4 ); + for (uint32_t i = 0; i < binCount; i++) sbinMin[i] = bvhvec3( BVH_FAR ), sbinMax[i] = bvhvec3( -BVH_FAR ); + // populate bins with clipped fragments + const float planeDist = (node.aabbMax[a] - node.aabbMin[a]) / (binCount * 0.9999f); + const float rPlaneDist = 1.0f / planeDist, nodeMin = node.aabbMin[a]; + for (unsigned i = 0; i < node.triCount; i++) + { + const uint32_t fi = primIdx[node.leftFirst + i]; + const int bin1 = tinybvh_clamp( (int32_t)((fragment[fi].bmin[a] - nodeMin) * rPlaneDist), 0, binCount - 1 ); + const int bin2 = tinybvh_clamp( (int32_t)((fragment[fi].bmax[a] - nodeMin) * rPlaneDist), 0, binCount - 1 ); + countIn[bin1]++, countOut[bin2]++; + if (bin2 == bin1) // fragment fits in a single bin + sbinMin[bin1] = tinybvh_min( sbinMin[bin1], fragment[fi].bmin ), + sbinMax[bin1] = tinybvh_max( sbinMax[bin1], fragment[fi].bmax ); + else for (int j = bin1; j <= bin2; j++) + { + // clip fragment to each bin it overlaps + bvhvec3 bmin = node.aabbMin, bmax = node.aabbMax; + bmin[a] = nodeMin + planeDist * j; + bmax[a] = j == (int)(binCount - 2) ? node.aabbMax[a] : (bmin[a] + planeDist); + Fragment orig = fragment[fi]; + Fragment tmpFrag; + if (!ClipFrag( orig, tmpFrag, bmin, bmax, minDim, a )) continue; + sbinMin[j] = tinybvh_min( sbinMin[j], tmpFrag.bmin ); + sbinMax[j] = tinybvh_max( sbinMax[j], tmpFrag.bmax ); + } + } + // evaluate split candidates + bvhvec3 lBMin[MAXHQBINS - 1], rBMin[MAXHQBINS - 1], l1( BVH_FAR ), l2( -BVH_FAR ); + bvhvec3 lBMax[MAXHQBINS - 1], rBMax[MAXHQBINS - 1], r1( BVH_FAR ), r2( -BVH_FAR ); + float AL[MAXHQBINS], AR[MAXHQBINS]; + int NL[MAXHQBINS], NR[MAXHQBINS]; + for (uint32_t lN = 0, rN = 0, i = 0; i < binCount - 1; i++) + { + lBMin[i] = l1 = tinybvh_min( l1, sbinMin[i] ), rBMin[binCount - 2 - i] = r1 = tinybvh_min( r1, sbinMin[binCount - 1 - i] ); + lBMax[i] = l2 = tinybvh_max( l2, sbinMax[i] ), rBMax[binCount - 2 - i] = r2 = tinybvh_max( r2, sbinMax[binCount - 1 - i] ); + lN += countIn[i], rN += countOut[binCount - 1 - i]; + AL[i] = lN == 0 ? BVH_FAR : tinybvh_half_area( l2 - l1 ); + AR[binCount - 2 - i] = rN == 0 ? BVH_FAR : tinybvh_half_area( r2 - r1 ); + NL[i] = lN, NR[binCount - 2 - i] = rN; + } + // find best position for spatial split + for (uint32_t i = 0; i < binCount - 1; i++) + { + const float Cspatial = SplitCostSAH( rSAV, AL[i], NL[i], AR[i], NR[i] ); + if (Cspatial < minSplitCost && NL[i] + NR[i] < budget && NL[i] * NR[i] > 0) + { + spatial = true, minSplitCost = splitCost = Cspatial, bestAxis = a, bestPos = i; + bestLMin = lBMin[i], bestLMax = lBMax[i], bestRMin = rBMin[i], bestRMax = rBMax[i]; + bestNL = NL[i], bestNR = NR[i]; // for unsplitting + bestLMax[a] = bestRMin[a]; // accurate + } + } + } + } + // evaluate best split cost + if (splitCost >= noSplitCost) + { + for (uint32_t i = 0; i < node.triCount; i++) + primIdx[node.leftFirst + i] = fragment[primIdx[node.leftFirst + i]].primIdx; + break; // not splitting is better. + } + // double-buffered partition + uint32_t A = sliceStart, B = sliceEnd, src = node.leftFirst; + if (spatial) + { + // spatial partitioning + const float planeDist = (node.aabbMax[bestAxis] - node.aabbMin[bestAxis]) / (binCount * 0.9999f); + const float rPlaneDist = 1.0f / planeDist, nodeMin = node.aabbMin[bestAxis]; + for (uint32_t i = 0; i < node.triCount; i++) + { + const uint32_t fragIdx = primIdx[src++]; + const uint32_t bin1 = (uint32_t)tinybvh_max( (fragment[fragIdx].bmin[bestAxis] - nodeMin) * rPlaneDist, 0.0f ); + const uint32_t bin2 = (uint32_t)tinybvh_max( (fragment[fragIdx].bmax[bestAxis] - nodeMin) * rPlaneDist, 0.0f ); + if (bin2 <= bestPos) idxTmp[A++] = fragIdx; else if (bin1 > bestPos) idxTmp[--B] = fragIdx; else + { + #if defined SBVH_UNSPLITTING + // unsplitting: 1. Calculate what happens if we add this primitive entirely to the left side + if (bestNR > 1) + { + bvhvec3 unsplitLMin = tinybvh_min( bestLMin, fragment[fragIdx].bmin ); + bvhvec3 unsplitLMax = tinybvh_max( bestLMax, fragment[fragIdx].bmax ); + float AL = tinybvh_half_area( unsplitLMax - unsplitLMin ); + float AR = tinybvh_half_area( bestRMax - bestRMin ); + float CunsplitLeft = SplitCostSAH( rSAV, AL, bestNL, AR, bestNR - 1 ); + if (CunsplitLeft <= splitCost) + { + bestNR--, splitCost = CunsplitLeft, idxTmp[A++] = fragIdx; + bestLMin = unsplitLMin, bestLMax = unsplitLMax; + continue; + } + } + // 2. Calculate what happens if we add this primitive entirely to the right side + if (bestNL > 1) + { + const bvhvec3 unsplitRMin = tinybvh_min( bestRMin, fragment[fragIdx].bmin ); + const bvhvec3 unsplitRMax = tinybvh_max( bestRMax, fragment[fragIdx].bmax ); + const float AL = tinybvh_half_area( bestLMax - bestLMin ); + const float AR = tinybvh_half_area( unsplitRMax - unsplitRMin ); + const float CunsplitRight = SplitCostSAH( rSAV, AL, bestNL - 1, AR, bestNR ); + if (CunsplitRight <= splitCost) + { + bestNL--, splitCost = CunsplitRight, idxTmp[--B] = fragIdx; + bestRMin = unsplitRMin, bestRMax = unsplitRMax; + continue; + } + } + #endif + // split straddler + ALIGNED( 64 ) Fragment part1, part2; // keep all clipping in a single cacheline. + bool leftOK = false, rightOK = false; + float splitPos = bestLMax[bestAxis]; + SplitFrag( fragment[fragIdx], part1, part2, minDim, bestAxis, splitPos, leftOK, rightOK ); + if (leftOK && rightOK) + { + uint32_t newFragIdx = threadedBuild ? atomicNextFrag->fetch_add( 1 ) : nextFrag++; + fragment[fragIdx] = part1, idxTmp[A++] = fragIdx, + fragment[newFragIdx] = part2, idxTmp[--B] = newFragIdx; + } + else // didn't work out; unsplit (rare) + if (leftOK) idxTmp[A++] = fragIdx; else idxTmp[--B] = fragIdx; + } + } + // for spatial splits, we fully refresh the bounds: clipping is never fully stable.. + bestLMin = bestRMin = bvhvec3( BVH_FAR ), bestLMax = bestRMax = bvhvec3( -BVH_FAR ); + for (uint32_t i = sliceStart; i < A; i++) + bestLMin = tinybvh_min( bestLMin, fragment[idxTmp[i]].bmin ), + bestLMax = tinybvh_max( bestLMax, fragment[idxTmp[i]].bmax ); + for (uint32_t i = B; i < sliceEnd; i++) + bestRMin = tinybvh_min( bestRMin, fragment[idxTmp[i]].bmin ), + bestRMax = tinybvh_max( bestRMax, fragment[idxTmp[i]].bmax ); + } + else + { + // object partitioning + const float rpd = rpd3[bestAxis], nmin = nmin3[bestAxis]; + for (uint32_t i = 0; i < node.triCount; i++) + { + const uint32_t fr = primIdx[src + i]; + int32_t bi = (int32_t)(((fragment[fr].bmin[bestAxis] + fragment[fr].bmax[bestAxis]) * 0.5f - nmin) * rpd); + bi = tinybvh_clamp( bi, 0, binCount - 1 ); + if (bi <= (int32_t)bestPos) idxTmp[A++] = fr; else idxTmp[--B] = fr; + } + } + // copy back slice data + memcpy( primIdx + sliceStart, idxTmp + sliceStart, (sliceEnd - sliceStart) * 4 ); + // create child nodes + uint32_t leftCount = A - sliceStart, rightCount = sliceEnd - B; + if (leftCount == 0 || rightCount == 0) + { + // spatial split failed. We shouldn't get here, but we do sometimes.. + for (uint32_t i = 0; i < node.triCount; i++) + primIdx[node.leftFirst + i] = fragment[primIdx[node.leftFirst + i]].primIdx; + node.aabbMin = tinybvh_min( bestLMin, bestRMin ); + node.aabbMax = tinybvh_max( bestLMax, bestRMax ); + break; + } + int32_t leftChildIdx; + if (threadedBuild) leftChildIdx = atomicNewNodePtr->fetch_add( 2 ); else leftChildIdx = newNodePtr, newNodePtr += 2; + int32_t rightChildIdx = leftChildIdx + 1; + bvhNode[leftChildIdx].aabbMin = bestLMin, bvhNode[leftChildIdx].aabbMax = bestLMax; + bvhNode[leftChildIdx].leftFirst = sliceStart, bvhNode[leftChildIdx].triCount = leftCount; + bvhNode[rightChildIdx].aabbMin = bestRMin, bvhNode[rightChildIdx].aabbMax = bestRMax; + bvhNode[rightChildIdx].leftFirst = B, bvhNode[rightChildIdx].triCount = rightCount; + node.leftFirst = leftChildIdx, node.triCount = 0; + // recurse + if (depth < maxDepth && threadedBuild) + { + std::thread t1( &BuildHQTask_, leftChildIdx, depth + 1, maxDepth, sliceStart, (A + B) >> 1, idxTmp, this ); + std::thread t2( &BuildHQTask_, rightChildIdx, depth + 1, maxDepth, (A + B) >> 1, sliceEnd, idxTmp, this ); + t1.join(); + t2.join(); // TODO: join is only needed in the 'all done' section below. + break; + } + // proceed with left child, push right child on local stack + localTask[localTasks].node = rightChildIdx, localTask[localTasks].depth = depth; + localTask[localTasks].sliceStart = (A + B) >> 1, localTask[localTasks++].sliceEnd = sliceEnd; + nodeIdx = leftChildIdx, sliceEnd = (A + B) >> 1; + } + // pop a local task, if any are left + if (localTasks == 0) break; + nodeIdx = localTask[--localTasks].node, depth = localTask[localTasks].depth; + sliceStart = localTask[localTasks].sliceStart, sliceEnd = localTask[localTasks].sliceEnd; + } +} + +void BVH::BuildHQ() +{ + const uint32_t slack = triCount >> 1; // for split prims + uint32_t* idxTmp = (uint32_t*)AlignedAlloc( (triCount + slack) * sizeof( uint32_t ) ); + memset( idxTmp, 0, (triCount + slack) * 4 ); + // reset node pool + if (threadedBuild) + { + atomicNewNodePtr = new std::atomic( 2 ); + atomicNextFrag = new std::atomic( triCount ); + } + else + { + newNodePtr = 2; + nextFrag = triCount; + } + // subdivide recursively + uint32_t nodeIdx = 0, sliceStart = 0, sliceEnd = triCount + slack, depth = 0; + BuildHQTask( nodeIdx, depth, 5, sliceStart, sliceEnd, idxTmp ); + // all done. + AlignedFree( idxTmp ); + aabbMin = bvhNode[0].aabbMin, aabbMax = bvhNode[0].aabbMax; + refittable = false; // can't refit an SBVH + may_have_holes = false; // there may be holes in the index list, but not in the node list + if (threadedBuild) + { + newNodePtr = atomicNewNodePtr->load(); + nextFrag = atomicNextFrag->load(); + } + usedNodes = newNodePtr; + delete atomicNewNodePtr; + delete atomicNextFrag; + atomicNewNodePtr = 0; + atomicNextFrag = 0; + Compact(); +} + +// Optimize: Will happen via BVH_Verbose. +void BVH::Optimize( const uint32_t iterations, bool extreme, bool stochastic ) +{ + BVH_Verbose* verbose = new BVH_Verbose(); + verbose->ConvertFrom( *this ); + verbose->Optimize( iterations, extreme, stochastic ); + ConvertFrom( *verbose ); +} + +// Refitting: For animated meshes, where the topology remains intact. This +// includes trees waving in the wind, or subsequent frames for skinned +// animations. Repeated refitting tends to lead to deteriorated BVHs and +// slower ray tracing. Rebuild when this happens. +void BVH::Refit( const uint32_t /* unused */ ) +{ + BVH_FATAL_ERROR_IF( !refittable, "BVH::Refit( .. ), refitting an SBVH." ); + BVH_FATAL_ERROR_IF( bvhNode == 0, "BVH::Refit( .. ), bvhNode == 0." ); + BVH_FATAL_ERROR_IF( may_have_holes, "BVH::Refit( .. ), bvh may have holes." ); + BVH_FATAL_ERROR_IF( isTLAS(), "BVH::Refit( .. ), do not refit a TLAS, use Build(..)." ); + for (int32_t i = usedNodes - 1; i >= 0; i--) if (i != 1) + { + BVHNode& node = bvhNode[i]; + if (node.isLeaf()) // leaf: adjust to current triangle vertex positions + { + bvhvec4 bmin( BVH_FAR ), bmax( -BVH_FAR ); + if (vertIdx) for (uint32_t first = node.leftFirst, j = 0; j < node.triCount; j++) + { + const uint32_t vidx = primIdx[first + j] * 3; + const uint32_t i0 = vertIdx[vidx], i1 = vertIdx[vidx + 1], i2 = vertIdx[vidx + 2]; + const bvhvec4 v0 = verts[i0], v1 = verts[i1], v2 = verts[i2]; + const bvhvec4 t1 = tinybvh_min( v0, bmin ), t2 = tinybvh_max( v0, bmax ); + const bvhvec4 t3 = tinybvh_min( v1, v2 ), t4 = tinybvh_max( v1, v2 ); + bmin = tinybvh_min( t1, t3 ), bmax = tinybvh_max( t2, t4 ); + } + else for (uint32_t first = node.leftFirst, j = 0; j < node.triCount; j++) + { + const uint32_t vidx = primIdx[first + j] * 3; + const bvhvec4 v0 = verts[vidx], v1 = verts[vidx + 1], v2 = verts[vidx + 2]; + const bvhvec4 t1 = tinybvh_min( v0, bmin ), t2 = tinybvh_max( v0, bmax ); + const bvhvec4 t3 = tinybvh_min( v1, v2 ), t4 = tinybvh_max( v1, v2 ); + bmin = tinybvh_min( t1, t3 ), bmax = tinybvh_max( t2, t4 ); + } + node.aabbMin = bmin, node.aabbMax = bmax; + continue; + } + // interior node: adjust to child bounds + const BVHNode& left = bvhNode[node.leftFirst], & right = bvhNode[node.leftFirst + 1]; + node.aabbMin = tinybvh_min( left.aabbMin, right.aabbMin ); + node.aabbMax = tinybvh_max( left.aabbMax, right.aabbMax ); + } + aabbMin = bvhNode[0].aabbMin, aabbMax = bvhNode[0].aabbMax; +} + +#define FIX_COMBINE_LEAFS 1 + +// CombineLeafs: Collapse subtrees if the summed leaf prim count does not +// exceed the specified number. For BVH8_CPU construction. +uint32_t BVH::CombineLeafs( const uint32_t primCount, uint32_t& firstIdx, uint32_t nodeIdx ) +{ + BVHNode& node = bvhNode[nodeIdx]; + if (node.isLeaf()) + { + firstIdx = node.leftFirst; + return node.triCount; + } + uint32_t firstLeft = 0, leftCount = CombineLeafs( primCount, firstLeft, node.leftFirst ); + uint32_t firstRight = 0, rightCount = CombineLeafs( primCount, firstRight, node.leftFirst + 1 ); + firstIdx = tinybvh_min( firstLeft, firstRight ); + if (leftCount + rightCount <= primCount) + node.triCount = leftCount + rightCount, + node.leftFirst = firstIdx; + return leftCount + rightCount; +} + +// CombineLeafs: Combine leaf nodes if this improves tree SAH cost. For HPLOC postprocessing. +void BVH::CombineLeafs( const uint32_t nodeIdx ) +{ + BVHNode& node = bvhNode[nodeIdx]; + if (node.isLeaf()) return; + BVHNode& left = bvhNode[node.leftFirst]; + BVHNode& right = bvhNode[node.leftFirst + 1]; + if (left.isLeaf() && right.isLeaf()) + { + int combinedCount = left.triCount + right.triCount; + float rAnode = 1.0f / tinybvh_half_area( node.aabbMax - node.aabbMin ); + float Cnode = c_int * combinedCount; + float Cleft = c_int * left.triCount * tinybvh_half_area( left.aabbMax - left.aabbMin ) * rAnode; + float Cright = c_int * right.triCount * tinybvh_half_area( right.aabbMax - right.aabbMin ) * rAnode; + float Csplit = Cleft + Cright + c_trav; + if (Cnode < Csplit) if (right.leftFirst == (left.leftFirst + left.triCount)) + node.leftFirst = left.leftFirst, + node.triCount = combinedCount; + return; + } + CombineLeafs( node.leftFirst ); + CombineLeafs( node.leftFirst + 1 ); +} + +bool BVH::IntersectSphere( const bvhvec3& pos, const float r ) const +{ + const bvhvec3 bmin = pos - bvhvec3( r ), bmax = pos + bvhvec3( r ); + BVHNode* node = &bvhNode[0], * stack[64]; + uint32_t stackPtr = 0; + const float r2 = r * r; + while (1) + { + if (node->isLeaf()) + { + // check if the leaf aabb overlaps the sphere: https://gamedev.stackexchange.com/a/156877 + float dist2 = 0; + if (pos.x < node->aabbMin.x) dist2 += (node->aabbMin.x - pos.x) * (node->aabbMin.x - pos.x); + if (pos.x > node->aabbMax.x) dist2 += (pos.x - node->aabbMax.x) * (pos.x - node->aabbMax.x); + if (pos.y < node->aabbMin.y) dist2 += (node->aabbMin.y - pos.y) * (node->aabbMin.y - pos.y); + if (pos.y > node->aabbMax.y) dist2 += (pos.y - node->aabbMax.y) * (pos.y - node->aabbMax.y); + if (pos.z < node->aabbMin.z) dist2 += (node->aabbMin.z - pos.z) * (node->aabbMin.z - pos.z); + if (pos.z > node->aabbMax.z) dist2 += (pos.z - node->aabbMax.z) * (pos.z - node->aabbMax.z); + if (dist2 <= r2) + { + // tri/sphere test: https://gist.github.com/yomotsu/d845f21e2e1eb49f647f#file-gistfile1-js-L223 + for (uint32_t i = 0; i < node->triCount; i++) + { + uint32_t idx = primIdx[node->leftFirst + i]; + bvhvec3 a, b, c; + if (!vertIdx) idx *= 3, a = verts[idx], b = verts[idx + 1], c = verts[idx + 2]; else + { + const uint32_t i0 = vertIdx[idx * 3], i1 = vertIdx[idx * 3 + 1], i2 = vertIdx[idx * 3 + 2]; + a = verts[i0], b = verts[i1], c = verts[i2]; + } + const bvhvec3 A = a - pos, B = b - pos, C = c - pos; + const float rr = r * r; + const bvhvec3 V = tinybvh_cross( B - A, C - A ); + const float d = tinybvh_dot( A, V ), e = tinybvh_dot( V, V ); + if (d * d > rr * e) continue; + const float aa = tinybvh_dot( A, A ), ab = tinybvh_dot( A, B ), ac = tinybvh_dot( A, C ); + const float bb = tinybvh_dot( B, B ), bc = tinybvh_dot( B, C ), cc = tinybvh_dot( C, C ); + if ((aa > rr && ab > aa && ac > aa) || (bb > rr && ab > bb && bc > bb) || + (cc > rr && ac > cc && bc > cc)) continue; + const bvhvec3 AB = B - A, BC = C - B, CA = A - C; + const float d1 = ab - aa, d2 = bc - bb, d3 = ac - cc; + const float e1 = tinybvh_dot( AB, AB ), e2 = tinybvh_dot( BC, BC ), e3 = tinybvh_dot( CA, CA ); + const bvhvec3 Q1 = A * e1 - AB * d1, Q2 = B * e2 - BC * d2, Q3 = C * e3 - CA * d3; + const bvhvec3 QC = C * e1 - Q1, QA = A * e2 - Q2, QB = B * e3 - Q3; + if ((tinybvh_dot( Q1, Q1 ) > rr * e1 * e1 && tinybvh_dot( Q1, QC ) >= 0) || + (tinybvh_dot( Q2, Q2 ) > rr * e2 * e2 && tinybvh_dot( Q2, QA ) >= 0) || + (tinybvh_dot( Q3, Q3 ) > rr * e3 * e3 && tinybvh_dot( Q3, QB ) >= 0)) continue; + // const float dist = sqrtf( d * d / e ) - r; // we're not using this. + return true; + } + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + BVHNode* child1 = &bvhNode[node->leftFirst], * child2 = &bvhNode[node->leftFirst + 1]; + bool hit1 = child1->Intersect( bmin, bmax ), hit2 = child2->Intersect( bmin, bmax ); + if (hit1 && hit2) stack[stackPtr++] = child2, node = child1; + else if (hit1) node = child1; else if (hit2) node = child2; + else { if (stackPtr == 0) break; node = stack[--stackPtr]; } + } + return false; +} + +#define SLAB_TEST_TWO_NODES \ + float tx1a = (posX ? child1->aabbMin.x : child1->aabbMax.x) * ray.rD.x - rox; /* expect fma. */ \ + float ty1a = (posY ? child1->aabbMin.y : child1->aabbMax.y) * ray.rD.y - roy; \ + float tz1a = (posZ ? child1->aabbMin.z : child1->aabbMax.z) * ray.rD.z - roz; \ + float tx1b = (posX ? child2->aabbMin.x : child2->aabbMax.x) * ray.rD.x - rox; \ + float ty1b = (posY ? child2->aabbMin.y : child2->aabbMax.y) * ray.rD.y - roy; \ + float tz1b = (posZ ? child2->aabbMin.z : child2->aabbMax.z) * ray.rD.z - roz; \ + float tx2a = (posX ? child1->aabbMax.x : child1->aabbMin.x) * ray.rD.x - rox; \ + float ty2a = (posY ? child1->aabbMax.y : child1->aabbMin.y) * ray.rD.y - roy; \ + float tz2a = (posZ ? child1->aabbMax.z : child1->aabbMin.z) * ray.rD.z - roz; \ + float tx2b = (posX ? child2->aabbMax.x : child2->aabbMin.x) * ray.rD.x - rox; \ + float ty2b = (posY ? child2->aabbMax.y : child2->aabbMin.y) * ray.rD.y - roy; \ + float tz2b = (posZ ? child2->aabbMax.z : child2->aabbMin.z) * ray.rD.z - roz; \ + float tmina = tinybvh_max( tinybvh_max( tx1a, ty1a ), tinybvh_max( tz1a, 0.0f ) ); \ + float tminb = tinybvh_max( tinybvh_max( tx1b, ty1b ), tinybvh_max( tz1b, 0.0f ) ); \ + float tmaxa = tinybvh_min( tinybvh_min( tx2a, ty2a ), tinybvh_min( tz2a, ray.hit.t ) ); \ + float tmaxb = tinybvh_min( tinybvh_min( tx2b, ty2b ), tinybvh_min( tz2b, ray.hit.t ) ); \ + if (tmaxa >= tmina) dist1 = tmina; \ + if (tmaxb >= tminb) dist2 = tminb; + +int32_t BVH::Intersect( Ray& ray ) const +{ + VALIDATE_RAY( ray ); + if (!isTLAS()) + { + const bool posX = ray.D.x >= 0, posY = ray.D.y >= 0, posZ = ray.D.z >= 0; + if (!posX) goto negx1; + if (posY) { if (posZ) return Intersect( ray ); else return Intersect( ray ); } + if (posZ) return Intersect( ray ); else return Intersect( ray ); + negx1: + if (posY) { if (posZ) return Intersect( ray ); else return Intersect( ray ); } + if (posZ) return Intersect( ray ); else return Intersect( ray ); + } + else + { + const bool posX = ray.D.x >= 0, posY = ray.D.y >= 0, posZ = ray.D.z >= 0; + if (!posX) goto negx2; + if (posY) { if (posZ) return IntersectTLAS( ray ); else return IntersectTLAS( ray ); } + if (posZ) return IntersectTLAS( ray ); else return IntersectTLAS( ray ); + negx2: + if (posY) { if (posZ) return IntersectTLAS( ray ); else return IntersectTLAS( ray ); } + if (posZ) return IntersectTLAS( ray ); else return IntersectTLAS( ray ); + } +} + +template int32_t BVH::Intersect( Ray& ray ) const +{ + BVHNode* node = &bvhNode[0], * stack[256]; + uint32_t stackPtr = 0; + float cost = 0; + const float rox = ray.O.x * ray.rD.x; + const float roy = ray.O.y * ray.rD.y; + const float roz = ray.O.z * ray.rD.z; + while (1) + { + cost += c_trav; + if (node->isLeaf()) + { + // Performance note: if indexed primitives (ENABLE_INDEXED_GEOMETRY) and custom + // geometry (ENABLE_CUSTOM_GEOMETRY) are both disabled, this leaf code reduces + // to a regular loop over triangles. Otherwise, the extra flexibility comes at + // a small performance cost. + if (indexedEnabled && vertIdx != 0) for (uint32_t i = 0; i < node->triCount; i++, cost += c_int) + { + const uint32_t pi = primIdx[node->leftFirst + i]; + const uint32_t i0 = vertIdx[pi * 3], i1 = vertIdx[pi * 3 + 1], i2 = vertIdx[pi * 3 + 2]; + IntersectTri( ray, pi, verts, i0, i1, i2 ); + } + else if (customEnabled && customIntersect != 0) for (uint32_t i = 0; i < node->triCount; i++, cost += c_int) + { + if ((*customIntersect)(ray, primIdx[node->leftFirst + i])) + { + #if INST_IDX_BITS == 32 + ray.hit.inst = ray.instIdx; + #else + ray.hit.prim = (ray.hit.prim & PRIM_IDX_MASK) + ray.instIdx; + #endif + } + } + else for (uint32_t i = 0; i < node->triCount; i++, cost += c_int) + { + const uint32_t pi = primIdx[node->leftFirst + i]; + IntersectTri( ray, pi, verts, pi * 3, pi * 3 + 1, pi * 3 + 2 ); + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + continue; + } + BVHNode* child1 = &bvhNode[node->leftFirst], * child2 = &bvhNode[node->leftFirst + 1]; + float dist1 = BVH_FAR, dist2 = BVH_FAR; + SLAB_TEST_TWO_NODES; + if (dist1 > dist2) { tinybvh_swap( dist1, dist2 ); tinybvh_swap( child1, child2 ); } + if (dist1 == BVH_FAR /* missed both child nodes */) + { + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + else /* hit at least one node */ + { + node = child1; /* continue with the nearest */ + if (dist2 != BVH_FAR) stack[stackPtr++] = child2; /* push far child */ + } + } + return (int32_t)cost; // cast to not break interface. +} + +template int32_t BVH::IntersectTLAS( Ray& ray ) const +{ + BVHNode* node = &bvhNode[0], * stack[64]; + uint32_t stackPtr = 0; + float cost = 0; + const float rox = ray.O.x * ray.rD.x; + const float roy = ray.O.y * ray.rD.y; + const float roz = ray.O.z * ray.rD.z; + while (1) + { + cost += c_trav; + if (node->isLeaf()) + { + Ray tmp; + for (uint32_t i = 0; i < node->triCount; i++) + { + // BLAS traversal + const uint32_t instIdx = primIdx[node->leftFirst + i]; + const BLASInstance& inst = instList[instIdx]; + // Check if the ray should intersect this BLAS Instance, otherwise skip it + if (!(inst.mask & ray.mask)) continue; + const BVHBase* blas = blasList[inst.blasIdx]; + // 1. Transform ray with the inverse of the instance transform + tmp.O = tinybvh_transform_point( ray.O, inst.invTransform ); + tmp.D = tinybvh_transform_vector( ray.D, inst.invTransform ); + tmp.instIdx = instIdx << (32 - INST_IDX_BITS); + tmp.hit = ray.hit; + tmp.rD = tinybvh_rcp( tmp.D ); + // 2. Traverse BLAS with the transformed ray + // Note: Valid BVH layout options for BLASses are the regular BVH layout, + // the AVX-optimized BVH_SOA layout and the wide BVH4_CPU layout. When all + // BLASses are of the same layout this reduces to nearly zero cost for + // a small set of predictable branches. + assert( blas->layout == LAYOUT_BVH || blas->layout == LAYOUT_BVH4_CPU || + blas->layout == LAYOUT_BVH_SOA || blas->layout == LAYOUT_BVH8_AVX2 ); + if (blas->layout == LAYOUT_BVH) + { + // regular (triangle) BVH traversal + cost += ((BVH*)blas)->Intersect( tmp ); + } + else + { + #ifdef BVH_USESSE + if (blas->layout == LAYOUT_BVH4_CPU) cost += ((BVH4_CPU*)blas)->Intersect( tmp ); + #endif + #ifdef BVH_USEAVX + if (blas->layout == LAYOUT_BVH_SOA) cost += ((BVH_SoA*)blas)->Intersect( tmp ); + #endif + #ifdef BVH_USEAVX2 + if (blas->layout == LAYOUT_BVH8_AVX2) cost += ((BVH8_CPU*)blas)->Intersect( tmp ); + #endif + if (blas->layout == LAYOUT_VOXELSET) cost += ((VoxelSet*)blas)->Intersect( tmp ); + } + // 3. Restore ray + ray.hit = tmp.hit; + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + continue; + } + BVHNode* child1 = &bvhNode[node->leftFirst], * child2 = &bvhNode[node->leftFirst + 1]; + float dist1 = BVH_FAR, dist2 = BVH_FAR; + SLAB_TEST_TWO_NODES; + if (dist1 > dist2) { tinybvh_swap( dist1, dist2 ); tinybvh_swap( child1, child2 ); } + if (dist1 == BVH_FAR /* missed both child nodes */) + { + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + else /* hit at least one node */ + { + node = child1; /* continue with the nearest */ + if (dist2 != BVH_FAR) stack[stackPtr++] = child2; /* push far child */ + } + } + return (int32_t)cost; +} + +bool BVH::IsOccluded( const Ray& ray ) const +{ + VALIDATE_RAY( ray ); + if (!isTLAS()) + { + const bool posX = ray.D.x >= 0, posY = ray.D.y >= 0, posZ = ray.D.z >= 0; + if (!posX) goto negx1; + if (posY) { if (posZ) return IsOccluded( ray ); else return IsOccluded( ray ); } + if (posZ) return IsOccluded( ray ); else return IsOccluded( ray ); + negx1: + if (posY) { if (posZ) return IsOccluded( ray ); else return IsOccluded( ray ); } + if (posZ) return IsOccluded( ray ); else return IsOccluded( ray ); + } + else + { + const bool posX = ray.D.x >= 0, posY = ray.D.y >= 0, posZ = ray.D.z >= 0; + if (!posX) goto negx2; + if (posY) { if (posZ) return IsOccludedTLAS( ray ); else return IsOccludedTLAS( ray ); } + if (posZ) return IsOccludedTLAS( ray ); else return IsOccludedTLAS( ray ); + negx2: + if (posY) { if (posZ) return IsOccludedTLAS( ray ); else return IsOccludedTLAS( ray ); } + if (posZ) return IsOccludedTLAS( ray ); else return IsOccludedTLAS( ray ); + } +} + +template bool BVH::IsOccluded( const Ray& ray ) const +{ + BVHNode* node = &bvhNode[0], * stack[64]; + uint32_t stackPtr = 0; + const float rox = ray.O.x * ray.rD.x; + const float roy = ray.O.y * ray.rD.y; + const float roz = ray.O.z * ray.rD.z; + while (1) + { + if (node->isLeaf()) + { + if (indexedEnabled && vertIdx != 0) for (uint32_t i = 0; i < node->triCount; i++) + { + const uint32_t pi = primIdx[node->leftFirst + i], vi0 = pi * 3; + const uint32_t i0 = vertIdx[vi0], i1 = vertIdx[vi0 + 1], i2 = vertIdx[vi0 + 2]; + if (TriOccludes( ray, verts, pi, i0, i1, i2 )) return true; + } + else if (customEnabled && customIsOccluded != 0) + { + for (uint32_t i = 0; i < node->triCount; i++) + if ((*customIsOccluded)(ray, primIdx[node->leftFirst + i])) return true; + } + else for (uint32_t i = 0; i < node->triCount; i++) + { + const uint32_t pi = primIdx[node->leftFirst + i], vi0 = pi * 3; + if (TriOccludes( ray, verts, pi, vi0, vi0 + 1, vi0 + 2 )) return true; + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + continue; + } + BVHNode* child1 = &bvhNode[node->leftFirst]; + BVHNode* child2 = &bvhNode[node->leftFirst + 1]; + float dist1 = BVH_FAR, dist2 = BVH_FAR; + SLAB_TEST_TWO_NODES; + if (dist1 > dist2) { tinybvh_swap( dist1, dist2 ); tinybvh_swap( child1, child2 ); } + if (dist1 == BVH_FAR /* missed both child nodes */) + { + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + else /* hit at least one node */ + { + node = child1; /* continue with the nearest */ + if (dist2 != BVH_FAR) stack[stackPtr++] = child2; /* push far child */ + } + } + return false; +} + +template bool BVH::IsOccludedTLAS( const Ray& ray ) const +{ + BVHNode* node = &bvhNode[0], * stack[64]; + uint32_t stackPtr = 0; + Ray tmp; + const float rox = ray.O.x * ray.rD.x; + const float roy = ray.O.y * ray.rD.y; + const float roz = ray.O.z * ray.rD.z; + while (1) + { + if (node->isLeaf()) + { + for (uint32_t i = 0; i < node->triCount; i++) + { + // BLAS traversal + BLASInstance& inst = instList[primIdx[node->leftFirst + i]]; + const BVHBase* blas = blasList[inst.blasIdx]; + // Check if the ray should intersect this BLAS Instance, otherwise skip it + if (!(inst.mask & ray.mask)) continue; + // 1. Transform ray with the inverse of the instance transform + tmp.O = tinybvh_transform_point( ray.O, inst.invTransform ); + tmp.D = tinybvh_transform_vector( ray.D, inst.invTransform ); + tmp.hit.t = ray.hit.t; + tmp.rD = tinybvh_rcp( tmp.D ); + // 2. Traverse BLAS with the transformed ray + assert( blas->layout == LAYOUT_BVH || blas->layout == LAYOUT_BVH_SOA || + blas->layout == LAYOUT_BVH8_AVX2 || blas->layout == LAYOUT_BVH4_CPU ); + if (blas->layout == LAYOUT_BVH) + { + // regular (triangle) BVH traversal + if (((BVH*)blas)->IsOccluded( tmp )) return true; + } + else + { + #ifdef BVH_USESSE + if (blas->layout == LAYOUT_BVH4_CPU) { if (((BVH4_CPU*)blas)->IsOccluded( tmp )) return true; } + #endif + #ifdef BVH_USEAVX + if (blas->layout == LAYOUT_BVH_SOA) { if (((BVH_SoA*)blas)->IsOccluded( tmp )) return true; } + #endif + #ifdef BVH_USEAVX2 + if (blas->layout == LAYOUT_BVH8_AVX2) { if (((BVH8_CPU*)blas)->IsOccluded( tmp )) return true; } + #endif + if (blas->layout == LAYOUT_VOXELSET) { if (((VoxelSet*)blas)->IsOccluded( tmp )) return true; } + } + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + continue; + } + BVHNode* child1 = &bvhNode[node->leftFirst], * child2 = &bvhNode[node->leftFirst + 1]; + float dist1 = BVH_FAR, dist2 = BVH_FAR; + SLAB_TEST_TWO_NODES; + if (dist1 > dist2) { tinybvh_swap( dist1, dist2 ); tinybvh_swap( child1, child2 ); } + if (dist1 == BVH_FAR /* missed both child nodes */) + { + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + else /* hit at least one node */ + { + node = child1; /* continue with the nearest */ + if (dist2 != BVH_FAR) stack[stackPtr++] = child2; /* push far child */ + } + } + return false; +} + +// Intersect a WALD_32BYTE BVH with a ray packet. +// The 256 rays travel together to better utilize the caches and to amortize the cost +// of memory transfers over the rays in the bundle. +// Note that this basic implementation assumes a specific layout of the rays. Provided +// as 'proof of concept', should not be used in production code. +// Based on Large Ray Packets for Real-time Whitted Ray Tracing, Overbeck et al., 2008, +// extended with sorted traversal and reduced stack traffic. +void BVH::Intersect256Rays( Ray* packet ) const +{ + // convenience macro +#define CALC_TMIN_TMAX_WITH_SLABTEST_ON_RAY( r ) const bvhvec3 rD = packet[r].rD, t1 = o1 * rD, t2 = o2 * rD; \ + const float tmin = tinybvh_max( tinybvh_max( tinybvh_min( t1.x, t2.x ), tinybvh_min( t1.y, t2.y ) ), tinybvh_min( t1.z, t2.z ) ); \ + const float tmax = tinybvh_min( tinybvh_min( tinybvh_max( t1.x, t2.x ), tinybvh_max( t1.y, t2.y ) ), tinybvh_max( t1.z, t2.z ) ); + // Corner rays are: 0, 51, 204 and 255 + // Construct the bounding planes, with normals pointing outwards + const bvhvec3 O = packet[0].O; // same for all rays in this case + const bvhvec3 p0 = packet[0].O + packet[0].D; // top-left + const bvhvec3 p1 = packet[51].O + packet[51].D; // top-right + const bvhvec3 p2 = packet[204].O + packet[204].D; // bottom-left + const bvhvec3 p3 = packet[255].O + packet[255].D; // bottom-right + const bvhvec3 plane0 = tinybvh_normalize( tinybvh_cross( p0 - O, p0 - p2 ) ); // left plane + const bvhvec3 plane1 = tinybvh_normalize( tinybvh_cross( p3 - O, p3 - p1 ) ); // right plane + const bvhvec3 plane2 = tinybvh_normalize( tinybvh_cross( p1 - O, p1 - p0 ) ); // top plane + const bvhvec3 plane3 = tinybvh_normalize( tinybvh_cross( p2 - O, p2 - p3 ) ); // bottom plane + const int32_t sign0x = plane0.x < 0 ? 4 : 0, sign0y = plane0.y < 0 ? 5 : 1, sign0z = plane0.z < 0 ? 6 : 2; + const int32_t sign1x = plane1.x < 0 ? 4 : 0, sign1y = plane1.y < 0 ? 5 : 1, sign1z = plane1.z < 0 ? 6 : 2; + const int32_t sign2x = plane2.x < 0 ? 4 : 0, sign2y = plane2.y < 0 ? 5 : 1, sign2z = plane2.z < 0 ? 6 : 2; + const int32_t sign3x = plane3.x < 0 ? 4 : 0, sign3y = plane3.y < 0 ? 5 : 1, sign3z = plane3.z < 0 ? 6 : 2; + const float d0 = tinybvh_dot( O, plane0 ), d1 = tinybvh_dot( O, plane1 ); + const float d2 = tinybvh_dot( O, plane2 ), d3 = tinybvh_dot( O, plane3 ); + // Traverse the tree with the packet + int32_t first = 0, last = 255; // first and last active ray in the packet + const BVHNode* node = &bvhNode[0]; + ALIGNED( 64 ) uint32_t stack[64], stackPtr = 0; + while (1) + { + if (node->isLeaf()) + { + // handle leaf node + for (uint32_t j = 0; j < node->triCount; j++) + { + const uint32_t idx = primIdx[node->leftFirst + j], vid = idx * 3; + const bvhvec3 e1 = verts[vid + 1] - verts[vid], e2 = verts[vid + 2] - verts[vid]; + const bvhvec3 s = O - bvhvec3( verts[vid] ); + for (int32_t i = first; i <= last; i++) + { + Ray& ray = packet[i]; + const bvhvec3 h = tinybvh_cross( ray.D, e2 ); + const float a = tinybvh_dot( e1, h ); + if (fabs( a ) < 0.0000001f) continue; // ray parallel to triangle + const float f = 1 / a, u = f * tinybvh_dot( s, h ); + const bvhvec3 q = tinybvh_cross( s, e1 ); + const float v = f * tinybvh_dot( ray.D, q ); + if (u < 0 || v < 0 || u + v > 1) continue; + const float t = f * tinybvh_dot( e2, q ); + if (t <= 0 || t >= ray.hit.t) continue; + ray.hit.t = t, ray.hit.u = u, ray.hit.v = v; + #if INST_IDX_BITS == 32 + ray.hit.prim = idx, ray.hit.inst = ray.instIdx; + #else + ray.hit.prim = idx + ray.instIdx; + #endif + } + } + if (stackPtr == 0) break; else // pop + last = stack[--stackPtr], node = bvhNode + stack[--stackPtr], + first = last >> 8, last &= 255; + } + else + { + // fetch pointers to child nodes + const BVHNode* left = bvhNode + node->leftFirst; + const BVHNode* right = bvhNode + node->leftFirst + 1; + bool visitLeft = true, visitRight = true; + int32_t leftFirst = first, leftLast = last, rightFirst = first, rightLast = last; + float distLeft, distRight; + { + // see if we want to intersect the left child + const bvhvec3 o1( left->aabbMin.x - O.x, left->aabbMin.y - O.y, left->aabbMin.z - O.z ); + const bvhvec3 o2( left->aabbMax.x - O.x, left->aabbMax.y - O.y, left->aabbMax.z - O.z ); + // 1. Early-in test: if first ray hits the node, the packet visits the node + bool earlyHit; + { + CALC_TMIN_TMAX_WITH_SLABTEST_ON_RAY( first ); + earlyHit = (tmax >= tmin && tmin < packet[first].hit.t && tmax >= 0); + distLeft = tmin; + } + if (!earlyHit) // 2. Early-out test: if the node aabb is outside the four planes, we skip the node + { + float* minmax = (float*)left; + bvhvec3 c0( minmax[sign0x], minmax[sign0y], minmax[sign0z] ); + bvhvec3 c1( minmax[sign1x], minmax[sign1y], minmax[sign1z] ); + bvhvec3 c2( minmax[sign2x], minmax[sign2y], minmax[sign2z] ); + bvhvec3 c3( minmax[sign3x], minmax[sign3y], minmax[sign3z] ); + if (tinybvh_dot( c0, plane0 ) > d0 || tinybvh_dot( c1, plane1 ) > d1 || + tinybvh_dot( c2, plane2 ) > d2 || tinybvh_dot( c3, plane3 ) > d3) + visitLeft = false; + else // 3. Last resort: update first and last, stay in node if first > last + { + for (; leftFirst <= leftLast; leftFirst++) + { + CALC_TMIN_TMAX_WITH_SLABTEST_ON_RAY( leftFirst ); + if (tmax >= tmin && tmin < packet[leftFirst].hit.t && tmax >= 0) { distLeft = tmin; break; } + } + for (; leftLast >= leftFirst; leftLast--) + { + CALC_TMIN_TMAX_WITH_SLABTEST_ON_RAY( leftLast ); + if (tmax >= tmin && tmin < packet[leftLast].hit.t && tmax >= 0) break; + } + visitLeft = leftLast >= leftFirst; + } + } + } + { + // see if we want to intersect the right child + const bvhvec3 o1( right->aabbMin.x - O.x, right->aabbMin.y - O.y, right->aabbMin.z - O.z ); + const bvhvec3 o2( right->aabbMax.x - O.x, right->aabbMax.y - O.y, right->aabbMax.z - O.z ); + // 1. Early-in test: if first ray hits the node, the packet visits the node + bool earlyHit; + { + CALC_TMIN_TMAX_WITH_SLABTEST_ON_RAY( first ); + earlyHit = (tmax >= tmin && tmin < packet[first].hit.t && tmax >= 0); + distRight = tmin; + } + if (!earlyHit) // 2. Early-out test: if the node aabb is outside the four planes, we skip the node + { + float* minmax = (float*)right; + bvhvec3 c0( minmax[sign0x], minmax[sign0y], minmax[sign0z] ); + bvhvec3 c1( minmax[sign1x], minmax[sign1y], minmax[sign1z] ); + bvhvec3 c2( minmax[sign2x], minmax[sign2y], minmax[sign2z] ); + bvhvec3 c3( minmax[sign3x], minmax[sign3y], minmax[sign3z] ); + if (tinybvh_dot( c0, plane0 ) > d0 || tinybvh_dot( c1, plane1 ) > d1 || + tinybvh_dot( c2, plane2 ) > d2 || tinybvh_dot( c3, plane3 ) > d3) + visitRight = false; + else // 3. Last resort: update first and last, stay in node if first > last + { + for (; rightFirst <= rightLast; rightFirst++) + { + CALC_TMIN_TMAX_WITH_SLABTEST_ON_RAY( rightFirst ); + if (tmax >= tmin && tmin < packet[rightFirst].hit.t && tmax >= 0) { distRight = tmin; break; } + } + for (; rightLast >= first; rightLast--) + { + CALC_TMIN_TMAX_WITH_SLABTEST_ON_RAY( rightLast ); + if (tmax >= tmin && tmin < packet[rightLast].hit.t && tmax >= 0) break; + } + visitRight = rightLast >= rightFirst; + } + } + } + // process intersection result + if (visitLeft && visitRight) + { + if (distLeft < distRight) // push right, continue with left + { + stack[stackPtr++] = node->leftFirst + 1; + stack[stackPtr++] = (rightFirst << 8) + rightLast; + node = left, first = leftFirst, last = leftLast; + } + else // push left, continue with right + { + stack[stackPtr++] = node->leftFirst; + stack[stackPtr++] = (leftFirst << 8) + leftLast; + node = right, first = rightFirst, last = rightLast; + } + } + else if (visitLeft) // continue with left + node = left, first = leftFirst, last = leftLast; + else if (visitRight) // continue with right + node = right, first = rightFirst, last = rightLast; + else if (stackPtr == 0) break; else // pop + last = stack[--stackPtr], node = bvhNode + stack[--stackPtr], + first = last >> 8, last &= 255; + } + } +} + +int32_t BVH::NodeCount() const +{ + // Determine the number of nodes in the tree. Typically the result should + // be usedNodes - 1 (second node is always unused), but some builders may + // have unused nodes besides node 1. TODO: Support more layouts. + uint32_t retVal = 0, nodeIdx = 0, stack[64], stackPtr = 0; + while (1) + { + const BVHNode& n = bvhNode[nodeIdx]; + retVal++; + if (n.isLeaf()) { if (stackPtr == 0) break; else nodeIdx = stack[--stackPtr]; } + else nodeIdx = n.leftFirst, stack[stackPtr++] = n.leftFirst + 1; + } + return retVal; +} + +int32_t BVH::LeafCount() const +{ + // Determine the number of nodes in the tree. Typically the result should + // be usedNodes - 1 (second node is always unused), but some builders may + // have unused nodes besides node 1. TODO: Support more layouts. + uint32_t retVal = 0, nodeIdx = 0, stack[64], stackPtr = 0; + while (1) + { + const BVHNode& n = bvhNode[nodeIdx]; + if (n.isLeaf()) { retVal++; if (stackPtr == 0) break; else nodeIdx = stack[--stackPtr]; } + else nodeIdx = n.leftFirst, stack[stackPtr++] = n.leftFirst + 1; + } + return retVal; +} + +// Compact: Reduce the size of a BVH by removing any unused nodes. +// This is useful after an SBVH build or multi-threaded build, but also after +// calling MergeLeafs. Some operations, such as Optimize, *require* a +// compacted tree to work correctly. +void BVH::Compact() +{ + BVH_FATAL_ERROR_IF( bvhNode == 0, "BVH::Compact(), bvhNode == 0." ); + if (bvhNode[0].isLeaf()) return; // nothing to compact. + BVHNode* tmp = (BVHNode*)AlignedAlloc( sizeof( BVHNode ) * allocatedNodes /* do *not* trim */ ); + uint32_t* idx = (uint32_t*)AlignedAlloc( sizeof( uint32_t ) * idxCount ); + memcpy( tmp, bvhNode, 2 * sizeof( BVHNode ) ); + newNodePtr = 2; + uint32_t newIdxPtr = 0; + uint32_t nodeIdx = 0, stack[128], stackPtr = 0; + while (1) + { + BVHNode& node = tmp[nodeIdx]; + if (node.isLeaf()) + { + const uint32_t leafStart = newIdxPtr; + for (uint32_t i = 0; i < node.triCount; i++) idx[newIdxPtr++] = primIdx[node.leftFirst + i]; + node.leftFirst = leafStart; + if (!stackPtr) break; + nodeIdx = stack[--stackPtr]; + } + else + { + const BVHNode& left = bvhNode[node.leftFirst]; + const BVHNode& right = bvhNode[node.leftFirst + 1]; + tmp[newNodePtr] = left, tmp[newNodePtr + 1] = right; + const uint32_t todo1 = newNodePtr, todo2 = newNodePtr + 1; + node.leftFirst = newNodePtr, newNodePtr += 2; + nodeIdx = todo1; + stack[stackPtr++] = todo2; + } + } + usedNodes = newNodePtr; + AlignedFree( bvhNode ); + AlignedFree( primIdx ); + bvhNode = tmp; + primIdx = idx; +} + +// VoxelSet implementation +// ---------------------------------------------------------------------------- + +VoxelSet::VoxelSet() +{ + grid = (uint32_t*)AlignedAlloc( gridSize * sizeof( uint32_t ) ); + memset( grid, 0, gridSize * sizeof( uint32_t ) ); + brick = (uint32_t*)AlignedAlloc( brickSize * brickCount * sizeof( uint32_t ) ); + memset( brick, 0, brickSize * brickCount * sizeof( uint32_t ) ); + topGrid = (uint32_t*)AlignedAlloc( topGridSize / 8 ); + freeBrickPtr = 1; // first available brick; we'll skip 0 + aabbMin = bvhvec3( 0 ), aabbMax = bvhvec3( 1 ); // a voxel object is always (1,1,1) in object space. +} + +void VoxelSet::Set( const uint32_t x, const uint32_t y, const uint32_t z, const uint32_t v ) +{ + // note: not thread-safe. + const uint32_t bx = x / brickDim, by = y / brickDim, bz = z / brickDim; + const uint32_t gridIdx = bx + by * gridDim + bz * gridDim * gridDim; + uint32_t brickIdx = grid[gridIdx]; + if (!brickIdx) + { + if (freeBrickPtr == brickCount) // we ran out; reallocate + { + uint32_t newBrickCount = brickCount + (brickCount >> 2); + uint32_t* newBrickPool = (uint32_t*)AlignedAlloc( newBrickCount * brickSize * sizeof( uint32_t ) ); + memcpy( newBrickPool, brick, brickCount * brickSize * sizeof( uint32_t ) ); + memset( newBrickPool + brickCount * brickSize, 0, (newBrickCount - brickCount) * brickSize * sizeof( uint32_t ) ); + AlignedFree( brick ); + brick = newBrickPool, brickCount = newBrickCount; + } + brickIdx = grid[gridIdx] = freeBrickPtr++; + } + const uint32_t voxelIdx = (x & (brickDim - 1)) + (y & (brickDim - 1)) * brickDim + (z & (brickDim - 1)) * brickDim * brickDim; + brick[brickIdx * brickSize + voxelIdx] = v; +} + +void VoxelSet::UpdateTopGrid() +{ + memset( topGrid, 0, topGridSize / 8 ); + for (int x = 0; x < topGridDim; x++) for (int y = 0; y < topGridDim; y++) for (int z = 0; z < topGridDim; z++) + { + uint32_t* gridBase = grid + x * groupDim + y * groupDim * gridDim + z * groupDim * gridDim * gridDim; + bool hasContent = false; + for (int u = 0; u < groupDim; u++) for (int v = 0; v < groupDim; v++) for (int w = 0; w < groupDim; w++) + if (gridBase[u + v * gridDim + w * gridDim * gridDim]) + { + hasContent = true; + goto break3; + } + break3: + if (!hasContent) continue; + uint32_t topIdx = x + y * topGridDim + z * topGridDim * topGridDim; + topGrid[topIdx >> 5] |= 1 << (topIdx & 31); + } +} + +bool VoxelSet::Setup3DDDA( const Ray& ray, const bvhvec3& Dsign, DDAState& state, const bvhint3& step, bvhvec3& tdelta, float& t ) const +{ + // if ray is not inside the object aabb: advance until it is + if (!(ray.O.x >= 0 && ray.O.x <= 1 && ray.O.y >= 0 && ray.O.y <= 1 && ray.O.z >= 0 && ray.O.z <= 1)) + { + float tx1 = -ray.O.x * ray.rD.x, tx2 = (1 - ray.O.x) * ray.rD.x; + float tmin = tinybvh_min( tx1, tx2 ), tmax = tinybvh_max( tx1, tx2 ); + float ty1 = -ray.O.y * ray.rD.y, ty2 = (1 - ray.O.y) * ray.rD.y; + tmin = tinybvh_max( tmin, tinybvh_min( ty1, ty2 ) ); + tmax = tinybvh_min( tmax, tinybvh_max( ty1, ty2 ) ); + float tz1 = -ray.O.z * ray.rD.z, tz2 = (1 - ray.O.z) * ray.rD.z; + tmin = tinybvh_max( tmin, tinybvh_min( tz1, tz2 ) ); + tmax = tinybvh_min( tmax, tinybvh_max( tz1, tz2 ) ); + if (tmax < tmin || tmin > ray.hit.t || tmax < 0) return false; else t = tmin; + } + // setup amanatides & woo - assume object size is 1x1x1, from (0,0,0) to (1,1,1) + static const float cellSize = 1.0f / topGridDim; + const bvhvec3 posInGrid = (ray.O + ray.D * (t + 0.0000025f)) * (float)topGridDim; + const bvhvec3 gridPlanes = (bvhvec3( ceilf( posInGrid.x ), ceilf( posInGrid.y ), ceilf( posInGrid.z ) ) - Dsign) * cellSize; + const bvhint3 P( + tinybvh_clamp( (int)posInGrid.x, 0, topGridDim - 1 ), + tinybvh_clamp( (int)posInGrid.y, 0, topGridDim - 1 ), + tinybvh_clamp( (int)posInGrid.z, 0, topGridDim - 1 ) + ); + state.X = P.x, state.Y = P.y, state.Z = P.z; + state.tmax = (gridPlanes - ray.O) * ray.rD; + tdelta = bvhvec3( (float)step.x, (float)step.y, (float)step.z ) * cellSize * ray.rD; + // proceed with traversal + return true; +} + +bvhvec3 VoxelSet::GetNormal( const Ray& ray ) const +{ + const bvhvec3 I1 = (ray.O + ray.hit.t * ray.D) * (float)objectDim; // our object is (1,1,1) in object space, so this scales each voxel to (1,1,1) + const bvhvec3 fG( I1.x - floorf( I1.x ), I1.y - floorf( I1.y ), I1.z - floorf( I1.z ) ); + const bvhvec3 d = tinybvh_min( fG, 1.0f - fG ); + const float mind = tinybvh_min( tinybvh_min( d.x, d.y ), d.z ); + if (mind == d.x) return bvhvec3( ray.D.x > 0 ? -1.0f : 1.0f, 0, 0 ); + else if (mind == d.y) return bvhvec3( 0, ray.D.y > 0 ? -1.0f : 1.0f, 0 ); + else return bvhvec3( 0, 0, ray.D.z > 0 ? -1.0f : 1.0f ); +} + +int32_t VoxelSet::Intersect( Ray& ray ) const +{ + // setup Amanatides & Woo grid traversal + ALIGNED( 64 ) DDAState l1_, l2_; + const uint32_t xsign = *(uint32_t*)&ray.D.x >> 31; + const uint32_t ysign = *(uint32_t*)&ray.D.y >> 31; + const uint32_t zsign = *(uint32_t*)&ray.D.z >> 31; + const bvhvec3 Dsign = bvhvec3( (float)xsign, (float)ysign, (float)zsign ); + const bvhint3 step( 1 - (int)xsign * 2, 1 - (int)ysign * 2, 1 - (int)zsign * 2 ); + bvhvec3 tdelta; + float t = 0; + if (!Setup3DDDA( ray, Dsign, l1_, step, tdelta, t )) return 0; + const bvhvec3 l2tdelta = tdelta * (1.0f / groupDim); + const bvhvec3 l3tdelta = l2tdelta * (1.0f / brickDim); + uint32_t* gridBase = 0; + int32_t steps = 0; + // start stepping: + while (1) + { + const uint32_t tidx = l1_.X + l1_.Y * topGridDim + l1_.Z * topGridDim * topGridDim; + const uint32_t cell = topGrid[tidx >> 5] & (1 << (tidx & 31)); + if (cell) + { + // setup midlevel traversal + const bvhvec3 posInGrid = (ray.O + (t + 0.0000025f) * ray.D) * (float)gridDim; + const bvhvec3 gridPlanes = (bvhvec3( ceilf( posInGrid.x ), ceilf( posInGrid.y ), ceilf( posInGrid.z ) ) - Dsign) * (1.0f / gridDim); + l2_.X = tinybvh_clamp( (int)posInGrid.x, l1_.X * groupDim, l1_.X * groupDim + (groupDim - 1) ); + l2_.Y = tinybvh_clamp( (int)posInGrid.y, l1_.Y * groupDim, l1_.Y * groupDim + (groupDim - 1) ); + l2_.Z = tinybvh_clamp( (int)posInGrid.z, l1_.Z * groupDim, l1_.Z * groupDim + (groupDim - 1) ); + l2_.tmax = (gridPlanes - ray.O) * ray.rD; + gridBase = grid + ((l2_.X + l2_.Y * gridDim + l2_.Z * gridDim * gridDim) & superMask); + l2_.X &= groupDim - 1, l2_.Y &= groupDim - 1, l2_.Z &= groupDim - 1; + // step through midlevel cells + while (1) + { + const uint32_t brickCell = gridBase[l2_.X + l2_.Y * gridDim + l2_.Z * gridDim * gridDim]; + if (brickCell) + { + // setup 3DDDA for brick traversal + uint32_t* brickData = brick + brickCell * brickSize; + const bvhvec3 posInBrick = (ray.O + (t + 0.0000025f) * ray.D) * (float)objectDim; + uint32_t X = tinybvh_clamp( (int)posInBrick.x - (l2_.X + l1_.X * groupDim) * brickDim, 0, brickDim - 1 ); + uint32_t Y = tinybvh_clamp( (int)posInBrick.y - (l2_.Y + l1_.Y * groupDim) * brickDim, 0, brickDim - 1 ); + uint32_t Z = tinybvh_clamp( (int)posInBrick.z - (l2_.Z + l1_.Z * groupDim) * brickDim, 0, brickDim - 1 ); + const bvhvec3 brickPlanes = (bvhvec3( ceilf( posInBrick.x ), ceilf( posInBrick.y ), ceilf( posInBrick.z ) ) - Dsign) * (1.0f / objectDim); + bvhvec3 tmax = (brickPlanes - ray.O) * ray.rD; + // step through brick + while (1) + { + steps++; + const uint32_t v = brickData[X + Y * brickDim + Z * brickDim * brickDim]; + if (v) + { + ray.hit.t = t; + #if INST_IDX_BITS == 32 + ray.hit.prim = v; + ray.hit.inst = ray.instIdx; // store in dedicated field + #elif INST_IDX_BITS == 8 + ray.hit.prim = v + (ray.instIdx << 24); // store in alpha + #else + ray.hit.prim = v; + ray.hit.u = *(float*)&ray.instIdx; // store in u; hack + #endif + return steps; + } + if (tmax.x < tmax.y) + { + if (tmax.x < tmax.z) + { + if ((X += step.x) >= brickDim) break; + t = tmax.x, tmax.x += l3tdelta.x; + } + else + { + if ((Z += step.z) >= brickDim) break; + t = tmax.z, tmax.z += l3tdelta.z; + } + } + else + { + if (tmax.y < tmax.z) + { + if ((Y += step.y) >= brickDim) break; + t = tmax.y, tmax.y += l3tdelta.y; + } + else + { + if ((Z += step.z) >= brickDim) break; + t = tmax.z, tmax.z += l3tdelta.z; + } + } + } + } + if (l2_.tmax.x < l2_.tmax.y) + { + if (l2_.tmax.x < l2_.tmax.z) + { + if ((l2_.X += step.x) >= groupDim) break; + t = l2_.tmax.x, l2_.tmax.x += l2tdelta.x; + } + else + { + if ((l2_.Z += step.z) >= groupDim) break; + t = l2_.tmax.z, l2_.tmax.z += l2tdelta.z; + } + } + else + { + if (l2_.tmax.y < l2_.tmax.z) + { + if ((l2_.Y += step.y) >= groupDim) break; + t = l2_.tmax.y, l2_.tmax.y += l2tdelta.y; + } + else + { + if ((l2_.Z += step.z) >= groupDim) break; + t = l2_.tmax.z, l2_.tmax.z += l2tdelta.z; + } + } + } + } + if (l1_.tmax.x < l1_.tmax.y) + { + if (l1_.tmax.x < l1_.tmax.z) + { + if ((l1_.X += step.x) >= topGridDim) break; + t = l1_.tmax.x, l1_.tmax.x += tdelta.x; + } + else + { + if ((l1_.Z += step.z) >= topGridDim) break; + t = l1_.tmax.z, l1_.tmax.z += tdelta.z; + } + } + else + { + if (l1_.tmax.y < l1_.tmax.z) + { + if ((l1_.Y += step.y) >= topGridDim) break; + t = l1_.tmax.y, l1_.tmax.y += tdelta.y; + } + else + { + if ((l1_.Z += step.z) >= topGridDim) break; + t = l1_.tmax.z, l1_.tmax.z += tdelta.z; + } + } + } + return 0; +} + +bool VoxelSet::IsOccluded( const Ray& ray ) const +{ + // setup Amanatides & Woo grid traversal + ALIGNED( 64 ) DDAState l1_, l2_; + const uint32_t xsign = *(uint32_t*)&ray.D.x >> 31; + const uint32_t ysign = *(uint32_t*)&ray.D.y >> 31; + const uint32_t zsign = *(uint32_t*)&ray.D.z >> 31; + const bvhvec3 Dsign = bvhvec3( (float)xsign, (float)ysign, (float)zsign ); + const bvhint3 step( 1 - (int)xsign * 2, 1 - (int)ysign * 2, 1 - (int)zsign * 2 ); + bvhvec3 tdelta; + float t = 0; + if (!Setup3DDDA( ray, Dsign, l1_, step, tdelta, t )) return false; + const bvhvec3 l2tdelta = tdelta * (1.0f / groupDim); + const bvhvec3 l3tdelta = l2tdelta * (1.0f / brickDim); + uint32_t* gridBase = 0; + // start stepping: + while (t < ray.hit.t) + { + const uint32_t tidx = l1_.X + l1_.Y * topGridDim + l1_.Z * topGridDim * topGridDim; + const uint32_t cell = topGrid[tidx >> 5] & (1 << (tidx & 31)); + if (cell) + { + // setup midlevel traversal + const bvhvec3 posInGrid = (ray.O + (t + 0.0000025f) * ray.D) * (float)gridDim; + const bvhvec3 gridPlanes = (bvhvec3( ceilf( posInGrid.x ), ceilf( posInGrid.y ), ceilf( posInGrid.z ) ) - Dsign) * (1.0f / gridDim); + l2_.X = tinybvh_clamp( (int)posInGrid.x, l1_.X * groupDim, l1_.X * groupDim + (groupDim - 1) ); + l2_.Y = tinybvh_clamp( (int)posInGrid.y, l1_.Y * groupDim, l1_.Y * groupDim + (groupDim - 1) ); + l2_.Z = tinybvh_clamp( (int)posInGrid.z, l1_.Z * groupDim, l1_.Z * groupDim + (groupDim - 1) ); + l2_.tmax = (gridPlanes - ray.O) * ray.rD; + gridBase = grid + ((l2_.X + l2_.Y * gridDim + l2_.Z * gridDim * gridDim) & superMask); + l2_.X &= groupDim - 1, l2_.Y &= groupDim - 1, l2_.Z &= groupDim - 1; + // step through midlevel cells + while (1) + { + const uint32_t brickCell = gridBase[l2_.X + l2_.Y * gridDim + l2_.Z * gridDim * gridDim]; + if (brickCell) + { + // setup 3DDDA for brick traversal + uint32_t* brickData = brick + brickCell * brickSize; + const bvhvec3 posInBrick = (ray.O + (t + 0.0000025f) * ray.D) * (float)objectDim; + uint32_t X = tinybvh_clamp( (int)posInBrick.x - (l2_.X + l1_.X * groupDim) * brickDim, 0u, brickDim - 1 ); + uint32_t Y = tinybvh_clamp( (int)posInBrick.y - (l2_.Y + l1_.Y * groupDim) * brickDim, 0u, brickDim - 1 ); + uint32_t Z = tinybvh_clamp( (int)posInBrick.z - (l2_.Z + l1_.Z * groupDim) * brickDim, 0u, brickDim - 1 ); + const bvhvec3 brickPlanes = (bvhvec3( ceilf( posInBrick.x ), ceilf( posInBrick.y ), ceilf( posInBrick.z ) ) - Dsign) * (1.0f / objectDim); + bvhvec3 tmax = (brickPlanes - ray.O) * ray.rD; + // step through brick + while (1) + { + const uint32_t v = brickData[X + Y * brickDim + Z * brickDim * brickDim]; + if (v) return t < ray.hit.t; + if (tmax.x < tmax.y) + { + if (tmax.x < tmax.z) + { + if ((X += step.x) >= brickDim) break; + t = tmax.x, tmax.x += l3tdelta.x; + } + else + { + if ((Z += step.z) >= brickDim) break; + t = tmax.z, tmax.z += l3tdelta.z; + } + } + else + { + if (tmax.y < tmax.z) + { + if ((Y += step.y) >= brickDim) break; + t = tmax.y, tmax.y += l3tdelta.y; + } + else + { + if ((Z += step.z) >= brickDim) break; + t = tmax.z, tmax.z += l3tdelta.z; + } + } + } + } + if (l2_.tmax.x < l2_.tmax.y) + { + if (l2_.tmax.x < l2_.tmax.z) + { + if ((l2_.X += step.x) >= groupDim) break; + t = l2_.tmax.x, l2_.tmax.x += l2tdelta.x; + } + else + { + if ((l2_.Z += step.z) >= groupDim) break; + t = l2_.tmax.z, l2_.tmax.z += l2tdelta.z; + } + } + else + { + if (l2_.tmax.y < l2_.tmax.z) + { + if ((l2_.Y += step.y) >= groupDim) break; + t = l2_.tmax.y, l2_.tmax.y += l2tdelta.y; + } + else + { + if ((l2_.Z += step.z) >= groupDim) break; + t = l2_.tmax.z, l2_.tmax.z += l2tdelta.z; + } + } + } + } + if (l1_.tmax.x < l1_.tmax.y) + { + if (l1_.tmax.x < l1_.tmax.z) + { + if ((l1_.X += step.x) >= topGridDim) break; + t = l1_.tmax.x, l1_.tmax.x += tdelta.x; + } + else + { + if ((l1_.Z += step.z) >= topGridDim) break; + t = l1_.tmax.z, l1_.tmax.z += tdelta.z; + } + } + else + { + if (l1_.tmax.y < l1_.tmax.z) + { + if ((l1_.Y += step.y) >= topGridDim) break; + t = l1_.tmax.y, l1_.tmax.y += tdelta.y; + } + else + { + if ((l1_.Z += step.z) >= topGridDim) break; + t = l1_.tmax.z, l1_.tmax.z += tdelta.z; + } + } + } + // we shouldn't get here + return false; +} + +// BVH_Verbose implementation +// ---------------------------------------------------------------------------- + +void BVH_Verbose::ConvertFrom( const BVH& original, bool /* unused here */ ) +{ + // allocate space + uint32_t spaceNeeded = original.triCount * (refittable ? 2 : 3); + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + bvhNode = (BVHNode*)AlignedAlloc( sizeof( BVHNode ) * spaceNeeded ); + allocatedNodes = spaceNeeded; + } + memset( bvhNode, 0, sizeof( BVHNode ) * spaceNeeded ); + CopyBasePropertiesFrom( original ); + this->verts = original.verts; + this->fragment = original.fragment; + this->primIdx = original.primIdx; + bvhNode[0].parent = 0xffffffff; // root sentinel + // convert + uint32_t nodeIdx = 0, parent = 0xffffffff, stack[128], stackPtr = 0; + while (1) + { + const BVH::BVHNode& orig = original.bvhNode[nodeIdx]; + bvhNode[nodeIdx].aabbMin = orig.aabbMin, bvhNode[nodeIdx].aabbMax = orig.aabbMax; + bvhNode[nodeIdx].triCount = orig.triCount, bvhNode[nodeIdx].parent = parent; + if (orig.isLeaf()) + { + bvhNode[nodeIdx].firstTri = orig.leftFirst; + if (stackPtr == 0) break; + nodeIdx = stack[--stackPtr]; + parent = stack[--stackPtr]; + } + else + { + bvhNode[nodeIdx].left = orig.leftFirst; + bvhNode[nodeIdx].right = orig.leftFirst + 1; + stack[stackPtr++] = nodeIdx; + stack[stackPtr++] = orig.leftFirst + 1; + parent = nodeIdx; + nodeIdx = orig.leftFirst; + } + } + usedNodes = original.usedNodes; +} + +int32_t BVH_Verbose::NodeCount() const +{ + // Determine the number of nodes in the tree. Typically the result should + // be usedNodes - 1 (second node is always unused), but some builders may + // have unused nodes besides node 1. TODO: Support more layouts. + uint32_t retVal = 0, nodeIdx = 0, stack[64], stackPtr = 0; + while (1) + { + const BVHNode& n = bvhNode[nodeIdx]; + retVal++; + if (n.isLeaf()) { if (stackPtr == 0) break; else nodeIdx = stack[--stackPtr]; } + else nodeIdx = n.left, stack[stackPtr++] = n.right; + } + return retVal; +} + +float BVH_Verbose::SAHCost( const uint32_t nodeIdx ) const +{ + // Determine the SAH cost of the tree. This provides an indication + // of the quality of the BVH: Lower is better. + const BVHNode& n = bvhNode[nodeIdx]; + const float SAn = SA( n.aabbMin, n.aabbMax ); + if (n.isLeaf()) return c_int * SAn * n.triCount; + float cost = c_trav * SAn + SAHCost( n.left ) + SAHCost( n.right ); + return nodeIdx == 0 ? (cost / SAn) : cost; +} + +void BVH_Verbose::Refit( const uint32_t nodeIdx, bool skipLeafs ) +{ + BVH_FATAL_ERROR_IF( !refittable && !skipLeafs, "BVH_Verbose::Refit( .. ), refitting an SBVH." ); + BVH_FATAL_ERROR_IF( bvhNode == 0, "BVH_Verbose::Refit( .. ), bvhNode == 0." ); + BVH_FATAL_ERROR_IF( bvh_over_indices && !skipLeafs, "BVH_Verbose::Refit( .. ), bvh used indexed tris." ); + BVHNode& node = bvhNode[nodeIdx]; + if (node.isLeaf()) // leaf: adjust to current triangle vertex positions + { + if (skipLeafs) return; + bvhvec3 bmin( BVH_FAR ), bmax( -BVH_FAR ); + for (uint32_t first = node.firstTri, j = 0; j < node.triCount; j++) + { + const uint32_t vertIdx = primIdx[first + j] * 3; + const bvhvec3 v0 = verts[vertIdx]; + const bvhvec3 v1 = verts[vertIdx + 1]; + const bvhvec3 v2 = verts[vertIdx + 2]; + bmin = tinybvh_min( bmin, v0 ), bmax = tinybvh_max( bmax, v0 ); + bmin = tinybvh_min( bmin, v1 ), bmax = tinybvh_max( bmax, v1 ); + bmin = tinybvh_min( bmin, v2 ), bmax = tinybvh_max( bmax, v2 ); + } + node.aabbMin = bmin, node.aabbMax = bmax; + } + else + { + Refit( node.left, skipLeafs ); + Refit( node.right, skipLeafs ); + node.aabbMin = tinybvh_min( bvhNode[node.left].aabbMin, bvhNode[node.right].aabbMin ); + node.aabbMax = tinybvh_max( bvhNode[node.left].aabbMax, bvhNode[node.right].aabbMax ); + } + if (nodeIdx == 0) aabbMin = node.aabbMin, aabbMax = node.aabbMax; +} + +void BVH_Verbose::CheckFit( const uint32_t nodeIdx, bool skipLeafs ) +{ + BVHNode& node = bvhNode[nodeIdx]; + bvhvec3 bmin( BVH_FAR ), bmax( -BVH_FAR ); + if (node.isLeaf()) // leaf: adjust to current triangle vertex positions + { + if (skipLeafs) return; + for (uint32_t first = node.firstTri, j = 0; j < node.triCount; j++) + { + const uint32_t vertIdx = primIdx[first + j] * 3; + const bvhvec3 v0 = verts[vertIdx]; + const bvhvec3 v1 = verts[vertIdx + 1]; + const bvhvec3 v2 = verts[vertIdx + 2]; + bmin = tinybvh_min( bmin, v0 ), bmax = tinybvh_max( bmax, v0 ); + bmin = tinybvh_min( bmin, v1 ), bmax = tinybvh_max( bmax, v1 ); + bmin = tinybvh_min( bmin, v2 ), bmax = tinybvh_max( bmax, v2 ); + } + } + else + { + CheckFit( node.left, skipLeafs ); + CheckFit( node.right, skipLeafs ); + bmin = tinybvh_min( bvhNode[node.left].aabbMin, bvhNode[node.right].aabbMin ); + bmax = tinybvh_max( bvhNode[node.left].aabbMax, bvhNode[node.right].aabbMax ); + } +} + +void BVH_Verbose::Compact() +{ + BVH_FATAL_ERROR_IF( bvhNode == 0, "BVH_Verbose::Compact(), bvhNode == 0." ); + if (bvhNode[0].isLeaf()) return; // nothing to compact. + BVHNode* tmp = (BVHNode*)AlignedAlloc( sizeof( BVHNode ) * usedNodes ); + memcpy( tmp, bvhNode, 2 * sizeof( BVHNode ) ); + uint32_t newNodePtr = 2, nodeIdx = 0, stack[64], stackPtr = 0; + while (1) + { + BVHNode& node = tmp[nodeIdx]; + const BVHNode& left = bvhNode[node.left]; + const BVHNode& right = bvhNode[node.right]; + tmp[newNodePtr] = left, tmp[newNodePtr + 1] = right; + const uint32_t todo1 = newNodePtr, todo2 = newNodePtr + 1; + node.left = newNodePtr++, node.right = newNodePtr++; + if (!left.isLeaf()) stack[stackPtr++] = todo1; + if (!right.isLeaf()) stack[stackPtr++] = todo2; + if (!stackPtr) break; + nodeIdx = stack[--stackPtr]; + } + usedNodes = newNodePtr; + AlignedFree( bvhNode ); + bvhNode = tmp; +} + +void BVH_Verbose::SortIndices() +{ + // create a new primIdx array which has the primitive indices sorted by depth-first traversal order. + uint32_t nodeIdx = 0, stack[256], stackPtr = 0, * tmp = new uint32_t[triCount], nextIdx = 0; + while (1) + { + BVHNode& node = bvhNode[nodeIdx]; + if (node.isLeaf()) + { + uint32_t tmpFirst = nextIdx; + for (unsigned i = 0; i < node.triCount; i++) tmp[nextIdx++] = primIdx[node.firstTri + i]; + node.firstTri = tmpFirst; + if (stackPtr == 0) break; else nodeIdx = stack[--stackPtr]; + continue; + } + nodeIdx = node.left; + stack[stackPtr++] = node.right; + } + memcpy( primIdx, tmp, triCount * 4 ); + delete[] tmp; +} + +void BVH_Verbose::Optimize( const uint32_t iterations, const bool extreme, bool stochastic ) +{ + // allocate array for sorting; size is upper-bound. + SortItem* sortList = (SortItem*)AlignedAlloc( usedNodes * sizeof( SortItem ) ); + // optimize by reinserting subtrees with a high cost - Section 3.4 of the paper. + for (uint32_t i = 0; i < iterations; i++) + { + // calculate combined cost for all nodes + uint32_t interiorNodes = 0; + for (uint32_t j = 2; j < usedNodes; j++) + { + const BVHNode& node = bvhNode[j]; + if (node.isLeaf()) continue; + if (node.parent == 0) continue; + if (bvhNode[node.parent].parent == 0) continue; + const float A = node.SA(), AL = bvhNode[node.left].SA(), AR = bvhNode[node.right].SA(); + float Mmin = A / tinybvh_min( 1e-10f, tinybvh_min( AL, AR ) ); + float Msum = A / tinybvh_min( 1e-10f, 0.5f * (AL + AR) ); + float Mcomb = A * Msum * Mmin; + sortList[interiorNodes].idx = j, sortList[interiorNodes++].cost = Mcomb; + } + // last couple of iterations we will process more nodes. + const float portion = stochastic ? 0.5f : (extreme ? (0.01f + (0.6f * (float)i) / (float)iterations) : 0.01f); + const int limit = (uint32_t)(portion * (float)interiorNodes); + const int step = tinybvh_max( 1, (int)(portion / 0.02f) ); + // sort list - partial quick sort. + struct Task { uint32_t first, last; } stack[4096]; + int pivot, first = 0, last = (int)interiorNodes - 1, stackPtr = 0; + while (1) + { + if (first >= last) + { + if (stackPtr == 0) break; else first = stack[--stackPtr].first, last = stack[stackPtr].last; + continue; + } + pivot = first; + SortItem t, e = sortList[first]; + for (int j = first + 1; j <= last; j++) if (sortList[j].cost > e.cost) + t = sortList[j], sortList[j] = sortList[++pivot], sortList[pivot] = t; + t = sortList[pivot], sortList[pivot] = sortList[first], sortList[first] = t; + if (pivot < limit) stack[stackPtr].first = pivot + 1, stack[stackPtr++].last = last; + last = pivot - 1; + } + // reinsert selected nodes + BVHNode bckp[5]; + int start = 0; + if (stochastic) + { + float r = (float)rand() / RAND_MAX; + r = tinybvh_max( 0.0f, (r * 1.2f) - 0.3f ); // 0 .. 0.9f + start = (int)((float)limit * r); + } + bool finishingTouch = false; + for (int j = start; j < limit; j += stochastic ? ((rand() & 63) + 1) : step) + { + // prepare change + const uint32_t Nid = sortList[j].idx; + BVHNode& N = bvhNode[Nid]; + if (N.parent == 0) continue; + const uint32_t Pid = N.parent; + BVHNode& P = bvhNode[Pid]; + if (P.parent == 0) continue; + const uint32_t X1 = P.parent, X2 = (P.left == Nid ? P.right : P.left); + // compute SAH before change + float sahBefore = SAHCostUp( Nid ); + // execute change + bckp[0] = bvhNode[X1]; + if (bvhNode[X1].left == Pid) bvhNode[X1].left = X2; + else /* verbose[X1].right == Pid */ bvhNode[X1].right = X2; + const uint32_t p2 = bvhNode[X2].parent; + bvhNode[X2].parent = X1; + const uint32_t Lid = N.left, Rid = N.right; + RefitUp( X2 ); + // ReinsertNode( L, Nid ); ReinsertNode( R, Pid ); + const uint32_t Xbest1 = FindBestNewPosition( Lid ), XA = bvhNode[Xbest1].parent; + sahBefore += SAHCostUp( Xbest1 ); + bckp[1] = bvhNode[Nid]; + N.left = Xbest1, N.right = Lid, N.parent = XA; + bckp[2] = bvhNode[XA]; + if (bvhNode[XA].left == Xbest1) bvhNode[XA].left = Nid; else bvhNode[XA].right = Nid; + const uint32_t p3 = bvhNode[Xbest1].parent, p4 = bvhNode[Lid].parent; + bvhNode[Xbest1].parent = Nid, bvhNode[Lid].parent = Nid; + RefitUp( Nid ); + const uint32_t Xbest2 = FindBestNewPosition( Rid ), XB = bvhNode[Xbest2].parent; + sahBefore += SAHCostUp( Xbest2 ); + bckp[3] = bvhNode[Pid]; + P.left = Xbest2, P.right = Rid, P.parent = XB; + bckp[4] = bvhNode[XB]; + if (bvhNode[XB].left == Xbest2) bvhNode[XB].left = Pid; else bvhNode[XB].right = Pid; + const uint32_t p1 = bvhNode[Xbest2].parent, p0 = bvhNode[Rid].parent; + bvhNode[Xbest2].parent = Pid, bvhNode[Rid].parent = Pid; + RefitUp( Pid ); + // compute SAH after change + float sahAfter = SAHCostUp( X1 ) + SAHCostUp( Nid ) + SAHCostUp( Pid ); + if (finishingTouch && (sahBefore / sahAfter > 1.01f)) break; + if (sahAfter < sahBefore) continue; + // undo change, mind the order. + bvhNode[Rid].parent = p0, bvhNode[Xbest2].parent = p1, bvhNode[XB] = bckp[4]; + bvhNode[Pid] = bckp[3], bvhNode[Lid].parent = p4, bvhNode[Xbest1].parent = p3; + bvhNode[XA] = bckp[2], bvhNode[Nid] = bckp[1], bvhNode[X2].parent = p2, bvhNode[X1] = bckp[0]; + RefitUp( XB ); + RefitUp( XA ); + RefitUp( Nid ); + } + Refit( 0, true ); + } + AlignedFree( sortList ); +} + +// Single-primitive leafs: Prepare the BVH for optimization. While it is not strictly +// necessary to have a single primitive per leaf, it will yield a slightly better +// optimized BVH. The leafs of the optimized BVH should be collapsed ('MergeLeafs') +// to obtain the final tree. +void BVH_Verbose::SplitLeafs( const uint32_t maxPrims ) +{ + uint32_t nodeIdx = 0, stack[64], stackPtr = 0; + while (1) + { + BVHNode& node = bvhNode[nodeIdx]; + if (!node.isLeaf()) nodeIdx = node.left, stack[stackPtr++] = node.right; else + { + // split this leaf + if (node.triCount > maxPrims) + { + const uint32_t newIdx1 = usedNodes++, newIdx2 = usedNodes++; + BVHNode& new1 = bvhNode[newIdx1], & new2 = bvhNode[newIdx2]; + new1.firstTri = node.firstTri, new1.triCount = node.triCount / 2; + new1.parent = new2.parent = nodeIdx, new1.left = new1.right = 0; + new2.firstTri = node.firstTri + new1.triCount; + new2.triCount = node.triCount - new1.triCount, new2.left = new2.right = 0; + node.left = newIdx1, node.right = newIdx2, node.triCount = 0; + new1.aabbMin = new2.aabbMin = bvhvec3( BVH_FAR ), new1.aabbMax = new2.aabbMax = bvhvec3( -BVH_FAR ); + for (uint32_t fi, i = 0; i < new1.triCount; i++) + fi = primIdx[new1.firstTri + i], + new1.aabbMin = tinybvh_min( new1.aabbMin, fragment[fi].bmin ), + new1.aabbMax = tinybvh_max( new1.aabbMax, fragment[fi].bmax ); + for (uint32_t fi, i = 0; i < new2.triCount; i++) + fi = primIdx[new2.firstTri + i], + new2.aabbMin = tinybvh_min( new2.aabbMin, fragment[fi].bmin ), + new2.aabbMax = tinybvh_max( new2.aabbMax, fragment[fi].bmax ); + // recurse + if (new1.triCount > 1) stack[stackPtr++] = newIdx1; + if (new2.triCount > 1) stack[stackPtr++] = newIdx2; + } + if (stackPtr == 0) break; else nodeIdx = stack[--stackPtr]; + } + } +} + +// MergeLeafs: After optimizing a BVH, single-primitive leafs should be merged whenever +// SAH indicates this is an improvement. +void BVH_Verbose::MergeLeafs() +{ + // allocate some working space + uint32_t* subtreeTriCount = (uint32_t*)AlignedAlloc( usedNodes * 4 ); + uint32_t* newIdx = (uint32_t*)AlignedAlloc( idxCount * 4 ); + memset( subtreeTriCount, 0, usedNodes * 4 ); + CountSubtreeTris( 0, subtreeTriCount ); + uint32_t stack[64], stackPtr = 0, nodeIdx = 0, newIdxPtr = 0; + while (1) + { + BVHNode& node = bvhNode[nodeIdx]; + if (node.isLeaf()) + { + uint32_t start = newIdxPtr; + MergeSubtree( nodeIdx, newIdx, newIdxPtr ); + node.firstTri = start; + // pop new task + if (stackPtr == 0) break; + nodeIdx = stack[--stackPtr]; + } + else + { + const uint32_t leftCount = subtreeTriCount[node.left]; + const uint32_t rightCount = subtreeTriCount[node.right]; + const uint32_t mergedCount = leftCount + rightCount; + // cost of unsplit + float Cunsplit = SA( node.aabbMin, node.aabbMax ) * mergedCount * c_int; + // cost of leaving things as they are + BVHNode& left = bvhNode[node.left]; + BVHNode& right = bvhNode[node.right]; + float Ckeepsplit = c_trav + c_int * (left.SA() * leftCount + right.SA() * rightCount); + if (Cunsplit <= Ckeepsplit) + { + // collapse the subtree + uint32_t start = newIdxPtr; + MergeSubtree( nodeIdx, newIdx, newIdxPtr ); + node.firstTri = start, node.triCount = mergedCount; + node.left = node.right = 0; + // pop new task + if (stackPtr == 0) break; + nodeIdx = stack[--stackPtr]; + } + else /* recurse */ nodeIdx = node.left, stack[stackPtr++] = node.right; + } + } + // cleanup + AlignedFree( subtreeTriCount ); + AlignedFree( primIdx ); + primIdx = newIdx, may_have_holes = true; // all over the place, in fact +} + +// BVH_GPU implementation +// ---------------------------------------------------------------------------- + +BVH_GPU::~BVH_GPU() +{ + if (!ownBVH) bvh = BVH(); // clear out pointers we don't own. + AlignedFree( bvhNode ); +} + +void BVH_GPU::Build( const bvhvec4* vertices, const uint32_t primCount ) +{ + Build( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +void BVH_GPU::Build( const bvhvec4slice& vertices ) +{ + bvh.context = context; + bvh.BuildDefault( vertices ); + ConvertFrom( bvh, false ); +} + +void BVH_GPU::Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + // build the BVH with a continuous array of bvhvec4 vertices, indexed by 'indices'. + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH_GPU::Build( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + // build the BVH from vertices stored in a slice, indexed by 'indices'. + bvh.context = context; + bvh.BuildDefault( vertices, indices, prims ); + ConvertFrom( bvh, false ); +} + +void BVH_GPU::Build( BLASInstance* instances, const uint32_t instCount, BVHBase** blasses, const uint32_t blasCount ) +{ + // build a TLAS based on the array of BLASInstance records. + bvh.context = context; + bvh.Build( instances, instCount, blasses, blasCount ); + ConvertFrom( bvh, false ); +} + +void BVH_GPU::BuildHQ( const bvhvec4* vertices, const uint32_t primCount ) +{ + BuildHQ( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +void BVH_GPU::BuildHQ( const bvhvec4slice& vertices ) +{ + bvh.BuildHQ( vertices ); + ConvertFrom( bvh, false ); +} + +void BVH_GPU::BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + BuildHQ( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH_GPU::BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + bvh.context = context; + bvh.BuildHQ( vertices, indices, prims ); + ConvertFrom( bvh, false ); +} + +void BVH_GPU::Optimize( const uint32_t iterations, bool extreme ) +{ + bvh.Optimize( iterations, extreme ); + ConvertFrom( bvh, false ); +} + +void BVH_GPU::ConvertFrom( const BVH& original, bool compact ) +{ + // get a copy of the original bvh + if (&original != &bvh) ownBVH = false; // bvh isn't ours; don't delete in destructor. + bvh = original; + // allocate space + const uint32_t spaceNeeded = compact ? original.usedNodes : original.allocatedNodes; + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + bvhNode = (BVHNode*)AlignedAlloc( sizeof( BVHNode ) * spaceNeeded ); + allocatedNodes = spaceNeeded; + } + memset( bvhNode, 0, sizeof( BVHNode ) * spaceNeeded ); + CopyBasePropertiesFrom( original ); + // recursively convert nodes + uint32_t newNodePtr = 0, nodeIdx = 0, stack[128], stackPtr = 0; + while (1) + { + const BVH::BVHNode& orig = original.bvhNode[nodeIdx]; + const uint32_t idx = newNodePtr++; + if (orig.isLeaf()) + { + this->bvhNode[idx].triCount = orig.triCount; + this->bvhNode[idx].firstTri = orig.leftFirst; + if (!stackPtr) break; + nodeIdx = stack[--stackPtr]; + uint32_t newNodeParent = stack[--stackPtr]; + this->bvhNode[newNodeParent].right = newNodePtr; + } + else + { + const BVH::BVHNode& left = original.bvhNode[orig.leftFirst]; + const BVH::BVHNode& right = original.bvhNode[orig.leftFirst + 1]; + this->bvhNode[idx].lmin = left.aabbMin, this->bvhNode[idx].rmin = right.aabbMin; + this->bvhNode[idx].lmax = left.aabbMax, this->bvhNode[idx].rmax = right.aabbMax; + this->bvhNode[idx].left = newNodePtr; // right will be filled when popped + stack[stackPtr++] = idx; + stack[stackPtr++] = orig.leftFirst + 1; + nodeIdx = orig.leftFirst; + } + } + usedNodes = newNodePtr; +} + +int32_t BVH_GPU::Intersect( Ray& ray ) const +{ + VALIDATE_RAY( ray ); + BVHNode* node = &bvhNode[0], * stack[64]; + const bvhvec4slice& verts = bvh.verts; + const uint32_t* primIdx = bvh.primIdx; + uint32_t stackPtr = 0; + float cost = 0; + while (1) + { + cost += c_trav; + if (node->isLeaf()) + { + if (indexedEnabled && bvh.vertIdx != 0) for (uint32_t i = 0; i < node->triCount; i++, cost += c_int) + { + const uint32_t pi = primIdx[node->firstTri + i]; + const uint32_t i0 = bvh.vertIdx[pi * 3], i1 = bvh.vertIdx[pi * 3 + 1], i2 = bvh.vertIdx[pi * 3 + 2]; + IntersectTri( ray, pi, verts, i0, i1, i2 ); + } + else for (uint32_t i = 0; i < node->triCount; i++, cost += c_int) + { + const uint32_t pi = primIdx[node->firstTri + i]; + IntersectTri( ray, pi, verts, pi * 3, pi * 3 + 1, pi * 3 + 2 ); + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + continue; + } + const bvhvec3 lmin = node->lmin - ray.O, lmax = node->lmax - ray.O; + const bvhvec3 rmin = node->rmin - ray.O, rmax = node->rmax - ray.O; + float dist1 = BVH_FAR, dist2 = BVH_FAR; + const bvhvec3 t1a = lmin * ray.rD, t2a = lmax * ray.rD; + const bvhvec3 t1b = rmin * ray.rD, t2b = rmax * ray.rD; + const float tmina = tinybvh_max( tinybvh_max( tinybvh_min( t1a.x, t2a.x ), tinybvh_min( t1a.y, t2a.y ) ), tinybvh_min( t1a.z, t2a.z ) ); + const float tmaxa = tinybvh_min( tinybvh_min( tinybvh_max( t1a.x, t2a.x ), tinybvh_max( t1a.y, t2a.y ) ), tinybvh_max( t1a.z, t2a.z ) ); + const float tminb = tinybvh_max( tinybvh_max( tinybvh_min( t1b.x, t2b.x ), tinybvh_min( t1b.y, t2b.y ) ), tinybvh_min( t1b.z, t2b.z ) ); + const float tmaxb = tinybvh_min( tinybvh_min( tinybvh_max( t1b.x, t2b.x ), tinybvh_max( t1b.y, t2b.y ) ), tinybvh_max( t1b.z, t2b.z ) ); + if (tmaxa >= tmina && tmina < ray.hit.t && tmaxa >= 0) dist1 = tmina; + if (tmaxb >= tminb && tminb < ray.hit.t && tmaxb >= 0) dist2 = tminb; + uint32_t lidx = node->left, ridx = node->right; + if (dist1 > dist2) + { + float t = dist1; dist1 = dist2; dist2 = t; + uint32_t i = lidx; lidx = ridx; ridx = i; + } + if (dist1 == BVH_FAR) + { + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + else + { + node = bvhNode + lidx; + if (dist2 != BVH_FAR) stack[stackPtr++] = bvhNode + ridx; + } + } + return (int32_t)cost; // cast to not break interface. +} + +// BVH_SoA implementation +// ---------------------------------------------------------------------------- + +BVH_SoA::~BVH_SoA() +{ + if (!ownBVH) bvh = BVH(); // clear out pointers we don't own. + AlignedFree( bvhNode ); +} + +void BVH_SoA::Build( const bvhvec4* vertices, const uint32_t primCount ) +{ + Build( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +void BVH_SoA::Build( const bvhvec4slice& vertices ) +{ + bvh.context = context; // properly propagate context to fix issue #66. + bvh.BuildDefault( vertices ); + ConvertFrom( bvh, false ); +} + +void BVH_SoA::Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + // build the BVH with a continuous array of bvhvec4 vertices, indexed by 'indices'. + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH_SoA::Build( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + // build the BVH from vertices stored in a slice, indexed by 'indices'. + bvh.context = context; + bvh.BuildDefault( vertices, indices, prims ); + ConvertFrom( bvh, false ); +} + +void BVH_SoA::BuildHQ( const bvhvec4* vertices, const uint32_t primCount ) +{ + BuildHQ( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +void BVH_SoA::BuildHQ( const bvhvec4slice& vertices ) +{ + bvh.context = context; // properly propagate context to fix issue #66. + bvh.BuildHQ( vertices ); + ConvertFrom( bvh, false ); +} + +void BVH_SoA::BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + BuildHQ( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH_SoA::BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + bvh.context = context; + bvh.BuildHQ( vertices, indices, prims ); + ConvertFrom( bvh, false ); +} + +void BVH_SoA::Optimize( const uint32_t iterations, bool extreme ) +{ + bvh.Optimize( iterations, extreme ); + ConvertFrom( bvh, false ); +} + +void BVH_SoA::Save( const char* fileName ) +{ + bvh.Save( fileName ); +} + +bool BVH_SoA::Load( const char* fileName, const bvhvec4* vertices, const uint32_t primCount ) +{ + return Load( fileName, bvhvec4slice{ vertices, primCount * 3, sizeof( bvhvec4 ) } ); +} + +bool BVH_SoA::Load( const char* fileName, const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ) +{ + return Load( fileName, bvhvec4slice{ vertices, primCount * 3, sizeof( bvhvec4 ) }, indices, primCount ); +} + +bool BVH_SoA::Load( const char* fileName, const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ) +{ + if (!bvh.Load( fileName, vertices, indices, primCount )) return false; + ConvertFrom( bvh, false ); + return true; +} + +void BVH_SoA::ConvertFrom( const BVH& original, bool compact ) +{ + // get a copy of the original bvh + if (&original != &bvh) ownBVH = false; // bvh isn't ours; don't delete in destructor. + bvh = original; + // allocate space + const uint32_t spaceNeeded = compact ? bvh.usedNodes : bvh.allocatedNodes; + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + bvhNode = (BVHNode*)AlignedAlloc( sizeof( BVHNode ) * spaceNeeded ); + allocatedNodes = spaceNeeded; + } + memset( bvhNode, 0, sizeof( BVHNode ) * spaceNeeded ); + CopyBasePropertiesFrom( bvh ); + // recursively convert nodes + uint32_t newAlt2Node = 0, nodeIdx = 0, stack[128], stackPtr = 0; + while (1) + { + const BVH::BVHNode& node = bvh.bvhNode[nodeIdx]; + const uint32_t idx = newAlt2Node++; + if (node.isLeaf()) + { + bvhNode[idx].triCount = node.triCount; + bvhNode[idx].firstTri = node.leftFirst; + if (!stackPtr) break; + nodeIdx = stack[--stackPtr]; + uint32_t newNodeParent = stack[--stackPtr]; + bvhNode[newNodeParent].right = newAlt2Node; + } + else + { + const BVH::BVHNode& left = bvh.bvhNode[node.leftFirst]; + const BVH::BVHNode& right = bvh.bvhNode[node.leftFirst + 1]; + // This BVH layout requires BVH_USEAVX/BVH_USENEON for traversal, but at least we + // can convert to it without SSE/AVX/NEON support. + bvhNode[idx].xxxx = SIMD_SETRVEC( left.aabbMin.x, left.aabbMax.x, right.aabbMin.x, right.aabbMax.x ); + bvhNode[idx].yyyy = SIMD_SETRVEC( left.aabbMin.y, left.aabbMax.y, right.aabbMin.y, right.aabbMax.y ); + bvhNode[idx].zzzz = SIMD_SETRVEC( left.aabbMin.z, left.aabbMax.z, right.aabbMin.z, right.aabbMax.z ); + bvhNode[idx].left = newAlt2Node; // right will be filled when popped + stack[stackPtr++] = idx; + stack[stackPtr++] = node.leftFirst + 1; + nodeIdx = node.leftFirst; + } + } + usedNodes = newAlt2Node; +} + +// BVH_SoA::Intersect can be found in the BVH_USEAVX section later in this file. + +// Generic (templated) MBVH implementation +// ---------------------------------------------------------------------------- + +template MBVH::~MBVH() +{ + if (!ownBVH) bvh = BVH(); // clear out pointers we don't own. + AlignedFree( mbvhNode ); +} + +template void MBVH::Build( const bvhvec4* vertices, const uint32_t primCount ) +{ + Build( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +template void MBVH::Build( const bvhvec4slice& vertices ) +{ + bvh.context = context; // properly propagate context to fix issue #66. + bvh.BuildDefault( vertices ); + ConvertFrom( bvh, false ); +} + +template void MBVH::Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + // build the BVH with a continuous array of bvhvec4 vertices, indexed by 'indices'. + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +template void MBVH::Build( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + // build the BVH from vertices stored in a slice, indexed by 'indices'. + bvh.context = context; + bvh.BuildDefault( vertices, indices, prims ); + ConvertFrom( bvh, true ); +} + +template void MBVH::BuildHQ( const bvhvec4* vertices, const uint32_t primCount ) +{ + BuildHQ( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +template void MBVH::BuildHQ( const bvhvec4slice& vertices ) +{ + bvh.context = context; + bvh.BuildHQ( vertices ); + ConvertFrom( bvh, true ); +} + +template void MBVH::BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +template void MBVH::BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + bvh.context = context; + bvh.BuildHQ( vertices, indices, prims ); + ConvertFrom( bvh, true ); +} + +template void MBVH::Optimize( const uint32_t iterations, bool extreme ) +{ + bvh.Optimize( iterations, extreme ); + ConvertFrom( bvh, true ); +} + +template uint32_t MBVH::LeafCount( const uint32_t nodeIdx ) const +{ + MBVHNode& node = mbvhNode[nodeIdx]; + if (node.isLeaf()) return 1; + uint32_t count = 0; + for (uint32_t i = 0; i < node.childCount; i++) count += LeafCount( node.child[i] ); + return count; +} + +template void MBVH::Refit( const uint32_t nodeIdx ) +{ + MBVHNode& node = mbvhNode[nodeIdx]; + if (node.isLeaf()) + { + bvhvec3 bmin( BVH_FAR ), bmax( -BVH_FAR ); + if (bvh.vertIdx) for (uint32_t first = node.firstTri, j = 0; j < node.triCount; j++) + { + const uint32_t vidx = bvh.primIdx[first + j] * 3; + const uint32_t i0 = bvh.vertIdx[vidx], i1 = bvh.vertIdx[vidx + 1], i2 = bvh.vertIdx[vidx + 2]; + const bvhvec3 v0 = bvh.verts[i0], v1 = bvh.verts[i1], v2 = bvh.verts[i2]; + bmin = tinybvh_min( bmin, tinybvh_min( tinybvh_min( v0, v1 ), v2 ) ); + bmax = tinybvh_max( bmax, tinybvh_max( tinybvh_max( v0, v1 ), v2 ) ); + } + else for (uint32_t first = node.firstTri, j = 0; j < node.triCount; j++) + { + const uint32_t vidx = bvh.primIdx[first + j] * 3; + const bvhvec3 v0 = bvh.verts[vidx], v1 = bvh.verts[vidx + 1], v2 = bvh.verts[vidx + 2]; + bmin = tinybvh_min( bmin, tinybvh_min( tinybvh_min( v0, v1 ), v2 ) ); + bmax = tinybvh_max( bmax, tinybvh_max( tinybvh_max( v0, v1 ), v2 ) ); + } + node.aabbMin = bmin, node.aabbMax = bmax; + } + else + { + for (unsigned i = 0; i < node.childCount; i++) Refit( node.child[i] ); + MBVHNode& firstChild = mbvhNode[node.child[0]]; + bvhvec3 bmin = firstChild.aabbMin, bmax = firstChild.aabbMax; + for (unsigned i = 1; i < node.childCount; i++) + { + MBVHNode& child = mbvhNode[node.child[i]]; + bmin = tinybvh_min( bmin, child.aabbMin ); + bmax = tinybvh_max( bmax, child.aabbMax ); + } + } + if (nodeIdx == 0) aabbMin = node.aabbMin, aabbMax = node.aabbMax; +} + +template float MBVH::SAHCost( const uint32_t nodeIdx ) const +{ + // Determine the SAH cost of the tree. This provides an indication + // of the quality of the BVH: Lower is better. + const MBVHNode& n = mbvhNode[nodeIdx]; + const float sa = BVH::SA( n.aabbMin, n.aabbMax ); + if (n.isLeaf()) return c_int * sa * n.triCount; + float cost = c_trav * sa; + for (unsigned i = 0; i < M; i++) if (n.child[i] != 0) cost += SAHCost( n.child[i] ); + return nodeIdx == 0 ? (cost / sa) : cost; +} + +template void MBVH::ConvertFrom( const BVH& original, bool compact ) +{ + // get a copy of the original bvh + if (&original != &bvh) ownBVH = false; // bvh isn't ours; don't delete in destructor. + bvh = original; + // allocate space + uint32_t spaceNeeded = compact ? original.usedNodes : original.allocatedNodes; + constexpr bool M8 = M == 8; + if (M8) spaceNeeded += original.usedNodes >> 1; // cwbvh / SplitLeafs + if (allocatedNodes < spaceNeeded) + { + AlignedFree( mbvhNode ); + mbvhNode = (MBVHNode*)AlignedAlloc( spaceNeeded * sizeof( MBVHNode ) ); + allocatedNodes = spaceNeeded; + } + memset( mbvhNode, 0, sizeof( MBVHNode ) * spaceNeeded ); + CopyBasePropertiesFrom( original ); + // create an mbvh node for each bvh2 node + for (uint32_t i = 0; i < original.usedNodes; i++) if (i != 1) + { + BVH::BVHNode& orig = original.bvhNode[i]; + MBVHNode& node = this->mbvhNode[i]; + node.aabbMin = orig.aabbMin, node.aabbMax = orig.aabbMax; + if (orig.isLeaf()) node.triCount = orig.triCount, node.firstTri = orig.leftFirst; + else node.child[0] = orig.leftFirst, node.child[1] = orig.leftFirst + 1, node.childCount = 2; + } + // collapse + uint32_t stack[128], stackPtr = 0, nodeIdx = 0; // i.e., root node + while (1) + { + MBVHNode& node = this->mbvhNode[nodeIdx]; + while (node.childCount < M) + { + int32_t bestChild = -1; + float bestChildSA = 0; + for (uint32_t i = 0; i < node.childCount; i++) + { + // see if we can adopt child i + const MBVHNode& child = this->mbvhNode[node.child[i]]; + if (!child.isLeaf() && node.childCount - 1 + child.childCount <= M) + { + const float childSA = SA( child.aabbMin, child.aabbMax ); + if (childSA > bestChildSA) bestChild = i, bestChildSA = childSA; + } + } + if (bestChild == -1) break; // could not adopt + const MBVHNode& child = this->mbvhNode[node.child[bestChild]]; + node.child[bestChild] = child.child[0]; + for (uint32_t i = 1; i < child.childCount; i++) + node.child[node.childCount++] = child.child[i]; + } + // we're done with the node; proceed with the children. + for (uint32_t i = 0; i < node.childCount; i++) + { + const uint32_t childIdx = node.child[i]; + const MBVHNode& child = this->mbvhNode[childIdx]; + if (!child.isLeaf()) stack[stackPtr++] = childIdx; + } + if (stackPtr == 0) break; + nodeIdx = stack[--stackPtr]; + } + // special case where root is leaf: add extra level - cwbvh needs this. + MBVHNode& root = this->mbvhNode[0]; + if (root.isLeaf()) + { + mbvhNode[1] = root; + root.childCount = 1; + root.child[0] = 1; + root.triCount = 0; + } + // finalize + usedNodes = original.usedNodes; + this->may_have_holes = true; +} + +// BVH4_GPU implementation +// ---------------------------------------------------------------------------- + +BVH4_GPU::~BVH4_GPU() +{ + if (!ownBVH4) bvh4 = MBVH<4>(); // clear out pointers we don't own. + AlignedFree( bvh4Data ); +} + +void BVH4_GPU::Build( const bvhvec4* vertices, const uint32_t primCount ) +{ + Build( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +void BVH4_GPU::Build( const bvhvec4slice& vertices ) +{ + bvh4.context = context; + bvh4.Build( vertices ); + ConvertFrom( bvh4, true ); +} + +void BVH4_GPU::Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + // build the BVH with a continuous array of bvhvec4 vertices, indexed by 'indices'. + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH4_GPU::Build( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + // build the BVH from vertices stored in a slice, indexed by 'indices'. + bvh4.context = context; + bvh4.Build( vertices, indices, prims ); + ConvertFrom( bvh4, true ); +} + +void BVH4_GPU::BuildHQ( const bvhvec4* vertices, const uint32_t primCount ) +{ + BuildHQ( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +void BVH4_GPU::BuildHQ( const bvhvec4slice& vertices ) +{ + bvh4.context = context; + bvh4.BuildHQ( vertices ); + ConvertFrom( bvh4, true ); +} + +void BVH4_GPU::BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH4_GPU::BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + bvh4.context = context; + bvh4.BuildHQ( vertices, indices, prims ); + ConvertFrom( bvh4, true ); +} + +void BVH4_GPU::Optimize( const uint32_t iterations, bool extreme ) +{ + bvh4.Optimize( iterations, extreme ); + ConvertFrom( bvh4, true ); +} + +void BVH4_GPU::ConvertFrom( const MBVH<4>& original, bool compact ) +{ + // get a copy of the original bvh4 + if (&original != &bvh4) ownBVH4 = false; // bvh isn't ours; don't delete in destructor. + bvh4 = original; + // Convert a 4-wide BVH to a format suitable for GPU traversal. Layout: + // offs 0: aabbMin (12 bytes), 4x quantized child xmin (4 bytes) + // offs 16: aabbMax (12 bytes), 4x quantized child xmax (4 bytes) + // offs 32: 4x child ymin, then ymax, zmax, zmax (total 16 bytes) + // offs 48: 4x child node info: leaf if MSB set. + // Leaf: 15 bits for tri count, 16 for offset + // Interior: 32 bits for position of child node. + // Triangle data ('by value') immediately follows each leaf node. + uint32_t blocksNeeded = compact ? (bvh4.usedNodes * 4) : (bvh4.allocatedNodes * 4); // here, 'block' is 16 bytes. + blocksNeeded += 6 * bvh4.triCount; // this layout stores tris in the same buffer. + if (allocatedBlocks < blocksNeeded) + { + AlignedFree( bvh4Data ); + bvh4Data = (bvhvec4*)AlignedAlloc( blocksNeeded * 16 ); + allocatedBlocks = blocksNeeded; + } + memset( bvh4Data, 0, 16 * blocksNeeded ); + CopyBasePropertiesFrom( bvh4 ); + // start conversion + uint32_t nodeIdx = 0, newAlt4Ptr = 0, stack[128], stackPtr = 0, retValPos = 0; + while (1) + { + const MBVH<4>::MBVHNode& orig = bvh4.mbvhNode[nodeIdx]; + // convert BVH4 node - must be an interior node. + assert( !orig.isLeaf() ); + bvhvec4* nodeBase = bvh4Data + newAlt4Ptr; + uint32_t baseAlt4Ptr = newAlt4Ptr; + newAlt4Ptr += 4; + nodeBase[0] = bvhvec4( orig.aabbMin, 0 ); + nodeBase[1] = bvhvec4( (orig.aabbMax - orig.aabbMin) * (1.0f / 255.0f), 0 ); + MBVH<4>::MBVHNode* childNode[4] = { + &bvh4.mbvhNode[orig.child[0]], &bvh4.mbvhNode[orig.child[1]], + &bvh4.mbvhNode[orig.child[2]], &bvh4.mbvhNode[orig.child[3]] + }; + // start with leaf child node conversion + uint32_t childInfo[4] = { 0, 0, 0, 0 }; // will store in final fields later + for (int32_t i = 0; i < 4; i++) if (childNode[i]->isLeaf()) + { + childInfo[i] = newAlt4Ptr - baseAlt4Ptr; + childInfo[i] |= childNode[i]->triCount << 16; + childInfo[i] |= 0x80000000; + for (uint32_t j = 0; j < childNode[i]->triCount; j++) + { + uint32_t t = bvh4.bvh.primIdx[childNode[i]->firstTri + j]; + uint32_t ti0, ti1, ti2; + if (bvh4.bvh.vertIdx) + ti0 = bvh4.bvh.vertIdx[t * 3], + ti1 = bvh4.bvh.vertIdx[t * 3 + 1], + ti2 = bvh4.bvh.vertIdx[t * 3 + 2]; + else + ti0 = t * 3, ti1 = t * 3 + 1, ti2 = t * 3 + 2; + #ifdef BVH4_GPU_COMPRESSED_TRIS + PrecomputeTriangle( verts, ti0, ti1, ti2, (float*)&bvh4Alt[newAlt4Ptr] ); + bvh4Alt[newAlt4Ptr + 3] = bvhvec4( 0, 0, 0, *(float*)&t ); + newAlt4Ptr += 4; + #else + bvhvec4 v0 = bvh4.bvh.verts[ti0]; + bvh4Data[newAlt4Ptr + 1] = bvh4.bvh.verts[ti1] - v0; + bvh4Data[newAlt4Ptr + 2] = bvh4.bvh.verts[ti2] - v0; + v0.w = *(float*)&t; // as_float + bvh4Data[newAlt4Ptr + 0] = v0; + newAlt4Ptr += 3; + #endif + } + } + // process interior nodes + for (int32_t i = 0; i < 4; i++) if (!childNode[i]->isLeaf()) + { + // childInfo[i] = node.child[i] == 0 ? 0 : GPUFormatBVH4( node.child[i] ); + if (orig.child[i] == 0) childInfo[i] = 0; else + { + stack[stackPtr++] = (uint32_t)(((float*)&nodeBase[3] + i) - (float*)bvh4Data); + stack[stackPtr++] = orig.child[i]; + } + } + // store child node bounds, quantized + const bvhvec3 extent = orig.aabbMax - orig.aabbMin; + bvhvec3 scale; + scale.x = extent.x > 1e-10f ? (254.999f / extent.x) : 0; + scale.y = extent.y > 1e-10f ? (254.999f / extent.y) : 0; + scale.z = extent.z > 1e-10f ? (254.999f / extent.z) : 0; + uint8_t* slot0 = (uint8_t*)&nodeBase[0] + 12; // 4 chars + uint8_t* slot1 = (uint8_t*)&nodeBase[1] + 12; // 4 chars + uint8_t* slot2 = (uint8_t*)&nodeBase[2]; // 16 chars + if (orig.child[0]) + { + const bvhvec3 relBMin = childNode[0]->aabbMin - orig.aabbMin, relBMax = childNode[0]->aabbMax - orig.aabbMin; + slot0[0] = (uint8_t)floorf( relBMin.x * scale.x ), slot1[0] = (uint8_t)ceilf( relBMax.x * scale.x ); + slot2[0] = (uint8_t)floorf( relBMin.y * scale.y ), slot2[4] = (uint8_t)ceilf( relBMax.y * scale.y ); + slot2[8] = (uint8_t)floorf( relBMin.z * scale.z ), slot2[12] = (uint8_t)ceilf( relBMax.z * scale.z ); + } + if (orig.child[1]) + { + const bvhvec3 relBMin = childNode[1]->aabbMin - orig.aabbMin, relBMax = childNode[1]->aabbMax - orig.aabbMin; + slot0[1] = (uint8_t)floorf( relBMin.x * scale.x ), slot1[1] = (uint8_t)ceilf( relBMax.x * scale.x ); + slot2[1] = (uint8_t)floorf( relBMin.y * scale.y ), slot2[5] = (uint8_t)ceilf( relBMax.y * scale.y ); + slot2[9] = (uint8_t)floorf( relBMin.z * scale.z ), slot2[13] = (uint8_t)ceilf( relBMax.z * scale.z ); + } + if (orig.child[2]) + { + const bvhvec3 relBMin = childNode[2]->aabbMin - orig.aabbMin, relBMax = childNode[2]->aabbMax - orig.aabbMin; + slot0[2] = (uint8_t)floorf( relBMin.x * scale.x ), slot1[2] = (uint8_t)ceilf( relBMax.x * scale.x ); + slot2[2] = (uint8_t)floorf( relBMin.y * scale.y ), slot2[6] = (uint8_t)ceilf( relBMax.y * scale.y ); + slot2[10] = (uint8_t)floorf( relBMin.z * scale.z ), slot2[14] = (uint8_t)ceilf( relBMax.z * scale.z ); + } + if (orig.child[3]) + { + const bvhvec3 relBMin = childNode[3]->aabbMin - orig.aabbMin, relBMax = childNode[3]->aabbMax - orig.aabbMin; + slot0[3] = (uint8_t)floorf( relBMin.x * scale.x ), slot1[3] = (uint8_t)ceilf( relBMax.x * scale.x ); + slot2[3] = (uint8_t)floorf( relBMin.y * scale.y ), slot2[7] = (uint8_t)ceilf( relBMax.y * scale.y ); + slot2[11] = (uint8_t)floorf( relBMin.z * scale.z ), slot2[15] = (uint8_t)ceilf( relBMax.z * scale.z ); + } + // finalize node + nodeBase[3] = bvhvec4( + *(float*)&childInfo[0], *(float*)&childInfo[1], + *(float*)&childInfo[2], *(float*)&childInfo[3] + ); + // pop new work from the stack + if (retValPos > 0) ((uint32_t*)bvh4Data)[retValPos] = baseAlt4Ptr; + if (stackPtr == 0) break; + nodeIdx = stack[--stackPtr]; + retValPos = stack[--stackPtr]; + } + usedBlocks = newAlt4Ptr; +} + +// IntersectAlt4Nodes. For testing the converted data only; not efficient. +// This code replicates how traversal on GPU happens. +#define SWAP(A,B,C,D) tmp=A,A=B,B=tmp,tmp2=C,C=D,D=tmp2; +struct uchar4 { uint8_t x, y, z, w; }; +static uchar4 as_uchar4( const float v ) { union { float t; uchar4 t4; }; t = v; return t4; } +static uint32_t as_uint( const float v ) { return *(uint32_t*)&v; } +int32_t BVH4_GPU::Intersect( Ray& ray ) const +{ + // traverse a blas + VALIDATE_RAY( ray ); + uint32_t offset = 0, stack[128], stackPtr = 0, tmp2 /* for SWAP macro */; + float cost = 0; + while (1) + { + cost += c_trav; + // fetch the node + const bvhvec4 data0 = bvh4Data[offset + 0], data1 = bvh4Data[offset + 1]; + const bvhvec4 data2 = bvh4Data[offset + 2], data3 = bvh4Data[offset + 3]; + // extract aabb + const bvhvec3 bmin = data0, extent = data1; // pre-scaled by 1/255 + // reconstruct conservative child aabbs + const uchar4 d0 = as_uchar4( data0.w ), d1 = as_uchar4( data1.w ), d2 = as_uchar4( data2.x ); + const uchar4 d3 = as_uchar4( data2.y ), d4 = as_uchar4( data2.z ), d5 = as_uchar4( data2.w ); + const bvhvec3 c0min = bmin + extent * bvhvec3( d0.x, d2.x, d4.x ), c0max = bmin + extent * bvhvec3( d1.x, d3.x, d5.x ); + const bvhvec3 c1min = bmin + extent * bvhvec3( d0.y, d2.y, d4.y ), c1max = bmin + extent * bvhvec3( d1.y, d3.y, d5.y ); + const bvhvec3 c2min = bmin + extent * bvhvec3( d0.z, d2.z, d4.z ), c2max = bmin + extent * bvhvec3( d1.z, d3.z, d5.z ); + const bvhvec3 c3min = bmin + extent * bvhvec3( d0.w, d2.w, d4.w ), c3max = bmin + extent * bvhvec3( d1.w, d3.w, d5.w ); + // intersect child aabbs + const bvhvec3 t1a = (c0min - ray.O) * ray.rD, t2a = (c0max - ray.O) * ray.rD; + const bvhvec3 t1b = (c1min - ray.O) * ray.rD, t2b = (c1max - ray.O) * ray.rD; + const bvhvec3 t1c = (c2min - ray.O) * ray.rD, t2c = (c2max - ray.O) * ray.rD; + const bvhvec3 t1d = (c3min - ray.O) * ray.rD, t2d = (c3max - ray.O) * ray.rD; + const bvhvec3 minta = tinybvh_min( t1a, t2a ), maxta = tinybvh_max( t1a, t2a ); + const bvhvec3 mintb = tinybvh_min( t1b, t2b ), maxtb = tinybvh_max( t1b, t2b ); + const bvhvec3 mintc = tinybvh_min( t1c, t2c ), maxtc = tinybvh_max( t1c, t2c ); + const bvhvec3 mintd = tinybvh_min( t1d, t2d ), maxtd = tinybvh_max( t1d, t2d ); + const float tmina = tinybvh_max( tinybvh_max( tinybvh_max( minta.x, minta.y ), minta.z ), 0.0f ); + const float tminb = tinybvh_max( tinybvh_max( tinybvh_max( mintb.x, mintb.y ), mintb.z ), 0.0f ); + const float tminc = tinybvh_max( tinybvh_max( tinybvh_max( mintc.x, mintc.y ), mintc.z ), 0.0f ); + const float tmind = tinybvh_max( tinybvh_max( tinybvh_max( mintd.x, mintd.y ), mintd.z ), 0.0f ); + const float tmaxa = tinybvh_min( tinybvh_min( tinybvh_min( maxta.x, maxta.y ), maxta.z ), ray.hit.t ); + const float tmaxb = tinybvh_min( tinybvh_min( tinybvh_min( maxtb.x, maxtb.y ), maxtb.z ), ray.hit.t ); + const float tmaxc = tinybvh_min( tinybvh_min( tinybvh_min( maxtc.x, maxtc.y ), maxtc.z ), ray.hit.t ); + const float tmaxd = tinybvh_min( tinybvh_min( tinybvh_min( maxtd.x, maxtd.y ), maxtd.z ), ray.hit.t ); + float dist0 = tmina > tmaxa ? BVH_FAR : tmina, dist1 = tminb > tmaxb ? BVH_FAR : tminb; + float dist2 = tminc > tmaxc ? BVH_FAR : tminc, dist3 = tmind > tmaxd ? BVH_FAR : tmind, tmp; + // get child node info fields + uint32_t c0info = as_uint( data3.x ), c1info = as_uint( data3.y ); + uint32_t c2info = as_uint( data3.z ), c3info = as_uint( data3.w ); + if (dist0 < dist2) SWAP( dist0, dist2, c0info, c2info ); + if (dist1 < dist3) SWAP( dist1, dist3, c1info, c3info ); + if (dist0 < dist1) SWAP( dist0, dist1, c0info, c1info ); + if (dist2 < dist3) SWAP( dist2, dist3, c2info, c3info ); + if (dist1 < dist2) SWAP( dist1, dist2, c1info, c2info ); + // process results, starting with farthest child, so nearest ends on top of stack + uint32_t nextNode = 0; + uint32_t leaf[4] = { 0, 0, 0, 0 }, leafs = 0; + if (dist0 < BVH_FAR) + { + if (c0info & 0x80000000) leaf[leafs++] = c0info; else if (c0info) stack[stackPtr++] = c0info; + } + if (dist1 < BVH_FAR) + { + if (c1info & 0x80000000) leaf[leafs++] = c1info; else if (c1info) stack[stackPtr++] = c1info; + } + if (dist2 < BVH_FAR) + { + if (c2info & 0x80000000) leaf[leafs++] = c2info; else if (c2info) stack[stackPtr++] = c2info; + } + if (dist3 < BVH_FAR) + { + if (c3info & 0x80000000) leaf[leafs++] = c3info; else if (c3info) stack[stackPtr++] = c3info; + } + // process encountered leafs, if any + for (uint32_t i = 0; i < leafs; i++) + { + const uint32_t N = (leaf[i] >> 16) & 0x7fff; + uint32_t triStart = offset + (leaf[i] & 0xffff); + for (uint32_t j = 0; j < N; j++, triStart += 3) + { + cost += c_int; + const bvhvec3 e2 = bvhvec3( bvh4Data[triStart + 2] ); + const bvhvec3 e1 = bvhvec3( bvh4Data[triStart + 1] ); + const bvhvec3 v0 = bvh4Data[triStart + 0]; + MOLLER_TRUMBORE_TEST( ray.hit.t, continue ); + ray.hit.t = t, ray.hit.u = u, ray.hit.v = v; + ray.hit.prim = as_uint( bvh4Data[triStart + 0].w ); + } + } + // continue with nearest node or first node on the stack + if (nextNode) offset = nextNode; else + { + if (!stackPtr) break; + offset = stack[--stackPtr]; + } + } + return (int32_t)cost; // cast to not break interface. +} + +// BVH4_CPU implementation +// ---------------------------------------------------------------------------- + +BVH4_CPU::~BVH4_CPU() +{ + if (!ownBVH4) bvh4 = MBVH<4>(); // clear out pointers we don't own. + AlignedFree( bvh4Data ); +} + +void BVH4_CPU::Build( const bvhvec4* vertices, const uint32_t primCount ) +{ + Build( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +void BVH4_CPU::Build( const bvhvec4slice& vertices ) +{ + bvh4.bvh.context = bvh4.context = context; + bvh4.bvh.BuildDefault( vertices ); + ConvertFrom( bvh4 ); +} + +void BVH4_CPU::Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + // build the BVH with a continuous array of bvhvec4 vertices, indexed by 'indices'. + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH4_CPU::Build( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + // build the BVH from vertices stored in a slice, indexed by 'indices'. + bvh4.bvh.context = bvh4.context = context; + bvh4.bvh.BuildDefault( vertices, indices, prims ); + ConvertFrom( bvh4 ); +} + +void BVH4_CPU::BuildHQ( const bvhvec4* vertices, const uint32_t primCount ) +{ + BuildHQ( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +void BVH4_CPU::BuildHQ( const bvhvec4slice& vertices ) +{ + bvh4.bvh.context = bvh4.context = context; + bvh4.bvh.BuildHQ( vertices ); + ConvertFrom( bvh4 ); +} + +void BVH4_CPU::BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH4_CPU::BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + bvh4.bvh.context = bvh4.context = context; + bvh4.bvh.BuildHQ( vertices, indices, prims ); + ConvertFrom( bvh4 ); +} + +void BVH4_CPU::Save( const char* fileName ) +{ + std::fstream s{ fileName, s.binary | s.out }; + uint32_t header = TINY_BVH_VERSION_SUB + (TINY_BVH_VERSION_MINOR << 8) + (TINY_BVH_VERSION_MAJOR << 16) + (layout << 24); + s.write( (char*)&header, sizeof( uint32_t ) ); + s.write( (char*)&triCount, sizeof( uint32_t ) ); + s.write( (char*)this, sizeof( BVH4_CPU ) ); + s.write( (char*)bvh4Data, usedBlocks * 64 ); +} + +bool BVH4_CPU::Load( const char* fileName, const uint32_t expectedTris ) +{ + // open file and check contents + std::fstream s{ fileName, s.binary | s.in }; + if (!s) return false; + BVHContext tmp = context; + uint32_t header, fileTriCount; + s.read( (char*)&header, sizeof( uint32_t ) ); + if (((header >> 8) & 255) != TINY_BVH_VERSION_MINOR || + ((header >> 16) & 255) != TINY_BVH_VERSION_MAJOR || + (header & 255) != TINY_BVH_VERSION_SUB || (header >> 24) != layout) return false; + s.read( (char*)&fileTriCount, sizeof( uint32_t ) ); + if (fileTriCount != expectedTris) return false; + // all checks passed; safe to overwrite *this + s.read( (char*)this, sizeof( BVH4_CPU ) ); + context = tmp; // can't load context; function pointers will differ. + bvh4Data = (CacheLine*)AlignedAlloc( usedBlocks * 64 ); + allocatedBlocks = usedBlocks; + s.read( (char*)bvh4Data, usedBlocks * 64 ); + bvh4 = MBVH<4>(); + return true; +} + +void BVH4_CPU::Optimize( const uint32_t iterations, bool extreme ) +{ + bvh4.Optimize( iterations, extreme ); + ConvertFrom( bvh4 ); +} + +void BVH4_CPU::Refit() +{ + bvh4.Refit(); + ConvertFrom( bvh4 ); +} + +float BVH4_CPU::SAHCost( const uint32_t nodeIdx ) const +{ + return bvh4.SAHCost( nodeIdx ); +} + +#define SORT(a,b) { if (dist[a] < dist[b]) { float h = dist[a]; dist[a] = dist[b], dist[b] = h; } } + +void BVH4_CPU::ConvertFrom( MBVH<4>& original ) +{ + // Note: identical to BVH8_CPU version, just with fewer lanes. + // get a copy of the input bvh4 + if (&original != &bvh4) ownBVH4 = false; // bvh isn't ours; don't delete in destructor. + bvh4 = original; + // prepare input bvh4 + uint32_t firstIdx = 0; + bvh4.bvh.CombineLeafs( 4, firstIdx, 0 ); + bvh4.bvh.SplitLeafs( 4 ); + bvh4.ConvertFrom( bvh4.bvh, true ); + // allocate if needed + uint32_t nodesNeeded = bvh4.usedNodes, leafsNeeded = bvh4.LeafCount(); + uint32_t blocksNeeded = nodesNeeded * (sizeof( BVHNode ) / 64); // here, block = cacheline. + blocksNeeded += leafsNeeded * (sizeof( BVHTri4Leaf ) / 64); + if (allocatedBlocks < blocksNeeded) + { + AlignedFree( bvh4Data ); + void* (*allocator)(size_t, void*) = malloc64; + #if defined BVH8_ALIGN_4K + allocator = malloc4k; + #elif defined BVH8_ALIGN_32K + allocator = malloc32k; + #endif + bvh4Data = (CacheLine*)allocator( blocksNeeded * 64, 0 ); + allocatedBlocks = blocksNeeded; + } + CopyBasePropertiesFrom( bvh4 ); + // start conversion + uint32_t newBlockPtr = 0, nodeIdx = 0, stack[256], stackPtr = 0; + while (1) + { + const MBVH<4>::MBVHNode& orig = bvh4.mbvhNode[nodeIdx]; + BVHNode* newNode = (BVHNode*)(bvh4Data + newBlockPtr); + newBlockPtr += sizeof( BVHNode ) / 64; + memset( newNode, 0, sizeof( BVHNode ) ); + // calculate the permutation offsets for the node + for (uint32_t q = 0; q < 8; q++) + { + const bvhvec3 D( q & 1 ? 1.0f : -1.0f, q & 2 ? 1.0f : -1.0f, q & 4 ? 1.0f : -1.0f ); + union { float dist[4]; uint32_t idist[4]; }; + for (int i = 0; i < 4; i++) if (orig.child[i] == 0) + dist[i] = 1e30f, idist[i] = (idist[i] & 0xfffffffc) + i; + else + { + const MBVH<4>::MBVHNode& c = bvh4.mbvhNode[orig.child[i]]; + const bvhvec3 p( q & 1 ? c.aabbMin.x : c.aabbMax.x, q & 2 ? c.aabbMin.y : c.aabbMax.y, q & 4 ? c.aabbMin.z : c.aabbMax.z ); + dist[i] = tinybvh_dot( D, p ), idist[i] = (idist[i] & 0xfffffff8) + i; + } + // apply sorting network - https://bertdobbelaere.github.io/sorting_networks.html#N4L5D3 + SORT( 0, 2 ); SORT( 1, 3 ); SORT( 0, 1 ); SORT( 2, 3 ); SORT( 1, 2 ); + for (int i = 0; i < 4; i++) ((uint32_t*)&newNode->perm4)[i] += (idist[i] & 3) << (q * 2); + } + // fill remaining fields + int32_t cidx = 0; + for (int32_t i = 0; i < 4; i++) if (orig.child[i]) + { + const MBVH<4>::MBVHNode& child = bvh4.mbvhNode[orig.child[i]]; + ((float*)&newNode->xmin4)[cidx] = child.aabbMin.x, ((float*)&newNode->xmax4)[cidx] = child.aabbMax.x; + ((float*)&newNode->ymin4)[cidx] = child.aabbMin.y, ((float*)&newNode->ymax4)[cidx] = child.aabbMax.y; + ((float*)&newNode->zmin4)[cidx] = child.aabbMin.z, ((float*)&newNode->zmax4)[cidx] = child.aabbMax.z; + if (child.isLeaf()) + { + // emit leaf node: group of up to 4 triangles in AoS format. + ((uint32_t*)&newNode->child4)[cidx] = newBlockPtr + LEAF_BIT; + BVHTri4Leaf* leaf = (BVHTri4Leaf*)(bvh4Data + newBlockPtr); + newBlockPtr += sizeof( BVHTri4Leaf ) / 64; + for (uint32_t i0, i1, i2, l = 0; l < 4; l++) + { + uint32_t primIdx = bvh4.bvh.primIdx[child.firstTri + tinybvh_min( l, child.triCount - 1u )]; + GET_PRIM_INDICES_I0_I1_I2( bvh4.bvh, primIdx ); + const bvhvec4 v0 = bvh4.bvh.verts[i0], e1 = bvh4.bvh.verts[i1] - v0, e2 = bvh4.bvh.verts[i2] - v0; + leaf->SetData( v0, e1, e2, primIdx, l ); + } + } + else + { + uint32_t* slot = (uint32_t*)&newNode->child4 + cidx; + stack[stackPtr++] = (uint32_t)(slot - (uint32_t*)bvh4Data); + stack[stackPtr++] = orig.child[i]; + } + cidx++; + } + for (; cidx < 4; cidx++) + ((float*)&newNode->xmin4)[cidx] = 1e30f, ((float*)&newNode->xmax4)[cidx] = 1.00001e30f, + ((float*)&newNode->ymin4)[cidx] = 1e30f, ((float*)&newNode->ymax4)[cidx] = 1.00001e30f, + ((float*)&newNode->zmin4)[cidx] = 1e30f, ((float*)&newNode->zmax4)[cidx] = 1.00001e30f, + ((uint32_t*)&newNode->child4)[cidx] |= EMPTY_BIT; + // pop next task + if (!stackPtr) break; + nodeIdx = stack[--stackPtr]; + const uint32_t offset = stack[--stackPtr]; + ((uint32_t*)bvh4Data)[offset] = newBlockPtr; + } + usedBlocks = newBlockPtr; +} + +// BVH8_CPU implementation +// ---------------------------------------------------------------------------- + +BVH8_CPU::~BVH8_CPU() +{ + if (!ownBVH8) bvh8 = MBVH<8>(); // clear out pointers we don't own. + AlignedFree( bvh8Data ); +} + +void BVH8_CPU::Build( const bvhvec4* vertices, const uint32_t primCount ) +{ + Build( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +void BVH8_CPU::Build( const bvhvec4slice& vertices ) +{ + bvh8.bvh.context = bvh8.context = context; + bvh8.bvh.BuildDefault( vertices ); + bvh8.bvh.Compact(); + ConvertFrom( bvh8 ); +} + +void BVH8_CPU::Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + // build the BVH with a continuous array of bvhvec4 vertices, indexed by 'indices'. + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH8_CPU::Build( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + // build the BVH from vertices stored in a slice, indexed by 'indices'. + bvh8.bvh.context = bvh8.context = context; + bvh8.bvh.BuildDefault( vertices, indices, prims ); + bvh8.bvh.Compact(); + ConvertFrom( bvh8 ); +} + +void BVH8_CPU::BuildHQ( const bvhvec4* vertices, const uint32_t primCount ) +{ + BuildHQ( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +void BVH8_CPU::BuildHQ( const bvhvec4slice& vertices ) +{ + bvh8.bvh.context = bvh8.context = context; + bvh8.bvh.BuildHQ( vertices ); + ConvertFrom( bvh8 ); +} + +void BVH8_CPU::BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH8_CPU::BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + bvh8.bvh.context = bvh8.context = context; + bvh8.bvh.BuildHQ( vertices, indices, prims ); + ConvertFrom( bvh8 ); +} + +void BVH8_CPU::Save( const char* fileName ) +{ + std::fstream s{ fileName, s.binary | s.out }; + uint32_t header = TINY_BVH_VERSION_SUB + (TINY_BVH_VERSION_MINOR << 8) + (TINY_BVH_VERSION_MAJOR << 16) + (layout << 24); + s.write( (char*)&header, sizeof( uint32_t ) ); + s.write( (char*)&triCount, sizeof( uint32_t ) ); + s.write( (char*)this, sizeof( BVH8_CPU ) ); + s.write( (char*)bvh8Data, usedBlocks * 64 ); +} + +bool BVH8_CPU::Load( const char* fileName, const uint32_t expectedTris ) +{ + // open file and check contents + std::fstream s{ fileName, s.binary | s.in }; + if (!s) return false; + BVHContext tmp = context; + uint32_t header, fileTriCount; + s.read( (char*)&header, sizeof( uint32_t ) ); + if (((header >> 8) & 255) != TINY_BVH_VERSION_MINOR || + ((header >> 16) & 255) != TINY_BVH_VERSION_MAJOR || + (header & 255) != TINY_BVH_VERSION_SUB || (header >> 24) != layout) return false; + s.read( (char*)&fileTriCount, sizeof( uint32_t ) ); + if (fileTriCount != expectedTris) return false; + // all checks passed; safe to overwrite *this + s.read( (char*)this, sizeof( BVH8_CPU ) ); + context = tmp; // can't load context; function pointers will differ. + bvh8Data = (CacheLine*)AlignedAlloc( usedBlocks * 64 ); + allocatedBlocks = usedBlocks; + s.read( (char*)bvh8Data, usedBlocks * 64 ); + bvh8 = MBVH<8>(); + return true; +} + +void BVH8_CPU::Optimize( const uint32_t iterations, bool extreme ) +{ + bvh8.Optimize( iterations, extreme ); + ConvertFrom( bvh8 ); +} + +void BVH8_CPU::Refit() +{ + bvh8.Refit(); + ConvertFrom( bvh8 ); +} + +float BVH8_CPU::SAHCost( const uint32_t nodeIdx ) const +{ + return bvh8.SAHCost( nodeIdx ); +} + +void BVH8_CPU::ConvertFrom( MBVH<8>& original ) +{ + // get a copy of the input bvh8 + if (&original != &bvh8) ownBVH8 = false; // bvh isn't ours; don't delete in destructor. + bvh8 = original; + // prepare input bvh8 + uint32_t firstIdx = 0; + bvh8.bvh.CombineLeafs( 4, firstIdx, 0 ); + bvh8.bvh.SplitLeafs( 4 ); + bvh8.ConvertFrom( bvh8.bvh, true ); + // allocate if needed + uint32_t nodesNeeded = bvh8.usedNodes, leafsNeeded = bvh8.LeafCount(); + uint32_t blocksNeeded = nodesNeeded * (sizeof( BVHNode ) / 64); // here, block = cacheline. + blocksNeeded += leafsNeeded * (sizeof( BVHTri4Leaf ) / 64); + if (allocatedBlocks < blocksNeeded) + { + AlignedFree( bvh8Data ); + void* (*allocator)(size_t, void*) = malloc64; + #if defined BVH8_ALIGN_4K + allocator = malloc4k; + #elif defined BVH8_ALIGN_32K + allocator = malloc32k; + #endif + bvh8Data = (CacheLine*)allocator( blocksNeeded * 64, 0 ); + allocatedBlocks = blocksNeeded; + } + CopyBasePropertiesFrom( bvh8 ); + // start conversion + uint32_t newBlockPtr = 0, nodeIdx = 0, stack[256], stackPtr = 0; + while (1) + { + const MBVH<8>::MBVHNode& orig = bvh8.mbvhNode[nodeIdx]; + BVHNode* newNode = (BVHNode*)(bvh8Data + newBlockPtr); + newBlockPtr += sizeof( BVHNode ) / 64; + memset( newNode, 0, sizeof( BVHNode ) ); + // calculate the permutation offsets for the node + for (uint32_t q = 0; q < 8; q++) + { + const bvhvec3 D( q & 1 ? 1.0f : -1.0f, q & 2 ? 1.0f : -1.0f, q & 4 ? 1.0f : -1.0f ); + union { float dist[8]; uint32_t idist[8]; }; + for (int i = 0; i < 8; i++) if (orig.child[i] == 0) + dist[i] = 1e30f, idist[i] = (idist[i] & 0xfffffff8) + i; + else + { + const MBVH<8>::MBVHNode& c = bvh8.mbvhNode[orig.child[i]]; + const bvhvec3 p( q & 1 ? c.aabbMin.x : c.aabbMax.x, q & 2 ? c.aabbMin.y : c.aabbMax.y, q & 4 ? c.aabbMin.z : c.aabbMax.z ); + dist[i] = tinybvh_dot( D, p ), idist[i] = (idist[i] & 0xfffffff8) + i; + } + // apply sorting network - https://bertdobbelaere.github.io/sorting_networks.html#N8L19D6 + SORT( 0, 2 ); SORT( 1, 3 ); SORT( 4, 6 ); SORT( 5, 7 ); SORT( 0, 4 ); + SORT( 1, 5 ); SORT( 2, 6 ); SORT( 3, 7 ); SORT( 0, 1 ); SORT( 2, 3 ); + SORT( 4, 5 ); SORT( 6, 7 ); SORT( 2, 4 ); SORT( 3, 5 ); SORT( 1, 4 ); + SORT( 3, 6 ); SORT( 1, 2 ); SORT( 3, 4 ); SORT( 5, 6 ); + for (int i = 0; i < 8; i++) ((uint32_t*)&newNode->perm8)[i] += (idist[i] & 7) << (q * 3); + } + // fill remaining fields + int32_t cidx = 0; + for (int32_t i = 0; i < 8; i++) if (orig.child[i]) + { + const MBVH<8>::MBVHNode& child = bvh8.mbvhNode[orig.child[i]]; + ((float*)&newNode->xmin8)[cidx] = child.aabbMin.x, ((float*)&newNode->xmax8)[cidx] = child.aabbMax.x; + ((float*)&newNode->ymin8)[cidx] = child.aabbMin.y, ((float*)&newNode->ymax8)[cidx] = child.aabbMax.y; + ((float*)&newNode->zmin8)[cidx] = child.aabbMin.z, ((float*)&newNode->zmax8)[cidx] = child.aabbMax.z; + if (child.isLeaf()) + { + // emit leaf node: group of up to 4 triangles in AoS format. + ((uint32_t*)&newNode->child8)[cidx] = newBlockPtr + LEAF_BIT; + BVHTri4Leaf* leaf = (BVHTri4Leaf*)(bvh8Data + newBlockPtr); + newBlockPtr += sizeof( BVHTri4Leaf ) / 64; + for (uint32_t i0, i1, i2, l = 0; l < 4; l++) + { + uint32_t primIdx = bvh8.bvh.primIdx[child.firstTri + tinybvh_min( l, child.triCount - 1u )]; + GET_PRIM_INDICES_I0_I1_I2( bvh8.bvh, primIdx ); + const bvhvec4 v0 = bvh8.bvh.verts[i0], e1 = bvh8.bvh.verts[i1] - v0, e2 = bvh8.bvh.verts[i2] - v0; + leaf->SetData( v0, e1, e2, primIdx, l ); + } + } + else + { + uint32_t* slot = (uint32_t*)&newNode->child8 + cidx; + stack[stackPtr++] = (uint32_t)(slot - (uint32_t*)bvh8Data); + stack[stackPtr++] = orig.child[i]; + } + cidx++; + } + for (; cidx < 8; cidx++) + { + ((float*)&newNode->xmin8)[cidx] = 1e30f, ((float*)&newNode->xmax8)[cidx] = 1.00001e30f; + ((float*)&newNode->ymin8)[cidx] = 1e30f, ((float*)&newNode->ymax8)[cidx] = 1.00001e30f; + ((float*)&newNode->zmin8)[cidx] = 1e30f, ((float*)&newNode->zmax8)[cidx] = 1.00001e30f; + ((uint32_t*)&newNode->child8)[cidx] |= EMPTY_BIT; + } + // pop next task + if (!stackPtr) break; + nodeIdx = stack[--stackPtr]; + const uint32_t offset = stack[--stackPtr]; + ((uint32_t*)bvh8Data)[offset] = newBlockPtr; + } + usedBlocks = newBlockPtr; +} + +// BVH8_CWBVH implementation +// ---------------------------------------------------------------------------- + +BVH8_CWBVH::~BVH8_CWBVH() +{ + if (!ownBVH8) bvh8 = MBVH<8>(); // clear out pointers we don't own. + AlignedFree( bvh8Data ); + AlignedFree( bvh8Tris ); +} + +void BVH8_CWBVH::Optimize( const uint32_t iterations, bool extreme ) +{ + bvh8.Optimize( iterations, extreme ); + ConvertFrom( bvh8, true ); +} + +float BVH8_CWBVH::SAHCost( const uint32_t nodeIdx ) const +{ + return bvh8.SAHCost( nodeIdx ); +} + +void BVH8_CWBVH::Save( const char* fileName ) +{ + std::fstream s{ fileName, s.binary | s.out }; + uint32_t header = TINY_BVH_VERSION_SUB + (TINY_BVH_VERSION_MINOR << 8) + (TINY_BVH_VERSION_MAJOR << 16) + (layout << 24); + s.write( (char*)&header, sizeof( uint32_t ) ); + s.write( (char*)&triCount, sizeof( uint32_t ) ); + s.write( (char*)this, sizeof( BVH8_CWBVH ) ); + s.write( (char*)bvh8Data, usedBlocks * 16 ); + s.write( (char*)bvh8Tris, bvh8.idxCount * 4 * 16 ); +} + +bool BVH8_CWBVH::Load( const char* fileName, const uint32_t expectedTris ) +{ + // open file and check contents + std::fstream s{ fileName, s.binary | s.in }; + if (!s) return false; + BVHContext tmp = context; + uint32_t header, fileTriCount; + s.read( (char*)&header, sizeof( uint32_t ) ); + if (((header >> 8) & 255) != TINY_BVH_VERSION_MINOR || + ((header >> 16) & 255) != TINY_BVH_VERSION_MAJOR || + (header & 255) != TINY_BVH_VERSION_SUB || (header >> 24) != layout) return false; + s.read( (char*)&fileTriCount, sizeof( uint32_t ) ); + if (fileTriCount != expectedTris) return false; + // all checks passed; safe to overwrite *this + s.read( (char*)this, sizeof( BVH8_CWBVH ) ); + context = tmp; // can't load context; function pointers will differ. + bvh8Data = (bvhvec4*)AlignedAlloc( usedBlocks * 16 ); + bvh8Tris = (bvhvec4*)AlignedAlloc( bvh8.idxCount * 4 * 16 ); + allocatedBlocks = usedBlocks; + s.read( (char*)bvh8Data, usedBlocks * 16 ); + s.read( (char*)bvh8Tris, bvh8.idxCount * 4 * 16 ); + bvh8 = MBVH<8>(); + return true; +} + +void BVH8_CWBVH::Build( const bvhvec4* vertices, const uint32_t primCount ) +{ + Build( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +void BVH8_CWBVH::Build( const bvhvec4slice& vertices ) +{ + bvh8.bvh.context = bvh8.context = context; + bvh8.bvh.BuildDefault( vertices ); + bvh8.bvh.Compact(); + bvh8.bvh.SplitLeafs( 3 ); + bvh8.ConvertFrom( bvh8.bvh, false ); + ConvertFrom( bvh8, true ); +} + +void BVH8_CWBVH::Build( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + // build the BVH with a continuous array of bvhvec4 vertices, indexed by 'indices'. + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH8_CWBVH::Build( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + // build the BVH from vertices stored in a slice, indexed by 'indices'. + bvh8.bvh.context = bvh8.context = context; + bvh8.bvh.BuildDefault( vertices, indices, prims ); + bvh8.bvh.Compact(); + bvh8.bvh.SplitLeafs( 3 ); + bvh8.ConvertFrom( bvh8.bvh, true ); + ConvertFrom( bvh8, true ); +} + +void BVH8_CWBVH::BuildHQ( const bvhvec4* vertices, const uint32_t primCount ) +{ + BuildHQ( bvhvec4slice( vertices, primCount * 3, sizeof( bvhvec4 ) ) ); +} + +void BVH8_CWBVH::BuildHQ( const bvhvec4slice& vertices ) +{ + bvh8.bvh.context = bvh8.context = context; + bvh8.bvh.BuildHQ( vertices ); + bvh8.bvh.SplitLeafs( 3 ); + bvh8.ConvertFrom( bvh8.bvh, true ); + ConvertFrom( bvh8, true ); +} + +void BVH8_CWBVH::BuildHQ( const bvhvec4* vertices, const uint32_t* indices, const uint32_t prims ) +{ + Build( bvhvec4slice{ vertices, prims * 3, sizeof( bvhvec4 ) }, indices, prims ); +} + +void BVH8_CWBVH::BuildHQ( const bvhvec4slice& vertices, const uint32_t* indices, uint32_t prims ) +{ + bvh8.bvh.context = bvh8.context = context; + bvh8.bvh.BuildHQ( vertices, indices, prims ); + bvh8.bvh.SplitLeafs( 3 ); + bvh8.ConvertFrom( bvh8.bvh, true ); + ConvertFrom( bvh8, true ); +} + +// Convert a BVH8 to the format specified in: "Efficient Incoherent Ray Traversal on GPUs Through +// Compressed Wide BVHs", Ylitie et al. 2017. Adapted from code by "AlanWBFT". +void BVH8_CWBVH::ConvertFrom( MBVH<8>& original, bool ) +{ + // get a copy of the original bvh8 + if (&original != &bvh8) ownBVH8 = false; // bvh isn't ours; don't delete in destructor. + bvh8 = original; + BVH_FATAL_ERROR_IF( bvh8.mbvhNode[0].isLeaf(), "BVH8_CWBVH::ConvertFrom( .. ), converting a single-node bvh." ); + // allocate memory + uint32_t spaceNeeded = bvh8.triCount * 5; // CWBVH nodes use 80 bytes each. + if (spaceNeeded > allocatedBlocks) + { + bvh8Data = (bvhvec4*)AlignedAlloc( spaceNeeded * 16 ); + bvh8Tris = (bvhvec4*)AlignedAlloc( bvh8.idxCount * 4 * 16 ); + allocatedBlocks = spaceNeeded; + } + memset( bvh8Data, 0, spaceNeeded * 16 ); + memset( bvh8Tris, 0, bvh8.idxCount * 3 * 16 ); + CopyBasePropertiesFrom( bvh8 ); + MBVH<8>::MBVHNode* stackNodePtr[256]; + uint32_t stackNodeAddr[256], stackPtr = 1, nodeDataPtr = 5, triDataPtr = 0; + stackNodePtr[0] = &bvh8.mbvhNode[0], stackNodeAddr[0] = 0; + // start conversion + while (stackPtr > 0) + { + MBVH<8>::MBVHNode* orig = stackNodePtr[--stackPtr]; + const int32_t currentNodeAddr = stackNodeAddr[stackPtr]; + bvhvec3 nodeLo = orig->aabbMin, nodeHi = orig->aabbMax; + // greedy child node ordering + const bvhvec3 nodeCentroid = (nodeLo + nodeHi) * 0.5f; + float cost[8][8]; + int32_t assignment[8]; + bool isSlotEmpty[8]; + for (int32_t s = 0; s < 8; s++) + { + isSlotEmpty[s] = true, assignment[s] = -1; + bvhvec3 ds( + (((s >> 2) & 1) == 1) ? -1.0f : 1.0f, + (((s >> 1) & 1) == 1) ? -1.0f : 1.0f, + (((s >> 0) & 1) == 1) ? -1.0f : 1.0f + ); + for (int32_t i = 0; i < 8; i++) if (orig->child[i] == 0) cost[s][i] = BVH_FAR; else + { + MBVH<8>::MBVHNode* const child = &bvh8.mbvhNode[orig->child[i]]; + bvhvec3 childCentroid = (child->aabbMin + child->aabbMax) * 0.5f; + cost[s][i] = tinybvh_dot( childCentroid - nodeCentroid, ds ); + } + } + while (1) + { + float minCost = BVH_FAR; + int32_t minEntryx = -1, minEntryy = -1; + for (int32_t s = 0; s < 8; s++) for (int32_t i = 0; i < 8; i++) + if (assignment[i] == -1 && isSlotEmpty[s] && cost[s][i] < minCost) + minCost = cost[s][i], minEntryx = s, minEntryy = i; + if (minEntryx == -1 && minEntryy == -1) break; + isSlotEmpty[minEntryx] = false, assignment[minEntryy] = minEntryx; + } + for (int32_t i = 0; i < 8; i++) if (assignment[i] == -1) for (int32_t s = 0; s < 8; s++) if (isSlotEmpty[s]) + { + isSlotEmpty[s] = false, assignment[i] = s; + break; + } + const MBVH<8>::MBVHNode oldNode = *orig; + for (int32_t i = 0; i < 8; i++) orig->child[assignment[i]] = oldNode.child[i]; + // calculate quantization parameters for each axis + const int32_t ex = (int32_t)((int8_t)ceilf( log2f( (nodeHi.x - nodeLo.x) / 255.0f ) )); + const int32_t ey = (int32_t)((int8_t)ceilf( log2f( (nodeHi.y - nodeLo.y) / 255.0f ) )); + const int32_t ez = (int32_t)((int8_t)ceilf( log2f( (nodeHi.z - nodeLo.z) / 255.0f ) )); + // encode output + int32_t internalChildCount = 0, leafChildTriCount = 0, childBaseIndex = 0, triangleBaseIndex = 0; + uint8_t imask = 0; + for (int32_t i = 0; i < 8; i++) + { + if (orig->child[i] == 0) continue; + MBVH<8>::MBVHNode* const child = &bvh8.mbvhNode[orig->child[i]]; + const int32_t qlox = (int32_t)floorf( (child->aabbMin.x - nodeLo.x) / powf( 2, (float)ex ) ); + const int32_t qloy = (int32_t)floorf( (child->aabbMin.y - nodeLo.y) / powf( 2, (float)ey ) ); + const int32_t qloz = (int32_t)floorf( (child->aabbMin.z - nodeLo.z) / powf( 2, (float)ez ) ); + const int32_t qhix = (int32_t)ceilf( (child->aabbMax.x - nodeLo.x) / powf( 2, (float)ex ) ); + const int32_t qhiy = (int32_t)ceilf( (child->aabbMax.y - nodeLo.y) / powf( 2, (float)ey ) ); + const int32_t qhiz = (int32_t)ceilf( (child->aabbMax.z - nodeLo.z) / powf( 2, (float)ez ) ); + uint8_t* const baseAddr = (uint8_t*)&bvh8Data[currentNodeAddr + 2]; + baseAddr[i + 0] = (uint8_t)qlox, baseAddr[i + 24] = (uint8_t)qhix; + baseAddr[i + 8] = (uint8_t)qloy, baseAddr[i + 32] = (uint8_t)qhiy; + baseAddr[i + 16] = (uint8_t)qloz, baseAddr[i + 40] = (uint8_t)qhiz; + if (!child->isLeaf()) + { + // interior node, set params and push onto stack + const int32_t childNodeAddr = nodeDataPtr; + if (internalChildCount++ == 0) childBaseIndex = childNodeAddr / 5; + nodeDataPtr += 5, imask |= 1 << i; + // set the meta field - This calculation assumes children are stored contiguously. + uint8_t* const childMetaField = ((uint8_t*)&bvh8Data[currentNodeAddr + 1]) + 8; + childMetaField[i] = (1 << 5) | (24 + (uint8_t)i); // I don't see how this accounts for empty children? + stackNodePtr[stackPtr] = child, stackNodeAddr[stackPtr++] = childNodeAddr; // counted in float4s + internalChildCount++; + continue; + } + // leaf node + const uint32_t tcount = child->triCount; // will not exceed 3. + if (leafChildTriCount == 0) triangleBaseIndex = triDataPtr; + int32_t unaryEncodedTriCount = tcount == 1 ? 0b001 : tcount == 2 ? 0b011 : 0b111; + // set the meta field - This calculation assumes children are stored contiguously. + uint8_t* const childMetaField = ((uint8_t*)&bvh8Data[currentNodeAddr + 1]) + 8; + childMetaField[i] = (uint8_t)((unaryEncodedTriCount << 5) | leafChildTriCount); + leafChildTriCount += tcount; + for (uint32_t j = 0; j < tcount; j++) + { + int32_t triIdx = bvh8.bvh.primIdx[child->firstTri + j]; + uint32_t ti0, ti1, ti2; + if (bvh8.bvh.vertIdx) + ti0 = bvh8.bvh.vertIdx[triIdx * 3], + ti1 = bvh8.bvh.vertIdx[triIdx * 3 + 1], + ti2 = bvh8.bvh.vertIdx[triIdx * 3 + 2]; + else + ti0 = triIdx * 3, ti1 = triIdx * 3 + 1, ti2 = triIdx * 3 + 2; + #ifdef CWBVH_COMPRESSED_TRIS + PrecomputeTriangle( verts, ti0, ti1, ti2, (float*)&bvh8Tris[triDataPtr] ); + bvh8Tris[triDataPtr + 3] = bvhvec4( 0, 0, 0, *(float*)&triIdx ); + triDataPtr += 4; + #else + bvhvec4 t = bvh8.bvh.verts[ti0]; + bvh8Tris[triDataPtr + 0] = bvh8.bvh.verts[ti2] - t; + bvh8Tris[triDataPtr + 1] = bvh8.bvh.verts[ti1] - t; + t.w = *(float*)&triIdx; + bvh8Tris[triDataPtr + 2] = t, triDataPtr += 3; + #endif + } + } + uint8_t exyzAndimask[4] = { *(uint8_t*)&ex, *(uint8_t*)&ey, *(uint8_t*)&ez, imask }; + bvh8Data[currentNodeAddr + 0] = bvhvec4( nodeLo, *(float*)&exyzAndimask ); + bvh8Data[currentNodeAddr + 1].x = *(float*)&childBaseIndex; + bvh8Data[currentNodeAddr + 1].y = *(float*)&triangleBaseIndex; + } + usedBlocks = nodeDataPtr; +} + +// ============================================================================ +// +// I M P L E M E N T A T I O N - A V X / S S E C O D E +// +// ============================================================================ + +#ifdef BVH_USESSE + +inline __m128 fastrcp4( const __m128 a ) +{ + __m128 res = _mm_rcp_ps( a ); + __m128 muls = _mm_mul_ps( a, _mm_mul_ps( res, res ) ); + return _mm_sub_ps( _mm_add_ps( res, res ), muls ); +} + +static uint32_t __popc( uint32_t x ) +{ +#if defined _MSC_VER && !defined __clang__ + return __popcnt( x ); +#elif defined __GNUC__ || defined __clang__ + return __builtin_popcount( x ); +#endif +} + +static const unsigned __A = 0x03020100, __B = 0x07060504, __C = 0x0B0A0908, __D = 0x0F0E0D0C; +ALIGNED( 64 ) static __m128i idxLUT4_[16] = { + _mm_set_epi32( 0, 0, 0, 0 ), // 0000 + _mm_set_epi32( 0, 0, 0, __A ), // 0001 + _mm_set_epi32( 0, 0, 0, __B ), // 0010 + _mm_set_epi32( 0, 0, __B, __A ), // 0011 + _mm_set_epi32( 0, 0, 0, __C ), // 0100 + _mm_set_epi32( 0, 0, __C, __A ), // 0101 + _mm_set_epi32( 0, 0, __C, __B ), // 0110 + _mm_set_epi32( 0, __C, __B, __A ), // 0111 + _mm_set_epi32( 0, 0, 0, __D ), // 1000 + _mm_set_epi32( 0, 0, __D, __A ), // 1001 + _mm_set_epi32( 0, 0, __D, __B ), // 1010 + _mm_set_epi32( 0, __D, __B, __A ), // 1011 + _mm_set_epi32( 0, 0, __D, __C ), // 1100 + _mm_set_epi32( 0, __D, __C, __A ), // 1101 + _mm_set_epi32( 0, __D, __C, __B ), // 1110 + _mm_set_epi32( __D, __C, __B, __A ) // 1111 +}; + +int32_t BVH4_CPU::Intersect( Ray& ray ) const +{ + VALIDATE_RAY( ray ); + const bool posX = ray.D.x >= 0, posY = ray.D.y >= 0, posZ = ray.D.z >= 0; + if (!posX) goto negx; + if (posY) { if (posZ) return Intersect( ray ); else return Intersect( ray ); } + if (posZ) return Intersect( ray ); else return Intersect( ray ); +negx: + if (posY) { if (posZ) return Intersect( ray ); else return Intersect( ray ); } + if (posZ) return Intersect( ray ); else return Intersect( ray ); +} + +template int32_t BVH4_CPU::Intersect( Ray& ray ) const +{ + ALIGNED( 64 ) int32_t nodeStack[256]; + ALIGNED( 64 ) float distStack[256]; + const __m128 zero4 = _mm_setzero_ps(); + __m128 t4 = _mm_set1_ps( ray.hit.t ); + ALIGNED( 64 ) int32_t stackPtr = 0, nodeIdx = 0; + union ALIGNED( 32 ) { __m256i c8s; uint32_t cs[8]; }; + constexpr int signShift = (posX ? 2 : 0) + (posY ? 4 : 0) + (posZ ? 8 : 0); + const __m128 rx4 = _mm_set1_ps( ray.O.x * ray.rD.x ), rdx4 = _mm_set1_ps( ray.rD.x ); + const __m128 ry4 = _mm_set1_ps( ray.O.y * ray.rD.y ), rdy4 = _mm_set1_ps( ray.rD.y ); + const __m128 rz4 = _mm_set1_ps( ray.O.z * ray.rD.z ), rdz4 = _mm_set1_ps( ray.rD.z ); + const __m128 ox4 = _mm_set1_ps( ray.O.x ), oy4 = _mm_set1_ps( ray.O.y ), oz4 = _mm_set1_ps( ray.O.z ); + const __m128 dx4 = _mm_set1_ps( ray.D.x ), dy4 = _mm_set1_ps( ray.D.y ), dz4 = _mm_set1_ps( ray.D.z ); + const __m128 one4 = _mm_set1_ps( 1 ), inf4 = _mm_set1_ps( 1e34f ); +#ifndef __AVX__ + const __m128i shftmsk4 = _mm_set1_epi32( 3 ), mul4 = _mm_set1_epi32( 0x04040404 ), add4 = _mm_set1_epi32( 0x03020100 ); +#endif +#ifdef _DEBUG + // sorry, not even this can be tolerated in this function. Only in debug. + uint32_t steps = 0; +#endif + while (1) + { + #ifdef _DEBUG + steps++; + #endif + while (!(nodeIdx & LEAF_BIT)) + { + const BVHNode* n = (BVHNode*)(bvh4Data + nodeIdx); + const __m128 tx1 = _mm_sub_ps( _mm_mul_ps( posX ? n->xmin4 : n->xmax4, rdx4 ), rx4 ); + const __m128 ty1 = _mm_sub_ps( _mm_mul_ps( posY ? n->ymin4 : n->ymax4, rdy4 ), ry4 ); + const __m128 tz1 = _mm_sub_ps( _mm_mul_ps( posZ ? n->zmin4 : n->zmax4, rdz4 ), rz4 ); + const __m128 tx2 = _mm_sub_ps( _mm_mul_ps( posX ? n->xmax4 : n->xmin4, rdx4 ), rx4 ); + const __m128 ty2 = _mm_sub_ps( _mm_mul_ps( posY ? n->ymax4 : n->ymin4, rdy4 ), ry4 ); + const __m128 tz2 = _mm_sub_ps( _mm_mul_ps( posZ ? n->zmax4 : n->zmin4, rdz4 ), rz4 ); + __m128 tmin = _mm_max_ps( _mm_max_ps( _mm_max_ps( zero4, tx1 ), ty1 ), tz1 ); + const __m128 tmax = _mm_min_ps( _mm_min_ps( _mm_min_ps( tx2, t4 ), ty2 ), tz2 ); + const __m128 mask4 = _mm_cmple_ps( tmin, tmax ); + const uint32_t mask = _mm_movemask_ps( mask4 ); + const uint32_t validNodes = __popc( mask ); + if (validNodes == 1) + { + const uint32_t lane = __bfind( mask ); + nodeIdx = ((uint32_t*)&n->child4)[lane]; + } + else if (validNodes) + { + #ifdef __AVX__ + // avx1 path + const __m128i index = _mm_srli_epi32( n->perm4, signShift ); + const uint32_t m = _mm_movemask_ps( _mm_permutevar_ps( mask4, index ) ); + tmin = _mm_permutevar_ps( tmin, index ); + const __m128i c4 = _mm_castps_si128( _mm_permutevar_ps( _mm_castsi128_ps( n->child4 ), index ) ); + #else + // sse4.2 path, 3 extra ops to emulate _mm_permutevar_ps via _mm_shuffle_epi8 + const __m128i raw4 = _mm_and_si128( _mm_srli_epi32( n->perm4, signShift ), shftmsk4 ); + const __m128i shfl16 = _mm_add_epi32( _mm_mullo_epi32( raw4, mul4 ), add4 ); + const uint32_t m = _mm_movemask_ps( _mm_castsi128_ps( _mm_shuffle_epi8( _mm_castps_si128( mask4 ), shfl16 ) ) ); + tmin = _mm_castsi128_ps( _mm_shuffle_epi8( _mm_castps_si128( tmin ), shfl16 ) ); + const __m128i c4 = _mm_shuffle_epi8( n->child4, shfl16 ); + #endif + const __m128i cpi = idxLUT4_[m]; + const __m128 dist4 = _mm_castsi128_ps( _mm_shuffle_epi8( _mm_castps_si128( tmin ), cpi ) ); + const __m128i child4 = _mm_shuffle_epi8( c4, cpi ); + _mm_storeu_si128( (__m128i*)(nodeStack + stackPtr), child4 ); + _mm_storeu_ps( (float*)(distStack + stackPtr), dist4 ); + stackPtr += validNodes - 1; + nodeIdx = nodeStack[stackPtr]; + } + else + { + if (!stackPtr) goto the_end; + nodeIdx = nodeStack[--stackPtr]; + } + } + // Moeller-Trumbore ray/triangle intersection algorithm for four triangles + uint32_t n; + memcpy( &n, &nodeIdx, 4 ); + const BVHTri4Leaf* leaf = (BVHTri4Leaf*)(bvh4Data + (n & 0x1fffffff)); + const __m128 hx4 = _mm_sub_ps( _mm_mul_ps( dy4, leaf->e2z4 ), _mm_mul_ps( dz4, leaf->e2y4 ) ); + const __m128 hy4 = _mm_sub_ps( _mm_mul_ps( dz4, leaf->e2x4 ), _mm_mul_ps( dx4, leaf->e2z4 ) ); + const __m128 hz4 = _mm_sub_ps( _mm_mul_ps( dx4, leaf->e2y4 ), _mm_mul_ps( dy4, leaf->e2x4 ) ); + const __m128 sx4 = _mm_sub_ps( ox4, leaf->v0x4 ), sy4 = _mm_sub_ps( oy4, leaf->v0y4 ), sz4 = _mm_sub_ps( oz4, leaf->v0z4 ); + const __m128 det4 = _mm_add_ps( _mm_mul_ps( leaf->e1z4, hz4 ), _mm_add_ps( _mm_mul_ps( leaf->e1x4, hx4 ), _mm_mul_ps( leaf->e1y4, hy4 ) ) ); + const __m128 qz4 = _mm_sub_ps( _mm_mul_ps( sx4, leaf->e1y4 ), _mm_mul_ps( sy4, leaf->e1x4 ) ); + const __m128 qx4 = _mm_sub_ps( _mm_mul_ps( sy4, leaf->e1z4 ), _mm_mul_ps( sz4, leaf->e1y4 ) ); + const __m128 qy4 = _mm_sub_ps( _mm_mul_ps( sz4, leaf->e1x4 ), _mm_mul_ps( sx4, leaf->e1z4 ) ); + const __m128 inv_det4 = fastrcp4( det4 ); + const __m128 u4 = _mm_mul_ps( _mm_add_ps( _mm_mul_ps( sz4, hz4 ), _mm_add_ps( _mm_mul_ps( sx4, hx4 ), _mm_mul_ps( sy4, hy4 ) ) ), inv_det4 ); + const __m128 v4 = _mm_mul_ps( _mm_add_ps( _mm_mul_ps( dz4, qz4 ), _mm_add_ps( _mm_mul_ps( dx4, qx4 ), _mm_mul_ps( dy4, qy4 ) ) ), inv_det4 ); + const __m128 ta4 = _mm_mul_ps( _mm_add_ps( _mm_mul_ps( leaf->e2z4, qz4 ), _mm_add_ps( _mm_mul_ps( leaf->e2x4, qx4 ), _mm_mul_ps( leaf->e2y4, qy4 ) ) ), inv_det4 ); + const __m128 mask1 = _mm_and_ps( _mm_cmpge_ps( u4, zero4 ), _mm_cmpge_ps( v4, zero4 ) ); + const __m128 mask2 = _mm_cmple_ps( _mm_add_ps( u4, v4 ), one4 ); + const __m128 mask3 = _mm_and_ps( _mm_cmplt_ps( ta4, t4 ), _mm_cmpgt_ps( ta4, zero4 ) ); + __m128 combined = _mm_and_ps( _mm_and_ps( mask1, mask2 ), mask3 ); + uint32_t imask = _mm_movemask_ps( combined ); + // evaluate opacity map, if present (SSE version). + if (opmap) if (imask) + { + const __m128 fN4 = _mm_set1_ps( (float)opmapN ); + const __m128i row4 = _mm_cvttps_epi32( _mm_mul_ps( _mm_add_ps( u4, v4 ), fN4 ) ); + const __m128i dia4 = _mm_cvttps_epi32( _mm_mul_ps( _mm_sub_ps( one4, u4 ), fN4 ) ); + const __m128i v0 = _mm_mullo_epi32( row4, row4 ); + const __m128i v1 = _mm_cvttps_epi32( _mm_mul_ps( v4, fN4 ) ); + const __m128i v2 = _mm_sub_epi32( dia4, _mm_sub_epi32( _mm_set1_epi32( opmapN - 1 ), row4 ) ); + union { uint32_t idx[4]; __m128i idx4; }; + union { uint32_t omask[4]; __m128 omask4; }; + idx4 = _mm_add_epi32( _mm_add_epi32( v0, v1 ), v2 ); + // proceed with scalar code for gather operation - TODO: better approach? + omask[0] = omask[1] = omask[2] = omask[3] = 0; + for (int i = 0; i < 4; i++) if (imask & (1 << i)) + { + uint32_t* om = opmap + leaf->primIdx[i] * ((opmapN * opmapN + 31) >> 5); + if (om[idx[i] >> 5] & (1 << (idx[i] & 31))) omask[i] = 0xffffffff; + } + // combine + combined = _mm_and_ps( combined, omask4 ); + imask = _mm_movemask_ps( combined ); + } + if (imask) + { + const __m128 dist4 = _mm_blendv_ps( inf4, ta4, combined ); + // compute broadcasted horizontal minimum of dist4 + const __m128 a = _mm_min_ps( dist4, _mm_shuffle_ps( dist4, dist4, _MM_SHUFFLE( 2, 1, 0, 3 ) ) ); + const __m128 c = _mm_min_ps( a, _mm_shuffle_ps( a, a, _MM_SHUFFLE( 1, 0, 3, 2 ) ) ); + const uint32_t lane = __bfind( _mm_movemask_ps( _mm_cmpeq_ps( c, dist4 ) ) ); + // update hit record + const __m128 _d4 = dist4; + const float t = ((float*)&_d4)[lane]; + const __m128 _u4 = u4, _v4 = v4; + ray.hit.t = t, ray.hit.u = ((float*)&_u4)[lane], ray.hit.v = ((float*)&_v4)[lane]; + #if INST_IDX_BITS == 32 + ray.hit.prim = leaf->primIdx[lane], ray.hit.inst = ray.instIdx; + #else + ray.hit.prim = leaf->primIdx[lane] + ray.instIdx; + #endif + t4 = _mm_set1_ps( t ); + // compress stack + uint32_t outStackPtr = 0; + for (int32_t i = 0; i < stackPtr; i += 4) + { + __m128i node4 = _mm_load_si128( (__m128i*)(nodeStack + i) ); + __m128 d4 = _mm_load_ps( (float*)(distStack + i) ); + const uint32_t mask = _mm_movemask_ps( _mm_cmple_ps( d4, t4 ) ); + const __m128i shfl16 = idxLUT4_[mask]; + const __m128i dst4 = _mm_shuffle_epi8( _mm_castps_si128( d4 ), shfl16 ); + node4 = _mm_shuffle_epi8( node4, shfl16 ); + _mm_storeu_si128( (__m128i*)(distStack + outStackPtr), dst4 ); + _mm_storeu_si128( (__m128i*)(nodeStack + outStackPtr), node4 ); + const int32_t numItems = tinybvh_min( 4, stackPtr - i ), validMask = (1 << numItems) - 1; + outStackPtr += __popc( mask & validMask ); + } + stackPtr = outStackPtr; + } + if (!stackPtr) break; + nodeIdx = nodeStack[--stackPtr]; + } +the_end: +#ifdef _DEBUG + return steps; +#else + return 0; +#endif +} + +bool BVH4_CPU::IsOccluded( const Ray& ray ) const +{ + VALIDATE_RAY( ray ); + const bool posX = ray.D.x >= 0, posY = ray.D.y >= 0, posZ = ray.D.z >= 0; + if (!posX) goto negx; + if (posY) { if (posZ) return IsOccluded( ray ); else return IsOccluded( ray ); } + if (posZ) return IsOccluded( ray ); else return IsOccluded( ray ); +negx: + if (posY) { if (posZ) return IsOccluded( ray ); else return IsOccluded( ray ); } + if (posZ) return IsOccluded( ray ); else return IsOccluded( ray ); +} + +template bool BVH4_CPU::IsOccluded( const Ray& ray ) const +{ + ALIGNED( 64 ) uint32_t nodeStack[256]; + ALIGNED( 64 ) int32_t stackPtr = 0, nodeIdx = 0; + const __m128 t4 = _mm_set1_ps( ray.hit.t ); + const __m128 rx4 = _mm_set1_ps( ray.O.x * ray.rD.x ), rdx4 = _mm_set1_ps( ray.rD.x ); + const __m128 ry4 = _mm_set1_ps( ray.O.y * ray.rD.y ), rdy4 = _mm_set1_ps( ray.rD.y ); + const __m128 rz4 = _mm_set1_ps( ray.O.z * ray.rD.z ), rdz4 = _mm_set1_ps( ray.rD.z ); + const __m128 ox4 = _mm_set1_ps( ray.O.x ), oy4 = _mm_set1_ps( ray.O.y ), oz4 = _mm_set1_ps( ray.O.z ); + const __m128 dx4 = _mm_set1_ps( ray.D.x ), dy4 = _mm_set1_ps( ray.D.y ), dz4 = _mm_set1_ps( ray.D.z ); + const __m128 one4 = _mm_set1_ps( 1.0f ), zero4 = _mm_setzero_ps(); + while (1) + { + while (!(nodeIdx & LEAF_BIT)) + { + const BVHNode* n = (BVHNode*)(bvh4Data + nodeIdx); + const __m128 tx1 = _mm_sub_ps( _mm_mul_ps( posX ? n->xmin4 : n->xmax4, rdx4 ), rx4 ); + const __m128 ty1 = _mm_sub_ps( _mm_mul_ps( posY ? n->ymin4 : n->ymax4, rdy4 ), ry4 ); + const __m128 tz1 = _mm_sub_ps( _mm_mul_ps( posZ ? n->zmin4 : n->zmax4, rdz4 ), rz4 ); + const __m128 tx2 = _mm_sub_ps( _mm_mul_ps( posX ? n->xmax4 : n->xmin4, rdx4 ), rx4 ); + const __m128 ty2 = _mm_sub_ps( _mm_mul_ps( posY ? n->ymax4 : n->ymin4, rdy4 ), ry4 ); + const __m128 tz2 = _mm_sub_ps( _mm_mul_ps( posZ ? n->zmax4 : n->zmin4, rdz4 ), rz4 ); + const __m128 tmin = _mm_max_ps( _mm_max_ps( _mm_max_ps( _mm_setzero_ps(), tx1 ), ty1 ), tz1 ); + const __m128 tmax = _mm_min_ps( _mm_min_ps( _mm_min_ps( tx2, t4 ), ty2 ), tz2 ); + const __m128 mask4 = _mm_cmple_ps( tmin, tmax ); + const uint32_t mask = _mm_movemask_ps( mask4 ); + const uint32_t validNodes = __popc( mask ); + if (validNodes == 1) + { + const uint32_t lane = __bfind( mask ); + nodeIdx = ((uint32_t*)&n->child4)[lane]; + } + else if (validNodes) + { + const __m128i cpi = idxLUT4_[mask]; + const __m128i child4 = _mm_shuffle_epi8( n->child4, cpi ); + _mm_storeu_si128( (__m128i*)(nodeStack + stackPtr), child4 ); + stackPtr += validNodes - 1; + nodeIdx = nodeStack[stackPtr]; + } + else + { + if (!stackPtr) return false; + nodeIdx = nodeStack[--stackPtr]; + } + } + uint32_t n; + memcpy( &n, &nodeIdx, 4 ); + // Moeller-Trumbore ray/triangle intersection algorithm for four triangles + const BVHTri4Leaf* leaf = (BVHTri4Leaf*)(bvh4Data + (n & 0x1fffffff)); + const __m128 hx4 = _mm_sub_ps( _mm_mul_ps( dy4, leaf->e2z4 ), _mm_mul_ps( dz4, leaf->e2y4 ) ); + const __m128 hy4 = _mm_sub_ps( _mm_mul_ps( dz4, leaf->e2x4 ), _mm_mul_ps( dx4, leaf->e2z4 ) ); + const __m128 hz4 = _mm_sub_ps( _mm_mul_ps( dx4, leaf->e2y4 ), _mm_mul_ps( dy4, leaf->e2x4 ) ); + const __m128 sx4 = _mm_sub_ps( ox4, leaf->v0x4 ), sy4 = _mm_sub_ps( oy4, leaf->v0y4 ), sz4 = _mm_sub_ps( oz4, leaf->v0z4 ); + const __m128 det4 = _mm_add_ps( _mm_mul_ps( leaf->e1z4, hz4 ), _mm_add_ps( _mm_mul_ps( leaf->e1x4, hx4 ), _mm_mul_ps( leaf->e1y4, hy4 ) ) ); + const __m128 qz4 = _mm_sub_ps( _mm_mul_ps( sx4, leaf->e1y4 ), _mm_mul_ps( sy4, leaf->e1x4 ) ); + const __m128 qx4 = _mm_sub_ps( _mm_mul_ps( sy4, leaf->e1z4 ), _mm_mul_ps( sz4, leaf->e1y4 ) ); + const __m128 qy4 = _mm_sub_ps( _mm_mul_ps( sz4, leaf->e1x4 ), _mm_mul_ps( sx4, leaf->e1z4 ) ); + const __m128 inv_det4 = fastrcp4( det4 ); + const __m128 u4 = _mm_mul_ps( _mm_add_ps( _mm_mul_ps( sz4, hz4 ), _mm_add_ps( _mm_mul_ps( sx4, hx4 ), _mm_mul_ps( sy4, hy4 ) ) ), inv_det4 ); + const __m128 v4 = _mm_mul_ps( _mm_add_ps( _mm_mul_ps( dz4, qz4 ), _mm_add_ps( _mm_mul_ps( dx4, qx4 ), _mm_mul_ps( dy4, qy4 ) ) ), inv_det4 ); + const __m128 ta4 = _mm_mul_ps( _mm_add_ps( _mm_mul_ps( leaf->e2z4, qz4 ), _mm_add_ps( _mm_mul_ps( leaf->e2x4, qx4 ), _mm_mul_ps( leaf->e2y4, qy4 ) ) ), inv_det4 ); + const __m128 mask1 = _mm_and_ps( _mm_cmpge_ps( u4, zero4 ), _mm_cmpge_ps( v4, zero4 ) ); + const __m128 mask2 = _mm_cmple_ps( _mm_add_ps( u4, v4 ), one4 ); + const __m128 mask3 = _mm_and_ps( _mm_cmplt_ps( ta4, t4 ), _mm_cmpgt_ps( ta4, zero4 ) ); + __m128 combined = _mm_and_ps( _mm_and_ps( mask1, mask2 ), mask3 ); + // evaluate opacity map, if present (SSE version). + if (_mm_movemask_ps( combined )) + { + if (!opmap) return true; + // evaluate opacity map, SSE version. + const __m128 fN4 = _mm_set1_ps( (float)opmapN ); + const __m128i row4 = _mm_cvttps_epi32( _mm_mul_ps( _mm_add_ps( u4, v4 ), fN4 ) ); + const __m128i dia4 = _mm_cvttps_epi32( _mm_mul_ps( _mm_sub_ps( one4, u4 ), fN4 ) ); + const __m128i v0 = _mm_mullo_epi32( row4, row4 ); + const __m128i v1 = _mm_cvttps_epi32( _mm_mul_ps( v4, fN4 ) ); + const __m128i v2 = _mm_sub_epi32( dia4, _mm_sub_epi32( _mm_set1_epi32( opmapN - 1 ), row4 ) ); + union { uint32_t idx[4]; __m128i idx4; }; + idx4 = _mm_add_epi32( _mm_add_epi32( v0, v1 ), v2 ); + // proceed with scalar code for gather operation - TODO: better approach? + const uint32_t imask = _mm_movemask_ps( combined ); + for (int i = 0; i < 4; i++) if (imask & (1 << i)) + { + uint32_t* om = opmap + leaf->primIdx[i] * ((opmapN * opmapN + 31) >> 5); + if (om[idx[i] >> 5] & (1 << (idx[i] & 31))) return true; + } + } + // we continue. + if (!stackPtr) return false; + nodeIdx = nodeStack[--stackPtr]; + } +} + +#endif // BVH_USESSE + +#ifdef BVH_USEAVX + +// Ultra-fast single-threaded AVX binned-SAH-builder. +// This code produces BVHs nearly identical to reference, but much faster. +// On a 12th gen laptop i7 CPU, Sponza Crytek (~260k tris) is processed in 51ms. +// The code relies on the availability of AVX instructions. AVX2 is not needed. +#if defined _MSC_VER && !defined __clang__ +#define LANE(a,b) a.m128_f32[b] +#define LANE8(a,b) a.m256_f32[b] +// Not using clang/g++ method under MSCC; compiler may benefit from .m128_i32. +#define ILANE(a,b) a.m128i_i32[b] +#else +#define LANE(a,b) a[b] +#define LANE8(a,b) a[b] +// Below method reduces to a single instruction. +#define ILANE(a,b) _mm_cvtsi128_si32(_mm_castps_si128( _mm_shuffle_ps(_mm_castsi128_ps( a ), _mm_castsi128_ps( a ), b))) +#endif +inline __m256 fastrcp8( const __m256 a ) +{ + __m256 res = _mm256_rcp_ps( a ); + __m256 muls = _mm256_mul_ps( a, _mm256_mul_ps( res, res ) ); + return _mm256_sub_ps( _mm256_add_ps( res, res ), muls ); +} +inline float halfArea( const __m128 a /* a contains extent of aabb */ ) +{ + return LANE( a, 0 ) * LANE( a, 1 ) + LANE( a, 1 ) * LANE( a, 2 ) + LANE( a, 2 ) * LANE( a, 3 ); +} +inline float halfArea( const __m256& a /* a contains aabb itself, with min.xyz negated */ ) +{ +#ifndef _MSC_VER + // g++ doesn't seem to like the faster construct + float* c = (float*)&a; + float ex = c[4] + c[0], ey = c[5] + c[1], ez = c[6] + c[2]; + return ex * ey + ey * ez + ez * ex; +#else + const __m128 q = _mm256_castps256_ps128( _mm256_add_ps( _mm256_permute2f128_ps( a, a, 5 ), a ) ); + const __m128 v = _mm_mul_ps( q, _mm_shuffle_ps( q, q, 9 ) ); + return LANE( v, 0 ) + LANE( v, 1 ) + LANE( v, 2 ); +#endif +} +#define PROCESS_PLANE( a, pos, ANLR, lN, rN, lb, rb ) if (lN * rN != 0) { \ + ANLR = halfArea( lb ) * (float)lN + halfArea( rb ) * (float)rN; if (ANLR < splitCost) \ + splitCost = ANLR, bestAxis = a, bestPos = pos, bestLBox = lb, bestRBox = rb; } +#if defined _MSC_VER +#pragma warning ( push ) +#pragma warning( disable:4701 ) // "potentially uninitialized local variable 'bestLBox' used" +#pragma warning (disable:4324) // "lambda structure was padded due to alignment specifier" +#elif defined __GNUC__ && !defined __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +void BVH::BuildAVX( const bvhvec4* vertices, const uint32_t primCount ) +{ + // build the BVH with a continuous array of bvhvec4 vertices: + // in this case, the stride for the slice is 16 bytes. + BuildAVX( bvhvec4slice{ vertices, primCount * 3, sizeof( bvhvec4 ) } ); +} +void BVH::BuildAVX( const bvhvec4slice& vertices ) +{ + PrepareAVXBuild( vertices, 0, 0 ); + BuildAVXSubtree( 0u, 0u ); +} +void BVH::BuildAVX( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ) +{ + // build the BVH with an indexed array of bvhvec4 vertices. + bvhvec4slice slice( vertices, primCount * 3, sizeof( bvhvec4 ) ); + BuildAVX( slice, indices, primCount ); +} +void BVH::BuildAVX( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ) +{ + PrepareAVXBuild( vertices, indices, primCount ); + BuildAVXSubtree( 0u, 0u ); +} +void BVH::PrepareAVXBuild( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t prims ) +{ + BVH_FATAL_ERROR_IF( vertices.count == 0, "BVH::PrepareAVXBuild( .. ), primCount == 0." ); + BVH_FATAL_ERROR_IF( vertices.stride & 15, "BVH::PrepareAVXBuild( .. ), stride must be multiple of 16." ); + // some constants + static const __m128 min4 = _mm_set1_ps( BVH_FAR ), max4 = _mm_set1_ps( -BVH_FAR ); + // reset node pool + uint32_t primCount = prims > 0 ? prims : vertices.count / 3; + const uint32_t spaceNeeded = primCount * 2; + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + AlignedFree( primIdx ); + AlignedFree( fragment ); + primIdx = (uint32_t*)AlignedAlloc( primCount * sizeof( uint32_t ) ); + bvhNode = (BVHNode*)AlignedAlloc( spaceNeeded * sizeof( BVHNode ) ); + allocatedNodes = spaceNeeded; + memset( &bvhNode[1], 0, 32 ); // avoid crash in refit. + fragment = (Fragment*)AlignedAlloc( primCount * sizeof( Fragment ) ); + } + else BVH_FATAL_ERROR_IF( !rebuildable, "BVH::BuildAVX( .. ), bvh not rebuildable." ); + verts = vertices; // note: we're not copying this data; don't delete. + vertIdx = (uint32_t*)indices; + triCount = idxCount = primCount; + newNodePtr = 2; + struct FragSSE { __m128 bmin4, bmax4; }; + FragSSE* frag4 = (FragSSE*)fragment; + const __m128* verts4 = (__m128*)verts.data; // that's why it must be 16-byte aligned. + // assign all triangles to the root node + BVHNode& root = bvhNode[0]; + root.leftFirst = 0, root.triCount = triCount; + // initialize fragments and update root bounds + __m128 rootMin = min4, rootMax = max4; + uint32_t stride4 = verts.stride / 16; + if (indices) + { + BVH_FATAL_ERROR_IF( vertices.count == 0, "BVH::PrepareAVXBuild( .. ), empty vertex slice." ); + BVH_FATAL_ERROR_IF( prims == 0, "BVH::PrepareAVXBuild( .. ), prims == 0." ); + // build the BVH over indexed triangles + for (uint32_t i = 0; i < triCount; i++) + { + const uint32_t i0 = indices[i * 3], i1 = indices[i * 3 + 1], i2 = indices[i * 3 + 2]; + const __m128 v0 = verts4[i0 * stride4], v1 = verts4[i1 * stride4], v2 = verts4[i2 * stride4]; + const __m128 t1 = _mm_min_ps( _mm_min_ps( v0, v1 ), v2 ); + const __m128 t2 = _mm_max_ps( _mm_max_ps( v0, v1 ), v2 ); + frag4[i].bmin4 = t1, frag4[i].bmax4 = t2, rootMin = _mm_min_ps( rootMin, t1 ), rootMax = _mm_max_ps( rootMax, t2 ); + primIdx[i] = i; + } + } + else + { + BVH_FATAL_ERROR_IF( vertices.count == 0, "BVH::PrepareAVXBuild( .. ), empty vertex slice." ); + BVH_FATAL_ERROR_IF( prims != 0, "BVH::PrepareAVXBuild( .. ), indices == 0." ); + // build the BVH over a list of vertices: three per triangle + for (uint32_t i = 0; i < triCount; i++) + { + const __m128 v0 = verts4[(i * 3) * stride4], v1 = verts4[(i * 3 + 1) * stride4], v2 = verts4[(i * 3 + 2) * stride4]; + const __m128 t1 = _mm_min_ps( _mm_min_ps( v0, v1 ), v2 ); + const __m128 t2 = _mm_max_ps( _mm_max_ps( v0, v1 ), v2 ); + frag4[i].bmin4 = t1, frag4[i].bmax4 = t2, rootMin = _mm_min_ps( rootMin, t1 ), rootMax = _mm_max_ps( rootMax, t2 ); + primIdx[i] = i; + } + } + root.aabbMin = *(bvhvec3*)&rootMin, root.aabbMax = *(bvhvec3*)&rootMax; + bvh_over_indices = indices != nullptr; +} + +void BVH::BuildAVXBinTask( const uint32_t first, const uint32_t last, __m256* binbox, __m256* orig, + uint32_t* count, const __m128& nmin4, const __m128& rpd4 ) +{ + struct FragSSE { __m128 bmin4, bmax4; }; + FragSSE* frag4 = (FragSSE*)fragment; + __m256* frag8 = (__m256*)fragment; + uint32_t fi = primIdx[first]; + memset( count, 0, 3 * AVXBINS * 4 ); + memcpy( binbox, orig, 3 * AVXBINS * 32 ); + __m256 r0, r1, r2, f = _mm256_xor_ps( _mm256_and_ps( frag8[fi], mask6 ), signFlip8 ); + const __m128 fmin = _mm_and_ps( frag4[fi].bmin4, mask3 ), fmax = _mm_and_ps( frag4[fi].bmax4, mask3 ); + const __m128i bi4 = _mm_cvtps_epi32( _mm_sub_ps( _mm_mul_ps( _mm_sub_ps( _mm_add_ps( fmax, fmin ), nmin4 ), rpd4 ), half4 ) ); + union { __m128i bc4; uint32_t bc[4]; }; + bc4 = _mm_max_epi32( _mm_min_epi32( bi4, maxbin4 ), _mm_setzero_si128() ); // clamp needed after all + uint32_t i0 = bc[0], i1 = bc[1], i2 = bc[2], * ti = primIdx + first + 1; + for (uint32_t i = first; i < last - 1; i++) + { + uint32_t fid = *ti++; + #if defined __GNUC__ || _MSC_VER < 1920 + if (fid > triCount) fid = triCount - 1; // never happens but g++ *and* vs2017 need this to not crash... + #endif + const __m256 b0 = binbox[i0], b1 = binbox[AVXBINS + i1], b2 = binbox[2 * AVXBINS + i2]; + const __m128 frmin = _mm_and_ps( frag4[fid].bmin4, mask3 ), frmax = _mm_and_ps( frag4[fid].bmax4, mask3 ); + r0 = _mm256_max_ps( b0, f ), r1 = _mm256_max_ps( b1, f ), r2 = _mm256_max_ps( b2, f ); + const __m128i b4 = _mm_cvtps_epi32( _mm_sub_ps( _mm_mul_ps( _mm_sub_ps( _mm_add_ps( frmax, frmin ), nmin4 ), rpd4 ), half4 ) ); + bc4 = _mm_max_epi32( _mm_min_epi32( b4, maxbin4 ), _mm_setzero_si128() ); // clamp needed after all + f = _mm256_xor_ps( _mm256_and_ps( frag8[fid], mask6 ), signFlip8 ); + count[i0]++, count[AVXBINS + i1]++, count[AVXBINS * 2 + i2]++; + binbox[i0] = r0, i0 = bc[0]; + binbox[AVXBINS + i1] = r1, i1 = bc[1]; + binbox[2 * AVXBINS + i2] = r2, i2 = bc[2]; + } + // final business for final fragment + const __m256 b0 = binbox[i0], b1 = binbox[AVXBINS + i1], b2 = binbox[2 * AVXBINS + i2]; + count[i0]++, count[AVXBINS + i1]++, count[AVXBINS * 2 + i2]++; + r0 = _mm256_max_ps( b0, f ), r1 = _mm256_max_ps( b1, f ), r2 = _mm256_max_ps( b2, f ); + binbox[i0] = r0, binbox[AVXBINS + i1] = r1, binbox[2 * AVXBINS + i2] = r2; +} + +void BuildAVXSubtree_( uint32_t n, uint32_t d, BVH* bvh ) { bvh->BuildAVXSubtree( n, d ); } +void BVH::BuildAVXSubtree( uint32_t nodeIdx, uint32_t depth ) +{ + // aligned data + constexpr uint32_t slices = 8; + ALIGNED( 64 ) __m256 slicebinbox[slices][3 * AVXBINS]; + ALIGNED( 64 ) uint32_t slicecount[slices][3 * AVXBINS]; + ALIGNED( 64 ) __m256 binboxOrig[3 * AVXBINS]; // 768 bytes + ALIGNED( 64 ) __m256 bestLBox, bestRBox; // 64 bytes + __m256* binbox = slicebinbox[0]; + uint32_t* count = slicecount[0]; + for (uint32_t i = 0; i < 3 * AVXBINS; i++) binboxOrig[i] = max8; // binbox initialization template + if (depth == 0) + { + // avoid threaded building for small meshes: not efficient; build multiple in parallel instead. + #ifdef NO_THREADED_BUILDS + threadedBuild = false; + #else + if (triCount < MT_BUILD_THRESHOLD) threadedBuild = false; else + { + atomicNewNodePtr = new std::atomic( newNodePtr ); + } + #endif + } + // subdivide recursively + ALIGNED( 64 ) uint32_t task[256], taskCount = 0; + BVHNode& root = bvhNode[0]; + const bvhvec3 minDim = (root.aabbMax - root.aabbMin) * 1e-7f; + while (1) + { + while (1) + { + BVHNode& node = bvhNode[nodeIdx]; + __m128* node4 = (__m128*) & bvhNode[nodeIdx]; + // find optimal object split + const __m128 d4 = _mm_blendv_ps( min1, _mm_sub_ps( node4[1], node4[0] ), mask3 ); + const __m128 nmin4 = _mm_mul_ps( _mm_and_ps( node4[0], mask3 ), two4 ); + const __m128 rpd4 = _mm_and_ps( _mm_div_ps( binmul3, d4 ), _mm_cmpneq_ps( d4, _mm_setzero_ps() ) ); + // implementation of Section 4.1 of "Parallel Spatial Splits in Bounding Volume Hierarchies": + // main loop operates on two fragments to minimize dependencies and maximize ILP. + if (0) // depth < 5 && threadedBuild && node.triCount > 10000) + { + // DISABLED for now until a good jobsystem arrives. + #if 0 + // run binning in parallel slices + const uint32_t sliceSize = node.triCount / slices; + for (int slice = 0; slice < (int)slices; slice++) + { + // calculate task start and end + const uint32_t first = node.leftFirst + sliceSize * slice; + const uint32_t last = slice == (slices - 1) ? (node.leftFirst + node.triCount) : (first + sliceSize); + __m256* sbb = slicebinbox[slice], * bo = binboxOrig; + uint32_t* sc = slicecount[slice]; + // BVH* thisBVH = this; // avoid warnings / complexities of capturing this + // binningJobs->Execute( [=]() { thisBVH->BuildAVXBinTask( first, last, sbb, bo, sc, nmin4, rpd4 ); } ); + } + // binningJobs->Wait(); + // combine results from threads + for (int a = 0; a < 3; a++) for (int i = 0; i < AVXBINS; i++) + for (int ai = a * AVXBINS + i, slice = 1; slice < (int)slices; slice++) count[ai] += slicecount[slice][ai], + binbox[ai] = _mm256_max_ps( binbox[ai], slicebinbox[slice][ai] ); + #endif + } + else + BuildAVXBinTask( node.leftFirst, node.leftFirst + node.triCount, binbox, binboxOrig, count, nmin4, rpd4 ); + // calculate per-split totals + float splitCost = BVH_FAR, rSAV = 1.0f / node.SurfaceArea(); + uint32_t bestAxis = 0, bestPos = 0; + const __m256* bb = binbox; + for (int32_t a = 0; a < 3; a++, bb += AVXBINS) if ((node.aabbMax[a] - node.aabbMin[a]) > minDim[a]) + { + // hardcoded bin processing for AVXBINS == 8 + assert( AVXBINS == 8 ); + const uint32_t* cnt = count + a * AVXBINS; + const uint32_t lN0 = cnt[0], rN0 = cnt[7]; + const __m256 lb0 = bb[0], rb0 = bb[7]; + const uint32_t lN1 = lN0 + cnt[1], rN1 = rN0 + cnt[6], lN2 = lN1 + cnt[2]; + const uint32_t rN2 = rN1 + cnt[5], lN3 = lN2 + cnt[3], rN3 = rN2 + cnt[4]; + const __m256 lb1 = _mm256_max_ps( lb0, bb[1] ), rb1 = _mm256_max_ps( rb0, bb[6] ); + const __m256 lb2 = _mm256_max_ps( lb1, bb[2] ), rb2 = _mm256_max_ps( rb1, bb[5] ); + const __m256 lb3 = _mm256_max_ps( lb2, bb[3] ), rb3 = _mm256_max_ps( rb2, bb[4] ); + const uint32_t lN4 = lN3 + cnt[4], rN4 = rN3 + cnt[3], lN5 = lN4 + cnt[5]; + const uint32_t rN5 = rN4 + cnt[2], lN6 = lN5 + cnt[6], rN6 = rN5 + cnt[1]; + const __m256 lb4 = _mm256_max_ps( lb3, bb[4] ), rb4 = _mm256_max_ps( rb3, bb[3] ); + const __m256 lb5 = _mm256_max_ps( lb4, bb[5] ), rb5 = _mm256_max_ps( rb4, bb[2] ); + const __m256 lb6 = _mm256_max_ps( lb5, bb[6] ), rb6 = _mm256_max_ps( rb5, bb[1] ); + float ANLR3 = BVH_FAR; PROCESS_PLANE( a, 3, ANLR3, lN3, rN3, lb3, rb3 ); // most likely split + float ANLR2 = BVH_FAR; PROCESS_PLANE( a, 2, ANLR2, lN2, rN4, lb2, rb4 ); + float ANLR4 = BVH_FAR; PROCESS_PLANE( a, 4, ANLR4, lN4, rN2, lb4, rb2 ); + float ANLR5 = BVH_FAR; PROCESS_PLANE( a, 5, ANLR5, lN5, rN1, lb5, rb1 ); + float ANLR1 = BVH_FAR; PROCESS_PLANE( a, 1, ANLR1, lN1, rN5, lb1, rb5 ); + float ANLR0 = BVH_FAR; PROCESS_PLANE( a, 0, ANLR0, lN0, rN6, lb0, rb6 ); + float ANLR6 = BVH_FAR; PROCESS_PLANE( a, 6, ANLR6, lN6, rN0, lb6, rb0 ); // least likely split + } + splitCost = c_trav + c_int * rSAV * splitCost; + float noSplitCost = (float)node.triCount * c_int; + if (splitCost >= noSplitCost) break; // not splitting is better. + // in-place partition + const float rpd = (*(bvhvec3*)&rpd4)[bestAxis], nmin = (*(bvhvec3*)&nmin4)[bestAxis]; + uint32_t i = node.leftFirst, j = node.leftFirst + node.triCount, t, fr = primIdx[i]; + for (uint32_t k = 0; k < node.triCount; k++) + { + const uint32_t bi = (uint32_t)((fragment[fr].bmax[bestAxis] + fragment[fr].bmin[bestAxis] - nmin) * rpd); + if (bi <= bestPos) fr = primIdx[++i]; else t = fr, fr = primIdx[i] = primIdx[--j], primIdx[j] = t; + } + // create child nodes and recurse + uint32_t n; + if (threadedBuild) n = atomicNewNodePtr->fetch_add( 2 ); else n = newNodePtr, newNodePtr += 2; + const uint32_t leftCount = i - node.leftFirst, rightCount = node.triCount - leftCount; + if (leftCount == 0 || rightCount == 0 || taskCount == BVH_NUM_ELEMS( task )) break; // should not happen. + *(__m256*)& bvhNode[n] = _mm256_xor_ps( bestLBox, signFlip8 ); + bvhNode[n].leftFirst = node.leftFirst, bvhNode[n].triCount = leftCount; + node.leftFirst = n, node.triCount = 0; + *(__m256*)& bvhNode[n + 1] = _mm256_xor_ps( bestRBox, signFlip8 ); + bvhNode[n + 1].leftFirst = i, bvhNode[n + 1].triCount = rightCount; + if (leftCount + rightCount > 2000 && depth < 5 && threadedBuild) + { + std::thread t1( &BuildAVXSubtree_, n, depth + 1, this ); + std::thread t2( &BuildAVXSubtree_, n + 1, depth + 1, this ); + t1.join(); + t2.join(); // TODO: join is only needed in the 'all done' section below. + break; + } + task[taskCount++] = n + 1, nodeIdx = n; + } + // fetch subdivision task from stack + if (taskCount == 0) break; else nodeIdx = task[--taskCount]; + } + // all done. + if (depth == 0) + { + // tree has been built. + aabbMin = bvhNode[0].aabbMin, aabbMax = bvhNode[0].aabbMax; + refittable = true; // not using spatial splits: can refit this BVH + may_have_holes = false; // there are no holes in the list of nodes. + if (threadedBuild) + { + newNodePtr = atomicNewNodePtr->load(); + delete atomicNewNodePtr; + atomicNewNodePtr = 0; + } + usedNodes = newNodePtr; + } +} + +#if defined _MSC_VER +#pragma warning ( pop ) // restore 4701 +#elif defined __GNUC__ && !defined __clang__ +#pragma GCC diagnostic pop // restore -Wmaybe-uninitialized +#endif + +// Intersect a BVH with a ray packet, basic SSE-optimized version. +// Note: This yields +10% on 10th gen Intel CPUs, but a small loss on +// more recent hardware. This function needs a full conversion to work +// with groups of 8 rays at a time - TODO. +void BVH::Intersect256RaysSSE( Ray* packet ) const +{ + // Corner rays are: 0, 51, 204 and 255 + // Construct the bounding planes, with normals pointing outwards + bvhvec3 O = packet[0].O; // same for all rays in this case + __m128 O4 = *(__m128*) & packet[0].O; + __m128 mask4 = _mm_cmpeq_ps( _mm_setzero_ps(), _mm_set_ps( 1, 0, 0, 0 ) ); + bvhvec3 p0 = packet[0].O + packet[0].D; // top-left + bvhvec3 p1 = packet[51].O + packet[51].D; // top-right + bvhvec3 p2 = packet[204].O + packet[204].D; // bottom-left + bvhvec3 p3 = packet[255].O + packet[255].D; // bottom-right + bvhvec3 plane0 = tinybvh_normalize( tinybvh_cross( p0 - O, p0 - p2 ) ); // left plane + bvhvec3 plane1 = tinybvh_normalize( tinybvh_cross( p3 - O, p3 - p1 ) ); // right plane + bvhvec3 plane2 = tinybvh_normalize( tinybvh_cross( p1 - O, p1 - p0 ) ); // top plane + bvhvec3 plane3 = tinybvh_normalize( tinybvh_cross( p2 - O, p2 - p3 ) ); // bottom plane + int32_t sign0x = plane0.x < 0 ? 4 : 0, sign0y = plane0.y < 0 ? 5 : 1, sign0z = plane0.z < 0 ? 6 : 2; + int32_t sign1x = plane1.x < 0 ? 4 : 0, sign1y = plane1.y < 0 ? 5 : 1, sign1z = plane1.z < 0 ? 6 : 2; + int32_t sign2x = plane2.x < 0 ? 4 : 0, sign2y = plane2.y < 0 ? 5 : 1, sign2z = plane2.z < 0 ? 6 : 2; + int32_t sign3x = plane3.x < 0 ? 4 : 0, sign3y = plane3.y < 0 ? 5 : 1, sign3z = plane3.z < 0 ? 6 : 2; + float t0 = tinybvh_dot( O, plane0 ), t1 = tinybvh_dot( O, plane1 ); + float t2 = tinybvh_dot( O, plane2 ), t3 = tinybvh_dot( O, plane3 ); + // Traverse the tree with the packet + int32_t first = 0, last = 255; // first and last active ray in the packet + BVHNode* node = &bvhNode[0]; + ALIGNED( 64 ) uint32_t stack[64], stackPtr = 0; + while (1) + { + if (node->isLeaf()) + { + // handle leaf node + for (uint32_t j = 0; j < node->triCount; j++) + { + const uint32_t idx = primIdx[node->leftFirst + j], vid = idx * 3; + const bvhvec3 e1 = verts[vid + 1] - verts[vid], e2 = verts[vid + 2] - verts[vid]; + const bvhvec3 s = O - bvhvec3( verts[vid] ); + for (int32_t i = first; i <= last; i++) + { + Ray& ray = packet[i]; + const bvhvec3 h = tinybvh_cross( ray.D, e2 ); + const float a = tinybvh_dot( e1, h ); + if (fabs( a ) < 0.0000001f) continue; // ray parallel to triangle + const float f = 1 / a, u = f * tinybvh_dot( s, h ); + const bvhvec3 q = tinybvh_cross( s, e1 ); + const float v = f * tinybvh_dot( ray.D, q ); + if (u < 0 || v < 0 || u + v > 1) continue; + const float t = f * tinybvh_dot( e2, q ); + if (t <= 0 || t >= ray.hit.t) continue; + ray.hit.t = t, ray.hit.u = u, ray.hit.v = v, ray.hit.prim = idx; + } + } + if (stackPtr == 0) break; else // pop + last = stack[--stackPtr], node = bvhNode + stack[--stackPtr], + first = last >> 8, last &= 255; + } + else + { + // fetch pointers to child nodes + BVHNode* left = bvhNode + node->leftFirst; + BVHNode* right = bvhNode + node->leftFirst + 1; + bool visitLeft = true, visitRight = true; + int32_t leftFirst = first, leftLast = last, rightFirst = first, rightLast = last; + float distLeft, distRight; + { + // see if we want to intersect the left child + const __m128 minO4 = _mm_sub_ps( *(__m128*) & left->aabbMin, O4 ); + const __m128 maxO4 = _mm_sub_ps( *(__m128*) & left->aabbMax, O4 ); + // 1. Early-in test: if first ray hits the node, the packet visits the node + bool earlyHit; + { + const __m128 rD4 = *(__m128*) & packet[first].rD; + const __m128 st1 = _mm_mul_ps( _mm_and_ps( minO4, mask4 ), rD4 ); + const __m128 st2 = _mm_mul_ps( _mm_and_ps( maxO4, mask4 ), rD4 ); + const __m128 vmax4 = _mm_max_ps( st1, st2 ), vmin4 = _mm_min_ps( st1, st2 ); + const float tmax = tinybvh_min( LANE( vmax4, 0 ), tinybvh_min( LANE( vmax4, 1 ), LANE( vmax4, 2 ) ) ); + const float tmin = tinybvh_max( LANE( vmin4, 0 ), tinybvh_max( LANE( vmin4, 1 ), LANE( vmin4, 2 ) ) ); + earlyHit = (tmax >= tmin && tmin < packet[first].hit.t && tmax >= 0); + distLeft = tmin; + } + // 2. Early-out test: if the node aabb is outside the four planes, we skip the node + if (!earlyHit) + { + float* minmax = (float*)left; + bvhvec3 c0( minmax[sign0x], minmax[sign0y], minmax[sign0z] ); + bvhvec3 c1( minmax[sign1x], minmax[sign1y], minmax[sign1z] ); + bvhvec3 c2( minmax[sign2x], minmax[sign2y], minmax[sign2z] ); + bvhvec3 c3( minmax[sign3x], minmax[sign3y], minmax[sign3z] ); + if (tinybvh_dot( c0, plane0 ) > t0 || tinybvh_dot( c1, plane1 ) > t1 || + tinybvh_dot( c2, plane2 ) > t2 || tinybvh_dot( c3, plane3 ) > t3) + visitLeft = false; + else + { + // 3. Last resort: update first and last, stay in node if first > last + for (; leftFirst <= leftLast; leftFirst++) + { + const __m128 rD4 = *(__m128*) & packet[leftFirst].rD; + const __m128 st1 = _mm_mul_ps( _mm_and_ps( minO4, mask4 ), rD4 ); + const __m128 st2 = _mm_mul_ps( _mm_and_ps( maxO4, mask4 ), rD4 ); + const __m128 vmax4 = _mm_max_ps( st1, st2 ), vmin4 = _mm_min_ps( st1, st2 ); + const float tmax = tinybvh_min( LANE( vmax4, 0 ), tinybvh_min( LANE( vmax4, 1 ), LANE( vmax4, 2 ) ) ); + const float tmin = tinybvh_max( LANE( vmin4, 0 ), tinybvh_max( LANE( vmin4, 1 ), LANE( vmin4, 2 ) ) ); + if (tmax >= tmin && tmin < packet[leftFirst].hit.t && tmax >= 0) { distLeft = tmin; break; } + } + for (; leftLast >= leftFirst; leftLast--) + { + const __m128 rD4 = *(__m128*) & packet[leftLast].rD; + const __m128 st1 = _mm_mul_ps( _mm_and_ps( minO4, mask4 ), rD4 ); + const __m128 st2 = _mm_mul_ps( _mm_and_ps( maxO4, mask4 ), rD4 ); + const __m128 vmax4 = _mm_max_ps( st1, st2 ), vmin4 = _mm_min_ps( st1, st2 ); + const float tmax = tinybvh_min( LANE( vmax4, 0 ), tinybvh_min( LANE( vmax4, 1 ), LANE( vmax4, 2 ) ) ); + const float tmin = tinybvh_max( LANE( vmin4, 0 ), tinybvh_max( LANE( vmin4, 1 ), LANE( vmin4, 2 ) ) ); + if (tmax >= tmin && tmin < packet[leftLast].hit.t && tmax >= 0) break; + } + visitLeft = leftLast >= leftFirst; + } + } + } + { + // see if we want to intersect the right child + const __m128 minO4 = _mm_sub_ps( *(__m128*) & right->aabbMin, O4 ); + const __m128 maxO4 = _mm_sub_ps( *(__m128*) & right->aabbMax, O4 ); + // 1. Early-in test: if first ray hits the node, the packet visits the node + bool earlyHit; + { + const __m128 rD4 = *(__m128*) & packet[first].rD; + const __m128 st1 = _mm_mul_ps( minO4, rD4 ), st2 = _mm_mul_ps( maxO4, rD4 ); + const __m128 vmax4 = _mm_max_ps( st1, st2 ), vmin4 = _mm_min_ps( st1, st2 ); + const float tmax = tinybvh_min( LANE( vmax4, 0 ), tinybvh_min( LANE( vmax4, 1 ), LANE( vmax4, 2 ) ) ); + const float tmin = tinybvh_max( LANE( vmin4, 0 ), tinybvh_max( LANE( vmin4, 1 ), LANE( vmin4, 2 ) ) ); + earlyHit = (tmax >= tmin && tmin < packet[first].hit.t && tmax >= 0); + distRight = tmin; + } + // 2. Early-out test: if the node aabb is outside the four planes, we skip the node + if (!earlyHit) + { + float* minmax = (float*)right; + bvhvec3 c0( minmax[sign0x], minmax[sign0y], minmax[sign0z] ); + bvhvec3 c1( minmax[sign1x], minmax[sign1y], minmax[sign1z] ); + bvhvec3 c2( minmax[sign2x], minmax[sign2y], minmax[sign2z] ); + bvhvec3 c3( minmax[sign3x], minmax[sign3y], minmax[sign3z] ); + if (tinybvh_dot( c0, plane0 ) > t0 || tinybvh_dot( c1, plane1 ) > t1 || + tinybvh_dot( c2, plane2 ) > t2 || tinybvh_dot( c3, plane3 ) > t3) + visitRight = false; + else + { + // 3. Last resort: update first and last, stay in node if first > last + for (; rightFirst <= rightLast; rightFirst++) + { + const __m128 rD4 = *(__m128*) & packet[rightFirst].rD; + const __m128 st1 = _mm_mul_ps( _mm_and_ps( minO4, mask4 ), rD4 ); + const __m128 st2 = _mm_mul_ps( _mm_and_ps( maxO4, mask4 ), rD4 ); + const __m128 vmax4 = _mm_max_ps( st1, st2 ), vmin4 = _mm_min_ps( st1, st2 ); + const float tmax1 = tinybvh_min( LANE( vmax4, 0 ), tinybvh_min( LANE( vmax4, 1 ), LANE( vmax4, 2 ) ) ); + const float tmin1 = tinybvh_max( LANE( vmin4, 0 ), tinybvh_max( LANE( vmin4, 1 ), LANE( vmin4, 2 ) ) ); + if (tmax1 >= tmin1 && tmin1 < packet[rightFirst].hit.t && tmax1 >= 0) { distRight = tmin1; break; } + } + for (; rightLast >= first; rightLast--) + { + const __m128 rD4 = *(__m128*) & packet[rightLast].rD; + const __m128 st1 = _mm_mul_ps( _mm_and_ps( minO4, mask4 ), rD4 ); + const __m128 st2 = _mm_mul_ps( _mm_and_ps( maxO4, mask4 ), rD4 ); + const __m128 vmax4 = _mm_max_ps( st1, st2 ), vmin4 = _mm_min_ps( st1, st2 ); + const float tmax1 = tinybvh_min( LANE( vmax4, 0 ), tinybvh_min( LANE( vmax4, 1 ), LANE( vmax4, 2 ) ) ); + const float tmin1 = tinybvh_max( LANE( vmin4, 0 ), tinybvh_max( LANE( vmin4, 1 ), LANE( vmin4, 2 ) ) ); + if (tmax1 >= tmin1 && tmin1 < packet[rightLast].hit.t && tmax1 >= 0) break; + } + visitRight = rightLast >= rightFirst; + } + } + } + // process intersection result + if (visitLeft && visitRight) + { + if (distLeft < distRight) + { + // push right, continue with left + stack[stackPtr++] = node->leftFirst + 1; + stack[stackPtr++] = (rightFirst << 8) + rightLast; + node = left, first = leftFirst, last = leftLast; + } + else + { + // push left, continue with right + stack[stackPtr++] = node->leftFirst; + stack[stackPtr++] = (leftFirst << 8) + leftLast; + node = right, first = rightFirst, last = rightLast; + } + } + else if (visitLeft) // continue with left + node = left, first = leftFirst, last = leftLast; + else if (visitRight) // continue with right + node = right, first = rightFirst, last = rightLast; + else if (stackPtr == 0) break; else // pop + last = stack[--stackPtr], node = bvhNode + stack[--stackPtr], + first = last >> 8, last &= 255; + } + } +} + +// Traverse the 'structure of arrays' BVH layout. +int32_t BVH_SoA::Intersect( Ray& ray ) const +{ + VALIDATE_RAY( ray ); + BVHNode* node = &bvhNode[0], * stack[64]; + const bvhvec4slice& verts = bvh.verts; + const uint32_t* primIdx = bvh.primIdx; + uint32_t stackPtr = 0; + float cost = 0; + const __m128 Ox4 = _mm_set1_ps( ray.O.x ), rDx4 = _mm_set1_ps( ray.rD.x ); + const __m128 Oy4 = _mm_set1_ps( ray.O.y ), rDy4 = _mm_set1_ps( ray.rD.y ); + const __m128 Oz4 = _mm_set1_ps( ray.O.z ), rDz4 = _mm_set1_ps( ray.rD.z ); + while (1) + { + cost += c_trav; + if (node->isLeaf()) + { + if (indexedEnabled && bvh.vertIdx != 0) for (uint32_t i = 0; i < node->triCount; i++, cost += c_int) + { + const uint32_t pi = primIdx[node->firstTri + i]; + const uint32_t i0 = bvh.vertIdx[pi * 3], i1 = bvh.vertIdx[pi * 3 + 1], i2 = bvh.vertIdx[pi * 3 + 2]; + IntersectTri( ray, pi, verts, i0, i1, i2 ); + } + else for (uint32_t i = 0; i < node->triCount; i++, cost += c_int) + { + const uint32_t pi = primIdx[node->firstTri + i]; + IntersectTri( ray, pi, verts, pi * 3, pi * 3 + 1, pi * 3 + 2 ); + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + continue; + } + __m128 x4 = _mm_mul_ps( _mm_sub_ps( node->xxxx, Ox4 ), rDx4 ); + __m128 y4 = _mm_mul_ps( _mm_sub_ps( node->yyyy, Oy4 ), rDy4 ); + __m128 z4 = _mm_mul_ps( _mm_sub_ps( node->zzzz, Oz4 ), rDz4 ); + // transpose + __m128 t0 = _mm_unpacklo_ps( x4, y4 ), t2 = _mm_unpacklo_ps( z4, z4 ); + __m128 t1 = _mm_unpackhi_ps( x4, y4 ), t3 = _mm_unpackhi_ps( z4, z4 ); + const __m128 xyzw1a = _mm_shuffle_ps( t0, t2, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + const __m128 xyzw2a = _mm_shuffle_ps( t0, t2, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + const __m128 xyzw1b = _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + const __m128 xyzw2b = _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + // process + const __m128 tmina4 = _mm_min_ps( xyzw1a, xyzw2a ), tmaxa4 = _mm_max_ps( xyzw1a, xyzw2a ); + const __m128 tminb4 = _mm_min_ps( xyzw1b, xyzw2b ), tmaxb4 = _mm_max_ps( xyzw1b, xyzw2b ); + // transpose back + t0 = _mm_unpacklo_ps( tmina4, tmaxa4 ), t2 = _mm_unpacklo_ps( tminb4, tmaxb4 ); + t1 = _mm_unpackhi_ps( tmina4, tmaxa4 ), t3 = _mm_unpackhi_ps( tminb4, tmaxb4 ); + x4 = _mm_shuffle_ps( t0, t2, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + y4 = _mm_shuffle_ps( t0, t2, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + z4 = _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + uint32_t lidx = node->left, ridx = node->right; + const __m128 min4 = _mm_max_ps( _mm_max_ps( _mm_max_ps( x4, y4 ), z4 ), _mm_setzero_ps() ); + const __m128 max4 = _mm_min_ps( _mm_min_ps( _mm_min_ps( x4, y4 ), z4 ), _mm_set1_ps( ray.hit.t ) ); + const float tmina_0 = LANE( min4, 0 ), tmaxa_1 = LANE( max4, 1 ); + const float tminb_2 = LANE( min4, 2 ), tmaxb_3 = LANE( max4, 3 ); + float dist1 = tmaxa_1 >= tmina_0 ? tmina_0 : BVH_FAR; + float dist2 = tmaxb_3 >= tminb_2 ? tminb_2 : BVH_FAR; + if (dist1 > dist2) + { + const float t = dist1; dist1 = dist2; dist2 = t; + const uint32_t i = lidx; lidx = ridx; ridx = i; + } + if (dist1 == BVH_FAR) + { + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + else + { + node = bvhNode + lidx; + if (dist2 != BVH_FAR) stack[stackPtr++] = bvhNode + ridx; + } + } + return (int32_t)cost; +} + +// Find occlusions in the second alternative BVH layout (ALT_SOA). +bool BVH_SoA::IsOccluded( const Ray& ray ) const +{ + BVHNode* node = &bvhNode[0], * stack[64]; + const bvhvec4slice& verts = bvh.verts; + const uint32_t* primIdx = bvh.primIdx; + uint32_t stackPtr = 0; + const __m128 Ox4 = _mm_set1_ps( ray.O.x ), rDx4 = _mm_set1_ps( ray.rD.x ); + const __m128 Oy4 = _mm_set1_ps( ray.O.y ), rDy4 = _mm_set1_ps( ray.rD.y ); + const __m128 Oz4 = _mm_set1_ps( ray.O.z ), rDz4 = _mm_set1_ps( ray.rD.z ); + while (1) + { + if (node->isLeaf()) + { + if (indexedEnabled && bvh.vertIdx != 0) for (uint32_t i = 0; i < node->triCount; i++) + { + const uint32_t pi = primIdx[node->firstTri + i], vi0 = pi * 3; + const uint32_t i0 = bvh.vertIdx[vi0], i1 = bvh.vertIdx[vi0 + 1], i2 = bvh.vertIdx[vi0 + 2]; + if (TriOccludes( ray, verts, pi, i0, i1, i2 )) return true; + } + else for (uint32_t i = 0; i < node->triCount; i++) + { + const uint32_t pi = primIdx[node->firstTri + i], vi0 = pi * 3; + if (TriOccludes( ray, verts, pi, vi0, vi0 + 1, vi0 + 2 )) return true; + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + continue; + } + __m128 x4 = _mm_mul_ps( _mm_sub_ps( node->xxxx, Ox4 ), rDx4 ); + __m128 y4 = _mm_mul_ps( _mm_sub_ps( node->yyyy, Oy4 ), rDy4 ); + __m128 z4 = _mm_mul_ps( _mm_sub_ps( node->zzzz, Oz4 ), rDz4 ); + // transpose + __m128 t0 = _mm_unpacklo_ps( x4, y4 ), t2 = _mm_unpacklo_ps( z4, z4 ); + __m128 t1 = _mm_unpackhi_ps( x4, y4 ), t3 = _mm_unpackhi_ps( z4, z4 ); + __m128 xyzw1a = _mm_shuffle_ps( t0, t2, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + __m128 xyzw2a = _mm_shuffle_ps( t0, t2, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + __m128 xyzw1b = _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + __m128 xyzw2b = _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + // process + __m128 tmina4 = _mm_min_ps( xyzw1a, xyzw2a ), tmaxa4 = _mm_max_ps( xyzw1a, xyzw2a ); + __m128 tminb4 = _mm_min_ps( xyzw1b, xyzw2b ), tmaxb4 = _mm_max_ps( xyzw1b, xyzw2b ); + // transpose back + t0 = _mm_unpacklo_ps( tmina4, tmaxa4 ), t2 = _mm_unpacklo_ps( tminb4, tmaxb4 ); + t1 = _mm_unpackhi_ps( tmina4, tmaxa4 ), t3 = _mm_unpackhi_ps( tminb4, tmaxb4 ); + x4 = _mm_shuffle_ps( t0, t2, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + y4 = _mm_shuffle_ps( t0, t2, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + z4 = _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + uint32_t lidx = node->left, ridx = node->right; + const __m128 min4 = _mm_max_ps( _mm_max_ps( _mm_max_ps( x4, y4 ), z4 ), _mm_setzero_ps() ); + const __m128 max4 = _mm_min_ps( _mm_min_ps( _mm_min_ps( x4, y4 ), z4 ), _mm_set1_ps( ray.hit.t ) ); + const float tmina_0 = LANE( min4, 0 ), tmaxa_1 = LANE( max4, 1 ); + const float tminb_2 = LANE( min4, 2 ), tmaxb_3 = LANE( max4, 3 ); + float dist1 = tmaxa_1 >= tmina_0 ? tmina_0 : BVH_FAR; + float dist2 = tmaxb_3 >= tminb_2 ? tminb_2 : BVH_FAR; + if (dist1 > dist2) + { + float t = dist1; dist1 = dist2; dist2 = t; + uint32_t i = lidx; lidx = ridx; ridx = i; + } + if (dist1 == BVH_FAR) + { + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + else + { + node = bvhNode + lidx; + if (dist2 != BVH_FAR) stack[stackPtr++] = bvhNode + ridx; + } + } + return false; +} + +// Intersect_CWBVH: +// Intersect a compressed 8-wide BVH with a ray. For debugging only, not efficient. +// Not technically limited to BVH_USEAVX, but __lzcnt and __popcnt will require +// exotic compiler flags (in combination with __builtin_ia32_lzcnt_u32), so... Since +// this is just here to test data before it goes to the GPU: MSVC-only for now. +#define STACK_POP() { ngroup = traversalStack[--stackPtr]; } +#define STACK_PUSH() { traversalStack[stackPtr++] = ngroup; } +inline uint32_t extract_byte( const uint32_t i, const uint32_t n ) { return (i >> (n * 8)) & 0xFF; } +inline uint32_t sign_extend_s8x4( const uint32_t i ) +{ + // asm("prmt.b32 %0, %1, 0x0, 0x0000BA98;" : "=r"(v) : "r"(i)); // BA98: 1011`1010`1001`1000 + // with the given parameters, prmt will extend the sign to all bits in a byte. + uint32_t b0 = (i & 0b10000000000000000000000000000000) ? 0xff000000 : 0; + uint32_t b1 = (i & 0b00000000100000000000000000000000) ? 0x00ff0000 : 0; + uint32_t b2 = (i & 0b00000000000000001000000000000000) ? 0x0000ff00 : 0; + uint32_t b3 = (i & 0b00000000000000000000000010000000) ? 0x000000ff : 0; + return b0 + b1 + b2 + b3; // probably can do better than this. +} +int32_t BVH8_CWBVH::Intersect( Ray& ray ) const +{ + bvhuint2 traversalStack[128]; + uint32_t hitAddr = 0, stackPtr = 0; + bvhvec2 triangleuv( 0, 0 ); + const bvhvec4* blasNodes = bvh8Data, * blasTris = bvh8Tris; + float tmin = 0, tmax = ray.hit.t; + const uint32_t octinv = (7 - ((ray.D.x < 0 ? 4 : 0) | (ray.D.y < 0 ? 2 : 0) | (ray.D.z < 0 ? 1 : 0))) * 0x1010101; + bvhuint2 ngroup = bvhuint2( 0, 0b10000000000000000000000000000000 ), tgroup = bvhuint2( 0 ); + do + { + if (ngroup.y > 0x00FFFFFF) + { + const uint32_t hits = ngroup.y, imask = ngroup.y; + const uint32_t child_bit_index = __bfind( hits ), child_node_base_index = ngroup.x; + ngroup.y &= ~(1 << child_bit_index); + if (ngroup.y > 0x00FFFFFF) { STACK_PUSH( /* nodeGroup */ ); } + { + const uint32_t slot_index = (child_bit_index - 24) ^ (octinv & 255); + const uint32_t relative_index = __popc( imask & ~(0xFFFFFFFF << slot_index) ); + const uint32_t child_node_index = child_node_base_index + relative_index; + const bvhvec4 n0 = blasNodes[child_node_index * 5 + 0], n1 = blasNodes[child_node_index * 5 + 1]; + const bvhvec4 n2 = blasNodes[child_node_index * 5 + 2], n3 = blasNodes[child_node_index * 5 + 3]; + const bvhvec4 n4 = blasNodes[child_node_index * 5 + 4], p = n0; + bvhint3 e; + e.x = (int32_t) * ((int8_t*)&n0.w + 0), e.y = (int32_t) * ((int8_t*)&n0.w + 1), e.z = (int32_t) * ((int8_t*)&n0.w + 2); + ngroup.x = as_uint( n1.x ), tgroup.x = as_uint( n1.y ), tgroup.y = 0; + uint32_t hitmask = 0; + const uint32_t vx = (e.x + 127) << 23u; const float adjusted_idirx = *(float*)&vx * ray.rD.x; + const uint32_t vy = (e.y + 127) << 23u; const float adjusted_idiry = *(float*)&vy * ray.rD.y; + const uint32_t vz = (e.z + 127) << 23u; const float adjusted_idirz = *(float*)&vz * ray.rD.z; + const float origx = -(ray.O.x - p.x) * ray.rD.x; + const float origy = -(ray.O.y - p.y) * ray.rD.y; + const float origz = -(ray.O.z - p.z) * ray.rD.z; + { // First 4 + const uint32_t meta4 = *(uint32_t*)&n1.z, is_inner4 = (meta4 & (meta4 << 1)) & 0x10101010; + const uint32_t inner_mask4 = sign_extend_s8x4( is_inner4 << 3 ); + const uint32_t bit_index4 = (meta4 ^ (octinv & inner_mask4)) & 0x1F1F1F1F; + const uint32_t child_bits4 = (meta4 >> 5) & 0x07070707; + uint32_t swizzledLox = (ray.rD.x < 0) ? *(uint32_t*)&n3.z : *(uint32_t*)&n2.x, swizzledHix = (ray.rD.x < 0) ? *(uint32_t*)&n2.x : *(uint32_t*)&n3.z; + uint32_t swizzledLoy = (ray.rD.y < 0) ? *(uint32_t*)&n4.x : *(uint32_t*)&n2.z, swizzledHiy = (ray.rD.y < 0) ? *(uint32_t*)&n2.z : *(uint32_t*)&n4.x; + uint32_t swizzledLoz = (ray.rD.z < 0) ? *(uint32_t*)&n4.z : *(uint32_t*)&n3.x, swizzledHiz = (ray.rD.z < 0) ? *(uint32_t*)&n3.x : *(uint32_t*)&n4.z; + float tminx[4], tminy[4], tminz[4], tmaxx[4], tmaxy[4], tmaxz[4]; + tminx[0] = ((swizzledLox >> 0) & 0xFF) * adjusted_idirx + origx, tminx[1] = ((swizzledLox >> 8) & 0xFF) * adjusted_idirx + origx, tminx[2] = ((swizzledLox >> 16) & 0xFF) * adjusted_idirx + origx; + tminx[3] = ((swizzledLox >> 24) & 0xFF) * adjusted_idirx + origx, tminy[0] = ((swizzledLoy >> 0) & 0xFF) * adjusted_idiry + origy, tminy[1] = ((swizzledLoy >> 8) & 0xFF) * adjusted_idiry + origy; + tminy[2] = ((swizzledLoy >> 16) & 0xFF) * adjusted_idiry + origy, tminy[3] = ((swizzledLoy >> 24) & 0xFF) * adjusted_idiry + origy, tminz[0] = ((swizzledLoz >> 0) & 0xFF) * adjusted_idirz + origz; + tminz[1] = ((swizzledLoz >> 8) & 0xFF) * adjusted_idirz + origz, tminz[2] = ((swizzledLoz >> 16) & 0xFF) * adjusted_idirz + origz, tminz[3] = ((swizzledLoz >> 24) & 0xFF) * adjusted_idirz + origz; + tmaxx[0] = ((swizzledHix >> 0) & 0xFF) * adjusted_idirx + origx, tmaxx[1] = ((swizzledHix >> 8) & 0xFF) * adjusted_idirx + origx, tmaxx[2] = ((swizzledHix >> 16) & 0xFF) * adjusted_idirx + origx; + tmaxx[3] = ((swizzledHix >> 24) & 0xFF) * adjusted_idirx + origx, tmaxy[0] = ((swizzledHiy >> 0) & 0xFF) * adjusted_idiry + origy, tmaxy[1] = ((swizzledHiy >> 8) & 0xFF) * adjusted_idiry + origy; + tmaxy[2] = ((swizzledHiy >> 16) & 0xFF) * adjusted_idiry + origy, tmaxy[3] = ((swizzledHiy >> 24) & 0xFF) * adjusted_idiry + origy, tmaxz[0] = ((swizzledHiz >> 0) & 0xFF) * adjusted_idirz + origz; + tmaxz[1] = ((swizzledHiz >> 8) & 0xFF) * adjusted_idirz + origz, tmaxz[2] = ((swizzledHiz >> 16) & 0xFF) * adjusted_idirz + origz, tmaxz[3] = ((swizzledHiz >> 24) & 0xFF) * adjusted_idirz + origz; + for (int32_t i = 0; i < 4; i++) + { + // Use VMIN, VMAX to compute the slabs + const float cmin = tinybvh_max( tinybvh_max( tinybvh_max( tminx[i], tminy[i] ), tminz[i] ), tmin ); + const float cmax = tinybvh_min( tinybvh_min( tinybvh_min( tmaxx[i], tmaxy[i] ), tmaxz[i] ), tmax ); + if (cmin <= cmax) hitmask |= extract_byte( child_bits4, i ) << extract_byte( bit_index4, i ); + } + } + { // Second 4 + const uint32_t meta4 = *(uint32_t*)&n1.w, is_inner4 = (meta4 & (meta4 << 1)) & 0x10101010; + const uint32_t inner_mask4 = sign_extend_s8x4( is_inner4 << 3 ); + const uint32_t bit_index4 = (meta4 ^ (octinv & inner_mask4)) & 0x1F1F1F1F; + const uint32_t child_bits4 = (meta4 >> 5) & 0x07070707; + uint32_t swizzledLox = (ray.rD.x < 0) ? *(uint32_t*)&n3.w : *(uint32_t*)&n2.y, swizzledHix = (ray.rD.x < 0) ? *(uint32_t*)&n2.y : *(uint32_t*)&n3.w; + uint32_t swizzledLoy = (ray.rD.y < 0) ? *(uint32_t*)&n4.y : *(uint32_t*)&n2.w, swizzledHiy = (ray.rD.y < 0) ? *(uint32_t*)&n2.w : *(uint32_t*)&n4.y; + uint32_t swizzledLoz = (ray.rD.z < 0) ? *(uint32_t*)&n4.w : *(uint32_t*)&n3.y, swizzledHiz = (ray.rD.z < 0) ? *(uint32_t*)&n3.y : *(uint32_t*)&n4.w; + float tminx[4], tminy[4], tminz[4], tmaxx[4], tmaxy[4], tmaxz[4]; + tminx[0] = ((swizzledLox >> 0) & 0xFF) * adjusted_idirx + origx, tminx[1] = ((swizzledLox >> 8) & 0xFF) * adjusted_idirx + origx, tminx[2] = ((swizzledLox >> 16) & 0xFF) * adjusted_idirx + origx; + tminx[3] = ((swizzledLox >> 24) & 0xFF) * adjusted_idirx + origx, tminy[0] = ((swizzledLoy >> 0) & 0xFF) * adjusted_idiry + origy, tminy[1] = ((swizzledLoy >> 8) & 0xFF) * adjusted_idiry + origy; + tminy[2] = ((swizzledLoy >> 16) & 0xFF) * adjusted_idiry + origy, tminy[3] = ((swizzledLoy >> 24) & 0xFF) * adjusted_idiry + origy, tminz[0] = ((swizzledLoz >> 0) & 0xFF) * adjusted_idirz + origz; + tminz[1] = ((swizzledLoz >> 8) & 0xFF) * adjusted_idirz + origz, tminz[2] = ((swizzledLoz >> 16) & 0xFF) * adjusted_idirz + origz, tminz[3] = ((swizzledLoz >> 24) & 0xFF) * adjusted_idirz + origz; + tmaxx[0] = ((swizzledHix >> 0) & 0xFF) * adjusted_idirx + origx, tmaxx[1] = ((swizzledHix >> 8) & 0xFF) * adjusted_idirx + origx, tmaxx[2] = ((swizzledHix >> 16) & 0xFF) * adjusted_idirx + origx; + tmaxx[3] = ((swizzledHix >> 24) & 0xFF) * adjusted_idirx + origx, tmaxy[0] = ((swizzledHiy >> 0) & 0xFF) * adjusted_idiry + origy, tmaxy[1] = ((swizzledHiy >> 8) & 0xFF) * adjusted_idiry + origy; + tmaxy[2] = ((swizzledHiy >> 16) & 0xFF) * adjusted_idiry + origy, tmaxy[3] = ((swizzledHiy >> 24) & 0xFF) * adjusted_idiry + origy, tmaxz[0] = ((swizzledHiz >> 0) & 0xFF) * adjusted_idirz + origz; + tmaxz[1] = ((swizzledHiz >> 8) & 0xFF) * adjusted_idirz + origz, tmaxz[2] = ((swizzledHiz >> 16) & 0xFF) * adjusted_idirz + origz, tmaxz[3] = ((swizzledHiz >> 24) & 0xFF) * adjusted_idirz + origz; + for (int32_t i = 0; i < 4; i++) + { + const float cmin = tinybvh_max( tinybvh_max( tinybvh_max( tminx[i], tminy[i] ), tminz[i] ), tmin ); + const float cmax = tinybvh_min( tinybvh_min( tinybvh_min( tmaxx[i], tmaxy[i] ), tmaxz[i] ), tmax ); + if (cmin <= cmax) hitmask |= extract_byte( child_bits4, i ) << extract_byte( bit_index4, i ); + } + } + ngroup.y = (hitmask & 0xFF000000) | (as_uint( n0.w ) >> 24), tgroup.y = hitmask & 0x00FFFFFF; + } + } + else tgroup = ngroup, ngroup = bvhuint2( 0 ); + while (tgroup.y != 0) + { + uint32_t triangleIndex = __bfind( tgroup.y ); + tgroup.y -= 1 << triangleIndex; + int32_t triAddr = tgroup.x + triangleIndex * 3; + const bvhvec3 e2 = bvhvec3( blasTris[triAddr + 0] ), e1 = bvhvec3( blasTris[triAddr + 1] ); + const bvhvec3 v0 = blasTris[triAddr + 2]; + MOLLER_TRUMBORE_TEST( tmax, continue ); + triangleuv = bvhvec2( u, v ), tmax = t; + hitAddr = as_uint( blasTris[triAddr + 2].w ); + } + if (ngroup.y > 0x00FFFFFF) continue; + if (stackPtr > 0) { STACK_POP( /* nodeGroup */ ); } + else + { + ray.hit.t = tmax; + if (tmax < BVH_FAR) ray.hit.u = triangleuv.x, ray.hit.v = triangleuv.y, ray.hit.prim = hitAddr; + break; + } + } while (true); + return 0; +} + +#ifdef BVH_USEAVX2 + +#define TO256(x) _mm256_cvtepu8_epi32( _mm_cvtsi64_si128( x ) ) +ALIGNED( 64 ) static const __m256i idxLUT256[256] = { + TO256( 506097522914230528 ), TO256( 1976943448883713 ), TO256( 1976943448883712 ), TO256( 7722435347202 ), TO256( 1976943448883456 ), TO256( 7722435347201 ), TO256( + 7722435347200 ), TO256( 30165763075 ), TO256( 1976943448817920 ), TO256( 7722435346945 ), TO256( 7722435346944 ), TO256( 30165763074 ), TO256( 7722435346688 ), TO256( + 30165763073 ), TO256( 30165763072 ), TO256( 117835012 ), TO256( 1976943432040704 ), TO256( 7722435281409 ), TO256( 7722435281408 ), TO256( 30165762818 ), TO256( + 7722435281152 ), TO256( 30165762817 ), TO256( 30165762816 ), TO256( 117835011 ), TO256( 7722435215616 ), TO256( 30165762561 ), TO256( 30165762560 ), TO256( 117835010 ), TO256( + 30165762304 ), TO256( 117835009 ), TO256( 117835008 ), TO256( 460293 ), TO256( 1976939137073408 ), TO256( 7722418504193 ), TO256( 7722418504192 ), TO256( 30165697282 ), TO256( + 7722418503936 ), TO256( 30165697281 ), TO256( 30165697280 ), TO256( 117834755 ), TO256( 7722418438400 ), TO256( 30165697025 ), TO256( 30165697024 ), TO256( 117834754 ), TO256( + 30165696768 ), TO256( 117834753 ), TO256( 117834752 ), TO256( 460292 ), TO256( 7722401661184 ), TO256( 30165631489 ), TO256( 30165631488 ), TO256( 117834498 ), TO256( 30165631232 ), TO256( + 117834497 ), TO256( 117834496 ), TO256( 460291 ), TO256( 30165565696 ), TO256( 117834241 ), TO256( 117834240 ), TO256( 460290 ), TO256( 117833984 ), TO256( 460289 ), TO256( 460288 ), TO256( 1798 ), TO256( + 1975839625445632 ), TO256( 7718123536897 ), TO256( 7718123536896 ), TO256( 30148920066 ), TO256( 7718123536640 ), TO256( 30148920065 ), TO256( 30148920064 ), TO256( + 117769219 ), TO256( 7718123471104 ), TO256( 30148919809 ), TO256( 30148919808 ), TO256( 117769218 ), TO256( 30148919552 ), TO256( 117769217 ), TO256( 117769216 ), TO256( 460036 ), TO256( + 7718106693888 ), TO256( 30148854273 ), TO256( 30148854272 ), TO256( 117768962 ), TO256( 30148854016 ), TO256( 117768961 ), TO256( 117768960 ), TO256( 460035 ), TO256( 30148788480 ), TO256( + 117768705 ), TO256( 117768704 ), TO256( 460034 ), TO256( 117768448 ), TO256( 460033 ), TO256( 460032 ), TO256( 1797 ), TO256( 7713811726592 ), TO256( 30132077057 ), TO256( 30132077056 ), TO256( + 117703426 ), TO256( 30132076800 ), TO256( 117703425 ), TO256( 117703424 ), TO256( 459779 ), TO256( 30132011264 ), TO256( 117703169 ), TO256( 117703168 ), TO256( 459778 ), TO256( 117702912 ), TO256( + 459777 ), TO256( 459776 ), TO256( 1796 ), TO256( 30115234048 ), TO256( 117637633 ), TO256( 117637632 ), TO256( 459522 ), TO256( 117637376 ), TO256( 459521 ), TO256( 459520 ), TO256( 1795 ), TO256( 117571840 ), TO256( + 459265 ), TO256( 459264 ), TO256( 1794 ), TO256( 459008 ), TO256( 1793 ), TO256( 1792 ), TO256( 7 ), TO256( 1694364648734976 ), TO256( 6618611909121 ), TO256( 6618611909120 ), TO256( 25853952770 ), TO256( + 6618611908864 ), TO256( 25853952769 ), TO256( 25853952768 ), TO256( 100992003 ), TO256( 6618611843328 ), TO256( 25853952513 ), TO256( 25853952512 ), TO256( 100992002 ), TO256( + 25853952256 ), TO256( 100992001 ), TO256( 100992000 ), TO256( 394500 ), TO256( 6618595066112 ), TO256( 25853886977 ), TO256( 25853886976 ), TO256( 100991746 ), TO256( 25853886720 ), TO256( + 100991745 ), TO256( 100991744 ), TO256( 394499 ), TO256( 25853821184 ), TO256( 100991489 ), TO256( 100991488 ), TO256( 394498 ), TO256( 100991232 ), TO256( 394497 ), TO256( 394496 ), TO256( 1541 ), TO256( + 6614300098816 ), TO256( 25837109761 ), TO256( 25837109760 ), TO256( 100926210 ), TO256( 25837109504 ), TO256( 100926209 ), TO256( 100926208 ), TO256( 394243 ), TO256( 25837043968 ), TO256( + 100925953 ), TO256( 100925952 ), TO256( 394242 ), TO256( 100925696 ), TO256( 394241 ), TO256( 394240 ), TO256( 1540 ), TO256( 25820266752 ), TO256( 100860417 ), TO256( 100860416 ), TO256( 393986 ), TO256( + 100860160 ), TO256( 393985 ), TO256( 393984 ), TO256( 1539 ), TO256( 100794624 ), TO256( 393729 ), TO256( 393728 ), TO256( 1538 ), TO256( 393472 ), TO256( 1537 ), TO256( 1536 ), TO256( 6 ), TO256( 5514788471040 ), TO256( + 21542142465 ), TO256( 21542142464 ), TO256( 84148994 ), TO256( 21542142208 ), TO256( 84148993 ), TO256( 84148992 ), TO256( 328707 ), TO256( 21542076672 ), TO256( 84148737 ), TO256( + 84148736 ), TO256( 328706 ), TO256( 84148480 ), TO256( 328705 ), TO256( 328704 ), TO256( 1284 ), TO256( 21525299456 ), TO256( 84083201 ), TO256( 84083200 ), TO256( 328450 ), TO256( 84082944 ), TO256( 328449 ), TO256( + 328448 ), TO256( 1283 ), TO256( 84017408 ), TO256( 328193 ), TO256( 328192 ), TO256( 1282 ), TO256( 327936 ), TO256( 1281 ), TO256( 1280 ), TO256( 5 ), TO256( 17230332160 ), TO256( 67305985 ), TO256( 67305984 ), TO256( 262914 ), TO256( + 67305728 ), TO256( 262913 ), TO256( 262912 ), TO256( 1027 ), TO256( 67240192 ), TO256( 262657 ), TO256( 262656 ), TO256( 1026 ), TO256( 262400 ), TO256( 1025 ), TO256( 1024 ), TO256( 4 ), TO256( 50462976 ), TO256( 197121 ), TO256( + 197120 ), TO256( 770 ), TO256( 196864 ), TO256( 769 ), TO256( 768 ), TO256( 3 ), TO256( 131328 ), TO256( 513 ), TO256( 512 ), TO256( 2 ), TO256( 256 ), TO256( 1 ), TO256( 0 ), TO256( 0 ) +}; + +int32_t BVH8_CPU::Intersect( Ray& ray ) const +{ + VALIDATE_RAY( ray ); + const bool posX = ray.D.x >= 0, posY = ray.D.y >= 0, posZ = ray.D.z >= 0; + if (!posX) goto negx; + if (posY) { if (posZ) return Intersect( ray ); else return Intersect( ray ); } + if (posZ) return Intersect( ray ); else return Intersect( ray ); +negx: + if (posY) { if (posZ) return Intersect( ray ); else return Intersect( ray ); } + if (posZ) return Intersect( ray ); else return Intersect( ray ); +} + +float min8( const __m256 x ) +{ + const __m128 hiQuad = _mm256_extractf128_ps( x, 1 ), loQuad = _mm256_castps256_ps128( x ); + const __m128 minQuad = _mm_min_ps( loQuad, hiQuad ), loDual = minQuad; + const __m128 hiDual = _mm_movehl_ps( minQuad, minQuad ), minDual = _mm_min_ps( loDual, hiDual ); + const __m128 lo = minDual, hi = _mm_shuffle_ps( minDual, minDual, 1 ); + const __m128 res = _mm_min_ss( lo, hi ); + return _mm_cvtss_f32( res ); +} + +template int32_t BVH8_CPU::Intersect( Ray& ray ) const +{ + ALIGNED( 64 ) int32_t nodeStack[256]; + ALIGNED( 64 ) float distStack[256]; + const __m256 zero8 = _mm256_setzero_ps(); + __m256 t8 = _mm256_set1_ps( ray.hit.t ); + ALIGNED( 64 ) int32_t stackPtr = 0, nodeIdx = 0; + union ALIGNED( 32 ) { __m256i c8s; uint32_t cs[8]; }; + constexpr int signShift = (posX ? 3 : 0) + (posY ? 6 : 0) + (posZ ? 12 : 0); + const __m256 rx8 = _mm256_set1_ps( ray.O.x * ray.rD.x ), rdx8 = _mm256_set1_ps( ray.rD.x ); + const __m256 ry8 = _mm256_set1_ps( ray.O.y * ray.rD.y ), rdy8 = _mm256_set1_ps( ray.rD.y ); + const __m256 rz8 = _mm256_set1_ps( ray.O.z * ray.rD.z ), rdz8 = _mm256_set1_ps( ray.rD.z ); + const __m128 ox4 = _mm_set1_ps( ray.O.x ), oy4 = _mm_set1_ps( ray.O.y ), oz4 = _mm_set1_ps( ray.O.z ); + const __m128 dx4 = _mm_set1_ps( ray.D.x ), dy4 = _mm_set1_ps( ray.D.y ), dz4 = _mm_set1_ps( ray.D.z ); + const __m128 one4 = _mm_set1_ps( 1 ), inf4 = _mm_set1_ps( 1e34f ); +#ifdef _DEBUG + // sorry, not even this can be tolerated in this function. Only in debug. + uint32_t steps = 0; +#endif + while (1) + { + #ifdef _DEBUG + steps++; + #endif + while (!(nodeIdx & LEAF_BIT)) + { + const BVHNode* n = (BVHNode*)(bvh8Data + nodeIdx); + const __m256 tx1 = _mm256_fmsub_ps( posX ? n->xmin8 : n->xmax8, rdx8, rx8 ); + const __m256 ty1 = _mm256_fmsub_ps( posY ? n->ymin8 : n->ymax8, rdy8, ry8 ); + const __m256 tz1 = _mm256_fmsub_ps( posZ ? n->zmin8 : n->zmax8, rdz8, rz8 ); + const __m256 tx2 = _mm256_fmsub_ps( posX ? n->xmax8 : n->xmin8, rdx8, rx8 ); + const __m256 ty2 = _mm256_fmsub_ps( posY ? n->ymax8 : n->ymin8, rdy8, ry8 ); + const __m256 tz2 = _mm256_fmsub_ps( posZ ? n->zmax8 : n->zmin8, rdz8, rz8 ); + __m256 tmin = _mm256_max_ps( _mm256_max_ps( _mm256_max_ps( zero8, tx1 ), ty1 ), tz1 ); + __m256 tmax = _mm256_min_ps( _mm256_min_ps( _mm256_min_ps( tx2, t8 ), ty2 ), tz2 ); + const __m256 mask8 = _mm256_cmp_ps( tmin, tmax, _CMP_LE_OQ ); + const uint32_t mask = _mm256_movemask_ps( mask8 ); + const uint32_t validNodes = __popc( mask ); + if (validNodes == 1) + { + const uint32_t lane = __bfind( mask ); + nodeIdx = ((uint32_t*)&n->child8)[lane]; + } + else if (validNodes > 0) + { + const __m256i index = _mm256_srli_epi32( n->perm8, signShift ); + const uint32_t m = _mm256_movemask_ps( _mm256_permutevar8x32_ps( mask8, index ) ); + tmin = _mm256_permutevar8x32_ps( tmin, index ); + const __m256i cpi = idxLUT256[255 - m]; + const __m256i c8 = _mm256_permutevar8x32_epi32( n->child8, index ); + const __m256 dist8 = _mm256_permutevar8x32_ps( tmin, cpi ); + const __m256i child8 = _mm256_permutevar8x32_epi32( c8, cpi ); + _mm256_storeu_si256( (__m256i*)(nodeStack + stackPtr), child8 ); + _mm256_storeu_ps( (float*)(distStack + stackPtr), dist8 ); + stackPtr += validNodes - 1; + nodeIdx = nodeStack[stackPtr]; + } + else + { + if (!stackPtr) goto the_end; + nodeIdx = nodeStack[--stackPtr]; + } + } + // Moeller-Trumbore ray/triangle intersection algorithm for four triangles + uint32_t n; + memcpy( &n, &nodeIdx, 4 ); + const BVHTri4Leaf* leaf = (BVHTri4Leaf*)(bvh8Data + (n & 0x1fffffff)); + const __m128 hx4 = _mm_fmsub_ps( dy4, leaf->e2z4, _mm_mul_ps( dz4, leaf->e2y4 ) ); + const __m128 hy4 = _mm_fmsub_ps( dz4, leaf->e2x4, _mm_mul_ps( dx4, leaf->e2z4 ) ); + const __m128 hz4 = _mm_fmsub_ps( dx4, leaf->e2y4, _mm_mul_ps( dy4, leaf->e2x4 ) ); + const __m128 sx4 = _mm_sub_ps( ox4, leaf->v0x4 ), sy4 = _mm_sub_ps( oy4, leaf->v0y4 ); + const __m128 sz4 = _mm_sub_ps( oz4, leaf->v0z4 ); + const __m128 det4 = _mm_fmadd_ps( leaf->e1z4, hz4, _mm_fmadd_ps( leaf->e1x4, hx4, _mm_mul_ps( leaf->e1y4, hy4 ) ) ); + const __m128 qz4 = _mm_fmsub_ps( sx4, leaf->e1y4, _mm_mul_ps( sy4, leaf->e1x4 ) ); + const __m128 qx4 = _mm_fmsub_ps( sy4, leaf->e1z4, _mm_mul_ps( sz4, leaf->e1y4 ) ); + const __m128 qy4 = _mm_fmsub_ps( sz4, leaf->e1x4, _mm_mul_ps( sx4, leaf->e1z4 ) ); + const __m128 inv_det4 = fastrcp4( det4 ); + const __m128 u4 = _mm_mul_ps( _mm_fmadd_ps( sz4, hz4, _mm_fmadd_ps( sx4, hx4, _mm_mul_ps( sy4, hy4 ) ) ), inv_det4 ); + const __m128 v4 = _mm_mul_ps( _mm_fmadd_ps( dz4, qz4, _mm_fmadd_ps( dx4, qx4, _mm_mul_ps( dy4, qy4 ) ) ), inv_det4 ); + const __m128 ta4 = _mm_mul_ps( _mm_fmadd_ps( leaf->e2z4, qz4, _mm_fmadd_ps( leaf->e2x4, qx4, _mm_mul_ps( leaf->e2y4, qy4 ) ) ), inv_det4 ); + const __m128 mask1 = _mm_cmpge_ps( u4, _mm_setzero_ps() ), mask2 = _mm_cmpge_ps( v4, _mm_setzero_ps() ); + const __m128 mask3 = _mm_cmple_ps( _mm_add_ps( u4, v4 ), one4 ); + const __m128 mask4 = _mm_cmpgt_ps( ta4, _mm_setzero_ps() ); + const __m128 mask5 = _mm_cmplt_ps( ta4, _mm256_extractf128_ps( t8, 0 ) ); + __m128 combined = _mm_and_ps( _mm_and_ps( _mm_and_ps( mask1, mask2 ), _mm_and_ps( mask3, mask4 ) ), mask5 ); + uint32_t imask = _mm_movemask_ps( combined ); + // evaluate opacity map, if present (SSE version). + if (opmap) if (imask) + { + const __m128 fN4 = _mm_set1_ps( (float)opmapN ); + const __m128i row4 = _mm_cvttps_epi32( _mm_mul_ps( _mm_add_ps( u4, v4 ), fN4 ) ); + const __m128i dia4 = _mm_cvttps_epi32( _mm_mul_ps( _mm_sub_ps( one4, u4 ), fN4 ) ); + const __m128i v0 = _mm_mullo_epi32( row4, row4 ); + const __m128i v1 = _mm_cvttps_epi32( _mm_mul_ps( v4, fN4 ) ); + const __m128i v2 = _mm_sub_epi32( dia4, _mm_sub_epi32( _mm_set1_epi32( opmapN - 1 ), row4 ) ); + union { uint32_t idx[4]; __m128i idx4; }; + union { uint32_t omask[4]; __m128 omask4; }; + idx4 = _mm_add_epi32( _mm_add_epi32( v0, v1 ), v2 ); + // proceed with scalar code for gather operation - TODO: better approach? + omask[0] = omask[1] = omask[2] = omask[3] = 0; + for (int i = 0; i < 4; i++) if (imask & (1 << i)) + { + uint32_t* om = opmap + leaf->primIdx[i] * ((opmapN * opmapN + 31) >> 5); + if (om[idx[i] >> 5] & (1 << (idx[i] & 31))) omask[i] = 0xffffffff; + } + // combine + combined = _mm_and_ps( combined, omask4 ); + imask = _mm_movemask_ps( combined ); + } + if (imask) + { + // compute broadcasted horizontal minimum of dist4 + const __m128 dist4 = _mm_blendv_ps( inf4, ta4, combined ); + const __m128 a = _mm_min_ps( dist4, _mm_shuffle_ps( dist4, dist4, _MM_SHUFFLE( 2, 1, 0, 3 ) ) ); + const __m128 c = _mm_min_ps( a, _mm_shuffle_ps( a, a, _MM_SHUFFLE( 1, 0, 3, 2 ) ) ); + const uint32_t lane = __bfind( _mm_movemask_ps( _mm_cmpeq_ps( c, dist4 ) ) ); + // update hit record + const __m128 _d4 = dist4; + const float t = ((float*)&_d4)[lane]; + const __m128 _u4 = u4, _v4 = v4; + ray.hit.t = t, ray.hit.u = ((float*)&_u4)[lane], ray.hit.v = ((float*)&_v4)[lane]; + #if INST_IDX_BITS == 32 + ray.hit.prim = leaf->primIdx[lane], ray.hit.inst = ray.instIdx; + #else + ray.hit.prim = leaf->primIdx[lane] + ray.instIdx; + #endif + t8 = _mm256_set1_ps( t ); + // compress stack + uint32_t outStackPtr = 0; + for (int32_t i = 0; i < stackPtr; i += 8) + { + __m256i node8 = _mm256_load_si256( (__m256i*)(nodeStack + i) ); + __m256 dist8 = _mm256_load_ps( (float*)(distStack + i) ); + const uint32_t mask = _mm256_movemask_ps( _mm256_cmp_ps( dist8, t8, _CMP_LE_OQ ) ); + const __m256i cpi = idxLUT256[255 - mask]; + dist8 = _mm256_permutevar8x32_ps( dist8, cpi ), node8 = _mm256_permutevar8x32_epi32( node8, cpi ); + _mm256_storeu_ps( (float*)(distStack + outStackPtr), dist8 ); + _mm256_storeu_si256( (__m256i*)(nodeStack + outStackPtr), node8 ); + const int32_t numItems = tinybvh_min( 8, stackPtr - i ), validMask = (1 << numItems) - 1; + outStackPtr += __popc( mask & validMask ); + } + stackPtr = outStackPtr; + } + if (!stackPtr) break; + nodeIdx = nodeStack[--stackPtr]; + } +the_end: +#ifdef _DEBUG + return steps; +#else + return 0; +#endif +} + +bool BVH8_CPU::IsOccluded( const Ray& ray ) const +{ + VALIDATE_RAY( ray ); + const bool posX = ray.D.x >= 0, posY = ray.D.y >= 0, posZ = ray.D.z >= 0; + if (!posX) goto negx; + if (posY) { if (posZ) return IsOccluded( ray ); else return IsOccluded( ray ); } + if (posZ) return IsOccluded( ray ); else return IsOccluded( ray ); +negx: + if (posY) { if (posZ) return IsOccluded( ray ); else return IsOccluded( ray ); } + if (posZ) return IsOccluded( ray ); else return IsOccluded( ray ); +} + +template bool BVH8_CPU::IsOccluded( const Ray& ray ) const +{ + ALIGNED( 64 ) uint32_t nodeStack[256]; + ALIGNED( 64 ) int32_t stackPtr = 0, nodeIdx = 0; + const __m256 t8 = _mm256_set1_ps( ray.hit.t ); + const __m256 rx8 = _mm256_set1_ps( ray.O.x * ray.rD.x ), rdx8 = _mm256_set1_ps( ray.rD.x ); + const __m256 ry8 = _mm256_set1_ps( ray.O.y * ray.rD.y ), rdy8 = _mm256_set1_ps( ray.rD.y ); + const __m256 rz8 = _mm256_set1_ps( ray.O.z * ray.rD.z ), rdz8 = _mm256_set1_ps( ray.rD.z ); + const __m128 ox4 = _mm_set1_ps( ray.O.x ), oy4 = _mm_set1_ps( ray.O.y ), oz4 = _mm_set1_ps( ray.O.z ); + const __m128 dx4 = _mm_set1_ps( ray.D.x ), dy4 = _mm_set1_ps( ray.D.y ), dz4 = _mm_set1_ps( ray.D.z ); + const __m128 t4 = _mm_set1_ps( ray.hit.t ); + const __m128 one4 = _mm_set1_ps( 1.0f ), zero4 = _mm_setzero_ps(); + while (1) + { + while (!(nodeIdx & LEAF_BIT)) + { + const BVHNode* n = (BVHNode*)(bvh8Data + nodeIdx); + const __m256i c8 = n->child8; + const __m256 tx1 = _mm256_fmsub_ps( posX ? n->xmin8 : n->xmax8, rdx8, rx8 ); + const __m256 ty1 = _mm256_fmsub_ps( posY ? n->ymin8 : n->ymax8, rdy8, ry8 ); + const __m256 tz1 = _mm256_fmsub_ps( posZ ? n->zmin8 : n->zmax8, rdz8, rz8 ); + const __m256 tx2 = _mm256_fmsub_ps( posX ? n->xmax8 : n->xmin8, rdx8, rx8 ); + const __m256 ty2 = _mm256_fmsub_ps( posY ? n->ymax8 : n->ymin8, rdy8, ry8 ); + const __m256 tz2 = _mm256_fmsub_ps( posZ ? n->zmax8 : n->zmin8, rdz8, rz8 ); + const __m256 tmin = _mm256_max_ps( _mm256_max_ps( _mm256_max_ps( _mm256_setzero_ps(), tx1 ), ty1 ), tz1 ); + const __m256 tmax = _mm256_min_ps( _mm256_min_ps( _mm256_min_ps( tx2, t8 ), ty2 ), tz2 ); + const __m256 mask8 = _mm256_cmp_ps( tmin, tmax, _CMP_LE_OQ ); + const uint32_t mask = _mm256_movemask_ps( mask8 ); // _mm256_or_ps( _mm256_castsi256_ps( c8 ), mask8 ) ); + const uint32_t validNodes = __popc( mask ); + if (validNodes == 1) + { + const uint32_t lane = __bfind( mask ); + nodeIdx = ((uint32_t*)&n->child8)[lane]; + } + else if (validNodes > 0) + { + const __m256i cpi = idxLUT256[255 - mask]; + const __m256i child8 = _mm256_permutevar8x32_epi32( c8, cpi ); + _mm256_storeu_si256( (__m256i*)(nodeStack + stackPtr), child8 ); + stackPtr += validNodes - 1; + nodeIdx = nodeStack[stackPtr]; + } + else + { + if (!stackPtr) return false; + nodeIdx = nodeStack[--stackPtr]; + } + } + uint32_t n; + memcpy( &n, &nodeIdx, 4 ); + // Moeller-Trumbore ray/triangle intersection algorithm for four triangles + const BVHTri4Leaf* leaf = (BVHTri4Leaf*)(bvh8Data + (n & 0x1fffffff)); + const __m128 hx4 = _mm_fmsub_ps( dy4, leaf->e2z4, _mm_mul_ps( dz4, leaf->e2y4 ) ); + const __m128 hy4 = _mm_fmsub_ps( dz4, leaf->e2x4, _mm_mul_ps( dx4, leaf->e2z4 ) ); + const __m128 hz4 = _mm_fmsub_ps( dx4, leaf->e2y4, _mm_mul_ps( dy4, leaf->e2x4 ) ); + const __m128 sx4 = _mm_sub_ps( ox4, leaf->v0x4 ); + const __m128 sy4 = _mm_sub_ps( oy4, leaf->v0y4 ); + const __m128 sz4 = _mm_sub_ps( oz4, leaf->v0z4 ); + const __m128 det4 = _mm_fmadd_ps( leaf->e1z4, hz4, _mm_fmadd_ps( leaf->e1x4, hx4, _mm_mul_ps( leaf->e1y4, hy4 ) ) ); + const __m128 qz4 = _mm_fmsub_ps( sx4, leaf->e1y4, _mm_mul_ps( sy4, leaf->e1x4 ) ); + const __m128 qx4 = _mm_fmsub_ps( sy4, leaf->e1z4, _mm_mul_ps( sz4, leaf->e1y4 ) ); + const __m128 qy4 = _mm_fmsub_ps( sz4, leaf->e1x4, _mm_mul_ps( sx4, leaf->e1z4 ) ); + const __m128 inv_det4 = fastrcp4( det4 ); + const __m128 u4 = _mm_mul_ps( _mm_fmadd_ps( sz4, hz4, _mm_fmadd_ps( sx4, hx4, _mm_mul_ps( sy4, hy4 ) ) ), inv_det4 ); + const __m128 v4 = _mm_mul_ps( _mm_fmadd_ps( dz4, qz4, _mm_fmadd_ps( dx4, qx4, _mm_mul_ps( dy4, qy4 ) ) ), inv_det4 ); + const __m128 ta4 = _mm_mul_ps( _mm_fmadd_ps( leaf->e2z4, qz4, _mm_fmadd_ps( leaf->e2x4, qx4, _mm_mul_ps( leaf->e2y4, qy4 ) ) ), inv_det4 ); + const __m128 mask1 = _mm_cmpge_ps( u4, zero4 ); + const __m128 mask2 = _mm_cmpge_ps( v4, zero4 ); + const __m128 mask3 = _mm_cmple_ps( _mm_add_ps( u4, v4 ), one4 ); + const __m128 mask4 = _mm_cmplt_ps( ta4, t4 ); + const __m128 mask5 = _mm_cmpgt_ps( ta4, zero4 ); + __m128 combined = _mm_and_ps( _mm_and_ps( _mm_and_ps( mask1, mask2 ), _mm_and_ps( mask3, mask4 ) ), mask5 ); + if (_mm_movemask_ps( combined )) + { + if (!opmap) return true; + // evaluate opacity map, SSE version. + const __m128 fN4 = _mm_set1_ps( (float)opmapN ); + const __m128i row4 = _mm_cvttps_epi32( _mm_mul_ps( _mm_add_ps( u4, v4 ), fN4 ) ); + const __m128i dia4 = _mm_cvttps_epi32( _mm_mul_ps( _mm_sub_ps( one4, u4 ), fN4 ) ); + const __m128i v0 = _mm_mullo_epi32( row4, row4 ); + const __m128i v1 = _mm_cvttps_epi32( _mm_mul_ps( v4, fN4 ) ); + const __m128i v2 = _mm_sub_epi32( dia4, _mm_sub_epi32( _mm_set1_epi32( opmapN - 1 ), row4 ) ); + union { uint32_t idx[4]; __m128i idx4; }; + idx4 = _mm_add_epi32( _mm_add_epi32( v0, v1 ), v2 ); + // proceed with scalar code for gather operation - TODO: better approach? + const uint32_t imask = _mm_movemask_ps( combined ); + for (int i = 0; i < 4; i++) if (imask & (1 << i)) + { + uint32_t* om = opmap + leaf->primIdx[i] * ((opmapN * opmapN + 31) >> 5); + if (om[idx[i] >> 5] & (1 << (idx[i] & 31))) return true; + } + } + // continue + if (!stackPtr) return false; + nodeIdx = nodeStack[--stackPtr]; + } +} + +#endif // BVH_USEAVX2 + +#endif // BVH_USEAVX + +// ============================================================================ +// +// I M P L E M E N T A T I O N - A R M / N E O N C O D E +// +// ============================================================================ + +#ifdef BVH_USENEON + +#define ILANE(a,b) vgetq_lane_s32(a, b) + +inline float halfArea( const float32x4_t a /* a contains extent of aabb */ ) +{ + ALIGNED( 64 ) float v[4]; + vst1q_f32( v, a ); + return v[0] * v[1] + v[1] * v[2] + v[2] * v[3]; +} +inline float halfArea( const float32x4x2_t& a /* a contains aabb itself, with min.xyz negated */ ) +{ + ALIGNED( 64 ) float c[8]; + vst1q_f32( c, a.val[0] ); + vst1q_f32( c + 4, a.val[1] ); + + float ex = c[4] + c[0], ey = c[5] + c[1], ez = c[6] + c[2]; + return ex * ey + ey * ez + ez * ex; +} + +#define PROCESS_PLANE( a, pos, ANLR, lN, rN, lb, rb ) if (lN * rN != 0) { \ + ANLR = halfArea( lb ) * (float)lN + halfArea( rb ) * (float)rN; \ + const float C = c_trav + c_int * rSAV * ANLR; if (C < splitCost) \ + splitCost = C, bestAxis = a, bestPos = pos, bestLBox = lb, bestRBox = rb; } + +void BVH::BuildNEON( const bvhvec4* vertices, const uint32_t primCount ) +{ + // build the BVH with a continuous array of bvhvec4 vertices: + // in this case, the stride for the slice is 16 bytes. + BuildNEON( bvhvec4slice{ vertices, primCount * 3, sizeof( bvhvec4 ) } ); +} +void BVH::BuildNEON( const bvhvec4slice& vertices ) +{ + PrepareNEONBuild( vertices, 0, 0 ); + BuildNEON(); +} +void BVH::BuildNEON( const bvhvec4* vertices, const uint32_t* indices, const uint32_t primCount ) +{ + // build the BVH with an indexed array of bvhvec4 vertices. + BuildNEON( bvhvec4slice{ vertices, primCount * 3, sizeof( bvhvec4 ) }, indices, primCount ); +} +void BVH::BuildNEON( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t primCount ) +{ + PrepareNEONBuild( vertices, indices, primCount ); + BuildNEON(); +} +void BVH::PrepareNEONBuild( const bvhvec4slice& vertices, const uint32_t* indices, const uint32_t prims ) +{ + BVH_FATAL_ERROR_IF( vertices.count == 0, "BVH::PrepareNEONBuild( .. ), primCount == 0." ); + BVH_FATAL_ERROR_IF( vertices.stride & 15, "BVH::PrepareNEONBuild( .. ), stride must be multiple of 16." ); + // some constants + static const float32x4_t min4 = vdupq_n_f32( BVH_FAR ), max4 = vdupq_n_f32( -BVH_FAR ); + // reset node pool + uint32_t primCount = prims > 0 ? prims : vertices.count / 3; + const uint32_t spaceNeeded = primCount * 2; + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + AlignedFree( primIdx ); + AlignedFree( fragment ); + primIdx = (uint32_t*)AlignedAlloc( primCount * sizeof( uint32_t ) ); + bvhNode = (BVHNode*)AlignedAlloc( spaceNeeded * sizeof( BVHNode ) ); + allocatedNodes = spaceNeeded; + memset( &bvhNode[1], 0, 32 ); // avoid crash in refit. + fragment = (Fragment*)AlignedAlloc( primCount * sizeof( Fragment ) ); + } + else BVH_FATAL_ERROR_IF( !rebuildable, "BVH::BuildAVX( .. ), bvh not rebuildable." ); + verts = vertices; // note: we're not copying this data; don't delete. + vertIdx = (uint32_t*)indices; + triCount = idxCount = primCount; + newNodePtr = 2; + struct FragSSE { float32x4_t bmin4, bmax4; }; + FragSSE* frag4 = (FragSSE*)fragment; + const float32x4_t* verts4 = (float32x4_t*)verts.data; // that's why it must be 16-byte aligned. + // assign all triangles to the root node + BVHNode& root = bvhNode[0]; + root.leftFirst = 0, root.triCount = triCount; + // initialize fragments and update root bounds + float32x4_t rootMin = min4, rootMax = max4; + if (indices) + { + BVH_FATAL_ERROR_IF( vertices.count == 0, "BVH::PrepareAVXBuild( .. ), empty vertex slice." ); + BVH_FATAL_ERROR_IF( prims == 0, "BVH::PrepareAVXBuild( .. ), prims == 0." ); + // build the BVH over indexed triangles + for (uint32_t i = 0; i < triCount; i++) + { + const uint32_t i0 = indices[i * 3], i1 = indices[i * 3 + 1], i2 = indices[i * 3 + 2]; + const float32x4_t v0 = verts4[i0], v1 = verts4[i1], v2 = verts4[i2]; + const float32x4_t t1 = vminq_f32( vminq_f32( v0, v1 ), v2 ); + const float32x4_t t2 = vmaxq_f32( vmaxq_f32( v0, v1 ), v2 ); + frag4[i].bmin4 = t1, frag4[i].bmax4 = t2, rootMin = vminq_f32( rootMin, t1 ), rootMax = vmaxq_f32( rootMax, t2 ); + primIdx[i] = i; + } + } + else + { + BVH_FATAL_ERROR_IF( vertices.count == 0, "BVH::PrepareAVXBuild( .. ), empty vertex slice." ); + BVH_FATAL_ERROR_IF( prims != 0, "BVH::PrepareAVXBuild( .. ), indices == 0." ); + // build the BVH over a list of vertices: three per triangle + for (uint32_t i = 0; i < triCount; i++) + { + const float32x4_t v0 = verts4[i * 3], v1 = verts4[i * 3 + 1], v2 = verts4[i * 3 + 2]; + const float32x4_t t1 = vminq_f32( vminq_f32( v0, v1 ), v2 ); + const float32x4_t t2 = vmaxq_f32( vmaxq_f32( v0, v1 ), v2 ); + frag4[i].bmin4 = t1, frag4[i].bmax4 = t2, rootMin = vminq_f32( rootMin, t1 ), rootMax = vmaxq_f32( rootMax, t2 ); + primIdx[i] = i; + } + } + root.aabbMin = *(bvhvec3*)&rootMin, root.aabbMax = *(bvhvec3*)&rootMax; + bvh_over_indices = indices != nullptr; +} + +inline float32x4x2_t _mm256_set1_ps( float v ) +{ + float32x4_t v4 = vdupq_n_f32( v ); + return float32x4x2_t{ v4, v4 }; +} + +inline float32x4x2_t _mm256_and_ps( float32x4x2_t v0, float32x4x2_t v1 ) +{ + float32x4_t r0 = vreinterpretq_f32_s32( vandq_s32( vreinterpretq_s32_f32( v0.val[0] ), vreinterpretq_s32_f32( v1.val[0] ) ) ); + float32x4_t r1 = vreinterpretq_f32_s32( vandq_s32( vreinterpretq_s32_f32( v0.val[1] ), vreinterpretq_s32_f32( v1.val[1] ) ) ); + return float32x4x2_t{ r0, r1 }; +} + +inline float32x4x2_t _mm256_max_ps( float32x4x2_t v0, float32x4x2_t v1 ) +{ + float32x4_t r0 = vmaxq_f32( v0.val[0], v1.val[0] ); + float32x4_t r1 = vmaxq_f32( v0.val[1], v1.val[1] ); + return float32x4x2_t{ r0, r1 }; +} + +inline float32x4x2_t _mm256_xor_ps( float32x4x2_t v0, float32x4x2_t v1 ) +{ + float32x4_t r0 = vreinterpretq_f32_s32( veorq_s32( vreinterpretq_s32_f32( v0.val[0] ), vreinterpretq_s32_f32( v1.val[0] ) ) ); + float32x4_t r1 = vreinterpretq_f32_s32( veorq_s32( vreinterpretq_s32_f32( v0.val[1] ), vreinterpretq_s32_f32( v1.val[1] ) ) ); + return float32x4x2_t{ r0, r1 }; +} + +void BVH::BuildNEON() +{ +#if 1 + // NEON code needs an overhaul. + Build(); +#else + // aligned data + ALIGNED( 64 ) float32x4x2_t binbox[3 * AVXBINS]; // 768 bytes + ALIGNED( 64 ) float32x4x2_t binboxOrig[3 * AVXBINS]; // 768 bytes + ALIGNED( 64 ) uint32_t count[3][AVXBINS]{}; // 96 bytes + ALIGNED( 64 ) float32x4x2_t bestLBox, bestRBox; // 64 bytes + // some constants + static const float32x4_t half4 = vdupq_n_f32( 0.5f ); + static const float32x4_t two4 = vdupq_n_f32( 2.0f ), min1 = vdupq_n_f32( -1 ); + static const int32x4_t maxbin4 = vdupq_n_s32( 7 ); + static const float32x4_t mask3 = vreinterpretq_f32_u32( vceqq_s32( SIMD_SETRVECS( 0, 0, 0, 1 ), vdupq_n_s32( 0 ) ) ); + static const float32x4_t binmul3 = vdupq_n_f32( AVXBINS * 0.49999f ); + static const float32x4x2_t max8 = _mm256_set1_ps( -BVH_FAR ), mask6 = { mask3, mask3 }; + static const float32x4_t signFlip4 = SIMD_SETRVEC( -0.0f, -0.0f, -0.0f, 0.0f ); + static const float32x4x2_t signFlip8 = { signFlip4, vdupq_n_f32( 0 ) }; + for (uint32_t i = 0; i < 3 * AVXBINS; i++) binboxOrig[i] = max8; // binbox initialization template + struct FragSSE { float32x4_t bmin4, bmax4; }; + FragSSE* frag4 = (FragSSE*)fragment; + float32x4x2_t* frag8 = (float32x4x2_t*)fragment; + // subdivide recursively + ALIGNED( 64 ) uint32_t task[128], taskCount = 0, nodeIdx = 0; + BVHNode& root = bvhNode[0]; + const bvhvec3 minDim = (root.aabbMax - root.aabbMin) * 1e-7f; + while (1) + { + while (1) + { + BVHNode& node = bvhNode[nodeIdx]; + float32x4_t* node4 = (float32x4_t*)&bvhNode[nodeIdx]; + // find optimal object split + const float32x4_t d4 = vbslq_f32( vshrq_n_u32( vreinterpretq_u32_f32( mask3 ), 31 ), vsubq_f32( node4[1], node4[0] ), min1 ); + const float32x4_t nmin4 = vmulq_f32( vreinterpretq_f32_s32( vandq_s32( vreinterpretq_s32_f32( node4[0] ), vreinterpretq_s32_f32( mask3 ) ) ), two4 ); + const float32x4_t rpd4 = vreinterpretq_f32_s32( vandq_s32( vreinterpretq_s32_f32( vdivq_f32( binmul3, d4 ) ), vmvnq_s32( vreinterpretq_s32_u32( vceqq_f32( d4, vdupq_n_f32( 0 ) ) ) ) ) ); + // implementation of Section 4.1 of "Parallel Spatial Splits in Bounding Volume Hierarchies": + // main loop operates on two fragments to minimize dependencies and maximize ILP. + uint32_t fi = primIdx[node.leftFirst]; + memset( count, 0, sizeof( count ) ); + float32x4x2_t r0, r1, r2, f = _mm256_xor_ps( _mm256_and_ps( frag8[fi], mask6 ), signFlip8 ); + const float32x4_t fmin = vreinterpretq_f32_u32( vandq_u32( vreinterpretq_u32_f32( frag4[fi].bmin4 ), vreinterpretq_u32_f32( mask3 ) ) ); + const float32x4_t fmax = vreinterpretq_f32_u32( vandq_u32( vreinterpretq_u32_f32( frag4[fi].bmax4 ), vreinterpretq_u32_f32( mask3 ) ) ); + const int32x4_t bi4 = vcvtq_s32_f32( vrndnq_f32( vsubq_f32( vmulq_f32( vsubq_f32( vaddq_f32( frag4[fi].bmax4, frag4[fi].bmin4 ), nmin4 ), rpd4 ), half4 ) ) ); + const int32x4_t b4c = vmaxq_s32( vminq_s32( bi4, maxbin4 ), vdupq_n_s32( 0 ) ); // clamp needed after all + memcpy( binbox, binboxOrig, sizeof( binbox ) ); + uint32_t i0 = ILANE( b4c, 0 ), i1 = ILANE( b4c, 1 ), i2 = ILANE( b4c, 2 ), * ti = primIdx + node.leftFirst + 1; + for (uint32_t i = 0; i < node.triCount - 1; i++) + { + uint32_t fid = *ti++; + // #if defined __GNUC__ || _MSC_VER < 1920 + // if (fid > triCount) fid = triCount - 1; // never happens but g++ *and* vs2017 need this to not crash... + // #endif + const float32x4x2_t b0 = binbox[i0], b1 = binbox[AVXBINS + i1], b2 = binbox[2 * AVXBINS + i2]; + const float32x4_t frmin = vreinterpretq_f32_u32( vandq_u32( vreinterpretq_u32_f32( frag4[fid].bmin4 ), vreinterpretq_u32_f32( mask3 ) ) ); + const float32x4_t frmax = vreinterpretq_f32_u32( vandq_u32( vreinterpretq_u32_f32( frag4[fid].bmax4 ), vreinterpretq_u32_f32( mask3 ) ) ); + r0 = _mm256_max_ps( b0, f ), r1 = _mm256_max_ps( b1, f ), r2 = _mm256_max_ps( b2, f ); + const int32x4_t b4 = vcvtq_s32_f32( vrndnq_f32( (vsubq_f32( vmulq_f32( vsubq_f32( vaddq_f32( frmax, frmin ), nmin4 ), rpd4 ), half4 )) ) ); + const int32x4_t bc4 = vmaxq_s32( vminq_s32( b4, maxbin4 ), vdupq_n_s32( 0 ) ); // clamp needed after all + f = _mm256_xor_ps( _mm256_and_ps( frag8[fid], mask6 ), signFlip8 ), count[0][i0]++, count[1][i1]++, count[2][i2]++; + binbox[i0] = r0, i0 = ILANE( bc4, 0 ); + binbox[AVXBINS + i1] = r1, i1 = ILANE( bc4, 1 ); + binbox[2 * AVXBINS + i2] = r2, i2 = ILANE( bc4, 2 ); + } + // final business for final fragment + const float32x4x2_t b0 = binbox[i0], b1 = binbox[AVXBINS + i1], b2 = binbox[2 * AVXBINS + i2]; + count[0][i0]++, count[1][i1]++, count[2][i2]++; + r0 = _mm256_max_ps( b0, f ), r1 = _mm256_max_ps( b1, f ), r2 = _mm256_max_ps( b2, f ); + binbox[i0] = r0, binbox[AVXBINS + i1] = r1, binbox[2 * AVXBINS + i2] = r2; + // calculate per-split totals + float splitCost = BVH_FAR, rSAV = 1.0f / node.SurfaceArea(); + uint32_t bestAxis = 0, bestPos = 0, n = newNodePtr, j = node.leftFirst + node.triCount, src = node.leftFirst; + const float32x4x2_t* bb = binbox; + for (int32_t a = 0; a < 3; a++, bb += AVXBINS) if ((node.aabbMax[a] - node.aabbMin[a]) > minDim[a]) + { + // hardcoded bin processing for AVXBINS == 8 + assert( AVXBINS == 8 ); + const uint32_t lN0 = count[a][0], rN0 = count[a][7]; + const float32x4x2_t lb0 = bb[0], rb0 = bb[7]; + const uint32_t lN1 = lN0 + count[a][1], rN1 = rN0 + count[a][6], lN2 = lN1 + count[a][2]; + const uint32_t rN2 = rN1 + count[a][5], lN3 = lN2 + count[a][3], rN3 = rN2 + count[a][4]; + const float32x4x2_t lb1 = _mm256_max_ps( lb0, bb[1] ), rb1 = _mm256_max_ps( rb0, bb[6] ); + const float32x4x2_t lb2 = _mm256_max_ps( lb1, bb[2] ), rb2 = _mm256_max_ps( rb1, bb[5] ); + const float32x4x2_t lb3 = _mm256_max_ps( lb2, bb[3] ), rb3 = _mm256_max_ps( rb2, bb[4] ); + const uint32_t lN4 = lN3 + count[a][4], rN4 = rN3 + count[a][3], lN5 = lN4 + count[a][5]; + const uint32_t rN5 = rN4 + count[a][2], lN6 = lN5 + count[a][6], rN6 = rN5 + count[a][1]; + const float32x4x2_t lb4 = _mm256_max_ps( lb3, bb[4] ), rb4 = _mm256_max_ps( rb3, bb[3] ); + const float32x4x2_t lb5 = _mm256_max_ps( lb4, bb[5] ), rb5 = _mm256_max_ps( rb4, bb[2] ); + const float32x4x2_t lb6 = _mm256_max_ps( lb5, bb[6] ), rb6 = _mm256_max_ps( rb5, bb[1] ); + float ANLR3 = BVH_FAR; PROCESS_PLANE( a, 3, ANLR3, lN3, rN3, lb3, rb3 ); // most likely split + float ANLR2 = BVH_FAR; PROCESS_PLANE( a, 2, ANLR2, lN2, rN4, lb2, rb4 ); + float ANLR4 = BVH_FAR; PROCESS_PLANE( a, 4, ANLR4, lN4, rN2, lb4, rb2 ); + float ANLR5 = BVH_FAR; PROCESS_PLANE( a, 5, ANLR5, lN5, rN1, lb5, rb1 ); + float ANLR1 = BVH_FAR; PROCESS_PLANE( a, 1, ANLR1, lN1, rN5, lb1, rb5 ); + float ANLR0 = BVH_FAR; PROCESS_PLANE( a, 0, ANLR0, lN0, rN6, lb0, rb6 ); + float ANLR6 = BVH_FAR; PROCESS_PLANE( a, 6, ANLR6, lN6, rN0, lb6, rb0 ); // least likely split + } + float noSplitCost = (float)node.triCount * c_int; + if (splitCost >= noSplitCost) break; // not splitting is better. + // in-place partition + const float rpd = (*(bvhvec3*)&rpd4)[bestAxis], nmin = (*(bvhvec3*)&nmin4)[bestAxis]; + uint32_t t, fr = primIdx[src]; + for (uint32_t i = 0; i < node.triCount; i++) + { + const uint32_t bi = (uint32_t)((fragment[fr].bmax[bestAxis] + fragment[fr].bmin[bestAxis] - nmin) * rpd); + if (bi <= bestPos) fr = primIdx[++src]; else t = fr, fr = primIdx[src] = primIdx[--j], primIdx[j] = t; + } + // create child nodes and recurse + const uint32_t leftCount = src - node.leftFirst, rightCount = node.triCount - leftCount; + if (leftCount == 0 || rightCount == 0 || taskCount == BVH_NUM_ELEMS( task )) break; // should not happen. + *(float32x4x2_t*)&bvhNode[n] = _mm256_xor_ps( bestLBox, signFlip8 ); + bvhNode[n].leftFirst = node.leftFirst, bvhNode[n].triCount = leftCount; + node.leftFirst = n++, node.triCount = 0, newNodePtr += 2; + *(float32x4x2_t*)&bvhNode[n] = _mm256_xor_ps( bestRBox, signFlip8 ); + bvhNode[n].leftFirst = j, bvhNode[n].triCount = rightCount; + task[taskCount++] = n, nodeIdx = n - 1; + } + // fetch subdivision task from stack + if (taskCount == 0) break; else nodeIdx = task[--taskCount]; + } + // all done. + aabbMin = bvhNode[0].aabbMin, aabbMax = bvhNode[0].aabbMax; + refittable = true; // not using spatial splits: can refit this BVH + may_have_holes = false; // the AVX builder produces a continuous list of nodes + usedNodes = newNodePtr; +#endif +} + +// Traverse the second alternative BVH layout (ALT_SOA). +int32_t BVH_SoA::Intersect( Ray& ray ) const +{ + VALIDATE_RAY( ray ); + BVHNode* node = &bvhNode[0], * stack[64]; + const bvhvec4slice& verts = bvh.verts; + const uint32_t* primIdx = bvh.primIdx; + uint32_t stackPtr = 0; + float cost = 0; + const float32x4_t Ox4 = vdupq_n_f32( ray.O.x ), rDx4 = vdupq_n_f32( ray.rD.x ); + const float32x4_t Oy4 = vdupq_n_f32( ray.O.y ), rDy4 = vdupq_n_f32( ray.rD.y ); + const float32x4_t Oz4 = vdupq_n_f32( ray.O.z ), rDz4 = vdupq_n_f32( ray.rD.z ); + // const float32x4_t inf4 = vdupq_n_f32( BVH_FAR ); + while (1) + { + cost += c_trav; + if (node->isLeaf()) + { + for (uint32_t i = 0; i < node->triCount; i++, cost += c_int) + { + const uint32_t tidx = primIdx[node->firstTri + i], vertIdx = tidx * 3; + const bvhvec3 v0 = verts[vertIdx]; + const bvhvec3 e1 = bvhvec3( verts[vertIdx + 1] ) - v0; + const bvhvec3 e2 = bvhvec3( verts[vertIdx + 2] ) - v0; + MOLLER_TRUMBORE_TEST( ray.hit.t, continue ); + ray.hit.t = t, ray.hit.u = u, ray.hit.v = v, ray.hit.prim = tidx; + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + continue; + } + float32x4_t x4 = vmulq_f32( vsubq_f32( node->xxxx, Ox4 ), rDx4 ); + float32x4_t y4 = vmulq_f32( vsubq_f32( node->yyyy, Oy4 ), rDy4 ); + float32x4_t z4 = vmulq_f32( vsubq_f32( node->zzzz, Oz4 ), rDz4 ); + // transpose + float32x4_t t0 = vzip1q_f32( x4, y4 ), t2 = vzip1q_f32( z4, z4 ); + float32x4_t t1 = vzip2q_f32( x4, y4 ), t3 = vzip2q_f32( z4, z4 ); + float32x4_t xyzw1a = vcombine_f32( vget_low_f32( t0 ), vget_low_f32( t2 ) ); + float32x4_t xyzw2a = vcombine_f32( vget_high_f32( t0 ), vget_high_f32( t2 ) ); + float32x4_t xyzw1b = vcombine_f32( vget_low_f32( t1 ), vget_low_f32( t3 ) ); + float32x4_t xyzw2b = vcombine_f32( vget_high_f32( t1 ), vget_high_f32( t3 ) ); + // process + float32x4_t tmina4 = vminq_f32( xyzw1a, xyzw2a ), tmaxa4 = vmaxq_f32( xyzw1a, xyzw2a ); + float32x4_t tminb4 = vminq_f32( xyzw1b, xyzw2b ), tmaxb4 = vmaxq_f32( xyzw1b, xyzw2b ); + // transpose back + t0 = vzip1q_f32( tmina4, tmaxa4 ), t2 = vzip1q_f32( tminb4, tmaxb4 ); + t1 = vzip2q_f32( tmina4, tmaxa4 ), t3 = vzip2q_f32( tminb4, tmaxb4 ); + x4 = vcombine_f32( vget_low_f32( t0 ), vget_low_f32( t2 ) ); + y4 = vcombine_f32( vget_high_f32( t0 ), vget_high_f32( t2 ) ); + z4 = vcombine_f32( vget_low_f32( t1 ), vget_low_f32( t3 ) ); + uint32_t lidx = node->left, ridx = node->right; + const float32x4_t min4 = vmaxq_f32( vmaxq_f32( vmaxq_f32( x4, y4 ), z4 ), vdupq_n_f32( 0 ) ); + const float32x4_t max4 = vminq_f32( vminq_f32( vminq_f32( x4, y4 ), z4 ), vdupq_n_f32( ray.hit.t ) ); + const float tmina_0 = vgetq_lane_f32( min4, 0 ), tmaxa_1 = vgetq_lane_f32( max4, 1 ); + const float tminb_2 = vgetq_lane_f32( min4, 2 ), tmaxb_3 = vgetq_lane_f32( max4, 3 ); + float dist1 = tmaxa_1 >= tmina_0 ? tmina_0 : BVH_FAR; + float dist2 = tmaxb_3 >= tminb_2 ? tminb_2 : BVH_FAR; + if (dist1 > dist2) + { + float t = dist1; dist1 = dist2; dist2 = t; + uint32_t i = lidx; lidx = ridx; ridx = i; + } + if (dist1 == BVH_FAR) + { + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + else + { + node = bvhNode + lidx; + if (dist2 != BVH_FAR) stack[stackPtr++] = bvhNode + ridx; + } + } + return (int32_t)cost; +} + +bool BVH_SoA::IsOccluded( const Ray& ray ) const +{ + BVHNode* node = &bvhNode[0], * stack[64]; + const bvhvec4slice& verts = bvh.verts; + const uint32_t* primIdx = bvh.primIdx; + uint32_t stackPtr = 0; + const float32x4_t Ox4 = vdupq_n_f32( ray.O.x ), rDx4 = vdupq_n_f32( ray.rD.x ); + const float32x4_t Oy4 = vdupq_n_f32( ray.O.y ), rDy4 = vdupq_n_f32( ray.rD.y ); + const float32x4_t Oz4 = vdupq_n_f32( ray.O.z ), rDz4 = vdupq_n_f32( ray.rD.z ); + while (1) + { + if (node->isLeaf()) + { + for (uint32_t i = 0; i < node->triCount; i++) + { + const uint32_t tidx = primIdx[node->firstTri + i], vertIdx = tidx * 3; + const bvhvec3 v0 = verts[vertIdx]; + const bvhvec3 e1 = bvhvec3( verts[vertIdx + 1] ) - v0; + const bvhvec3 e2 = bvhvec3( verts[vertIdx + 2] ) - v0; + MOLLER_TRUMBORE_TEST( ray.hit.t, continue ); + return true; + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + continue; + } + float32x4_t x4 = vmulq_f32( vsubq_f32( node->xxxx, Ox4 ), rDx4 ); + float32x4_t y4 = vmulq_f32( vsubq_f32( node->yyyy, Oy4 ), rDy4 ); + float32x4_t z4 = vmulq_f32( vsubq_f32( node->zzzz, Oz4 ), rDz4 ); + // transpose + float32x4_t t0 = vzip1q_f32( x4, y4 ), t2 = vzip1q_f32( z4, z4 ); + float32x4_t t1 = vzip2q_f32( x4, y4 ), t3 = vzip2q_f32( z4, z4 ); + float32x4_t xyzw1a = vcombine_f32( vget_low_f32( t0 ), vget_low_f32( t2 ) ); + float32x4_t xyzw2a = vcombine_f32( vget_high_f32( t0 ), vget_high_f32( t2 ) ); + float32x4_t xyzw1b = vcombine_f32( vget_low_f32( t1 ), vget_low_f32( t3 ) ); + float32x4_t xyzw2b = vcombine_f32( vget_high_f32( t1 ), vget_high_f32( t3 ) ); + // process + float32x4_t tmina4 = vminq_f32( xyzw1a, xyzw2a ), tmaxa4 = vmaxq_f32( xyzw1a, xyzw2a ); + float32x4_t tminb4 = vminq_f32( xyzw1b, xyzw2b ), tmaxb4 = vmaxq_f32( xyzw1b, xyzw2b ); + // transpose back + t0 = vzip1q_f32( tmina4, tmaxa4 ), t2 = vzip1q_f32( tminb4, tmaxb4 ); + t1 = vzip2q_f32( tmina4, tmaxa4 ), t3 = vzip2q_f32( tminb4, tmaxb4 ); + x4 = vcombine_f32( vget_low_f32( t0 ), vget_low_f32( t2 ) ); + y4 = vcombine_f32( vget_high_f32( t0 ), vget_high_f32( t2 ) ); + z4 = vcombine_f32( vget_low_f32( t1 ), vget_low_f32( t3 ) ); + uint32_t lidx = node->left, ridx = node->right; + const float32x4_t min4 = vmaxq_f32( vmaxq_f32( vmaxq_f32( x4, y4 ), z4 ), vdupq_n_f32( 0 ) ); + const float32x4_t max4 = vminq_f32( vminq_f32( vminq_f32( x4, y4 ), z4 ), vdupq_n_f32( ray.hit.t ) ); + const float tmina_0 = vgetq_lane_f32( min4, 0 ), tmaxa_1 = vgetq_lane_f32( max4, 1 ); + const float tminb_2 = vgetq_lane_f32( min4, 2 ), tmaxb_3 = vgetq_lane_f32( max4, 3 ); + float dist1 = tmaxa_1 >= tmina_0 ? tmina_0 : BVH_FAR; + float dist2 = tmaxb_3 >= tminb_2 ? tminb_2 : BVH_FAR; + if (dist1 > dist2) + { + float t = dist1; dist1 = dist2; dist2 = t; + uint32_t i = lidx; lidx = ridx; ridx = i; + } + if (dist1 == BVH_FAR) + { + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + else + { + node = bvhNode + lidx; + if (dist2 != BVH_FAR) stack[stackPtr++] = bvhNode + ridx; + } + } + return false; +} + +#endif // BVH_USENEON + +// ============================================================================ +// +// D O U B L E P R E C I S I O N S U P P O R T +// +// ============================================================================ + +#ifdef DOUBLE_PRECISION_SUPPORT + +// Destructor +BVH_Double::~BVH_Double() +{ + AlignedFree( fragment ); + AlignedFree( bvhNode ); + AlignedFree( primIdx ); +} + +void BVH_Double::Build( void (*customGetAABB)(const uint64_t, bvhdbl3&, bvhdbl3&), const uint64_t primCount ) +{ + BVH_FATAL_ERROR_IF( primCount == 0, "BVH_Double::Build( void (*customGetAABB)( .. ), instCount ), instCount == 0." ); + triCount = idxCount = primCount; + const uint64_t spaceNeeded = primCount * 2; // upper limit + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + AlignedFree( primIdx ); + AlignedFree( fragment ); + bvhNode = (BVHNode*)AlignedAlloc( spaceNeeded * sizeof( BVHNode ) ); + allocatedNodes = spaceNeeded; + primIdx = (uint64_t*)AlignedAlloc( primCount * sizeof( uint64_t ) ); + fragment = (Fragment*)AlignedAlloc( primCount * sizeof( Fragment ) ); + } + // copy relevant data from instance array + BVHNode& root = bvhNode[0]; + root.leftFirst = 0, root.triCount = primCount, root.aabbMin = bvhvec3( BVH_FAR ), root.aabbMax = bvhvec3( -BVH_FAR ); + for (uint32_t i = 0; i < primCount; i++) + { + customGetAABB( i, fragment[i].bmin, fragment[i].bmax ); + root.aabbMin = tinybvh_min( root.aabbMin, fragment[i].bmin ), fragment[i].primIdx = i; + root.aabbMax = tinybvh_max( root.aabbMax, fragment[i].bmax ), primIdx[i] = i; + } + // start build + newNodePtr = 1; + Build(); +} + +void BVH_Double::Build( BLASInstanceEx* bvhs, const uint64_t instCount, BVH_Double** blasses, const uint64_t bCount ) +{ + BVH_FATAL_ERROR_IF( instCount == 0, "BVH_Double::Build( BLASInstanceEx*, instCount ), instCount == 0." ); + triCount = idxCount = instCount; + const uint64_t spaceNeeded = instCount * 2; // upper limit + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + AlignedFree( primIdx ); + AlignedFree( fragment ); + bvhNode = (BVHNode*)AlignedAlloc( spaceNeeded * sizeof( BVHNode ) ); + allocatedNodes = spaceNeeded; + primIdx = (uint64_t*)AlignedAlloc( instCount * sizeof( uint64_t ) ); + fragment = (Fragment*)AlignedAlloc( instCount * sizeof( Fragment ) ); + } + instList = (BLASInstanceEx*)bvhs; + blasList = blasses; + blasCount = bCount; + // copy relevant data from instance array + BVHNode& root = bvhNode[0]; + root.leftFirst = 0, root.triCount = instCount, root.aabbMin = bvhdbl3( BVH_DBL_FAR ), root.aabbMax = bvhdbl3( -BVH_DBL_FAR ); + for (uint64_t i = 0; i < instCount; i++) + { + if (blasList) // if a null pointer is passed, we'll assume the BLASInstances have been updated elsewhere. + { + uint64_t blasIdx = instList[i].blasIdx; + BVH_Double* blas = blasList[blasIdx]; + instList[i].Update( blas ); + } + fragment[i].bmin = instList[i].aabbMin, fragment[i].primIdx = i, fragment[i].bmax = instList[i].aabbMax; + root.aabbMin = tinybvh_min( root.aabbMin, instList[i].aabbMin ); + root.aabbMax = tinybvh_max( root.aabbMax, instList[i].aabbMax ), primIdx[i] = i; + } + // start build + newNodePtr = 1; + Build(); +} + +void BVH_Double::Build( const bvhdbl3* vertices, const uint64_t primCount ) +{ + PrepareBuild( vertices, primCount ); + Build(); +} + +void BVH_Double::PrepareBuild( const bvhdbl3* vertices, const uint64_t primCount ) +{ + BVH_FATAL_ERROR_IF( primCount == 0, "BVH_Double::PrepareBuild( .. ), primCount == 0." ); + const uint64_t spaceNeeded = primCount * 2; // upper limit + // allocate memory on first build + if (allocatedNodes < spaceNeeded) + { + AlignedFree( bvhNode ); + AlignedFree( primIdx ); + AlignedFree( fragment ); + bvhNode = (BVHNode*)AlignedAlloc( spaceNeeded * sizeof( BVHNode ) ); + allocatedNodes = spaceNeeded; + primIdx = (uint64_t*)AlignedAlloc( primCount * sizeof( uint64_t ) ); + fragment = (Fragment*)AlignedAlloc( primCount * sizeof( Fragment ) ); + } + verts = (bvhdbl3*)vertices; // note: we're not copying this data; don't delete. + idxCount = triCount = primCount; + // prepare fragments + BVHNode& root = bvhNode[0]; + root.leftFirst = 0, root.triCount = triCount, root.aabbMin = bvhdbl3( BVH_DBL_FAR ), root.aabbMax = bvhdbl3( -BVH_DBL_FAR ); + for (uint32_t i = 0; i < triCount; i++) + { + const bvhdbl3 v0 = verts[i * 3], v1 = verts[i * 3 + 1], v2 = verts[i * 3 + 2]; + fragment[i].bmin = tinybvh_min( tinybvh_min( v0, v1 ), v2 ); + fragment[i].bmax = tinybvh_max( tinybvh_max( v0, v1 ), v2 ); + root.aabbMin = tinybvh_min( root.aabbMin, fragment[i].bmin ); + root.aabbMax = tinybvh_max( root.aabbMax, fragment[i].bmax ), primIdx[i] = i; + } + // reset node pool + newNodePtr = 1; + // all set; actual build happens in BVH_Double::Build. +} + +void BuildDouble_( uint64_t nodeIdx, uint32_t depth, BVH_Double* bvh ) +{ + bvh->Build( nodeIdx, depth ); +} +void BVH_Double::Build( uint64_t nodeIdx, uint32_t depth ) +{ + // initialize atomic counter on first call + if (depth == 0) atomicNewNodePtr = new std::atomic( newNodePtr ); + // avoid threaded building for small meshes: not efficient; build multiple in parallel instead. +#ifdef NO_THREADED_BUILDS + depth = 999; +#else + if (triCount < MT_BUILD_THRESHOLD) depth = 999; +#endif + // subdivide root node recursively + BVHNode& root = bvhNode[0]; + uint64_t task[256], taskCount = 0; + bvhdbl3 minDim = (root.aabbMax - root.aabbMin) * 1e-20; + bvhdbl3 bestLMin( 0 ), bestLMax( 0 ), bestRMin( 0 ), bestRMax( 0 ); + while (1) + { + while (1) + { + BVHNode& node = bvhNode[nodeIdx]; + // find optimal object split + bvhdbl3 binMin[3][BVHBINS], binMax[3][BVHBINS]; + for (uint32_t a = 0; a < 3; a++) for (uint32_t i = 0; i < BVHBINS; i++) + binMin[a][i] = bvhdbl3( BVH_DBL_FAR ), binMax[a][i] = bvhdbl3( -BVH_DBL_FAR ); + uint32_t count[3][BVHBINS]; + memset( count, 0, BVHBINS * 3 * sizeof( uint32_t ) ); + const bvhdbl3 rpd3 = bvhdbl3( bvhdbl3( BVHBINS ) / (node.aabbMax - node.aabbMin) ), nmin3 = node.aabbMin; + for (uint32_t i = 0; i < node.triCount; i++) // process all tris for x,y and z at once + { + const uint64_t fi = primIdx[node.leftFirst + i]; + const bvhdbl3 fbi = ((fragment[fi].bmin + fragment[fi].bmax) * 0.5 - nmin3) * rpd3; + bvhint3 bi( (int32_t)fbi.x, (int32_t)fbi.y, (int32_t)fbi.z ); + bi.x = tinybvh_clamp( bi.x, 0, BVHBINS - 1 ); + bi.y = tinybvh_clamp( bi.y, 0, BVHBINS - 1 ); + bi.z = tinybvh_clamp( bi.z, 0, BVHBINS - 1 ); + binMin[0][bi.x] = tinybvh_min( binMin[0][bi.x], fragment[fi].bmin ); + binMax[0][bi.x] = tinybvh_max( binMax[0][bi.x], fragment[fi].bmax ), count[0][bi.x]++; + binMin[1][bi.y] = tinybvh_min( binMin[1][bi.y], fragment[fi].bmin ); + binMax[1][bi.y] = tinybvh_max( binMax[1][bi.y], fragment[fi].bmax ), count[1][bi.y]++; + binMin[2][bi.z] = tinybvh_min( binMin[2][bi.z], fragment[fi].bmin ); + binMax[2][bi.z] = tinybvh_max( binMax[2][bi.z], fragment[fi].bmax ), count[2][bi.z]++; + } + // calculate per-split totals + double splitCost = BVH_DBL_FAR, rSAV = 1.0 / node.SurfaceArea(); + uint32_t bestAxis = 0, bestPos = 0; + for (int32_t a = 0; a < 3; a++) if ((node.aabbMax[a] - node.aabbMin[a]) > minDim[a]) + { + bvhdbl3 lBMin[BVHBINS - 1], rBMin[BVHBINS - 1], l1( BVH_DBL_FAR ), l2( -BVH_DBL_FAR ); + bvhdbl3 lBMax[BVHBINS - 1], rBMax[BVHBINS - 1], r1( BVH_DBL_FAR ), r2( -BVH_DBL_FAR ); + double ANL[BVHBINS - 1], ANR[BVHBINS - 1]; + for (uint32_t lN = 0, rN = 0, i = 0; i < BVHBINS - 1; i++) + { + lBMin[i] = l1 = tinybvh_min( l1, binMin[a][i] ); + rBMin[BVHBINS - 2 - i] = r1 = tinybvh_min( r1, binMin[a][BVHBINS - 1 - i] ); + lBMax[i] = l2 = tinybvh_max( l2, binMax[a][i] ); + rBMax[BVHBINS - 2 - i] = r2 = tinybvh_max( r2, binMax[a][BVHBINS - 1 - i] ); + lN += count[a][i], rN += count[a][BVHBINS - 1 - i]; + ANL[i] = lN == 0 ? BVH_DBL_FAR : (tinybvh_half_area( l2 - l1 ) * (double)lN); + ANR[BVHBINS - 2 - i] = rN == 0 ? BVH_DBL_FAR : (tinybvh_half_area( r2 - r1 ) * (double)rN); + } + // evaluate bin totals to find best position for object split + for (uint32_t i = 0; i < BVHBINS - 1; i++) + { + const double C = c_trav + rSAV * c_int * (ANL[i] + ANR[i]); + if (C < splitCost) + { + splitCost = C, bestAxis = a, bestPos = i; + bestLMin = lBMin[i], bestRMin = rBMin[i], bestLMax = lBMax[i], bestRMax = rBMax[i]; + } + } + } + double noSplitCost = (double)node.triCount * c_int; + if (splitCost >= noSplitCost) break; // not splitting is better. + // in-place partition + uint64_t j = node.leftFirst + node.triCount, src = node.leftFirst; + const double rpd = rpd3[bestAxis], nmin = nmin3[bestAxis]; + for (uint64_t i = 0; i < node.triCount; i++) + { + const uint64_t fi = primIdx[src]; + int32_t bi = (uint32_t)(((fragment[fi].bmin[bestAxis] + fragment[fi].bmax[bestAxis]) * 0.5 - nmin) * rpd); + bi = tinybvh_clamp( bi, 0, BVHBINS - 1 ); + if ((uint32_t)bi <= bestPos) src++; else tinybvh_swap( primIdx[src], primIdx[--j] ); + } + // create child nodes + uint64_t leftCount = src - node.leftFirst, rightCount = node.triCount - leftCount; + if (leftCount == 0 || rightCount == 0 || taskCount == BVH_NUM_ELEMS( task )) break; // should not happen. + const uint64_t n = atomicNewNodePtr->fetch_add( 2 ); + bvhNode[n].aabbMin = bestLMin, bvhNode[n].aabbMax = bestLMax; + bvhNode[n].leftFirst = node.leftFirst, bvhNode[n].triCount = leftCount; + bvhNode[n + 1].aabbMin = bestRMin, bvhNode[n + 1].aabbMax = bestRMax; + bvhNode[n + 1].leftFirst = j, bvhNode[n + 1].triCount = rightCount; + node.leftFirst = n, node.triCount = 0; + if (depth < 5) + { + std::thread t1( &BuildDouble_, n, depth + 1, this ); + std::thread t2( &BuildDouble_, n + 1, depth + 1, this ); + t1.join(); + t2.join(); + break; + } + // recurse + task[taskCount++] = n + 1, nodeIdx = n; + } + // fetch subdivision task from stack + if (taskCount == 0) break; else nodeIdx = task[--taskCount]; + } + if (depth == 0 || triCount < MT_BUILD_THRESHOLD) + { + // all done. + aabbMin = bvhNode[0].aabbMin, aabbMax = bvhNode[0].aabbMax; + refittable = true; // not using spatial splits: can refit this BVH + may_have_holes = false; // the reference builder produces a continuous list of nodes + bvh_over_aabbs = (verts == 0); // bvh over aabbs is suitable as TLAS + usedNodes = newNodePtr = atomicNewNodePtr->load(); + delete atomicNewNodePtr; + } +} + +double BVH_Double::BVHNode::SurfaceArea() const +{ + const bvhdbl3 e = aabbMax - aabbMin; + return e.x * e.y + e.y * e.z + e.z * e.x; +} + +double BVH_Double::SAHCost( const uint64_t nodeIdx ) const +{ + // Determine the SAH cost of a double-precision tree. + const BVHNode& n = bvhNode[nodeIdx]; + if (n.isLeaf()) return c_int * n.SurfaceArea() * n.triCount; + double cost = c_trav * n.SurfaceArea() + SAHCost( n.leftFirst ) + SAHCost( n.leftFirst + 1 ); + return nodeIdx == 0 ? (cost / n.SurfaceArea()) : cost; +} + +// Traverse the default BVH layout, double-precision. +int32_t BVH_Double::Intersect( RayEx& ray ) const +{ + if (instList) return IntersectTLAS( ray ); + BVHNode* node = &bvhNode[0], * stack[64]; + uint32_t stackPtr = 0; + float cost = 0; + while (1) + { + cost += c_trav; + if (node->isLeaf()) + { + if (customEnabled && customIntersect != 0) + { + for (uint32_t i = 0; i < node->triCount; i++, cost += c_int) + if ((*customIntersect)(ray, primIdx[node->leftFirst + i])) + ray.hit.inst = ray.instIdx; + } + else for (uint32_t i = 0; i < node->triCount; i++, cost += c_int) + { + const uint64_t idx = primIdx[node->leftFirst + i]; + const uint64_t vertIdx = idx * 3; + const bvhdbl3 e1 = verts[vertIdx + 1] - verts[vertIdx]; + const bvhdbl3 e2 = verts[vertIdx + 2] - verts[vertIdx]; + const bvhdbl3 h = tinybvh_cross( ray.D, e2 ); + const double a = tinybvh_dot( e1, h ); + if (fabs( a ) < 0.0000001) continue; // ray parallel to triangle + const double f = 1 / a; + const bvhdbl3 s = ray.O - bvhdbl3( verts[vertIdx] ); + const double u = f * tinybvh_dot( s, h ); + const bvhdbl3 q = tinybvh_cross( s, e1 ); + const double v = f * tinybvh_dot( ray.D, q ); + if (u < 0 || v < 0 || u + v > 1) continue; + const double t = f * tinybvh_dot( e2, q ); + if (t > 0 && t < ray.hit.t) + { + // register a hit: ray is shortened to t + ray.hit.t = t, ray.hit.u = u, ray.hit.v = v; + ray.hit.prim = idx; + ray.hit.inst = ray.instIdx; + } + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + continue; + } + BVHNode* child1 = &bvhNode[node->leftFirst]; + BVHNode* child2 = &bvhNode[node->leftFirst + 1]; + double dist1 = child1->Intersect( ray ), dist2 = child2->Intersect( ray ); + if (dist1 > dist2) { tinybvh_swap( dist1, dist2 ); tinybvh_swap( child1, child2 ); } + if (dist1 == BVH_DBL_FAR /* missed both child nodes */) + { + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + else /* hit at least one node */ + { + node = child1; /* continue with the nearest */ + if (dist2 != BVH_DBL_FAR) stack[stackPtr++] = child2; /* push far child */ + } + } + return (int32_t)cost; +} + +// Traverse a double-precision TLAS. +int32_t BVH_Double::IntersectTLAS( RayEx& ray ) const +{ + BVHNode* node = &bvhNode[0], * stack[64]; + uint32_t stackPtr = 0; + float cost = 0; + while (1) + { + cost += c_trav; + if (node->isLeaf()) + { + RayEx tmp; + for (uint32_t i = 0; i < node->triCount; i++) + { + // BLAS traversal + const uint64_t instIdx = primIdx[node->leftFirst + i]; + BLASInstanceEx& inst = instList[instIdx]; + if (!(inst.mask & ray.mask)) continue; + BVH_Double* blas = blasList[inst.blasIdx]; + // 1. Transform ray with the inverse of the instance transform + tmp.O = tinybvh_transform_point( ray.O, inst.invTransform ); + tmp.D = tinybvh_transform_vector( ray.D, inst.invTransform ); + tmp.rD = bvhdbl3( 1.0 / tmp.D.x, 1.0 / tmp.D.y, 1.0 / tmp.D.z ); + tmp.hit = ray.hit; + // 2. Traverse BLAS with the transformed ray + tmp.instIdx = instIdx; + cost += blas->Intersect( tmp ); + // 3. Restore ray + ray.hit = tmp.hit; + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + continue; + } + BVHNode* child1 = &bvhNode[node->leftFirst]; + BVHNode* child2 = &bvhNode[node->leftFirst + 1]; + double dist1 = child1->Intersect( ray ), dist2 = child2->Intersect( ray ); + if (dist1 > dist2) { tinybvh_swap( dist1, dist2 ); tinybvh_swap( child1, child2 ); } + if (dist1 == BVH_DBL_FAR /* missed both child nodes */) + { + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + else /* hit at least one node */ + { + node = child1; /* continue with the nearest */ + if (dist2 != BVH_DBL_FAR) stack[stackPtr++] = child2; /* push far child */ + } + } + return (int32_t)cost; +} + +bool BVH_Double::IsOccluded( const RayEx& ray ) const +{ + if (instList) return IsOccludedTLAS( ray ); + BVHNode* node = &bvhNode[0], * stack[64]; + uint32_t stackPtr = 0; + while (1) + { + if (node->isLeaf()) + { + if (customEnabled && customIntersect != 0) for (uint32_t i = 0; i < node->triCount; i++) + (*customIsOccluded)(ray, primIdx[node->leftFirst + i]); + else for (uint32_t i = 0; i < node->triCount; i++) + { + const uint64_t idx = primIdx[node->leftFirst + i]; + const uint64_t vertIdx = idx * 3; + const bvhdbl3 e1 = verts[vertIdx + 1] - verts[vertIdx]; + const bvhdbl3 e2 = verts[vertIdx + 2] - verts[vertIdx]; + const bvhdbl3 h = tinybvh_cross( ray.D, e2 ); + const double a = tinybvh_dot( e1, h ); + if (fabs( a ) < 0.0000001) continue; // ray parallel to triangle + const double f = 1 / a; + const bvhdbl3 s = ray.O - bvhdbl3( verts[vertIdx] ); + const double u = f * tinybvh_dot( s, h ); + const bvhdbl3 q = tinybvh_cross( s, e1 ); + const double v = f * tinybvh_dot( ray.D, q ); + if (u < 0 || v < 0 || u + v > 1) continue; + const double t = f * tinybvh_dot( e2, q ); + if (t > 0 && t < ray.hit.t) return true; + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + continue; + } + BVHNode* child1 = &bvhNode[node->leftFirst]; + BVHNode* child2 = &bvhNode[node->leftFirst + 1]; + double dist1 = child1->Intersect( ray ), dist2 = child2->Intersect( ray ); + if (dist1 > dist2) { tinybvh_swap( dist1, dist2 ); tinybvh_swap( child1, child2 ); } + if (dist1 == BVH_DBL_FAR /* missed both child nodes */) + { + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + else /* hit at least one node */ + { + node = child1; /* continue with the nearest */ + if (dist2 != BVH_DBL_FAR) stack[stackPtr++] = child2; /* push far child */ + } + } + return false; +} + +bool BVH_Double::IsOccludedTLAS( const RayEx& ray ) const +{ + BVHNode* node = &bvhNode[0], * stack[64]; + uint32_t stackPtr = 0; + RayEx tmp; + while (1) + { + if (node->isLeaf()) + { + for (uint32_t i = 0; i < node->triCount; i++) + { + // BLAS traversal + BLASInstanceEx& inst = instList[primIdx[node->leftFirst + i]]; + if (!(inst.mask & ray.mask)) continue; + BVH_Double* blas = blasList[inst.blasIdx]; + // 1. Transform ray with the inverse of the instance transform + tmp.O = tinybvh_transform_point( ray.O, inst.invTransform ); + tmp.D = tinybvh_transform_vector( ray.D, inst.invTransform ); + tmp.rD.x = tmp.D.x > 1e-24 ? (1.0 / tmp.D.x) : (tmp.D.x < -1e-24 ? (1.0 / tmp.D.x) : BVH_DBL_FAR); + tmp.rD.y = tmp.D.y > 1e-24 ? (1.0 / tmp.D.y) : (tmp.D.y < -1e-24 ? (1.0 / tmp.D.y) : BVH_DBL_FAR); + tmp.rD.z = tmp.D.z > 1e-24 ? (1.0 / tmp.D.z) : (tmp.D.z < -1e-24 ? (1.0 / tmp.D.z) : BVH_DBL_FAR); + // 2. Traverse BLAS with the transformed ray + if (blas->IsOccluded( tmp )) return true; + } + if (stackPtr == 0) break; else node = stack[--stackPtr]; + continue; + } + BVHNode* child1 = &bvhNode[node->leftFirst]; + BVHNode* child2 = &bvhNode[node->leftFirst + 1]; + double dist1 = child1->Intersect( ray ), dist2 = child2->Intersect( ray ); + if (dist1 > dist2) { tinybvh_swap( dist1, dist2 ); tinybvh_swap( child1, child2 ); } + if (dist1 == BVH_DBL_FAR /* missed both child nodes */) + { + if (stackPtr == 0) break; else node = stack[--stackPtr]; + } + else /* hit at least one node */ + { + node = child1; /* continue with the nearest */ + if (dist2 != BVH_DBL_FAR) stack[stackPtr++] = child2; /* push far child */ + } + } + return false; +} + +// BVHNode::Intersect, double precision +double BVH_Double::BVHNode::Intersect( const RayEx& ray ) const +{ + // double-precision "slab test" ray/AABB intersection + double tx1 = (aabbMin.x - ray.O.x) * ray.rD.x, tx2 = (aabbMax.x - ray.O.x) * ray.rD.x; + double tmin = tinybvh_min( tx1, tx2 ), tmax = tinybvh_max( tx1, tx2 ); + double ty1 = (aabbMin.y - ray.O.y) * ray.rD.y, ty2 = (aabbMax.y - ray.O.y) * ray.rD.y; + tmin = tinybvh_max( tmin, tinybvh_min( ty1, ty2 ) ); + tmax = tinybvh_min( tmax, tinybvh_max( ty1, ty2 ) ); + double tz1 = (aabbMin.z - ray.O.z) * ray.rD.z, tz2 = (aabbMax.z - ray.O.z) * ray.rD.z; + tmin = tinybvh_max( tmin, tinybvh_min( tz1, tz2 ) ); + tmax = tinybvh_min( tmax, tinybvh_max( tz1, tz2 ) ); + if (tmax >= tmin && tmin < ray.hit.t && tmax >= 0) return tmin; else return BVH_DBL_FAR; +} + +#endif + +// ============================================================================ +// +// H E L P E R S +// +// ============================================================================ + +// Update +void BLASInstance::Update( BVHBase* blas ) +{ + InvertTransform(); // TODO: done unconditionally; for a big TLAS this may be wasteful. Detect changes automatically? + // transform the eight corners of the root node aabb using the + // instance transform and calculate the worldspace aabb over those. + aabbMin = bvhvec3( BVH_FAR ), aabbMax = bvhvec3( -BVH_FAR ); + bvhvec3 bmin = blas->aabbMin, bmax = blas->aabbMax; + for (int32_t j = 0; j < 8; j++) + { + const bvhvec3 p( j & 1 ? bmax.x : bmin.x, j & 2 ? bmax.y : bmin.y, j & 4 ? bmax.z : bmin.z ); + const bvhvec3 t = tinybvh_transform_point( p, transform ); + aabbMin = tinybvh_min( aabbMin, t ), aabbMax = tinybvh_max( aabbMax, t ); + } +} + +// InvertTransform - calculate the inverse of the matrix stored in 'transform' +void BLASInstance::InvertTransform() +{ + // math from MESA, via http://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix + const float* T = (const float*)&this->transform; + float* iT = (float*)&this->invTransform; + iT[0] = T[5] * T[10] * T[15] - T[5] * T[11] * T[14] - T[9] * T[6] * T[15] + T[9] * T[7] * T[14] + T[13] * T[6] * T[11] - T[13] * T[7] * T[10]; + iT[1] = -T[1] * T[10] * T[15] + T[1] * T[11] * T[14] + T[9] * T[2] * T[15] - T[9] * T[3] * T[14] - T[13] * T[2] * T[11] + T[13] * T[3] * T[10]; + iT[2] = T[1] * T[6] * T[15] - T[1] * T[7] * T[14] - T[5] * T[2] * T[15] + T[5] * T[3] * T[14] + T[13] * T[2] * T[7] - T[13] * T[3] * T[6]; + iT[3] = -T[1] * T[6] * T[11] + T[1] * T[7] * T[10] + T[5] * T[2] * T[11] - T[5] * T[3] * T[10] - T[9] * T[2] * T[7] + T[9] * T[3] * T[6]; + iT[4] = -T[4] * T[10] * T[15] + T[4] * T[11] * T[14] + T[8] * T[6] * T[15] - T[8] * T[7] * T[14] - T[12] * T[6] * T[11] + T[12] * T[7] * T[10]; + iT[5] = T[0] * T[10] * T[15] - T[0] * T[11] * T[14] - T[8] * T[2] * T[15] + T[8] * T[3] * T[14] + T[12] * T[2] * T[11] - T[12] * T[3] * T[10]; + iT[6] = -T[0] * T[6] * T[15] + T[0] * T[7] * T[14] + T[4] * T[2] * T[15] - T[4] * T[3] * T[14] - T[12] * T[2] * T[7] + T[12] * T[3] * T[6]; + iT[7] = T[0] * T[6] * T[11] - T[0] * T[7] * T[10] - T[4] * T[2] * T[11] + T[4] * T[3] * T[10] + T[8] * T[2] * T[7] - T[8] * T[3] * T[6]; + iT[8] = T[4] * T[9] * T[15] - T[4] * T[11] * T[13] - T[8] * T[5] * T[15] + T[8] * T[7] * T[13] + T[12] * T[5] * T[11] - T[12] * T[7] * T[9]; + iT[9] = -T[0] * T[9] * T[15] + T[0] * T[11] * T[13] + T[8] * T[1] * T[15] - T[8] * T[3] * T[13] - T[12] * T[1] * T[11] + T[12] * T[3] * T[9]; + iT[10] = T[0] * T[5] * T[15] - T[0] * T[7] * T[13] - T[4] * T[1] * T[15] + T[4] * T[3] * T[13] + T[12] * T[1] * T[7] - T[12] * T[3] * T[5]; + iT[11] = -T[0] * T[5] * T[11] + T[0] * T[7] * T[9] + T[4] * T[1] * T[11] - T[4] * T[3] * T[9] - T[8] * T[1] * T[7] + T[8] * T[3] * T[5]; + iT[12] = -T[4] * T[9] * T[14] + T[4] * T[10] * T[13] + T[8] * T[5] * T[14] - T[8] * T[6] * T[13] - T[12] * T[5] * T[10] + T[12] * T[6] * T[9]; + iT[13] = T[0] * T[9] * T[14] - T[0] * T[10] * T[13] - T[8] * T[1] * T[14] + T[8] * T[2] * T[13] + T[12] * T[1] * T[10] - T[12] * T[2] * T[9]; + iT[14] = -T[0] * T[5] * T[14] + T[0] * T[6] * T[13] + T[4] * T[1] * T[14] - T[4] * T[2] * T[13] - T[12] * T[1] * T[6] + T[12] * T[2] * T[5]; + iT[15] = T[0] * T[5] * T[10] - T[0] * T[6] * T[9] - T[4] * T[1] * T[10] + T[4] * T[2] * T[9] + T[8] * T[1] * T[6] - T[8] * T[2] * T[5]; + const float det = T[0] * iT[0] + T[1] * iT[4] + T[2] * iT[8] + T[3] * iT[12]; + if (det == 0) return; // actually, invert failed. That's bad. + const float invdet = 1.0f / det; + for (int i = 0; i < 16; i++) iT[i] *= invdet; +} + +#ifdef DOUBLE_PRECISION_SUPPORT + +// Update +void BLASInstanceEx::Update( BVH_Double* blas ) +{ + InvertTransform(); // TODO: done unconditionally; for a big TLAS this may be wasteful. Detect changes automatically? + // transform the eight corners of the root node aabb using the + // instance transform and calculate the worldspace aabb over those. + aabbMin = bvhdbl3( BVH_FAR ), aabbMax = bvhdbl3( -BVH_FAR ); + bvhdbl3 bmin = blas->aabbMin, bmax = blas->aabbMax; + for (int32_t j = 0; j < 8; j++) + { + const bvhdbl3 p( j & 1 ? bmax.x : bmin.x, j & 2 ? bmax.y : bmin.y, j & 4 ? bmax.z : bmin.z ); + const bvhdbl3 t = tinybvh_transform_point( p, transform ); + aabbMin = tinybvh_min( aabbMin, t ), aabbMax = tinybvh_max( aabbMax, t ); + } +} + +// InvertTransform - calculate the inverse of the matrix stored in 'transform' +void BLASInstanceEx::InvertTransform() +{ + // math from MESA, via http://stackoverflow.com/questions/1148309/inverting-a-4x4-matrix + const double* T = this->transform; + invTransform[0] = T[5] * T[10] * T[15] - T[5] * T[11] * T[14] - T[9] * T[6] * T[15] + T[9] * T[7] * T[14] + T[13] * T[6] * T[11] - T[13] * T[7] * T[10]; + invTransform[1] = -T[1] * T[10] * T[15] + T[1] * T[11] * T[14] + T[9] * T[2] * T[15] - T[9] * T[3] * T[14] - T[13] * T[2] * T[11] + T[13] * T[3] * T[10]; + invTransform[2] = T[1] * T[6] * T[15] - T[1] * T[7] * T[14] - T[5] * T[2] * T[15] + T[5] * T[3] * T[14] + T[13] * T[2] * T[7] - T[13] * T[3] * T[6]; + invTransform[3] = -T[1] * T[6] * T[11] + T[1] * T[7] * T[10] + T[5] * T[2] * T[11] - T[5] * T[3] * T[10] - T[9] * T[2] * T[7] + T[9] * T[3] * T[6]; + invTransform[4] = -T[4] * T[10] * T[15] + T[4] * T[11] * T[14] + T[8] * T[6] * T[15] - T[8] * T[7] * T[14] - T[12] * T[6] * T[11] + T[12] * T[7] * T[10]; + invTransform[5] = T[0] * T[10] * T[15] - T[0] * T[11] * T[14] - T[8] * T[2] * T[15] + T[8] * T[3] * T[14] + T[12] * T[2] * T[11] - T[12] * T[3] * T[10]; + invTransform[6] = -T[0] * T[6] * T[15] + T[0] * T[7] * T[14] + T[4] * T[2] * T[15] - T[4] * T[3] * T[14] - T[12] * T[2] * T[7] + T[12] * T[3] * T[6]; + invTransform[7] = T[0] * T[6] * T[11] - T[0] * T[7] * T[10] - T[4] * T[2] * T[11] + T[4] * T[3] * T[10] + T[8] * T[2] * T[7] - T[8] * T[3] * T[6]; + invTransform[8] = T[4] * T[9] * T[15] - T[4] * T[11] * T[13] - T[8] * T[5] * T[15] + T[8] * T[7] * T[13] + T[12] * T[5] * T[11] - T[12] * T[7] * T[9]; + invTransform[9] = -T[0] * T[9] * T[15] + T[0] * T[11] * T[13] + T[8] * T[1] * T[15] - T[8] * T[3] * T[13] - T[12] * T[1] * T[11] + T[12] * T[3] * T[9]; + invTransform[10] = T[0] * T[5] * T[15] - T[0] * T[7] * T[13] - T[4] * T[1] * T[15] + T[4] * T[3] * T[13] + T[12] * T[1] * T[7] - T[12] * T[3] * T[5]; + invTransform[11] = -T[0] * T[5] * T[11] + T[0] * T[7] * T[9] + T[4] * T[1] * T[11] - T[4] * T[3] * T[9] - T[8] * T[1] * T[7] + T[8] * T[3] * T[5]; + invTransform[12] = -T[4] * T[9] * T[14] + T[4] * T[10] * T[13] + T[8] * T[5] * T[14] - T[8] * T[6] * T[13] - T[12] * T[5] * T[10] + T[12] * T[6] * T[9]; + invTransform[13] = T[0] * T[9] * T[14] - T[0] * T[10] * T[13] - T[8] * T[1] * T[14] + T[8] * T[2] * T[13] + T[12] * T[1] * T[10] - T[12] * T[2] * T[9]; + invTransform[14] = -T[0] * T[5] * T[14] + T[0] * T[6] * T[13] + T[4] * T[1] * T[14] - T[4] * T[2] * T[13] - T[12] * T[1] * T[6] + T[12] * T[2] * T[5]; + invTransform[15] = T[0] * T[5] * T[10] - T[0] * T[6] * T[9] - T[4] * T[1] * T[10] + T[4] * T[2] * T[9] + T[8] * T[1] * T[6] - T[8] * T[2] * T[5]; + const double det = T[0] * invTransform[0] + T[1] * invTransform[4] + T[2] * invTransform[8] + T[3] * invTransform[12]; + if (det == 0) return; // actually, invert failed. That's bad. + const double invdet = 1. / det; + for (int i = 0; i < 16; i++) invTransform[i] *= invdet; +} + +#endif + +// SA +float BVHBase::SA( const bvhvec3& aabbMin, const bvhvec3& aabbMax ) +{ + bvhvec3 e = aabbMax - aabbMin; // extent of the node + return e.x * e.y + e.y * e.z + e.z * e.x; +} + +// IntersectTri +void BVHBase::IntersectTri( Ray& ray, const uint32_t triIdx, const bvhvec4slice& verts, const uint32_t i0, const uint32_t i1, const uint32_t i2 ) const +{ +#ifdef WATERTIGHT_TRITEST + // Woop et al.'s Watertight intersection algorithm. + // PART 1 - Precalculations + uint32_t kz = tinybvh_maxdim( ray.D ), kx = (1 << kz) & 3, ky = (1 << kx) & 3; + if (ray.D[kz] < 0) std::swap( kx, ky ); + const float Sz = ray.rD[kz], Sx = ray.D[kx] * Sz, Sy = ray.D[ky] * Sz; + // PART 2 - Intersection + const bvhvec3 C = bvhvec3( verts[i0] ) - ray.O; + const bvhvec3 A = bvhvec3( verts[i1] ) - ray.O; + const bvhvec3 B = bvhvec3( verts[i2] ) - ray.O; + const float Ax = A[kx] - Sx * A[kz], Ay = A[ky] - Sy * A[kz]; + const float Bx = B[kx] - Sx * B[kz], By = B[ky] - Sy * B[kz]; + const float Cx = C[kx] - Sx * C[kz], Cy = C[ky] - Sy * C[kz]; + const float U = Cx * By - Cy * Bx, V = Ax * Cy - Ay * Cx, W = Bx * Ay - By * Ax; + if ((U < 0 || V < 0 || W < 0) && (U > 0 || V > 0 || W > 0)) return; + const float det = U + V + W; + if (det == 0) return; + const float Az = Sz * A[kz], Bz = Sz * B[kz], Cz = Sz * C[kz]; + const float T = U * Az + V * Bz + W * Cz; + const float invDet = 1.0f / det, t = T * invDet; + if (t >= ray.hit.t || t < 0) return; + const float u = U * invDet, v = V * invDet; +#else + // Moeller-Trumbore ray/triangle intersection algorithm. + const bvhvec4 v0_ = verts[i0]; + const bvhvec3 v0 = v0_, e1 = verts[i1] - v0_, e2 = verts[i2] - v0_; + MOLLER_TRUMBORE_TEST( ray.hit.t, return ); +#endif + // evaluate opacity map, if present. + if (opmap) + { + const float fN = (float)opmapN; + const int row = int( (u + v) * fN ), diag = int( (1 - u) * fN ); + const int idx = (row * row) + int( v * fN ) + (diag - (opmapN - 1 - row)); + uint32_t* om = opmap + triIdx * ((opmapN * opmapN + 31) >> 5); + if (!(om[idx >> 5] & (1 << (idx & 31)))) return; + } + // register a hit: ray is shortened to t. + ray.hit.t = t, ray.hit.u = u, ray.hit.v = v; +#if INST_IDX_BITS == 32 + ray.hit.prim = triIdx, ray.hit.inst = ray.instIdx; +#else + ray.hit.prim = triIdx + ray.instIdx; +#endif +} + +// TriOccludes +bool BVHBase::TriOccludes( const Ray& ray, const bvhvec4slice& verts, const uint32_t triIdx, const uint32_t i0, const uint32_t i1, const uint32_t i2 ) const +{ +#ifdef WATERTIGHT_TRITEST + // Woop et al.'s Watertight intersection algorithm. + // PART 1 - Precalculations + uint32_t kz = tinybvh_maxdim( ray.D ), kx = (1 << kz) & 3, ky = (1 << kx) & 3; + if (ray.D[kz] < 0) std::swap( kx, ky ); + const float Sz = ray.rD[kz], Sx = ray.D[kx] * Sz, Sy = ray.D[ky] * Sz; + // PART 2 - Intersection + const bvhvec3 A = bvhvec3( verts[i0] ) - ray.O; + const bvhvec3 B = bvhvec3( verts[i1] ) - ray.O; + const bvhvec3 C = bvhvec3( verts[i2] ) - ray.O; + const float Ax = A[kx] - Sx * A[kz], Ay = A[ky] - Sy * A[kz]; + const float Bx = B[kx] - Sx * B[kz], By = B[ky] - Sy * B[kz]; + const float Cx = C[kx] - Sx * C[kz], Cy = C[ky] - Sy * C[kz]; + const float U = Cx * By - Cy * Bx, V = Ax * Cy - Ay * Cx, W = Bx * Ay - By * Ax; + if ((U < 0 || V < 0 || W < 0) && (U > 0 || V > 0 || W > 0)) return false; + const float det = U + V + W; + if (det == 0) return false; + const float Az = Sz * A[kz], Bz = Sz * B[kz], Cz = Sz * C[kz]; + const float T = U * Az + V * Bz + W * Cz; + const float invDet = 1.0f / det, t = T * invDet; + if (t < 0 || t > ray.hit.t) return false; +#else + // Moeller-Trumbore ray/triangle intersection algorithm + const bvhvec4 v0_ = verts[i0]; + const bvhvec3 v0 = v0_, e1 = verts[i1] - v0_, e2 = verts[i2] - v0_; + MOLLER_TRUMBORE_TEST( ray.hit.t, return false ); +#endif + // evaluate opacity map, if present. + if (opmap) + { + const float fN = (float)opmapN; + const int row = int( (u + v) * fN ), diag = int( (1 - u) * fN ); + const int idx = (row * row) + int( v * fN ) + (diag - (opmapN - 1 - row)); + uint32_t* om = opmap + triIdx * ((opmapN * opmapN + 31) >> 5); + if (!(om[idx >> 5] & (1 << (idx & 31)))) return false; + } + // occluded. + return true; +} + +// PrecomputeTriangle (helper), transforms a triangle to the format used in: +// Fast Ray-Triangle Intersections by Coordinate Transformation. Baldwin & Weber, 2016. +void BVHBase::PrecomputeTriangle( const bvhvec4slice& vert, const uint32_t ti0, const uint32_t ti1, const uint32_t ti2, float* T ) +{ + bvhvec3 v0 = vert[ti0], v1 = vert[ti1], v2 = vert[ti2]; + bvhvec3 e1 = v1 - v0, e2 = v2 - v0, N = tinybvh_cross( e1, e2 ); + float x1, x2, n = tinybvh_dot( v0, N ), rN; + if (fabs( N[0] ) > fabs( N[1] ) && fabs( N[0] ) > fabs( N[2] )) + { + x1 = v1.y * v0.z - v1.z * v0.y, x2 = v2.y * v0.z - v2.z * v0.y, rN = 1.0f / N.x; + T[0] = 0, T[1] = e2.z * rN, T[2] = -e2.y * rN, T[3] = x2 * rN; + T[4] = 0, T[5] = -e1.z * rN, T[6] = e1.y * rN, T[7] = -x1 * rN; + T[8] = 1, T[9] = N.y * rN, T[10] = N.z * rN, T[11] = -n * rN; + } + else if (fabs( N.y ) > fabs( N.z )) + { + x1 = v1.z * v0.x - v1.x * v0.z, x2 = v2.z * v0.x - v2.x * v0.z, rN = 1.0f / N.y; + T[0] = -e2.z * rN, T[1] = 0, T[2] = e2.x * rN, T[3] = x2 * rN; + T[4] = e1.z * rN, T[5] = 0, T[6] = -e1.x * rN, T[7] = -x1 * rN; + T[8] = N.x * rN, T[9] = 1, T[10] = N.z * rN, T[11] = -n * rN; + } + else if (fabs( N.z ) > 0) + { + x1 = v1.x * v0.y - v1.y * v0.x, x2 = v2.x * v0.y - v2.y * v0.x, rN = 1.0f / N.z; + T[0] = e2.y * rN, T[1] = -e2.x * rN, T[2] = 0, T[3] = x2 * rN; + T[4] = -e1.y * rN, T[5] = e1.x * rN, T[6] = 0, T[7] = -x1 * rN; + T[8] = N.x * rN, T[9] = N.y * rN, T[10] = 1, T[11] = -n * rN; + } + else memset( T, 0, 12 * 4 ); +} + +bool BVH::BVHNode::Intersect( const bvhvec3& bmin, const bvhvec3& bmax ) const +{ + return bmin.x < aabbMax.x && bmax.x > aabbMin.x && + bmin.y < aabbMax.y && bmax.y > aabbMin.y && + bmin.z < aabbMax.z && bmax.z > aabbMin.z; +} + +// Faster ClipFrag, which clips against only two planes if a tri wasn't clipped before. +bool BVH::ClipFrag( const Fragment& orig, Fragment& newFrag, bvhvec3 bmin, bvhvec3 bmax, bvhvec3 minDim, const uint32_t axis ) const +{ + // find intersection of bmin/bmax and orig bmin/bmax + bmin = tinybvh_max( bmin, orig.bmin ), bmax = tinybvh_min( bmax, orig.bmax ); + const bvhvec3 extent = bmax - bmin; + uint32_t Nin = 3, vidx = orig.primIdx * 3; + if (orig.clipped) + { + // generic case: Sutherland-Hodgeman against six bounding planes + bvhvec3 vin[16], vout[16]; + if (vertIdx) + vin[0] = verts[vertIdx[vidx]], vin[1] = verts[vertIdx[vidx + 1]], vin[2] = verts[vertIdx[vidx + 2]]; + else + vin[0] = verts[vidx], vin[1] = verts[vidx + 1], vin[2] = verts[vidx + 2]; + for (uint32_t a = 0; a < 3; a++) + { + const float eps = minDim[a]; + if (extent[a] > eps) + { + uint32_t Nout = 0; + const float l = bmin[a], r = bmax[a]; + for (uint32_t v = 0; v < Nin; v++) + { + bvhvec3 v0 = vin[v], v1 = vin[(v + 1) % Nin]; + const bool v0in = v0[a] >= l - eps, v1in = v1[a] >= l - eps; + if (!(v0in || v1in)) continue; else if (v0in ^ v1in) + { + bvhvec3 C = v0 + (l - v0[a]) / (v1[a] - v0[a]) * (v1 - v0); + C[a] = l /* accurate */, vout[Nout++] = C; + } + if (v1in) vout[Nout++] = v1; + } + Nin = 0; + for (uint32_t v = 0; v < Nout; v++) + { + bvhvec3 v0 = vout[v], v1 = vout[(v + 1) % Nout]; + const bool v0in = v0[a] <= r + eps, v1in = v1[a] <= r + eps; + if (!(v0in || v1in)) continue; else if (v0in ^ v1in) + { + bvhvec3 C = v0 + (r - v0[a]) / (v1[a] - v0[a]) * (v1 - v0); + C[a] = r /* accurate */, vin[Nin++] = C; + } + if (v1in) vin[Nin++] = v1; + } + } + } + bvhvec3 mn( BVH_FAR ), mx( -BVH_FAR ); + for (uint32_t i = 0; i < Nin; i++) mn = tinybvh_min( mn, vin[i] ), mx = tinybvh_max( mx, vin[i] ); + newFrag.primIdx = orig.primIdx; + newFrag.bmin = tinybvh_max( mn, bmin ), newFrag.bmax = tinybvh_min( mx, bmax ); + newFrag.clipped = 1; + return Nin > 0; + } + else + { + // special case: if this fragment has not been clipped before, only clip against planes on split axis. + bool hasVerts = false; + bvhvec3 mn( BVH_FAR ), mx( -BVH_FAR ), vout[4], C; + if (extent[axis] > minDim[axis]) + { + const float l = bmin[axis], r = bmax[axis]; + uint32_t Nout = 0; + { + bvhvec3 v0, v1, v2; + if (vertIdx) + v0 = verts[vertIdx[vidx]], v1 = verts[vertIdx[vidx + 1]], v2 = verts[vertIdx[vidx + 2]]; + else + v0 = verts[vidx + 0], v1 = verts[vidx + 1], v2 = verts[vidx + 2]; + bool v0in = v0[axis] >= l, v1in = v1[axis] >= l, v2in = v2[axis] >= l; + if (v0in || v1in) + { + if (v0in ^ v1in) + { + const float f = tinybvh_clamp( (l - v0[axis]) / (v1[axis] - v0[axis]), 0.0f, 1.0f ); + C = v0 + f * (v1 - v0), C[axis] = l /* accurate */, vout[Nout++] = C; + } + if (v1in) vout[Nout++] = v1; + } + if (v1in || v2in) + { + if (v1in ^ v2in) + { + const float f = tinybvh_clamp( (l - v1[axis]) / (v2[axis] - v1[axis]), 0.0f, 1.0f ); + C = v1 + f * (v2 - v1), C[axis] = l /* accurate */, vout[Nout++] = C; + } + if (v2in) vout[Nout++] = v2; + } + if (v2in || v0in) + { + if (v2in ^ v0in) + { + const float f = tinybvh_clamp( (l - v2[axis]) / (v0[axis] - v2[axis]), 0.0f, 1.0f ); + C = v2 + f * (v0 - v2), C[axis] = l /* accurate */, vout[Nout++] = C; + } + if (v0in) vout[Nout++] = v0; + } + } + for (uint32_t v = 0; v < Nout; v++) + { + bvhvec3 v0 = vout[v], v1 = vout[(v + 1) % Nout]; + const bool v0in = v0[axis] <= r, v1in = v1[axis] <= r; + if (!(v0in || v1in)) continue; else if (v0in ^ v1in) + { + const float f = tinybvh_clamp( (r - v0[axis]) / (v1[axis] - v0[axis]), 0.0f, 1.0f ); + C = v0 + f * (v1 - v0), C[axis] = r /* accurate */, hasVerts = true; + mn = tinybvh_min( mn, C ), mx = tinybvh_max( mx, C ); + } + if (v1in) hasVerts = true, mn = tinybvh_min( mn, v1 ), mx = tinybvh_max( mx, v1 ); + } + } + newFrag.bmin = tinybvh_max( mn, bmin ), newFrag.bmax = tinybvh_min( mx, bmax ); + newFrag.primIdx = orig.primIdx, newFrag.clipped = 1; + return hasVerts; + } +} + +// SplitFrag: cut a fragment in two new fragments. +void BVH::SplitFrag( const Fragment& orig, Fragment& left, Fragment& right, const bvhvec3& minDim, const uint32_t splitAxis, const float splitPos, bool& leftOK, bool& rightOK ) const +{ + // method: we will split the fragment against the main split axis into two new fragments. + // In case the original fragment was clipped before, we first clip to the AABB of 'orig'. + bvhvec3 vin[16], vout[16], vleft[16], vright[16]; // occasionally exceeds 9, but never 12 + uint32_t vidx = orig.primIdx * 3, Nin = 3, Nout = 0, Nleft = 0, Nright = 0; + if (!vertIdx) vin[0] = verts[vidx], vin[1] = verts[vidx + 1], vin[2] = verts[vidx + 2]; + else vin[0] = verts[vertIdx[vidx]], vin[1] = verts[vertIdx[vidx + 1]], vin[2] = verts[vertIdx[vidx + 2]]; + const bvhvec3 extent = orig.bmax - orig.bmin; + if (orig.clipped) for (int a = 0; a < 3; a++) if (extent[a] > minDim[a]) + { + float l = orig.bmin[a], r = orig.bmax[a]; + Nout = 0; + for (uint32_t v = 0; v < Nin; v++) + { + bvhvec3 v0 = vin[v], v1 = vin[(v + 1) % Nin]; + const bool v0in = v0[a] >= l, v1in = v1[a] >= l; + if (!(v0in || v1in)) continue; else if (v0in ^ v1in) + { + const float f = tinybvh_clamp( (l - v0[a]) / (v1[a] - v0[a]), 0.0f, 1.0f ); + bvhvec3 C = v0 + f * (v1 - v0); + C[a] = l /* accurate */, vout[Nout++] = C; + } + if (v1in) vout[Nout++] = v1; + } + Nin = 0; + for (uint32_t v = 0; v < Nout; v++) + { + bvhvec3 v0 = vout[v], v1 = vout[(v + 1) % Nout]; + const bool v0in = v0[a] <= r, v1in = v1[a] <= r; + if (!(v0in || v1in)) continue; else if (v0in ^ v1in) + { + const float f = tinybvh_clamp( (r - v0[a]) / (v1[a] - v0[a]), 0.0f, 1.0f ); + bvhvec3 C = v0 + f * (v1 - v0); + C[a] = r /* accurate */, vin[Nin++] = C; + } + if (v1in) vin[Nin++] = v1; + } + } + for (uint32_t v = 0; v < Nin; v++) + { + bvhvec3 v0 = vin[v], v1 = vin[(v + 1) % Nin]; + bool v0left = v0[splitAxis] < splitPos, v1left = v1[splitAxis] < splitPos; + if (v0left && v1left) vleft[Nleft++] = v1; else if (!v0left && !v1left) vright[Nright++] = v1; else + { + const float f = tinybvh_clamp( (splitPos - v0[splitAxis]) / (v1[splitAxis] - v0[splitAxis]), 0.0f, 1.0f ); + bvhvec3 C = v0 + f * (v1 - v0); + C[splitAxis] = splitPos; + if (v0left) vleft[Nleft++] = vright[Nright++] = C, vright[Nright++] = v1; + else vright[Nright++] = vleft[Nleft++] = C, vleft[Nleft++] = v1; + } + } + // calculate left and right fragments + left.bmin = right.bmin = bvhvec3( BVH_FAR ), left.bmax = right.bmax = bvhvec3( -BVH_FAR ); + for (uint32_t i = 0; i < Nleft; i++) + left.bmin = tinybvh_min( left.bmin, vleft[i] ), + left.bmax = tinybvh_max( left.bmax, vleft[i] ); + for (uint32_t i = 0; i < Nright; i++) + right.bmin = tinybvh_min( right.bmin, vright[i] ), + right.bmax = tinybvh_max( right.bmax, vright[i] ); + left.clipped = right.clipped = 1, left.primIdx = right.primIdx = orig.primIdx; + leftOK = Nleft > 0, rightOK = Nright > 0; +} + +// RefitUp: Update bounding boxes of ancestors of the specified node. +void BVH_Verbose::RefitUp( uint32_t nodeIdx ) +{ + while (1) + { + BVHNode& node = bvhNode[nodeIdx]; + if (!node.isLeaf()) + { + const BVHNode& left = bvhNode[node.left]; + const BVHNode& right = bvhNode[node.right]; + node.aabbMin = tinybvh_min( left.aabbMin, right.aabbMin ); + node.aabbMax = tinybvh_max( left.aabbMax, right.aabbMax ); + } + if (nodeIdx == 0) break; else nodeIdx = node.parent; + } +} + +// SAHCostUp: Calculate the SAH cost of a node and its ancestry +float BVH_Verbose::SAHCostUp( uint32_t nodeIdx ) const +{ + float sum = 0; + while (nodeIdx != 0xffffffff) + { + BVHNode& node = bvhNode[nodeIdx]; + sum += BVH::SA( node.aabbMin, node.aabbMax ); + nodeIdx = node.parent; + } + return sum; +} + +// FindBestNewPosition +// Part of "Fast Insertion-Based Optimization of Bounding Volume Hierarchies" +// K.I.S.S. version with brute-force array search. +uint32_t BVH_Verbose::FindBestNewPosition( const uint32_t Lid ) const +{ + struct Task { float ci; uint32_t node; }; + ALIGNED( 64 ) Task task[512]; + float Cbest = BVH_FAR; + int tasks = 1 /* doesn't exceed 70 for Crytek Sponza */, Xbest = 0; + const BVHNode& L = bvhNode[Lid]; + // reinsert L into BVH + task[0].node = 0, task[0].ci = 0; + while (tasks > 0) + { + // 'pop' task with smallest Ci + uint32_t bestTask = 0; + float minCi = task[0].ci; // tnx Brian + for (int j = 1; j < tasks; j++) if (task[j].ci < minCi) minCi = task[j].ci, bestTask = j; + const uint32_t Xid = task[bestTask].node; + const float CiLX = task[bestTask].ci; + if (--tasks > 0) task[bestTask] = task[tasks]; + // execute task + const BVHNode& X = bvhNode[Xid]; + if (CiLX + L.SA() >= Cbest) break; + const float CdLX = SA( tinybvh_min( L.aabbMin, X.aabbMin ), tinybvh_max( L.aabbMax, X.aabbMax ) ); + const float CLX = CiLX + CdLX; + if (CLX < Cbest && Xid != 0) Cbest = CLX, Xbest = Xid; + const float Ci = CLX - X.SA(); + if (Ci + L.SA() >= Cbest || X.isLeaf()) continue; + task[tasks].node = X.left, task[tasks++].ci = Ci; + task[tasks].node = X.right, task[tasks++].ci = Ci; + } + return Xbest; +} + +// Determine for each node in the tree the number of primitives +// stored in that subtree. Helper function for MergeLeafs. +uint32_t BVH_Verbose::CountSubtreeTris( const uint32_t nodeIdx, uint32_t* counters ) +{ + BVHNode& node = bvhNode[nodeIdx]; + uint32_t result = node.triCount; + if (!result) result = CountSubtreeTris( node.left, counters ) + CountSubtreeTris( node.right, counters ); + counters[nodeIdx] = result; + return result; +} + +// Write the triangle indices stored in a subtree to a continuous +// slice in the 'newIdx' array. Helper function for MergeLeafs. +void BVH_Verbose::MergeSubtree( const uint32_t nodeIdx, uint32_t* newIdx, uint32_t& newIdxPtr ) +{ + BVHNode& node = bvhNode[nodeIdx]; + if (node.isLeaf()) + { + memcpy( newIdx + newIdxPtr, primIdx + node.firstTri, node.triCount * 4 ); + newIdxPtr += node.triCount; + return; + } + MergeSubtree( node.left, newIdx, newIdxPtr ); + MergeSubtree( node.right, newIdx, newIdxPtr ); +} + +} // namespace tinybvh + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#endif // TINYBVH_IMPLEMENTATION diff --git a/Libraries/tinybvh/README.md b/Libraries/tinybvh/README.md new file mode 100644 index 000000000..09f986c54 --- /dev/null +++ b/Libraries/tinybvh/README.md @@ -0,0 +1,10 @@ +Vendored dependency: `tiny_bvh` + +- File: `Include/tiny_bvh.h` +- Upstream project: https://github.com/jbikker/tinybvh +- Imported from the winding-number integration source snapshot +- License: MIT (license header retained in vendored file) + +Notes: +- This is vendored for boolean/winding integration in lib3mf. +- Future work may switch this dependency to package-manager resolution (e.g. vcpkg). diff --git a/SDK/Examples/Python/assign_materials_and_carry_slicer_metadata.py b/SDK/Examples/Python/assign_materials_and_carry_slicer_metadata.py new file mode 100644 index 000000000..57c4243fc --- /dev/null +++ b/SDK/Examples/Python/assign_materials_and_carry_slicer_metadata.py @@ -0,0 +1,292 @@ +#!/usr/bin/env python3 + +"""Example helper for assigning object materials and carrying slicer metadata. + +This script handles the standard 3MF part of the workflow: +- read an input 3MF +- assign one base material per mesh object +- optionally collapse all mesh objects into one ComponentsObject build item +- optionally copy `/Metadata/...` attachments from a slicer-produced template 3MF +- optionally inject a raw `project_settings.config` attachment +- optionally transplant slicer-specific package entries from a template 3MF after writing + +Important: +- Material assignment is standard 3MF and supported by lib3mf. +- Bambu/Orca "extruder" semantics are slicer-specific and not modeled by lib3mf. + For those, this script either copies attachment bytes or transplants package entries + from a known-good slicer-generated template. + +This variant assumes the PyPI package is installed: + import lib3mf + from lib3mf import get_wrapper +""" + +from __future__ import annotations + +import argparse +import io +import zipfile + +import lib3mf +from lib3mf import get_wrapper + + +DEFAULT_RELATIONSHIP = "http://schemas.3mf.io/package/2015/relationships/metadata/attachment" + + +def parse_hex_color(text: str) -> lib3mf.Color: + value = text.strip() + if value.startswith("#"): + value = value[1:] + if len(value) != 6: + raise ValueError(f"Color must be RRGGBB, got: {text}") + + color = lib3mf.Color() + color.Red = int(value[0:2], 16) + color.Green = int(value[2:4], 16) + color.Blue = int(value[4:6], 16) + color.Alpha = 255 + return color + + +def identity_transform(wrapper): + return wrapper.GetIdentityTransform() + + +def iter_objects(model): + it = model.GetObjects() + while it.MoveNext(): + yield it.GetCurrentObject() + + +def iter_mesh_objects(model): + for obj in iter_objects(model): + if obj.IsMeshObject(): + yield obj + + +def iter_build_items(model): + it = model.GetBuildItems() + while it.MoveNext(): + yield it.GetCurrent() + + +def assign_materials(model: Lib3MF.Model, mesh_objects, material_specs): + if not material_specs: + return None + + base_materials = model.AddBaseMaterialGroup() + property_ids = [] + + for idx, spec in enumerate(material_specs, start=1): + if "=" in spec: + name, color_text = spec.split("=", 1) + elif ":" in spec: + name, color_text = spec.split(":", 1) + else: + name = f"Material {idx}" + color_text = spec + property_id = base_materials.AddMaterial(name.strip(), parse_hex_color(color_text.strip())) + property_ids.append(property_id) + + mesh_objects = list(mesh_objects) + if len(mesh_objects) > len(property_ids): + raise ValueError( + f"Need at least {len(mesh_objects)} material specs, got {len(property_ids)}." + ) + + resource_id = base_materials.GetResourceID() + for mesh_object, property_id in zip(mesh_objects, property_ids): + mesh_object.SetObjectLevelProperty(resource_id, property_id) + + return base_materials + + +def collapse_meshes_to_components(model, wrapper, mesh_objects): + mesh_objects = list(mesh_objects) + if not mesh_objects: + raise ValueError("No mesh objects found to collapse.") + + components = model.AddComponentsObject() + components.SetName("Combined Part") + transform = identity_transform(wrapper) + + for mesh_object in mesh_objects: + components.AddComponent(mesh_object, transform) + + existing_build_items = list(iter_build_items(model)) + for build_item in existing_build_items: + model.RemoveBuildItem(build_item) + + model.AddBuildItem(components, transform) + return components + + +def copy_metadata_attachments_from_template(wrapper, target_model, template_3mf: str): + template_model = wrapper.CreateModel() + template_reader = template_model.QueryReader("3mf") + template_reader.ReadFromFile(template_3mf) + + copied = [] + for index in range(template_model.GetAttachmentCount()): + attachment = template_model.GetAttachment(index) + path = attachment.GetPath() + if not path.startswith("/Metadata/"): + continue + + target_attachment = target_model.AddAttachment(path, attachment.GetRelationShipType()) + target_attachment.ReadFromBuffer(attachment.WriteToBuffer()) + copied.append(path) + + return copied + + +def add_project_settings_attachment(model, config_path: str, uri: str, relationship_type: str): + attachment = model.AddAttachment(uri, relationship_type) + attachment.ReadFromFile(config_path) + return attachment + + +def normalize_zip_name(name: str) -> str: + return name[1:] if name.startswith("/") else name + + +def should_copy_zip_entry(entry_name: str, prefixes): + normalized = normalize_zip_name(entry_name) + return any(normalized.startswith(normalize_zip_name(prefix)) for prefix in prefixes) + + +def transplant_package_entries(template_3mf: str, output_3mf: str, prefixes): + if not prefixes: + return [] + + copied = [] + with zipfile.ZipFile(output_3mf, "r") as output_zip: + existing_entries = { + info.filename: output_zip.read(info.filename) + for info in output_zip.infolist() + } + + with zipfile.ZipFile(template_3mf, "r") as template_zip: + for info in template_zip.infolist(): + if should_copy_zip_entry(info.filename, prefixes): + existing_entries[info.filename] = template_zip.read(info.filename) + copied.append(info.filename) + + buffer = io.BytesIO() + with zipfile.ZipFile(buffer, "w", compression=zipfile.ZIP_DEFLATED) as merged_zip: + for filename in sorted(existing_entries.keys()): + merged_zip.writestr(filename, existing_entries[filename]) + + with open(output_3mf, "wb") as handle: + handle.write(buffer.getvalue()) + + return copied + + +def main() -> int: + parser = argparse.ArgumentParser(description="Assign object materials and carry slicer-specific package metadata.") + parser.add_argument("input_3mf", help="Input 3MF file, e.g. from OpenSCAD") + parser.add_argument("output_3mf", help="Output 3MF file") + parser.add_argument( + "--material", + action="append", + default=[], + help="Material spec in the form Name=#RRGGBB or Name:RRGGBB. Repeat once per mesh object.", + ) + parser.add_argument( + "--collapse-to-components", + action="store_true", + help="Replace current build items with a single ComponentsObject referencing all mesh objects.", + ) + parser.add_argument( + "--copy-metadata-from", + help="Copy every attachment under /Metadata/ from a slicer-produced template 3MF.", + ) + parser.add_argument( + "--project-settings-config", + help="Attach a raw project_settings.config file into the package.", + ) + parser.add_argument( + "--project-settings-uri", + default="/Metadata/project_settings.config", + help="Package URI for the injected config attachment.", + ) + parser.add_argument( + "--project-settings-relationship", + default=DEFAULT_RELATIONSHIP, + help="Relationship type for the injected config attachment.", + ) + parser.add_argument( + "--transplant-from-template", + help="After writing the output 3MF, copy selected raw package entries from this slicer-authored template.", + ) + parser.add_argument( + "--transplant-prefix", + action="append", + default=[], + help="Package prefix to copy from the template zip, e.g. /Metadata/ or /3D/Objects/. Repeat as needed.", + ) + args = parser.parse_args() + + wrapper = get_wrapper() + model = wrapper.CreateModel() + reader = model.QueryReader("3mf") + reader.SetStrictModeActive(False) + reader.ReadFromFile(args.input_3mf) + + mesh_objects = list(iter_mesh_objects(model)) + if not mesh_objects: + raise RuntimeError("Input model does not contain any mesh objects.") + + assign_materials(model, mesh_objects, args.material) + + if args.collapse_to_components: + collapse_meshes_to_components(model, wrapper, mesh_objects) + + copied_paths = [] + if args.copy_metadata_from: + copied_paths = copy_metadata_attachments_from_template(wrapper, model, args.copy_metadata_from) + + if args.project_settings_config: + add_project_settings_attachment( + model, + args.project_settings_config, + args.project_settings_uri, + args.project_settings_relationship, + ) + + model_metadata = model.GetMetaDataGroup() + + writer = model.QueryWriter("3mf") + writer.WriteToFile(args.output_3mf) + + transplanted_entries = [] + if args.transplant_from_template and args.transplant_prefix: + transplanted_entries = transplant_package_entries( + args.transplant_from_template, + args.output_3mf, + args.transplant_prefix, + ) + + print(f"Read {len(mesh_objects)} mesh object(s) from {args.input_3mf}") + if args.material: + print(f"Assigned {min(len(args.material), len(mesh_objects))} material mapping(s)") + if args.collapse_to_components: + print("Collapsed mesh build items into one ComponentsObject build item") + if copied_paths: + print("Copied metadata attachments:") + for path in copied_paths: + print(f" {path}") + if args.project_settings_config: + print(f"Attached raw project settings file as {args.project_settings_uri}") + if transplanted_entries: + print("Transplanted package entries from template:") + for path in transplanted_entries: + print(f" {path}") + print(f"Wrote {args.output_3mf}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Source/API/lib3mf.cpp b/Source/API/lib3mf.cpp index 1e0ab8585..2fe6e022b 100644 --- a/Source/API/lib3mf.cpp +++ b/Source/API/lib3mf.cpp @@ -120,6 +120,12 @@ void CWrapper::GetSpecificationVersion (const std::string & sSpecificationURL, b nMicro = NMR_SPECVERSION_IMPLICIT_MICRO; bIsSupported = true; } + else if (!sSpecificationURL.compare(std::string(XML_3MF_NAMESPACE_BOOLEANSPEC))) { + nMajor = NMR_SPECVERSION_BOOLEAN_MAJOR; + nMinor = NMR_SPECVERSION_BOOLEAN_MINOR; + nMicro = NMR_SPECVERSION_BOOLEAN_MICRO; + bIsSupported = true; + } else { bIsSupported = false; } @@ -275,4 +281,3 @@ sLib3MFTransform CWrapper::GetTranslationTransform(const Lib3MF_single fVectorX, return Transform; } - diff --git a/Source/API/lib3mf_booleanobject.cpp b/Source/API/lib3mf_booleanobject.cpp new file mode 100644 index 000000000..7fb868e2f --- /dev/null +++ b/Source/API/lib3mf_booleanobject.cpp @@ -0,0 +1,183 @@ +/*++ + +Copyright (C) 2024 3MF Consortium (Original Author) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: This is a stub class definition of CBooleanObject + +*/ + +#include "lib3mf_booleanobject.hpp" +#include "lib3mf_interfaceexception.hpp" + +// Include custom headers here. +#include "lib3mf_meshobject.hpp" +#include "Model/Classes/NMR_ModelBooleanObject.h" +#include "Model/Classes/NMR_ModelComponentsObject.h" +#include "Model/Classes/NMR_ModelMeshObject.h" +#include "lib3mf_utils.hpp" + +using namespace Lib3MF::Impl; + +/************************************************************************************************************************* + Class definition of CBooleanObject +**************************************************************************************************************************/ + +CBooleanObject::CBooleanObject(NMR::PModelResource pResource) + : CResource(pResource), CObject(pResource) +{ +} + +NMR::PModelBooleanObject CBooleanObject::booleanObject() +{ + auto pBooleanObject = std::dynamic_pointer_cast(resource()); + if (!pBooleanObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDOBJECT); + return pBooleanObject; +} + +bool CBooleanObject::IsBooleanObject() +{ + return true; +} + +bool CBooleanObject::IsMeshObject() +{ + return false; +} + +bool CBooleanObject::IsComponentsObject() +{ + return false; +} + +bool CBooleanObject::IsLevelSetObject() +{ + return false; +} + +void CBooleanObject::SetBaseObject(IObject* pBaseObject, const Lib3MF::sTransform Transform) +{ + if (!pBaseObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDPARAM); + + auto pBaseResource = booleanObject()->getModel()->findResource( + booleanObject()->getModel()->currentPath(), + pBaseObject->GetResourceID()); + auto pBaseModelObject = std::dynamic_pointer_cast(pBaseResource); + if (!pBaseModelObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDOBJECT); + if (dynamic_cast(pBaseModelObject.get()) != nullptr) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCOMPONENTSOBJECT); + + booleanObject()->setBaseObject(pBaseModelObject.get(), Lib3MF::TransformToMatrix(Transform)); +} + +IObject * CBooleanObject::GetBaseObject() +{ + auto pBaseObject = booleanObject()->getBaseObject(); + if (!pBaseObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDOBJECT); + + auto pResource = booleanObject()->getModel()->findResource(pBaseObject->getPackageResourceID()); + return CObject::fnCreateObjectFromModelResource(pResource, true); +} + +void CBooleanObject::SetBaseTransform(const Lib3MF::sTransform Transform) +{ + booleanObject()->setBaseTransform(Lib3MF::TransformToMatrix(Transform)); +} + +Lib3MF::sTransform CBooleanObject::GetBaseTransform() +{ + return Lib3MF::MatrixToTransform(booleanObject()->getBaseTransform()); +} + +void CBooleanObject::SetOperation(const Lib3MF::eBooleanOperation eOperation) +{ + booleanObject()->setOperation((NMR::eModelBooleanOperation)eOperation); +} + +Lib3MF::eBooleanOperation CBooleanObject::GetOperation() +{ + return (Lib3MF::eBooleanOperation)booleanObject()->getOperation(); +} + +void CBooleanObject::SetCSGModeEnabled(const bool bCSGModeEnabled) +{ + booleanObject()->setCSGModeEnabled(bCSGModeEnabled); +} + +bool CBooleanObject::GetCSGModeEnabled() +{ + return booleanObject()->getCSGModeEnabled(); +} + +void CBooleanObject::SetExtractionGridResolution(const Lib3MF_uint32 nGridResolution) +{ + booleanObject()->setExtractionGridResolution(nGridResolution); +} + +Lib3MF_uint32 CBooleanObject::GetExtractionGridResolution() +{ + return booleanObject()->getExtractionGridResolution(); +} + +Lib3MF_uint32 CBooleanObject::GetOperandCount() +{ + return booleanObject()->getOperandCount(); +} + +void CBooleanObject::AddOperand(IMeshObject* pOperandObject, const Lib3MF::sTransform Transform) +{ + if (!pOperandObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDPARAM); + + auto pOperandResource = booleanObject()->getModel()->findResource( + booleanObject()->getModel()->currentPath(), + pOperandObject->GetResourceID()); + auto pMeshObject = std::dynamic_pointer_cast(pOperandResource); + if (!pMeshObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDMESHOBJECT); + + booleanObject()->addOperand(pMeshObject.get(), Lib3MF::TransformToMatrix(Transform)); +} + +Lib3MF::sTransform CBooleanObject::GetOperand(const Lib3MF_uint32 nIndex, IMeshObject*& pOperandObject) +{ + auto operand = booleanObject()->getOperand(nIndex); + auto pMeshObject = dynamic_cast(operand->getObject()); + if (!pMeshObject) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDMESHOBJECT); + + auto pResource = booleanObject()->getModel()->findResource(pMeshObject->getPackageResourceID()); + std::unique_ptr pObject(CObject::fnCreateObjectFromModelResource(pResource, true)); + auto pMeshObjectInterface = dynamic_cast(pObject.get()); + if (!pMeshObjectInterface) + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDMESHOBJECT); + + pOperandObject = pMeshObjectInterface; + pObject.release(); + return Lib3MF::MatrixToTransform(operand->getTransform()); +} diff --git a/Source/API/lib3mf_booleanobjectiterator.cpp b/Source/API/lib3mf_booleanobjectiterator.cpp new file mode 100644 index 000000000..83b01648c --- /dev/null +++ b/Source/API/lib3mf_booleanobjectiterator.cpp @@ -0,0 +1,46 @@ +/*++ + +Copyright (C) 2024 3MF Consortium (Original Author) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: This is a stub class definition of CBooleanObjectIterator + +*/ + +#include "lib3mf_booleanobjectiterator.hpp" +#include "lib3mf_interfaceexception.hpp" + +// Include custom headers here. +#include "lib3mf_booleanobject.hpp" + +using namespace Lib3MF::Impl; + +/************************************************************************************************************************* + Class definition of CBooleanObjectIterator +**************************************************************************************************************************/ + +IBooleanObject * CBooleanObjectIterator::GetCurrentBooleanObject() +{ + return new CBooleanObject(GetCurrentResource()); +} diff --git a/Source/API/lib3mf_componentsobject.cpp b/Source/API/lib3mf_componentsobject.cpp index 2c6530e05..db05dfe7f 100644 --- a/Source/API/lib3mf_componentsobject.cpp +++ b/Source/API/lib3mf_componentsobject.cpp @@ -131,3 +131,7 @@ bool CComponentsObject::IsLevelSetObject() return false; } +bool CComponentsObject::IsBooleanObject() +{ + return false; +} diff --git a/Source/API/lib3mf_levelset.cpp b/Source/API/lib3mf_levelset.cpp index f73d39f5c..21d4fd227 100644 --- a/Source/API/lib3mf_levelset.cpp +++ b/Source/API/lib3mf_levelset.cpp @@ -267,6 +267,11 @@ bool CLevelSet::IsLevelSetObject() return true; } +bool CLevelSet::IsBooleanObject() +{ + return false; +} + IVolumeData * CLevelSet::GetVolumeData() { NMR::PModelVolumeData pVolumeData = levelSetObject()->getVolumeData(); @@ -275,4 +280,4 @@ IVolumeData * CLevelSet::GetVolumeData() return nullptr; } return new CVolumeData(pVolumeData); -} \ No newline at end of file +} diff --git a/Source/API/lib3mf_meshobject.cpp b/Source/API/lib3mf_meshobject.cpp index d8d71d977..c2192bdf0 100644 --- a/Source/API/lib3mf_meshobject.cpp +++ b/Source/API/lib3mf_meshobject.cpp @@ -388,6 +388,11 @@ bool CMeshObject::IsLevelSetObject() return false; } +bool CMeshObject::IsBooleanObject() +{ + return false; +} + bool CMeshObject::IsValid() { return meshObject()->isValid(); @@ -465,4 +470,3 @@ ITriangleSet* CMeshObject::GetTriangleSet(const Lib3MF_uint32 nIndex) return new CTriangleSet(pTriangleSet, pMeshObject); } - diff --git a/Source/API/lib3mf_model.cpp b/Source/API/lib3mf_model.cpp index 8bffb590f..0959baefe 100644 --- a/Source/API/lib3mf_model.cpp +++ b/Source/API/lib3mf_model.cpp @@ -42,6 +42,8 @@ Abstract: This is a stub class definition of CModel #include "lib3mf_resourceiterator.hpp" #include "lib3mf_componentsobject.hpp" #include "lib3mf_componentsobjectiterator.hpp" +#include "lib3mf_booleanobject.hpp" +#include "lib3mf_booleanobjectiterator.hpp" #include "lib3mf_basematerialgroup.hpp" #include "lib3mf_metadatagroup.hpp" #include "lib3mf_attachment.hpp" @@ -82,6 +84,7 @@ Abstract: This is a stub class definition of CModel #include "Model/Classes/NMR_ModelFunctionFromImage3D.h" #include "Model/Classes/NMR_ModelVolumeData.h" #include "Model/Classes/NMR_ModelLevelSetObject.h" +#include "Model/Classes/NMR_ModelBooleanObject.h" #include "Common/NMR_SecureContentTypes.h" #include "lib3mf_utils.hpp" @@ -140,6 +143,9 @@ IResource* CModel::createIResourceFromModelResource(NMR::PModelResource pResourc if (auto p = std::dynamic_pointer_cast(pResource)) { return new CComponentsObject(p); } + if (auto p = std::dynamic_pointer_cast(pResource)) { + return new CBooleanObject(p); + } if (auto p = std::dynamic_pointer_cast(pResource)) { return new CCompositeMaterials(p); @@ -314,6 +320,16 @@ IComponentsObject * CModel::GetComponentsObjectByID(const Lib3MF_uint32 nUniqueR throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDCOMPONENTSOBJECT); } +IBooleanObject * CModel::GetBooleanObjectByID(const Lib3MF_uint32 nUniqueResourceID) +{ + NMR::PModelResource pObjectResource = model().findResource(nUniqueResourceID); + if (auto pBooleanObject = std::dynamic_pointer_cast(pObjectResource)) { + return new CBooleanObject(pBooleanObject); + } + else + throw ELib3MFInterfaceException(LIB3MF_ERROR_INVALIDOBJECT); +} + IColorGroup * CModel::GetColorGroupByID(const Lib3MF_uint32 nUniqueResourceID) { NMR::PModelResource pResource = model().findResource(nUniqueResourceID); @@ -452,6 +468,19 @@ IComponentsObjectIterator * CModel::GetComponentsObjects() return pResult.release(); } +IBooleanObjectIterator * CModel::GetBooleanObjects() +{ + auto pResult = std::unique_ptr(new CBooleanObjectIterator()); + Lib3MF_uint32 nObjectsCount = model().getObjectCount(); + + for (Lib3MF_uint32 nIdx = 0; nIdx < nObjectsCount; nIdx++) { + auto resource = model().getObjectResource(nIdx); + if (dynamic_cast(resource.get())) + pResult->addResource(resource); + } + return pResult.release(); +} + ITexture2DIterator * CModel::GetTexture2Ds() { auto pResult = std::unique_ptr(new CTexture2DIterator()); @@ -611,6 +640,15 @@ IComponentsObject * CModel::AddComponentsObject () return new CComponentsObject(pNewResource); } +IBooleanObject * CModel::AddBooleanObject() +{ + NMR::ModelResourceID NewResourceID = model().generateResourceID(); + NMR::PModelBooleanObject pNewResource = std::make_shared(NewResourceID, &model()); + + model().addResource(pNewResource); + return new CBooleanObject(pNewResource); +} + ISliceStack * CModel::AddSliceStack(const Lib3MF_double dZBottom) { NMR::ModelResourceID NewResourceID = model().generateResourceID(); diff --git a/Source/API/lib3mf_object.cpp b/Source/API/lib3mf_object.cpp index 12ef901c5..996efccc0 100644 --- a/Source/API/lib3mf_object.cpp +++ b/Source/API/lib3mf_object.cpp @@ -33,6 +33,7 @@ Abstract: This is a stub class definition of CObject #include "lib3mf_meshobject.hpp" #include "lib3mf_levelset.hpp" +#include "lib3mf_booleanobject.hpp" #include "lib3mf_componentsobject.hpp" #include "lib3mf_metadatagroup.hpp" #include "lib3mf_slicestack.hpp" @@ -41,6 +42,7 @@ Abstract: This is a stub class definition of CObject // Include custom headers here. #include "Model/Classes/NMR_ModelMeshObject.h" #include "Model/Classes/NMR_ModelLevelSetObject.h" +#include "Model/Classes/NMR_ModelBooleanObject.h" #include "Model/Classes/NMR_ModelComponentsObject.h" using namespace Lib3MF::Impl; @@ -72,6 +74,11 @@ IObject* CObject::fnCreateObjectFromModelResource(NMR::PModelResource pResource, return new CLevelSet(pResource); } + NMR::CModelBooleanObject * pBooleanObject = dynamic_cast (pResource.get()); + if (pBooleanObject) { + return new CBooleanObject(pResource); + } + NMR::CModelComponentsObject * pComponentsResource = dynamic_cast (pResource.get()); if (pComponentsResource) { return new CComponentsObject(pResource); @@ -137,6 +144,12 @@ bool CObject::IsLevelSetObject() throw ELib3MFInterfaceException(LIB3MF_ERROR_SHOULDNOTBECALLED); } +bool CObject::IsBooleanObject() +{ + // overwritten by child class + throw ELib3MFInterfaceException(LIB3MF_ERROR_SHOULDNOTBECALLED); +} + IMeshObject * CObject::AsMeshObject() { @@ -267,4 +280,3 @@ Lib3MF::sBox CObject::GetOutbox() return s; } - diff --git a/Source/Common/Boolean/NMR_BooleanDistanceField.cpp b/Source/Common/Boolean/NMR_BooleanDistanceField.cpp new file mode 100644 index 000000000..51504dfae --- /dev/null +++ b/Source/Common/Boolean/NMR_BooleanDistanceField.cpp @@ -0,0 +1,486 @@ +/*++ + +Copyright (C) 2026 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +Signed-distance field generation for winding-number based boolean CSG. + +--*/ + +#include "Common/Boolean/NMR_BooleanDistanceField.h" + +#include "Common/NMR_Exception.h" +#include "Common/Winding/NMR_WindingNumber.h" +#include "tiny_bvh.h" +#include "nanoflann.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace NMR::Boolean { + + namespace { + sBooleanVec3d operator+(const sBooleanVec3d & a, const sBooleanVec3d & b) { return { a.x + b.x, a.y + b.y, a.z + b.z }; } + sBooleanVec3d operator-(const sBooleanVec3d & a, const sBooleanVec3d & b) { return { a.x - b.x, a.y - b.y, a.z - b.z }; } + sBooleanVec3d operator*(const sBooleanVec3d & a, const double s) { return { a.x * s, a.y * s, a.z * s }; } + double dot(const sBooleanVec3d & a, const sBooleanVec3d & b) { return a.x * b.x + a.y * b.y + a.z * b.z; } + + double combineField(double a, double b, eModelBooleanOperation operation) + { + switch (operation) { + case eModelBooleanOperation::Union: + return std::min(a, b); + case eModelBooleanOperation::Intersection: + return std::max(a, b); + case eModelBooleanOperation::Difference: + return std::max(a, -b); + default: + return std::min(a, b); + } + } + + struct sDistanceMesh { + struct sCentroidAdaptor { + const std::vector> * pCentroids = nullptr; + size_t kdtree_get_point_count() const { return pCentroids ? pCentroids->size() : 0; } + double kdtree_get_pt(size_t idx, size_t dim) const { return (*pCentroids)[idx][dim]; } + template bool kdtree_get_bbox(BBOX &) const { return false; } + }; + + using CentroidKDTree = nanoflann::KDTreeSingleIndexAdaptor< + nanoflann::L2_Simple_Adaptor, + sCentroidAdaptor, 3, size_t>; + + std::vector> vertices; + std::vector> triangles; + std::vector> centroids; + std::vector triVertsPacked; + sCentroidAdaptor centroidAdaptor; + std::unique_ptr pCentroidKDTree; + std::unique_ptr pBVH; + + sDistanceMesh() = default; + sDistanceMesh(const sDistanceMesh &) = delete; + sDistanceMesh & operator=(const sDistanceMesh &) = delete; + + sDistanceMesh(sDistanceMesh && other) noexcept + : vertices(std::move(other.vertices)), + triangles(std::move(other.triangles)), + centroids(std::move(other.centroids)), + triVertsPacked(std::move(other.triVertsPacked)), + pBVH(std::move(other.pBVH)) + { + rebuildCentroidKDTree(); + } + + sDistanceMesh & operator=(sDistanceMesh && other) noexcept + { + if (this != &other) { + vertices = std::move(other.vertices); + triangles = std::move(other.triangles); + centroids = std::move(other.centroids); + triVertsPacked = std::move(other.triVertsPacked); + pBVH = std::move(other.pBVH); + rebuildCentroidKDTree(); + } + return *this; + } + + private: + void rebuildCentroidKDTree() + { + centroidAdaptor.pCentroids = nullptr; + pCentroidKDTree.reset(); + if (!centroids.empty()) { + centroidAdaptor.pCentroids = ¢roids; + pCentroidKDTree = std::make_unique(3, centroidAdaptor, nanoflann::KDTreeSingleIndexAdaptorParams(12)); + pCentroidKDTree->buildIndex(); + } + } + }; + + struct sWindingMesh { + std::vector vertices; + std::vector triangles; + std::unique_ptr winding; + }; + + sWindingMesh makeWindingMesh(_In_ CMesh * pMesh) + { + if (!pMesh) + throw CNMRException(NMR_ERROR_INVALIDPARAM); + if (pMesh->getNodeCount() < 3 || pMesh->getFaceCount() < 1) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + + sWindingMesh result; + result.vertices.resize(pMesh->getNodeCount()); + result.triangles.resize(pMesh->getFaceCount()); + + for (nfUint32 nNodeIdx = 0; nNodeIdx < pMesh->getNodeCount(); ++nNodeIdx) { + const auto * pNode = pMesh->getNode(nNodeIdx); + result.vertices[nNodeIdx] = { + static_cast(pNode->m_position.m_fields[0]), + static_cast(pNode->m_position.m_fields[1]), + static_cast(pNode->m_position.m_fields[2]) + }; + } + + for (nfUint32 nFaceIdx = 0; nFaceIdx < pMesh->getFaceCount(); ++nFaceIdx) { + const auto * pFace = pMesh->getFace(nFaceIdx); + result.triangles[nFaceIdx] = { + static_cast(pFace->m_nodeindices[0]), + static_cast(pFace->m_nodeindices[1]), + static_cast(pFace->m_nodeindices[2]) + }; + } + + tg::windingNumber::Settings settings; + settings.taylorOrder = 2; + settings.accuracy = 2.0; + result.winding = std::make_unique(result.vertices, result.triangles, settings); + return result; + } + + sDistanceMesh makeDistanceMesh(_In_ CMesh * pMesh) + { + if (!pMesh) + throw CNMRException(NMR_ERROR_INVALIDPARAM); + + sDistanceMesh result; + result.vertices.resize(pMesh->getNodeCount()); + for (nfUint32 nNodeIdx = 0; nNodeIdx < pMesh->getNodeCount(); ++nNodeIdx) { + const auto * pNode = pMesh->getNode(nNodeIdx); + result.vertices[nNodeIdx] = { + static_cast(pNode->m_position.m_fields[0]), + static_cast(pNode->m_position.m_fields[1]), + static_cast(pNode->m_position.m_fields[2]) + }; + } + + result.triangles.resize(pMesh->getFaceCount()); + result.centroids.resize(pMesh->getFaceCount()); + result.triVertsPacked.reserve(static_cast(pMesh->getFaceCount()) * 3ULL); + for (nfUint32 nFaceIdx = 0; nFaceIdx < pMesh->getFaceCount(); ++nFaceIdx) { + const auto * pFace = pMesh->getFace(nFaceIdx); + const auto i0 = static_cast(pFace->m_nodeindices[0]); + const auto i1 = static_cast(pFace->m_nodeindices[1]); + const auto i2 = static_cast(pFace->m_nodeindices[2]); + result.triangles[nFaceIdx] = { i0, i1, i2 }; + + const auto & v0 = result.vertices[i0]; + const auto & v1 = result.vertices[i1]; + const auto & v2 = result.vertices[i2]; + result.centroids[nFaceIdx] = { + (v0[0] + v1[0] + v2[0]) / 3.0, + (v0[1] + v1[1] + v2[1]) / 3.0, + (v0[2] + v1[2] + v2[2]) / 3.0 + }; + result.triVertsPacked.emplace_back(v0[0], v0[1], v0[2]); + result.triVertsPacked.emplace_back(v1[0], v1[1], v1[2]); + result.triVertsPacked.emplace_back(v2[0], v2[1], v2[2]); + } + + if (!result.centroids.empty()) { + result.centroidAdaptor.pCentroids = &result.centroids; + result.pCentroidKDTree = std::make_unique(3, result.centroidAdaptor, nanoflann::KDTreeSingleIndexAdaptorParams(12)); + result.pCentroidKDTree->buildIndex(); + } + + if (!result.triangles.empty()) { + result.pBVH = std::make_unique(); + result.pBVH->Build(result.triVertsPacked.data(), static_cast(result.triangles.size())); + } + + return result; + } + + double pointTriangleDistanceSquared(const sBooleanVec3d & p, const sBooleanVec3d & a, const sBooleanVec3d & b, const sBooleanVec3d & c) + { + const sBooleanVec3d ab = b - a; + const sBooleanVec3d ac = c - a; + const sBooleanVec3d ap = p - a; + + const double d1 = dot(ab, ap); + const double d2 = dot(ac, ap); + if (d1 <= 0.0 && d2 <= 0.0) + return dot(ap, ap); + + const sBooleanVec3d bp = p - b; + const double d3 = dot(ab, bp); + const double d4 = dot(ac, bp); + if (d3 >= 0.0 && d4 <= d3) + return dot(bp, bp); + + const double vc = d1 * d4 - d3 * d2; + if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) { + const double v = d1 / (d1 - d3); + const sBooleanVec3d proj = a + ab * v; + const sBooleanVec3d diff = p - proj; + return dot(diff, diff); + } + + const sBooleanVec3d cp = p - c; + const double d5 = dot(ab, cp); + const double d6 = dot(ac, cp); + if (d6 >= 0.0 && d5 <= d6) + return dot(cp, cp); + + const double vb = d5 * d2 - d1 * d6; + if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0) { + const double w = d2 / (d2 - d6); + const sBooleanVec3d proj = a + ac * w; + const sBooleanVec3d diff = p - proj; + return dot(diff, diff); + } + + const double va = d3 * d6 - d5 * d4; + if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0) { + const sBooleanVec3d bc = c - b; + const double w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + const sBooleanVec3d proj = b + bc * w; + const sBooleanVec3d diff = p - proj; + return dot(diff, diff); + } + + const double denom = 1.0 / (va + vb + vc); + const double v = vb * denom; + const double w = vc * denom; + const sBooleanVec3d proj = a + ab * v + ac * w; + const sBooleanVec3d diff = p - proj; + return dot(diff, diff); + } + + double pointAABBDistanceSquared(const sBooleanVec3d & p, const tinybvh::bvhdbl3 & bmin, const tinybvh::bvhdbl3 & bmax) + { + const double dx = (p.x < bmin.x) ? (bmin.x - p.x) : ((p.x > bmax.x) ? (p.x - bmax.x) : 0.0); + const double dy = (p.y < bmin.y) ? (bmin.y - p.y) : ((p.y > bmax.y) ? (p.y - bmax.y) : 0.0); + const double dz = (p.z < bmin.z) ? (bmin.z - p.z) : ((p.z > bmax.z) ? (p.z - bmax.z) : 0.0); + return dx * dx + dy * dy + dz * dz; + } + + double unsignedDistanceToMesh(const sDistanceMesh & mesh, const sBooleanVec3d & p) + { + if (mesh.triangles.empty() || !mesh.pBVH || mesh.pBVH->usedNodes == 0 || mesh.pBVH->bvhNode == nullptr) + return std::numeric_limits::infinity(); + + double minDist2 = std::numeric_limits::infinity(); + + if (mesh.pCentroidKDTree) { + constexpr size_t kSeeds = 4; + const size_t k = std::min(kSeeds, mesh.triangles.size()); + std::array seedIndices{}; + std::array seedDists{}; + nanoflann::KNNResultSet resultSet(k); + resultSet.init(seedIndices.data(), seedDists.data()); + const double query[3] = { p.x, p.y, p.z }; + mesh.pCentroidKDTree->findNeighbors(resultSet, query, nanoflann::SearchParameters(16)); + + for (size_t i = 0; i < k; ++i) { + const auto triId = seedIndices[i]; + const auto & tri = mesh.triangles[triId]; + const auto & av = mesh.vertices[tri[0]]; + const auto & bv = mesh.vertices[tri[1]]; + const auto & cv = mesh.vertices[tri[2]]; + const sBooleanVec3d a{ av[0], av[1], av[2] }; + const sBooleanVec3d b{ bv[0], bv[1], bv[2] }; + const sBooleanVec3d c{ cv[0], cv[1], cv[2] }; + minDist2 = std::min(minDist2, pointTriangleDistanceSquared(p, a, b, c)); + } + } + + std::vector> stack; + stack.reserve(256); + + uint64_t nodeIdx = 0; + double nodeDist2 = pointAABBDistanceSquared(p, mesh.pBVH->bvhNode[0].aabbMin, mesh.pBVH->bvhNode[0].aabbMax); + + while (true) { + if (nodeDist2 < minDist2) { + const auto & node = mesh.pBVH->bvhNode[nodeIdx]; + if (node.isLeaf()) { + for (uint64_t triOffset = 0; triOffset < node.triCount; ++triOffset) { + const auto triId = mesh.pBVH->primIdx[node.leftFirst + triOffset]; + const auto & tri = mesh.triangles[triId]; + const auto & av = mesh.vertices[tri[0]]; + const auto & bv = mesh.vertices[tri[1]]; + const auto & cv = mesh.vertices[tri[2]]; + const sBooleanVec3d a{ av[0], av[1], av[2] }; + const sBooleanVec3d b{ bv[0], bv[1], bv[2] }; + const sBooleanVec3d c{ cv[0], cv[1], cv[2] }; + minDist2 = std::min(minDist2, pointTriangleDistanceSquared(p, a, b, c)); + } + } + else { + const uint64_t leftIdx = node.leftFirst; + const uint64_t rightIdx = node.leftFirst + 1; + + const auto & leftNode = mesh.pBVH->bvhNode[leftIdx]; + const auto & rightNode = mesh.pBVH->bvhNode[rightIdx]; + + const double leftDist2 = pointAABBDistanceSquared(p, leftNode.aabbMin, leftNode.aabbMax); + const double rightDist2 = pointAABBDistanceSquared(p, rightNode.aabbMin, rightNode.aabbMax); + const uint64_t nearIdx = (leftDist2 <= rightDist2) ? leftIdx : rightIdx; + const uint64_t farIdx = (leftDist2 <= rightDist2) ? rightIdx : leftIdx; + const double nearDist2 = (leftDist2 <= rightDist2) ? leftDist2 : rightDist2; + const double farDist2 = (leftDist2 <= rightDist2) ? rightDist2 : leftDist2; + + if (farDist2 < minDist2) + stack.emplace_back(farIdx, farDist2); + + if (nearDist2 < minDist2) { + nodeIdx = nearIdx; + nodeDist2 = nearDist2; + continue; + } + } + } + + if (stack.empty()) + break; + nodeIdx = stack.back().first; + nodeDist2 = stack.back().second; + stack.pop_back(); + } + + return std::sqrt(std::max(minDist2, 0.0)); + } + + double signedDistanceField( + _In_ const tg::windingNumber & winding, + _In_ const sDistanceMesh & distanceMesh, + _In_ const sBooleanVec3d & point) + { + const auto w = winding.query({ point.x, point.y, point.z }); + const double sign = (std::abs(w) >= 0.5) ? -1.0 : 1.0; + return sign * unsignedDistanceToMesh(distanceMesh, point); + } + + void computeBounds(_In_ const std::vector & meshes, _Out_ sBooleanVec3d & minCorner, _Out_ sBooleanVec3d & maxCorner) + { + minCorner = { 1e300, 1e300, 1e300 }; + maxCorner = { -1e300, -1e300, -1e300 }; + bool hasAnyNode = false; + + for (auto * pMesh : meshes) { + if (!pMesh) + continue; + + for (nfUint32 nNodeIdx = 0; nNodeIdx < pMesh->getNodeCount(); ++nNodeIdx) { + const auto * pNode = pMesh->getNode(nNodeIdx); + hasAnyNode = true; + minCorner.x = std::min(minCorner.x, static_cast(pNode->m_position.m_fields[0])); + minCorner.y = std::min(minCorner.y, static_cast(pNode->m_position.m_fields[1])); + minCorner.z = std::min(minCorner.z, static_cast(pNode->m_position.m_fields[2])); + maxCorner.x = std::max(maxCorner.x, static_cast(pNode->m_position.m_fields[0])); + maxCorner.y = std::max(maxCorner.y, static_cast(pNode->m_position.m_fields[1])); + maxCorner.z = std::max(maxCorner.z, static_cast(pNode->m_position.m_fields[2])); + } + } + + if (!hasAnyNode) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + + const sBooleanVec3d size = maxCorner - minCorner; + const double padding = std::max({ size.x, size.y, size.z, 1e-3 }) * 0.05; + minCorner.x -= padding; minCorner.y -= padding; minCorner.z -= padding; + maxCorner.x += padding; maxCorner.y += padding; maxCorner.z += padding; + } + } + + sBooleanFieldData buildCSGField( + _In_ CMesh * pBaseMesh, + _In_ const std::vector & operandMeshes, + _In_ eModelBooleanOperation operation, + _In_ nfUint32 nGridResolution) + { + if (!pBaseMesh || operandMeshes.empty()) + throw CNMRException(NMR_ERROR_INVALIDPARAM); + + auto baseWinding = makeWindingMesh(pBaseMesh); + auto baseDistance = makeDistanceMesh(pBaseMesh); + std::vector operandWindings; + std::vector operandDistances; + operandWindings.reserve(operandMeshes.size()); + operandDistances.reserve(operandMeshes.size()); + for (const auto & operandMesh : operandMeshes) { + operandWindings.push_back(makeWindingMesh(operandMesh.get())); + operandDistances.push_back(makeDistanceMesh(operandMesh.get())); + } + + std::vector allMeshes; + allMeshes.reserve(operandMeshes.size() + 1); + allMeshes.push_back(pBaseMesh); + for (const auto & operandMesh : operandMeshes) + allMeshes.push_back(operandMesh.get()); + + sBooleanFieldData fieldData; + computeBounds(allMeshes, fieldData.minCorner, fieldData.maxCorner); + if (nGridResolution < 2 || nGridResolution > static_cast(std::numeric_limits::max())) + throw CNMRException(NMR_ERROR_INVALIDPARAM); + fieldData.resolution = static_cast(nGridResolution); + fieldData.values.resize(static_cast(fieldData.resolution) * fieldData.resolution * fieldData.resolution); + + const auto idx = [resolution = fieldData.resolution](int x, int y, int z) -> size_t { + return static_cast(z) * resolution * resolution + + static_cast(y) * resolution + + static_cast(x); + }; + + const sBooleanVec3d step = { + (fieldData.maxCorner.x - fieldData.minCorner.x) / static_cast(fieldData.resolution - 1), + (fieldData.maxCorner.y - fieldData.minCorner.y) / static_cast(fieldData.resolution - 1), + (fieldData.maxCorner.z - fieldData.minCorner.z) / static_cast(fieldData.resolution - 1) + }; + + for (int z = 0; z < fieldData.resolution; ++z) { + for (int y = 0; y < fieldData.resolution; ++y) { + for (int x = 0; x < fieldData.resolution; ++x) { + const sBooleanVec3d p = { + fieldData.minCorner.x + x * step.x, + fieldData.minCorner.y + y * step.y, + fieldData.minCorner.z + z * step.z + }; + + double csgField = signedDistanceField(*baseWinding.winding, baseDistance, p); + for (size_t opIdx = 0; opIdx < operandWindings.size(); ++opIdx) { + const double operandField = signedDistanceField(*operandWindings[opIdx].winding, operandDistances[opIdx], p); + csgField = combineField(csgField, operandField, operation); + } + fieldData.values[idx(x, y, z)] = csgField; + } + } + } + + return fieldData; + } + +} diff --git a/Source/Common/Boolean/NMR_BooleanEngine.cpp b/Source/Common/Boolean/NMR_BooleanEngine.cpp new file mode 100644 index 000000000..2b76b2208 --- /dev/null +++ b/Source/Common/Boolean/NMR_BooleanEngine.cpp @@ -0,0 +1,62 @@ +/*++ + +Copyright (C) 2026 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +Winding-number based boolean field construction and CSG composition. + +--*/ + +#include "Common/Boolean/NMR_BooleanEngine.h" + +#include "Common/Boolean/NMR_BooleanDistanceField.h" +#include "Common/Boolean/NMR_BooleanSurfacePostProcess.h" +#include "Common/Boolean/NMR_MarchingCubes.h" +#include "Common/NMR_Exception.h" + +namespace NMR { + + void CBooleanEngine::evaluate( + _In_ CMesh * pBaseMesh, + _In_ const std::vector & operandMeshes, + _In_ eModelBooleanOperation operation, + _In_ CMesh * pResultMesh, + _In_ nfUint32 nGridResolution) + { + if (!pResultMesh) + throw CNMRException(NMR_ERROR_INVALIDPARAM); + + const auto fieldData = Boolean::buildCSGField(pBaseMesh, operandMeshes, operation, nGridResolution); + Boolean::extractIsoSurfaceMarchingCubes( + pResultMesh, + fieldData.values, + fieldData.resolution, + { fieldData.minCorner.x, fieldData.minCorner.y, fieldData.minCorner.z }, + { fieldData.maxCorner.x, fieldData.maxCorner.y, fieldData.maxCorner.z }); + Boolean::smoothAndProjectExtractedSurface(pResultMesh, fieldData); + } + +} diff --git a/Source/Common/Boolean/NMR_BooleanSurfacePostProcess.cpp b/Source/Common/Boolean/NMR_BooleanSurfacePostProcess.cpp new file mode 100644 index 000000000..329a34f51 --- /dev/null +++ b/Source/Common/Boolean/NMR_BooleanSurfacePostProcess.cpp @@ -0,0 +1,207 @@ +/*++ + +Copyright (C) 2026 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +Mesh post-processing helpers for extracted boolean surfaces. + +--*/ + +#include "Common/Boolean/NMR_BooleanSurfacePostProcess.h" + +#include +#include +#include +#include + +namespace NMR::Boolean { + + namespace { + sBooleanVec3d operator+(const sBooleanVec3d & a, const sBooleanVec3d & b) { return { a.x + b.x, a.y + b.y, a.z + b.z }; } + sBooleanVec3d operator-(const sBooleanVec3d & a, const sBooleanVec3d & b) { return { a.x - b.x, a.y - b.y, a.z - b.z }; } + sBooleanVec3d operator*(const sBooleanVec3d & a, const double s) { return { a.x * s, a.y * s, a.z * s }; } + + double sampleFieldTrilinear(_In_ const sBooleanFieldData & fieldData, _In_ const sBooleanVec3d & p) + { + const int resolution = fieldData.resolution; + const auto idx = [resolution](int x, int y, int z) -> size_t { + return static_cast(z) * resolution * resolution + + static_cast(y) * resolution + + static_cast(x); + }; + + const auto sampleCoord = [resolution](double t) -> std::pair { + const double scaled = std::clamp(t * static_cast(resolution - 1), 0.0, static_cast(resolution - 1)); + const int i0 = std::clamp(static_cast(std::floor(scaled)), 0, resolution - 2); + const double frac = scaled - static_cast(i0); + return { i0, frac }; + }; + + const double dx = std::max(fieldData.maxCorner.x - fieldData.minCorner.x, 1e-9); + const double dy = std::max(fieldData.maxCorner.y - fieldData.minCorner.y, 1e-9); + const double dz = std::max(fieldData.maxCorner.z - fieldData.minCorner.z, 1e-9); + const auto [ix, tx] = sampleCoord((p.x - fieldData.minCorner.x) / dx); + const auto [iy, ty] = sampleCoord((p.y - fieldData.minCorner.y) / dy); + const auto [iz, tz] = sampleCoord((p.z - fieldData.minCorner.z) / dz); + + const double c000 = fieldData.values[idx(ix, iy, iz)]; + const double c100 = fieldData.values[idx(ix + 1, iy, iz)]; + const double c010 = fieldData.values[idx(ix, iy + 1, iz)]; + const double c110 = fieldData.values[idx(ix + 1, iy + 1, iz)]; + const double c001 = fieldData.values[idx(ix, iy, iz + 1)]; + const double c101 = fieldData.values[idx(ix + 1, iy, iz + 1)]; + const double c011 = fieldData.values[idx(ix, iy + 1, iz + 1)]; + const double c111 = fieldData.values[idx(ix + 1, iy + 1, iz + 1)]; + + const double c00 = c000 * (1.0 - tx) + c100 * tx; + const double c10 = c010 * (1.0 - tx) + c110 * tx; + const double c01 = c001 * (1.0 - tx) + c101 * tx; + const double c11 = c011 * (1.0 - tx) + c111 * tx; + const double c0 = c00 * (1.0 - ty) + c10 * ty; + const double c1 = c01 * (1.0 - ty) + c11 * ty; + return c0 * (1.0 - tz) + c1 * tz; + } + } + + void smoothAndProjectExtractedSurface( + _In_ CMesh * pMesh, + _In_ const sBooleanFieldData & fieldData) + { + if (!pMesh) + return; + + const int resolution = fieldData.resolution; + if (resolution < 3 || fieldData.values.empty()) + return; + + const nfUint32 nNodeCount = pMesh->getNodeCount(); + if (nNodeCount < 3) + return; + + std::vector> neighbors(static_cast(nNodeCount)); + const nfUint32 nFaceCount = pMesh->getFaceCount(); + for (nfUint32 f = 0; f < nFaceCount; ++f) { + const auto * pFace = pMesh->getFace(f); + const nfUint32 a = static_cast(pFace->m_nodeindices[0]); + const nfUint32 b = static_cast(pFace->m_nodeindices[1]); + const nfUint32 c = static_cast(pFace->m_nodeindices[2]); + if (a == b || b == c || c == a) + continue; + neighbors[a].push_back(b); neighbors[a].push_back(c); + neighbors[b].push_back(a); neighbors[b].push_back(c); + neighbors[c].push_back(a); neighbors[c].push_back(b); + } + + for (auto & nn : neighbors) { + std::sort(nn.begin(), nn.end()); + nn.erase(std::unique(nn.begin(), nn.end()), nn.end()); + } + + std::vector positions(static_cast(nNodeCount)); + for (nfUint32 i = 0; i < nNodeCount; ++i) { + const auto * pNode = pMesh->getNode(i); + positions[i] = { + static_cast(pNode->m_position.m_fields[0]), + static_cast(pNode->m_position.m_fields[1]), + static_cast(pNode->m_position.m_fields[2]) + }; + } + + auto laplacianStep = [&](double weight) { + std::vector updated = positions; + for (nfUint32 i = 0; i < nNodeCount; ++i) { + const auto & nn = neighbors[i]; + if (nn.empty()) + continue; + sBooleanVec3d avg{ 0.0, 0.0, 0.0 }; + for (const auto n : nn) + avg = avg + positions[n]; + const double invCount = 1.0 / static_cast(nn.size()); + avg = avg * invCount; + const sBooleanVec3d lap = avg - positions[i]; + updated[i] = positions[i] + lap * weight; + } + positions.swap(updated); + }; + + constexpr int smoothingIterations = 3; + constexpr double lambda = 0.33; + constexpr double mu = -0.34; + for (int iter = 0; iter < smoothingIterations; ++iter) { + laplacianStep(lambda); + laplacianStep(mu); + } + + const sBooleanVec3d cellStep = { + (fieldData.maxCorner.x - fieldData.minCorner.x) / static_cast(resolution - 1), + (fieldData.maxCorner.y - fieldData.minCorner.y) / static_cast(resolution - 1), + (fieldData.maxCorner.z - fieldData.minCorner.z) / static_cast(resolution - 1) + }; + const sBooleanVec3d gradEps = { + std::max(0.5 * cellStep.x, 1e-6), + std::max(0.5 * cellStep.y, 1e-6), + std::max(0.5 * cellStep.z, 1e-6) + }; + + constexpr int projectionIterations = 2; + for (int iter = 0; iter < projectionIterations; ++iter) { + for (nfUint32 i = 0; i < nNodeCount; ++i) { + sBooleanVec3d p = positions[i]; + const double f = sampleFieldTrilinear(fieldData, p); + if (std::abs(f) < 1e-5) + continue; + + const sBooleanVec3d pxp{ p.x + gradEps.x, p.y, p.z }; + const sBooleanVec3d pxm{ p.x - gradEps.x, p.y, p.z }; + const sBooleanVec3d pyp{ p.x, p.y + gradEps.y, p.z }; + const sBooleanVec3d pym{ p.x, p.y - gradEps.y, p.z }; + const sBooleanVec3d pzp{ p.x, p.y, p.z + gradEps.z }; + const sBooleanVec3d pzm{ p.x, p.y, p.z - gradEps.z }; + + const sBooleanVec3d grad{ + (sampleFieldTrilinear(fieldData, pxp) - sampleFieldTrilinear(fieldData, pxm)) / (2.0 * gradEps.x), + (sampleFieldTrilinear(fieldData, pyp) - sampleFieldTrilinear(fieldData, pym)) / (2.0 * gradEps.y), + (sampleFieldTrilinear(fieldData, pzp) - sampleFieldTrilinear(fieldData, pzm)) / (2.0 * gradEps.z) + }; + + const double gradNorm2 = grad.x * grad.x + grad.y * grad.y + grad.z * grad.z; + if (gradNorm2 < 1e-12) + continue; + + const double stepScale = std::clamp(f / gradNorm2, -2.0, 2.0); + positions[i] = p - grad * stepScale; + } + } + + for (nfUint32 i = 0; i < nNodeCount; ++i) { + auto * pNode = pMesh->getNode(i); + pNode->m_position.m_fields[0] = static_cast(positions[i].x); + pNode->m_position.m_fields[1] = static_cast(positions[i].y); + pNode->m_position.m_fields[2] = static_cast(positions[i].z); + } + } + +} diff --git a/Source/Common/Boolean/NMR_MarchingCubes.cpp b/Source/Common/Boolean/NMR_MarchingCubes.cpp new file mode 100644 index 000000000..a1140f522 --- /dev/null +++ b/Source/Common/Boolean/NMR_MarchingCubes.cpp @@ -0,0 +1,221 @@ +/*++ + +Copyright (C) 2026 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +Marching-cubes iso-surface extraction for scalar fields. + +--*/ + +#include "Common/Boolean/NMR_MarchingCubes.h" + +#include "Common/NMR_Exception.h" + +#include + +namespace NMR::Boolean { + + namespace { + std::array interpolateIso0(const std::array & p0, const std::array & p1, double v0, double v1) + { + const double t = (v0 == v1) ? 0.5 : (v0 / (v0 - v1)); + return { + p0[0] + (p1[0] - p0[0]) * t, + p0[1] + (p1[1] - p0[1]) * t, + p0[2] + (p1[2] - p0[2]) * t + }; + } + + static const int kMcEdgeTable[256] = { + 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc, 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 + }; + +#include "NMR_MarchingCubesTriTable.inc" + } + + void extractIsoSurfaceMarchingCubes( + _In_ CMesh * pResultMesh, + _In_ const std::vector & fieldValues, + _In_ int resolution, + _In_ const std::array & minCorner, + _In_ const std::array & maxCorner) + { + if (!pResultMesh || resolution < 2) + throw CNMRException(NMR_ERROR_INVALIDPARAM); + + const int nx = resolution - 1; + const int ny = resolution - 1; + const int nz = resolution - 1; + const int vnx = resolution; + const int vny = resolution; + const int vnz = resolution; + + const std::array delta = { + (maxCorner[0] - minCorner[0]) / static_cast(resolution - 1), + (maxCorner[1] - minCorner[1]) / static_cast(resolution - 1), + (maxCorner[2] - minCorner[2]) / static_cast(resolution - 1) + }; + + auto valueIndex = [vnx, vny](int x, int y, int z) -> size_t { + return static_cast(x) + + static_cast(vnx) * (static_cast(y) + static_cast(vny) * static_cast(z)); + }; + + const int cornerOffset[8][3] = { + {0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}, + {0, 0, 1}, {1, 0, 1}, {1, 1, 1}, {0, 1, 1} + }; + const int edgeToCorners[12][2] = { + {0, 1}, {1, 2}, {2, 3}, {3, 0}, + {4, 5}, {5, 6}, {6, 7}, {7, 4}, + {0, 4}, {1, 5}, {2, 6}, {3, 7} + }; + + const size_t xEdgeCount = static_cast(nx) * static_cast(vny) * static_cast(vnz); + const size_t yEdgeCount = static_cast(vnx) * static_cast(ny) * static_cast(vnz); + const size_t zEdgeCount = static_cast(vnx) * static_cast(vny) * static_cast(nz); + std::vector xEdge(xEdgeCount, -1); + std::vector yEdge(yEdgeCount, -1); + std::vector zEdge(zEdgeCount, -1); + std::vector meshNodes; + + auto xEdgeIndex = [nx, vny](int ex, int ey, int ez) -> size_t { + return static_cast(ex) + + static_cast(nx) * (static_cast(ey) + static_cast(vny) * static_cast(ez)); + }; + auto yEdgeIndex = [vnx, ny](int ex, int ey, int ez) -> size_t { + return static_cast(ex) + + static_cast(vnx) * (static_cast(ey) + static_cast(ny) * static_cast(ez)); + }; + auto zEdgeIndex = [vnx, vny](int ex, int ey, int ez) -> size_t { + return static_cast(ex) + + static_cast(vnx) * (static_cast(ey) + static_cast(vny) * static_cast(ez)); + }; + + auto cornerValue = [&](int cx, int cy, int cz, int corner) -> double { + const int gx = cx + cornerOffset[corner][0]; + const int gy = cy + cornerOffset[corner][1]; + const int gz = cz + cornerOffset[corner][2]; + return fieldValues[valueIndex(gx, gy, gz)]; + }; + + auto cornerPosition = [&](int cx, int cy, int cz, int corner) -> std::array { + const int gx = cx + cornerOffset[corner][0]; + const int gy = cy + cornerOffset[corner][1]; + const int gz = cz + cornerOffset[corner][2]; + return { + minCorner[0] + static_cast(gx) * delta[0], + minCorner[1] + static_cast(gy) * delta[1], + minCorner[2] + static_cast(gz) * delta[2] + }; + }; + + auto edgeVertex = [&](int cx, int cy, int cz, int edge) -> int { + int * pSlot = nullptr; + switch (edge) { + case 0: pSlot = &xEdge[xEdgeIndex(cx, cy, cz)]; break; + case 2: pSlot = &xEdge[xEdgeIndex(cx, cy + 1, cz)]; break; + case 4: pSlot = &xEdge[xEdgeIndex(cx, cy, cz + 1)]; break; + case 6: pSlot = &xEdge[xEdgeIndex(cx, cy + 1, cz + 1)]; break; + case 3: pSlot = &yEdge[yEdgeIndex(cx, cy, cz)]; break; + case 1: pSlot = &yEdge[yEdgeIndex(cx + 1, cy, cz)]; break; + case 7: pSlot = &yEdge[yEdgeIndex(cx, cy, cz + 1)]; break; + case 5: pSlot = &yEdge[yEdgeIndex(cx + 1, cy, cz + 1)]; break; + case 8: pSlot = &zEdge[zEdgeIndex(cx, cy, cz)]; break; + case 9: pSlot = &zEdge[zEdgeIndex(cx + 1, cy, cz)]; break; + case 11: pSlot = &zEdge[zEdgeIndex(cx, cy + 1, cz)]; break; + case 10: pSlot = &zEdge[zEdgeIndex(cx + 1, cy + 1, cz)]; break; + default: break; + } + + if (pSlot && *pSlot >= 0) + return *pSlot; + + const int c0 = edgeToCorners[edge][0]; + const int c1 = edgeToCorners[edge][1]; + const auto p0 = cornerPosition(cx, cy, cz, c0); + const auto p1 = cornerPosition(cx, cy, cz, c1); + const double v0 = cornerValue(cx, cy, cz, c0); + const double v1 = cornerValue(cx, cy, cz, c1); + const auto p = interpolateIso0(p0, p1, v0, v1); + + auto * pNode = pResultMesh->addNode(static_cast(p[0]), static_cast(p[1]), static_cast(p[2])); + const int nNodeIdx = static_cast(meshNodes.size()); + meshNodes.push_back(pNode); + if (pSlot) + *pSlot = nNodeIdx; + return nNodeIdx; + }; + + for (int cz = 0; cz < nz; ++cz) { + for (int cy = 0; cy < ny; ++cy) { + for (int cx = 0; cx < nx; ++cx) { + int cubeIndex = 0; + for (int corner = 0; corner < 8; ++corner) { + if (cornerValue(cx, cy, cz, corner) >= 0.0) + cubeIndex |= (1 << corner); + } + + const int edgeMask = kMcEdgeTable[cubeIndex]; + if (edgeMask == 0) + continue; + + int edgeNodes[12] = { 0 }; + for (int edge = 0; edge < 12; ++edge) { + if (edgeMask & (1 << edge)) + edgeNodes[edge] = edgeVertex(cx, cy, cz, edge); + } + + const int * pTriangles = kMcTriTable[cubeIndex]; + for (int t = 0; pTriangles[t] != -1; t += 3) { + const int ia = edgeNodes[pTriangles[t]]; + const int ib = edgeNodes[pTriangles[t + 1]]; + const int ic = edgeNodes[pTriangles[t + 2]]; + if (ia == ib || ib == ic || ic == ia) + continue; + pResultMesh->addFace(meshNodes[ia], meshNodes[ib], meshNodes[ic]); + } + } + } + } + } + +} diff --git a/Source/Common/Boolean/NMR_MarchingCubesTriTable.inc b/Source/Common/Boolean/NMR_MarchingCubesTriTable.inc new file mode 100644 index 000000000..0ab1f3058 --- /dev/null +++ b/Source/Common/Boolean/NMR_MarchingCubesTriTable.inc @@ -0,0 +1,292 @@ +/*++ + +Copyright (C) 2026 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +Marching-cubes triangle table used by boolean surface extraction. + +--*/ + +// clang-format off +static const int kMcTriTable[256][16] = { +{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,8,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,1,9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{1,8,3,9,8,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{1,2,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,8,3,1,2,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{9,2,10,0,2,9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{2,8,3,2,10,8,10,9,8,-1,-1,-1,-1,-1,-1,-1}, +{3,11,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,11,2,8,11,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{1,9,0,2,3,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{1,11,2,1,9,11,9,8,11,-1,-1,-1,-1,-1,-1,-1}, +{3,10,1,11,10,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,10,1,0,8,10,8,11,10,-1,-1,-1,-1,-1,-1,-1}, +{3,9,0,3,11,9,11,10,9,-1,-1,-1,-1,-1,-1,-1}, +{9,8,10,10,8,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{4,7,8,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{4,3,0,7,3,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,1,9,8,4,7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{4,1,9,4,7,1,7,3,1,-1,-1,-1,-1,-1,-1,-1}, +{1,2,10,8,4,7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{3,4,7,3,0,4,1,2,10,-1,-1,-1,-1,-1,-1,-1}, +{9,2,10,9,0,2,8,4,7,-1,-1,-1,-1,-1,-1,-1}, +{2,10,9,2,9,7,2,7,3,7,9,4,-1,-1,-1,-1}, +{8,4,7,3,11,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{11,4,7,11,2,4,2,0,4,-1,-1,-1,-1,-1,-1,-1}, +{9,0,1,8,4,7,2,3,11,-1,-1,-1,-1,-1,-1,-1}, +{4,7,11,9,4,11,9,11,2,9,2,1,-1,-1,-1,-1}, +{3,10,1,3,11,10,7,8,4,-1,-1,-1,-1,-1,-1,-1}, +{1,11,10,1,4,11,1,0,4,7,11,4,-1,-1,-1,-1}, +{4,7,8,9,0,11,9,11,10,11,0,3,-1,-1,-1,-1}, +{4,7,11,4,11,9,9,11,10,-1,-1,-1,-1,-1,-1,-1}, +{9,5,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{9,5,4,0,8,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,5,4,1,5,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{8,5,4,8,3,5,3,1,5,-1,-1,-1,-1,-1,-1,-1}, +{1,2,10,9,5,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{3,0,8,1,2,10,4,9,5,-1,-1,-1,-1,-1,-1,-1}, +{5,2,10,5,4,2,4,0,2,-1,-1,-1,-1,-1,-1,-1}, +{2,10,5,3,2,5,3,5,4,3,4,8,-1,-1,-1,-1}, +{9,5,4,2,3,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,11,2,0,8,11,4,9,5,-1,-1,-1,-1,-1,-1,-1}, +{0,5,4,0,1,5,2,3,11,-1,-1,-1,-1,-1,-1,-1}, +{2,1,5,2,5,8,2,8,11,4,8,5,-1,-1,-1,-1}, +{10,3,11,10,1,3,9,5,4,-1,-1,-1,-1,-1,-1,-1}, +{4,9,5,0,8,1,8,10,1,8,11,10,-1,-1,-1,-1}, +{5,4,0,5,0,11,5,11,10,11,0,3,-1,-1,-1,-1}, +{5,4,8,5,8,10,10,8,11,-1,-1,-1,-1,-1,-1,-1}, +{9,7,8,5,7,9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{9,3,0,9,5,3,5,7,3,-1,-1,-1,-1,-1,-1,-1}, +{0,7,8,0,1,7,1,5,7,-1,-1,-1,-1,-1,-1,-1}, +{1,5,3,3,5,7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{9,7,8,9,5,7,10,1,2,-1,-1,-1,-1,-1,-1,-1}, +{10,1,2,9,5,0,5,3,0,5,7,3,-1,-1,-1,-1}, +{8,0,2,8,2,5,8,5,7,10,5,2,-1,-1,-1,-1}, +{2,10,5,2,5,3,3,5,7,-1,-1,-1,-1,-1,-1,-1}, +{7,9,5,7,8,9,3,11,2,-1,-1,-1,-1,-1,-1,-1}, +{9,5,7,9,7,2,9,2,0,2,7,11,-1,-1,-1,-1}, +{2,3,11,0,1,8,1,7,8,1,5,7,-1,-1,-1,-1}, +{11,2,1,11,1,7,7,1,5,-1,-1,-1,-1,-1,-1,-1}, +{9,5,8,8,5,7,10,1,3,10,3,11,-1,-1,-1,-1}, +{5,7,0,5,0,9,7,11,0,1,0,10,11,10,0,-1}, +{11,10,0,11,0,3,10,5,0,8,0,7,5,7,0,-1}, +{11,10,5,7,11,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{10,6,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,8,3,5,10,6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{9,0,1,5,10,6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{1,8,3,1,9,8,5,10,6,-1,-1,-1,-1,-1,-1,-1}, +{1,6,5,2,6,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{1,6,5,1,2,6,3,0,8,-1,-1,-1,-1,-1,-1,-1}, +{9,6,5,9,0,6,0,2,6,-1,-1,-1,-1,-1,-1,-1}, +{5,9,8,5,8,2,5,2,6,3,2,8,-1,-1,-1,-1}, +{2,3,11,10,6,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{11,0,8,11,2,0,10,6,5,-1,-1,-1,-1,-1,-1,-1}, +{0,1,9,2,3,11,5,10,6,-1,-1,-1,-1,-1,-1,-1}, +{5,10,6,1,9,2,9,11,2,9,8,11,-1,-1,-1,-1}, +{6,3,11,6,5,3,5,1,3,-1,-1,-1,-1,-1,-1,-1}, +{0,8,11,0,11,5,0,5,1,5,11,6,-1,-1,-1,-1}, +{3,11,6,0,3,6,0,6,5,0,5,9,-1,-1,-1,-1}, +{6,5,9,6,9,11,11,9,8,-1,-1,-1,-1,-1,-1,-1}, +{5,10,6,4,7,8,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{4,3,0,4,7,3,6,5,10,-1,-1,-1,-1,-1,-1,-1}, +{1,9,0,5,10,6,8,4,7,-1,-1,-1,-1,-1,-1,-1}, +{10,6,5,1,9,7,1,7,3,7,9,4,-1,-1,-1,-1}, +{6,1,2,6,5,1,4,7,8,-1,-1,-1,-1,-1,-1,-1}, +{1,2,5,5,2,6,3,0,4,3,4,7,-1,-1,-1,-1}, +{8,4,7,9,0,5,0,6,5,0,2,6,-1,-1,-1,-1}, +{7,3,9,7,9,4,3,2,9,5,9,6,2,6,9,-1}, +{3,11,2,7,8,4,10,6,5,-1,-1,-1,-1,-1,-1,-1}, +{5,10,6,4,7,2,4,2,0,2,7,11,-1,-1,-1,-1}, +{0,1,9,4,7,8,2,3,11,5,10,6,-1,-1,-1,-1}, +{9,2,1,9,11,2,9,4,11,7,11,4,5,10,6,-1}, +{8,4,7,3,11,5,3,5,1,5,11,6,-1,-1,-1,-1}, +{5,1,11,5,11,6,1,0,11,7,11,4,0,4,11,-1}, +{0,5,9,0,6,5,0,3,6,11,6,3,8,4,7,-1}, +{6,5,9,6,9,11,4,7,9,7,11,9,-1,-1,-1,-1}, +{10,4,9,6,4,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{4,10,6,4,9,10,0,8,3,-1,-1,-1,-1,-1,-1,-1}, +{10,0,1,10,6,0,6,4,0,-1,-1,-1,-1,-1,-1,-1}, +{8,3,1,8,1,6,8,6,4,6,1,10,-1,-1,-1,-1}, +{1,4,9,1,2,4,2,6,4,-1,-1,-1,-1,-1,-1,-1}, +{3,0,8,1,2,9,2,4,9,2,6,4,-1,-1,-1,-1}, +{0,2,4,4,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{8,3,2,8,2,4,4,2,6,-1,-1,-1,-1,-1,-1,-1}, +{10,4,9,10,6,4,11,2,3,-1,-1,-1,-1,-1,-1,-1}, +{0,8,2,2,8,11,4,9,10,4,10,6,-1,-1,-1,-1}, +{3,11,2,0,1,6,0,6,4,6,1,10,-1,-1,-1,-1}, +{6,4,1,6,1,10,4,8,1,2,1,11,8,11,1,-1}, +{9,6,4,9,3,6,9,1,3,11,6,3,-1,-1,-1,-1}, +{8,11,1,8,1,0,11,6,1,9,1,4,6,4,1,-1}, +{3,11,6,3,6,0,0,6,4,-1,-1,-1,-1,-1,-1,-1}, +{6,4,8,11,6,8,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{7,10,6,7,8,10,8,9,10,-1,-1,-1,-1,-1,-1,-1}, +{0,7,3,0,10,7,0,9,10,6,7,10,-1,-1,-1,-1}, +{10,6,7,1,10,7,1,7,8,1,8,0,-1,-1,-1,-1}, +{10,6,7,10,7,1,1,7,3,-1,-1,-1,-1,-1,-1,-1}, +{1,2,6,1,6,8,1,8,9,8,6,7,-1,-1,-1,-1}, +{2,6,9,2,9,1,6,7,9,0,9,3,7,3,9,-1}, +{7,8,0,7,0,6,6,0,2,-1,-1,-1,-1,-1,-1,-1}, +{7,3,2,6,7,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{2,3,11,10,6,8,10,8,9,8,6,7,-1,-1,-1,-1}, +{2,0,7,2,7,11,0,9,7,6,7,10,9,10,7,-1}, +{1,8,0,1,7,8,1,10,7,6,7,10,2,3,11,-1}, +{11,2,1,11,1,7,10,6,1,6,7,1,-1,-1,-1,-1}, +{8,9,6,8,6,7,9,1,6,11,6,3,1,3,6,-1}, +{0,9,1,11,6,7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{7,8,0,7,0,6,3,11,0,11,6,0,-1,-1,-1,-1}, +{7,11,6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{7,6,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{3,0,8,11,7,6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,1,9,11,7,6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{8,1,9,8,3,1,11,7,6,-1,-1,-1,-1,-1,-1,-1}, +{10,1,2,6,11,7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{1,2,10,3,0,8,6,11,7,-1,-1,-1,-1,-1,-1,-1}, +{2,9,0,2,10,9,6,11,7,-1,-1,-1,-1,-1,-1,-1}, +{6,11,7,2,10,3,10,8,3,10,9,8,-1,-1,-1,-1}, +{7,2,3,6,2,7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{7,0,8,7,6,0,6,2,0,-1,-1,-1,-1,-1,-1,-1}, +{2,7,6,2,3,7,0,1,9,-1,-1,-1,-1,-1,-1,-1}, +{1,6,2,1,8,6,1,9,8,8,7,6,-1,-1,-1,-1}, +{10,7,6,10,1,7,1,3,7,-1,-1,-1,-1,-1,-1,-1}, +{10,7,6,1,7,10,1,8,7,1,0,8,-1,-1,-1,-1}, +{0,3,7,0,7,10,0,10,9,6,10,7,-1,-1,-1,-1}, +{7,6,10,7,10,8,8,10,9,-1,-1,-1,-1,-1,-1,-1}, +{6,8,4,11,8,6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{3,6,11,3,0,6,0,4,6,-1,-1,-1,-1,-1,-1,-1}, +{8,6,11,8,4,6,9,0,1,-1,-1,-1,-1,-1,-1,-1}, +{9,4,6,9,6,3,9,3,1,11,3,6,-1,-1,-1,-1}, +{6,8,4,6,11,8,2,10,1,-1,-1,-1,-1,-1,-1,-1}, +{1,2,10,3,0,11,0,6,11,0,4,6,-1,-1,-1,-1}, +{4,11,8,4,6,11,0,2,9,2,10,9,-1,-1,-1,-1}, +{10,9,3,10,3,2,9,4,3,11,3,6,4,6,3,-1}, +{8,2,3,8,4,2,4,6,2,-1,-1,-1,-1,-1,-1,-1}, +{0,4,2,4,6,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{1,9,0,2,3,4,2,4,6,4,3,8,-1,-1,-1,-1}, +{1,9,4,1,4,2,2,4,6,-1,-1,-1,-1,-1,-1,-1}, +{8,1,3,8,6,1,8,4,6,6,10,1,-1,-1,-1,-1}, +{10,1,0,10,0,6,6,0,4,-1,-1,-1,-1,-1,-1,-1}, +{4,6,3,4,3,8,6,10,3,0,3,9,10,9,3,-1}, +{10,9,4,6,10,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{4,9,5,7,6,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,8,3,4,9,5,11,7,6,-1,-1,-1,-1,-1,-1,-1}, +{5,0,1,5,4,0,7,6,11,-1,-1,-1,-1,-1,-1,-1}, +{11,7,6,8,3,4,3,5,4,3,1,5,-1,-1,-1,-1}, +{9,5,4,10,1,2,7,6,11,-1,-1,-1,-1,-1,-1,-1}, +{6,11,7,1,2,10,0,8,3,4,9,5,-1,-1,-1,-1}, +{7,6,11,5,4,10,4,2,10,4,0,2,-1,-1,-1,-1}, +{3,4,8,3,5,4,3,2,5,10,5,2,11,7,6,-1}, +{7,2,3,7,6,2,5,4,9,-1,-1,-1,-1,-1,-1,-1}, +{9,5,4,0,8,6,0,6,2,6,8,7,-1,-1,-1,-1}, +{3,6,2,3,7,6,1,5,0,5,4,0,-1,-1,-1,-1}, +{6,2,8,6,8,7,2,1,8,4,8,5,1,5,8,-1}, +{9,5,4,10,1,6,1,7,6,1,3,7,-1,-1,-1,-1}, +{1,6,10,1,7,6,1,0,7,8,7,0,9,5,4,-1}, +{4,0,10,4,10,5,0,3,10,6,10,7,3,7,10,-1}, +{7,6,10,7,10,8,5,4,10,4,8,10,-1,-1,-1,-1}, +{6,9,5,6,11,9,11,8,9,-1,-1,-1,-1,-1,-1,-1}, +{3,6,11,0,6,3,0,5,6,0,9,5,-1,-1,-1,-1}, +{0,11,8,0,5,11,0,1,5,5,6,11,-1,-1,-1,-1}, +{6,11,3,6,3,5,5,3,1,-1,-1,-1,-1,-1,-1,-1}, +{1,2,10,9,5,11,9,11,8,11,5,6,-1,-1,-1,-1}, +{0,11,3,0,6,11,0,9,6,5,6,9,1,2,10,-1}, +{11,8,5,11,5,6,8,0,5,10,5,2,0,2,5,-1}, +{6,11,3,6,3,5,2,10,3,10,5,3,-1,-1,-1,-1}, +{5,8,9,5,2,8,5,6,2,3,8,2,-1,-1,-1,-1}, +{9,5,6,9,6,0,0,6,2,-1,-1,-1,-1,-1,-1,-1}, +{1,5,8,1,8,0,5,6,8,3,8,2,6,2,8,-1}, +{1,5,6,2,1,6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{1,3,6,1,6,10,3,8,6,5,6,9,8,9,6,-1}, +{10,1,0,10,0,6,9,5,0,5,6,0,-1,-1,-1,-1}, +{0,3,8,5,6,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{10,5,6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{11,5,10,7,5,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{11,5,10,11,7,5,8,3,0,-1,-1,-1,-1,-1,-1,-1}, +{5,11,7,5,10,11,1,9,0,-1,-1,-1,-1,-1,-1,-1}, +{10,7,5,10,11,7,9,8,1,8,3,1,-1,-1,-1,-1}, +{11,1,2,11,7,1,7,5,1,-1,-1,-1,-1,-1,-1,-1}, +{0,8,3,1,2,7,1,7,5,7,2,11,-1,-1,-1,-1}, +{9,7,5,9,2,7,9,0,2,2,11,7,-1,-1,-1,-1}, +{7,5,2,7,2,11,5,9,2,3,2,8,9,8,2,-1}, +{2,5,10,2,3,5,3,7,5,-1,-1,-1,-1,-1,-1,-1}, +{8,2,0,8,5,2,8,7,5,10,2,5,-1,-1,-1,-1}, +{9,0,1,5,10,3,5,3,7,3,10,2,-1,-1,-1,-1}, +{9,8,2,9,2,1,8,7,2,10,2,5,7,5,2,-1}, +{1,3,5,3,7,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,8,7,0,7,1,1,7,5,-1,-1,-1,-1,-1,-1,-1}, +{9,0,3,9,3,5,5,3,7,-1,-1,-1,-1,-1,-1,-1}, +{9,8,7,5,9,7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{5,8,4,5,10,8,10,11,8,-1,-1,-1,-1,-1,-1,-1}, +{5,0,4,5,11,0,5,10,11,11,3,0,-1,-1,-1,-1}, +{0,1,9,8,4,10,8,10,11,10,4,5,-1,-1,-1,-1}, +{10,11,4,10,4,5,11,3,4,9,4,1,3,1,4,-1}, +{2,5,1,2,8,5,2,11,8,4,5,8,-1,-1,-1,-1}, +{0,4,11,0,11,3,4,5,11,2,11,1,5,1,11,-1}, +{0,2,5,0,5,9,2,11,5,4,5,8,11,8,5,-1}, +{9,4,5,2,11,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{2,5,10,3,5,2,3,4,5,3,8,4,-1,-1,-1,-1}, +{5,10,2,5,2,4,4,2,0,-1,-1,-1,-1,-1,-1,-1}, +{3,10,2,3,5,10,3,8,5,4,5,8,0,1,9,-1}, +{5,10,2,5,2,4,1,9,2,9,4,2,-1,-1,-1,-1}, +{8,4,5,8,5,3,3,5,1,-1,-1,-1,-1,-1,-1,-1}, +{0,4,5,1,0,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{8,4,5,8,5,3,9,0,5,0,3,5,-1,-1,-1,-1}, +{9,4,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{4,11,7,4,9,11,9,10,11,-1,-1,-1,-1,-1,-1,-1}, +{0,8,3,4,9,7,9,11,7,9,10,11,-1,-1,-1,-1}, +{1,10,11,1,11,4,1,4,0,7,4,11,-1,-1,-1,-1}, +{3,1,4,3,4,8,1,10,4,7,4,11,10,11,4,-1}, +{4,11,7,9,11,4,9,2,11,9,1,2,-1,-1,-1,-1}, +{9,7,4,9,11,7,9,1,11,2,11,1,0,8,3,-1}, +{11,7,4,11,4,2,2,4,0,-1,-1,-1,-1,-1,-1,-1}, +{11,7,4,11,4,2,8,3,4,3,2,4,-1,-1,-1,-1}, +{2,9,10,2,7,9,2,3,7,7,4,9,-1,-1,-1,-1}, +{9,10,7,9,7,4,10,2,7,8,7,0,2,0,7,-1}, +{3,7,10,3,10,2,7,4,10,1,10,0,4,0,10,-1}, +{1,10,2,8,7,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{4,9,1,4,1,7,7,1,3,-1,-1,-1,-1,-1,-1,-1}, +{4,9,1,4,1,7,0,8,1,8,7,1,-1,-1,-1,-1}, +{4,0,3,7,4,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{4,8,7,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{9,10,8,10,11,8,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{3,0,9,3,9,11,11,9,10,-1,-1,-1,-1,-1,-1,-1}, +{0,1,10,0,10,8,8,10,11,-1,-1,-1,-1,-1,-1,-1}, +{3,1,10,11,3,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{1,2,11,1,11,9,9,11,8,-1,-1,-1,-1,-1,-1,-1}, +{3,0,9,3,9,11,1,2,9,2,11,9,-1,-1,-1,-1}, +{0,2,11,8,0,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{3,2,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{2,3,8,2,8,10,10,8,9,-1,-1,-1,-1,-1,-1,-1}, +{9,10,2,0,9,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{2,3,8,2,8,10,0,1,8,1,10,8,-1,-1,-1,-1}, +{1,10,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{1,3,8,9,1,8,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,9,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{0,3,8,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, +{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} +}; +// clang-format on diff --git a/Source/Common/NMR_Exception.cpp b/Source/Common/NMR_Exception.cpp index 7d3761862..d34614c35 100644 --- a/Source/Common/NMR_Exception.cpp +++ b/Source/Common/NMR_Exception.cpp @@ -369,6 +369,7 @@ namespace NMR { case NMR_ERROR_INVALIDOBJECTLEVELPID: return "A MeshObject with triangle-properties has an invalid object-level property."; case NMR_ERROR_BUILDITEMOBJECT_MUSTNOTBE_OTHER: return "Build-item must not reference object of type OTHER."; case NMR_ERROR_OBJECTLEVELPID_ON_COMPONENTSOBJECT: return "A components object must not have an object-level PID."; + case NMR_ERROR_OBJECTLEVELPID_ON_BOOLEANOBJECT: return "A boolean object must not have an object-level PID."; case NMR_ERROR_BEAMLATTICENODESTOOCLOSE: return "Nodes used for a beam are closer then the specified minimal length."; case NMR_ERROR_BEAMLATTICE_INVALID_REPRESENTATIONRESOURCE: return "The resource defined as representationmesh is invalid."; case NMR_ERROR_BEAMLATTICE_INVALID_OBJECTTYPE: return "Beamlattice is defined on wrong object type."; diff --git a/Source/Common/Winding/NMR_WindingNumber.cpp b/Source/Common/Winding/NMR_WindingNumber.cpp new file mode 100644 index 000000000..483f94fc5 --- /dev/null +++ b/Source/Common/Winding/NMR_WindingNumber.cpp @@ -0,0 +1,1352 @@ +/*++ + +Copyright (C) 2026 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +Internal generalized winding number helper used by boolean processing. +Copyright remains with 3MF Consortium; this file is licensed under +the BSD terms above for inclusion in lib3mf. + +--*/ + +#include "Common/Winding/NMR_WindingNumber.h" + +#include +#include +#include +#include +#include +#include + +#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) + #define TG_X86 1 +#else + #define TG_X86 0 +#endif + +#if TG_X86 && (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)) + #include +#endif + +#if defined(TGWN_DISABLE_AVX2) + #define TG_HAS_AVX2 0 +#elif TG_X86 && (defined(__GNUC__) || defined(__clang__)) + #define TG_HAS_AVX2 1 +#elif TG_X86 && defined(_MSC_VER) && defined(__AVX2__) + #define TG_HAS_AVX2 1 +#else + #define TG_HAS_AVX2 0 +#endif + +#define TINYBVH_IMPLEMENTATION +#include "tiny_bvh.h" +#include "nanoflann.hpp" + +namespace tg { + // Implementation details: + // - Builds a BVH over triangles (mesh) or points (point cloud). + // - Stores Taylor expansion moments per BVH node. + // - Evaluates using approximation for far nodes and exact contributions for leaves. + struct windingNumber::Impl { + using Point = windingNumber::Point; + using Triangle = std::array; + + struct vec3 { + double x, y, z; + + vec3() : x(0), y(0), z(0) { + } + + vec3(double X, double Y, double Z) : x(X), y(Y), z(Z) { + } + + vec3 operator+(const vec3 &o) const { return {x + o.x, y + o.y, z + o.z}; } + vec3 operator-(const vec3 &o) const { return {x - o.x, y - o.y, z - o.z}; } + vec3 operator*(double s) const { return {x * s, y * s, z * s}; } + }; + + struct aabb { + vec3 mn, mx; + }; + + // Per-node aggregates for Taylor expansion about averageP. + // N = area-weighted normal sum (mesh) or dipole sum (points). + // Nij/Nijk terms match the "Fast Winding Numbers for Soups and Clouds" expansion. + struct nodeAgg { + bool isLeaf = false; + uint64_t left = 0, right = 0; + uint64_t firstPrim = 0, primCount = 0; + + vec3 averageP{}, N{}; + double maxPDist2 = 0.0; + + vec3 NijDiag{}; + double Nxy_Nyx = 0, Nyz_Nzy = 0, Nzx_Nxz = 0; + + vec3 NijkDiag{}; + double sumPermuteNxyz = 0; + double twoNxxy_Nyxx = 0, twoNxxz_Nzxx = 0; + double twoNyyz_Nzyy = 0, twoNyyx_Nxyy = 0; + double twoNzzx_Nxzz = 0, twoNzzy_Nyzz = 0; + }; + + // Temporary build data used while constructing aggregates bottom-up. + struct buildData { + aabb box{}; + vec3 averageP{}, areaP{}, N{}; + double area = 0, maxPDist2 = 0; + + vec3 NijDiag{}; + double Nxy = 0, Nyx = 0, Nyz = 0, Nzy = 0, Nzx = 0, Nxz = 0; + + vec3 NijkDiag{}; + double sumPermuteNxyz = 0; + double twoNxxy_Nyxx = 0, twoNxxz_Nzxx = 0; + double twoNyyz_Nzyy = 0, twoNyyx_Nxyy = 0; + double twoNzzx_Nxzz = 0, twoNzzy_Nyzz = 0; + }; + + // KD-tree adaptor for nanoflann (point cloud normal/area estimation). + struct PointCloudAdaptor { + const std::vector *pts; + + PointCloudAdaptor(const std::vector &p) : pts(&p) { + } + + size_t kdtree_get_point_count() const { return pts->size(); } + double kdtree_get_pt(size_t idx, size_t dim) const { return (*pts)[idx][dim]; } + + template + bool kdtree_get_bbox(BBOX &) const { return false; } + }; + + using KDTree = nanoflann::KDTreeSingleIndexAdaptor< + nanoflann::L2_Simple_Adaptor, + PointCloudAdaptor, 3, size_t>; + + enum class Mode { Mesh, PointCloud }; + + Mode mode = Mode::Mesh; + + Settings settings; + + // Mesh data (triangle soup). + std::vector vertices; + std::vector triangles; + std::vector triVecArea, triCentroid; + std::vector triBounds; + + // Point cloud data (oriented points). + std::vector cloudPoints; + std::vector cloudNormals; // Unit normals. + std::vector cloudAreas; // Area per point. + std::vector cloudDipoles; // Area-weighted normal (A * n). + std::vector cloudBounds; // Degenerate AABB per point. + + // BVH (shared for mesh and point cloud modes). + tinybvh::BVH_Double bvh; + std::vector triVertsPacked; // For mesh mode + std::vector nodes; + + static constexpr double pi() { return 3.141592653589793238462643383279502884; } + static constexpr int kPacketSize = 4; + + static vec3 toVec3(const Point &v) { return {v[0], v[1], v[2]}; } + static double dot(const vec3 &a, const vec3 &b) { return a.x * b.x + a.y * b.y + a.z * b.z; } + + static vec3 cross(const vec3 &a, const vec3 &b) { + return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; + } + + static double norm(const vec3 &a) { return std::sqrt(dot(a, a)); } + + static aabb triAabb(const vec3 &a, const vec3 &b, const vec3 &c) { + return { + {std::min({a.x, b.x, c.x}), std::min({a.y, b.y, c.y}), std::min({a.z, b.z, c.z})}, + {std::max({a.x, b.x, c.x}), std::max({a.y, b.y, c.y}), std::max({a.z, b.z, c.z})} + }; + } + + static vec3 aabbCenter(const aabb &bb) { return (bb.mn + bb.mx) * 0.5; } + + static aabb aabbUnion(const aabb &a, const aabb &b) { + return { + {std::min(a.mn.x, b.mn.x), std::min(a.mn.y, b.mn.y), std::min(a.mn.z, b.mn.z)}, + {std::max(a.mx.x, b.mx.x), std::max(a.mx.y, b.mx.y), std::max(a.mx.z, b.mx.z)} + }; + } + + static double maxPDist2FromBox(const aabb &bb, const vec3 &p) { + double dx = std::max(std::abs(p.x - bb.mn.x), std::abs(bb.mx.x - p.x)); + double dy = std::max(std::abs(p.y - bb.mn.y), std::abs(bb.mx.y - p.y)); + double dz = std::max(std::abs(p.z - bb.mn.z), std::abs(bb.mx.z - p.z)); + return dx * dx + dy * dy + dz * dz; + } + + uint64_t primIndex(uint64_t i) const { return bvh.primIdx[i]; } + + + // Estimate per-point normals using PCA on k nearest neighbors. + void estimateNormals(int k) { + size_t N = cloudPoints.size(); + cloudNormals.resize(N); + + PointCloudAdaptor adaptor(cloudPoints); + KDTree kdtree(3, adaptor, nanoflann::KDTreeSingleIndexAdaptorParams(10)); + kdtree.buildIndex(); + + // Compute centroid for orientation + vec3 centroid{}; + for (const auto &p: cloudPoints) { + centroid = centroid + toVec3(p); + } + centroid = centroid * (1.0 / N); + + std::vector indices(k + 1); + std::vector dists(k + 1); + + for (size_t i = 0; i < N; ++i) { + size_t found = kdtree.knnSearch(cloudPoints[i].data(), k + 1, indices.data(), dists.data()); + + // Compute local centroid of neighbors + vec3 localCentroid{}; + size_t count = 0; + for (size_t j = 0; j < found; ++j) { + localCentroid = localCentroid + toVec3(cloudPoints[indices[j]]); + ++count; + } + if (count > 0) localCentroid = localCentroid * (1.0 / count); + + // Build covariance matrix + double cov[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + for (size_t j = 0; j < found; ++j) { + vec3 d = toVec3(cloudPoints[indices[j]]) - localCentroid; + cov[0][0] += d.x * d.x; + cov[0][1] += d.x * d.y; + cov[0][2] += d.x * d.z; + cov[1][0] += d.y * d.x; + cov[1][1] += d.y * d.y; + cov[1][2] += d.y * d.z; + cov[2][0] += d.z * d.x; + cov[2][1] += d.z * d.y; + cov[2][2] += d.z * d.z; + } + + // Find smallest eigenvector using power iteration on inverse + // (or just use the cross product of two largest eigenvectors) + // Simplified: use iterative method to find normal + vec3 normal = computeSmallestEigenvector(cov); + + // Orient normal to point away from global centroid + vec3 toPoint = toVec3(cloudPoints[i]) - centroid; + if (dot(normal, toPoint) < 0) { + normal = normal * -1.0; + } + + cloudNormals[i] = normal; + } + } + + // Compute smallest eigenvector of 3x3 symmetric matrix using Jacobi iteration. + static vec3 computeSmallestEigenvector(double cov[3][3]) { + // Copy matrix + double a[3][3]; + double v[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; // Eigenvectors + + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + a[i][j] = cov[i][j]; + + // Jacobi iteration + for (int iter = 0; iter < 50; ++iter) { + // Find largest off-diagonal element + int p = 0, q = 1; + double maxVal = std::abs(a[0][1]); + if (std::abs(a[0][2]) > maxVal) { + maxVal = std::abs(a[0][2]); + p = 0; + q = 2; + } + if (std::abs(a[1][2]) > maxVal) { + maxVal = std::abs(a[1][2]); + p = 1; + q = 2; + } + + if (maxVal < 1e-15) break; + + // Compute rotation + double theta = 0.5 * std::atan2(2.0 * a[p][q], a[q][q] - a[p][p]); + double c = std::cos(theta), s = std::sin(theta); + + // Apply rotation to a + double app = a[p][p], aqq = a[q][q], apq = a[p][q]; + a[p][p] = c * c * app - 2 * c * s * apq + s * s * aqq; + a[q][q] = s * s * app + 2 * c * s * apq + c * c * aqq; + a[p][q] = a[q][p] = 0; + + for (int i = 0; i < 3; ++i) { + if (i != p && i != q) { + double aip = a[i][p], aiq = a[i][q]; + a[i][p] = a[p][i] = c * aip - s * aiq; + a[i][q] = a[q][i] = s * aip + c * aiq; + } + } + + // Apply rotation to eigenvectors + for (int i = 0; i < 3; ++i) { + double vip = v[i][p], viq = v[i][q]; + v[i][p] = c * vip - s * viq; + v[i][q] = s * vip + c * viq; + } + } + + // Find index of smallest eigenvalue + int minIdx = 0; + if (a[1][1] < a[minIdx][minIdx]) minIdx = 1; + if (a[2][2] < a[minIdx][minIdx]) minIdx = 2; + + vec3 normal{v[0][minIdx], v[1][minIdx], v[2][minIdx]}; + double len = norm(normal); + if (len > 0) normal = normal * (1.0 / len); + return normal; + } + + // Build mesh precomputes + BVH + per-node aggregates. + void build() { + size_t T = triangles.size(); + triVecArea.resize(T); + triCentroid.resize(T); + triBounds.resize(T); + triVertsPacked.clear(); + triVertsPacked.reserve(T * 3); + + for (size_t t = 0; t < T; ++t) { + const auto &idx = triangles[t]; + vec3 a = toVec3(vertices[idx[0]]); + vec3 b = toVec3(vertices[idx[1]]); + vec3 c = toVec3(vertices[idx[2]]); + triVecArea[t] = cross(b - a, c - a) * 0.5; + triCentroid[t] = (a + b + c) * (1.0 / 3.0); + triBounds[t] = triAabb(a, b, c); + triVertsPacked.emplace_back(a.x, a.y, a.z); + triVertsPacked.emplace_back(b.x, b.y, b.z); + triVertsPacked.emplace_back(c.x, c.y, c.z); + } + + bvh.Build(triVertsPacked.data(), (uint64_t) T); + buildAggregates(); + } + + // Estimate per-point areas using k-nearest neighbors. + void estimateAreas(int k) { + size_t N = cloudPoints.size(); + cloudAreas.resize(N); + + PointCloudAdaptor adaptor(cloudPoints); + KDTree kdtree(3, adaptor, nanoflann::KDTreeSingleIndexAdaptorParams(10)); + kdtree.buildIndex(); + + std::vector indices(k + 1); + std::vector dists(k + 1); + + for (size_t i = 0; i < N; ++i) { + // Query k+1 neighbors (includes self) + size_t found = kdtree.knnSearch(cloudPoints[i].data(), k + 1, indices.data(), dists.data()); + + // Average distance to neighbors (excluding self at index 0) + double avgDist = 0; + size_t count = 0; + for (size_t j = 1; j < found; ++j) { + avgDist += std::sqrt(dists[j]); + ++count; + } + if (count > 0) avgDist /= count; + + // Area ~ π * r² where r is average neighbor distance + // This gives area of a disk with radius = avgDist + cloudAreas[i] = pi() * avgDist * avgDist; + } + } + + // Build point cloud BVH + aggregates (dipole model). + void buildPointCloud() { + size_t N = cloudPoints.size(); + cloudNormals.resize(N); + cloudDipoles.resize(N); + cloudBounds.resize(N); + triVertsPacked.clear(); + triVertsPacked.reserve(N * 3); + + // Compute dipoles and create degenerate triangles for BVH + for (size_t i = 0; i < N; ++i) { + vec3 p = toVec3(cloudPoints[i]); + + // Dipole = area * normal + cloudDipoles[i] = cloudNormals[i] * cloudAreas[i]; + + // Degenerate AABB (point) + cloudBounds[i] = {p, p}; + + // Create a tiny degenerate triangle for BVH building + // All three vertices are the same point + triVertsPacked.emplace_back(p.x, p.y, p.z); + triVertsPacked.emplace_back(p.x, p.y, p.z); + triVertsPacked.emplace_back(p.x, p.y, p.z); + } + + bvh.Build(triVertsPacked.data(), (uint64_t) N); + buildAggregatesPointCloud(); + } + + // Build aggregates for point cloud mode (dipole moments). + void buildAggregatesPointCloud() { + uint64_t nodeCount = bvh.usedNodes; + nodes.assign(nodeCount, nodeAgg{}); + std::vector bd(nodeCount); + + for (uint64_t i = 0; i < nodeCount; ++i) { + const auto &n = bvh.bvhNode[i]; + auto &nd = nodes[i]; + nd.isLeaf = n.triCount > 0; + if (nd.isLeaf) { + nd.firstPrim = n.leftFirst; + nd.primCount = n.triCount; + } else { + nd.left = n.leftFirst; + nd.right = n.leftFirst + 1; + } + } + + int order = std::clamp(settings.taylorOrder, 0, 2); + + for (uint64_t i = nodeCount; i-- > 0;) { + auto &nd = nodes[i]; + auto &out = bd[i]; + + if (nd.isLeaf) { + bool hasBox = false; + vec3 areaP{}, Nsum{}; + double area = 0; + + for (uint64_t t = 0; t < nd.primCount; ++t) { + uint64_t ptId = primIndex(nd.firstPrim + t); + vec3 p = toVec3(cloudPoints[ptId]); + vec3 dipole = cloudDipoles[ptId]; + double ptArea = cloudAreas[ptId]; + + if (!hasBox) { + out.box = {p, p}; + hasBox = true; + } else { + out.box.mn.x = std::min(out.box.mn.x, p.x); + out.box.mn.y = std::min(out.box.mn.y, p.y); + out.box.mn.z = std::min(out.box.mn.z, p.z); + out.box.mx.x = std::max(out.box.mx.x, p.x); + out.box.mx.y = std::max(out.box.mx.y, p.y); + out.box.mx.z = std::max(out.box.mx.z, p.z); + } + + areaP = areaP + p * ptArea; + area += ptArea; + Nsum = Nsum + dipole; + } + + out.areaP = areaP; + out.area = area; + out.N = Nsum; + out.averageP = area > 0 ? areaP * (1 / area) : aabbCenter(out.box); + out.maxPDist2 = maxPDist2FromBox(out.box, out.averageP); + + // For points, Nijk terms about the point itself are zero + // We only need to apply the shift + for (uint64_t t = 0; t < nd.primCount; ++t) { + uint64_t ptId = primIndex(nd.firstPrim + t); + buildData child{}; + child.averageP = toVec3(cloudPoints[ptId]); + child.N = cloudDipoles[ptId]; + // Nij, Nijk are all zero for a single point about its own center + applyShift(out, child, child.averageP - out.averageP, order); + } + } else { + const auto &L = bd[nd.left]; + const auto &R = bd[nd.right]; + out.box = aabbUnion(L.box, R.box); + out.areaP = L.areaP + R.areaP; + out.area = L.area + R.area; + out.N = L.N + R.N; + out.averageP = out.area > 0 ? out.areaP * (1 / out.area) : aabbCenter(out.box); + out.maxPDist2 = maxPDist2FromBox(out.box, out.averageP); + + if (order >= 1) out.NijDiag = L.NijDiag + R.NijDiag; + if (order >= 2) { + out.NijkDiag = L.NijkDiag + R.NijkDiag; + out.sumPermuteNxyz = L.sumPermuteNxyz + R.sumPermuteNxyz; + out.twoNxxy_Nyxx = L.twoNxxy_Nyxx + R.twoNxxy_Nyxx; + out.twoNxxz_Nzxx = L.twoNxxz_Nzxx + R.twoNxxz_Nzxx; + out.twoNyyz_Nzyy = L.twoNyyz_Nzyy + R.twoNyyz_Nzyy; + out.twoNyyx_Nxyy = L.twoNyyx_Nxyy + R.twoNyyx_Nxyy; + out.twoNzzx_Nxzz = L.twoNzzx_Nxzz + R.twoNzzx_Nxzz; + out.twoNzzy_Nyzz = L.twoNzzy_Nyzz + R.twoNzzy_Nyzz; + } + applyShift(out, L, L.averageP - out.averageP, order); + applyShift(out, R, R.averageP - out.averageP, order); + } + + nd.averageP = out.averageP; + nd.N = out.N; + nd.maxPDist2 = out.maxPDist2; + nd.NijDiag = out.NijDiag; + nd.Nxy_Nyx = out.Nxy + out.Nyx; + nd.Nyz_Nzy = out.Nyz + out.Nzy; + nd.Nzx_Nxz = out.Nzx + out.Nxz; + nd.NijkDiag = out.NijkDiag; + nd.sumPermuteNxyz = out.sumPermuteNxyz; + nd.twoNxxy_Nyxx = out.twoNxxy_Nyxx; + nd.twoNxxz_Nzxx = out.twoNxxz_Nzxx; + nd.twoNyyz_Nzyy = out.twoNyyz_Nzyy; + nd.twoNyyx_Nxyy = out.twoNyyx_Nxyy; + nd.twoNzzx_Nxzz = out.twoNzzx_Nxzz; + nd.twoNzzy_Nyzz = out.twoNzzy_Nyzz; + } + } + + // Dipole contribution for a single oriented point. + static double dipole(const vec3 &dipoleVec, const vec3 &ptPos, const vec3 &queryPos) { + vec3 r = ptPos - queryPos; + double r2 = dot(r, r); + if (r2 == 0) return 0; + double r3 = r2 * std::sqrt(r2); + return dot(dipoleVec, r) / r3; + } + + // Exact solid angle for a triangle at query point p. + static double solidAngle(const vec3 &a, const vec3 &b, const vec3 &c, const vec3 &p) { + vec3 qa = a - p, qb = b - p, qc = c - p; + double al = norm(qa), bl = norm(qb), cl = norm(qc); + if (al == 0 || bl == 0 || cl == 0) return 0; + vec3 qan = qa * (1 / al), qbn = qb * (1 / bl), qcn = qc * (1 / cl); + double num = dot(qan, cross(qbn - qan, qcn - qan)); + if (num == 0) return 0; + double den = 1 + dot(qan, qbn) + dot(qan, qcn) + dot(qbn, qcn); + return 2 * std::atan2(num, den); + } + + // Compute second-order triangle integrals for Taylor terms (order 2). + void computeNijkTerms(const vec3 &a, const vec3 &b, const vec3 &c, + const vec3 &P, const vec3 &N, buildData &out) { + double area = norm(N); + if (area == 0) return; + vec3 n = N * (1 / area); + + vec3 vals[3] = {a, b, c}; + int ox[3] = {0, 1, 2}, oy[3] = {0, 1, 2}, oz[3] = {0, 1, 2}; + + auto sort3 = [&](int *o, auto get) { + if (get(vals[0]) > get(vals[1])) std::swap(o[0], o[1]); + if (get(vals[o[0]]) > get(vals[2])) std::swap(o[0], o[2]); + if (get(vals[o[1]]) > get(vals[o[2]])) std::swap(o[1], o[2]); + }; + sort3(ox, [](const vec3 &v) { return v.x; }); + sort3(oy, [](const vec3 &v) { return v.y; }); + sort3(oz, [](const vec3 &v) { return v.z; }); + + double dx = vals[ox[2]].x - vals[ox[0]].x; + double dy = vals[oy[2]].y - vals[oy[0]].y; + double dz = vals[oz[2]].z - vals[oz[0]].z; + + auto getComp = [](const vec3 &v, int i) { return i == 0 ? v.x : (i == 1 ? v.y : v.z); }; + + auto integrate = [&](const vec3 &aa, const vec3 &bb, const vec3 &cc, + double *ii, double *ij, double *ik, int i) { + vec3 oab = bb - aa, oac = cc - aa, ocb = bb - cc; + double oac_i = getComp(oac, i), oab_i = getComp(oab, i); + if (oac_i == 0) return; + double t = oab_i / oac_i; + int j = (i + 1) % 3, k = (i + 2) % 3; + double jdiff = t * getComp(oac, j) - getComp(oab, j); + double kdiff = t * getComp(oac, k) - getComp(oab, k); + + vec3 ca{jdiff * getComp(oab, k) - kdiff * getComp(oab, j), kdiff * oab_i, jdiff * oab_i}; + vec3 cc_{ + jdiff * getComp(ocb, k) - kdiff * getComp(ocb, j), kdiff * getComp(ocb, i), jdiff * getComp(ocb, i) + }; + double sa = norm(ca), sc = norm(cc_); + double Pai = getComp(aa, i) - getComp(P, i), Pci = getComp(cc, i) - getComp(P, i); + double ocb_i = getComp(ocb, i); + + *ii = sa * (0.5 * Pai * Pai + (2.0 / 3) * Pai * oab_i + 0.25 * oab_i * oab_i) + + sc * (0.5 * Pci * Pci + (2.0 / 3) * Pci * ocb_i + 0.25 * ocb_i * ocb_i); + + for (int jk: {j, k}) { + double *out_ptr = (jk == j) ? ij : ik; + double diff = (jk == j) ? jdiff : kdiff; + if (!out_ptr) continue; + double bmid = getComp(bb, jk) + 0.5 * diff; + double Paj = getComp(aa, jk) - getComp(P, jk), Pcj = getComp(cc, jk) - getComp(P, jk); + *out_ptr = sa * (0.5 * Pai * Paj + (1.0 / 3) * Pai * (bmid - getComp(aa, jk)) + (1.0 / 3) * Paj * + oab_i + 0.25 * oab_i * (bmid - getComp(aa, jk))) + + sc * (0.5 * Pci * Pcj + (1.0 / 3) * Pci * (bmid - getComp(cc, jk)) + (1.0 / 3) * Pcj * + ocb_i + 0.25 * ocb_i * (bmid - getComp(cc, jk))); + } + }; + + double ixx = 0, ixy = 0, iyy = 0, iyz = 0, izz = 0, izx = 0; + if (dx > 0) + integrate(vals[ox[0]], vals[ox[1]], vals[ox[2]], &ixx, + (dx >= dy && dy > 0) ? &ixy : nullptr, (dx >= dz && dz > 0) ? &izx : nullptr, 0); + if (dy > 0) + integrate(vals[oy[0]], vals[oy[1]], vals[oy[2]], &iyy, + (dy >= dz && dz > 0) ? &iyz : nullptr, (dx < dy && dx > 0) ? &ixy : nullptr, 1); + if (dz > 0) + integrate(vals[oz[0]], vals[oz[1]], vals[oz[2]], &izz, + (dx < dz && dx > 0) ? &izx : nullptr, (dy < dz && dy > 0) ? &iyz : nullptr, 2); + + out.NijkDiag.x += ixx * n.x; + out.NijkDiag.y += iyy * n.y; + out.NijkDiag.z += izz * n.z; + out.sumPermuteNxyz += 2 * (n.x * iyz + n.y * izx + n.z * ixy); + out.twoNxxy_Nyxx += 2 * n.x * ixy + n.y * ixx; + out.twoNxxz_Nzxx += 2 * n.x * izx + n.z * ixx; + out.twoNyyz_Nzyy += 2 * n.y * iyz + n.z * iyy; + out.twoNyyx_Nxyy += 2 * n.y * ixy + n.x * iyy; + out.twoNzzx_Nxzz += 2 * n.z * izx + n.x * izz; + out.twoNzzy_Nyzz += 2 * n.z * iyz + n.y * izz; + } + + // Shift child moments to parent's expansion center. + void applyShift(buildData &parent, const buildData &child, const vec3 &d, int order) { + const vec3 &N = child.N; + parent.NijDiag.x += N.x * d.x; + parent.NijDiag.y += N.y * d.y; + parent.NijDiag.z += N.z * d.z; + + double Nxy = child.Nxy + N.x * d.y, Nyx = child.Nyx + N.y * d.x; + double Nyz = child.Nyz + N.y * d.z, Nzy = child.Nzy + N.z * d.y; + double Nzx = child.Nzx + N.z * d.x, Nxz = child.Nxz + N.x * d.z; + parent.Nxy += Nxy; + parent.Nyx += Nyx; + parent.Nyz += Nyz; + parent.Nzy += Nzy; + parent.Nzx += Nzx; + parent.Nxz += Nxz; + + if (order < 2) return; + + parent.NijkDiag.x += 2 * d.x * child.NijDiag.x + d.x * d.x * N.x; + parent.NijkDiag.y += 2 * d.y * child.NijDiag.y + d.y * d.y * N.y; + parent.NijkDiag.z += 2 * d.z * child.NijDiag.z + d.z * d.z * N.z; + parent.sumPermuteNxyz += d.x * (Nyz + Nzy) + d.y * (Nzx + Nxz) + d.z * (Nxy + Nyx); + parent.twoNxxy_Nyxx += 2 * (d.y * child.NijDiag.x + d.x * child.Nxy + N.x * d.x * d.y) + 2 * child.Nyx * d.x + + N.y * d.x * d.x; + parent.twoNxxz_Nzxx += 2 * (d.z * child.NijDiag.x + d.x * child.Nxz + N.x * d.x * d.z) + 2 * child.Nzx * d.x + + N.z * d.x * d.x; + parent.twoNyyz_Nzyy += 2 * (d.z * child.NijDiag.y + d.y * child.Nyz + N.y * d.y * d.z) + 2 * child.Nzy * d.y + + N.z * d.y * d.y; + parent.twoNyyx_Nxyy += 2 * (d.x * child.NijDiag.y + d.y * child.Nyx + N.y * d.y * d.x) + 2 * child.Nxy * d.y + + N.x * d.y * d.y; + parent.twoNzzx_Nxzz += 2 * (d.x * child.NijDiag.z + d.z * child.Nzx + N.z * d.z * d.x) + 2 * child.Nxz * d.z + + N.x * d.z * d.z; + parent.twoNzzy_Nyzz += 2 * (d.y * child.NijDiag.z + d.z * child.Nzy + N.z * d.z * d.y) + 2 * child.Nyz * d.z + + N.y * d.z * d.z; + } + + // Build aggregates for mesh mode. + void buildAggregates() { + uint64_t nodeCount = bvh.usedNodes; + nodes.assign(nodeCount, nodeAgg{}); + std::vector bd(nodeCount); + + for (uint64_t i = 0; i < nodeCount; ++i) { + const auto &n = bvh.bvhNode[i]; + auto &nd = nodes[i]; + nd.isLeaf = n.triCount > 0; + if (nd.isLeaf) { + nd.firstPrim = n.leftFirst; + nd.primCount = n.triCount; + } else { + nd.left = n.leftFirst; + nd.right = n.leftFirst + 1; + } + } + + int order = std::clamp(settings.taylorOrder, 0, 2); + + for (uint64_t i = nodeCount; i-- > 0;) { + auto &nd = nodes[i]; + auto &out = bd[i]; + + if (nd.isLeaf) { + bool hasBox = false; + vec3 areaP{}, Nsum{}; + double area = 0; + + for (uint64_t t = 0; t < nd.primCount; ++t) { + uint64_t triId = primIndex(nd.firstPrim + t); + if (!hasBox) { + out.box = triBounds[triId]; + hasBox = true; + } else out.box = aabbUnion(out.box, triBounds[triId]); + vec3 N = triVecArea[triId]; + double ta = norm(N); + areaP = areaP + triCentroid[triId] * ta; + area += ta; + Nsum = Nsum + N; + } + + out.areaP = areaP; + out.area = area; + out.N = Nsum; + out.averageP = area > 0 ? areaP * (1 / area) : aabbCenter(out.box); + out.maxPDist2 = maxPDist2FromBox(out.box, out.averageP); + + for (uint64_t t = 0; t < nd.primCount; ++t) { + uint64_t triId = primIndex(nd.firstPrim + t); + buildData child{}; + child.averageP = triCentroid[triId]; + child.N = triVecArea[triId]; + + if (order >= 2) { + const auto &idx = triangles[triId]; + computeNijkTerms(toVec3(vertices[idx[0]]), toVec3(vertices[idx[1]]), + toVec3(vertices[idx[2]]), child.averageP, child.N, child); + out.NijkDiag = out.NijkDiag + child.NijkDiag; + out.sumPermuteNxyz += child.sumPermuteNxyz; + out.twoNxxy_Nyxx += child.twoNxxy_Nyxx; + out.twoNxxz_Nzxx += child.twoNxxz_Nzxx; + out.twoNyyz_Nzyy += child.twoNyyz_Nzyy; + out.twoNyyx_Nxyy += child.twoNyyx_Nxyy; + out.twoNzzx_Nxzz += child.twoNzzx_Nxzz; + out.twoNzzy_Nyzz += child.twoNzzy_Nyzz; + } + applyShift(out, child, child.averageP - out.averageP, order); + } + } else { + const auto &L = bd[nd.left]; + const auto &R = bd[nd.right]; + out.box = aabbUnion(L.box, R.box); + out.areaP = L.areaP + R.areaP; + out.area = L.area + R.area; + out.N = L.N + R.N; + out.averageP = out.area > 0 ? out.areaP * (1 / out.area) : aabbCenter(out.box); + out.maxPDist2 = maxPDist2FromBox(out.box, out.averageP); + + if (order >= 1) out.NijDiag = L.NijDiag + R.NijDiag; + if (order >= 2) { + out.NijkDiag = L.NijkDiag + R.NijkDiag; + out.sumPermuteNxyz = L.sumPermuteNxyz + R.sumPermuteNxyz; + out.twoNxxy_Nyxx = L.twoNxxy_Nyxx + R.twoNxxy_Nyxx; + out.twoNxxz_Nzxx = L.twoNxxz_Nzxx + R.twoNxxz_Nzxx; + out.twoNyyz_Nzyy = L.twoNyyz_Nzyy + R.twoNyyz_Nzyy; + out.twoNyyx_Nxyy = L.twoNyyx_Nxyy + R.twoNyyx_Nxyy; + out.twoNzzx_Nxzz = L.twoNzzx_Nxzz + R.twoNzzx_Nxzz; + out.twoNzzy_Nyzz = L.twoNzzy_Nyzz + R.twoNzzy_Nyzz; + } + applyShift(out, L, L.averageP - out.averageP, order); + applyShift(out, R, R.averageP - out.averageP, order); + } + + nd.averageP = out.averageP; + nd.N = out.N; + nd.maxPDist2 = out.maxPDist2; + nd.NijDiag = out.NijDiag; + nd.Nxy_Nyx = out.Nxy + out.Nyx; + nd.Nyz_Nzy = out.Nyz + out.Nzy; + nd.Nzx_Nxz = out.Nzx + out.Nxz; + nd.NijkDiag = out.NijkDiag; + nd.sumPermuteNxyz = out.sumPermuteNxyz; + nd.twoNxxy_Nyxx = out.twoNxxy_Nyxx; + nd.twoNxxz_Nzxx = out.twoNxxz_Nzxx; + nd.twoNyyz_Nzyy = out.twoNyyz_Nzyy; + nd.twoNyyx_Nxyy = out.twoNyyx_Nxyy; + nd.twoNzzx_Nxzz = out.twoNzzx_Nxzz; + nd.twoNzzy_Nyzz = out.twoNzzy_Nyzz; + } + } + + static std::vector &getStack() { + thread_local std::vector stack; + stack.clear(); + stack.reserve(128); + return stack; + } + +#if TG_HAS_AVX2 && (defined(__GNUC__) || defined(__clang__)) + #define TG_AVX2_TARGET __attribute__((target("avx2"))) +#elif TG_HAS_AVX2 && defined(_MSC_VER) + #define TG_AVX2_TARGET +#else + #define TG_AVX2_TARGET +#endif + +#if TG_HAS_AVX2 + struct alignas(32) Packet { + double x[kPacketSize]; + double y[kPacketSize]; + double z[kPacketSize]; + }; + + struct PacketItem { + uint64_t node; + uint8_t mask; + }; + + static std::vector &getPacketStack() { + thread_local std::vector stack; + stack.clear(); + stack.reserve(128); + return stack; + } + + TG_AVX2_TARGET static inline __m256d avxDot3(__m256d ax, __m256d ay, __m256d az, + __m256d bx, __m256d by, __m256d bz) { + return _mm256_add_pd(_mm256_add_pd(_mm256_mul_pd(ax, bx), _mm256_mul_pd(ay, by)), + _mm256_mul_pd(az, bz)); + } + + TG_AVX2_TARGET static inline void avxCross3(__m256d ax, __m256d ay, __m256d az, + __m256d bx, __m256d by, __m256d bz, + __m256d &cx, __m256d &cy, __m256d &cz) { + cx = _mm256_sub_pd(_mm256_mul_pd(ay, bz), _mm256_mul_pd(az, by)); + cy = _mm256_sub_pd(_mm256_mul_pd(az, bx), _mm256_mul_pd(ax, bz)); + cz = _mm256_sub_pd(_mm256_mul_pd(ax, by), _mm256_mul_pd(ay, bx)); + } + + TG_AVX2_TARGET void approxPacketFull(const nodeAgg &nd, const Packet &p, double *out) const { + __m256d px = _mm256_load_pd(p.x); + __m256d py = _mm256_load_pd(p.y); + __m256d pz = _mm256_load_pd(p.z); + __m256d cx = _mm256_set1_pd(nd.averageP.x); + __m256d cy = _mm256_set1_pd(nd.averageP.y); + __m256d cz = _mm256_set1_pd(nd.averageP.z); + + __m256d qx = _mm256_sub_pd(px, cx); + __m256d qy = _mm256_sub_pd(py, cy); + __m256d qz = _mm256_sub_pd(pz, cz); + + __m256d ql2 = avxDot3(qx, qy, qz, qx, qy, qz); + __m256d ql_m2 = _mm256_div_pd(_mm256_set1_pd(1.0), ql2); + __m256d ql_m1 = _mm256_sqrt_pd(ql_m2); + + __m256d qnx = _mm256_mul_pd(qx, ql_m1); + __m256d qny = _mm256_mul_pd(qy, ql_m1); + __m256d qnz = _mm256_mul_pd(qz, ql_m1); + + __m256d Nx = _mm256_set1_pd(nd.N.x); + __m256d Ny = _mm256_set1_pd(nd.N.y); + __m256d Nz = _mm256_set1_pd(nd.N.z); + __m256d omega = _mm256_mul_pd(_mm256_set1_pd(-1.0), + _mm256_mul_pd(ql_m2, avxDot3(qnx, qny, qnz, Nx, Ny, Nz))); + + int order = std::clamp(settings.taylorOrder, 0, 2); + if (order >= 1) { + __m256d q2x = _mm256_mul_pd(qnx, qnx); + __m256d q2y = _mm256_mul_pd(qny, qny); + __m256d q2z = _mm256_mul_pd(qnz, qnz); + __m256d ql_m3 = _mm256_mul_pd(ql_m2, ql_m1); + + __m256d Nijx = _mm256_set1_pd(nd.NijDiag.x); + __m256d Nijy = _mm256_set1_pd(nd.NijDiag.y); + __m256d Nijz = _mm256_set1_pd(nd.NijDiag.z); + __m256d dq2Nij = _mm256_add_pd(_mm256_add_pd(_mm256_mul_pd(q2x, Nijx), _mm256_mul_pd(q2y, Nijy)), + _mm256_mul_pd(q2z, Nijz)); + __m256d cross = _mm256_add_pd( + _mm256_add_pd(_mm256_mul_pd(_mm256_mul_pd(qnx, qny), _mm256_set1_pd(nd.Nxy_Nyx)), + _mm256_mul_pd(_mm256_mul_pd(qny, qnz), _mm256_set1_pd(nd.Nyz_Nzy))), + _mm256_mul_pd(_mm256_mul_pd(qnz, qnx), _mm256_set1_pd(nd.Nzx_Nxz))); + + __m256d NijSum = _mm256_set1_pd(nd.NijDiag.x + nd.NijDiag.y + nd.NijDiag.z); + __m256d term1 = _mm256_mul_pd(ql_m3, + _mm256_sub_pd(NijSum, _mm256_mul_pd(_mm256_set1_pd(3.0), + _mm256_add_pd(dq2Nij, cross)))); + omega = _mm256_add_pd(omega, term1); + + if (order >= 2) { + __m256d q3x = _mm256_mul_pd(q2x, qnx); + __m256d q3y = _mm256_mul_pd(q2y, qny); + __m256d q3z = _mm256_mul_pd(q2z, qnz); + __m256d ql_m4 = _mm256_mul_pd(ql_m2, ql_m2); + + __m256d t0x = _mm256_set1_pd(nd.twoNyyx_Nxyy + nd.twoNzzx_Nxzz); + __m256d t0y = _mm256_set1_pd(nd.twoNzzy_Nyzz + nd.twoNxxy_Nyxx); + __m256d t0z = _mm256_set1_pd(nd.twoNxxz_Nzxx + nd.twoNyyz_Nzyy); + + __m256d t1x = _mm256_add_pd(_mm256_mul_pd(qny, _mm256_set1_pd(nd.twoNxxy_Nyxx)), + _mm256_mul_pd(qnz, _mm256_set1_pd(nd.twoNxxz_Nzxx))); + __m256d t1y = _mm256_add_pd(_mm256_mul_pd(qnz, _mm256_set1_pd(nd.twoNyyz_Nzyy)), + _mm256_mul_pd(qnx, _mm256_set1_pd(nd.twoNyyx_Nxyy))); + __m256d t1z = _mm256_add_pd(_mm256_mul_pd(qnx, _mm256_set1_pd(nd.twoNzzx_Nxzz)), + _mm256_mul_pd(qny, _mm256_set1_pd(nd.twoNzzy_Nyzz))); + + __m256d tnX = _mm256_set1_pd(3.0 * nd.NijkDiag.x); + __m256d tnY = _mm256_set1_pd(3.0 * nd.NijkDiag.y); + __m256d tnZ = _mm256_set1_pd(3.0 * nd.NijkDiag.z); + __m256d s0x = _mm256_add_pd(tnX, t0x); + __m256d s0y = _mm256_add_pd(tnY, t0y); + __m256d s0z = _mm256_add_pd(tnZ, t0z); + + __m256d dot_q_s0 = avxDot3(qnx, qny, qnz, s0x, s0y, s0z); + __m256d dot_q3_nijk = _mm256_add_pd(_mm256_add_pd(_mm256_mul_pd(q3x, _mm256_set1_pd(nd.NijkDiag.x)), + _mm256_mul_pd(q3y, _mm256_set1_pd(nd.NijkDiag.y))), + _mm256_mul_pd(q3z, _mm256_set1_pd(nd.NijkDiag.z))); + __m256d dot_q2_t1 = _mm256_add_pd(_mm256_add_pd(_mm256_mul_pd(q2x, t1x), _mm256_mul_pd(q2y, t1y)), + _mm256_mul_pd(q2z, t1z)); + __m256d qxyz = _mm256_mul_pd(_mm256_mul_pd(qnx, qny), qnz); + + __m256d term2 = _mm256_mul_pd(ql_m4, + _mm256_sub_pd(_mm256_mul_pd(_mm256_set1_pd(1.5), dot_q_s0), + _mm256_mul_pd(_mm256_set1_pd(7.5), + _mm256_add_pd(_mm256_add_pd(dot_q3_nijk, + _mm256_mul_pd(qxyz, _mm256_set1_pd(nd.sumPermuteNxyz))), + dot_q2_t1)))); + omega = _mm256_add_pd(omega, term2); + } + } + + _mm256_store_pd(out, omega); + } + + TG_AVX2_TARGET void solidAnglePacketFull(const vec3 &a, const vec3 &b, const vec3 &c, const Packet &p, double *out) const { + __m256d ax = _mm256_set1_pd(a.x), ay = _mm256_set1_pd(a.y), az = _mm256_set1_pd(a.z); + __m256d bx = _mm256_set1_pd(b.x), by = _mm256_set1_pd(b.y), bz = _mm256_set1_pd(b.z); + __m256d cx = _mm256_set1_pd(c.x), cy = _mm256_set1_pd(c.y), cz = _mm256_set1_pd(c.z); + __m256d px = _mm256_load_pd(p.x); + __m256d py = _mm256_load_pd(p.y); + __m256d pz = _mm256_load_pd(p.z); + + __m256d Ax = _mm256_sub_pd(ax, px), Ay = _mm256_sub_pd(ay, py), Az = _mm256_sub_pd(az, pz); + __m256d Bx = _mm256_sub_pd(bx, px), By = _mm256_sub_pd(by, py), Bz = _mm256_sub_pd(bz, pz); + __m256d Cx = _mm256_sub_pd(cx, px), Cy = _mm256_sub_pd(cy, py), Cz = _mm256_sub_pd(cz, pz); + + __m256d aN = _mm256_sqrt_pd(avxDot3(Ax, Ay, Az, Ax, Ay, Az)); + __m256d bN = _mm256_sqrt_pd(avxDot3(Bx, By, Bz, Bx, By, Bz)); + __m256d cN = _mm256_sqrt_pd(avxDot3(Cx, Cy, Cz, Cx, Cy, Cz)); + + __m256d invA = _mm256_div_pd(_mm256_set1_pd(1.0), aN); + __m256d invB = _mm256_div_pd(_mm256_set1_pd(1.0), bN); + __m256d invC = _mm256_div_pd(_mm256_set1_pd(1.0), cN); + + __m256d Anx = _mm256_mul_pd(Ax, invA), Any = _mm256_mul_pd(Ay, invA), Anz = _mm256_mul_pd(Az, invA); + __m256d Bnx = _mm256_mul_pd(Bx, invB), Bny = _mm256_mul_pd(By, invB), Bnz = _mm256_mul_pd(Bz, invB); + __m256d Cnx = _mm256_mul_pd(Cx, invC), Cny = _mm256_mul_pd(Cy, invC), Cnz = _mm256_mul_pd(Cz, invC); + + __m256d crossX, crossY, crossZ; + avxCross3(Bnx, Bny, Bnz, Cnx, Cny, Cnz, crossX, crossY, crossZ); + __m256d num = avxDot3(Anx, Any, Anz, crossX, crossY, crossZ); + + __m256d cosAB = avxDot3(Anx, Any, Anz, Bnx, Bny, Bnz); + __m256d cosBC = avxDot3(Bnx, Bny, Bnz, Cnx, Cny, Cnz); + __m256d cosCA = avxDot3(Cnx, Cny, Cnz, Anx, Any, Anz); + __m256d denom = _mm256_add_pd(_mm256_add_pd(_mm256_add_pd(_mm256_set1_pd(1.0), cosAB), cosBC), cosCA); + + alignas(32) double numArr[kPacketSize]; + alignas(32) double denomArr[kPacketSize]; + _mm256_store_pd(numArr, num); + _mm256_store_pd(denomArr, denom); + for (int i = 0; i < kPacketSize; ++i) { + out[i] += 2.0 * std::atan2(numArr[i], denomArr[i]); + } + } + + TG_AVX2_TARGET void dipolePacketFull(const vec3 &dipoleVec, const vec3 &ptPos, const Packet &p, double *out) const { + __m256d px = _mm256_load_pd(p.x); + __m256d py = _mm256_load_pd(p.y); + __m256d pz = _mm256_load_pd(p.z); + __m256d ox = _mm256_set1_pd(ptPos.x); + __m256d oy = _mm256_set1_pd(ptPos.y); + __m256d oz = _mm256_set1_pd(ptPos.z); + + __m256d rx = _mm256_sub_pd(ox, px); + __m256d ry = _mm256_sub_pd(oy, py); + __m256d rz = _mm256_sub_pd(oz, pz); + __m256d r2 = avxDot3(rx, ry, rz, rx, ry, rz); + __m256d r = _mm256_sqrt_pd(r2); + __m256d r3 = _mm256_mul_pd(r2, r); + + __m256d dx = _mm256_set1_pd(dipoleVec.x); + __m256d dy = _mm256_set1_pd(dipoleVec.y); + __m256d dz = _mm256_set1_pd(dipoleVec.z); + __m256d dotd = avxDot3(dx, dy, dz, rx, ry, rz); + __m256d contrib = _mm256_div_pd(dotd, r3); + + alignas(32) double vals[kPacketSize]; + _mm256_store_pd(vals, contrib); + for (int i = 0; i < kPacketSize; ++i) { + out[i] += vals[i]; + } + } +#endif + + // Evaluate Taylor approximation of a node at point p. + // Approximation uses expansion in q = (p - averageP) with terms up to taylorOrder. + double approx(const nodeAgg &nd, const vec3 &p) const { + vec3 qraw = p - nd.averageP; + double ql2 = dot(qraw, qraw); + if (ql2 == 0) return 0; + + double ql_m2 = 1 / ql2, ql_m1 = std::sqrt(ql_m2); + vec3 q = qraw * ql_m1; + double omega = -ql_m2 * (q.x * nd.N.x + q.y * nd.N.y + q.z * nd.N.z); + + int order = std::clamp(settings.taylorOrder, 0, 2); + if (order >= 1) { + vec3 q2{q.x * q.x, q.y * q.y, q.z * q.z}; + double ql_m3 = ql_m2 * ql_m1; + double dq2Nij = q2.x * nd.NijDiag.x + q2.y * nd.NijDiag.y + q2.z * nd.NijDiag.z; + double cross = q.x * q.y * nd.Nxy_Nyx + q.y * q.z * nd.Nyz_Nzy + q.z * q.x * nd.Nzx_Nxz; + omega += ql_m3 * ((nd.NijDiag.x + nd.NijDiag.y + nd.NijDiag.z) - 3 * (dq2Nij + cross)); + + if (order >= 2) { + vec3 q3{q2.x * q.x, q2.y * q.y, q2.z * q.z}; + double ql_m4 = ql_m2 * ql_m2; + vec3 t0{ + nd.twoNyyx_Nxyy + nd.twoNzzx_Nxzz, nd.twoNzzy_Nyzz + nd.twoNxxy_Nyxx, + nd.twoNxxz_Nzxx + nd.twoNyyz_Nzyy + }; + vec3 t1{ + q.y * nd.twoNxxy_Nyxx + q.z * nd.twoNxxz_Nzxx, q.z * nd.twoNyyz_Nzyy + q.x * nd.twoNyyx_Nxyy, + q.x * nd.twoNzzx_Nxzz + q.y * nd.twoNzzy_Nyzz + }; + vec3 tn{3 * nd.NijkDiag.x, 3 * nd.NijkDiag.y, 3 * nd.NijkDiag.z}; + vec3 s0{tn.x + t0.x, tn.y + t0.y, tn.z + t0.z}; + omega += ql_m4 * (1.5 * (q.x * s0.x + q.y * s0.y + q.z * s0.z) + - 7.5 * (q3.x * nd.NijkDiag.x + q3.y * nd.NijkDiag.y + q3.z * nd.NijkDiag.z + + q.x * q.y * q.z * nd.sumPermuteNxyz + q2.x * t1.x + q2.y * t1.y + q2.z + * t1.z)); + } + } + return omega; + } + + // Query winding number at a single point. + // Traversal: approximate nodes if (distance^2 > maxPDist2 * accuracy^2), + // otherwise descend to leaves and accumulate exact contributions. + double queryOne(const Point &pt) const { + vec3 p = toVec3(pt); + double sum = 0; + double acc2 = settings.accuracy * settings.accuracy; + + auto &stack = getStack(); + stack.push_back(0); + + while (!stack.empty()) { + uint64_t ni = stack.back(); + stack.pop_back(); + const auto &nd = nodes[ni]; + + vec3 q = p - nd.averageP; + if (dot(q, q) > nd.maxPDist2 * acc2) { + sum += approx(nd, p); + continue; + } + + if (!nd.isLeaf) { + stack.push_back(nd.right); + stack.push_back(nd.left); + continue; + } + + if (mode == Mode::Mesh) { + for (uint64_t t = 0; t < nd.primCount; ++t) { + uint64_t triId = primIndex(nd.firstPrim + t); + const auto &idx = triangles[triId]; + sum += solidAngle(toVec3(vertices[idx[0]]), toVec3(vertices[idx[1]]), + toVec3(vertices[idx[2]]), p); + } + } else { + // Point cloud mode - use dipole + for (uint64_t t = 0; t < nd.primCount; ++t) { + uint64_t ptId = primIndex(nd.firstPrim + t); + sum += dipole(cloudDipoles[ptId], toVec3(cloudPoints[ptId]), p); + } + } + } + return sum / (4 * pi()); + } + + // Query many points in parallel. + std::vector queryMany(const std::vector &points) const { + std::vector out(points.size()); + if (points.empty()) return out; + + unsigned int tc = settings.threads; + if (tc == 0) tc = std::thread::hardware_concurrency(); + tc = std::max(1u, std::min(tc, (unsigned int) points.size())); + + std::atomic next{0}; + const size_t chunk = 2048; + + auto worker = [&]() { + while (true) { + size_t start = next.fetch_add(chunk, std::memory_order_relaxed); + if (start >= points.size()) break; + size_t end = std::min(points.size(), start + chunk); +#if TG_HAS_AVX2 + bool useAvx2 = false; + #if defined(__GNUC__) || defined(__clang__) + useAvx2 = __builtin_cpu_supports("avx2"); + #elif defined(_MSC_VER) + int info[4] = {0}; + __cpuidex(info, 0, 0); + if (info[0] >= 7) { + __cpuidex(info, 7, 0); + useAvx2 = (info[1] & (1 << 5)) != 0; + } + #endif + + if (useAvx2) { + size_t i = start; + for (; i + kPacketSize <= end; i += kPacketSize) { + Packet pack{}; + for (int k = 0; k < kPacketSize; ++k) { + pack.x[k] = points[i + k][0]; + pack.y[k] = points[i + k][1]; + pack.z[k] = points[i + k][2]; + } + + alignas(32) double sums[kPacketSize] = {0, 0, 0, 0}; + double acc2 = settings.accuracy * settings.accuracy; + auto &stack = getPacketStack(); + stack.push_back({0, 0x0F}); + + while (!stack.empty()) { + PacketItem item = stack.back(); + stack.pop_back(); + const auto &nd = nodes[item.node]; + uint8_t mask = item.mask; + + if (!nd.isLeaf) { + uint8_t approxMask = 0; + uint8_t descendMask = 0; + for (int lane = 0; lane < kPacketSize; ++lane) { + if (!((mask >> lane) & 1)) continue; + vec3 p = {pack.x[lane], pack.y[lane], pack.z[lane]}; + vec3 q = p - nd.averageP; + if (dot(q, q) > nd.maxPDist2 * acc2) approxMask |= (1u << lane); + else descendMask |= (1u << lane); + } + + if (approxMask) { + if (approxMask == 0x0F) { + alignas(32) double vals[kPacketSize]; + approxPacketFull(nd, pack, vals); + for (int lane = 0; lane < kPacketSize; ++lane) sums[lane] += vals[lane]; + } else { + for (int lane = 0; lane < kPacketSize; ++lane) { + if (!((approxMask >> lane) & 1)) continue; + vec3 p = {pack.x[lane], pack.y[lane], pack.z[lane]}; + sums[lane] += approx(nd, p); + } + } + } + + if (descendMask) { + stack.push_back({nd.left, descendMask}); + stack.push_back({nd.right, descendMask}); + } + continue; + } + + if (mode == Mode::Mesh) { + for (uint64_t t = 0; t < nd.primCount; ++t) { + uint64_t triId = primIndex(nd.firstPrim + t); + const auto &idx = triangles[triId]; + vec3 a = toVec3(vertices[idx[0]]); + vec3 b = toVec3(vertices[idx[1]]); + vec3 c = toVec3(vertices[idx[2]]); + + if (mask == 0x0F) { + solidAnglePacketFull(a, b, c, pack, sums); + } else { + for (int lane = 0; lane < kPacketSize; ++lane) { + if (!((mask >> lane) & 1)) continue; + vec3 p = {pack.x[lane], pack.y[lane], pack.z[lane]}; + sums[lane] += solidAngle(a, b, c, p); + } + } + } + } else { + for (uint64_t t = 0; t < nd.primCount; ++t) { + uint64_t ptId = primIndex(nd.firstPrim + t); + vec3 dip = cloudDipoles[ptId]; + vec3 pos = toVec3(cloudPoints[ptId]); + + if (mask == 0x0F) { + dipolePacketFull(dip, pos, pack, sums); + } else { + for (int lane = 0; lane < kPacketSize; ++lane) { + if (!((mask >> lane) & 1)) continue; + vec3 p = {pack.x[lane], pack.y[lane], pack.z[lane]}; + sums[lane] += dipole(dip, pos, p); + } + } + } + } + } + + for (int lane = 0; lane < kPacketSize; ++lane) { + out[i + lane] = sums[lane] / (4 * pi()); + } + } + for (; i < end; ++i) { + out[i] = queryOne(points[i]); + } + } else { + for (size_t i = start; i < end; ++i) { + out[i] = queryOne(points[i]); + } + } +#else + for (size_t i = start; i < end; ++i) { + out[i] = queryOne(points[i]); + } +#endif + } + }; + + std::vector threads; + threads.reserve(tc); + for (unsigned int i = 0; i < tc; ++i) threads.emplace_back(worker); + for (auto &t: threads) t.join(); + + return out; + } + }; + + // Public API + windingNumber::windingNumber(const std::vector &vertices, + const std::vector &triangles, + Settings s) + : impl_(std::make_unique()) { + if (vertices.empty() || triangles.empty()) { + throw std::runtime_error("Mesh cannot be empty"); + } + for (const auto &tri: triangles) { + if (tri[0] >= vertices.size() || tri[1] >= vertices.size() || tri[2] >= vertices.size()) { + throw std::runtime_error("Triangle index out of range"); + } + } + impl_->settings = s; + impl_->mode = Impl::Mode::Mesh; + impl_->vertices = vertices; + impl_->triangles = triangles; + impl_->build(); + } + + windingNumber::windingNumber(const std::vector &points, Settings s) + : impl_(std::make_unique()) { + if (points.empty()) { + throw std::runtime_error("Point cloud cannot be empty"); + } + + impl_->settings = s; + impl_->mode = Impl::Mode::PointCloud; + impl_->cloudPoints = points; + + // Estimate normals using PCA + impl_->estimateNormals(s.kNeighbors); + + // Estimate areas using k-NN + impl_->estimateAreas(s.kNeighbors); + impl_->buildPointCloud(); + } + + windingNumber::windingNumber(const std::vector &points, + const std::vector &normals, + Settings s) + : impl_(std::make_unique()) { + if (points.size() != normals.size()) { + throw std::runtime_error("Points and normals must have same size"); + } + if (points.empty()) { + throw std::runtime_error("Point cloud cannot be empty"); + } + + impl_->settings = s; + impl_->mode = Impl::Mode::PointCloud; + + // Copy points + impl_->cloudPoints = points; + + // Normalize and store normals + impl_->cloudNormals.resize(points.size()); + for (size_t i = 0; i < points.size(); ++i) { + Impl::vec3 n = Impl::toVec3(normals[i]); + double len = Impl::norm(n); + if (len > 0) n = n * (1.0 / len); + impl_->cloudNormals[i] = n; + } + + // Estimate areas using k-NN + impl_->estimateAreas(s.kNeighbors); + impl_->buildPointCloud(); + } + + windingNumber::windingNumber(const std::vector &points, + const std::vector &normals, + const std::vector &areas, + Settings s) + : impl_(std::make_unique()) { + if (points.size() != normals.size() || points.size() != areas.size()) { + throw std::runtime_error("Points, normals, and areas must have same size"); + } + if (points.empty()) { + throw std::runtime_error("Point cloud cannot be empty"); + } + + impl_->settings = s; + impl_->mode = Impl::Mode::PointCloud; + + // Copy points and areas + impl_->cloudPoints = points; + impl_->cloudAreas = areas; + + // Normalize and store normals + impl_->cloudNormals.resize(points.size()); + for (size_t i = 0; i < points.size(); ++i) { + Impl::vec3 n = Impl::toVec3(normals[i]); + double len = Impl::norm(n); + if (len > 0) n = n * (1.0 / len); + impl_->cloudNormals[i] = n; + } + + impl_->buildPointCloud(); + } + + windingNumber::~windingNumber() = default; + + windingNumber::windingNumber(windingNumber &&) noexcept = default; + + windingNumber &windingNumber::operator=(windingNumber &&) noexcept = default; + + double windingNumber::query(const Point &p) const { + return impl_->queryOne(p); + } + + std::vector windingNumber::query(const std::vector &points) const { + return impl_->queryMany(points); + } +} // namespace tg diff --git a/Source/Model/Classes/NMR_Model.cpp b/Source/Model/Classes/NMR_Model.cpp index 32e58c37f..09b385a76 100644 --- a/Source/Model/Classes/NMR_Model.cpp +++ b/Source/Model/Classes/NMR_Model.cpp @@ -34,6 +34,7 @@ A model is an in memory representation of the 3MF file. #include "Model/Classes/NMR_Model.h" #include "Model/Classes/NMR_ModelObject.h" #include "Model/Classes/NMR_ModelMeshObject.h" +#include "Model/Classes/NMR_ModelBooleanObject.h" #include "Model/Classes/NMR_ModelConstants.h" #include "Model/Classes/NMR_ModelTypes.h" #include "Model/Classes/NMR_ModelAttachment.h" @@ -1514,6 +1515,13 @@ namespace NMR { if (m_FunctionLookup.size() > 0) return true; } + + if (sExtension == XML_3MF_NAMESPACE_BOOLEANSPEC) { + for (size_t i = 0; i < m_ObjectLookup.size(); i++) { + if (dynamic_cast(m_ObjectLookup[i].get()) != nullptr) + return true; + } + } return false; } @@ -1816,4 +1824,3 @@ namespace NMR { throw CNMRException(NMR_ERROR_RESOURCENOTFOUND); } } // namespace NMR - diff --git a/Source/Model/Classes/NMR_ModelBooleanObject.cpp b/Source/Model/Classes/NMR_ModelBooleanObject.cpp new file mode 100644 index 000000000..a890f437b --- /dev/null +++ b/Source/Model/Classes/NMR_ModelBooleanObject.cpp @@ -0,0 +1,369 @@ +/*++ + +Copyright (C) 2026 3MF Consortium + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Abstract: + +Minimal in-memory representation of a boolean object. + +--*/ + +#include "Model/Classes/NMR_ModelBooleanObject.h" + +#include "Model/Classes/NMR_ModelConstants.h" +#include "Model/Classes/NMR_ModelMeshObject.h" +#include "Model/Classes/NMR_ModelComponentsObject.h" +#include "Model/Classes/NMR_ModelLevelSetObject.h" +#include "Common/Boolean/NMR_BooleanEngine.h" +#include "Common/NMR_Exception.h" +#include + +namespace NMR { + + namespace { + nfBool referencesObjectRecursive(_In_ CModelObject * pCandidate, _In_ CModelObject * pTarget, _Inout_ std::unordered_set & visited) + { + if (pCandidate == nullptr || pTarget == nullptr) + return false; + if (pCandidate == pTarget) + return true; + if (!visited.insert(pCandidate).second) + return false; + + auto pBooleanCandidate = dynamic_cast(pCandidate); + if (pBooleanCandidate == nullptr) + return false; + + if (referencesObjectRecursive(pBooleanCandidate->getBaseObject(), pTarget, visited)) + return true; + + for (nfUint32 nOperandIndex = 0; nOperandIndex < pBooleanCandidate->getOperandCount(); ++nOperandIndex) { + auto pOperand = pBooleanCandidate->getOperand(nOperandIndex); + if (pOperand && referencesObjectRecursive(pOperand->getObject(), pTarget, visited)) + return true; + } + + return false; + } + + nfBool createsBooleanReferenceCycle(_In_ CModelObject * pProposedBase, _In_ CModelBooleanObject * pBooleanObject) + { + std::unordered_set visited; + return referencesObjectRecursive(pProposedBase, pBooleanObject, visited); + } + + nfBool isBeamLatticeMeshObject(_In_ CModelObject * pObject) + { + auto pMeshObject = dynamic_cast(pObject); + if (pMeshObject == nullptr) + return false; + + auto pMesh = pMeshObject->getMesh(); + return (pMesh != nullptr) && (pMesh->getBeamCount() > 0); + } + + } + + CModelBooleanObject::CModelBooleanObject(_In_ const ModelResourceID sID, _In_ CModel * pModel) + : CModelObject(sID, pModel), + m_eOperation(eModelBooleanOperation::Union), + m_bCSGModeEnabled(false), + m_nExtractionGridResolution(160) + { + } + + CModelBooleanObject::~CModelBooleanObject() = default; + + void CModelBooleanObject::setBaseObject(_In_ CModelObject * pObject, _In_ const NMATRIX3 & mTransform) + { + if (pObject == nullptr) + throw CNMRException(NMR_ERROR_INVALIDPARAM); + if (dynamic_cast(pObject) != nullptr) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + if (pObject->getObjectType() != MODELOBJECTTYPE_MODEL) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + if (isBeamLatticeMeshObject(pObject)) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + if (createsBooleanReferenceCycle(pObject, this)) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + + m_pBaseObject = std::make_shared(pObject, mTransform); + } + + CModelObject * CModelBooleanObject::getBaseObject() const + { + return m_pBaseObject ? m_pBaseObject->getObject() : nullptr; + } + + void CModelBooleanObject::setBaseTransform(_In_ const NMATRIX3 & mTransform) + { + if (!m_pBaseObject) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + + m_pBaseObject->setTransform(mTransform); + } + + NMATRIX3 CModelBooleanObject::getBaseTransform() const + { + return m_pBaseObject ? m_pBaseObject->getTransform() : fnMATRIX3_identity(); + } + + void CModelBooleanObject::setOperation(_In_ eModelBooleanOperation eOperation) + { + m_eOperation = eOperation; + } + + eModelBooleanOperation CModelBooleanObject::getOperation() const + { + return m_eOperation; + } + + std::string CModelBooleanObject::getOperationString() const + { + switch (m_eOperation) { + case eModelBooleanOperation::Union: + return XML_3MF_VALUE_BOOLEAN_OPERATION_UNION; + case eModelBooleanOperation::Difference: + return XML_3MF_VALUE_BOOLEAN_OPERATION_DIFFERENCE; + case eModelBooleanOperation::Intersection: + return XML_3MF_VALUE_BOOLEAN_OPERATION_INTERSECTION; + } + + return XML_3MF_VALUE_BOOLEAN_OPERATION_UNION; + } + + nfBool CModelBooleanObject::setOperationString(_In_ const std::string & sOperation, _In_ nfBool bRaiseException) + { + if (sOperation == XML_3MF_VALUE_BOOLEAN_OPERATION_UNION) { + m_eOperation = eModelBooleanOperation::Union; + return true; + } + if (sOperation == XML_3MF_VALUE_BOOLEAN_OPERATION_DIFFERENCE) { + m_eOperation = eModelBooleanOperation::Difference; + return true; + } + if (sOperation == XML_3MF_VALUE_BOOLEAN_OPERATION_INTERSECTION) { + m_eOperation = eModelBooleanOperation::Intersection; + return true; + } + + if (bRaiseException) + throw CNMRException(NMR_ERROR_NAMESPACE_INVALID_ATTRIBUTE); + + return false; + } + + void CModelBooleanObject::setCSGModeEnabled(_In_ nfBool bEnabled) + { + m_bCSGModeEnabled = bEnabled; + } + + nfBool CModelBooleanObject::getCSGModeEnabled() const + { + return m_bCSGModeEnabled; + } + + void CModelBooleanObject::setExtractionGridResolution(_In_ nfUint32 nGridResolution) + { + if (nGridResolution < 2) + throw CNMRException(NMR_ERROR_INVALIDPARAM); + m_nExtractionGridResolution = nGridResolution; + } + + nfUint32 CModelBooleanObject::getExtractionGridResolution() const + { + return m_nExtractionGridResolution; + } + + void CModelBooleanObject::addOperand(_In_ CModelObject * pObject, _In_ const NMATRIX3 & mTransform) + { + if (pObject == nullptr) + throw CNMRException(NMR_ERROR_INVALIDPARAM); + if (dynamic_cast(pObject) == nullptr) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + if (pObject->getObjectType() != MODELOBJECTTYPE_MODEL) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + if (isBeamLatticeMeshObject(pObject)) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + + m_Operands.push_back(std::make_shared(pObject, mTransform)); + } + + nfUint32 CModelBooleanObject::getOperandCount() const + { + return (nfUint32)m_Operands.size(); + } + + PModelComponent CModelBooleanObject::getOperand(_In_ nfUint32 nIdx) const + { + if (nIdx >= m_Operands.size()) + throw CNMRException(NMR_ERROR_INVALIDINDEX); + + return m_Operands[nIdx]; + } + + void CModelBooleanObject::mergeToMesh(_In_ CMesh * pMesh, _In_ const NMATRIX3 mMatrix) + { + if (pMesh == nullptr) + throw CNMRException(NMR_ERROR_INVALIDPARAM); + if (!m_pBaseObject) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + if (!m_bCSGModeEnabled && m_eOperation != eModelBooleanOperation::Union) + throw CNMRException(NMR_ERROR_NOTIMPLEMENTED); + + PMesh pWorkingMesh = std::make_shared(); + m_pBaseObject->mergeToMesh(pWorkingMesh.get(), mMatrix); + std::vector operandMeshes; + operandMeshes.reserve(m_Operands.size()); + + for (const auto & operand : m_Operands) { + if (!operand) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + auto pOperandMesh = std::make_shared(); + operand->mergeToMesh(pOperandMesh.get(), mMatrix); + operandMeshes.push_back(pOperandMesh); + } + + if (m_bCSGModeEnabled) { + PMesh pCsgMesh = std::make_shared(); + CBooleanEngine::evaluate(pWorkingMesh.get(), operandMeshes, m_eOperation, pCsgMesh.get(), m_nExtractionGridResolution); + pMesh->mergeMesh(pCsgMesh.get(), fnMATRIX3_identity()); + return; + } + + // Temporary realization path: flatten referenced meshes with transforms. + // This is exact for union; difference and intersection require the CSG path. + for (const auto & operandMesh : operandMeshes) + pWorkingMesh->mergeMesh(operandMesh.get(), fnMATRIX3_identity()); + + pMesh->mergeMesh(pWorkingMesh.get(), fnMATRIX3_identity()); + } + + nfBool CModelBooleanObject::isValid() + { + if (!m_pBaseObject || m_Operands.empty()) + return false; + + auto pBase = m_pBaseObject->getObject(); + if (!pBase) + return false; + + if (dynamic_cast(pBase) != nullptr) + return false; + if (pBase->getObjectType() != MODELOBJECTTYPE_MODEL) + return false; + if (isBeamLatticeMeshObject(pBase)) + return false; + + for (const auto & operand : m_Operands) { + if (dynamic_cast(operand->getObject()) == nullptr) + return false; + if (operand->getObject()->getObjectType() != MODELOBJECTTYPE_MODEL) + return false; + if (isBeamLatticeMeshObject(operand->getObject())) + return false; + } + + return true; + } + + void CModelBooleanObject::calculateComponentDepthLevel(nfUint32 nLevel) + { + CModelObject::calculateComponentDepthLevel(nLevel); + + if (m_pBaseObject && m_pBaseObject->getObject()) + m_pBaseObject->getObject()->calculateComponentDepthLevel(nLevel + 1); + + for (const auto & operand : m_Operands) { + if (operand && operand->getObject()) + operand->getObject()->calculateComponentDepthLevel(nLevel + 1); + } + } + + nfBool CModelBooleanObject::hasSlices(nfBool bRecursive) + { + if (!bRecursive) + return (this->getSliceStack().get() != nullptr); + + if (this->getSliceStack().get() != nullptr) + return true; + + if (m_pBaseObject && m_pBaseObject->getObject()->hasSlices(true)) + return true; + + for (const auto & operand : m_Operands) { + if (operand->getObject()->hasSlices(true)) + return true; + } + + return false; + } + + nfBool CModelBooleanObject::isValidForSlices(const NMATRIX3& totalParentMatrix) + { + if (this->getSliceStack().get() && !fnMATRIX3_isplanar(totalParentMatrix)) + return false; + + if (m_pBaseObject) { + auto baseMatrix = fnMATRIX3_multiply(totalParentMatrix, m_pBaseObject->getTransform()); + if (!m_pBaseObject->getObject()->isValidForSlices(baseMatrix)) + return false; + } + + for (const auto & operand : m_Operands) { + auto operandMatrix = fnMATRIX3_multiply(totalParentMatrix, operand->getTransform()); + if (!operand->getObject()->isValidForSlices(operandMatrix)) + return false; + } + + return true; + } + + void CModelBooleanObject::extendOutbox(_Out_ NOUTBOX3& vOutBox, _In_ const NMATRIX3 mAccumulatedMatrix) + { + if (m_pBaseObject) { + m_pBaseObject->getObject()->extendOutbox(vOutBox, fnMATRIX3_multiply(mAccumulatedMatrix, m_pBaseObject->getTransform())); + } + + for (const auto & operand : m_Operands) { + operand->getObject()->extendOutbox(vOutBox, fnMATRIX3_multiply(mAccumulatedMatrix, operand->getTransform())); + } + } + + ResourceDependencies CModelBooleanObject::getDependencies() + { + ResourceDependencies dependencies; + + if (m_pBaseObject && m_pBaseObject->getObject()) + dependencies.push_back(m_pBaseObject->getObject()->getPackageResourceID()); + + for (const auto & operand : m_Operands) { + if (operand->getObject()) + dependencies.push_back(operand->getObject()->getPackageResourceID()); + } + + return dependencies; + } +} diff --git a/Source/Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_Boolean.cpp b/Source/Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_Boolean.cpp new file mode 100644 index 000000000..242ddd3d8 --- /dev/null +++ b/Source/Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_Boolean.cpp @@ -0,0 +1,92 @@ +/*++ +--*/ +#include "Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_Boolean.h" + +#include "Model/Classes/NMR_ModelConstants.h" +#include "Common/NMR_Exception.h" +#include "Common/NMR_StringUtils.h" +#include "Common/Math/NMR_Matrix.h" + +namespace NMR { + CModelReaderNode_Boolean2307_Boolean::CModelReaderNode_Boolean2307_Boolean(_In_ CModel * pModel, _In_ PModelWarnings pWarnings) + : CModelReaderNode(pWarnings), m_pModel(pModel), m_nObjectID(0), m_bHasObjectID(false), m_bHasTransform(false), m_bHasPath(false), m_mTransform(fnMATRIX3_identity()) + { + } + + void CModelReaderNode_Boolean2307_Boolean::parseXML(_In_ CXmlReader * pXMLReader) + { + parseName(pXMLReader); + parseAttributes(pXMLReader); + if (m_bHasPath && !pXMLReader->NamespaceRegistered(XML_3MF_NAMESPACE_PRODUCTIONSPEC)) + throw CNMRException(NMR_ERROR_PRODUCTIONEXTENSION_REQUIRED); + parseContent(pXMLReader); + } + + void CModelReaderNode_Boolean2307_Boolean::OnAttribute(_In_z_ const nfChar * pAttributeName, _In_z_ const nfChar * pAttributeValue) + { + if (strcmp(pAttributeName, XML_3MF_ATTRIBUTE_BOOLEAN_OBJECTID) == 0) { + if (m_bHasObjectID) + throw CNMRException(NMR_ERROR_DUPLICATECOMPONENTOBJECTID); + m_nObjectID = fnStringToUint32(pAttributeValue); + m_bHasObjectID = true; + } + else if (strcmp(pAttributeName, XML_3MF_ATTRIBUTE_BOOLEAN_TRANSFORM) == 0) { + if (m_bHasTransform) + throw CNMRException(NMR_ERROR_DUPLICATETRANSFORM); + m_mTransform = fnMATRIX3_fromString(pAttributeValue); + m_bHasTransform = true; + } + else if (strcmp(pAttributeName, XML_3MF_ATTRIBUTE_BOOLEAN_PATH) == 0) { + if (m_bHasPath) + throw CNMRException(NMR_ERROR_DUPLICATEPATH); + m_sPath = pAttributeValue; + if (!fnStartsWithPathDelimiter(m_sPath)) + m_pWarnings->addException(CNMRException(NMR_ERROR_PATH_NOT_ABSOLUTE), mrwInvalidOptionalValue); + m_bHasPath = true; + } + else + m_pWarnings->addException(CNMRException(NMR_ERROR_NAMESPACE_INVALID_ATTRIBUTE), mrwInvalidOptionalValue); + } + + void CModelReaderNode_Boolean2307_Boolean::OnNSAttribute(_In_z_ const nfChar * pAttributeName, _In_z_ const nfChar * pAttributeValue, _In_z_ const nfChar * pNameSpace) + { + if (strcmp(pNameSpace, XML_3MF_NAMESPACE_PRODUCTIONSPEC) == 0) { + if (strcmp(pAttributeName, XML_3MF_PRODUCTION_PATH) == 0) { + if (m_bHasPath) + throw CNMRException(NMR_ERROR_DUPLICATEPATH); + m_sPath = pAttributeValue; + if (!fnStartsWithPathDelimiter(m_sPath)) + m_pWarnings->addException(CNMRException(NMR_ERROR_PATH_NOT_ABSOLUTE), mrwInvalidOptionalValue); + m_bHasPath = true; + } + else + m_pWarnings->addException(CNMRException(NMR_ERROR_NAMESPACE_INVALID_ATTRIBUTE), mrwInvalidOptionalValue); + } + } + + CModelObject * CModelReaderNode_Boolean2307_Boolean::getObject() + { + if (!m_bHasObjectID) + throw CNMRException(NMR_ERROR_MISSINGMODELOBJECTID); + + PPackageResourceID pRID; + if (m_bHasPath) { + if (m_pModel->currentPath() != m_pModel->rootPath()) + throw CNMRException(NMR_ERROR_REFERENCESTOODEEP); + pRID = m_pModel->findPackageResourceID(m_sPath, m_nObjectID); + } + else { + pRID = m_pModel->findPackageResourceID(m_pModel->currentPath(), m_nObjectID); + } + + if (pRID.get()) + return m_pModel->findObject(pRID->getUniqueID()); + + return nullptr; + } + + NMATRIX3 CModelReaderNode_Boolean2307_Boolean::getTransform() const + { + return m_mTransform; + } +} diff --git a/Source/Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_BooleanShape.cpp b/Source/Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_BooleanShape.cpp new file mode 100644 index 000000000..5a5e7bef5 --- /dev/null +++ b/Source/Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_BooleanShape.cpp @@ -0,0 +1,112 @@ +/*++ +--*/ +#include "Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_BooleanShape.h" + +#include "Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_Boolean.h" +#include "Model/Classes/NMR_ModelConstants.h" +#include "Common/NMR_Exception.h" +#include "Common/NMR_StringUtils.h" +#include "Common/Math/NMR_Matrix.h" + +namespace NMR { + CModelReaderNode_Boolean2307_BooleanShape::CModelReaderNode_Boolean2307_BooleanShape(_In_ CModel * pModel, _In_ PModelBooleanObject pBooleanObject, _In_ PModelWarnings pWarnings) + : CModelReaderNode(pWarnings), m_pModel(pModel), m_pBooleanObject(pBooleanObject), m_nObjectID(0), m_bHasObjectID(false), m_bHasTransform(false), m_bHasPath(false), m_mTransform(fnMATRIX3_identity()) + { + } + + void CModelReaderNode_Boolean2307_BooleanShape::parseXML(_In_ CXmlReader * pXMLReader) + { + parseName(pXMLReader); + parseAttributes(pXMLReader); + if (m_bHasPath && !pXMLReader->NamespaceRegistered(XML_3MF_NAMESPACE_PRODUCTIONSPEC)) + throw CNMRException(NMR_ERROR_PRODUCTIONEXTENSION_REQUIRED); + parseContent(pXMLReader); + + if (!m_bHasObjectID) + throw CNMRException(NMR_ERROR_MISSINGMODELOBJECTID); + + PPackageResourceID pRID; + if (m_bHasPath) { + if (m_pModel->currentPath() != m_pModel->rootPath()) + throw CNMRException(NMR_ERROR_REFERENCESTOODEEP); + pRID = m_pModel->findPackageResourceID(m_sPath, m_nObjectID); + } + else { + pRID = m_pModel->findPackageResourceID(m_pModel->currentPath(), m_nObjectID); + } + + auto pObject = pRID ? m_pModel->findObject(pRID->getUniqueID()) : nullptr; + if (!pObject) + throw CNMRException(NMR_ERROR_COULDNOTFINDCOMPONENTOBJECT); + + m_pBooleanObject->setBaseObject(pObject, m_mTransform); + if (m_pBooleanObject->getOperandCount() == 0) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + } + + void CModelReaderNode_Boolean2307_BooleanShape::OnAttribute(_In_z_ const nfChar * pAttributeName, _In_z_ const nfChar * pAttributeValue) + { + if (strcmp(pAttributeName, XML_3MF_ATTRIBUTE_BOOLEAN_OBJECTID) == 0) { + if (m_bHasObjectID) + throw CNMRException(NMR_ERROR_DUPLICATECOMPONENTOBJECTID); + m_nObjectID = fnStringToUint32(pAttributeValue); + m_bHasObjectID = true; + } + else if (strcmp(pAttributeName, XML_3MF_ATTRIBUTE_BOOLEAN_TRANSFORM) == 0) { + if (m_bHasTransform) + throw CNMRException(NMR_ERROR_DUPLICATETRANSFORM); + m_mTransform = fnMATRIX3_fromString(pAttributeValue); + m_bHasTransform = true; + } + else if (strcmp(pAttributeName, XML_3MF_ATTRIBUTE_BOOLEAN_PATH) == 0) { + if (m_bHasPath) + throw CNMRException(NMR_ERROR_DUPLICATEPATH); + m_sPath = pAttributeValue; + if (!fnStartsWithPathDelimiter(m_sPath)) + m_pWarnings->addException(CNMRException(NMR_ERROR_PATH_NOT_ABSOLUTE), mrwInvalidOptionalValue); + m_bHasPath = true; + } + else if (strcmp(pAttributeName, XML_3MF_ATTRIBUTE_BOOLEAN_OPERATION) == 0) { + if (!m_pBooleanObject->setOperationString(pAttributeValue, false)) + m_pWarnings->addException(CNMRException(NMR_ERROR_NAMESPACE_INVALID_ATTRIBUTE), mrwInvalidOptionalValue); + } + else + m_pWarnings->addException(CNMRException(NMR_ERROR_NAMESPACE_INVALID_ATTRIBUTE), mrwInvalidOptionalValue); + } + + void CModelReaderNode_Boolean2307_BooleanShape::OnNSAttribute(_In_z_ const nfChar * pAttributeName, _In_z_ const nfChar * pAttributeValue, _In_z_ const nfChar * pNameSpace) + { + if (strcmp(pNameSpace, XML_3MF_NAMESPACE_PRODUCTIONSPEC) == 0) { + if (strcmp(pAttributeName, XML_3MF_PRODUCTION_PATH) == 0) { + if (m_bHasPath) + throw CNMRException(NMR_ERROR_DUPLICATEPATH); + m_sPath = pAttributeValue; + if (!fnStartsWithPathDelimiter(m_sPath)) + m_pWarnings->addException(CNMRException(NMR_ERROR_PATH_NOT_ABSOLUTE), mrwInvalidOptionalValue); + m_bHasPath = true; + } + else + m_pWarnings->addException(CNMRException(NMR_ERROR_NAMESPACE_INVALID_ATTRIBUTE), mrwInvalidOptionalValue); + } + } + + void CModelReaderNode_Boolean2307_BooleanShape::OnNSChildElement(_In_z_ const nfChar * pChildName, _In_z_ const nfChar * pNameSpace, _In_ CXmlReader * pXMLReader) + { + if (strcmp(pNameSpace, XML_3MF_NAMESPACE_BOOLEANSPEC) == 0) { + if (strcmp(pChildName, XML_3MF_ELEMENT_BOOLEAN) == 0) { + auto pXMLNode = std::make_shared(m_pModel, m_pWarnings); + pXMLNode->parseXML(pXMLReader); + auto pObject = pXMLNode->getObject(); + if (!pObject) + throw CNMRException(NMR_ERROR_COULDNOTFINDCOMPONENTOBJECT); + m_pBooleanObject->addOperand(pObject, pXMLNode->getTransform()); + } + else { + m_pWarnings->addException(CNMRException(NMR_ERROR_NAMESPACE_INVALID_ELEMENT), mrwInvalidOptionalValue); + } + } + else { + m_pWarnings->addException(CNMRException(NMR_ERROR_NAMESPACE_INVALID_ELEMENT), mrwInvalidOptionalValue); + } + } +} diff --git a/Source/Model/Reader/NMR_ModelReaderNode_ModelBase.cpp b/Source/Model/Reader/NMR_ModelReaderNode_ModelBase.cpp index a421891f7..014731bec 100644 --- a/Source/Model/Reader/NMR_ModelReaderNode_ModelBase.cpp +++ b/Source/Model/Reader/NMR_ModelReaderNode_ModelBase.cpp @@ -88,7 +88,8 @@ namespace NMR { strcmp(sExtensionURI.c_str(), XML_3MF_NAMESPACE_BEAMLATTICEBALLSSPEC) != 0 && strcmp(sExtensionURI.c_str(), XML_3MF_NAMESPACE_SECURECONTENTSPEC) != 0 && strcmp(sExtensionURI.c_str(), XML_3MF_NAMESPACE_VOLUMETRICSPEC) != 0 && - strcmp(sExtensionURI.c_str(), XML_3MF_NAMESPACE_IMPLICITSPEC) != 0 + strcmp(sExtensionURI.c_str(), XML_3MF_NAMESPACE_IMPLICITSPEC) != 0 && + strcmp(sExtensionURI.c_str(), XML_3MF_NAMESPACE_BOOLEANSPEC) != 0 ) { m_pWarnings->addWarning(NMR_ERROR_REQUIREDEXTENSIONNOTSUPPORTED, mrwInvalidMandatoryValue); diff --git a/Source/Model/Reader/v100/NMR_ModelReaderNode100_Object.cpp b/Source/Model/Reader/v100/NMR_ModelReaderNode100_Object.cpp index 2fca11dfa..43852174d 100644 --- a/Source/Model/Reader/v100/NMR_ModelReaderNode100_Object.cpp +++ b/Source/Model/Reader/v100/NMR_ModelReaderNode100_Object.cpp @@ -36,10 +36,12 @@ Stream. #include "Model/Reader/v100/NMR_ModelReaderNode100_Mesh.h" #include "Model/Reader/v100/NMR_ModelReaderNode100_MetaDataGroup.h" #include "Model/Reader/v100/NMR_ModelReaderNode100_Components.h" +#include "Model/Reader/Boolean2307/NMR_ModelReaderNode_Boolean2307_BooleanShape.h" #include "Model/Reader/Volumetric2201/NMR_ModelReaderNode_LevelSet.h" #include "Model/Classes/NMR_ModelConstants.h" #include "Model/Classes/NMR_ModelMeshObject.h" +#include "Model/Classes/NMR_ModelBooleanObject.h" #include "Model/Classes/NMR_ModelAttachment.h" #include "Common/NMR_StringUtils.h" @@ -344,6 +346,32 @@ namespace NMR { mrwInvalidOptionalValue); } } + if (strcmp(pNameSpace, XML_3MF_NAMESPACE_BOOLEANSPEC) == 0) + { + if (strcmp(pChildName, XML_3MF_ELEMENT_BOOLEANSHAPE) == 0) + { + if (m_pObject.get()) + throw CNMRException(NMR_ERROR_AMBIGUOUSOBJECTDEFINITON); + + auto booleanObject = std::make_shared(m_nID, m_pModel); + m_pObject = booleanObject; + if (m_bHasType) { + if (!m_pObject->setObjectTypeString(m_sType, false)) + m_pWarnings->addWarning(NMR_ERROR_INVALIDMODELOBJECTTYPE, mrwInvalidOptionalValue); + } + + auto pXMLNode = std::make_shared(m_pModel, booleanObject, m_pWarnings); + pXMLNode->parseXML(pXMLReader); + m_pModel->addResource(m_pObject); + + if (m_bHasDefaultPropertyIndex || m_bHasDefaultPropertyID) + m_pWarnings->addException(CNMRException(NMR_ERROR_OBJECTLEVELPID_ON_BOOLEANOBJECT), mrwInvalidOptionalValue); + } + else + { + m_pWarnings->addException(CNMRException(NMR_ERROR_NAMESPACE_INVALID_ELEMENT), mrwInvalidOptionalValue); + } + } } // Create the object-level property from m_nObjectLevelPropertyID, if defined diff --git a/Source/Model/Writer/v100/NMR_ModelWriterNode100_Model.cpp b/Source/Model/Writer/v100/NMR_ModelWriterNode100_Model.cpp index 53b659e42..01e0bb5dd 100644 --- a/Source/Model/Writer/v100/NMR_ModelWriterNode100_Model.cpp +++ b/Source/Model/Writer/v100/NMR_ModelWriterNode100_Model.cpp @@ -49,6 +49,7 @@ This is the class for exporting the 3mf model stream root node. #include "Model/Classes/NMR_ModelMultiPropertyGroup.h" #include "Model/Classes/NMR_ModelMeshObject.h" #include "Model/Classes/NMR_ModelComponentsObject.h" +#include "Model/Classes/NMR_ModelBooleanObject.h" #include "Model/Classes/NMR_Model.h" #include "Common/NMR_Exception.h" #include "Common/NMR_Exception_Windows.h" @@ -86,6 +87,7 @@ namespace NMR { m_bWriteObjects = true; m_bWriteVolumetricExtension = true; m_bWriteImplicitExtension = true; + m_bWriteBooleanExtension = false; m_bWriteCustomNamespaces = true; @@ -217,6 +219,15 @@ namespace NMR { } } + if (m_bWriteBooleanExtension) { + writeConstPrefixedStringAttribute(XML_3MF_ATTRIBUTE_XMLNS, XML_3MF_NAMESPACEPREFIX_BOOLEAN, XML_3MF_NAMESPACE_BOOLEANSPEC); + if (m_pModel->RequireExtension(XML_3MF_NAMESPACE_BOOLEANSPEC)) { + if (sRequiredExtensions.size() > 0) + sRequiredExtensions = sRequiredExtensions + " "; + sRequiredExtensions = sRequiredExtensions + XML_3MF_NAMESPACEPREFIX_BOOLEAN; + } + } + if (m_bWriteCustomNamespaces) { nfUint32 nNSCount = m_pXMLWriter->GetNamespaceCount(); for (nfUint32 iNSCount = 0; iNSCount < nNSCount; iNSCount++) { @@ -651,6 +662,76 @@ namespace NMR { ModelWriter_LevelSet.writeToXML(); } + CModelBooleanObject * pBooleanObject = + dynamic_cast(&object); + if (pBooleanObject) + { + writeBooleanObject(pBooleanObject); + } + + writeFullEndElement(); + } + + void CModelWriterNode100_Model::writeBooleanObject(_In_ CModelBooleanObject * pBooleanObject) + { + __NMRASSERT(pBooleanObject); + + CModelObject * pBaseObject = pBooleanObject->getBaseObject(); + if (!pBaseObject) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + + PPackageResourceID pBaseObjectID = pBaseObject->getPackageResourceID(); + if (!pBaseObjectID) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + + nfUint32 nOperandCount = pBooleanObject->getOperandCount(); + if (nOperandCount == 0) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + + writeStartElementWithPrefix(XML_3MF_ELEMENT_BOOLEANSHAPE, XML_3MF_NAMESPACEPREFIX_BOOLEAN); + writeIntAttribute(XML_3MF_ATTRIBUTE_BOOLEAN_OBJECTID, pBaseObjectID->getModelResourceID()); + writeStringAttribute(XML_3MF_ATTRIBUTE_BOOLEAN_OPERATION, pBooleanObject->getOperationString()); + if (!fnMATRIX3_isIdentity(pBooleanObject->getBaseTransform())) + writeStringAttribute(XML_3MF_ATTRIBUTE_BOOLEAN_TRANSFORM, fnMATRIX3_toString(pBooleanObject->getBaseTransform())); + + if (pBaseObjectID->getPath() != m_pModel->currentPath()) { + if (m_pModel->currentPath() != m_pModel->rootPath()) { + throw CNMRException(NMR_ERROR_REFERENCESTOODEEP); + } + if (!m_bWriteProductionExtension) { + throw CNMRException(NMR_ERROR_PRODUCTIONEXTENSION_REQUIRED); + } + + writePrefixedStringAttribute(XML_3MF_NAMESPACEPREFIX_PRODUCTION, XML_3MF_PRODUCTION_PATH, pBaseObjectID->getPath()); + } + + for (nfUint32 nIndex = 0; nIndex < nOperandCount; nIndex++) { + PModelComponent pOperand = pBooleanObject->getOperand(nIndex); + CModelObject * pOperandObject = pOperand ? pOperand->getObject() : nullptr; + if (!pOperandObject) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + + PPackageResourceID pOperandObjectID = pOperandObject->getPackageResourceID(); + if (!pOperandObjectID) + throw CNMRException(NMR_ERROR_INVALIDOBJECT); + + writeStartElementWithPrefix(XML_3MF_ELEMENT_BOOLEAN, XML_3MF_NAMESPACEPREFIX_BOOLEAN); + writeIntAttribute(XML_3MF_ATTRIBUTE_BOOLEAN_OBJECTID, pOperandObjectID->getModelResourceID()); + if (pOperandObjectID->getPath() != m_pModel->currentPath()) { + if (m_pModel->currentPath() != m_pModel->rootPath()) { + throw CNMRException(NMR_ERROR_REFERENCESTOODEEP); + } + if (!m_bWriteProductionExtension) { + throw CNMRException(NMR_ERROR_PRODUCTIONEXTENSION_REQUIRED); + } + + writePrefixedStringAttribute(XML_3MF_NAMESPACEPREFIX_PRODUCTION, XML_3MF_PRODUCTION_PATH, pOperandObjectID->getPath()); + } + if (pOperand->hasTransform()) + writeStringAttribute(XML_3MF_ATTRIBUTE_BOOLEAN_TRANSFORM, pOperand->getTransformString()); + writeEndElement(); + } + writeFullEndElement(); } @@ -1247,7 +1328,6 @@ namespace NMR { void CModelWriterNode100_Model::detectRequiredExtensions() { - // Scan all mesh objects to detect if balls are present std::list objectList = m_pModel->getSortedObjectList(); for(auto iIterator = objectList.begin(); iIterator != objectList.end(); iIterator++) @@ -1258,6 +1338,10 @@ namespace NMR { continue; } + if (dynamic_cast(pObject) != nullptr) { + m_bWriteBooleanExtension = true; + } + // Check if object is a mesh Object with beam lattice CModelMeshObject *pMeshObject = dynamic_cast(pObject); if(pMeshObject) @@ -1269,10 +1353,14 @@ namespace NMR { if (nBallCount > 0 || eBallMode != eModelBeamLatticeBallMode::MODELBEAMLATTICEBALLMODE_NONE) { m_bWriteBeamLatticeBallsExtension = true; - break; // Found balls, no need to continue scanning } } } + + // The old beam-lattice-balls early exit moved here so the same scan can also find boolean objects. + if (m_bWriteBooleanExtension && m_bWriteBeamLatticeBallsExtension) { + break; + } } } diff --git a/Tests/CPP_Bindings/CMakeLists.txt b/Tests/CPP_Bindings/CMakeLists.txt index bb5b2d0b4..927c041e8 100644 --- a/Tests/CPP_Bindings/CMakeLists.txt +++ b/Tests/CPP_Bindings/CMakeLists.txt @@ -11,6 +11,7 @@ set(SRCS_UNITTEST ./Source/BaseMaterialGroup.cpp ./Source/BeamLattice.cpp ./Source/BeamSets.cpp + ./Source/Boolean.cpp ./Source/BuildItems.cpp ./Source/ColorGroup.cpp ./Source/CompositeMaterials.cpp @@ -48,6 +49,12 @@ set(SRCS_UNITTEST set(CMAKE_CURRENT_BINARY_DIR ${CMAKE_BINARY_DIR}) add_executable(${TESTNAME} ${SRCS_UNITTEST} ${GTEST_SRC_FILES}) +if (LIB3MF_ENABLE_EXPENSIVE_TESTS) + target_compile_definitions(${TESTNAME} PRIVATE LIB3MF_ENABLE_EXPENSIVE_TESTS=1) +else() + target_compile_definitions(${TESTNAME} PRIVATE LIB3MF_ENABLE_EXPENSIVE_TESTS=0) +endif() + set(STARTUPPROJECT ${TESTNAME}) target_include_directories(${TESTNAME} PRIVATE diff --git a/Tests/CPP_Bindings/Source/Boolean.cpp b/Tests/CPP_Bindings/Source/Boolean.cpp new file mode 100644 index 000000000..8bd670a2a --- /dev/null +++ b/Tests/CPP_Bindings/Source/Boolean.cpp @@ -0,0 +1,520 @@ +/*++ +--*/ + +#include "UnitTest_Utilities.h" +#include "lib3mf_implicit.hpp" + +namespace { + +Lib3MF::PMeshObject addBoxMesh(const Lib3MF::PModel & model) +{ + std::vector vertices; + std::vector triangles; + fnCreateBox(vertices, triangles); + + auto mesh = model->AddMeshObject(); + mesh->SetGeometry(vertices, triangles); + return mesh; +} + +void addSingleBeam(const Lib3MF::PMeshObject & mesh) +{ + Lib3MF::sBeam beam; + beam.m_Indices[0] = 0; + beam.m_Indices[1] = 1; + beam.m_Radii[0] = 0.5; + beam.m_Radii[1] = 0.5; + mesh->BeamLattice()->AddBeam(beam); +} + +Lib3MF::sTransform translatedIdentity(Lib3MF::PWrapper wrapper, float x, float y, float z) +{ + auto transform = wrapper->GetIdentityTransform(); + transform.m_Fields[3][0] = x; + transform.m_Fields[3][1] = y; + transform.m_Fields[3][2] = z; + return transform; +} + +} + +class BooleanRead : public Lib3MFTest { +}; + +TEST_F(BooleanRead, ReadAndIterateBooleanObjects) +{ + auto model = wrapper->CreateModel(); + auto reader = model->QueryReader("3mf"); + reader->ReadFromFile(sTestFilesPath + "/" + "Boolean" + "/" + "booleans_operations.3mf"); + + auto booleanObjects = model->GetBooleanObjects(); + ASSERT_EQ(booleanObjects->Count(), 2); + ASSERT_TRUE(booleanObjects->MoveNext()); + + auto booleanObjectA = booleanObjects->GetCurrentBooleanObject(); + ASSERT_TRUE(booleanObjectA->IsBooleanObject()); + ASSERT_EQ(booleanObjectA->GetOperation(), Lib3MF::eBooleanOperation::Intersection); + ASSERT_EQ(booleanObjectA->GetBaseObject()->GetModelResourceID(), 4u); + ASSERT_EQ(booleanObjectA->GetOperandCount(), 1u); + Lib3MF::PMeshObject operandA; + auto transformA = booleanObjectA->GetOperand(0, operandA); + (void)transformA; + ASSERT_EQ(operandA->GetModelResourceID(), 5u); + + ASSERT_TRUE(booleanObjects->MoveNext()); + auto booleanObjectB = booleanObjects->GetCurrentBooleanObject(); + ASSERT_TRUE(booleanObjectB->IsBooleanObject()); + ASSERT_EQ(booleanObjectB->GetOperation(), Lib3MF::eBooleanOperation::Difference); + ASSERT_EQ(booleanObjectB->GetBaseObject()->GetModelResourceID(), 6u); + ASSERT_EQ(booleanObjectB->GetOperandCount(), 3u); + Lib3MF::PMeshObject operandB; + booleanObjectB->GetOperand(0, operandB); + ASSERT_EQ(operandB->GetModelResourceID(), 3u); + + auto resourceByID = model->GetBooleanObjectByID(booleanObjectB->GetResourceID()); + ASSERT_EQ(resourceByID->GetOperandCount(), 3u); +} + +TEST_F(BooleanRead, ReadWriteReadBooleanObjects) +{ + auto model = wrapper->CreateModel(); + auto reader = model->QueryReader("3mf"); + reader->ReadFromFile(sTestFilesPath + "/" + "Boolean" + "/" + "booleans_operations.3mf"); + + std::vector buffer; + auto writer = model->QueryWriter("3mf"); + writer->WriteToBuffer(buffer); + + auto roundTripModel = wrapper->CreateModel(); + auto roundTripReader = roundTripModel->QueryReader("3mf"); + roundTripReader->ReadFromBuffer(buffer); + + auto booleanObjects = roundTripModel->GetBooleanObjects(); + ASSERT_EQ(booleanObjects->Count(), 2); + ASSERT_TRUE(booleanObjects->MoveNext()); + + auto booleanObjectA = booleanObjects->GetCurrentBooleanObject(); + ASSERT_EQ(booleanObjectA->GetOperation(), Lib3MF::eBooleanOperation::Intersection); + ASSERT_EQ(booleanObjectA->GetBaseObject()->GetModelResourceID(), 4u); + ASSERT_EQ(booleanObjectA->GetOperandCount(), 1u); + + ASSERT_TRUE(booleanObjects->MoveNext()); + auto booleanObjectB = booleanObjects->GetCurrentBooleanObject(); + ASSERT_EQ(booleanObjectB->GetOperation(), Lib3MF::eBooleanOperation::Difference); + ASSERT_EQ(booleanObjectB->GetBaseObject()->GetModelResourceID(), 6u); + ASSERT_EQ(booleanObjectB->GetOperandCount(), 3u); +} + +TEST_F(BooleanRead, CreateAndRoundTripBooleanObject) +{ + auto model = wrapper->CreateModel(); + auto baseMesh = addBoxMesh(model); + auto operandMesh = addBoxMesh(model); + auto booleanObject = model->AddBooleanObject(); + + auto baseTransform = translatedIdentity(wrapper, 1.0f, 2.0f, 3.0f); + auto operandTransform = translatedIdentity(wrapper, -4.0f, 5.0f, -6.0f); + + booleanObject->SetBaseObject(baseMesh.get(), baseTransform); + booleanObject->SetOperation(Lib3MF::eBooleanOperation::Difference); + booleanObject->AddOperand(operandMesh.get(), operandTransform); + model->AddBuildItem(booleanObject.get(), wrapper->GetIdentityTransform()); + + ASSERT_TRUE(booleanObject->IsBooleanObject()); + ASSERT_EQ(booleanObject->GetBaseObject()->GetResourceID(), baseMesh->GetResourceID()); + ASSERT_EQ(booleanObject->GetOperation(), Lib3MF::eBooleanOperation::Difference); + ASSERT_EQ(booleanObject->GetOperandCount(), 1u); + Lib3MF::helper::CompareTransforms(booleanObject->GetBaseTransform(), baseTransform); + + Lib3MF::PMeshObject readOperand; + auto roundTripOperandTransform = booleanObject->GetOperand(0, readOperand); + ASSERT_EQ(readOperand->GetResourceID(), operandMesh->GetResourceID()); + Lib3MF::helper::CompareTransforms(roundTripOperandTransform, operandTransform); + + std::vector buffer; + model->QueryWriter("3mf")->WriteToBuffer(buffer); + + auto roundTripModel = wrapper->CreateModel(); + auto reader = roundTripModel->QueryReader("3mf"); + reader->ReadFromBuffer(buffer); + CheckReaderWarnings(reader, 0); + + auto booleanObjects = roundTripModel->GetBooleanObjects(); + ASSERT_EQ(booleanObjects->Count(), 1); + ASSERT_TRUE(booleanObjects->MoveNext()); + auto roundTripBoolean = booleanObjects->GetCurrentBooleanObject(); + ASSERT_EQ(roundTripBoolean->GetOperation(), Lib3MF::eBooleanOperation::Difference); + ASSERT_EQ(roundTripBoolean->GetBaseObject()->GetModelResourceID(), baseMesh->GetModelResourceID()); + ASSERT_EQ(roundTripBoolean->GetOperandCount(), 1u); + Lib3MF::helper::CompareTransforms(roundTripBoolean->GetBaseTransform(), baseTransform); + + Lib3MF::PMeshObject roundTripOperand; + auto roundTripTransform = roundTripBoolean->GetOperand(0, roundTripOperand); + ASSERT_EQ(roundTripOperand->GetModelResourceID(), operandMesh->GetModelResourceID()); + Lib3MF::helper::CompareTransforms(roundTripTransform, operandTransform); +} + +TEST_F(BooleanRead, ApiRejectsInvalidStateAndAccess) +{ + auto model = wrapper->CreateModel(); + auto baseMesh = addBoxMesh(model); + auto operandMesh = addBoxMesh(model); + auto supportMesh = addBoxMesh(model); + auto booleanObject = model->AddBooleanObject(); + auto componentsObject = model->AddComponentsObject(); + supportMesh->SetType(Lib3MF::eObjectType::Support); + + ASSERT_SPECIFIC_THROW(booleanObject->GetBaseObject(), Lib3MF::ELib3MFException); + ASSERT_SPECIFIC_THROW(booleanObject->SetBaseTransform(wrapper->GetIdentityTransform()), Lib3MF::ELib3MFException); + ASSERT_SPECIFIC_THROW(booleanObject->SetBaseObject(nullptr, wrapper->GetIdentityTransform()), Lib3MF::ELib3MFException); + ASSERT_SPECIFIC_THROW(booleanObject->SetBaseObject(componentsObject.get(), wrapper->GetIdentityTransform()), Lib3MF::ELib3MFException); + ASSERT_SPECIFIC_THROW(booleanObject->SetBaseObject(supportMesh.get(), wrapper->GetIdentityTransform()), Lib3MF::ELib3MFException); + + booleanObject->SetBaseObject(baseMesh.get(), wrapper->GetIdentityTransform()); + booleanObject->AddOperand(operandMesh.get(), wrapper->GetIdentityTransform()); + + ASSERT_SPECIFIC_THROW(booleanObject->AddOperand(nullptr, wrapper->GetIdentityTransform()), Lib3MF::ELib3MFException); + ASSERT_SPECIFIC_THROW(booleanObject->AddOperand(supportMesh.get(), wrapper->GetIdentityTransform()), Lib3MF::ELib3MFException); + ASSERT_SPECIFIC_THROW({ + Lib3MF::PMeshObject invalidOperand; + booleanObject->GetOperand(1, invalidOperand); + }, Lib3MF::ELib3MFException); + ASSERT_SPECIFIC_THROW(model->GetBooleanObjectByID(baseMesh->GetResourceID()), Lib3MF::ELib3MFException); +} + +TEST_F(BooleanRead, ApiRejectsBeamLatticeMeshes) +{ + auto model = wrapper->CreateModel(); + auto regularMesh = addBoxMesh(model); + auto beamMesh = addBoxMesh(model); + auto booleanObject = model->AddBooleanObject(); + addSingleBeam(beamMesh); + + ASSERT_SPECIFIC_THROW(booleanObject->SetBaseObject(beamMesh.get(), wrapper->GetIdentityTransform()), Lib3MF::ELib3MFException); + + booleanObject->SetBaseObject(regularMesh.get(), wrapper->GetIdentityTransform()); + ASSERT_SPECIFIC_THROW(booleanObject->AddOperand(beamMesh.get(), wrapper->GetIdentityTransform()), Lib3MF::ELib3MFException); +} + +TEST_F(BooleanRead, ApiAcceptsLevelSetAsBaseObject) +{ + auto model = wrapper->CreateModel(); + auto levelSet = model->AddLevelSet(); + auto operandMesh = addBoxMesh(model); + auto booleanObject = model->AddBooleanObject(); + + booleanObject->SetBaseObject(levelSet.get(), wrapper->GetIdentityTransform()); + booleanObject->AddOperand(operandMesh.get(), wrapper->GetIdentityTransform()); + + ASSERT_EQ(booleanObject->GetBaseObject()->GetResourceID(), levelSet->GetResourceID()); +} + +TEST_F(BooleanRead, ApiRejectsBooleanBaseReferenceCycles) +{ + auto model = wrapper->CreateModel(); + auto mesh = addBoxMesh(model); + auto booleanA = model->AddBooleanObject(); + auto booleanB = model->AddBooleanObject(); + + booleanA->SetBaseObject(booleanB.get(), wrapper->GetIdentityTransform()); + ASSERT_SPECIFIC_THROW(booleanB->SetBaseObject(booleanA.get(), wrapper->GetIdentityTransform()), Lib3MF::ELib3MFException); + + // Keep both objects otherwise structurally valid to ensure failure is from cycle rejection. + booleanA->AddOperand(mesh.get(), wrapper->GetIdentityTransform()); + booleanB->AddOperand(mesh.get(), wrapper->GetIdentityTransform()); +} + +TEST_F(BooleanRead, ApiControlsCSGModeAndExtractionResolution) +{ + auto model = wrapper->CreateModel(); + auto baseMesh = addBoxMesh(model); + auto operandMesh = addBoxMesh(model); + auto booleanObject = model->AddBooleanObject(); + + booleanObject->SetBaseObject(baseMesh.get(), wrapper->GetIdentityTransform()); + booleanObject->SetOperation(Lib3MF::eBooleanOperation::Difference); + booleanObject->AddOperand(operandMesh.get(), wrapper->GetIdentityTransform()); + + ASSERT_FALSE(booleanObject->GetCSGModeEnabled()); + booleanObject->SetCSGModeEnabled(true); + ASSERT_TRUE(booleanObject->GetCSGModeEnabled()); + + booleanObject->SetExtractionGridResolution(144); + ASSERT_EQ(booleanObject->GetExtractionGridResolution(), 144u); + ASSERT_SPECIFIC_THROW(booleanObject->SetExtractionGridResolution(0), Lib3MF::ELib3MFException); +} + +TEST_F(BooleanRead, CreateAndRoundTripBooleanObjectWithExternalReferences) +{ + auto model = wrapper->CreateModel(); + auto baseMesh = addBoxMesh(model); + auto operandMesh = addBoxMesh(model); + auto booleanObject = model->AddBooleanObject(); + auto basePart = model->FindOrCreatePackagePart("/3D/boolean_base.model"); + auto operandPart = model->FindOrCreatePackagePart("/3D/boolean_operand.model"); + + booleanObject->SetBaseObject(baseMesh.get(), wrapper->GetIdentityTransform()); + booleanObject->SetOperation(Lib3MF::eBooleanOperation::Union); + booleanObject->AddOperand(operandMesh.get(), wrapper->GetTranslationTransform(2.0, 0.0, 0.0)); + baseMesh->SetPackagePart(basePart.get()); + operandMesh->SetPackagePart(operandPart.get()); + model->AddBuildItem(booleanObject.get(), wrapper->GetIdentityTransform()); + + std::vector buffer; + model->QueryWriter("3mf")->WriteToBuffer(buffer); + + auto roundTripModel = wrapper->CreateModel(); + auto reader = roundTripModel->QueryReader("3mf"); + reader->ReadFromBuffer(buffer); + CheckReaderWarnings(reader, 0); + + auto booleanObjects = roundTripModel->GetBooleanObjects(); + ASSERT_EQ(booleanObjects->Count(), 1); + ASSERT_TRUE(booleanObjects->MoveNext()); + + auto roundTripBoolean = booleanObjects->GetCurrentBooleanObject(); + ASSERT_EQ(roundTripBoolean->GetBaseObject()->PackagePart()->GetPath(), "/3D/boolean_base.model"); + ASSERT_EQ(roundTripBoolean->GetOperandCount(), 1u); + + Lib3MF::PMeshObject operand; + roundTripBoolean->GetOperand(0, operand); + ASSERT_EQ(operand->PackagePart()->GetPath(), "/3D/boolean_operand.model"); +} + +TEST_F(BooleanRead, WriterRejectsIncompleteBooleanObjects) +{ + { + auto model = wrapper->CreateModel(); + auto booleanObject = model->AddBooleanObject(); + model->AddBuildItem(booleanObject.get(), wrapper->GetIdentityTransform()); + + std::vector buffer; + ASSERT_SPECIFIC_THROW(model->QueryWriter("3mf")->WriteToBuffer(buffer), Lib3MF::ELib3MFException); + } + + { + auto model = wrapper->CreateModel(); + auto baseMesh = addBoxMesh(model); + auto booleanObject = model->AddBooleanObject(); + booleanObject->SetBaseObject(baseMesh.get(), wrapper->GetIdentityTransform()); + model->AddBuildItem(booleanObject.get(), wrapper->GetIdentityTransform()); + + std::vector buffer; + ASSERT_SPECIFIC_THROW(model->QueryWriter("3mf")->WriteToBuffer(buffer), Lib3MF::ELib3MFException); + } +} + +TEST_F(BooleanRead, WriterRejectsDeepBooleanReferences) +{ + auto model = wrapper->CreateModel(); + auto baseMesh = addBoxMesh(model); + auto operandMesh = addBoxMesh(model); + auto booleanObject = model->AddBooleanObject(); + auto nonRootPart = model->FindOrCreatePackagePart("/3D/nonroot_boolean.model"); + + booleanObject->SetBaseObject(baseMesh.get(), wrapper->GetIdentityTransform()); + booleanObject->SetOperation(Lib3MF::eBooleanOperation::Difference); + booleanObject->AddOperand(operandMesh.get(), wrapper->GetIdentityTransform()); + booleanObject->SetPackagePart(nonRootPart.get()); + model->AddBuildItem(booleanObject.get(), wrapper->GetIdentityTransform()); + + std::vector buffer; + ASSERT_SPECIFIC_THROW(model->QueryWriter("3mf")->WriteToBuffer(buffer), Lib3MF::ELib3MFException); +} + +TEST_F(BooleanRead, InvalidOperationYieldsWarningAndDefaultsToUnion) +{ + auto model = wrapper->CreateModel(); + auto reader = model->QueryReader("3mf"); + reader->ReadFromFile(sTestFilesPath + "/" + "Boolean" + "/" + "invalid_operation_warning.3mf"); + CheckReaderWarnings(reader, 1); + + auto booleanObjects = model->GetBooleanObjects(); + ASSERT_EQ(booleanObjects->Count(), 1); + ASSERT_TRUE(booleanObjects->MoveNext()); + ASSERT_EQ(booleanObjects->GetCurrentBooleanObject()->GetOperation(), Lib3MF::eBooleanOperation::Union); +} + +TEST_F(BooleanRead, MissingBaseObjectIDThrows) +{ + auto model = wrapper->CreateModel(); + auto reader = model->QueryReader("3mf"); + ASSERT_SPECIFIC_THROW(reader->ReadFromFile(sTestFilesPath + "/" + "Boolean" + "/" + "missing_base_objectid.3mf"), Lib3MF::ELib3MFException); +} + +TEST_F(BooleanRead, MissingOperandsThrow) +{ + auto model = wrapper->CreateModel(); + auto reader = model->QueryReader("3mf"); + ASSERT_SPECIFIC_THROW(reader->ReadFromFile(sTestFilesPath + "/" + "Boolean" + "/" + "missing_operands.3mf"), Lib3MF::ELib3MFException); +} + +TEST_F(BooleanRead, MissingPathTargetThrows) +{ + auto model = wrapper->CreateModel(); + auto reader = model->QueryReader("3mf"); + ASSERT_SPECIFIC_THROW(reader->ReadFromFile(sTestFilesPath + "/" + "Boolean" + "/" + "missing_path_target.3mf"), Lib3MF::ELib3MFException); +} + +TEST_F(BooleanRead, PathWithoutProductionExtensionThrows) +{ + auto model = wrapper->CreateModel(); + auto reader = model->QueryReader("3mf"); + ASSERT_SPECIFIC_THROW(reader->ReadFromFile(sTestFilesPath + "/" + "Boolean" + "/" + "path_without_production_extension.3mf"), Lib3MF::ELib3MFException); +} + +TEST_F(BooleanRead, ComponentsBaseObjectThrows) +{ + auto model = wrapper->CreateModel(); + auto reader = model->QueryReader("3mf"); + ASSERT_SPECIFIC_THROW(reader->ReadFromFile(sTestFilesPath + "/" + "Boolean" + "/" + "components_base_object.3mf"), Lib3MF::ELib3MFException); +} + +TEST_F(BooleanRead, NonMeshOperandThrows) +{ + auto model = wrapper->CreateModel(); + auto reader = model->QueryReader("3mf"); + ASSERT_SPECIFIC_THROW(reader->ReadFromFile(sTestFilesPath + "/" + "Boolean" + "/" + "components_operand_object.3mf"), Lib3MF::ELib3MFException); +} + +TEST_F(BooleanRead, UnknownBooleanAttributeYieldsWarning) +{ + auto model = wrapper->CreateModel(); + auto reader = model->QueryReader("3mf"); + reader->ReadFromFile(sTestFilesPath + "/" + "Boolean" + "/" + "unknown_attribute_warning.3mf"); + CheckReaderWarnings(reader, 1); + + auto booleanObjects = model->GetBooleanObjects(); + ASSERT_EQ(booleanObjects->Count(), 1); + ASSERT_TRUE(booleanObjects->MoveNext()); + ASSERT_EQ(booleanObjects->GetCurrentBooleanObject()->GetOperandCount(), 1u); +} + +TEST_F(BooleanRead, STLWriterMaterializesUnionBooleanObject) +{ + auto model = wrapper->CreateModel(); + auto baseMesh = addBoxMesh(model); + auto operandMesh = addBoxMesh(model); + auto booleanObject = model->AddBooleanObject(); + + booleanObject->SetBaseObject(baseMesh.get(), wrapper->GetIdentityTransform()); + booleanObject->SetOperation(Lib3MF::eBooleanOperation::Union); + booleanObject->AddOperand(operandMesh.get(), wrapper->GetTranslationTransform(1.0, 0.0, 0.0)); + model->AddBuildItem(booleanObject.get(), wrapper->GetIdentityTransform()); + + std::vector buffer; + model->QueryWriter("stl")->WriteToBuffer(buffer); + ASSERT_FALSE(buffer.empty()); +} + +TEST_F(BooleanRead, STLWriterRejectsNonUnionBooleanWithoutCSG) +{ + auto testOperation = [&](Lib3MF::eBooleanOperation operation) { + auto model = wrapper->CreateModel(); + auto baseMesh = addBoxMesh(model); + auto operandMesh = addBoxMesh(model); + auto booleanObject = model->AddBooleanObject(); + + booleanObject->SetBaseObject(baseMesh.get(), wrapper->GetIdentityTransform()); + booleanObject->SetOperation(operation); + booleanObject->AddOperand(operandMesh.get(), wrapper->GetTranslationTransform(2.0, 0.0, 0.0)); + model->AddBuildItem(booleanObject.get(), wrapper->GetIdentityTransform()); + + std::vector buffer; + ASSERT_SPECIFIC_THROW(model->QueryWriter("stl")->WriteToBuffer(buffer), Lib3MF::ELib3MFException); + }; + + testOperation(Lib3MF::eBooleanOperation::Difference); + testOperation(Lib3MF::eBooleanOperation::Intersection); +} + +TEST_F(BooleanRead, ObjectTypeIntrospectionIsConsistent) +{ + auto model = wrapper->CreateModel(); + auto mesh = addBoxMesh(model); + auto components = model->AddComponentsObject(); + auto booleanObject = model->AddBooleanObject(); + auto levelSet = model->AddLevelSet(); + + booleanObject->SetBaseObject(mesh.get(), wrapper->GetIdentityTransform()); + booleanObject->AddOperand(mesh.get(), wrapper->GetIdentityTransform()); + + ASSERT_TRUE(booleanObject->IsBooleanObject()); + ASSERT_FALSE(booleanObject->IsMeshObject()); + ASSERT_FALSE(booleanObject->IsComponentsObject()); + ASSERT_FALSE(booleanObject->IsLevelSetObject()); + + ASSERT_TRUE(mesh->IsMeshObject()); + ASSERT_FALSE(mesh->IsBooleanObject()); + + ASSERT_TRUE(components->IsComponentsObject()); + ASSERT_FALSE(components->IsBooleanObject()); + + ASSERT_TRUE(levelSet->IsLevelSetObject()); + ASSERT_FALSE(levelSet->IsBooleanObject()); +} + +TEST_F(BooleanRead, WriteBooleanWithHigherResourceDependencies) +{ + auto model = wrapper->CreateModel(); + auto booleanObject = model->AddBooleanObject(); + auto baseMesh = addBoxMesh(model); + auto operandMesh = addBoxMesh(model); + + booleanObject->SetBaseObject(baseMesh.get(), wrapper->GetIdentityTransform()); + booleanObject->SetOperation(Lib3MF::eBooleanOperation::Union); + booleanObject->AddOperand(operandMesh.get(), wrapper->GetIdentityTransform()); + model->AddBuildItem(booleanObject.get(), wrapper->GetIdentityTransform()); + + std::vector buffer; + model->QueryWriter("3mf")->WriteToBuffer(buffer); + ASSERT_FALSE(buffer.empty()); +} + +TEST_F(BooleanRead, ExperimentalCSGPathMaterializesSurface) +{ +#if !defined(LIB3MF_ENABLE_EXPENSIVE_TESTS) || (LIB3MF_ENABLE_EXPENSIVE_TESTS == 0) + GTEST_SKIP() << "Disabled by default. Reconfigure with -DLIB3MF_ENABLE_EXPENSIVE_TESTS=ON to run this test."; +#endif + + auto model = wrapper->CreateModel(); + auto baseMesh = addBoxMesh(model); + auto operandMesh = addBoxMesh(model); + auto booleanObject = model->AddBooleanObject(); + + booleanObject->SetBaseObject(baseMesh.get(), wrapper->GetIdentityTransform()); + booleanObject->SetOperation(Lib3MF::eBooleanOperation::Difference); + booleanObject->AddOperand(operandMesh.get(), wrapper->GetTranslationTransform(0.25, 0.0, 0.0)); + booleanObject->SetCSGModeEnabled(true); + booleanObject->SetExtractionGridResolution(160); + model->AddBuildItem(booleanObject.get(), wrapper->GetIdentityTransform()); + + std::vector buffer; + model->QueryWriter("stl")->WriteToBuffer(buffer); + ASSERT_FALSE(buffer.empty()); +} + +TEST_F(BooleanRead, ReadBooleanFileAndExtractSTLToDisk) +{ +#if !defined(LIB3MF_ENABLE_EXPENSIVE_TESTS) || (LIB3MF_ENABLE_EXPENSIVE_TESTS == 0) + GTEST_SKIP() << "Disabled by default. Reconfigure with -DLIB3MF_ENABLE_EXPENSIVE_TESTS=ON to run this test."; +#endif + + auto model = wrapper->CreateModel(); + auto reader = model->QueryReader("3mf"); + reader->ReadFromFile(sTestFilesPath + "/" + "Boolean" + "/" + "booleans_operations.3mf"); + auto booleanObjects = model->GetBooleanObjects(); + while (booleanObjects->MoveNext()) { + auto booleanObject = booleanObjects->GetCurrentBooleanObject(); + booleanObject->SetCSGModeEnabled(true); + booleanObject->SetExtractionGridResolution(160); + } + + const std::string outputPath = "boolean_from_file_extracted.stl"; + auto stlWriter = model->QueryWriter("stl"); + stlWriter->WriteToFile(outputPath); + + std::ifstream file(outputPath, std::ios::binary | std::ios::ate); + ASSERT_TRUE(file.good()); + ASSERT_GT(file.tellg(), 0); +} diff --git a/Tests/CPP_Bindings/Source/Wrapper.cpp b/Tests/CPP_Bindings/Source/Wrapper.cpp index 11903a46b..2e27b8278 100644 --- a/Tests/CPP_Bindings/Source/Wrapper.cpp +++ b/Tests/CPP_Bindings/Source/Wrapper.cpp @@ -124,6 +124,12 @@ namespace Lib3MF ASSERT_EQ(nMajor, 0); ASSERT_EQ(nMinor, 8); ASSERT_EQ(nMicro, 0); + + Lib3MFTest::wrapper->GetSpecificationVersion("http://schemas.3mf.io/3dmanufacturing/booleanoperations/2023/07", bIsSupported, nMajor, nMinor, nMicro); + ASSERT_TRUE(bIsSupported); + ASSERT_EQ(nMajor, 1); + ASSERT_EQ(nMinor, 1); + ASSERT_EQ(nMicro, 1); } TEST(Wrapper, CreateModel) diff --git a/Tests/TestFiles/Boolean/booleans_operations.3mf b/Tests/TestFiles/Boolean/booleans_operations.3mf new file mode 100644 index 000000000..f5390bc03 Binary files /dev/null and b/Tests/TestFiles/Boolean/booleans_operations.3mf differ diff --git a/Tests/TestFiles/Boolean/components_base_object.3mf b/Tests/TestFiles/Boolean/components_base_object.3mf new file mode 100644 index 000000000..55623b7d7 Binary files /dev/null and b/Tests/TestFiles/Boolean/components_base_object.3mf differ diff --git a/Tests/TestFiles/Boolean/components_operand_object.3mf b/Tests/TestFiles/Boolean/components_operand_object.3mf new file mode 100644 index 000000000..2eb6f37b5 Binary files /dev/null and b/Tests/TestFiles/Boolean/components_operand_object.3mf differ diff --git a/Tests/TestFiles/Boolean/invalid_operation_warning.3mf b/Tests/TestFiles/Boolean/invalid_operation_warning.3mf new file mode 100644 index 000000000..394cf867a Binary files /dev/null and b/Tests/TestFiles/Boolean/invalid_operation_warning.3mf differ diff --git a/Tests/TestFiles/Boolean/missing_base_objectid.3mf b/Tests/TestFiles/Boolean/missing_base_objectid.3mf new file mode 100644 index 000000000..57de5d442 Binary files /dev/null and b/Tests/TestFiles/Boolean/missing_base_objectid.3mf differ diff --git a/Tests/TestFiles/Boolean/missing_operands.3mf b/Tests/TestFiles/Boolean/missing_operands.3mf new file mode 100644 index 000000000..43de9360b Binary files /dev/null and b/Tests/TestFiles/Boolean/missing_operands.3mf differ diff --git a/Tests/TestFiles/Boolean/missing_path_target.3mf b/Tests/TestFiles/Boolean/missing_path_target.3mf new file mode 100644 index 000000000..87cf511ef Binary files /dev/null and b/Tests/TestFiles/Boolean/missing_path_target.3mf differ diff --git a/Tests/TestFiles/Boolean/path_without_production_extension.3mf b/Tests/TestFiles/Boolean/path_without_production_extension.3mf new file mode 100644 index 000000000..e63c376f6 Binary files /dev/null and b/Tests/TestFiles/Boolean/path_without_production_extension.3mf differ diff --git a/Tests/TestFiles/Boolean/unknown_attribute_warning.3mf b/Tests/TestFiles/Boolean/unknown_attribute_warning.3mf new file mode 100644 index 000000000..9fb956efa Binary files /dev/null and b/Tests/TestFiles/Boolean/unknown_attribute_warning.3mf differ diff --git a/docs/third_party_patches.md b/docs/third_party_patches.md new file mode 100644 index 000000000..5dca3fdcc --- /dev/null +++ b/docs/third_party_patches.md @@ -0,0 +1,46 @@ +# Third-Party Local Patches + +This file tracks local edits made to vendored third-party code in this repository. +Goal: keep these patches minimal, auditable, and removable once upstreamed. + +## tinybvh + +### Why patched + +`lib3mf` currently vendors tinybvh in: + +- `Libraries/tinybvh/Include/tiny_bvh.h` + +Two issues required local fixes for CI/toolchain stability: + +1. WASM/Emscripten build failure: + tinybvh selected `_aligned_malloc` in an Emscripten path where it is unavailable. +2. MinGW stability concern in heavy CSG tests: + thread-spawning behavior in tinybvh build path is a likely crash source on some toolchains. + +### Local changes + +1. **WASM allocator fix** (vendor header patch): + file: `Libraries/tinybvh/Include/tiny_bvh.h` + change: treat `__EMSCRIPTEN__` like Linux for aligned allocation macro selection. + +2. **Threaded tinybvh build control** (CMake option): + file: `CMakeLists.txt` + option: `LIB3MF_TINYBVH_THREADED_BUILDS` + behavior: + - defaults `OFF` on `MINGW` and `EMSCRIPTEN` + - defaults `ON` elsewhere + - when `OFF`, `NO_THREADED_BUILDS=1` is passed as a compile definition. + +### Notes + +- These changes are intentionally minimal. +- This is a temporary compatibility patch set; preferred long-term state is to consume upstream tinybvh fixes. + +### Removal criteria + +Remove local patches once all are true: + +1. Upstream tinybvh includes a proper Emscripten allocator path. +2. MinGW CI is stable for boolean CSG tests without `NO_THREADED_BUILDS`. +3. `lib3mf` CI passes on Linux/Windows/MinGW/WASM after removing the local edits. diff --git a/patch_wasm_bindings.py b/patch_wasm_bindings.py new file mode 100644 index 000000000..eaf6fde3b --- /dev/null +++ b/patch_wasm_bindings.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 + +import re +import sys +from pathlib import Path + + +DEFAULT_BINDING = Path("Autogenerated/Bindings/WASM/lib3mf_bindings.cpp") + + +BOOLEAN_BROKEN = """static emscripten::val wrap_BooleanObject_GetOperand(CBooleanObject &self, const Lib3MF_uint32& Index) { + emscripten::val output = emscripten::val::object(); + PMeshObject OperandObject; + Lib3MF_struct return_value = self.GetOperand(Index, OperandObject); + output.set("return", return_value); + output.set("OperandObject", OperandObject); + return output; +}""" + +BOOLEAN_FIXED = """static emscripten::val wrap_BooleanObject_GetOperand(CBooleanObject &self, const Lib3MF_uint32& Index) { + emscripten::val output = emscripten::val::object(); + PMeshObject OperandObject; + sTransformWrapper return_value; + return_value.value = self.GetOperand(Index, OperandObject); + output.set("return", return_value); + output.set("OperandObject", OperandObject); + return output; +}""" + + +VECTOR_STRUCT_PATCHES = [ + ( + """static emscripten::val wrap_MeshObject_GetVertices(CMeshObject &self) { + emscripten::val output = emscripten::val::object(); + std::vector Vertices; + self.GetVertices(Vertices); + output.set("Vertices", Vertices); + return output; +}""", + """static emscripten::val wrap_MeshObject_GetVertices(CMeshObject &self) { + emscripten::val output = emscripten::val::object(); + std::vector Vertices; + self.GetVertices(Vertices); + std::vector wrapped_Vertices; + wrapped_Vertices.reserve(Vertices.size()); + for (const auto& v : Vertices) wrapped_Vertices.push_back(sPositionWrapper{v}); + output.set("Vertices", wrapped_Vertices); + return output; +}""", + ), + ( + """static emscripten::val wrap_MeshObject_GetTriangleIndices(CMeshObject &self) { + emscripten::val output = emscripten::val::object(); + std::vector Indices; + self.GetTriangleIndices(Indices); + output.set("Indices", Indices); + return output; +}""", + """static emscripten::val wrap_MeshObject_GetTriangleIndices(CMeshObject &self) { + emscripten::val output = emscripten::val::object(); + std::vector Indices; + self.GetTriangleIndices(Indices); + std::vector wrapped_Indices; + wrapped_Indices.reserve(Indices.size()); + for (const auto& v : Indices) wrapped_Indices.push_back(sTriangleWrapper{v}); + output.set("Indices", wrapped_Indices); + return output; +}""", + ), + ( + """static emscripten::val wrap_MeshObject_GetAllTriangleProperties(CMeshObject &self) { + emscripten::val output = emscripten::val::object(); + std::vector PropertiesArray; + self.GetAllTriangleProperties(PropertiesArray); + output.set("PropertiesArray", PropertiesArray); + return output; +}""", + """static emscripten::val wrap_MeshObject_GetAllTriangleProperties(CMeshObject &self) { + emscripten::val output = emscripten::val::object(); + std::vector PropertiesArray; + self.GetAllTriangleProperties(PropertiesArray); + std::vector wrapped_PropertiesArray; + wrapped_PropertiesArray.reserve(PropertiesArray.size()); + for (const auto& v : PropertiesArray) wrapped_PropertiesArray.push_back(sTrianglePropertiesWrapper{v}); + output.set("PropertiesArray", wrapped_PropertiesArray); + return output; +}""", + ), + ( + """static emscripten::val wrap_BeamLattice_GetBeams(CBeamLattice &self) { + emscripten::val output = emscripten::val::object(); + std::vector BeamInfo; + self.GetBeams(BeamInfo); + output.set("BeamInfo", BeamInfo); + return output; +}""", + """static emscripten::val wrap_BeamLattice_GetBeams(CBeamLattice &self) { + emscripten::val output = emscripten::val::object(); + std::vector BeamInfo; + self.GetBeams(BeamInfo); + std::vector wrapped_BeamInfo; + wrapped_BeamInfo.reserve(BeamInfo.size()); + for (const auto& v : BeamInfo) wrapped_BeamInfo.push_back(sBeamWrapper{v}); + output.set("BeamInfo", wrapped_BeamInfo); + return output; +}""", + ), + ( + """static emscripten::val wrap_BeamLattice_GetBalls(CBeamLattice &self) { + emscripten::val output = emscripten::val::object(); + std::vector BallInfo; + self.GetBalls(BallInfo); + output.set("BallInfo", BallInfo); + return output; +}""", + """static emscripten::val wrap_BeamLattice_GetBalls(CBeamLattice &self) { + emscripten::val output = emscripten::val::object(); + std::vector BallInfo; + self.GetBalls(BallInfo); + std::vector wrapped_BallInfo; + wrapped_BallInfo.reserve(BallInfo.size()); + for (const auto& v : BallInfo) wrapped_BallInfo.push_back(sBallWrapper{v}); + output.set("BallInfo", wrapped_BallInfo); + return output; +}""", + ), + ( + """static emscripten::val wrap_CompositeMaterials_GetComposite(CCompositeMaterials &self, const Lib3MF_uint32& PropertyID) { + emscripten::val output = emscripten::val::object(); + std::vector Composite; + self.GetComposite(PropertyID, Composite); + output.set("Composite", Composite); + return output; +}""", + """static emscripten::val wrap_CompositeMaterials_GetComposite(CCompositeMaterials &self, const Lib3MF_uint32& PropertyID) { + emscripten::val output = emscripten::val::object(); + std::vector Composite; + self.GetComposite(PropertyID, Composite); + std::vector wrapped_Composite; + wrapped_Composite.reserve(Composite.size()); + for (const auto& v : Composite) wrapped_Composite.push_back(sCompositeConstituentWrapper{v}); + output.set("Composite", wrapped_Composite); + return output; +}""", + ), + ( + """static emscripten::val wrap_Slice_GetVertices(CSlice &self) { + emscripten::val output = emscripten::val::object(); + std::vector Vertices; + self.GetVertices(Vertices); + output.set("Vertices", Vertices); + return output; +}""", + """static emscripten::val wrap_Slice_GetVertices(CSlice &self) { + emscripten::val output = emscripten::val::object(); + std::vector Vertices; + self.GetVertices(Vertices); + std::vector wrapped_Vertices; + wrapped_Vertices.reserve(Vertices.size()); + for (const auto& v : Vertices) wrapped_Vertices.push_back(sPosition2DWrapper{v}); + output.set("Vertices", wrapped_Vertices); + return output; +}""", + ), +] + + +def apply_literal_patch(contents: str, old: str, new: str, description: str) -> tuple[str, bool]: + if new in contents: + return contents, False + if old not in contents: + raise RuntimeError(f"missing expected pattern for {description}") + return contents.replace(old, new, 1), True + + +def main() -> int: + target = Path(sys.argv[1]) if len(sys.argv) > 1 else DEFAULT_BINDING + contents = target.read_text(encoding="utf-8") + changed = False + + contents, local_changed = apply_literal_patch( + contents, + BOOLEAN_BROKEN, + BOOLEAN_FIXED, + "BooleanObject_GetOperand", + ) + changed = changed or local_changed + + for old, new in VECTOR_STRUCT_PATCHES: + contents, local_changed = apply_literal_patch( + contents, + old, + new, + old.split("{", 1)[0].strip(), + ) + changed = changed or local_changed + + if changed: + target.write_text(contents, encoding="utf-8") + print(f"{target}: patched WASM bindings") + else: + print(f"{target}: already patched") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())