IanToujou_Gateway-Edge/Assets/GLTFUtility/Plugins/draco/GLTFUtilityDracoLoader.cs
// Copyright 2017 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;

public unsafe class GLTFUtilityDracoLoader {
	// These values must be exactly the same as the values in draco_types.h.
	// Attribute data type.
	enum DataType {
		DT_INVALID = 0,
		DT_INT8,
		DT_UINT8,
		DT_INT16,
		DT_UINT16,
		DT_INT32,
		DT_UINT32,
		DT_INT64,
		DT_UINT64,
		DT_FLOAT32,
		DT_FLOAT64,
		DT_BOOL
	}

	// These values must be exactly the same as the values in
	// geometry_attribute.h.
	// Attribute type.
	enum AttributeType {
		INVALID = -1,
		POSITION = 0,
		NORMAL = 1,
		COLOR = 2,
		TEX_COORD = 3,
		GENERIC = 4
	}

	// The order must be consistent with C++ interface.
	[StructLayout(LayoutKind.Sequential)] public struct DracoData {
		public int dataType;
		public IntPtr data;
	}

	[StructLayout(LayoutKind.Sequential)] public struct DracoAttribute {
		public int attributeType;
		public int dataType;
		public int numComponents;
		public int uniqueId;
	}

	[StructLayout(LayoutKind.Sequential)] public struct DracoMesh {
		public int numFaces;
		public int numVertices;
		public int numAttributes;
	}

	public struct MeshAttributes {
		public int pos, norms, uv, joints, weights, col;

		public MeshAttributes(int pos, int norms, int uv, int joints, int weights, int col) {
			this.pos = pos;
			this.norms = norms;
			this.uv = uv;
			this.joints = joints;
			this.weights = weights;
			this.col = col;
		}
	}

	[StructLayout(LayoutKind.Sequential)] public struct Vector4<T> where T : struct {
		public T x;
		public T y;
		public T z;
		public T w;
	}

	public class AsyncMesh {
		public int[] tris;
		public Vector3[] verts;
		public Vector2[] uv;
		public Vector3[] norms;
		public BoneWeight[] boneWeights;
		public Color[] colors;
	}

#if !UNITY_EDITOR && (UNITY_WEBGL || UNITY_IOS)
        const string DRACODEC_UNITY_LIB = "__Internal";
#elif UNITY_ANDROID || UNITY_STANDALONE || UNITY_WSA || UNITY_EDITOR || PLATFORM_LUMIN
	const string DRACODEC_UNITY_LIB = "dracodec_unity";
#endif

	// Release data associated with DracoMesh.
	[DllImport(DRACODEC_UNITY_LIB)] private static extern void ReleaseDracoMesh(
		DracoMesh * * mesh);
	// Release data associated with DracoAttribute.
	[DllImport(DRACODEC_UNITY_LIB)] private static extern void
	ReleaseDracoAttribute(DracoAttribute * * attr);
	// Release attribute data.
	[DllImport(DRACODEC_UNITY_LIB)] private static extern void ReleaseDracoData(
		DracoData * * data);

	// Decodes compressed Draco::Mesh in buffer to mesh. On input, mesh
	// must be null. The returned mesh must released with ReleaseDracoMesh.
	[DllImport(DRACODEC_UNITY_LIB)] private static extern int DecodeDracoMesh(
		byte[] buffer, int length, DracoMesh * * mesh);

	// Returns the DracoAttribute at index in mesh. On input, attribute must be
	// null. The returned attr must be released with ReleaseDracoAttribute.
	[DllImport(DRACODEC_UNITY_LIB)] private static extern bool GetAttribute(
		DracoMesh * mesh, int index, DracoAttribute * * attr);
	// Returns the DracoAttribute of type at index in mesh. On input, attribute
	// must be null. E.g. If the mesh has two texture coordinates then
	// GetAttributeByType(mesh, AttributeType.TEX_COORD, 1, &attr); will return
	// the second TEX_COORD attribute. The returned attr must be released with
	// ReleaseDracoAttribute.
	[DllImport(DRACODEC_UNITY_LIB)] private static extern bool GetAttributeByType(
		DracoMesh * mesh, AttributeType type, int index, DracoAttribute * * attr);
	// Returns the DracoAttribute with unique_id in mesh. On input, attribute
	// must be null.The returned attr must be released with
	// ReleaseDracoAttribute.
	[DllImport(DRACODEC_UNITY_LIB)] private static extern bool
	GetAttributeByUniqueId(DracoMesh * mesh, int unique_id,
		DracoAttribute * * attr);

	// Returns an array of indices as well as the type of data in data_type. On
	// input, indices must be null. The returned indices must be released with
	// ReleaseDracoData.
	[DllImport(DRACODEC_UNITY_LIB)] private static extern bool GetMeshIndices(
		DracoMesh * mesh, DracoData * * indices);
	// Returns an array of attribute data as well as the type of data in
	// data_type. On input, data must be null. The returned data must be
	// released with ReleaseDracoData.
	[DllImport(DRACODEC_UNITY_LIB)] private static extern bool GetAttributeData(
		DracoMesh * mesh, DracoAttribute * attr, DracoData * * data);

