aboutsummaryrefslogtreecommitdiffstats
path: root/dev/MinGfx/src/mesh.h
blob: f3f877e670fdc6a941ddffee57982bd12d96c6a1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
/*
 This file is part of the MinGfx Project.
 
 Copyright (c) 2017,2018 Regents of the University of Minnesota.
 All Rights Reserved.
 
 Original Author(s) of this File:
	Dan Keefe, 2018, University of Minnesota
	
 Author(s) of Significant Updates/Modifications to the File:
	...
 */

#ifndef SRC_MESH_H_
#define SRC_MESH_H_



#include "bvh.h"
#include "color.h"
#include "opengl_headers.h"
#include "point2.h"
#include "point3.h"
#include "vector3.h"

#include <vector>


namespace mingfx {

class Matrix4;
    
/** A triangle mesh data structure that can be rendered with a ShaderProgram
 like DefaultShader.  The mesh can be created algorithmically by adding triangles
 one at a time or it can be loaded from an .obj file.
 
 Vertices are required -- you cannot have a mesh without vertices, but other
 attributes (normals, colors, texture coordinates) are optional.  When Draw()
 is called the mesh will automatically set these other attributes if available.
 
 Example of loading from a file:
 ~~~
 // during initialization
 Mesh m;
 m.LoadFromOBJ(Platform::FindMinGfxDataFile("teapot.obj"));
 // also create a shader to draw it.
 DefaultShader s;
 
 
 // later to draw
 Matrix4 M;
 Matrix4 V = Matrix4::LookAt(Point3(0,0,3), Point3(0,0,0), Vector3(0,1,0));
 Matrix4 P = Matrix4::Perspective(60.0, aspect_ratio(), 0.1, 10.0);
 s.Draw(M, V, P, m, DefaultShader::MaterialProperties());
 ~~~
 
 Example of creating a mesh algorithmically:
 ~~~
 Mesh mesh1;
 int tri_id;
 // add a first triangle
 tri_id = mesh1.AddTriangle(Point3(0,0,0), Point3(1,0,0), Point3(1,1,0));
 // set attributes for the vertices
 mesh1.SetNormals(tri_id, Vector3(0,0,1), Vector3(0,0,1), Vector3(0,0,1));
 mesh1.SetTexCoords(tri_id, 0, Point2(0,1), Point2(1,1), Point2(1,0));

 // add a second triangle and attributes
 tri_id = mesh1.AddTriangle(Point3(0,0,0), Point3(1,1,0), Point3(0,1,0));
 mesh1.SetNormals(tri_id, Vector3(0,0,1), Vector3(0,0,1), Vector3(0,0,1));
 mesh1.SetTexCoords(tri_id, 0, Point2(0,1), Point2(1,0), Point2(0,0));
 
 // call this when done to save to the GPU
 mesh1.UpdateGPUMemory();
 
 // then you can draw the same way as in the previous example.
 ~~~
 In the mode used above where you add one triangle at a time there is no way to
 reuse vertices in multiple triangles.  If you need to do this for efficiency
 or other reasons, then you can use an indexed faces mode where you set the
 mesh data structures directly.
 
 Example of creating a mesh that renders in an indexed faces mode:
 ~~~
 std::vector<unsigned int> indices;
 std::vector<Point3> vertices;
 std::vector<Vector3> normals;
 std::vector<Point2> texcoords;

 // four vertices, each requires 3 floats: (x,y,z)
 vertices.push_back(Point3(0,0,0));
 vertices.push_back(Point3(1,0,0));
 vertices.push_back(Point3(1,1,0));
 vertices.push_back(Point3(0,1,0));

 // four normals, each requires 3 floats: (x,y,z)
 normals.push_back(Vector3(0,0,1));
 normals.push_back(Vector3(0,0,1));
 normals.push_back(Vector3(0,0,1));
 normals.push_back(Vector3(0,0,1));

 // four texture coords, each requires 2 floats: (u,v)
 texcoords.push_back(Point2(0,1));
 texcoords.push_back(Point2(1,1));
 texcoords.push_back(Point2(1,0));
 texcoords.push_back(Point2(0,0));
 
 // indices into the arrays above for the first triangle
 indices.push_back(0); 
 indices.push_back(1); 
 indices.push_back(2);
 
 // indices for the second triangle, note some are reused
 indices.push_back(0); 
 indices.push_back(2); 
 indices.push_back(3);
 
 Mesh mesh1;
 mesh1.SetVertices(vertices);
 mesh1.SetNormals(normals);
 mesh1.SetTexCoords(0, texcoords);
 mesh1.SetIndices(indices);
 mesh1.UpdateGPUMemory();
 
 // then you can draw the same way as in the previous example.
 ~~~
 */
class Mesh {
public:
    /// Creates an empty mesh.
    Mesh();
    
