Exemple #1
0
 /// <summary>
 /// Remove any GL resource we may still have in use
 /// </summary>
 public override void Dispose()
 {
     if (Faces != null)
     {
         foreach (Face f in Faces)
         {
             if (f.UserData != null && f.UserData is FaceData)
             {
                 FaceData data = (FaceData)f.UserData;
                 data.Dispose();
                 data = null;
             }
         }
         Faces = null;
     }
     base.Dispose();
 }
Exemple #2
0
        private void CalculateBoundingBox(RenderPrimitive rprim)
        {
            Primitive prim = rprim.BasePrim;

            // Calculate bounding volumes for each prim and adjust textures
            rprim.BoundingVolume = new BoundingVolume();
            for (int j = 0; j < rprim.Faces.Count; j++)
            {
                Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);
                if (teFace == null) continue;

                Face face = rprim.Faces[j];
                FaceData data = new FaceData();

                data.BoundingVolume.CreateBoundingVolume(face, prim.Scale);
                rprim.BoundingVolume.AddVolume(data.BoundingVolume, prim.Scale);

                // With linear texture animation in effect, texture repeats and offset are ignored
                if ((prim.TextureAnim.Flags & Primitive.TextureAnimMode.ANIM_ON) != 0
                    && (prim.TextureAnim.Flags & Primitive.TextureAnimMode.ROTATE) == 0
                    && (prim.TextureAnim.Face == 255 || prim.TextureAnim.Face == j))
                {
                    teFace.RepeatU = 1;
                    teFace.RepeatV = 1;
                    teFace.OffsetU = 0;
                    teFace.OffsetV = 0;
                }

                // Need to adjust UV for spheres as they are sort of half-prim
                if (prim.PrimData.ProfileCurve == ProfileCurve.HalfCircle)
                {
                    teFace = (Primitive.TextureEntryFace)teFace.Clone();
                    teFace.RepeatV *= 2;
                    teFace.OffsetV += 0.5f;
                }

                // Sculpt UV vertically flipped compared to prims. Flip back
                if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero && prim.Sculpt.Type != SculptType.Mesh)
                {
                    teFace = (Primitive.TextureEntryFace)teFace.Clone();
                    teFace.RepeatV *= -1;
                }

                // Texture transform for this face
                renderer.TransformTexCoords(face.Vertices, face.Center, teFace, prim.Scale);

                // Set the UserData for this face to our FaceData struct
                face.UserData = data;
                rprim.Faces[j] = face;
            }
        }
Exemple #3
0
        private void updateAVtes(RenderAvatar ra)
        {
            if (ra.avatar.Textures == null)
                return;

            int[] tes = { 8, 9, 10, 11, 19, 20 };

            foreach (int fi in tes)
            {
                Primitive.TextureEntryFace TEF = ra.avatar.Textures.FaceTextures[fi];
                if (TEF == null)
                    continue;

                if (ra.data[fi] == null || ra.data[fi].TextureInfo.TextureID != TEF.TextureID || ra.data[fi].TextureInfo.TexturePointer < 1 )
                {
                    FaceData data = new FaceData();
                    ra.data[fi] = data;
                    data.TextureInfo.TextureID = TEF.TextureID;

                    DownloadTexture(new TextureLoadItem()
                    {
                        Data = data,
                        Prim = ra.avatar,
                        TeFace = ra.avatar.Textures.FaceTextures[fi]
                    }, true);
                }
            }
        }
Exemple #4
0
        private void updateAVtes(RenderAvatar ra)
        {
            if (ra.avatar.Textures == null)
                return;

            foreach (int fi in RenderAvatar.BakedTextures.Keys)
            {
                Primitive.TextureEntryFace TEF = ra.avatar.Textures.FaceTextures[fi];
                if (TEF == null)
                    continue;

                if (ra.data[fi] == null || ra.data[fi].TextureInfo.TextureID != TEF.TextureID || ra.data[fi].TextureInfo.TexturePointer < 1)
                {
                    FaceData data = new FaceData();
                    ra.data[fi] = data;
                    data.TextureInfo.TextureID = TEF.TextureID;

                    ImageType type = ImageType.Baked;
                    if (ra.avatar.COFVersion > 0) // This avatar was server baked
                    {
                        type = ImageType.ServerBaked;
                    }

                    DownloadTexture(new TextureLoadItem()
                    {
                        Data = data,
                        Prim = ra.avatar,
                        TeFace = ra.avatar.Textures.FaceTextures[fi],
                        ImageType = type,
                        BakeName = RenderAvatar.BakedTextures[fi],
                        AvatarID = ra.avatar.ID
                    }, true);
                }
            }
        }
