diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Resources/NormalChannel.shader b/UnityGLTF/Assets/UnityGLTF/Runtime/Resources/NormalChannel.shader index e438325d3..3dcc580fc 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Resources/NormalChannel.shader +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Resources/NormalChannel.shader @@ -60,10 +60,10 @@ // unpacked.xyz = LinearToGammaSpace(unpacked.xyz); float4 result = float4(unpacked * 0.5f + 0.5f, 1); #ifdef UNITY_COLORSPACE_GAMMA - #else // hack for linear color space, need to figure out // right way to sample textures. result.xyz = GammaToLinearSpace(result.xyz); + #else #endif return result; } diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs index 583f35d7c..485d7d912 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/Extensions/SchemaExtensions.cs @@ -332,6 +332,14 @@ public static UnityEngine.Color ToUnityColorLinear(this GLTF.Math.Color color) return c; } + public static GLTF.Math.Color ToNumericsColor(this UnityEngine.Color color) + { + if (QualitySettings.activeColorSpace == ColorSpace.Linear) + return color.ToNumericsColorLinear(); + else + return color.ToNumericsColorRaw(); + } + public static GLTF.Math.Color ToNumericsColorRaw(this UnityEngine.Color color) { var c = color; @@ -436,8 +444,10 @@ public static UnityEngine.Vector2[] FlipTexCoordArrayVAndCopy(UnityEngine.Vector for (var i = 0; i < array.Length; i++) { - returnArray[i].x = array[i].x; - returnArray[i].y = 1.0f - array[i].y; + var x = array[i].x; + var y = 1.0f - array[i].y; + returnArray[i].x = float.IsNaN(x) ? 0 : x; + returnArray[i].y = float.IsNaN(y) ? 0 : y; } return returnArray; @@ -470,9 +480,9 @@ public static UnityEngine.Vector3[] ConvertVector3CoordinateSpaceAndCopy(Vector3 for (int i = 0; i < array.Length; i++) { - returnArray[i].x = array[i].x * coordinateSpaceCoordinateScale.X; - returnArray[i].y = array[i].y * coordinateSpaceCoordinateScale.Y; - returnArray[i].z = array[i].z * coordinateSpaceCoordinateScale.Z; + returnArray[i].x = (float.IsNaN(array[i].x) ? 0 : array[i].x) * coordinateSpaceCoordinateScale.X; + returnArray[i].y = (float.IsNaN(array[i].y) ? 0 : array[i].y) * coordinateSpaceCoordinateScale.Y; + returnArray[i].z = (float.IsNaN(array[i].z) ? 0 : array[i].z) * coordinateSpaceCoordinateScale.Z; } return returnArray; @@ -506,10 +516,10 @@ public static Vector4[] ConvertVector4CoordinateSpaceAndCopy(Vector4[] array, GL for (var i = 0; i < array.Length; i++) { - returnArray[i].x = array[i].x * coordinateSpaceCoordinateScale.X; - returnArray[i].y = array[i].y * coordinateSpaceCoordinateScale.Y; - returnArray[i].z = array[i].z * coordinateSpaceCoordinateScale.Z; - returnArray[i].w = array[i].w * coordinateSpaceCoordinateScale.W; + returnArray[i].x = (float.IsNaN(array[i].x) ? 0 : array[i].x) * coordinateSpaceCoordinateScale.X; + returnArray[i].y = (float.IsNaN(array[i].y) ? 0 : array[i].y) * coordinateSpaceCoordinateScale.Y; + returnArray[i].z = (float.IsNaN(array[i].z) ? 0 : array[i].z) * coordinateSpaceCoordinateScale.Z; + returnArray[i].w = (float.IsNaN(array[i].w) ? 0 : array[i].w) * coordinateSpaceCoordinateScale.W; } return returnArray; diff --git a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs index 4d2eb4546..1182c75fe 100644 --- a/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs +++ b/UnityGLTF/Assets/UnityGLTF/Runtime/Scripts/GLTFSceneExporter.cs @@ -82,6 +82,8 @@ private struct ImageInfo private List _animatedNodes; private List _skinnedNodes; private Dictionary _bakedMeshes; + private Dictionary _textureHashes = new Dictionary(); + private Dictionary _textureNames = new Dictionary(); private int _exportLayerMask; private ExportOptions _exportOptions; @@ -254,6 +256,8 @@ public GLTFSceneExporter(Transform[] rootTransforms, ExportOptions options) _imageInfos = new List(); _materials = new List(); _textures = new List(); + _textureHashes = new Dictionary(); + _textureNames = new Dictionary(); _buffer = new GLTFBuffer(); _bufferId = new BufferId @@ -648,15 +652,16 @@ private void ExportTexture(Texture2D texture, string outputPath) private string ConstructImageFilenamePath(Texture2D texture, string outputPath, string enforceExtension = null) { var imagePath = _exportOptions.TexturePathRetriever(texture); + var textureName = GetTextureName(texture); if (string.IsNullOrEmpty(imagePath)) { - imagePath = Path.Combine(outputPath, texture.name); + imagePath = Path.Combine(outputPath, textureName); } var filenamePath = Path.Combine(outputPath, imagePath); if (!ExportFullPath) { - filenamePath = outputPath + "/" + texture.name; + filenamePath = outputPath + "/" + textureName; } var file = new FileInfo(filenamePath); file.Directory.Create(); @@ -1119,6 +1124,8 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh) return null; } var materialsObj = renderer ? renderer.sharedMaterials : smr.sharedMaterials; + MaterialPropertyBlock materialProperty = new MaterialPropertyBlock(); + if (renderer) renderer.GetPropertyBlock(materialProperty); var prims = new MeshPrimitive[meshObj.subMeshCount]; List nonEmptyPrims = null; @@ -1133,22 +1140,22 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh) { AccessorId aPosition = null, aNormal = null, aTangent = null, aTexcoord0 = null, aTexcoord1 = null, aColor0 = null; - aPosition = ExportAccessor(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(meshObj.vertices, SchemaExtensions.CoordinateSpaceConversionScale)); + aPosition = ExportAccessorVec3(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(meshObj.vertices, SchemaExtensions.CoordinateSpaceConversionScale)); if (meshObj.normals.Length != 0) - aNormal = ExportAccessor(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(meshObj.normals, SchemaExtensions.CoordinateSpaceConversionScale)); + aNormal = ExportAccessorVec3(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(meshObj.normals, SchemaExtensions.CoordinateSpaceConversionScale)); if (meshObj.tangents.Length != 0) - aTangent = ExportAccessor(SchemaExtensions.ConvertVector4CoordinateSpaceAndCopy(meshObj.tangents, SchemaExtensions.TangentSpaceConversionScale)); + aTangent = ExportAccessorVec4(SchemaExtensions.ConvertVector4CoordinateSpaceAndCopy(meshObj.tangents, SchemaExtensions.TangentSpaceConversionScale)); if (meshObj.uv.Length != 0) - aTexcoord0 = ExportAccessor(SchemaExtensions.FlipTexCoordArrayVAndCopy(meshObj.uv)); + aTexcoord0 = ExportAccessorVec2(SchemaExtensions.FlipTexCoordArrayVAndCopy(meshObj.uv)); if (meshObj.uv2.Length != 0) - aTexcoord1 = ExportAccessor(SchemaExtensions.FlipTexCoordArrayVAndCopy(meshObj.uv2)); + aTexcoord1 = ExportAccessorVec2(SchemaExtensions.FlipTexCoordArrayVAndCopy(meshObj.uv2)); if (settings.ExportVertexColors && meshObj.colors.Length != 0) - aColor0 = ExportAccessor(QualitySettings.activeColorSpace == ColorSpace.Linear ? meshObj.colors : meshObj.colors.ToLinear()); + aColor0 = ExportAccessorColor(QualitySettings.activeColorSpace == ColorSpace.Linear ? meshObj.colors : meshObj.colors.ToLinear()); _meshToPrims.Add(meshObj, new MeshAccessors() { @@ -1203,9 +1210,12 @@ private MeshPrimitive[] ExportPrimitive(GameObject gameObject, GLTFMesh mesh) } var submeshPrimitive = accessors.subMeshPrimitives[submesh]; + var hasUVs = accessors.aTexcoord0 != null; + var materialId = ExportMaterial(materialsObj[submesh], materialProperty); + prims[submesh] = new MeshPrimitive(submeshPrimitive, _root) { - Material = ExportMaterial(materialsObj[submesh]), + Material = materialId, }; } @@ -1287,7 +1297,7 @@ private static bool EmptyPrimitive(MeshPrimitive prim) return false; } - private MaterialId ExportMaterial(Material materialObj) + private MaterialId ExportMaterial(Material materialObj, MaterialPropertyBlock materialPropertyBlock) { //TODO if material is null MaterialId id = GetMaterialId(_root, materialObj); @@ -1347,7 +1357,8 @@ private MaterialId ExportMaterial(Material materialObj) if (materialObj.HasProperty("_EmissionColor")) { var c = materialObj.GetColor("_EmissionColor"); - material.EmissiveFactor = c.ToNumericsColorLinear(); + if (c != Color.black && c != Color.clear) + material.EmissiveFactor = c.ToNumericsColor(); } if (materialObj.HasProperty("_EmissionMap")) @@ -1360,7 +1371,7 @@ private MaterialId ExportMaterial(Material materialObj) { material.EmissiveTexture = ExportTextureInfo(emissionTex, TextureMapType.Emission); - ExportTextureTransform(material.EmissiveTexture, materialObj, "_EmissionMap"); + ExportTextureTransform(material.EmissiveTexture, materialObj, materialPropertyBlock, "_EmissionMap"); } else { @@ -1379,7 +1390,7 @@ private MaterialId ExportMaterial(Material materialObj) if(normalTex is Texture2D) { material.NormalTexture = ExportNormalTextureInfo(normalTex, TextureMapType.Bump, materialObj); - ExportTextureTransform(material.NormalTexture, materialObj, "_BumpMap"); + ExportTextureTransform(material.NormalTexture, materialObj, materialPropertyBlock, "_BumpMap"); } else { @@ -1396,7 +1407,7 @@ private MaterialId ExportMaterial(Material materialObj) if(occTex is Texture2D) { material.OcclusionTexture = ExportOcclusionTextureInfo(occTex, TextureMapType.Occlusion, materialObj); - ExportTextureTransform(material.OcclusionTexture, materialObj, "_OcclusionMap"); + ExportTextureTransform(material.OcclusionTexture, materialObj, materialPropertyBlock, "_OcclusionMap"); } else { @@ -1406,39 +1417,41 @@ private MaterialId ExportMaterial(Material materialObj) } if( IsUnlit(materialObj)){ - ExportUnlit( material, materialObj ); + ExportUnlit( material, materialObj, materialPropertyBlock ); } else if (IsPBRMetallicRoughness(materialObj)) { - material.PbrMetallicRoughness = ExportPBRMetallicRoughness(materialObj); + material.PbrMetallicRoughness = ExportPBRMetallicRoughness(materialObj, materialPropertyBlock); } else if (IsPBRSpecularGlossiness(materialObj)) { - ExportPBRSpecularGlossiness(material, materialObj); + ExportPBRSpecularGlossiness(material, materialObj, materialPropertyBlock); } else if (IsCommonConstant(materialObj)) { - material.CommonConstant = ExportCommonConstant(materialObj); + material.CommonConstant = ExportCommonConstant(materialObj, materialPropertyBlock); } else if (materialObj.HasProperty("_BaseMap")) { var mainTex = materialObj.GetTexture("_BaseMap"); + var color = (materialObj.HasProperty("_BaseColor") + ? materialObj.GetColor("_BaseColor") + : Color.white).ToNumericsColor(); material.PbrMetallicRoughness = new PbrMetallicRoughness() { - BaseColorFactor = (materialObj.HasProperty("_BaseColor") - ? materialObj.GetColor("_BaseColor") - : Color.white).ToNumericsColorLinear(), + BaseColorFactor = color, BaseColorTexture = mainTex ? ExportTextureInfo(mainTex, TextureMapType.Main) : null }; } else if (materialObj.HasProperty("_ColorTexture")) { var mainTex = materialObj.GetTexture("_ColorTexture"); + var color = (materialObj.HasProperty("_BaseColor") + ? materialObj.GetColor("_BaseColor") + : Color.white).ToNumericsColor(); material.PbrMetallicRoughness = new PbrMetallicRoughness() { - BaseColorFactor = (materialObj.HasProperty("_BaseColor") - ? materialObj.GetColor("_BaseColor") - : Color.white).ToNumericsColorLinear(), + BaseColorFactor = color, BaseColorTexture = mainTex ? ExportTextureInfo(mainTex, TextureMapType.Main) : null }; } @@ -1450,14 +1463,14 @@ private MaterialId ExportMaterial(Material materialObj) { material.PbrMetallicRoughness = new PbrMetallicRoughness() { MetallicFactor = 0, RoughnessFactor = 1.0f }; material.PbrMetallicRoughness.BaseColorTexture = ExportTextureInfo(mainTex, TextureMapType.Main); - ExportTextureTransform(material.PbrMetallicRoughness.BaseColorTexture, materialObj, "_MainTex"); + ExportTextureTransform(material.PbrMetallicRoughness.BaseColorTexture, materialObj, materialPropertyBlock, "_MainTex"); } if (materialObj.HasProperty("_TintColor")) //particles use _TintColor instead of _Color { if (material.PbrMetallicRoughness == null) material.PbrMetallicRoughness = new PbrMetallicRoughness() { MetallicFactor = 0, RoughnessFactor = 1.0f }; - material.PbrMetallicRoughness.BaseColorFactor = materialObj.GetColor("_TintColor").ToNumericsColorLinear(); + material.PbrMetallicRoughness.BaseColorFactor = materialObj.GetColor("_TintColor").ToNumericsColor(); } material.DoubleSided = true; } @@ -1517,7 +1530,7 @@ private void ExportBlendShapes(SkinnedMeshRenderer smr, Mesh meshObj, int submes if (!settings.BlendShapeExportSparseAccessors) { - exportTargets.Add(SemanticProperties.POSITION, ExportAccessor(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(deltaVertices, SchemaExtensions.CoordinateSpaceConversionScale))); + exportTargets.Add(SemanticProperties.POSITION, ExportAccessorVec3(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(deltaVertices, SchemaExtensions.CoordinateSpaceConversionScale))); } else { @@ -1537,7 +1550,7 @@ private void ExportBlendShapes(SkinnedMeshRenderer smr, Mesh meshObj, int submes { if (!settings.BlendShapeExportSparseAccessors) { - exportTargets.Add(SemanticProperties.NORMAL, ExportAccessor(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(deltaNormals, SchemaExtensions.CoordinateSpaceConversionScale))); + exportTargets.Add(SemanticProperties.NORMAL, ExportAccessorVec3(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(deltaNormals, SchemaExtensions.CoordinateSpaceConversionScale))); } else { @@ -1549,13 +1562,13 @@ private void ExportBlendShapes(SkinnedMeshRenderer smr, Mesh meshObj, int submes { if (!settings.BlendShapeExportSparseAccessors) { - exportTargets.Add(SemanticProperties.TANGENT, ExportAccessor(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(deltaTangents, SchemaExtensions.CoordinateSpaceConversionScale))); + exportTargets.Add(SemanticProperties.TANGENT, ExportAccessorVec3(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(deltaTangents, SchemaExtensions.CoordinateSpaceConversionScale))); } else { // var baseAccessor = _meshToPrims[meshObj].aTangent; // exportTargets.Add(SemanticProperties.TANGENT, ExportSparseAccessor(baseAccessor, SchemaExtensions.ConvertVector4CoordinateSpaceAndCopy(meshObj.tangents, SchemaExtensions.TangentSpaceConversionScale), SchemaExtensions.ConvertVector4CoordinateSpaceAndCopy(deltaVertices, SchemaExtensions.TangentSpaceConversionScale))); - exportTargets.Add(SemanticProperties.TANGENT, ExportAccessor(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(deltaTangents, SchemaExtensions.CoordinateSpaceConversionScale))); + exportTargets.Add(SemanticProperties.TANGENT, ExportAccessorVec3(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(deltaTangents, SchemaExtensions.CoordinateSpaceConversionScale))); Debug.LogWarning("Blend Shape Tangents for " + meshObj + " won't be exported with sparse accessors – sparse accessor for tangents isn't supported right now."); } } @@ -1620,11 +1633,10 @@ private bool IsCommonConstant(Material material) material.HasProperty("_LightFactor"); } - private void ExportTextureTransform(TextureInfo def, Material mat, string texName) + private void ExportTextureTransform(TextureInfo def, Material mat, MaterialPropertyBlock materialPropertyBlock, string texName) { Vector2 offset = mat.GetTextureOffset(texName); Vector2 scale = mat.GetTextureScale(texName); - if (offset == Vector2.zero && scale == Vector2.one) { // difficult choice here: some shaders might support texture transform per-texture, others use the main transform. @@ -1634,6 +1646,24 @@ private void ExportTextureTransform(TextureInfo def, Material mat, string texNam // return; } + if (materialPropertyBlock.HasVector(texName + "_ST")) + { + var scaleAndOffset = materialPropertyBlock.GetVector(texName + "_ST"); // scale and tile + scale = new Vector2(scaleAndOffset.x, scaleAndOffset.y); + offset = new Vector2(scaleAndOffset.z, scaleAndOffset.w); + } + else if (materialPropertyBlock.HasVector("_MainTex_ST")) + { + // difficult choice here: some shaders might support texture transform per-texture, others use the main transform. + var scaleAndOffset = materialPropertyBlock.GetVector("_MainTex_ST"); // scale and tile + scale = new Vector2(scaleAndOffset.x, scaleAndOffset.y); + offset = new Vector2(scaleAndOffset.z, scaleAndOffset.w); + } + + // uv coordinate system seems to be flipped in unity + if (scale != Vector2.one) + offset.y = -(offset.y + scale.y); + if (_root.ExtensionsUsed == null) { _root.ExtensionsUsed = new List( @@ -1663,7 +1693,7 @@ private void ExportTextureTransform(TextureInfo def, Material mat, string texNam def.Extensions = new Dictionary(); def.Extensions[ExtTextureTransformExtensionFactory.EXTENSION_NAME] = new ExtTextureTransformExtension( - new GLTF.Math.Vector2(offset.x, -offset.y), + new GLTF.Math.Vector2(offset.x, offset.y), 0, // TODO: support rotation new GLTF.Math.Vector2(scale.x, scale.y), 0 // TODO: support UV channels @@ -1704,17 +1734,17 @@ private OcclusionTextureInfo ExportOcclusionTextureInfo( return info; } - private PbrMetallicRoughness ExportPBRMetallicRoughness(Material material) + private PbrMetallicRoughness ExportPBRMetallicRoughness(Material material, MaterialPropertyBlock materialPropertyBlock) { var pbr = new PbrMetallicRoughness() { MetallicFactor = 0, RoughnessFactor = 1.0f }; if (material.HasProperty("_BaseColor")) { - pbr.BaseColorFactor = material.GetColor("_BaseColor").ToNumericsColorLinear(); + pbr.BaseColorFactor = material.GetColor("_BaseColor").ToNumericsColor(); } else if (material.HasProperty("_Color")) { - pbr.BaseColorFactor = material.GetColor("_Color").ToNumericsColorLinear(); + pbr.BaseColorFactor = material.GetColor("_Color").ToNumericsColor(); } if (material.HasProperty("_TintColor")) //particles use _TintColor instead of _Color @@ -1726,7 +1756,7 @@ private PbrMetallicRoughness ExportPBRMetallicRoughness(Material material) white = (c.r + c.g + c.b) / 3.0f; //multiply alpha by overall whiteness of TintColor } - pbr.BaseColorFactor = (material.GetColor("_TintColor") * white).ToNumericsColorLinear() ; + pbr.BaseColorFactor = (material.GetColor("_TintColor") * white).ToNumericsColor() ; } if (material.HasProperty("_MainTex") || material.HasProperty("_BaseMap")) //TODO if additive particle, render black into alpha @@ -1740,7 +1770,7 @@ private PbrMetallicRoughness ExportPBRMetallicRoughness(Material material) if(mainTex is Texture2D) { pbr.BaseColorTexture = ExportTextureInfo(mainTex, TextureMapType.Main); - ExportTextureTransform(pbr.BaseColorTexture, material, mainTexPropertyName); + ExportTextureTransform(pbr.BaseColorTexture, material, materialPropertyBlock, mainTexPropertyName); } else { @@ -1762,7 +1792,9 @@ private PbrMetallicRoughness ExportPBRMetallicRoughness(Material material) // legacy workaround: the UnityGLTF shaders misuse "_Glossiness" as roughness but don't have a keyword for it. if(material.shader.name.Equals("GLTF/PbrMetallicRoughness", StringComparison.Ordinal)) smoothness = 1 - smoothness; - pbr.RoughnessFactor = (metallicGlossMap && material.HasProperty("_GlossMapScale")) ? material.GetFloat("_GlossMapScale") : 1.0 - smoothness; + var roughnessFactor = (metallicGlossMap && material.HasProperty("_GlossMapScale")) ? material.GetFloat("_GlossMapScale") : 1.0f - smoothness; + roughnessFactor = Mathf.Clamp01(roughnessFactor); + pbr.RoughnessFactor = roughnessFactor; } if (material.HasProperty("_MetallicGlossMap")) @@ -1776,7 +1808,7 @@ private PbrMetallicRoughness ExportPBRMetallicRoughness(Material material) pbr.MetallicRoughnessTexture = ExportTextureInfo(mrTex, TextureMapType.MetallicGloss); if (material.IsKeywordEnabled("_METALLICGLOSSMAP")) pbr.MetallicFactor = 1.0f; - ExportTextureTransform(pbr.MetallicRoughnessTexture, material, "_MetallicGlossMap"); + ExportTextureTransform(pbr.MetallicRoughnessTexture, material, materialPropertyBlock, "_MetallicGlossMap"); } else { @@ -1788,8 +1820,8 @@ private PbrMetallicRoughness ExportPBRMetallicRoughness(Material material) return pbr; } - private void ExportUnlit(GLTFMaterial def, Material material){ - + private void ExportUnlit(GLTFMaterial def, Material material, MaterialPropertyBlock materialPropertyBlock) + { const string extname = KHR_MaterialsUnlitExtensionFactory.EXTENSION_NAME; DeclareExtensionUsage( extname, true ); def.AddExtension( extname, new KHR_MaterialsUnlitExtension()); @@ -1798,7 +1830,7 @@ private void ExportUnlit(GLTFMaterial def, Material material){ if (material.HasProperty("_Color")) { - pbr.BaseColorFactor = material.GetColor("_Color").ToNumericsColorLinear(); + pbr.BaseColorFactor = material.GetColor("_Color").ToNumericsColor(); } if (material.HasProperty("_MainTex")) @@ -1809,7 +1841,7 @@ private void ExportUnlit(GLTFMaterial def, Material material){ if(mainTex is Texture2D) { pbr.BaseColorTexture = ExportTextureInfo(mainTex, TextureMapType.Main); - ExportTextureTransform(pbr.BaseColorTexture, material, "_MainTex"); + ExportTextureTransform(pbr.BaseColorTexture, material, materialPropertyBlock, "_MainTex"); } else { @@ -1823,7 +1855,7 @@ private void ExportUnlit(GLTFMaterial def, Material material){ } - private void ExportPBRSpecularGlossiness(GLTFMaterial material, Material materialObj) + private void ExportPBRSpecularGlossiness(GLTFMaterial material, Material materialObj, MaterialPropertyBlock materialPropertyBlock) { if (_root.ExtensionsUsed == null) { @@ -1859,7 +1891,7 @@ private void ExportPBRSpecularGlossiness(GLTFMaterial material, Material materia if (materialObj.HasProperty("_Color")) { - diffuseFactor = materialObj.GetColor("_Color").ToNumericsColorLinear(); + diffuseFactor = materialObj.GetColor("_Color").ToNumericsColor(); } if (materialObj.HasProperty("_MainTex")) @@ -1871,7 +1903,7 @@ private void ExportPBRSpecularGlossiness(GLTFMaterial material, Material materia if (mainTex is Texture2D) { diffuseTexture = ExportTextureInfo(mainTex, TextureMapType.Main); - ExportTextureTransform(diffuseTexture, materialObj, "_MainTex"); + ExportTextureTransform(diffuseTexture, materialObj, materialPropertyBlock, "_MainTex"); } else { @@ -1885,7 +1917,7 @@ private void ExportPBRSpecularGlossiness(GLTFMaterial material, Material materia var specGlossMap = materialObj.GetTexture("_SpecGlossMap"); if (specGlossMap == null) { - var specColor = materialObj.GetColor("_SpecColor").ToNumericsColorLinear(); + var specColor = materialObj.GetColor("_SpecColor").ToNumericsColor(); specularFactor = new GLTF.Math.Vector3(specColor.R, specColor.G, specColor.B); } } @@ -1908,7 +1940,7 @@ private void ExportPBRSpecularGlossiness(GLTFMaterial material, Material materia if (mgTex is Texture2D) { specularGlossinessTexture = ExportTextureInfo(mgTex, TextureMapType.SpecGloss); - ExportTextureTransform(specularGlossinessTexture, materialObj, "_SpecGlossMap"); + ExportTextureTransform(specularGlossinessTexture, materialObj, materialPropertyBlock, "_SpecGlossMap"); } else { @@ -1926,7 +1958,7 @@ private void ExportPBRSpecularGlossiness(GLTFMaterial material, Material materia ); } - private MaterialCommonConstant ExportCommonConstant(Material materialObj) + private MaterialCommonConstant ExportCommonConstant(Material materialObj, MaterialPropertyBlock materialPropertyBlock) { if (_root.ExtensionsUsed == null) { @@ -1953,7 +1985,7 @@ private MaterialCommonConstant ExportCommonConstant(Material materialObj) if (materialObj.HasProperty("_AmbientFactor")) { - constant.AmbientFactor = materialObj.GetColor("_AmbientFactor").ToNumericsColorRaw(); + constant.AmbientFactor = materialObj.GetColor("_AmbientFactor").ToNumericsColor(); } if (materialObj.HasProperty("_LightMap")) @@ -1963,14 +1995,14 @@ private MaterialCommonConstant ExportCommonConstant(Material materialObj) if (lmTex != null) { constant.LightmapTexture = ExportTextureInfo(lmTex, TextureMapType.Light); - ExportTextureTransform(constant.LightmapTexture, materialObj, "_LightMap"); + ExportTextureTransform(constant.LightmapTexture, materialObj, materialPropertyBlock, "_LightMap"); } } if (materialObj.HasProperty("_LightFactor")) { - constant.LightmapFactor = materialObj.GetColor("_LightFactor").ToNumericsColorRaw(); + constant.LightmapFactor = materialObj.GetColor("_LightFactor").ToNumericsColor(); } return constant; @@ -2001,6 +2033,7 @@ private TextureId ExportTexture(Texture textureObj, TextureMapType textureMapTyp textureObj.name = (_root.Textures.Count + 1).ToString(); } + SetUniqueTextureName(textureObj); if (ExportNames) { texture.Name = textureObj.name; @@ -2032,9 +2065,10 @@ private TextureId ExportTexture(Texture textureObj, TextureMapType textureMapTyp private string GetImageOutputPath(Texture texture) { var imagePath = _exportOptions.TexturePathRetriever(texture); + var textureName = GetTextureName(texture); if (string.IsNullOrEmpty(imagePath)) { - imagePath = texture.name; + imagePath = textureName; } var filenamePath = imagePath; @@ -2052,7 +2086,7 @@ private string GetImageOutputPath(Texture texture) filenamePath = Path.GetFileName(filenamePath); if (!isGltfCompatible) { - filenamePath = Path.ChangeExtension(texture.name, ".png"); + filenamePath = Path.ChangeExtension(textureName, ".png"); } } @@ -2068,7 +2102,6 @@ private ImageId ExportImage(Texture texture, TextureMapType textureMapType) } var image = new GLTFImage(); - if (ExportNames) { image.Name = texture.name; @@ -2077,23 +2110,23 @@ private ImageId ExportImage(Texture texture, TextureMapType textureMapType) if (texture.GetType() == typeof(RenderTexture)) { Texture2D tempTexture = new Texture2D(texture.width, texture.height); - tempTexture.name = texture.name; RenderTexture.active = texture as RenderTexture; tempTexture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0); tempTexture.Apply(); + tempTexture.name = texture.name; texture = tempTexture; } #if UNITY_2017_1_OR_NEWER if (texture.GetType() == typeof(CustomRenderTexture)) { Texture2D tempTexture = new Texture2D(texture.width, texture.height); - tempTexture.name = texture.name; RenderTexture.active = texture as CustomRenderTexture; tempTexture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0); tempTexture.Apply(); - texture = tempTexture; + tempTexture.name = texture.name; + texture = tempTexture; } #endif @@ -2118,6 +2151,57 @@ private ImageId ExportImage(Texture texture, TextureMapType textureMapType) return id; } + private string GetTextureName(Texture texture) + { + var name = texture.name; + name = name.Replace('.', '_'); + return name; + } + + private void SetUniqueTextureName(Texture texture) + { + var name = texture.name; + var textureHash = texture.GetInstanceID(); + if (texture is Texture2D tex2D) + textureHash = CreateHashValue(tex2D); + name = name.Replace('.', '_'); + var uniqueName = name; + var number = 0; + var isUniqueTexture = !_textureHashes.ContainsKey(textureHash); + while (isUniqueTexture && _textureHashes.ContainsValue(uniqueName) && number++ < 1000) + uniqueName = name + number.ToString(); + + if (isUniqueTexture) + _textureHashes.Add(textureHash, uniqueName); + else + uniqueName = _textureHashes[textureHash]; + texture.name = uniqueName; + } + + /// + /// Creates a hashcode from a number of samples of a texture + /// + /// + /// + private int CreateHashValue(Texture2D tex) + { + var hash = CombineHashCodes(tex.width, tex.height); + var pixels = tex.GetPixels32(); + var numberOfSamples = Mathf.Min(59, pixels.Length); + var pixelStep = pixels.Length / numberOfSamples; + for (int i = 0; i < numberOfSamples; i++) + { + var c = pixels[pixelStep * i]; + hash += CombineHashCodes(c.r + c.g, c.b + c.a); + } + return hash; + } + + internal static int CombineHashCodes(int h1, int h2) + { + return ((h1 << 5) + h1) ^ h2; + } + bool TryGetTextureDataFromDisk(TextureMapType textureMapType, Texture texture, out string path, out byte[] data) { path = null; @@ -2369,22 +2453,8 @@ private AccessorId ExportAccessor(int[] arr, bool isIndices = false) accessor.Count = count; accessor.Type = GLTFAccessorAttributeType.SCALAR; - int min = arr[0]; - int max = arr[0]; - - for (var i = 1; i < count; i++) - { - var cur = arr[i]; - - if (cur < min) - { - min = cur; - } - if (cur > max) - { - max = cur; - } - } + int min = arr.Min(v => v); + int max = arr.Max(v => v); AlignToBoundary(_bufferWriter.BaseStream, 0x00); uint byteOffset = CalculateAlignment((uint)_bufferWriter.BaseStream.Position, 4); @@ -2467,7 +2537,7 @@ private AccessorId ExportAccessor(int[] arr, bool isIndices = false) return id; } - private AccessorId ExportAccessor(Vector2[] arr) + private AccessorId ExportAccessorVec2(Vector2[] arr) { uint count = (uint)arr.Length; @@ -2481,32 +2551,10 @@ private AccessorId ExportAccessor(Vector2[] arr) accessor.Count = count; accessor.Type = GLTFAccessorAttributeType.VEC2; - float minX = arr[0].x; - float minY = arr[0].y; - float maxX = arr[0].x; - float maxY = arr[0].y; - - for (var i = 1; i < count; i++) - { - var cur = arr[i]; - - if (cur.x < minX) - { - minX = cur.x; - } - if (cur.y < minY) - { - minY = cur.y; - } - if (cur.x > maxX) - { - maxX = cur.x; - } - if (cur.y > maxY) - { - maxY = cur.y; - } - } + float minX = arr.Min(v => v.x); + float minY = arr.Min(v => v.y); + float maxX = arr.Max(v => v.x); + float maxY = arr.Max(v => v.y); accessor.Min = new List { minX, minY }; accessor.Max = new List { maxX, maxY }; @@ -2534,7 +2582,7 @@ private AccessorId ExportAccessor(Vector2[] arr) return id; } - private AccessorId ExportAccessor(Vector3[] arr) + private AccessorId ExportAccessorVec3(Vector3[] arr) { uint count = (uint)arr.Length; @@ -2548,42 +2596,12 @@ private AccessorId ExportAccessor(Vector3[] arr) accessor.Count = count; accessor.Type = GLTFAccessorAttributeType.VEC3; - float minX = arr[0].x; - float minY = arr[0].y; - float minZ = arr[0].z; - float maxX = arr[0].x; - float maxY = arr[0].y; - float maxZ = arr[0].z; - - for (var i = 1; i < count; i++) - { - var cur = arr[i]; - - if (cur.x < minX) - { - minX = cur.x; - } - if (cur.y < minY) - { - minY = cur.y; - } - if (cur.z < minZ) - { - minZ = cur.z; - } - if (cur.x > maxX) - { - maxX = cur.x; - } - if (cur.y > maxY) - { - maxY = cur.y; - } - if (cur.z > maxZ) - { - maxZ = cur.z; - } - } + float minX = arr.Min(v => v.x); + float minY = arr.Min(v => v.y); + float minZ = arr.Min(v => v.z); + float maxX = arr.Max(v => v.x); + float maxY = arr.Max(v => v.y); + float maxZ = arr.Max(v => v.z); accessor.Min = new List { minX, minY, minZ }; accessor.Max = new List { maxX, maxY, maxZ }; @@ -2673,43 +2691,14 @@ private AccessorId ExportSparseAccessor(AccessorId baseAccessor, Vector3[] baseD // we need to calculate the min/max of the entire new data array uint count = (uint) arr.Length; - var firstElement = baseData != null ? (baseData[0] + arr[0]) : arr[0]; - float minX = firstElement.x; - float minY = firstElement.y; - float minZ = firstElement.z; - float maxX = firstElement.x; - float maxY = firstElement.y; - float maxZ = firstElement.z; + var arrZippedWithBase = baseData != null ? arr.Zip(baseData, (v, b) => b + v) : arr; - for (var i = 1; i < count; i++) - { - var cur = baseData != null ? (baseData[i] + arr[i]) : arr[i]; - - if (cur.x < minX) - { - minX = cur.x; - } - if (cur.y < minY) - { - minY = cur.y; - } - if (cur.z < minZ) - { - minZ = cur.z; - } - if (cur.x > maxX) - { - maxX = cur.x; - } - if (cur.y > maxY) - { - maxY = cur.y; - } - if (cur.z > maxZ) - { - maxZ = cur.z; - } - } + float minX = arrZippedWithBase.Min(v => v.x); + float minY = arrZippedWithBase.Min(v => v.y); + float minZ = arrZippedWithBase.Min(v => v.z); + float maxX = arrZippedWithBase.Max(v => v.x); + float maxY = arrZippedWithBase.Max(v => v.y); + float maxZ = arrZippedWithBase.Max(v => v.z); accessor.Min = new List { minX, minY, minZ }; accessor.Max = new List { maxX, maxY, maxZ }; @@ -2761,7 +2750,7 @@ private AccessorId ExportSparseAccessor(AccessorId baseAccessor, Vector3[] baseD return id; } - private AccessorId ExportAccessor(Vector4[] arr) + private AccessorId ExportAccessorVec4(Vector4[] arr) { uint count = (uint)arr.Length; @@ -2775,52 +2764,14 @@ private AccessorId ExportAccessor(Vector4[] arr) accessor.Count = count; accessor.Type = GLTFAccessorAttributeType.VEC4; - float minX = arr[0].x; - float minY = arr[0].y; - float minZ = arr[0].z; - float minW = arr[0].w; - float maxX = arr[0].x; - float maxY = arr[0].y; - float maxZ = arr[0].z; - float maxW = arr[0].w; - - for (var i = 1; i < count; i++) - { - var cur = arr[i]; - - if (cur.x < minX) - { - minX = cur.x; - } - if (cur.y < minY) - { - minY = cur.y; - } - if (cur.z < minZ) - { - minZ = cur.z; - } - if (cur.w < minW) - { - minW = cur.w; - } - if (cur.x > maxX) - { - maxX = cur.x; - } - if (cur.y > maxY) - { - maxY = cur.y; - } - if (cur.z > maxZ) - { - maxZ = cur.z; - } - if (cur.w > maxW) - { - maxW = cur.w; - } - } + float minX = arr.Min(v => v.x); + float minY = arr.Min(v => v.y); + float minZ = arr.Min(v => v.z); + float minW = arr.Min(v => v.w); + float maxX = arr.Max(v => v.x); + float maxY = arr.Max(v => v.y); + float maxZ = arr.Max(v => v.z); + float maxW = arr.Max(v => v.w); accessor.Min = new List { minX, minY, minZ, minW }; accessor.Max = new List { maxX, maxY, maxZ, maxW }; @@ -2850,7 +2801,7 @@ private AccessorId ExportAccessor(Vector4[] arr) return id; } - private AccessorId ExportAccessor(Color[] arr) + private AccessorId ExportAccessorColor(Color[] arr) { uint count = (uint)arr.Length; @@ -2864,52 +2815,15 @@ private AccessorId ExportAccessor(Color[] arr) accessor.Count = count; accessor.Type = GLTFAccessorAttributeType.VEC4; - float minR = arr[0].r; - float minG = arr[0].g; - float minB = arr[0].b; - float minA = arr[0].a; - float maxR = arr[0].r; - float maxG = arr[0].g; - float maxB = arr[0].b; - float maxA = arr[0].a; - - for (var i = 1; i < count; i++) - { - var cur = arr[i]; - if (cur.r < minR) - { - minR = cur.r; - } - if (cur.g < minG) - { - minG = cur.g; - } - if (cur.b < minB) - { - minB = cur.b; - } - if (cur.a < minA) - { - minA = cur.a; - } - if (cur.r > maxR) - { - maxR = cur.r; - } - if (cur.g > maxG) - { - maxG = cur.g; - } - if (cur.b > maxB) - { - maxB = cur.b; - } - if (cur.a > maxA) - { - maxA = cur.a; - } - } + float minR = arr.Min(c => c.r); + float minG = arr.Min(c => c.g); + float minB = arr.Min(c => c.b); + float minA = arr.Min(c => c.a); + float maxR = arr.Max(c => c.r); + float maxG = arr.Max(c => c.g); + float maxB = arr.Max(c => c.b); + float maxA = arr.Max(c => c.a); accessor.Min = new List { minR, minG, minB, minA }; accessor.Max = new List { maxR, maxG, maxB, maxA }; @@ -3247,7 +3161,7 @@ private void ConvertClipToGLTFAnimation(ref AnimationClip clip, ref Transform tr if(haveAnimation) { - AccessorId timeAccessor = ExportAccessor(times); + AccessorId timeAccessor = ExportAccessorFloat(times); // Translation if(positions != null) @@ -3265,7 +3179,7 @@ private void ConvertClipToGLTFAnimation(ref AnimationClip clip, ref Transform tr AnimationSampler Tsampler = new AnimationSampler(); Tsampler.Input = timeAccessor; - Tsampler.Output = ExportAccessor(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(positions, SchemaExtensions.CoordinateSpaceConversionScale)); + Tsampler.Output = ExportAccessorVec3(SchemaExtensions.ConvertVector3CoordinateSpaceAndCopy(positions, SchemaExtensions.CoordinateSpaceConversionScale)); Tchannel.Sampler = new AnimationSamplerId { Id = animation.Samplers.Count, @@ -3293,7 +3207,7 @@ private void ConvertClipToGLTFAnimation(ref AnimationClip clip, ref Transform tr AnimationSampler Rsampler = new AnimationSampler(); Rsampler.Input = timeAccessor; // Float, for time - Rsampler.Output = ExportAccessor(rotations, true); // Vec4 for + Rsampler.Output = ExportAccessorVec4(rotations, true); // Vec4 for Rchannel.Sampler = new AnimationSamplerId { Id = animation.Samplers.Count, @@ -3321,7 +3235,7 @@ private void ConvertClipToGLTFAnimation(ref AnimationClip clip, ref Transform tr AnimationSampler Ssampler = new AnimationSampler(); Ssampler.Input = timeAccessor; // Float, for time - Ssampler.Output = ExportAccessor(scales); // Vec3 for scale + Ssampler.Output = ExportAccessorVec3(scales); // Vec3 for scale Schannel.Sampler = new AnimationSamplerId { Id = animation.Samplers.Count, @@ -3348,7 +3262,7 @@ private void ConvertClipToGLTFAnimation(ref AnimationClip clip, ref Transform tr AnimationSampler Wsampler = new AnimationSampler(); Wsampler.Input = timeAccessor; - Wsampler.Output = ExportAccessor(weights); + Wsampler.Output = ExportAccessorFloat(weights); Wchannel.Sampler = new AnimationSamplerId() { Id = animation.Samplers.Count, @@ -3764,7 +3678,7 @@ private void ExportSkinFromNode(Transform transform) }); } - gltfSkin.InverseBindMatrices = ExportAccessor(mesh.bindposes); + gltfSkin.InverseBindMatrices = ExportAccessorMatrix(mesh.bindposes); Vector4[] bones = boneWeightToBoneVec4(mesh.boneWeights); Vector4[] weights = boneWeightToWeightVec4(mesh.boneWeights); @@ -3775,7 +3689,7 @@ private void ExportSkinFromNode(Transform transform) if (!prim.Attributes.ContainsKey("JOINTS_0")) prim.Attributes.Add("JOINTS_0", ExportAccessorUint(bones)); if (!prim.Attributes.ContainsKey("WEIGHTS_0")) - prim.Attributes.Add("WEIGHTS_0", ExportAccessor(weights)); + prim.Attributes.Add("WEIGHTS_0", ExportAccessorVec4(weights)); } _root.Nodes[_exportedTransforms[transform.GetInstanceID()]].Skin = new SkinId() { Id = _root.Skins.Count, Root = _root }; @@ -3818,52 +3732,14 @@ private AccessorId ExportAccessorUint(Vector4[] arr) accessor.Count = count; accessor.Type = GLTFAccessorAttributeType.VEC4; - float minX = arr[0].x; - float minY = arr[0].y; - float minZ = arr[0].z; - float minW = arr[0].w; - float maxX = arr[0].x; - float maxY = arr[0].y; - float maxZ = arr[0].z; - float maxW = arr[0].w; - - for (var i = 1; i < count; i++) - { - var cur = arr[i]; - - if (cur.x < minX) - { - minX = cur.x; - } - if (cur.y < minY) - { - minY = cur.y; - } - if (cur.z < minZ) - { - minZ = cur.z; - } - if (cur.w < minW) - { - minW = cur.w; - } - if (cur.x > maxX) - { - maxX = cur.x; - } - if (cur.y > maxY) - { - maxY = cur.y; - } - if (cur.z > maxZ) - { - maxZ = cur.z; - } - if (cur.w > maxW) - { - maxW = cur.w; - } - } + float minX = arr.Min(v => v.x); + float minY = arr.Min(v => v.y); + float minZ = arr.Min(v => v.z); + float minW = arr.Min(v => v.w); + float maxX = arr.Max(v => v.x); + float maxY = arr.Max(v => v.y); + float maxZ = arr.Max(v => v.z); + float maxW = arr.Max(v => v.w); accessor.Min = new List { minX, minY, minZ, minW }; accessor.Max = new List { maxX, maxY, maxZ, maxW }; @@ -3894,7 +3770,7 @@ private AccessorId ExportAccessorUint(Vector4[] arr) } // This is used for Quaternions / Rotations - private AccessorId ExportAccessor(Vector4[] arr, bool switchHandedness = false) + private AccessorId ExportAccessorVec4(Vector4[] arr, bool switchHandedness = false) { var count = (uint)arr.Length; @@ -3908,57 +3784,17 @@ private AccessorId ExportAccessor(Vector4[] arr, bool switchHandedness = false) accessor.Count = count; accessor.Type = GLTFAccessorAttributeType.VEC4; - var a0 = arr[0]; - a0 = switchHandedness ? a0.switchHandedness() : a0; - a0 = a0.normalized; - float minX = a0.x; - float minY = a0.y; - float minZ = a0.z; - float minW = a0.w; - float maxX = a0.x; - float maxY = a0.y; - float maxZ = a0.z; - float maxW = a0.w; - - for (var i = 1; i < count; i++) - { - var cur = arr[i]; - cur = switchHandedness ? cur.switchHandedness() : cur; - cur = cur.normalized; - - if (cur.x < minX) - { - minX = cur.x; - } - if (cur.y < minY) - { - minY = cur.y; - } - if (cur.z < minZ) - { - minZ = cur.z; - } - if (cur.w < minW) - { - minW = cur.w; - } - if (cur.x > maxX) - { - maxX = cur.x; - } - if (cur.y > maxY) - { - maxY = cur.y; - } - if (cur.z > maxZ) - { - maxZ = cur.z; - } - if (cur.w > maxW) - { - maxW = cur.w; - } - } + var arrSwitched = switchHandedness ? arr.Select(v => v.switchHandedness()) : arr; + arrSwitched = arrSwitched.Select(x => x.normalized); + + float minX = arrSwitched.Min(v => v.x); + float minY = arrSwitched.Min(v => v.y); + float minZ = arrSwitched.Min(v => v.z); + float minW = arrSwitched.Min(v => v.w); + float maxX = arrSwitched.Max(v => v.x); + float maxY = arrSwitched.Max(v => v.y); + float maxZ = arrSwitched.Max(v => v.z); + float maxW = arrSwitched.Max(v => v.w); accessor.Min = new List { minX, minY, minZ, minW }; accessor.Max = new List { maxX, maxY, maxZ, maxW }; @@ -3966,14 +3802,12 @@ private AccessorId ExportAccessor(Vector4[] arr, bool switchHandedness = false) AlignToBoundary(_bufferWriter.BaseStream, 0x00); uint byteOffset = CalculateAlignment((uint)_bufferWriter.BaseStream.Position, 4); - foreach (var vec in arr) + foreach (var vec in arrSwitched) { - Vector4 vect = switchHandedness ? vec.switchHandedness() : vec; - vect = vect.normalized; - _bufferWriter.Write(vect.x); - _bufferWriter.Write(vect.y); - _bufferWriter.Write(vect.z); - _bufferWriter.Write(vect.w); + _bufferWriter.Write(vec.x); + _bufferWriter.Write(vec.y); + _bufferWriter.Write(vec.z); + _bufferWriter.Write(vec.w); } uint byteLength = CalculateAlignment((uint)_bufferWriter.BaseStream.Position - byteOffset, 4); @@ -3990,7 +3824,7 @@ private AccessorId ExportAccessor(Vector4[] arr, bool switchHandedness = false) return id; } - private AccessorId ExportAccessor(float[] arr) + private AccessorId ExportAccessorFloat(float[] arr) { var count = (uint)arr.Length; @@ -4004,22 +3838,8 @@ private AccessorId ExportAccessor(float[] arr) accessor.Count = count; accessor.Type = GLTFAccessorAttributeType.SCALAR; - float min = arr[0]; - float max = arr[0]; - - for (var i = 1; i < count; i++) - { - var cur = arr[i]; - - if (cur < min) - { - min = cur; - } - if (cur > max) - { - max = cur; - } - } + float min = arr.Min(v => v); + float max = arr.Max(v => v); accessor.Min = new List { min }; accessor.Max = new List { max }; @@ -4047,7 +3867,7 @@ private AccessorId ExportAccessor(float[] arr) return id; } - private AccessorId ExportAccessor(Matrix4x4[] arr) + private AccessorId ExportAccessorMatrix(Matrix4x4[] arr) { var count = (uint)arr.Length;