public override void Write() { Indent(); jsonWriter.Write("{\n"); IndentIn(); Indent(); jsonWriter.Write("\"inverseBindMatrices\": " + invBindMatricesAccessorIndex + ",\n"); Indent(); jsonWriter.Write("\"joints\": [\n"); IndentIn(); foreach (Transform j in joints) { CommaNL(); Indent(); jsonWriter.Write("" + GlTF_Writer.nodeIDs.IndexOf(GlTF_Node.GetIDFromObject(j))); } IndentOut(); jsonWriter.WriteLine(); Indent(); jsonWriter.Write("],\n"); Indent(); jsonWriter.Write("\"name\": \"" + name + "\"\n"); IndentOut(); Indent(); jsonWriter.Write("}"); }
public void Populate(AnimationClip clip, Transform tr, bool bake = true) { // 1. browse clip, collect all curves and create a TargetCurveSet for each target Dictionary <string, TargetCurveSet> targetCurvesBinding = new Dictionary <string, TargetCurveSet>(); collectClipCurves(clip, ref targetCurvesBinding); // Baking needs all properties, fill missing curves with transform data in 2 keyframes (start, endTime) // where endTime is clip duration generateMissingCurves(clip.length, ref tr, ref targetCurvesBinding); if (bake) { // Bake animation for all animated nodes foreach (string target in targetCurvesBinding.Keys) { Transform targetTr = target.Length > 0 ? tr.Find(target) : tr; if (targetTr == null) { continue; } Transform targetObject = targetTr; string targetId = GlTF_Node.GetNameFromObject(targetObject); int targetUUID = GlTF_Node.GetIDFromObject(targetObject); // Initialize accessors for current animation GlTF_Accessor timeAccessor = new GlTF_Accessor(targetId + "_TimeAccessor_" + clip.name, GlTF_Accessor.Type.SCALAR, GlTF_Accessor.ComponentType.FLOAT); timeAccessor.bufferView = GlTF_Writer.floatBufferView; int timeAccessorIndex = GlTF_Writer.accessors.Count; GlTF_Writer.accessors.Add(timeAccessor); // Translation GlTF_Channel chTranslation = new GlTF_Channel("translation", animSamplers.Count); GlTF_Target targetTranslation = new GlTF_Target(); targetTranslation.id = targetId; targetTranslation.uuid = targetUUID; targetTranslation.path = "translation"; chTranslation.target = targetTranslation; channels.Add(chTranslation); GlTF_AnimSampler sTranslation = new GlTF_AnimSampler(timeAccessorIndex, GlTF_Writer.accessors.Count); GlTF_Accessor translationAccessor = new GlTF_Accessor(targetId + "_TranslationAccessor_" + clip.name, GlTF_Accessor.Type.VEC3, GlTF_Accessor.ComponentType.FLOAT); translationAccessor.bufferView = GlTF_Writer.vec3BufferViewAnim; GlTF_Writer.accessors.Add(translationAccessor); animSamplers.Add(sTranslation); // Rotation GlTF_Channel chRotation = new GlTF_Channel("rotation", animSamplers.Count); GlTF_Target targetRotation = new GlTF_Target(); targetRotation.id = GlTF_Node.GetNameFromObject(targetObject); targetRotation.uuid = targetUUID; targetRotation.path = "rotation"; chRotation.target = targetRotation; channels.Add(chRotation); GlTF_AnimSampler sRotation = new GlTF_AnimSampler(timeAccessorIndex, GlTF_Writer.accessors.Count); GlTF_Accessor rotationAccessor = new GlTF_Accessor(targetId + "_RotationAccessor_" + clip.name, GlTF_Accessor.Type.VEC4, GlTF_Accessor.ComponentType.FLOAT); rotationAccessor.bufferView = GlTF_Writer.vec4BufferViewAnim; GlTF_Writer.accessors.Add(rotationAccessor); animSamplers.Add(sRotation); // Scale GlTF_Channel chScale = new GlTF_Channel("scale", animSamplers.Count); GlTF_Target targetScale = new GlTF_Target(); targetScale.id = GlTF_Node.GetNameFromObject(targetObject); targetScale.uuid = targetUUID; targetScale.path = "scale"; chScale.target = targetScale; channels.Add(chScale); GlTF_AnimSampler sScale = new GlTF_AnimSampler(timeAccessorIndex, GlTF_Writer.accessors.Count); GlTF_Accessor scaleAccessor = new GlTF_Accessor(targetId + "_ScaleAccessor_" + clip.name, GlTF_Accessor.Type.VEC3, GlTF_Accessor.ComponentType.FLOAT); scaleAccessor.bufferView = GlTF_Writer.vec3BufferViewAnim; GlTF_Writer.accessors.Add(scaleAccessor); animSamplers.Add(sScale); // Bake and populate animation data float[] times = null; Vector3[] positions = null; Vector3[] scales = null; Vector4[] rotations = null; bakeCurveSet(targetCurvesBinding[target], clip.length, bakingFramerate, ref times, ref positions, ref rotations, ref scales); // Populate accessors timeAccessor.Populate(times); translationAccessor.Populate(positions); rotationAccessor.Populate(rotations, false); scaleAccessor.Populate(scales, true); } } else { Debug.LogError("Only baked animation is supported for now. Skipping animation"); } }
public IEnumerator Export(string path, Preset presetAsset, bool buildZip, bool exportPBRMaterials, bool exportAnimation = true, bool doConvertImages = false) { writer = new GlTF_Writer(); writer.Init(); done = false; bool debugRightHandedScale = false; GlTF_Writer.exportedFiles.Clear(); if (debugRightHandedScale) { GlTF_Writer.convertRightHanded = false; } writer.extraString.Add("exporterVersion", GlTF_Writer.exporterVersion); Transform[] transforms0 = Selection.GetTransforms(SelectionMode.TopLevel); //// Create rootNode //GlTF_Node correctionNode = new GlTF_Node(); //correctionNode.id = GetInstanceID(transforms0[0]); //correctionNode.name = transforms0[0].name; //GlTF_Writer.nodes.Add(correctionNode); //GlTF_Writer.nodeIDs.Add(correctionNode.id); //GlTF_Writer.nodeNames.Add(correctionNode.name); //GlTF_Writer.rootNodes.Add(correctionNode); //path = toGlTFname(path); savedPath = Path.GetDirectoryName(path); // Temp list to keep track of skeletons Dictionary <string, GlTF_Skin> parsedSkins = new Dictionary <string, GlTF_Skin>(); parsedSkins.Clear(); // first, collect objects in the scene, add to lists Transform[] transforms = Selection.GetTransforms(SelectionMode.Deep); List <Transform> trs = new List <Transform>(transforms); // Prefilter selected nodes and look for skinning in order to list "bones" nodes //FIXME: improve this List <Transform> bones = new List <Transform>(); foreach (Transform tr in trs) { if (!tr.gameObject.activeSelf) { continue; } SkinnedMeshRenderer skin = tr.GetComponent <SkinnedMeshRenderer>(); if (skin) { foreach (Transform bone in skin.bones) { bones.Add(bone); } } } nbSelectedObjects = trs.Count; int nbDisabledObjects = 0; int nbIndex = 1; foreach (Transform tr in trs) { if (tr.gameObject.activeInHierarchy == false) { nbDisabledObjects++; continue; } // Initialize the node GlTF_Node node = new GlTF_Node(); node.id = GetInstanceID(tr);//GlTF_Node.GetNameFromObject(tr); node.name = GlTF_Writer.cleanNonAlphanumeric(tr.name); if (tr.GetComponent <Camera>() != null) { parseUnityCamera(tr); } if (tr.GetComponent <Light>() != null) { parseUnityLight(tr); } Mesh m = GetMesh(tr); if (m != null) { GlTF_Mesh mesh = new GlTF_Mesh(); mesh.id = GetInstanceID(m); mesh.name = GlTF_Writer.cleanNonAlphanumeric(GlTF_Mesh.GetNameFromObject(m)); GlTF_Accessor positionAccessor = new GlTF_Accessor(GlTF_Accessor.GetNameFromObject(m, "position"), GlTF_Accessor.Type.VEC3, GlTF_Accessor.ComponentType.FLOAT); positionAccessor.bufferView = GlTF_Writer.vec3BufferView; GlTF_Writer.accessors.Add(positionAccessor); GlTF_Accessor normalAccessor = null; if (m.normals.Length > 0) { normalAccessor = new GlTF_Accessor(GlTF_Accessor.GetNameFromObject(m, "normal"), GlTF_Accessor.Type.VEC3, GlTF_Accessor.ComponentType.FLOAT); normalAccessor.bufferView = GlTF_Writer.vec3BufferView; GlTF_Writer.accessors.Add(normalAccessor); } GlTF_Accessor colorAccessor = null; if (m.colors.Length > 0) { colorAccessor = new GlTF_Accessor(GlTF_Accessor.GetNameFromObject(m, "color"), GlTF_Accessor.Type.VEC4, GlTF_Accessor.ComponentType.FLOAT); colorAccessor.bufferView = GlTF_Writer.vec4BufferView; GlTF_Writer.accessors.Add(colorAccessor); } GlTF_Accessor uv0Accessor = null; if (m.uv.Length > 0) { uv0Accessor = new GlTF_Accessor(GlTF_Accessor.GetNameFromObject(m, "uv0"), GlTF_Accessor.Type.VEC2, GlTF_Accessor.ComponentType.FLOAT); uv0Accessor.bufferView = GlTF_Writer.vec2BufferView; GlTF_Writer.accessors.Add(uv0Accessor); } GlTF_Accessor uv1Accessor = null; if (m.uv2.Length > 0) { // check if object is affected by a lightmap uv1Accessor = new GlTF_Accessor(GlTF_Accessor.GetNameFromObject(m, "uv1"), GlTF_Accessor.Type.VEC2, GlTF_Accessor.ComponentType.FLOAT); uv1Accessor.bufferView = GlTF_Writer.vec2BufferView; GlTF_Writer.accessors.Add(uv1Accessor); } GlTF_Accessor uv2Accessor = null; if (m.uv3.Length > 0) { uv2Accessor = new GlTF_Accessor(GlTF_Accessor.GetNameFromObject(m, "uv2"), GlTF_Accessor.Type.VEC2, GlTF_Accessor.ComponentType.FLOAT); uv2Accessor.bufferView = GlTF_Writer.vec2BufferView; GlTF_Writer.accessors.Add(uv2Accessor); } GlTF_Accessor uv3Accessor = null; if (m.uv4.Length > 0) { uv3Accessor = new GlTF_Accessor(GlTF_Accessor.GetNameFromObject(m, "uv3"), GlTF_Accessor.Type.VEC2, GlTF_Accessor.ComponentType.FLOAT); uv3Accessor.bufferView = GlTF_Writer.vec2BufferView; GlTF_Writer.accessors.Add(uv3Accessor); } GlTF_Accessor jointAccessor = null; if (exportAnimation && m.boneWeights.Length > 0) { jointAccessor = new GlTF_Accessor(GlTF_Accessor.GetNameFromObject(m, "joints"), GlTF_Accessor.Type.VEC4, GlTF_Accessor.ComponentType.USHORT); jointAccessor.bufferView = GlTF_Writer.vec4UshortBufferView; GlTF_Writer.accessors.Add(jointAccessor); } GlTF_Accessor weightAccessor = null; if (exportAnimation && m.boneWeights.Length > 0) { weightAccessor = new GlTF_Accessor(GlTF_Accessor.GetNameFromObject(m, "weights"), GlTF_Accessor.Type.VEC4, GlTF_Accessor.ComponentType.FLOAT); weightAccessor.bufferView = GlTF_Writer.vec4BufferView; GlTF_Writer.accessors.Add(weightAccessor); } GlTF_Accessor tangentAccessor = null; if (m.tangents.Length > 0) { tangentAccessor = new GlTF_Accessor(GlTF_Accessor.GetNameFromObject(m, "tangents"), GlTF_Accessor.Type.VEC4, GlTF_Accessor.ComponentType.FLOAT); tangentAccessor.bufferView = GlTF_Writer.vec4BufferView; GlTF_Writer.accessors.Add(tangentAccessor); } var smCount = m.subMeshCount; for (var i = 0; i < smCount; ++i) { GlTF_Primitive primitive = new GlTF_Primitive(); primitive.name = GlTF_Primitive.GetNameFromObject(m, i); primitive.index = i; GlTF_Attributes attributes = new GlTF_Attributes(); attributes.positionAccessor = positionAccessor; attributes.normalAccessor = normalAccessor; attributes.colorAccessor = colorAccessor; attributes.texCoord0Accessor = uv0Accessor; attributes.texCoord1Accessor = uv1Accessor; attributes.texCoord2Accessor = uv2Accessor; attributes.texCoord3Accessor = uv3Accessor; attributes.jointAccessor = jointAccessor; attributes.weightAccessor = weightAccessor; attributes.tangentAccessor = tangentAccessor; primitive.attributes = attributes; GlTF_Accessor indexAccessor = new GlTF_Accessor(GlTF_Accessor.GetNameFromObject(m, "indices_" + i), GlTF_Accessor.Type.SCALAR, GlTF_Accessor.ComponentType.USHORT); indexAccessor.bufferView = GlTF_Writer.ushortBufferView; GlTF_Writer.accessors.Add(indexAccessor); primitive.indices = indexAccessor; var mr = GetRenderer(tr); var sm = mr.sharedMaterials; if (i < sm.Length) { var mat = sm[i]; var matName = GlTF_Material.GetNameFromObject(mat); if (GlTF_Writer.materialNames.Contains(matName)) { primitive.materialIndex = GlTF_Writer.materialNames.IndexOf(matName); // THIS INDIRECTION CAN BE REMOVED! } else { GlTF_Material material = new GlTF_Material(); material.name = GlTF_Writer.cleanNonAlphanumeric(mat.name); primitive.materialIndex = GlTF_Writer.materials.Count; GlTF_Writer.materialNames.Add(matName); GlTF_Writer.materials.Add(material); //technique var s = mat.shader; var techName = GlTF_Technique.GetNameFromObject(s); if (GlTF_Writer.techniqueNames.Contains(techName)) { material.instanceTechniqueIndex = GlTF_Writer.techniqueNames.IndexOf(techName); // THIS INDIRECTION CAN BE REMOVED! } else { GlTF_Technique tech = new GlTF_Technique(); tech.name = techName; GlTF_Technique.Parameter tParam = new GlTF_Technique.Parameter(); tParam.name = "position"; tParam.type = GlTF_Technique.Type.FLOAT_VEC3; tParam.semantic = GlTF_Technique.Semantic.POSITION; tech.parameters.Add(tParam); GlTF_Technique.Attribute tAttr = new GlTF_Technique.Attribute(); tAttr.name = "a_position"; tAttr.param = tParam.name; tech.attributes.Add(tAttr); if (normalAccessor != null) { tParam = new GlTF_Technique.Parameter(); tParam.name = "normal"; tParam.type = GlTF_Technique.Type.FLOAT_VEC3; tParam.semantic = GlTF_Technique.Semantic.NORMAL; tech.parameters.Add(tParam); tAttr = new GlTF_Technique.Attribute(); tAttr.name = "a_normal"; tAttr.param = tParam.name; tech.attributes.Add(tAttr); } if (uv0Accessor != null) { tParam = new GlTF_Technique.Parameter(); tParam.name = "texcoord0"; tParam.type = GlTF_Technique.Type.FLOAT_VEC2; tParam.semantic = GlTF_Technique.Semantic.TEXCOORD_0; tech.parameters.Add(tParam); tAttr = new GlTF_Technique.Attribute(); tAttr.name = "a_texcoord0"; tAttr.param = tParam.name; tech.attributes.Add(tAttr); } if (uv1Accessor != null) { tParam = new GlTF_Technique.Parameter(); tParam.name = "texcoord1"; tParam.type = GlTF_Technique.Type.FLOAT_VEC2; tParam.semantic = GlTF_Technique.Semantic.TEXCOORD_1; tech.parameters.Add(tParam); tAttr = new GlTF_Technique.Attribute(); tAttr.name = "a_texcoord1"; tAttr.param = tParam.name; tech.attributes.Add(tAttr); } if (uv2Accessor != null) { tParam = new GlTF_Technique.Parameter(); tParam.name = "texcoord2"; tParam.type = GlTF_Technique.Type.FLOAT_VEC2; tParam.semantic = GlTF_Technique.Semantic.TEXCOORD_2; tech.parameters.Add(tParam); tAttr = new GlTF_Technique.Attribute(); tAttr.name = "a_texcoord2"; tAttr.param = tParam.name; tech.attributes.Add(tAttr); } if (uv3Accessor != null) { tParam = new GlTF_Technique.Parameter(); tParam.name = "texcoord3"; tParam.type = GlTF_Technique.Type.FLOAT_VEC2; tParam.semantic = GlTF_Technique.Semantic.TEXCOORD_3; tech.parameters.Add(tParam); tAttr = new GlTF_Technique.Attribute(); tAttr.name = "a_texcoord3"; tAttr.param = tParam.name; tech.attributes.Add(tAttr); } tech.AddDefaultUniforms(); // Populate technique with shader data GlTF_Writer.techniqueNames.Add(techName); GlTF_Writer.techniques.Add(tech); // create program GlTF_Program program = new GlTF_Program(); program.name = GlTF_Program.GetNameFromObject(s); tech.program = program.name; foreach (var attr in tech.attributes) { program.attributes.Add(attr.name); } GlTF_Writer.programs.Add(program); } unityToPBRMaterial(mat, ref material); } } mesh.primitives.Add(primitive); } // If gameobject having SkinnedMeshRenderer component has been transformed, // the mesh would need to be baked here. mesh.Populate(m); GlTF_Writer.meshes.Add(mesh); node.meshIndex = GlTF_Writer.meshes.IndexOf(mesh); } // Parse animations if (exportAnimation) { Animator a = tr.GetComponent <Animator>(); if (a != null) { AnimationClip[] clips = AnimationUtility.GetAnimationClips(tr.gameObject); for (int i = 0; i < clips.Length; i++) { //FIXME It seems not good to generate one animation per animator. GlTF_Animation anim = new GlTF_Animation(GlTF_Writer.cleanNonAlphanumeric(clips[i].name)); anim.Populate(clips[i], tr, GlTF_Writer.bakeAnimation); if (anim.channels.Count > 0) { GlTF_Writer.animations.Add(anim); } } } Animation animation = tr.GetComponent <Animation>(); if (animation != null) { AnimationClip clip = animation.clip; //FIXME It seems not good to generate one animation per animator. GlTF_Animation anim = new GlTF_Animation(GlTF_Writer.cleanNonAlphanumeric(clip.name)); anim.Populate(clip, tr, GlTF_Writer.bakeAnimation); if (anim.channels.Count > 0) { GlTF_Writer.animations.Add(anim); } } } // Parse transform if (tr.parent == null) { Matrix4x4 mat = Matrix4x4.identity; if (debugRightHandedScale) { mat.m22 = -1; } mat = mat * Matrix4x4.TRS(tr.localPosition, tr.localRotation, tr.localScale); node.matrix = new GlTF_Matrix(mat); } // Use good transform if parent object is not in selection else if (!trs.Contains(tr.parent)) { node.hasParent = false; Matrix4x4 mat = Matrix4x4.identity; if (debugRightHandedScale) { mat.m22 = -1; } mat = mat * tr.localToWorldMatrix; node.matrix = new GlTF_Matrix(mat); } else { node.hasParent = true; if (tr.localPosition != Vector3.zero) { node.translation = new GlTF_Translation(tr.localPosition); } if (tr.localScale != Vector3.one) { node.scale = new GlTF_Scale(tr.localScale); } if (tr.localRotation != Quaternion.identity) { node.rotation = new GlTF_Rotation(tr.localRotation); } } if (!node.hasParent) { // correctionNode.childrenNames.Add(node.name); // correctionNode.childrenIDs.Add(node.id); GlTF_Writer.rootNodes.Add(node); } if (tr.GetComponent <Camera>() != null) { node.cameraName = GlTF_Writer.cleanNonAlphanumeric(tr.name); } else if (tr.GetComponent <Light>() != null) { node.lightName = GlTF_Writer.cleanNonAlphanumeric(tr.name); } // Parse node's skin data GlTF_Accessor invBindMatrixAccessor = null; SkinnedMeshRenderer skinMesh = tr.GetComponent <SkinnedMeshRenderer>(); if (exportAnimation && skinMesh != null && skinMesh.enabled && checkSkinValidity(skinMesh, trs) && skinMesh.rootBone != null) { GlTF_Skin skin = new GlTF_Skin(); skin.name = GlTF_Writer.cleanNonAlphanumeric(skinMesh.rootBone.name);// + "_skeleton_" + GlTF_Writer.cleanNonAlphanumeric(node.name) + tr.GetInstanceID(); // Create invBindMatrices accessor invBindMatrixAccessor = new GlTF_Accessor(skin.name + "invBindMatrices", GlTF_Accessor.Type.MAT4, GlTF_Accessor.ComponentType.FLOAT); invBindMatrixAccessor.bufferView = GlTF_Writer.mat4BufferView; GlTF_Writer.accessors.Add(invBindMatrixAccessor); // Generate skin data skin.Populate(tr, ref invBindMatrixAccessor, GlTF_Writer.accessors.Count - 1); GlTF_Writer.skins.Add(skin); node.skinIndex = GlTF_Writer.skins.IndexOf(skin); } foreach (Transform t in tr.transform) { if (t.gameObject.activeInHierarchy) { node.childrenNames.Add(GlTF_Node.GetNameFromObject(t)); node.childrenIDs.Add(GlTF_Node.GetIDFromObject(t)); } } GlTF_Writer.nodeNames.Add(node.name); GlTF_Writer.nodeIDs.Add(node.id); GlTF_Writer.nodes.Add(node); // Debug.Log(node.name + "_" + node.id + "_" + nbIndex++); } if (GlTF_Writer.meshes.Count == 0) { Debug.Log("No visible objects have been exported. Aboring export"); yield return(false); } writer.OpenFiles(path); writer.Write(); writer.CloseFiles(); if (nbDisabledObjects > 0) { Debug.Log(nbDisabledObjects + " disabled object ignored during export"); } Debug.Log("Scene has been exported to " + path); EditorUtility.DisplayDialog("状态", "导出成功", "Ok"); done = true; yield return(true); }