mirror of
https://github.com/RaindropViewer/RaindropViewer.git
synced 2026-07-30 19:12:15 +00:00
306 lines
12 KiB
C#
306 lines
12 KiB
C#
using System;
|
|
using System.Threading;
|
|
using OpenMetaverse;
|
|
using OpenMetaverse.Rendering;
|
|
using Plugins.CommonDependencies;
|
|
using Raindrop.Netcom;
|
|
using Raindrop.Render;
|
|
using Raindrop.Rendering;
|
|
using UnityEngine;
|
|
using Logger = OpenMetaverse.Logger;
|
|
using Mesh = UnityEngine.Mesh;
|
|
using RenderSettings = Raindrop.Rendering.RenderSettings;
|
|
|
|
namespace Raindrop.Unity3D
|
|
{
|
|
//every generated terrain object has this component.
|
|
// it subscribes to the terrain of the sim it is responsible for rendering.
|
|
public class TerrainMeshController
|
|
{
|
|
private RaindropInstance instance { get { return ServiceLocator.Instance.Get<RaindropInstance>(); } }
|
|
private GridClient Client { get { return instance.Client; } }
|
|
private RaindropNetcom netcom { get { return instance.Netcom; } }
|
|
bool Active => instance.Client.Network.Connected;
|
|
|
|
public bool isReadyToMesh => instance.Client.Network.CurrentSim != null;
|
|
|
|
Simulator sim = null;
|
|
Texture2D terrainImage = null;
|
|
|
|
UnityEngine.Vector3[] newVertices;
|
|
UnityEngine.Vector2[] newUV;
|
|
int[] newTriangles;
|
|
|
|
float[,] heightTable = new float[256, 256]; //heightmap of terrain
|
|
bool fetchingTerrainTexture = false; //semaphore for reading terrain tex.
|
|
bool terrainTextureNeedsToBeSplatted = false; //does the texture need to be redrawn?
|
|
private OpenMetaverse.Simulator thisSim;
|
|
float terrainTimeSinceUpdate = Rendering.RenderSettings.MinimumTimeBetweenTerrainUpdated + 1f; // Update terrain om first run
|
|
MeshmerizerR renderer;
|
|
|
|
Face terrainFace; //seems like a 'face' is the secondlife kind of face (where each prim can have up to 8 faces.)
|
|
ColorVertex[] terrainVertices;
|
|
uint[] terrainIndices;
|
|
|
|
private bool terrainMesherIsIdle = true;
|
|
public bool Modified = false; //is the terrain data in OSL(backend) modified since our rendering?
|
|
private readonly TerrainMeshView _terrainMeshView;
|
|
|
|
//reference to the view-layer mesh object. as it is unity API, modify this only on main thread.
|
|
public Mesh mesh_ref;
|
|
|
|
public TerrainMeshController(TerrainMeshView terrainMeshView, Mesh terrainMeshRef)
|
|
{
|
|
mesh_ref = terrainMeshRef;
|
|
this._terrainMeshView = terrainMeshView;
|
|
Client.Terrain.LandPatchReceived += new EventHandler<LandPatchReceivedEventArgs>(Terrain_LandPatchReceived);
|
|
Client.Network.SimChanged += NetworkOnSimChanged;
|
|
}
|
|
|
|
// if we are too far away from the sim, and for more than 3 seconds, we will kill ourselves.
|
|
private void NetworkOnSimChanged(object sender, SimChangedEventArgs e)
|
|
{
|
|
//todo: update current sim.
|
|
try
|
|
{
|
|
thisSim = instance.Client.Network.CurrentSim;
|
|
}
|
|
catch (Exception exp)
|
|
{
|
|
Logger.Log(exp.ToString(), Helpers.LogLevel.Warning);
|
|
}
|
|
|
|
|
|
// var istoofar = CheckIfTooFar();
|
|
// if (istoofar)
|
|
// {
|
|
// DeleteMe();
|
|
// }
|
|
//
|
|
// bool CheckIfTooFar()
|
|
// {
|
|
// ulong avi_sSim = instance.Client.Network.CurrentSim.Handle;
|
|
// if (avi_sSim != thisSim.Handle) //different sim now.
|
|
// {
|
|
// var mySim_v3 = MapSpaceConverters.Handle2MapSpace(thisSim.Handle, 0);
|
|
// var avatar_s_Sim_v3 = MapSpaceConverters.Handle2MapSpace(avi_sSim, 0);
|
|
//
|
|
// return (Vector3.Distance(mySim_v3, avatar_s_Sim_v3) > deleteDistanceThreshold);
|
|
//
|
|
// // ResetTerrainTex();
|
|
// }
|
|
//
|
|
// return false;
|
|
// }
|
|
}
|
|
|
|
void Terrain_LandPatchReceived(object sender, LandPatchReceivedEventArgs e)
|
|
{
|
|
//backend does not know current sim
|
|
if (thisSim == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//filter for packets from current sim only.
|
|
if (e.Simulator.Handle == thisSim.Handle)
|
|
{
|
|
this.Modified = true;
|
|
}
|
|
}
|
|
|
|
// construct a flat land mesh of 256*256 size
|
|
private void buildBasicLandMesh()
|
|
{
|
|
int step = 1;
|
|
for (int x = 0; x < 256; x += step)
|
|
{
|
|
for (int y = 0; y < 256; y += step)
|
|
{
|
|
float z = 0;
|
|
UnityEngine.Vector3[] newVertices;
|
|
|
|
heightTable[x, y] = z;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//this performs re-meshing and re-texturing. ONLY IF MODIFIED and sufficient time elapsed.
|
|
public void Render(float timeSinceLastRenderCall)
|
|
{
|
|
terrainTimeSinceUpdate += timeSinceLastRenderCall;
|
|
|
|
//1. meshing part.
|
|
// Lock check: if terrainmesher is working, we shall not go further.
|
|
if (terrainMesherIsIdle == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (sim == null || sim.Terrain == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Modified && terrainTimeSinceUpdate > RenderSettings.MinimumTimeBetweenTerrainUpdated)
|
|
{
|
|
Logger.Log("queueing the terrain to be computed! ", Helpers.LogLevel.Info);
|
|
//ResetTerrainTex(/*false*/);
|
|
QueueComputeTerrainMesh();
|
|
}
|
|
|
|
|
|
//2. painting part
|
|
if (terrainTextureNeedsToBeSplatted)
|
|
{
|
|
//PaintTerrain();
|
|
}
|
|
Modified = false;
|
|
|
|
//
|
|
// if (terrainIndices == null || terrainVertices == null)
|
|
// {
|
|
// Logger.Log("terrain indices is null", Helpers.LogLevel.Info);
|
|
// return;
|
|
// }
|
|
|
|
//set texture to mesh
|
|
|
|
|
|
|
|
//update / draw new mesh.
|
|
|
|
}
|
|
|
|
//queue a job of terrain-computing and splatting
|
|
private void QueueComputeTerrainMesh()
|
|
{
|
|
if (sim == null || sim.Terrain == null)
|
|
{
|
|
Logger.Log("update terrain failed as the sim or terrain is null", Helpers.LogLevel.Info);
|
|
return;
|
|
}
|
|
|
|
Logger.Log("putting into threadpool", Helpers.LogLevel.Info);
|
|
// Lock: terrainmesher is working.
|
|
terrainMesherIsIdle = false;
|
|
|
|
ThreadPool.QueueUserWorkItem(sync =>
|
|
{
|
|
Logger.Log("QueueUserWorkItem", Helpers.LogLevel.Info);
|
|
|
|
// 1. generate heightTable from patches in backend.
|
|
int step = 1;
|
|
for (int x = 0; x < 256; x += step)
|
|
{
|
|
for (int y = 0; y < 256; y += step)
|
|
{
|
|
float z = 0;
|
|
int patchNr = ((int)x / 16) * 16 + (int)y / 16;
|
|
if (sim.Terrain[patchNr] != null
|
|
&& sim.Terrain[patchNr].Data != null)
|
|
{
|
|
float[] data = sim.Terrain[patchNr].Data;
|
|
z = data[(int)x % 16 * 16 + (int)y % 16];
|
|
}
|
|
heightTable[x, y] = z;
|
|
}
|
|
}
|
|
Logger.Log("finished 1. generate heightTable from patches in backend", Helpers.LogLevel.Info);
|
|
|
|
// 2. create mesh-face from heighttable, using the meshmeriser.
|
|
terrainFace = renderer.TerrainMesh(heightTable, 0f, 255f, 0f, 255f); //generate mesh with heights //the result is a huge struct 'Face'
|
|
Logger.Log("finished 2. create mesh-face from heighttable, using the meshmeriser.", Helpers.LogLevel.Info);
|
|
|
|
// 3. convert omv "face" to unity mesh:
|
|
UniMesher.MeshFromFace(terrainFace, mesh_ref);
|
|
Logger.Log("finished 3. onvert omv face to unity mesh", Helpers.LogLevel.Info);
|
|
|
|
|
|
|
|
// 3. painting the face. we use ColorVertex objects to represent the color of the vertices of the terrain face.
|
|
// terrainVertices = new ColorVertex[terrainFace.Vertices.Count];
|
|
// for (int i = 0; i < terrainFace.Vertices.Count; i++) //for each vert in terrainFace, append the vert to terraiVerticies
|
|
// {
|
|
// byte[] part = Utils.IntToBytes(i); // i = 0x 0000 0000 0000 0000 0000 0000 0000 0000 - 32 bits / 4 bytes.
|
|
// terrainVertices[i] = new ColorVertex()
|
|
// {
|
|
// Vertex = terrainFace.Vertices[i],
|
|
// Color = new Color4b()
|
|
// {
|
|
// R = part[0],
|
|
// G = part[1],
|
|
// B = part[2],
|
|
// A = 253 // terrain picking
|
|
// }
|
|
// };
|
|
// }
|
|
// terrainIndices = new uint[terrainFace.Indices.Count];
|
|
// for (int i = 0; i < terrainIndices.Length; i++)
|
|
// {
|
|
// terrainIndices[i] = terrainFace.Indices[i];
|
|
// }
|
|
// Modified = false;
|
|
// //terrainTextureNeedsUpdate = true;
|
|
// terrainTimeSinceUpdate = 0f;
|
|
//
|
|
// terrainMesherIsIdle = true;
|
|
|
|
// 4. apply this face-mesh to the gameobject's mesh component.
|
|
// terrainMeshUpdater.setMesh(ref terrainMesh, terrainFace);
|
|
|
|
|
|
// Lock: terrainmesher is finished working.
|
|
terrainMesherIsIdle = true;
|
|
});
|
|
}
|
|
|
|
|
|
//delete terrain tex
|
|
private void ResetTerrainTex()
|
|
{
|
|
if (terrainImage != null)
|
|
{
|
|
GameObject.Destroy(terrainImage);
|
|
terrainImage = null;
|
|
}
|
|
|
|
fetchingTerrainTexture = false;
|
|
Modified = true;
|
|
|
|
//temporary
|
|
//terrainTextureNeedsUpdate = true;
|
|
}
|
|
|
|
|
|
// todo: use cpu to generate splat map;
|
|
// todo: use shader to do splatting.
|
|
void PaintTerrain()
|
|
{
|
|
if (!fetchingTerrainTexture)
|
|
{
|
|
fetchingTerrainTexture = true;
|
|
ThreadPool.QueueUserWorkItem(sync =>
|
|
{
|
|
Simulator sim = instance.Client.Network.CurrentSim;
|
|
terrainImage = TerrainSplat.Splat(instance, heightTable,
|
|
new UUID[] { sim.TerrainDetail0, sim.TerrainDetail1, sim.TerrainDetail2, sim.TerrainDetail3 },
|
|
new float[] { sim.TerrainStartHeight00, sim.TerrainStartHeight01, sim.TerrainStartHeight10, sim.TerrainStartHeight11 },
|
|
new float[] { sim.TerrainHeightRange00, sim.TerrainHeightRange01, sim.TerrainHeightRange10, sim.TerrainHeightRange11 });
|
|
|
|
fetchingTerrainTexture = false;
|
|
terrainTextureNeedsToBeSplatted = false;
|
|
_terrainMeshView.SetTexture(terrainImage);
|
|
});
|
|
}
|
|
}
|
|
|
|
// assign the terrain mesh to a certain simulator.
|
|
public void init(Simulator sim_ref)
|
|
{
|
|
sim = sim_ref;
|
|
|
|
}
|
|
}
|
|
} |