mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 09:23:17 +02:00
First draft version of tighter shadow frustum
This commit is contained in:
parent
ae8a9e68c8
commit
6cf689a385
11 changed files with 335 additions and 22 deletions
|
@ -25,7 +25,10 @@ LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum
|
|||
_schemaBuffer = std::make_shared<gpu::Buffer>(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<float>(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<Schema>().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<Schema>().projection = shadowFrustum.getProjection();
|
||||
_schemaBuffer.edit<Schema>().viewInverse = viewInverse.getMatrix();
|
||||
}
|
||||
|
||||
const glm::mat4& LightStage::Shadow::getView() const {
|
||||
return _frustum->getView();
|
||||
}
|
||||
|
|
|
@ -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<ViewFrustum> getFrustum() const { return _frustum; }
|
||||
|
||||
const glm::mat4& getView() const;
|
||||
|
|
|
@ -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<int[4], 6> 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<BoxVertex>(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<float>::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<LightStage>();
|
||||
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<PipelineSortShapes>("PipelineSortShadowSort", culledShadowSelection);
|
||||
const auto sortedShapes = task.addJob<DepthSortShapes>("DepthSortShadowMap", sortedPipelines);
|
||||
const auto sortedShapesAndBounds = task.addJob<DepthSortShapesAndComputeBounds>("DepthSortShadowMap", sortedPipelines, true);
|
||||
|
||||
// GPU jobs: Render to shadow map
|
||||
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapes, shapePlumber);
|
||||
task.addJob<RenderShadowMap>("RenderShadowMap", sortedShapesAndBounds, shapePlumber);
|
||||
|
||||
task.addJob<RenderShadowTeardown>("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()));
|
||||
|
|
|
@ -21,11 +21,11 @@ class ViewFrustum;
|
|||
|
||||
class RenderShadowMap {
|
||||
public:
|
||||
using JobModel = render::Job::ModelI<RenderShadowMap, render::ShapeBounds>;
|
||||
using Inputs = render::VaryingSet2<render::ShapeBounds, AABox>;
|
||||
using JobModel = render::Job::ModelI<RenderShadowMap, Inputs>;
|
||||
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<ShapeBounds, AABox>;
|
||||
using JobModel = Job::ModelIO<DepthSortShapesAndComputeBounds, ShapeBounds, Outputs>;
|
||||
|
||||
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<DepthSortItems, ItemBounds, ItemBounds>;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -14,10 +14,12 @@
|
|||
#include <assert.h>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <bitset>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#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<Triangle> 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) {
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
|
||||
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);
|
||||
|
|
|
@ -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() {} ;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue