diff --git a/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLMeshObject.cs b/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLMeshObject.cs
index 91d6a47a9..3c1866db5 100644
--- a/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLMeshObject.cs
+++ b/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLMeshObject.cs
@@ -17,34 +17,55 @@
using UnityEngine.Assertions;
// Experimental is necessary for gathering GraphicsFormat of the texture.
using UnityEngine.Experimental.Rendering;
-using Unity.Collections;
+using System.Collections.Generic;
namespace RGLUnityPlugin
{
+
+ internal interface IRGLObject
+ {
+ GameObject RepresentedGO { get; }
+ int? CategoryId { get; }
+ string CategoryName { get; }
+
+ void Update();
+
+ void DestroyInRGL();
+ }
+
///
/// RGL counterpart of Unity GameObjects.
/// Contains information about RGL entity and RGLMesh.
///
- public class RGLObject
+ public abstract class RGLObject : IRGLObject
{
- public string Identifier;
- public RGLMesh RglMesh;
- public RGLTexture Texture;
- public Func GetLocalToWorld;
- public GameObject RepresentedGO;
- public int? categoryId;
- public string categoryName;
-
+ private readonly string identifier;
+ private RGLTexture rglTexture;
private IntPtr rglEntityPtr;
- public RGLObject(string identifier, RGLMesh rglMesh, Func getLocalToWorld, GameObject representedGO)
+ protected RGLMesh rglMesh;
+
+ public GameObject RepresentedGO { get; }
+ public int? CategoryId { get; private set; }
+ public string CategoryName { get; private set; }
+
+ // There are different stratiegies for obtaining a RGLMesh so we have to also destroy it differently.
+ protected abstract RGLMesh GetRGLMeshFrom(T meshSource);
+ protected abstract void DestroyRGLMesh();
+
+ protected abstract Matrix4x4 GetLocalToWorld();
+
+ protected RGLObject(string identifier, GameObject representedGO, T meshSource)
{
- Identifier = identifier;
- RglMesh = rglMesh;
- GetLocalToWorld = getLocalToWorld;
+ this.identifier = identifier;
RepresentedGO = representedGO;
-
+ rglMesh = GetRGLMeshFrom(meshSource);
+ if (rglMesh == null)
+ {
+ throw new RGLException($"Could not create RGLMesh from gameobject '{representedGO.name}'.");
+ }
UploadToRGL();
+ SetIntensityTexture();
var semanticCategory = RepresentedGO.GetComponentInParent();
if (semanticCategory != null)
@@ -56,20 +77,42 @@ public RGLObject(string identifier, RGLMesh rglMesh, Func getLocalToW
~RGLObject()
{
- DestroyFromRGL();
+ DestroyInRGL();
}
- public void DestroyFromRGL()
+ public virtual void DestroyInRGL()
{
- if (rglEntityPtr != IntPtr.Zero)
+ if (rglEntityPtr == IntPtr.Zero)
{
- RGLNativeAPI.CheckErr(RGLNativeAPI.rgl_entity_destroy(rglEntityPtr));
- rglEntityPtr = IntPtr.Zero;
+ return;
+ }
+
+ RGLNativeAPI.CheckErr(RGLNativeAPI.rgl_entity_destroy(rglEntityPtr));
+ rglEntityPtr = IntPtr.Zero;
+
+ DestroyRGLMesh();
+ rglMesh = null;
+
+ if (rglTexture != null)
+ {
+ RGLTextureSharingManager.UnregisterRGLTextureInstance(rglTexture);
+ rglTexture = null;
+ }
+ }
+
+ public void Update()
+ {
+ UpdateTransform();
+ if (rglMesh is RGLSkinnedMesh rglSkinnedMesh)
+ {
+ rglSkinnedMesh.UpdateSkinnedMesh();
}
}
- public void UpdateTransform()
+ protected virtual void UpdateTransform()
{
+ Assert.IsFalse(rglEntityPtr == IntPtr.Zero);
+
Matrix4x4 m = GetLocalToWorld();
float[] matrix3x4 =
{
@@ -89,25 +132,25 @@ public void UpdateTransform()
public override int GetHashCode()
{
- return Identifier.GetHashCode();
+ return identifier.GetHashCode();
}
public override bool Equals(object obj)
{
- return obj is RGLObject rglObject && Identifier.Equals(rglObject.Identifier);
+ return obj is RGLObject rglObject && identifier.Equals(rglObject.identifier);
}
- protected void UploadToRGL()
+ private void UploadToRGL()
{
// Mesh should be uploaded.
- Assert.IsFalse(RglMesh.rglMeshPtr == IntPtr.Zero);
+ Assert.IsFalse(rglMesh.RGLMeshPtr == IntPtr.Zero);
unsafe
{
try
{
RGLNativeAPI.CheckErr(
- RGLNativeAPI.rgl_entity_create(out rglEntityPtr, IntPtr.Zero, RglMesh.rglMeshPtr));
+ RGLNativeAPI.rgl_entity_create(out rglEntityPtr, IntPtr.Zero, rglMesh.RGLMeshPtr));
}
catch (RGLException)
{
@@ -124,32 +167,228 @@ private void UpdateSemanticCategory(SemanticCategory semanticCategory)
return;
}
- categoryId = semanticCategory.CategoryId;
- categoryName = semanticCategory.gameObject.name;
- RGLNativeAPI.CheckErr(RGLNativeAPI.rgl_entity_set_id(rglEntityPtr, categoryId.Value));
+ CategoryId = semanticCategory.CategoryId;
+ CategoryName = semanticCategory.gameObject.name;
+ RGLNativeAPI.CheckErr(RGLNativeAPI.rgl_entity_set_id(rglEntityPtr, CategoryId.Value));
}
- public void SetIntensityTexture(RGLTexture texture)
+ private void SetIntensityTexture()
{
- unsafe
+ var intensityTextureComponent = RepresentedGO.GetComponent();
+
+ if( intensityTextureComponent == null || intensityTextureComponent.texture == null)
{
- try
+ return;
+ }
+
+ rglTexture = RGLTextureSharingManager.RegisterRGLTextureInstance(intensityTextureComponent.texture);
+ try
+ {
+ RGLNativeAPI.CheckErr(
+ RGLNativeAPI.rgl_entity_set_intensity_texture(rglEntityPtr, rglTexture.RGLTexturePtr));
+ }
+ catch (RGLException)
+ {
+ Debug.LogError($"Cannot assign texture: {rglTexture.Texture.name}, to entity: {RepresentedGO.name}");
+ throw;
+ }
+
+ // Mesh should be uploaded before assigning UVs.
+ Assert.IsFalse(rglMesh.RGLMeshPtr == IntPtr.Zero);
+
+ rglMesh.UploadUVs();
+ }
+ }
+
+ public class RGLMeshRendererObject : RGLObject
+ {
+ private readonly Func getLocalToWorld;
+
+ // By default localToWorld is taken from MeshRenderer, but developer can override it by passing overrideGetLocalToWorld.
+ public RGLMeshRendererObject(MeshRenderer meshRenderer, Func overrideGetLocalToWorld = null):
+ base(
+ $"{meshRenderer.gameObject.name}#{meshRenderer.gameObject.GetInstanceID()}",
+ meshRenderer.gameObject,
+ meshRenderer
+ )
+ {
+ var rendererTransform = meshRenderer.transform;
+ getLocalToWorld = overrideGetLocalToWorld != null ? overrideGetLocalToWorld : () => rendererTransform.localToWorldMatrix;
+ }
+
+ protected override RGLMesh GetRGLMeshFrom(MeshRenderer meshRenderer)
+ {
+ var meshFilter = meshRenderer.GetComponent();
+ if (meshFilter.sharedMesh == null)
+ {
+ Debug.LogWarning($"Shared mesh of {meshRenderer.gameObject.name} is null, skipping");
+ return null;
+ }
+
+ return RGLMeshSharingManager.RegisterRGLMeshInstance(meshFilter.sharedMesh);
+ }
+
+ protected override Matrix4x4 GetLocalToWorld()
+ {
+ return getLocalToWorld();
+ }
+
+ protected override void DestroyRGLMesh()
+ {
+ RGLMeshSharingManager.UnregisterRGLMeshInstance(rglMesh);
+ }
+ }
+
+ public class RGLSkinnedMeshRendererObject : RGLObject
+ {
+ private readonly Transform skinnedMeshRendererTransform;
+
+ public RGLSkinnedMeshRendererObject(SkinnedMeshRenderer skinnedMeshRenderer) :
+ base(
+ $"{skinnedMeshRenderer.gameObject.name}#{skinnedMeshRenderer.gameObject.GetInstanceID()}",
+ skinnedMeshRenderer.gameObject,
+ skinnedMeshRenderer
+ )
+ {
+ skinnedMeshRendererTransform = skinnedMeshRenderer.transform;
+ }
+
+ protected override RGLMesh GetRGLMeshFrom(SkinnedMeshRenderer skinnedMeshRenderer)
+ {
+ // Skinned meshes cannot be shared by using RGLMeshSharingManager
+ return new RGLSkinnedMesh(skinnedMeshRenderer.gameObject.GetInstanceID(), skinnedMeshRenderer);
+ }
+
+ protected override Matrix4x4 GetLocalToWorld()
+ {
+ return skinnedMeshRendererTransform.localToWorldMatrix;
+ }
+
+ protected override void DestroyRGLMesh()
+ {
+ rglMesh.DestroyInRGL();
+ }
+ }
+
+ public class RGLColliderObject : RGLObject
+ {
+ private readonly Collider collider;
+
+ public RGLColliderObject(Collider collider) :
+ base($"{collider.gameObject.name}#{collider.gameObject.GetInstanceID()}",
+ collider.gameObject,
+ collider
+ )
+ {
+ this.collider = collider;
+ }
+
+ protected override RGLMesh GetRGLMeshFrom(Collider collider)
+ {
+ return RGLMeshSharingManager.RegisterRGLMeshInstance(ColliderUtilities.GetMeshForCollider(collider));
+ }
+
+ protected override Matrix4x4 GetLocalToWorld()
+ {
+ return collider.transform.localToWorldMatrix * ColliderUtilities.GetColliderTransformMatrix(collider);
+ }
+
+ protected override void DestroyRGLMesh()
+ {
+ RGLMeshSharingManager.UnregisterRGLMeshInstance(rglMesh);
+ }
+ }
+
+ public class RGLTerrainObject : RGLObject
+ {
+ private readonly List terrainSubObjects;
+ private readonly Transform terrainTransform;
+
+ public RGLTerrainObject(Terrain terrain) :
+ base($"{terrain.gameObject.name}#{terrain.gameObject.GetInstanceID()}", terrain.gameObject, terrain)
+ {
+ terrainTransform = terrain.transform;
+ terrainSubObjects = new List();
+ var treePrototypes = terrain.terrainData.treePrototypes;
+
+ var treePrototypesRenderers = new List[treePrototypes.Length];
+ var treePrototypeHasLODGroup = new bool[treePrototypes.Length];
+
+ for (var i = 0; i < treePrototypes.Length; i++)
+ {
+ treePrototypesRenderers[i] = new List();
+ var treePrefab = treePrototypes[i].prefab;
+ if (treePrefab.TryGetComponent(out var lodGroup))
{
- RGLNativeAPI.CheckErr(
- RGLNativeAPI.rgl_entity_set_intensity_texture(rglEntityPtr, texture.rglTexturePtr));
- Texture = texture;
+ if (lodGroup.GetLODs().Length == 0)
+ {
+ Debug.LogWarning($"No LOD levels in LODGroup of tree prototype {treePrefab.name}");
+ continue;
+ }
+ var lod = lodGroup.GetLODs()[0];
+ foreach (var renderer in lod.renderers)
+ {
+ if (renderer.gameObject.TryGetComponent(out _))
+ {
+ treePrototypesRenderers[i].Add(renderer);
+ }
+ }
+ treePrototypeHasLODGroup[i] = true;
+ } else if (treePrefab.TryGetComponent(out var mr) && treePrefab.TryGetComponent(out _))
+ {
+ treePrototypesRenderers[i].Add(mr);
+ treePrototypeHasLODGroup[i] = false;
}
- catch (RGLException)
+ }
+
+ for (var treeIndex = 0; treeIndex < terrain.terrainData.treeInstanceCount; treeIndex++)
+ {
+ var prototypeIndex = terrain.terrainData.GetTreeInstance(treeIndex).prototypeIndex;
+ foreach (var renderer in treePrototypesRenderers[prototypeIndex])
{
- Debug.LogError($"Cannot assign texture: {texture.Identifier}, to entity: {Identifier}");
- throw;
+ var treePose = TerrainUtilities.GetTreePose(terrain, treeIndex, treePrototypeHasLODGroup[prototypeIndex]);
+ if (renderer is MeshRenderer mr)
+ {
+ terrainSubObjects.Add(new RGLMeshRendererObject(mr,() =>
+ terrain.transform.localToWorldMatrix * treePose * mr.localToWorldMatrix));
+ }
}
+ }
+ }
- // Mesh should be uploaded before assigning UVs.
- Assert.IsFalse(RglMesh.rglMeshPtr == IntPtr.Zero);
+ protected override Matrix4x4 GetLocalToWorld()
+ {
+ return terrainTransform.localToWorldMatrix;
+ }
+
+ public override void DestroyInRGL()
+ {
+ foreach (var terrainSubObject in terrainSubObjects)
+ {
+ terrainSubObject.DestroyInRGL();
+ }
+
+ base.DestroyInRGL();
+ }
- RglMesh.UploadUVs();
+ protected override void UpdateTransform()
+ {
+ foreach (var terrainSubObject in terrainSubObjects)
+ {
+ terrainSubObject.Update();
}
+
+ base.UpdateTransform();
+ }
+
+ protected override RGLMesh GetRGLMeshFrom(Terrain terrain)
+ {
+ return new RGLMesh(terrain.gameObject.GetInstanceID(), TerrainUtilities.GetTerrainMesh(terrain));
+ }
+
+ protected override void DestroyRGLMesh()
+ {
+ rglMesh.DestroyInRGL();
}
}
@@ -160,12 +399,12 @@ public void SetIntensityTexture(RGLTexture texture)
///
public class RGLMesh
{
- public string Identifier;
- public Mesh Mesh;
+ public int Identifier;
+ protected Mesh Mesh;
- public IntPtr rglMeshPtr = IntPtr.Zero;
+ public IntPtr RGLMeshPtr = IntPtr.Zero;
- public RGLMesh(string identifier, Mesh mesh)
+ public RGLMesh(int identifier, Mesh mesh)
{
Identifier = identifier;
Mesh = mesh;
@@ -175,15 +414,15 @@ public RGLMesh(string identifier, Mesh mesh)
~RGLMesh()
{
- DestroyFromRGL();
+ DestroyInRGL();
}
- public void DestroyFromRGL()
+ public void DestroyInRGL()
{
- if (rglMeshPtr != IntPtr.Zero)
+ if (RGLMeshPtr != IntPtr.Zero)
{
- RGLNativeAPI.CheckErr(RGLNativeAPI.rgl_mesh_destroy(rglMeshPtr));
- rglMeshPtr = IntPtr.Zero;
+ RGLNativeAPI.CheckErr(RGLNativeAPI.rgl_mesh_destroy(RGLMeshPtr));
+ RGLMeshPtr = IntPtr.Zero;
}
}
@@ -200,7 +439,7 @@ protected void UploadToRGL()
if (!verticesOK || !indicesOK)
{
throw new NotSupportedException(
- $"Could not get mesh data with mesh identifier {Identifier}. The mesh may be not readable or empty.");
+ $"Could not get mesh data from Mesh '{Mesh.name}'. The mesh may be not readable or empty.");
}
unsafe
@@ -212,13 +451,13 @@ protected void UploadToRGL()
try
{
RGLNativeAPI.CheckErr(
- RGLNativeAPI.rgl_mesh_create(out rglMeshPtr,
+ RGLNativeAPI.rgl_mesh_create(out RGLMeshPtr,
(IntPtr) pVertices, vertices.Length,
(IntPtr) pIndices, indices.Length / 3));
}
catch (RGLException)
{
- if (rglMeshPtr != IntPtr.Zero) RGLNativeAPI.rgl_mesh_destroy(rglMeshPtr);
+ if (RGLMeshPtr != IntPtr.Zero) RGLNativeAPI.rgl_mesh_destroy(RGLMeshPtr);
throw;
}
}
@@ -233,28 +472,28 @@ public void UploadUVs()
if(!uvOK)
{
- Debug.LogWarning($"Could not assign UVs to mesh: {Identifier}. Mash has no UV, or UV are empty.");
+ Debug.LogWarning($"Could not assign UVs to mesh: '{Mesh.name}'. Mesh has no UV, or UV are empty.");
}
else
{
unsafe
- {
+ {
fixed(Vector2* pUVs = UVs)
{
try
{
RGLNativeAPI.CheckErr(
- RGLNativeAPI.rgl_mesh_set_texture_coords(rglMeshPtr,
+ RGLNativeAPI.rgl_mesh_set_texture_coords(RGLMeshPtr,
(IntPtr)pUVs,
UVs.Length));
}
catch (RGLException)
{
- Debug.LogWarning($"Could not assign UVs to mesh: {Identifier}.");
+ Debug.LogWarning($"Could not assign UVs to mesh: '{Mesh.name}'.");
throw;
}
}
- }
+ }
}
}
}
@@ -264,20 +503,20 @@ public void UploadUVs()
///
public class RGLSkinnedMesh : RGLMesh
{
- public SkinnedMeshRenderer SkinnedMeshRenderer;
+ private readonly SkinnedMeshRenderer skinnedMeshRenderer;
- public RGLSkinnedMesh(string identifier, SkinnedMeshRenderer smr)
+ public RGLSkinnedMesh(int identifier, SkinnedMeshRenderer smr)
{
Identifier = identifier;
Mesh = new Mesh();
- SkinnedMeshRenderer = smr;
- SkinnedMeshRenderer.BakeMesh(Mesh, true);
+ skinnedMeshRenderer = smr;
+ skinnedMeshRenderer.BakeMesh(Mesh, true);
UploadToRGL();
}
public void UpdateSkinnedMesh()
{
- SkinnedMeshRenderer.BakeMesh(Mesh, true);
+ skinnedMeshRenderer.BakeMesh(Mesh, true);
unsafe
{
// Accessing .vertices perform a CPU copy!
@@ -288,7 +527,7 @@ public void UpdateSkinnedMesh()
fixed (Vector3* pVertices = Mesh.vertices)
{
RGLNativeAPI.CheckErr(
- RGLNativeAPI.rgl_mesh_update_vertices(rglMeshPtr, (IntPtr) pVertices, Mesh.vertices.Length));
+ RGLNativeAPI.rgl_mesh_update_vertices(RGLMeshPtr, (IntPtr) pVertices, Mesh.vertices.Length));
}
}
}
@@ -300,9 +539,9 @@ public void UpdateSkinnedMesh()
///
public class RGLTexture
{
- public int Identifier;
- public Texture2D Texture;
- public IntPtr rglTexturePtr = IntPtr.Zero;
+ public readonly int Identifier;
+ public readonly Texture2D Texture;
+ public IntPtr RGLTexturePtr = IntPtr.Zero;
public RGLTexture(){}
@@ -315,15 +554,15 @@ public RGLTexture(Texture2D texture, int identifier)
~RGLTexture()
{
- DestroyFromRGL();
+ DestroyInRGL();
}
- public void DestroyFromRGL()
+ public void DestroyInRGL()
{
- if (rglTexturePtr != IntPtr.Zero)
+ if (RGLTexturePtr != IntPtr.Zero)
{
- RGLNativeAPI.CheckErr(RGLNativeAPI.rgl_texture_destroy(rglTexturePtr));
- rglTexturePtr = IntPtr.Zero;
+ RGLNativeAPI.CheckErr(RGLNativeAPI.rgl_texture_destroy(RGLTexturePtr));
+ RGLTexturePtr = IntPtr.Zero;
}
}
@@ -335,13 +574,13 @@ protected void UploadToRGL()
if (!resolutionOK)
{
throw new NotSupportedException(
- $"Could not get texture data. Resolution seems to be broken.");
+ $"Could not get texture data from Texture '{Texture.name}'. Resolution seems to be broken.");
}
if (!graphicsFormatOK)
{
throw new NotSupportedException(
- $"Could not get texture data. Texture format has to be equal to R8_UNorm.");
+ $"Could not get texture data from Texture '{Texture.name}'. Texture format has to be equal to R8_UNorm.");
}
unsafe
@@ -352,22 +591,22 @@ protected void UploadToRGL()
{
RGLNativeAPI.CheckErr(
RGLNativeAPI.rgl_texture_create(
- out rglTexturePtr,
+ out RGLTexturePtr,
(IntPtr) textureDataPtr,
Texture.width,
Texture.height));
}
catch (RGLException)
{
- if (rglTexturePtr != IntPtr.Zero)
+ if (RGLTexturePtr != IntPtr.Zero)
{
- RGLNativeAPI.rgl_texture_destroy(rglTexturePtr);
- rglTexturePtr = IntPtr.Zero;
+ RGLNativeAPI.rgl_texture_destroy(RGLTexturePtr);
+ RGLTexturePtr = IntPtr.Zero;
}
- throw;
+ throw;
}
- }
- }
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLMeshSharingManager.cs b/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLMeshSharingManager.cs
new file mode 100644
index 000000000..136a1c596
--- /dev/null
+++ b/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLMeshSharingManager.cs
@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace RGLUnityPlugin
+{
+ public class RGLMeshSharingManager
+ {
+ private static Dictionary sharedMeshes = new Dictionary(); //
+ private static Dictionary sharedMeshesUsageCount = new Dictionary(); //
+
+ public static RGLMesh RegisterRGLMeshInstance(Mesh unityMesh)
+ {
+ var meshId = unityMesh.GetInstanceID();
+ if (!sharedMeshes.ContainsKey(meshId))
+ {
+ var rglMesh = new RGLMesh(meshId, unityMesh);
+ sharedMeshes.Add(meshId, rglMesh);
+ sharedMeshesUsageCount.Add(meshId, 1);
+ }
+ else
+ {
+ sharedMeshesUsageCount[meshId]++;
+ }
+
+ return sharedMeshes[meshId];
+ }
+
+ public static void UnregisterRGLMeshInstance(RGLMesh rglMesh)
+ {
+ var meshId = rglMesh.Identifier;
+ if (sharedMeshes[meshId] is null)
+ {
+ Debug.LogWarning($"Trying to unregister absent in RGLMeshSharingManager mesh of id: {meshId}, ignoring request");
+ return;
+ }
+
+ sharedMeshesUsageCount[meshId]--;
+ if (sharedMeshesUsageCount[meshId] == 0)
+ {
+ sharedMeshes[meshId].DestroyInRGL();
+ sharedMeshes.Remove(meshId);
+ sharedMeshesUsageCount.Remove(meshId);
+ }
+ }
+
+ public static void Clear()
+ {
+ foreach (var mesh in sharedMeshes)
+ {
+ mesh.Value.DestroyInRGL();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLMeshSharingManager.cs.meta b/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLMeshSharingManager.cs.meta
new file mode 100644
index 000000000..1aa510c44
--- /dev/null
+++ b/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLMeshSharingManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f3925eb4ce66baa129634b42f2ff2c6f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLTextureManager.cs b/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLTextureManager.cs
new file mode 100644
index 000000000..f876a1804
--- /dev/null
+++ b/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLTextureManager.cs
@@ -0,0 +1,53 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace RGLUnityPlugin
+{
+ public class RGLTextureSharingManager
+ {
+ private static Dictionary sharedTextures = new Dictionary(); //
+ private static Dictionary sharedTexturesUsageCount = new Dictionary(); //
+
+ public static RGLTexture RegisterRGLTextureInstance(Texture2D texture)
+ {
+ var textureID = texture.GetInstanceID();
+
+ if(!sharedTextures.ContainsKey(textureID))
+ {
+ var rglTextureToAdd = new RGLTexture(texture, textureID);
+ sharedTextures.Add(textureID, rglTextureToAdd);
+ sharedTexturesUsageCount.Add(textureID, 0);
+ }
+
+ sharedTexturesUsageCount[textureID] += 1;
+
+ return sharedTextures[textureID];
+ }
+
+ public static void UnregisterRGLTextureInstance(RGLTexture rglTexture)
+ {
+ var textureId = rglTexture.Identifier;
+ if (sharedTextures[textureId] is null)
+ {
+ Debug.LogWarning($"Trying to unregister absent in RGLTextureSharingManager texture of id: {textureId}, ignoring request");
+ return;
+ }
+
+ sharedTexturesUsageCount[textureId]--;
+ if (sharedTexturesUsageCount[textureId] == 0)
+ {
+ sharedTextures[textureId].DestroyInRGL();
+ sharedTextures.Remove(textureId);
+ sharedTextures.Remove(textureId);
+ }
+ }
+
+ public static void Clear()
+ {
+ foreach (var mesh in sharedTextures)
+ {
+ mesh.Value.DestroyInRGL();
+ }
+ }
+ }
+}
diff --git a/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLTextureManager.cs.meta b/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLTextureManager.cs.meta
new file mode 100644
index 000000000..2d0afa663
--- /dev/null
+++ b/Assets/RGLUnityPlugin/Scripts/LowLevelWrappers/RGLTextureManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 91c9c04927411d5dcb8a4505e5e1e81b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/RGLUnityPlugin/Scripts/SceneManager.cs b/Assets/RGLUnityPlugin/Scripts/SceneManager.cs
index fe4b92f65..24e697a9e 100644
--- a/Assets/RGLUnityPlugin/Scripts/SceneManager.cs
+++ b/Assets/RGLUnityPlugin/Scripts/SceneManager.cs
@@ -53,35 +53,29 @@ public enum MeshSource
private string semanticCategoryDictionaryFile;
// Getting meshes strategies
- private delegate IEnumerable IntoRGLObjectsStrategy(IEnumerable gameObjects);
+ private delegate IEnumerable IntoRGLObjectsStrategy(IEnumerable gameObjects);
private IntoRGLObjectsStrategy IntoRGLObjects;
// Keeping track of the scene objects
private HashSet lastFrameGameObjects = new HashSet();
- private readonly Dictionary uploadedRGLObjects = new Dictionary();
+ private readonly Dictionary uploadedRGLObjects = new Dictionary();
// This dictionary keeps tracks of identifier -> instance id of objects that were removed (e.g. temporary NPCs)
// This is needed to include them in the instance id dictionary yaml saved at the end of simulation.
// Since categoryId can be changed in the runtime, this is filled only on object removal / simulation end.
- private Dictionary semanticDict = new Dictionary();
-
- private static Dictionary sharedMeshes = new Dictionary(); //
- private static Dictionary sharedMeshesUsageCount = new Dictionary(); //
-
- private static Dictionary sharedTextures = new Dictionary(); //
- private static Dictionary sharedTexturesUsageCount = new Dictionary(); //
+ private readonly Dictionary semanticDict = new Dictionary();
public static ITimeSource TimeSource { get; set; } = new UnityTimeSource();
private int lastUpdateFrame = -1;
- void OnDisable()
+ private void OnDisable()
{
Clear();
}
- void OnValidate()
+ private void OnValidate()
{
UpdateMeshSource();
}
@@ -98,7 +92,7 @@ private void UpdateMeshSource()
{
IntoRGLObjectsStrategy UpdatedIntoRGLObjects = meshSource switch
{
- MeshSource.OnlyColliders => IntoRGLObjectsUsingCollider,
+ MeshSource.OnlyColliders => IntoRGLObjectsUsingColliders,
MeshSource.RegularMeshesAndCollidersInsteadOfSkinned => IntoRGLObjectsHybrid,
MeshSource.RegularMeshesAndSkinnedMeshes => IntoRGLObjectsUsingMeshes,
_ => throw new ArgumentOutOfRangeException()
@@ -144,101 +138,49 @@ public void DoUpdate()
// Added
var toAddGOs = new HashSet(thisFrameGOs);
toAddGOs.ExceptWith(lastFrameGameObjects);
- RGLObject[] toAdd = IntoRGLObjects(toAddGOs).ToArray();
+ var toAdd = IntoRGLObjects(toAddGOs).ToArray();
+ var toAddTerrain = IntoRGLTerrainObjects(toAddGOs).ToArray();
+ if (toAddTerrain.Length != 0)
+ {
+ toAdd = toAdd.Concat(toAddTerrain).ToArray();
+ }
// Removed
var toRemoveGOs = new HashSet(lastFrameGameObjects);
toRemoveGOs.ExceptWith(thisFrameGOs);
toRemoveGOs.IntersectWith(uploadedRGLObjects.Keys);
- RGLObject[] toRemove = toRemoveGOs.Select((o) => uploadedRGLObjects[o]).ToArray();
-
- // Skinned
- RGLObject[] newToSkin = toAdd.Where(o => o.RglMesh is RGLSkinnedMesh).ToArray();
- RGLObject[] existingToSkin = uploadedRGLObjects.Values
- .Where(o => o.RglMesh is RGLSkinnedMesh)
- .Except(toRemove).ToArray();
- RGLObject[] toSkin = existingToSkin.Concat(newToSkin).ToArray();
+ var toRemove = toRemoveGOs.Select((o) => uploadedRGLObjects[o]).ToArray();
lastFrameGameObjects = thisFrameGOs;
Profiler.EndSample();
- Profiler.BeginSample("Add new textures");
- AddTextures(toAdd);
- Profiler.EndSample();
-
Profiler.BeginSample("Remove despawned objects");
foreach (var rglObject in toRemove)
{
- if(rglObject.Texture != null)
- {
- sharedTexturesUsageCount[rglObject.Texture.Identifier] -=1;
- }
-
- if (!(rglObject.RglMesh is RGLSkinnedMesh))
- {
- sharedMeshesUsageCount[rglObject.RglMesh.Identifier] -= 1;
- }
-
- updateSemanticDict(rglObject);
- rglObject.DestroyFromRGL();
+ rglObject.DestroyInRGL();
uploadedRGLObjects.Remove(rglObject.RepresentedGO);
}
-
- Profiler.EndSample();
-
- // TODO: this can be parallelized and moved to Update()
- Profiler.BeginSample("Update skinned meshes");
- foreach (var rglObject in toSkin)
- {
- var skinnedMesh = rglObject.RglMesh as RGLSkinnedMesh;
- Assert.IsNotNull(skinnedMesh);
- skinnedMesh.UpdateSkinnedMesh();
- }
-
Profiler.EndSample();
-
+
Profiler.BeginSample("Mark spawned objects as updated");
foreach (var rglObject in toAdd)
{
// Game Objects must not have duplicate representations.
// Occasionally, this assertion may fail due to disabled Read/Write setting of the prefab's mesh.
Assert.IsFalse(uploadedRGLObjects.ContainsKey(rglObject.RepresentedGO));
-
- if (!(rglObject.RglMesh is RGLSkinnedMesh)) sharedMeshesUsageCount[rglObject.RglMesh.Identifier] += 1;
+
uploadedRGLObjects.Add(rglObject.RepresentedGO, rglObject);
}
-
Profiler.EndSample();
// TODO(prybicki): This part can take up to 8ms on Shinjuku scene, two ideas to optimize it soon:
// - Implement batch update in RGL
// - Use Transform.hasChanged to filter out some objects
- Profiler.BeginSample("Update transforms");
+ Profiler.BeginSample("Update transforms and skinned meshes");
foreach (var gameRGLObject in uploadedRGLObjects)
{
- gameRGLObject.Value.UpdateTransform();
- }
-
- Profiler.EndSample();
-
- Profiler.BeginSample("Destroy unused meshes");
- foreach (var meshUsageCounter in sharedMeshesUsageCount.Where(x => x.Value < 1).ToList())
- {
- sharedMeshes[meshUsageCounter.Key].DestroyFromRGL();
- sharedMeshes.Remove(meshUsageCounter.Key);
- sharedMeshesUsageCount.Remove(meshUsageCounter.Key);
+ gameRGLObject.Value.Update();
}
-
- Profiler.EndSample();
-
- Profiler.BeginSample("Destroy unused textures");
- foreach(var textureUsageCounter in sharedTexturesUsageCount.Where(x =>x.Value < 1).ToList())
- {
- sharedTextures[textureUsageCounter.Key].DestroyFromRGL();
- sharedTextures.Remove(textureUsageCounter.Key);
- sharedTexturesUsageCount.Remove(textureUsageCounter.Key);
- }
-
Profiler.EndSample();
}
@@ -253,39 +195,27 @@ private void SynchronizeSceneTime()
private void Clear()
{
- if (!lastFrameGameObjects.Any() && !uploadedRGLObjects.Any() && !sharedMeshes.Any()) return;
-
- foreach (var rglMesh in sharedMeshes)
- {
- rglMesh.Value.DestroyFromRGL();
- }
+ if (!lastFrameGameObjects.Any() && !uploadedRGLObjects.Any()) return;
foreach (var rglObject in uploadedRGLObjects)
{
- rglObject.Value.DestroyFromRGL();
+ rglObject.Value.DestroyInRGL();
}
-
- foreach ( var rglTexture in sharedTextures)
- {
- rglTexture.Value.DestroyFromRGL();
- }
-
+
+ RGLMeshSharingManager.Clear();
+ RGLTextureSharingManager.Clear();
uploadedRGLObjects.Clear();
lastFrameGameObjects.Clear();
- sharedMeshes.Clear();
- sharedMeshesUsageCount.Clear();
- sharedTextures.Clear();
- sharedMeshesUsageCount.Clear();
Debug.Log("RGLSceneManager: cleared");
}
- private void updateSemanticDict(RGLObject rglObject)
+ private void updateSemanticDict(IRGLObject rglObject)
{
- if (rglObject.categoryId.HasValue)
+ if (rglObject.CategoryId.HasValue)
{
- if (!semanticDict.ContainsKey(rglObject.categoryName))
+ if (!semanticDict.ContainsKey(rglObject.CategoryName))
{
- semanticDict.Add(rglObject.categoryName, rglObject.categoryId.Value);
+ semanticDict.Add(rglObject.CategoryName, rglObject.CategoryId.Value);
}
}
}
@@ -311,7 +241,7 @@ private void OnApplicationQuit()
/// Yields a collection of RGL objects based on active colliders found in provided game objects.
/// This function ignores whether gameObject is active, if filtering is needed, it should be done earlier.
///
- private static IEnumerable IntoRGLObjectsUsingCollider(IEnumerable gameObjects)
+ private static IEnumerable IntoRGLObjectsUsingColliders(IEnumerable gameObjects)
{
foreach (var gameObject in gameObjects)
{
@@ -331,7 +261,14 @@ private static IEnumerable IntoRGLObjectsUsingCollider(IEnumerable IntoRGLObjectsUsingCollider(IEnumerable
- private static IEnumerable IntoRGLObjectsHybrid(IEnumerable gameObjects)
+ private static IEnumerable IntoRGLObjectsHybrid(IEnumerable gameObjects)
{
var collidersToYield = new HashSet();
foreach (var renderer in GetUniqueRenderersInGameObjects(gameObjects))
@@ -362,19 +299,13 @@ private static IEnumerable IntoRGLObjectsHybrid(IEnumerable();
- if (mf.sharedMesh == null)
- {
- Debug.LogWarning($"Shared mesh of {mr.gameObject} is null, skipping");
- continue;
- }
- yield return MeshFilterToRGLObject(mf);
+ yield return new RGLMeshRendererObject(mr);
}
}
foreach (var collider in collidersToYield)
{
- yield return ColliderToRGLObject(collider);
+ yield return new RGLColliderObject(collider);
}
}
@@ -383,20 +314,13 @@ private static IEnumerable IntoRGLObjectsHybrid(IEnumerable
- private static IEnumerable IntoRGLObjectsUsingMeshes(IEnumerable gameObjects)
+ private static IEnumerable IntoRGLObjectsUsingMeshes(IEnumerable gameObjects)
{
foreach (var renderer in GetUniqueRenderersInGameObjects(gameObjects))
{
- var go = renderer.gameObject;
if (renderer is MeshRenderer mr)
{
- var mf = mr.GetComponent();
- if (mf.sharedMesh == null)
- {
- Debug.LogWarning($"Shared mesh of {mr.gameObject} is null, skipping");
- continue;
- }
- yield return MeshFilterToRGLObject(mf);
+ yield return new RGLMeshRendererObject(mr);
}
if (renderer is SkinnedMeshRenderer smr)
@@ -406,52 +330,21 @@ private static IEnumerable IntoRGLObjectsUsingMeshes(IEnumerable smr.transform.localToWorldMatrix,
- go);
- }
- }
- }
- private static RGLObject ColliderToRGLObject(Collider collider)
- {
- var mesh = ColliderUtilities.GetMeshForCollider(collider);
- string meshId = $"r#{mesh.GetInstanceID()}";
- if (!sharedMeshes.ContainsKey(meshId))
- {
- RGLMesh rglMesh = new RGLMesh(meshId, mesh);
- sharedMeshes.Add(meshId, rglMesh);
- sharedMeshesUsageCount.Add(meshId, 0);
+ yield return new RGLSkinnedMeshRendererObject(smr);
+ }
}
-
- var gameObject = collider.gameObject;
- return new RGLObject($"{gameObject.name}#{gameObject.GetInstanceID()}",
- sharedMeshes[meshId],
- () => collider.transform.localToWorldMatrix *
- ColliderUtilities.GetColliderTransformMatrix(collider),
- gameObject);
}
- private static RGLObject MeshFilterToRGLObject(MeshFilter meshFilter)
+ private static IEnumerable IntoRGLTerrainObjects(IEnumerable gameObjects)
{
- var mesh = meshFilter.sharedMesh;
- string meshId = $"r#{mesh.GetInstanceID()}";
- if (!sharedMeshes.ContainsKey(meshId))
+ foreach (var gameObject in gameObjects)
{
- RGLMesh rglMesh = new RGLMesh(meshId, mesh);
- sharedMeshes.Add(meshId, rglMesh);
- sharedMeshesUsageCount.Add(meshId, 0);
+ if (gameObject.TryGetComponent(out var terrain))
+ {
+ yield return new RGLTerrainObject(terrain);
+ }
}
-
- var gameObject = meshFilter.gameObject;
- return new RGLObject($"{gameObject.name}#{gameObject.GetInstanceID()}",
- sharedMeshes[meshId],
- () => gameObject.transform.localToWorldMatrix,
- gameObject);
}
///
@@ -512,42 +405,6 @@ private static IEnumerable GetUniqueRenderersInGameObjects(IEnumerable
foreach (var mr in mrs) yield return mr;
}
- ///
- /// Searches through new rglObjects for ones containing IntensityTexture component.
- /// If present, check if the found texture was already sent to RGL.
- /// If not, send it and write its identifier to sharedTextures dictionary.
- /// After that assign rglTexture to the proper rglObject.
- ///
- private static void AddTextures(IEnumerable rglObjects)
- {
- foreach (var rglObject in rglObjects)
- {
- var intensityTextureComponent = rglObject.RepresentedGO.GetComponent();
-
- if( intensityTextureComponent == null)
- {
- continue;
- }
-
- if( intensityTextureComponent.texture == null)
- {
- continue;
- }
-
- int textureID = intensityTextureComponent.texture.GetInstanceID();
-
- if(!sharedTextures.ContainsKey(textureID))
- {
- var rglTextureToAdd = new RGLTexture(intensityTextureComponent.texture, textureID);
- sharedTextures.Add(textureID, rglTextureToAdd);
- sharedTexturesUsageCount.Add(textureID, 0);
- }
-
- rglObject.SetIntensityTexture(sharedTextures[textureID]);
- sharedTexturesUsageCount[textureID] += 1;
- }
- }
-
private static bool IsNotActiveOrParentHasLidar(GameObject gameObject)
{
return !gameObject.activeInHierarchy || gameObject.GetComponentsInParent().Length != 0;
diff --git a/Assets/RGLUnityPlugin/Scripts/Utilities/TerrainUtilities.cs b/Assets/RGLUnityPlugin/Scripts/Utilities/TerrainUtilities.cs
new file mode 100644
index 000000000..580653935
--- /dev/null
+++ b/Assets/RGLUnityPlugin/Scripts/Utilities/TerrainUtilities.cs
@@ -0,0 +1,126 @@
+using System.Collections.Generic;
+using UnityEngine.Rendering;
+using UnityEngine;
+
+namespace RGLUnityPlugin
+{
+ public class TerrainUtilities
+ {
+ public static Mesh GetTerrainMesh(Terrain terrain)
+ {
+ var terrainData = terrain.terrainData;
+ var heightmapResolution = terrainData.heightmapResolution;
+ var holesResolution = terrainData.holesResolution;
+ var correctHolesResolution = holesResolution == heightmapResolution - 1;
+ var heights = terrainData.GetHeights(0, 0, heightmapResolution, heightmapResolution);
+ var solidSurfaceTiles = terrainData.GetHoles(0, 0, holesResolution, holesResolution);
+ var scale = terrainData.heightmapScale;
+ var vertices = new Vector3[heightmapResolution * heightmapResolution];
+
+ if (!correctHolesResolution)
+ {
+ Debug.LogWarning($"Terrain {terrain.GetInstanceID()} holes resolution is incorrect, holes will be ignored by RGL");
+ }
+
+ for (var z = 0; z < heightmapResolution; z++)
+ {
+ for (var x = 0; x < heightmapResolution; x++)
+ {
+ vertices[x * heightmapResolution + z].x = x * scale.x;
+ vertices[x * heightmapResolution + z].y = heights[z, x] * scale.y;
+ vertices[x * heightmapResolution + z].z = z * scale.z;
+ }
+ }
+
+ // this is the number of squares alongside each axis of the terrain
+ // e.g. if you have a 3x3 grid of points you can fill the space between them using a 2x2 square grid
+ var tileResolution = heightmapResolution - 1;
+
+ // count solid terrain tiles (not holes)
+ var tileCount = 0;
+ foreach (var solidSurface in solidSurfaceTiles)
+ {
+ if (solidSurface)
+ {
+ tileCount++;
+ }
+ }
+
+ if (!correctHolesResolution)
+ {
+ tileCount = tileResolution * tileResolution;
+ }
+
+ // there are 2 triangles per square tile, so 6 indices
+ var triangles = new int[tileCount * 2 * 3];
+
+ var trianglesIndex = 0;
+ for (var z = 0; z < tileResolution; z++)
+ {
+ for (var x = 0; x < tileResolution; x++)
+ {
+ if (correctHolesResolution && !solidSurfaceTiles[z, x])
+ {
+ continue;
+ }
+
+ var sampleBase = x * heightmapResolution + z;
+
+ // first triangle of tile
+ triangles[trianglesIndex++] = sampleBase;
+ triangles[trianglesIndex++] = sampleBase + heightmapResolution;
+ triangles[trianglesIndex++] = sampleBase + heightmapResolution + 1;
+
+ // second triangle of tile
+ triangles[trianglesIndex++] = sampleBase;
+ triangles[trianglesIndex++] = sampleBase + 1;
+ triangles[trianglesIndex++] = sampleBase + 1 + heightmapResolution;
+ }
+ }
+
+ var uv = new Vector2[vertices.Length];
+ for (var i = 0; i < vertices.Length; i++)
+ {
+ uv[i] = new Vector2(vertices[i].x / (tileResolution * scale.x), vertices[i].z / (tileResolution * scale.z));
+ }
+
+ var heightmapMesh = new Mesh();
+ heightmapMesh.indexFormat = IndexFormat.UInt32;
+ heightmapMesh.vertices = vertices;
+ heightmapMesh.triangles = triangles;
+ heightmapMesh.uv = uv;
+
+ return heightmapMesh;
+ }
+
+ public static Matrix4x4 GetTreePose(Terrain terrain, int treeIndex, bool applyRotationAndScale)
+ {
+ var terrainPosition = terrain.transform.position;
+ var terrainData = terrain.terrainData;
+ var resolution = terrainData.heightmapResolution;
+ var heightmapScale = terrainData.heightmapScale;
+ var treeInstance = terrainData.GetTreeInstance(treeIndex);
+ var treePosition = treeInstance.position;
+
+ var translation = new Vector3(treePosition.x, 0, treePosition.z);
+ translation.x *= heightmapScale.x * (resolution - 1);
+ translation.z *= heightmapScale.z * (resolution - 1);
+ var samplePose = new Vector3(terrainPosition.x + translation.x, 0, terrainPosition.z + translation.z);
+ translation.y = terrain.SampleHeight(samplePose);
+
+ var rotation = Quaternion.identity;
+ if (applyRotationAndScale)
+ {
+ rotation = Quaternion.AngleAxis(treeInstance.rotation * Mathf.Rad2Deg, Vector3.up);
+ }
+
+ var scale = Vector3.one;
+ if (applyRotationAndScale)
+ {
+ scale = new Vector3(treeInstance.widthScale, treeInstance.heightScale, treeInstance.widthScale);
+ }
+
+ return Matrix4x4.TRS(translation, rotation, scale);
+ }
+ }
+}
diff --git a/Assets/RGLUnityPlugin/Scripts/Utilities/TerrainUtilities.cs.meta b/Assets/RGLUnityPlugin/Scripts/Utilities/TerrainUtilities.cs.meta
new file mode 100644
index 000000000..0c582e395
--- /dev/null
+++ b/Assets/RGLUnityPlugin/Scripts/Utilities/TerrainUtilities.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 1719548a6c3b4d6b90c6162fec245261
+timeCreated: 1690364114
\ No newline at end of file