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 }