private void addPolygon(ref BSPNode root, BSPFaceRef face, Polygon transformed) { if(root == null) root = new BSPNode(); if(root.PolygonsFront.Count == 0) { // We though root.Front == null && root.Back == null root.Plane = transformed.Plane; root.PolygonsFront = new List<BSPFaceRef> { face }; return; } var positive = 0; var negative = 0; var inPlane = 0; foreach(var v in transformed.Vertices) { var dist = root.Plane.Distance(v.Position); if (dist > SPLIT_EPSILON) positive++; else if (dist < -SPLIT_EPSILON) negative++; else inPlane++; } if(positive > 0 && negative == 0) // SPLIT_FRONT { addPolygon(ref root.Front, face, transformed); } else if(positive == 0 && negative > 0) // SPLIT_BACK { addPolygon(ref root.Back, face, transformed); } else // SPLIT_IN_PLANE { if(transformed.Plane.Normal.Dot(root.Plane.Normal) > 0.9) { root.PolygonsFront.Add(face); } else { root.PolygonsBack.Add(face); } } }
/// <summary> /// Assign animated texture to a polygon. /// </summary> public static bool Res_Poly_SetAnimTexture(Polygon polygon, uint texIndex, World world) { polygon.AnimID = 0; // Reset to 0 by default for (var i = 0; i < world.AnimSequences.Count; i++) { for (var j = 0; j < world.AnimSequences[i].Frames.Count; j++) { if(world.AnimSequences[i].FrameList[j] == texIndex) { // If we have found assigned texture ID in animation texture lists, // we assign corresponding animation sequence to this polygon, // additionally specifying frame offset. polygon.AnimID = (ushort)(i + 1); // Animation sequence ID. polygon.FrameOffset = (ushort)j; // Animation frame offset. return true; } } } return false; // No such TexInfo found in animation textures lists. }
public static void tr_setupTexturedFace(Mesh trMesh, BaseMesh mesh, ushort[] vertexIndices, Polygon p) { if (trMesh.Lights.Length == trMesh.Vertices.Length) { for (var i = 0; i < p.Vertices.Count; i++) { p.Vertices[i].Color[0] = 1.0f - trMesh.Lights[vertexIndices[i]] / 8192.0f; p.Vertices[i].Color[1] = 1.0f - trMesh.Lights[vertexIndices[i]] / 8192.0f; p.Vertices[i].Color[2] = 1.0f - trMesh.Lights[vertexIndices[i]] / 8192.0f; p.Vertices[i].Color[3] = 1.0f; } } else { foreach (var v in p.Vertices) { v.Color = new float[] {1, 1, 1, 1}; } } }
public static void tr_setupColoredFace(Mesh trMesh, Level tr, BaseMesh mesh, ushort[] vertexIndices, int color, Polygon p) { var tmp = trMesh.Lights.Length == trMesh.Vertices.Length; for (var i = 0; i < p.Vertices.Count; i++) { p.Vertices[i].Color[0] = tr.Palette.Colour[color].R / 255.0f; p.Vertices[i].Color[1] = tr.Palette.Colour[color].G / 255.0f; p.Vertices[i].Color[2] = tr.Palette.Colour[color].B / 255.0f; if(tmp) { p.Vertices[i].Color[0] = p.Vertices[i].Color[0] * 1.0f - trMesh.Lights[vertexIndices[i]] / 8192.0f; p.Vertices[i].Color[1] = p.Vertices[i].Color[1] * 1.0f - trMesh.Lights[vertexIndices[i]] / 8192.0f; p.Vertices[i].Color[2] = p.Vertices[i].Color[2] * 1.0f - trMesh.Lights[vertexIndices[i]] / 8192.0f; } p.Vertices[i].Color[3] = 1.0f; p.Vertices[i].TexCoord[0] = (i & 2) == 2 ? 1.0f : 0.0f; p.Vertices[i].TexCoord[1] = i >= 2 ? 1.0f : 0.0f; } mesh.UsesVertexColors = true; }
public static void tr_accumulateNormals(Mesh trMesh, BaseMesh mesh, int numCorners, ushort[] vertexIndices, Polygon p) { p.Vertices.Resize(numCorners, () => new Vertex()); for (var i = 0; i < numCorners; i++) { p.Vertices[i].Position = trMesh.Vertices[vertexIndices[i]].ToVector3(); } p.FindNormal(); for (var i = 0; i < numCorners; i++) { mesh.Vertices[vertexIndices[i]].Normal += p.Plane.Normal; } }
public static void tr_copyNormals(Polygon polygon, BaseMesh mesh, ushort[] mesh_vertex_indices) { for (var i = 0; i < polygon.Vertices.Count; i++) { polygon.Vertices[i].Normal = mesh.Vertices[mesh_vertex_indices[i]].Normal; } }
public static void tr_setupRoomVertices(World world, Level tr, Loader.Room tr_room, BaseMesh mesh, int numCorners, ushort[] vertices, ushort masked_texture, Polygon p) { p.Vertices.Resize(numCorners, () => new Vertex()); for (var i = 0; i < numCorners; i++) { p.Vertices[i].Position = tr_room.Vertices[vertices[i]].Vertex.ToVector3(); } p.FindNormal(); for (var i = 0; i < numCorners; i++) { mesh.Vertices[vertices[i]].Normal += p.Plane.Normal; p.Vertices[i].Normal = p.Plane.Normal; p.Vertices[i].Color = TR_color_to_arr(tr_room.Vertices[vertices[i]].Color); } var tex = tr.ObjectTextures[masked_texture]; SetAnimTexture(p, masked_texture, world); p.BlendMode = tex.TransparencyFlags; world.TextureAtlas.GetCoordinates(masked_texture, false, p); }
public void AddNewPolygonList(List<TransparentPolygonReference> p, Transform transform, Frustum frustum, Camera cam) { foreach (var pp in p) { var transformed = new Polygon(); transformed.Vertices.Resize(pp.Polygon.Vertices.Count, () => new Vertex()); transformed.Transform(pp.Polygon, transform); transformed.DoubleSide = pp.Polygon.DoubleSide; if(frustum.IsPolyVisible(transformed, cam)) { addPolygon(ref _root, new BSPFaceRef(transform, pp), transformed); } } }
public static void TR_GenMesh(World world, int mesh_index, BaseMesh mesh, Level tr) { var texMask = world.EngineVersion == Loader.Engine.TR4 ? TextureIndexMaskTr4 : TextureIndexMask; /* TR WAD FORMAT DOCUMENTATION! * tr4_face[3,4]_t: * flipped texture & 0x8000 (1 bit ) - horizontal flipping. * shape texture & 0x7000 (3 bits ) - texture sample shape. * index texture & $0FFF (12 bits) - texture sample index. * * if bit [15] is set, as in ( texture and $8000 ), it indicates that the texture * sample must be flipped horizontally prior to be used. * Bits [14..12] as in ( texture and $7000 ), are used to store the texture * shape, given by: ( texture and $7000 ) shr 12. * The valid values are: 0, 2, 4, 6, 7, as assigned to a square starting from * the top-left corner and going clockwise: 0, 2, 4, 6 represent the positions * of the square angle of the triangles, 7 represents a quad. */ var trMesh = tr.Meshes[mesh_index]; mesh.ID = (uint)mesh_index; mesh.Center.X = trMesh.Centre.X; mesh.Center.Y = trMesh.Centre.Z; mesh.Center.Z = trMesh.Centre.Y; mesh.Radius = trMesh.CollisionSize; mesh.TexturePageCount = world.TextureAtlas.NumAtlasPages + 1; mesh.Vertices.Resize(trMesh.Vertices.Length, () => new Vertex()); for (var i = 0; i < mesh.Vertices.Count; i++) { mesh.Vertices[i].Position = trMesh.Vertices[i].ToVector3(); mesh.Vertices[i].Normal = Vector3.Zero; // paranoid } mesh.FindBB(); mesh.Polygons.Clear(); // textured triangles foreach (var face3 in trMesh.TexturedTriangles) { var p = new Polygon(); var tex = tr.ObjectTextures[face3.Texture & texMask]; p.DoubleSide = face3.Texture >> 15 != 0; // CORRECT, BUT WRONG IN TR3-5 SetAnimTexture(p, (uint)face3.Texture & texMask, world); p.BlendMode = face3.Lighting.HasFlagUns(0x01) ? BlendingMode.Multiply : tex.TransparencyFlags; tr_accumulateNormals(trMesh, mesh, 3, face3.Vertices, p); tr_setupTexturedFace(trMesh, mesh, face3.Vertices, p); world.TextureAtlas.GetCoordinates((uint) face3.Texture & texMask, false, p); mesh.Polygons.Add(p); } // coloured triangles foreach (var face3 in trMesh.ColouredTriangles) { var p = new Polygon(); var col = face3.Texture & 0xff; p.TexIndex = (ushort)world.TextureAtlas.NumAtlasPages; p.BlendMode = BlendingMode.Opaque; p.AnimID = 0; tr_accumulateNormals(trMesh, mesh, 3, face3.Vertices, p); tr_setupColoredFace(trMesh, tr, mesh, face3.Vertices, col, p); mesh.Polygons.Add(p); } // textured rectangles foreach (var face4 in trMesh.TexturedRectangles) { var p = new Polygon(); var tex = tr.ObjectTextures[face4.Texture & texMask]; p.DoubleSide = face4.Texture >> 15 != 0; // CORRECT, BUT WRONG IN TR3-5 SetAnimTexture(p, (uint)face4.Texture & texMask, world); p.BlendMode = face4.Lighting.HasFlagUns(0x01) ? BlendingMode.Multiply : tex.TransparencyFlags; tr_accumulateNormals(trMesh, mesh, 4, face4.Vertices, p); tr_setupTexturedFace(trMesh, mesh, face4.Vertices, p); world.TextureAtlas.GetCoordinates((uint)face4.Texture & texMask, false, p); mesh.Polygons.Add(p); } // coloured rectangles foreach (var face4 in trMesh.ColouredRectangles) { var p = new Polygon(); var col = face4.Texture & 0xff; p.TexIndex = (ushort)world.TextureAtlas.NumAtlasPages; p.BlendMode = BlendingMode.Opaque; p.AnimID = 0; tr_accumulateNormals(trMesh, mesh, 4, face4.Vertices, p); tr_setupColoredFace(trMesh, tr, mesh, face4.Vertices, col, p); mesh.Polygons.Add(p); } // let us normalise normals %) foreach (var v in mesh.Vertices) { v.Normal = v.Normal.SafeNormalize(); } // triangles var j = 0; for (var i = 0; i < trMesh.TexturedTriangles.Length; i++, j++) { tr_copyNormals(mesh.Polygons[j], mesh, trMesh.TexturedTriangles[i].Vertices); } for (var i = 0; i < trMesh.ColouredTriangles.Length; i++, j++) { tr_copyNormals(mesh.Polygons[j], mesh, trMesh.ColouredTriangles[i].Vertices); } // triangles for (var i = 0; i < trMesh.TexturedRectangles.Length; i++, j++) { tr_copyNormals(mesh.Polygons[j], mesh, trMesh.TexturedRectangles[i].Vertices); } for (var i = 0; i < trMesh.ColouredRectangles.Length; i++, j++) { tr_copyNormals(mesh.Polygons[j], mesh, trMesh.ColouredRectangles[i].Vertices); } mesh.Vertices.Clear(); mesh.GenFaces(); mesh.PolySortInMesh(); }
public void Split(Plane n, ref Polygon front, ref Polygon back) { front.Plane = Plane; front.AnimID = AnimID; front.FrameOffset = FrameOffset; front.DoubleSide = DoubleSide; front.TexIndex = TexIndex; front.BlendMode = BlendMode; back.Plane = Plane; back.AnimID = AnimID; back.FrameOffset = FrameOffset; back.DoubleSide = DoubleSide; back.TexIndex = TexIndex; back.BlendMode = BlendMode; var prev_v = Vertices.Last(); var dist0 = n.Distance(prev_v.Position); foreach (var curr_v in Vertices) { var dist1 = n.Distance(curr_v.Position); if(Math.Abs(dist1) > SPLIT_EPSILON) { if ((dist1 > SPLIT_EPSILON && dist0 < -SPLIT_EPSILON) || (dist1 < -SPLIT_EPSILON && dist0 > SPLIT_EPSILON)) { var dir = curr_v.Position - prev_v.Position; float t; var tv = new Vertex { Position = n.RayIntersect(prev_v.Position, dir, out t), Normal = prev_v.Normal.Lerp(curr_v.Normal, t).Normalized(), Color = new[] { prev_v.Color[0] + t * (curr_v.Color[0] - prev_v.Color[0]), prev_v.Color[1] + t * (curr_v.Color[1] - prev_v.Color[1]), prev_v.Color[2] + t * (curr_v.Color[2] - prev_v.Color[2]), prev_v.Color[3] + t * (curr_v.Color[3] - prev_v.Color[3]) }, TexCoord = new[] { prev_v.TexCoord[0] + t * (curr_v.TexCoord[0] - prev_v.TexCoord[0]), prev_v.TexCoord[1] + t * (curr_v.TexCoord[1] - prev_v.TexCoord[1]) } }; front.Vertices.Add(tv); back.Vertices.Add(tv); } if (dist1 > SPLIT_EPSILON) front.Vertices.Add(curr_v); else back.Vertices.Add(curr_v); } else { front.Vertices.Add(curr_v); back.Vertices.Add(curr_v); } prev_v = curr_v; dist0 = dist1; } }
public bool IntersectPolygon(Polygon p2) { if(SplitClassify(p2.Plane) != PolygonSplit.InBoth || p2.SplitClassify(Plane) != PolygonSplit.InBoth) { return false; // Quick check } var resultBuf = new List<Vector3>(); // Intersection of polygon p1 and plane p2 var prev_v = Vertices.Last(); var dist0 = p2.Plane.Distance(prev_v.Position); foreach (var curr_v in Vertices) { var dist1 = p2.Plane.Distance(curr_v.Position); if ((dist1 > SPLIT_EPSILON && dist0 < -SPLIT_EPSILON) || (dist1 < -SPLIT_EPSILON && dist0 > SPLIT_EPSILON)) { resultBuf.Add(p2.Plane.RayIntersect(prev_v.Position, curr_v.Position - prev_v.Position)); } else { resultBuf.Add(curr_v.Position); } if (resultBuf.Count >= 2) break; dist0 = dist1; prev_v = curr_v; } // Splitting p2 by p1 split plane prev_v = Vertices.Last(); dist0 = p2.Plane.Distance(prev_v.Position); foreach (var curr_v in Vertices) { var dist1 = p2.Plane.Distance(curr_v.Position); if ((dist1 > SPLIT_EPSILON && dist0 < -SPLIT_EPSILON) || (dist1 < -SPLIT_EPSILON && dist0 > SPLIT_EPSILON)) { resultBuf.Add(p2.Plane.RayIntersect(prev_v.Position, curr_v.Position - prev_v.Position)); } else { resultBuf.Add(curr_v.Position); } if (resultBuf.Count >= 4) break; dist0 = dist1; prev_v = curr_v; } var dir = Plane.Normal.Cross(p2.Plane.Normal); // vector of two planes intersection line var t = Math.Abs(dir.X); dist0 = Math.Abs(dir.Y); var dist1_ = Math.Abs(dir.Z); var dist2 = 0.0f; var pn = PLANE_X; if(t < dist0) { t = dist0; pn = PLANE_Y; } if(t < dist1_) { pn = PLANE_Z; } switch(pn) { case PLANE_X: dist0 = (resultBuf[1][0] - resultBuf[0][0]) / dir[0]; dist1_ = (resultBuf[2][0] - resultBuf[0][0]) / dir[0]; dist2 = (resultBuf[3][0] - resultBuf[0][0]) / dir[0]; break; case PLANE_Y: dist0 = (resultBuf[1][1] - resultBuf[0][1]) / dir[1]; dist1_ = (resultBuf[2][1] - resultBuf[0][1]) / dir[1]; dist2 = (resultBuf[3][1] - resultBuf[0][1]) / dir[1]; break; case PLANE_Z: dist0 = (resultBuf[1][2] - resultBuf[0][2]) / dir[2]; dist1_ = (resultBuf[2][2] - resultBuf[0][2]) / dir[2]; dist2 = (resultBuf[3][2] - resultBuf[0][2]) / dir[2]; break; } if(dist0 > 0) { return !((dist1_ < 0 && dist2 < 0) || (dist1_ > dist0 && dist2 > dist0)); } return !((dist1_ < dist0 && dist2 < dist0) || (dist1_ > 0 && dist2 > 0)); }
public void Transform(Polygon src, Transform tr) { Vertices.Resize(src.Vertices.Count, () => new Vertex()); Plane.Normal = tr.Basis.MultiplyByVector(src.Plane.Normal); for (var i = 0; i < src.Vertices.Count; i++) { Vertices[i].Position = tr * src.Vertices[i].Position; Vertices[i].Normal = tr.Basis.MultiplyByVector(src.Vertices[i].Normal); } Plane.MoveTo(Vertices[0].Position); }
public void VTransform(Polygon src, Transform tr) { Plane.Normal = tr.Basis.MultiplyByVector(src.Plane.Normal); for (var i = 0; i < src.Vertices.Count; i++) { Vertices[i].Position = tr * src.Vertices[i].Position; } Plane.MoveTo(Vertices[0].Position); }
public void Move(Polygon src, Vector3 move) { for (var i = 0; i < src.Vertices.Count; i++) { Vertices[i].Position = src.Vertices[i].Position + move; } Plane = src.Plane; Plane.MoveTo(Vertices[0].Position); }
public Polygon(Polygon rhs) { Vertices = rhs.Vertices; TexIndex = rhs.TexIndex; AnimID = rhs.AnimID; FrameOffset = rhs.FrameOffset; BlendMode = rhs.BlendMode; DoubleSide = rhs.DoubleSide; Plane = rhs.Plane; }
public bool IsAABBVisible(Vector3 bbMin, Vector3 bbMax, Camera cam) { var poly = new Polygon(); poly.Vertices = new List<Vertex>(4); var ins = true; // X AXIS if(cam.Position.X < bbMin.X) { poly.Plane.Normal.X = -1.0f; poly.Plane.Dot = -bbMin.X; poly.Vertices[0].Position[0] = bbMin[0]; poly.Vertices[0].Position[1] = bbMax[1]; poly.Vertices[0].Position[2] = bbMax[2]; poly.Vertices[1].Position[0] = bbMin[0]; poly.Vertices[1].Position[1] = bbMin[1]; poly.Vertices[1].Position[2] = bbMax[2]; poly.Vertices[2].Position[0] = bbMin[0]; poly.Vertices[2].Position[1] = bbMin[1]; poly.Vertices[2].Position[2] = bbMin[2]; poly.Vertices[3].Position[0] = bbMin[0]; poly.Vertices[3].Position[1] = bbMax[1]; poly.Vertices[3].Position[2] = bbMin[2]; if(IsPolyVisible(poly, cam)) { return true; } ins = false; } else if(cam.Position.X > bbMax.X) { poly.Plane.Normal.X = 1.0f; poly.Plane.Dot = bbMax.X; poly.Vertices[0].Position[0] = bbMax[0]; poly.Vertices[0].Position[1] = bbMax[1]; poly.Vertices[0].Position[2] = bbMax[2]; poly.Vertices[1].Position[0] = bbMax[0]; poly.Vertices[1].Position[1] = bbMin[1]; poly.Vertices[1].Position[2] = bbMax[2]; poly.Vertices[2].Position[0] = bbMax[0]; poly.Vertices[2].Position[1] = bbMin[1]; poly.Vertices[2].Position[2] = bbMin[2]; poly.Vertices[3].Position[0] = bbMax[0]; poly.Vertices[3].Position[1] = bbMax[1]; poly.Vertices[3].Position[2] = bbMin[2]; if (IsPolyVisible(poly, cam)) { return true; } ins = false; } // Y AXIS poly.Plane.Normal.X = 0; poly.Plane.Normal.Z = 0; if (cam.Position.Y < bbMin.Y) { poly.Plane.Normal.Y = -1.0f; poly.Plane.Dot = -bbMin.Y; poly.Vertices[0].Position[0] = bbMax[0]; poly.Vertices[0].Position[1] = bbMin[1]; poly.Vertices[0].Position[2] = bbMax[2]; poly.Vertices[1].Position[0] = bbMin[0]; poly.Vertices[1].Position[1] = bbMin[1]; poly.Vertices[1].Position[2] = bbMax[2]; poly.Vertices[2].Position[0] = bbMin[0]; poly.Vertices[2].Position[1] = bbMin[1]; poly.Vertices[2].Position[2] = bbMin[2]; poly.Vertices[3].Position[0] = bbMax[0]; poly.Vertices[3].Position[1] = bbMin[1]; poly.Vertices[3].Position[2] = bbMin[2]; if (IsPolyVisible(poly, cam)) { return true; } ins = false; } else if (cam.Position.Y > bbMax.Y) { poly.Plane.Normal.Y = 1.0f; poly.Plane.Dot = -bbMax.Y; poly.Vertices[0].Position[0] = bbMax[0]; poly.Vertices[0].Position[1] = bbMax[1]; poly.Vertices[0].Position[2] = bbMax[2]; poly.Vertices[1].Position[0] = bbMax[0]; poly.Vertices[1].Position[1] = bbMin[1]; poly.Vertices[1].Position[2] = bbMax[2]; poly.Vertices[2].Position[0] = bbMax[0]; poly.Vertices[2].Position[1] = bbMin[1]; poly.Vertices[2].Position[2] = bbMin[2]; poly.Vertices[3].Position[0] = bbMax[0]; poly.Vertices[3].Position[1] = bbMax[1]; poly.Vertices[3].Position[2] = bbMin[2]; if (IsPolyVisible(poly, cam)) { return true; } ins = false; } // Z AXIS poly.Plane.Normal.X = 0; poly.Plane.Normal.Y = 0; if (cam.Position.Z < bbMin.Z) { poly.Plane.Normal.Z = -1.0f; poly.Plane.Dot = -bbMin.Z; poly.Vertices[0].Position[0] = bbMax[0]; poly.Vertices[0].Position[1] = bbMax[1]; poly.Vertices[0].Position[2] = bbMin[2]; poly.Vertices[1].Position[0] = bbMin[0]; poly.Vertices[1].Position[1] = bbMax[1]; poly.Vertices[1].Position[2] = bbMin[2]; poly.Vertices[2].Position[0] = bbMin[0]; poly.Vertices[2].Position[1] = bbMin[1]; poly.Vertices[2].Position[2] = bbMin[2]; poly.Vertices[3].Position[0] = bbMax[0]; poly.Vertices[3].Position[1] = bbMin[1]; poly.Vertices[3].Position[2] = bbMin[2]; if (IsPolyVisible(poly, cam)) { return true; } ins = false; } else if (cam.Position.Z > bbMax.Z) { poly.Plane.Normal.Z = 1.0f; poly.Plane.Dot = -bbMax.Z; poly.Vertices[0].Position[0] = bbMax[0]; poly.Vertices[0].Position[1] = bbMax[1]; poly.Vertices[0].Position[2] = bbMax[2]; poly.Vertices[1].Position[0] = bbMin[0]; poly.Vertices[1].Position[1] = bbMax[1]; poly.Vertices[1].Position[2] = bbMax[2]; poly.Vertices[2].Position[0] = bbMin[0]; poly.Vertices[2].Position[1] = bbMin[1]; poly.Vertices[2].Position[2] = bbMax[2]; poly.Vertices[3].Position[0] = bbMax[0]; poly.Vertices[3].Position[1] = bbMin[1]; poly.Vertices[3].Position[2] = bbMax[2]; if (IsPolyVisible(poly, cam)) { return true; } ins = false; } return ins; }
/// <summary> /// Check polygon visibility through the portal. /// </summary> public bool IsPolyVisible(Polygon p, Camera cam) { if (!p.DoubleSide && p.Plane.Distance(cam.Position) < 0) return false; // Direction from the camera position to an arbitrary vertex frustum StaticFuncs.Assert(Vertices.Any()); var dir = Vertices[0] - cam.Position; var lambda = 0.0f; // Polygon fits whole frustum (shouldn't happen, but we check anyway) if(p.RayIntersect(dir, cam.Position, ref lambda)) { return true; } // Generate queue order var nextPlaneIdx = 0; // 3 neighboring clipping planes var currentPlane = Planes.Last(); var prevPlane = Planes[Planes.Count - 2]; // in case no intersection var ins = true; // iterate through all the planes of this frustum for (var i = 0; i < Vertices.Count; i++) { var nextPlane = Planes[nextPlaneIdx]; // Queue vertices for testing var prevVertex = p.Vertices.Last(); // signed distance from the current point to the previous plane var dist0 = currentPlane.Distance(prevVertex.Position); var outs = true; // iterate through all the vertices of the polygon foreach (var currentVertex in p.Vertices) { var dist1 = currentPlane.Distance(currentVertex.Position); // the split point in the plane if(Math.Abs(dist0) < SPLIT_EPSILON) { if(prevPlane.Distance(prevVertex.Position) > -SPLIT_EPSILON && nextPlane.Distance(prevVertex.Position) > -SPLIT_EPSILON && Normal.Distance(prevVertex.Position) > -SPLIT_EPSILON) { // Frustum-vertex intersection test is passed return true; } } // vertices from different sides of the plane (or on it) if(dist0 * dist1 < 0 && Math.Abs(dist1) >= SPLIT_EPSILON) { // vector connecting vertices dir = currentVertex.Position - prevVertex.Position; // We are looking for the point of intersection var T = currentPlane.RayIntersect(prevVertex.Position, dir); if(prevPlane.Distance(T) > -SPLIT_EPSILON && nextPlane.Distance(T) > -SPLIT_EPSILON) { // Frustum-ray intersection test is passed return true; } } // point is outside if(dist1 < -SPLIT_EPSILON) { ins = false; } else { outs = false; } // We moved all the vertices of the polygon prevVertex = currentVertex; // We moved all distances dist0 = dist1; // finished with all polygon vertices } if(outs) { // all points are outside of the current plane - definitely exit return false; } // We moved all the clipping planes prevPlane = currentPlane; currentPlane = nextPlane; nextPlaneIdx++; // finished with all planes of this frustum } if(ins) { // all the vertices are inside - test is passed return true; } return false; }
/** Animated textures loading. * Natively, animated textures stored as a stream of bitu16s, which * is then parsed on the fly. What we do is parse this stream to the * proper structures to be used later within renderer. */ public static unsafe void TR_GenAnimTextures(World world, Level tr) { var p0 = new Polygon(); var p = new Polygon(); p0.Vertices.Resize(3, () => new Vertex()); p.Vertices.Resize(3, () => new Vertex()); fixed(ushort* tmp = tr.AnimatedTextures) { var pointer = tmp; var numUvrotates = tr.AnimatedTexturesUVCount; var numSequences = *pointer++; // First word in a stream is sequence count. world.AnimSequences.Resize(numSequences, () => new AnimSeq()); for(var i = 0; i < numSequences; i++) { var seq = world.AnimSequences[i]; seq.Frames.Resize(*pointer++ + 1, () => new TexFrame()); seq.FrameList.Resize(seq.Frames.Count); // Fill up new sequence with frame list seq.AnimType = TR_ANIMTEXTURE.Forward; seq.FrameLock = false; // by default anim is playing seq.UVRotate = false; // by default uvrotate seq.ReverseDirection = false; // Needed for proper reverse-type start-up. seq.FrameRate = 0.05f; // Should be passed as 1 / FPS. seq.FrameTime = 0.0f; // Reset frame time to initial state. seq.CurrentFrame = 0; // Reset current frame to zero. for(var j = 0; j < seq.Frames.Count; j++) { seq.FrameList[j] = *pointer++; // Add one frame. } // UVRotate textures case. // In TR4-5, it is possible to define special UVRotate animation mode. // It is specified by num_uvrotates variable. If sequence belongs to // UVRotate range, each frame will be divided in half and continously // scrolled from one part to another by shifting UV coordinates. // In OpenTomb, we can have BOTH UVRotate and classic frames mode // applied to the same sequence, but there we specify compatibility // method for TR4-5. var uvrotateScript = 0; var tmp1 = EngineLua["UVRotate"]; if(tmp1 != null) { uvrotateScript = (int) tmp1; } if(i < numUvrotates) { seq.FrameLock = false; // by default anim is playing seq.UVRotate = true; // Get texture height and divide it in half. // This way, we get a reference value which is used to identify // if scrolling is completed or not. seq.Frames.Resize(8, () => new TexFrame()); seq.UVRotateMax = world.TextureAtlas.GetTextureHeight(seq.FrameList[0]) / 2f; seq.UVRotateSpeed = seq.UVRotateMax / seq.Frames.Count; seq.FrameList.Resize(8); if(uvrotateScript > 0) { seq.AnimType = TR_ANIMTEXTURE.Forward; } else if(uvrotateScript < 0) { seq.AnimType = TR_ANIMTEXTURE.Backward; } EngineWorld.TextureAtlas.GetCoordinates(seq.FrameList[0], false, p, 0, true); for(var j = 0; j < seq.Frames.Count; j++) { EngineWorld.TextureAtlas.GetCoordinates(seq.FrameList[0], false, p, (int)(j * seq.UVRotateSpeed), true); seq.Frames[j].TextureIndex = p.TexIndex; var A0 = new[] { p0.Vertices[1].TexCoord[0] - p0.Vertices[0].TexCoord[0], // TODO: p0 hasn't been modified?? p0.Vertices[1].TexCoord[1] - p0.Vertices[0].TexCoord[1] }; var B0 = new[] { p0.Vertices[2].TexCoord[0] - p0.Vertices[0].TexCoord[0], p0.Vertices[2].TexCoord[1] - p0.Vertices[0].TexCoord[1] }; var A = new[] { p.Vertices[1].TexCoord[0] - p.Vertices[0].TexCoord[0], p.Vertices[1].TexCoord[1] - p.Vertices[0].TexCoord[1] }; var B = new[] { p.Vertices[2].TexCoord[0] - p.Vertices[0].TexCoord[0], p.Vertices[2].TexCoord[1] - p.Vertices[0].TexCoord[1] }; var d = A0[0] * B0[1] - A0[1] * B0[0]; seq.Frames[j].Mat[0 + 0 * 2] = (A[0] * B0[1] - A0[1] * B[0]) / d; seq.Frames[j].Mat[1 + 0 * 2] = -(A[1] * B0[1] - A0[1] * B[1]) / d; seq.Frames[j].Mat[0 + 1 * 2] = -(A0[0] * B[0] - A[0] * B0[0]) / d; seq.Frames[j].Mat[1 + 1 * 2] = (A0[0] * B[1] - A[1] * B0[0]) / d; seq.Frames[j].Move[0] = p.Vertices[0].TexCoord[0] - (p0.Vertices[0].TexCoord[0] * seq.Frames[j].Mat[0 + 0 * 2] + p0.Vertices[0].TexCoord[1] * seq.Frames[j].Mat[0 + 1 * 2]); seq.Frames[j].Move[1] = p.Vertices[0].TexCoord[1] - (p0.Vertices[0].TexCoord[0] * seq.Frames[j].Mat[1 + 0 * 2] + p0.Vertices[0].TexCoord[1] * seq.Frames[j].Mat[1 + 1 * 2]); } } else { EngineWorld.TextureAtlas.GetCoordinates(seq.FrameList[0], false, p0); for (var j = 0; j < seq.Frames.Count; j++) { EngineWorld.TextureAtlas.GetCoordinates(seq.FrameList[j], false, p); seq.Frames[j].TextureIndex = p.TexIndex; var A0 = new[] { p0.Vertices[1].TexCoord[0] - p0.Vertices[0].TexCoord[0], p0.Vertices[1].TexCoord[1] - p0.Vertices[0].TexCoord[1] }; var B0 = new[] { p0.Vertices[2].TexCoord[0] - p0.Vertices[0].TexCoord[0], p0.Vertices[2].TexCoord[1] - p0.Vertices[0].TexCoord[1] }; var A = new[] { p.Vertices[1].TexCoord[0] - p.Vertices[0].TexCoord[0], p.Vertices[1].TexCoord[1] - p.Vertices[0].TexCoord[1] }; var B = new[] { p.Vertices[2].TexCoord[0] - p.Vertices[0].TexCoord[0], p.Vertices[2].TexCoord[1] - p.Vertices[0].TexCoord[1] }; var d = A0[0] * B0[1] - A0[1] * B0[0]; seq.Frames[j].Mat[0 + 0 * 2] = (A[0] * B0[1] - A0[1] * B[0]) / d; seq.Frames[j].Mat[1 + 0 * 2] = -(A[1] * B0[1] - A0[1] * B[1]) / d; seq.Frames[j].Mat[0 + 1 * 2] = -(A0[0] * B[0] - A[0] * B0[0]) / d; seq.Frames[j].Mat[1 + 1 * 2] = (A0[0] * B[1] - A[1] * B0[0]) / d; seq.Frames[j].Move[0] = p.Vertices[0].TexCoord[0] - (p0.Vertices[0].TexCoord[0] * seq.Frames[j].Mat[0 + 0 * 2] + p0.Vertices[0].TexCoord[1] * seq.Frames[j].Mat[0 + 1 * 2]); seq.Frames[j].Move[1] = p.Vertices[0].TexCoord[1] - (p0.Vertices[0].TexCoord[0] * seq.Frames[j].Mat[1 + 0 * 2] + p0.Vertices[0].TexCoord[1] * seq.Frames[j].Mat[1 + 1 * 2]); } } } } }