1 //  SuperTuxKart - a fun racing game with go-kart
2 //  Copyright (C) 2018 SuperTuxKart-Team
3 //
4 //  This program is free software; you can redistribute it and/or
5 //  modify it under the terms of the GNU General Public License
6 //  as published by the Free Software Foundation; either version 3
7 //  of the License, or (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program; if not, write to the Free Software
16 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17 
18 #include "graphics/sp/sp_mesh_buffer.hpp"
19 #include "graphics/sp/sp_texture.hpp"
20 #include "graphics/central_settings.hpp"
21 #include "graphics/graphics_restrictions.hpp"
22 #include "graphics/material.hpp"
23 #include "graphics/sp/sp_shader.hpp"
24 #include "graphics/sp/sp_shader_manager.hpp"
25 #include "graphics/sp/sp_texture_manager.hpp"
26 #include "race/race_manager.hpp"
27 #include "utils/mini_glm.hpp"
28 #include "utils/string_utils.hpp"
29 
30 #include <set>
31 
32 namespace SP
33 {
34 // ----------------------------------------------------------------------------
~SPMeshBuffer()35 SPMeshBuffer::~SPMeshBuffer()
36 {
37 #ifndef SERVER_ONLY
38     for (unsigned i = 0; i < DCT_FOR_VAO; i++)
39     {
40         if (m_vao[i] != 0)
41         {
42             glDeleteVertexArrays(1, &m_vao[i]);
43         }
44         if (m_ins_array[i] != 0)
45         {
46             if (CVS->isARBBufferStorageUsable())
47             {
48                 glBindBuffer(GL_ARRAY_BUFFER, m_ins_array[i]);
49                 glUnmapBuffer(GL_ARRAY_BUFFER);
50                 glBindBuffer(GL_ARRAY_BUFFER, 0);
51             }
52             glDeleteBuffers(1, &m_ins_array[i]);
53         }
54     }
55     if (m_ibo != 0)
56     {
57         glDeleteBuffers(1, &m_ibo);
58     }
59     if (m_vbo != 0)
60     {
61         glDeleteBuffers(1, &m_vbo);
62     }
63 #endif
64 }   // ~SPMeshBuffer
65 
66 // ----------------------------------------------------------------------------
initDrawMaterial()67 void SPMeshBuffer::initDrawMaterial()
68 {
69 #ifndef SERVER_ONLY
70     Material* m = std::get<2>(m_stk_material[0]);
71     if (RaceManager::get()->getReverseTrack() && m->getMirrorAxisInReverse() != ' ')
72     {
73         for (unsigned i = 0; i < getVertexCount(); i++)
74         {
75             using namespace MiniGLM;
76             if (m->getMirrorAxisInReverse() == 'V')
77             {
78                 m_vertices[i].m_all_uvs[1] =
79                     toFloat16(1.0f - toFloat32(m_vertices[i].m_all_uvs[1]));
80             }
81             else
82             {
83                 m_vertices[i].m_all_uvs[0] =
84                     toFloat16(1.0f - toFloat32(m_vertices[i].m_all_uvs[0]));
85             }
86         }
87     }   // reverse track and texture needs mirroring
88 #endif
89 }   // initDrawMaterial
90 
91 // ----------------------------------------------------------------------------
initTexture()92 bool SPMeshBuffer::initTexture()
93 {
94 #ifndef SERVER_ONLY
95     for (unsigned i = 0; i < m_stk_material.size(); i++)
96     {
97         for (unsigned j = 0; j < 6; j++)
98         {
99             if (!m_textures[i][j]->initialized())
100             {
101                 return false;
102             }
103         }
104     }
105 #endif
106     return true;
107 }   // initTexture
108 
109 // ----------------------------------------------------------------------------
uploadGLMesh()110 void SPMeshBuffer::uploadGLMesh()
111 {
112     if (m_uploaded_gl)
113     {
114         return;
115     }
116     m_uploaded_gl = true;
117 #ifndef SERVER_ONLY
118     if (!m_shaders[0])
119     {
120         Log::warn("SPMeshBuffer", "%s shader is missing",
121             std::get<2>(m_stk_material[0])->getShaderName().c_str());
122         return;
123     }
124 
125     m_textures.resize(m_stk_material.size());
126     for (unsigned i = 0; i < m_stk_material.size(); i++)
127     {
128         for (unsigned j = 0; j < 6; j++)
129         {
130             m_textures[i][j] = SPTextureManager::get()->getTexture
131                 (m_shaders[0]->hasTextureLayer(j) ?
132                 std::get<2>(m_stk_material[i])->getSamplerPath(j) : "",
133                 j == 0 ? std::get<2>(m_stk_material[i]) : NULL,
134                 m_shaders[0]->isSrgbForTextureLayer(j),
135                 std::get<2>(m_stk_material[i])->getContainerId());
136         }
137         // Use the original spm uv texture 1 and 2 for compare in scene manager
138         m_tex_cmp[std::get<2>(m_stk_material[i])->getSamplerPath(0) +
139             std::get<2>(m_stk_material[i])->getSamplerPath(1)] = i;
140     }
141 
142     bool use_2_uv = std::get<2>(m_stk_material[0])->use2UV();
143     bool use_tangents = m_shaders[0]->useTangents();
144     const unsigned pitch = 48 - (use_tangents ? 0 : 4) - (use_2_uv ? 0 : 4) -
145         (m_skinned ? 0 : 16);
146     m_pitch = pitch;
147 
148     if (m_vbo != 0)
149     {
150         glDeleteBuffers(1, &m_vbo);
151     }
152     glGenBuffers(1, &m_vbo);
153     glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
154 
155     unsigned v_size = (unsigned)m_vertices.size() * pitch;
156     glBufferData(GL_ARRAY_BUFFER, v_size, NULL, GL_DYNAMIC_DRAW);
157     size_t offset = 0;
158     char* ptr = (char*)glMapBufferRange(GL_ARRAY_BUFFER, 0, v_size,
159         GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT |
160         GL_MAP_INVALIDATE_BUFFER_BIT);
161     v_size = 0;
162     for (unsigned i = 0 ; i < m_vertices.size(); i++)
163     {
164         offset = 0;
165         memcpy(ptr + v_size + offset, &m_vertices[i].m_position.X, 12);
166         offset += 12;
167 
168         memcpy(ptr + v_size + offset, &m_vertices[i].m_normal, 12);
169         offset += 4;
170 
171         video::SColor vc = m_vertices[i].m_color;
172         if (CVS->isDeferredEnabled())
173         {
174             vc.setRed(srgb255ToLinear(vc.getRed()));
175             vc.setGreen(srgb255ToLinear(vc.getGreen()));
176             vc.setBlue(srgb255ToLinear(vc.getBlue()));
177         }
178         memcpy(ptr + v_size + offset, &vc, 4);
179         offset += 4;
180 
181         memcpy(ptr + v_size + offset, &m_vertices[i].m_all_uvs[0], 4);
182         offset += 4;
183         if (use_2_uv)
184         {
185             memcpy(ptr + v_size + offset, &m_vertices[i].m_all_uvs[2], 4);
186             offset += 4;
187         }
188         if (use_tangents)
189         {
190             memcpy(ptr + v_size + offset, &m_vertices[i].m_tangent, 4);
191             offset += 4;
192         }
193         if (m_skinned)
194         {
195             memcpy(ptr + v_size + offset, &m_vertices[i].m_joint_idx[0], 16);
196         }
197         v_size += pitch;
198     }
199     glUnmapBuffer(GL_ARRAY_BUFFER);
200 
201     SPTextureManager::get()->increaseGLCommandFunctionCount(1);
202     SPTextureManager::get()->addGLCommandFunction
203         (std::bind(&SPMeshBuffer::initTexture, this));
204 
205     if (m_ibo != 0)
206     {
207         glDeleteBuffers(1, &m_ibo);
208     }
209     glGenBuffers(1, &m_ibo);
210     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
211 
212     glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * 2,
213         m_indices.data(), GL_STATIC_DRAW);
214     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
215 
216 #endif
217 }   // uploadGLMesh
218 
219 // ----------------------------------------------------------------------------
recreateVAO(unsigned i)220 void SPMeshBuffer::recreateVAO(unsigned i)
221 {
222 #ifndef SERVER_ONLY
223     if (!m_shaders[0])
224     {
225         return;
226     }
227     bool use_2_uv = std::get<2>(m_stk_material[0])->use2UV();
228     bool use_tangents = m_shaders[0]->useTangents();
229     const unsigned pitch = m_pitch;
230 
231     size_t offset = 0;
232 
233     if (m_ins_array[i] == 0)
234     {
235         glGenBuffers(1, &m_ins_array[i]);
236     }
237     else
238     {
239         if (CVS->isARBBufferStorageUsable())
240         {
241             glBindBuffer(GL_ARRAY_BUFFER, m_ins_array[i]);
242             glUnmapBuffer(GL_ARRAY_BUFFER);
243             glBindBuffer(GL_ARRAY_BUFFER, 0);
244         }
245         glDeleteBuffers(1, &m_ins_array[i]);
246         glGenBuffers(1, &m_ins_array[i]);
247     }
248     glBindBuffer(GL_ARRAY_BUFFER, m_ins_array[i]);
249 #ifndef USE_GLES2
250     if (CVS->isARBBufferStorageUsable())
251     {
252         glBufferStorage(GL_ARRAY_BUFFER, m_gl_instance_size[i] * 44, NULL,
253             GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
254         m_ins_dat_mapped_ptr[i] = glMapBufferRange(GL_ARRAY_BUFFER, 0,
255             m_gl_instance_size[i] * 44,
256             GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
257     }
258     else
259 #endif
260     {
261         glBufferData(GL_ARRAY_BUFFER, m_gl_instance_size[i] * 44, NULL,
262             GL_DYNAMIC_DRAW);
263     }
264     glBindBuffer(GL_ARRAY_BUFFER, 0);
265 
266     if (m_vao[i] != 0)
267     {
268         glDeleteVertexArrays(1, &m_vao[i]);
269     }
270     glGenVertexArrays(1, &m_vao[i]);
271     glBindVertexArray(m_vao[i]);
272 
273     glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
274     // Position
275     glEnableVertexAttribArray(0);
276     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, pitch, (void*)offset);
277     offset += 12;
278 
279     // Normal, if 10bit vector normalization is wrongly done by drivers, use
280     // original value and normalize ourselves in shader
281     glEnableVertexAttribArray(1);
282     glVertexAttribPointer(1, 4, GL_INT_2_10_10_10_REV,
283         GraphicsRestrictions::isDisabled
284         (GraphicsRestrictions::GR_CORRECT_10BIT_NORMALIZATION) ?
285         GL_FALSE : GL_TRUE, pitch, (void*)offset);
286     offset += 4;
287 
288     // Vertex color
289     glEnableVertexAttribArray(2);
290     glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, pitch,
291         (void*)offset);
292     offset += 4;
293 
294     // 1st texture coordinates
295     glEnableVertexAttribArray(3);
296     glVertexAttribPointer(3, 2, GL_HALF_FLOAT, GL_FALSE, pitch, (void*)offset);
297     offset += 4;
298     if (use_2_uv)
299     {
300         // 2nd texture coordinates
301         glEnableVertexAttribArray(4);
302         glVertexAttribPointer(4, 2, GL_HALF_FLOAT, GL_FALSE, pitch,
303             (void*)offset);
304         offset += 4;
305     }
306     else
307     {
308         glDisableVertexAttribArray(4);
309     }
310 
311     if (use_tangents)
312     {
313         // Tangent and bi-tanget sign
314         glEnableVertexAttribArray(5);
315         glVertexAttribPointer(5, 4, GL_INT_2_10_10_10_REV,
316             GraphicsRestrictions::isDisabled
317             (GraphicsRestrictions::GR_CORRECT_10BIT_NORMALIZATION) ?
318             GL_FALSE : GL_TRUE, pitch, (void*)offset);
319             offset += 4;
320     }
321     else
322     {
323         glDisableVertexAttribArray(5);
324     }
325 
326     if (m_skinned)
327     {
328         // 4 Joint indices
329         glEnableVertexAttribArray(6);
330         glVertexAttribIPointer(6, 4, GL_SHORT, pitch, (void*)offset);
331         offset += 8;
332         // 4 Joint weights
333         glEnableVertexAttribArray(7);
334         glVertexAttribPointer(7, 4, GL_HALF_FLOAT, GL_FALSE, pitch,
335             (void*)offset);
336         offset += 8;
337     }
338     else
339     {
340         glDisableVertexAttribArray(6);
341         glDisableVertexAttribArray(7);
342     }
343     glDisableVertexAttribArray(13);
344     glDisableVertexAttribArray(14);
345     glDisableVertexAttribArray(15);
346 
347     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
348     glBindBuffer(GL_ARRAY_BUFFER, m_ins_array[i]);
349     // Origin
350     glEnableVertexAttribArray(8);
351     glVertexAttribPointer(8, 3, GL_FLOAT, GL_FALSE, 44, (void*)0);
352     glVertexAttribDivisorARB(8, 1);
353     // Rotation (quaternion in 4 32bit floats)
354     glEnableVertexAttribArray(9);
355     glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, 44, (void*)12);
356     glVertexAttribDivisorARB(9, 1);
357     // Scale (3 half floats and .w unused)
358     glEnableVertexAttribArray(10);
359     glVertexAttribPointer(10, 4, GL_HALF_FLOAT, GL_FALSE, 44, (void*)28);
360     glVertexAttribDivisorARB(10, 1);
361     // Texture translation
362     glEnableVertexAttribArray(11);
363     glVertexAttribPointer(11, 2, GL_SHORT, GL_TRUE, 44, (void*)36);
364     glVertexAttribDivisorARB(11, 1);
365     // Misc data (skinning offset and hue change)
366     glEnableVertexAttribArray(12);
367     glVertexAttribIPointer(12, 2, GL_SHORT, 44, (void*)40);
368     glVertexAttribDivisorARB(12, 1);
369 
370     glBindVertexArray(0);
371     glBindBuffer(GL_ARRAY_BUFFER, 0);
372     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
373 #endif
374 }   // uploadGLMesh
375 
376 // ----------------------------------------------------------------------------
uploadInstanceData()377 void SPMeshBuffer::uploadInstanceData()
378 {
379 #ifndef SERVER_ONLY
380     for (unsigned i = 0; i < DCT_FOR_VAO; i++)
381     {
382         if (m_ins_dat[i].empty())
383         {
384             continue;
385         }
386 
387         unsigned new_size =
388             m_gl_instance_size[i] == 0 ? 1 : m_gl_instance_size[i];
389         while (m_ins_dat[i].size() > new_size)
390         {
391             // Power of 2 allocation strategy, like std::vector in gcc
392             new_size <<= 1;
393         }
394         if (new_size != m_gl_instance_size[i])
395         {
396             m_gl_instance_size[i] = new_size;
397             recreateVAO(i);
398         }
399         if (CVS->isARBBufferStorageUsable())
400         {
401             memcpy(m_ins_dat_mapped_ptr[i], m_ins_dat[i].data(),
402                 m_ins_dat[i].size() * 44);
403         }
404         else
405         {
406             glBindBuffer(GL_ARRAY_BUFFER, m_ins_array[i]);
407             void* ptr = glMapBufferRange(GL_ARRAY_BUFFER, 0,
408                 m_ins_dat[i].size() * 44, GL_MAP_WRITE_BIT |
409                 GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
410             memcpy(ptr, m_ins_dat[i].data(), m_ins_dat[i].size() * 44);
411             glUnmapBuffer(GL_ARRAY_BUFFER);
412             glBindBuffer(GL_ARRAY_BUFFER, 0);
413         }
414     }
415 #endif
416     m_uploaded_instance = true;
417 }   // uploadInstanceData
418 
419 // ----------------------------------------------------------------------------
enableTextureMatrix(unsigned mat_id)420 void SPMeshBuffer::enableTextureMatrix(unsigned mat_id)
421 {
422 #ifndef SERVER_ONLY
423     assert(mat_id < m_stk_material.size());
424     // Make the 31 bit in normal to be 1
425     uploadGLMesh();
426     if (m_vbo == 0 || m_ibo == 0)
427         return;
428     auto& ret = m_stk_material[mat_id];
429     glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
430     std::set<uint16_t> used_vertices;
431     for (unsigned int j = 0; j < std::get<1>(ret); j += 3)
432     {
433         for (unsigned int k = 0; k < 3; k++)
434         {
435             const uint16_t vertex_id = m_indices[std::get<0>(ret) + j + k];
436             auto ret = used_vertices.find(vertex_id);
437             if (ret == used_vertices.end())
438             {
439                 if ((m_vertices[vertex_id].m_normal & (1 << 30)) != 0)
440                 {
441                     // Already enabled
442                     glBindBuffer(GL_ARRAY_BUFFER, 0);
443                     return;
444                 }
445                 used_vertices.insert(vertex_id);
446                 m_vertices[vertex_id].m_normal |= 1 << 30;
447                 glBufferSubData(GL_ARRAY_BUFFER,
448                     (vertex_id * m_pitch) + 12 /*3 position*/, 4,
449                     &m_vertices[vertex_id].m_normal);
450             }
451         }
452     }
453     glBindBuffer(GL_ARRAY_BUFFER, 0);
454 #endif
455 }   // enableTextureMatrix
456 
457 // ----------------------------------------------------------------------------
reloadTextureCompare()458 void SPMeshBuffer::reloadTextureCompare()
459 {
460     assert(!m_textures.empty());
461     m_tex_cmp.clear();
462     for (unsigned i = 0; i < m_stk_material.size(); i++)
463     {
464         const std::string name =
465             m_textures[i][0]->getPath() + m_textures[i][1]->getPath();
466         m_tex_cmp[name] = i;
467     }
468 }   // reloadTextureCompare
469 
470 // ----------------------------------------------------------------------------
setSTKMaterial(Material * m)471 void SPMeshBuffer::setSTKMaterial(Material* m)
472 {
473     m_stk_material[0] = std::make_tuple(0u, getIndexCount(), m);
474     const std::string shader_name =
475         std::get<2>(m_stk_material[0])->getShaderName();
476     const std::string skinned_shader_name =
477         std::get<2>(m_stk_material[0])->getShaderName() + "_skinned";
478 
479     m_shaders[0] = SPShaderManager::get()->getSPShader(shader_name);
480     if (!m_shaders[0])
481     {
482         Log::warn("SPMeshBuffer", "%s shader is missing, fallback to solid",
483             shader_name.c_str());
484         m_shaders[0] = SPShaderManager::get()->getSPShader("solid");
485     }
486 
487     m_shaders[1] = SPShaderManager::get()->getSPShader(skinned_shader_name);
488     if (!m_shaders[1])
489         m_shaders[1] = SPShaderManager::get()->getSPShader("solid_skinned");
490 }   // setSTKMaterial
491 
492 }
493