private static GrannyModelInfo loadModelInfo(string filename) { string currentLine = ""; string numberRegex = "([-+]?[0-9]+\\.?[0-9]*) "; StreamReader streamReader = new StreamReader(filename); while (!currentLine.StartsWith("skeleton")) { currentLine = streamReader.ReadLine(); } GrannyModelInfo modelInfo = new GrannyModelInfo(); GrannySkeletonInfo skeletonInfo = new GrannySkeletonInfo(); List <GrannyBoneInfo> skeletonBones = new List <GrannyBoneInfo>(); while (!currentLine.StartsWith("meshes")) { currentLine = streamReader.ReadLine(); if (!currentLine.StartsWith("meshes")) { string regexString = "([0-9]+) \"(.+)\" "; for (int i = 0; i < 24; i++) { regexString = regexString + numberRegex; } Regex regex = new Regex(regexString.Trim()); MatchCollection mc = regex.Matches(currentLine); foreach (Match m in mc) { GrannyBoneInfo boneInfo = new GrannyBoneInfo(); GrannyTransformInfo transformInfo = new GrannyTransformInfo(); //int boneindex = NumberUtils.parseInt(m.Groups[1].Value.Trim()); string boneName = m.Groups[2].Value; int parentIndex = NumberUtils.parseInt(m.Groups[3].Value.Trim()); float[] position = new float[3]; position[0] = NumberUtils.parseFloat(m.Groups[4].Value.Trim()); position[1] = NumberUtils.parseFloat(m.Groups[5].Value.Trim()); position[2] = NumberUtils.parseFloat(m.Groups[6].Value.Trim()); float[] orientation = new float[4]; orientation[0] = NumberUtils.parseFloat(m.Groups[7].Value.Trim()); orientation[1] = NumberUtils.parseFloat(m.Groups[8].Value.Trim()); orientation[2] = NumberUtils.parseFloat(m.Groups[9].Value.Trim()); orientation[3] = NumberUtils.parseFloat(m.Groups[10].Value.Trim()); float[] scaleShear = new float[9]; scaleShear[0] = 1.0f; scaleShear[1] = 0.0f; scaleShear[2] = 0.0f; scaleShear[3] = 0.0f; scaleShear[4] = 1.0f; scaleShear[5] = 0.0f; scaleShear[6] = 0.0f; scaleShear[7] = 0.0f; scaleShear[8] = 1.0f; float[] invWorld = new float[16]; for (int j = 0; j < 16; j++) { invWorld[j] = NumberUtils.parseFloat(m.Groups[j + 11].Value.Trim()); } bool hasPosition = true; bool hasOrientation = true; int flags = 0; if (NumberUtils.almostEquals(position[0], 0.0f, 4) && NumberUtils.almostEquals(position[1], 0.0f, 4) && NumberUtils.almostEquals(position[2], 0.0f, 4)) { hasPosition = false; } if (NumberUtils.almostEquals(orientation[0], 0.0f, 5) && NumberUtils.almostEquals(orientation[1], 0.0f, 5) && NumberUtils.almostEquals(orientation[2], 0.0f, 5) && NumberUtils.almostEquals(orientation[3], 1.0f, 5)) { hasOrientation = false; } if (hasPosition) { flags = flags + 1; } if (hasOrientation) { flags = flags + 2; } transformInfo.flags = flags; transformInfo.position = position; transformInfo.orientation = orientation; transformInfo.scaleShear = scaleShear; boneInfo.name = boneName; boneInfo.parentIndex = parentIndex; boneInfo.localTransform = transformInfo; boneInfo.inverseWorldTransform = invWorld; boneInfo.LODError = 0; skeletonBones.Add(boneInfo); } } } skeletonInfo.bones = skeletonBones; // Read Meshes int numMeshes = NumberUtils.parseInt(currentLine.Replace("meshes:", "")); List <GrannyMeshInfo> meshInfos = new List <GrannyMeshInfo>(); for (int meshId = 0; meshId < numMeshes; meshId++) { string meshName = ""; while (!currentLine.StartsWith("mesh:")) { currentLine = streamReader.ReadLine(); } string regexString = "\"(.+)\""; Regex regex = new Regex(regexString); MatchCollection mc = regex.Matches(currentLine); foreach (Match m in mc) { meshName = m.Groups[1].Value; } // Read Vertices List <GrannyVertexInfo> vertexInfos = new List <GrannyVertexInfo>(); while (!currentLine.StartsWith("triangles")) { currentLine = streamReader.ReadLine(); if (!currentLine.StartsWith("vertices") && !currentLine.StartsWith("triangles")) { int spacesCount = 0; foreach (char c in currentLine) { if (c == ' ') { spacesCount++; } } bool hasBinormalsAndTangents = spacesCount > 15; regexString = ""; for (int i = 0; i < 16; i++) { regexString = regexString + numberRegex; } if (hasBinormalsAndTangents) { for (int i = 0; i < 6; i++) { regexString = regexString + numberRegex; } } regex = new Regex(regexString.Trim()); mc = regex.Matches(currentLine); foreach (Match m in mc) { GrannyVertexInfo vertexInfo = new GrannyVertexInfo(); float[] position = new float[3]; position[0] = NumberUtils.parseFloat(m.Groups[1].Value.Trim()); position[1] = NumberUtils.parseFloat(m.Groups[2].Value.Trim()); position[2] = NumberUtils.parseFloat(m.Groups[3].Value.Trim()); float[] normal = new float[3]; normal[0] = NumberUtils.parseFloat(m.Groups[4].Value.Trim()); normal[1] = NumberUtils.parseFloat(m.Groups[5].Value.Trim()); normal[2] = NumberUtils.parseFloat(m.Groups[6].Value.Trim()); float[] uv = new float[2]; uv[0] = NumberUtils.parseFloat(m.Groups[7].Value.Trim()); uv[1] = NumberUtils.parseFloat(m.Groups[8].Value.Trim()); int[] boneIndices = new int[4]; boneIndices[0] = NumberUtils.parseInt(m.Groups[9].Value.Trim()); boneIndices[1] = NumberUtils.parseInt(m.Groups[10].Value.Trim()); boneIndices[2] = NumberUtils.parseInt(m.Groups[11].Value.Trim()); boneIndices[3] = NumberUtils.parseInt(m.Groups[12].Value.Trim()); int[] boneWeights = new int[4]; boneWeights[0] = NumberUtils.parseInt(m.Groups[13].Value.Trim()); boneWeights[1] = NumberUtils.parseInt(m.Groups[14].Value.Trim()); boneWeights[2] = NumberUtils.parseInt(m.Groups[15].Value.Trim()); boneWeights[3] = NumberUtils.parseInt(m.Groups[16].Value.Trim()); if (hasBinormalsAndTangents) { float[] tangent = new float[3]; tangent[0] = NumberUtils.parseFloat(m.Groups[17].Value.Trim()); tangent[1] = NumberUtils.parseFloat(m.Groups[18].Value.Trim()); tangent[2] = NumberUtils.parseFloat(m.Groups[19].Value.Trim()); float[] binormal = new float[3]; binormal[0] = NumberUtils.parseFloat(m.Groups[20].Value.Trim()); binormal[1] = NumberUtils.parseFloat(m.Groups[21].Value.Trim()); binormal[2] = NumberUtils.parseFloat(m.Groups[22].Value.Trim()); vertexInfo.binormal = binormal; vertexInfo.tangent = tangent; } vertexInfo.position = position; vertexInfo.normal = normal; vertexInfo.boneIndices = boneIndices; vertexInfo.boneWeights = boneWeights; vertexInfo.uv = uv; vertexInfos.Add(vertexInfo); } } } // Read Triangles List <int[]> triangles = new List <int[]>(); while (!currentLine.StartsWith("mesh") && !currentLine.StartsWith("end")) { currentLine = streamReader.ReadLine(); if (!currentLine.StartsWith("mesh") && !currentLine.StartsWith("end")) { regexString = ""; for (int i = 0; i < 3; i++) { regexString = regexString + "([-+]?[0-9]+\\.?[0-9]*) "; } regex = new Regex(regexString.Trim()); mc = regex.Matches(currentLine); foreach (Match m in mc) { int[] triangle = new int[3]; triangle[0] = NumberUtils.parseInt(m.Groups[1].Value.Trim()); triangle[1] = NumberUtils.parseInt(m.Groups[2].Value.Trim()); triangle[2] = NumberUtils.parseInt(m.Groups[3].Value.Trim()); triangles.Add(triangle); } } } List <PrimaryTopologyGroupInfo> groupInfos = new List <PrimaryTopologyGroupInfo>(); groupInfos.Add(new PrimaryTopologyGroupInfo(0, 0, triangles.Count)); GrannyMeshInfo meshInfo = new GrannyMeshInfo(); meshInfo.name = meshName; meshInfo.vertices = vertexInfos; meshInfo.triangles = triangles; meshInfo.primaryTopologyGroupInfos = groupInfos; meshInfo.materialBindingNames = new List <string> { "DefaultMaterialBinding" }; meshInfos.Add(meshInfo); } modelInfo.skeleton = skeletonInfo; modelInfo.meshBindings = meshInfos; streamReader.Close(); return(modelInfo); }
public static void br2Export(IGrannyFile grannyFile, int currentModelIndex) { string fileExtension = ".br2"; string outputFilename = ""; string numberFormat = "f8"; if (grannyFile.Models.Count > 1) { outputFilename = grannyFile.Filename.Replace(".fgx", "_model" + currentModelIndex + fileExtension); outputFilename = outputFilename.Replace(".FGX", "_model" + currentModelIndex + fileExtension); } else { outputFilename = grannyFile.Filename.Replace(".fgx", fileExtension); outputFilename = outputFilename.Replace(".FGX", fileExtension); } StreamWriter outputWriter = new StreamWriter(new FileStream(outputFilename, FileMode.Create)); IGrannyModel model = grannyFile.Models[currentModelIndex]; IGrannySkeleton skeleton = model.Skeleton; // Lookup so we can identify the meshes belonging the current model in the list of file meshes Dictionary <int, int> meshBindingToMesh = new Dictionary <int, int>(); HashSet <string> distinctMeshNames = new HashSet <string>(); for (int i = 0; i < model.MeshBindings.Count; i++) { for (int j = 0; j < grannyFile.Meshes.Count; j++) { GrannyMeshWrapper modelMesh = new GrannyMeshWrapper(model.MeshBindings[i]); IGrannyMesh fileMesh = grannyFile.Meshes[j]; if (modelMesh.meshEqual(fileMesh)) { meshBindingToMesh.Add(i, j); } } distinctMeshNames.Add(model.MeshBindings[i].Name); } // Used to give meshes distinct names where we have multiple meshes with the same name in our source gr2 Dictionary <string, int> meshNameCount = new Dictionary <string, int>(); foreach (string meshName in distinctMeshNames) { meshNameCount.Add(meshName, 0); } List <GrannyMeshInfo> grannyMeshInfos = new List <GrannyMeshInfo>(); for (int i = 0; i < model.MeshBindings.Count; i++) { GrannyMeshWrapper meshWrapper = new GrannyMeshWrapper(model.MeshBindings[i]); grannyMeshInfos.Add(meshWrapper.getMeshInfo()); } BiLookup <int, string> boneLookup = new BiLookup <int, string>(); for (int i = 0; i < skeleton.Bones.Count; i++) { boneLookup.Add(i, skeleton.Bones[i].Name); } Dictionary <string, double[]> boneNameToPositionMap = new Dictionary <string, double[]>(); foreach (IGrannyBone bone in skeleton.Bones) { double[] bonePosition = NB2NA2FileOps.getBoneWorldPosition(bone); boneNameToPositionMap.Add(bone.Name, bonePosition); //MemoryUtil.memLogLine("boneName: " + bone.Name + " bone position:" + bonePosition[0] + " " + bonePosition[1] + " " + bonePosition[2]); } outputWriter.WriteLine("// Nexus Buddy BR2 - Exported from Nexus Buddy 2"); outputWriter.WriteLine("skeleton"); // Write Bones //outputWriter.WriteLine("Bones: " + skeleton.Bones.Count); for (int boneIndex = 0; boneIndex < skeleton.Bones.Count; boneIndex++) { IGrannyBone bone = skeleton.Bones[boneIndex]; string boneName = bone.Name; IGrannyTransform transform = bone.LocalTransform; float[] orientation = transform.Orientation; float[] position = transform.Position; float[] invWorldTransform = bone.InverseWorldTransform; StringBuilder boneStringBuilder = new StringBuilder(boneIndex + " \"" + boneName + "\" " + bone.ParentIndex + " " + position[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + position[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + position[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[3].ToString(numberFormat, CultureInfo.InvariantCulture)); foreach (float j in invWorldTransform) { boneStringBuilder.Append(" " + j.ToString(numberFormat, CultureInfo.InvariantCulture)); } outputWriter.WriteLine(boneStringBuilder); } // Write Meshes outputWriter.WriteLine("meshes: " + model.MeshBindings.Count); for (int mi = 0; mi < grannyMeshInfos.Count; mi++) { GrannyMeshInfo grannyMeshInfo = grannyMeshInfos[mi]; string meshName = model.MeshBindings[mi].Name; meshNameCount[meshName]++; if (meshNameCount[meshName] > 1) { meshName += meshNameCount[meshName]; } //StringBuilder boneStringBuilder = new StringBuilder(); outputWriter.WriteLine("mesh:\"" + meshName + "\""); // Write Vertices outputWriter.WriteLine("vertices"); for (int vi = 0; vi < grannyMeshInfo.vertices.Count; vi++) { GrannyVertexInfo vertex = grannyMeshInfo.vertices[vi]; string boneName0 = grannyMeshInfo.boneBindings[vertex.boneIndices[0]]; float boneWeight0 = (float)vertex.boneWeights[0] / 255; int boneId0 = NB2NA2FileOps.getBoneIdForBoneName(boneLookup, boneNameToPositionMap, boneName0, boneWeight0, vertex.position); string boneName1 = grannyMeshInfo.boneBindings[vertex.boneIndices[1]]; float boneWeight1 = (float)vertex.boneWeights[1] / 255; int boneId1 = NB2NA2FileOps.getBoneIdForBoneName(boneLookup, boneNameToPositionMap, boneName1, boneWeight1, vertex.position); string boneName2 = grannyMeshInfo.boneBindings[vertex.boneIndices[2]]; float boneWeight2 = (float)vertex.boneWeights[2] / 255; int boneId2 = NB2NA2FileOps.getBoneIdForBoneName(boneLookup, boneNameToPositionMap, boneName2, boneWeight2, vertex.position); string boneName3 = grannyMeshInfo.boneBindings[vertex.boneIndices[3]]; float boneWeight3 = (float)vertex.boneWeights[3] / 255; int boneId3 = NB2NA2FileOps.getBoneIdForBoneName(boneLookup, boneNameToPositionMap, boneName3, boneWeight3, vertex.position); float[] tangents = new float[3]; if (vertex.tangent == null) { tangents[0] = vertex.normal[0]; tangents[1] = vertex.normal[1]; tangents[2] = vertex.normal[2]; } else { tangents[0] = vertex.tangent[0]; tangents[1] = vertex.tangent[1]; tangents[2] = vertex.tangent[2]; } float[] binormals = new float[3]; if (vertex.binormal == null) { binormals[0] = vertex.normal[0]; binormals[1] = vertex.normal[1]; binormals[2] = vertex.normal[2]; } else { binormals[0] = vertex.binormal[0]; binormals[1] = vertex.binormal[1]; binormals[2] = vertex.binormal[2]; } outputWriter.WriteLine(vertex.position[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.position[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.position[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.normal[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.normal[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.normal[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.uv[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.uv[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + boneId0 + " " + boneId1 + " " + boneId2 + " " + boneId3 + " " + vertex.boneWeights[0] + " " + vertex.boneWeights[1] + " " + vertex.boneWeights[2] + " " + vertex.boneWeights[3] + " " + tangents[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + tangents[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + tangents[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + binormals[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + binormals[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + binormals[2].ToString(numberFormat, CultureInfo.InvariantCulture) ); } // Write Triangles outputWriter.WriteLine("triangles"); for (int ti = 0; ti < grannyMeshInfo.triangles.Count; ti++) { int[] triangle = grannyMeshInfo.triangles[ti]; outputWriter.WriteLine(triangle[0] + " " + triangle[1] + " " + triangle[2]); } } outputWriter.WriteLine("end"); outputWriter.Close(); }
//public int getGroup0TriCount() //{ // return *(int*)(*(int*)(*(int*)(getPrimaryTopologyPtr()) + 4) + 8); //} //public void setGroup0TriCount(int num) //{ // *(int*)(*(int*)(*(int*)(getPrimaryTopologyPtr()) + 4) + 8) = num; //} public unsafe GrannyMeshInfo getMeshInfo() { GrannyMeshInfo meshInfo = new GrannyMeshInfo(); meshInfo.name = Marshal.PtrToStringAnsi((IntPtr)(sbyte *)*(int *)(m_pkMesh)); List <string> boneBindings = new List <string>(); for (int i = 0; i < getNumBoneBindings(); i++) { boneBindings.Add(Marshal.PtrToStringAnsi((IntPtr)(sbyte *)*(int *)(*(int *)(getBoneBindingsPtr()) + i * 44))); } meshInfo.boneBindings = boneBindings; meshInfo.setVertexStructInfos(getVertexStructInfos()); int vertexCount = getNumVertices(); IntPtr vertexPtr = getVerticesPtr(); int vertexSize = meshInfo.bytesPerVertex; List <GrannyVertexInfo> vertexInfos = new List <GrannyVertexInfo>(); for (int i = 0; i < vertexCount; i++) { GrannyVertexInfo currentVertex = new GrannyVertexInfo(); GrannyMeshVertexStructInfo structInfo = meshInfo.getVertexStructInfoByName("Position"); if (structInfo != null) { currentVertex.position = new float[3]; currentVertex.position[0] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)); currentVertex.position[1] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)); currentVertex.position[2] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)); } structInfo = meshInfo.getVertexStructInfoByName("BoneWeights"); if (structInfo != null) { currentVertex.boneWeights = new int[structInfo.count]; for (int j = 0; j < structInfo.count; j++) { currentVertex.boneWeights[j] = (byte)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * j)); } } else { int structInfoCount = 8; currentVertex.boneWeights = new int[structInfoCount]; currentVertex.boneWeights[0] = 255; for (int j = 1; j < structInfoCount; j++) { currentVertex.boneWeights[j] = 0; } } structInfo = meshInfo.getVertexStructInfoByName("BoneIndices"); if (structInfo != null) { currentVertex.boneIndices = new int[structInfo.count]; for (int j = 0; j < structInfo.count; j++) { currentVertex.boneIndices[j] = (byte)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * j)); } } else { int structInfoCount = 8; currentVertex.boneIndices = new int[structInfoCount]; for (int j = 0; j < structInfoCount; j++) { currentVertex.boneIndices[j] = 0; } } structInfo = meshInfo.getVertexStructInfoByName("Normal"); if (structInfo != null) { currentVertex.normal = new float[3]; currentVertex.normal[0] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)); currentVertex.normal[1] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)); currentVertex.normal[2] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)); } structInfo = meshInfo.getVertexStructInfoByName("Binormal"); if (structInfo != null) { currentVertex.binormal = new float[3]; currentVertex.binormal[0] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)); currentVertex.binormal[1] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)); currentVertex.binormal[2] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)); } structInfo = meshInfo.getVertexStructInfoByName("Tangent"); if (structInfo != null) { currentVertex.tangent = new float[3]; currentVertex.tangent[0] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)); currentVertex.tangent[1] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)); currentVertex.tangent[2] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)); } structInfo = meshInfo.getVertexStructInfoByName("TextureCoordinates0"); currentVertex.uv = new float[2]; if (structInfo != null) { currentVertex.uv[0] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)); currentVertex.uv[1] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)); } structInfo = meshInfo.getVertexStructInfoByName("TextureCoordinates1"); currentVertex.uv2 = new float[2]; if (structInfo != null) { currentVertex.uv2[0] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)); currentVertex.uv2[1] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)); } else { currentVertex.uv2[0] = 0f; currentVertex.uv2[1] = 0f; } structInfo = meshInfo.getVertexStructInfoByName("TextureCoordinates2"); currentVertex.uv3 = new float[2]; if (structInfo != null) { currentVertex.uv3[0] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)); currentVertex.uv3[1] = (float)structInfo.convert(*(int *)(vertexPtr) + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)); } else { currentVertex.uv3[0] = 0f; currentVertex.uv3[1] = 0f; } vertexInfos.Add(currentVertex); } meshInfo.vertices = vertexInfos; if (*(int *)(getMaterialBindingsPtr()) != 0) { string materialName = Marshal.PtrToStringAnsi((IntPtr)(sbyte *)*(int *)*(int *)*(int *)(getMaterialBindingsPtr())); meshInfo.materialName = materialName; } int numTriangleGroups = *(int *)*(int *)getPrimaryTopologyPtr(); List <PrimaryTopologyGroupInfo> primaryTopologyGroupInfos = new List <PrimaryTopologyGroupInfo>(); for (int i = 0; i < numTriangleGroups; i++) { PrimaryTopologyGroupInfo primaryTopologyGroupInfo = new PrimaryTopologyGroupInfo(); primaryTopologyGroupInfo.groupMaterialIndex = *(int *)(*(int *)(*(int *)(getPrimaryTopologyPtr()) + 4) + i * 12 + 0); primaryTopologyGroupInfo.groupTriFirst = *(int *)(*(int *)(*(int *)(getPrimaryTopologyPtr()) + 4) + i * 12 + 4); primaryTopologyGroupInfo.groupTriCount = *(int *)(*(int *)(*(int *)(getPrimaryTopologyPtr()) + 4) + i * 12 + 8); primaryTopologyGroupInfos.Add(primaryTopologyGroupInfo); } meshInfo.primaryTopologyGroupInfos = primaryTopologyGroupInfos; int numIndices16 = getNumIndices16(); int numIndices = getNumIndices(); List <int> indices = new List <int>(); if (numIndices16 > 0) { for (int i = 0; i < numIndices16; i++) { indices.Add((int)*(ushort *)(*(int *)(*(int *)(getPrimaryTopologyPtr()) + 28) + i * 2)); } } else if (numIndices > 0) { for (int i = 0; i < numIndices; i++) { indices.Add(*(int *)(*(int *)(*(int *)(getPrimaryTopologyPtr()) + 16) + i * 4)); } } if (indices.Count > 0) { List <int[]> triangles = new List <int[]>(); foreach (PrimaryTopologyGroupInfo info in primaryTopologyGroupInfos) { for (int iTriangle = info.groupTriFirst; iTriangle < info.groupTriFirst + info.groupTriCount; iTriangle++) { int index0 = iTriangle * 3; int index1 = index0 + 1; int index2 = index0 + 2; int[] triangle = new int[4]; triangle[0] = indices[index0]; triangle[1] = indices[index1]; triangle[2] = indices[index2]; triangle[3] = info.groupMaterialIndex; triangles.Add(triangle); } } meshInfo.triangles = triangles; } return(meshInfo); }
unsafe private void writeVertices(GrannyMeshInfo meshInfo) { int vertexCount = meshInfo.vertices.Count; setNumVertices(vertexCount); int vertexSize = meshInfo.bytesPerVertex; int oldVerticesPtr = *(int *)getVerticesPtr(); *(int *)(getVerticesPtr()) = (int)Marshal.AllocHGlobal(vertexCount * vertexSize); int newVerticesPtr = *(int *)getVerticesPtr(); List <GrannyVertexInfo> vertexInfos = meshInfo.vertices; for (int i = 0; i < vertexCount; i++) { MemoryUtil.MemCpy((void *)(newVerticesPtr + i * vertexSize), (void *)oldVerticesPtr, (uint)vertexSize); GrannyVertexInfo currentVertex = vertexInfos[i]; GrannyMeshVertexStructInfo structInfo = meshInfo.getVertexStructInfoByName("Position"); if (structInfo != null) { *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = currentVertex.position[0]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = currentVertex.position[1]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)) = currentVertex.position[2]; } structInfo = meshInfo.getVertexStructInfoByName("BoneWeights"); if (structInfo != null) { for (int j = 0; j < structInfo.count; j++) { if (j < currentVertex.boneWeights.Length) { *(byte *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * j)) = (byte)currentVertex.boneWeights[j]; } else { *(byte *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * j)) = (byte)0; } } } structInfo = meshInfo.getVertexStructInfoByName("BoneIndices"); if (structInfo != null) { for (int j = 0; j < structInfo.count; j++) { if (j < currentVertex.boneIndices.Length) { *(byte *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * j)) = (byte)currentVertex.boneIndices[j]; } else { *(byte *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * j)) = (byte)0; } } } structInfo = meshInfo.getVertexStructInfoByName("Normal"); if (structInfo != null) { // granny_real32 *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = currentVertex.normal[0]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = currentVertex.normal[1]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)) = currentVertex.normal[2]; } structInfo = meshInfo.getVertexStructInfoByName("Tangent"); if (structInfo != null && currentVertex.tangent != null) { // granny_real32 *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = currentVertex.tangent[0]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = currentVertex.tangent[1]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)) = currentVertex.tangent[2]; } structInfo = meshInfo.getVertexStructInfoByName("Binormal"); if (structInfo != null && currentVertex.binormal != null) { // granny_real32 *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = currentVertex.binormal[0]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = currentVertex.binormal[1]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)) = currentVertex.binormal[2]; } structInfo = meshInfo.getVertexStructInfoByName("TextureCoordinates0"); if (structInfo != null) { // granny_real32 *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = currentVertex.uv[0]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = currentVertex.uv[1]; } structInfo = meshInfo.getVertexStructInfoByName("TextureCoordinates1"); if (structInfo != null) { // granny_real32 *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = currentVertex.uv2[0]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = currentVertex.uv2[1]; } structInfo = meshInfo.getVertexStructInfoByName("TextureCoordinates2"); if (structInfo != null) { // granny_real32 *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = currentVertex.uv3[0]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = currentVertex.uv3[1]; } } }
unsafe private void writeVertices(GrannyMeshInfo meshInfo, bool isLeaderFormat, bool isSceneFormat) { int vertexCount = meshInfo.vertices.Count; setNumVertices(vertexCount); int vertexSize = meshInfo.bytesPerVertex; int oldVerticesPtr = *(int *)getVerticesPtr(); *(int *)(getVerticesPtr()) = (int)Marshal.AllocHGlobal(vertexCount * vertexSize); int newVerticesPtr = *(int *)getVerticesPtr(); List <GrannyVertexInfo> vertexInfos = meshInfo.vertices; for (int i = 0; i < vertexCount; i++) { MemoryUtil.MemCpy((void *)(newVerticesPtr + i * vertexSize), (void *)oldVerticesPtr, (uint)vertexSize); GrannyVertexInfo currentVertex = vertexInfos[i]; GrannyMeshVertexStructInfo structInfo = meshInfo.getVertexStructInfoByName("Position"); if (structInfo != null) { *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = currentVertex.position[0]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = currentVertex.position[1]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)) = currentVertex.position[2]; } if (!isSceneFormat) { structInfo = meshInfo.getVertexStructInfoByName("BoneWeights"); if (structInfo != null) { *(byte *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = (byte)currentVertex.boneWeights[0]; *(byte *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = (byte)currentVertex.boneWeights[1]; *(byte *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)) = (byte)currentVertex.boneWeights[2]; *(byte *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 3)) = (byte)currentVertex.boneWeights[3]; } structInfo = meshInfo.getVertexStructInfoByName("BoneIndices"); if (structInfo != null) { *(byte *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = (byte)currentVertex.boneIndices[0]; *(byte *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = (byte)currentVertex.boneIndices[1]; *(byte *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)) = (byte)currentVertex.boneIndices[2]; *(byte *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 3)) = (byte)currentVertex.boneIndices[3]; } } if (isLeaderFormat || isSceneFormat) { structInfo = meshInfo.getVertexStructInfoByName("Normal"); if (structInfo != null) { // granny_real16 *(ushort *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = NumberUtils.floatToHalf(currentVertex.normal[0]); *(ushort *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = NumberUtils.floatToHalf(currentVertex.normal[1]); *(ushort *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)) = NumberUtils.floatToHalf(currentVertex.normal[2]); } structInfo = meshInfo.getVertexStructInfoByName("Binormal"); if (structInfo != null && currentVertex.binormal != null) { // granny_real16 *(ushort *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = NumberUtils.floatToHalf(currentVertex.binormal[0]); *(ushort *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = NumberUtils.floatToHalf(currentVertex.binormal[1]); *(ushort *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)) = NumberUtils.floatToHalf(currentVertex.binormal[2]); } structInfo = meshInfo.getVertexStructInfoByName("Tangent"); if (structInfo != null && currentVertex.tangent != null) { // granny_real16 *(ushort *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = NumberUtils.floatToHalf(currentVertex.tangent[0]); *(ushort *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = NumberUtils.floatToHalf(currentVertex.tangent[1]); *(ushort *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)) = NumberUtils.floatToHalf(currentVertex.tangent[2]); } } else { structInfo = meshInfo.getVertexStructInfoByName("Normal"); if (structInfo != null) { // granny_real32 *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = currentVertex.normal[0]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = currentVertex.normal[1]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 2)) = currentVertex.normal[2]; } } if (isLeaderFormat || isSceneFormat) { structInfo = meshInfo.getVertexStructInfoByName("TextureCoordinates0"); if (structInfo != null) { // granny_real16 *(ushort *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = NumberUtils.floatToHalf(currentVertex.uv[0]); *(ushort *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = NumberUtils.floatToHalf(currentVertex.uv[1]); } } else { structInfo = meshInfo.getVertexStructInfoByName("TextureCoordinates0"); if (structInfo != null) { // granny_real32 *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 0)) = currentVertex.uv[0]; *(float *)(newVerticesPtr + (i * vertexSize) + structInfo.offset + (structInfo.length * 1)) = currentVertex.uv[1]; } } } }
private static WriteModelToCN6Response WriteModelToCN6(IGrannyFile grannyFile, bool isBatch, IGrannyModel model, string filterMeshName) { List <string> textureMaps = new List <string>(); string fileExtension = ".cn6"; string outputFilename = ""; string numberFormat = "f8"; List <string> decalMeshNames = new List <string>(); IGrannySkeleton skeleton = model.Skeleton; string decalMeshName = ""; if (filterMeshName != null) { decalMeshName = "_DCL_" + GetSafeFilename(filterMeshName); } if (grannyFile.Models.Count > 1) { string modelFilename = "__" + model.Name + decalMeshName + fileExtension; outputFilename = grannyFile.Filename.Replace(".gr2", modelFilename); outputFilename = outputFilename.Replace(".GR2", modelFilename); } else { outputFilename = grannyFile.Filename.Replace(".gr2", fileExtension); outputFilename = outputFilename.Replace(".GR2", fileExtension); } if (isBatch) { outputFilename = outputFilename.Replace(fileExtension, "_batch" + fileExtension); } string directory = Path.GetDirectoryName(outputFilename); string filename = Path.GetFileName(outputFilename); outputFilename = directory + "\\" + GetSafeFilename(filename); StreamWriter outputWriter = new StreamWriter(new FileStream(outputFilename, FileMode.Create)); // Lookup so we can identify the meshes belonging the current model in the list of file meshes Dictionary <int, int> meshBindingToMesh = new Dictionary <int, int>(); HashSet <string> distinctMeshNames = new HashSet <string>(); for (int i = 0; i < model.MeshBindings.Count; i++) { for (int j = 0; j < grannyFile.Meshes.Count; j++) { GrannyMeshWrapper modelMesh = new GrannyMeshWrapper(model.MeshBindings[i]); IGrannyMesh fileMesh = grannyFile.Meshes[j]; if (modelMesh.meshEqual(fileMesh)) { meshBindingToMesh.Add(i, j); } } distinctMeshNames.Add(model.MeshBindings[i].Name); } // Used to give meshes distinct names where we have multiple meshes with the same name in our source gr2 Dictionary <string, int> meshNameCount = new Dictionary <string, int>(); foreach (string meshName in distinctMeshNames) { meshNameCount.Add(meshName, 0); } List <GrannyMeshInfo> grannyMeshInfos = new List <GrannyMeshInfo>(); for (int i = 0; i < model.MeshBindings.Count; i++) { GrannyMeshWrapper meshWrapper = new GrannyMeshWrapper(model.MeshBindings[i]); grannyMeshInfos.Add(meshWrapper.getMeshInfo()); } BiLookup <int, string> boneLookup = new BiLookup <int, string>(); for (int i = 0; i < skeleton.Bones.Count; i++) { boneLookup.Add(i, skeleton.Bones[i].Name); } Dictionary <string, double[]> boneNameToPositionMap = new Dictionary <string, double[]>(); foreach (IGrannyBone bone in skeleton.Bones) { if (!boneNameToPositionMap.ContainsKey(bone.Name)) { double[] bonePosition = NB2Exporter.getBoneWorldPosition(bone); boneNameToPositionMap.Add(bone.Name, bonePosition); } } string headerLine = "// CivNexus6 CN6 - Exported from Nexus Buddy " + NexusBuddyApplicationForm.getVersionString(); outputWriter.WriteLine(headerLine); outputWriter.WriteLine("skeleton"); // Write Bones for (int boneIndex = 0; boneIndex < skeleton.Bones.Count; boneIndex++) { IGrannyBone bone = skeleton.Bones[boneIndex]; string boneName = bone.Name; IGrannyTransform transform = bone.LocalTransform; float[] orientation = transform.Orientation; float[] position = transform.Position; float[] invWorldTransform = bone.InverseWorldTransform; StringBuilder boneStringBuilder = new StringBuilder(boneIndex + " \"" + boneName + "\" " + bone.ParentIndex + " " + position[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + position[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + position[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[3].ToString(numberFormat, CultureInfo.InvariantCulture)); foreach (float j in invWorldTransform) { boneStringBuilder.Append(" " + j.ToString(numberFormat, CultureInfo.InvariantCulture)); } outputWriter.WriteLine(boneStringBuilder); } List <IndieMaterial> modelMaterials = new List <IndieMaterial>(); foreach (IGrannyMesh mesh in model.MeshBindings) { IGrannyMaterial material = mesh.MaterialBindings[0]; foreach (ListViewItem materialListItem in NexusBuddyApplicationForm.form.materialList.Items) { IndieMaterial shader = (IndieMaterial)materialListItem.Tag; GrannyMaterialWrapper matWrap1 = new GrannyMaterialWrapper(shader.GetMaterial()); GrannyMaterialWrapper matWrap2 = new GrannyMaterialWrapper(material); if (matWrap1.getName().Equals(matWrap2.getName()) && !modelMaterials.Contains(shader)) { modelMaterials.Add(shader); } } } int meshCount = 0; for (int mi = 0; mi < grannyMeshInfos.Count; mi++) { IGrannyMesh mesh = model.MeshBindings[mi]; string meshName = model.MeshBindings[mi].Name; GrannyMeshWrapper meshWrapper = new GrannyMeshWrapper(mesh); GrannyMeshInfo meshInfo = meshWrapper.getMeshInfo(); bool isFlatMesh = true; foreach (GrannyVertexInfo vertexInfo in meshInfo.vertices) { float zPos = vertexInfo.position[2]; bool isZero = NumberUtils.almostEquals(zPos, 0.0f, 4); if (!isZero) { isFlatMesh = false; break; } } if (filterMeshName == null) { if (isFlatMesh) { decalMeshNames.Add(meshName); } else { meshCount++; } } else { meshCount = 1; } } if (meshCount == 0) { outputWriter.Close(); File.Delete(outputFilename); return(new WriteModelToCN6Response(decalMeshNames, textureMaps)); } // Write Meshes outputWriter.WriteLine("meshes:" + meshCount); for (int mi = 0; mi < grannyMeshInfos.Count; mi++) { GrannyMeshInfo grannyMeshInfo = grannyMeshInfos[mi]; string meshName = model.MeshBindings[mi].Name; if ((filterMeshName == null && !decalMeshNames.Contains(meshName)) || meshName.Equals(filterMeshName)) { meshNameCount[meshName]++; if (meshNameCount[meshName] > 1) { meshName += meshNameCount[meshName]; } outputWriter.WriteLine("mesh:\"" + meshName + "\""); // Write Materials outputWriter.WriteLine("materials"); foreach (IGrannyMaterial material in model.MeshBindings[mi].MaterialBindings) { IndieMaterial shader = NexusBuddyApplicationForm.GetIndieMaterialFromMaterial(material); string baseMap; if (shader.GetType() == typeof(IndieLeaderGlassShader)) { baseMap = "Leader_cornea"; } else if (shader.GetType() == typeof(IndieBuildingShader)) { baseMap = shader.Diffuse; } else if (shader.GetType().Name.StartsWith("IndieLeaderFur")) { baseMap = shader.Fur_BaseMap; } else if (shader.GetType().Name.StartsWith("IndieLeader")) { baseMap = shader.DiffuseMap; } else if (shader.GetType() == typeof(IndieLandmarkStencilShader)) { baseMap = shader.BaseTextureMap; } else { baseMap = shader.BaseTextureMap; } if (!String.IsNullOrEmpty(baseMap)) { outputWriter.WriteLine("\"" + baseMap + "\""); textureMaps.Add(baseMap); } else { outputWriter.WriteLine("\"" + material.Name + "\""); } } // Write Vertices outputWriter.WriteLine("vertices"); for (int vi = 0; vi < grannyMeshInfo.vertices.Count; vi++) { GrannyVertexInfo vertex = grannyMeshInfo.vertices[vi]; string[] boneNames = new string[8]; float[] boneWeights = new float[8]; int[] boneIds = new int[8]; for (int z = 0; z < 8; z++) { if (z > 3) { //boneNames[z] = grannyMeshInfo.boneBindings[vertex.boneIndices[z]]; boneWeights[z] = 0f; boneIds[z] = 0; } else { boneNames[z] = grannyMeshInfo.boneBindings[vertex.boneIndices[z]]; boneWeights[z] = (float)vertex.boneWeights[z] / 255; boneIds[z] = NB2Exporter.getBoneIdForBoneName(boneLookup, boneNameToPositionMap, boneNames[z], boneWeights[z], vertex.position); } } float[] tangents = new float[3]; if (vertex.tangent == null) { tangents[0] = vertex.normal[0]; tangents[1] = vertex.normal[1]; tangents[2] = vertex.normal[2]; } else { tangents[0] = vertex.tangent[0]; tangents[1] = vertex.tangent[1]; tangents[2] = vertex.tangent[2]; } float[] binormals = new float[3]; if (vertex.binormal == null) { binormals[0] = vertex.normal[0]; binormals[1] = vertex.normal[1]; binormals[2] = vertex.normal[2]; } else { binormals[0] = vertex.binormal[0]; binormals[1] = vertex.binormal[1]; binormals[2] = vertex.binormal[2]; } outputWriter.WriteLine(vertex.position[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.position[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.position[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.normal[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.normal[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.normal[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + tangents[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + tangents[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + tangents[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + binormals[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + binormals[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + binormals[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.uv[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.uv[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + 0.00000000f.ToString(numberFormat, CultureInfo.InvariantCulture) + " " + 0.00000000f.ToString(numberFormat, CultureInfo.InvariantCulture) + " " + 0.00000000f.ToString(numberFormat, CultureInfo.InvariantCulture) + " " + 0.00000000f.ToString(numberFormat, CultureInfo.InvariantCulture) + " " + boneIds[0] + " " + boneIds[1] + " " + boneIds[2] + " " + boneIds[3] + " 0 0 0 0 " + vertex.boneWeights[0] + " " + vertex.boneWeights[1] + " " + vertex.boneWeights[2] + " " + vertex.boneWeights[3] + " 0 0 0 0 " ); } // Write Triangles outputWriter.WriteLine("triangles"); for (int ti = 0; ti < grannyMeshInfo.triangles.Count; ti++) { int[] triangle = grannyMeshInfo.triangles[ti]; outputWriter.WriteLine(triangle[0] + " " + triangle[1] + " " + triangle[2] + " 0"); } } } outputWriter.WriteLine("end"); outputWriter.Close(); WriteModelToCN6Response response = new WriteModelToCN6Response(decalMeshNames, textureMaps); return(response); }
public static void exportNB2Model(IGrannyFile grannyFile, int modelId) { string fileExtension = ".nb2"; string outputFilename = ""; string numberFormat = "f6"; if (grannyFile.Models.Count > 1) { string modelFilename = "__" + grannyFile.Models[modelId].Name + fileExtension; outputFilename = grannyFile.Filename.Replace(".gr2", modelFilename); outputFilename = outputFilename.Replace(".GR2", modelFilename); } else { outputFilename = grannyFile.Filename.Replace(".gr2", fileExtension); outputFilename = outputFilename.Replace(".GR2", fileExtension); } StreamWriter outputWriter = new StreamWriter(new FileStream(outputFilename.ToLower(), FileMode.Create)); IGrannyModel model = grannyFile.Models[modelId]; IGrannySkeleton skeleton = model.Skeleton; // Lookup so we can identify the meshes belonging the current model in the list of file meshes Dictionary <int, int> meshBindingToMesh = new Dictionary <int, int>(); HashSet <string> distinctMeshNames = new HashSet <string>(); for (int i = 0; i < model.MeshBindings.Count; i++) { for (int j = 0; j < grannyFile.Meshes.Count; j++) { GrannyMeshWrapper modelMesh = new GrannyMeshWrapper(model.MeshBindings[i]); IGrannyMesh fileMesh = grannyFile.Meshes[j]; if (modelMesh.meshEqual(fileMesh)) { meshBindingToMesh.Add(i, j); } } distinctMeshNames.Add(model.MeshBindings[i].Name); } // Used to give meshes distinct names where we have multiple meshes with the same name in our source gr2 Dictionary <string, int> meshNameCount = new Dictionary <string, int>(); foreach (string meshName in distinctMeshNames) { meshNameCount.Add(meshName, 0); } List <GrannyMeshInfo> grannyMeshInfos = new List <GrannyMeshInfo>(); for (int i = 0; i < model.MeshBindings.Count; i++) { GrannyMeshWrapper meshWrapper = new GrannyMeshWrapper(model.MeshBindings[i]); grannyMeshInfos.Add(meshWrapper.getMeshInfo()); } BiLookup <int, string> boneLookup = new BiLookup <int, string>(); for (int i = 0; i < skeleton.Bones.Count; i++) { boneLookup.Add(i, skeleton.Bones[i].Name); } Dictionary <string, double[]> boneNameToPositionMap = new Dictionary <string, double[]>(); double[] bonePosition; foreach (IGrannyBone bone in skeleton.Bones) { try { bonePosition = getBoneWorldPosition(bone); boneNameToPositionMap.Add(bone.Name, bonePosition); } catch (ArgumentException) { } } outputWriter.WriteLine("// Nexus Buddy NB2 - Exported from Nexus Buddy 2"); outputWriter.WriteLine("Frames: 30"); outputWriter.WriteLine("Frame: 1"); List <IndieMaterial> modelMaterials = new List <IndieMaterial>(); foreach (IGrannyMesh mesh in model.MeshBindings) { IGrannyMaterial material = mesh.MaterialBindings[0]; foreach (ListViewItem materialListItem in NexusBuddyApplicationForm.form.materialList.Items) { IndieMaterial shader = (IndieMaterial)materialListItem.Tag; GrannyMaterialWrapper matWrap1 = new GrannyMaterialWrapper(shader.GetMaterial()); GrannyMaterialWrapper matWrap2 = new GrannyMaterialWrapper(material); if (matWrap1.getName().Equals(matWrap2.getName()) && !modelMaterials.Contains(shader)) { modelMaterials.Add(shader); } } } // Write Meshes outputWriter.WriteLine("Meshes: " + model.MeshBindings.Count); for (int mi = 0; mi < grannyMeshInfos.Count; mi++) { GrannyMeshInfo grannyMeshInfo = grannyMeshInfos[mi]; string meshName = model.MeshBindings[mi].Name; meshNameCount[meshName]++; if (meshNameCount[meshName] > 1) { meshName += meshNameCount[meshName]; } var materialBindingName = model.MeshBindings[mi].MaterialBindings[0].Name; var materialIndex = 0; foreach (IndieMaterial material in modelMaterials) { if (materialBindingName.Equals(material.GetMaterial().Name)) { break; } materialIndex++; } outputWriter.WriteLine("\"" + meshName + "\" 0 " + materialIndex); // Write Vertices outputWriter.WriteLine(grannyMeshInfo.vertices.Count); for (int vi = 0; vi < grannyMeshInfo.vertices.Count; vi++) { GrannyVertexInfo vertex = grannyMeshInfo.vertices[vi]; string boneName0 = grannyMeshInfo.boneBindings[vertex.boneIndices[0]]; float boneWeight0 = (float)vertex.boneWeights[0] / 255; int boneId0 = getBoneIdForBoneName(boneLookup, boneNameToPositionMap, boneName0, boneWeight0, vertex.position); string boneName1 = grannyMeshInfo.boneBindings[vertex.boneIndices[1]]; float boneWeight1 = (float)vertex.boneWeights[1] / 255; int boneId1 = getBoneIdForBoneName(boneLookup, boneNameToPositionMap, boneName1, boneWeight1, vertex.position); string boneName2 = grannyMeshInfo.boneBindings[vertex.boneIndices[2]]; float boneWeight2 = (float)vertex.boneWeights[2] / 255; int boneId2 = getBoneIdForBoneName(boneLookup, boneNameToPositionMap, boneName2, boneWeight2, vertex.position); string boneName3 = grannyMeshInfo.boneBindings[vertex.boneIndices[3]]; float boneWeight3 = (float)vertex.boneWeights[3] / 255; int boneId3 = getBoneIdForBoneName(boneLookup, boneNameToPositionMap, boneName3, boneWeight3, vertex.position); outputWriter.WriteLine("0 " + vertex.position[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.position[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.position[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.uv[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.uv[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + boneId0 + " " + boneWeight0.ToString(numberFormat, CultureInfo.InvariantCulture) + " " + boneId1 + " " + boneWeight1.ToString(numberFormat, CultureInfo.InvariantCulture) + " " + boneId2 + " " + boneWeight2.ToString(numberFormat, CultureInfo.InvariantCulture) + " " + boneId3 + " " + boneWeight3.ToString(numberFormat, CultureInfo.InvariantCulture) ); } // Write Normals outputWriter.WriteLine(grannyMeshInfo.vertices.Count); for (int ni = 0; ni < grannyMeshInfo.vertices.Count; ni++) { GrannyVertexInfo vertex = grannyMeshInfo.vertices[ni]; outputWriter.WriteLine(vertex.normal[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.normal[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.normal[2].ToString(numberFormat, CultureInfo.InvariantCulture)); } // Write Triangles outputWriter.WriteLine(grannyMeshInfo.triangles.Count); for (int ti = 0; ti < grannyMeshInfo.triangles.Count; ti++) { int[] triangle = grannyMeshInfo.triangles[ti]; outputWriter.WriteLine("0 " + triangle[0] + " " + triangle[1] + " " + triangle[2] + " " + triangle[0] + " " + triangle[1] + " " + triangle[2] + " 1"); } } // Write Materials outputWriter.WriteLine("Materials: " + modelMaterials.Count); for (int i = 0; i < modelMaterials.Count; i++) { outputWriter.WriteLine("\"" + modelMaterials[i].GetMaterial().Name + "\""); outputWriter.WriteLine("0.200000 0.200000 0.200000 1.000000"); outputWriter.WriteLine("0.800000 0.800000 0.800000 1.000000"); outputWriter.WriteLine("0.000000 0.000000 0.000000 1.000000"); outputWriter.WriteLine("0.000000 0.000000 0.000000 1.000000"); outputWriter.WriteLine("0.000000"); outputWriter.WriteLine("1.000000"); string map1 = "ColorMap_" + i; string map2 = "AlphaMap_" + i; string baseMap; string srefmap; IndieMaterial shader = modelMaterials[i]; if (shader.GetType() == typeof(IndieBuildingShader)) { baseMap = shader.Diffuse; srefmap = shader.BuildingSREF; } else if (shader.GetType() == typeof(IndieLandmarkStencilShader)) { baseMap = shader.BaseTextureMap; srefmap = shader.SpecTextureMap; } else { baseMap = shader.BaseTextureMap; srefmap = shader.SREFMap; } if (!String.IsNullOrEmpty(baseMap)) { map1 = baseMap; } if (!String.IsNullOrEmpty(srefmap)) { map2 = srefmap; } outputWriter.WriteLine("\"" + map1 + "\""); outputWriter.WriteLine("\"" + map2 + "\""); } // Write Bones outputWriter.WriteLine("Bones: " + skeleton.Bones.Count); for (int bi = 0; bi < skeleton.Bones.Count; bi++) { IGrannyBone bone = skeleton.Bones[bi]; string boneName = bone.Name; outputWriter.WriteLine("\"" + boneName + "\""); if (bone.ParentIndex == -1) { outputWriter.WriteLine("\"\""); } else { string parentBoneName = skeleton.Bones[bone.ParentIndex].Name; outputWriter.WriteLine("\"" + parentBoneName + "\""); } IGrannyTransform transform = bone.LocalTransform; float[] orientation = transform.Orientation; float[] position = transform.Position; //Matrix3D boneWorldMatrix = getBoneWorldMatrix(bone); //ETransformFlags transformFlags = transform.Flags; //bool hasPosition = transformFlags.HasFlag(ETransformFlags.GrannyHasPosition); //bool hasOrientation = transformFlags.HasFlag(ETransformFlags.GrannyHasOrientation); // bone: flags, posx, posy, posz, quatx, quaty, quatz, quatw outputWriter.WriteLine("0 " + position[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + position[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + position[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[3].ToString(numberFormat, CultureInfo.InvariantCulture)); // number of position keys outputWriter.WriteLine("0"); // number of rotation keys outputWriter.WriteLine("0"); } outputWriter.Close(); }
private static List <GrannyModelInfo> loadModelInfos(string filename, int vertexFormat) { string currentLine = ""; string numberRegex = "([-+]?[0-9]+\\.?[0-9]*) "; StreamReader streamReader = new StreamReader(filename); bool endOfFile = false; List <GrannyModelInfo> modelInfos = new List <GrannyModelInfo>(); while (!endOfFile) { while (!currentLine.StartsWith("skeleton")) { if (currentLine.Equals("end")) { endOfFile = true; break; } currentLine = streamReader.ReadLine(); } if (endOfFile) { break; } GrannyModelInfo modelInfo = new GrannyModelInfo(); GrannySkeletonInfo skeletonInfo = new GrannySkeletonInfo(); List <GrannyBoneInfo> skeletonBones = new List <GrannyBoneInfo>(); while (!currentLine.StartsWith("meshes")) { currentLine = streamReader.ReadLine(); if (!currentLine.StartsWith("meshes")) { string regexString = "([0-9]+) \"(.+)\" "; for (int i = 0; i < 24; i++) { regexString = regexString + numberRegex; } Regex regex = new Regex(regexString.Trim()); MatchCollection mc = regex.Matches(currentLine); foreach (Match m in mc) { GrannyBoneInfo boneInfo = new GrannyBoneInfo(); GrannyTransformInfo transformInfo = new GrannyTransformInfo(); //int boneindex = NumberUtils.parseInt(m.Groups[1].Value.Trim()); string boneName = m.Groups[2].Value; int parentIndex = NumberUtils.parseInt(m.Groups[3].Value.Trim()); float[] position = new float[3]; position[0] = NumberUtils.parseFloat(m.Groups[4].Value.Trim()); position[1] = NumberUtils.parseFloat(m.Groups[5].Value.Trim()); position[2] = NumberUtils.parseFloat(m.Groups[6].Value.Trim()); float[] orientation = new float[4]; orientation[0] = NumberUtils.parseFloat(m.Groups[7].Value.Trim()); orientation[1] = NumberUtils.parseFloat(m.Groups[8].Value.Trim()); orientation[2] = NumberUtils.parseFloat(m.Groups[9].Value.Trim()); orientation[3] = NumberUtils.parseFloat(m.Groups[10].Value.Trim()); float[] scaleShear = new float[9]; scaleShear[0] = 1.0f; scaleShear[1] = 0.0f; scaleShear[2] = 0.0f; scaleShear[3] = 0.0f; scaleShear[4] = 1.0f; scaleShear[5] = 0.0f; scaleShear[6] = 0.0f; scaleShear[7] = 0.0f; scaleShear[8] = 1.0f; float[] invWorld = new float[16]; for (int j = 0; j < 16; j++) { invWorld[j] = NumberUtils.parseFloat(m.Groups[j + 11].Value.Trim()); } bool hasPosition = true; bool hasOrientation = true; int flags = 0; if (NumberUtils.almostEquals(position[0], 0.0f, 4) && NumberUtils.almostEquals(position[1], 0.0f, 4) && NumberUtils.almostEquals(position[2], 0.0f, 4)) { hasPosition = false; } if (NumberUtils.almostEquals(orientation[0], 0.0f, 5) && NumberUtils.almostEquals(orientation[1], 0.0f, 5) && NumberUtils.almostEquals(orientation[2], 0.0f, 5) && NumberUtils.almostEquals(orientation[3], 1.0f, 5)) { hasOrientation = false; } if (hasPosition) { flags = flags + 1; } if (hasOrientation) { flags = flags + 2; } transformInfo.flags = flags; transformInfo.position = position; transformInfo.orientation = orientation; transformInfo.scaleShear = scaleShear; boneInfo.name = boneName; boneInfo.parentIndex = parentIndex; boneInfo.localTransform = transformInfo; boneInfo.inverseWorldTransform = invWorld; boneInfo.LODError = 0; skeletonBones.Add(boneInfo); } } } skeletonInfo.bones = skeletonBones; // Read Meshes int numMeshes = NumberUtils.parseInt(currentLine.Replace("meshes:", "")); List <GrannyMeshInfo> meshInfos = new List <GrannyMeshInfo>(); for (int meshId = 0; meshId < numMeshes; meshId++) { string meshName = ""; while (!currentLine.StartsWith("mesh:")) { currentLine = streamReader.ReadLine(); } string regexString = "\"(.+)\""; Regex regex = new Regex(regexString); MatchCollection mc = regex.Matches(currentLine); foreach (Match m in mc) { meshName = m.Groups[1].Value.Replace("#M", ""); } // Read Materials List <string> materialNames = new List <string>(); while (!currentLine.StartsWith("vertices")) { currentLine = streamReader.ReadLine(); if (!currentLine.StartsWith("materials") && !currentLine.StartsWith("vertices")) { mc = regex.Matches(currentLine); foreach (Match m in mc) { string materialName = m.Groups[1].Value; materialNames.Add(materialName); } } } // Read Vertices int unweightedVertexCount = 0; List <GrannyVertexInfo> vertexInfos = new List <GrannyVertexInfo>(); while (!currentLine.StartsWith("triangles")) { currentLine = streamReader.ReadLine(); if (!currentLine.StartsWith("vertices") && !currentLine.StartsWith("triangles")) { regexString = ""; for (int i = 0; i < 34; i++) { regexString = regexString + numberRegex; } regex = new Regex(regexString.Trim()); mc = regex.Matches(currentLine); foreach (Match m in mc) { GrannyVertexInfo vertexInfo = new GrannyVertexInfo(); float[] position = new float[3]; position[0] = NumberUtils.parseFloat(m.Groups[1].Value.Trim()); position[1] = NumberUtils.parseFloat(m.Groups[2].Value.Trim()); position[2] = NumberUtils.parseFloat(m.Groups[3].Value.Trim()); float[] normal = new float[3]; normal[0] = NumberUtils.parseFloat(m.Groups[4].Value.Trim()); normal[1] = NumberUtils.parseFloat(m.Groups[5].Value.Trim()); normal[2] = NumberUtils.parseFloat(m.Groups[6].Value.Trim()); float[] tangent = new float[3]; tangent[0] = NumberUtils.parseFloat(m.Groups[7].Value.Trim()); tangent[1] = NumberUtils.parseFloat(m.Groups[8].Value.Trim()); tangent[2] = NumberUtils.parseFloat(m.Groups[9].Value.Trim()); float[] binormal = new float[3]; binormal[0] = NumberUtils.parseFloat(m.Groups[10].Value.Trim()); binormal[1] = NumberUtils.parseFloat(m.Groups[11].Value.Trim()); binormal[2] = NumberUtils.parseFloat(m.Groups[12].Value.Trim()); float[] uv = new float[2]; uv[0] = NumberUtils.parseFloat(m.Groups[13].Value.Trim()); uv[1] = NumberUtils.parseFloat(m.Groups[14].Value.Trim()); float[] uv2 = new float[2]; uv2[0] = NumberUtils.parseFloat(m.Groups[15].Value.Trim()); uv2[1] = NumberUtils.parseFloat(m.Groups[16].Value.Trim()); float[] uv3 = new float[2]; uv3[0] = NumberUtils.parseFloat(m.Groups[17].Value.Trim()); uv3[1] = NumberUtils.parseFloat(m.Groups[18].Value.Trim()); int[] boneIndices = new int[8]; for (int j = 0; j < 8; j++) { boneIndices[j] = NumberUtils.parseInt(m.Groups[j + 19].Value.Trim()); } int[] boneWeights = new int[8]; for (int j = 0; j < 8; j++) { boneWeights[j] = NumberUtils.parseInt(m.Groups[j + 27].Value.Trim()); } // Assign unweighted vertices to root bone and record count if (boneWeights[0] == -1) { for (int j = 0; j < 8; j++) { boneIndices[j] = 0; } boneWeights[0] = 255; for (int j = 1; j < 8; j++) { boneWeights[j] = 0; } unweightedVertexCount++; } vertexInfo.position = position; vertexInfo.normal = normal; vertexInfo.tangent = tangent; vertexInfo.binormal = binormal; vertexInfo.uv = uv; vertexInfo.uv2 = uv2; vertexInfo.uv3 = uv3; vertexInfo.boneIndices = boneIndices; vertexInfo.boneWeights = boneWeights; vertexInfos.Add(vertexInfo); } } } if (unweightedVertexCount > 0 && vertexFormat == 0) { MessageBox.Show(unweightedVertexCount + " unweighted vertices have been assigned to root bone.", "Warning!", MessageBoxButtons.OK, MessageBoxIcon.Hand); } // Read Triangles List <int[]> triangles = new List <int[]>(); while (!currentLine.StartsWith("mesh") && !currentLine.StartsWith("end") && !currentLine.StartsWith("skeleton")) { currentLine = streamReader.ReadLine(); if (!currentLine.StartsWith("mesh") && !currentLine.StartsWith("end")) { regexString = ""; for (int i = 0; i < 4; i++) { regexString = regexString + "([-+]?[0-9]+\\.?[0-9]*) "; } regex = new Regex(regexString.Trim()); mc = regex.Matches(currentLine); foreach (Match m in mc) { int[] triangle = new int[4]; triangle[0] = NumberUtils.parseInt(m.Groups[1].Value.Trim()); triangle[1] = NumberUtils.parseInt(m.Groups[2].Value.Trim()); triangle[2] = NumberUtils.parseInt(m.Groups[3].Value.Trim()); triangle[3] = NumberUtils.parseInt(m.Groups[4].Value.Trim()); triangles.Add(triangle); } } } List <PrimaryTopologyGroupInfo> groupInfos = new List <PrimaryTopologyGroupInfo>(); int maxMaterialIndex = 0; int groupIndexStart = 0; int triIndex; for (triIndex = 0; triIndex < triangles.Count; triIndex++) { int[] triangle = triangles[triIndex]; int triMaterialIndex = triangle[3]; if (triMaterialIndex > maxMaterialIndex) { groupInfos.Add(new PrimaryTopologyGroupInfo(maxMaterialIndex, groupIndexStart, triIndex - groupIndexStart)); maxMaterialIndex = triMaterialIndex; groupIndexStart = triIndex; } } groupInfos.Add(new PrimaryTopologyGroupInfo(maxMaterialIndex, groupIndexStart, triIndex - groupIndexStart)); GrannyMeshInfo meshInfo = new GrannyMeshInfo(); meshInfo.name = meshName; meshInfo.vertices = vertexInfos; meshInfo.triangles = triangles; meshInfo.primaryTopologyGroupInfos = groupInfos; meshInfo.materialBindingNames = materialNames; meshInfos.Add(meshInfo); } modelInfo.skeleton = skeletonInfo; modelInfo.meshBindings = meshInfos; modelInfos.Add(modelInfo); } streamReader.Close(); return(modelInfos); }
public static void exportNB2Model(IGrannyFile grannyFile, int modelId) { string fileExtension = ".nb2"; string outputFilename = ""; string numberFormat = "f6"; if (grannyFile.Models.Count > 1) { outputFilename = grannyFile.Filename.Replace(".fgx", "_model" + modelId + fileExtension); outputFilename = outputFilename.Replace(".FGX", "_model" + modelId + fileExtension); } else { outputFilename = grannyFile.Filename.Replace(".fgx", fileExtension); outputFilename = outputFilename.Replace(".FGX", fileExtension); } StreamWriter outputWriter = new StreamWriter(new FileStream(outputFilename, FileMode.Create)); IGrannyModel model = grannyFile.Models[modelId]; IGrannySkeleton skeleton = model.Skeleton; GrannySkeletonWrapper skeletonWrapper = new GrannySkeletonWrapper(skeleton); skeletonWrapper.readSkeletonInfo(); // Lookup so we can identify the meshes belonging the current model in the list of file meshes Dictionary <int, int> meshBindingToMesh = new Dictionary <int, int>(); HashSet <string> distinctMeshNames = new HashSet <string>(); for (int i = 0; i < model.MeshBindings.Count; i++) { for (int j = 0; j < grannyFile.Meshes.Count; j++) { GrannyMeshWrapper modelMesh = new GrannyMeshWrapper(model.MeshBindings[i]); IGrannyMesh fileMesh = grannyFile.Meshes[j]; if (modelMesh.meshEqual(fileMesh)) { meshBindingToMesh.Add(i, j); } } distinctMeshNames.Add(model.MeshBindings[i].Name); } // Used to give meshes distinct names where we have multiple meshes with the same name in our source gr2 Dictionary <string, int> meshNameCount = new Dictionary <string, int>(); foreach (string meshName in distinctMeshNames) { meshNameCount.Add(meshName, 0); } List <GrannyMeshInfo> grannyMeshInfos = new List <GrannyMeshInfo>(); for (int i = 0; i < model.MeshBindings.Count; i++) { GrannyMeshWrapper meshWrapper = new GrannyMeshWrapper(model.MeshBindings[i]); grannyMeshInfos.Add(meshWrapper.getMeshInfo()); } BiLookup <int, string> boneLookup = new BiLookup <int, string>(); for (int i = 0; i < skeleton.Bones.Count; i++) { boneLookup.Add(i, skeleton.Bones[i].Name); } Dictionary <string, double[]> boneNameToPositionMap = new Dictionary <string, double[]>(); foreach (IGrannyBone bone in skeleton.Bones) { double[] bonePosition = getBoneWorldPosition(bone); boneNameToPositionMap.Add(bone.Name, bonePosition); } outputWriter.WriteLine("// Nexus Buddy NB2 - Exported from Nexus Buddy 2"); outputWriter.WriteLine("Frames: 30"); outputWriter.WriteLine("Frame: 1"); // Write Meshes outputWriter.WriteLine("Meshes: " + model.MeshBindings.Count); for (int mi = 0; mi < grannyMeshInfos.Count; mi++) { GrannyMeshInfo grannyMeshInfo = grannyMeshInfos[mi]; string meshName = model.MeshBindings[mi].Name; meshNameCount[meshName]++; if (meshNameCount[meshName] > 1) { meshName += meshNameCount[meshName]; } outputWriter.WriteLine("\"" + meshName + "\" 0 0"); // Write Vertices outputWriter.WriteLine(grannyMeshInfo.vertices.Count); for (int vi = 0; vi < grannyMeshInfo.vertices.Count; vi++) { GrannyVertexInfo vertex = grannyMeshInfo.vertices[vi]; string boneName0 = grannyMeshInfo.boneBindings[vertex.boneIndices[0]]; float boneWeight0 = (float)vertex.boneWeights[0] / 255; int boneId0 = getBoneIdForBoneName(boneLookup, boneNameToPositionMap, boneName0, boneWeight0, vertex.position); string boneName1 = grannyMeshInfo.boneBindings[vertex.boneIndices[1]]; float boneWeight1 = (float)vertex.boneWeights[1] / 255; int boneId1 = getBoneIdForBoneName(boneLookup, boneNameToPositionMap, boneName1, boneWeight1, vertex.position); string boneName2 = grannyMeshInfo.boneBindings[vertex.boneIndices[2]]; float boneWeight2 = (float)vertex.boneWeights[2] / 255; int boneId2 = getBoneIdForBoneName(boneLookup, boneNameToPositionMap, boneName2, boneWeight2, vertex.position); string boneName3 = grannyMeshInfo.boneBindings[vertex.boneIndices[3]]; float boneWeight3 = (float)vertex.boneWeights[3] / 255; int boneId3 = getBoneIdForBoneName(boneLookup, boneNameToPositionMap, boneName3, boneWeight3, vertex.position); outputWriter.WriteLine("0 " + vertex.position[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.position[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.position[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.uv[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.uv[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + boneId0 + " " + boneWeight0.ToString(numberFormat, CultureInfo.InvariantCulture) + " " + boneId1 + " " + boneWeight1.ToString(numberFormat, CultureInfo.InvariantCulture) + " " + boneId2 + " " + boneWeight2.ToString(numberFormat, CultureInfo.InvariantCulture) + " " + boneId3 + " " + boneWeight3.ToString(numberFormat, CultureInfo.InvariantCulture) ); } // Write Normals outputWriter.WriteLine(grannyMeshInfo.vertices.Count); for (int ni = 0; ni < grannyMeshInfo.vertices.Count; ni++) { GrannyVertexInfo vertex = grannyMeshInfo.vertices[ni]; outputWriter.WriteLine(vertex.normal[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.normal[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + vertex.normal[2].ToString(numberFormat, CultureInfo.InvariantCulture)); } // Write Triangles outputWriter.WriteLine(grannyMeshInfo.triangles.Count); for (int ti = 0; ti < grannyMeshInfo.triangles.Count; ti++) { int[] triangle = grannyMeshInfo.triangles[ti]; outputWriter.WriteLine("0 " + triangle[0] + " " + triangle[1] + " " + triangle[2] + " " + triangle[0] + " " + triangle[1] + " " + triangle[2] + " 1"); } } // Write Material outputWriter.WriteLine("Materials: 1"); outputWriter.WriteLine("\"Material_0\""); outputWriter.WriteLine("0.200000 0.200000 0.200000 1.000000"); outputWriter.WriteLine("0.800000 0.800000 0.800000 1.000000"); outputWriter.WriteLine("0.000000 0.000000 0.000000 1.000000"); outputWriter.WriteLine("0.000000 0.000000 0.000000 1.000000"); outputWriter.WriteLine("0.000000"); outputWriter.WriteLine("1.000000"); outputWriter.WriteLine("\"Material_0\""); outputWriter.WriteLine("\"\""); // Write Bones outputWriter.WriteLine("Bones: " + skeleton.Bones.Count); for (int bi = 0; bi < skeleton.Bones.Count; bi++) { IGrannyBone bone = skeleton.Bones[bi]; string boneName = bone.Name; outputWriter.WriteLine("\"" + boneName + "\""); if (bone.ParentIndex == -1) { outputWriter.WriteLine("\"\""); } else { string parentBoneName = skeleton.Bones[bone.ParentIndex].Name; outputWriter.WriteLine("\"" + parentBoneName + "\""); } IGrannyTransform transform = bone.LocalTransform; float[] orientation = transform.Orientation; float[] position = transform.Position; outputWriter.WriteLine("0 " + position[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + position[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + position[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[0].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[1].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[2].ToString(numberFormat, CultureInfo.InvariantCulture) + " " + orientation[3].ToString(numberFormat, CultureInfo.InvariantCulture)); // number of position keys outputWriter.WriteLine("0"); // number of rotation keys outputWriter.WriteLine("0"); } outputWriter.Close(); }