private void GetVertexes(BSPNode node, byte[] interpreterData) { if (node == null) { return; } if (node.faces != null) { foreach (var face in node.faces) { foreach (var point in face.Points) { Fix x = point.Point.X; Fix y = point.Point.Y; Fix z = point.Point.Z; //TODO this is a bit slow overall var vec = new FixVector(x, y, z); if (!vertexDict.ContainsKey(vec)) { vertexDict.Add(vec, vertexDict.Count + mVertexOffset); } } } } GetVertexes(node.Front, interpreterData); GetVertexes(node.Back, interpreterData); }
public void GetFaces(BSPNode node, byte[] data, ref int modelDataOffset) { if (node == null) { return; } if (node.Front == null && node.Back != null) { throw new Exception("ModelData::GetFaces: Front is null but back isn't."); } else if (node.Front != null && node.Back == null) { throw new Exception("ModelData::GetFaces: Back is null but front isn't."); } if (node.Front != null && node.Back != null) { if (node.Point.X == node.Point.Y && node.Point.Y == node.Point.Z && node.Point.Z == 0.0f) { //TODO: figure out if this is actually an error condition. throw new Exception("ModelData::GetFaces: splitter point is 0 but this probably isn't a problem. Probably."); } // Sort start int sortStatPosition = modelDataOffset; PolymodelBuilder.SetShort(data, ref modelDataOffset, 4); // SORTNORM opcode PolymodelBuilder.SetShort(data, ref modelDataOffset, 0); // int n_points FixVector normal = new FixVector(node.Normal.X, node.Normal.Y, node.Normal.Z); FixVector point = new FixVector(node.Point.X, node.Point.Y, node.Point.Z); PolymodelBuilder.SetFixVector(data, ref modelDataOffset, normal); PolymodelBuilder.SetFixVector(data, ref modelDataOffset, point); int backOffset = modelDataOffset; PolymodelBuilder.SetShort(data, ref modelDataOffset, (short)backOffset); // fix the back offset later int frontOffset = modelDataOffset; PolymodelBuilder.SetShort(data, ref modelDataOffset, (short)frontOffset); // fix the front offset later // Terminator opcode PolymodelBuilder.SetShort(data, ref modelDataOffset, ModelOpCode.End); // Process front and store offset int frontOffsetValue = modelDataOffset - sortStatPosition; GetFaces(node.Front, data, ref modelDataOffset); // Process back and store offset int backOffsetValue = modelDataOffset - sortStatPosition; GetFaces(node.Back, data, ref modelDataOffset); // Store the end position int endPosition = modelDataOffset; if (frontOffsetValue > short.MaxValue || backOffsetValue > short.MaxValue || modelDataOffset < 0) { throw new ArgumentException("Model is too complex: 32KB displacement limit exceeded."); } // Correct the back offset modelDataOffset = backOffset; PolymodelBuilder.SetShort(data, ref modelDataOffset, (short)frontOffsetValue); // fix the back offset later // Correct the front offset modelDataOffset = frontOffset; PolymodelBuilder.SetShort(data, ref modelDataOffset, (short)backOffsetValue); // fix the back offset later // Restore the offset to the end position modelDataOffset = endPosition; if (node.faces != null && node.faces.Any()) { throw new Exception("Missing faces!"); } } else if (node.faces != null) { int facesStatPosition = modelDataOffset; BSPVertex vert; short vertexNum; foreach (var face in node.faces) { if (face.TextureID == -1) { // Flat poly opcode PolymodelBuilder.SetShort(data, ref modelDataOffset, ModelOpCode.FlatPoly); short pointc = (short)face.Points.Count(); PolymodelBuilder.SetShort(data, ref modelDataOffset, pointc); Fix x = face.Point.X; Fix y = face.Point.Y; Fix z = face.Point.Z; var facePoint = new FixVector(x, y, z); PolymodelBuilder.SetFixVector(data, ref modelDataOffset, facePoint); x = face.Normal.X; y = face.Normal.Y; z = face.Normal.Z; var normal = new FixVector(x, y, z); PolymodelBuilder.SetFixVector(data, ref modelDataOffset, normal); PolymodelBuilder.SetShort(data, ref modelDataOffset, (short)face.Color); for (short i = 0; i < pointc; i++) { vert = face.Points[i]; FixVector vec = new FixVector(vert.Point.X, vert.Point.Y, vert.Point.Z); vertexNum = (short)vertexDict[vec]; PolymodelBuilder.SetShort(data, ref modelDataOffset, vertexNum); } if (pointc % 2 == 0) { PolymodelBuilder.SetShort(data, ref modelDataOffset, 0); } } else { // tmapped poly opcode PolymodelBuilder.SetShort(data, ref modelDataOffset, ModelOpCode.TexturedPoly); short pointc = (short)face.Points.Count(); PolymodelBuilder.SetShort(data, ref modelDataOffset, pointc); Fix x = face.Point.X; Fix y = face.Point.Y; Fix z = face.Point.Z; var facePoint = new FixVector(x, y, z); PolymodelBuilder.SetFixVector(data, ref modelDataOffset, facePoint); x = face.Normal.X; y = face.Normal.Y; z = face.Normal.Z; var normal = new FixVector(x, y, z); PolymodelBuilder.SetFixVector(data, ref modelDataOffset, normal); PolymodelBuilder.SetShort(data, ref modelDataOffset, (short)face.TextureID); for (short i = 0; i < pointc; i++) { vert = face.Points[i]; FixVector vec = new FixVector(vert.Point.X, vert.Point.Y, vert.Point.Z); vertexNum = (short)vertexDict[vec]; PolymodelBuilder.SetShort(data, ref modelDataOffset, vertexNum); } if (pointc % 2 == 0) { PolymodelBuilder.SetShort(data, ref modelDataOffset, 0); } for (short i = 0; i < pointc; i++) { x = face.Points[i].UVs.X; y = face.Points[i].UVs.Y; z = face.Points[i].UVs.Z; var uv = new FixVector(x, y, z); PolymodelBuilder.SetFixVector(data, ref modelDataOffset, uv); } } } PolymodelBuilder.SetShort(data, ref modelDataOffset, ModelOpCode.End); } }
public void BuildTree(BSPNode node, List <BSPFace> faces) { #if DEBUG_SPLITTER_DIAGONISTICS if (recursionLevel == 0) { Console.WriteLine("----------------------------------------------"); Console.WriteLine("STARTING"); Console.WriteLine("----------------------------------------------"); } recursionLevel++; #endif List <BSPFace> frontList = new List <BSPFace>(); List <BSPFace> backList = new List <BSPFace>(); node.Splitter = FindSplitter(faces, ref node.Point, ref node.Normal); if (node.Splitter == null) //If a splitter wasn't found, this set of faces is convex { node.faces = faces; node.type = BSPNodeType.Leaf; } else //A splitter is known, so do any needed splits and recurse { foreach (BSPFace face in faces) { if (face != node.Splitter) //splitter is classified and added later. { ClassifyFace(face, node.Splitter.Point, node.Splitter.Normal); //else // //[ISB] Fix bug with splitters ending up on both sides. Doom puts them in front implicity // face.Classification = BSPClassification.Front; switch (face.Classification) { case BSPClassification.Front: frontList.Add(face); break; case BSPClassification.Back: backList.Add(face); break; case BSPClassification.Spanning: BSPFace frontFace = new BSPFace(); BSPFace backFace = new BSPFace(); if (face.TextureID == -1) // colored face { frontFace.Color = backFace.Color = face.Color; } SplitPolygon(face, node.Splitter.Point, node.Splitter.Normal, ref frontFace, ref backFace); frontList.Add(frontFace); backList.Add(backFace); break; default: throw new Exception("BSPTree::BuildTree: Face has invalid classification."); } } } //Where does the splitter go? //Try it in both the front and back. Whichever one generates the highest score fails. int frontScore = 0; int backScore = 0; int tempScore; List <BSPFace> faceTemp; if (frontList.Count > 0) { faceTemp = new List <BSPFace>(frontList); faceTemp.Add(node.Splitter); foreach (BSPFace face in faceTemp) { tempScore = EvalulateSplitter(faceTemp, face); if (tempScore != int.MaxValue && tempScore > frontScore) { frontScore = tempScore; } } } else { frontScore = int.MaxValue; } if (backList.Count > 0) { faceTemp = new List <BSPFace>(backList); faceTemp.Add(node.Splitter); foreach (BSPFace face in faceTemp) { tempScore = EvalulateSplitter(faceTemp, face); if (tempScore != int.MaxValue && tempScore > backScore) { backScore = tempScore; } } } else { backScore = int.MaxValue; } if (frontScore > backScore) { frontList.Add(node.Splitter); } else { backList.Add(node.Splitter); } #if DEBUG_SPLITTER_DIAGONISTICS if (recursionLevel <= 2) { Console.WriteLine("Frontlist:{0} Backlist:{1}, fs<bs:{2}", frontList.Count, backList.Count, frontScore < backScore); } #endif if (frontList.Count > 0) { BSPNode newNode = new BSPNode(); newNode.type = BSPNodeType.Node; #if DEBUG_SPLITTER_DIAGONISTICS if (recursionLevel <= 1) { Console.WriteLine("Doing front."); } #endif BuildTree(newNode, frontList); node.Front = newNode; } if (backList.Count > 0) { BSPNode newNode = new BSPNode(); newNode.type = BSPNodeType.Node; #if DEBUG_SPLITTER_DIAGONISTICS if (recursionLevel <= 1) { Console.WriteLine("Doing back."); } #endif BuildTree(newNode, backList); node.Back = newNode; } } #if DEBUG_SPLITTER_DIAGONISTICS recursionLevel--; #endif }