private PmxRigidBody ReadPmxRigidBody() { var body = new PmxRigidBody(); body.Name = ReadString() ?? string.Empty; body.NameEnglish = ReadString() ?? string.Empty; body.BoneIndex = _reader.ReadVarLenIntAsInt32(BoneElementSize); body.GroupIndex = _reader.ReadByte(); // TODO: Signed? Unsigned? var bits = _reader.ReadUInt16(); var passGroup = PmxBodyPassGroup.FromFlagBits(bits); body.PassGroup = passGroup; body.BoundingBoxKind = (BoundingBoxKind)_reader.ReadByte(); body.BoundingBoxSize = _reader.ReadVector3(); body.Position = _reader.ReadVector3(); body.RotationAngles = _reader.ReadVector3(); body.Mass = _reader.ReadSingle(); body.PositionDamping = _reader.ReadSingle(); body.RotationDamping = _reader.ReadSingle(); body.Restitution = _reader.ReadSingle(); body.Friction = _reader.ReadSingle(); body.KineticMode = (KineticMode)_reader.ReadByte(); return(body); }
private void WritePmxRigidBody([NotNull] PmxRigidBody body) { WriteString(body.Name); WriteString(body.NameEnglish); _writer.WriteInt32AsVarLenInt(body.BoneIndex, BoneElementSize); _writer.Write((byte)body.GroupIndex); _writer.Write(body.PassGroup.ToFlagBits()); _writer.Write((byte)body.BoundingBoxKind); _writer.Write(body.BoundingBoxSize); _writer.Write(body.Position); _writer.Write(body.RotationAngles); _writer.Write(body.Mass); _writer.Write(body.PositionDamping); _writer.Write(body.RotationDamping); _writer.Write(body.Restitution); _writer.Write(body.Friction); _writer.Write((byte)body.KineticMode); }
private void AppendStaticBodies([NotNull, ItemNotNull] PmxBone[] bones, [NotNull, ItemNotNull] SwayCollider[] swayColliders, [NotNull, ItemNotNull] List <PmxRigidBody> bodies) { foreach (var collider in swayColliders) { var mltdBoneName = collider.Path; if (mltdBoneName.Contains(BoneLookup.BoneNamePart_BodyScale)) { mltdBoneName = mltdBoneName.Replace(BoneLookup.BoneNamePart_BodyScale, string.Empty); } var pmxBoneName = _pmxCreator._boneLookup.GetPmxBoneName(mltdBoneName); var body = new PmxRigidBody(); var correspondingBone = bones.Find(o => o.Name == pmxBoneName); Debug.Assert(correspondingBone != null, nameof(correspondingBone) + " != null"); body.Name = correspondingBone.Name; body.NameEnglish = correspondingBone.NameEnglish; body.BoneIndex = correspondingBone.BoneIndex; body.KineticMode = KineticMode.Static; body.Mass = 1; body.PositionDamping = 0.9f; body.RotationDamping = 1.0f; body.Friction = 0.0f; body.Restitution = 0.0f; var part = GetBodyCoordSystemPart(collider); body.Part = part; switch (part) { case CoordSystemPart.Torso: body.GroupIndex = PassGroupIndex.Torso; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Torso); body.PassGroup.Pass(PassGroupIndex.Arms); body.PassGroup.Pass(PassGroupIndex.Legs); body.PassGroup.Pass(PassGroupIndex.Head); break; case CoordSystemPart.LeftArm: case CoordSystemPart.RightArm: body.GroupIndex = PassGroupIndex.Arms; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Torso); body.PassGroup.Pass(PassGroupIndex.Arms); body.PassGroup.Pass(PassGroupIndex.Legs); body.PassGroup.Pass(PassGroupIndex.Head); break; case CoordSystemPart.Legs: body.GroupIndex = PassGroupIndex.Legs; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Torso); body.PassGroup.Pass(PassGroupIndex.Arms); body.PassGroup.Pass(PassGroupIndex.Legs); body.PassGroup.Pass(PassGroupIndex.Head); break; case CoordSystemPart.Head: body.GroupIndex = PassGroupIndex.Head; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Torso); body.PassGroup.Pass(PassGroupIndex.Arms); body.PassGroup.Pass(PassGroupIndex.Legs); body.PassGroup.Pass(PassGroupIndex.Head); break; default: throw new ArgumentOutOfRangeException(); } switch (collider.Type) { case ColliderType.Sphere: { body.BoundingBoxKind = BoundingBoxKind.Sphere; body.BoundingBoxSize = new Vector3(collider.Radius, 0, 0); body.Position = correspondingBone.InitialPosition; } break; case ColliderType.Capsule: { body.BoundingBoxKind = BoundingBoxKind.Capsule; body.BoundingBoxSize = new Vector3(collider.Radius, collider.Distance, 0); body.RotationAngles = MapRotation(part, collider.Axis); var offset = MapTranslation(collider.Offset.ToOpenTK(), part, collider.Axis); if (_pmxCreator._conversionConfig.ScaleToPmxSize) { offset = offset * _pmxCreator._scalingConfig.ScaleUnityToPmx; } body.Position = correspondingBone.InitialPosition + offset; } break; case ColliderType.Plane: case ColliderType.Bridge: // TODO: How to handle these? continue; default: throw new ArgumentOutOfRangeException(); } if (_pmxCreator._conversionConfig.ScaleToPmxSize) { body.BoundingBoxSize = body.BoundingBoxSize * _pmxCreator._scalingConfig.ScaleUnityToPmx; } bodies.Add(body); } }
private void AppendSwayBones([NotNull, ItemNotNull] PmxBone[] bones, [NotNull, ItemNotNull] SwayBone[] swayBones, [NotNull, ItemNotNull] List <PmxRigidBody> bodies) { // "Semi-root" sway bones foreach (var swayBone in swayBones) { var part = GetBodyCoordSystemPart(swayBone); switch (part) { case CoordSystemPart.Torso: case CoordSystemPart.LeftArm: case CoordSystemPart.RightArm: case CoordSystemPart.Legs: case CoordSystemPart.Head: continue; case CoordSystemPart.Breasts: case CoordSystemPart.Accessories: continue; case CoordSystemPart.Skirt: case CoordSystemPart.Hair: break; default: throw new ArgumentOutOfRangeException(); } var guessedParentPath = swayBone.Path.BreakUntilLast('/'); var parentBody = swayBones.Find(sb => sb.Path == guessedParentPath); if (parentBody != null) { // This is a child rigid body. continue; } var mltdBoneName = swayBone.Path; if (mltdBoneName.Contains(BoneLookup.BoneNamePart_BodyScale)) { mltdBoneName = mltdBoneName.Replace(BoneLookup.BoneNamePart_BodyScale, string.Empty); } var pmxBoneName = _pmxCreator._boneLookup.GetPmxBoneName(mltdBoneName); var correspondingBone = bones.Find(o => o.Name == pmxBoneName); if (correspondingBone == null) { Trace.WriteLine($"Warning: Semi-root sway bone is missing. Skipping this bone. PMX name: {pmxBoneName}; MLTD name: {mltdBoneName}"); continue; } var body = new PmxRigidBody(); body.Part = part; body.BoneIndex = correspondingBone.BoneIndex; body.Name = correspondingBone.Name; body.NameEnglish = correspondingBone.NameEnglish; body.KineticMode = KineticMode.Dynamic; body.Mass = ComputeMass(swayBone.Radius); body.PositionDamping = 0.9f; body.RotationDamping = 1.0f; body.Friction = 0.0f; body.Restitution = 0.0f; switch (part) { case CoordSystemPart.Head: body.GroupIndex = PassGroupIndex.Head; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Torso); body.PassGroup.Pass(PassGroupIndex.Arms); body.PassGroup.Pass(PassGroupIndex.Legs); body.PassGroup.Pass(PassGroupIndex.Head); break; case CoordSystemPart.Hair: body.GroupIndex = PassGroupIndex.Hair; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Head); body.PassGroup.Pass(PassGroupIndex.Hair); break; case CoordSystemPart.Skirt: body.GroupIndex = PassGroupIndex.Skirt; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Skirt); body.PassGroup.Pass(PassGroupIndex.Torso); break; case CoordSystemPart.Breasts: body.GroupIndex = PassGroupIndex.Breasts; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Breasts); body.PassGroup.Pass(PassGroupIndex.Torso); break; case CoordSystemPart.Accessories: body.GroupIndex = PassGroupIndex.Accessories; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Accessories); break; default: throw new ArgumentOutOfRangeException(); } switch (swayBone.Type) { case ColliderType.Sphere: { body.BoundingBoxKind = BoundingBoxKind.Sphere; body.BoundingBoxSize = new Vector3(swayBone.Radius, 0, 0); var childBone = bones.Find(bo => bo.ParentIndex == correspondingBone.BoneIndex); Debug.Assert(childBone != null, nameof(childBone) + " != null"); body.Position = (correspondingBone.InitialPosition + childBone.InitialPosition) / 2; } break; case ColliderType.Capsule: case ColliderType.Plane: case ColliderType.Bridge: throw new ArgumentOutOfRangeException(nameof(swayBone.Type), swayBone.Type, "Only sphere colliders are supported in SwayBone."); default: throw new ArgumentOutOfRangeException(); } if (_pmxCreator._conversionConfig.ScaleToPmxSize) { body.BoundingBoxSize = body.BoundingBoxSize * _pmxCreator._scalingConfig.ScaleUnityToPmx; } bodies.Add(body); } // Normal sway bones foreach (var swayBone in swayBones) { var mltdBoneName = swayBone.Path; if (mltdBoneName.Contains(BoneLookup.BoneNamePart_BodyScale)) { mltdBoneName = mltdBoneName.Replace(BoneLookup.BoneNamePart_BodyScale, string.Empty); } var pmxBoneName = _pmxCreator._boneLookup.GetPmxBoneName(mltdBoneName); var body = new PmxRigidBody(); var part = GetBodyCoordSystemPart(swayBone); body.Part = part; var correspondingBone = bones.Find(o => o.Name == pmxBoneName); if (correspondingBone == null) { Trace.WriteLine($"Warning: Normal sway bone is missing. Skipping this bone. PMX name: {pmxBoneName}; MLTD name: {mltdBoneName}"); continue; } switch (part) { case CoordSystemPart.Skirt: case CoordSystemPart.Hair: { var cb = correspondingBone; correspondingBone = bones.Find(b => b.ParentIndex == cb.BoneIndex); Debug.Assert(correspondingBone != null, "Normal sway (skirt/hair): " + nameof(correspondingBone) + " != null"); break; } case CoordSystemPart.Accessories: case CoordSystemPart.Breasts: break; } body.BoneIndex = correspondingBone.BoneIndex; body.Name = correspondingBone.Name; body.NameEnglish = correspondingBone.NameEnglish; switch (part) { case CoordSystemPart.Skirt: case CoordSystemPart.Hair: body.KineticMode = KineticMode.Dynamic; break; case CoordSystemPart.Accessories: case CoordSystemPart.Breasts: body.KineticMode = KineticMode.DynamicWithBone; break; } body.Mass = ComputeMass(swayBone.Radius); body.PositionDamping = 0.9f; body.RotationDamping = 1.0f; body.Friction = 0.0f; body.Restitution = 0.0f; switch (part) { case CoordSystemPart.Head: body.GroupIndex = PassGroupIndex.Head; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Torso); body.PassGroup.Pass(PassGroupIndex.Arms); body.PassGroup.Pass(PassGroupIndex.Legs); body.PassGroup.Pass(PassGroupIndex.Head); break; case CoordSystemPart.Hair: body.GroupIndex = PassGroupIndex.Hair; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Hair); body.PassGroup.Pass(PassGroupIndex.Head); break; case CoordSystemPart.Skirt: body.GroupIndex = PassGroupIndex.Skirt; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Skirt); break; case CoordSystemPart.Breasts: body.GroupIndex = PassGroupIndex.Breasts; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Breasts); body.PassGroup.Pass(PassGroupIndex.Torso); break; case CoordSystemPart.Accessories: body.GroupIndex = PassGroupIndex.Accessories; body.PassGroup.BlockAll(); body.PassGroup.Pass(PassGroupIndex.Accessories); break; default: throw new ArgumentOutOfRangeException(); } switch (swayBone.Type) { case ColliderType.Sphere: { body.BoundingBoxKind = BoundingBoxKind.Sphere; body.BoundingBoxSize = new Vector3(swayBone.Radius, 0, 0); var childBone = bones.Find(bo => bo.ParentIndex == correspondingBone.BoneIndex); if (childBone != null) { body.Position = (correspondingBone.InitialPosition + childBone.InitialPosition) / 2; } else { var parentBone = bones.Find(bo => bo.BoneIndex == correspondingBone.ParentIndex); Debug.Assert(parentBone != null, nameof(parentBone) + " != null"); var delta = correspondingBone.InitialPosition - parentBone.InitialPosition; body.Position = correspondingBone.InitialPosition + delta / 2; } } break; case ColliderType.Capsule: case ColliderType.Plane: case ColliderType.Bridge: throw new ArgumentOutOfRangeException(nameof(swayBone.Type), swayBone.Type, "Only sphere colliders are supported in SwayBone."); default: throw new ArgumentOutOfRangeException(); } if (_pmxCreator._conversionConfig.ScaleToPmxSize) { body.BoundingBoxSize = body.BoundingBoxSize * _pmxCreator._scalingConfig.ScaleUnityToPmx; } bodies.Add(body); } }
private PmxModel ReadPmxModel() { var model = new PmxModel(); model.Name = ReadString() ?? string.Empty; model.NameEnglish = ReadString() ?? string.Empty; model.Comment = ReadString() ?? string.Empty; model.CommentEnglish = ReadString() ?? string.Empty; ReadVertexInfo(); ReadFaceInfo(); ReadTextureInfo(); ReadMaterialInfo(); ReadBoneInfo(); ReadMorphInfo(); ReadNodeInfo(); ReadRigidBodyInfo(); ReadJointInfo(); ReadSoftBodyInfo(); return(model); void ReadVertexInfo() { var vertexCount = _reader.ReadInt32(); var vertices = new PmxVertex[vertexCount]; for (var i = 0; i < vertexCount; ++i) { vertices[i] = ReadPmxVertex(); } model.Vertices = vertices; } void ReadFaceInfo() { var faceCount = _reader.ReadInt32(); var faceIndices = new int[faceCount]; for (var i = 0; i < faceCount; ++i) { faceIndices[i] = _reader.ReadVarLenIntAsInt32(VertexElementSize, true); } model.FaceTriangles = faceIndices; } void ReadTextureInfo() { var textureCount = _reader.ReadInt32(); var textureNameMap = new Dictionary <int, string>(); var textureIndexLookup = new Dictionary <string, int>(); for (var i = 0; i < textureCount; ++i) { var textureName = ReadString() ?? string.Empty; textureNameMap[i] = textureName; textureIndexLookup[textureName] = i; } textureNameMap[-1] = string.Empty; TextureNameMap = textureNameMap; TextureIndexLookup = textureIndexLookup; } void ReadMaterialInfo() { var materialCount = _reader.ReadInt32(); var materials = new PmxMaterial[materialCount]; for (var i = 0; i < materialCount; ++i) { materials[i] = ReadPmxMaterial(); } model.Materials = materials; } void ReadBoneInfo() { var boneCount = _reader.ReadInt32(); var bones = new PmxBone[boneCount]; for (var i = 0; i < boneCount; ++i) { bones[i] = ReadPmxBone(); bones[i].BoneIndex = i; } model.Bones = bones; model.BonesDictionary = bones.ToDictionary(bone => bone.Name); var rootBoneIndexList = new List <int>(); for (var i = 0; i < bones.Length; ++i) { var bone = bones[i]; if (bone.ParentIndex < 0) { rootBoneIndexList.Add(i); } else { bone.Parent = bones[bone.ParentIndex]; } if (bone.AppendParentIndex >= 0) { bone.AppendParent = bones[bone.AppendParentIndex]; } if (bone.ExternalParentIndex >= 0) { bone.ExternalParent = bones[bone.ExternalParentIndex]; } if (bone.HasFlag(BoneFlags.IK)) { var ik = bone.IK; Debug.Assert(ik != null, nameof(ik) + " != null"); ik.TargetBone = bones[ik.TargetBoneIndex]; foreach (var link in ik.Links) { if (link.BoneIndex >= 0) { link.Bone = bones[link.BoneIndex]; } } } } model.RootBoneIndices = rootBoneIndexList.ToArray(); foreach (var bone in bones) { bone.SetToBindingPose(); } } void ReadMorphInfo() { var morphCount = _reader.ReadInt32(); var morphs = new PmxMorph[morphCount]; for (var i = 0; i < morphCount; ++i) { morphs[i] = ReadPmxMorph(); } model.Morphs = morphs; } void ReadNodeInfo() { var nodeCount = _reader.ReadInt32(); var nodes = new PmxNode[nodeCount]; for (var i = 0; i < nodeCount; ++i) { var node = ReadPmxNode(); nodes[i] = node; if (node.IsSystemNode) { if (node.Name == "Root") { model.RootNodeIndex = i; } else if (node.Name == "表情") { model.FacialExpressionNodeIndex = i; } } } model.Nodes = nodes; } void ReadRigidBodyInfo() { var bodyCount = _reader.ReadInt32(); var bodies = new PmxRigidBody[bodyCount]; for (var i = 0; i < bodyCount; ++i) { bodies[i] = ReadPmxRigidBody(); } model.RigidBodies = bodies; } void ReadJointInfo() { var jointCount = _reader.ReadInt32(); var joints = new PmxJoint[jointCount]; for (var i = 0; i < jointCount; ++i) { joints[i] = ReadPmxJoint(); } model.Joints = joints; } void ReadSoftBodyInfo() { if (DetailedVersion < 2.1f) { return; } var bodyCount = _reader.ReadInt32(); var bodies = new PmxSoftBody[bodyCount]; for (var i = 0; i < bodyCount; ++i) { bodies[i] = ReadPmxSoftBody(); } model.SoftBodies = bodies; } }