709 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			709 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| /*
 | |
|  * 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;
 | |
|     }
 | |
| }
 | |
| 
 |