public void CreateDoubleSkinnedScene() { TestContext.CurrentContext.AttachGltfValidatorLinks(); // create two materials var pink = new MaterialBuilder("material1") .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(1, 0, 1, 1)) .WithDoubleSide(true); var yellow = new MaterialBuilder("material2") .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(1, 1, 0, 1)) .WithDoubleSide(true); // create the mesh const int jointIdx0 = 0; // index of joint node 0 const int jointIdx1 = 1; // index of joint node 1 const int jointIdx2 = 2; // index of joint node 2 var v1 = new SKINNEDVERTEX4(new Vector3(-10, 0, +10), (jointIdx0, 1)); var v2 = new SKINNEDVERTEX4(new Vector3(+10, 0, +10), (jointIdx0, 1)); var v3 = new SKINNEDVERTEX4(new Vector3(+10, 0, -10), (jointIdx0, 1)); var v4 = new SKINNEDVERTEX4(new Vector3(-10, 0, -10), (jointIdx0, 1)); var v5 = new SKINNEDVERTEX4(new Vector3(-10, 40, +10), (jointIdx0, 0.5f), (jointIdx1, 0.5f)); var v6 = new SKINNEDVERTEX4(new Vector3(+10, 40, +10), (jointIdx0, 0.5f), (jointIdx1, 0.5f)); var v7 = new SKINNEDVERTEX4(new Vector3(+10, 40, -10), (jointIdx0, 0.5f), (jointIdx1, 0.5f)); var v8 = new SKINNEDVERTEX4(new Vector3(-10, 40, -10), (jointIdx0, 0.5f), (jointIdx1, 0.5f)); var v9 = new SKINNEDVERTEX4(new Vector3(-5, 80, +5), (jointIdx2, 1)); var v10 = new SKINNEDVERTEX4(new Vector3(+5, 80, +5), (jointIdx2, 1)); var v11 = new SKINNEDVERTEX4(new Vector3(+5, 80, -5), (jointIdx2, 1)); var v12 = new SKINNEDVERTEX4(new Vector3(-5, 80, -5), (jointIdx2, 1)); var mesh = SKINNEDVERTEX4.CreateCompatibleMesh("mesh1"); #if DEBUG mesh.VertexPreprocessor.SetValidationPreprocessors(); #else mesh.VertexPreprocessor.SetSanitizerPreprocessors(); #endif mesh.UsePrimitive(pink).AddQuadrangle(v1, v2, v6, v5); mesh.UsePrimitive(pink).AddQuadrangle(v2, v3, v7, v6); mesh.UsePrimitive(pink).AddQuadrangle(v3, v4, v8, v7); mesh.UsePrimitive(pink).AddQuadrangle(v4, v1, v5, v8); mesh.UsePrimitive(yellow).AddQuadrangle(v5, v6, v10, v9); mesh.UsePrimitive(yellow).AddQuadrangle(v6, v7, v11, v10); mesh.UsePrimitive(yellow).AddQuadrangle(v7, v8, v12, v11); mesh.UsePrimitive(yellow).AddQuadrangle(v8, v5, v9, v12); mesh.Validate(); // create the skeleton armature 1 for the skinned mesh. var armature1 = new NodeBuilder("Skeleton1"); var joint0 = armature1.CreateNode("Joint 0").WithLocalTranslation(new Vector3(0, 0, 0)); // jointIdx0 var joint1 = joint0.CreateNode("Joint 1").WithLocalTranslation(new Vector3(0, 40, 0)); // jointIdx1 var joint2 = joint1.CreateNode("Joint 2").WithLocalTranslation(new Vector3(0, 40, 0)); // jointIdx2 joint1.UseRotation("Base Track") .WithPoint(1, Quaternion.Identity) .WithPoint(2, Quaternion.CreateFromYawPitchRoll(0, 1, 0)) .WithPoint(3, Quaternion.CreateFromYawPitchRoll(0, 0, 1)) .WithPoint(4, Quaternion.Identity); // create the skeleton armature 2 for the skinned mesh. var armature2 = new NodeBuilder("Skeleton2").WithLocalTranslation(new Vector3(100, 0, 0)); var joint3 = armature2.CreateNode("Joint 3").WithLocalTranslation(new Vector3(0, 0, 0)); // jointIdx0 var joint4 = joint3.CreateNode("Joint 4").WithLocalTranslation(new Vector3(0, 40, 0)); // jointIdx1 var joint5 = joint4.CreateNode("Joint 5").WithLocalTranslation(new Vector3(0, 40, 0)); // jointIdx2 joint4.UseRotation("Base Track") .WithPoint(1, Quaternion.Identity) .WithPoint(2, Quaternion.CreateFromYawPitchRoll(0, 1, 0)) .WithPoint(3, Quaternion.CreateFromYawPitchRoll(0, 0, 1)) .WithPoint(4, Quaternion.Identity); // create scene var scene = new SceneBuilder(); scene.AddSkinnedMesh ( mesh, armature1.WorldMatrix, joint0, // joint used for skinning joint index 0 joint1, // joint used for skinning joint index 1 joint2 // joint used for skinning joint index 2 ); scene.AddSkinnedMesh ( mesh, armature2.WorldMatrix, joint3, // joint used for skinning joint index 0 joint4, // joint used for skinning joint index 1 joint5 // joint used for skinning joint index 2 ); scene.AttachToCurrentTest("skinned.glb"); scene.AttachToCurrentTest("skinned.gltf"); }
public ModelRoot SceneGroup( List <Vitaboy.Mesh> meshes, List <Vitaboy.Animation> animations, List <Microsoft.Xna.Framework.Graphics.Texture2D> textures, Vitaboy.Skeleton skel ) { var builder = new SharpGLTF.Scenes.SceneBuilder(); var framerate = 1 / 36f; var nodes = new Dictionary <Vitaboy.Bone, NodeBuilder>(); var transforms = new List <Matrix4x4>(); skel.ComputeBonePositions(skel.RootBone, Microsoft.Xna.Framework.Matrix.Identity); ConvertBone(nodes, skel.RootBone, new NodeBuilder(skel.RootBone.Name)); //animations must be uploaded as part of the bone. add them as animation tracks to the existing nodes var timeprops = new Dictionary <string, float>(); var useAnimID = animations.Count > 1; foreach (var anim in animations) { var name = anim.XSkillName ?? anim.Name; foreach (var motion in anim.Motions) { //find the bone we're creating a curve for var bone = nodes.Values.FirstOrDefault(x => x.Name == motion.BoneName); if (bone == null) { continue; //cannot add this curve to a bone } var root = bone.Name == "ROOT"; var scale = RecursiveScale(bone.Parent); if (motion.TimeProperties != null) { foreach (var tps in motion.TimeProperties) { foreach (var tp in tps.Items) { foreach (var prop in tp.Properties.Items) { foreach (var keyPair in prop.KeyPairs) { var tpname = name + "/" + tp.ID.ToString() + "/" + keyPair.Key; float value = 1; if (!float.TryParse(keyPair.Value, out value)) { value = 1; tpname += "=" + keyPair.Value; } timeprops[tpname] = value; } } } } } //create curves for rotation and translation CurveBuilder <Vector3> transCurve = null; CurveBuilder <Quaternion> rotCurve = null; if (motion.HasTranslation) { transCurve = bone.Translation.UseTrackBuilder(name); } if (motion.HasRotation) { rotCurve = bone.Rotation.UseTrackBuilder(name); } for (int i = 0; i < motion.FrameCount; i++) { var quat = new Quaternion(); Vector3 trans = new Vector3(); if (rotCurve != null) { quat = QuatConvert(anim.Rotations[i + motion.FirstRotationIndex]); } if (transCurve != null) { trans = Vec3Convert(anim.Translations[i + motion.FirstTranslationIndex]) / scale; } if (root) { quat = RotateQ * quat; trans = Vector3.Transform(trans, RotateM); } rotCurve?.SetPoint(i * framerate, quat, true); transCurve?.SetPoint(i * framerate, trans, true); } if (bone.Name == "R_LEG") { } } } var resultMeshes = new List <MeshBuilder <VertexPositionNormal, VertexTexture1, VertexJoints8x4> >(); var orderedBones = skel.Bones.Select(x => nodes[x]).ToArray(); var world = Matrix4x4.Identity; //Matrix4x4.CreateFromAxisAngle(new Vector3(0, 1, 0), (float)Math.PI / 2f); var outMesh = new MeshBuilder <VertexPositionNormal, VertexTexture1, VertexJoints8x4>("Avatar"); var meshi = 0; foreach (var mesh in meshes) { var tex = textures[meshi++]; var data = TextureToPng(tex); var material = new MaterialBuilder(mesh.SkinName ?? ("mesh_" + meshi)); var mtex = material.UseChannel(KnownChannels.BaseColor).UseTexture().WithPrimaryImage(new ArraySegment <byte>(data)); var prim = outMesh.UsePrimitive(material, 3); //todo: blend verts var previous = new List <VertexBuilder <VertexPositionNormal, VertexTexture1, VertexJoints8x4> >(); for (int i = 0; i < mesh.IndexBuffer.Length; i++) { var point = mesh.IndexBuffer[(i / 3) * 3 + (2 - (i % 3))]; //flip triangles var vert = mesh.VertexBuffer[point]; var texc = new Vector2(vert.TextureCoordinate.X, vert.TextureCoordinate.Y); var boneInd = (int)vert.Parameters.X; var blendInd = (int)vert.Parameters.Y; var mat = skel.Bones[boneInd].AbsoluteMatrix * RotateMX; var bmat = skel.Bones[blendInd].AbsoluteMatrix * RotateMX; VertexBuilder <VertexPositionNormal, VertexTexture1, VertexJoints8x4> nvert = ( new VertexPositionNormal( Vector3.Lerp(Vec3Convert(Microsoft.Xna.Framework.Vector3.Transform(vert.Position, mat)), Vec3Convert(Microsoft.Xna.Framework.Vector3.Transform(vert.BvPosition, bmat)), vert.Parameters.Z), Vector3.Lerp(Vec3Convert(Microsoft.Xna.Framework.Vector3.TransformNormal(vert.Normal, mat)), Vec3Convert(Microsoft.Xna.Framework.Vector3.TransformNormal(vert.BvNormal, bmat)), vert.Parameters.Z) ), new VertexTexture1(texc), new VertexJoints8x4(new SharpGLTF.Transforms.SparseWeight8(new Vector4(boneInd, blendInd, 0, 0), new Vector4(1 - vert.Parameters.Z, vert.Parameters.Z, 0, 0))) ); if (previous.Count == 2) { prim.AddTriangle(previous[0], previous[1], nvert); previous.Clear(); } else { previous.Add(nvert); } } } var skin = builder.AddSkinnedMesh(outMesh, world, orderedBones); skin.Name = "Skeleton"; var schema = builder.ToSchema2(); /* * var children = schema.LogicalScenes.FirstOrDefault().VisualChildren; * foreach (var child in children) * { * if (child.Skin == null) { //armature * var extras = child.TryUseExtrasAsDictionary(true); * foreach (var tp in timeprops) * { * extras.Add(tp.Key, tp.Value); * } * } * } */ //var extras = schema.LogicalNodes.FirstOrDefault(x => x.Name == "ROOT").TryUseExtrasAsDictionary(true); var extras = schema.LogicalScenes.FirstOrDefault().TryUseExtrasAsDictionary(true); foreach (var tp in timeprops) { extras.Add(tp.Key, tp.Value); } return(schema); }