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 void CreateLODNodesFNV(NiFile file, BSMultiBoundNode node, QuadDesc quad, List<ShapeDesc> shapes) { foreach (ShapeDesc shapeDesc in shapes) { 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 || useHDFlag)) { str = str + "HD"; } //segmentedTriShape.SetNameIndex(file.AddString(str)); segmentedTriShape.SetFlags((ushort)14); segmentedTriShape.SetFlags2((ushort)8); //segmentedTriShape.SetBSProperty(1, -1); segmentedTriShape.SetTranslation(new Vector3((float)quad.x * 4096f, (float)quad.y * 4096f, 0.0f)); segmentedTriShape.SetRotation(new Matrix33(true)); segmentedTriShape.SetScale((float)this.quadLevel); BSShaderTextureSet shaderTextureSet = new BSShaderTextureSet(); BSShaderPPLightingProperty lightingShaderProperty = new BSShaderPPLightingProperty(); segmentedTriShape.SetProperties(file.AddBlock((NiObject)lightingShaderProperty)); lightingShaderProperty.SetTextureSet(file.AddBlock((NiObject)shaderTextureSet)); shaderTextureSet.SetNumTextures(6); shaderTextureSet.SetTexture(0, shapeDesc.textures[0]); shaderTextureSet.SetTexture(1, shapeDesc.textures[1]); segmentedTriShape.SetData(file.AddBlock((NiObject)shapeDesc.data)); 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); } this.GenerateMultibound(file, node, quad, shapeDesc.boundingBox); } }
private void GenerateMultibound(NiFile file, BSMultiBoundNode node, QuadDesc curQuad, BBox bb) { if ((Game.Mode != "fnv") || (Game.Mode == "fnv" && node.GetMultiBound() == -1)) { BSMultiBound bsMultiBound = new BSMultiBound(); node.SetMultiBound(file.AddBlock((NiObject)bsMultiBound)); BSMultiBoundAABB bsMultiBoundAabb = new BSMultiBoundAABB(); bsMultiBound.SetData(file.AddBlock((NiObject)bsMultiBoundAabb)); float num1 = (float)curQuad.x * 4096f; float num2 = (float)curQuad.y * 4096f; bsMultiBoundAabb.SetPosition(new Vector3((float)(((double)num1 + (double)bb.px1 + ((double)num1 + (double)bb.px2)) / 2.0), (float)(((double)num2 + (double)bb.py1 + ((double)num2 + (double)bb.py2)) / 2.0), (float)(((double)bb.pz1 + (double)bb.pz2) / 2.0))); bsMultiBoundAabb.SetExtent(new Vector3((float)(((double)bb.px2 - (double)bb.px1) / 2.0), (float)(((double)bb.py2 - (double)bb.py1) / 2.0), (float)(((double)bb.pz2 - (double)bb.pz1) / 2.0))); } }
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); } }