1 /*
2  *  BSD 3-Clause License
3  *  
4  *  Copyright (c) 2016, Mango-Engine Team
5  *  All rights reserved.
6  *  
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions are met:
9  *  
10  *  * Redistributions of source code must retain the above copyright notice, this
11  *    list of conditions and the following disclaimer.
12  *  
13  *  * Redistributions in binary form must reproduce the above copyright notice,
14  *    this list of conditions and the following disclaimer in the documentation
15  *    and/or other materials provided with the distribution.
16  *  
17  *  * Neither the name of the copyright holder nor the names of its
18  *    contributors may be used to endorse or promote products derived from
19  *    this software without specific prior written permission.
20  *  
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24  *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 module mango_engine.graphics.opengl.gl_model;
33 
34 version(mango_GLBackend) {
35     import mango_engine.game;
36     import mango_engine.graphics.renderer;
37     import mango_engine.graphics.texture;
38     import mango_engine.graphics.shader;
39     import mango_engine.graphics.model;
40 
41     import mango_engine.graphics.opengl.gl_texture;
42     import mango_engine.graphics.opengl.gl_shader;
43     import mango_engine.graphics.opengl.gl_types;
44 
45     import derelict.opengl3.gl3;
46 
47     import gl3n.linalg;
48 
49     class GLModel : Model {
50 
51         /// Enum containing array positions for Mesh VBOs.
52         static enum VBOIndexes {
53             VBO_VERTICES,
54             VBO_INDICES,
55             VBO_TEXTURES
56         }
57 
58         protected shared size_t _drawCount;
59 
60         protected shared VBO[uint] vboList;
61         private shared VAO _vao;
62 
63         /// The VAO belonging to the Mesh.
64         @property VAO vao() @trusted nothrow { return cast(VAO) _vao; }
65 
66         /++
67             The amount of points (vertices) that will be
68             rendered. This is equal to the amount of
69             indices.
70         +/
71         @property size_t drawCount() @trusted nothrow { return cast(size_t) _drawCount; }
72 
73         /++
74             The amount of points (vertices) that will be
75             rendered. This is equal to the amount of
76             indices.
77         +/
78         @property protected void drawCount(shared size_t drawCount) @safe nothrow { _drawCount = drawCount; }
79 
80         this(in string name, GameManager game, Vertex[] vertices, uint[] indices, Texture texture, ShaderProgram shader) @safe {
81             super(name, game, vertices, indices, texture, shader);
82 
83             this._drawCount = indices.length;
84 
85             this.game.renderer.submitOperation(&this.setup);
86         }
87 
88         private void setup() @system {
89             _vao = cast(shared) VAO.generateNew();
90             vao.bind();
91 
92             auto indicesVBO = new VBO(GL_ELEMENT_ARRAY_BUFFER);
93             indicesVBO.bind();
94             indicesVBO.setData(indices);
95             //indicesVBO.setDataRaw(indices.ptr, cast(GLsizei) (indices.length * uint.sizeof));
96 
97             //------------------- Vertices
98             auto verticesVBO = new VBO(GL_ARRAY_BUFFER);
99             verticesVBO.bind();
100             verticesVBO.setDataRaw(
101                 cast(void*) positionVerticesToFloats(cast(Vertex[]) vertices),
102                 cast(GLsizei) (vertices.length * vec3.sizeof) // Single vertex is a vec3 * the amount of vertices
103             );
104 
105             glEnableVertexAttribArray(0);
106             glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, cast(void*) 0);
107             //------------------- End Vertices
108 
109             vboList[VBOIndexes.VBO_VERTICES] = cast(shared) verticesVBO;
110             vboList[VBOIndexes.VBO_INDICES] = cast(shared) indicesVBO;
111 
112             // Check if using Textured vertices.
113             if(cast(TexturedVertex[]) vertices) {
114                 auto textureVBO = new VBO(GL_ARRAY_BUFFER);
115                 textureVBO.bind();
116                 textureVBO.setDataRaw(
117                     cast(void*) textureVerticesToFloats(cast(Vertex[]) vertices),
118                     cast(GLsizei) (vertices.length * vec2.sizeof)
119                 );
120 
121                 glEnableVertexAttribArray(1);
122                 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, cast(void*) 0);
123 
124                 vboList[VBOIndexes.VBO_TEXTURES] = cast(shared) textureVBO;
125             }
126 
127             vao.unbind();
128         }
129 
130         override {
131             void cleanup() @system {
132                 this.game.renderer.submitOperation(() {
133                     vao.bind();
134                     foreach(vbo; vboList.values) {
135                         (cast(VBO) vbo).cleanup();
136                     }
137                     vao.unbind();
138                     vao.cleanup();
139                 });
140             }
141 
142             protected void replaceVertices_() @system {
143                 this.game.renderer.submitOperation(() {
144                     this.vao.bind();
145 
146                     VBO verticesVBO = cast(VBO) this.vboList[VBOIndexes.VBO_VERTICES];
147 
148                     verticesVBO.bind();
149                     verticesVBO.setDataRaw( // Replace buffer with new vertex buffer
150                         cast(void*) positionVerticesToFloats(cast(Vertex[]) this.vertices),
151                         cast(GLsizei) (vertices.length * vec3.sizeof) // Single vertex is a vec3 * the amount of vertices
152                     );
153 
154                     if(cast(TexturedVertex[]) this.vertices) {
155                         VBO texturesVBO = cast(VBO) this.vboList[VBOIndexes.VBO_TEXTURES];
156 
157                         texturesVBO.bind();
158                         texturesVBO.setDataRaw( // Replace buffer with new texture vertex buffer
159                             cast(void*) textureVerticesToFloats(cast(Vertex[]) vertices),
160                             cast(GLsizei) (vertices.length * vec2.sizeof)
161                         );
162                     }
163 
164                     this.vao.unbind();
165                 });              
166             }
167 
168             protected void replaceVertex_(size_t pos, Vertex v) @system {
169                 this.game.renderer.submitOperation(() {
170                     this.vao.bind();
171 
172                     VBO verticesVBO = cast(VBO) this.vboList[VBOIndexes.VBO_VERTICES];
173                     verticesVBO.bind();
174                     verticesVBO.setSubData( // Replace the vertex
175                          cast(void*) positionVerticesToFloats([v]),
176                          (pos * vec3.sizeof), vec3.sizeof // Single vertex is a vec3 * the amount of vertices
177                     );
178 
179                     if(cast(TexturedVertex[]) this.vertices && cast(TexturedVertex) v) {
180                         VBO texturesVBO = cast(VBO) this.vboList[VBOIndexes.VBO_TEXTURES];
181 
182                         texturesVBO.bind();
183                         texturesVBO.setSubData( // Replace the Vertex
184                             cast(void*) textureVerticesToFloats([v]),
185                             (pos * vec2.sizeof), vec2.sizeof // Vec2 is the size of a single texture vertex
186                         );
187                     }
188 
189                     this.vao.unbind();
190                 });
191             }
192         
193             /// Will be called in the Renderer thread from GLRenderer
194             protected void render_(Renderer renderer) @system {
195                 (cast(GLTexture) texture).use();
196                 (cast(GLShaderProgram) shader).use();
197 
198                 vao.bind();
199                 
200                 glDrawElements(GL_TRIANGLES, cast(GLsizei) drawCount,
201                     GL_UNSIGNED_INT,
202                     cast(void*) 0
203                 );
204 
205                 vao.unbind();
206             }
207         }
208     }
209 }