Exemple #5
0
        private void UpdatePrimBlocking(Primitive prim)
        {
            FacetedMesh mesh = null;
            FacetedMesh existingMesh = null;

            lock (Prims)
            {
                if (Prims.ContainsKey(prim.LocalID))
                {
                    existingMesh = Prims[prim.LocalID];
                }
            }

            if (prim.Textures == null)
                return;

            try
            {
                if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero)
                {
                    if (prim.Sculpt.Type != SculptType.Mesh)
                    { // Regular sculptie
                        Image img = null;
                        if (!LoadTexture(prim.Sculpt.SculptTexture, ref img, true))
                            return;
                        mesh = renderer.GenerateFacetedSculptMesh(prim, (Bitmap)img, DetailLevel.Highest);
                    }
                    else
                    { // Mesh
                        AutoResetEvent gotMesh = new AutoResetEvent(false);
                        bool meshSuccess = false;

                        Client.Assets.RequestMesh(prim.Sculpt.SculptTexture, (success, meshAsset) =>
                            {
                                if (!success || !FacetedMesh.TryDecodeFromAsset(prim, meshAsset, DetailLevel.Highest, out mesh))
                                {
                                    Logger.Log("Failed to fetch or decode the mesh asset", Helpers.LogLevel.Warning, Client);
                                }
                                else
                                {
                                    meshSuccess = true;
                                }
                                gotMesh.Set();
                            });

                        if (!gotMesh.WaitOne(20 * 1000, false)) return;
                        if (!meshSuccess) return;
                    }
                }
                else
                {
                    mesh = renderer.GenerateFacetedMesh(prim, DetailLevel.Highest);
                }
            }
            catch
            {
                return;
            }

            // Create a FaceData struct for each face that stores the 3D data
            // in a OpenGL friendly format
            for (int j = 0; j < mesh.Faces.Count; j++)
            {
                Face face = mesh.Faces[j];
                FaceData data = new FaceData();

                // Vertices for this face
                data.Vertices = new float[face.Vertices.Count * 3];
                data.Normals = new float[face.Vertices.Count * 3];
                for (int k = 0; k < face.Vertices.Count; k++)
                {
                    data.Vertices[k * 3 + 0] = face.Vertices[k].Position.X;
                    data.Vertices[k * 3 + 1] = face.Vertices[k].Position.Y;
                    data.Vertices[k * 3 + 2] = face.Vertices[k].Position.Z;

                    data.Normals[k * 3 + 0] = face.Vertices[k].Normal.X;
                    data.Normals[k * 3 + 1] = face.Vertices[k].Normal.Y;
                    data.Normals[k * 3 + 2] = face.Vertices[k].Normal.Z;
                }

                // Indices for this face
                data.Indices = face.Indices.ToArray();

                // Texture transform for this face
                Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);
                renderer.TransformTexCoords(face.Vertices, face.Center, teFace, prim.Scale);

                // Texcoords for this face
                data.TexCoords = new float[face.Vertices.Count * 2];
                for (int k = 0; k < face.Vertices.Count; k++)
                {
                    data.TexCoords[k * 2 + 0] = face.Vertices[k].TexCoord.X;
                    data.TexCoords[k * 2 + 1] = face.Vertices[k].TexCoord.Y;
                }

                // Set the UserData for this face to our FaceData struct
                face.UserData = data;
                mesh.Faces[j] = face;

                if (existingMesh != null &&
                    j < existingMesh.Faces.Count &&
                    existingMesh.Faces[j].TextureFace.TextureID == teFace.TextureID &&
                    ((FaceData)existingMesh.Faces[j].UserData).TextureInfo.TexturePointer != 0
                    )
                {
                    FaceData existingData = (FaceData)existingMesh.Faces[j].UserData;
                    data.TextureInfo.TexturePointer = existingData.TextureInfo.TexturePointer;
                }
                else
                {

                    var textureItem = new TextureLoadItem()
                    {
                        Data = data,
                        Prim = prim,
                        TeFace = teFace
                    };

                    PendingTextures.Enqueue(textureItem);
                }

            }

            lock (Prims)
            {
                Prims[prim.LocalID] = mesh;
            }
            SafeInvalidate();
        }
