/// <summary> /// IRenderer interface method /// Renders a model in a viewport /// </summary> public bool RenderModel(RMModel model, Rhino.DocObjects.ViewportInfo viewport) { if (null == model || null == viewport || !model.IsReadyForRendering) { return(false); } Model = model; CheckGLError("intro call"); //HACK: necessary on some Android GPUs (don't delete this check) viewport.SetFrustumNearFar(model.BBox); // Disable Blending and set the depth function GL.DepthFunc(DepthFunction.Gequal); GL.Disable(EnableCap.Blend); // Get the shader... ActiveShader = FastDrawing ? GetShader("PerVertexLighting") : GetShader("PerPixelLighting"); // Render calls... if (model != null) { // First, render all opaque objects that are not instances foreach (var obj in model.DisplayMeshes) { if (obj != null) { RenderObject(obj, viewport, false); } } // Second, render all opaque objects that are instances foreach (var obj in model.DisplayInstanceMeshes) { if (obj != null) { RenderObject(obj, viewport, true); } } // Third, we're done drawing our instances, so set the ModelViewMatrix's XForm back to the identity matrix. ActiveShader.SetModelViewMatrix(Rhino.Geometry.Transform.Identity); // Fourth, render all transparent meshes... RenderTransparentObjects(model); } // Disable the shader ActiveShader.Disable(); return(true); }
/// <summary> /// For derived class implementers. /// <para>This method is called with argument true when class user calls Dispose(), while with argument false when /// the Garbage Collector invokes the finalizer, or Finalize() method.</para> /// <para>You must reclaim all used unmanaged resources in both cases, and can use this chance to call Dispose on disposable fields if the argument is true.</para> /// <para>Also, you must call the base virtual method within your overriding method.</para> /// </summary> /// <param name="disposing">true if the call comes from the Dispose() method; false if it comes from the Garbage Collector finalizer.</param> protected virtual void Dispose(bool disposing) { // Free unmanaged resources... // Free managed resources...but only if called from Dispose // (If called from Finalize then the objects might not exist anymore) if (disposing) { if (Model != null) { Model.Dispose(); Model = null; } Frame = System.Drawing.RectangleF.Empty; if (Viewport != null) { Viewport.Dispose(); Viewport = null; } CurrentMaterial = null; if (ActiveShader != null) { GL.DeleteProgram(ActiveShader.Handle); ActiveShader.Disable(); m_activeShader = null; } if (Shaders != null) { Shaders.Clear(); Shaders = null; } } }
/// <summary> /// Renders the object in a viewport /// </summary> private void RenderObject(DisplayObject obj, Rhino.DocObjects.ViewportInfo viewport, bool isInstance) { // If the object is invisible, return. if (!obj.IsVisible) { return; } // If the layer that the object is on is turned off, return. if (!Model.LayerIsVisibleAtIndex(obj.LayerIndex)) { return; } DisplayMesh displayMesh = isInstance ? ((DisplayInstanceMesh)obj).Mesh : (DisplayMesh)obj; if (displayMesh.WillFitOnGPU == false) { return; } uint vertex_buffer = 0; uint index_buffer = 0; if (displayMesh != null) { // Material setup, if necessary... displayMesh.Material.AmbientColor = new [] { 0.0f, 0.0f, 0.0f, 1.0f }; //We want to ignore the ambient color if (CurrentMaterial.RuntimeId != displayMesh.Material.RuntimeId) { ActiveShader.SetupMaterial(displayMesh.Material); } CurrentMaterial = displayMesh.Material; if (displayMesh.IndexBufferHandle == Globals.UNSET_HANDLE) { // Generate the Index VBO GL.GenBuffers(1, out index_buffer); GL.BindBuffer(BufferTarget.ElementArrayBuffer, index_buffer); displayMesh.IndexBufferHandle = index_buffer; } if (displayMesh.VertexBufferHandle == Globals.UNSET_HANDLE) { // Generate the VertexBuffer GL.GenBuffers(1, out vertex_buffer); GL.BindBuffer(BufferTarget.ArrayBuffer, vertex_buffer); displayMesh.VertexBufferHandle = vertex_buffer; displayMesh.LoadDataForVBOs(displayMesh.Mesh); // If CheckGLError turns up an error (likely an OutOfMemory warning because the mesh won't fit on the GPU) // we need to delete the buffers associated with that displayMesh and mark it as too big. bool causesGLError = CheckGLError("GL.BufferData"); if (causesGLError) { GL.DeleteBuffers(1, ref vertex_buffer); GL.DeleteBuffers(1, ref index_buffer); displayMesh.WillFitOnGPU = false; GC.Collect(); } else { displayMesh.WillFitOnGPU = true; } // If we have drawn all the partitions associated with the underlying openNURBS mesh in the ModelFile, we can delete it... if (displayMesh.Mesh.PartitionCount == displayMesh.PartitionIndex + 1) { Model.ModelFile.Objects.Delete(displayMesh.FileObjectId); } displayMesh.Mesh = null; } if (displayMesh.WillFitOnGPU == false) { return; } // Vertices // ORDER MATTERS...if you don't do things in this order, you will get very frusterated. // First, enable the VertexAttribArray for positions int rglVertex = ActiveShader.RglVertexIndex; GL.EnableVertexAttribArray(rglVertex); // Second, Bind the ArrayBuffer GL.BindBuffer(BufferTarget.ArrayBuffer, displayMesh.VertexBufferHandle); // Third, tell GL where to look for the data... GL.VertexAttribPointer(rglVertex, 3, VertexAttribPointerType.Float, false, displayMesh.Stride, IntPtr.Zero); CheckGLError("GL.VertexAttribPointer"); // Normals if (displayMesh.HasVertexNormals) { int rglNormal = ActiveShader.RglNormalIndex; GL.EnableVertexAttribArray(rglNormal); GL.VertexAttribPointer(rglNormal, 3, VertexAttribPointerType.Float, false, displayMesh.Stride, (IntPtr)(Marshal.SizeOf(typeof(Rhino.Geometry.Point3f)))); CheckGLError("GL.VertexAttribPointer"); } // Colors if (displayMesh.HasVertexColors) { int rglColor = ActiveShader.RglColorIndex; GL.EnableVertexAttribArray(rglColor); GL.VertexAttribPointer(rglColor, 4, VertexAttribPointerType.Float, false, displayMesh.Stride, (IntPtr)(Marshal.SizeOf(typeof(Rhino.Display.Color4f)))); CheckGLError("GL.VertexAttribPointer"); } if (isInstance) { if (!FastDrawing) { // Check for inversions on transforms, but only if we are drawing the final "high-quality" frame // (because our PerPixel shader does not flip normals based on modelView matrices). if ((obj as DisplayInstanceMesh).XForm.Determinant < -Globals.ON_ZERO_TOLERANCE) { // an inversion (happens in mirrored instances, etc.) // this means the transformation will turn the mesh // inside out, so we have to reverse our front face // convention. GL.FrontFace(FrontFaceDirection.Cw); } else { GL.FrontFace(FrontFaceDirection.Ccw); } } ActiveShader.SetModelViewMatrix((obj as DisplayInstanceMesh).XForm); } // Bind Indices GL.BindBuffer(BufferTarget.ElementArrayBuffer, displayMesh.IndexBufferHandle); CheckGLError("GL.BindBuffer"); // Draw... #if __ANDROID__ GL.DrawElements(All.Triangles, displayMesh.IndexBufferLength, All.UnsignedShort, IntPtr.Zero); #endif #if __IOS__ GL.DrawElements(BeginMode.Triangles, displayMesh.IndexBufferLength, DrawElementsType.UnsignedShort, IntPtr.Zero); #endif // Disable any and all arrays and buffers we might have used... GL.BindBuffer(BufferTarget.ArrayBuffer, displayMesh.VertexBufferHandle); GL.DisableVertexAttribArray(ActiveShader.RglColorIndex); GL.DisableVertexAttribArray(ActiveShader.RglNormalIndex); GL.DisableVertexAttribArray(ActiveShader.RglVertexIndex); GL.BindBuffer(BufferTarget.ElementArrayBuffer, displayMesh.IndexBufferHandle); GL.BindBuffer(BufferTarget.ArrayBuffer, 0); GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0); } }