// ======================================================================== // // Copyright 2009-2017 Intel Corporation // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // // You may obtain a copy of the License at // // // // http://www.apache.org/licenses/LICENSE-2.0 // // // // Unless required by applicable law or agreed to in writing, software // // distributed under the License is distributed on an "AS IS" BASIS, // // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // // See the License for the specific language governing permissions and // // limitations under the License. // // ======================================================================== // #pragma once #include "default.h" namespace embree { class Scene; /*! Base class all geometries are derived from */ class Geometry { friend class Scene; public: /*! type of geometry */ enum Type { TRIANGLE_MESH = 1, USER_GEOMETRY = 2, BEZIER_CURVES = 4, SUBDIV_MESH = 8, INSTANCE = 16, QUAD_MESH = 32, LINE_SEGMENTS = 64 }; public: /*! Geometry constructor */ Geometry (Scene* scene, Type type, size_t numPrimitives, size_t numTimeSteps, RTCGeometryFlags flags); /*! Geometry destructor */ virtual ~Geometry(); /*! updates intersection filter function counts in scene */ void updateIntersectionFilters(bool enable); public: /*! tests if geometry is enabled */ __forceinline bool isEnabled() const { return numPrimitives && enabled; } /*! tests if geometry is disabled */ __forceinline bool isDisabled() const { return !isEnabled(); } /*! tests if geomery is used by any instance (including world space instance) */ __forceinline bool isUsed() const { return used; } /*! tests if geometry is used by any non-world space instance */ __forceinline bool isInstanced() const { return used-enabled; } /*! tests if geometry is modified */ __forceinline bool isModified() const { return numPrimitives && modified; } /*! clears modified flag */ __forceinline void clearModified() { modified = false; } /*! test if this is a static geometry */ __forceinline bool isStatic() const { return flags == RTC_GEOMETRY_STATIC; } /*! test if this is a deformable geometry */ __forceinline bool isDeformable() const { return flags == RTC_GEOMETRY_DEFORMABLE; } /*! test if this is a dynamic geometry */ __forceinline bool isDynamic() const { return flags == RTC_GEOMETRY_DYNAMIC; } /*! returns geometry type */ __forceinline Type getType() const { return type; } /*! returns number of primitives */ __forceinline size_t size() const { return numPrimitives; } /*! sets the number of primitives */ __forceinline void setNumPrimitives(size_t numPrimitives_in) { if ((ssize_t)numPrimitives_in == -1) return; if (numPrimitives_in == numPrimitives) return; numPrimitives = numPrimitives_in; numPrimitivesChanged = true; } /*! for all geometries */ public: /*! Enable geometry. */ virtual void enable (); /*! Update geometry. */ virtual void update (); /*! Update geometry buffer. */ virtual void updateBuffer (RTCBufferType type) { update(); // update everything for geometries not supporting this call } /*! Disable geometry. */ virtual void disable (); /*! Free buffers that are unused */ virtual void immutable () {} /*! Verify the geometry */ virtual bool verify () { return true; } /*! called if geometry is switching from disabled to enabled state */ virtual void enabling() = 0; /*! called if geometry is switching from enabled to disabled state */ virtual void disabling() = 0; /*! called before every build */ virtual void preCommit(); /*! called after every build */ virtual void postCommit(); /*! sets constant tessellation rate for the geometry */ virtual void setTessellationRate(float N) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set user data pointer. */ virtual void setUserData (void* ptr); /*! Get user data pointer. */ __forceinline void* getUserData() const { return userPtr; } /*! interpolates user data to the specified u/v location */ virtual void interpolate(unsigned primID, float u, float v, RTCBufferType buffer, float* P, float* dPdu, float* dPdv, float* ddPdudu, float* ddPdvdv, float* ddPdudv, size_t numFloats) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! interpolates user data to the specified u/v locations */ virtual void interpolateN(const void* valid_i, const unsigned* primIDs, const float* u, const float* v, size_t numUVs, RTCBufferType buffer, float* P, float* dPdu, float* dPdv, float* ddPdudu, float* ddPdvdv, float* ddPdudv, size_t numFloats); /*! for subdivision surfaces only */ public: virtual void setSubdivisionMode (unsigned topologyID, RTCSubdivisionMode mode) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } virtual void setIndexBuffer(RTCBufferType vertexBuffer, RTCBufferType indexBuffer) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! for triangle meshes and bezier curves only */ public: /*! Sets ray mask. */ virtual void setMask (unsigned mask) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Maps specified buffer. */ virtual void* map(RTCBufferType type) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); return nullptr; } /*! Unmap specified buffer. */ virtual void unmap(RTCBufferType type) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Sets specified buffer. */ virtual void setBuffer(RTCBufferType type, void* ptr, size_t offset, size_t stride, size_t size) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set displacement function. */ virtual void setDisplacementFunction (RTCDisplacementFunc filter, RTCBounds* bounds) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set displacement function. */ virtual void setDisplacementFunction2 (RTCDisplacementFunc2 filter, RTCBounds* bounds) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set intersection filter function for single rays. */ virtual void setIntersectionFilterFunction (RTCFilterFunc filter, bool ispc = false); /*! Set intersection filter function for ray packets of size 4. */ virtual void setIntersectionFilterFunction4 (RTCFilterFunc4 filter4, bool ispc = false); /*! Set intersection filter function for ray packets of size 8. */ virtual void setIntersectionFilterFunction8 (RTCFilterFunc8 filter8, bool ispc = false); /*! Set intersection filter function for ray packets of size 16. */ virtual void setIntersectionFilterFunction16 (RTCFilterFunc16 filter16, bool ispc = false); /*! Set intersection filter function for ray packets of size N. */ virtual void setIntersectionFilterFunctionN (RTCFilterFuncN filterN); /*! Set occlusion filter function for single rays. */ virtual void setOcclusionFilterFunction (RTCFilterFunc filter, bool ispc = false); /*! Set occlusion filter function for ray packets of size 4. */ virtual void setOcclusionFilterFunction4 (RTCFilterFunc4 filter4, bool ispc = false); /*! Set occlusion filter function for ray packets of size 8. */ virtual void setOcclusionFilterFunction8 (RTCFilterFunc8 filter8, bool ispc = false); /*! Set occlusion filter function for ray packets of size 16. */ virtual void setOcclusionFilterFunction16 (RTCFilterFunc16 filter16, bool ispc = false); /*! Set occlusion filter function for ray packets of size N. */ virtual void setOcclusionFilterFunctionN (RTCFilterFuncN filterN); /*! for instances only */ public: /*! Sets transformation of the instance */ virtual void setTransform(const AffineSpace3fa& transform, size_t timeStep) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! for user geometries only */ public: /*! Set bounds function. */ virtual void setBoundsFunction (RTCBoundsFunc bounds) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set bounds function. */ virtual void setBoundsFunction2 (RTCBoundsFunc2 bounds, void* userPtr) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set bounds function. */ virtual void setBoundsFunction3 (RTCBoundsFunc3 bounds, void* userPtr) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set intersect function for single rays. */ virtual void setIntersectFunction (RTCIntersectFunc intersect, bool ispc = false) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set intersect function for ray packets of size 4. */ virtual void setIntersectFunction4 (RTCIntersectFunc4 intersect4, bool ispc = false) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set intersect function for ray packets of size 8. */ virtual void setIntersectFunction8 (RTCIntersectFunc8 intersect8, bool ispc = false) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set intersect function for ray packets of size 16. */ virtual void setIntersectFunction16 (RTCIntersectFunc16 intersect16, bool ispc = false) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set intersect function for streams of single rays. */ virtual void setIntersectFunction1Mp (RTCIntersectFunc1Mp intersect) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set intersect function for ray packets of size N. */ virtual void setIntersectFunctionN (RTCIntersectFuncN intersect) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set occlusion function for single rays. */ virtual void setOccludedFunction (RTCOccludedFunc occluded, bool ispc = false) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set occlusion function for ray packets of size 4. */ virtual void setOccludedFunction4 (RTCOccludedFunc4 occluded4, bool ispc = false) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set occlusion function for ray packets of size 8. */ virtual void setOccludedFunction8 (RTCOccludedFunc8 occluded8, bool ispc = false) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set occlusion function for ray packets of size 16. */ virtual void setOccludedFunction16 (RTCOccludedFunc16 occluded16, bool ispc = false) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set occlusion function for streams of single rays. */ virtual void setOccludedFunction1Mp (RTCOccludedFunc1Mp occluded) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! Set occlusion function for ray packets of size N. */ virtual void setOccludedFunctionN (RTCOccludedFuncN occluded) { throw_RTCError(RTC_INVALID_OPERATION,"operation not supported for this geometry"); } /*! returns number of time segments */ __forceinline unsigned numTimeSegments () const { return numTimeSteps-1; } public: __forceinline bool hasIntersectionFilter1() const { return (hasIntersectionFilterMask & (HAS_FILTER1 | HAS_FILTERN)) != 0; } __forceinline bool hasOcclusionFilter1 () const { return (hasOcclusionFilterMask & (HAS_FILTER1 | HAS_FILTERN)) != 0; } template __forceinline bool hasIntersectionFilter() const; template __forceinline bool hasOcclusionFilter() const; template __forceinline bool hasISPCIntersectionFilter() const; template __forceinline bool hasISPCOcclusionFilter() const; public: /*! calculates the linear bounds of a primitive at the itimeGlobal'th time segment */ template __forceinline static LBBox3fa linearBounds(const BoundsFunc& bounds, size_t itimeGlobal, size_t numTimeStepsGlobal, size_t numTimeSteps) { if (numTimeStepsGlobal == numTimeSteps) { const BBox3fa bounds0 = bounds(itimeGlobal+0); const BBox3fa bounds1 = bounds(itimeGlobal+1); return LBBox3fa(bounds0, bounds1); } const int timeSegments = int(numTimeSteps-1); const int timeSegmentsGlobal = int(numTimeStepsGlobal-1); const float invTimeSegmentsGlobal = rcp(float(timeSegmentsGlobal)); const int itimeScaled = int(itimeGlobal) * timeSegments; const int itime0 = itimeScaled / timeSegmentsGlobal; const int rtime0 = itimeScaled % timeSegmentsGlobal; const float ftime0 = float(rtime0) * invTimeSegmentsGlobal; const int rtime1 = rtime0 + timeSegments; if (rtime1 <= timeSegmentsGlobal) { const BBox3fa b0 = bounds(itime0+0); const BBox3fa b1 = bounds(itime0+1); const float ftime1 = float(rtime1) * invTimeSegmentsGlobal; return LBBox3fa(lerp(b0, b1, ftime0), lerp(b0, b1, ftime1)); } const BBox3fa b0 = bounds(itime0+0); const BBox3fa b1 = bounds(itime0+1); const BBox3fa b2 = bounds(itime0+2); const float ftime1 = float(rtime1-timeSegmentsGlobal) * invTimeSegmentsGlobal; BBox3fa bounds0 = lerp(b0, b1, ftime0); BBox3fa bounds1 = lerp(b1, b2, ftime1); const BBox3fa b1Lerp = lerp(bounds0, bounds1, float(timeSegmentsGlobal-rtime0) * rcp(float(timeSegments))); bounds0.lower = min(bounds0.lower, bounds0.lower - (b1Lerp.lower - b1.lower)); bounds1.lower = min(bounds1.lower, bounds1.lower - (b1Lerp.lower - b1.lower)); bounds0.upper = max(bounds0.upper, bounds0.upper + (b1.upper - b1Lerp.upper)); bounds1.upper = max(bounds1.upper, bounds1.upper + (b1.upper - b1Lerp.upper)); return LBBox3fa(bounds0, bounds1); } /*! calculates the linear bounds of a primitive at the itimeGlobal'th time segment, if it's valid */ template __forceinline static bool linearBounds(const BoundsFunc& bounds, size_t itimeGlobal, size_t numTimeStepsGlobal, size_t numTimeSteps, LBBox3fa& lbbox) { if (numTimeStepsGlobal == numTimeSteps) { BBox3fa bounds0; if (unlikely(!bounds(itimeGlobal+0, bounds0))) return false; BBox3fa bounds1; if (unlikely(!bounds(itimeGlobal+1, bounds1))) return false; lbbox = LBBox3fa(bounds0, bounds1); return true; } const int timeSegments = int(numTimeSteps-1); const int timeSegmentsGlobal = int(numTimeStepsGlobal-1); const float invTimeSegmentsGlobal = rcp(float(timeSegmentsGlobal)); const int itimeScaled = int(itimeGlobal) * timeSegments; const int itime0 = itimeScaled / timeSegmentsGlobal; const int rtime0 = itimeScaled % timeSegmentsGlobal; const float ftime0 = float(rtime0) * invTimeSegmentsGlobal; const int rtime1 = rtime0 + timeSegments; if (rtime1 <= timeSegmentsGlobal) { BBox3fa b0; if (unlikely(!bounds(itime0+0, b0))) return false; BBox3fa b1; if (unlikely(!bounds(itime0+1, b1))) return false; const float ftime1 = float(rtime1) * invTimeSegmentsGlobal; lbbox = LBBox3fa(lerp(b0, b1, ftime0), lerp(b0, b1, ftime1)); return true; } BBox3fa b0; if (unlikely(!bounds(itime0+0, b0))) return false; BBox3fa b1; if (unlikely(!bounds(itime0+1, b1))) return false; BBox3fa b2; if (unlikely(!bounds(itime0+2, b2))) return false; const float ftime1 = float(rtime1-timeSegmentsGlobal) * invTimeSegmentsGlobal; BBox3fa bounds0 = lerp(b0, b1, ftime0); BBox3fa bounds1 = lerp(b1, b2, ftime1); const BBox3fa b1Lerp = lerp(bounds0, bounds1, float(timeSegmentsGlobal-rtime0) * rcp(float(timeSegments))); bounds0.lower = min(bounds0.lower, bounds0.lower - (b1Lerp.lower - b1.lower)); bounds1.lower = min(bounds1.lower, bounds1.lower - (b1Lerp.lower - b1.lower)); bounds0.upper = max(bounds0.upper, bounds0.upper + (b1.upper - b1Lerp.upper)); bounds1.upper = max(bounds1.upper, bounds1.upper + (b1.upper - b1Lerp.upper)); lbbox = LBBox3fa(bounds0, bounds1); return true; } /*! calculates the build bounds of a primitive at the itimeGlobal'th time segment, if it's valid */ template __forceinline static bool buildBounds(const BoundsFunc& bounds, size_t itimeGlobal, size_t numTimeStepsGlobal, size_t numTimeSteps, BBox3fa& bbox) { LBBox3fa lbbox; if (!linearBounds(bounds, itimeGlobal, numTimeStepsGlobal, numTimeSteps, lbbox)) return false; bbox = 0.5f * (lbbox.bounds0 + lbbox.bounds1); return true; } /*! checks if a primitive is valid at the itimeGlobal'th time segment */ template __forceinline static bool validLinearBounds(const ValidFunc& valid, size_t itimeGlobal, size_t numTimeStepsGlobal, size_t numTimeSteps) { if (numTimeStepsGlobal == numTimeSteps) { if (unlikely(!valid(itimeGlobal+0))) return false; if (unlikely(!valid(itimeGlobal+1))) return false; return true; } const int timeSegments = int(numTimeSteps-1); const int timeSegmentsGlobal = int(numTimeStepsGlobal-1); const int itimeScaled = int(itimeGlobal) * timeSegments; const int itime0 = itimeScaled / timeSegmentsGlobal; const int rtime0 = itimeScaled % timeSegmentsGlobal; const int rtime1 = rtime0 + timeSegments; if (rtime1 <= timeSegmentsGlobal) { if (unlikely(!valid(itime0+0))) return false; if (unlikely(!valid(itime0+1))) return false; return true; } if (unlikely(!valid(itime0+0))) return false; if (unlikely(!valid(itime0+1))) return false; if (unlikely(!valid(itime0+2))) return false; return true; } public: Scene* parent; //!< pointer to scene this mesh belongs to unsigned id; //!< internal geometry ID Type type; //!< geometry type size_t numPrimitives; //!< number of primitives of this geometry bool numPrimitivesChanged; //!< true if number of primitives changed unsigned numTimeSteps; //!< number of time steps float fnumTimeSegments; //!< number of time segments (precalculation) RTCGeometryFlags flags; //!< flags of geometry bool enabled; //!< true if geometry is enabled bool modified; //!< true if geometry is modified void* userPtr; //!< user pointer unsigned mask; //!< for masking out geometry std::atomic used; //!< counts by how many enabled instances this geometry is used public: RTCFilterFunc intersectionFilter1; RTCFilterFunc occlusionFilter1; RTCFilterFunc4 intersectionFilter4; RTCFilterFunc4 occlusionFilter4; RTCFilterFunc8 intersectionFilter8; RTCFilterFunc8 occlusionFilter8; RTCFilterFunc16 intersectionFilter16; RTCFilterFunc16 occlusionFilter16; RTCFilterFuncN intersectionFilterN; RTCFilterFuncN occlusionFilterN; public: enum { HAS_FILTER1 = 1, HAS_FILTER4 = 2, HAS_FILTER8 = 4, HAS_FILTER16 = 8, HAS_FILTERN = 16 }; int hasIntersectionFilterMask; int hasOcclusionFilterMask; int ispcIntersectionFilterMask; int ispcOcclusionFilterMask; }; #if defined(__SSE__) template<> __forceinline bool Geometry::hasIntersectionFilter() const { return (hasIntersectionFilterMask & (HAS_FILTER4 | HAS_FILTERN)) != 0; } template<> __forceinline bool Geometry::hasOcclusionFilter () const { return (hasOcclusionFilterMask & (HAS_FILTER4 | HAS_FILTERN)) != 0; } template<> __forceinline bool Geometry::hasISPCIntersectionFilter() const { return (ispcIntersectionFilterMask & HAS_FILTER4) != 0; } template<> __forceinline bool Geometry::hasISPCOcclusionFilter () const { return (ispcOcclusionFilterMask & HAS_FILTER4) != 0; } #endif #if defined(__AVX__) template<> __forceinline bool Geometry::hasIntersectionFilter() const { return (hasIntersectionFilterMask & (HAS_FILTER8 | HAS_FILTERN)) != 0; } template<> __forceinline bool Geometry::hasOcclusionFilter () const { return (hasOcclusionFilterMask & (HAS_FILTER8 | HAS_FILTERN)) != 0; } template<> __forceinline bool Geometry::hasISPCIntersectionFilter() const { return (ispcIntersectionFilterMask & HAS_FILTER8) != 0; } template<> __forceinline bool Geometry::hasISPCOcclusionFilter () const { return (ispcOcclusionFilterMask & HAS_FILTER8) != 0; } #endif #if defined(__AVX512F__) template<> __forceinline bool Geometry::hasIntersectionFilter() const { return (hasIntersectionFilterMask & (HAS_FILTER16 | HAS_FILTERN)) != 0; } template<> __forceinline bool Geometry::hasOcclusionFilter () const { return (hasOcclusionFilterMask & (HAS_FILTER16 | HAS_FILTERN)) != 0; } template<> __forceinline bool Geometry::hasISPCIntersectionFilter() const { return (ispcIntersectionFilterMask & HAS_FILTER16) != 0; } template<> __forceinline bool Geometry::hasISPCOcclusionFilter () const { return (ispcOcclusionFilterMask & HAS_FILTER16) != 0; } #endif /* calculate time segment itime and fractional time ftime */ __forceinline int getTimeSegment(float time, float numTimeSegments, float& ftime) { const float timeScaled = time * numTimeSegments; const float itimef = clamp(floor(timeScaled), 0.0f, numTimeSegments-1.0f); ftime = timeScaled - itimef; return int(itimef); } template __forceinline vint getTimeSegment(const vfloat& time, const vfloat& numTimeSegments, vfloat& ftime) { const vfloat timeScaled = time * numTimeSegments; const vfloat itimef = clamp(floor(timeScaled), vfloat(zero), numTimeSegments-1.0f); ftime = timeScaled - itimef; return vint(itimef); } }