Exemple #6
0
        private void UpdatePrimBlocking(Primitive prim)
        {
            FacetedMesh mesh         = null;
            FacetedMesh existingMesh = null;

            lock (Prims)
            {
                if (Prims.ContainsKey(prim.LocalID))
                {
                    existingMesh = Prims[prim.LocalID];
                }
            }

            if (prim.Textures == null)
            {
                return;
            }

            try
            {
                if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero)
                {
                    if (prim.Sculpt.Type != SculptType.Mesh)
                    { // Regular sculptie
                        Image img = null;
                        if (!LoadTexture(prim.Sculpt.SculptTexture, ref img, true))
                        {
                            return;
                        }
                        mesh = renderer.GenerateFacetedSculptMesh(prim, (Bitmap)img, DetailLevel.Highest);
                    }
                    else
                    { // Mesh
                        AutoResetEvent gotMesh     = new AutoResetEvent(false);
                        bool           meshSuccess = false;

                        Client.Assets.RequestMesh(prim.Sculpt.SculptTexture, (success, meshAsset) =>
                        {
                            if (!success || !FacetedMesh.TryDecodeFromAsset(prim, meshAsset, DetailLevel.Highest, out mesh))
                            {
                                Logger.Log("Failed to fetch or decode the mesh asset", Helpers.LogLevel.Warning, Client);
                            }
                            else
                            {
                                meshSuccess = true;
                            }
                            gotMesh.Set();
                        });

                        if (!gotMesh.WaitOne(20 * 1000, false))
                        {
                            return;
                        }
                        if (!meshSuccess)
                        {
                            return;
                        }
                    }
                }
                else
                {
                    mesh = renderer.GenerateFacetedMesh(prim, DetailLevel.Highest);
                }
            }
            catch
            {
                return;
            }

            // Create a FaceData struct for each face that stores the 3D data
            // in a OpenGL friendly format
            for (int j = 0; j < mesh.Faces.Count; j++)
            {
                Face     face = mesh.Faces[j];
                FaceData data = new FaceData
                {
                    Vertices = new float[face.Vertices.Count * 3],
                    Normals  = new float[face.Vertices.Count * 3]
                };

                // Vertices for this face
                for (int k = 0; k < face.Vertices.Count; k++)
                {
                    data.Vertices[k * 3 + 0] = face.Vertices[k].Position.X;
                    data.Vertices[k * 3 + 1] = face.Vertices[k].Position.Y;
                    data.Vertices[k * 3 + 2] = face.Vertices[k].Position.Z;

                    data.Normals[k * 3 + 0] = face.Vertices[k].Normal.X;
                    data.Normals[k * 3 + 1] = face.Vertices[k].Normal.Y;
                    data.Normals[k * 3 + 2] = face.Vertices[k].Normal.Z;
                }

                // Indices for this face
                data.Indices = face.Indices.ToArray();

                // Texture transform for this face
                Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);
                renderer.TransformTexCoords(face.Vertices, face.Center, teFace, prim.Scale);

                // Texcoords for this face
                data.TexCoords = new float[face.Vertices.Count * 2];
                for (int k = 0; k < face.Vertices.Count; k++)
                {
                    data.TexCoords[k * 2 + 0] = face.Vertices[k].TexCoord.X;
                    data.TexCoords[k * 2 + 1] = face.Vertices[k].TexCoord.Y;
                }

                // Set the UserData for this face to our FaceData struct
                face.UserData = data;
                mesh.Faces[j] = face;


                if (existingMesh != null &&
                    j < existingMesh.Faces.Count &&
                    existingMesh.Faces[j].TextureFace.TextureID == teFace.TextureID &&
                    ((FaceData)existingMesh.Faces[j].UserData).TextureInfo.TexturePointer != 0
                    )
                {
                    FaceData existingData = (FaceData)existingMesh.Faces[j].UserData;
                    data.TextureInfo.TexturePointer = existingData.TextureInfo.TexturePointer;
                }
                else
                {
                    var textureItem = new TextureLoadItem()
                    {
                        Data   = data,
                        Prim   = prim,
                        TeFace = teFace
                    };

                    PendingTextures.Enqueue(textureItem);
                }
            }

            lock (Prims)
            {
                Prims[prim.LocalID] = mesh;
            }
            SafeInvalidate();
        }
