/// </summary>\r
Dictionary<uint, RenderPrimitive> Prims = new Dictionary<uint, RenderPrimitive>();\r
List<SceneObject> SortedObjects;\r
+ List<SceneObject> OccludedObjects;\r
List<RenderAvatar> VisibleAvatars;\r
Dictionary<uint, RenderAvatar> Avatars = new Dictionary<uint, RenderAvatar>();\r
\r
/// <summary>\r
/// Render avatars\r
/// </summary>\r
- public bool AvatarRenderingEnabled = true;\r
+ public bool AvatarRenderingEnabled = false;\r
\r
/// <summary>\r
/// Show avatar skeloton\r
/// </summary>\r
public bool RenderAvatarSkeleton = false;\r
\r
+ /// <summary>\r
+ /// Should we try to optimize by not drawing objects occluded behind other objects\r
+ /// </summary>\r
+ public bool OcclusionCullingEnabled = true;\r
+\r
+ /// <summary>\r
+ /// Cache images after jpeg2000 decode. Uses a lot of disk space and can cause disk trashing\r
+ /// </summary>\r
+ public bool CacheDecodedTextures = false;\r
+\r
#endregion Public fields\r
\r
#region Private fields\r
OpenTK.Vector4 specularColor;\r
float drawDistance = 48f;\r
float drawDistanceSquared = 48f * 48f;\r
- bool useDecodedImageCache = true;\r
\r
GridClient Client;\r
RadegastInstance Instance;\r
item.Data.TextureInfo.IsMask = isMask;\r
\r
imageBytes = mi.ExportTGA();\r
- if (useDecodedImageCache)\r
+ if (CacheDecodedTextures)\r
{\r
RHelp.SaveCachedImage(imageBytes, item.TeFace.TextureID, hasAlpha, fullAlpha, isMask);\r
}\r
GL.PopAttrib();\r
}\r
\r
-\r
- void RenderBoundingBox(SceneObject prim)\r
- {\r
- Vector3 scale = prim.BasePrim.Scale;\r
- BoundingVolume bbox = prim.BoundingVolume;\r
- GL.PushAttrib(AttribMask.AllAttribBits);\r
- GL.Disable(EnableCap.Fog);\r
- GL.Disable(EnableCap.Texture2D);\r
- GL.Disable(EnableCap.Lighting);\r
- GL.Disable(EnableCap.CullFace);\r
- GL.Disable(EnableCap.AlphaTest);\r
-\r
- GL.DepthMask(false);\r
- GL.ColorMask(false, false, false, false);\r
-\r
- GL.PushMatrix();\r
- GL.MultMatrix(Math3D.CreateSRTMatrix(scale, prim.RenderRotation, prim.RenderPosition));\r
- GL.Color3(1f, 0f, 0f);\r
- GL.Begin(BeginMode.Quads);\r
- var bmin = bbox.Min;\r
- var bmax = bbox.Max;\r
-\r
- //front\r
- GL.Vertex3(bmin.X, bmin.Y, bmin.Z);\r
- GL.Vertex3(bmax.X, bmin.Y, bmin.Z);\r
- GL.Vertex3(bmax.X, bmax.Y, bmin.Z);\r
- GL.Vertex3(bmin.X, bmax.Y, bmin.Z);\r
-\r
- // back\r
- GL.Vertex3(bmin.X, bmin.Y, bmax.Z);\r
- GL.Vertex3(bmax.X, bmin.Y, bmax.Z);\r
- GL.Vertex3(bmax.X, bmax.Y, bmax.Z);\r
- GL.Vertex3(bmin.X, bmax.Y, bmax.Z);\r
-\r
- // up\r
- GL.Vertex3(bmin.X, bmax.Y, bmax.Z);\r
- GL.Vertex3(bmax.X, bmax.Y, bmax.Z);\r
- GL.Vertex3(bmax.X, bmax.Y, bmin.Z);\r
- GL.Vertex3(bmin.X, bmax.Y, bmin.Z);\r
-\r
- // down\r
- GL.Vertex3(bmin.X, bmin.Y, bmax.Z);\r
- GL.Vertex3(bmax.X, bmin.Y, bmax.Z);\r
- GL.Vertex3(bmax.X, bmin.Y, bmin.Z);\r
- GL.Vertex3(bmin.X, bmin.Y, bmin.Z);\r
-\r
- // left side\r
- GL.Vertex3(bmin.X, bmin.Y, bmax.Z);\r
- GL.Vertex3(bmin.X, bmax.Y, bmax.Z);\r
- GL.Vertex3(bmin.X, bmax.Y, bmin.Z);\r
- GL.Vertex3(bmin.X, bmin.Y, bmin.Z);\r
-\r
- // rigth side\r
- GL.Vertex3(bmax.X, bmin.Y, bmax.Z);\r
- GL.Vertex3(bmax.X, bmax.Y, bmax.Z);\r
- GL.Vertex3(bmax.X, bmax.Y, bmin.Z);\r
- GL.Vertex3(bmax.X, bmin.Y, bmin.Z);\r
-\r
- GL.End();\r
- GL.PopMatrix();\r
-\r
- GL.ColorMask(true, true, true, true);\r
- GL.DepthMask(true);\r
-\r
- GL.PopAttrib();\r
- }\r
-\r
void RenderPrim(RenderPrimitive mesh, RenderPass pass, int primNr)\r
{\r
if (!AvatarRenderingEnabled && mesh.Attached) return;\r
// Do we have animated texture on this face\r
bool animatedTexture = false;\r
\r
+ // Initialise flags tracking what type of faces this prim has\r
+ if (pass == RenderPass.Simple)\r
+ {\r
+ mesh.HasSimpleFaces = false;\r
+ }\r
+ else if (pass == RenderPass.Alpha)\r
+ {\r
+ mesh.HasAlphaFaces = false;\r
+ }\r
+\r
// Draw the prim faces\r
for (int j = 0; j < mesh.Faces.Count; j++)\r
{\r
if (belongToAlphaPass && pass != RenderPass.Alpha) continue;\r
if (!belongToAlphaPass && pass == RenderPass.Alpha) continue;\r
\r
+ if (pass == RenderPass.Simple)\r
+ {\r
+ mesh.HasSimpleFaces = true;\r
+ }\r
+ else if (pass == RenderPass.Alpha)\r
+ {\r
+ mesh.HasAlphaFaces = true;\r
+ }\r
+\r
if (teFace.Fullbright)\r
{\r
GL.Disable(EnableCap.Lighting);\r
{\r
SortedObjects = new List<SceneObject>();\r
VisibleAvatars = new List<RenderAvatar>();\r
+ if (OcclusionCullingEnabled)\r
+ {\r
+ OccludedObjects = new List<SceneObject>();\r
+ }\r
\r
lock (Prims)\r
{\r
if (LODFactor(obj.DistanceSquared, obj.BasePrim.Scale, obj.BoundingVolume.R) < minLODFactor) continue;\r
\r
obj.Attached = false;\r
- SortedObjects.Add(obj);\r
+ if (OcclusionCullingEnabled && obj.Occluded())\r
+ {\r
+ OccludedObjects.Add(obj);\r
+ }\r
+ else\r
+ {\r
+ SortedObjects.Add(obj);\r
+ }\r
}\r
\r
// Calculate avatar positions and perform interpolation tasks\r
obj.AttachedStateKnown = true;\r
}\r
\r
- SortedObjects.Add(obj);\r
+ if (OcclusionCullingEnabled && obj.Occluded())\r
+ {\r
+ OccludedObjects.Add(obj);\r
+ }\r
+ else\r
+ {\r
+ SortedObjects.Add(obj);\r
+ }\r
}\r
}\r
\r
SortedObjects.Sort();\r
}\r
\r
+ void RenderBoundingBox(SceneObject prim)\r
+ {\r
+ Vector3 scale = prim.BasePrim.Scale;\r
+\r
+ GL.PushMatrix();\r
+ GL.MultMatrix(Math3D.CreateSRTMatrix(scale, prim.RenderRotation, prim.RenderPosition));\r
+\r
+ if (useVBO)\r
+ {\r
+ GL.DrawElements(BeginMode.Quads, RHelp.CubeIndices.Length, DrawElementsType.UnsignedShort, IntPtr.Zero);\r
+ }\r
+ else\r
+ {\r
+ GL.VertexPointer(3, VertexPointerType.Float, 0, RHelp.CubeVertices);\r
+ GL.DrawElements(BeginMode.Quads, RHelp.CubeIndices.Length, DrawElementsType.UnsignedShort, RHelp.CubeIndices);\r
+ }\r
+ GL.PopMatrix();\r
+ }\r
+\r
+ int boundingBoxVBO = -1;\r
+ int boundingBoxVIndexVBO = -1;\r
+\r
+ private void RenderOccludedObjects()\r
+ {\r
+ if (!OcclusionCullingEnabled) return;\r
+\r
+ GL.EnableClientState(ArrayCap.VertexArray);\r
+ GL.ColorMask(false, false, false, false);\r
+ GL.Disable(EnableCap.CullFace);\r
+ GL.Disable(EnableCap.Lighting);\r
+\r
+ if (useVBO)\r
+ {\r
+ if (boundingBoxVBO == -1)\r
+ {\r
+ GL.GenBuffers(1, out boundingBoxVBO);\r
+ GL.BindBuffer(BufferTarget.ArrayBuffer, boundingBoxVBO);\r
+ GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(sizeof(float) * RHelp.CubeVertices.Length), RHelp.CubeVertices, BufferUsageHint.StaticDraw);\r
+ }\r
+ else\r
+ {\r
+ GL.BindBuffer(BufferTarget.ArrayBuffer, boundingBoxVBO);\r
+ }\r
+\r
+ if (boundingBoxVIndexVBO == -1)\r
+ {\r
+ GL.GenBuffers(1, out boundingBoxVIndexVBO);\r
+ GL.BindBuffer(BufferTarget.ElementArrayBuffer, boundingBoxVIndexVBO);\r
+ GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(sizeof(ushort) * RHelp.CubeIndices.Length), RHelp.CubeIndices, BufferUsageHint.StaticDraw);\r
+ }\r
+ else\r
+ {\r
+ GL.BindBuffer(BufferTarget.ElementArrayBuffer, boundingBoxVIndexVBO);\r
+ }\r
+\r
+ GL.VertexPointer(3, VertexPointerType.Float, 0, (IntPtr)0);\r
+ }\r
+\r
+ foreach (SceneObject obj in OccludedObjects)\r
+ {\r
+ if ((!obj.HasAlphaFaces && !obj.HasSimpleFaces)) continue;\r
+ obj.HasSimpleFaces = true;\r
+ obj.HasAlphaFaces = false;\r
+ obj.StartSimpleQuery();\r
+ RenderBoundingBox(obj);\r
+ obj.EndSimpleQuery();\r
+ }\r
+\r
+ if (useVBO)\r
+ {\r
+ GL.BindBuffer(BufferTarget.ArrayBuffer, 0);\r
+ GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);\r
+ }\r
+\r
+ GL.ColorMask(true, true, true, true);\r
+ GL.DisableClientState(ArrayCap.VertexArray);\r
+ GL.Enable(EnableCap.CullFace);\r
+ GL.Enable(EnableCap.Lighting);\r
+ }\r
+\r
private void RenderObjects(RenderPass pass)\r
{\r
if (!PrimitiveRenderingEnabled) return;\r
{\r
// Don't render objects that are outside the draw distane\r
if (Vector3.DistanceSquared(myPos, obj.RenderPosition) > drawDistanceSquared) continue;\r
-\r
+ obj.StartQuery(pass);\r
RenderPrim((RenderPrimitive)obj, pass, ix);\r
+ obj.EndQuery(pass);\r
}\r
}\r
\r
RenderAvatars(RenderPass.Simple);\r
GL.Disable(EnableCap.AlphaTest);\r
\r
+ GL.DepthMask(false);\r
+ RenderOccludedObjects();\r
+\r
// Alpha blending elements, disable writing to depth buffer\r
GL.Enable(EnableCap.Blend);\r
- GL.DepthMask(false);\r
RenderWater();\r
RenderObjects(RenderPass.Alpha);\r
GL.DepthMask(true);\r
TexturesPtrMap[item.TeFace.TextureID] = item.Data.TextureInfo;\r
if (item.TextureData == null && item.TGAData == null)\r
{\r
- if (useDecodedImageCache && RHelp.LoadCachedImage(item.TeFace.TextureID, out item.TGAData, out item.Data.TextureInfo.HasAlpha, out item.Data.TextureInfo.FullAlpha, out item.Data.TextureInfo.IsMask))\r
+ if (CacheDecodedTextures && RHelp.LoadCachedImage(item.TeFace.TextureID, out item.TGAData, out item.Data.TextureInfo.HasAlpha, out item.Data.TextureInfo.FullAlpha, out item.Data.TextureInfo.IsMask))\r
{\r
PendingTextures.Enqueue(item);\r
}\r
gotImage.Reset();\r
bool hasAlpha, fullAlpha, isMask;\r
byte[] tgaData;\r
- if (useDecodedImageCache && RHelp.LoadCachedImage(textureID, out tgaData, out hasAlpha, out fullAlpha, out isMask))\r
+ if (RHelp.LoadCachedImage(textureID, out tgaData, out hasAlpha, out fullAlpha, out isMask))\r
{\r
img = LoadTGAClass.LoadTGA(new MemoryStream(tgaData));\r
}\r
}\r
tgaData = mi.ExportTGA();\r
img = LoadTGAClass.LoadTGA(new MemoryStream(tgaData));\r
- if (useDecodedImageCache)\r
- {\r
- RHelp.SaveCachedImage(tgaData, textureID, (mi.Channels & ManagedImage.ImageChannels.Alpha) != 0, false, false);\r
- }\r
+ RHelp.SaveCachedImage(tgaData, textureID, (mi.Channels & ManagedImage.ImageChannels.Alpha) != 0, false, false);\r
}\r
gotImage.Set();\r
}\r
private void cbMisc_CheckedChanged(object sender, EventArgs e)\r
{\r
miscEnabled = cbMisc.Checked;\r
- AvatarRenderingEnabled = miscEnabled;\r
+ OcclusionCullingEnabled = miscEnabled;\r
}\r
}\r
}\r
public virtual Primitive BasePrim { get; set; }\r
/// <summary>Were initial initialization tasks done</summary>\r
public bool Initialized;\r
+ public int AlphaQueryID = -1;\r
+ public int SimpleQueryID = -1;\r
+ public bool HasAlphaFaces;\r
+ public bool HasSimpleFaces;\r
+\r
#endregion Public fields\r
\r
/// <summary>\r
else\r
return 0;\r
}\r
+\r
+ public void StartQuery(RenderPass pass)\r
+ {\r
+ if (pass == RenderPass.Simple)\r
+ {\r
+ StartSimpleQuery();\r
+ }\r
+ else if (pass == RenderPass.Alpha)\r
+ {\r
+ StartAlphaQuery();\r
+ }\r
+ }\r
+\r
+ public void EndQuery(RenderPass pass)\r
+ {\r
+ if (pass == RenderPass.Simple)\r
+ {\r
+ EndSimpleQuery();\r
+ }\r
+ else if (pass == RenderPass.Alpha)\r
+ {\r
+ EndAlphaQuery();\r
+ }\r
+ }\r
+\r
+ public void StartAlphaQuery()\r
+ {\r
+ if (AlphaQueryID == -1)\r
+ {\r
+ GL.GenQueries(1, out AlphaQueryID);\r
+ }\r
+ if (AlphaQueryID > 0)\r
+ {\r
+ GL.BeginQuery(QueryTarget.SamplesPassed, AlphaQueryID);\r
+ }\r
+ }\r
+\r
+ public void EndAlphaQuery()\r
+ {\r
+ if (AlphaQueryID > 0)\r
+ {\r
+ GL.EndQuery(QueryTarget.SamplesPassed);\r
+ }\r
+ }\r
+\r
+ public void StartSimpleQuery()\r
+ {\r
+ if (SimpleQueryID == -1)\r
+ {\r
+ GL.GenQueries(1, out SimpleQueryID);\r
+ }\r
+ if (SimpleQueryID > 0)\r
+ {\r
+ GL.BeginQuery(QueryTarget.SamplesPassed, SimpleQueryID);\r
+ }\r
+ }\r
+\r
+ public void EndSimpleQuery()\r
+ {\r
+ if (SimpleQueryID > 0)\r
+ {\r
+ GL.EndQuery(QueryTarget.SamplesPassed);\r
+ }\r
+ }\r
+\r
+ public bool Occluded()\r
+ {\r
+ if ((SimpleQueryID == -1 && AlphaQueryID == -1))\r
+ {\r
+ return false;\r
+ }\r
+\r
+ if ((!HasAlphaFaces && !HasSimpleFaces)) return true;\r
+\r
+ int samples = 1;\r
+ if (HasSimpleFaces && SimpleQueryID > 0)\r
+ {\r
+ GL.GetQueryObject(SimpleQueryID, GetQueryObjectParam.QueryResult, out samples);\r
+ }\r
+ if (HasSimpleFaces && samples > 0)\r
+ {\r
+ return false;\r
+ }\r
+\r
+ samples = 1;\r
+ if (HasAlphaFaces && AlphaQueryID > 0)\r
+ {\r
+ GL.GetQueryObject(AlphaQueryID, GetQueryObjectParam.QueryResult, out samples);\r
+ }\r
+ if (HasAlphaFaces && samples > 0)\r
+ {\r
+ return false;\r
+ }\r
+\r
+ return true;\r
+ }\r
}\r
\r
public class RenderPrimitive : SceneObject\r
}\r
}\r
#endregion Cached image save and load\r
+\r
+ #region Static vertices and indices for a cube (used for bounding box drawing)\r
+ /**********************************************\r
+ 5 --- 4\r
+ /| /|\r
+ 1 --- 0 |\r
+ | 6 --| 7\r
+ |/ |/\r
+ 2 --- 3\r
+ ***********************************************/\r
+ public static readonly float[] CubeVertices = new float[]\r
+ {\r
+ 0.5f, 0.5f, 0.5f, // 0\r
+ -0.5f, 0.5f, 0.5f, // 1\r
+ -0.5f, -0.5f, 0.5f, // 2\r
+ 0.5f, -0.5f, 0.5f, // 3\r
+ 0.5f, 0.5f, -0.5f, // 4\r
+ -0.5f, 0.5f, -0.5f, // 5\r
+ -0.5f, -0.5f, -0.5f, // 6\r
+ 0.5f, -0.5f, -0.5f // 7\r
+ };\r
+\r
+ public static readonly ushort[] CubeIndices = new ushort[]\r
+ {\r
+ 0, 1, 2, 3, // Front Face\r
+ 4, 5, 6, 7, // Back Face\r
+ 1, 2, 6, 5, // Left Face\r
+ 0, 3, 7, 4, // Right Face\r
+ 0, 1, 5, 4, // Top Face\r
+ 2, 3, 7, 6 // Bottom Face\r
+ };\r
+ #endregion Static vertices and indices for a cube (used for bounding box drawing)\r
}\r
\r
/// <summary>\r
Array.Copy(source.RenderData.Indices, RenderData.Indices, source.RenderData.Indices.Length);\r
Array.Copy(source.RenderData.weights, RenderData.weights, source.RenderData.weights.Length);\r
Array.Copy(source.RenderData.skinJoints, RenderData.skinJoints, source.RenderData.skinJoints.Length);\r
- \r
+\r
RenderData.Center = new Vector3(source.RenderData.Center);\r
\r
teFaceID = source.teFaceID;\r
}\r
else\r
{\r
- \r
+\r
ba = av.skel.mBones[jointname];\r
}\r
\r
if (bb != null)\r
{\r
lerp = Vector3.Lerp(ba.getDeltaOffset(), bb.getDeltaOffset(), weight);\r
- offset = Vector3.Lerp(ba.getTotalOffset(), bb.getTotalOffset(), weight); \r
+ offset = Vector3.Lerp(ba.getTotalOffset(), bb.getTotalOffset(), weight);\r
}\r
else\r
{\r
}\r
\r
Vector3 pos = new Vector3(MorphRenderData.Vertices[v], MorphRenderData.Vertices[v + 1], MorphRenderData.Vertices[v + 2]);\r
- \r
+\r
//move back to mesh local coords\r
pos = pos - offset;\r
// apply LERPd offset\r
\r
public void resetallmorphs()\r
{\r
- for (int i = 0; i < OrigRenderData.Vertices.Length/3; i++)\r
+ for (int i = 0; i < OrigRenderData.Vertices.Length / 3; i++)\r
{\r
- \r
+\r
MorphRenderData.Vertices[i * 3] = OrigRenderData.Vertices[i * 3];\r
MorphRenderData.Vertices[(i * 3) + 1] = OrigRenderData.Vertices[i * 3 + 1];\r
MorphRenderData.Vertices[(i * 3) + 2] = OrigRenderData.Vertices[i * 3 + 2];\r
//MorphRenderData.Normals[(i * 3) + 1] = OrigRenderData.Normals[i * 3 + 1];\r
//MorphRenderData.Normals[(i * 3) + 2] = OrigRenderData.Normals[i * 3 + 2];\r
\r
- RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2] ;\r
+ RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2];\r
RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1];\r
\r
}\r
this.morphtest(av, vpe.ParamID, value);\r
\r
x++;\r
- // if (x > 100)\r
+ // if (x > 100)\r
// break;\r
}\r
\r
{\r
//actually get the last for a pose the last frame is probably the static one once\r
//we have reached position\r
- binBVHJointKey rot = joint.rotationkeys[joint.rotationkeys.Length-1];\r
+ binBVHJointKey rot = joint.rotationkeys[joint.rotationkeys.Length - 1];\r
\r
Vector3 pos = new Vector3(0, 0, 0);\r
//binBVHJointKey pos = joint.positionkeys[0];\r
\r
private bool rotdirty = true;\r
private bool posdirty = true;\r
- \r
+\r
private Vector3 mTotalPos;\r
private Quaternion mTotalRot;\r
\r
{\r
b.mParentBone = parent.name;\r
parent.children.Add(b);\r
- } \r
+ }\r
\r
mBones.Add(b.name, b);\r
mIndexedBones.Add(boneaddindex++, b);\r
this.scale = Bone.mBones[name].orig_scale + scale;\r
this.rot = Bone.mBones[name].orig_rot * rot;\r
\r
- markdirty(); \r
+ markdirty();\r
}\r
\r
// If we deform a bone mark this bone and all its children as dirty. \r
return mTotalPos;\r
}\r
else\r
- { \r
+ {\r
return getOffset();\r
}\r
}\r
}\r
else\r
{\r
- getOffset(); \r
+ getOffset();\r
return mDeltaPos;\r
}\r
}\r