	// Decodes a Draco mesh, creates a Unity mesh from the decoded data and
	// adds the Unity mesh to meshes. encodedData is the compressed Draco mesh.
	public unsafe AsyncMesh LoadMesh(byte[] encodedData, MeshAttributes attributes) {
		DracoMesh * mesh = null;
		if (DecodeDracoMesh(encodedData, encodedData.Length, & mesh) <= 0) {
			Debug.Log("Failed: Decoding error.");
			return null;
		}

		AsyncMesh unityMesh = CreateAsyncMesh(mesh, attributes);

		int numFaces = mesh -> numFaces;
		ReleaseDracoMesh( & mesh);
		if (numFaces > 0) return unityMesh;
		else return null;
	}

	// Creates a Unity mesh from the decoded Draco mesh.
	public unsafe AsyncMesh CreateAsyncMesh(DracoMesh * dracoMesh, MeshAttributes attributes) {
		int numFaces = dracoMesh -> numFaces;

		AsyncMesh mesh = new AsyncMesh();
		mesh.tris = new int[dracoMesh -> numFaces * 3];
		mesh.verts = new Vector3[dracoMesh -> numVertices];

		// Copy face indices.
		DracoData * indicesData;
		GetMeshIndices(dracoMesh, & indicesData);
		int elementSize =
			DataTypeSize((GLTFUtilityDracoLoader.DataType) indicesData -> dataType);
		int * indices = (int * ) (indicesData -> data);
		var indicesPtr = UnsafeUtility.AddressOf(ref mesh.tris[0]);
		UnsafeUtility.MemCpy(indicesPtr, indices,
			mesh.tris.Length * elementSize);
		ReleaseDracoData( & indicesData);

		DracoAttribute * attr = null;

		// Copy positions.
		if (GetAttributeByUniqueId(dracoMesh, attributes.pos, & attr)) {
			DracoData * posData = null;
			GetAttributeData(dracoMesh, attr, & posData);
			elementSize = DataTypeSize((GLTFUtilityDracoLoader.DataType) posData -> dataType) *
				attr -> numComponents;
		var newVerticesPtr = UnsafeUtility.AddressOf(ref mesh.verts[0]);
			UnsafeUtility.MemCpy(newVerticesPtr, (void * ) posData -> data,
				dracoMesh -> numVertices * elementSize);
			ReleaseDracoData( & posData);
			ReleaseDracoAttribute( & attr);
		}

		// Copy normals.
		if (GetAttributeByUniqueId(dracoMesh, attributes.norms, & attr)) {
			DracoData * normData = null;
			if (GetAttributeData(dracoMesh, attr, & normData)) {
				elementSize =
					DataTypeSize((GLTFUtilityDracoLoader.DataType) normData -> dataType) *
					attr -> numComponents;
				mesh.norms = new Vector3[dracoMesh -> numVertices];
				var newNormalsPtr = UnsafeUtility.AddressOf(ref mesh.norms[0]);
				UnsafeUtility.MemCpy(newNormalsPtr, (void * ) normData -> data,
					dracoMesh -> numVertices * elementSize);
				ReleaseDracoData( & normData);
				ReleaseDracoAttribute( & attr);
			}
		}

		// Copy texture coordinates.
		if (GetAttributeByUniqueId(dracoMesh, attributes.uv, & attr)) {
			DracoData * texData = null;
			if (GetAttributeData(dracoMesh, attr, & texData)) {
				elementSize =
					DataTypeSize((GLTFUtilityDracoLoader.DataType) texData -> dataType) *
					attr -> numComponents;
				mesh.uv = new Vector2[dracoMesh -> numVertices];
				var newUVsPtr = UnsafeUtility.AddressOf(ref mesh.uv[0]);
				UnsafeUtility.MemCpy(newUVsPtr, (void * ) texData -> data,
					dracoMesh -> numVertices * elementSize);
				ReleaseDracoData( & texData);
				ReleaseDracoAttribute( & attr);
			}
		}

		// Copy colors.
		if (GetAttributeByUniqueId(dracoMesh, attributes.col, & attr)) {
			DracoData * colorData = null;
			if (GetAttributeData(dracoMesh, attr, & colorData)) {
				elementSize =
					DataTypeSize((GLTFUtilityDracoLoader.DataType) colorData -> dataType) *
					attr -> numComponents;
				mesh.colors = new Color[dracoMesh -> numVertices];
				var newColorsPtr = UnsafeUtility.AddressOf(ref mesh.colors[0]);
				UnsafeUtility.MemCpy(newColorsPtr, (void * ) colorData -> data,
					dracoMesh -> numVertices * elementSize);
				ReleaseDracoData( & colorData);
				ReleaseDracoAttribute( & attr);
			}
		}

		// Copy weights.
		Vector4[] weights = null;
		if (GetAttributeByUniqueId(dracoMesh, attributes.weights, & attr)) {
			DracoData * weightData = null;
			if (GetAttributeData(dracoMesh, attr, & weightData)) {
				elementSize =
					DataTypeSize((GLTFUtilityDracoLoader.DataType) weightData -> dataType) *
					attr -> numComponents;
				if (attr -> dataType == 9) {
					weights = new Vector4[dracoMesh -> numVertices];
					var newWeightsPtr = UnsafeUtility.AddressOf(ref weights[0]);
					UnsafeUtility.MemCpy(newWeightsPtr, (void * ) weightData -> data,
						dracoMesh -> numVertices * elementSize);
				} else if (attr -> dataType == 4) {
					var newWeightsInt = new Vector4<UInt16>[dracoMesh -> numVertices];
					var newWeightsPtr = UnsafeUtility.AddressOf(ref newWeightsInt[0]);
					UnsafeUtility.MemCpy(newWeightsPtr, (void * ) weightData -> data,
						dracoMesh -> numVertices * elementSize);
					weights = newWeightsInt.Select(x => new Vector4(x.x, x.y, x.z, x.w)).ToArray();
				}

				ReleaseDracoData( & weightData);
				ReleaseDracoAttribute( & attr);
			}
		}

		// Copy joints.
		Vector4[] joints = null;
		if (GetAttributeByUniqueId(dracoMesh, attributes.joints, & attr)) {
			DracoData * jointData = null;
			if (GetAttributeData(dracoMesh, attr, & jointData)) {
				elementSize =
					DataTypeSize((GLTFUtilityDracoLoader.DataType) jointData -> dataType) *
					attr -> numComponents;
				if (attr -> dataType == 9) {
					joints = new Vector4[dracoMesh -> numVertices];
					var newJointsPtr = UnsafeUtility.AddressOf(ref joints[0]);
					UnsafeUtility.MemCpy(newJointsPtr, (void * ) jointData -> data,
						dracoMesh -> numVertices * elementSize);
				} else if (attr -> dataType == 4) {
					var newJointsInt = new Vector4<UInt16>[dracoMesh -> numVertices];
					var newJointsPtr = UnsafeUtility.AddressOf(ref newJointsInt[0]);
					UnsafeUtility.MemCpy(newJointsPtr, (void * ) jointData -> data,
						dracoMesh -> numVertices * elementSize);
					joints = newJointsInt.Select(x => new Vector4(x.x, x.y, x.z, x.w)).ToArray();
				}

				ReleaseDracoData( & jointData);
				ReleaseDracoAttribute( & attr);
			}
		}

/* #if UNITY_2017_3_OR_NEWER
		mesh.indexFormat = (newVertices.Length > System.UInt16.MaxValue) ?
			UnityEngine.Rendering.IndexFormat.UInt32 :
			UnityEngine.Rendering.IndexFormat.UInt16;
#else
		if (newVertices.Length > System.UInt16.MaxValue) {
			throw new System.Exception("Draco meshes with more than 65535 vertices are only supported from Unity 2017.3 onwards.");
		}
#endif */

		if (joints != null && weights != null) {
			if (joints.Length == weights.Length) {
				BoneWeight[] boneWeights = new BoneWeight[weights.Length];
				for (int k = 0; k < boneWeights.Length; k++) {
					NormalizeWeights(ref weights[k]);
					boneWeights[k].weight0 = weights[k].x;
					boneWeights[k].weight1 = weights[k].y;
					boneWeights[k].weight2 = weights[k].z;
					boneWeights[k].weight3 = weights[k].w;
					boneWeights[k].boneIndex0 = Mathf.RoundToInt(joints[k].x);
					boneWeights[k].boneIndex1 = Mathf.RoundToInt(joints[k].y);
					boneWeights[k].boneIndex2 = Mathf.RoundToInt(joints[k].z);
					boneWeights[k].boneIndex3 = Mathf.RoundToInt(joints[k].w);
				}
				mesh.boneWeights = boneWeights;
			} else Debug.LogWarning("Draco: joints and weights not same length. Skipped");
		}
		return mesh;
	}

	public void NormalizeWeights(ref Vector4 weights) {
		float total = weights.x + weights.y + weights.z + weights.w;
		if (total == 0) return;
		float mult = 1f / total;
		weights.x *= mult;
		weights.y *= mult;
		weights.z *= mult;
		weights.w *= mult;
	}

	private int DataTypeSize(DataType dt) {
		switch (dt) {
			case DataType.DT_INT8:
			case DataType.DT_UINT8:
				return 1;
			case DataType.DT_INT16:
			case DataType.DT_UINT16:
				return 2;
			case DataType.DT_INT32:
			case DataType.DT_UINT32:
				return 4;
			case DataType.DT_INT64:
			case DataType.DT_UINT64:
				return 8;
			case DataType.DT_FLOAT32:
				return 4;
			case DataType.DT_FLOAT64:
				return 8;
			case DataType.DT_BOOL:
				return 1;
			default:
				return -1;
		}
	}
}