1 module nanogui.glcanvas; 2 /* 3 NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>. 4 The widget drawing code is based on the NanoVG demo application 5 by Mikko Mononen. 6 7 All rights reserved. Use of this source code is governed by a 8 BSD-style license that can be found in the LICENSE.txt file. 9 */ 10 /** 11 * \file nanogui/glcanvas.h 12 * 13 * \brief Canvas widget for rendering OpenGL content. This widget was 14 * contributed by Jan Winkler. 15 */ 16 17 import nanogui.widget; 18 // #include <nanogui/opengl.h> 19 // #include <nanogui/glutil.h> 20 21 /** 22 * \class GLCanvas glcanvas.d 23 * 24 * \brief Canvas widget for rendering OpenGL content. This widget was 25 * contributed by Jan Winkler. 26 * 27 * Canvas widget that can be used to display arbitrary OpenGL content. This is 28 * useful to display and manipulate 3D objects as part of an interactive 29 * application. The implementation uses scissoring to ensure that rendered 30 * objects don't spill into neighboring widgets. 31 * 32 * \rst 33 * **Usage** 34 * Override :func:`nanogui.GLCanvas.drawGL` in subclasses to provide 35 * custom drawing code. 36 * 37 * \endrst 38 */ 39 class GLCanvas : Widget 40 { 41 import nanogui.common : Vector2f, Vector2i, Vector4i; 42 public: 43 /** 44 * Creates a GLCanvas attached to the specified parent. 45 * 46 * \param parent 47 * The Widget to attach this GLCanvas to. 48 */ 49 this(Widget parent, int w, int h) 50 { 51 super(parent); 52 mBackgroundColor = Vector4i(128, 128, 128, 255); 53 mDrawBorder = true; 54 mSize = Vector2i(w, h); 55 lastWidth = w; 56 lastHeight = h; 57 58 initBuffers; 59 60 screen.addGLCanvas(this); 61 } 62 63 ~this() 64 { 65 screen.removeGLCanvas(this); 66 releaseBuffers; 67 } 68 69 /// Returns the background color. 70 final const(Color) backgroundColor() const { return mBackgroundColor; } 71 72 /// Sets the background color. 73 final void backgroundColor(const Color backgroundColor) { mBackgroundColor = backgroundColor; } 74 75 /// Set whether to draw the widget border or not. 76 final void drawBorder(const bool bDrawBorder) { mDrawBorder = bDrawBorder; } 77 78 /// Return whether the widget border gets drawn or not. 79 bool drawBorder() const { return mDrawBorder; } 80 81 /// Draw the canvas. 82 override void draw(ref NanoContext ctx) 83 { 84 Widget.draw(ctx); 85 86 if (mDrawBorder) 87 drawWidgetBorder(ctx); 88 89 auto mImage = glCreateImageFromOpenGLTexture(ctx, mColorBuf.handle, width, height, NVGImageFlag.NoDelete); 90 assert(mImage.valid); 91 auto mPaint = ctx.imagePattern( 92 mPos.x + 1, 93 mPos.y + 1.0f, 94 mSize.x - 2, 95 mSize.y - 2, 96 0, 97 mImage); 98 99 ctx.beginPath; 100 ctx.rect( 101 mPos.x + 1, 102 mPos.y + 1.0f, 103 mSize.x - 2, 104 mSize.y - 2 105 ); 106 ctx.fillPaint(mPaint); 107 ctx.fill; 108 } 109 110 /// Draw the GL scene. Override this method to draw the actual GL content. 111 void drawGL() {} 112 113 // /// Save the state of this GLCanvas to the specified Serializer. 114 // virtual void save(Serializer &s) const override; 115 116 // /// Set the state of this GLCanvas from the specified Serializer. 117 // virtual bool load(Serializer &s) override; 118 119 protected: 120 /// Internal helper function for drawing the widget border 121 void drawWidgetBorder(NanoContext ctx) const 122 { 123 // import arsd.nanovega; 124 ctx.beginPath; 125 ctx.strokeWidth(1.0f); 126 ctx.roundedRect(mPos.x - 0.5f, mPos.y - 0.5f, 127 mSize.x + 1, mSize.y + 1, mTheme.mWindowCornerRadius); 128 ctx.strokeColor(mTheme.mBorderLight); 129 ctx.roundedRect(mPos.x - 1.0f, mPos.y - 1.0f, 130 mSize.x + 2, mSize.y + 2, mTheme.mWindowCornerRadius); 131 ctx.strokeColor(mTheme.mBorderDark); 132 ctx.stroke; 133 } 134 135 import gfm.opengl : GLTexture2D, GLFBO, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, 136 GL_CLAMP_TO_EDGE, GL_RGBA, GL_UNSIGNED_BYTE, glClear, GL_COLOR_BUFFER_BIT, 137 GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT, GLRenderBuffer, GL_DEPTH_COMPONENT; 138 139 /// The background color (what is used with ``glClearColor``). 140 Color mBackgroundColor; 141 142 /// Whether to draw the widget border or not. 143 bool mDrawBorder; 144 GLTexture2D mColorBuf; 145 GLRenderBuffer mDepthBuf; 146 147 package: 148 GLFBO mFbo; 149 int lastWidth, lastHeight; 150 151 void initBuffers() 152 { 153 mColorBuf = new GLTexture2D(); 154 mColorBuf.setMinFilter(GL_LINEAR_MIPMAP_LINEAR); 155 mColorBuf.setMagFilter(GL_LINEAR); 156 mColorBuf.setWrapS(GL_CLAMP_TO_EDGE); 157 mColorBuf.setWrapT(GL_CLAMP_TO_EDGE); 158 mColorBuf.setImage(0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null); 159 mColorBuf.generateMipmap(); 160 161 mDepthBuf = new GLRenderBuffer(GL_DEPTH_COMPONENT, width, height); 162 163 mFbo = new GLFBO(); 164 mFbo.use(); 165 mFbo.color(0).attach(mColorBuf); 166 mFbo.depth.attach(mDepthBuf); 167 mFbo.unuse(); 168 } 169 170 void releaseBuffers() 171 { 172 mFbo.destroy(); 173 mDepthBuf.destroy(); 174 mColorBuf.destroy(); 175 } 176 }