public static void Set(string key, ShapeDesc value) { if (!Contains(key)) { _list.TryAdd(key, value); } }
public ShapeDesc(ShapeDesc shapeDesc) { this.name = shapeDesc.name; this.staticName = shapeDesc.name; this.staticModel = shapeDesc.name; this.geometry = null; this.shaderType = shapeDesc.shaderType; this.effectShader = shapeDesc.effectShader; this.lightingShader = shapeDesc.lightingShader; this.texturingProperty = shapeDesc.texturingProperty; this.materialProperty = shapeDesc.materialProperty; this.sourceTextureBase = shapeDesc.sourceTextureBase; this.sourceTextureDetail = shapeDesc.sourceTextureDetail; this.sourceTextureGlow = shapeDesc.sourceTextureGlow; this.sourceTextureBump = shapeDesc.sourceTextureBump; this.shaderHash = shapeDesc.shaderHash; this.shapeHash = shapeDesc.shapeHash; this.material = shapeDesc.material; this.textures = shapeDesc.textures; this.isPassThru = shapeDesc.isPassThru; this.isGroup = shapeDesc.isGroup; this.isHighDetail = shapeDesc.isHighDetail; this.hasVertexColor = shapeDesc.hasVertexColor; this.allWhite = shapeDesc.allWhite; this.isDoubleSided = shapeDesc.isDoubleSided; this.isAlpha = shapeDesc.isAlpha; this.isDecal = shapeDesc.isDecal; this.enableParent = shapeDesc.enableParent; this.TextureClampMode = shapeDesc.TextureClampMode; this.boundingBox = new BBox(); this.x = shapeDesc.x; this.y = shapeDesc.y; this.segments = new List <SegmentDesc>(); this.translation = shapeDesc.translation; this.rotation = shapeDesc.rotation; this.scale = shapeDesc.scale; }
private void GenerateSegments(QuadDesc curQuad, ref ShapeDesc shape) { // use x, y of object instead of boundingbox center to determine segment int num1 = (int)((double)shape.x / ((double)this.quadOffset / 4.0)); int num2 = (int)((double)shape.y / ((double)this.quadOffset / 4.0)); if (num1 > 3) num1 = 3; if (num1 < 0) num1 = 0; if (num2 > 3) num2 = 3; if (num2 < 0) num2 = 0; SegmentDesc segmentDesc = new SegmentDesc(); segmentDesc.id = 4 * num1 + num2; segmentDesc.startTriangle = 0U; segmentDesc.numTriangles = shape.data.GetNumTriangles(); shape.segments = new List<SegmentDesc>(); shape.segments.Add(segmentDesc); }
private ShapeDesc TransformShape(QuadDesc quad, StaticDesc stat, NiFile file, NiTriBasedGeom geom, Matrix44 parentTransform, float parentScale) { BBox bbox = new BBox(float.MaxValue, float.MinValue, float.MaxValue, float.MinValue, float.MaxValue, float.MinValue); ShapeDesc shape1 = new ShapeDesc(); NiTriShape shape2; NiTriShapeData data = new NiTriShapeData(); //Console.WriteLine("Reading block #" + geom.GetData()); if (geom.GetClassName() == "NiTriStrips") { shape2 = new NiTriShape(geom); data = new NiTriShapeData((NiTriStripsData)file.GetBlockAtIndex(geom.GetData())); } else if (geom.GetClassName() == "BSLODTriShape") { shape2 = new NiTriShape(geom); data = (NiTriShapeData)file.GetBlockAtIndex(geom.GetData()); } else { shape2 = new NiTriShape(geom); data = (NiTriShapeData)file.GetBlockAtIndex(geom.GetData()); } if (verbose && !data.HasVertexColors() && ((this.GetShaderFlags2(file, shape2) & 32) == 32)) { if (!stat.staticModels[this.quadIndex].Contains("glacierrubbletrim0")) { logFile.WriteLog("Vertex Colors Flag, but no vertex colors in " + stat.staticModels[this.quadIndex]); } } if (((int)data.GetBSNumUVSets() & 1) == 0) return shape1; float _x = stat.x - (float)quad.x * 4096f; float _y = stat.y - (float)quad.y * 4096f; Matrix33 matrix33_1 = new Matrix33(true); Matrix33 matrix33_2 = new Matrix33(true); Matrix33 matrix33_3 = new Matrix33(true); matrix33_1.SetRotationX(Utils.ToRadians(-stat.rotX)); matrix33_2.SetRotationY(Utils.ToRadians(-stat.rotY)); matrix33_3.SetRotationZ(Utils.ToRadians(-stat.rotZ)); Matrix44 matrix44 = new Matrix44(new Matrix33(true) * matrix33_1 * matrix33_2 * matrix33_3, new Vector3(_x, _y, stat.z), 1f); List<Vector3> vertices = new List<Vector3>(data.GetVertices()); List<Vector3> normals = new List<Vector3>(data.GetNormals()); List<Vector3> tangents = new List<Vector3>(data.GetTangents()); List<Vector3> bitangents = new List<Vector3>(data.GetBitangents()); // generate tangents independent of fix tangents setting bool newtangents = false; bool newbitangents = false; if (this.generateTangents && data.HasNormals()) { if (tangents.Count == 0) { newtangents = true; for (int index = 0; index < vertices.Count; ++index) tangents.Add(new Vector3(0.0f, 0.0f, 0.0f)); } if (bitangents.Count == 0) { newbitangents = true; for (int index = 0; index < vertices.Count; ++index) bitangents.Add(new Vector3(0.0f, 0.0f, 0.0f)); } } for (int index = 0; index < vertices.Count; ++index) { vertices[index] *= shape2.GetScale() * parentScale; vertices[index] *= shape2.GetTransform() * parentTransform; vertices[index] *= stat.scale; vertices[index] *= matrix44; if (data.HasNormals()) { normals[index] *= parentTransform.RemoveTranslation() * shape2.GetTransform().RemoveTranslation(); normals[index] *= matrix44.RemoveTranslation(); // adjust tangents as well if (tangents.Count != 0) { tangents[index] *= parentTransform.RemoveTranslation() * shape2.GetTransform().RemoveTranslation(); tangents[index] *= matrix44.RemoveTranslation(); } // adjust bitangents as well if (bitangents.Count != 0) { bitangents[index] *= parentTransform.RemoveTranslation() * shape2.GetTransform().RemoveTranslation(); bitangents[index] *= matrix44.RemoveTranslation(); } // fix tangents when they were newly generated if (newtangents || newbitangents || this.fixTangents) { Vector3 vector3_1 = Vector3.Cross(normals[index], new Vector3(0.0f, 0.0f, 1f)); Vector3 vector3_2 = Vector3.Cross(normals[index], new Vector3(0.0f, 1f, 0.0f)); if (newtangents) { tangents[index] = (double)vector3_1.Length > (double)vector3_2.Length ? vector3_1 : vector3_2; tangents[index].Normalize(); } if (newbitangents) { bitangents[index] = Vector3.Cross(normals[index], tangents[index]); bitangents[index].Normalize(); } } } bbox.GrowByVertex(vertices[index]); vertices[index] /= (float)this.quadLevel; } data.SetVertices(vertices); if (data.HasNormals()) { data.SetNormals(normals); data.SetHasTangents(false); } data.SetCenter(new Vector3((float)(((double)bbox.px1 + (double)bbox.px2) / 2.0), (float)(((double)bbox.py1 + (double)bbox.py2) / 2.0), (float)(((double)bbox.pz1 + (double)bbox.pz2) / 2.0)) / (float)this.quadLevel); data.SetRadius(this.CalcRadius(data)); data.SetConsistencyFlags((ushort)0); data.SetKeepFlags((byte)51); data.SetSkyrimMaterial(0U); shape1.shape = shape2; shape1.data = data; // relative x, y for segment shape1.x = _x; shape1.y = _y; shape1.boundingBox = bbox; //Console.WriteLine(stat.staticName + " 2 " + stat.refID + " " + file + " " + shape2.GetName() + " "); shape1.textures = this.GetMeshTextures(file, shape2); shape1.material = stat.materialName; shape1.isHighDetail = false; if (useHDFlag) { if ((stat.refFlags & 131072) == 131072) { if (HDMeshList.Any(stat.staticModels[this.quadIndex].ToLower().Contains)) { shape1.isHighDetail = true; } else if (notHDMeshList.Any(stat.staticModels[this.quadIndex].ToLower().Contains)) { shape1.isHighDetail = false; } else { shape1.isHighDetail = true; } } } // Shader Flags 2 SLSF2_Double_Sided shape1.isDoubleSided = (this.GetShaderFlags2(file, shape2) & 16) == 16; // clamp mode for atlas shape1.TextureClampMode = this.GetTextureClampMode(file, shape2); /*if ((this.GetShaderFlags1(file, shape2) & 1) == 1) { //logFile.WriteLog("Specular" + stat.staticModels[this.quadIndex]); }*/ if (AtlasList.Contains(shape1.textures[0])) { if (this.UVAtlas(data, shape1.textures[0], stat)) { string[] strArray = new string[2]; strArray[0] = AtlasList.Get(shape1.textures[0]).AtlasTexture; strArray[1] = AtlasList.Get(shape1.textures[0]).AtlasTextureN; shape1.textures = strArray; shape1.TextureClampMode = 0U; shape1.isHighDetail = false; } } else { if (useOptimizer && quadLevel != 4 && shape1.textures[0].ToLower().Contains("mountainslab01")) { string[] strArray = new string[2]; strArray[0] = "textures\\landscape\\mountains\\mountainslab02.dds"; strArray[1] = "textures\\landscape\\mountains\\mountainslab02_n.dds"; shape1.textures = strArray; } if (notHDTextureList.Any(shape1.textures[0].Contains)) { if (this.verbose) { logFile.WriteLog("No atlas for " + shape1.textures[0] + " in " + stat.staticModels[this.quadIndex]); } } else { if (!useHDFlag) { shape1.isHighDetail = true; } } } if (notHDTextureList.Any(shape1.textures[0].Contains)) { shape1.isHighDetail = false; } if (HDTextureList.Any(shape1.textures[0].Contains)) { shape1.isHighDetail = true; } if (shape1.textures[0].Contains("dyndolod\\lod")) { shape1.TextureClampMode = 0U; shape1.isHighDetail = false; } if (!this.generateVertexColors || (this.quadLevel != 4 && !shape1.isHighDetail)) { data.SetHasVertexColors(false); shape1.hasVertexColor = false; } if (this.generateTangents) { //shape1.material.Length != 0 if (!useOptimizer || (useOptimizer && (this.quadLevel == 4 || shape1.isHighDetail))) { data.SetTangents(tangents); data.SetBitangents(bitangents); } } if (this.removeUnseenFaces && quad.hasTerrainVertices) { this.RemoveUnseenFaces(quad, data, shape1); if ((int)data.GetNumTriangles() == 0) return (ShapeDesc)null; } else { quad.outValues.totalTriCount += data.GetNumTriangles(); quad.outValues.reducedTriCount += data.GetNumTriangles(); } this.GenerateSegments(quad, ref shape1); return shape1; }
private void RemoveUnseenFaces(QuadDesc quad, NiTriShapeData data, ShapeDesc shape) { List<Triangle> triangles = data.GetTriangles(); List<Vector3> vertices = data.GetVertices(); int count = triangles.Count; quad.outValues.totalTriCount += count; int loops = 0; if (this.removeUnderwaterFaces) { loops = 1; } for (int loop = 0; loop <= loops; loop++) { for (int index = 0; index < triangles.Count; ++index) { QuadDesc quadCurrent = new QuadDesc(); List<Triangle> trianglesCompare = new List<Triangle>(); List<Vector3> verticesCompare = new List<Vector3>(); bool[] vertexBelow = new bool[3]; for (int index1 = 0; index1 < 3; index1++) { Vector3 vertex = vertices[triangles[index][index1]]; float x = vertex[0]; float y = vertex[1]; int vertexQuadx = quad.x + cellquad(x * quadLevel, southWestX); int vertexQuady = quad.y + cellquad(y * quadLevel, southWestY); if (quad.x != vertexQuadx || quad.y != vertexQuady) { for (int index3 = 0; index3 < this.quadList.Count; ++index3) { if (vertexQuadx == this.quadList[index3].x && vertexQuady == this.quadList[index3].y) { quadCurrent = this.quadList[index3]; x -= (vertexQuadx - quad.x) / quadLevel * 4096; y -= (vertexQuady - quad.y) / quadLevel * 4096; break; } } } else { quadCurrent = quad; } if (!quadCurrent.hasTerrainVertices) { continue; } Vector3 vertex1 = new Vector3(x, y, vertex[2]); List<Triangle> trianglesTerrain = new List<Triangle>(); List<Vector3> verticesTerrain = new List<Vector3>(); if (this.removeUnderwaterFaces && loop == 0) { if (quadCurrent.waterQuadTree != null) { trianglesTerrain = quadCurrent.waterQuadTree.entirequad.triangles; verticesTerrain = quadCurrent.waterQuadTree.vertices; } } else { trianglesTerrain = quadCurrent.terrainQuadTree.GetSegment(vertex1, quadLevel); verticesTerrain = quadCurrent.terrainQuadTree.vertices; } if (trianglesTerrain == null) { vertexBelow[index1] = true; } else { if (trianglesTerrain.Count != 0) { float num1 = float.MaxValue; num1 = GetTriangleHeight(verticesTerrain, trianglesTerrain, vertex1); if (vertex1[2] < num1) { vertexBelow[index1] = true; } } } } if (vertexBelow[0] && vertexBelow[1] && vertexBelow[2]) { triangles.RemoveAt(index); --index; } } } // remove elements from other lists too if (triangles.Count != 0 && triangles.Count != count) { Dictionary<ushort, ushort> dictionary = new Dictionary<ushort, ushort>(); List<Vector3> vertices2 = new List<Vector3>(); List<UVCoord> uvcoords = data.GetUVCoords(); List<UVCoord> uvcoords2 = new List<UVCoord>(); List<Color4> vertexcolors = data.GetVertexColors(); List<Color4> vertexcolors2 = new List<Color4>(); List<Vector3> normals = data.GetNormals(); List<Vector3> normals2 = new List<Vector3>(); List<Vector3> tangents = data.GetTangents(); List<Vector3> tangents2 = new List<Vector3>(); List<Vector3> bitangents = data.GetBitangents(); List<Vector3> bitangents2 = new List<Vector3>(); ushort index2 = 0; bool vertexcolors_allwhite = true; for (int index = 0; index < triangles.Count; ++index) { for (int index3 = 0; index3 < 3; ++index3) { ushort v1 = triangles[index][index3]; if (!dictionary.ContainsKey(v1)) { dictionary.Add(v1, index2); ++index2; if (vertices.Count != 0) { vertices2.Add(vertices[v1]); } if (uvcoords.Count != 0) { uvcoords2.Add(uvcoords[v1]); } if (vertexcolors.Count != 0) { float r = vertexcolors[v1][0]; float g = vertexcolors[v1][1]; float b = vertexcolors[v1][2]; if (r != 1f || b != 1f || g != 1f) { vertexcolors_allwhite = false; } vertexcolors2.Add(vertexcolors[v1]); } if (normals.Count != 0) { normals2.Add(normals[v1]); } if (tangents.Count != 0) { tangents2.Add(tangents[v1]); } if (bitangents.Count != 0) { bitangents2.Add(bitangents[v1]); } } } } // update indexes of triangles for (int index = 0; index < triangles.Count; ++index) { for (int index3 = 0; index3 < 3; ++index3) { ushort key = triangles[index][index3]; if (!dictionary.ContainsKey(key)) { this.logFile.WriteLog("no index for " + key); } ushort v1 = dictionary[key]; Triangle triangle; (triangle = triangles[index])[index3] = (v1); } } if (vertices2.Count != 0) { data.SetVertices(vertices2); } if (uvcoords2.Count != 0) { data.SetUVCoords(uvcoords2); } if (vertexcolors2.Count != 0) { if (vertexcolors_allwhite) { data.SetHasVertexColors(false); shape.hasVertexColor = false; } else { data.SetHasVertexColors(true); shape.hasVertexColor = true; data.SetVertexColors(vertexcolors2); } } if (normals2.Count != 0) { data.SetNormals(normals2); } if (tangents2.Count != 0) { data.SetTangents(tangents2); } if (bitangents2.Count != 0) { data.SetBitangents(bitangents2); } } quad.outValues.reducedTriCount += triangles.Count; data.SetTriangles(triangles); }
private void MergeNodes(List<ShapeDesc> shapes) { int count = shapes.Count; Dictionary<string, List<ShapeDesc>> dictionary = new Dictionary<string, List<ShapeDesc>>(); for (int index = 0; index < shapes.Count; ++index) { ShapeDesc shape = new ShapeDesc(); shape = shapes[index]; string key = shape.textures[0].ToLower(); // use vertex color flag to seperate? // less draw calls more important than reducing a few bytes? // overall it is not even reducing bytes... so no /*if (useOptimizer && shape.data.HasVertexColors()) { key = key + "_VC"; }*/ // use material name from list file, Snow/Ash if (!this.ignoreMaterial && shape.material != "") { key = key + "_" + shape.material; } // only level 4 should have HD if (shape.isHighDetail && (this.quadLevel == 4 || useHDFlag)) { key = key + "_HD"; } // double-sided if (shape.isDoubleSided) { key = key + "_DS"; } // clamp mode key = key + "_" + shape.TextureClampMode; if (dictionary.ContainsKey(key)) { dictionary[key].Add(shape); } else { dictionary.Add(key, new List<ShapeDesc>()); dictionary[key].Add(shape); } } shapes.Clear(); foreach (KeyValuePair<string, List<ShapeDesc>> keyValuePair in dictionary) { if (keyValuePair.Value.Count == 1) { shapes.Add(keyValuePair.Value[0]); } else { SegmentDesc segmentDesc = (SegmentDesc)null; uint num1 = 0U; uint num2 = 0U; int num3 = -1; NiTriShape niTriShape = new NiTriShape(); NiTriShapeData data = new NiTriShapeData(); List<ShapeDesc> list = keyValuePair.Value; string str1 = list[0].textures[0]; string str2 = list[0].textures[1]; string str3 = list[0].material; //bool isVC = list[0].hasVertexColor; bool isHD = list[0].isHighDetail; // double-sided bool isDS = list[0].isDoubleSided; // clamp mode uint clampmode = list[0].TextureClampMode; bool allwhite = true; list.Sort((Comparison<ShapeDesc>)((a, b) => { if (a.segments[0].id > b.segments[0].id) return 1; return a.segments[0].id < b.segments[0].id ? -1 : 0; })); ShapeDesc shapeDesc = new ShapeDesc(); shapeDesc.boundingBox.Set(float.MaxValue, float.MinValue, float.MaxValue, float.MinValue, float.MaxValue, float.MinValue); for (int index1 = 0; index1 < list.Count; ++index1) { NiTriShapeData niTriShapeData = list[index1].data; List<Vector3> vertices = niTriShapeData.GetVertices(); List<Triangle> triangles = new List<Triangle>(niTriShapeData.GetTriangles()); if ((uint)data.GetNumVertices() + (uint)vertices.Count > (uint)ushort.MaxValue || (uint)data.GetNumTriangles() + (uint)triangles.Count > (uint)ushort.MaxValue) { data.SetCenter(shapeDesc.boundingBox.GetCenter() / (float)this.quadLevel); data.SetRadius(this.CalcRadius(data)); shapeDesc.segments.Add(segmentDesc); shapeDesc.shape = niTriShape; shapeDesc.data = data; shapeDesc.textures = new string[2]; shapeDesc.textures[0] = str1; shapeDesc.textures[1] = str2; shapeDesc.material = str3; shapeDesc.isHighDetail = isHD; //shapeDesc.hasVertexColor = isVC; // double-sided shapeDesc.isDoubleSided = isDS; //clamp mode shapeDesc.TextureClampMode = clampmode; shapes.Add(shapeDesc); shapeDesc = new ShapeDesc(); shapeDesc.boundingBox.Set(float.MaxValue, float.MinValue, float.MaxValue, float.MinValue, float.MaxValue, float.MinValue); niTriShape = new NiTriShape(); data = new NiTriShapeData(); num1 = 0U; num2 = 0U; num3 = -1; } for (int index2 = 0; index2 < (int)niTriShapeData.GetNumVertices(); ++index2) { Vector3 vector3 = vertices[index2] * (float)this.quadLevel; shapeDesc.boundingBox.GrowByVertex(vector3); } data.AppendVertices(niTriShapeData.GetVertices()); data.AppendNormals(niTriShapeData.GetNormals()); data.AppendUVCoords(niTriShapeData.GetUVCoords()); if (this.generateTangents) { //str3.Length != 0 if (!useOptimizer || (useOptimizer && (this.quadLevel == 4 || isHD))) { data.AppendTangents(niTriShapeData.GetTangents()); data.AppendBitangents(niTriShapeData.GetBitangents()); } } // only level 4 should have vertex colors if (this.generateVertexColors && (this.quadLevel == 4 || isHD)) { List<Color4> colors = new List<Color4>(); if (niTriShapeData.HasVertexColors()) { colors = niTriShapeData.GetVertexColors(); data.AppendVertexColors(colors); for (int index2 = 0; index2 < colors.Count; index2++) { if (colors[index2][0] != 1f || colors[index2][2] != 1f || colors[index2][2] != 1f) { allwhite = false; break; } } } else { for (int index2 = 0; index2 < vertices.Count; ++index2) { colors.Add(new Color4(1f, 1f, 1f, 1f)); } data.AppendVertexColors(colors); } } List<Triangle> trianglesNew = new List<Triangle>(); for (int index2 = 0; index2 < (int)niTriShapeData.GetNumTriangles(); ++index2) { trianglesNew.Add(new Triangle((ushort)(triangles[index2][0] + num1), (ushort)(triangles[index2][1] + num1), (ushort)(triangles[index2][2] + num1))); } data.AppendTriangles(trianglesNew); num1 += (uint)niTriShapeData.GetNumVertices(); if (list[index1].segments[0].id != num3) { if (num3 != -1) { num2 += (uint)segmentDesc.numTriangles * 3U; shapeDesc.segments.Add(segmentDesc); } segmentDesc = new SegmentDesc(); segmentDesc.id = list[index1].segments[0].id; segmentDesc.startTriangle = num2; segmentDesc.numTriangles = list[index1].segments[0].numTriangles; num3 = list[index1].segments[0].id; } else segmentDesc.numTriangles += list[index1].data.GetNumTriangles(); } data.SetCenter(shapeDesc.boundingBox.GetCenter() / (float)this.quadLevel); data.SetRadius(this.CalcRadius(data)); shapeDesc.segments.Add(segmentDesc); shapeDesc.shape = niTriShape; shapeDesc.data = data; shapeDesc.textures = new string[2]; shapeDesc.textures[0] = str1; shapeDesc.textures[1] = str2; shapeDesc.material = str3; //shapeDesc.hasVertexColor = isVC; shapeDesc.isHighDetail = isHD; // double-sided shapeDesc.isDoubleSided = isDS; // clamp mode shapeDesc.TextureClampMode = clampmode; if (allwhite) { shapeDesc.data.SetHasVertexColors(false); shapeDesc.hasVertexColor = false; } else { shapeDesc.data.SetHasVertexColors(true); shapeDesc.hasVertexColor = true; } shapes.Add(shapeDesc); } } }