public void GenerateLOD(int index, int level, List<StaticDesc> statics) { this.quadIndex = index; if (Game.Mode == "fnv") { this.quadLevel = 4; this.quadOffset = 16384f; if (level == 4) { return; } } else { this.quadLevel = level * 4; this.quadOffset = (float)level * 16384f; } if (this.lodLevelToGenerate != -1 && this.lodLevelToGenerate != this.quadLevel) return; //Console.WriteLine("Level=" + LODLevel + " quadLevel=" + quadLevel + " offset=" + quadOffset); List<QuadDesc> list1 = this.SortMeshesIntoQuads(statics); if (this.removeUnseenFaces) { for (int index1 = 0; index1 < list1.Count; index1++) { QuadDesc quad = list1[index1]; if (this.LoadTerrainQuad(quad, out quad.terrainQuadTree, out quad.waterQuadTree, out quad.boundingBox)) { quad.hasTerrainVertices = true; list1[index1] = quad; } } } this.quadList = list1; List<Thread> list2 = new List<Thread>(); for (int index1 = 0; index1 < list1.Count; ++index1) { QuadDesc quadDesc = list1[index1]; if ((this.lodX == -1 || this.lodX == quadDesc.x) && (this.lodY == -1 || this.lodY == quadDesc.y)) { while (list2.Count == 8) { for (int index2 = 0; index2 < list2.Count; ++index2) { if (!list2[index2].IsAlive) { list2.RemoveAt(index2); --index2; } } } list2.Add(new Thread((ParameterizedThreadStart)(state => { QuadDesc quad = (QuadDesc)state; if (this.verbose) { this.logFile.WriteLog("Started LOD level " + this.quadLevel.ToString() + " coord [" + quad.x.ToString() + ", " + quad.y.ToString() + "]"); } NiFile file = new NiFile(); NiNode rootNiNode = new NiNode(); BSMultiBoundNode rootBSMultiBoundNode = new BSMultiBoundNode(); if (Game.Mode != "fnv") { file.AddBlock((NiObject)rootNiNode); rootNiNode.SetNameIndex(file.AddString("obj")); } else { file.AddBlock((NiObject)rootBSMultiBoundNode); } List<ShapeDesc> shapes = new List<ShapeDesc>(); for (int index11 = 0; index11 < quad.statics.Count; ++index11) { if (!(quad.statics[index11].staticModels[index] == "")) { if (Game.Mode == "fnv") { shapes.AddRange((IEnumerable<ShapeDesc>)this.ParseNif(quad, quad.statics[index11], index, file, rootNiNode)); } else { shapes.AddRange((IEnumerable<ShapeDesc>)this.ParseNif(quad, quad.statics[index11], index, file, rootBSMultiBoundNode)); } } } if (this.mergeShapes) this.MergeNodes(shapes); if (Game.Mode != "fnv") { this.CreateLODNodes(file, rootNiNode, quad, shapes); if ((int)rootNiNode.GetNumChildren() == 0) { //logFile.WriteLog("quad empty " + quadLevel + " " + quad.x + ", " + quad.y); //return; } } else { this.CreateLODNodesFNV(file, rootBSMultiBoundNode, quad, shapes); if ((int)rootBSMultiBoundNode.GetNumChildren() == 0) { //logFile.WriteLog("quad empty " + quadLevel + " " + quad.x + ", " + quad.y); return; } } if (Game.Mode == "fnv") { if (level == 1) { file.Write(this.outputDir + (object)"\\" + this.worldspaceName + ".Level4.X" + quad.x.ToString() + ".Y" + quad.y.ToString() + ".nif", logFile); } else { file.Write(this.outputDir + (object)"\\" + this.worldspaceName + ".Level4.High.X" + quad.x.ToString() + ".Y" + quad.y.ToString() + ".nif", logFile); } } else { file.Write(this.outputDir + (object)"\\" + this.worldspaceName + "." + this.quadLevel.ToString() + "." + quad.x.ToString() + "." + quad.y.ToString() + ".bto", logFile); } this.logFile.WriteLog("Finished LOD level " + (object)this.quadLevel.ToString() + " coord [" + quad.x.ToString() + ", " + quad.y.ToString() + "] [" + quad.outValues.totalTriCount.ToString() + "/" + quad.outValues.reducedTriCount.ToString() + "]"); }))); list2[list2.Count - 1].Start((object)quadDesc); } } while (list2.Count > 0) { for (int index1 = 0; index1 < list2.Count; ++index1) { if (!list2[index1].IsAlive) { list2.RemoveAt(index1); --index1; } } } int num1 = 0; int num2 = 0; foreach (QuadDesc quadDesc in list1) { num1 += quadDesc.outValues.totalTriCount; num2 += quadDesc.outValues.reducedTriCount; } this.logFile.WriteLog("LOD level " + this.quadLevel.ToString() + " total triangles " + num1.ToString() + " reduced to " + num2.ToString()); }
private List<ShapeDesc> IterateNodes(QuadDesc quad, StaticDesc stat, int level, NiFile file, NiNode parentNode, Matrix44 parentTransform, float parentScale) { List<ShapeDesc> list = new List<ShapeDesc>(); if (parentNode == null || parentNode.IsHidden()) return list; int nameIndex = parentNode.GetNameIndex(); string str = nameIndex != -1 ? file.GetStringAtIndex(nameIndex) : parentNode.GetName(); if (str != null && str.ToLower().Contains("editormarker")) return list; Matrix44 parentTransform1 = parentNode.GetTransform() * parentTransform; if (ignoreTransRot.Any(stat.staticModels[level].Contains)) { parentTransform1 = parentTransform; } float parentScale1 = parentNode.GetScale() * parentScale; uint numChildren = parentNode.GetNumChildren(); for (int index = 0; (long)index < (long)numChildren; ++index) { NiObject blockAtIndex = file.GetBlockAtIndex(parentNode.GetChildAtIndex(index)); if (blockAtIndex != null) { if (blockAtIndex.IsDerivedType("NiNode")) { list.AddRange((IEnumerable<ShapeDesc>)this.IterateNodes(quad, stat, level, file, (NiNode)blockAtIndex, parentTransform1, parentScale1)); } else if (blockAtIndex.IsDerivedType("NiTriBasedGeom")) { NiTriBasedGeom geomOld = (NiTriBasedGeom)blockAtIndex; if (index + 1 < numChildren) { NiObject blockAtIndexNext = file.GetBlockAtIndex(parentNode.GetChildAtIndex(index + 1)); if (blockAtIndexNext.IsDerivedType("NiTriBasedGeom")) { NiTriBasedGeom geomOld2 = (NiTriBasedGeom)blockAtIndexNext; if (geomOld.GetData() == geomOld2.GetData()) { //Console.WriteLine(geomOld.GetData() + " = " + geomOld2.GetData()); NiTriBasedGeom geomNew = new NiTriBasedGeom(); geomNew.SetFlags(14); geomNew.SetTranslation(geomOld.GetTranslation()); geomNew.SetRotation(geomOld.GetRotation()); geomNew.SetScale(geomOld.GetScale()); geomNew.SetNumProperties(geomOld.GetNumProperties()); for (int index2 = 0; (long)index2 < (long)geomOld.GetNumProperties(); ++index2) { geomNew.SetProperties(geomOld.GetProperty(index2)); } NiTriShapeData dataOld = (NiTriShapeData)file.GetBlockAtIndex(geomOld.GetData()); NiTriShapeData dataNew = new NiTriShapeData(); dataNew.SetNumVertices(dataOld.GetNumVertices()); dataNew.SetHasVertices(dataOld.HasVertices()); dataNew.SetVertices(new List<Vector3>(dataOld.GetVertices())); dataNew.SetNumUVSets(dataOld.GetBSNumUVSets()); dataNew.SetHasNormals(dataOld.HasNormals()); dataNew.SetNormals(new List<Vector3>(dataOld.GetNormals())); dataNew.SetTangents(new List<Vector3>(dataOld.GetTangents())); dataNew.SetBitangents(new List<Vector3>(dataOld.GetBitangents())); dataNew.SetCenter(dataOld.GetCenter()); dataNew.SetRadius(dataOld.GetRadius()); dataNew.SetHasVertexColors(dataOld.HasVertexColors()); dataNew.SetVertexColors(dataOld.GetVertexColors()); dataNew.SetUVCoords(dataOld.GetUVCoords()); dataNew.SetConsistencyFlags(dataOld.GetConsistencyFlags()); dataNew.SetNumTriangles(dataOld.GetNumTriangles()); dataNew.SetNumTrianglePoints(dataOld.GetNumTrianglePoints()); dataNew.SetHasTriangles(dataOld.HasTriangles()); dataNew.SetTriangles(new List<Triangle>(dataOld.GetTriangles())); int newdata = file.AddBlock(dataNew); geomNew.SetData(newdata); int newblock = file.AddBlock(geomNew); blockAtIndex = file.GetBlockAtIndex(newblock); //Console.WriteLine("New block " + newdata + " = " + newblock); } } } ShapeDesc shapeDesc = this.TransformShape(quad, stat, file, (NiTriBasedGeom)blockAtIndex, parentTransform1, parentScale1); if (shapeDesc != null && shapeDesc.shape != null) { list.Add(shapeDesc); } } } } /* if (useOptimizer) { List<ShapeDesc> list2 = new List<ShapeDesc>(); for (int shapeIndex = 0; shapeIndex < list.Count; shapeIndex++) { ShapeDesc shapeDesc = list[shapeIndex]; NiTriShapeData data = shapeDesc.data; List<Triangle> triangles = data.GetTriangles(); if (data.HasVertexColors() && triangles.Count != 0) { NiTriShapeData data1 = new NiTriShapeData(); NiTriShapeData data2 = new NiTriShapeData(); List<Color4> vertexcolors = data.GetVertexColors(); bool vertexcolors_white = true; bool vertexcolors_colors = true; if (vertexcolors_white && vertexcolors_colors) { Dictionary<ushort, ushort>[] dictionary = new Dictionary<ushort, ushort>[] { new Dictionary<ushort, ushort>(), new Dictionary<ushort, ushort>() }; List<Vector3> vertices = data.GetVertices(); List<UVCoord> uvcoords = data.GetUVCoords(); List<Vector3> normals = data.GetNormals(); List<Vector3> tangents = data.GetTangents(); List<Vector3> bitangents = data.GetBitangents(); List<Triangle>[] triangles1 = new List<Triangle>[] { new List<Triangle>(), new List<Triangle>() }; List<Vector3>[] vertices1 = new List<Vector3>[] { new List<Vector3>(), new List<Vector3>() }; List<UVCoord>[] uvcoords1 = new List<UVCoord>[] { new List<UVCoord>(), new List<UVCoord>() }; List<Color4>[] vertexcolors1 = new List<Color4>[] { new List<Color4>(), new List<Color4>() }; List<Vector3>[] normals1 = new List<Vector3>[] { new List<Vector3>(), new List<Vector3>() }; List<Vector3>[] tangents1 = new List<Vector3>[] { new List<Vector3>(), new List<Vector3>() }; List<Vector3>[] bitangents1 = new List<Vector3>[] { new List<Vector3>(), new List<Vector3>() }; UInt16[] index2 = new UInt16[] { 0, 0 }; for (int index = 0; index < triangles.Count; ++index) { ushort indexA = triangles[index][0]; ushort indexB = triangles[index][1]; ushort indexC = triangles[index][2]; float colorA = vertexcolors[indexA][0] + vertexcolors[indexA][1] + vertexcolors[indexA][2]; float colorB = vertexcolors[indexB][0] + vertexcolors[indexB][1] + vertexcolors[indexB][2]; float colorC = vertexcolors[indexC][0] + vertexcolors[indexC][1] + vertexcolors[indexC][2]; int shapeSplit = new int(); if (colorA > 2.6f && colorB > 2.6f && colorC > 2.6f) { shapeSplit = 0; } else { shapeSplit = 1; } if (!dictionary[shapeSplit].ContainsKey(indexA)) { dictionary[shapeSplit].Add(indexA, index2[shapeSplit]); vertices1[shapeSplit].Add(vertices[indexA]); if (uvcoords.Count != 0) { uvcoords1[shapeSplit].Add(uvcoords[indexA]); } if (shapeSplit == 1 && vertexcolors.Count != 0) { vertexcolors1[shapeSplit].Add(vertexcolors[indexA]); } if (normals.Count != 0) { normals1[shapeSplit].Add(normals[indexA]); } if (tangents.Count != 0) { tangents1[shapeSplit].Add(tangents[indexA]); } if (bitangents.Count != 0) { bitangents1[shapeSplit].Add(bitangents[indexA]); } indexA = index2[shapeSplit]; ++index2[shapeSplit]; } else { indexA = dictionary[shapeSplit][indexA]; } if (!dictionary[shapeSplit].ContainsKey(indexB)) { dictionary[shapeSplit].Add(indexB, index2[shapeSplit]); vertices1[shapeSplit].Add(vertices[indexB]); if (uvcoords.Count != 0) { uvcoords1[shapeSplit].Add(uvcoords[indexB]); } if (shapeSplit == 1 && vertexcolors.Count != 0) { vertexcolors1[shapeSplit].Add(vertexcolors[indexB]); } if (normals.Count != 0) { normals1[shapeSplit].Add(normals[indexB]); } if (tangents.Count != 0) { tangents1[shapeSplit].Add(tangents[indexB]); } if (bitangents.Count != 0) { bitangents1[shapeSplit].Add(bitangents[indexB]); } indexB = index2[shapeSplit]; ++index2[shapeSplit]; } else { indexB = dictionary[shapeSplit][indexB]; } if (!dictionary[shapeSplit].ContainsKey(indexC)) { dictionary[shapeSplit].Add(indexC, index2[shapeSplit]); vertices1[shapeSplit].Add(vertices[indexC]); if (uvcoords.Count != 0) { uvcoords1[shapeSplit].Add(uvcoords[indexC]); } if (shapeSplit == 1 && vertexcolors.Count != 0) { vertexcolors1[shapeSplit].Add(vertexcolors[indexC]); } if (normals.Count != 0) { normals1[shapeSplit].Add(normals[indexC]); } if (tangents.Count != 0) { tangents1[shapeSplit].Add(tangents[indexC]); } if (bitangents.Count != 0) { bitangents1[shapeSplit].Add(bitangents[indexC]); } indexC = index2[shapeSplit]; ++index2[shapeSplit]; } else { indexC = dictionary[shapeSplit][indexC]; } triangles1[shapeSplit].Add(new Triangle(indexA, indexB, indexC)); } if (triangles1[0].Count != 0) { ShapeDesc shapeDesc1 = new ShapeDesc(); shapeDesc1.shape = shapeDesc.shape; shapeDesc1.material = shapeDesc.material; shapeDesc1.textures = shapeDesc.textures; shapeDesc1.isHighDetail = shapeDesc.isHighDetail; shapeDesc1.hasVertexColor = false; shapeDesc1.isDoubleSided = shapeDesc.isDoubleSided; shapeDesc1.TextureClampMode = shapeDesc.TextureClampMode; shapeDesc1.boundingBox = shapeDesc.boundingBox; shapeDesc1.x = shapeDesc.x; shapeDesc1.y = shapeDesc.y; shapeDesc1.segments = shapeDesc.segments; data1.SetNumVertices((ushort)vertices1[0].Count); data1.SetHasVertices(true); data1.SetNumUVSets(4097); data1.SetHasNormals(true); data1.SetCenter(data.GetCenter()); data1.SetRadius(data.GetRadius()); data1.SetConsistencyFlags(data.GetConsistencyFlags()); data1.SetNumTriangles((ushort)triangles1[0].Count); data1.SetNumTrianglePoints((uint)triangles1[0].Count * 3); data1.SetHasTriangles(true); data1.SetHasVertexColors(false); data1.SetVertices(vertices1[0]); data1.SetNormals(normals1[0]); data1.SetTangents(tangents1[0]); data1.SetBitangents(bitangents1[0]); data1.SetUVCoords(uvcoords1[0]); //data1.SetVertexColors(vertexcolors1[0]); data1.SetTriangles(triangles1[0]); shapeDesc1.data = data1; list2.Add(shapeDesc1); } if (triangles1[1].Count != 0) { ShapeDesc shapeDesc2 = new ShapeDesc(); shapeDesc2.shape = shapeDesc.shape; shapeDesc2.material = shapeDesc.material; shapeDesc2.textures = shapeDesc.textures; shapeDesc2.isHighDetail = shapeDesc.isHighDetail; shapeDesc2.hasVertexColor = true; shapeDesc2.isDoubleSided = shapeDesc.isDoubleSided; shapeDesc2.TextureClampMode = shapeDesc.TextureClampMode; shapeDesc2.boundingBox = shapeDesc.boundingBox; shapeDesc2.x = shapeDesc.x; shapeDesc2.y = shapeDesc.y; shapeDesc2.segments = shapeDesc.segments; data2.SetNumVertices((ushort)vertices1[0].Count); data2.SetHasVertices(true); data2.SetNumUVSets(4097); data2.SetHasNormals(true); data2.SetCenter(data.GetCenter()); data2.SetRadius(data.GetRadius()); data2.SetConsistencyFlags(data.GetConsistencyFlags()); data2.SetNumTriangles((ushort)triangles1[0].Count); data2.SetNumTrianglePoints((uint)triangles1[0].Count * 3); data2.SetHasTriangles(true); data2.SetHasVertexColors(true); data2.SetVertices(vertices1[1]); data2.SetNormals(normals1[1]); data2.SetTangents(tangents1[1]); data2.SetBitangents(bitangents1[1]); data2.SetUVCoords(uvcoords1[1]); data2.SetVertexColors(vertexcolors1[1]); data2.SetTriangles(triangles1[1]); shapeDesc2.data = data2; list2.Add(shapeDesc2); } } else { list2.Add(shapeDesc); } } else { list2.Add(shapeDesc); } } return list2; }*/ return list; }
public void GenerateTest(string strfile, LogFile logFile) { NiFile file = new NiFile(); NiNode rootNode = new NiNode(); file.AddBlock((NiObject)rootNode); this.quadLevel = 4; this.quadOffset = 16384f; QuadDesc quad = new QuadDesc(); quad.x = 0; quad.y = 0; quad.statics = new List<StaticDesc>(); StaticDesc curStat = new StaticDesc(); curStat.refID = "0"; curStat.rotX = 0.0f; curStat.rotY = 0.0f; curStat.rotZ = 0.0f; curStat.scale = 1f; curStat.x = 0.0f; curStat.y = 0.0f; curStat.z = 0.0f; curStat.staticName = "Test"; curStat.staticFullModel = strfile; curStat.staticModels = new string[3]; curStat.staticModels[0] = strfile; curStat.staticModels[1] = strfile; curStat.staticModels[2] = strfile; quad.statics.Add(curStat); List<ShapeDesc> shapes = this.ParseNif(quad, curStat, 0, file, rootNode); this.MergeNodes(shapes); for (int index = 0; index < shapes.Count; ++index) { BSSegmentedTriShape segmentedTriShape = new BSSegmentedTriShape((NiGeometry)shapes[index].shape); segmentedTriShape.SetRotation(new Matrix33(true)); segmentedTriShape.SetTranslation(new Vector3(0.0f, 0.0f, 0.0f)); rootNode.AddChild(file.AddBlock((NiObject)segmentedTriShape)); segmentedTriShape.SetData(file.AddBlock((NiObject)shapes[index].data)); } file.Write(this.outputDir + "\\" + this.worldspaceName + ".test.nif", logFile); }
private void CreateLODNodes(NiFile file, NiNode rootNode, QuadDesc quad, List<ShapeDesc> shapes) { foreach (ShapeDesc shapeDesc in shapes) { BSMultiBoundNode node = new BSMultiBoundNode(); rootNode.AddChild(file.AddBlock((NiObject)node)); BSSegmentedTriShape segmentedTriShape = new BSSegmentedTriShape((NiGeometry)shapeDesc.shape); node.AddChild(file.AddBlock((NiObject)segmentedTriShape)); node.SetCullMode(1U); string str = "obj"; // use material name from list file, Snow/Ash if (!this.ignoreMaterial && shapeDesc.material != "") { str = str + shapeDesc.material; } // only level 4 should have HD if (shapeDesc.isHighDetail && (this.quadLevel == 4 || this.useHDFlag)) { str = str + "HD"; } segmentedTriShape.SetNameIndex(file.AddString(str)); segmentedTriShape.SetFlags((ushort)14); segmentedTriShape.SetFlags2((ushort)8320); segmentedTriShape.SetData(file.AddBlock((NiObject)shapeDesc.data)); segmentedTriShape.SetBSProperty(1, -1); segmentedTriShape.SetScale((float)this.quadLevel); segmentedTriShape.SetRotation(new Matrix33(true)); segmentedTriShape.SetTranslation(new Vector3((float)quad.x * 4096f, (float)quad.y * 4096f, 0.0f)); for (int index = 0; index < 16; ++index) { segmentedTriShape.AddSegment(new BSSegment(0U, (ushort)0)); } for (int index = 0; index < shapeDesc.segments.Count; ++index) { BSSegment segmentAtIndex = segmentedTriShape.GetSegmentAtIndex(shapeDesc.segments[index].id); segmentAtIndex.startTriangle = shapeDesc.segments[index].startTriangle; segmentAtIndex.numTriangles = shapeDesc.segments[index].numTriangles; segmentedTriShape.SetSegment(shapeDesc.segments[index].id, segmentAtIndex); } for (int index = 15; index >= 0 && (int)segmentedTriShape.GetSegmentAtIndex(index).numTriangles == 0; --index) { segmentedTriShape.RemoveSegment(index); } BSShaderTextureSet shaderTextureSet = new BSShaderTextureSet(); BSLightingShaderProperty lightingShaderProperty = new BSLightingShaderProperty(); segmentedTriShape.SetBSProperty(0, file.AddBlock((NiObject)lightingShaderProperty)); lightingShaderProperty.SetTextureSet(file.AddBlock((NiObject)shaderTextureSet)); lightingShaderProperty.SetLightingEffect1(0.0f); lightingShaderProperty.SetLightingEffect2(0.0f); lightingShaderProperty.SetGlossiness(1f); lightingShaderProperty.SetTextureClampMode(shapeDesc.TextureClampMode); uint num1 = 2151677952U; /*if (this.quadLevel == 4) { num1 = 2151678720U; // Recieve/Cast Shadows - no worky :( }*/ uint num3 = 1U; // ZBuffer_Write if (shapeDesc.isHighDetail && (this.quadLevel == 4 || this.useHDFlag)) { num3 |= 2147483648U; //HD_LOD_Objects } else { num3 |= 4U; //LOD_Objects } if (shapeDesc.data.HasVertexColors()) num3 |= 32U; // set double-sided flag if (shapeDesc.isDoubleSided) num3 |= 16; lightingShaderProperty.SetShaderFlags1(num1); lightingShaderProperty.SetShaderFlags2(num3); shaderTextureSet.SetNumTextures(9); shaderTextureSet.SetTexture(0, shapeDesc.textures[0]); shaderTextureSet.SetTexture(1, shapeDesc.textures[1]); this.GenerateMultibound(file, node, quad, shapeDesc.boundingBox); } }
private List<ShapeDesc> ParseNif(QuadDesc quad, StaticDesc curStat, int level, NiFile file, NiNode rootNode) { NiFile file1 = new NiFile(); file1.Read(this.gameDir, curStat.staticModels[level], this.logFile); return this.IterateNodes(quad, curStat, level, file1, (NiNode)file1.GetBlockAtIndex(0), new Matrix44(true), 1f); }