366 lines
13 KiB
C#
366 lines
13 KiB
C#
// 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;
|
|
}
|
|
}
|
|
} |