// ----------------------------------------- // DumpAnims // ----------------------------------------- void DumpAnims(GameObject _animPrefab, GLTF _gltf, BufferInfo _bufInfo) { // get animations GameObject prefabInst = PrefabUtility.InstantiatePrefab(_animPrefab) as GameObject; List <AnimationClip> clips = Utils.GetAnimationClips(prefabInst); // get joints List <GameObject> joints = new List <GameObject>(); Utils.RecurseNode(prefabInst, _go => { // this is not a joint if (_go.GetComponent <SkinnedMeshRenderer>() != null) { return(false); } joints.Add(_go); return(true); }); // if (clips != null) { // process AnimationClip(s) foreach (AnimationClip clip in clips) { int accOffset = _bufInfo.GetAccessorCount(); AnimData animData = DumpAnimData(prefabInst, clip); DumpBufferInfoFromAnimData(animData, _bufInfo); GLTF_AnimationEx gltfAnim = DumpGltfAnimationEx(animData, joints, accOffset); _gltf.animations.Add(gltfAnim); } } Object.DestroyImmediate(prefabInst); }
Dictionary <string, JSON_Asset> saveAssets( string dest, List <Object> prefabs, List <Object> modelPrefabs, List <Mesh> meshes, List <Texture> textures, List <Texture> spriteTextures, List <Material> materials, List <Font> fonts ) { Dictionary <string, JSON_Asset> assetsJson = new Dictionary <string, JSON_Asset>(); // DELME { // // save meshes // var destMeshes = Path.Combine(dest, "meshes"); // foreach (Mesh mesh in meshes) { // string id = Utils.AssetID(mesh); // GLTF gltf = new GLTF(); // gltf.asset = new GLTF_Asset // { // version = "1.0.0", // generator = "u3d-exporter" // }; // BufferInfo bufInfo = new BufferInfo // { // id = id, // name = mesh.name // }; // DumpMesh(mesh, gltf, bufInfo, 0); // DumpBuffer(bufInfo, gltf); // Save( // destMeshes, // id, // gltf, // new List<BufferInfo> { bufInfo } // ); // // add asset to table // assetsJson.Add(id, new JSON_Asset { // type = "mesh", // urls = new Dictionary<string, string> { // { "gltf", "meshes/" + id + ".gltf" }, // { "bin", "meshes/" + id + ".bin" } // } // }); // } // } DELME // ======================================== // save animations // ======================================== var destAnims = Path.Combine(dest, "anims"); foreach (GameObject prefab in prefabs) { // skip ModelPrefab if (PrefabUtility.GetPrefabType(prefab) == PrefabType.ModelPrefab) { Debug.LogWarning("Can not export model prefab " + prefab.name + " in the scene"); continue; } // skip non-animation prefab bool isAnimPrefab = Utils.IsAnimPrefab(prefab); if (isAnimPrefab == false) { continue; } // get animations GameObject prefabInst = PrefabUtility.InstantiatePrefab(prefab) as GameObject; List <AnimationClip> clips = Utils.GetAnimationClips(prefabInst); // get joints List <GameObject> joints = new List <GameObject>(); Utils.RecurseNode(prefabInst, _go => { // this is not a joint if (_go.GetComponent <SkinnedMeshRenderer>() != null) { return(false); } joints.Add(_go); return(true); }); // dump animation clips if (clips != null) { // process AnimationClip(s) foreach (AnimationClip clip in clips) { string id = Utils.AssetID(clip); GLTF gltf = new GLTF(); gltf.asset = new GLTF_Asset { version = "1.0.0", generator = "u3d-exporter" }; BufferInfo bufInfo = new BufferInfo { id = id, name = prefab.name }; AnimData animData = DumpAnimData(prefabInst, clip); DumpBufferInfoFromAnimData(animData, bufInfo); GLTF_AnimationEx gltfAnim = DumpGltfAnimationEx(animData, joints, 0); gltf.animations.Add(gltfAnim); DumpBuffer(bufInfo, gltf); Save( destAnims, id + ".anim", gltf, new List <BufferInfo> { bufInfo } ); // add asset to table try { if (!assetsJson.ContainsKey(id)) { assetsJson.Add(id, new JSON_Asset { type = "animation", urls = new Dictionary <string, string> { { "anim", "anims/" + id + ".anim" }, { "bin", "anims/" + id + ".bin" } } }); } } catch (System.SystemException e) { Debug.LogError("Failed to add " + id + " to assets: " + e); } } } Object.DestroyImmediate(prefabInst); } // ======================================== // save prefabs // ======================================== var destMeshes = Path.Combine(dest, "meshes"); var destPrefabs = Path.Combine(dest, "prefabs"); // create dest directory if (!Directory.Exists(destMeshes)) { Directory.CreateDirectory(destMeshes); } if (!Directory.Exists(destPrefabs)) { Directory.CreateDirectory(destPrefabs); } foreach (GameObject prefab in prefabs) { string id = Utils.AssetID(prefab); // save prefabs if (PrefabUtility.GetPrefabType(prefab) == PrefabType.ModelPrefab) { Debug.LogWarning("Can not export model prefab " + prefab.name + " in the scene"); continue; } var prefabJson = DumpPrefab(prefab); string path; string json = JsonConvert.SerializeObject(prefabJson, Formatting.Indented); path = Path.Combine(destPrefabs, id + ".json"); StreamWriter writer = new StreamWriter(path); writer.Write(json); writer.Close(); // Debug.Log(Path.GetFileName(path) + " saved."); // add asset to table if (!assetsJson.ContainsKey(id)) { assetsJson.Add(id, new JSON_Asset { type = "prefab", urls = new Dictionary <string, string> { { "json", "prefabs/" + id + ".json" }, } }); } } // save model prefab (as gltf) foreach (GameObject modelPrefab in modelPrefabs) { string id = Utils.AssetID(modelPrefab); // save model prefabs GLTF gltf = new GLTF(); gltf.asset = new GLTF_Asset { version = "1.0.0", generator = "u3d-exporter" }; BufferInfo bufInfo = new BufferInfo { id = id, name = modelPrefab.name }; bool isAnimPrefab = Utils.IsAnimPrefab(modelPrefab); if (isAnimPrefab) { DumpSkinningModel(modelPrefab, gltf, bufInfo); DumpBuffer(bufInfo, gltf); } else { DumpModel(modelPrefab, gltf, bufInfo); DumpBuffer(bufInfo, gltf); } Save( destMeshes, id + ".gltf", gltf, new List <BufferInfo> { bufInfo } ); // add asset to table if (!assetsJson.ContainsKey(id)) { assetsJson.Add(id, new JSON_Asset { type = "gltf", urls = new Dictionary <string, string> { { "gltf", "meshes/" + id + ".gltf" }, { "bin", "meshes/" + id + ".bin" } } }); } } // save meshes (as gltf) foreach (Mesh mesh in meshes) { string id = Utils.AssetID(mesh); // save model prefabs GLTF gltf = new GLTF(); gltf.asset = new GLTF_Asset { version = "1.0.0", generator = "u3d-exporter" }; BufferInfo bufInfo = new BufferInfo { id = id, name = mesh.name }; DumpMesh(mesh, gltf, bufInfo, 0); DumpBuffer(bufInfo, gltf); Save( destMeshes, id + ".mesh", gltf, new List <BufferInfo> { bufInfo } ); // add asset to table if (!assetsJson.ContainsKey(id)) { assetsJson.Add(id, new JSON_Asset { type = "mesh", urls = new Dictionary <string, string> { { "mesh", "meshes/" + id + ".mesh" }, { "bin", "meshes/" + id + ".bin" } } }); } } // ======================================== // save textures // ======================================== var destTextures = Path.Combine(dest, "textures"); // create dest directory if (!Directory.Exists(destTextures)) { Directory.CreateDirectory(destTextures); } foreach (Texture tex in textures) { var textureJson = DumpTexture(tex); string path; string json = JsonConvert.SerializeObject(textureJson, Formatting.Indented); string id = Utils.AssetID(tex); // json path = Path.Combine(destTextures, id + ".json"); StreamWriter writer = new StreamWriter(path); writer.Write(json); writer.Close(); // image string assetPath = AssetDatabase.GetAssetPath(tex); path = Path.Combine(destTextures, id + Utils.AssetExt(tex)); File.Copy(assetPath, path, true); // Debug.Log(Path.GetFileName(path) + " saved."); // add asset to table if (!assetsJson.ContainsKey(id)) { assetsJson.Add(id, new JSON_Asset { type = "texture", urls = new Dictionary <string, string> { { "json", "textures/" + id + ".json" }, { "image", "textures/" + id + Utils.AssetExt(tex) }, } }); } } // ======================================== // save sprite textures // ======================================== var destSprites = Path.Combine(dest, "sprites"); // create dest directory if (!Directory.Exists(destSprites)) { Directory.CreateDirectory(destSprites); } foreach (Texture spriteTex in spriteTextures) { var spriteTextureJson = DumpSpriteTexture(spriteTex); string path; string json = JsonConvert.SerializeObject(spriteTextureJson, Formatting.Indented); string id = Utils.AssetID(spriteTex); // json path = Path.Combine(destSprites, id + ".json"); StreamWriter writer = new StreamWriter(path); writer.Write(json); writer.Close(); // image string assetPath = AssetDatabase.GetAssetPath(spriteTex); path = Path.Combine(destSprites, id + Utils.AssetExt(spriteTex)); File.Copy(assetPath, path); // add asset to table if (!assetsJson.ContainsKey(id)) { assetsJson.Add(id, new JSON_Asset { type = "texture", urls = new Dictionary <string, string> { { "json", "sprites/" + id + ".json" }, { "image", "sprites/" + id + Utils.AssetExt(spriteTex) }, } }); } } // ======================================== // save fonts // ======================================== var destFonts = Path.Combine(dest, "fonts"); if (!Directory.Exists(destFonts)) { Directory.CreateDirectory(destFonts); } foreach (Font font in fonts) { if (font.dynamic) { if (font.fontNames.Length > 1) // system font // do nothing { } else // opentype font { JSON_OpenTypeFont fontJson = DumpOpenTypeFont(font); // save font json string path; string json = JsonConvert.SerializeObject(fontJson, Formatting.Indented); string id = Utils.AssetID(font); path = Path.Combine(destFonts, id + ".json"); StreamWriter writer = new StreamWriter(path); writer.Write(json); writer.Close(); // save font file (ttf) Texture tex = font.material.mainTexture; string assetPath = AssetDatabase.GetAssetPath(font); path = Path.Combine(destFonts, id + Utils.AssetExt(font)); File.Copy(assetPath, path, true); if (!assetsJson.ContainsKey(id)) { assetsJson.Add(id, new JSON_Asset { type = "otfont", urls = new Dictionary <string, string> { { "json", "fonts/" + id + ".json" }, { "bin", "fonts/" + id + Utils.AssetExt(font) }, } }); } } } else // bitmapFont { JSON_BitmapFont fontJson = DumpBitmapFont(font); string path; string json = JsonConvert.SerializeObject(fontJson, Formatting.Indented); string id = Utils.AssetID(font); path = Path.Combine(destFonts, id + ".json"); StreamWriter writer = new StreamWriter(path); writer.Write(json); writer.Close(); if (!assetsJson.ContainsKey(id)) { assetsJson.Add(id, new JSON_Asset { type = "bmfont", urls = new Dictionary <string, string> { { "json", "fonts/" + id + ".json" }, } }); } } } // ======================================== // save materials // ======================================== var destMaterials = Path.Combine(dest, "materials"); // create dest directory if (!Directory.Exists(destMaterials)) { Directory.CreateDirectory(destMaterials); } foreach (Material mat in materials) { var materialJson = DumpMaterial(mat); if (materialJson == null) { continue; } string path; string json = JsonConvert.SerializeObject(materialJson, Formatting.Indented); string id = Utils.AssetID(mat); // json path = Path.Combine(destMaterials, id + ".json"); StreamWriter writer = new StreamWriter(path); writer.Write(json); writer.Close(); // Debug.Log(Path.GetFileName(path) + " saved."); // add asset to table if (!assetsJson.ContainsKey(id)) { assetsJson.Add(id, new JSON_Asset { type = "material", urls = new Dictionary <string, string> { { "json", "materials/" + id + ".json" }, } }); } } // ======================================== // save assets // ======================================== { string path = Path.Combine(dest, "assets.json"); string json = JsonConvert.SerializeObject(assetsJson, Formatting.Indented); StreamWriter writer = new StreamWriter(path); writer.Write(json); writer.Close(); } return(assetsJson); }
// ----------------------------------------- // DumpGltfAnimationEx // ----------------------------------------- GLTF_AnimationEx DumpGltfAnimationEx(AnimData _animData, List <GameObject> _joints, int _accOffset) { GLTF_AnimationEx result = new GLTF_AnimationEx(); result.name = _animData.name; List <GLTF_AnimChannelEx> channels = new List <GLTF_AnimChannelEx>(); int offset = 2; // acc offset start from 2 (0 is time0, 1 is times) foreach (var entry in _animData.nameToFrames) { NodeFrames frames = entry.Value; // T if (frames.tlist.Count > 0) { // NOTE: index 0 = "time0", index 1 = "time1" GLTF_AnimChannelEx channel = new GLTF_AnimChannelEx { input = (frames.tlist.Count == 1 ? 0 : 1) + _accOffset, output = offset + _accOffset, node = _joints.IndexOf(frames.node), path = "translation", }; channels.Add(channel); offset += 1; } // S if (frames.slist.Count > 0) { // NOTE: index 0 = "time0", index 1 = "time1" GLTF_AnimChannelEx channel = new GLTF_AnimChannelEx { input = (frames.slist.Count == 1 ? 0 : 1) + _accOffset, output = offset + _accOffset, node = _joints.IndexOf(frames.node), path = "scale", }; channels.Add(channel); offset += 1; } // R if (frames.rlist.Count > 0) { // NOTE: index 0 = "time0", index 1 = "time1" GLTF_AnimChannelEx channel = new GLTF_AnimChannelEx { input = (frames.rlist.Count == 1 ? 0 : 1) + _accOffset, output = offset + _accOffset, node = _joints.IndexOf(frames.node), path = "rotation", }; channels.Add(channel); offset += 1; } } result.channels = channels; return(result); }
// ----------------------------------------- // DumpBufferInfoFromAnimData // ----------------------------------------- void DumpBufferInfoFromAnimData(AnimData _animData, BufferInfo _bufInfo) { List <AccessorInfo> accessors = new List <AccessorInfo>(); // calculate total length of animation-data int length = 0; AccessorInfo acc; // time0 acc = new AccessorInfo { name = "time0@" + _animData.name, offset = length, count = 1, compType = ComponentType.FLOAT32, attrType = AttrType.SCALAR }; accessors.Add(acc); length += 4; // times acc = new AccessorInfo { name = "times@" + _animData.name, offset = length, count = _animData.times.Count, compType = ComponentType.FLOAT32, attrType = AttrType.SCALAR }; accessors.Add(acc); length += _animData.times.Count * 4; // frames foreach (var entry in _animData.nameToFrames) { NodeFrames frames = entry.Value; if (frames.tlist.Count > 0) { acc = new AccessorInfo { name = frames.node.name + "_T@" + _animData.name, offset = length, count = frames.tlist.Count, compType = ComponentType.FLOAT32, attrType = AttrType.VEC3 }; accessors.Add(acc); length += frames.tlist.Count * 12; } if (frames.slist.Count > 0) { acc = new AccessorInfo { name = frames.node.name + "_S@" + _animData.name, offset = length, count = frames.slist.Count, compType = ComponentType.FLOAT32, attrType = AttrType.VEC3 }; accessors.Add(acc); length += frames.slist.Count * 12; } if (frames.rlist.Count > 0) { acc = new AccessorInfo { name = frames.node.name + "_R@" + _animData.name, offset = length, count = frames.rlist.Count, compType = ComponentType.FLOAT32, attrType = AttrType.VEC4 }; accessors.Add(acc); length += frames.rlist.Count * 16; } } // write data byte[] clipData; using (MemoryStream stream = new MemoryStream(length)) { using (BinaryWriter writer = new BinaryWriter(stream)) { // time0 writer.Write(0.0f); // times for (int i = 0; i < _animData.times.Count; ++i) { writer.Write(_animData.times[i]); } foreach (var entry in _animData.nameToFrames) { NodeFrames frames = entry.Value; if (frames.tlist.Count > 0) { for (int i = 0; i < frames.tlist.Count; ++i) { // NOTE: convert LH to RH writer.Write(frames.tlist[i].x); writer.Write(frames.tlist[i].y); writer.Write(-frames.tlist[i].z); } } if (frames.slist.Count > 0) { for (int i = 0; i < frames.slist.Count; ++i) { writer.Write(frames.slist[i].x); writer.Write(frames.slist[i].y); writer.Write(frames.slist[i].z); } } if (frames.rlist.Count > 0) { for (int i = 0; i < frames.rlist.Count; ++i) { // NOTE: convert LH to RH writer.Write(-frames.rlist[i].x); writer.Write(-frames.rlist[i].y); writer.Write(frames.rlist[i].z); writer.Write(frames.rlist[i].w); } } } } clipData = stream.ToArray(); } // buffer view BufferViewInfo bufView = new BufferViewInfo { name = _animData.name, offset = _bufInfo.data.Length, length = clipData.Length, type = BufferType.NONE, accessors = accessors }; // byte[] data = new byte[_bufInfo.data.Length + clipData.Length]; int offset = 0; System.Buffer.BlockCopy(_bufInfo.data, 0, data, offset, _bufInfo.data.Length); offset += _bufInfo.data.Length; System.Buffer.BlockCopy(clipData, 0, data, offset, clipData.Length); offset += clipData.Length; // _bufInfo.data = data; _bufInfo.bufferViews.Add(bufView); }
// ----------------------------------------- // DumpAnimData // ----------------------------------------- AnimData DumpAnimData(GameObject _prefabInst, AnimationClip _clip) { AnimData animData = new AnimData(); // name animData.name = _clip.name; // get frames float step = 1.0f / _clip.frameRate; for (float t = 0.0f; t < _clip.length; t += step) { animData.times.Add(t); } animData.times.Add(_clip.length); // sample frames for (int i = 0; i < animData.times.Count; ++i) { float t = animData.times[i]; _clip.SampleAnimation(_prefabInst, t); Utils.RecurseNode(_prefabInst, _go => { // this is not a joint if (_go.GetComponent <SkinnedMeshRenderer>() != null) { return(false); } // dump translation, scale and rotation in current frame string name = _go.name; NodeFrames frames; if (animData.nameToFrames.TryGetValue(name, out frames) == false) { frames = new NodeFrames(); frames.node = _go; animData.nameToFrames.Add(name, frames); } frames.tlist.Add(_go.transform.localPosition); frames.slist.Add(_go.transform.localScale); frames.rlist.Add(_go.transform.localRotation); return(true); }); } // strip empty frames (keep 1 frame for it) foreach (var entry in animData.nameToFrames) { NodeFrames frames = entry.Value; // T bool hasFrameT = false; for (int i = 0; i < frames.tlist.Count; ++i) { if (frames.tlist[i] != frames.tlist[0]) { hasFrameT = true; break; } } if (hasFrameT == false) { if (frames.tlist[0] == Vector3.zero) { frames.tlist.Clear(); } else { frames.tlist.RemoveRange(1, frames.tlist.Count - 1); hasFrameT = true; } } // S bool hasFrameS = false; for (int i = 0; i < frames.slist.Count; ++i) { if (frames.slist[i] != frames.slist[0]) { hasFrameS = true; break; } } if (hasFrameS == false) { if (frames.slist[0] == Vector3.one) { frames.slist.Clear(); } else { frames.slist.RemoveRange(1, frames.slist.Count - 1); hasFrameS = true; } } // R bool hasFrameR = false; for (int i = 0; i < frames.rlist.Count; ++i) { if (frames.rlist[i] != frames.rlist[0]) { hasFrameR = true; break; } } if (hasFrameR == false) { if (frames.rlist[0] == Quaternion.identity) { frames.rlist.Clear(); } else { frames.rlist.RemoveRange(1, frames.rlist.Count - 1); hasFrameR = true; } } // if (!hasFrameT && !hasFrameS && !hasFrameR) { animData.nameToFrames.Remove(name); } } return(animData); }