diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 079c63f367..945bb3b9e6 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -25,7 +25,10 @@ LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema); } -void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth, float farDepth) { +void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, + float viewMinShadowDistance, float viewMaxShadowDistance, + float nearDepth, float farDepth) { + assert(viewMinShadowDistance < viewMaxShadowDistance); assert(nearDepth < farDepth); // Orient the keylight frustum @@ -48,8 +51,8 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa const Transform view{ _frustum->getView()}; const Transform viewInverse{ view.getInverseMatrix() }; - auto nearCorners = viewFrustum.getCorners(nearDepth); - auto farCorners = viewFrustum.getCorners(farDepth); + auto nearCorners = viewFrustum.getCorners(viewMinShadowDistance); + auto farCorners = viewFrustum.getCorners(viewMaxShadowDistance); vec3 min{ viewInverse.transform(nearCorners.bottomLeft) }; vec3 max{ min }; @@ -73,6 +76,8 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa fitFrustum(farCorners.topLeft); fitFrustum(farCorners.topRight); + // Re-adjust near shadow distance to + max.z = glm::max(max.z, -nearDepth); glm::mat4 ortho = glm::ortho(min.x, max.x, min.y, max.y, -max.z, -min.z); _frustum->setProjection(ortho); @@ -84,6 +89,16 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, floa _schemaBuffer.edit().viewInverse = viewInverse.getMatrix(); } +void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) { + const Transform view{ shadowFrustum.getView() }; + const Transform viewInverse{ view.getInverseMatrix() }; + + *_frustum = shadowFrustum; + // Update the buffer + _schemaBuffer.edit().projection = shadowFrustum.getProjection(); + _schemaBuffer.edit().viewInverse = viewInverse.getMatrix(); +} + const glm::mat4& LightStage::Shadow::getView() const { return _frustum->getView(); } diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 66d73c9a6e..a6e369c2f4 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -48,8 +48,9 @@ public: Shadow(model::LightPointer light); - void setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth, float farDepth); + void setKeylightFrustum(const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f); + void setFrustum(const ViewFrustum& shadowFrustum); const std::shared_ptr getFrustum() const { return _frustum; } const glm::mat4& getView() const; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 7171543abc..80b988d055 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -22,25 +22,131 @@ #include "DeferredLightingEffect.h" #include "FramebufferCache.h" +#define SHADOW_FRUSTUM_NEAR 1.0f +#define SHADOW_FRUSTUM_FAR 100.0f + using namespace render; extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state); -void RenderShadowMap::run(const render::RenderContextPointer& renderContext, - const render::ShapeBounds& inShapes) { +static void computeNearFar(const Triangle& triangle, const Plane shadowClipPlanes[4], float& near, float& far) { + static const int MAX_TRIANGLE_COUNT = 16; + Triangle clippedTriangles[MAX_TRIANGLE_COUNT]; + auto clippedTriangleCount = clipTriangleWithPlanes(triangle, shadowClipPlanes, 4, clippedTriangles, MAX_TRIANGLE_COUNT); + + for (auto i = 0; i < clippedTriangleCount; i++) { + const auto& clippedTriangle = clippedTriangles[i]; + + near = glm::min(near, -clippedTriangle.v0.z); + near = glm::min(near, -clippedTriangle.v1.z); + near = glm::min(near, -clippedTriangle.v2.z); + + far = glm::max(far, -clippedTriangle.v0.z); + far = glm::max(far, -clippedTriangle.v1.z); + far = glm::max(far, -clippedTriangle.v2.z); + } +} + +static void computeNearFar(const glm::vec3 sceneBoundVertices[8], const Plane shadowClipPlanes[4], float& near, float& far) { + // This code is inspired from Microsoft's CascadedShadowMaps11 sample which is under MIT licence. + // See https://code.msdn.microsoft.com/windowsdesktop/Direct3D-Shadow-Win32-2d72a4f2/sourcecode?fileId=121915&pathId=1645833187 + // Basically it decomposes the object bounding box in triangles and clips each triangle with the shadow + // frustum planes. Finally it computes the minimum and maximum depth of the clipped triangle vertices + // in shadow space to extract the near and far distances of the shadow frustum. + static const std::array boxQuadVertexIndices = { { + { TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR }, + { TOP_LEFT_NEAR, BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR }, + { TOP_RIGHT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR }, + { TOP_LEFT_FAR, BOTTOM_LEFT_FAR, BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR }, + { BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR, BOTTOM_RIGHT_NEAR, BOTTOM_LEFT_NEAR }, + { TOP_LEFT_FAR, TOP_RIGHT_FAR, TOP_RIGHT_NEAR, TOP_LEFT_NEAR } + } }; + Triangle triangle; + + for (auto quadVertexIndices : boxQuadVertexIndices) { + triangle.v0 = sceneBoundVertices[quadVertexIndices[0]]; + triangle.v1 = sceneBoundVertices[quadVertexIndices[1]]; + triangle.v2 = sceneBoundVertices[quadVertexIndices[2]]; + computeNearFar(triangle, shadowClipPlanes, near, far); + triangle.v1 = sceneBoundVertices[quadVertexIndices[3]]; + computeNearFar(triangle, shadowClipPlanes, near, far); + } +} + +static void adjustNearFar(const AABox& inShapeBounds, ViewFrustum& shadowFrustum) { + const Transform shadowView{ shadowFrustum.getView() }; + const Transform shadowViewInverse{ shadowView.getInverseMatrix() }; + + glm::vec3 sceneBoundVertices[8]; + // Keep only the left, right, top and bottom shadow frustum planes as we wish to determine + // the near and far + Plane shadowClipPlanes[4]; + int i; + + // The vertices of the scene bounding box are expressed in the shadow frustum's local space + for (i = 0; i < 8; i++) { + sceneBoundVertices[i] = shadowViewInverse.transform(inShapeBounds.getVertex(static_cast(i))); + } + // This indirection array is just a protection in case the ViewFrustum::PlaneIndex enum + // changes order especially as we don't need to test the NEAR and FAR planes. + static const ViewFrustum::PlaneIndex planeIndices[4] = { + ViewFrustum::TOP_PLANE, + ViewFrustum::BOTTOM_PLANE, + ViewFrustum::LEFT_PLANE, + ViewFrustum::RIGHT_PLANE + }; + // Same goes for the shadow frustum planes. + for (i = 0; i < 4; i++) { + const auto& worldPlane = shadowFrustum.getPlanes()[planeIndices[i]]; + // We assume the transform doesn't have a non uniform scale component to apply the + // transform to the normal without using the correct transpose of inverse, which should be the + // case for a view matrix. + auto planeNormal = shadowViewInverse.transformDirection(worldPlane.getNormal()); + auto planePoint = shadowViewInverse.transform(worldPlane.getPoint()); + shadowClipPlanes[i].setNormalAndPoint(planeNormal, planePoint); + } + + float near = std::numeric_limits::max(); + float far = 0.0f; + + computeNearFar(sceneBoundVertices, shadowClipPlanes, near, far); + + const auto depthEpsilon = 0.25f; + auto projMatrix = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, near - depthEpsilon, far + depthEpsilon); + auto shadowProjection = shadowFrustum.getProjection(); + + shadowProjection[2][2] = projMatrix[2][2]; + shadowProjection[3][2] = projMatrix[3][2]; + shadowFrustum.setProjection(shadowProjection); + shadowFrustum.calculate(); +} + +void RenderShadowMap::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); + const auto& inShapes = inputs.get0(); + const auto& inShapeBounds = inputs.get1(); + auto lightStage = renderContext->_scene->getStage(); assert(lightStage); - const auto shadow = lightStage->getCurrentKeyShadow(); + auto shadow = lightStage->getCurrentKeyShadow(); if (!shadow) return; const auto& fbo = shadow->framebuffer; RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; + auto adjustedShadowFrustum = args->getViewFrustum(); + + // Adjust the frustum near and far depths based on the rendered items bounding box to have + // the minimal Z range. + adjustNearFar(inShapeBounds, adjustedShadowFrustum); + // Reapply the frustum as it has been adjusted + shadow->setFrustum(adjustedShadowFrustum); + args->popViewFrustum(); + args->pushViewFrustum(adjustedShadowFrustum); gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; @@ -55,8 +161,13 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH, vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, true); - batch.setProjectionTransform(shadow->getProjection()); - batch.setViewTransform(shadow->getView(), false); + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat, false); auto shadowPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); @@ -109,10 +220,10 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende // Sort const auto sortedPipelines = task.addJob("PipelineSortShadowSort", culledShadowSelection); - const auto sortedShapes = task.addJob("DepthSortShadowMap", sortedPipelines); + const auto sortedShapesAndBounds = task.addJob("DepthSortShadowMap", sortedPipelines, true); // GPU jobs: Render to shadow map - task.addJob("RenderShadowMap", sortedShapes, shapePlumber); + task.addJob("RenderShadowMap", sortedShapesAndBounds, shapePlumber); task.addJob("ShadowTeardown", cachedMode); } @@ -135,8 +246,8 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O auto nearClip = args->getViewFrustum().getNearClip(); float nearDepth = -args->_boomOffset.z; - const int SHADOW_FAR_DEPTH = 20; - globalShadow->setKeylightFrustum(args->getViewFrustum(), nearDepth, nearClip + SHADOW_FAR_DEPTH); + const float SHADOW_MAX_DISTANCE = 20.0f; + globalShadow->setKeylightFrustum(args->getViewFrustum(), nearDepth, nearClip + SHADOW_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); // Set the keylight render args args->pushViewFrustum(*(globalShadow->getFrustum())); diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 031f44a42d..7b2bbeb306 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -21,11 +21,11 @@ class ViewFrustum; class RenderShadowMap { public: - using JobModel = render::Job::ModelI; + using Inputs = render::VaryingSet2; + using JobModel = render::Job::ModelI; RenderShadowMap(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} - void run(const render::RenderContextPointer& renderContext, - const render::ShapeBounds& inShapes); + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); protected: render::ShapePlumberPointer _shapePlumber; diff --git a/libraries/render/src/render/SortTask.cpp b/libraries/render/src/render/SortTask.cpp index 5b7ead4b6a..f789f8c5c6 100644 --- a/libraries/render/src/render/SortTask.cpp +++ b/libraries/render/src/render/SortTask.cpp @@ -40,7 +40,8 @@ struct BackToFrontSort { } }; -void render::depthSortItems(const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems) { +void render::depthSortItems(const RenderContextPointer& renderContext, bool frontToBack, + const ItemBounds& inItems, ItemBounds& outItems, AABox* bounds) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); @@ -75,8 +76,18 @@ void render::depthSortItems(const RenderContextPointer& renderContext, bool fron } // Finally once sorted result to a list of itemID - for (auto& item : itemBoundSorts) { - outItems.emplace_back(ItemBound(item._id, item._bounds)); + if (!bounds) { + for (auto& item : itemBoundSorts) { + outItems.emplace_back(ItemBound(item._id, item._bounds)); + } + } else if (!itemBoundSorts.empty()) { + if (bounds->isNull()) { + *bounds = itemBoundSorts.front()._bounds; + } + for (auto& item : itemBoundSorts) { + *bounds += item._bounds; + outItems.emplace_back(ItemBound(item._id, item._bounds)); + } } } @@ -115,6 +126,25 @@ void DepthSortShapes::run(const RenderContextPointer& renderContext, const Shape } } +void DepthSortShapesAndComputeBounds::run(const RenderContextPointer& renderContext, const ShapeBounds& inShapes, Outputs& outputs) { + auto& outShapes = outputs.edit0(); + auto& outBounds = outputs.edit1(); + + outShapes.clear(); + outShapes.reserve(inShapes.size()); + outBounds = AABox(); + + for (auto& pipeline : inShapes) { + auto& inItems = pipeline.second; + auto outItems = outShapes.find(pipeline.first); + if (outItems == outShapes.end()) { + outItems = outShapes.insert(std::make_pair(pipeline.first, ItemBounds{})).first; + } + + depthSortItems(renderContext, _frontToBack, inItems, outItems->second, &outBounds); + } +} + void DepthSortItems::run(const RenderContextPointer& renderContext, const ItemBounds& inItems, ItemBounds& outItems) { depthSortItems(renderContext, _frontToBack, inItems, outItems); } diff --git a/libraries/render/src/render/SortTask.h b/libraries/render/src/render/SortTask.h index dfeb22d540..de670b1676 100644 --- a/libraries/render/src/render/SortTask.h +++ b/libraries/render/src/render/SortTask.h @@ -15,7 +15,7 @@ #include "Engine.h" namespace render { - void depthSortItems(const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems); + void depthSortItems(const RenderContextPointer& renderContext, bool frontToBack, const ItemBounds& inItems, ItemBounds& outItems, AABox* bounds = nullptr); class PipelineSortShapes { public: @@ -33,6 +33,17 @@ namespace render { void run(const RenderContextPointer& renderContext, const ShapeBounds& inShapes, ShapeBounds& outShapes); }; + class DepthSortShapesAndComputeBounds { + public: + using Outputs = VaryingSet2; + using JobModel = Job::ModelIO; + + bool _frontToBack; + DepthSortShapesAndComputeBounds(bool frontToBack = true) : _frontToBack(frontToBack) {} + + void run(const RenderContextPointer& renderContext, const ShapeBounds& inShapes, Outputs& outputs); + }; + class DepthSortItems { public: using JobModel = Job::ModelIO; diff --git a/libraries/shared/src/AABox.h b/libraries/shared/src/AABox.h index eef83974ea..24485eaad6 100644 --- a/libraries/shared/src/AABox.h +++ b/libraries/shared/src/AABox.h @@ -127,10 +127,11 @@ public: AABox getOctreeChild(OctreeChild child) const; // returns the AABox of the would be octree child of this AABox + glm::vec4 getPlane(BoxFace face) const; + private: glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const; glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const; - glm::vec4 getPlane(BoxFace face) const; static BoxFace getOppositeFace(BoxFace face); diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 6b9718fbb8..e502d44a08 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -14,10 +14,12 @@ #include #include #include +#include #include #include "NumericalConstants.h" #include "GLMHelpers.h" +#include "Plane.h" glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) { // compute the projection of the point vector onto the segment vector @@ -314,6 +316,134 @@ bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direc return false; } +static void getTrianglePlaneIntersectionPoints(const glm::vec3 trianglePoints[3], const float pointPlaneDistances[3], + const int clippedPointIndex, const int keptPointIndices[2], + glm::vec3 points[2]) { + assert(clippedPointIndex >= 0 && clippedPointIndex < 3); + const auto& clippedPoint = trianglePoints[clippedPointIndex]; + const float clippedPointPlaneDistance = pointPlaneDistances[clippedPointIndex]; + for (auto i = 0; i < 2; i++) { + assert(keptPointIndices[i] >= 0 && keptPointIndices[i] < 3); + const auto& keptPoint = trianglePoints[keptPointIndices[i]]; + const float keptPointPlaneDistance = pointPlaneDistances[keptPointIndices[i]]; + auto intersectionEdgeRatio = clippedPointPlaneDistance / (clippedPointPlaneDistance - keptPointPlaneDistance); + points[i] = clippedPoint + (keptPoint - clippedPoint) * intersectionEdgeRatio; + } +} + +int clipTriangleWithPlane(const Triangle& triangle, const Plane& plane, Triangle* clippedTriangles, int maxClippedTriangleCount) { + float pointDistanceToPlane[3]; + std::bitset<3> arePointsClipped; + glm::vec3 triangleVertices[3] = { triangle.v0, triangle.v1, triangle.v2 }; + int clippedTriangleCount = 0; + int i; + + assert(clippedTriangleCount > 0); + + for (i = 0; i < 3; i++) { + pointDistanceToPlane[i] = plane.distance(triangleVertices[i]); + arePointsClipped.set(i, pointDistanceToPlane[i] < 0.0f); + } + + switch (arePointsClipped.count()) { + case 0: + // Easy, the entire triangle is kept as is. + *clippedTriangles = triangle; + clippedTriangleCount = 1; + break; + + case 1: + { + int clippedPointIndex = 2; + int keptPointIndices[2] = { 0, 1 }; + glm::vec3 newVertices[2]; + + // Determine which point was clipped. + if (arePointsClipped.test(0)) { + clippedPointIndex = 0; + keptPointIndices[0] = 2; + } else if (arePointsClipped.test(1)) { + clippedPointIndex = 1; + keptPointIndices[1] = 2; + } + // We have a quad now, so we need to create two triangles. + getTrianglePlaneIntersectionPoints(triangleVertices, pointDistanceToPlane, clippedPointIndex, keptPointIndices, newVertices); + clippedTriangles->v0 = triangleVertices[keptPointIndices[0]]; + clippedTriangles->v1 = triangleVertices[keptPointIndices[1]]; + clippedTriangles->v2 = newVertices[1]; + clippedTriangles++; + clippedTriangleCount++; + + if (clippedTriangleCount < maxClippedTriangleCount) { + clippedTriangles->v0 = triangleVertices[keptPointIndices[0]]; + clippedTriangles->v1 = newVertices[0]; + clippedTriangles->v2 = newVertices[1]; + clippedTriangles++; + clippedTriangleCount++; + } + } + break; + + case 2: + { + int keptPointIndex = 2; + int clippedPointIndices[2] = { 0, 1 }; + glm::vec3 newVertices[2]; + + // Determine which point was NOT clipped. + if (!arePointsClipped.test(0)) { + keptPointIndex = 0; + clippedPointIndices[0] = 2; + } else if (!arePointsClipped.test(1)) { + keptPointIndex = 1; + clippedPointIndices[1] = 2; + } + // We have a single triangle + getTrianglePlaneIntersectionPoints(triangleVertices, pointDistanceToPlane, keptPointIndex, clippedPointIndices, newVertices); + clippedTriangles->v0 = triangleVertices[keptPointIndex]; + clippedTriangles->v1 = newVertices[0]; + clippedTriangles->v2 = newVertices[1]; + clippedTriangleCount = 1; + } + break; + + default: + // Entire triangle is clipped. + break; + } + + return clippedTriangleCount; +} + +int clipTriangleWithPlanes(const Triangle& triangle, const Plane* planes, int planeCount, Triangle* clippedTriangles, int maxClippedTriangleCount) { + auto planesEnd = planes + planeCount; + int triangleCount = 1; + std::vector trianglesToTest; + + assert(maxClippedTriangleCount > 0); + + *clippedTriangles = triangle; + + while (planes < planesEnd) { + int clippedSubTriangleCount; + + trianglesToTest.clear(); + trianglesToTest.insert(trianglesToTest.begin(), clippedTriangles, clippedTriangles + triangleCount); + triangleCount = 0; + + for (const auto& triangleToTest : trianglesToTest) { + clippedSubTriangleCount = clipTriangleWithPlane(triangleToTest, *planes, + clippedTriangles + triangleCount, maxClippedTriangleCount - triangleCount); + triangleCount += clippedSubTriangleCount; + if (triangleCount >= maxClippedTriangleCount) { + return triangleCount; + } + } + ++planes; + } + return triangleCount; +} + // Do line segments (r1p1.x, r1p1.y)--(r1p2.x, r1p2.y) and (r2p1.x, r2p1.y)--(r2p2.x, r2p2.y) intersect? // from: http://ptspts.blogspot.com/2010/06/how-to-determine-if-two-line-segments.html bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2) { diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index eb9424d938..dcb90643b6 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -15,6 +15,8 @@ #include #include +class Plane; + glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end); /// Computes the penetration between a point and a sphere (centered at the origin) @@ -109,6 +111,8 @@ inline bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3 return findRayTriangleIntersection(origin, direction, triangle.v0, triangle.v1, triangle.v2, distance, allowBackface); } +int clipTriangleWithPlane(const Triangle& triangle, const Plane& plane, Triangle* clippedTriangles, int maxClippedTriangleCount); +int clipTriangleWithPlanes(const Triangle& triangle, const Plane* planes, int planeCount, Triangle* clippedTriangles, int maxClippedTriangleCount); bool doLineSegmentsIntersect(glm::vec2 r1p1, glm::vec2 r1p2, glm::vec2 r2p1, glm::vec2 r2p2); bool isOnSegment(float xi, float yi, float xj, float yj, float xk, float yk); diff --git a/libraries/shared/src/Plane.h b/libraries/shared/src/Plane.h index c903ad9db7..cf17ca7201 100644 --- a/libraries/shared/src/Plane.h +++ b/libraries/shared/src/Plane.h @@ -19,7 +19,9 @@ class Plane { public: - Plane(const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3) { set3Points(v1,v2,v3); } + Plane(const glm::vec3 &v1, const glm::vec3 &v2, const glm::vec3 &v3) { set3Points(v1, v2, v3); } + Plane(const glm::vec3 &normal, const glm::vec3 &point) { setNormalAndPoint(normal, point); } + Plane(float a, float b, float c, float d) { setCoefficients(a, b, c, d); } Plane() : _normal(0.0f), _point(0.0f), _dCoefficient(0.0f) {}; ~Plane() {} ; diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index 316fcb2f04..71d8b6c915 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -149,6 +149,7 @@ public: Vec4 transform(const Vec4& pos) const; Vec3 transform(const Vec3& pos) const; + Vec3 transformDirection(const Vec3& pos) const; bool containsNaN() const { return isNaN(_rotation) || isNaN(glm::dot(_scale, _translation)); } @@ -541,6 +542,13 @@ inline Transform::Vec3 Transform::transform(const Vec3& pos) const { return Vec3(result.x / result.w, result.y / result.w, result.z / result.w); } +inline Transform::Vec3 Transform::transformDirection(const Vec3& pos) const { + Mat4 m; + getMatrix(m); + Vec4 result = m * Vec4(pos, 0.0f); + return Vec3(result.x, result.y, result.z); +} + inline Transform::Mat4& Transform::getCachedMatrix(Transform::Mat4& result) const { updateCache(); result = (*_matrix);