/// <summary> /// Writes the skeleton hierarchy to the DAE. /// </summary> /// <param name="skeleton">The skeleton</param> /// <param name="index">Index of the current bone (root bone when it's not a recursive call)</param> /// <param name="nodes">List with the DAE nodes</param> private static void writeSkeleton(List <RenderBase.OBone> skeleton, int index, ref List <daeNode> nodes) { daeNode node = new daeNode(); node.name = skeleton[index].name; node.id = node.name + "_bone_id"; node.sid = node.name; node.type = "JOINT"; RenderBase.OMatrix transform = new RenderBase.OMatrix(); transform *= RenderBase.OMatrix.rotateX(skeleton[index].rotation.x); transform *= RenderBase.OMatrix.rotateY(skeleton[index].rotation.y); transform *= RenderBase.OMatrix.rotateZ(skeleton[index].rotation.z); transform *= RenderBase.OMatrix.translate(skeleton[index].translation); node.matrix.set(transform); for (int i = 0; i < skeleton.Count; i++) { if (skeleton[i].parentId == index) { if (node.childs == null) { node.childs = new List <daeNode>(); } writeSkeleton(skeleton, i, ref node.childs); } } nodes.Add(node); }
/// <summary> /// Transforms a Node from relative to absolute positions. /// </summary> /// <param name="nodes">A list with all nodes</param> /// <param name="index">Index of the node to convert</param> /// <param name="target">Target matrix to save node transformation</param> private static void transformNode(List <node> nodes, int index, ref RenderBase.OMatrix target) { target *= nodes[index].transform; if (nodes[index].parentId > -1) { transformNode(nodes, nodes[index].parentId, ref target); } }
public RenderBase.OMatrix get() { RenderBase.OMatrix output = new RenderBase.OMatrix(); string[] values = data.Split(Convert.ToChar(" ")); int k = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { output[j, i] = float.Parse(values[k++]); } } return(output); }
public void set(RenderBase.OMatrix mtx) { StringBuilder strArray = new StringBuilder(); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (i == 3 && j == 3) { strArray.Append(mtx[j, i].ToString(CultureInfo.InvariantCulture)); } else { strArray.Append(mtx[j, i].ToString(CultureInfo.InvariantCulture) + " "); } } } data = strArray.ToString(); }
/// <summary> /// Exports a Model to the Collada format. /// See: https://www.khronos.org/files/collada_spec_1_4.pdf for more information. /// </summary> /// <param name="model">The Model that will be exported</param> /// <param name="fileName">The output File Name</param> /// <param name="modelIndex">Index of the model to be exported</param> /// <param name="skeletalAnimationIndex">(Optional) Index of the skeletal animation</param> public static void export(RenderBase.OModelGroup model, string fileName, int modelIndex, int skeletalAnimationIndex = -1) { RenderBase.OModel mdl = model.model[modelIndex]; COLLADA dae = new COLLADA(); COLLADA daeShiny = new COLLADA(); dae.asset.created = DateTime.Now.ToString("yyyy-MM-ddThh:mm:ssZ"); dae.asset.modified = dae.asset.created; daeShiny.asset.created = DateTime.Now.ToString("yyyy-MM-ddThh:mm:ssZ"); daeShiny.asset.modified = dae.asset.created; bool hasShiny = false; // Attempt to detect packaged textures. See http://www.github.com/Quibilia/Ohana3DS-Transfigured for directions. try { string modelDir; string sDir; string[] texDirs; string[] texFiles; modelDir = Path.GetFileNameWithoutExtension(fileName).Remove(4); sDir = Path.GetDirectoryName(fileName); texDirs = Directory.GetDirectories(Path.Combine(sDir, "../../Textures/")); foreach (string texDir in texDirs) { if (texDir.Contains(modelDir)) { texFiles = Directory.GetFiles(texDir); if (Directory.Exists(texDir.Replace("Textures", "Models/DAE")) == false && texDir.Contains("Xtra") == false) { Directory.CreateDirectory(texDir.Replace("Textures", "Models/DAE")); } bool hasIris = false; foreach (string texFile in texFiles) { if (texFile.Contains("Iris1")) { hasIris = true; } } foreach (string texFile in texFiles) { if (texFile.Contains("Xtra") == false) { Bitmap tempBMP = (Bitmap)Bitmap.FromFile(texFile); if (texFile.Contains("Body") && texFile.Contains("Nor") == false) { int w = tempBMP.Width / 2; Bitmap scaledBMP = new Bitmap(tempBMP, tempBMP.Width / 2, tempBMP.Height); Bitmap reverseBMP = new Bitmap(tempBMP, tempBMP.Width / 2, tempBMP.Height); reverseBMP.RotateFlip(RotateFlipType.RotateNoneFlipX); Bitmap finalBMP = new Bitmap(tempBMP.Width, tempBMP.Height); for (int x = 0; x < scaledBMP.Width; x++) { for (int y = 0; y < scaledBMP.Height; y++) { finalBMP.SetPixel(x, y, scaledBMP.GetPixel(x, y)); } } for (int x = 0; x < reverseBMP.Width; x++) { for (int y = 0; y < reverseBMP.Height; y++) { finalBMP.SetPixel(x + reverseBMP.Width, y, reverseBMP.GetPixel(x, y)); } } if (texFile.Contains("BodyA2") == false && texFile.Contains("BodyB2") == false && texFile.Contains("Body2") == false) { finalBMP.Save(texFile.Replace("Textures", "Models/DAE")); model.texture.Add(new RenderBase.OTexture(finalBMP, texFile.Replace("Textures", "Models/DAE"))); } } else if (texFile.Contains("Iris1")) { Bitmap eyeBMP = (Bitmap)Bitmap.FromFile(texFile.Replace("Iris", "Eye")); foreach (string norFile in texFiles) { if (norFile.Contains("Xtra") == false && norFile.Contains("EyeNor")) { Bitmap thisEye = new Bitmap(eyeBMP.Width / 2, eyeBMP.Height / 4); Bitmap norBMP = (Bitmap)Bitmap.FromFile(norFile); Bitmap thisNor = new Bitmap(norBMP.Width / 4, norBMP.Height / 4); Bitmap finalBMP = new Bitmap(eyeBMP.Width * 2, eyeBMP.Height); int eye = 0; int half = 0; bool reflectEye = false; bool reflectIris = true; bool reflectNor = true; for (int eyeY = 0; eyeY < 4; eyeY++) { eye = 0; for (int eyeX = 0; eyeX < 2; eyeX++) { reflectEye = !reflectEye; reflectNor = !reflectNor; reflectIris = !reflectIris; for (half = 0; half < 2; half++) { for (int x = half * (eyeBMP.Width / 2); x < (half + 1) * (eyeBMP.Width / 2); x++) { for (int y = eyeY * (eyeBMP.Height / 4); y < (eyeY + 1) * (eyeBMP.Height / 4); y++) { thisEye.SetPixel(x - (half * (eyeBMP.Width / 2)), y - (eyeY * (eyeBMP.Height / 4)), eyeBMP.GetPixel(x, y)); } } for (int x = (half * 2) * (norBMP.Width / 4); x < ((half * 2) + 1) * (norBMP.Width / 4); x++) { for (int y = eyeY * (norBMP.Height / 4); y < (eyeY + 1) * (norBMP.Height / 4); y++) { thisNor.SetPixel(x - ((half * 2) * (norBMP.Width / 4)), y - (eyeY * (norBMP.Height / 4)), norBMP.GetPixel(x, y)); } } if (reflectEye == true) { thisEye.RotateFlip(RotateFlipType.RotateNoneFlipX); } reflectEye = !reflectEye; if (reflectNor == true) { thisNor.RotateFlip(RotateFlipType.RotateNoneFlipX); } if (reflectIris == true) { tempBMP.RotateFlip(RotateFlipType.RotateNoneFlipX); reflectIris = false; } // By now, tempBMP, thisEye, and thisNor should all be the same size. for (int x = 0; x < tempBMP.Width; x++) { for (int y = 0; y < tempBMP.Height; y++) { if (tempBMP.GetPixel(x, y).A >= 0x7F && thisNor.GetPixel(x, y).A >= 0x7F) { thisEye.SetPixel(x, y, tempBMP.GetPixel(x, y)); } } } for (int x = eye * (norBMP.Width / 4); x < (eye + 1) * (norBMP.Width / 4); x++) { for (int y = eyeY * (norBMP.Height / 4); y < (eyeY + 1) * (norBMP.Height / 4); y++) { finalBMP.SetPixel(x, y, thisEye.GetPixel(x - (eye * (norBMP.Width / 4)), y - (eyeY * (norBMP.Height / 4)))); } } if (eye == 3) { eye--; } else if (eye == 1) { eye += 2; } else if (eye == 0) { eye++; } else if (eye == 2) { eye -= 2; } } } } finalBMP.Save(texFile.Replace("Textures", "Models/DAE")); model.texture.Add(new RenderBase.OTexture(finalBMP, texFile.Replace("Textures", "Models/DAE"))); } } } else if (texFile.Contains("Mouth1") && texFile.Contains("Nor") == false) { bool hasNormals = false; foreach (string norFile in texFiles) { // If the mouth has a normal file, the UV coordinates expect it split across the image border. if (norFile.Contains("Mouth") && norFile.Contains("Nor")) { hasNormals = true; } } if (hasNormals == true) { Bitmap thisHalf = new Bitmap(tempBMP.Width / 2, tempBMP.Height / 4); Bitmap finalBMP = new Bitmap(tempBMP.Width * 2, tempBMP.Height); for (int halfY = 0; halfY < 4; halfY++) { for (int halfX = 0; halfX < 2; halfX++) { for (int x = halfX * (tempBMP.Width / 2); x < (halfX + 1) * (tempBMP.Width / 2); x++) { for (int y = halfY * (tempBMP.Height / 4); y < (halfY + 1) * (tempBMP.Height / 4); y++) { thisHalf.SetPixel(x - (halfX * (tempBMP.Width / 2)), y - (halfY * (tempBMP.Height / 4)), tempBMP.GetPixel(x, y)); } } for (int x = halfX * (tempBMP.Width / 2); x < (halfX + 1) * (tempBMP.Width / 2); x++) { for (int y = halfY * (tempBMP.Height / 4); y < (halfY + 1) * (tempBMP.Height / 4); y++) { finalBMP.SetPixel(x, y, thisHalf.GetPixel(x - (halfX * (tempBMP.Width / 2)), y - (halfY * (tempBMP.Height / 4)))); } } for (int x = halfX * (tempBMP.Width / 2); x < (halfX + 1) * (tempBMP.Width / 2); x++) { for (int y = halfY * (tempBMP.Height / 4); y < (halfY + 1) * (tempBMP.Height / 4); y++) { finalBMP.SetPixel(finalBMP.Width - (x + 1), y, thisHalf.GetPixel(x - (halfX * (tempBMP.Width / 2)), y - (halfY * (tempBMP.Height / 4)))); } } } } finalBMP.Save(texFile.Replace("Textures", "Models/DAE")); model.texture.Add(new RenderBase.OTexture(finalBMP, texFile.Replace("Textures", "Models/DAE"))); } else { // If the mouth doesn't have a normal file, the UV coordinates expect it as-is. Hooray! tempBMP.Save(texFile.Replace("Textures", "Models/DAE")); model.texture.Add(new RenderBase.OTexture(tempBMP, texFile.Replace("Textures", "Models/DAE"))); } } else if (texFile.Contains("Eye1") && hasIris == false && texFile.Contains("AEye") == false && texFile.Contains("BEye") == false & texFile.Contains("CEye") == false) { if (tempBMP.Width == tempBMP.Height) { tempBMP.Save(texFile.Replace("Textures", "Models/DAE")); model.texture.Add(new RenderBase.OTexture(tempBMP, texFile.Replace("Textures", "Models/DAE"))); } else { Bitmap thisEye = new Bitmap(tempBMP.Width / 2, tempBMP.Height / 4); Bitmap finalBMP = new Bitmap(tempBMP.Width * 2, tempBMP.Height); for (int eyeY = 0; eyeY < 4; eyeY++) { for (int eyeX = 0; eyeX < 2; eyeX++) { for (int x = eyeX * (tempBMP.Width / 2); x < (eyeX + 1) * (tempBMP.Width / 2); x++) { for (int y = eyeY * (tempBMP.Height / 4); y < (eyeY + 1) * (tempBMP.Height / 4); y++) { thisEye.SetPixel(x - (eyeX * (tempBMP.Width / 2)), y - (eyeY * (tempBMP.Height / 4)), tempBMP.GetPixel(x, y)); } } for (int x = eyeX * (tempBMP.Width / 2); x < (eyeX + 1) * (tempBMP.Width / 2); x++) { for (int y = eyeY * (tempBMP.Height / 4); y < (eyeY + 1) * (tempBMP.Height / 4); y++) { finalBMP.SetPixel(x, y, thisEye.GetPixel(x - (eyeX * (tempBMP.Width / 2)), y - (eyeY * (tempBMP.Height / 4)))); } } for (int x = eyeX * (tempBMP.Width / 2); x < (eyeX + 1) * (tempBMP.Width / 2); x++) { for (int y = eyeY * (tempBMP.Height / 4); y < (eyeY + 1) * (tempBMP.Height / 4); y++) { finalBMP.SetPixel(finalBMP.Width - (x + 1), y, thisEye.GetPixel(x - (eyeX * (tempBMP.Width / 2)), y - (eyeY * (tempBMP.Height / 4)))); } } } } finalBMP.Save(texFile.Replace("Textures", "Models/DAE")); model.texture.Add(new RenderBase.OTexture(finalBMP, texFile.Replace("Textures", "Models/DAE"))); } } else if (texFile.Contains("AEye") || texFile.Contains("BEye") || texFile.Contains("CEye")) { Bitmap finalBMP = new Bitmap(tempBMP, tempBMP.Width / 2, tempBMP.Height); finalBMP.Save(texFile.Replace("Textures", "Models/DAE")); model.texture.Add(new RenderBase.OTexture(finalBMP, texFile.Replace("Textures", "Models/DAE"))); } else if (texFile.Contains("Fire")) { Bitmap finalBMP = new Bitmap(tempBMP); foreach (string bodyFile in texFiles) { if ((bodyFile.Contains("Body1") || bodyFile.Contains("BodyA1")) && bodyFile.Contains("Nor") == false) { Bitmap bodyBMP = (Bitmap)Bitmap.FromFile(bodyFile); byte r, g, b; // Most-Frequent Body Color Color MFBC = Color.FromArgb(0xFF, 0xFF, 0x00, 0x00), LFBC = Color.FromArgb(0xFF, 0xFF, 0xFF, 0x00); int LF = 0, SF = 65536; List<Color> colors = new List<Color>(); List<int> freqs = new List<int>(); for (int x = 0; x < bodyBMP.Width; x++) { for (int y = 0; y < bodyBMP.Height; y++) { r = bodyBMP.GetPixel(x, y).R; g = bodyBMP.GetPixel(x, y).G; b = bodyBMP.GetPixel(x, y).B; if (bodyBMP.GetPixel(x, y).A != 0x00) { bool matchFound = false; foreach (Color c in colors) { if (c.R == r && c.G == g && c.B == b) { matchFound = true; int freq = freqs[colors.IndexOf(c)]; freq++; freqs[colors.IndexOf(c)] = freq; } } if (matchFound == false) { colors.Add(Color.FromArgb(r, g, b)); freqs.Add(1); } } } } int tolerance = 18; foreach (Color c in colors) { if (freqs[colors.IndexOf(c)] > LF) { LF = freqs[colors.IndexOf(c)]; MFBC = c; } else if (freqs[colors.IndexOf(c)] + 150 < SF) { if (c.R <= (c.G - tolerance) || c.R >= (c.G + tolerance)) { if (c.B <= (c.G - tolerance) || c.B >= (c.G + tolerance)) { SF = freqs[colors.IndexOf(c)]; LFBC = c; } } } } bool mfbcgray = false; bool lfbcgray = false; bool bothgray = false; bool neithergray = false; if (MFBC.R > (MFBC.G - tolerance) && MFBC.R < (MFBC.G + tolerance)) { if (MFBC.B > (MFBC.G - tolerance) && MFBC.B < (MFBC.G + tolerance)) { mfbcgray = true; } } if (LFBC.R > (LFBC.G - tolerance) && LFBC.R < (LFBC.G + tolerance)) { if (LFBC.B > (LFBC.G - tolerance) && LFBC.B < (LFBC.G + tolerance)) { lfbcgray = true; } } if (mfbcgray && lfbcgray) { bothgray = true; } if (!mfbcgray && !lfbcgray) { neithergray = true; } Color baseOperator; Color accentOperator; if (bothgray) { baseOperator = Color.FromArgb(0xFF, 0xFF, 0x00, 0x00); accentOperator = Color.FromArgb(0xFF, 0xFF, 0xFF, 0x00); } else if (neithergray) { baseOperator = Color.FromArgb(0xFF, 0xFF, 0x00, 0x00); accentOperator = Color.FromArgb(0xFF, 0xFF, 0xFF, 0x00); } else { if (mfbcgray) { baseOperator = LFBC; accentOperator = LFBC; } else if (lfbcgray) { baseOperator = MFBC; accentOperator = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF); } else { // Just to satisfy C#... baseOperator = MFBC; accentOperator = LFBC; } } for (int x = 0; x < finalBMP.Width; x++) { for (int y = 0; y < finalBMP.Height; y++) { Color tempColor = tempBMP.GetPixel(x, y); Color finalColor; if (tempColor.R < 0x10 && tempColor.G < 0x10 && tempColor.B < 0x10) { finalColor = Color.FromArgb(0x1F, baseOperator.R, baseOperator.G, baseOperator.B); } else { finalColor = Color.FromArgb(0x1F, tempColor.R & accentOperator.R, tempColor.G & accentOperator.G, tempColor.B & accentOperator.B); } finalBMP.SetPixel(x, y, finalColor); } } finalBMP.Save(texFile.Replace("Textures", "Models/DAE")); model.texture.Add(new RenderBase.OTexture(finalBMP, texFile.Replace("Textures", "Models/DAE"))); } } } else { if (texFile.Contains("Iris2") == false && texFile.Contains("Eye2") == false && texFile.Contains("Mouth2") == false) { tempBMP.Save(texFile.Replace("Textures", "Models/DAE")); model.texture.Add(new RenderBase.OTexture(tempBMP, texFile.Replace("Textures", "Models/DAE"))); } } } } } } } catch // No packaged textures detected... { } foreach (RenderBase.OTexture tex in model.texture) { daeImage img = new daeImage(); string n = Path.GetFileNameWithoutExtension(tex.name); img.id = n; img.name = n; if (tex.name.Contains("Shiny")) { img.id += "_shiny"; img.name += "_shiny"; hasShiny = true; } img.id += "_id"; img.init_from = tex.name; if (img.id.Contains("_shiny")) { daeShiny.library_images.Add(img); } else { dae.library_images.Add(img); } } #region Normal int currentMat = 0; foreach (RenderBase.OMaterial mat in mdl.material) { if (mat.name0 == null) { mat.name0 = mat.name; } mdl.material[currentMat].name = mat.name; mdl.material[currentMat].name0 = mat.name0; currentMat++; daeMaterial mtl = new daeMaterial(); mtl.name = mat.name + "_mat"; mtl.id = mtl.name + "_id"; mtl.instance_effect.url = "#eff_" + mtl.id; dae.library_materials.Add(mtl); daeEffect eff = new daeEffect(); eff.id = "eff_" + mtl.id; eff.name = "eff_" + mtl.name; daeParam surface = new daeParam(); surface.surface = new daeParamSurfaceElement(); surface.sid = "img_surface"; surface.surface.type = "2D"; surface.surface.init_from = mat.name0 + "_id"; surface.surface.format = "PNG"; eff.profile_COMMON.newparam.Add(surface); bool fire = false; for (int i = 0; i < dae.library_images.Count; i++) { if (dae.library_images[i].init_from.Contains(mat.name) || dae.library_images[i].id.Contains(mat.name) || dae.library_images[i].init_from.Contains(mat.name0) || dae.library_images[i].id.Contains(mat.name0)) { if (dae.library_images[i].init_from.Contains("Nor") == false) { if (mat.name.Contains("Fire")) { fire = true; dae.library_images[i].name = dae.library_images[i].name.Replace("Mask", "1"); dae.library_images[i].id = dae.library_images[i].id.Replace("Mask", "1"); dae.library_images[i].init_from = dae.library_images[i].init_from.Replace("Mask", "1"); mat.name = mat.name.Replace("Mask", "1"); mat.name0 = mat.name0.Replace("Mask", "1"); } surface.surface.init_from = dae.library_images[i].id; } } } daeParam sampler = new daeParam(); sampler.sampler2D = new daeParamSampler2DElement(); sampler.sid = "img_sampler"; sampler.sampler2D.source = "img_surface"; switch (mat.textureMapper[0].wrapU) { case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_s = "WRAP"; break; case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_s = "MIRROR"; break; case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_s = "CLAMP"; break; case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_s = "BORDER"; break; default: sampler.sampler2D.wrap_s = "WRAP"; break; } switch (mat.textureMapper[0].wrapV) { case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_t = "WRAP"; break; case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_t = "MIRROR"; break; case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_t = "CLAMP"; break; case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_t = "BORDER"; break; default: sampler.sampler2D.wrap_t = "WRAP"; break; } switch (mat.textureMapper[0].minFilter) { case RenderBase.OTextureMinFilter.linearMipmapLinear: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_LINEAR"; break; case RenderBase.OTextureMinFilter.linearMipmapNearest: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_NEAREST"; break; case RenderBase.OTextureMinFilter.nearestMipmapLinear: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_LINEAR"; break; case RenderBase.OTextureMinFilter.nearestMipmapNearest: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_NEAREST"; break; default: sampler.sampler2D.minfilter = "NONE"; break; } switch (mat.textureMapper[0].magFilter) { case RenderBase.OTextureMagFilter.linear: sampler.sampler2D.magfilter = "LINEAR"; break; case RenderBase.OTextureMagFilter.nearest: sampler.sampler2D.magfilter = "NEAREST"; break; default: sampler.sampler2D.magfilter = "NONE"; break; } sampler.sampler2D.mipfilter = sampler.sampler2D.magfilter; eff.profile_COMMON.newparam.Add(sampler); eff.profile_COMMON.technique.sid = "img_technique"; eff.profile_COMMON.technique.phong.emission.set(Color.Black); eff.profile_COMMON.technique.phong.ambient.set(Color.Black); eff.profile_COMMON.technique.phong.specular.set(Color.White); eff.profile_COMMON.technique.phong.diffuse.texture.texture = "img_sampler"; eff.profile_COMMON.technique.phong.transparency = new daePhongTransparent(); if (fire) { eff.profile_COMMON.technique.phong.transparency.value = "0.1"; } else { eff.profile_COMMON.technique.phong.transparency.value = "1.0"; } dae.library_effects.Add(eff); } string jointNames = null; string invBindPoses = null; for (int index = 0; index < mdl.skeleton.Count; index++) { RenderBase.OMatrix transform = new RenderBase.OMatrix(); transformSkeleton(mdl.skeleton, index, ref transform); jointNames += mdl.skeleton[index].name; daeMatrix mtx = new daeMatrix(); mtx.set(transform.invert()); invBindPoses += mtx.data; if (index < mdl.skeleton.Count - 1) { jointNames += " "; invBindPoses += " "; } } int meshIndex = 0; daeVisualScene vs = new daeVisualScene(); vs.name = "vs_" + mdl.name; vs.id = vs.name + "_id"; if (mdl.skeleton.Count > 0) writeSkeleton(mdl.skeleton, 0, ref vs.node); bool rightIris = false; foreach (RenderBase.OMesh obj in mdl.mesh) { float largestUV = 0.0f; //Geometry daeGeometry geometry = new daeGeometry(); string meshName = "mesh_" + meshIndex++ + "_" + obj.name; geometry.id = meshName + "_id"; geometry.name = meshName; MeshUtils.optimizedMesh mesh = MeshUtils.optimizeMesh(obj); List<float> positions = new List<float>(); List<float> normals = new List<float>(); List<float> uv0 = new List<float>(); List<float> uv1 = new List<float>(); List<float> uv2 = new List<float>(); List<float> colors = new List<float>(); if (obj.name.Contains("Iris")) { foreach (RenderBase.OVertex vtx in mesh.vertices) { if (mesh.texUVCount > 0) { if (vtx.texture0.x > largestUV) { largestUV = vtx.texture0.x; } } } foreach (RenderBase.OVertex vtx in mesh.vertices) { if (mesh.texUVCount > 0) { vtx.texture0.x = largestUV - vtx.texture0.x; if (rightIris) { vtx.texture0.x = 1.25f - vtx.texture0.x; } } if (mesh.texUVCount > 1) { vtx.texture1.x = largestUV - vtx.texture1.x; if (rightIris) { vtx.texture1.x = 1.25f - vtx.texture1.x; } } if (mesh.texUVCount > 2) { vtx.texture2.x = largestUV - vtx.texture2.x; if (rightIris) { vtx.texture2.x = 1.25f - vtx.texture2.x; } } } rightIris = !rightIris; } else if (obj.name.Contains("Eye")) { foreach (RenderBase.OVertex vtx in mesh.vertices) { if (mesh.texUVCount > 0) { if (vtx.texture0.x > largestUV) { largestUV = vtx.texture0.x; } } } foreach (RenderBase.OVertex vtx in mesh.vertices) { if (mesh.texUVCount > 0) { vtx.texture0.x = largestUV - vtx.texture0.x; } if (mesh.texUVCount > 1) { vtx.texture1.x = largestUV - vtx.texture1.x; } if (mesh.texUVCount > 2) { vtx.texture2.x = largestUV - vtx.texture2.x; } } } foreach (RenderBase.OVertex vtx in mesh.vertices) { positions.Add(vtx.position.x); positions.Add(vtx.position.y); positions.Add(vtx.position.z); if (mesh.hasNormal) { normals.Add(vtx.normal.x); normals.Add(vtx.normal.y); normals.Add(vtx.normal.z); } if (mesh.texUVCount > 0) { uv0.Add(vtx.texture0.x); uv0.Add(vtx.texture0.y); } if (mesh.texUVCount > 1) { uv1.Add(vtx.texture1.x); uv1.Add(vtx.texture1.y); } if (mesh.texUVCount > 2) { uv2.Add(vtx.texture2.x); uv2.Add(vtx.texture2.y); } if (mesh.hasColor) { colors.Add(((vtx.diffuseColor >> 16) & 0xff) / 255f); colors.Add(((vtx.diffuseColor >> 8) & 0xff) / 255f); colors.Add((vtx.diffuseColor & 0xff) / 255f); colors.Add(((vtx.diffuseColor >> 24) & 0xff) / 255f); } } daeSource position = new daeSource(); position.name = meshName + "_position"; position.id = position.name + "_id"; position.float_array = new daeFloatArray(); position.float_array.id = position.name + "_array_id"; position.float_array.set(positions); position.technique_common.accessor.source = "#" + position.float_array.id; position.technique_common.accessor.count = (uint)mesh.vertices.Count; position.technique_common.accessor.stride = 3; position.technique_common.accessor.addParam("X", "float"); position.technique_common.accessor.addParam("Y", "float"); position.technique_common.accessor.addParam("Z", "float"); geometry.mesh.source.Add(position); daeSource normal = new daeSource(); if (mesh.hasNormal) { normal.name = meshName + "_normal"; normal.id = normal.name + "_id"; normal.float_array = new daeFloatArray(); normal.float_array.id = normal.name + "_array_id"; normal.float_array.set(normals); normal.technique_common.accessor.source = "#" + normal.float_array.id; normal.technique_common.accessor.count = (uint)mesh.vertices.Count; normal.technique_common.accessor.stride = 3; normal.technique_common.accessor.addParam("X", "float"); normal.technique_common.accessor.addParam("Y", "float"); normal.technique_common.accessor.addParam("Z", "float"); geometry.mesh.source.Add(normal); } daeSource[] texUV = new daeSource[3]; for (int i = 0; i < mesh.texUVCount; i++) { texUV[i] = new daeSource(); texUV[i].name = meshName + "_uv" + i; texUV[i].id = texUV[i].name + "_id"; texUV[i].float_array = new daeFloatArray(); texUV[i].float_array.id = texUV[i].name + "_array_id"; texUV[i].technique_common.accessor.source = "#" + texUV[i].float_array.id; texUV[i].technique_common.accessor.count = (uint)mesh.vertices.Count; texUV[i].technique_common.accessor.stride = 2; texUV[i].technique_common.accessor.addParam("S", "float"); texUV[i].technique_common.accessor.addParam("T", "float"); geometry.mesh.source.Add(texUV[i]); } daeSource color = new daeSource(); if (mesh.hasColor) { color.name = meshName + "_color"; color.id = color.name + "_id"; color.float_array = new daeFloatArray(); color.float_array.id = color.name + "_array_id"; color.float_array.set(colors); color.technique_common.accessor.source = "#" + color.float_array.id; color.technique_common.accessor.count = (uint)mesh.vertices.Count; color.technique_common.accessor.stride = 4; color.technique_common.accessor.addParam("R", "float"); color.technique_common.accessor.addParam("G", "float"); color.technique_common.accessor.addParam("B", "float"); color.technique_common.accessor.addParam("A", "float"); geometry.mesh.source.Add(color); } geometry.mesh.vertices.id = meshName + "_vertices_id"; geometry.mesh.vertices.addInput("POSITION", "#" + position.id); geometry.mesh.triangles.material = mdl.material[obj.materialId].name + "_mat"; geometry.mesh.triangles.addInput("VERTEX", "#" + geometry.mesh.vertices.id); if (mesh.hasNormal) geometry.mesh.triangles.addInput("NORMAL", "#" + normal.id); if (mesh.hasColor) geometry.mesh.triangles.addInput("COLOR", "#" + color.id); if (mesh.texUVCount > 0) { texUV[0].float_array.set(uv0); geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[0].id); } if (mesh.texUVCount > 1) { texUV[1].float_array.set(uv1); geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[1].id, 0, 1); } if (mesh.texUVCount > 2) { texUV[2].float_array.set(uv2); geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[2].id, 0, 2); } geometry.mesh.triangles.set(mesh.indices); dae.library_geometries.Add(geometry); bool hasNode = obj.vertices[0].node.Count > 0; bool hasWeight = obj.vertices[0].weight.Count > 0; bool hasController = hasNode && hasWeight; //Controller daeController controller = new daeController(); if (hasController) { controller.id = meshName + "_ctrl_id"; controller.skin.source = "#" + geometry.id; controller.skin.bind_shape_matrix.set(new RenderBase.OMatrix()); daeSource joints = new daeSource(); joints.id = meshName + "_ctrl_joint_names_id"; joints.Name_array = new daeNameArray(); joints.Name_array.id = meshName + "_ctrl_joint_names_array_id"; joints.Name_array.count = (uint)mdl.skeleton.Count; joints.Name_array.data = jointNames; joints.technique_common.accessor.source = "#" + joints.Name_array.id; joints.technique_common.accessor.count = joints.Name_array.count; joints.technique_common.accessor.stride = 1; joints.technique_common.accessor.addParam("JOINT", "Name"); controller.skin.src.Add(joints); daeSource bindPoses = new daeSource(); bindPoses.id = meshName + "_ctrl_inv_bind_poses_id"; bindPoses.float_array = new daeFloatArray(); bindPoses.float_array.id = meshName + "_ctrl_inv_bind_poses_array_id"; bindPoses.float_array.count = (uint)(mdl.skeleton.Count * 16); bindPoses.float_array.data = invBindPoses; bindPoses.technique_common.accessor.source = "#" + bindPoses.float_array.id; bindPoses.technique_common.accessor.count = (uint)mdl.skeleton.Count; bindPoses.technique_common.accessor.stride = 16; bindPoses.technique_common.accessor.addParam("TRANSFORM", "float4x4"); controller.skin.src.Add(bindPoses); daeSource weights = new daeSource(); weights.id = meshName + "_ctrl_weights_id"; weights.float_array = new daeFloatArray(); weights.float_array.id = meshName + "_ctrl_weights_array_id"; weights.technique_common.accessor.source = "#" + weights.float_array.id; weights.technique_common.accessor.stride = 1; weights.technique_common.accessor.addParam("WEIGHT", "float"); StringBuilder w = new StringBuilder(); StringBuilder vcount = new StringBuilder(); StringBuilder v = new StringBuilder(); float[] wLookBack = new float[32]; uint wLookBackIndex = 0; int buffLen = 0; int wIndex = 0; int wCount = 0; foreach (RenderBase.OVertex vtx in mesh.vertices) { int count = Math.Min(vtx.node.Count, vtx.weight.Count); vcount.Append(count + " "); for (int n = 0; n < count; n++) { v.Append(vtx.node[n] + " "); bool found = false; uint bPos = (wLookBackIndex - 1) & 0x1f; for (int i = 0; i < buffLen; i++) { if (wLookBack[bPos] == vtx.weight[n]) { v.Append(wIndex - (i + 1) + " "); found = true; break; } bPos = (bPos - 1) & 0x1f; } if (!found) { v.Append(wIndex++ + " "); w.Append(vtx.weight[n].ToString(CultureInfo.InvariantCulture) + " "); wCount++; wLookBack[wLookBackIndex] = vtx.weight[n]; wLookBackIndex = (wLookBackIndex + 1) & 0x1f; if (buffLen < wLookBack.Length) buffLen++; } } } weights.float_array.data = w.ToString().TrimEnd(); weights.float_array.count = (uint)wCount; weights.technique_common.accessor.count = (uint)wCount; controller.skin.src.Add(weights); controller.skin.vertex_weights.vcount = vcount.ToString().TrimEnd(); controller.skin.vertex_weights.v = v.ToString().TrimEnd(); controller.skin.vertex_weights.count = (uint)mesh.vertices.Count; controller.skin.joints.addInput("JOINT", "#" + joints.id); controller.skin.joints.addInput("INV_BIND_MATRIX", "#" + bindPoses.id); controller.skin.vertex_weights.addInput("JOINT", "#" + joints.id); controller.skin.vertex_weights.addInput("WEIGHT", "#" + weights.id, 1); if (dae.library_controllers == null) dae.library_controllers = new List<daeController>(); dae.library_controllers.Add(controller); } //Visual scene node daeNode node = new daeNode(); node.name = "vsn_" + meshName; node.id = node.name + "_id"; node.matrix.set(new RenderBase.OMatrix()); if (hasController) { node.instance_controller = new daeInstanceController(); node.instance_controller.url = "#" + controller.id; node.instance_controller.skeleton = "#" + mdl.skeleton[0].name + "_bone_id"; node.instance_controller.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name + "_mat"; node.instance_controller.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id"; } else { node.instance_geometry = new daeInstanceGeometry(); node.instance_geometry.url = "#" + geometry.id; node.instance_geometry.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name + "_mat"; node.instance_geometry.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id"; } vs.node.Add(node); } dae.library_visual_scenes.Add(vs); daeInstaceVisualScene scene = new daeInstaceVisualScene(); scene.url = "#" + vs.id; dae.scene.Add(scene); XmlWriterSettings settings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true }; XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", "http://www.collada.org/2005/11/COLLADASchema"); XmlSerializer serializer = new XmlSerializer(typeof(COLLADA)); XmlWriter output = XmlWriter.Create(new FileStream(fileName.Replace(".bch", ".dae").Replace("BCH", "DAE"), FileMode.Create), settings); serializer.Serialize(output, dae, ns); output.Close(); #endregion #region Shiny if (hasShiny) { currentMat = 0; foreach (RenderBase.OMaterial mat in mdl.material) { if (mat.name0 == null) { mat.name0 = mat.name; } mat.name += "_shiny"; mat.name0 += "_shiny"; mdl.material[currentMat].name = mat.name; mdl.material[currentMat].name0 = mat.name0; currentMat++; daeMaterial mtl = new daeMaterial(); mtl.name = mat.name + "_mat"; mtl.id = mtl.name + "_id"; mtl.instance_effect.url = "#eff_" + mtl.id; daeShiny.library_materials.Add(mtl); daeEffect eff = new daeEffect(); eff.id = "eff_" + mtl.id; eff.name = "eff_" + mtl.name; daeParam surface = new daeParam(); surface.surface = new daeParamSurfaceElement(); surface.sid = "img_surface"; surface.surface.type = "2D"; surface.surface.init_from = mat.name0 + "_id"; surface.surface.format = "PNG"; eff.profile_COMMON.newparam.Add(surface); bool fire = false; for (int i = 0; i < daeShiny.library_images.Count; i++) { if (daeShiny.library_images[i].init_from.Contains(mat.name) || daeShiny.library_images[i].id.Contains(mat.name) || daeShiny.library_images[i].init_from.Contains(mat.name0) || daeShiny.library_images[i].id.Contains(mat.name0)) { if (daeShiny.library_images[i].init_from.Contains("Nor") == false) { if (mat.name.Contains("Fire")) { fire = true; daeShiny.library_images[i].name = daeShiny.library_images[i].name.Replace("Mask", "1"); daeShiny.library_images[i].id = daeShiny.library_images[i].id.Replace("Mask", "1"); daeShiny.library_images[i].init_from = daeShiny.library_images[i].init_from.Replace("Mask", "1"); mat.name = mat.name.Replace("Mask", "1"); mat.name0 = mat.name0.Replace("Mask", "1"); } surface.surface.init_from = daeShiny.library_images[i].id; } } } if (mat.name.Contains("Fire")) { fire = true; } daeParam sampler = new daeParam(); sampler.sampler2D = new daeParamSampler2DElement(); sampler.sid = "img_sampler"; sampler.sampler2D.source = "img_surface"; switch (mat.textureMapper[0].wrapU) { case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_s = "WRAP"; break; case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_s = "MIRROR"; break; case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_s = "CLAMP"; break; case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_s = "BORDER"; break; default: sampler.sampler2D.wrap_s = "WRAP"; break; } switch (mat.textureMapper[0].wrapV) { case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_t = "WRAP"; break; case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_t = "MIRROR"; break; case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_t = "CLAMP"; break; case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_t = "BORDER"; break; default: sampler.sampler2D.wrap_t = "WRAP"; break; } switch (mat.textureMapper[0].minFilter) { case RenderBase.OTextureMinFilter.linearMipmapLinear: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_LINEAR"; break; case RenderBase.OTextureMinFilter.linearMipmapNearest: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_NEAREST"; break; case RenderBase.OTextureMinFilter.nearestMipmapLinear: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_LINEAR"; break; case RenderBase.OTextureMinFilter.nearestMipmapNearest: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_NEAREST"; break; default: sampler.sampler2D.minfilter = "NONE"; break; } switch (mat.textureMapper[0].magFilter) { case RenderBase.OTextureMagFilter.linear: sampler.sampler2D.magfilter = "LINEAR"; break; case RenderBase.OTextureMagFilter.nearest: sampler.sampler2D.magfilter = "NEAREST"; break; default: sampler.sampler2D.magfilter = "NONE"; break; } sampler.sampler2D.mipfilter = sampler.sampler2D.magfilter; eff.profile_COMMON.newparam.Add(sampler); eff.profile_COMMON.technique.sid = "img_technique"; eff.profile_COMMON.technique.phong.emission.set(Color.Black); eff.profile_COMMON.technique.phong.ambient.set(Color.Black); eff.profile_COMMON.technique.phong.specular.set(Color.White); eff.profile_COMMON.technique.phong.diffuse.texture.texture = "img_sampler"; eff.profile_COMMON.technique.phong.transparency = new daePhongTransparent(); if (fire) { eff.profile_COMMON.technique.phong.transparency.value = "0.1"; } else { eff.profile_COMMON.technique.phong.transparency.value = "1.0"; } daeShiny.library_effects.Add(eff); } jointNames = null; invBindPoses = null; for (int index = 0; index < mdl.skeleton.Count; index++) { RenderBase.OMatrix transform = new RenderBase.OMatrix(); transformSkeleton(mdl.skeleton, index, ref transform); jointNames += mdl.skeleton[index].name; daeMatrix mtx = new daeMatrix(); mtx.set(transform.invert()); invBindPoses += mtx.data; if (index < mdl.skeleton.Count - 1) { jointNames += " "; invBindPoses += " "; } } meshIndex = 0; vs = new daeVisualScene(); vs.name = "vs_" + mdl.name; vs.id = vs.name + "_id"; if (mdl.skeleton.Count > 0) writeSkeleton(mdl.skeleton, 0, ref vs.node); rightIris = false; foreach (RenderBase.OMesh obj in mdl.mesh) { float largestUV = 0.0f; //Geometry daeGeometry geometry = new daeGeometry(); string meshName = "mesh_" + meshIndex++ + "_" + obj.name; geometry.id = meshName + "_id"; geometry.name = meshName; MeshUtils.optimizedMesh mesh = MeshUtils.optimizeMesh(obj); List<float> positions = new List<float>(); List<float> normals = new List<float>(); List<float> uv0 = new List<float>(); List<float> uv1 = new List<float>(); List<float> uv2 = new List<float>(); List<float> colors = new List<float>(); if (obj.name.Contains("Iris")) { foreach (RenderBase.OVertex vtx in mesh.vertices) { if (mesh.texUVCount > 0) { if (vtx.texture0.x > largestUV) { largestUV = vtx.texture0.x; } } } foreach (RenderBase.OVertex vtx in mesh.vertices) { if (mesh.texUVCount > 0) { vtx.texture0.x = largestUV - vtx.texture0.x; if (rightIris) { vtx.texture0.x = 1.25f - vtx.texture0.x; } } if (mesh.texUVCount > 1) { vtx.texture1.x = largestUV - vtx.texture1.x; if (rightIris) { vtx.texture1.x = 1.25f - vtx.texture1.x; } } if (mesh.texUVCount > 2) { vtx.texture2.x = largestUV - vtx.texture2.x; if (rightIris) { vtx.texture2.x = 1.25f - vtx.texture2.x; } } } rightIris = !rightIris; } else if (obj.name.Contains("Eye")) { foreach (RenderBase.OVertex vtx in mesh.vertices) { if (mesh.texUVCount > 0) { if (vtx.texture0.x > largestUV) { largestUV = vtx.texture0.x; } } } foreach (RenderBase.OVertex vtx in mesh.vertices) { if (mesh.texUVCount > 0) { vtx.texture0.x = largestUV - vtx.texture0.x; } if (mesh.texUVCount > 1) { vtx.texture1.x = largestUV - vtx.texture1.x; } if (mesh.texUVCount > 2) { vtx.texture2.x = largestUV - vtx.texture2.x; } } } foreach (RenderBase.OVertex vtx in mesh.vertices) { positions.Add(vtx.position.x); positions.Add(vtx.position.y); positions.Add(vtx.position.z); if (mesh.hasNormal) { normals.Add(vtx.normal.x); normals.Add(vtx.normal.y); normals.Add(vtx.normal.z); } if (mesh.texUVCount > 0) { uv0.Add(vtx.texture0.x); uv0.Add(vtx.texture0.y); } if (mesh.texUVCount > 1) { uv1.Add(vtx.texture1.x); uv1.Add(vtx.texture1.y); } if (mesh.texUVCount > 2) { uv2.Add(vtx.texture2.x); uv2.Add(vtx.texture2.y); } if (mesh.hasColor) { colors.Add(((vtx.diffuseColor >> 16) & 0xff) / 255f); colors.Add(((vtx.diffuseColor >> 8) & 0xff) / 255f); colors.Add((vtx.diffuseColor & 0xff) / 255f); colors.Add(((vtx.diffuseColor >> 24) & 0xff) / 255f); } } daeSource position = new daeSource(); position.name = meshName + "_position"; position.id = position.name + "_id"; position.float_array = new daeFloatArray(); position.float_array.id = position.name + "_array_id"; position.float_array.set(positions); position.technique_common.accessor.source = "#" + position.float_array.id; position.technique_common.accessor.count = (uint)mesh.vertices.Count; position.technique_common.accessor.stride = 3; position.technique_common.accessor.addParam("X", "float"); position.technique_common.accessor.addParam("Y", "float"); position.technique_common.accessor.addParam("Z", "float"); geometry.mesh.source.Add(position); daeSource normal = new daeSource(); if (mesh.hasNormal) { normal.name = meshName + "_normal"; normal.id = normal.name + "_id"; normal.float_array = new daeFloatArray(); normal.float_array.id = normal.name + "_array_id"; normal.float_array.set(normals); normal.technique_common.accessor.source = "#" + normal.float_array.id; normal.technique_common.accessor.count = (uint)mesh.vertices.Count; normal.technique_common.accessor.stride = 3; normal.technique_common.accessor.addParam("X", "float"); normal.technique_common.accessor.addParam("Y", "float"); normal.technique_common.accessor.addParam("Z", "float"); geometry.mesh.source.Add(normal); } daeSource[] texUV = new daeSource[3]; for (int i = 0; i < mesh.texUVCount; i++) { texUV[i] = new daeSource(); texUV[i].name = meshName + "_uv" + i; texUV[i].id = texUV[i].name + "_id"; texUV[i].float_array = new daeFloatArray(); texUV[i].float_array.id = texUV[i].name + "_array_id"; texUV[i].technique_common.accessor.source = "#" + texUV[i].float_array.id; texUV[i].technique_common.accessor.count = (uint)mesh.vertices.Count; texUV[i].technique_common.accessor.stride = 2; texUV[i].technique_common.accessor.addParam("S", "float"); texUV[i].technique_common.accessor.addParam("T", "float"); geometry.mesh.source.Add(texUV[i]); } daeSource color = new daeSource(); if (mesh.hasColor) { color.name = meshName + "_color"; color.id = color.name + "_id"; color.float_array = new daeFloatArray(); color.float_array.id = color.name + "_array_id"; color.float_array.set(colors); color.technique_common.accessor.source = "#" + color.float_array.id; color.technique_common.accessor.count = (uint)mesh.vertices.Count; color.technique_common.accessor.stride = 4; color.technique_common.accessor.addParam("R", "float"); color.technique_common.accessor.addParam("G", "float"); color.technique_common.accessor.addParam("B", "float"); color.technique_common.accessor.addParam("A", "float"); geometry.mesh.source.Add(color); } geometry.mesh.vertices.id = meshName + "_vertices_id"; geometry.mesh.vertices.addInput("POSITION", "#" + position.id); geometry.mesh.triangles.material = mdl.material[obj.materialId].name + "_mat"; geometry.mesh.triangles.addInput("VERTEX", "#" + geometry.mesh.vertices.id); if (mesh.hasNormal) geometry.mesh.triangles.addInput("NORMAL", "#" + normal.id); if (mesh.hasColor) geometry.mesh.triangles.addInput("COLOR", "#" + color.id); if (mesh.texUVCount > 0) { texUV[0].float_array.set(uv0); geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[0].id); } if (mesh.texUVCount > 1) { texUV[1].float_array.set(uv1); geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[1].id, 0, 1); } if (mesh.texUVCount > 2) { texUV[2].float_array.set(uv2); geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[2].id, 0, 2); } geometry.mesh.triangles.set(mesh.indices); daeShiny.library_geometries.Add(geometry); bool hasNode = obj.vertices[0].node.Count > 0; bool hasWeight = obj.vertices[0].weight.Count > 0; bool hasController = hasNode && hasWeight; //Controller daeController controller = new daeController(); if (hasController) { controller.id = meshName + "_ctrl_id"; controller.skin.source = "#" + geometry.id; controller.skin.bind_shape_matrix.set(new RenderBase.OMatrix()); daeSource joints = new daeSource(); joints.id = meshName + "_ctrl_joint_names_id"; joints.Name_array = new daeNameArray(); joints.Name_array.id = meshName + "_ctrl_joint_names_array_id"; joints.Name_array.count = (uint)mdl.skeleton.Count; joints.Name_array.data = jointNames; joints.technique_common.accessor.source = "#" + joints.Name_array.id; joints.technique_common.accessor.count = joints.Name_array.count; joints.technique_common.accessor.stride = 1; joints.technique_common.accessor.addParam("JOINT", "Name"); controller.skin.src.Add(joints); daeSource bindPoses = new daeSource(); bindPoses.id = meshName + "_ctrl_inv_bind_poses_id"; bindPoses.float_array = new daeFloatArray(); bindPoses.float_array.id = meshName + "_ctrl_inv_bind_poses_array_id"; bindPoses.float_array.count = (uint)(mdl.skeleton.Count * 16); bindPoses.float_array.data = invBindPoses; bindPoses.technique_common.accessor.source = "#" + bindPoses.float_array.id; bindPoses.technique_common.accessor.count = (uint)mdl.skeleton.Count; bindPoses.technique_common.accessor.stride = 16; bindPoses.technique_common.accessor.addParam("TRANSFORM", "float4x4"); controller.skin.src.Add(bindPoses); daeSource weights = new daeSource(); weights.id = meshName + "_ctrl_weights_id"; weights.float_array = new daeFloatArray(); weights.float_array.id = meshName + "_ctrl_weights_array_id"; weights.technique_common.accessor.source = "#" + weights.float_array.id; weights.technique_common.accessor.stride = 1; weights.technique_common.accessor.addParam("WEIGHT", "float"); StringBuilder w = new StringBuilder(); StringBuilder vcount = new StringBuilder(); StringBuilder v = new StringBuilder(); float[] wLookBack = new float[32]; uint wLookBackIndex = 0; int buffLen = 0; int wIndex = 0; int wCount = 0; foreach (RenderBase.OVertex vtx in mesh.vertices) { int count = Math.Min(vtx.node.Count, vtx.weight.Count); vcount.Append(count + " "); for (int n = 0; n < count; n++) { v.Append(vtx.node[n] + " "); bool found = false; uint bPos = (wLookBackIndex - 1) & 0x1f; for (int i = 0; i < buffLen; i++) { if (wLookBack[bPos] == vtx.weight[n]) { v.Append(wIndex - (i + 1) + " "); found = true; break; } bPos = (bPos - 1) & 0x1f; } if (!found) { v.Append(wIndex++ + " "); w.Append(vtx.weight[n].ToString(CultureInfo.InvariantCulture) + " "); wCount++; wLookBack[wLookBackIndex] = vtx.weight[n]; wLookBackIndex = (wLookBackIndex + 1) & 0x1f; if (buffLen < wLookBack.Length) buffLen++; } } } weights.float_array.data = w.ToString().TrimEnd(); weights.float_array.count = (uint)wCount; weights.technique_common.accessor.count = (uint)wCount; controller.skin.src.Add(weights); controller.skin.vertex_weights.vcount = vcount.ToString().TrimEnd(); controller.skin.vertex_weights.v = v.ToString().TrimEnd(); controller.skin.vertex_weights.count = (uint)mesh.vertices.Count; controller.skin.joints.addInput("JOINT", "#" + joints.id); controller.skin.joints.addInput("INV_BIND_MATRIX", "#" + bindPoses.id); controller.skin.vertex_weights.addInput("JOINT", "#" + joints.id); controller.skin.vertex_weights.addInput("WEIGHT", "#" + weights.id, 1); if (daeShiny.library_controllers == null) daeShiny.library_controllers = new List<daeController>(); daeShiny.library_controllers.Add(controller); } //Visual scene node daeNode node = new daeNode(); node.name = "vsn_" + meshName; node.id = node.name + "_id"; node.matrix.set(new RenderBase.OMatrix()); if (hasController) { node.instance_controller = new daeInstanceController(); node.instance_controller.url = "#" + controller.id; node.instance_controller.skeleton = "#" + mdl.skeleton[0].name + "_bone_id"; node.instance_controller.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name + "_mat"; node.instance_controller.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id"; } else { node.instance_geometry = new daeInstanceGeometry(); node.instance_geometry.url = "#" + geometry.id; node.instance_geometry.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name + "_mat"; node.instance_geometry.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id"; } vs.node.Add(node); } daeShiny.library_visual_scenes.Add(vs); scene = new daeInstaceVisualScene(); scene.url = "#" + vs.id; daeShiny.scene.Add(scene); settings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true }; ns = new XmlSerializerNamespaces(); ns.Add("", "http://www.collada.org/2005/11/COLLADASchema"); serializer = new XmlSerializer(typeof(COLLADA)); output = XmlWriter.Create(new FileStream(fileName.Replace(".bch", "_Shiny.dae").Replace("BCH", "DAE"), FileMode.Create), settings); serializer.Serialize(output, daeShiny, ns); output.Close(); } #endregion }
public RenderBase.OMatrix get() { RenderBase.OMatrix output = new RenderBase.OMatrix(); string[] values = data.Split(Convert.ToChar(" ")); int k = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { output[j, i] = float.Parse(values[k++]); } } return output; }
/// <summary> /// Writes the skeleton hierarchy to the DAE. /// </summary> /// <param name="skeleton">The skeleton</param> /// <param name="index">Index of the current bone (root bone when it's not a recursive call)</param> /// <param name="nodes">List with the DAE nodes</param> private static void writeSkeleton(List<RenderBase.OBone> skeleton, int index, ref List<daeNode> nodes) { daeNode node = new daeNode(); node.name = skeleton[index].name; node.id = node.name + "_bone_id"; node.sid = node.name; node.type = "JOINT"; RenderBase.OMatrix transform = new RenderBase.OMatrix(); transform *= RenderBase.OMatrix.rotateX(skeleton[index].rotation.x); transform *= RenderBase.OMatrix.rotateY(skeleton[index].rotation.y); transform *= RenderBase.OMatrix.rotateZ(skeleton[index].rotation.z); transform *= RenderBase.OMatrix.translate(skeleton[index].translation); node.matrix.set(transform); for (int i = 0; i < skeleton.Count; i++) { if (skeleton[i].parentId == index) { if (node.childs == null) node.childs = new List<daeNode>(); writeSkeleton(skeleton, i, ref node.childs); } } nodes.Add(node); }
/// <summary> /// Reads a 4x3 Matrix from the file. /// </summary> /// <param name="input">BinaryReader of the CGFX file</param> /// <returns></returns> private static RenderBase.OMatrix getMatrix(BinaryReader input) { RenderBase.OMatrix output = new RenderBase.OMatrix(); output.M11 = input.ReadSingle(); output.M21 = input.ReadSingle(); output.M31 = input.ReadSingle(); output.M41 = input.ReadSingle(); output.M12 = input.ReadSingle(); output.M22 = input.ReadSingle(); output.M32 = input.ReadSingle(); output.M42 = input.ReadSingle(); output.M13 = input.ReadSingle(); output.M23 = input.ReadSingle(); output.M33 = input.ReadSingle(); output.M43 = input.ReadSingle(); return output; }
/// <summary> /// Loads a CGFX file. /// Note that CGFX must start at offset 0x0 (don't try using it for CGFXs inside containers). /// </summary> /// <param name="data">Stream of the CGFX file.</param> /// <returns></returns> public static RenderBase.OModelGroup load(Stream data) { BinaryReader input = new BinaryReader(data); RenderBase.OModelGroup models = new RenderBase.OModelGroup(); cgfxHeader header = new cgfxHeader(); header.magic = IOUtils.readString(input, 0, 4); header.endian = input.ReadUInt16(); header.length = input.ReadUInt16(); header.revision = input.ReadUInt32(); header.fileLength = input.ReadUInt32(); header.entries = input.ReadUInt32(); data.Seek(header.length, SeekOrigin.Begin); dataHeader dataHeader = new dataHeader(); dataHeader.magic = IOUtils.readString(input, (uint)data.Position, 4); dataHeader.length = input.ReadUInt32(); dataHeader.models = getDictionary(input); dataHeader.textures = getDictionary(input); dataHeader.lookUpTables = getDictionary(input); dataHeader.materials = getDictionary(input); dataHeader.shaders = getDictionary(input); dataHeader.cameras = getDictionary(input); dataHeader.lights = getDictionary(input); dataHeader.fogs = getDictionary(input); dataHeader.scenes = getDictionary(input); dataHeader.skeletalAnimations = getDictionary(input); dataHeader.materialAnimations = getDictionary(input); dataHeader.visibilityAnimations = getDictionary(input); dataHeader.cameraAnimations = getDictionary(input); dataHeader.lightAnimations = getDictionary(input); dataHeader.emitters = getDictionary(input); //Textures foreach (dictEntry textureEntry in dataHeader.textures) { data.Seek(textureEntry.dataOffset, SeekOrigin.Begin); uint type = input.ReadUInt32(); string txobMagic = IOUtils.readString(input, (uint)data.Position, 4); uint revision = input.ReadUInt32(); string name = IOUtils.readString(input, getRelativeOffset(input)); uint userDataEntries = input.ReadUInt32(); uint userDataOffset = getRelativeOffset(input); int height = (int)input.ReadUInt32(); int width = (int)input.ReadUInt32(); uint openGLFormat = input.ReadUInt32(); uint openGLType = input.ReadUInt32(); uint mipmapLevels = input.ReadUInt32(); uint textureObject = input.ReadUInt32(); uint locationFlags = input.ReadUInt32(); RenderBase.OTextureFormat format = (RenderBase.OTextureFormat)input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); uint dataLength = input.ReadUInt32(); uint dataOffset = getRelativeOffset(input); uint dynamicAllocator = input.ReadUInt32(); uint bitsPerPixel = input.ReadUInt32(); uint locationAddress = input.ReadUInt32(); uint memoryAddress = input.ReadUInt32(); byte[] buffer = new byte[dataLength]; data.Seek(dataOffset, SeekOrigin.Begin); input.Read(buffer, 0, buffer.Length); models.texture.Add(new RenderBase.OTexture(TextureCodec.decode(buffer, width, height, format), name)); } //Skeletal animations foreach (dictEntry skeletalAnimationEntry in dataHeader.skeletalAnimations) { data.Seek(skeletalAnimationEntry.dataOffset, SeekOrigin.Begin); RenderBase.OSkeletalAnimation skeletalAnimation = new RenderBase.OSkeletalAnimation(); string canmMagic = IOUtils.readString(input, (uint)input.BaseStream.Position, 4); uint revision = input.ReadUInt32(); skeletalAnimation.name = IOUtils.readString(input, getRelativeOffset(input)); string targetAnimationGroupName = IOUtils.readString(input, getRelativeOffset(input)); skeletalAnimation.loopMode = (RenderBase.OLoopMode)input.ReadUInt32(); skeletalAnimation.frameSize = input.ReadSingle(); List<dictEntry> memberAnimationDataDictionary = getDictionary(input); uint userDataEntries = input.ReadUInt32(); uint userDataOffset = getRelativeOffset(input); foreach (dictEntry entry in memberAnimationDataDictionary) { RenderBase.OSkeletalAnimationBone bone = new RenderBase.OSkeletalAnimationBone(); bone.name = IOUtils.readString(input, entry.nameOffset); data.Seek(entry.dataOffset, SeekOrigin.Begin); uint boneFlags = input.ReadUInt32(); string bonePath = IOUtils.readString(input, getRelativeOffset(input)); if ((revision >> 24) < 7) data.Seek(8, SeekOrigin.Current); cgfxSegmentType segmentType = (cgfxSegmentType)input.ReadUInt32(); switch (segmentType) { case cgfxSegmentType.transform: data.Seek(0xc, SeekOrigin.Current); uint notExistMask = 0x80000; uint constantMask = 0x200; for (int j = 0; j < 2; j++) { for (int axis = 0; axis < 3; axis++) { bool notExist = (boneFlags & notExistMask) > 0; bool constant = (boneFlags & constantMask) > 0; RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); frame.exists = !notExist; if (frame.exists) { if (constant) { frame.interpolation = RenderBase.OInterpolationMode.linear; frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0)); } else { uint frameOffset = getRelativeOffset(input); long position = data.Position; data.Seek(frameOffset, SeekOrigin.Begin); getAnimationKeyFrame(input, frame); data.Seek(position, SeekOrigin.Begin); } } else data.Seek(4, SeekOrigin.Current); if (j == 0) { switch (axis) { case 0: bone.rotationX = frame; break; case 1: bone.rotationY = frame; break; case 2: bone.rotationZ = frame; break; } } else { switch (axis) { case 0: bone.translationX = frame; break; case 1: bone.translationY = frame; break; case 2: bone.translationZ = frame; break; } } notExistMask <<= 1; constantMask <<= 1; } notExistMask <<= 1; constantMask <<= 1; data.Seek(4, SeekOrigin.Current); } break; case cgfxSegmentType.transformQuaternion: bone.isFrameFormat = true; uint rotationOffset = getRelativeOffset(input); uint translationOffset = getRelativeOffset(input); uint scaleOffset = getRelativeOffset(input); if ((boneFlags & 0x10) == 0) { bone.rotationQuaternion.exists = true; data.Seek(rotationOffset, SeekOrigin.Begin); bone.rotationQuaternion.startFrame = input.ReadSingle(); bone.rotationQuaternion.endFrame = input.ReadSingle(); input.ReadUInt32(); uint constantFlags = input.ReadUInt32(); if ((constantFlags & 1) > 0) { bone.rotationQuaternion.vector.Add(new RenderBase.OVector4( input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle())); } else { uint rotationEntries = (uint)(bone.rotationQuaternion.endFrame - bone.rotationQuaternion.startFrame); for (int j = 0; j < rotationEntries; j++) { bone.rotationQuaternion.vector.Add(new RenderBase.OVector4( input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle())); uint flags = input.ReadUInt32(); } } } else data.Seek(4, SeekOrigin.Current); if ((boneFlags & 8) == 0) { bone.translation.exists = true; data.Seek(translationOffset, SeekOrigin.Begin); bone.translation.startFrame = input.ReadSingle(); bone.translation.endFrame = input.ReadSingle(); input.ReadUInt32(); uint constantFlags = input.ReadUInt32(); if ((constantFlags & 1) > 0) { bone.translation.vector.Add(new RenderBase.OVector4( input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), 0)); } else { uint translationEntries = (uint)(bone.rotationQuaternion.endFrame - bone.rotationQuaternion.startFrame); for (int j = 0; j < translationEntries; j++) { bone.translation.vector.Add(new RenderBase.OVector4( input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), 0)); uint flags = input.ReadUInt32(); } } } else data.Seek(4, SeekOrigin.Current); break; } skeletalAnimation.bone.Add(bone); } models.skeletalAnimation.list.Add(skeletalAnimation); } //Models foreach (dictEntry modelEntry in dataHeader.models) { data.Seek(modelEntry.dataOffset, SeekOrigin.Begin); cmdlHeader cmdlHeader = new cmdlHeader(); uint flags = input.ReadUInt32(); cmdlHeader.hasSkeleton = (flags & 0x80) > 0; string cmdlMagic = IOUtils.readString(input, (uint)input.BaseStream.Position, 4); uint revision = input.ReadUInt32(); cmdlHeader.modelName = IOUtils.readString(input, getRelativeOffset(input)); cmdlHeader.userDataEntries = input.ReadUInt32(); cmdlHeader.userDataDictionaryOffset = getRelativeOffset(input); input.ReadUInt32(); flags = input.ReadUInt32(); cmdlHeader.isBranchVisible = (flags & 1) > 0; cmdlHeader.childCount = input.ReadUInt32(); input.ReadUInt32(); //Unused cmdlHeader.animationGroupEntries = input.ReadUInt32(); cmdlHeader.animationGroupDictionaryOffset = getRelativeOffset(input); cmdlHeader.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); cmdlHeader.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); cmdlHeader.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); cmdlHeader.localMatrix = getMatrix(input); cmdlHeader.worldMatrix = getMatrix(input); cmdlHeader.objectEntries = input.ReadUInt32(); cmdlHeader.objectPointerTableOffset = getRelativeOffset(input); cmdlHeader.materials = getDictionary(input); cmdlHeader.shapeEntries = input.ReadUInt32(); cmdlHeader.shapePointerTableOffset = getRelativeOffset(input); cmdlHeader.objectNodes = getDictionary(input); flags = input.ReadUInt32(); cmdlHeader.isVisible = (flags & 1) > 0; cmdlHeader.isNonUniformScalable = (flags & 0x100) > 0; cmdlHeader.cullMode = (RenderBase.OModelCullingMode)input.ReadUInt32(); cmdlHeader.layerId = input.ReadUInt32(); if (cmdlHeader.hasSkeleton) cmdlHeader.skeletonOffset = getRelativeOffset(input); RenderBase.OModel model = new RenderBase.OModel(); model.name = cmdlHeader.modelName; model.transform = cmdlHeader.worldMatrix; //Materials foreach (dictEntry materialEntry in cmdlHeader.materials) { data.Seek(materialEntry.dataOffset, SeekOrigin.Begin); RenderBase.OMaterial material = new RenderBase.OMaterial(); flags = input.ReadUInt32(); string mtobMagic = IOUtils.readString(input, (uint)input.BaseStream.Position, 4); revision = input.ReadUInt32(); material.name = IOUtils.readString(input, getRelativeOffset(input)); uint userDataEntries = input.ReadUInt32(); uint userDataOffset = getRelativeOffset(input); flags = input.ReadUInt32(); material.isFragmentLightEnabled = (flags & 1) > 0; material.isVertexLightEnabled = (flags & 2) > 0; material.isHemiSphereLightEnabled = (flags & 4) > 0; material.isHemiSphereOcclusionEnabled = (flags & 8) > 0; material.isFogEnabled = (flags & 0x10) > 0; material.rasterization.isPolygonOffsetEnabled = (flags & 0x20) > 0; uint textureCoordinatesConfig = input.ReadUInt32(); uint translucencyKind = input.ReadUInt32(); /* * Material color */ MeshUtils.getColorFloat(input); //Emission (stored as float4) MeshUtils.getColorFloat(input); //Ambient (stored as float4) MeshUtils.getColorFloat(input); //Diffuse (stored as float4) MeshUtils.getColorFloat(input); //Specular 0 (stored as float4) MeshUtils.getColorFloat(input); //Specular 1 (stored as float4) MeshUtils.getColorFloat(input); //Constant 0 (stored as float4) MeshUtils.getColorFloat(input); //Constant 1 (stored as float4) MeshUtils.getColorFloat(input); //Constant 2 (stored as float4) MeshUtils.getColorFloat(input); //Constant 3 (stored as float4) MeshUtils.getColorFloat(input); //Constant 4 (stored as float4) MeshUtils.getColorFloat(input); //Constant 5 (stored as float4) material.materialColor.emission = MeshUtils.getColor(input); material.materialColor.ambient = MeshUtils.getColor(input); material.materialColor.diffuse = MeshUtils.getColor(input); material.materialColor.specular0 = MeshUtils.getColor(input); material.materialColor.specular1 = MeshUtils.getColor(input); material.materialColor.constant0 = MeshUtils.getColor(input); material.materialColor.constant1 = MeshUtils.getColor(input); material.materialColor.constant2 = MeshUtils.getColor(input); material.materialColor.constant3 = MeshUtils.getColor(input); material.materialColor.constant4 = MeshUtils.getColor(input); material.materialColor.constant5 = MeshUtils.getColor(input); /* * Rasterization */ material.rasterization.isPolygonOffsetEnabled = (input.ReadUInt32() & 1) > 0; material.rasterization.cullMode = (RenderBase.OCullMode)input.ReadUInt32(); material.rasterization.polygonOffsetUnit = input.ReadSingle(); data.Seek(0xc, SeekOrigin.Current); /* * Fragment operation */ //Depth operation flags = input.ReadUInt32(); PICACommandReader depthCommands = new PICACommandReader(data, 4, true); material.fragmentOperation.depth = depthCommands.getDepthTest(); material.fragmentOperation.depth.isTestEnabled = (flags & 1) > 0; material.fragmentOperation.depth.isMaskEnabled = (flags & 2) > 0; //Blend operation RenderBase.OBlendMode blendMode = RenderBase.OBlendMode.notUsed; switch (input.ReadUInt32()) { case 0: blendMode = RenderBase.OBlendMode.notUsed; break; case 1: blendMode = RenderBase.OBlendMode.blend; break; case 2: blendMode = RenderBase.OBlendMode.blend; break; //Separate blend case 3: blendMode = RenderBase.OBlendMode.logical; break; } Color blendColor = MeshUtils.getColorFloat(input); PICACommandReader blendCommands = new PICACommandReader(data, 5, true); material.fragmentOperation.blend = blendCommands.getBlendOperation(); material.fragmentOperation.blend.mode = blendMode; material.fragmentOperation.blend.blendColor = blendColor; //Stencil operation input.ReadUInt32(); PICACommandReader stencilCommands = new PICACommandReader(data, 4, true); material.fragmentOperation.stencil = stencilCommands.getStencilTest(); /* * Texture coordinates */ uint usedTextureCoordinates = input.ReadUInt32(); for (int i = 0; i < 3; i++) { RenderBase.OTextureCoordinator coordinator = new RenderBase.OTextureCoordinator(); uint sourceCoordinate = input.ReadUInt32(); coordinator.projection = (RenderBase.OTextureProjection)input.ReadUInt32(); coordinator.referenceCamera = input.ReadUInt32(); uint matrixMode = input.ReadUInt32(); coordinator.scaleU = input.ReadSingle(); coordinator.scaleV = input.ReadSingle(); coordinator.rotate = input.ReadSingle(); coordinator.translateU = input.ReadSingle(); coordinator.translateV = input.ReadSingle(); bool isEnabled = (input.ReadUInt32() & 1) > 0; RenderBase.OMatrix transformMatrix = getMatrix(input); material.textureCoordinator[i] = coordinator; } /* * Texture mappers */ uint[] mapperOffsets = new uint[4]; mapperOffsets[0] = getRelativeOffset(input); mapperOffsets[1] = getRelativeOffset(input); mapperOffsets[2] = getRelativeOffset(input); mapperOffsets[3] = getRelativeOffset(input); long position = data.Position; for (int i = 0; i < 3; i++) { if (mapperOffsets[i] != 0) { data.Seek(mapperOffsets[i], SeekOrigin.Begin); flags = input.ReadUInt32(); uint dynamicAllocator = input.ReadUInt32(); uint textureHeaderOffset = getRelativeOffset(input); uint samplerOffset = getRelativeOffset(input); PICACommandReader textureCommands = new PICACommandReader(data, 13, true); switch (i) { case 0: material.textureMapper[i] = textureCommands.getTexUnit0Mapper(); material.textureMapper[i].borderColor = textureCommands.getTexUnit0BorderColor(); data.Seek(textureHeaderOffset + 0x18, SeekOrigin.Begin); material.name0 = IOUtils.readString(input, getRelativeOffset(input)); break; case 1: material.textureMapper[i] = textureCommands.getTexUnit1Mapper(); material.textureMapper[i].borderColor = textureCommands.getTexUnit1BorderColor(); data.Seek(textureHeaderOffset + 0x18, SeekOrigin.Begin); material.name1 = IOUtils.readString(input, getRelativeOffset(input)); break; case 2: material.textureMapper[i] = textureCommands.getTexUnit2Mapper(); material.textureMapper[i].borderColor = textureCommands.getTexUnit2BorderColor(); data.Seek(textureHeaderOffset + 0x18, SeekOrigin.Begin); material.name2 = IOUtils.readString(input, getRelativeOffset(input)); break; } data.Seek(samplerOffset, SeekOrigin.Begin); Color borderColor = MeshUtils.getColorFloat(input); //Not needed, we already got from Commands buffer material.textureMapper[i].LODBias = input.ReadSingle(); } } data.Seek(position, SeekOrigin.Begin); uint shaderOffset = getRelativeOffset(input); uint fragmentShaderOffset = getRelativeOffset(input); uint shaderProgramDescriptionIndex = input.ReadUInt32(); uint shaderParametersCount = input.ReadUInt32(); uint shaderParametersPointerTableOffset = getRelativeOffset(input); material.lightSetIndex = input.ReadUInt32(); material.fogIndex = input.ReadUInt32(); uint shadingParametersHash = input.ReadUInt32(); uint shaderParametersHash = input.ReadUInt32(); uint textureCoordinatorsHash = input.ReadUInt32(); uint textureSamplersHash = input.ReadUInt32(); uint textureMappersHash = input.ReadUInt32(); uint materialColorHash = input.ReadUInt32(); uint rasterizationHash = input.ReadUInt32(); uint fragmentLightingHash = input.ReadUInt32(); uint fragmentLightingTableHash = input.ReadUInt32(); uint fragmentLightingTableParametersHash = input.ReadUInt32(); uint textureCombinersHash = input.ReadUInt32(); uint alphaTestHash = input.ReadUInt32(); uint fragmentOperationHash = input.ReadUInt32(); uint materialId = input.ReadUInt32(); /* * Shader */ if (shaderOffset != 0) { data.Seek(shaderOffset, SeekOrigin.Begin); flags = input.ReadUInt32(); string shdrMagic = IOUtils.readString(input, (uint)data.Position, 4); revision = input.ReadUInt32(); string shaderName = IOUtils.readString(input, getRelativeOffset(input)); userDataEntries = input.ReadUInt32(); userDataOffset = getRelativeOffset(input); string referenceShaderName = IOUtils.readString(input, getRelativeOffset(input)); input.ReadUInt32(); material.shaderReference = new RenderBase.OReference(shaderName, referenceShaderName); } /* * Fragment shader */ if (fragmentShaderOffset != 0) { data.Seek(fragmentShaderOffset, SeekOrigin.Begin); material.fragmentShader.bufferColor = MeshUtils.getColorFloat(input); flags = input.ReadUInt32(); material.fragmentShader.lighting.isClampHighLight = (flags & 1) > 0; material.fragmentShader.lighting.isDistribution0Enabled = (flags & 2) > 0; material.fragmentShader.lighting.isDistribution1Enabled = (flags & 4) > 0; material.fragmentShader.lighting.isGeometryFactor0Enabled = (flags & 8) > 0; material.fragmentShader.lighting.isGeometryFactor1Enabled = (flags & 0x10) > 0; material.fragmentShader.lighting.isReflectionEnabled = (flags & 0x20) > 0; material.fragmentShader.layerConfig = input.ReadUInt32(); material.fragmentShader.lighting.fresnelConfig = (RenderBase.OFresnelConfig)input.ReadUInt32(); material.fragmentShader.bump.texture = (RenderBase.OBumpTexture)input.ReadUInt32(); material.fragmentShader.bump.mode = (RenderBase.OBumpMode)input.ReadUInt32(); flags = input.ReadUInt32(); material.fragmentShader.bump.isBumpRenormalize = (flags & 1) > 0; uint fragmentLightingTableOffset = getRelativeOffset(input); position = data.Position; data.Seek(fragmentLightingTableOffset, SeekOrigin.Begin); material.fragmentShader.lighting.reflectanceRSampler = getFragmentSampler(input, getRelativeOffset(input)); material.fragmentShader.lighting.reflectanceGSampler = getFragmentSampler(input, getRelativeOffset(input)); material.fragmentShader.lighting.reflectanceBSampler = getFragmentSampler(input, getRelativeOffset(input)); material.fragmentShader.lighting.distribution0Sampler = getFragmentSampler(input, getRelativeOffset(input)); material.fragmentShader.lighting.distribution1Sampler = getFragmentSampler(input, getRelativeOffset(input)); material.fragmentShader.lighting.fresnelSampler = getFragmentSampler(input, getRelativeOffset(input)); data.Seek(position, SeekOrigin.Begin); input.ReadUInt32(); PICACommandReader combiner0Commands = new PICACommandReader(data, 6, true); input.ReadUInt32(); PICACommandReader combiner1Commands = new PICACommandReader(data, 6, true); input.ReadUInt32(); PICACommandReader combiner2Commands = new PICACommandReader(data, 6, true); input.ReadUInt32(); PICACommandReader combiner3Commands = new PICACommandReader(data, 6, true); input.ReadUInt32(); PICACommandReader combiner4Commands = new PICACommandReader(data, 6, true); input.ReadUInt32(); PICACommandReader combiner5Commands = new PICACommandReader(data, 6, true); material.fragmentShader.textureCombiner[0] = combiner0Commands.getTevStage(0); material.fragmentShader.textureCombiner[1] = combiner1Commands.getTevStage(1); material.fragmentShader.textureCombiner[2] = combiner2Commands.getTevStage(2); material.fragmentShader.textureCombiner[3] = combiner3Commands.getTevStage(3); material.fragmentShader.textureCombiner[4] = combiner4Commands.getTevStage(4); material.fragmentShader.textureCombiner[5] = combiner5Commands.getTevStage(5); PICACommandReader alphaCommands = new PICACommandReader(data, 2, true); material.fragmentShader.alphaTest = alphaCommands.getAlphaTest(); } model.material.Add(material); } //Skeleton bool isSkeletonTranslateAnimationEnabled; if (cmdlHeader.hasSkeleton) { data.Seek(cmdlHeader.skeletonOffset, SeekOrigin.Begin); flags = input.ReadUInt32(); string skeletonMagic = IOUtils.readString(input, (uint)input.BaseStream.Position, 4); //SOBJ revision = input.ReadUInt32(); string name = IOUtils.readString(input, getRelativeOffset(input)); input.ReadUInt32(); input.ReadUInt32(); List<dictEntry> skeletonDictionary = getDictionary(input); uint rootBoneOffset = getRelativeOffset(input); cgfxSkeletonScalingRule scalingRule = (cgfxSkeletonScalingRule)input.ReadUInt32(); isSkeletonTranslateAnimationEnabled = (input.ReadUInt32() & 1) > 0; foreach (dictEntry boneEntry in skeletonDictionary) { data.Seek(boneEntry.dataOffset, SeekOrigin.Begin); RenderBase.OBone bone = new RenderBase.OBone(); bone.name = IOUtils.readString(input, getRelativeOffset(input)); uint boneFlags = input.ReadUInt32(); bone.isSegmentScaleCompensate = (boneFlags & 0x20) > 0; uint boneId = input.ReadUInt32(); bone.parentId = (short)input.ReadInt32(); int parentOffset = input.ReadInt32(); int childOffset = input.ReadInt32(); int previousSiblingOffset = input.ReadInt32(); int nextSiblingOffset = input.ReadInt32(); bone.scale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); bone.rotation = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); bone.translation = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); bone.absoluteScale = new RenderBase.OVector3(bone.scale); RenderBase.OMatrix localMatrix = getMatrix(input); RenderBase.OMatrix worldMatrix = getMatrix(input); RenderBase.OMatrix invBaseMatrix = getMatrix(input); bone.billboardMode = (RenderBase.OBillboardMode)input.ReadInt32(); uint userDataEntries = input.ReadUInt32(); uint userDataOffset = getRelativeOffset(input); model.skeleton.Add(bone); } } List<RenderBase.OMatrix> skeletonTransform = new List<RenderBase.OMatrix>(); for (int index = 0; index < model.skeleton.Count; index++) { RenderBase.OMatrix transform = new RenderBase.OMatrix(); transformSkeleton(model.skeleton, index, ref transform); skeletonTransform.Add(transform); } //Shapes List<cgfxShapeEntry> shapeHeader = new List<cgfxShapeEntry>(); for (int i = 0; i < cmdlHeader.shapeEntries; i++) { data.Seek(cmdlHeader.shapePointerTableOffset + (i * 4), SeekOrigin.Begin); data.Seek(getRelativeOffset(input), SeekOrigin.Begin); cgfxShapeEntry shape = new cgfxShapeEntry(); flags = input.ReadUInt32(); string sobjMagic = IOUtils.readString(input, (uint)input.BaseStream.Position, 4); revision = input.ReadUInt32(); shape.name = IOUtils.readString(input, getRelativeOffset(input)); shape.userDataEntries = input.ReadUInt32(); shape.userDataDictionaryOffset = getRelativeOffset(input); flags = input.ReadUInt32(); shape.boundingBoxOffset = getRelativeOffset(input); shape.positionOffset = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); shape.facesGroupEntries = input.ReadUInt32(); shape.facesGroupOffset = getRelativeOffset(input); input.ReadUInt32(); shape.vertexGroupEntries = input.ReadUInt32(); shape.vertexGroupOffset = getRelativeOffset(input); shape.blendShapeOffset = getRelativeOffset(input); shapeHeader.Add(shape); } List<RenderBase.OMesh> shapes = new List<RenderBase.OMesh>(); foreach (cgfxShapeEntry shapeEntry in shapeHeader) { RenderBase.OMesh shape = new RenderBase.OMesh(); data.Seek(shapeEntry.vertexGroupOffset, SeekOrigin.Begin); data.Seek(getRelativeOffset(input), SeekOrigin.Begin); input.ReadUInt32(); //Useless name offset input.ReadUInt32(); //Useless User Data entries input.ReadUInt32(); //Useless User Data offset uint bufferObject = input.ReadUInt32(); uint locationFlag = input.ReadUInt32(); uint vshAttributesBufferLength = input.ReadUInt32(); uint vshAttributesBufferOffset = getRelativeOffset(input); uint locationAddress = input.ReadUInt32(); uint memoryArea = input.ReadUInt32(); uint vshAttributesBufferStride = input.ReadUInt32(); uint vshAttributesBufferComponentsEntries = input.ReadUInt32(); uint vshAttributesBufferComponentsOffset = getRelativeOffset(input); List<attributeFormat> vshAttributeFormats = new List<attributeFormat>(); for (int i = 0; i < vshAttributesBufferComponentsEntries; i++) { data.Seek(vshAttributesBufferComponentsOffset + (i * 4), SeekOrigin.Begin); data.Seek(getRelativeOffset(input), SeekOrigin.Begin); attributeFormat format = new attributeFormat(); flags = input.ReadUInt32(); format.attribute = (PICACommand.vshAttribute)input.ReadUInt32(); format.isInterleaved = input.ReadUInt32() == 2; bufferObject = input.ReadUInt32(); locationFlag = input.ReadUInt32(); uint attributesStreamLength = input.ReadUInt32(); uint attributesStreamOffset = getRelativeOffset(input); locationAddress = input.ReadUInt32(); memoryArea = input.ReadUInt32(); format.type = (attributeFormatType)(input.ReadUInt32() & 0xf); format.attributeLength = input.ReadUInt32(); format.scale = input.ReadSingle(); format.offset = input.ReadUInt32(); vshAttributeFormats.Add(format); } //Faces for (int faceIndex = 0; faceIndex < shapeEntry.facesGroupEntries; faceIndex++) { data.Seek(shapeEntry.facesGroupOffset + (faceIndex * 4), SeekOrigin.Begin); data.Seek(getRelativeOffset(input), SeekOrigin.Begin); uint nodeListEntries = input.ReadUInt32(); uint nodeListOffset = getRelativeOffset(input); RenderBase.OSkinningMode skinningMode = RenderBase.OSkinningMode.none; switch (input.ReadUInt32()) //Skinning Mode { case 0: skinningMode = RenderBase.OSkinningMode.none; break; case 1: skinningMode = RenderBase.OSkinningMode.rigidSkinning; break; case 2: skinningMode = RenderBase.OSkinningMode.smoothSkinning; break; } uint faceMainHeaderEntries = input.ReadUInt32(); uint faceMainHeaderOffset = getRelativeOffset(input); //Bone nodes List<uint> nodeList = new List<uint>(); data.Seek(nodeListOffset, SeekOrigin.Begin); for (int i = 0; i < nodeListEntries; i++) nodeList.Add(input.ReadUInt32()); //Face-related stuff data.Seek(faceMainHeaderOffset, SeekOrigin.Begin); data.Seek(getRelativeOffset(input), SeekOrigin.Begin); uint faceDescriptorEntries = input.ReadUInt32(); uint faceDescriptorOffset = getRelativeOffset(input); input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); data.Seek(faceDescriptorOffset, SeekOrigin.Begin); data.Seek(getRelativeOffset(input), SeekOrigin.Begin); PICACommand.indexBufferFormat idxBufferFormat = (PICACommand.indexBufferFormat)((input.ReadUInt32() & 2) >> 1); input.ReadUInt32(); uint idxBufferLength = input.ReadUInt32(); uint idxBufferOffset = getRelativeOffset(input); for (int attribute = 0; attribute < vshAttributeFormats.Count; attribute++) { attributeFormat format = vshAttributeFormats[attribute]; switch (format.attribute) { case PICACommand.vshAttribute.normal: shape.hasNormal = true; break; case PICACommand.vshAttribute.tangent: shape.hasTangent = true; break; case PICACommand.vshAttribute.color: shape.hasColor = true; break; case PICACommand.vshAttribute.textureCoordinate0: shape.texUVCount = Math.Max(shape.texUVCount, 1); break; case PICACommand.vshAttribute.textureCoordinate1: shape.texUVCount = Math.Max(shape.texUVCount, 2); break; case PICACommand.vshAttribute.textureCoordinate2: shape.texUVCount = Math.Max(shape.texUVCount, 3); break; } } if (nodeList.Count > 0) { shape.hasNode = true; shape.hasWeight = true; } data.Seek(idxBufferOffset, SeekOrigin.Begin); for (int i = 0; i < idxBufferLength; i++) { ushort index = 0; switch (idxBufferFormat) { case PICACommand.indexBufferFormat.unsignedShort: index = input.ReadUInt16(); i++; break; case PICACommand.indexBufferFormat.unsignedByte: index = input.ReadByte(); break; } long dataPosition = data.Position; long vertexOffset = vshAttributesBufferOffset + (index * vshAttributesBufferStride); RenderBase.OVertex vertex = new RenderBase.OVertex(); vertex.diffuseColor = 0xffffffff; for (int attribute = 0; attribute < vshAttributeFormats.Count; attribute++) { attributeFormat format = vshAttributeFormats[attribute]; if (format.attribute == PICACommand.vshAttribute.boneWeight) format.type = attributeFormatType.unsignedByte; data.Seek(vertexOffset + format.offset, SeekOrigin.Begin); RenderBase.OVector4 vector = getVector(input, format); switch (format.attribute) { case PICACommand.vshAttribute.position: float x = (vector.x * format.scale) + shapeEntry.positionOffset.x; float y = (vector.y * format.scale) + shapeEntry.positionOffset.y; float z = (vector.z * format.scale) + shapeEntry.positionOffset.z; vertex.position = new RenderBase.OVector3(x, y, z); break; case PICACommand.vshAttribute.normal: vertex.normal = new RenderBase.OVector3(vector.x * format.scale, vector.y * format.scale, vector.z * format.scale); break; case PICACommand.vshAttribute.tangent: vertex.tangent = new RenderBase.OVector3(vector.x * format.scale, vector.y * format.scale, vector.z * format.scale); break; case PICACommand.vshAttribute.color: uint r = MeshUtils.saturate((vector.x * format.scale) * 0xff); uint g = MeshUtils.saturate((vector.y * format.scale) * 0xff); uint b = MeshUtils.saturate((vector.z * format.scale) * 0xff); uint a = MeshUtils.saturate((vector.w * format.scale) * 0xff); vertex.diffuseColor = b | (g << 8) | (r << 16) | (a << 24); break; case PICACommand.vshAttribute.textureCoordinate0: vertex.texture0 = new RenderBase.OVector2(vector.x * format.scale, vector.y * format.scale); break; case PICACommand.vshAttribute.textureCoordinate1: vertex.texture1 = new RenderBase.OVector2(vector.x * format.scale, vector.y * format.scale); break; case PICACommand.vshAttribute.textureCoordinate2: vertex.texture2 = new RenderBase.OVector2(vector.x * format.scale, vector.y * format.scale); break; case PICACommand.vshAttribute.boneIndex: int b0 = (int)vector.x; int b1 = (int)vector.y; int b2 = (int)vector.z; int b3 = (int)vector.w; if (b0 < nodeList.Count && format.attributeLength > 0) vertex.node.Add((int)nodeList[b0]); if (skinningMode == RenderBase.OSkinningMode.smoothSkinning) { if (b1 < nodeList.Count && format.attributeLength > 1) vertex.node.Add((int)nodeList[b1]); if (b2 < nodeList.Count && format.attributeLength > 2) vertex.node.Add((int)nodeList[b2]); if (b3 < nodeList.Count && format.attributeLength > 3) vertex.node.Add((int)nodeList[b3]); } break; case PICACommand.vshAttribute.boneWeight: if (format.attributeLength > 0) vertex.weight.Add(vector.x * format.scale); if (skinningMode == RenderBase.OSkinningMode.smoothSkinning) { if (format.attributeLength > 1) vertex.weight.Add(vector.y * format.scale); if (format.attributeLength > 2) vertex.weight.Add(vector.z * format.scale); if (format.attributeLength > 3) vertex.weight.Add(vector.w * format.scale); } break; } } //If the node list have 4 or less bones, then there is no need to store the indices per vertex //Instead, the entire list is used, since it supports up to 4 bones. if (vertex.node.Count == 0 && nodeList.Count <= 4) { for (int n = 0; n < nodeList.Count; n++) vertex.node.Add((int)nodeList[n]); if (vertex.weight.Count == 0) vertex.weight.Add(1); } if (skinningMode != RenderBase.OSkinningMode.smoothSkinning && vertex.node.Count > 0) { //Note: Rigid skinning can have only one bone per vertex //Note2: Vertex with Rigid skinning seems to be always have meshes centered, so is necessary to make them follow the skeleton if (vertex.node[0] < skeletonTransform.Count) { if (vertex.weight.Count == 0) vertex.weight.Add(1); vertex.position = RenderBase.OVector3.transform(vertex.position, skeletonTransform[vertex.node[0]]); } } MeshUtils.calculateBounds(model, vertex); shape.vertices.Add(vertex); data.Seek(dataPosition, SeekOrigin.Begin); } } shapes.Add(shape); } //Objects List<cgfxObjectEntry> objectHeader = new List<cgfxObjectEntry>(); for (int i = 0; i < cmdlHeader.objectEntries; i++) { data.Seek(cmdlHeader.objectPointerTableOffset + (i * 4), SeekOrigin.Begin); data.Seek(getRelativeOffset(input), SeekOrigin.Begin); cgfxObjectEntry obj = new cgfxObjectEntry(); flags = input.ReadUInt32(); string msobMagic = IOUtils.readString(input, (uint)input.BaseStream.Position, 4); revision = input.ReadUInt32(); obj.name = IOUtils.readString(input, getRelativeOffset(input)); obj.userDataEntries = input.ReadUInt32(); obj.userDataDictionaryOffset = getRelativeOffset(input); obj.shapeIndex = input.ReadUInt32(); obj.materialId = input.ReadUInt32(); obj.ownerModelOffset = getRelativeOffset(input); obj.isVisible = (input.ReadByte() & 1) > 0; obj.renderPriority = input.ReadByte(); obj.objectNodeVisibilityIndex = input.ReadUInt16(); obj.currentPrimitiveIndex = input.ReadUInt16(); //Theres still a bunch of stuff after this, but isn't really needed objectHeader.Add(obj); } List<objectNode> objectNodeList = new List<objectNode>(); foreach (dictEntry objectNodeEntry in cmdlHeader.objectNodes) { objectNode node = new objectNode(); data.Seek(objectNodeEntry.dataOffset, SeekOrigin.Begin); node.name = IOUtils.readString(input, getRelativeOffset(input)); node.isVisible = input.ReadUInt32() == 1; objectNodeList.Add(node); } foreach (cgfxObjectEntry obj in objectHeader) { RenderBase.OMesh modelObject = shapes[(int)obj.shapeIndex]; if (objectNodeList.Count > 0) { modelObject.name = objectNodeList[obj.objectNodeVisibilityIndex].name ; modelObject.isVisible = objectNodeList[obj.objectNodeVisibilityIndex].isVisible; } modelObject.materialId = (ushort)obj.materialId; modelObject.renderPriority = obj.renderPriority; model.mesh.Add(modelObject); } models.model.Add(model); } data.Close(); return models; }
/// <summary> /// Loads a BCH file. /// Note that BCH must start at offset 0x0 (don't try using it for BCHs inside containers). /// </summary> /// <param name="data">Memory Stream of the BCH file. The Stream will not be usable after</param> /// <returns></returns> public static RenderBase.OModelGroup load(MemoryStream data) { BinaryReader input = new BinaryReader(data); BinaryWriter writer = new BinaryWriter(data); RenderBase.OModelGroup models = new RenderBase.OModelGroup(); //Primary header bchHeader header = new bchHeader(); header.magic = IOUtils.readString(input, 0); data.Seek(4, SeekOrigin.Current); header.backwardCompatibility = input.ReadByte(); header.forwardCompatibility = input.ReadByte(); header.version = input.ReadUInt16(); header.mainHeaderOffset = input.ReadUInt32(); header.stringTableOffset = input.ReadUInt32(); header.gpuCommandsOffset = input.ReadUInt32(); header.dataOffset = input.ReadUInt32(); if (header.backwardCompatibility > 0x20) header.dataExtendedOffset = input.ReadUInt32(); header.relocationTableOffset = input.ReadUInt32(); header.mainHeaderLength = input.ReadUInt32(); header.stringTableLength = input.ReadUInt32(); header.gpuCommandsLength = input.ReadUInt32(); header.dataLength = input.ReadUInt32(); if (header.backwardCompatibility > 0x20) header.dataExtendedLength = input.ReadUInt32(); header.relocationTableLength = input.ReadUInt32(); header.uninitializedDataSectionLength = input.ReadUInt32(); header.uninitializedDescriptionSectionLength = input.ReadUInt32(); if (header.backwardCompatibility > 7) { header.flags = input.ReadUInt16(); header.addressCount = input.ReadUInt16(); } //Transform relative offsets to absolute offsets, also add extra bits if necessary. //The game does this on RAM after the BCH is loaded, so offsets to data is absolute and points to VRAM. for (uint o = header.relocationTableOffset; o < header.relocationTableOffset + header.relocationTableLength; o += 4) { data.Seek(o, SeekOrigin.Begin); uint value = input.ReadUInt32(); uint offset = value & 0x1ffffff; byte flags = (byte)(value >> 25); switch (flags) { case 0: data.Seek((offset * 4) + header.mainHeaderOffset, SeekOrigin.Begin); writer.Write(peek(input) + header.mainHeaderOffset); break; case 1: data.Seek(offset + header.mainHeaderOffset, SeekOrigin.Begin); writer.Write(peek(input) + header.stringTableOffset); break; case 2: data.Seek((offset * 4) + header.mainHeaderOffset, SeekOrigin.Begin); writer.Write(peek(input) + header.gpuCommandsOffset); break; case 7: case 0xc: data.Seek((offset * 4) + header.mainHeaderOffset, SeekOrigin.Begin); writer.Write(peek(input) + header.dataOffset); break; } //The moron that designed the format used different flags on different versions, instead of keeping compatibility. data.Seek((offset * 4) + header.gpuCommandsOffset, SeekOrigin.Begin); if (header.backwardCompatibility < 6) { switch (flags) { case 0x23: writer.Write(peek(input) + header.dataOffset); break; //Texture case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Vertex case 0x26: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x27: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else if (header.backwardCompatibility < 8) { switch (flags) { case 0x24: writer.Write(peek(input) + header.dataOffset); break; //Texture case 0x26: writer.Write(peek(input) + header.dataOffset); break; //Vertex case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x28: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else if (header.backwardCompatibility < 0x21) { switch (flags) { case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Texture case 0x27: writer.Write(peek(input) + header.dataOffset); break; //Vertex case 0x28: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode case 0x29: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode } } else { switch (flags) { case 0x25: writer.Write(peek(input) + header.dataOffset); break; //Texture case 0x26: writer.Write(peek(input) + header.dataOffset); break; //Vertex relative to Data Offset case 0x27: writer.Write(((peek(input) + header.dataOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Offset case 0x28: writer.Write((peek(input) + header.dataOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Offset case 0x2b: writer.Write(peek(input) + header.dataExtendedOffset); break; //Vertex relative to Data Extended Offset case 0x2c: writer.Write(((peek(input) + header.dataExtendedOffset) & 0x7fffffff) | 0x80000000); break; //Index 16 bits mode relative to Data Extended Offset case 0x2d: writer.Write((peek(input) + header.dataExtendedOffset) & 0x7fffffff); break; //Index 8 bits mode relative to Data Extended Offset } } } //Content header data.Seek(header.mainHeaderOffset, SeekOrigin.Begin); bchContentHeader contentHeader = new bchContentHeader { modelsPointerTableOffset = input.ReadUInt32(), modelsPointerTableEntries = input.ReadUInt32(), modelsNameOffset = input.ReadUInt32(), materialsPointerTableOffset = input.ReadUInt32(), materialsPointerTableEntries = input.ReadUInt32(), materialsNameOffset = input.ReadUInt32(), shadersPointerTableOffset = input.ReadUInt32(), shadersPointerTableEntries = input.ReadUInt32(), shadersNameOffset = input.ReadUInt32(), texturesPointerTableOffset = input.ReadUInt32(), texturesPointerTableEntries = input.ReadUInt32(), texturesNameOffset = input.ReadUInt32(), materialsLUTPointerTableOffset = input.ReadUInt32(), materialsLUTPointerTableEntries = input.ReadUInt32(), materialsLUTNameOffset = input.ReadUInt32(), lightsPointerTableOffset = input.ReadUInt32(), lightsPointerTableEntries = input.ReadUInt32(), lightsNameOffset = input.ReadUInt32(), camerasPointerTableOffset = input.ReadUInt32(), camerasPointerTableEntries = input.ReadUInt32(), camerasNameOffset = input.ReadUInt32(), fogsPointerTableOffset = input.ReadUInt32(), fogsPointerTableEntries = input.ReadUInt32(), fogsNameOffset = input.ReadUInt32(), skeletalAnimationsPointerTableOffset = input.ReadUInt32(), skeletalAnimationsPointerTableEntries = input.ReadUInt32(), skeletalAnimationsNameOffset = input.ReadUInt32(), materialAnimationsPointerTableOffset = input.ReadUInt32(), materialAnimationsPointerTableEntries = input.ReadUInt32(), materialAnimationsNameOffset = input.ReadUInt32(), visibilityAnimationsPointerTableOffset = input.ReadUInt32(), visibilityAnimationsPointerTableEntries = input.ReadUInt32(), visibilityAnimationsNameOffset = input.ReadUInt32(), lightAnimationsPointerTableOffset = input.ReadUInt32(), lightAnimationsPointerTableEntries = input.ReadUInt32(), lightAnimationsNameOffset = input.ReadUInt32(), cameraAnimationsPointerTableOffset = input.ReadUInt32(), cameraAnimationsPointerTableEntries = input.ReadUInt32(), cameraAnimationsNameOffset = input.ReadUInt32(), fogAnimationsPointerTableOffset = input.ReadUInt32(), fogAnimationsPointerTableEntries = input.ReadUInt32(), fogAnimationsNameOffset = input.ReadUInt32(), scenePointerTableOffset = input.ReadUInt32(), scenePointerTableEntries = input.ReadUInt32(), sceneNameOffset = input.ReadUInt32() }; //Note: NameOffset are PATRICIA trees //Shaders for (int index = 0; index < contentHeader.shadersPointerTableEntries; index++) { data.Seek(contentHeader.shadersPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); uint shaderDataOffset = input.ReadUInt32(); uint shaderDataLength = input.ReadUInt32(); } //Textures for (int index = 0; index < contentHeader.texturesPointerTableEntries; index++) { data.Seek(contentHeader.texturesPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); uint textureCommandsOffset = input.ReadUInt32(); uint textureCommandsWordCount = input.ReadUInt32(); data.Seek(0x14, SeekOrigin.Current); string textureName = readString(input); data.Seek(textureCommandsOffset, SeekOrigin.Begin); PICACommandReader textureCommands = new PICACommandReader(data, textureCommandsWordCount); //Note: It have textures for the 3 texture units. //The other texture units are used with textureCoordinate1 and 2. data.Seek(textureCommands.getTexUnit0Address(), SeekOrigin.Begin); Size textureSize = textureCommands.getTexUnit0Size(); byte[] buffer = new byte[textureSize.Width * textureSize.Height * 4]; input.Read(buffer, 0, buffer.Length); Bitmap texture = TextureCodec.decode( buffer, textureSize.Width, textureSize.Height, textureCommands.getTexUnit0Format()); models.texture.Add(new RenderBase.OTexture(texture, textureName)); } //LookUp Tables for (int index = 0; index < contentHeader.materialsLUTPointerTableEntries; index++) { data.Seek(contentHeader.materialsLUTPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); input.ReadUInt32(); uint samplersCount = input.ReadUInt32(); string name = readString(input); RenderBase.OLookUpTable table = new RenderBase.OLookUpTable(); table.name = name; for (int i = 0; i < samplersCount; i++) { RenderBase.OLookUpTableSampler sampler = new RenderBase.OLookUpTableSampler(); input.ReadUInt32(); uint tableOffset = input.ReadUInt32(); uint tableWordCount = input.ReadUInt32(); sampler.name = readString(input); long dataPosition = data.Position; data.Seek(tableOffset, SeekOrigin.Begin); PICACommandReader lutCommands = new PICACommandReader(data, tableWordCount); sampler.table = lutCommands.getFSHLookUpTable(); table.sampler.Add(sampler); data.Seek(dataPosition, SeekOrigin.Begin); } models.lookUpTable.Add(table); } //Lights for (int index = 0; index < contentHeader.lightsPointerTableEntries; index++) { data.Seek(contentHeader.lightsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OLight light = new RenderBase.OLight(); light.name = readString(input); light.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); light.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); light.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); uint lightFlags = input.ReadUInt32(); switch (lightFlags & 0xf) { case 1: light.lightUse = RenderBase.OLightUse.hemiSphere; break; case 2: light.lightUse = RenderBase.OLightUse.ambient; break; case 5: case 6: case 7: light.lightUse = RenderBase.OLightUse.vertex; switch (lightFlags & 0xf) { case 5: light.lightType = RenderBase.OLightType.directional; break; case 6: light.lightType = RenderBase.OLightType.point; break; case 7: light.lightType = RenderBase.OLightType.spot; break; } break; case 9: case 0xa: case 0xb: light.lightUse = RenderBase.OLightUse.fragment; switch (lightFlags & 0xf) { case 9: light.lightType = RenderBase.OLightType.directional; break; case 0xa: light.lightType = RenderBase.OLightType.point; break; case 0xb: light.lightType = RenderBase.OLightType.spot; break; } break; default: Debug.WriteLine(string.Format("BCH: Warning - Unknow Light Flags {0}", lightFlags.ToString("X8"))); break; } light.isLightEnabled = (lightFlags & 0x100) > 0; light.isTwoSideDiffuse = (lightFlags & 0x10000) > 0; light.isDistanceAttenuationEnabled = (lightFlags & 0x20000) > 0; light.angleSampler.input = (RenderBase.OFragmentSamplerInput)((lightFlags >> 24) & 0xf); light.angleSampler.scale = (RenderBase.OFragmentSamplerScale)((lightFlags >> 28) & 0xf); input.ReadUInt32(); switch (light.lightUse) { case RenderBase.OLightUse.hemiSphere: light.groundColor = MeshUtils.getColorFloat(input); light.skyColor = MeshUtils.getColorFloat(input); light.direction = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); light.lerpFactor = input.ReadSingle(); break; case RenderBase.OLightUse.ambient: light.ambient = MeshUtils.getColor(input); break; case RenderBase.OLightUse.vertex: light.ambient = MeshUtils.getColorFloat(input); light.diffuse = MeshUtils.getColorFloat(input); light.direction = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); light.distanceAttenuationConstant = input.ReadSingle(); light.distanceAttenuationLinear = input.ReadSingle(); light.distanceAttenuationQuadratic = input.ReadSingle(); light.spotExponent = input.ReadSingle(); light.spotCutoffAngle = input.ReadSingle(); break; case RenderBase.OLightUse.fragment: light.ambient = MeshUtils.getColor(input); light.diffuse = MeshUtils.getColor(input); light.specular0 = MeshUtils.getColor(input); light.specular1 = MeshUtils.getColor(input); light.direction = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); input.ReadUInt32(); input.ReadUInt32(); light.attenuationStart = input.ReadSingle(); light.attenuationEnd = input.ReadSingle(); light.distanceSampler.tableName = readString(input); light.distanceSampler.samplerName = readString(input); light.angleSampler.tableName = readString(input); light.angleSampler.samplerName = readString(input); break; } models.light.Add(light); } //Cameras for (int index = 0; index < contentHeader.camerasPointerTableEntries; index++) { data.Seek(contentHeader.camerasPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OCamera camera = new RenderBase.OCamera(); camera.name = readString(input); camera.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); camera.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); camera.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); uint cameraFlags = input.ReadUInt32(); camera.isInheritingTargetRotate = (cameraFlags & 0x10000) > 0; camera.isInheritingTargetTranslate = (cameraFlags & 0x20000) > 0; camera.isInheritingUpRotate = (cameraFlags & 0x40000) > 0; camera.view = (RenderBase.OCameraView)(cameraFlags & 0xf); camera.projection = (RenderBase.OCameraProjection)((cameraFlags >> 8) & 0xf); input.ReadSingle(); uint viewOffset = input.ReadUInt32(); uint projectionOffset = input.ReadUInt32(); data.Seek(viewOffset, SeekOrigin.Begin); camera.target = new RenderBase.OVector3(); camera.rotation = new RenderBase.OVector3(); camera.upVector = new RenderBase.OVector3(); RenderBase.OVector3 target = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); switch (camera.view) { case RenderBase.OCameraView.aimTarget: camera.target = target; camera.twist = input.ReadSingle(); break; case RenderBase.OCameraView.lookAtTarget: camera.target = target; camera.upVector = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); break; case RenderBase.OCameraView.rotate: camera.rotation = target; break; } data.Seek(projectionOffset, SeekOrigin.Begin); camera.zNear = input.ReadSingle(); camera.zFar = input.ReadSingle(); camera.aspectRatio = input.ReadSingle(); switch (camera.projection) { case RenderBase.OCameraProjection.perspective: camera.fieldOfViewY = input.ReadSingle(); break; case RenderBase.OCameraProjection.orthogonal: camera.height = input.ReadSingle(); break; } models.camera.Add(camera); } //Fogs for (int index = 0; index < contentHeader.fogsPointerTableEntries; index++) { data.Seek(contentHeader.fogsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OFog fog = new RenderBase.OFog(); fog.name = readString(input); fog.transformScale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); fog.transformRotate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); fog.transformTranslate = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); uint fogFlags = input.ReadUInt32(); fog.fogUpdater = (RenderBase.OFogUpdater)(fogFlags & 0xf); fog.isZFlip = (fogFlags & 0x100) > 0; fog.isAttenuateDistance = (fogFlags & 0x200) > 0; fog.fogColor = MeshUtils.getColor(input); fog.minFogDepth = input.ReadSingle(); fog.maxFogDepth = input.ReadSingle(); fog.fogDensity = input.ReadSingle(); models.fog.Add(fog); } //Skeletal Animations for (int index = 0; index < contentHeader.skeletalAnimationsPointerTableEntries; index++) { data.Seek(contentHeader.skeletalAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OSkeletalAnimation skeletalAnimation = new RenderBase.OSkeletalAnimation(); skeletalAnimation.name = readString(input); uint animationFlags = input.ReadUInt32(); skeletalAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); skeletalAnimation.frameSize = input.ReadSingle(); uint boneTableOffset = input.ReadUInt32(); uint boneTableEntries = input.ReadUInt32(); uint metaDataPointerOffset = input.ReadUInt32(); if (metaDataPointerOffset != 0) { data.Seek(metaDataPointerOffset, SeekOrigin.Begin); skeletalAnimation.userData = getMetaData(input); } for (int i = 0; i < boneTableEntries; i++) { data.Seek(boneTableOffset + (i * 4), SeekOrigin.Begin); uint offset = input.ReadUInt32(); RenderBase.OSkeletalAnimationBone bone = new RenderBase.OSkeletalAnimationBone(); data.Seek(offset, SeekOrigin.Begin); bone.name = readString(input); uint animationTypeFlags = input.ReadUInt32(); uint flags = input.ReadUInt32(); input.ReadUInt32(); RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); switch (segmentType) { case RenderBase.OSegmentType.transform: data.Seek(offset + 0x18, SeekOrigin.Begin); uint notExistMask = 0x80000; uint constantMask = 0x200; for (int j = 0; j < 2; j++) { for (int axis = 0; axis < 3; axis++) { bool notExist = (flags & notExistMask) > 0; bool constant = (flags & constantMask) > 0; RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); frame.exists = !notExist; if (frame.exists) { if (constant) { frame.interpolation = RenderBase.OInterpolationMode.linear; frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0)); } else { uint frameOffset = input.ReadUInt32(); long position = data.Position; data.Seek(frameOffset, SeekOrigin.Begin); getAnimationKeyFrame(input, frame); data.Seek(position, SeekOrigin.Begin); } } else data.Seek(4, SeekOrigin.Current); if (j == 0) { switch (axis) { case 0: bone.rotationX = frame; break; case 1: bone.rotationY = frame; break; case 2: bone.rotationZ = frame; break; } } else { switch (axis) { case 0: bone.translationX = frame; break; case 1: bone.translationY = frame; break; case 2: bone.translationZ = frame; break; } } notExistMask <<= 1; constantMask <<= 1; } constantMask <<= 1; } break; case RenderBase.OSegmentType.transformQuaternion: bone.isFrameFormat = true; long originalPos = data.Position; uint rotationOffset = input.ReadUInt32(); uint translationOffset = input.ReadUInt32(); if ((flags & 0x10) == 0) { bone.rotationQuaternion.exists = true; data.Seek(rotationOffset, SeekOrigin.Begin); if ((flags & 2) > 0) { bone.rotationQuaternion.vector.Add(new RenderBase.OVector4( input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle())); } else { bone.rotationQuaternion.startFrame = input.ReadSingle(); bone.rotationQuaternion.endFrame = input.ReadSingle(); uint rotationFlags = input.ReadUInt32(); uint rotationDataOffset = input.ReadUInt32(); uint rotationEntries = input.ReadUInt32(); data.Seek(rotationDataOffset, SeekOrigin.Begin); for (int j = 0; j < rotationEntries; j++) { bone.rotationQuaternion.vector.Add(new RenderBase.OVector4( input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle())); } } } if ((flags & 8) == 0) { bone.translation.exists = true; data.Seek(translationOffset, SeekOrigin.Begin); if ((flags & 1) > 0) { bone.translation.vector.Add(new RenderBase.OVector4( input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), 0)); } else { bone.translation.startFrame = input.ReadSingle(); bone.translation.endFrame = input.ReadSingle(); uint translationFlags = input.ReadUInt32(); uint translationDataOffset = input.ReadUInt32(); uint translationEntries = input.ReadUInt32(); data.Seek(translationDataOffset, SeekOrigin.Begin); for (int j = 0; j < translationEntries; j++) { bone.translation.vector.Add(new RenderBase.OVector4( input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), 0)); } } } break; case RenderBase.OSegmentType.transformMatrix: bone.isFullBakedFormat = true; input.ReadUInt32(); uint matrixOffset = input.ReadUInt32(); uint entries = input.ReadUInt32(); data.Seek(matrixOffset, SeekOrigin.Begin); for (int j = 0; j < entries; j++) { RenderBase.OMatrix transform = new RenderBase.OMatrix(); transform.M11 = input.ReadSingle(); transform.M21 = input.ReadSingle(); transform.M31 = input.ReadSingle(); transform.M41 = input.ReadSingle(); transform.M12 = input.ReadSingle(); transform.M22 = input.ReadSingle(); transform.M32 = input.ReadSingle(); transform.M42 = input.ReadSingle(); transform.M13 = input.ReadSingle(); transform.M23 = input.ReadSingle(); transform.M33 = input.ReadSingle(); transform.M43 = input.ReadSingle(); bone.transform.Add(transform); } break; default: throw new Exception(string.Format("BCH: Unknow Segment Type {0} on Skeletal Animation bone {1}! STOP!", segmentType, bone.name)); } skeletalAnimation.bone.Add(bone); } models.skeletalAnimation.list.Add(skeletalAnimation); } //Material Animations for (int index = 0; index < contentHeader.materialAnimationsPointerTableEntries; index++) { data.Seek(contentHeader.materialAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OMaterialAnimation materialAnimation = new RenderBase.OMaterialAnimation(); materialAnimation.name = readString(input); uint animationFlags = input.ReadUInt32(); materialAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); materialAnimation.frameSize = input.ReadSingle(); uint dataTableOffset = input.ReadUInt32(); uint dataTableEntries = input.ReadUInt32(); input.ReadUInt32(); uint textureNameTableOffset = input.ReadUInt32(); uint textureNameTableEntries = input.ReadUInt32(); data.Seek(textureNameTableOffset, SeekOrigin.Begin); for (int i = 0; i < textureNameTableEntries; i++) { string name = readString(input); materialAnimation.textureName.Add(name); } for (int i = 0; i < dataTableEntries; i++) { data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin); uint offset = input.ReadUInt32(); RenderBase.OMaterialAnimationData animationData = new RenderBase.OMaterialAnimationData(); data.Seek(offset, SeekOrigin.Begin); animationData.name = readString(input); uint animationTypeFlags = input.ReadUInt32(); uint flags = input.ReadUInt32(); animationData.type = (RenderBase.OMaterialAnimationType)(animationTypeFlags & 0xff); RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); int segmentCount = 0; switch (segmentType) { case RenderBase.OSegmentType.rgbaColor: segmentCount = 4; break; case RenderBase.OSegmentType.vector2: segmentCount = 2; break; case RenderBase.OSegmentType.single: segmentCount = 1; break; case RenderBase.OSegmentType.integer: segmentCount = 1; break; } for (int j = 0; j < segmentCount; j++) { RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin); frame.exists = (flags & (0x100 << j)) == 0; bool constant = (flags & (1 << j)) > 0; if (frame.exists) { if (constant) { frame.interpolation = RenderBase.OInterpolationMode.linear; frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0)); } else { uint frameOffset = input.ReadUInt32(); data.Seek(frameOffset, SeekOrigin.Begin); getAnimationKeyFrame(input, frame); } } animationData.frameList.Add(frame); } materialAnimation.data.Add(animationData); } models.materialAnimation.list.Add(materialAnimation); } //Visibility Animations for (int index = 0; index < contentHeader.visibilityAnimationsPointerTableEntries; index++) { data.Seek(contentHeader.visibilityAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OVisibilityAnimation visibilityAnimation = new RenderBase.OVisibilityAnimation(); visibilityAnimation.name = readString(input); uint animationFlags = input.ReadUInt32(); visibilityAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); visibilityAnimation.frameSize = input.ReadSingle(); uint dataTableOffset = input.ReadUInt32(); uint dataTableEntries = input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); for (int i = 0; i < dataTableEntries; i++) { data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin); uint offset = input.ReadUInt32(); RenderBase.OVisibilityAnimationData animationData = new RenderBase.OVisibilityAnimationData(); data.Seek(offset, SeekOrigin.Begin); animationData.name = readString(input); uint animationTypeFlags = input.ReadUInt32(); uint flags = input.ReadUInt32(); RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); if (segmentType == RenderBase.OSegmentType.boolean) { RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); if (segmentType == RenderBase.OSegmentType.boolean) frame = getAnimationKeyFrameBool(input); animationData.visibilityList = frame; } visibilityAnimation.data.Add(animationData); } models.visibilityAnimation.list.Add(visibilityAnimation); } //Light Animations for (int index = 0; index < contentHeader.lightAnimationsPointerTableEntries; index++) { data.Seek(contentHeader.lightAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OLightAnimation lightAnimation = new RenderBase.OLightAnimation(); lightAnimation.name = readString(input); uint animationFlags = input.ReadUInt32(); lightAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); lightAnimation.frameSize = input.ReadSingle(); uint dataTableOffset = input.ReadUInt32(); uint dataTableEntries = input.ReadUInt32(); input.ReadUInt32(); uint typeFlags = input.ReadUInt32(); lightAnimation.lightType = (RenderBase.OLightType)((typeFlags & 3) - 1); lightAnimation.lightUse = (RenderBase.OLightUse)((typeFlags >> 2) & 3); for (int i = 0; i < dataTableEntries; i++) { data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin); uint offset = input.ReadUInt32(); RenderBase.OLightAnimationData animationData = new RenderBase.OLightAnimationData(); data.Seek(offset, SeekOrigin.Begin); animationData.name = readString(input); uint animationTypeFlags = input.ReadUInt32(); uint flags = input.ReadUInt32(); animationData.type = (RenderBase.OLightAnimationType)(animationTypeFlags & 0xff); RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); int segmentCount = 0; switch (segmentType) { case RenderBase.OSegmentType.transform: segmentCount = 9; break; case RenderBase.OSegmentType.rgbaColor: segmentCount = 4; break; case RenderBase.OSegmentType.vector3: segmentCount = 3; break; case RenderBase.OSegmentType.single: segmentCount = 1; break; case RenderBase.OSegmentType.boolean: segmentCount = 1; break; } uint constantMask = 0x40; for (int j = 0; j < segmentCount; j++) { RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin); frame.exists = ((flags >> (segmentType == RenderBase.OSegmentType.transform ? 16 : 8)) & (1 << j)) == 0; if (frame.exists) { if (segmentType == RenderBase.OSegmentType.boolean) { frame = getAnimationKeyFrameBool(input); } else { bool constant; if (segmentType == RenderBase.OSegmentType.transform) { constant = (flags & constantMask) > 0; if (j == 5) constantMask <<= 2; else constantMask <<= 1; } else constant = (flags & (1 << j)) > 0; if (constant) { frame.interpolation = RenderBase.OInterpolationMode.linear; frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0.0f)); } else { uint frameOffset = input.ReadUInt32(); data.Seek(frameOffset, SeekOrigin.Begin); getAnimationKeyFrame(input, frame); } } } animationData.frameList.Add(frame); } lightAnimation.data.Add(animationData); } models.lightAnimation.list.Add(lightAnimation); } //Camera Animations for (int index = 0; index < contentHeader.cameraAnimationsPointerTableEntries; index++) { data.Seek(contentHeader.cameraAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OCameraAnimation cameraAnimation = new RenderBase.OCameraAnimation(); cameraAnimation.name = readString(input); uint animationFlags = input.ReadUInt32(); cameraAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); cameraAnimation.frameSize = input.ReadSingle(); uint dataTableOffset = input.ReadUInt32(); uint dataTableEntries = input.ReadUInt32(); input.ReadUInt32(); uint modeFlags = input.ReadUInt32(); cameraAnimation.viewMode = (RenderBase.OCameraView)(modeFlags & 0xf); cameraAnimation.projectionMode = (RenderBase.OCameraProjection)((modeFlags >> 8) & 0xf); for (int i = 0; i < dataTableEntries; i++) { data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin); uint offset = input.ReadUInt32(); RenderBase.OCameraAnimationData animationData = new RenderBase.OCameraAnimationData(); data.Seek(offset, SeekOrigin.Begin); animationData.name = readString(input); uint animationTypeFlags = input.ReadUInt32(); uint flags = input.ReadUInt32(); animationData.type = (RenderBase.OCameraAnimationType)(animationTypeFlags & 0xff); RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); int segmentCount = 0; switch (segmentType) { case RenderBase.OSegmentType.transform: segmentCount = 9; break; case RenderBase.OSegmentType.vector3: segmentCount = 3; break; case RenderBase.OSegmentType.single: segmentCount = 1; break; } uint constantMask = 0x40; for (int j = 0; j < segmentCount; j++) { RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin); frame.exists = ((flags >> (segmentType == RenderBase.OSegmentType.transform ? 16 : 8)) & (1 << j)) == 0; bool constant; if (segmentType == RenderBase.OSegmentType.transform) { constant = (flags & constantMask) > 0; if (j == 5) constantMask <<= 2; else constantMask <<= 1; } else constant = (flags & (1 << j)) > 0; if (frame.exists) { if (constant) { frame.interpolation = RenderBase.OInterpolationMode.linear; frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0.0f)); } else { uint frameOffset = input.ReadUInt32(); data.Seek(frameOffset, SeekOrigin.Begin); getAnimationKeyFrame(input, frame); } } animationData.frameList.Add(frame); } cameraAnimation.data.Add(animationData); } models.cameraAnimation.list.Add(cameraAnimation); } //Fog Animations for (int index = 0; index < contentHeader.fogAnimationsPointerTableEntries; index++) { data.Seek(contentHeader.fogAnimationsPointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OFogAnimation fogAnimation = new RenderBase.OFogAnimation(); fogAnimation.name = readString(input); uint animationFlags = input.ReadUInt32(); fogAnimation.loopMode = (RenderBase.OLoopMode)(animationFlags & 1); fogAnimation.frameSize = input.ReadSingle(); uint dataTableOffset = input.ReadUInt32(); uint dataTableEntries = input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); for (int i = 0; i < dataTableEntries; i++) { data.Seek(dataTableOffset + (i * 4), SeekOrigin.Begin); uint offset = input.ReadUInt32(); RenderBase.OFogAnimationData animationData = new RenderBase.OFogAnimationData(); data.Seek(offset, SeekOrigin.Begin); animationData.name = readString(input); uint animationTypeFlags = input.ReadUInt32(); uint flags = input.ReadUInt32(); RenderBase.OSegmentType segmentType = (RenderBase.OSegmentType)((animationTypeFlags >> 16) & 0xf); int segmentCount = segmentType == RenderBase.OSegmentType.rgbaColor ? 4 : 0; for (int j = 0; j < segmentCount; j++) { RenderBase.OAnimationKeyFrameGroup frame = new RenderBase.OAnimationKeyFrameGroup(); data.Seek(offset + 0xc + (j * 4), SeekOrigin.Begin); frame.exists = ((flags >> 8) & (1 << j)) == 0; if (frame.exists) { bool constant = (flags & (1 << j)) > 0; if (constant) { frame.interpolation = RenderBase.OInterpolationMode.linear; frame.keyFrames.Add(new RenderBase.OAnimationKeyFrame(input.ReadSingle(), 0.0f)); } else { uint frameOffset = input.ReadUInt32(); data.Seek(frameOffset, SeekOrigin.Begin); getAnimationKeyFrame(input, frame); } } animationData.colorList.Add(frame); } fogAnimation.data.Add(animationData); } models.fogAnimation.list.Add(fogAnimation); } //Scene Environment for (int index = 0; index < contentHeader.scenePointerTableEntries; index++) { data.Seek(contentHeader.scenePointerTableOffset + (index * 4), SeekOrigin.Begin); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); RenderBase.OScene scene = new RenderBase.OScene(); scene.name = readString(input); uint cameraReferenceOffset = input.ReadUInt32(); uint cameraReferenceEntries = input.ReadUInt32(); uint lightReferenceOffset = input.ReadUInt32(); uint lightReferenceEntries = input.ReadUInt32(); uint fogReferenceOffset = input.ReadUInt32(); uint fogReferenceEntries = input.ReadUInt32(); data.Seek(cameraReferenceOffset, SeekOrigin.Begin); for (int i = 0; i < cameraReferenceEntries; i++) { RenderBase.OSceneReference reference = new RenderBase.OSceneReference(); reference.slotIndex = input.ReadUInt32(); reference.name = readString(input); scene.cameras.Add(reference); } data.Seek(lightReferenceOffset, SeekOrigin.Begin); for (int i = 0; i < lightReferenceEntries; i++) { RenderBase.OSceneReference reference = new RenderBase.OSceneReference(); reference.slotIndex = input.ReadUInt32(); reference.name = readString(input); scene.lights.Add(reference); } data.Seek(fogReferenceOffset, SeekOrigin.Begin); for (int i = 0; i < fogReferenceEntries; i++) { RenderBase.OSceneReference reference = new RenderBase.OSceneReference(); reference.slotIndex = input.ReadUInt32(); reference.name = readString(input); scene.fogs.Add(reference); } } //Models for (int modelIndex = 0; modelIndex < contentHeader.modelsPointerTableEntries; modelIndex++) { RenderBase.OModel model = new RenderBase.OModel(); data.Seek(contentHeader.modelsPointerTableOffset + (modelIndex * 4), SeekOrigin.Begin); uint objectsHeaderOffset = input.ReadUInt32(); //Objects header data.Seek(objectsHeaderOffset, SeekOrigin.Begin); bchModelHeader modelHeader; modelHeader.flags = input.ReadByte(); modelHeader.skeletonScalingType = input.ReadByte(); modelHeader.silhouetteMaterialEntries = input.ReadUInt16(); modelHeader.worldTransform = new RenderBase.OMatrix(); modelHeader.worldTransform.M11 = input.ReadSingle(); modelHeader.worldTransform.M21 = input.ReadSingle(); modelHeader.worldTransform.M31 = input.ReadSingle(); modelHeader.worldTransform.M41 = input.ReadSingle(); modelHeader.worldTransform.M12 = input.ReadSingle(); modelHeader.worldTransform.M22 = input.ReadSingle(); modelHeader.worldTransform.M32 = input.ReadSingle(); modelHeader.worldTransform.M42 = input.ReadSingle(); modelHeader.worldTransform.M13 = input.ReadSingle(); modelHeader.worldTransform.M23 = input.ReadSingle(); modelHeader.worldTransform.M33 = input.ReadSingle(); modelHeader.worldTransform.M43 = input.ReadSingle(); modelHeader.materialsTableOffset = input.ReadUInt32(); modelHeader.materialsTableEntries = input.ReadUInt32(); modelHeader.materialsNameOffset = input.ReadUInt32(); modelHeader.verticesTableOffset = input.ReadUInt32(); modelHeader.verticesTableEntries = input.ReadUInt32(); data.Seek(header.backwardCompatibility > 6 ? 0x28 : 0x20, SeekOrigin.Current); modelHeader.skeletonOffset = input.ReadUInt32(); modelHeader.skeletonEntries = input.ReadUInt32(); modelHeader.skeletonNameOffset = input.ReadUInt32(); modelHeader.objectsNodeVisibilityOffset = input.ReadUInt32(); modelHeader.objectsNodeCount = input.ReadUInt32(); modelHeader.modelName = readString(input); modelHeader.objectsNodeNameEntries = input.ReadUInt32(); modelHeader.objectsNodeNameOffset = input.ReadUInt32(); input.ReadUInt32(); //0x0 modelHeader.metaDataPointerOffset = input.ReadUInt32(); model.transform = modelHeader.worldTransform; model.name = modelHeader.modelName; string[] objectName = new string[modelHeader.objectsNodeNameEntries]; data.Seek(modelHeader.objectsNodeNameOffset, SeekOrigin.Begin); int rootReferenceBit = input.ReadInt32(); //Radix tree uint rootLeftNode = input.ReadUInt16(); uint rootRightNode = input.ReadUInt16(); uint rootNameOffset = input.ReadUInt32(); for (int i = 0; i < modelHeader.objectsNodeNameEntries; i++) { int referenceBit = input.ReadInt32(); ushort leftNode = input.ReadUInt16(); ushort rightNode = input.ReadUInt16(); objectName[i] = readString(input); } //Materials for (int index = 0; index < modelHeader.materialsTableEntries; index++) { //Nota: As versões mais antigas tinham o Coordinator já no header do material. //As versões mais recentes tem uma seção reservada só pra ele, por isso possuem tamanho do header menor. if (header.backwardCompatibility < 0x21) data.Seek(modelHeader.materialsTableOffset + (index * 0x58), SeekOrigin.Begin); else data.Seek(modelHeader.materialsTableOffset + (index * 0x2c), SeekOrigin.Begin); RenderBase.OMaterial material = new RenderBase.OMaterial(); uint materialParametersOffset = input.ReadUInt32(); input.ReadUInt32(); //TODO input.ReadUInt32(); input.ReadUInt32(); uint textureCommandsOffset = input.ReadUInt32(); uint textureCommandsWordCount = input.ReadUInt32(); uint materialMapperOffset = 0; if (header.backwardCompatibility < 0x21) { materialMapperOffset = (uint)data.Position; data.Seek(0x30, SeekOrigin.Current); } else materialMapperOffset = input.ReadUInt32(); material.name0 = readString(input); material.name1 = readString(input); material.name2 = readString(input); material.name = readString(input); //Parameters //Same pointer of Materials section. Why? if (materialParametersOffset != 0) { data.Seek(materialParametersOffset, SeekOrigin.Begin); uint hash = input.ReadUInt32(); ushort materialFlags = input.ReadUInt16(); material.isFragmentLightEnabled = (materialFlags & 1) > 0; material.isVertexLightEnabled = (materialFlags & 2) > 0; material.isHemiSphereLightEnabled = (materialFlags & 4) > 0; material.isHemiSphereOcclusionEnabled = (materialFlags & 8) > 0; material.isFogEnabled = (materialFlags & 0x10) > 0; material.rasterization.isPolygonOffsetEnabled = (materialFlags & 0x20) > 0; ushort fragmentFlags = input.ReadUInt16(); material.fragmentShader.bump.isBumpRenormalize = (fragmentFlags & 1) > 0; material.fragmentShader.lighting.isClampHighLight = (fragmentFlags & 2) > 0; material.fragmentShader.lighting.isDistribution0Enabled = (fragmentFlags & 4) > 0; material.fragmentShader.lighting.isDistribution1Enabled = (fragmentFlags & 8) > 0; material.fragmentShader.lighting.isGeometryFactor0Enabled = (fragmentFlags & 0x10) > 0; material.fragmentShader.lighting.isGeometryFactor1Enabled = (fragmentFlags & 0x20) > 0; material.fragmentShader.lighting.isReflectionEnabled = (fragmentFlags & 0x40) > 0; RenderBase.OBlendMode blendMode = (RenderBase.OBlendMode)((fragmentFlags >> 10) & 3); input.ReadUInt32(); for (int i = 0; i < 3; i++) { RenderBase.OTextureCoordinator coordinator; uint projectionAndCamera = input.ReadUInt32(); coordinator.projection = (RenderBase.OTextureProjection)((projectionAndCamera >> 16) & 0xff); coordinator.referenceCamera = projectionAndCamera >> 24; coordinator.scaleU = input.ReadSingle(); coordinator.scaleV = input.ReadSingle(); coordinator.rotate = input.ReadSingle(); coordinator.translateU = input.ReadSingle(); coordinator.translateV = input.ReadSingle(); material.textureCoordinator[i] = coordinator; } material.lightSetIndex = input.ReadUInt16(); material.fogIndex = input.ReadUInt16(); material.materialColor.emission = MeshUtils.getColor(input); material.materialColor.ambient = MeshUtils.getColor(input); material.materialColor.diffuse = MeshUtils.getColor(input); material.materialColor.specular0 = MeshUtils.getColor(input); material.materialColor.specular1 = MeshUtils.getColor(input); material.materialColor.constant0 = MeshUtils.getColor(input); material.materialColor.constant1 = MeshUtils.getColor(input); material.materialColor.constant2 = MeshUtils.getColor(input); material.materialColor.constant3 = MeshUtils.getColor(input); material.materialColor.constant4 = MeshUtils.getColor(input); material.materialColor.constant5 = MeshUtils.getColor(input); material.fragmentOperation.blend.blendColor = MeshUtils.getColor(input); material.materialColor.colorScale = input.ReadSingle(); input.ReadUInt32(); //TODO: Figure out input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); input.ReadUInt32(); uint fragmentData = input.ReadUInt32(); material.fragmentShader.bump.texture = (RenderBase.OBumpTexture)(fragmentData >> 24); material.fragmentShader.bump.mode = (RenderBase.OBumpMode)((fragmentData >> 16) & 0xff); material.fragmentShader.lighting.fresnelConfig = (RenderBase.OFresnelConfig)((fragmentData >> 8) & 0xff); material.fragmentShader.layerConfig = fragmentData & 0xff; //Some Fragment Lighting related commands... This seems a bit out of place. long position = data.Position; PICACommandReader fshLightingCommands = new PICACommandReader(data, 6, true); PICACommand.fragmentSamplerAbsolute sAbs = fshLightingCommands.getReflectanceSamplerAbsolute(); material.fragmentShader.lighting.reflectanceRSampler.isAbsolute = sAbs.r; material.fragmentShader.lighting.reflectanceGSampler.isAbsolute = sAbs.g; material.fragmentShader.lighting.reflectanceBSampler.isAbsolute = sAbs.b; material.fragmentShader.lighting.distribution0Sampler.isAbsolute = sAbs.d0; material.fragmentShader.lighting.distribution1Sampler.isAbsolute = sAbs.d1; material.fragmentShader.lighting.fresnelSampler.isAbsolute = sAbs.fresnel; PICACommand.fragmentSamplerInput sInput = fshLightingCommands.getReflectanceSamplerInput(); material.fragmentShader.lighting.reflectanceRSampler.input = sInput.r; material.fragmentShader.lighting.reflectanceGSampler.input = sInput.g; material.fragmentShader.lighting.reflectanceBSampler.input = sInput.b; material.fragmentShader.lighting.distribution0Sampler.input = sInput.d0; material.fragmentShader.lighting.distribution1Sampler.input = sInput.d1; material.fragmentShader.lighting.fresnelSampler.input = sInput.fresnel; PICACommand.fragmentSamplerScale sScale = fshLightingCommands.getReflectanceSamplerScale(); material.fragmentShader.lighting.reflectanceRSampler.scale = sScale.r; material.fragmentShader.lighting.reflectanceGSampler.scale = sScale.g; material.fragmentShader.lighting.reflectanceBSampler.scale = sScale.b; material.fragmentShader.lighting.distribution0Sampler.scale = sScale.d0; material.fragmentShader.lighting.distribution1Sampler.scale = sScale.d1; material.fragmentShader.lighting.fresnelSampler.scale = sScale.fresnel; data.Seek(position + (4 * 6), SeekOrigin.Begin); //Just to be sure ;) RenderBase.OConstantColor[] constantList = new RenderBase.OConstantColor[6]; uint constantColor = input.ReadUInt32(); for (int i = 0; i < 6; i++) constantList[i] = (RenderBase.OConstantColor)((constantColor >> (i * 4)) & 0xf); material.rasterization.polygonOffsetUnit = input.ReadSingle(); uint fshCommandsOffset = input.ReadUInt32(); uint fshCommandsWordCount = input.ReadUInt32(); input.ReadUInt32(); material.fragmentShader.lighting.distribution0Sampler.tableName = readString(input); material.fragmentShader.lighting.distribution1Sampler.tableName = readString(input); material.fragmentShader.lighting.fresnelSampler.tableName = readString(input); material.fragmentShader.lighting.reflectanceRSampler.tableName = readString(input); material.fragmentShader.lighting.reflectanceGSampler.tableName = readString(input); material.fragmentShader.lighting.reflectanceBSampler.tableName = readString(input); material.fragmentShader.lighting.distribution0Sampler.samplerName = readString(input); material.fragmentShader.lighting.distribution1Sampler.samplerName = readString(input); material.fragmentShader.lighting.fresnelSampler.samplerName = readString(input); material.fragmentShader.lighting.reflectanceRSampler.samplerName = readString(input); material.fragmentShader.lighting.reflectanceGSampler.samplerName = readString(input); material.fragmentShader.lighting.reflectanceBSampler.samplerName = readString(input); material.shaderReference = new RenderBase.OReference(readString(input)); material.modelReference = new RenderBase.OReference(readString(input)); //User Data if (header.backwardCompatibility > 6) { uint metaDataPointerOffset = input.ReadUInt32(); if (metaDataPointerOffset != 0) { data.Seek(metaDataPointerOffset, SeekOrigin.Begin); material.userData = getMetaData(input); } } //Mapper data.Seek(materialMapperOffset, SeekOrigin.Begin); for (int i = 0; i < 3; i++) { RenderBase.OTextureMapper mapper; uint wrapAndMagFilter = input.ReadUInt32(); uint levelOfDetailAndMinFilter = input.ReadUInt32(); mapper.wrapU = (RenderBase.OTextureWrap)((wrapAndMagFilter >> 8) & 0xff); mapper.wrapV = (RenderBase.OTextureWrap)((wrapAndMagFilter >> 16) & 0xff); mapper.magFilter = (RenderBase.OTextureMagFilter)(wrapAndMagFilter >> 24); mapper.minFilter = (RenderBase.OTextureMinFilter)(levelOfDetailAndMinFilter & 0xff); mapper.minLOD = (levelOfDetailAndMinFilter >> 8) & 0xff; //max 232 mapper.LODBias = input.ReadSingle(); mapper.borderColor = MeshUtils.getColor(input); material.textureMapper[i] = mapper; } //Fragment Shader commands data.Seek(fshCommandsOffset, SeekOrigin.Begin); PICACommandReader fshCommands = new PICACommandReader(data, fshCommandsWordCount); for (byte stage = 0; stage < 6; stage++) material.fragmentShader.textureCombiner[stage] = fshCommands.getTevStage(stage); material.fragmentShader.bufferColor = fshCommands.getFragmentBufferColor(); material.fragmentOperation.blend = fshCommands.getBlendOperation(); material.fragmentOperation.blend.logicalOperation = fshCommands.getColorLogicOperation(); material.fragmentShader.alphaTest = fshCommands.getAlphaTest(); material.fragmentOperation.stencil = fshCommands.getStencilTest(); material.fragmentOperation.depth = fshCommands.getDepthTest(); material.rasterization.cullMode = fshCommands.getCullMode(); material.fragmentOperation.blend.mode = blendMode; } model.material.Add(material); } //Skeleton data.Seek(modelHeader.skeletonOffset, SeekOrigin.Begin); for (int index = 0; index < modelHeader.skeletonEntries; index++) { RenderBase.OBone bone = new RenderBase.OBone(); uint boneFlags = input.ReadUInt32(); bone.billboardMode = (RenderBase.OBillboardMode)((boneFlags >> 16) & 0xf); bone.isSegmentScaleCompensate = (boneFlags & 0x00400000) > 0; bone.parentId = input.ReadInt16(); ushort boneSpacer = input.ReadUInt16(); bone.scale = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); bone.rotation = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); bone.translation = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); bone.absoluteScale = new RenderBase.OVector3(bone.scale); RenderBase.OMatrix boneMatrix = new RenderBase.OMatrix(); boneMatrix.M11 = input.ReadSingle(); boneMatrix.M21 = input.ReadSingle(); boneMatrix.M31 = input.ReadSingle(); boneMatrix.M41 = input.ReadSingle(); boneMatrix.M12 = input.ReadSingle(); boneMatrix.M22 = input.ReadSingle(); boneMatrix.M32 = input.ReadSingle(); boneMatrix.M42 = input.ReadSingle(); boneMatrix.M13 = input.ReadSingle(); boneMatrix.M23 = input.ReadSingle(); boneMatrix.M33 = input.ReadSingle(); boneMatrix.M43 = input.ReadSingle(); bone.name = readString(input); uint metaDataPointerOffset = input.ReadUInt32(); if (metaDataPointerOffset != 0) { long position = data.Position; data.Seek(metaDataPointerOffset, SeekOrigin.Begin); bone.userData = getMetaData(input); data.Seek(position, SeekOrigin.Begin); } model.skeleton.Add(bone); } List<RenderBase.OMatrix> skeletonTransform = new List<RenderBase.OMatrix>(); for (int index = 0; index < modelHeader.skeletonEntries; index++) { RenderBase.OMatrix transform = new RenderBase.OMatrix(); transformSkeleton(model.skeleton, index, ref transform); skeletonTransform.Add(transform); } data.Seek(modelHeader.objectsNodeVisibilityOffset, SeekOrigin.Begin); uint nodeVisibility = input.ReadUInt32(); //Vertices header data.Seek(modelHeader.verticesTableOffset, SeekOrigin.Begin); List<bchObjectEntry> objects = new List<bchObjectEntry>(); for (int index = 0; index < modelHeader.verticesTableEntries; index++) { bchObjectEntry objectEntry = new bchObjectEntry(); objectEntry.materialId = input.ReadUInt16(); ushort flags = input.ReadUInt16(); if (header.backwardCompatibility != 8) objectEntry.isSilhouette = (flags & 1) > 0; objectEntry.nodeId = input.ReadUInt16(); objectEntry.renderPriority = input.ReadUInt16(); objectEntry.vshAttributesBufferCommandsOffset = input.ReadUInt32(); //Buffer 0 objectEntry.vshAttributesBufferCommandsWordCount = input.ReadUInt32(); objectEntry.facesHeaderOffset = input.ReadUInt32(); objectEntry.facesHeaderEntries = input.ReadUInt32(); objectEntry.vshExtraAttributesBufferCommandsOffset = input.ReadUInt32(); //Buffers 1-11 objectEntry.vshExtraAttributesBufferCommandsWordCount = input.ReadUInt32(); objectEntry.centerVector = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); objectEntry.flagsOffset = input.ReadUInt32(); input.ReadUInt32(); //ex: 0x0 fixo objectEntry.boundingBoxOffset = input.ReadUInt32(); objects.Add(objectEntry); } for (int objIndex = 0; objIndex < objects.Count; objIndex++) { if (objects[objIndex].isSilhouette) continue; //TODO: Figure out for what "Silhouette" is used. RenderBase.OMesh obj = new RenderBase.OMesh(); obj.materialId = objects[objIndex].materialId; obj.renderPriority = objects[objIndex].renderPriority; if (objects[objIndex].nodeId < objectName.Length) obj.name = objectName[objects[objIndex].nodeId]; else obj.name = "mesh" + objIndex.ToString(); obj.isVisible = (nodeVisibility & (1 << objects[objIndex].nodeId)) > 0; //Vertices data.Seek(objects[objIndex].vshAttributesBufferCommandsOffset, SeekOrigin.Begin); PICACommandReader vshCommands = new PICACommandReader(data, objects[objIndex].vshAttributesBufferCommandsWordCount); Stack<float> vshAttributesUniformReg6 = vshCommands.getVSHFloatUniformData(6); Stack<float> vshAttributesUniformReg7 = vshCommands.getVSHFloatUniformData(7); RenderBase.OVector4 positionOffset = new RenderBase.OVector4( vshAttributesUniformReg6.Pop(), vshAttributesUniformReg6.Pop(), vshAttributesUniformReg6.Pop(), vshAttributesUniformReg6.Pop()); float texture0Scale = vshAttributesUniformReg7.Pop(); float texture1Scale = vshAttributesUniformReg7.Pop(); float texture2Scale = vshAttributesUniformReg7.Pop(); float boneWeightScale = vshAttributesUniformReg7.Pop(); float positionScale = vshAttributesUniformReg7.Pop(); float normalScale = vshAttributesUniformReg7.Pop(); float tangentScale = vshAttributesUniformReg7.Pop(); float colorScale = vshAttributesUniformReg7.Pop(); //Faces uint facesCount = objects[objIndex].facesHeaderEntries; bool hasFaces = facesCount > 0; uint facesTableOffset = 0; if (!hasFaces) { data.Seek(modelHeader.verticesTableOffset + modelHeader.verticesTableEntries * 0x38, SeekOrigin.Begin); data.Seek(objIndex * 0x1c + 0x10, SeekOrigin.Current); facesTableOffset = input.ReadUInt32(); facesCount = input.ReadUInt32(); } for (uint f = 0; f < facesCount; f++) { RenderBase.OSkinningMode skinningMode = RenderBase.OSkinningMode.none; List<ushort> nodeList = new List<ushort>(); uint idxBufferOffset; PICACommand.indexBufferFormat idxBufferFormat; uint idxBufferTotalVertices; if (hasFaces) { uint baseOffset = objects[objIndex].facesHeaderOffset + f * 0x34; data.Seek(baseOffset, SeekOrigin.Begin); skinningMode = (RenderBase.OSkinningMode)input.ReadUInt16(); ushort nodeIdEntries = input.ReadUInt16(); for (int n = 0; n < nodeIdEntries; n++) nodeList.Add(input.ReadUInt16()); data.Seek(baseOffset + 0x2c, SeekOrigin.Begin); uint faceHeaderOffset = input.ReadUInt32(); uint faceHeaderWordCount = input.ReadUInt32(); data.Seek(faceHeaderOffset, SeekOrigin.Begin); PICACommandReader idxCommands = new PICACommandReader(data, faceHeaderWordCount); idxBufferOffset = idxCommands.getIndexBufferAddress(); idxBufferFormat = idxCommands.getIndexBufferFormat(); idxBufferTotalVertices = idxCommands.getIndexBufferTotalVertices(); } else { data.Seek(facesTableOffset + f * 8, SeekOrigin.Begin); idxBufferOffset = input.ReadUInt32(); idxBufferFormat = PICACommand.indexBufferFormat.unsignedShort; idxBufferTotalVertices = input.ReadUInt32(); } //Carregamento de dados relacionados ao Vertex Shader uint vshAttributesBufferOffset = vshCommands.getVSHAttributesBufferAddress(0); uint vshAttributesBufferStride = vshCommands.getVSHAttributesBufferStride(0); uint vshTotalAttributes = vshCommands.getVSHTotalAttributes(0); PICACommand.vshAttribute[] vshMainAttributesBufferPermutation = vshCommands.getVSHAttributesBufferPermutation(); uint[] vshAttributesBufferPermutation = vshCommands.getVSHAttributesBufferPermutation(0); PICACommand.attributeFormat[] vshAttributesBufferFormat = vshCommands.getVSHAttributesBufferFormat(); for (int attribute = 0; attribute < vshTotalAttributes; attribute++) { switch (vshMainAttributesBufferPermutation[vshAttributesBufferPermutation[attribute]]) { case PICACommand.vshAttribute.normal: obj.hasNormal = true; break; case PICACommand.vshAttribute.tangent: obj.hasTangent = true; break; case PICACommand.vshAttribute.color: obj.hasColor = true; break; case PICACommand.vshAttribute.textureCoordinate0: obj.texUVCount = Math.Max(obj.texUVCount, 1); break; case PICACommand.vshAttribute.textureCoordinate1: obj.texUVCount = Math.Max(obj.texUVCount, 2); break; case PICACommand.vshAttribute.textureCoordinate2: obj.texUVCount = Math.Max(obj.texUVCount, 3); break; } } if (nodeList.Count > 0) { obj.hasNode = true; obj.hasWeight = true; } data.Seek(idxBufferOffset, SeekOrigin.Begin); for (int faceIndex = 0; faceIndex < idxBufferTotalVertices; faceIndex++) { ushort index = 0; switch (idxBufferFormat) { case PICACommand.indexBufferFormat.unsignedShort: index = input.ReadUInt16(); break; case PICACommand.indexBufferFormat.unsignedByte: index = input.ReadByte(); break; } long dataPosition = data.Position; long vertexOffset = vshAttributesBufferOffset + (index * vshAttributesBufferStride); data.Seek(vertexOffset, SeekOrigin.Begin); RenderBase.OVertex vertex = new RenderBase.OVertex(); vertex.diffuseColor = 0xffffffff; for (int attribute = 0; attribute < vshTotalAttributes; attribute++) { //gdkchan self note: The Attribute type flags are used for something else on Bone Weight (and bone index?) PICACommand.vshAttribute att = vshMainAttributesBufferPermutation[vshAttributesBufferPermutation[attribute]]; PICACommand.attributeFormat format = vshAttributesBufferFormat[vshAttributesBufferPermutation[attribute]]; if (att == PICACommand.vshAttribute.boneWeight) format.type = PICACommand.attributeFormatType.unsignedByte; RenderBase.OVector4 vector = getVector(input, format); switch (att) { case PICACommand.vshAttribute.position: float x = (vector.x * positionScale) + positionOffset.x; float y = (vector.y * positionScale) + positionOffset.y; float z = (vector.z * positionScale) + positionOffset.z; vertex.position = new RenderBase.OVector3(x, y, z); break; case PICACommand.vshAttribute.normal: vertex.normal = new RenderBase.OVector3(vector.x * normalScale, vector.y * normalScale, vector.z * normalScale); break; case PICACommand.vshAttribute.tangent: vertex.tangent = new RenderBase.OVector3(vector.x * tangentScale, vector.y * tangentScale, vector.z * tangentScale); break; case PICACommand.vshAttribute.color: uint r = MeshUtils.saturate((vector.x * colorScale) * 0xff); uint g = MeshUtils.saturate((vector.y * colorScale) * 0xff); uint b = MeshUtils.saturate((vector.z * colorScale) * 0xff); uint a = MeshUtils.saturate((vector.w * colorScale) * 0xff); vertex.diffuseColor = b | (g << 8) | (r << 16) | (a << 24); break; case PICACommand.vshAttribute.textureCoordinate0: vertex.texture0 = new RenderBase.OVector2(vector.x * texture0Scale, vector.y * texture0Scale); break; case PICACommand.vshAttribute.textureCoordinate1: vertex.texture1 = new RenderBase.OVector2(vector.x * texture1Scale, vector.y * texture1Scale); break; case PICACommand.vshAttribute.textureCoordinate2: vertex.texture2 = new RenderBase.OVector2(vector.x * texture2Scale, vector.y * texture2Scale); break; case PICACommand.vshAttribute.boneIndex: vertex.node.Add(nodeList[(int)vector.x]); if (skinningMode == RenderBase.OSkinningMode.smoothSkinning) { if (format.attributeLength > 0) vertex.node.Add(nodeList[(int)vector.y]); if (format.attributeLength > 1) vertex.node.Add(nodeList[(int)vector.z]); if (format.attributeLength > 2) vertex.node.Add(nodeList[(int)vector.w]); } break; case PICACommand.vshAttribute.boneWeight: vertex.weight.Add(vector.x * boneWeightScale); if (skinningMode == RenderBase.OSkinningMode.smoothSkinning) { if (format.attributeLength > 0) vertex.weight.Add(vector.y * boneWeightScale); if (format.attributeLength > 1) vertex.weight.Add(vector.z * boneWeightScale); if (format.attributeLength > 2) vertex.weight.Add(vector.w * boneWeightScale); } break; } } //If the node list have 4 or less bones, then there is no need to store the indices per vertex //Instead, the entire list is used, since it supports up to 4 bones. if (vertex.node.Count == 0 && nodeList.Count <= 4) { for (int n = 0; n < nodeList.Count; n++) vertex.node.Add(nodeList[n]); if (vertex.weight.Count == 0) vertex.weight.Add(1); } if (skinningMode != RenderBase.OSkinningMode.smoothSkinning && vertex.node.Count > 0) { //Note: Rigid skinning can have only one bone per vertex //Note2: Vertex with Rigid skinning seems to be always have meshes centered, so is necessary to make them follow the skeleton if (vertex.weight.Count == 0) vertex.weight.Add(1); vertex.position = RenderBase.OVector3.transform(vertex.position, skeletonTransform[vertex.node[0]]); } MeshUtils.calculateBounds(model, vertex); obj.vertices.Add(vertex); data.Seek(dataPosition, SeekOrigin.Begin); } } //Bounding box if (objects[objIndex].boundingBoxOffset != 0) { data.Seek(objects[objIndex].boundingBoxOffset, SeekOrigin.Begin); uint bBoxDataOffset = input.ReadUInt32(); uint bBoxEntries = input.ReadUInt32(); uint bBoxNameOffset = input.ReadUInt32(); for (int index = 0; index < bBoxEntries; index++) { data.Seek(bBoxDataOffset + (index * 0xc), SeekOrigin.Begin); RenderBase.OOrientedBoundingBox bBox = new RenderBase.OOrientedBoundingBox(); bBox.name = readString(input); uint flags = input.ReadUInt32(); uint dataOffset = input.ReadUInt32(); data.Seek(dataOffset, SeekOrigin.Begin); bBox.centerPosition = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); bBox.orientationMatrix.M11 = input.ReadSingle(); bBox.orientationMatrix.M21 = input.ReadSingle(); bBox.orientationMatrix.M31 = input.ReadSingle(); bBox.orientationMatrix.M12 = input.ReadSingle(); bBox.orientationMatrix.M22 = input.ReadSingle(); bBox.orientationMatrix.M32 = input.ReadSingle(); bBox.orientationMatrix.M13 = input.ReadSingle(); bBox.orientationMatrix.M23 = input.ReadSingle(); bBox.orientationMatrix.M33 = input.ReadSingle(); bBox.size = new RenderBase.OVector3(input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); obj.boundingBox.Add(bBox); } } model.mesh.Add(obj); } if (modelHeader.metaDataPointerOffset != 0) { data.Seek(modelHeader.metaDataPointerOffset, SeekOrigin.Begin); model.userData = getMetaData(input); } for (int index = 0; index < modelHeader.skeletonEntries; index++) { scaleSkeleton(model.skeleton, index, index); } models.model.Add(model); } data.Close(); return models; }
/// <summary> /// Reads the Model PACKage from Dragon Quest VII. /// </summary> /// <param name="data">Stream of the data</param> /// <returns></returns> public static OContainer load(Stream data) { BinaryReader input = new BinaryReader(data); OContainer output = new OContainer(); List<sectionEntry> mainSection = getSection(input); //World nodes section data.Seek(mainSection[0].offset, SeekOrigin.Begin); List<node> nodes = new List<node>(); List<sectionEntry> worldNodesSection = getSection(input); foreach (sectionEntry entry in worldNodesSection) { data.Seek(entry.offset, SeekOrigin.Begin); node n = new node(); //Geometry node input.ReadUInt32(); //GNOD magic number input.ReadUInt32(); input.ReadUInt32(); n.parentId = input.ReadInt32(); n.name = IOUtils.readString(input, (uint)data.Position); data.Seek(entry.offset + 0x20, SeekOrigin.Begin); n.transform = new RenderBase.OMatrix(); RenderBase.OVector4 t = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); RenderBase.OVector4 r = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); RenderBase.OVector4 s = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); n.transform *= RenderBase.OMatrix.scale(new RenderBase.OVector3(s.x, s.y, s.z)); n.transform *= RenderBase.OMatrix.rotateX(r.x); n.transform *= RenderBase.OMatrix.rotateY(r.y); n.transform *= RenderBase.OMatrix.rotateZ(r.z); n.transform *= RenderBase.OMatrix.translate(new RenderBase.OVector3(t.x, t.y, t.z)); nodes.Add(n); } RenderBase.OMatrix[] nodesTransform = new RenderBase.OMatrix[nodes.Count]; for (int i = 0; i < nodes.Count; i++) { RenderBase.OMatrix transform = new RenderBase.OMatrix(); transformNode(nodes, i, ref transform); nodesTransform[i] = transform; } //Models section data.Seek(mainSection[1].offset, SeekOrigin.Begin); List<sectionEntry> modelsSection = getSection(input); foreach (sectionEntry entry in modelsSection) { data.Seek(entry.offset, SeekOrigin.Begin); //Field Data section /* * Usually have 3 entries. * 1st entry: Model CGFX * 2nd entry: Unknow CGFX, possibly animations * 3rd entry: Another FieldData section, possibly child object */ List<sectionEntry> fieldDataSection = getSection(input); data.Seek(fieldDataSection[0].offset, SeekOrigin.Begin); uint length = fieldDataSection[0].length; while ((length & 0x7f) != 0) length++; //Align byte[] buffer = new byte[length]; input.Read(buffer, 0, buffer.Length); OContainer.fileEntry file = new OContainer.fileEntry(); file.name = CGFX.getName(new MemoryStream(buffer)) + ".bcmdl"; file.data = buffer; output.content.Add(file); } //FILE section data.Seek(mainSection[2].offset, SeekOrigin.Begin); //TODO //Collision section data.Seek(mainSection[3].offset, SeekOrigin.Begin); //TODO //PARM(???) section data.Seek(mainSection[4].offset, SeekOrigin.Begin); //TODO //Textures CGFX data.Seek(mainSection[5].offset, SeekOrigin.Begin); byte[] texBuffer = new byte[mainSection[5].length]; input.Read(texBuffer, 0, texBuffer.Length); OContainer.fileEntry texFile = new OContainer.fileEntry(); texFile.name = "textures.bctex"; texFile.data = texBuffer; output.content.Add(texFile); data.Close(); return output; }
/// <summary> /// Exports a Model to the Collada format. /// See: https://www.khronos.org/files/collada_spec_1_4.pdf for more information. /// </summary> /// <param name="model">The Model that will be exported</param> /// <param name="fileName">The output File Name</param> /// <param name="modelIndex">Index of the model to be exported</param> /// <param name="skeletalAnimationIndex">(Optional) Index of the skeletal animation</param> public static void export(RenderBase.OModelGroup model, string fileName, int modelIndex, int skeletalAnimationIndex = -1) { RenderBase.OModel mdl = model.model[modelIndex]; COLLADA dae = new COLLADA(); dae.asset.created = DateTime.Now.ToString("yyyy-MM-ddThh:mm:ssZ"); dae.asset.modified = dae.asset.created; foreach (RenderBase.OTexture tex in model.texture) { daeImage img = new daeImage(); img.id = tex.name + "_id"; img.name = tex.name; img.init_from = "./" + tex.name + ".png"; dae.library_images.Add(img); } foreach (RenderBase.OMaterial mat in mdl.material) { daeMaterial mtl = new daeMaterial(); mtl.name = mat.name + "_mat"; mtl.id = mtl.name + "_id"; mtl.instance_effect.url = "#eff_" + mat.name + "_id"; dae.library_materials.Add(mtl); daeEffect eff = new daeEffect(); eff.id = "eff_" + mat.name + "_id"; eff.name = "eff_" + mat.name; daeParam surface = new daeParam(); surface.surface = new daeParamSurfaceElement(); surface.sid = "img_surface"; surface.surface.type = "2D"; surface.surface.init_from = mat.name0 + "_id"; surface.surface.format = "PNG"; eff.profile_COMMON.newparam.Add(surface); daeParam sampler = new daeParam(); sampler.sampler2D = new daeParamSampler2DElement(); sampler.sid = "img_sampler"; sampler.sampler2D.source = "img_surface"; switch (mat.textureMapper[0].wrapU) { case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_s = "WRAP"; break; case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_s = "MIRROR"; break; case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_s = "CLAMP"; break; case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_s = "BORDER"; break; default: sampler.sampler2D.wrap_s = "NONE"; break; } switch (mat.textureMapper[0].wrapV) { case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_t = "WRAP"; break; case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_t = "MIRROR"; break; case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_t = "CLAMP"; break; case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_t = "BORDER"; break; default: sampler.sampler2D.wrap_t = "NONE"; break; } switch (mat.textureMapper[0].minFilter) { case RenderBase.OTextureMinFilter.linearMipmapLinear: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_LINEAR"; break; case RenderBase.OTextureMinFilter.linearMipmapNearest: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_NEAREST"; break; case RenderBase.OTextureMinFilter.nearestMipmapLinear: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_LINEAR"; break; case RenderBase.OTextureMinFilter.nearestMipmapNearest: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_NEAREST"; break; default: sampler.sampler2D.minfilter = "NONE"; break; } switch (mat.textureMapper[0].magFilter) { case RenderBase.OTextureMagFilter.linear: sampler.sampler2D.magfilter = "LINEAR"; break; case RenderBase.OTextureMagFilter.nearest: sampler.sampler2D.magfilter = "NEAREST"; break; default: sampler.sampler2D.magfilter = "NONE"; break; } sampler.sampler2D.mipfilter = sampler.sampler2D.magfilter; eff.profile_COMMON.newparam.Add(sampler); eff.profile_COMMON.technique.sid = "img_technique"; eff.profile_COMMON.technique.phong.emission.set(Color.Black); eff.profile_COMMON.technique.phong.ambient.set(Color.Black); eff.profile_COMMON.technique.phong.specular.set(Color.White); eff.profile_COMMON.technique.phong.diffuse.texture.texture = "img_sampler"; dae.library_effects.Add(eff); } string jointNames = null; string invBindPoses = null; for (int index = 0; index < mdl.skeleton.Count; index++) { RenderBase.OMatrix transform = new RenderBase.OMatrix(); transformSkeleton(mdl.skeleton, index, ref transform); jointNames += mdl.skeleton[index].name; daeMatrix mtx = new daeMatrix(); mtx.set(transform.invert()); invBindPoses += mtx.data; if (index < mdl.skeleton.Count - 1) { jointNames += " "; invBindPoses += " "; } } int meshIndex = 0; daeVisualScene vs = new daeVisualScene(); vs.name = "vs_" + mdl.name; vs.id = vs.name + "_id"; if (mdl.skeleton.Count > 0) { writeSkeleton(mdl.skeleton, 0, ref vs.node); } foreach (RenderBase.OMesh obj in mdl.mesh) { //Geometry daeGeometry geometry = new daeGeometry(); string meshName = "mesh_" + meshIndex++ + "_" + obj.name; geometry.id = meshName + "_id"; geometry.name = meshName; MeshUtils.optimizedMesh mesh = MeshUtils.optimizeMesh(obj); List <float> positions = new List <float>(); List <float> normals = new List <float>(); List <float> uv0 = new List <float>(); List <float> uv1 = new List <float>(); List <float> uv2 = new List <float>(); List <float> colors = new List <float>(); foreach (RenderBase.OVertex vtx in mesh.vertices) { positions.Add(vtx.position.x); positions.Add(vtx.position.y); positions.Add(vtx.position.z); if (mesh.hasNormal) { normals.Add(vtx.normal.x); normals.Add(vtx.normal.y); normals.Add(vtx.normal.z); } if (mesh.texUVCount > 0) { uv0.Add(vtx.texture0.x); uv0.Add(vtx.texture0.y); } if (mesh.texUVCount > 1) { uv1.Add(vtx.texture1.x); uv1.Add(vtx.texture1.y); } if (mesh.texUVCount > 2) { uv2.Add(vtx.texture2.x); uv2.Add(vtx.texture2.y); } if (mesh.hasColor) { colors.Add(((vtx.diffuseColor >> 16) & 0xff) / 255f); colors.Add(((vtx.diffuseColor >> 8) & 0xff) / 255f); colors.Add((vtx.diffuseColor & 0xff) / 255f); colors.Add(((vtx.diffuseColor >> 24) & 0xff) / 255f); } } daeSource position = new daeSource(); position.name = meshName + "_position"; position.id = position.name + "_id"; position.float_array = new daeFloatArray(); position.float_array.id = position.name + "_array_id"; position.float_array.set(positions); position.technique_common.accessor.source = "#" + position.float_array.id; position.technique_common.accessor.count = (uint)mesh.vertices.Count; position.technique_common.accessor.stride = 3; position.technique_common.accessor.addParam("X", "float"); position.technique_common.accessor.addParam("Y", "float"); position.technique_common.accessor.addParam("Z", "float"); geometry.mesh.source.Add(position); daeSource normal = new daeSource(); if (mesh.hasNormal) { normal.name = meshName + "_normal"; normal.id = normal.name + "_id"; normal.float_array = new daeFloatArray(); normal.float_array.id = normal.name + "_array_id"; normal.float_array.set(normals); normal.technique_common.accessor.source = "#" + normal.float_array.id; normal.technique_common.accessor.count = (uint)mesh.vertices.Count; normal.technique_common.accessor.stride = 3; normal.technique_common.accessor.addParam("X", "float"); normal.technique_common.accessor.addParam("Y", "float"); normal.technique_common.accessor.addParam("Z", "float"); geometry.mesh.source.Add(normal); } daeSource[] texUV = new daeSource[3]; for (int i = 0; i < mesh.texUVCount; i++) { texUV[i] = new daeSource(); texUV[i].name = meshName + "_uv" + i; texUV[i].id = texUV[i].name + "_id"; texUV[i].float_array = new daeFloatArray(); texUV[i].float_array.id = texUV[i].name + "_array_id"; texUV[i].technique_common.accessor.source = "#" + texUV[i].float_array.id; texUV[i].technique_common.accessor.count = (uint)mesh.vertices.Count; texUV[i].technique_common.accessor.stride = 2; texUV[i].technique_common.accessor.addParam("S", "float"); texUV[i].technique_common.accessor.addParam("T", "float"); geometry.mesh.source.Add(texUV[i]); } daeSource color = new daeSource(); if (mesh.hasColor) { color.name = meshName + "_color"; color.id = color.name + "_id"; color.float_array = new daeFloatArray(); color.float_array.id = color.name + "_array_id"; color.float_array.set(colors); color.technique_common.accessor.source = "#" + color.float_array.id; color.technique_common.accessor.count = (uint)mesh.vertices.Count; color.technique_common.accessor.stride = 4; color.technique_common.accessor.addParam("R", "float"); color.technique_common.accessor.addParam("G", "float"); color.technique_common.accessor.addParam("B", "float"); color.technique_common.accessor.addParam("A", "float"); geometry.mesh.source.Add(color); } geometry.mesh.vertices.id = meshName + "_vertices_id"; geometry.mesh.vertices.addInput("POSITION", "#" + position.id); geometry.mesh.triangles.material = mdl.material[obj.materialId].name; geometry.mesh.triangles.addInput("VERTEX", "#" + geometry.mesh.vertices.id); if (mesh.hasNormal) { geometry.mesh.triangles.addInput("NORMAL", "#" + normal.id); } if (mesh.hasColor) { geometry.mesh.triangles.addInput("COLOR", "#" + color.id); } if (mesh.texUVCount > 0) { texUV[0].float_array.set(uv0); geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[0].id); } if (mesh.texUVCount > 1) { texUV[1].float_array.set(uv1); geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[1].id, 0, 1); } if (mesh.texUVCount > 2) { texUV[2].float_array.set(uv2); geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[2].id, 0, 2); } geometry.mesh.triangles.set(mesh.indices); dae.library_geometries.Add(geometry); bool hasNode = obj.vertices[0].node.Count > 0; bool hasWeight = obj.vertices[0].weight.Count > 0; bool hasController = hasNode && hasWeight; //Controller daeController controller = new daeController(); if (hasController) { controller.id = meshName + "_ctrl_id"; controller.skin.source = "#" + geometry.id; controller.skin.bind_shape_matrix.set(new RenderBase.OMatrix()); daeSource joints = new daeSource(); joints.id = meshName + "_ctrl_joint_names_id"; joints.Name_array = new daeNameArray(); joints.Name_array.id = meshName + "_ctrl_joint_names_array_id"; joints.Name_array.count = (uint)mdl.skeleton.Count; joints.Name_array.data = jointNames; joints.technique_common.accessor.source = "#" + joints.Name_array.id; joints.technique_common.accessor.count = joints.Name_array.count; joints.technique_common.accessor.stride = 1; joints.technique_common.accessor.addParam("JOINT", "Name"); controller.skin.src.Add(joints); daeSource bindPoses = new daeSource(); bindPoses.id = meshName + "_ctrl_inv_bind_poses_id"; bindPoses.float_array = new daeFloatArray(); bindPoses.float_array.id = meshName + "_ctrl_inv_bind_poses_array_id"; bindPoses.float_array.count = (uint)(mdl.skeleton.Count * 16); bindPoses.float_array.data = invBindPoses; bindPoses.technique_common.accessor.source = "#" + bindPoses.float_array.id; bindPoses.technique_common.accessor.count = (uint)mdl.skeleton.Count; bindPoses.technique_common.accessor.stride = 16; bindPoses.technique_common.accessor.addParam("TRANSFORM", "float4x4"); controller.skin.src.Add(bindPoses); daeSource weights = new daeSource(); weights.id = meshName + "_ctrl_weights_id"; weights.float_array = new daeFloatArray(); weights.float_array.id = meshName + "_ctrl_weights_array_id"; weights.technique_common.accessor.source = "#" + weights.float_array.id; weights.technique_common.accessor.stride = 1; weights.technique_common.accessor.addParam("WEIGHT", "float"); StringBuilder w = new StringBuilder(); StringBuilder vcount = new StringBuilder(); StringBuilder v = new StringBuilder(); float[] wLookBack = new float[32]; uint wLookBackIndex = 0; int buffLen = 0; int wIndex = 0; int wCount = 0; foreach (RenderBase.OVertex vtx in mesh.vertices) { int count = Math.Min(vtx.node.Count, vtx.weight.Count); vcount.Append(count + " "); for (int n = 0; n < count; n++) { v.Append(vtx.node[n] + " "); bool found = false; uint bPos = (wLookBackIndex - 1) & 0x1f; for (int i = 0; i < buffLen; i++) { if (wLookBack[bPos] == vtx.weight[n]) { v.Append(wIndex - (i + 1) + " "); found = true; break; } bPos = (bPos - 1) & 0x1f; } if (!found) { v.Append(wIndex++ + " "); w.Append(vtx.weight[n].ToString(CultureInfo.InvariantCulture) + " "); wCount++; wLookBack[wLookBackIndex] = vtx.weight[n]; wLookBackIndex = (wLookBackIndex + 1) & 0x1f; if (buffLen < wLookBack.Length) { buffLen++; } } } } weights.float_array.data = w.ToString().TrimEnd(); weights.float_array.count = (uint)wCount; weights.technique_common.accessor.count = (uint)wCount; controller.skin.src.Add(weights); controller.skin.vertex_weights.vcount = vcount.ToString().TrimEnd(); controller.skin.vertex_weights.v = v.ToString().TrimEnd(); controller.skin.vertex_weights.count = (uint)mesh.vertices.Count; controller.skin.joints.addInput("JOINT", "#" + joints.id); controller.skin.joints.addInput("INV_BIND_MATRIX", "#" + bindPoses.id); controller.skin.vertex_weights.addInput("JOINT", "#" + joints.id); controller.skin.vertex_weights.addInput("WEIGHT", "#" + weights.id, 1); if (dae.library_controllers == null) { dae.library_controllers = new List <daeController>(); } dae.library_controllers.Add(controller); } //Visual scene node daeNode node = new daeNode(); node.name = "vsn_" + meshName; node.id = node.name + "_id"; node.matrix.set(new RenderBase.OMatrix()); if (hasController) { node.instance_controller = new daeInstanceController(); node.instance_controller.url = "#" + controller.id; node.instance_controller.skeleton = "#" + mdl.skeleton[0].name + "_bone_id"; node.instance_controller.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name; node.instance_controller.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id"; } else { node.instance_geometry = new daeInstanceGeometry(); node.instance_geometry.url = "#" + geometry.id; node.instance_geometry.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name; node.instance_geometry.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id"; } vs.node.Add(node); } dae.library_visual_scenes.Add(vs); daeInstaceVisualScene scene = new daeInstaceVisualScene(); scene.url = "#" + vs.id; dae.scene.Add(scene); XmlWriterSettings settings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true }; XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", "http://www.collada.org/2005/11/COLLADASchema"); XmlSerializer serializer = new XmlSerializer(typeof(COLLADA)); XmlWriter output = XmlWriter.Create(new FileStream(fileName, FileMode.Create), settings); serializer.Serialize(output, dae, ns); output.Close(); }
/// <summary> /// Transforms a Skeleton from relative to absolute positions. /// </summary> /// <param name="skeleton">The skeleton</param> /// <param name="index">Index of the bone to convert</param> /// <param name="target">Target matrix to save bone transformation</param> private static void transformSkeleton(List <RenderBase.OBone> skeleton, int index, ref RenderBase.OMatrix target) { target *= RenderBase.OMatrix.rotateX(skeleton[index].rotation.x); target *= RenderBase.OMatrix.rotateY(skeleton[index].rotation.y); target *= RenderBase.OMatrix.rotateZ(skeleton[index].rotation.z); target *= RenderBase.OMatrix.translate(skeleton[index].translation); if (skeleton[index].parentId > -1) { transformSkeleton(skeleton, skeleton[index].parentId, ref target); } }
/// <summary> /// Exports a Model to the Collada format. /// See: https://www.khronos.org/files/collada_spec_1_4.pdf for more information. /// </summary> /// <param name="model">The Model that will be exported</param> /// <param name="fileName">The output File Name</param> /// <param name="modelIndex">Index of the model to be exported</param> /// <param name="skeletalAnimationIndex">(Optional) Index of the skeletal animation</param> public static void export(RenderBase.OModelGroup model, string fileName, int modelIndex, int skeletalAnimationIndex = -1) { RenderBase.OModel mdl = model.model[modelIndex]; COLLADA dae = new COLLADA(); dae.asset.created = DateTime.Now.ToString("yyyy-MM-ddThh:mm:ssZ"); dae.asset.modified = dae.asset.created; foreach (RenderBase.OTexture tex in model.texture) { daeImage img = new daeImage(); img.id = tex.name + "_id"; img.name = tex.name; img.init_from = "./" + tex.name + ".png"; dae.library_images.Add(img); } foreach (RenderBase.OMaterial mat in mdl.material) { daeMaterial mtl = new daeMaterial(); mtl.name = mat.name + "_mat"; mtl.id = mtl.name + "_id"; mtl.instance_effect.url = "#eff_" + mat.name + "_id"; dae.library_materials.Add(mtl); daeEffect eff = new daeEffect(); eff.id = "eff_" + mat.name + "_id"; eff.name = "eff_" + mat.name; daeParam surface = new daeParam(); surface.surface = new daeParamSurfaceElement(); surface.sid = "img_surface"; surface.surface.type = "2D"; surface.surface.init_from = mat.name0 + "_id"; surface.surface.format = "PNG"; eff.profile_COMMON.newparam.Add(surface); daeParam sampler = new daeParam(); sampler.sampler2D = new daeParamSampler2DElement(); sampler.sid = "img_sampler"; sampler.sampler2D.source = "img_surface"; switch (mat.textureMapper[0].wrapU) { case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_s = "WRAP"; break; case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_s = "MIRROR"; break; case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_s = "CLAMP"; break; case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_s = "BORDER"; break; default: sampler.sampler2D.wrap_s = "NONE"; break; } switch (mat.textureMapper[0].wrapV) { case RenderBase.OTextureWrap.repeat: sampler.sampler2D.wrap_t = "WRAP"; break; case RenderBase.OTextureWrap.mirroredRepeat: sampler.sampler2D.wrap_t = "MIRROR"; break; case RenderBase.OTextureWrap.clampToEdge: sampler.sampler2D.wrap_t = "CLAMP"; break; case RenderBase.OTextureWrap.clampToBorder: sampler.sampler2D.wrap_t = "BORDER"; break; default: sampler.sampler2D.wrap_t = "NONE"; break; } switch (mat.textureMapper[0].minFilter) { case RenderBase.OTextureMinFilter.linearMipmapLinear: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_LINEAR"; break; case RenderBase.OTextureMinFilter.linearMipmapNearest: sampler.sampler2D.minfilter = "LINEAR_MIPMAP_NEAREST"; break; case RenderBase.OTextureMinFilter.nearestMipmapLinear: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_LINEAR"; break; case RenderBase.OTextureMinFilter.nearestMipmapNearest: sampler.sampler2D.minfilter = "NEAREST_MIPMAP_NEAREST"; break; default: sampler.sampler2D.minfilter = "NONE"; break; } switch (mat.textureMapper[0].magFilter) { case RenderBase.OTextureMagFilter.linear: sampler.sampler2D.magfilter = "LINEAR"; break; case RenderBase.OTextureMagFilter.nearest: sampler.sampler2D.magfilter = "NEAREST"; break; default: sampler.sampler2D.magfilter = "NONE"; break; } sampler.sampler2D.mipfilter = sampler.sampler2D.magfilter; eff.profile_COMMON.newparam.Add(sampler); eff.profile_COMMON.technique.sid = "img_technique"; eff.profile_COMMON.technique.phong.emission.set(Color.Black); eff.profile_COMMON.technique.phong.ambient.set(Color.Black); eff.profile_COMMON.technique.phong.specular.set(Color.White); eff.profile_COMMON.technique.phong.diffuse.texture.texture = "img_sampler"; dae.library_effects.Add(eff); } string jointNames = null; string invBindPoses = null; for (int index = 0; index < mdl.skeleton.Count; index++) { RenderBase.OMatrix transform = new RenderBase.OMatrix(); transformSkeleton(mdl.skeleton, index, ref transform); jointNames += mdl.skeleton[index].name; daeMatrix mtx = new daeMatrix(); mtx.set(transform.invert()); invBindPoses += mtx.data; if (index < mdl.skeleton.Count - 1) { jointNames += " "; invBindPoses += " "; } } int meshIndex = 0; daeVisualScene vs = new daeVisualScene(); vs.name = "vs_" + mdl.name; vs.id = vs.name + "_id"; if (mdl.skeleton.Count > 0) writeSkeleton(mdl.skeleton, 0, ref vs.node); foreach (RenderBase.OMesh obj in mdl.mesh) { //Geometry daeGeometry geometry = new daeGeometry(); string meshName = "mesh_" + meshIndex++ + "_" + obj.name; geometry.id = meshName + "_id"; geometry.name = meshName; MeshUtils.optimizedMesh mesh = MeshUtils.optimizeMesh(obj); List<float> positions = new List<float>(); List<float> normals = new List<float>(); List<float> uv0 = new List<float>(); List<float> uv1 = new List<float>(); List<float> uv2 = new List<float>(); List<float> colors = new List<float>(); foreach (RenderBase.OVertex vtx in mesh.vertices) { positions.Add(vtx.position.x); positions.Add(vtx.position.y); positions.Add(vtx.position.z); if (mesh.hasNormal) { normals.Add(vtx.normal.x); normals.Add(vtx.normal.y); normals.Add(vtx.normal.z); } if (mesh.texUVCount > 0) { uv0.Add(vtx.texture0.x); uv0.Add(vtx.texture0.y); } if (mesh.texUVCount > 1) { uv1.Add(vtx.texture1.x); uv1.Add(vtx.texture1.y); } if (mesh.texUVCount > 2) { uv2.Add(vtx.texture2.x); uv2.Add(vtx.texture2.y); } if (mesh.hasColor) { colors.Add(((vtx.diffuseColor >> 16) & 0xff) / 255f); colors.Add(((vtx.diffuseColor >> 8) & 0xff) / 255f); colors.Add((vtx.diffuseColor & 0xff) / 255f); colors.Add(((vtx.diffuseColor >> 24) & 0xff) / 255f); } } daeSource position = new daeSource(); position.name = meshName + "_position"; position.id = position.name + "_id"; position.float_array = new daeFloatArray(); position.float_array.id = position.name + "_array_id"; position.float_array.set(positions); position.technique_common.accessor.source = "#" + position.float_array.id; position.technique_common.accessor.count = (uint)mesh.vertices.Count; position.technique_common.accessor.stride = 3; position.technique_common.accessor.addParam("X", "float"); position.technique_common.accessor.addParam("Y", "float"); position.technique_common.accessor.addParam("Z", "float"); geometry.mesh.source.Add(position); daeSource normal = new daeSource(); if (mesh.hasNormal) { normal.name = meshName + "_normal"; normal.id = normal.name + "_id"; normal.float_array = new daeFloatArray(); normal.float_array.id = normal.name + "_array_id"; normal.float_array.set(normals); normal.technique_common.accessor.source = "#" + normal.float_array.id; normal.technique_common.accessor.count = (uint)mesh.vertices.Count; normal.technique_common.accessor.stride = 3; normal.technique_common.accessor.addParam("X", "float"); normal.technique_common.accessor.addParam("Y", "float"); normal.technique_common.accessor.addParam("Z", "float"); geometry.mesh.source.Add(normal); } daeSource[] texUV = new daeSource[3]; for (int i = 0; i < mesh.texUVCount; i++) { texUV[i] = new daeSource(); texUV[i].name = meshName + "_uv" + i; texUV[i].id = texUV[i].name + "_id"; texUV[i].float_array = new daeFloatArray(); texUV[i].float_array.id = texUV[i].name + "_array_id"; texUV[i].technique_common.accessor.source = "#" + texUV[i].float_array.id; texUV[i].technique_common.accessor.count = (uint)mesh.vertices.Count; texUV[i].technique_common.accessor.stride = 2; texUV[i].technique_common.accessor.addParam("S", "float"); texUV[i].technique_common.accessor.addParam("T", "float"); geometry.mesh.source.Add(texUV[i]); } daeSource color = new daeSource(); if (mesh.hasColor) { color.name = meshName + "_color"; color.id = color.name + "_id"; color.float_array = new daeFloatArray(); color.float_array.id = color.name + "_array_id"; color.float_array.set(colors); color.technique_common.accessor.source = "#" + color.float_array.id; color.technique_common.accessor.count = (uint)mesh.vertices.Count; color.technique_common.accessor.stride = 4; color.technique_common.accessor.addParam("R", "float"); color.technique_common.accessor.addParam("G", "float"); color.technique_common.accessor.addParam("B", "float"); color.technique_common.accessor.addParam("A", "float"); geometry.mesh.source.Add(color); } geometry.mesh.vertices.id = meshName + "_vertices_id"; geometry.mesh.vertices.addInput("POSITION", "#" + position.id); geometry.mesh.triangles.material = mdl.material[obj.materialId].name; geometry.mesh.triangles.addInput("VERTEX", "#" + geometry.mesh.vertices.id); if (mesh.hasNormal) geometry.mesh.triangles.addInput("NORMAL", "#" + normal.id); if (mesh.hasColor) geometry.mesh.triangles.addInput("COLOR", "#" + color.id); if (mesh.texUVCount > 0) { texUV[0].float_array.set(uv0); geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[0].id); } if (mesh.texUVCount > 1) { texUV[1].float_array.set(uv1); geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[1].id, 0, 1); } if (mesh.texUVCount > 2) { texUV[2].float_array.set(uv2); geometry.mesh.triangles.addInput("TEXCOORD", "#" + texUV[2].id, 0, 2); } geometry.mesh.triangles.set(mesh.indices); dae.library_geometries.Add(geometry); bool hasNode = obj.vertices[0].node.Count > 0; bool hasWeight = obj.vertices[0].weight.Count > 0; bool hasController = hasNode && hasWeight; //Controller daeController controller = new daeController(); if (hasController) { controller.id = meshName + "_ctrl_id"; controller.skin.source = "#" + geometry.id; controller.skin.bind_shape_matrix.set(new RenderBase.OMatrix()); daeSource joints = new daeSource(); joints.id = meshName + "_ctrl_joint_names_id"; joints.Name_array = new daeNameArray(); joints.Name_array.id = meshName + "_ctrl_joint_names_array_id"; joints.Name_array.count = (uint)mdl.skeleton.Count; joints.Name_array.data = jointNames; joints.technique_common.accessor.source = "#" + joints.Name_array.id; joints.technique_common.accessor.count = joints.Name_array.count; joints.technique_common.accessor.stride = 1; joints.technique_common.accessor.addParam("JOINT", "Name"); controller.skin.src.Add(joints); daeSource bindPoses = new daeSource(); bindPoses.id = meshName +"_ctrl_inv_bind_poses_id"; bindPoses.float_array = new daeFloatArray(); bindPoses.float_array.id = meshName + "_ctrl_inv_bind_poses_array_id"; bindPoses.float_array.count = (uint)(mdl.skeleton.Count * 16); bindPoses.float_array.data = invBindPoses; bindPoses.technique_common.accessor.source = "#" + bindPoses.float_array.id; bindPoses.technique_common.accessor.count = (uint)mdl.skeleton.Count; bindPoses.technique_common.accessor.stride = 16; bindPoses.technique_common.accessor.addParam("TRANSFORM", "float4x4"); controller.skin.src.Add(bindPoses); daeSource weights = new daeSource(); weights.id = meshName + "_ctrl_weights_id"; weights.float_array = new daeFloatArray(); weights.float_array.id = meshName + "_ctrl_weights_array_id"; weights.technique_common.accessor.source = "#" + weights.float_array.id; weights.technique_common.accessor.stride = 1; weights.technique_common.accessor.addParam("WEIGHT", "float"); StringBuilder w = new StringBuilder(); StringBuilder vcount = new StringBuilder(); StringBuilder v = new StringBuilder(); float[] wLookBack = new float[32]; uint wLookBackIndex = 0; int buffLen = 0; int wIndex = 0; int wCount = 0; foreach (RenderBase.OVertex vtx in mesh.vertices) { int count = Math.Min(vtx.node.Count, vtx.weight.Count); vcount.Append(count + " "); for (int n = 0; n < count; n++) { v.Append(vtx.node[n] + " "); bool found = false; uint bPos = (wLookBackIndex - 1) & 0x1f; for (int i = 0; i < buffLen; i++) { if (wLookBack[bPos] == vtx.weight[n]) { v.Append(wIndex - (i + 1) + " "); found = true; break; } bPos = (bPos - 1) & 0x1f; } if (!found) { v.Append(wIndex++ + " "); w.Append(vtx.weight[n].ToString(CultureInfo.InvariantCulture) + " "); wCount++; wLookBack[wLookBackIndex] = vtx.weight[n]; wLookBackIndex = (wLookBackIndex + 1) & 0x1f; if (buffLen < wLookBack.Length) buffLen++; } } } weights.float_array.data = w.ToString().TrimEnd(); weights.float_array.count = (uint)wCount; weights.technique_common.accessor.count = (uint)wCount; controller.skin.src.Add(weights); controller.skin.vertex_weights.vcount = vcount.ToString().TrimEnd(); controller.skin.vertex_weights.v = v.ToString().TrimEnd(); controller.skin.vertex_weights.count = (uint)mesh.vertices.Count; controller.skin.joints.addInput("JOINT", "#" + joints.id); controller.skin.joints.addInput("INV_BIND_MATRIX", "#" + bindPoses.id); controller.skin.vertex_weights.addInput("JOINT", "#" + joints.id); controller.skin.vertex_weights.addInput("WEIGHT", "#" + weights.id, 1); if (dae.library_controllers == null) dae.library_controllers = new List<daeController>(); dae.library_controllers.Add(controller); } //Visual scene node daeNode node = new daeNode(); node.name = "vsn_" + meshName; node.id = node.name + "_id"; node.matrix.set(new RenderBase.OMatrix()); if (hasController) { node.instance_controller = new daeInstanceController(); node.instance_controller.url = "#" + controller.id; node.instance_controller.skeleton = "#" + mdl.skeleton[0].name + "_bone_id"; node.instance_controller.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name; node.instance_controller.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id"; } else { node.instance_geometry = new daeInstanceGeometry(); node.instance_geometry.url = "#" + geometry.id; node.instance_geometry.bind_material.technique_common.instance_material.symbol = mdl.material[obj.materialId].name; node.instance_geometry.bind_material.technique_common.instance_material.target = "#" + mdl.material[obj.materialId].name + "_mat_id"; } vs.node.Add(node); } dae.library_visual_scenes.Add(vs); daeInstaceVisualScene scene = new daeInstaceVisualScene(); scene.url = "#" + vs.id; dae.scene.Add(scene); XmlWriterSettings settings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true }; XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); ns.Add("", "http://www.collada.org/2005/11/COLLADASchema"); XmlSerializer serializer = new XmlSerializer(typeof(COLLADA)); XmlWriter output = XmlWriter.Create(new FileStream(fileName, FileMode.Create), settings); serializer.Serialize(output, dae, ns); output.Close(); }
/// <summary> /// Reads the Model PACKage from Dragon Quest VII. /// </summary> /// <param name="data">Stream of the data</param> /// <returns></returns> public static OContainer load(Stream data) { BinaryReader input = new BinaryReader(data); OContainer output = new OContainer(); List <sectionEntry> mainSection = getSection(input); //World nodes section data.Seek(mainSection[0].offset, SeekOrigin.Begin); List <node> nodes = new List <node>(); List <sectionEntry> worldNodesSection = getSection(input); foreach (sectionEntry entry in worldNodesSection) { data.Seek(entry.offset, SeekOrigin.Begin); node n = new node(); //Geometry node input.ReadUInt32(); //GNOD magic number input.ReadUInt32(); input.ReadUInt32(); n.parentId = input.ReadInt32(); n.name = IOUtils.readString(input, (uint)data.Position); data.Seek(entry.offset + 0x20, SeekOrigin.Begin); n.transform = new RenderBase.OMatrix(); RenderBase.OVector4 t = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); RenderBase.OVector4 r = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); RenderBase.OVector4 s = new RenderBase.OVector4(input.ReadSingle(), input.ReadSingle(), input.ReadSingle(), input.ReadSingle()); n.transform *= RenderBase.OMatrix.scale(new RenderBase.OVector3(s.x, s.y, s.z)); n.transform *= RenderBase.OMatrix.rotateX(r.x); n.transform *= RenderBase.OMatrix.rotateY(r.y); n.transform *= RenderBase.OMatrix.rotateZ(r.z); n.transform *= RenderBase.OMatrix.translate(new RenderBase.OVector3(t.x, t.y, t.z)); nodes.Add(n); } RenderBase.OMatrix[] nodesTransform = new RenderBase.OMatrix[nodes.Count]; for (int i = 0; i < nodes.Count; i++) { RenderBase.OMatrix transform = new RenderBase.OMatrix(); transformNode(nodes, i, ref transform); nodesTransform[i] = transform; } //Models section data.Seek(mainSection[1].offset, SeekOrigin.Begin); List <sectionEntry> modelsSection = getSection(input); foreach (sectionEntry entry in modelsSection) { data.Seek(entry.offset, SeekOrigin.Begin); //Field Data section /* * Usually have 3 entries. * 1st entry: Model CGFX * 2nd entry: Unknow CGFX, possibly animations * 3rd entry: Another FieldData section, possibly child object */ List <sectionEntry> fieldDataSection = getSection(input); data.Seek(fieldDataSection[0].offset, SeekOrigin.Begin); uint length = fieldDataSection[0].length; while ((length & 0x7f) != 0) { length++; //Align } byte[] buffer = new byte[length]; input.Read(buffer, 0, buffer.Length); OContainer.fileEntry file = new OContainer.fileEntry(); file.name = CGFX.getName(new MemoryStream(buffer)) + ".bcmdl"; file.data = buffer; output.content.Add(file); } //FILE section data.Seek(mainSection[2].offset, SeekOrigin.Begin); //TODO //Collision section data.Seek(mainSection[3].offset, SeekOrigin.Begin); //TODO //PARM(???) section data.Seek(mainSection[4].offset, SeekOrigin.Begin); //TODO //Textures CGFX data.Seek(mainSection[5].offset, SeekOrigin.Begin); byte[] texBuffer = new byte[mainSection[5].length]; input.Read(texBuffer, 0, texBuffer.Length); OContainer.fileEntry texFile = new OContainer.fileEntry(); texFile.name = "textures.bctex"; texFile.data = texBuffer; output.content.Add(texFile); data.Close(); return(output); }