using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using SharpGLTF.Schema2; using SharpGLTF.Geometry; using SharpGLTF.Geometry.VertexTypes; namespace InfiniteSkinnedTentacle { using VERTEX = VertexBuilder; using MESH = MeshBuilder; class Program { // Skinning use cases and examples: https://github.com/KhronosGroup/glTF/issues/1403 // hierarchy created: // Mesh1 // Skin1─> Armature1 // Skin2─> Armature2 // Skin3─> Armature3 // Scene // ├── Armature1 // │ ├── Bone1 // │ ├── Bone2 // │ └── Bone3 // ├── SkinnedMesh1─> Mesh1, Skin1 // ├── Armature2 // │ ├── Bone1 // │ ├── Bone2 // │ └── Bone3 // ├── SkinnedMesh2─> Mesh1, Skin2 // ├── Armature3 // │ ├── Bone1 // │ ├── Bone2 // │ └── Bone3 // └── SkinnedMesh3─> Mesh1, Skin3 // ... static void Main(string[] args) { var model = ModelRoot.CreateModel(); var scene = model.UseScene("default"); var mesh = model .CreateMeshes(CreateMesh(10)) .First(); RecusiveTentacle(scene, scene, Matrix4x4.CreateTranslation(+25, 0, +25), mesh, Quaternion.CreateFromYawPitchRoll(0f, 0.2f, 0f), 2); RecusiveTentacle(scene, scene, Matrix4x4.CreateTranslation(-25, 0, +25), mesh, Quaternion.CreateFromYawPitchRoll(0.2f, 0f, 0f), 2); RecusiveTentacle(scene, scene, Matrix4x4.CreateTranslation(-25, 0, -25), mesh, Quaternion.CreateFromYawPitchRoll(0f, 0f, 0.2f), 2); RecusiveTentacle(scene, scene, Matrix4x4.CreateTranslation(+25, 0, -25), mesh, Quaternion.CreateFromYawPitchRoll(0.2f, 0f, 0f), 2); model.SaveGLB("recursive tentacles.glb"); model.SaveGLTF("recursive tentacles.gltf"); model.SaveAsWavefront("recursive_tentacles_at_000.obj", model.LogicalAnimations[0], 0); model.SaveAsWavefront("recursive_tentacles_at_025.obj", model.LogicalAnimations[0], 0.25f); model.SaveAsWavefront("recursive_tentacles_at_050.obj", model.LogicalAnimations[0], 0.50f); model.SaveAsWavefront("recursive_tentacles_at_075.obj", model.LogicalAnimations[0], 0.75f); model.SaveAsWavefront("recursive_tentacles_at_100.obj", model.LogicalAnimations[0], 1); } static void RecusiveTentacle(Scene scene, IVisualNodeContainer parent, Matrix4x4 offset, Mesh mesh, Quaternion anim, int repeat) { parent = parent .CreateNode() .WithLocalTransform(offset); parent = AddTentacleSkeleton(scene, parent as Node, mesh, anim); if (repeat == 0) return; var scale = Matrix4x4.CreateScale(0.2f); RecusiveTentacle(scene, parent, Matrix4x4.CreateTranslation(+15, 0, +15) * scale, mesh, Quaternion.CreateFromYawPitchRoll(0f, 0.2f, 0f), repeat - 1); RecusiveTentacle(scene, parent, Matrix4x4.CreateTranslation(-15, 0, +15) * scale, mesh, Quaternion.CreateFromYawPitchRoll(0.2f, 0f, 0f), repeat - 1); RecusiveTentacle(scene, parent, Matrix4x4.CreateTranslation(-15, 0, -15) * scale, mesh, Quaternion.CreateFromYawPitchRoll(0f, 0f, 0.2f), repeat - 1); RecusiveTentacle(scene, parent, Matrix4x4.CreateTranslation(+15, 0, -15) * scale, mesh, Quaternion.CreateFromYawPitchRoll(0.2f, 0f, 0f), repeat - 1); } static Node AddTentacleSkeleton(Scene scene, Node skeleton, Mesh mesh, Quaternion anim) { var bindings = new List(); Node bone = null; for (int i = 0; i < 10; ++i) { if (bone == null) { bone = skeleton; } else { bone = bone.CreateNode(); bone.LocalTransform = Matrix4x4.CreateTranslation(0, 10, 0); } bone.WithRotationAnimation("Track0", (0, Quaternion.Identity), (1, anim), (2, Quaternion.Identity)); bindings.Add(bone); } scene .CreateNode() .WithSkinnedMesh(mesh, skeleton.WorldMatrix, bindings.ToArray()); return bindings.Last(); } static MESH CreateMesh(int boneCount) { var mesh = new MESH("skinned mesh"); mesh.VertexPreprocessor.SetValidationPreprocessors(); var prim = mesh.UsePrimitive(new SharpGLTF.Materials.MaterialBuilder("Default")); var a0 = default(VERTEX); var a1 = default(VERTEX); var a2 = default(VERTEX); var a3 = default(VERTEX); for (int i = 0; i < boneCount; ++i) { var b0 = new VERTEX(new Vector3(-5, i * 10, -5), Vector4.One, (i, 1)); var b1 = new VERTEX(new Vector3(+5, i * 10, -5), Vector4.One, (i, 1)); var b2 = new VERTEX(new Vector3(+5, i * 10, +5), Vector4.One, (i, 1)); var b3 = new VERTEX(new Vector3(-5, i * 10, +5), Vector4.One, (i, 1)); if (i == 0) { prim.AddQuadrangle(b0, b1, b2, b3); } else { prim.AddQuadrangle(b0, b1, a1, a0); prim.AddQuadrangle(b1, b2, a2, a1); prim.AddQuadrangle(b2, b3, a3, a2); prim.AddQuadrangle(b3, b0, a0, a3); } a0 = b0; a1 = b1; a2 = b2; a3 = b3; } prim.AddQuadrangle(a3, a2, a1, a0); return mesh; } } }