]> git.lizzy.rs Git - dragonfireclient.git/blob - src/client/mesh.cpp
bec72fb5e79b13f27a89b3043fa12aa644511372
[dragonfireclient.git] / src / client / mesh.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "mesh.h"
21 #include "debug.h"
22 #include "log.h"
23 #include "irrMap.h"
24 #include <cmath>
25 #include <iostream>
26 #include <IAnimatedMesh.h>
27 #include <SAnimatedMesh.h>
28 #include <IAnimatedMeshSceneNode.h>
29
30 inline static void applyShadeFactor(video::SColor& color, float factor)
31 {
32         color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
33         color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
34         color.setBlue(core::clamp(core::round32(color.getBlue()*factor), 0, 255));
35 }
36
37 void applyFacesShading(video::SColor &color, const v3f &normal)
38 {
39         /*
40                 Some drawtypes have normals set to (0, 0, 0), this must result in
41                 maximum brightness: shade factor 1.0.
42                 Shade factors for aligned cube faces are:
43                 +Y 1.000000 sqrt(1.0)
44                 -Y 0.447213 sqrt(0.2)
45                 +-X 0.670820 sqrt(0.45)
46                 +-Z 0.836660 sqrt(0.7)
47         */
48         float x2 = normal.X * normal.X;
49         float y2 = normal.Y * normal.Y;
50         float z2 = normal.Z * normal.Z;
51         if (normal.Y < 0)
52                 applyShadeFactor(color, 0.670820f * x2 + 0.447213f * y2 + 0.836660f * z2);
53         else if ((x2 > 1e-3) || (z2 > 1e-3))
54                 applyShadeFactor(color, 0.670820f * x2 + 1.000000f * y2 + 0.836660f * z2);
55 }
56
57 scene::IAnimatedMesh* createCubeMesh(v3f scale)
58 {
59         video::SColor c(255,255,255,255);
60         video::S3DVertex vertices[24] =
61         {
62                 // Up
63                 video::S3DVertex(-0.5,+0.5,-0.5, 0,1,0, c, 0,1),
64                 video::S3DVertex(-0.5,+0.5,+0.5, 0,1,0, c, 0,0),
65                 video::S3DVertex(+0.5,+0.5,+0.5, 0,1,0, c, 1,0),
66                 video::S3DVertex(+0.5,+0.5,-0.5, 0,1,0, c, 1,1),
67                 // Down
68                 video::S3DVertex(-0.5,-0.5,-0.5, 0,-1,0, c, 0,0),
69                 video::S3DVertex(+0.5,-0.5,-0.5, 0,-1,0, c, 1,0),
70                 video::S3DVertex(+0.5,-0.5,+0.5, 0,-1,0, c, 1,1),
71                 video::S3DVertex(-0.5,-0.5,+0.5, 0,-1,0, c, 0,1),
72                 // Right
73                 video::S3DVertex(+0.5,-0.5,-0.5, 1,0,0, c, 0,1),
74                 video::S3DVertex(+0.5,+0.5,-0.5, 1,0,0, c, 0,0),
75                 video::S3DVertex(+0.5,+0.5,+0.5, 1,0,0, c, 1,0),
76                 video::S3DVertex(+0.5,-0.5,+0.5, 1,0,0, c, 1,1),
77                 // Left
78                 video::S3DVertex(-0.5,-0.5,-0.5, -1,0,0, c, 1,1),
79                 video::S3DVertex(-0.5,-0.5,+0.5, -1,0,0, c, 0,1),
80                 video::S3DVertex(-0.5,+0.5,+0.5, -1,0,0, c, 0,0),
81                 video::S3DVertex(-0.5,+0.5,-0.5, -1,0,0, c, 1,0),
82                 // Back
83                 video::S3DVertex(-0.5,-0.5,+0.5, 0,0,1, c, 1,1),
84                 video::S3DVertex(+0.5,-0.5,+0.5, 0,0,1, c, 0,1),
85                 video::S3DVertex(+0.5,+0.5,+0.5, 0,0,1, c, 0,0),
86                 video::S3DVertex(-0.5,+0.5,+0.5, 0,0,1, c, 1,0),
87                 // Front
88                 video::S3DVertex(-0.5,-0.5,-0.5, 0,0,-1, c, 0,1),
89                 video::S3DVertex(-0.5,+0.5,-0.5, 0,0,-1, c, 0,0),
90                 video::S3DVertex(+0.5,+0.5,-0.5, 0,0,-1, c, 1,0),
91                 video::S3DVertex(+0.5,-0.5,-0.5, 0,0,-1, c, 1,1),
92         };
93
94         u16 indices[6] = {0,1,2,2,3,0};
95
96         scene::SMesh *mesh = new scene::SMesh();
97         for (u32 i=0; i<6; ++i)
98         {
99                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
100                 buf->append(vertices + 4 * i, 4, indices, 6);
101                 // Set default material
102                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
103                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
104                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
105                 // Add mesh buffer to mesh
106                 mesh->addMeshBuffer(buf);
107                 buf->drop();
108         }
109
110         scene::SAnimatedMesh *anim_mesh = new scene::SAnimatedMesh(mesh);
111         mesh->drop();
112         scaleMesh(anim_mesh, scale);  // also recalculates bounding box
113         return anim_mesh;
114 }
115
116 void scaleMesh(scene::IMesh *mesh, v3f scale)
117 {
118         if (mesh == NULL)
119                 return;
120
121         aabb3f bbox;
122         bbox.reset(0, 0, 0);
123
124         u32 mc = mesh->getMeshBufferCount();
125         for (u32 j = 0; j < mc; j++) {
126                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
127                 const u32 stride = getVertexPitchFromType(buf->getVertexType());
128                 u32 vertex_count = buf->getVertexCount();
129                 u8 *vertices = (u8 *)buf->getVertices();
130                 for (u32 i = 0; i < vertex_count; i++)
131                         ((video::S3DVertex *)(vertices + i * stride))->Pos *= scale;
132
133                 buf->recalculateBoundingBox();
134
135                 // calculate total bounding box
136                 if (j == 0)
137                         bbox = buf->getBoundingBox();
138                 else
139                         bbox.addInternalBox(buf->getBoundingBox());
140         }
141         mesh->setBoundingBox(bbox);
142 }
143
144 void translateMesh(scene::IMesh *mesh, v3f vec)
145 {
146         if (mesh == NULL)
147                 return;
148
149         aabb3f bbox;
150         bbox.reset(0, 0, 0);
151
152         u32 mc = mesh->getMeshBufferCount();
153         for (u32 j = 0; j < mc; j++) {
154                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
155                 const u32 stride = getVertexPitchFromType(buf->getVertexType());
156                 u32 vertex_count = buf->getVertexCount();
157                 u8 *vertices = (u8 *)buf->getVertices();
158                 for (u32 i = 0; i < vertex_count; i++)
159                         ((video::S3DVertex *)(vertices + i * stride))->Pos += vec;
160
161                 buf->recalculateBoundingBox();
162
163                 // calculate total bounding box
164                 if (j == 0)
165                         bbox = buf->getBoundingBox();
166                 else
167                         bbox.addInternalBox(buf->getBoundingBox());
168         }
169         mesh->setBoundingBox(bbox);
170 }
171
172 void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor &color)
173 {
174         const u32 stride = getVertexPitchFromType(buf->getVertexType());
175         u32 vertex_count = buf->getVertexCount();
176         u8 *vertices = (u8 *) buf->getVertices();
177         for (u32 i = 0; i < vertex_count; i++)
178                 ((video::S3DVertex *) (vertices + i * stride))->Color = color;
179 }
180
181 void setAnimatedMeshColor(scene::IAnimatedMeshSceneNode *node, const video::SColor &color)
182 {
183         for (u32 i = 0; i < node->getMaterialCount(); ++i) {
184                 node->getMaterial(i).EmissiveColor = color;
185         }
186 }
187
188 void setMeshColor(scene::IMesh *mesh, const video::SColor &color)
189 {
190         if (mesh == NULL)
191                 return;
192
193         u32 mc = mesh->getMeshBufferCount();
194         for (u32 j = 0; j < mc; j++)
195                 setMeshBufferColor(mesh->getMeshBuffer(j), color);
196 }
197
198 void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count)
199 {
200         const u32 stride = getVertexPitchFromType(buf->getVertexType());
201         assert(buf->getVertexCount() >= count);
202         u8 *vertices = (u8 *) buf->getVertices();
203         for (u32 i = 0; i < count; i++)
204                 ((video::S3DVertex*) (vertices + i * stride))->TCoords = uv[i];
205 }
206
207 template <typename F>
208 static void applyToMesh(scene::IMesh *mesh, const F &fn)
209 {
210         u16 mc = mesh->getMeshBufferCount();
211         for (u16 j = 0; j < mc; j++) {
212                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
213                 const u32 stride = getVertexPitchFromType(buf->getVertexType());
214                 u32 vertex_count = buf->getVertexCount();
215                 char *vertices = reinterpret_cast<char *>(buf->getVertices());
216                 for (u32 i = 0; i < vertex_count; i++)
217                         fn(reinterpret_cast<video::S3DVertex *>(vertices + i * stride));
218         }
219 }
220
221 void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor)
222 {
223         const u32 stride = getVertexPitchFromType(buf->getVertexType());
224         u32 vertex_count = buf->getVertexCount();
225         u8 *vertices = (u8 *) buf->getVertices();
226         for (u32 i = 0; i < vertex_count; i++) {
227                 video::S3DVertex *vertex = (video::S3DVertex *) (vertices + i * stride);
228                 video::SColor *vc = &(vertex->Color);
229                 // Reset color
230                 *vc = *buffercolor;
231                 // Apply shading
232                 applyFacesShading(*vc, vertex->Normal);
233         }
234 }
235
236 void setMeshColorByNormalXYZ(scene::IMesh *mesh,
237                 const video::SColor &colorX,
238                 const video::SColor &colorY,
239                 const video::SColor &colorZ)
240 {
241         if (!mesh)
242                 return;
243         auto colorizator = [=] (video::S3DVertex *vertex) {
244                 f32 x = fabs(vertex->Normal.X);
245                 f32 y = fabs(vertex->Normal.Y);
246                 f32 z = fabs(vertex->Normal.Z);
247                 if (x >= y && x >= z)
248                         vertex->Color = colorX;
249                 else if (y >= z)
250                         vertex->Color = colorY;
251                 else
252                         vertex->Color = colorZ;
253         };
254         applyToMesh(mesh, colorizator);
255 }
256
257 void setMeshColorByNormal(scene::IMesh *mesh, const v3f &normal,
258                 const video::SColor &color)
259 {
260         if (!mesh)
261                 return;
262         auto colorizator = [normal, color] (video::S3DVertex *vertex) {
263                 if (vertex->Normal == normal)
264                         vertex->Color = color;
265         };
266         applyToMesh(mesh, colorizator);
267 }
268
269 template <float v3f::*U, float v3f::*V>
270 static void rotateMesh(scene::IMesh *mesh, float degrees)
271 {
272         degrees *= M_PI / 180.0f;
273         float c = std::cos(degrees);
274         float s = std::sin(degrees);
275         auto rotator = [c, s] (video::S3DVertex *vertex) {
276                 float u = vertex->Pos.*U;
277                 float v = vertex->Pos.*V;
278                 vertex->Pos.*U = c * u - s * v;
279                 vertex->Pos.*V = s * u + c * v;
280         };
281         applyToMesh(mesh, rotator);
282 }
283
284 void rotateMeshXYby(scene::IMesh *mesh, f64 degrees)
285 {
286         rotateMesh<&v3f::X, &v3f::Y>(mesh, degrees);
287 }
288
289 void rotateMeshXZby(scene::IMesh *mesh, f64 degrees)
290 {
291         rotateMesh<&v3f::X, &v3f::Z>(mesh, degrees);
292 }
293
294 void rotateMeshYZby(scene::IMesh *mesh, f64 degrees)
295 {
296         rotateMesh<&v3f::Y, &v3f::Z>(mesh, degrees);
297 }
298
299 void rotateMeshBy6dFacedir(scene::IMesh *mesh, int facedir)
300 {
301         int axisdir = facedir >> 2;
302         facedir &= 0x03;
303         switch (facedir) {
304                 case 1: rotateMeshXZby(mesh, -90); break;
305                 case 2: rotateMeshXZby(mesh, 180); break;
306                 case 3: rotateMeshXZby(mesh, 90); break;
307         }
308         switch (axisdir) {
309                 case 1: rotateMeshYZby(mesh, 90); break; // z+
310                 case 2: rotateMeshYZby(mesh, -90); break; // z-
311                 case 3: rotateMeshXYby(mesh, -90); break; // x+
312                 case 4: rotateMeshXYby(mesh, 90); break; // x-
313                 case 5: rotateMeshXYby(mesh, -180); break;
314         }
315 }
316
317 void recalculateBoundingBox(scene::IMesh *src_mesh)
318 {
319         aabb3f bbox;
320         bbox.reset(0,0,0);
321         for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
322                 scene::IMeshBuffer *buf = src_mesh->getMeshBuffer(j);
323                 buf->recalculateBoundingBox();
324                 if (j == 0)
325                         bbox = buf->getBoundingBox();
326                 else
327                         bbox.addInternalBox(buf->getBoundingBox());
328         }
329         src_mesh->setBoundingBox(bbox);
330 }
331
332 bool checkMeshNormals(scene::IMesh *mesh)
333 {
334         // Assume correct normals if this many first faces get it right.
335         static const u16 MAX_FACES_TO_CHECK = 9;
336
337         u32 buffer_count = mesh->getMeshBufferCount();
338
339         for (u32 i = 0; i < buffer_count; i++) {
340                 scene::IMeshBuffer *buffer = mesh->getMeshBuffer(i);
341
342                 // Here we intentionally check only first normal, assuming that if buffer
343                 // has it valid, then most likely all other ones are fine too. We can
344                 // check all of the normals to have length, but it seems like an overkill
345                 // hurting the performance and covering only really weird broken models.
346                 f32 length = buffer->getNormal(0).getLength();
347
348                 if (!std::isfinite(length) || length < 1e-10f)
349                         return false;
350
351                 const u16 count = MYMIN(MAX_FACES_TO_CHECK * 3, buffer->getIndexCount() - 3);
352                 for (u16 i = 0; i < count; i += 3) {
353
354                         core::plane3df plane(buffer->getPosition(buffer->getIndices()[i]),
355                                         buffer->getPosition(buffer->getIndices()[i+1]),
356                                         buffer->getPosition(buffer->getIndices()[i+2]));
357
358                         for (u16 j = 0; j < 3; j++)
359                                 if (plane.Normal.dotProduct(buffer->getNormal(buffer->getIndices()[i+j])) <= 0)
360                                         return false;
361                 }
362
363         }
364
365         return true;
366 }
367
368 scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer)
369 {
370         switch (mesh_buffer->getVertexType()) {
371         case video::EVT_STANDARD: {
372                 video::S3DVertex *v = (video::S3DVertex *) mesh_buffer->getVertices();
373                 u16 *indices = mesh_buffer->getIndices();
374                 scene::SMeshBuffer *cloned_buffer = new scene::SMeshBuffer();
375                 cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
376                         mesh_buffer->getIndexCount());
377                 return cloned_buffer;
378         }
379         case video::EVT_2TCOORDS: {
380                 video::S3DVertex2TCoords *v =
381                         (video::S3DVertex2TCoords *) mesh_buffer->getVertices();
382                 u16 *indices = mesh_buffer->getIndices();
383                 scene::SMeshBufferLightMap *cloned_buffer =
384                         new scene::SMeshBufferLightMap();
385                 cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
386                         mesh_buffer->getIndexCount());
387                 return cloned_buffer;
388         }
389         case video::EVT_TANGENTS: {
390                 video::S3DVertexTangents *v =
391                         (video::S3DVertexTangents *) mesh_buffer->getVertices();
392                 u16 *indices = mesh_buffer->getIndices();
393                 scene::SMeshBufferTangents *cloned_buffer =
394                         new scene::SMeshBufferTangents();
395                 cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
396                         mesh_buffer->getIndexCount());
397                 return cloned_buffer;
398         }
399         }
400         // This should not happen.
401         sanity_check(false);
402         return NULL;
403 }
404
405 scene::SMesh* cloneMesh(scene::IMesh *src_mesh)
406 {
407         scene::SMesh* dst_mesh = new scene::SMesh();
408         for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
409                 scene::IMeshBuffer *temp_buf = cloneMeshBuffer(
410                         src_mesh->getMeshBuffer(j));
411                 dst_mesh->addMeshBuffer(temp_buf);
412                 temp_buf->drop();
413
414         }
415         return dst_mesh;
416 }
417
418 scene::IMesh* convertNodeboxesToMesh(const std::vector<aabb3f> &boxes,
419                 const f32 *uv_coords, float expand)
420 {
421         scene::SMesh* dst_mesh = new scene::SMesh();
422
423         for (u16 j = 0; j < 6; j++)
424         {
425                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
426                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
427                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
428                 dst_mesh->addMeshBuffer(buf);
429                 buf->drop();
430         }
431
432         video::SColor c(255,255,255,255);
433
434         for (aabb3f box : boxes) {
435                 box.repair();
436
437                 box.MinEdge.X -= expand;
438                 box.MinEdge.Y -= expand;
439                 box.MinEdge.Z -= expand;
440                 box.MaxEdge.X += expand;
441                 box.MaxEdge.Y += expand;
442                 box.MaxEdge.Z += expand;
443
444                 // Compute texture UV coords
445                 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
446                 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
447                 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
448                 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
449                 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
450                 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
451
452                 f32 txc_default[24] = {
453                         // up
454                         tx1, 1 - tz2, tx2, 1 - tz1,
455                         // down
456                         tx1, tz1, tx2, tz2,
457                         // right
458                         tz1, 1 - ty2, tz2, 1 - ty1,
459                         // left
460                         1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1,
461                         // back
462                         1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1,
463                         // front
464                         tx1, 1 - ty2, tx2, 1 - ty1,
465                 };
466
467                 // use default texture UV mapping if not provided
468                 const f32 *txc = uv_coords ? uv_coords : txc_default;
469
470                 v3f min = box.MinEdge;
471                 v3f max = box.MaxEdge;
472
473                 video::S3DVertex vertices[24] =
474                 {
475                         // up
476                         video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]),
477                         video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]),
478                         video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]),
479                         video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]),
480                         // down
481                         video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]),
482                         video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]),
483                         video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]),
484                         video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]),
485                         // right
486                         video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]),
487                         video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]),
488                         video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]),
489                         video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]),
490                         // left
491                         video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]),
492                         video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]),
493                         video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]),
494                         video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]),
495                         // back
496                         video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[16],txc[17]),
497                         video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[18],txc[17]),
498                         video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[18],txc[19]),
499                         video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[16],txc[19]),
500                         // front
501                         video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[20],txc[21]),
502                         video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[22],txc[21]),
503                         video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[22],txc[23]),
504                         video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]),
505                 };
506
507                 u16 indices[] = {0,1,2,2,3,0};
508
509                 for(u16 j = 0; j < 24; j += 4)
510                 {
511                         scene::IMeshBuffer *buf = dst_mesh->getMeshBuffer(j / 4);
512                         buf->append(vertices + j, 4, indices, 6);
513                 }
514         }
515         return dst_mesh;
516 }