    /// Copies all data and sets GPU dirty bit for the new mesh.
    Mesh(const Mesh &other);
    
    virtual ~Mesh();
    
    
    /** This reads a mesh stored in the common Wavefront Obj file format.  The
     loader here is simplistic and not guaranteed to work on all valid .obj
     files, but it should work on many simple ones. UpdateGPUMemory() is
     called automatically after the model is loaded. */
    void LoadFromOBJ(const std::string &filename);
    
    
    
    // ---- TRIANGLE LIST MODE ----
    // No indices are stored, each set of 3 vertices forms a triangle, and if the
    // triangles share vertices, those vertices need to be repeated.
    
    /** Adds a triangle to the mesh datastructure and returns a triangle ID.
     The ID should then be used as the first argument for follow-on calls to 
     SetNormals(), SetColors(), and SetTexCoords().  The vertices must be
     specified in counter-clockwise order so that the normal of the triangle
     can be determined following the right-hand rule. */
    int AddTriangle(Point3 v1, Point3 v2, Point3 v3);
    
    /** Updates the vertex positions for a triangle that has already been added
     to the mesh. */
    void UpdateTriangle(int triangle_id, Point3 v1, Point3 v2, Point3 v3);
    
    /** Sets the normals for the three vertices of a triangle that has already
     been added to the mesh */
    void SetNormals(int triangle_id, Vector3 n1, Vector3 n2, Vector3 n3);

    /** Sets per-vertex colors for the three vertices of a triangle that has already
     been added to the mesh */
    void SetColors(int triangle_id, Color c1, Color c2, Color c3);
    
    /** Sets the texture coordinates for the three vertices of a triangle that 
     has already been added to the mesh.  The first textureUnit is 0, and you
     should always use 0 for this parameter unless you are doing multi-texturing. */
    void SetTexCoords(int triangle_id, int texture_unit, Point2 uv1, Point2 uv2, Point2 uv3);
    
    

    // ---- INDEXED TRIANGLES MODE ----
    // Vertices are stored in an array and indices are stored in a separate array
    // each set of 3 indices into the vertex array defines one triangle.  Here,
    // you cannot add one triangle at a time to the mesh.  Instead you must set
    // the arrays of indices, vertices, and other attributes for the mesh at
    // once.
    
    /// Sets the vertex array for the mesh directly.
    void SetVertices(const std::vector<Point3> &verts);
    
    /// Sets the normal array for the mesh directly.
    void SetNormals(const std::vector<Vector3> &norms);
    
    /// Sets the per-vertex colors array for the mesh directly.
    void SetColors(const std::vector<Color> &colors);
    
    /// Sets a texture coordinates array for the mesh directly.
    void SetTexCoords(int texture_unit, const std::vector<Point2> &tex_coords);
    
    /// Sets the indices into the vertex array to use to create the triangles.
    /// Each consecutive set of 3 indices forms one triangle:
    /// (v1,v2,v3), (v1,v2,v3), (v1,v2,v3), ...
    void SetIndices(const std::vector<unsigned int> index_array);
    
    
    void SetInstanceTransforms(const std::vector<Matrix4> &xforms);
    
    
    // ---- These functions can be used instead of the above if you are working with
    // regular C-style arrays and floats rather than the higher level types like
    // Point3 and Vector3. ----
    
    /// Sets the vertex array for the mesh directly.  Vertices are stored as
    /// (x,y,z), (x,y,z), (x,y,z), ...
    /// This version of the function accepts a C-style array rather than std::vector<>
    void SetVertices(float *verts_array, int num_verts);

    /// Sets the normal array for the mesh directly.  Normals are stored as
    /// (x,y,z), (x,y,z), (x,y,z), ... following the same ordering as was used
    /// for SetVertices().
    /// This version of the function accepts a C-style array rather than std::vector<>
    void SetNormals(float *norms_array, int num_norms);

    /// Sets the per-vertex colors array for the mesh directly.  Colors are stored as
    /// (r,g,b,a), (r,g,b,a), (r,g,b,a), ... following the same ordering as was used
    /// for SetVertices().
    /// This version of the function accepts a C-style array rather than std::vector<>
    void SetColors(float *colors_array, int num_colors);

    /// Sets a texture coordinates array for the mesh directly.  Tex coords are stored as
    /// (u,v), (u,v), (u,v), ... following the same ordering as was used
    /// for SetVertices().
    /// This version of the function accepts a C-style array rather than std::vector<>
    void SetTexCoords(int texture_unit, float *tex_coords_array, int num_tex_coords);
    