Exemple #7
0
        private void RenderObjects(RenderPass pass)
        {
            lock (Prims)
            {
                int primNr = 0;
                foreach (FacetedMesh mesh in Prims.Values)
                {
                    primNr++;
                    Primitive prim = mesh.Prim;
                    // Individual prim matrix
                    GL.PushMatrix();

                    if (prim.ParentID == RootPrimLocalID)
                    {
                        FacetedMesh parent = null;
                        if (Prims.TryGetValue(prim.ParentID, out parent))
                        {
                            // Apply prim translation and rotation relative to the root prim
                            GL.MultMatrix(Math3D.CreateRotationMatrix(parent.Prim.Rotation));
                            //GL.MultMatrixf(Math3D.CreateTranslationMatrix(parent.Prim.Position));
                        }

                        // Prim roation relative to root
                        GL.MultMatrix(Math3D.CreateTranslationMatrix(prim.Position));
                    }

                    // Prim roation
                    GL.MultMatrix(Math3D.CreateRotationMatrix(prim.Rotation));

                    // Prim scaling
                    GL.Scale(prim.Scale.X, prim.Scale.Y, prim.Scale.Z);

                    // Draw the prim faces
                    for (int j = 0; j < mesh.Faces.Count; j++)
                    {
                        Primitive.TextureEntryFace teFace = mesh.Prim.Textures.FaceTextures[j];
                        Face     face = mesh.Faces[j];
                        FaceData data = (FaceData)face.UserData;

                        if (teFace == null)
                        {
                            teFace = mesh.Prim.Textures.DefaultTexture;
                        }

                        if (pass == RenderPass.Picking)
                        {
                            data.PickingID = primNr;
                            var primNrBytes = Utils.Int16ToBytes((short)primNr);
                            var faceColor   = new byte[] { primNrBytes[0], primNrBytes[1], (byte)j, 255 };

                            GL.Color4(faceColor);
                        }
                        else
                        {
                            bool belongToAlphaPass = (teFace.RGBA.A < 0.99) || data.TextureInfo.HasAlpha;

                            if (belongToAlphaPass && pass != RenderPass.Alpha)
                            {
                                continue;
                            }
                            if (!belongToAlphaPass && pass == RenderPass.Alpha)
                            {
                                continue;
                            }

                            // Don't render transparent faces
                            if (teFace.RGBA.A <= 0.01f)
                            {
                                continue;
                            }

                            switch (teFace.Shiny)
                            {
                            case Shininess.High:
                                GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 94f);
                                break;

                            case Shininess.Medium:
                                GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 64f);
                                break;

                            case Shininess.Low:
                                GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 24f);
                                break;

                            case Shininess.None:
                            default:
                                GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0f);
                                break;
                            }

                            var faceColor = new float[] { teFace.RGBA.R, teFace.RGBA.G, teFace.RGBA.B, teFace.RGBA.A };

                            GL.Color4(faceColor);
                            GL.Material(MaterialFace.Front, MaterialParameter.AmbientAndDiffuse, faceColor);
                            GL.Material(MaterialFace.Front, MaterialParameter.Specular, faceColor);

                            if (data.TextureInfo.TexturePointer != 0)
                            {
                                GL.Enable(EnableCap.Texture2D);
                            }
                            else
                            {
                                GL.Disable(EnableCap.Texture2D);
                            }

                            // Bind the texture
                            GL.BindTexture(TextureTarget.Texture2D, data.TextureInfo.TexturePointer);
                        }

                        GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, data.TexCoords);
                        GL.VertexPointer(3, VertexPointerType.Float, 0, data.Vertices);
                        GL.NormalPointer(NormalPointerType.Float, 0, data.Normals);
                        GL.DrawElements(PrimitiveType.Triangles, data.Indices.Length, DrawElementsType.UnsignedShort, data.Indices);
                    }

                    // Pop the prim matrix
                    GL.PopMatrix();
                }
            }
        }
        /// <summary>
        /// Render Primitive
        /// </summary>
        /// <param name="pass">Which pass are we currently in</param>
        /// <param name="pickingID">ID used to identify which object was picked</param>
        /// <param name="scene">Main scene renderer</param>
        /// <param name="time">Time it took to render the last frame</param>
        public override void Render(RenderPass pass, int pickingID, SceneWindow scene, float time)
        {
            //if (!RenderSettings.AvatarRenderingEnabled && Attached) return;//hack

            // Individual prim matrix
            GL.PushMatrix();

            // Prim roation and position and scale
            GL.MultMatrix(Math3D.CreateSRTMatrix(Prim.Scale, RenderRotation, RenderPosition));

            // Do we have animated texture on this face
            bool animatedTexture = false;

            // Initialise flags tracking what type of faces this prim has
            if (pass == RenderPass.Simple)
            {
                HasSimpleFaces = false;
            }
            else if (pass == RenderPass.Alpha)
            {
                HasAlphaFaces = false;
            }
            else if (pass == RenderPass.Invisible)
            {
                HasInvisibleFaces = false;
            }

            // Draw the prim faces
            for (int j = 0; j < Faces.Count; j++)
            {
                Primitive.TextureEntryFace teFace = Prim.Textures.GetFace((uint)j);
                Face     face = Faces[j];
                FaceData data = (FaceData)face.UserData;

                if (data == null)
                {
                    continue;
                }

                if (teFace == null)
                {
                    continue;
                }

                // Don't render transparent faces
                Color4 RGBA = teFace.RGBA;

                if (data.TextureInfo.FullAlpha || RGBA.A <= 0.01f)
                {
                    continue;
                }

                bool switchedLightsOff = false;

                if (pass == RenderPass.Picking)
                {
                    data.PickingID = pickingID;
                    var primNrBytes = Utils.UInt16ToBytes((ushort)pickingID);
                    var faceColor   = new byte[] { primNrBytes[0], primNrBytes[1], (byte)j, 255 };
                    GL.Color4(faceColor);
                }
                else if (pass == RenderPass.Invisible)
                {
                    if (!data.TextureInfo.IsInvisible)
                    {
                        continue;
                    }
                    HasInvisibleFaces = true;
                }
                else
                {
                    if (data.TextureInfo.IsInvisible)
                    {
                        continue;
                    }
                    bool belongToAlphaPass = (RGBA.A < 0.99f) || (data.TextureInfo.HasAlpha && !data.TextureInfo.IsMask);

                    if (belongToAlphaPass && pass != RenderPass.Alpha)
                    {
                        continue;
                    }
                    if (!belongToAlphaPass && pass == RenderPass.Alpha)
                    {
                        continue;
                    }

                    if (pass == RenderPass.Simple)
                    {
                        HasSimpleFaces = true;
                    }
                    else if (pass == RenderPass.Alpha)
                    {
                        HasAlphaFaces = true;
                    }

                    if (teFace.Fullbright)
                    {
                        GL.Disable(EnableCap.Lighting);
                        switchedLightsOff = true;
                    }

                    float shiny = 0f;
                    switch (teFace.Shiny)
                    {
                    case Shininess.High:
                        shiny = 0.96f;
                        break;

                    case Shininess.Medium:
                        shiny = 0.64f;
                        break;

                    case Shininess.Low:
                        shiny = 0.24f;
                        break;
                    }

                    if (shiny > 0f)
                    {
                        scene.StartShiny();
                    }
                    GL.Material(MaterialFace.Front, MaterialParameter.Shininess, shiny);
                    var faceColor = new float[] { RGBA.R, RGBA.G, RGBA.B, RGBA.A };
                    GL.Color4(faceColor);

                    GL.Material(MaterialFace.Front, MaterialParameter.Specular, new float[] { 0.5f, 0.5f, 0.5f, 1f });

                    if (data.TextureInfo.TexturePointer == 0)
                    {
                        TextureInfo teInfo;
                        if (scene.TryGetTextureInfo(teFace.TextureID, out teInfo))
                        {
                            data.TextureInfo = teInfo;
                        }
                    }

                    if (data.TextureInfo.TexturePointer == 0)
                    {
                        GL.Disable(EnableCap.Texture2D);
                        if (!data.TextureInfo.FetchFailed)
                        {
                            scene.DownloadTexture(new TextureLoadItem()
                            {
                                Prim   = this.Prim,
                                TeFace = teFace,
                                Data   = data
                            }, false);
                        }
                    }
                    else
                    {
                        // Is this face using texture animation
                        if ((Prim.TextureAnim.Flags & Primitive.TextureAnimMode.ANIM_ON) != 0 &&
                            (Prim.TextureAnim.Face == j || Prim.TextureAnim.Face == 255))
                        {
                            if (data.AnimInfo == null)
                            {
                                data.AnimInfo = new TextureAnimationInfo();
                            }
                            data.AnimInfo.PrimAnimInfo = Prim.TextureAnim;
                            data.AnimInfo.Step(time);
                            animatedTexture = true;
                        }
                        else if (data.AnimInfo != null) // Face texture not animated. Do we have previous anim setting?
                        {
                            data.AnimInfo = null;
                        }

                        GL.Enable(EnableCap.Texture2D);
                        GL.BindTexture(TextureTarget.Texture2D, data.TextureInfo.TexturePointer);
                    }
                }
                //hack

                /*
                 * if (!RenderSettings.UseVBO || data.VBOFailed)
                 * {
                 * Vertex[] verts = face.Vertices.ToArray();
                 * ushort[] indices = face.Indices.ToArray();
                 *
                 * unsafe
                 * {
                 * fixed (float* normalPtr = &verts[0].Normal.X)
                 * fixed (float* texPtr = &verts[0].TexCoord.X)
                 * {
                 * GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)normalPtr);
                 * GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)texPtr);
                 * GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, verts);
                 * GL.DrawElements(BeginMode.Triangles, indices.Length, DrawElementsType.UnsignedShort, indices);
                 * }
                 * }
                 * }
                 * else
                 * {
                 * if (data.CheckVBO(face))
                 * {
                 * Compat.BindBuffer(BufferTarget.ArrayBuffer, data.VertexVBO);
                 * Compat.BindBuffer(BufferTarget.ElementArrayBuffer, data.IndexVBO);
                 * GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)12);
                 * GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)(24));
                 * GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, (IntPtr)(0));
                 *
                 * GL.DrawElements(BeginMode.Triangles, face.Indices.Count, DrawElementsType.UnsignedShort, IntPtr.Zero);
                 * }
                 * Compat.BindBuffer(BufferTarget.ArrayBuffer, 0);
                 * Compat.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
                 *
                 * }
                 */
                if (switchedLightsOff)
                {
                    GL.Enable(EnableCap.Lighting);
                    switchedLightsOff = false;
                }
            }

            GL.BindTexture(TextureTarget.Texture2D, 0);
            RHelp.ResetMaterial();

            // Reset texture coordinates if we modified them in texture animation
            if (animatedTexture)
            {
                GL.MatrixMode(MatrixMode.Texture);
                GL.LoadIdentity();
                GL.MatrixMode(MatrixMode.Modelview);
            }

            // Pop the prim matrix
            GL.PopMatrix();

            base.Render(pass, pickingID, scene, time);
        }