// Unity /// <summary> /// Convert the PMX model into Unity meshes with correct materials. /// </summary> /// <returns>A parent GameObject that holds the sub-meshes in its children.</returns> public GameObject GetGameObject() { bool cancel = false; GameObject root = new GameObject(Name); GameObject modelParent = new GameObject("Model"); GameObject boneParent = new GameObject("Skeleton"); Material baseOpaque = Resources.Load <Material>("Materials/DefaultMaterial"); Material baseTransparent = Resources.Load <Material>("Materials/DefaultMaterialTransparent"); Material baseFade = Resources.Load <Material>("Materials/DefaultMaterialFade"); Material baseCutout = Resources.Load <Material>("Materials/DefaultMaterialCutout"); GameObject boneSpritePrefab = Resources.Load <GameObject>("Prefabs/BoneSprite"); List <Transform> boneObjectList = new List <Transform>(); // Skeleton... recursion is recursion. foreach (PmxBone rootBone in PmxBone.RootBones(Bones)) { GameObject rootBoneObject = BoneHierarchy.BuildHierarchy(rootBone, Bones, boneObjectList, boneSpritePrefab); boneObjectList.Add(rootBoneObject.transform); rootBoneObject.transform.SetParent(boneParent.transform); rootBoneObject.transform.position = rootBone.Position; } // Model for (int i = 0; i < Materials.Count; ++i) { PmxMaterial mat = Materials[i]; GameObject o = new GameObject(string.Format("{0} ({1})", Name, mat.NameJapanese)); // MESH List <PmxVertex> vert = new List <PmxVertex>(); // List of vertices that make up the sub-model List <int> tri = new List <int>(); // Every index corresponds to an index in vert List <BoneWeight> weights = new List <BoneWeight>(); List <Matrix4x4> bindposes = new List <Matrix4x4>(); foreach (PmxTriangle t in mat.Triangles(Triangles)) { vert.Add(Vertices[t.Vertex1]); tri.Add(vert.Count - 1); vert.Add(Vertices[t.Vertex2]); tri.Add(vert.Count - 1); vert.Add(Vertices[t.Vertex3]); tri.Add(vert.Count - 1); weights.Add(Vertices[t.Vertex1].DeformData.GetUnityWeight()); bindposes.Add(boneObjectList[weights[weights.Count - 1].boneIndex0].worldToLocalMatrix); weights.Add(Vertices[t.Vertex2].DeformData.GetUnityWeight()); bindposes.Add(boneObjectList[weights[weights.Count - 1].boneIndex0].worldToLocalMatrix); weights.Add(Vertices[t.Vertex3].DeformData.GetUnityWeight()); bindposes.Add(boneObjectList[weights[weights.Count - 1].boneIndex0].worldToLocalMatrix); } Mesh mesh = new Mesh(); mesh.name = mat.NameJapanese; mesh.vertices = PmxVertex.GetPositions(vert).ToArray(); mesh.normals = PmxVertex.GetNormals(vert).ToArray(); mesh.uv = PmxVertex.GetUVs(vert).ToArray(); mesh.triangles = tri.ToArray(); mesh.boneWeights = weights.ToArray(); mesh.bindposes = bindposes.ToArray(); o.AddComponent <MeshFilter>().sharedMesh = mesh; SkinnedMeshRenderer renderer = o.AddComponent <SkinnedMeshRenderer>(); renderer.sharedMesh = mesh; renderer.updateWhenOffscreen = true; renderer.bones = boneObjectList.ToArray(); // SKELETON // MATERIAL // Texture Texture2D tex = null; if (!string.IsNullOrEmpty(mat.DiffuseTexturePath)) { string path = Path.Combine(Path.GetDirectoryName(FilePath), mat.DiffuseTexturePath); if (File.Exists(path)) { tex = PmxMaterial.LoadTexture(path); } } // Execute early rendering mode directives RenderModeDirective.RenderMode mode = RenderModeDirective.RenderMode.Opaque; foreach (RenderModeDirective dir in mat.Directives.OfType <RenderModeDirective>()) { mode = dir.Mode; if (dir.AutoDetect) { if (!PmxMaterial.IsTransparent(mat, tex, dir.Threshold)) { mode = RenderModeDirective.RenderMode.Opaque; } } } // Set up material Material material; switch (mode) { case RenderModeDirective.RenderMode.Cutout: material = Material.Instantiate <Material>(baseCutout); break; case RenderModeDirective.RenderMode.Fade: material = Material.Instantiate <Material>(baseFade); break; case RenderModeDirective.RenderMode.Transparent: material = Material.Instantiate <Material>(baseTransparent); break; default: material = Material.Instantiate <Material>(baseOpaque); break; } material.name = mat.NameJapanese; material.color = mat.DiffuseColor; if (tex != null) { material.mainTexture = tex; } // Execute directives foreach (MaterialDirective dir in mat.Directives) { dir.Execute(material); } // Set up GameObject and components renderer.sharedMaterial = material; renderer.shadowCastingMode = mat.HasFlag(PmxMaterial.MaterialFlags.CastShadow | PmxMaterial.MaterialFlags.GroundShadow) ? ShadowCastingMode.On : ShadowCastingMode.Off; //renderer.receiveShadows = mat.HasFlag(PmxMaterial.MaterialFlags.ReceiveShadow); // Execute renderer directives foreach (MaterialDirective dir in mat.Directives) { dir.Execute(renderer); } o.transform.SetParent(modelParent.transform); if (cancel) { break; } } modelParent.transform.SetParent(root.transform); boneParent.transform.SetParent(root.transform); // Release loaded prefabs - source of memory leak? unneeded? //GameObject.Destroy(baseOpaque); //GameObject.Destroy(baseTransparent); //GameObject.Destroy(baseFade); //GameObject.Destroy(baseCutout); //GameObject.Destroy(boneSpritePrefab); if (cancel) { GameObject.Destroy(root); return(null); } return(root); }
// Loading #pragma warning disable 0162 /// <summary> /// Read and process the file. /// </summary> /// <param name="r">The binary reader pointing at the PMX file.</param> private void LoadPmx(BinaryReader r) { #region Header // Signature (4) byte[] sig = r.ReadBytes(PmxConstants.SignatureLength); if (!sig.ValidatePmxSignature()) { throw new PmxSignatureException(sig); } // Version (4) float parsedVersion = r.ReadSingle(); if (parsedVersion == 2.0f) { Version = PmxVersion.Pmx20; } else if (parsedVersion == 2.1f) { Version = PmxVersion.Pmx21; } // Globals (9) r.ReadByte(); // This is always 8 in PMX 2.0, safe to ignore TextEncoding = r.ReadByte() == 0 ? Encoding.Unicode : Encoding.UTF8; AdditionalUVCount = r.ReadByte(); // 0 to 4 PmxTypes.VertexIndex = r.ReadByte(); // Vertex index types are byte (1), ushort (2) or int (4) PmxTypes.TextureIndex = r.ReadByte(); // All other index types are sbyte (1), short (2) or int (4) PmxTypes.MaterialIndex = r.ReadByte(); PmxTypes.BoneIndex = r.ReadByte(); PmxTypes.MorphIndex = r.ReadByte(); PmxTypes.RigidbodyIndex = r.ReadByte(); // Model information NameJapanese = r.ReadPmxString(TextEncoding); NameEnglish = r.ReadPmxString(TextEncoding); InfoJapanese = r.ReadPmxString(TextEncoding); InfoEnglish = r.ReadPmxString(TextEncoding); #endregion #region Vertex data Vertices = new List <PmxVertex>(r.ReadInt32()); for (int i = 0; i < Vertices.Capacity; ++i) { // Standard vectors PmxVertex v = new PmxVertex(); v.Position = r.ReadVector3(); v.Normal = r.ReadVector3(); v.UV = r.ReadVector2() * new Vector2(1, -1); // Additional UV v.UVAdditional = new List <Vector4>(AdditionalUVCount); for (int j = 0; j < AdditionalUVCount; ++j) { v.UVAdditional.Add(r.ReadVector4()); } // Deform byte deformID = r.ReadByte(); switch (deformID) { case 0: // BDEF1 v.DeformData = new Bdef1Deform(r.ReadIndex(PmxTypes.IndexType.Bone)); break; case 1: // BDEF2 v.DeformData = new Bdef2Deform(r.ReadIndex(PmxTypes.IndexType.Bone), r.ReadIndex(PmxTypes.IndexType.Bone), r.ReadSingle()); break; case 2: // BDEF4 v.DeformData = new Bdef4Deform() { Bone1 = r.ReadIndex(PmxTypes.IndexType.Bone), Bone2 = r.ReadIndex(PmxTypes.IndexType.Bone), Bone3 = r.ReadIndex(PmxTypes.IndexType.Bone), Bone4 = r.ReadIndex(PmxTypes.IndexType.Bone), Weight1 = r.ReadSingle(), Weight2 = r.ReadSingle(), Weight3 = r.ReadSingle(), Weight4 = r.ReadSingle(), }; break; case 3: // SDEF v.DeformData = new SdefDeform() { Bone1 = r.ReadIndex(PmxTypes.IndexType.Bone), Bone2 = r.ReadIndex(PmxTypes.IndexType.Bone), Weight1 = r.ReadSingle(), C = r.ReadVector3(), R0 = r.ReadVector3(), R1 = r.ReadVector3() }; break; case 4: // QDEF v.DeformData = new QdefDeform() { Bone1 = r.ReadIndex(PmxTypes.IndexType.Bone), Bone2 = r.ReadIndex(PmxTypes.IndexType.Bone), Bone3 = r.ReadIndex(PmxTypes.IndexType.Bone), Bone4 = r.ReadIndex(PmxTypes.IndexType.Bone), Weight1 = r.ReadSingle(), Weight2 = r.ReadSingle(), Weight3 = r.ReadSingle(), Weight4 = r.ReadSingle(), }; break; default: throw new PmxDeformException(deformID); } v.EdgeSize = r.ReadSingle(); Vertices.Add(v); } #endregion #region Triangle data int trianglePointCount = r.ReadInt32(); if ((trianglePointCount % 3) != 0) { throw new PmxFormatException(string.Format("The triangle count is incorrect. Expected divisible by 3, got {0} (remainder {1})", trianglePointCount, trianglePointCount % 3)); } Triangles = new List <PmxTriangle>(trianglePointCount / 3); for (int i = 0; i < Triangles.Capacity; ++i) { PmxTriangle tri = new PmxTriangle(r.ReadIndex(PmxTypes.IndexType.Vertex), r.ReadIndex(PmxTypes.IndexType.Vertex), r.ReadIndex(PmxTypes.IndexType.Vertex)); Triangles.Add(tri); } #endregion #region Texture list Textures = new List <string>(r.ReadInt32()); for (int i = 0; i < Textures.Capacity; ++i) { Textures.Add(r.ReadPmxString(TextEncoding)); } #endregion #region Material data int assignedVertices = 0; Materials = new List <PmxMaterial>(r.ReadInt32()); for (int i = 0; i < Materials.Capacity; ++i) { PmxMaterial m = new PmxMaterial(r.ReadPmxString(TextEncoding), r.ReadPmxString(TextEncoding)); // Colors m.DiffuseColor = r.ReadColor4(); m.SpecularColor = r.ReadColor3(); m.SpecularExponent = r.ReadSingle(); m.AmbientColor = r.ReadColor3(); // Properties m.Flags = (PmxMaterial.MaterialFlags)r.ReadByte(); m.EdgeColor = r.ReadColor4(); m.EdgeSize = r.ReadSingle(); // Textures int index = r.ReadIndex(PmxTypes.IndexType.Texture); m.DiffuseTexturePath = index < 0 ? "" : Textures[index]; index = r.ReadIndex(PmxTypes.IndexType.Texture); m.SphereTexturePath = index < 0 ? "" : Textures[index]; m.SphereBlending = (PmxMaterial.SphereBlendingMode)r.ReadByte(); m.ToonReference = (PmxMaterial.ToonReferenceType)r.ReadByte(); if (m.ToonReference == PmxMaterial.ToonReferenceType.Internal) { index = r.ReadIndex(PmxTypes.IndexType.Texture); try { m.ToonTexturePath = index < 0 ? "" : Textures[index]; } catch { } } else { m.ToonInternalIndex = r.ReadByte(); } // Comment m.Note = r.ReadPmxString(TextEncoding); // Surfaces m.FirstVertex = assignedVertices; m.VertexCount = r.ReadInt32(); assignedVertices += m.VertexCount; Materials.Add(m); } #endregion #region Bone data Bones = new List <PmxBone>(r.ReadInt32()); for (int i = 0; i < Bones.Capacity; ++i) { PmxBone bone = r.ReadPmxBone(TextEncoding); bone.Index = Bones.Count; Bones.Add(bone); } #endregion #region Morph data Morphs = new List <PmxMorph>(r.ReadInt32()); for (int i = 0; i < Morphs.Capacity; ++i) { Morphs.Add(r.ReadPmxMorph(TextEncoding)); } #endregion // SKIP FROM HERE r.Close(); return; #region Frame data #endregion #region Rigidbody data #endregion #region Joint data #endregion #region Soft body data #endregion }