    /// Sets the indices into the vertex array to use to create the triangles.
    /// Each consecutive set of 3 indices forms one triangle:
    /// (v1,v2,v3), (v1,v2,v3), (v1,v2,v3), ...
    /// This version of the function accepts a C-style array rather than std::vector<>
    void SetIndices(unsigned int *index_array, int num_indices);

    
    
    /** This copies the entire mesh data structure to a vertex array in GPU memory,
     which must happen before you can draw the mesh.  For large meshes, this can
     take some time, so you may want to call this during initialization immediately
     after generating the mesh.  If you do not, it will be called automatically
     for you the first time Draw() is called. If the mesh contains normals, per-
     vertex colors and/or texture coordinates these are added as attributes within
     the vertex array. */
    void UpdateGPUMemory();
    
    /** This sends the mesh vertices and attributes down the graphics pipe using
     glDrawArrays() for the non-indexed mode and glDrawElements() for the indexed
     mode.  This is just the geometry -- for anything to show up on the screen,
     you must already have a ShaderProgram enabled before calling this function. */
    void Draw();
    

    
    /** This (re)calculates the normals for the mesh and stores them with the mesh
     data structure.  It assumes a faceted mesh, like a cube, where each triangle
     has separate vertices.  The normal is calculated for each triangle face and
     then the result is associated with each vertex that makes up the triangle.
     If you have a smooth mesh where vertices are shared between multiple faces
     then use CalcPerVertexNormals() instead. */
    void CalcPerFaceNormals();
    
    /** This (re)calculates the normals for the mesh and stores them with the mesh
     data structure.  It assumes a smooth mesh, like a sphere, where each vertex
     belongs to one or more triangles.  Each vertex normal is calculated as a
     weighted sum of the face normals for adjacent faces.  The weighting is based
     upon the relative areas of the neighboring faces (i.e., a large neighboring
     triangle contributes more to the vertex normal than a small one). */
    void CalcPerVertexNormals();
    
    
    /** This (re)calculates a Bounding Volume Hierarchy for the mesh, which can
     be used together with Ray::FastIntersectMesh() to do faster ray-mesh
     intersection testing. */
    void BuildBVH();
    
    /** Returns a pointer to the underlying BVH data structure.  If the data
     struture has not yet been build or needs to be updated due to a change in
     the geometry of the mesh, then the BVH is recalculated before returning
     the pointer. */
    BVH* bvh_ptr();
    
    // Access to properties indexed by vertex number
    
    /// The total number of vertices in the mesh.
    int num_vertices() const;
    
    /// Read only access to the vertex position data.  Data are returned as a Point3.  Indexed by vertex number.  Also see num_vertices().
	/// Use the SetVertices() function to set (or edit) vertex data.
	Point3 read_vertex_data(int vertex_id) const;
    
    /// Read only access to per-vertex normal data.  Data are returned as a Vector3.  Indexed by vertex number.  Also see num_vertices().
	/// Use the SetNormals() function to set (or edit) per-vertex normal data.
	Vector3 read_normal_data(int vertex_id) const;
    
    /// Read only access to per-vertex color data.  Data are returned as a Color.  Indexed by vertex number.  Also see num_vertices().
    /// Use the SetColors() function to set (or edit) per-vertex color data.
	Color read_color_data(int vertex_id) const;
    
    /// Read only access to per-vertex texture coordinates data.  Data are returned as a Point2.  Indexed by vertex number.  Also see num_vertices().
    /// Use the SetTexCoords() function to set (or edit) per-vertex tex coords.
	Point2 read_tex_coords_data(int texture_unit, int vertex_id) const;
    
    
    // Access to triangles
    
    /// The total number of triangles in the mesh.
    int num_triangles() const;
    
    /// Read only access to the indices that make up a particular triangle.  Data are returned as a 3-element array
    // of unsigned ints.  Use the SetIndices() function to set (or edit) the indices for the mesh.
	std::vector<unsigned int> read_triangle_indices_data(int triangle_id) const;
    
   
private:
    std::vector<float> verts_;
    std::vector<float> norms_;
    std::vector<float> colors_;
    std::vector< std::vector<float> > tex_coords_;
    std::vector<unsigned int> indices_;
    std::vector<float> instance_xforms_;
    
    bool gpu_dirty_;
    GLuint vertex_buffer_;
    GLuint vertex_array_;
    GLuint element_buffer_;
    
    bool bvh_dirty_;
    BVH bvh_;
};
    
    
} // end namespace


#endif