Initial Commit
This commit is contained in:
@@ -0,0 +1,708 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Licensed under the Oculus SDK License Agreement (the "License");
|
||||
* you may not use the Oculus SDK except in compliance with the License,
|
||||
* which is provided at the time of installation or download, or which
|
||||
* otherwise accompanies this software in either electronic or hard copy form.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://developer.oculus.com/licenses/oculussdk/
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/************************************************************************************
|
||||
* Filename : ONSPPropagationGeometry.cs
|
||||
* Content : Geometry Functions
|
||||
Attach to a game object with meshes and material scripts to create geometry
|
||||
NOTE: ensure that Oculus Spatialization is enabled for AudioSource components
|
||||
***********************************************************************************/
|
||||
|
||||
#define INCLUDE_TERRAIN_TREES
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Oculus.Spatializer.Propagation;
|
||||
|
||||
public class ONSPPropagationGeometry : MonoBehaviour
|
||||
{
|
||||
public static string GeometryAssetDirectory = "AudioGeometry";
|
||||
public static string GeometryAssetPath { get { return Application.streamingAssetsPath + "/" + GeometryAssetDirectory; } }
|
||||
//-------
|
||||
// PUBLIC
|
||||
|
||||
/// The path to the serialized mesh file that holds the preprocessed mesh geometry.
|
||||
public string filePathRelative = "";
|
||||
public string filePath { get { return GeometryAssetPath + "/" + filePathRelative; } }
|
||||
public bool fileEnabled = false;
|
||||
|
||||
public bool includeChildMeshes = true;
|
||||
|
||||
//-------
|
||||
// PRIVATE
|
||||
private IntPtr geometryHandle = IntPtr.Zero;
|
||||
|
||||
//-------
|
||||
// PUBLIC STATIC
|
||||
public static int OSPSuccess = 0;
|
||||
|
||||
public const string GEOMETRY_FILE_EXTENSION = "ovramesh";
|
||||
|
||||
private static string GetPath(Transform current)
|
||||
{
|
||||
if (current.parent == null)
|
||||
return current.gameObject.scene.name + "/" + current.name;
|
||||
return GetPath(current.parent) + "-" + current.name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If script is attached to a gameobject, it will try to create geometry
|
||||
/// </summary>
|
||||
void Awake()
|
||||
{
|
||||
CreatePropagationGeometry();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this function to create geometry handle
|
||||
/// </summary>
|
||||
void CreatePropagationGeometry()
|
||||
{
|
||||
// Create Geometry
|
||||
if (ONSPPropagation.Interface.CreateAudioGeometry(out geometryHandle) != OSPSuccess)
|
||||
{
|
||||
throw new Exception("Unable to create geometry handle");
|
||||
}
|
||||
|
||||
// Upload Mesh
|
||||
if (filePath != null && filePath.Length != 0 && fileEnabled && Application.isPlaying)
|
||||
{
|
||||
if (!ReadFile())
|
||||
{
|
||||
Debug.LogError("Failed to read file, attempting to regenerate audio geometry");
|
||||
|
||||
// We should not try to upload data dynamically if data already exists
|
||||
UploadGeometry();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UploadGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the world transform (TODO)
|
||||
/// </summary>
|
||||
private void Update()
|
||||
{
|
||||
if (geometryHandle == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
Matrix4x4 m = transform.localToWorldMatrix;
|
||||
// Note: flip Z to convert from left-handed (+Z forward) to right-handed (+Z backward)
|
||||
float[] matrix = { m[0,0], m[1,0], -m[2,0], m[3,0],
|
||||
m[0,1], m[1,1], -m[2,1], m[3,1],
|
||||
m[0,2], m[1,2], -m[2,2], m[3,2],
|
||||
m[0,3], m[1,3], -m[2,3], m[3,3] };
|
||||
|
||||
ONSPPropagation.Interface.AudioGeometrySetTransform(geometryHandle, matrix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call when destroyed
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
// DESTROY GEOMETRY
|
||||
if (geometryHandle != IntPtr.Zero && ONSPPropagation.Interface.DestroyAudioGeometry(geometryHandle) != OSPSuccess)
|
||||
{
|
||||
throw new Exception("Unable to destroy geometry");
|
||||
}
|
||||
|
||||
geometryHandle = IntPtr.Zero;
|
||||
}
|
||||
|
||||
//
|
||||
// FUNCTIONS FOR UPLOADING MESHES VIA GAME OBJECT
|
||||
//
|
||||
|
||||
static int terrainDecimation = 4;
|
||||
|
||||
private struct MeshMaterial
|
||||
{
|
||||
public MeshFilter meshFilter;
|
||||
public ONSPPropagationMaterial[] materials;
|
||||
}
|
||||
|
||||
private struct TerrainMaterial
|
||||
{
|
||||
public Terrain terrain;
|
||||
public ONSPPropagationMaterial[] materials;
|
||||
public Mesh[] treePrototypeMeshes;
|
||||
}
|
||||
|
||||
private static void traverseMeshHierarchy(GameObject obj, ONSPPropagationMaterial[] currentMaterials, bool includeChildren,
|
||||
List<MeshMaterial> meshMaterials, List<TerrainMaterial> terrainMaterials, bool ignoreStatic, ref int ignoredMeshCount)
|
||||
{
|
||||
if (!obj.activeInHierarchy)
|
||||
return;
|
||||
|
||||
// Check for LOD. If present, use only the highest LOD and don't recurse to children.
|
||||
// Without this, we can accidentally get all LODs merged together.
|
||||
LODGroup lodGroup = obj.GetComponent(typeof(LODGroup)) as LODGroup;
|
||||
if ( lodGroup != null )
|
||||
{
|
||||
LOD [] lods = lodGroup.GetLODs();
|
||||
if ( lods.Length > 0 )
|
||||
{
|
||||
// Get renderers for highest LOD.
|
||||
Renderer [] lodRenderers = lods[0].renderers;
|
||||
if ( lodRenderers.Length > 0 )
|
||||
{
|
||||
// Use the rendered game object to get the mesh instead, and don't go to children.
|
||||
obj = lodRenderers[0].gameObject;
|
||||
includeChildren = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MeshFilter[] meshes = obj.GetComponents<MeshFilter>();
|
||||
Terrain[] terrains = obj.GetComponents<Terrain>();
|
||||
ONSPPropagationMaterial[] materials = obj.GetComponents<ONSPPropagationMaterial>();
|
||||
|
||||
// Initialize the current material array to a new array if there are any new materials.
|
||||
if (materials != null && materials.Length > 0)
|
||||
{
|
||||
// Determine the length of the material array.
|
||||
int maxLength = materials.Length;
|
||||
if (currentMaterials != null)
|
||||
maxLength = Math.Max(maxLength, currentMaterials.Length);
|
||||
|
||||
ONSPPropagationMaterial[] newMaterials = new ONSPPropagationMaterial[maxLength];
|
||||
|
||||
// Copy the previous materials into the new array.
|
||||
if (currentMaterials != null)
|
||||
{
|
||||
for (int i = materials.Length; i < maxLength; i++)
|
||||
newMaterials[i] = currentMaterials[i];
|
||||
}
|
||||
currentMaterials = newMaterials;
|
||||
|
||||
// Copy the current materials.
|
||||
for (int i = 0; i < materials.Length; i++)
|
||||
currentMaterials[i] = materials[i];
|
||||
}
|
||||
|
||||
// Gather the meshes.
|
||||
foreach (MeshFilter meshFilter in meshes)
|
||||
{
|
||||
Mesh mesh = meshFilter.sharedMesh;
|
||||
if (mesh == null)
|
||||
continue;
|
||||
|
||||
if (ignoreStatic && !mesh.isReadable)
|
||||
{
|
||||
Debug.LogWarning("Mesh: " + meshFilter.gameObject.name + " not readable, cannot be static.", meshFilter.gameObject);
|
||||
++ignoredMeshCount;
|
||||
continue;
|
||||
}
|
||||
|
||||
MeshMaterial m = new MeshMaterial();
|
||||
m.meshFilter = meshFilter;
|
||||
m.materials = currentMaterials;
|
||||
meshMaterials.Add(m);
|
||||
}
|
||||
|
||||
// Gather the terrains.
|
||||
foreach (Terrain terrain in terrains)
|
||||
{
|
||||
TerrainMaterial m = new TerrainMaterial();
|
||||
m.terrain = terrain;
|
||||
m.materials = currentMaterials;
|
||||
terrainMaterials.Add(m);
|
||||
}
|
||||
|
||||
// Traverse to the child objects.
|
||||
if (includeChildren)
|
||||
{
|
||||
foreach (Transform child in obj.transform)
|
||||
{
|
||||
if (child.GetComponent<ONSPPropagationGeometry>() == null) // skip children which have their own component
|
||||
traverseMeshHierarchy(child.gameObject, currentMaterials, includeChildren, meshMaterials, terrainMaterials, ignoreStatic, ref ignoredMeshCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// CALL THIS ON GAME OBJECT THAT HAS GEOMETRY ATTACHED TO IT
|
||||
//
|
||||
private int uploadMesh(IntPtr geometryHandle, GameObject meshObject, Matrix4x4 worldToLocal)
|
||||
{
|
||||
int unused = 0;
|
||||
return uploadMesh(geometryHandle, meshObject, worldToLocal, false, ref unused);
|
||||
}
|
||||
|
||||
private int uploadMesh(IntPtr geometryHandle, GameObject meshObject, Matrix4x4 worldToLocal, bool ignoreStatic, ref int ignoredMeshCount)
|
||||
{
|
||||
// Get the child mesh objects.
|
||||
List<MeshMaterial> meshes = new List<MeshMaterial>();
|
||||
List<TerrainMaterial> terrains = new List<TerrainMaterial>();
|
||||
traverseMeshHierarchy(meshObject, null, includeChildMeshes, meshes, terrains, ignoreStatic, ref ignoredMeshCount);
|
||||
|
||||
//***********************************************************************
|
||||
// Count the number of vertices and indices.
|
||||
|
||||
int totalVertexCount = 0;
|
||||
uint totalIndexCount = 0;
|
||||
int totalFaceCount = 0;
|
||||
int totalMaterialCount = 0;
|
||||
|
||||
foreach (MeshMaterial m in meshes)
|
||||
{
|
||||
updateCountsForMesh(ref totalVertexCount, ref totalIndexCount, ref totalFaceCount, ref totalMaterialCount, m.meshFilter.sharedMesh);
|
||||
}
|
||||
|
||||
// TODO: expose tree material
|
||||
ONSPPropagationMaterial[] treeMaterials = new ONSPPropagationMaterial[1];
|
||||
|
||||
for (int i = 0; i < terrains.Count; ++i)
|
||||
{
|
||||
TerrainMaterial t = terrains[i];
|
||||
TerrainData terrain = t.terrain.terrainData;
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
int w = terrain.heightmapResolution;
|
||||
int h = terrain.heightmapResolution;
|
||||
#else
|
||||
int w = terrain.heightmapWidth;
|
||||
int h = terrain.heightmapHeight;
|
||||
#endif
|
||||
int wRes = (w - 1) / terrainDecimation + 1;
|
||||
int hRes = (h - 1) / terrainDecimation + 1;
|
||||
int vertexCount = wRes * hRes;
|
||||
int indexCount = (wRes - 1) * (hRes - 1) * 6;
|
||||
|
||||
totalMaterialCount++;
|
||||
totalVertexCount += vertexCount;
|
||||
totalIndexCount += (uint)indexCount;
|
||||
totalFaceCount += indexCount / 3;
|
||||
|
||||
#if INCLUDE_TERRAIN_TREES
|
||||
TreePrototype[] treePrototypes = terrain.treePrototypes;
|
||||
|
||||
if (treePrototypes.Length != 0)
|
||||
{
|
||||
if (treeMaterials[0] == null)
|
||||
{
|
||||
// Create the tree material
|
||||
treeMaterials[0] = gameObject.AddComponent<ONSPPropagationMaterial>();
|
||||
#if true
|
||||
treeMaterials[0].SetPreset(ONSPPropagationMaterial.Preset.Foliage);
|
||||
#else
|
||||
// Custom material that is highly transmissive
|
||||
treeMaterials[0].absorption.points = new List<ONSPPropagationMaterial.Point>{
|
||||
new ONSPPropagationMaterial.Point(125f, .03f),
|
||||
new ONSPPropagationMaterial.Point(250f, .06f),
|
||||
new ONSPPropagationMaterial.Point(500f, .11f),
|
||||
new ONSPPropagationMaterial.Point(1000f, .17f),
|
||||
new ONSPPropagationMaterial.Point(2000f, .27f),
|
||||
new ONSPPropagationMaterial.Point(4000f, .31f) };
|
||||
|
||||
treeMaterials[0].scattering.points = new List<ONSPPropagationMaterial.Point>{
|
||||
new ONSPPropagationMaterial.Point(125f, .20f),
|
||||
new ONSPPropagationMaterial.Point(250f, .3f),
|
||||
new ONSPPropagationMaterial.Point(500f, .4f),
|
||||
new ONSPPropagationMaterial.Point(1000f, .5f),
|
||||
new ONSPPropagationMaterial.Point(2000f, .7f),
|
||||
new ONSPPropagationMaterial.Point(4000f, .8f) };
|
||||
|
||||
treeMaterials[0].transmission.points = new List<ONSPPropagationMaterial.Point>(){
|
||||
new ONSPPropagationMaterial.Point(125f, .95f),
|
||||
new ONSPPropagationMaterial.Point(250f, .92f),
|
||||
new ONSPPropagationMaterial.Point(500f, .87f),
|
||||
new ONSPPropagationMaterial.Point(1000f, .81f),
|
||||
new ONSPPropagationMaterial.Point(2000f, .71f),
|
||||
new ONSPPropagationMaterial.Point(4000f, .67f) };
|
||||
#endif
|
||||
}
|
||||
|
||||
t.treePrototypeMeshes = new Mesh[treePrototypes.Length];
|
||||
|
||||
// assume the sharedMesh with the lowest vertex is the lowest LOD
|
||||
for (int j = 0; j < treePrototypes.Length; ++j)
|
||||
{
|
||||
GameObject prefab = treePrototypes[j].prefab;
|
||||
MeshFilter[] meshFilters = prefab.GetComponentsInChildren<MeshFilter>();
|
||||
int minVertexCount = int.MaxValue;
|
||||
int index = -1;
|
||||
for (int k = 0; k < meshFilters.Length; ++k)
|
||||
{
|
||||
int count = meshFilters[k].sharedMesh.vertexCount;
|
||||
if (count < minVertexCount)
|
||||
{
|
||||
minVertexCount = count;
|
||||
index = k;
|
||||
}
|
||||
}
|
||||
|
||||
t.treePrototypeMeshes[j] = meshFilters[index].sharedMesh;
|
||||
}
|
||||
|
||||
TreeInstance[] trees = terrain.treeInstances;
|
||||
foreach (TreeInstance tree in trees)
|
||||
{
|
||||
updateCountsForMesh(ref totalVertexCount, ref totalIndexCount, ref totalFaceCount,
|
||||
ref totalMaterialCount, t.treePrototypeMeshes[tree.prototypeIndex]);
|
||||
}
|
||||
|
||||
terrains[i] = t;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
// Copy the mesh data.
|
||||
|
||||
List<Vector3> tempVertices = new List<Vector3>();
|
||||
List<int> tempIndices = new List<int>();
|
||||
|
||||
MeshGroup[] groups = new MeshGroup[totalMaterialCount];
|
||||
float[] vertices = new float[totalVertexCount * 3];
|
||||
int[] indices = new int[totalIndexCount];
|
||||
|
||||
int vertexOffset = 0;
|
||||
int indexOffset = 0;
|
||||
int groupOffset = 0;
|
||||
|
||||
foreach (MeshMaterial m in meshes)
|
||||
{
|
||||
MeshFilter meshFilter = m.meshFilter;
|
||||
|
||||
// Compute the combined transform to go from mesh-local to geometry-local space.
|
||||
Matrix4x4 matrix = worldToLocal * meshFilter.gameObject.transform.localToWorldMatrix;
|
||||
|
||||
uploadMeshFilter(tempVertices, tempIndices, groups, vertices, indices, ref vertexOffset, ref indexOffset, ref groupOffset, meshFilter.sharedMesh, m.materials, matrix);
|
||||
}
|
||||
|
||||
foreach (TerrainMaterial t in terrains)
|
||||
{
|
||||
TerrainData terrain = t.terrain.terrainData;
|
||||
|
||||
// Compute the combined transform to go from mesh-local to geometry-local space.
|
||||
Matrix4x4 matrix = worldToLocal * t.terrain.gameObject.transform.localToWorldMatrix;
|
||||
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
int w = terrain.heightmapResolution;
|
||||
int h = terrain.heightmapResolution;
|
||||
#else
|
||||
int w = terrain.heightmapWidth;
|
||||
int h = terrain.heightmapHeight;
|
||||
#endif
|
||||
float[,] tData = terrain.GetHeights(0, 0, w, h);
|
||||
|
||||
Vector3 meshScale = terrain.size;
|
||||
meshScale = new Vector3(meshScale.x / (w - 1) * terrainDecimation, meshScale.y, meshScale.z / (h - 1) * terrainDecimation);
|
||||
int wRes = (w - 1) / terrainDecimation + 1;
|
||||
int hRes = (h - 1) / terrainDecimation + 1;
|
||||
int vertexCount = wRes * hRes;
|
||||
int triangleCount = (wRes - 1) * (hRes - 1) * 2;
|
||||
|
||||
// Initialize the group.
|
||||
groups[groupOffset].faceType = FaceType.TRIANGLES;
|
||||
groups[groupOffset].faceCount = (UIntPtr)triangleCount;
|
||||
groups[groupOffset].indexOffset = (UIntPtr)indexOffset;
|
||||
|
||||
if (t.materials != null && 0 < t.materials.Length)
|
||||
{
|
||||
t.materials[0].StartInternal();
|
||||
groups[groupOffset].material = t.materials[0].materialHandle;
|
||||
}
|
||||
else
|
||||
groups[groupOffset].material = IntPtr.Zero;
|
||||
|
||||
// Build vertices and UVs
|
||||
for (int y = 0; y < hRes; y++)
|
||||
{
|
||||
for (int x = 0; x < wRes; x++)
|
||||
{
|
||||
int offset = (vertexOffset + y * wRes + x) * 3;
|
||||
Vector3 v = matrix.MultiplyPoint3x4(Vector3.Scale(meshScale, new Vector3(y, tData[x * terrainDecimation, y * terrainDecimation], x)));
|
||||
vertices[offset + 0] = v.x;
|
||||
vertices[offset + 1] = v.y;
|
||||
vertices[offset + 2] = v.z;
|
||||
}
|
||||
}
|
||||
|
||||
// Build triangle indices: 3 indices into vertex array for each triangle
|
||||
for (int y = 0; y < hRes - 1; y++)
|
||||
{
|
||||
for (int x = 0; x < wRes - 1; x++)
|
||||
{
|
||||
// For each grid cell output two triangles
|
||||
indices[indexOffset + 0] = (vertexOffset + (y * wRes) + x);
|
||||
indices[indexOffset + 1] = (vertexOffset + ((y + 1) * wRes) + x);
|
||||
indices[indexOffset + 2] = (vertexOffset + (y * wRes) + x + 1);
|
||||
|
||||
indices[indexOffset + 3] = (vertexOffset + ((y + 1) * wRes) + x);
|
||||
indices[indexOffset + 4] = (vertexOffset + ((y + 1) * wRes) + x + 1);
|
||||
indices[indexOffset + 5] = (vertexOffset + (y * wRes) + x + 1);
|
||||
indexOffset += 6;
|
||||
}
|
||||
}
|
||||
|
||||
vertexOffset += vertexCount;
|
||||
groupOffset++;
|
||||
|
||||
#if INCLUDE_TERRAIN_TREES
|
||||
TreeInstance[] trees = terrain.treeInstances;
|
||||
foreach (TreeInstance tree in trees)
|
||||
{
|
||||
Vector3 pos = Vector3.Scale(tree.position, terrain.size);
|
||||
Matrix4x4 treeLocalToWorldMatrix = t.terrain.gameObject.transform.localToWorldMatrix;
|
||||
treeLocalToWorldMatrix.SetColumn(3, treeLocalToWorldMatrix.GetColumn(3) + new Vector4(pos.x, pos.y, pos.z, 0.0f));
|
||||
// TODO: tree rotation
|
||||
Matrix4x4 treeMatrix = worldToLocal * treeLocalToWorldMatrix;
|
||||
uploadMeshFilter(tempVertices, tempIndices, groups, vertices, indices, ref vertexOffset, ref indexOffset, ref groupOffset, t.treePrototypeMeshes[tree.prototypeIndex], treeMaterials, treeMatrix);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Upload mesh data
|
||||
return ONSPPropagation.Interface.AudioGeometryUploadMeshArrays(geometryHandle,
|
||||
vertices, totalVertexCount,
|
||||
indices, indices.Length,
|
||||
groups, groups.Length);
|
||||
}
|
||||
|
||||
private static void uploadMeshFilter(List<Vector3> tempVertices, List<int> tempIndices, MeshGroup[] groups, float[] vertices, int[] indices,
|
||||
ref int vertexOffset, ref int indexOffset, ref int groupOffset, Mesh mesh, ONSPPropagationMaterial[] materials, Matrix4x4 matrix)
|
||||
{
|
||||
// Get the mesh vertices.
|
||||
tempVertices.Clear();
|
||||
mesh.GetVertices(tempVertices);
|
||||
|
||||
// Copy the Vector3 vertices into a packed array of floats for the API.
|
||||
int meshVertexCount = tempVertices.Count;
|
||||
for (int i = 0; i < meshVertexCount; i++)
|
||||
{
|
||||
// Transform into the parent space.
|
||||
Vector3 v = matrix.MultiplyPoint3x4(tempVertices[i]);
|
||||
int offset = (vertexOffset + i) * 3;
|
||||
vertices[offset + 0] = v.x;
|
||||
vertices[offset + 1] = v.y;
|
||||
vertices[offset + 2] = v.z;
|
||||
}
|
||||
|
||||
// Copy the data for each submesh.
|
||||
for (int i = 0; i < mesh.subMeshCount; i++)
|
||||
{
|
||||
MeshTopology topology = mesh.GetTopology(i);
|
||||
|
||||
if (topology == MeshTopology.Triangles || topology == MeshTopology.Quads)
|
||||
{
|
||||
// Get the submesh indices.
|
||||
tempIndices.Clear();
|
||||
mesh.GetIndices(tempIndices, i);
|
||||
int subMeshIndexCount = tempIndices.Count;
|
||||
|
||||
// Copy and adjust the indices.
|
||||
for (int j = 0; j < subMeshIndexCount; j++)
|
||||
indices[indexOffset + j] = tempIndices[j] + vertexOffset;
|
||||
|
||||
// Initialize the group.
|
||||
if (topology == MeshTopology.Triangles)
|
||||
{
|
||||
groups[groupOffset + i].faceType = FaceType.TRIANGLES;
|
||||
groups[groupOffset + i].faceCount = (UIntPtr)(subMeshIndexCount / 3);
|
||||
}
|
||||
else if (topology == MeshTopology.Quads)
|
||||
{
|
||||
groups[groupOffset + i].faceType = FaceType.QUADS;
|
||||
groups[groupOffset + i].faceCount = (UIntPtr)(subMeshIndexCount / 4);
|
||||
}
|
||||
|
||||
groups[groupOffset + i].indexOffset = (UIntPtr)indexOffset;
|
||||
|
||||
if (materials != null && materials.Length != 0)
|
||||
{
|
||||
int matIndex = i;
|
||||
if (matIndex >= materials.Length)
|
||||
matIndex = materials.Length - 1;
|
||||
materials[matIndex].StartInternal();
|
||||
groups[groupOffset + i].material = materials[matIndex].materialHandle;
|
||||
}
|
||||
else
|
||||
groups[groupOffset + i].material = IntPtr.Zero;
|
||||
|
||||
indexOffset += subMeshIndexCount;
|
||||
}
|
||||
}
|
||||
|
||||
vertexOffset += meshVertexCount;
|
||||
groupOffset += mesh.subMeshCount;
|
||||
}
|
||||
|
||||
private static void updateCountsForMesh(ref int totalVertexCount, ref uint totalIndexCount, ref int totalFaceCount, ref int totalMaterialCount, Mesh mesh)
|
||||
{
|
||||
totalMaterialCount += mesh.subMeshCount;
|
||||
totalVertexCount += mesh.vertexCount;
|
||||
|
||||
for (int i = 0; i < mesh.subMeshCount; i++)
|
||||
{
|
||||
MeshTopology topology = mesh.GetTopology(i);
|
||||
if (topology == MeshTopology.Triangles || topology == MeshTopology.Quads)
|
||||
{
|
||||
uint meshIndexCount = mesh.GetIndexCount(i);
|
||||
totalIndexCount += meshIndexCount;
|
||||
|
||||
if (topology == MeshTopology.Triangles)
|
||||
totalFaceCount += (int)meshIndexCount / 3;
|
||||
else if (topology == MeshTopology.Quads)
|
||||
totalFaceCount += (int)meshIndexCount / 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
// UploadGeometry
|
||||
|
||||
public void UploadGeometry()
|
||||
{
|
||||
int ignoredMeshCount = 0;
|
||||
if (uploadMesh(geometryHandle, gameObject, gameObject.transform.worldToLocalMatrix, true, ref ignoredMeshCount) != OSPSuccess)
|
||||
throw new Exception("Unable to upload audio mesh geometry");
|
||||
|
||||
if (ignoredMeshCount != 0)
|
||||
{
|
||||
Debug.LogError("Failed to upload meshes, " + ignoredMeshCount + " static meshes ignored. Turn on \"File Enabled\" to process static meshes offline", gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
//***********************************************************************
|
||||
// WriteFile - Write the serialized mesh file.
|
||||
|
||||
public bool WriteFile()
|
||||
{
|
||||
if (filePathRelative == "")
|
||||
{
|
||||
filePathRelative = GetPath(transform);
|
||||
string modifier = "";
|
||||
int counter = 0;
|
||||
while (System.IO.File.Exists(filePath + modifier))
|
||||
{
|
||||
modifier = "-" + counter;
|
||||
++counter;
|
||||
|
||||
if (counter > 10000)
|
||||
{
|
||||
// sanity check to prevent hang
|
||||
throw new Exception("Unable to find sutiable file name");
|
||||
}
|
||||
}
|
||||
|
||||
filePathRelative = filePathRelative + modifier;
|
||||
Debug.Log("No file path specified, autogenerated: " + filePathRelative);
|
||||
}
|
||||
|
||||
// Create the directory
|
||||
int directoriesEnd = filePathRelative.LastIndexOf('/');
|
||||
if ( directoriesEnd >= 0 )
|
||||
{
|
||||
string directoryName = filePathRelative.Substring(0, directoriesEnd);
|
||||
System.IO.Directory.CreateDirectory(GeometryAssetPath + "/" + directoryName);
|
||||
}
|
||||
|
||||
// Create a temporary geometry.
|
||||
IntPtr tempGeometryHandle = IntPtr.Zero;
|
||||
if (ONSPPropagation.Interface.CreateAudioGeometry(out tempGeometryHandle) != OSPSuccess)
|
||||
{
|
||||
throw new Exception("Failed to create temp geometry handle");
|
||||
}
|
||||
|
||||
// Upload the mesh geometry.
|
||||
if (uploadMesh(tempGeometryHandle, gameObject, gameObject.transform.worldToLocalMatrix) != OSPSuccess)
|
||||
{
|
||||
Debug.LogError("Error uploading mesh " + gameObject.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the mesh to a file.
|
||||
if (ONSPPropagation.Interface.AudioGeometryWriteMeshFile(tempGeometryHandle, filePath) != OSPSuccess)
|
||||
{
|
||||
Debug.LogError("Error writing mesh file " + filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Destroy the geometry.
|
||||
if (ONSPPropagation.Interface.DestroyAudioGeometry(tempGeometryHandle) != OSPSuccess)
|
||||
{
|
||||
throw new Exception("Failed to destroy temp geometry handle");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
//***********************************************************************
|
||||
// ReadFile - Read the serialized mesh file.
|
||||
|
||||
public bool ReadFile()
|
||||
{
|
||||
if (filePath == null || filePath.Length == 0)
|
||||
{
|
||||
Debug.LogError("Invalid mesh file path");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ONSPPropagation.Interface.AudioGeometryReadMeshFile(geometryHandle, filePath) != OSPSuccess)
|
||||
{
|
||||
Debug.LogError("Error reading mesh file " + filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool WriteToObj()
|
||||
{
|
||||
// Create a temporary geometry.
|
||||
IntPtr tempGeometryHandle = IntPtr.Zero;
|
||||
if (ONSPPropagation.Interface.CreateAudioGeometry(out tempGeometryHandle) != OSPSuccess)
|
||||
{
|
||||
throw new Exception("Failed to create temp geometry handle");
|
||||
}
|
||||
|
||||
// Upload the mesh geometry.
|
||||
if (uploadMesh(tempGeometryHandle, gameObject, gameObject.transform.worldToLocalMatrix) != OSPSuccess)
|
||||
{
|
||||
Debug.LogError("Error uploading mesh " + gameObject.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the mesh to a .obj file.
|
||||
if (ONSPPropagation.Interface.AudioGeometryWriteMeshFileObj(tempGeometryHandle, filePath + ".obj") != OSPSuccess)
|
||||
{
|
||||
Debug.LogError("Error writing .obj file " + filePath + ".obj");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Destroy the geometry.
|
||||
if (ONSPPropagation.Interface.DestroyAudioGeometry(tempGeometryHandle) != OSPSuccess)
|
||||
{
|
||||
throw new Exception("Failed to destroy temp geometry handle");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user