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