terrain working - no culling
[demo] / src / meshgen.cc
1 #include <stdio.h>
2 #include "meshgen.h"
3 #include "mesh.h"
4
5 // ------ geosphere ------
6 #define PHI             1.618034
7
8 /* icosahedron points */
9 static Vec3 icosa_pt[] = {
10         Vec3(PHI, 1, 0),
11         Vec3(-PHI, 1, 0),
12         Vec3(PHI, -1, 0),
13         Vec3(-PHI, -1, 0),
14         Vec3(1, 0, PHI),
15         Vec3(1, 0, -PHI),
16         Vec3(-1, 0, PHI),
17         Vec3(-1, 0, -PHI),
18         Vec3(0, PHI, 1),
19         Vec3(0, -PHI, 1),
20         Vec3(0, PHI, -1),
21         Vec3(0, -PHI, -1)
22 };
23 /* indices that map to 20hedron pts to create the polygons */
24 enum { P11, P12, P13, P14, P21, P22, P23, P24, P31, P32, P33, P34 };
25 static int icosa_idx[] = {
26         P11, P31, P21,
27         P11, P22, P33,
28         P13, P21, P32,
29         P13, P34, P22,
30         P12, P23, P31,
31         P12, P33, P24,
32         P14, P32, P23,
33         P14, P24, P34,
34
35         P11, P33, P31,
36         P12, P31, P33,
37         P13, P32, P34,
38         P14, P34, P32,
39
40         P21, P13, P11,
41         P22, P11, P13,
42         P23, P12, P14,
43         P24, P14, P12,
44
45         P31, P23, P21,
46         P32, P21, P23,
47         P33, P22, P24,
48         P34, P24, P22
49 };
50
51 static void geosphere(std::vector<Vec3> *verts, const Vec3 &v1, const Vec3 &v2, const Vec3 &v3, int iter)
52 {
53         if(!iter) {
54                 verts->push_back(v1);
55                 verts->push_back(v2);
56                 verts->push_back(v3);
57                 return;
58         }
59
60         /* we find mid points of the 3 vertices + normalize */
61
62         Vec3 v12 = normalize(v1 + v2);
63         Vec3 v23 = normalize(v2 + v3);
64         Vec3 v31 = normalize(v3 + v1);
65
66         /* create 4 triangles (recursive subdivision of the initial icosahedron */
67
68         geosphere(verts, v1, v12, v31, iter - 1);
69         geosphere(verts, v2, v23, v12, iter - 1);
70         geosphere(verts, v3, v31, v23, iter - 1);
71         geosphere(verts, v12, v23, v31, iter - 1);
72 }
73
74 void gen_geosphere(Mesh *mesh, float rad, int subdiv, bool hemi)
75 {
76         /* the triangles of the icosahedron (fixed) */
77         int num_tri = (sizeof icosa_idx / sizeof *icosa_idx) / 3;
78
79         std::vector<Vec3> verts;
80         for(int i=0; i<num_tri; i++) {
81                 Vec3 v[3];
82
83                 /* we select the 3 vertices from the i-th triangle of the 20hedron */
84                 for(int j=0; j<3; j++) {
85                         int vidx = icosa_idx[i * 3 + j];
86                         v[j] = normalize(icosa_pt[vidx]);
87                 }
88
89                 /* if hemi, we discard the triangles of the lower part of the 20hedron */
90                 if(hemi && (v[0].y < 0.0 || v[1].y < 0.0 || v[2].y < 0.0)) {
91                         continue;
92                 }
93
94                 geosphere(&verts, v[0], v[1], v[2], subdiv);
95         }
96
97         /* now the verts contains all the sphere vertices */
98
99         int num_verts = (int)verts.size();
100
101         mesh->vertices.resize(num_verts);
102         mesh->normals.resize(num_verts);
103         mesh->tex_coords.resize(num_verts);
104
105         for(int i=0; i<num_verts; i++) {
106                 mesh->vertices[i] = verts[i] * rad;
107                 mesh->normals[i] = verts[i];
108
109                 float theta = atan2(verts[i].z, verts[i].x);
110                 float phi = acos(verts[i].y);
111
112                 float u = 0.5 * theta / M_PI + 0.5;
113                 float v = phi / M_PI;
114                 mesh->tex_coords[i] = Vec2(u, v);
115         }
116
117         /* TODO: optimize indices by detecting the common vertices */
118         mesh->indices.resize(num_verts);
119         for(int i=0; i<num_verts; i++) {
120                 mesh->indices[i] = i;
121         }
122         mesh->invalidate();
123 }
124
125 // ------ heightfield ------
126
127 static Vec3 hfield_vertex(float u, float v, float h, float xsz,
128                 float ysz, float height)
129 {
130         float x = u * xsz - xsz / 2.0;
131         float y = h * height;
132         float z = v * ysz - ysz / 2.0;
133
134         return Vec3(x, y, z);
135 }
136
137 void gen_heightfield(Mesh *mesh, float xsz, float ysz, float height, int usub,
138                 int vsub, float (*calc_height)(float u, float v, void *ptr), void *ptr)
139 {
140         /* 
141         usub and vsub is the number of subdivision at each axis
142         (heightfield = grid) 
143         */
144         if(usub < 1)
145                 usub = 1;
146
147         if(vsub < 1)
148                 vsub = 1;
149
150         int num_uvertices = usub + 1;
151         int num_vvertices = vsub + 1;
152
153         int num_quads = usub * vsub;
154         int num_triangles = num_quads * 2;
155
156         int num_vertices = num_uvertices * num_vvertices;
157
158         mesh->vertices.resize(num_vertices);
159         mesh->normals.resize(num_vertices);
160         mesh->tex_coords.resize(num_vertices);
161         mesh->indices.resize(num_triangles * 3);
162
163         int vidx = 0;
164         float du = 1.0 / (float)num_uvertices;
165         float dv = 1.0 / (float)num_vvertices;
166
167         for(int i=0; i<num_vvertices; i++) {
168                 float v = 1 - (float)i / (float)(num_vvertices - 1);
169                 for(int j=0; j<num_uvertices; j++) {
170                         float u = (float)j / (float)(num_uvertices - 1);
171                         Vec3 vtx = hfield_vertex(u, v, calc_height(u, v, ptr), xsz, ysz, height);
172
173                         /* calculating normal with forward differences:
174                         slopes in x, z, axis */
175
176                         Vec3 tangent = hfield_vertex(u + du, v, calc_height(u + du, v, ptr),
177                                         xsz, ysz, height) - vtx;
178
179                         Vec3 bitangent = hfield_vertex(u, v + dv, calc_height(u, v + dv, ptr),
180                                         xsz, ysz, height) - vtx;
181
182                         Vec3 normal = normalize(cross(bitangent, tangent));
183
184                         mesh->vertices[vidx] = vtx;
185                         mesh->normals[vidx] = normal;
186                         mesh->tex_coords[vidx] = Vec2(u, v);
187
188                         vidx++;
189                 }
190         }
191
192         /*
193                 indices: 
194         */
195         uint16_t *iptr = &mesh->indices[0];
196
197         for(int i=0; i<vsub; i++) {
198                 for(int j=0; j<usub; j++) {
199                         int a = num_vvertices * i + j;
200                         int b = a + 1;
201                         int d = num_vvertices * (i + 1) + j;
202                         int c = d + 1;
203
204                         /* 1st triangle */
205                         *iptr++ = a;
206                         *iptr++ = b;
207                         *iptr++ = c;
208
209                         /* 2nd triangle */
210
211                         *iptr++ = c;
212                         *iptr++ = d;
213                         *iptr++ = a;
214                 }
215         }
216         mesh->invalidate();
217 }