mirror of
https://git.mirrors.martin98.com/https://github.com/google/draco
synced 2025-04-21 05:09:52 +08:00
378 lines
13 KiB
C#
378 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.Runtime.InteropServices;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
using UnityEngine;
|
|
|
|
public unsafe class DracoMeshLoader
|
|
{
|
|
// 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,
|
|
COLOR,
|
|
TEX_COORD,
|
|
// A special id used to mark attributes that are not assigned to any known
|
|
// predefined use case. Such attributes are often used for a shader specific
|
|
// data.
|
|
GENERIC
|
|
};
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Release data associated with DracoMesh.
|
|
[DllImport ("dracodec_unity")] private static extern void ReleaseDracoMesh(
|
|
DracoMesh**mesh);
|
|
// Release data associated with DracoAttribute.
|
|
[DllImport ("dracodec_unity")] private static extern void
|
|
ReleaseDracoAttribute(DracoAttribute**attr);
|
|
// Release attribute data.
|
|
[DllImport ("dracodec_unity")] 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")] 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")] 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")] 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")] 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")] 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")] private static extern bool GetAttributeData(
|
|
DracoMesh* mesh, DracoAttribute* attr, DracoData**data);
|
|
|
|
public int LoadMeshFromAsset(string assetName, ref List<Mesh> meshes)
|
|
{
|
|
TextAsset asset =
|
|
Resources.Load(assetName, typeof(TextAsset)) as TextAsset;
|
|
if (asset == null) {
|
|
Debug.Log ("Didn't load file!");
|
|
return -1;
|
|
}
|
|
byte[] encodedData = asset.bytes;
|
|
Debug.Log(encodedData.Length.ToString());
|
|
if (encodedData.Length == 0) {
|
|
Debug.Log ("Didn't load encoded data!");
|
|
return -1;
|
|
}
|
|
return ConvertDracoMeshToUnity(encodedData, ref meshes);
|
|
}
|
|
|
|
// 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 int ConvertDracoMeshToUnity(byte[] encodedData,
|
|
ref List<Mesh> meshes)
|
|
{
|
|
float startTime = Time.realtimeSinceStartup;
|
|
DracoMesh *mesh = null;
|
|
if (DecodeDracoMesh(encodedData, encodedData.Length, &mesh) <= 0) {
|
|
Debug.Log("Failed: Decoding error.");
|
|
return -1;
|
|
}
|
|
|
|
float decodeTimeMilli =
|
|
(Time.realtimeSinceStartup - startTime) * 1000.0f;
|
|
Debug.Log("decodeTimeMilli: " + decodeTimeMilli.ToString());
|
|
|
|
Debug.Log("Num indices: " + mesh->numFaces.ToString());
|
|
Debug.Log("Num vertices: " + mesh->numVertices.ToString());
|
|
Debug.Log("Num attributes: " + mesh->numAttributes.ToString());
|
|
|
|
Mesh unityMesh = CreateUnityMesh(mesh);
|
|
UnityMeshToCamera(ref unityMesh);
|
|
meshes.Add(unityMesh);
|
|
|
|
int numFaces = mesh->numFaces;
|
|
ReleaseDracoMesh(&mesh);
|
|
return numFaces;
|
|
}
|
|
|
|
// Creates a Unity mesh from the decoded Draco mesh.
|
|
public unsafe Mesh CreateUnityMesh(DracoMesh *dracoMesh)
|
|
{
|
|
float startTime = Time.realtimeSinceStartup;
|
|
int numFaces = dracoMesh->numFaces;
|
|
int[] newTriangles = new int[dracoMesh->numFaces * 3];
|
|
Vector3[] newVertices = new Vector3[dracoMesh->numVertices];
|
|
Vector2[] newUVs = null;
|
|
Vector3[] newNormals = null;
|
|
Color[] newColors = null;
|
|
byte[] newGenerics = null;
|
|
|
|
// Copy face indices.
|
|
DracoData *indicesData;
|
|
GetMeshIndices(dracoMesh, &indicesData);
|
|
int elementSize =
|
|
DataTypeSize((DracoMeshLoader.DataType)indicesData->dataType);
|
|
int *indices = (int*)(indicesData->data);
|
|
var indicesPtr = UnsafeUtility.AddressOf(ref newTriangles[0]);
|
|
UnsafeUtility.MemCpy(indicesPtr, indices,
|
|
newTriangles.Length * elementSize);
|
|
ReleaseDracoData(&indicesData);
|
|
|
|
// Copy positions.
|
|
DracoAttribute *attr = null;
|
|
GetAttributeByType(dracoMesh, AttributeType.POSITION, 0, &attr);
|
|
DracoData* posData = null;
|
|
GetAttributeData(dracoMesh, attr, &posData);
|
|
elementSize = DataTypeSize((DracoMeshLoader.DataType)posData->dataType) *
|
|
attr->numComponents;
|
|
var newVerticesPtr = UnsafeUtility.AddressOf(ref newVertices[0]);
|
|
UnsafeUtility.MemCpy(newVerticesPtr, (void*)posData->data,
|
|
dracoMesh->numVertices * elementSize);
|
|
ReleaseDracoData(&posData);
|
|
ReleaseDracoAttribute(&attr);
|
|
|
|
// Copy normals.
|
|
if (GetAttributeByType(dracoMesh, AttributeType.NORMAL, 0, &attr)) {
|
|
DracoData* normData = null;
|
|
if (GetAttributeData(dracoMesh, attr, &normData)) {
|
|
elementSize =
|
|
DataTypeSize((DracoMeshLoader.DataType)normData->dataType) *
|
|
attr->numComponents;
|
|
newNormals = new Vector3[dracoMesh->numVertices];
|
|
var newNormalsPtr = UnsafeUtility.AddressOf(ref newNormals[0]);
|
|
UnsafeUtility.MemCpy(newNormalsPtr, (void*)normData->data,
|
|
dracoMesh->numVertices * elementSize);
|
|
Debug.Log("Decoded mesh normals.");
|
|
ReleaseDracoData(&normData);
|
|
ReleaseDracoAttribute(&attr);
|
|
}
|
|
}
|
|
|
|
// Copy texture coordinates.
|
|
if (GetAttributeByType(dracoMesh, AttributeType.TEX_COORD, 0, &attr)) {
|
|
DracoData* texData = null;
|
|
if (GetAttributeData(dracoMesh, attr, &texData)) {
|
|
elementSize =
|
|
DataTypeSize((DracoMeshLoader.DataType)texData->dataType) *
|
|
attr->numComponents;
|
|
newUVs = new Vector2[dracoMesh->numVertices];
|
|
var newUVsPtr = UnsafeUtility.AddressOf(ref newUVs[0]);
|
|
UnsafeUtility.MemCpy(newUVsPtr, (void*)texData->data,
|
|
dracoMesh->numVertices * elementSize);
|
|
Debug.Log("Decoded mesh texcoords.");
|
|
ReleaseDracoData(&texData);
|
|
ReleaseDracoAttribute(&attr);
|
|
}
|
|
}
|
|
|
|
// Copy colors.
|
|
if (GetAttributeByType(dracoMesh, AttributeType.COLOR, 0, &attr)) {
|
|
DracoData* colorData = null;
|
|
if (GetAttributeData(dracoMesh, attr, &colorData)) {
|
|
elementSize =
|
|
DataTypeSize((DracoMeshLoader.DataType)colorData->dataType) *
|
|
attr->numComponents;
|
|
newColors = new Color[dracoMesh->numVertices];
|
|
var newColorsPtr = UnsafeUtility.AddressOf(ref newColors[0]);
|
|
UnsafeUtility.MemCpy(newColorsPtr, (void*)colorData->data,
|
|
dracoMesh->numVertices * elementSize);
|
|
Debug.Log("Decoded mesh colors.");
|
|
ReleaseDracoData(&colorData);
|
|
ReleaseDracoAttribute(&attr);
|
|
}
|
|
}
|
|
|
|
// Copy generic data. This script does not do anyhting with the generic
|
|
// data.
|
|
if (GetAttributeByType(dracoMesh, AttributeType.GENERIC, 0, &attr)) {
|
|
DracoData* genericData = null;
|
|
if (GetAttributeData(dracoMesh, attr, &genericData)) {
|
|
elementSize =
|
|
DataTypeSize((DracoMeshLoader.DataType)genericData->dataType) *
|
|
attr->numComponents;
|
|
newGenerics = new byte[dracoMesh->numVertices * elementSize];
|
|
var newGenericPtr = UnsafeUtility.AddressOf(ref newGenerics[0]);
|
|
UnsafeUtility.MemCpy(newGenericPtr, (void*)genericData->data,
|
|
dracoMesh->numVertices * elementSize);
|
|
Debug.Log("Decoded mesh generic data.");
|
|
ReleaseDracoData(&genericData);
|
|
ReleaseDracoAttribute(&attr);
|
|
}
|
|
}
|
|
|
|
float copyDecodedDataTimeMilli =
|
|
(Time.realtimeSinceStartup - startTime) * 1000.0f;
|
|
Debug.Log("copyDecodedDataTimeMilli: " +
|
|
copyDecodedDataTimeMilli.ToString());
|
|
|
|
startTime = Time.realtimeSinceStartup;
|
|
Mesh mesh = new Mesh();
|
|
|
|
#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
|
|
|
|
mesh.vertices = newVertices;
|
|
mesh.SetTriangles(newTriangles, 0, true);
|
|
if (newUVs != null) {
|
|
mesh.uv = newUVs;
|
|
}
|
|
if (newNormals != null) {
|
|
mesh.normals = newNormals;
|
|
} else {
|
|
mesh.RecalculateNormals();
|
|
Debug.Log("Mesh doesn't have normals, recomputed.");
|
|
}
|
|
if (newColors != null) {
|
|
mesh.colors = newColors;
|
|
}
|
|
|
|
float convertTimeMilli =
|
|
(Time.realtimeSinceStartup - startTime) * 1000.0f;
|
|
Debug.Log("convertTimeMilli: " + convertTimeMilli.ToString());
|
|
return mesh;
|
|
}
|
|
|
|
// Scale and translate the decoded mesh so it will be visible to
|
|
// a new camera's default settings.
|
|
public unsafe void UnityMeshToCamera(ref Mesh mesh)
|
|
{
|
|
float startTime = Time.realtimeSinceStartup;
|
|
mesh.RecalculateBounds();
|
|
|
|
float scale = 0.5f / mesh.bounds.extents.x;
|
|
if (0.5f / mesh.bounds.extents.y < scale) {
|
|
scale = 0.5f / mesh.bounds.extents.y;
|
|
}
|
|
if (0.5f / mesh.bounds.extents.z < scale) {
|
|
scale = 0.5f / mesh.bounds.extents.z;
|
|
}
|
|
|
|
Vector3[] vertices = mesh.vertices;
|
|
int i = 0;
|
|
while (i < vertices.Length) {
|
|
vertices[i] *= scale;
|
|
i++;
|
|
}
|
|
|
|
mesh.vertices = vertices;
|
|
mesh.RecalculateBounds();
|
|
|
|
Vector3 translate = mesh.bounds.center;
|
|
translate.x = 0 - mesh.bounds.center.x;
|
|
translate.y = 0 - mesh.bounds.center.y;
|
|
translate.z = 2 - mesh.bounds.center.z;
|
|
|
|
i = 0;
|
|
while (i < vertices.Length) {
|
|
vertices[i] += translate;
|
|
i++;
|
|
}
|
|
mesh.vertices = vertices;
|
|
float transformTimeMilli =
|
|
(Time.realtimeSinceStartup - startTime) * 1000.0f;
|
|
Debug.Log("transformTimeMilli: " + transformTimeMilli.ToString());
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|