1 // Copyright 2014 Wouter van Oortmerssen. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "lobster/stdafx.h"
16 #include "lobster/glinterface.h"
17 #include "lobster/glincludes.h"
18 
GenBO_(GLenum type,size_t bytesize,const void * data)19 uint GenBO_(GLenum type, size_t bytesize, const void *data) {
20     uint bo;
21     GL_CALL(glGenBuffers(1, &bo));
22     GL_CALL(glBindBuffer(type, bo));
23     GL_CALL(glBufferData(type, bytesize, data, GL_STATIC_DRAW));
24     return bo;
25 }
26 
DeleteBO(uint id)27 void DeleteBO(uint id) {
28     GL_CALL(glDeleteBuffers(1, &id));
29 }
30 
AttribsSize(string_view fmt)31 size_t AttribsSize(string_view fmt) {
32     size_t size = 0;
33     for (auto c : fmt) {
34         switch (c) {
35             case 'P': case 'N':           size += 12; break;
36             case 'p': case 'n': case 'T': size +=  8; break;
37             case 'C': case 'W': case 'I': size +=  4; break;
38             default: assert(0);
39         }
40     }
41     return size;
42 }
43 
GetPrimitive(Primitive prim)44 GLenum GetPrimitive(Primitive prim) {
45     switch (prim) {
46         default: assert(0);
47         case PRIM_TRIS:  return GL_TRIANGLES;
48         case PRIM_FAN:   return GL_TRIANGLE_FAN;
49         case PRIM_LOOP:  return GL_LINE_LOOP;
50         case PRIM_POINT: return GL_POINTS;
51     }
52 }
53 
Surface(span<int> indices,Primitive _prim)54 Surface::Surface(span<int> indices, Primitive _prim) : numidx(indices.size()), prim(_prim) {
55     ibo = GenBO(GL_ELEMENT_ARRAY_BUFFER, indices);
56 }
57 
Render(Shader * sh)58 void Surface::Render(Shader *sh) {
59     sh->SetTextures(textures);
60     GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo));
61     GL_CALL(glDrawElements(GetPrimitive(prim), (GLsizei)numidx, GL_UNSIGNED_INT, 0));
62 }
63 
~Surface()64 Surface::~Surface() {
65     GL_CALL(glDeleteBuffers(1, &ibo));
66 }
67 
Init(const void * verts1,const void * verts2)68 void Geometry::Init(const void *verts1, const void *verts2) {
69     vbo1 = GenBO_(GL_ARRAY_BUFFER, vertsize1 * nverts, verts1);
70     if (verts2) vbo2 = GenBO_(GL_ARRAY_BUFFER, vertsize2 * nverts, verts2);
71     GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo1));
72     GL_CALL(glGenVertexArrays(1, &vao));
73     GL_CALL(glBindVertexArray(vao));
74     size_t offset = 0;
75     size_t vs = vertsize1;
76     for (auto attr : fmt) {
77         switch (attr) {
78             #define SETATTRIB(idx, comps, type, norm, size) \
79                 GL_CALL(glEnableVertexAttribArray(idx)); \
80                 GL_CALL(glVertexAttribPointer(idx, comps, type, norm, (GLsizei)vs, (void *)offset)); \
81                 offset += size; \
82                 break;
83             case 'P': SETATTRIB(0, 3, GL_FLOAT,         false, 12)
84             case 'p': SETATTRIB(0, 2, GL_FLOAT,         false,  8)
85             case 'N': SETATTRIB(1, 3, GL_FLOAT,         false, 12)
86             case 'n': SETATTRIB(1, 2, GL_FLOAT,         false,  8)
87             case 'T': SETATTRIB(2, 2, GL_FLOAT,         false,  8)
88             case 'C': SETATTRIB(3, 4, GL_UNSIGNED_BYTE, true,   4)
89             case 'W': SETATTRIB(4, 4, GL_UNSIGNED_BYTE, true,   4)
90             case 'I': SETATTRIB(5, 4, GL_UNSIGNED_BYTE, false,  4)
91             default:
92                 LOG_ERROR("unknown attribute type: ", string() + attr);
93                 assert(false);
94         }
95         if (vbo2) {
96             GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo2));
97             vs = vertsize2;
98             offset = 0;
99         }
100     }
101     GL_CALL(glBindVertexArray(0));
102 }
103 
RenderSetup()104 void Geometry::RenderSetup() {
105     GL_CALL(glBindVertexArray(vao));
106 }
107 
~Geometry()108 Geometry::~Geometry() {
109     GL_CALL(glDeleteBuffers(1, &vbo1));
110     if (vbo2) GL_CALL(glDeleteBuffers(1, &vbo2));
111     GL_CALL(glDeleteVertexArrays(1, &vao));
112 }
113 
BindAsSSBO(Shader * sh,string_view name)114 void Geometry::BindAsSSBO(Shader *sh, string_view name) {
115     UniformBufferObject(sh, nullptr, 0, name, true, vbo1);
116     assert(!vbo2);
117 }
118 
Render(Shader * sh)119 void Mesh::Render(Shader *sh) {
120     if (prim == PRIM_POINT) SetPointSprite(pointsize);
121     sh->Set();
122     if (numbones && numframes) {
123         int frame1 = ffloor(curanim);
124         int frame2 = frame1 + 1;
125         float frameoffset = curanim - frame1;
126         float3x4 *mat1 = &mats[(frame1 % numframes) * numbones],
127                  *mat2 = &mats[(frame2 % numframes) * numbones];
128         auto outframe = new float3x4[numbones];
129         for(int i = 0; i < numbones; i++) outframe[i] = mix(mat1[i], mat2[i], frameoffset);
130         sh->SetAnim(outframe, numbones);
131         delete[] outframe;
132     }
133     geom->RenderSetup();
134     if (surfs.size()) {
135         for (auto s : surfs) s->Render(sh);
136     } else {
137         GL_CALL(glDrawArrays(GetPrimitive(prim), 0, (GLsizei)geom->nverts));
138     }
139 }
140 
~Mesh()141 Mesh::~Mesh() {
142     delete geom;
143     for (auto s : surfs) delete s;
144     if (mats) delete[] mats;
145 }
146 
WritePLY(string & s,size_t nindices)147 bool Geometry::WritePLY(string &s, size_t nindices) {
148     #ifndef PLATFORM_ES3
149     s += cat("ply\n"
150              "format binary_little_endian 1.0\n"
151              "element vertex ", nverts, "\n");
152     for (auto fc : fmt) {
153         switch (fc) {
154             case 'P': s += "property float x\nproperty float y\nproperty float z\n"; break;
155             case 'p': s += "property float x\nproperty float y\n"; break;
156             case 'N': s += "property float nx\nproperty float ny\nproperty float nz\n"; break;
157             case 'n': s += "property float nx\nproperty float ny\n"; break;
158             case 'T': s += "property float u\nproperty float v\n"; break;
159             case 'C': s += "property uchar red\nproperty uchar green\n"
160                            "property uchar blue\nproperty uchar alpha\n"; break;
161             case 'W': s += "property uchar wa\nproperty uchar wb\n"
162                            "property uchar wc\nproperty uchar wd\n"; break;
163             case 'I': s += "property uchar ia\nproperty uchar ib\n"
164                            "property uchar ic\nproperty uchar id\n"; break;
165             default: assert(0);
166         }
167     }
168     s += cat("element face ", nindices / 3, "\n"
169              "property list int int vertex_index\n"
170              "end_header\n");
171     vector<uchar> vdata(nverts * vertsize1);
172     GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vbo1));
173     GL_CALL(glGetBufferSubData(GL_ARRAY_BUFFER, 0, vdata.size(), vdata.data()));
174     s.insert(s.end(), vdata.begin(), vdata.end());
175     return true;
176     #else
177     (void)s;
178     (void)nindices;
179 	(void)vertsize1;
180     return false;
181     #endif
182 }
183 
WritePLY(string & s)184 void Surface::WritePLY(string &s) {
185     #ifndef PLATFORM_ES3
186     vector<int> idata(numidx / 3 * 4);
187     GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo));
188     GL_CALL(glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, numidx * sizeof(int), idata.data()));
189     for (int i = (int)numidx - 3; i >= 0; i -= 3) {
190         auto di = i / 3 * 4;
191         idata[di + 3] = idata[i + 1];
192         idata[di + 2] = idata[i + 2];  // we cull GL_FRONT
193         idata[di + 1] = idata[i + 0];
194         idata[di] = 3;
195     }
196     s.insert(s.end(), (char *)idata.data(), ((char *)idata.data()) + idata.size() * sizeof(int));
197     #else
198     (void)s;
199     #endif
200 }
201 
SaveAsPLY(string_view filename)202 bool Mesh::SaveAsPLY(string_view filename) {
203     size_t nindices = 0;
204     for (auto &surf : surfs) nindices += surf->numidx;
205     string s;
206     if (!geom->WritePLY(s, nindices)) return false;
207     for (auto &surf : surfs) surf->WritePLY(s);
208     return WriteFile(filename, true, s);
209 }
210 
SetPointSprite(float scale)211 void SetPointSprite(float scale) {
212     pointscale = scale * custompointscale;
213     #ifdef PLATFORM_ES3
214         // glEnable(GL_POINT_SPRITE_OES);
215         // glTexEnvi(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE);
216     #else
217         #ifndef __APPLE__
218             // GL_CALL(glEnable(GL_POINT_SPRITE));
219             // GL_CALL(glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE));
220         #endif
221         GL_CALL(glEnable(GL_VERTEX_PROGRAM_POINT_SIZE));
222     #endif
223 }
224 
RenderArray(Primitive prim,Geometry * geom,uint ibo,size_t tcount)225 void RenderArray(Primitive prim, Geometry *geom, uint ibo, size_t tcount) {
226     GLenum glprim = GetPrimitive(prim);
227     geom->RenderSetup();
228     if (ibo) {
229         GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo));
230         GL_CALL(glDrawElements(glprim, (GLsizei)tcount, GL_UNSIGNED_INT, 0));
231     } else {
232         GL_CALL(glDrawArrays(glprim, 0, (GLsizei)geom->nverts));
233     }
234 }
235 
RenderUnitSquare(Shader * sh,Primitive prim,bool centered)236 void GeometryCache::RenderUnitSquare(Shader *sh, Primitive prim, bool centered) {
237     if (!quadgeom[centered]) {
238         static SpriteVert vb_square[4] = {
239             SpriteVert{ float2(0, 0), float2(0, 0) },
240             SpriteVert{ float2(0, 1), float2(0, 1) },
241             SpriteVert{ float2(1, 1), float2(1, 1) },
242             SpriteVert{ float2(1, 0), float2(1, 0) },
243         };
244         static SpriteVert vb_square_centered[4] = {
245             SpriteVert{ float2(-1, -1), float2(0, 0) },
246             SpriteVert{ float2(-1,  1), float2(0, 1) },
247             SpriteVert{ float2( 1,  1), float2(1, 1) },
248             SpriteVert{ float2( 1, -1), float2(1, 0) },
249         };
250         quadgeom[centered] =
251             new Geometry(make_span(centered ? vb_square_centered : vb_square, 4), "pT");
252     }
253     sh->Set();
254     RenderArray(prim, quadgeom[centered]);
255 }
256 
RenderQuad(Shader * sh,Primitive prim,bool centered,const float4x4 & trans)257 void GeometryCache::RenderQuad(Shader *sh, Primitive prim, bool centered, const float4x4 &trans) {
258     Transform2D(trans, [&]() { RenderUnitSquare(sh, prim, centered); });
259 }
260 
RenderLine2D(Shader * sh,Primitive prim,const float3 & v1,const float3 & v2,float thickness)261 void GeometryCache::RenderLine2D(Shader *sh, Primitive prim, const float3 &v1, const float3 &v2,
262                                  float thickness) {
263     auto v = (v2 - v1) / 2;
264     auto len = length(v);
265     auto vnorm = v / len;
266     auto trans = translation(v1 + v) *
267                  rotationZ(vnorm.xy()) *
268                  float4x4(float4(len, thickness / 2, 1, 1));
269     RenderQuad(sh, prim, true, trans);
270 }
271 
RenderLine3D(Shader * sh,const float3 & v1,const float3 & v2,const float3 &,float thickness)272 void GeometryCache::RenderLine3D(Shader *sh, const float3 &v1, const float3 &v2,
273                                  const float3 &/*campos*/, float thickness) {
274     GL_CALL(glDisable(GL_CULL_FACE));  // An exception in 3d mode.
275     // FIXME: need to rotate the line also to make it face the camera.
276     //auto camvec = normalize(campos - (v1 + v2) / 2);
277     auto v = v2 - v1;
278     auto vq = quatfromtwovectors(normalize(v), float3_x);
279     //auto sq = quatfromtwovectors(camvec, float3_z);
280     auto trans = translation((v1 + v2) / 2) *
281                  float3x3to4x4(rotation(vq)) *  // FIXME: cheaper?
282                  float4x4(float4(length(v) / 2, thickness, 1, 1));
283     RenderQuad(sh, PRIM_FAN, true, trans);
284     GL_CALL(glEnable(GL_CULL_FACE));
285 }
286 
RenderUnitCube(Shader * sh,int inside)287 void GeometryCache::RenderUnitCube(Shader *sh, int inside) {
288     struct cvert { float3 pos; float3 normal; float2 tc; };
289     if (!cube_geom[inside]) {
290         static float3 normals[] = {
291             float3(1, 0, 0), float3(-1,  0,  0),
292             float3(0, 1, 0), float3( 0, -1,  0),
293             float3(0, 0, 1), float3( 0,  0, -1),
294         };
295         static float2 tcs[] = { float2(0, 0), float2(1, 0), float2(1, 1), float2(0, 1) };
296         static const char *faces[6] = { "4576", "0231", "2673", "0154", "1375", "0462" };
297         static int indices[2][6] = { { 0, 1, 3, 1, 2, 3 }, { 0, 3, 1, 1, 3, 2 } };
298         vector<cvert> verts;
299         vector<int> triangles;
300         for (int n = 0; n < 6; n++) {
301             auto face = faces[n];
302             for (int i = 0; i < 6; i++) triangles.push_back(indices[inside][i] + (int)verts.size());
303             for (int vn = 0; vn < 4; vn++) {
304                 cvert vert;
305                 for (int d = 0; d < 3; d++) {
306                     vert.pos[d] = float((face[vn] & (1 << (2 - d))) != 0);
307                 }
308                 vert.normal = normals[n];
309                 vert.tc = tcs[vn];
310                 verts.push_back(vert);
311             }
312         }
313         cube_geom[inside] = new Geometry(make_span(verts), "PNT");
314         cube_ibo[inside] = GenBO(GL_ELEMENT_ARRAY_BUFFER, make_span(triangles));
315     }
316     sh->Set();
317     RenderArray(PRIM_TRIS, cube_geom[inside], cube_ibo[inside], 36);
318 }
319 
RenderCircle(Shader * sh,Primitive prim,int segments,float radius)320 void GeometryCache::RenderCircle(Shader *sh, Primitive prim, int segments, float radius) {
321     assert(segments >= 3);
322     auto &geom = circlevbos[segments];
323     if (!geom) {
324         vector<float3> vbuf(segments);
325         float step = PI * 2 / segments;
326         for (int i = 0; i < segments; i++) {
327             // + 1 to reduce "aliasing" from exact 0 / 90 degrees points
328             vbuf[i] = float3(sinf(i * step + 1),
329                              cosf(i * step + 1), 0);
330         }
331         geom = new Geometry(make_span(vbuf), "P");
332     }
333     Transform2D(float4x4(float4(float2_1 * radius, 1)), [&]() {
334         sh->Set();
335         RenderArray(prim, geom);
336     });
337 }
338 
RenderOpenCircle(Shader * sh,int segments,float radius,float thickness)339 void GeometryCache::RenderOpenCircle(Shader *sh, int segments, float radius, float thickness) {
340     assert(segments >= 3);
341     auto &vibo = opencirclevbos[{ segments, thickness }];
342     auto nverts = segments * 2;
343     auto nindices = segments * 6;
344     if (!vibo.first) {
345         vector<float3> vbuf(nverts);
346         vector<int> ibuf(nindices);
347         float step = PI * 2 / segments;
348         float inner = 1 - thickness;
349         for (int i = 0; i < segments; i++) {
350             // + 1 to reduce "aliasing" from exact 0 / 90 degrees points
351             float x = sinf(i * step + 1);
352             float y = cosf(i * step + 1);
353             vbuf[i * 2 + 0] = float3(x, y, 0);
354             vbuf[i * 2 + 1] = float3(x * inner, y * inner, 0);
355             ibuf[i * 6 + 0] = i * 2 + 0;
356             ibuf[i * 6 + 1] = ((i + 1) * 2 + 0) % nverts;
357             ibuf[i * 6 + 2] = i * 2 + 1;
358             ibuf[i * 6 + 3] = i * 2 + 1;
359             ibuf[i * 6 + 4] = ((i + 1) * 2 + 1) % nverts;
360             ibuf[i * 6 + 5] = ((i + 1) * 2 + 0) % nverts;
361         }
362         vibo.first = new Geometry(make_span(vbuf), "P");
363         vibo.second = GenBO(GL_ELEMENT_ARRAY_BUFFER, make_span(ibuf));
364     }
365     Transform2D(float4x4(float4(float2_1 * radius, 1)), [&]() {
366         sh->Set();
367         RenderArray(PRIM_TRIS, vibo.first, vibo.second, nindices);
368     });
369 }
370 
~GeometryCache()371 GeometryCache::~GeometryCache() {
372     for (int i = 0; i < 2; i++) {
373         delete quadgeom[i];
374         delete cube_geom[i];
375         if (cube_ibo[i]) DeleteBO(cube_ibo[i]);
376     }
377     for (auto &p : circlevbos) delete p.second;
378     for (auto &p : opencirclevbos) delete p.second.first;
379 }
380