diff --git a/C++ProjectTemplate b/C++ProjectTemplate index 9109d23..5e98f8d 100755 Binary files a/C++ProjectTemplate and b/C++ProjectTemplate differ diff --git a/main.cpp b/main.cpp index 1f51930..53a1225 100644 --- a/main.cpp +++ b/main.cpp @@ -299,7 +299,115 @@ private: v.z = v1.x * v2.y - v1.y * v2.x; return v; } + + vec3d Vector_IntersectPlane(vec3d &plane_p, vec3d &plane_n, vec3d &lineStart, vec3d &lineEnd) + { + plane_n = Vector_Normalise(plane_n); + float plane_d = -Vector_DotProduct(plane_n, plane_p); + float ad = Vector_DotProduct(lineStart, plane_n); + float bd = Vector_DotProduct(lineEnd, plane_n); + float t = (-plane_d - ad) / (bd - ad); + vec3d lineStartToEnd = Vector_Sub(lineEnd, lineStart); + vec3d lineToIntersect = Vector_Mul(lineStartToEnd, t); + return Vector_Add(lineStart, lineToIntersect); + } + + int Triangle_ClipAgainstPlane(vec3d plane_p, vec3d plane_n, triangle &in_tri, triangle &out_tri1, triangle &out_tri2) + { + // Make sure plane normal is indeed normal + plane_n = Vector_Normalise(plane_n); + + // Return signed shortest distance from point to plane, plane normal must be normalised + auto dist = [&](vec3d &p) + { + vec3d n = Vector_Normalise(p); + return (plane_n.x * p.x + plane_n.y * p.y + plane_n.z * p.z - Vector_DotProduct(plane_n, plane_p)); + }; + + // Create two temporary storage arrays to classify points either side of plane + // If distance sign is positive, point lies on "inside" of plane + vec3d* inside_points[3]; int nInsidePointCount = 0; + vec3d* outside_points[3]; int nOutsidePointCount = 0; + + // Get signed distance of each point in triangle to plane + float d0 = dist(in_tri.p[0]); + float d1 = dist(in_tri.p[1]); + float d2 = dist(in_tri.p[2]); + + if (d0 >= 0) { inside_points[nInsidePointCount++] = &in_tri.p[0]; } + else { outside_points[nOutsidePointCount++] = &in_tri.p[0]; } + if (d1 >= 0) { inside_points[nInsidePointCount++] = &in_tri.p[1]; } + else { outside_points[nOutsidePointCount++] = &in_tri.p[1]; } + if (d2 >= 0) { inside_points[nInsidePointCount++] = &in_tri.p[2]; } + else { outside_points[nOutsidePointCount++] = &in_tri.p[2]; } + + // Now classify triangle points, and break the input triangle into + // smaller output triangles if required. There are four possible + // outcomes... + + if (nInsidePointCount == 0) + { + // All points lie on the outside of plane, so clip whole triangle + // It ceases to exist + + return 0; // No returned triangles are valid + } + + if (nInsidePointCount == 3) + { + // All points lie on the inside of plane, so do nothing + // and allow the triangle to simply pass through + out_tri1 = in_tri; + + return 1; // Just the one returned original triangle is valid + } + if (nInsidePointCount == 1 && nOutsidePointCount == 2) + { + // Triangle should be clipped. As two points lie outside + // the plane, the triangle simply becomes a smaller triangle + + // Copy appearance info to new triangle + out_tri1.col = in_tri.col; + + // The inside point is valid, so keep that... + out_tri1.p[0] = *inside_points[0]; + + // but the two new points are at the locations where the + // original sides of the triangle (lines) intersect with the plane + out_tri1.p[1] = Vector_IntersectPlane(plane_p, plane_n, *inside_points[0], *outside_points[0]); + out_tri1.p[2] = Vector_IntersectPlane(plane_p, plane_n, *inside_points[0], *outside_points[1]); + + return 1; // Return the newly formed single triangle + } + + if (nInsidePointCount == 2 && nOutsidePointCount == 1) + { + // Triangle should be clipped. As two points lie inside the plane, + // the clipped triangle becomes a "quad". Fortunately, we can + // represent a quad with two new triangles + + // Copy appearance info to new triangles + out_tri1.col = in_tri.col; + out_tri2.col = in_tri.col; + + // The first triangle consists of the two inside points and a new + // point determined by the location where one side of the triangle + // intersects with the plane + out_tri1.p[0] = *inside_points[0]; + out_tri1.p[1] = *inside_points[1]; + out_tri1.p[2] = Vector_IntersectPlane(plane_p, plane_n, *inside_points[0], *outside_points[0]); + + // The second triangle is composed of one of he inside points, a + // new point determined by the intersection of the other side of the + // triangle and the plane, and the newly created point above + out_tri2.p[0] = *inside_points[1]; + out_tri2.p[1] = out_tri1.p[2]; + out_tri2.p[2] = Vector_IntersectPlane(plane_p, plane_n, *inside_points[1], *outside_points[0]); + + return 2; // Return two newly formed triangles which form a quad + } + } public: bool OnUserCreate() override @@ -391,37 +499,42 @@ public: triViewed.p[1]=Matrix_MultiplyVector(matView,triTransformed.p[1]); triViewed.p[2]=Matrix_MultiplyVector(matView,triTransformed.p[2]); - - // Project triangles from 3D --> 2D - triProjected.p[0]=Matrix_MultiplyVector(matProj,triViewed.p[0]); - triProjected.p[1]=Matrix_MultiplyVector(matProj,triViewed.p[1]); - triProjected.p[2]=Matrix_MultiplyVector(matProj,triViewed.p[2]); - triProjected.p[0]=Vector_Div(triProjected.p[0],triProjected.p[0].w); - triProjected.p[1]=Vector_Div(triProjected.p[1],triProjected.p[1].w); - triProjected.p[2]=Vector_Div(triProjected.p[2],triProjected.p[2].w); - triProjected.col=triTransformed.col; - - triProjected.p[0].x*=-1.0f; - triProjected.p[1].x*=-1.0f; - triProjected.p[2].x*=-1.0f; - triProjected.p[0].y*=-1.0f; - triProjected.p[1].y*=-1.0f; - triProjected.p[2].y*=-1.0f; - - // Scale into view - vec3d vOffsetView={1,1,0}; - triProjected.p[0] = Vector_Add(triProjected.p[0],vOffsetView); - triProjected.p[1] = Vector_Add(triProjected.p[1],vOffsetView); - triProjected.p[2] = Vector_Add(triProjected.p[2],vOffsetView); - triProjected.p[0].x *= 0.5f * (float)ScreenWidth(); - triProjected.p[0].y *= 0.5f * (float)ScreenHeight(); - triProjected.p[1].x *= 0.5f * (float)ScreenWidth(); - triProjected.p[1].y *= 0.5f * (float)ScreenHeight(); - triProjected.p[2].x *= 0.5f * (float)ScreenWidth(); - triProjected.p[2].y *= 0.5f * (float)ScreenHeight(); - - vecTrianglesToRaster.push_back(triProjected); - + int nClippedTriangles = 0; + triangle clipped[2]; + nClippedTriangles = Triangle_ClipAgainstPlane({ 0.0f, 0.0f, 0.1f }, { 0.0f, 0.0f, 1.0f }, triViewed, clipped[0], clipped[1]); + + for (int n=0;n 2D + triProjected.p[0]=Matrix_MultiplyVector(matProj,clipped[n].p[0]); + triProjected.p[1]=Matrix_MultiplyVector(matProj,clipped[n].p[1]); + triProjected.p[2]=Matrix_MultiplyVector(matProj,clipped[n].p[2]); + triProjected.col=triTransformed.col; + + triProjected.p[0]=Vector_Div(triProjected.p[0],triProjected.p[0].w); + triProjected.p[1]=Vector_Div(triProjected.p[1],triProjected.p[1].w); + triProjected.p[2]=Vector_Div(triProjected.p[2],triProjected.p[2].w); + + triProjected.p[0].x*=-1.0f; + triProjected.p[1].x*=-1.0f; + triProjected.p[2].x*=-1.0f; + triProjected.p[0].y*=-1.0f; + triProjected.p[1].y*=-1.0f; + triProjected.p[2].y*=-1.0f; + + // Scale into view + vec3d vOffsetView={1,1,0}; + triProjected.p[0] = Vector_Add(triProjected.p[0],vOffsetView); + triProjected.p[1] = Vector_Add(triProjected.p[1],vOffsetView); + triProjected.p[2] = Vector_Add(triProjected.p[2],vOffsetView); + triProjected.p[0].x *= 0.5f * (float)ScreenWidth(); + triProjected.p[0].y *= 0.5f * (float)ScreenHeight(); + triProjected.p[1].x *= 0.5f * (float)ScreenWidth(); + triProjected.p[1].y *= 0.5f * (float)ScreenHeight(); + triProjected.p[2].x *= 0.5f * (float)ScreenWidth(); + triProjected.p[2].y *= 0.5f * (float)ScreenHeight(); + + vecTrianglesToRaster.push_back(triProjected); + } } } @@ -441,7 +554,7 @@ public: {triProjected.uv[1].u,triProjected.uv[1].v}, {triProjected.uv[2].u,triProjected.uv[2].v}, },triProjected.col); - /*SetDecalMode(DecalMode::WIREFRAME); + SetDecalMode(DecalMode::WIREFRAME); DrawPolygonDecal(nullptr,{ {triProjected.p[0].x, triProjected.p[0].y}, {triProjected.p[1].x, triProjected.p[1].y}, @@ -450,12 +563,12 @@ public: {0,0}, {0,0}, {0,0}, - },BLACK);*/ + },BLACK); SetDecalStructure(DecalStructure::FAN); } SetDecalMode(DecalMode::NORMAL); - DrawStringDecal({0,0},"Triangles: "+std::to_string(meshCube.tris.size())); + DrawStringDecal({0,0},"Triangles: "+std::to_string(vecTrianglesToRaster.size())); return true;