public BoneAnimPath(Type type, string animName, string objectName) { this.type = type; var typeStr = TypeToString(type); target = new GlTF_Target(); target.id = objectName; target.path = typeStr; var subName = typeStr + "_" + animName + "_" + objectName; sampler = new GlTF_AnimSampler("sampler_" + subName, "param_" + subName); channel = new GlTF_Channel(sampler, target); accessor = new GlTF_Accessor("accessor_anim_" + subName, TypeToAccType(type), GlTF_Accessor.ComponentType.FLOAT); switch (type) { case Type.Translation: case Type.Scale: accessor.bufferView = GlTF_Writer.vec3BufferView; break; case Type.Rotation: accessor.bufferView = GlTF_Writer.vec4BufferView; break; } GlTF_Writer.accessors.Add(accessor); }
public void Populate(Transform m, ref GlTF_Accessor invBindMatricesAccessor, int invBindAccessorIndex) { SkinnedMeshRenderer skinMesh = m.GetComponent <SkinnedMeshRenderer>(); if (!skinMesh) { return; } // Populate bind poses. From https://docs.unity3d.com/ScriptReference/Mesh-bindposes.html: // The bind pose is bone's inverse transformation matrix // In this case we also make this matrix relative to the root // So that we can move the root game object around freely joints = new List <Transform>(); //Collect all bones from skin object. Order should be kept here since bones are referenced in the mesh foreach (Transform t in skinMesh.bones) { joints.Add(t); } Matrix4x4[] invBindMatrices = new Matrix4x4[joints.Count]; for (int i = 0; i < skinMesh.bones.Length; ++i) { // Generates inverseWorldMatrix in right-handed coordinate system Matrix4x4 invBind = skinMesh.sharedMesh.bindposes[i]; convertMatrixLeftToRightHandedness(ref invBind); invBindMatrices[i] = invBind; } invBindMatricesAccessor.Populate(invBindMatrices, m); invBindMatricesAccessorIndex = invBindAccessorIndex; }
/// Returns an accessor that uses a differenttype. /// This is useful if you want to create (for example) a VEC2 view of a buffer that holds VEC4s. /// If the type is smaller, you get a new accessor that uses the same bufferview /// If the type is the same, you get the same accessor back. /// If the type is bigger, you get an exception public static GlTF_Accessor CloneWithDifferentType( GlTF_Globals G, GlTF_Accessor fromAccessor, Type newType) { if (newType == fromAccessor.type) { return(fromAccessor); } var ret = new GlTF_Accessor(G, fromAccessor, newType); G.accessors.Add(ret); return(ret); }
// Returns null if the layout says there's no data in the specified texcoord public AttributeInfo?GetTexcoordInfo(int texcoord) { var numComponents = GetTexcoordSize(texcoord); if (numComponents == 0) { return(null); } return(new AttributeInfo( GlTF_Accessor.GetTypeForNumComponents(numComponents), GlTF_Accessor.ComponentType.FLOAT)); }
void PopulateTime(string animName, Keyframe[] keyFrames) { timeAccessor = new GlTF_Accessor("accessor_anim_time_" + animName, GlTF_Accessor.Type.SCALAR, GlTF_Accessor.ComponentType.FLOAT); timeAccessor.bufferView = GlTF_Writer.floatBufferView; GlTF_Writer.accessors.Add(timeAccessor); var times = new float[keyFrames.Length]; for (int i = 0; i < keyFrames.Length; i++) { times[i] = keyFrames[i].time; } timeAccessor.Populate(times); }
public void Populate(Transform m, ref GlTF_Accessor invBindMatricesAccessor, int invBindAccessorIndex) { SkinnedMeshRenderer skinMesh = m.GetComponent <SkinnedMeshRenderer>(); if (!skinMesh) { return; } // Populate bind poses. From https://docs.unity3d.com/ScriptReference/Mesh-bindposes.html: // The bind pose is bone's inverse transformation matrix // In this case we also make this matrix relative to the root // So that we can move the root game object around freely joints = new List <Transform>(); //Collect all bones from skin object. Order should be kept here since bones are referenced in the mesh foreach (Transform t in skinMesh.bones) { joints.Add(t); } // glTF expects a single hierarchy of bones, but Unity skips all the nodes that are not used. // Find the common ancestor of all used bones in order to get a valid bone herarchy rootBone = rebuildBoneHierarchy(skinMesh, ref joints); Matrix4x4[] invBindMatrices = new Matrix4x4[joints.Count]; for (int i = 0; i < invBindMatrices.Length; ++i) { // Generates inverseWorldMatrix in right-handed coordinate system // Manually converts world translation and rotation from left to right handed coordinates systems Vector3 pos = joints[i].position; Quaternion rot = joints[i].rotation; convertQuatLeftToRightHandedness(ref rot); convertVector3LeftToRightHandedness(ref pos); invBindMatrices[i] = Matrix4x4.TRS(pos, rot, joints[i].lossyScale).inverse *sceneRootMatrix.inverse; } invBindMatricesAccessor.Populate(invBindMatrices, m); invBindMatricesAccessorIndex = invBindAccessorIndex; }
public void Populate(Transform m, ref GlTF_Accessor invBindMatricesAccessor, int invBindAccessorIndex) { SkinnedMeshRenderer skinMesh = m.GetComponent <SkinnedMeshRenderer>(); if (!skinMesh) { return; } // Populate bind poses. From https://docs.unity3d.com/ScriptReference/Mesh-bindposes.html: // The bind pose is bone's inverse transformation matrix // In this case we also make this matrix relative to the root // So that we can move the root game object around freely Mesh mesh = skinMesh.sharedMesh; Matrix4x4[] invBindMatrices = new Matrix4x4[skinMesh.sharedMesh.bindposes.Length]; for (int i = 0; i < invBindMatrices.Length; ++i) { // Generates inverseWorldMatrix in right-handed coordinate system // Manually converts world translation and rotation from left to right handed coordinates systems Vector3 pos = skinMesh.bones[i].position; Quaternion rot = skinMesh.bones[i].rotation; convertQuatLeftToRightHandedness(ref rot); convertVector3LeftToRightHandedness(ref pos); invBindMatrices[i] = Matrix4x4.TRS(pos, rot, skinMesh.bones[i].lossyScale).inverse *sceneRootMatrix.inverse; } invBindMatricesAccessor.Populate(invBindMatrices, m); invBindMatricesAccessorIndex = invBindAccessorIndex; // Fill jointNames jointNames = new string[skinMesh.bones.Length]; for (int i = 0; i < skinMesh.bones.Length; ++i) { jointNames[i] = GlTF_Node.GetNameFromObject(skinMesh.bones[i]); } }
public void Populate(SkinnedMeshRenderer smr, List <Transform> skeletons) { name = GetNameFromObject(smr.transform); if (smr.rootBone != null) { boneNames = new List <string>(); List <Matrix4x4> boneMats = new List <Matrix4x4>(); var parent = smr.rootBone.parent; if (parent != null) { var mat = Matrix4x4.TRS(parent.localPosition, parent.localRotation, parent.localScale); bindShape = new GlTF_Matrix(mat); } else { bindShape = new GlTF_Matrix(Matrix4x4.identity); } bindShape.name = "bindShapeMatrix"; skeletons.Add(smr.rootBone); for (var i = 0; i < smr.bones.Length; ++i) { var found = IsBoneInHierarchy(smr.rootBone, smr.bones[i]); if (!found) { // set as its own skeleton to prevent error if it doesn't get included in rootBone hierarchy skeletons.Add(smr.bones[i]); } boneNames.Add(GlTF_Node.GetNameFromObject(smr.bones[i])); boneMats.Add(smr.sharedMesh.bindposes[i]); } ibmAccessor = new GlTF_Accessor("accessor_ibm_" + name, GlTF_Accessor.Type.MAT4, GlTF_Accessor.ComponentType.FLOAT); ibmAccessor.bufferView = GlTF_Writer.mat4BufferView; ibmAccessor.Populate(boneMats.ToArray()); GlTF_Writer.accessors.Add(ibmAccessor); } }
// Adds to gltfMesh the glTF dependencies (primitive, material, technique, program, shaders) // required by unityMesh, using matObjName for naming the various material-related glTF // components. This does not add any geometry from the mesh (that's done separately using // GlTF_Mesh.Populate()). // // This does not create the material either. It adds a reference to a material that // presumably will be created very soon (if it hasn't previously been created). private void AddMeshDependencies( ObjectName meshName, IExportableMaterial exportableMaterial, GlTF_Mesh gltfMesh, GlTF_VertexLayout gltfLayout) { GlTF_Primitive primitive = new GlTF_Primitive( new GlTF_Attributes(G, meshName, gltfLayout)); GlTF_Accessor indexAccessor = G.CreateAccessor( GlTF_Accessor.GetNameFromObject(meshName, "indices_0"), GlTF_Accessor.Type.SCALAR, GlTF_Accessor.ComponentType.USHORT, isNonVertexAttributeAccessor: true); primitive.indices = indexAccessor; if (gltfMesh.primitives.Count > 0) { Debug.LogError("More than one primitive per mesh is unimplemented and unsupported"); } gltfMesh.primitives.Add(primitive); // This needs to be a forward-reference (ie, by name) because G.materials[exportableMaterial] // may not have been created yet. primitive.materialName = GlTF_Material.GetNameFromObject(exportableMaterial); }
// Private to force people to use the better-named CloneWithDifferentType() method. private GlTF_Accessor(GlTF_Globals G, GlTF_Accessor fromAccessor, Type newType) : base(G) { m_clonedFrom = fromAccessor; if (newType >= fromAccessor.type) { throw new ArgumentException("newType must be smaller than fromAccessor.type"); } this.name = $"{fromAccessor.name}_{newType}"; this.bufferView = fromAccessor.bufferView; this.byteStride = fromAccessor.byteStride; this.type = newType; this.componentType = fromAccessor.componentType; // Leave these null; at serialization time, we "inherit" a value from m_clonedFrom // this.count = fromAccessor.count; // this.byteOffset = fromAccessor.byteOffset; // These aren't nullables because the purity isn't worth the pain, but at least poison them this.maxFloat = new Vector4(float.NaN, float.NaN, float.NaN, float.NaN); this.minFloat = new Vector4(float.NaN, float.NaN, float.NaN, float.NaN); this.minInt = 0x0D00B015; this.maxInt = 0x0D00B015; SanityCheckBufferViewStride(); }
public void Populate(AnimationClipCurveData curveData) { string propName = curveData.propertyName; if (times == null) // allocate one array of times, assumes all channels have same number of keys { timeAccessor = new GlTF_Accessor(name + "TimeAccessor", GlTF_Accessor.Type.SCALAR, GlTF_Accessor.ComponentType.FLOAT); timeAccessor.bufferView = GlTF_Writer.floatBufferView; GlTF_Writer.accessors.Add(timeAccessor); times = new float[curveData.curve.keys.Length]; for (int i = 0; i < curveData.curve.keys.Length; i++) { times[i] = curveData.curve.keys[i].time; } timeAccessor.Populate(times); } if (propName.Contains("m_LocalPosition")) { if (positions == null) { translationAccessor = new GlTF_Accessor(name + "TranslationAccessor", GlTF_Accessor.Type.VEC3, GlTF_Accessor.ComponentType.FLOAT); translationAccessor.bufferView = GlTF_Writer.vec3BufferView; GlTF_Writer.accessors.Add(translationAccessor); positions = new Vector3[curveData.curve.keys.Length]; } if (propName.Contains(".x")) { px = true; for (int i = 0; i < curveData.curve.keys.Length; i++) { positions[i].x = curveData.curve.keys[i].value; } } else if (propName.Contains(".y")) { py = true; for (int i = 0; i < curveData.curve.keys.Length; i++) { positions[i].y = curveData.curve.keys[i].value; } } else if (propName.Contains(".z")) { pz = true; for (int i = 0; i < curveData.curve.keys.Length; i++) { positions[i].z = curveData.curve.keys[i].value; } } if (px && py && pz) { translationAccessor.Populate(positions); } } if (propName.Contains("m_LocalScale")) { if (scales == null) { scaleAccessor = new GlTF_Accessor(name + "ScaleAccessor", GlTF_Accessor.Type.VEC3, GlTF_Accessor.ComponentType.FLOAT); scaleAccessor.bufferView = GlTF_Writer.vec3BufferView; GlTF_Writer.accessors.Add(scaleAccessor); scales = new Vector3[curveData.curve.keys.Length]; } if (propName.Contains(".x")) { sx = true; for (int i = 0; i < curveData.curve.keys.Length; i++) { scales[i].x = curveData.curve.keys[i].value; } } else if (propName.Contains(".y")) { sy = true; for (int i = 0; i < curveData.curve.keys.Length; i++) { scales[i].y = curveData.curve.keys[i].value; } } else if (propName.Contains(".z")) { sz = true; for (int i = 0; i < curveData.curve.keys.Length; i++) { scales[i].z = curveData.curve.keys[i].value; } } if (sx && sy && sz) { scaleAccessor.Populate(scales); } } if (propName.Contains("m_LocalRotation")) { if (rotations == null) { rotationAccessor = new GlTF_Accessor(name + "RotationAccessor", GlTF_Accessor.Type.VEC4, GlTF_Accessor.ComponentType.FLOAT); rotationAccessor.bufferView = GlTF_Writer.vec4BufferView; GlTF_Writer.accessors.Add(rotationAccessor); rotations = new Vector4[curveData.curve.keys.Length]; } if (propName.Contains(".x")) { rx = true; for (int i = 0; i < curveData.curve.keys.Length; i++) { rotations[i].x = curveData.curve.keys[i].value; } } else if (propName.Contains(".y")) { ry = true; for (int i = 0; i < curveData.curve.keys.Length; i++) { rotations[i].y = curveData.curve.keys[i].value; } } else if (propName.Contains(".z")) { rz = true; for (int i = 0; i < curveData.curve.keys.Length; i++) { rotations[i].z = curveData.curve.keys[i].value; } } else if (propName.Contains(".w")) { rw = true; for (int i = 0; i < curveData.curve.keys.Length; i++) { rotations[i].w = curveData.curve.keys[i].value; } } if (rx && ry && rz && rw) { rotationAccessor.Populate(scales); } } }
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); // Create rootNode GlTF_Node correctionNode = new GlTF_Node(); correctionNode.id = "UnityGlTF_root"; correctionNode.name = "UnityGlTF_root"; GlTF_Writer.nodes.Add(correctionNode); 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; foreach (Transform tr in trs) { if (tr.gameObject.activeInHierarchy == false) { nbDisabledObjects++; continue; } // Initialize the node GlTF_Node node = new GlTF_Node(); node.id = 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.name = GlTF_Writer.cleanNonAlphanumeric(GlTF_Mesh.GetNameFromObject(m) + tr.name); 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(a.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(animation.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.id); } 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)); } } GlTF_Writer.nodeNames.Add(node.id); GlTF_Writer.nodes.Add(node); } 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); if (buildZip) { ZipFile zip = new ZipFile(); Debug.Log(GlTF_Writer.exportedFiles.Count + " files generated"); string zipName = Path.GetFileNameWithoutExtension(path) + ".zip"; foreach (string originFilePath in GlTF_Writer.exportedFiles.Keys) { zip.AddFile(originFilePath, GlTF_Writer.exportedFiles[originFilePath]); } zip.Save(savedPath + "/" + zipName); // Remove all files foreach (string pa in GlTF_Writer.exportedFiles.Keys) { if (System.IO.File.Exists(pa)) { System.IO.File.Delete(pa); } } Debug.Log("Files have been cleaned"); } done = true; yield return(true); }
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); // 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.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.vec3BufferView; 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.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.vec4BufferView; 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.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.vec3BufferView; 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 GlTF_Attributes( GlTF_Globals G, ObjectName meshName, GlTF_VertexLayout layout) { // This prefix should be used with possibly-nonconforming gltf data: // - texcoords that are not 2-element or that don't contain texture coordinates // - made-up / mythical semantics like VERTEXID string nonconformingPrefix = (G.GltfCompatibilityMode) ? "_TB_UNITY_" : ""; m_layout = layout; { AttributeInfo positionInfo = layout.PositionInfo; positionAccessor = G.CreateAccessor( GlTF_Accessor.GetNameFromObject(meshName, "position"), positionInfo.accessorType, positionInfo.accessorComponentType); m_accessors.Add("POSITION", positionAccessor); } { if (layout.NormalInfo is AttributeInfo normalInfo) { normalAccessor = G.CreateAccessor( GlTF_Accessor.GetNameFromObject(meshName, "normal"), normalInfo.accessorType, normalInfo.accessorComponentType); // Genius particles put things that don't look like normals into the normal attribute. bool isNonconforming = (layout.m_tbLayout.normalSemantic == Semantic.Position); string prefix = isNonconforming ? nonconformingPrefix : ""; m_accessors.Add(prefix + "NORMAL", normalAccessor); } } { if (layout.ColorInfo is AttributeInfo cInfo) { colorAccessor = G.CreateAccessor( GlTF_Accessor.GetNameFromObject(meshName, "color"), cInfo.accessorType, cInfo.accessorComponentType, normalized: true); m_accessors.Add(G.Gltf2 ? "COLOR_0" : "COLOR", colorAccessor); } } { if (layout.TangentInfo is AttributeInfo tangentInfo) { tangentAccessor = G.CreateAccessor( GlTF_Accessor.GetNameFromObject(meshName, "tangent"), tangentInfo.accessorType, tangentInfo.accessorComponentType); m_accessors.Add("TANGENT", tangentAccessor); } } if (layout.PackVertexIdIntoTexcoord1W) { // The vertexid hack modifies the gl layout to extend texcoord1 so the vertexid // can be stuffed into it Debug.Assert(layout.m_tbLayout.GetTexcoordInfo(1).size == 3); Debug.Assert(layout.GetTexcoordSize(1) == 4); } GlTF_Accessor MakeAccessorFor(int texcoord) { var txcInfo = layout.GetTexcoordInfo(texcoord); if (txcInfo == null) { return(null); } Semantic tbSemantic = layout.m_tbLayout.GetTexcoordInfo(texcoord).semantic; string attrName = $"{nonconformingPrefix}TEXCOORD_{texcoord}"; // Timestamps are tunneled into us via a texcoord because there's not really a better way // due to GeometryPool limitations. But that's an internal implementation detail. I'd like // them to have a better attribute name in the gltf. if (tbSemantic == Semantic.Timestamp) { // For b/141876882; Poly doesn't like _TB_TIMESTAMP if (!G.Gltf2) { return(null); } attrName = "_TB_TIMESTAMP"; } var ret = G.CreateAccessor( GlTF_Accessor.GetNameFromObject(meshName, $"uv{texcoord}"), txcInfo.Value.accessorType, txcInfo.Value.accessorComponentType); m_accessors.Add(attrName, ret); return(ret); } texCoord0Accessor = MakeAccessorFor(0); texCoord1Accessor = MakeAccessorFor(1); texCoord2Accessor = MakeAccessorFor(2); texCoord3Accessor = MakeAccessorFor(3); if (G.GltfCompatibilityMode) { TiltBrush.GeometryPool.VertexLayout tbLayout = layout.m_tbLayout; switch (tbLayout.texcoord0.semantic) { case Semantic.Unspecified when tbLayout.texcoord0.size == 2: case Semantic.XyIsUv: case Semantic.XyIsUvZIsDistance: { GlTF_Accessor accessor = GlTF_Accessor.CloneWithDifferentType( G, texCoord0Accessor, GlTF_Accessor.Type.VEC2); m_accessors.Add("TEXCOORD_0", accessor); break; } } // No need to check the other texcoords because TB only ever puts texture coordinates // in texcoord0 } }
private void PopulateUv( int channel, TiltBrush.GeometryPool pool, GlTF_Accessor accessor, Semantic semantic) { bool packVertId = m_layout.PackVertexIdIntoTexcoord1W && channel == 1; if (packVertId) { // Guaranteed by GlTF_VertexLayout Debug.Assert(m_layout.m_tbLayout.GetTexcoordInfo(channel).size == 3); Debug.Assert(m_layout.GetTexcoordSize(channel) == 4); } if (accessor == null) { return; } if (channel < 0 || channel > 3) { throw new ArgumentException("Invalid channel"); } TiltBrush.GeometryPool.TexcoordData texcoordData = pool.GetTexcoordData(channel); if (semantic == Semantic.XyIsUvZIsDistance && accessor.type != GlTF_Accessor.Type.VEC3) { throw new ArgumentException("XyIsUvZIsDistance semantic can only be applied to VEC3"); } bool flipY; if (semantic == Semantic.Unspecified && channel == 0 && accessor.type == GlTF_Accessor.Type.VEC2) { Debug.LogWarning("Assuming Semantic.XyIsUv"); semantic = Semantic.XyIsUv; } switch (semantic) { case Semantic.Position: case Semantic.Vector: case Semantic.Timestamp: flipY = false; break; case Semantic.XyIsUvZIsDistance: case Semantic.XyIsUv: flipY = true; break; default: throw new ArgumentException("semantic"); } switch (accessor.type) { case GlTF_Accessor.Type.SCALAR: throw new NotImplementedException(); case GlTF_Accessor.Type.VEC2: accessor.Populate(texcoordData.v2, flipY: flipY, calculateMinMax: false); break; case GlTF_Accessor.Type.VEC3: accessor.Populate(texcoordData.v3, flipY: flipY, calculateMinMax: false); break; case GlTF_Accessor.Type.VEC4: if (packVertId) { // In the vertexId case, we actually have a vec3, which needs to be augmented to a vec4. // TODO: this should happen at some higher level. int i = 0; var v4 = texcoordData.v3.ConvertAll <Vector4>((v => new Vector4(v.x, v.y, v.z, i++))); accessor.Populate(v4, flipY: flipY, calculateMinMax: false); } else { accessor.Populate(texcoordData.v4, flipY: flipY, calculateMinMax: false); } break; default: throw new ArgumentException("Unexpected accessor.type"); } }
public void Populate (AnimationClipCurveData curveData) { string propName = curveData.propertyName; if (times == null) // allocate one array of times, assumes all channels have same number of keys { timeAccessor = new GlTF_Accessor(name+"TimeAccessor", "SCALAR", "FLOAT"); timeAccessor.bufferView = GlTF_Writer.floatBufferView; GlTF_Writer.accessors.Add (timeAccessor); times = new float[curveData.curve.keys.Length]; for (int i = 0; i < curveData.curve.keys.Length; i++) times[i] = curveData.curve.keys[i].time; timeAccessor.Populate (times); } if (propName.Contains("m_LocalPosition")) { if (positions == null) { translationAccessor = new GlTF_Accessor(name+"TranslationAccessor", "VEC3", "FLOAT"); translationAccessor.bufferView = GlTF_Writer.vec3BufferView; GlTF_Writer.accessors.Add (translationAccessor); positions = new Vector3[curveData.curve.keys.Length]; } if (propName.Contains (".x")) { px = true; for (int i = 0; i < curveData.curve.keys.Length; i++) positions[i].x = curveData.curve.keys[i].value; } else if (propName.Contains (".y")) { py = true; for (int i = 0; i < curveData.curve.keys.Length; i++) positions[i].y = curveData.curve.keys[i].value; } else if (propName.Contains (".z")) { pz = true; for (int i = 0; i < curveData.curve.keys.Length; i++) positions[i].z = curveData.curve.keys[i].value; } if (px && py && pz) translationAccessor.Populate (positions); } if (propName.Contains("m_LocalScale")) { if (scales == null) { scaleAccessor = new GlTF_Accessor(name+"ScaleAccessor", "VEC3", "FLOAT"); scaleAccessor.bufferView = GlTF_Writer.vec3BufferView; GlTF_Writer.accessors.Add (scaleAccessor); scales = new Vector3[curveData.curve.keys.Length]; } if (propName.Contains (".x")) { sx = true; for (int i = 0; i < curveData.curve.keys.Length; i++) scales[i].x = curveData.curve.keys[i].value; } else if (propName.Contains (".y")) { sy = true; for (int i = 0; i < curveData.curve.keys.Length; i++) scales[i].y = curveData.curve.keys[i].value; } else if (propName.Contains (".z")) { sz = true; for (int i = 0; i < curveData.curve.keys.Length; i++) scales[i].z = curveData.curve.keys[i].value; } if (sx && sy && sz) scaleAccessor.Populate (scales); } if (propName.Contains("m_LocalRotation")) { if (rotations == null) { rotationAccessor = new GlTF_Accessor(name+"RotationAccessor", "VEC4", "FLOAT"); rotationAccessor.bufferView = GlTF_Writer.vec4BufferView; GlTF_Writer.accessors.Add (rotationAccessor); rotations = new Vector4[curveData.curve.keys.Length]; } if (propName.Contains (".x")) { rx = true; for (int i = 0; i < curveData.curve.keys.Length; i++) rotations[i].x = curveData.curve.keys[i].value; } else if (propName.Contains (".y")) { ry = true; for (int i = 0; i < curveData.curve.keys.Length; i++) rotations[i].y = curveData.curve.keys[i].value; } else if (propName.Contains (".z")) { rz = true; for (int i = 0; i < curveData.curve.keys.Length; i++) rotations[i].z = curveData.curve.keys[i].value; } else if (propName.Contains (".w")) { rw = true; for (int i = 0; i < curveData.curve.keys.Length; i++) rotations[i].w = curveData.curve.keys[i].value; } if (rx && ry && rz && rw) rotationAccessor.Populate (scales); } }
public static BoundsDouble Export(string path, Transform[] trs, Transform root, out double minHeight, out double maxHeight) { minHeight = 0; maxHeight = 0; writer = new GlTF_Writer(); writer.Init(); if (presetAsset != null) { string psPath = AssetDatabase.GetAssetPath(presetAsset); if (psPath != null) { psPath = psPath.Remove(0, "Assets".Length); psPath = Application.dataPath + psPath; preset.Load(psPath); } } savedPath = Path.GetDirectoryName(path); savedFile = Path.GetFileNameWithoutExtension(path); EditorPrefs.SetString(KEY_PATH, savedPath); EditorPrefs.SetString(KEY_FILE, savedFile); Debug.Log("attempting to save to " + path); writer.OpenFiles(path); if (rtcScript != null && root != null) { var instance = Activator.CreateInstance(rtcScript.GetClass()); var rtc = instance as RTCCallback; if (rtc != null) { writer.RTCCenter = rtc.GetCenter(root); } } RotationCallback rotCallback = null;; if (rotScript != null) { var instance = Activator.CreateInstance(rotScript.GetClass()); rotCallback = instance as RotationCallback; } if (unpackTexture) { // prepass, for texture unpacker TextureUnpacker.Reset(); foreach (Transform tr in trs) { TextureUnpacker.CheckPackedTexture(tr, preset); } TextureUnpacker.Build(); } BoundsDouble bb = new BoundsDouble(); // first, collect objects in the scene, add to lists foreach (Transform tr in trs) { if (tr.GetComponent <Camera>() != null) { if (tr.GetComponent <Camera>().orthographic) { GlTF_Orthographic cam; cam = new GlTF_Orthographic(); cam.type = "orthographic"; cam.zfar = tr.GetComponent <Camera>().farClipPlane; cam.znear = tr.GetComponent <Camera>().nearClipPlane; cam.name = tr.name; //cam.orthographic.xmag = tr.camera. GlTF_Writer.cameras.Add(cam); } else { GlTF_Perspective cam; cam = new GlTF_Perspective(); cam.type = "perspective"; cam.zfar = tr.GetComponent <Camera>().farClipPlane; cam.znear = tr.GetComponent <Camera>().nearClipPlane; cam.aspect_ratio = tr.GetComponent <Camera>().aspect; cam.yfov = tr.GetComponent <Camera>().fieldOfView; cam.name = tr.name; GlTF_Writer.cameras.Add(cam); } } if (tr.GetComponent <Light>() != null) { switch (tr.GetComponent <Light>().type) { case LightType.Point: GlTF_PointLight pl = new GlTF_PointLight(); pl.color = new GlTF_ColorRGB(tr.GetComponent <Light>().color); pl.name = tr.name; GlTF_Writer.lights.Add(pl); break; case LightType.Spot: GlTF_SpotLight sl = new GlTF_SpotLight(); sl.color = new GlTF_ColorRGB(tr.GetComponent <Light>().color); sl.name = tr.name; GlTF_Writer.lights.Add(sl); break; case LightType.Directional: GlTF_DirectionalLight dl = new GlTF_DirectionalLight(); dl.color = new GlTF_ColorRGB(tr.GetComponent <Light>().color); dl.name = tr.name; GlTF_Writer.lights.Add(dl); break; case LightType.Area: GlTF_AmbientLight al = new GlTF_AmbientLight(); al.color = new GlTF_ColorRGB(tr.GetComponent <Light>().color); al.name = tr.name; GlTF_Writer.lights.Add(al); break; } } Mesh m = GetMesh(tr); if (m != null) { GlTF_Mesh mesh = new GlTF_Mesh(); mesh.name = 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 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) { 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); } 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.texCoord0Accessor = uv0Accessor; attributes.texCoord1Accessor = uv1Accessor; attributes.texCoord2Accessor = uv2Accessor; attributes.texCoord3Accessor = uv3Accessor; 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 && sm[i] != null) { var mat = sm[i]; var matName = GlTF_Material.GetNameFromObject(mat); primitive.materialName = matName; if (!GlTF_Writer.materials.ContainsKey(matName)) { GlTF_Material material = new GlTF_Material(); material.name = matName; GlTF_Writer.materials.Add(material.name, material); //technique var s = mat.shader; var techName = GlTF_Technique.GetNameFromObject(s); material.instanceTechniqueName = techName; if (!GlTF_Writer.techniques.ContainsKey(techName)) { GlTF_Technique tech = new GlTF_Technique(); tech.name = techName; GlTF_Technique.States ts = null; if (preset.techniqueStates.ContainsKey(s.name)) { ts = preset.techniqueStates[s.name]; } else if (preset.techniqueStates.ContainsKey("*")) { ts = preset.techniqueStates["*"]; } if (ts == null) { // Unless otherwise specified by a preset file, enable z-buffering. ts = new GlTF_Technique.States(); const int DEPTH_TEST = 2929; // int CULL_FACE = 2884; ts.enable = new int[1] { DEPTH_TEST }; } tech.states = ts; 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(writer.RTCCenter != null); GlTF_Writer.techniques.Add(techName, tech); int spCount = ShaderUtil.GetPropertyCount(s); for (var j = 0; j < spCount; ++j) { var pName = ShaderUtil.GetPropertyName(s, j); var pType = ShaderUtil.GetPropertyType(s, j); // Debug.Log(pName + " " + pType); GlTF_Technique.Uniform tUni; if (pType == ShaderUtil.ShaderPropertyType.Color) { tParam = new GlTF_Technique.Parameter(); tParam.name = pName; tParam.type = GlTF_Technique.Type.FLOAT_VEC4; tech.parameters.Add(tParam); tUni = new GlTF_Technique.Uniform(); tUni.name = pName; tUni.param = tParam.name; tech.uniforms.Add(tUni); } else if (pType == ShaderUtil.ShaderPropertyType.Vector) { tParam = new GlTF_Technique.Parameter(); tParam.name = pName; tParam.type = GlTF_Technique.Type.FLOAT_VEC4; tech.parameters.Add(tParam); tUni = new GlTF_Technique.Uniform(); tUni.name = pName; tUni.param = tParam.name; tech.uniforms.Add(tUni); } else if (pType == ShaderUtil.ShaderPropertyType.Float || pType == ShaderUtil.ShaderPropertyType.Range) { tParam = new GlTF_Technique.Parameter(); tParam.name = pName; tParam.type = GlTF_Technique.Type.FLOAT; tech.parameters.Add(tParam); tUni = new GlTF_Technique.Uniform(); tUni.name = pName; tUni.param = tParam.name; tech.uniforms.Add(tUni); } else if (pType == ShaderUtil.ShaderPropertyType.TexEnv) { var td = ShaderUtil.GetTexDim(s, j); if (td == UnityEngine.Rendering.TextureDimension.Tex2D) { tParam = new GlTF_Technique.Parameter(); tParam.name = pName; tParam.type = GlTF_Technique.Type.SAMPLER_2D; tech.parameters.Add(tParam); tUni = new GlTF_Technique.Uniform(); tUni.name = pName; tUni.param = tParam.name; tech.uniforms.Add(tUni); } } } // 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); // shader GlTF_Shader vs = new GlTF_Shader(); vs.name = GlTF_Shader.GetNameFromObject(s, GlTF_Shader.Type.Vertex); program.vertexShader = vs.name; vs.type = GlTF_Shader.Type.Vertex; vs.uri = preset.GetVertexShader(s.name); GlTF_Writer.shaders.Add(vs); GlTF_Shader fs = new GlTF_Shader(); fs.name = GlTF_Shader.GetNameFromObject(s, GlTF_Shader.Type.Fragment); program.fragmentShader = fs.name; fs.type = GlTF_Shader.Type.Fragment; fs.uri = preset.GetFragmentShader(s.name); GlTF_Writer.shaders.Add(fs); } int spCount2 = ShaderUtil.GetPropertyCount(s); for (var j = 0; j < spCount2; ++j) { var pName = ShaderUtil.GetPropertyName(s, j); var pType = ShaderUtil.GetPropertyType(s, j); if (pType == ShaderUtil.ShaderPropertyType.Color) { var matCol = new GlTF_Material.ColorValue(); matCol.name = pName; matCol.color = mat.GetColor(pName); material.values.Add(matCol); } else if (pType == ShaderUtil.ShaderPropertyType.Vector) { var matVec = new GlTF_Material.VectorValue(); matVec.name = pName; matVec.vector = mat.GetVector(pName); material.values.Add(matVec); } else if (pType == ShaderUtil.ShaderPropertyType.Float || pType == ShaderUtil.ShaderPropertyType.Range) { var matFloat = new GlTF_Material.FloatValue(); matFloat.name = pName; matFloat.value = mat.GetFloat(pName); material.values.Add(matFloat); } else if (pType == ShaderUtil.ShaderPropertyType.TexEnv) { var td = ShaderUtil.GetTexDim(s, j); if (td == UnityEngine.Rendering.TextureDimension.Tex2D) { var t = mat.GetTexture(pName); if (t == null) { continue; } var val = new GlTF_Material.StringValue(); val.name = pName; string texName = null; texName = GlTF_Texture.GetNameFromObject(t); val.value = texName; material.values.Add(val); if (!GlTF_Writer.textures.ContainsKey(texName)) { var texPath = ExportTexture(t, savedPath); GlTF_Image img = new GlTF_Image(); img.name = GlTF_Image.GetNameFromObject(t); img.uri = texPath; GlTF_Writer.images.Add(img); GlTF_Sampler sampler; var samplerName = GlTF_Sampler.GetNameFromObject(t); if (GlTF_Writer.samplers.ContainsKey(samplerName)) { sampler = GlTF_Writer.samplers[samplerName]; } else { sampler = new GlTF_Sampler(t); sampler.name = samplerName; GlTF_Writer.samplers[samplerName] = sampler; } GlTF_Texture texture = new GlTF_Texture(); texture.name = texName; texture.source = img.name; texture.samplerName = samplerName; GlTF_Writer.textures.Add(texName, texture); } } } } } } mesh.primitives.Add(primitive); } mesh.Populate(m); GlTF_Writer.meshes.Add(mesh); if (unpackTexture) { TextureUnpacker.ProcessMesh(mesh); } // calculate bounding box transform if (root != null) { Matrix4x4 brot = Matrix4x4.identity; if (rotCallback != null) { brot = rotCallback.GetBoundsRotationMatrix(root); } var pos = tr.position - root.position; // relative to parent var objMat = Matrix4x4.TRS(pos, tr.rotation, tr.lossyScale); //read vertices var ms = positionAccessor.bufferView.memoryStream; var offset = (int)positionAccessor.byteOffset; var len = positionAccessor.count; var buffer = new byte[len * 12]; var mspos = ms.Position; ms.Position = offset; ms.Read(buffer, 0, buffer.Length); minHeight = double.MaxValue; maxHeight = double.MinValue; double[] c = writer.RTCCenter; double[] minPos = new double[3]; minPos[0] = double.MaxValue; minPos[1] = double.MaxValue; minPos[2] = double.MaxValue; double[] maxPos = new double[3]; maxPos[0] = double.MinValue; maxPos[1] = double.MinValue; maxPos[2] = double.MinValue; for (int j = 0; j < len; ++j) { var x = System.BitConverter.ToSingle(buffer, j * 12); var y = System.BitConverter.ToSingle(buffer, j * 12 + 4); var z = System.BitConverter.ToSingle(buffer, j * 12 + 8); // local rotation var lx = objMat.m00 * x + objMat.m01 * y + objMat.m02 * z; var ly = objMat.m10 * x + objMat.m11 * y + objMat.m12 * z; var lz = objMat.m20 * x + objMat.m21 * y + objMat.m22 * z; minHeight = Math.Min(minHeight, ly); maxHeight = Math.Max(maxHeight, ly); // to world double wx = brot.m00 * lx + brot.m01 * ly + brot.m02 * lz; double wy = brot.m10 * lx + brot.m11 * ly + brot.m12 * lz; double wz = brot.m20 * lx + brot.m21 * ly + brot.m22 * lz; // local translation to world double tx = brot.m00 * pos.x + brot.m01 * pos.y + brot.m02 * pos.z; double ty = brot.m10 * pos.x + brot.m11 * pos.y + brot.m12 * pos.z; double tz = brot.m20 * pos.x + brot.m21 * pos.y + brot.m22 * pos.z; wx += tx; wy += ty; wz += tz; if (c != null) { wx += c[0]; wy += c[1]; wz += c[2]; } minPos[0] = Math.Min(minPos[0], wx); minPos[1] = Math.Min(minPos[1], wy); minPos[2] = Math.Min(minPos[2], wz); maxPos[0] = Math.Max(maxPos[0], wx); maxPos[1] = Math.Max(maxPos[1], wy); maxPos[2] = Math.Max(maxPos[2], wz); } ms.Position = mspos; BoundsDouble tbb = new BoundsDouble(); tbb.Encapsulate(new BoundsDouble(minPos, maxPos)); bb.Encapsulate(tbb); } } Animation a = tr.GetComponent <Animation>(); // Animator a = tr.GetComponent<Animator>(); if (a != null) { AnimationClip[] clips = AnimationUtility.GetAnimationClips(tr.gameObject); int nClips = clips.Length; // int nClips = a.GetClipCount(); for (int i = 0; i < nClips; i++) { GlTF_Animation anim = new GlTF_Animation(a.name); anim.Populate(clips[i]); GlTF_Writer.animations.Add(anim); } } // next, build hierarchy of nodes GlTF_Node node = new GlTF_Node(); Matrix4x4 rotMat = Matrix4x4.identity; if (root != null && rotCallback != null) { rotMat = rotCallback.GetNodeRotationMatrix(root); } if (tr == root) { Matrix4x4 mat = Matrix4x4.identity; mat.m22 = -1; // flip z axis if (rotMat != Matrix4x4.identity) { mat = rotMat; } // do not use global position if rtc is defined Vector3 pos = Vector3.zero; if (writer.RTCCenter == null) { pos = tr.localPosition; } mat = mat * Matrix4x4.TRS(pos, tr.localRotation, tr.localScale); 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); } } node.name = GlTF_Node.GetNameFromObject(tr); if (tr.GetComponent <Camera>() != null) { node.cameraName = tr.name; } else if (tr.GetComponent <Light>() != null) { node.lightName = tr.name; } else if (m != null) { node.meshNames.Add(GlTF_Mesh.GetNameFromObject(m)); } foreach (Transform t in tr.transform) { var found = false; foreach (var check in trs) { if (t == check) { found = true; break; } } if (found) { node.childrenNames.Add(GlTF_Node.GetNameFromObject(t)); } } GlTF_Writer.nodes.Add(node); } if (copyShaders && preset.shaderDir != null) { var sd = Path.Combine(Application.dataPath, preset.shaderDir); foreach (var shader in GlTF_Writer.shaders) { var srcPath = Path.Combine(sd, shader.uri); if (File.Exists(srcPath)) { var dstPath = Path.Combine(savedPath, shader.uri); File.Copy(srcPath, dstPath, true); } } } // third, add meshes etc to byte stream, keeping track of buffer offsets writer.Write(); writer.CloseFiles(); return(bb); }
void OnWizardCreate() // Create (Export) button has been hit (NOT wizard has been created!) { writer = new GlTF_Writer(); writer.Init (); /* Object[] deps = EditorUtility.CollectDependencies (trs); foreach (Object o in deps) { Debug.Log("obj "+o.name+" "+o.GetType()); } */ path = EditorUtility.SaveFilePanel("Save glTF file as", savedPath, savedFile, "gltf"); if (path.Length != 0) { Debug.Log ("attempting to save to "+path); writer.OpenFiles (path); // FOR NOW! GlTF_Sampler sampler = new GlTF_Sampler("sampler1"); // make the default one for now GlTF_Writer.samplers.Add (sampler); // first, collect objects in the scene, add to lists Transform[] trs = Selection.GetTransforms (SelectionMode.Deep); foreach (Transform tr in trs) { if (tr.camera != null) { if (tr.camera.isOrthoGraphic) { GlTF_Orthographic cam; cam = new GlTF_Orthographic(); cam.type = "orthographic"; cam.zfar = tr.camera.farClipPlane; cam.znear = tr.camera.nearClipPlane; cam.name = tr.name; //cam.orthographic.xmag = tr.camera. GlTF_Writer.cameras.Add(cam); } else { GlTF_Perspective cam; cam = new GlTF_Perspective(); cam.type = "perspective"; cam.zfar = tr.camera.farClipPlane; cam.znear = tr.camera.nearClipPlane; cam.aspect_ratio = tr.camera.aspect; cam.yfov = tr.camera.fieldOfView; cam.name = tr.name; GlTF_Writer.cameras.Add(cam); } } if (tr.light != null) { switch (tr.light.type) { case LightType.Point: GlTF_PointLight pl = new GlTF_PointLight(); pl.color = new GlTF_ColorRGB (tr.light.color); pl.name = tr.name; GlTF_Writer.lights.Add (pl); break; case LightType.Spot: GlTF_SpotLight sl = new GlTF_SpotLight(); sl.color = new GlTF_ColorRGB (tr.light.color); sl.name = tr.name; GlTF_Writer.lights.Add (sl); break; case LightType.Directional: GlTF_DirectionalLight dl = new GlTF_DirectionalLight(); dl.color = new GlTF_ColorRGB (tr.light.color); dl.name = tr.name; GlTF_Writer.lights.Add (dl); break; case LightType.Area: GlTF_AmbientLight al = new GlTF_AmbientLight(); al.color = new GlTF_ColorRGB (tr.light.color); al.name = tr.name; GlTF_Writer.lights.Add (al); break; } } MeshRenderer mr = tr.GetComponent<MeshRenderer>(); if (mr != null) { MeshFilter mf = tr.GetComponent<MeshFilter>(); Mesh m = mf.sharedMesh; GlTF_Accessor normalAccessor = new GlTF_Accessor("normalAccessor-" + tr.name + "_FIXTHIS", "VEC3", "FLOAT"); GlTF_Accessor positionAccessor = new GlTF_Accessor("positionAccessor-" + tr.name + "_FIXTHIS", "VEC3", "FLOAT"); GlTF_Accessor texCoord0Accessor = new GlTF_Accessor("texCoord0Accessor-" + tr.name + "_FIXTHIS", "VEC2", "FLOAT"); GlTF_Accessor indexAccessor = new GlTF_Accessor("indicesAccessor-" + tr.name + "_FIXTHIS", "SCALAR", "USHORT"); indexAccessor.bufferView = GlTF_Writer.ushortBufferView; normalAccessor.bufferView = GlTF_Writer.vec3BufferView; positionAccessor.bufferView = GlTF_Writer.vec3BufferView; texCoord0Accessor.bufferView = GlTF_Writer.vec2BufferView; GlTF_Mesh mesh = new GlTF_Mesh(); mesh.name = "mesh-" + tr.name; GlTF_Primitive primitive = new GlTF_Primitive(); primitive.name = "primitive-"+tr.name+"_FIXTHIS"; GlTF_Attributes attributes = new GlTF_Attributes(); attributes.normalAccessor = normalAccessor; attributes.positionAccessor = positionAccessor; attributes.texCoord0Accessor = texCoord0Accessor; primitive.attributes = attributes; primitive.indices = indexAccessor; mesh.primitives.Add (primitive); mesh.Populate (m); GlTF_Writer.accessors.Add (normalAccessor); GlTF_Writer.accessors.Add (positionAccessor); GlTF_Writer.accessors.Add (texCoord0Accessor); GlTF_Writer.accessors.Add (indexAccessor); GlTF_Writer.meshes.Add (mesh); // next, add material(s) to dictionary (when unique) string matName = mr.sharedMaterial.name; if (matName == "") matName = "material-diffault-diffuse"; else matName = "material-" + matName; primitive.materialName = matName; if (!GlTF_Writer.materials.ContainsKey (matName)) { GlTF_Material material = new GlTF_Material(); material.name = matName; if (mr.sharedMaterial.HasProperty ("shininess")) material.shininess = mr.sharedMaterial.GetFloat("shininess"); material.diffuse = new GlTF_MaterialColor ("diffuse", mr.sharedMaterial.color); //material.ambient = new GlTF_Color ("ambient", mr.material.color); if (mr.sharedMaterial.HasProperty ("specular")) { Color sc = mr.sharedMaterial.GetColor ("specular"); material.specular = new GlTF_MaterialColor ("specular", sc); } GlTF_Writer.materials.Add (material.name, material); // if there are textures, add them too if (mr.sharedMaterial.mainTexture != null) { if (!GlTF_Writer.textures.ContainsKey (mr.sharedMaterial.mainTexture.name)) { GlTF_Texture texture = new GlTF_Texture (); texture.name = mr.sharedMaterial.mainTexture.name; texture.source = AssetDatabase.GetAssetPath(mr.sharedMaterial.mainTexture); texture.samplerName = sampler.name; // FIX! For now! GlTF_Writer.textures.Add (mr.sharedMaterial.mainTexture.name, texture); material.diffuse = new GlTF_MaterialTexture ("diffuse", texture); } } } } Animation a = tr.animation; // Animator a = tr.GetComponent<Animator>(); if (a != null) { AnimationClip[] clips = AnimationUtility.GetAnimationClips(tr.gameObject); int nClips = clips.Length; // int nClips = a.GetClipCount(); for (int i = 0; i < nClips; i++) { GlTF_Animation anim = new GlTF_Animation(a.name); anim.Populate (clips[i]); GlTF_Writer.animations.Add (anim); } } // next, build hierarchy of nodes GlTF_Node node = new GlTF_Node(); if (tr.parent != null) 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); node.name = tr.name; if (tr.camera != null) { node.cameraName = tr.name; } else if (tr.light != null) node.lightName = tr.name; else if (mr != null) { node.meshNames.Add ("mesh-" + tr.name); } foreach (Transform t in tr.transform) node.childrenNames.Add ("node-" + t.name); GlTF_Writer.nodes.Add (node); } // third, add meshes etc to byte stream, keeping track of buffer offsets writer.Write (); writer.CloseFiles(); } }