]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mesh.cpp
Document zoom_fov in settingtypes.txt and minetest.conf.example
[dragonfireclient.git] / src / 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 <iostream>
25 #include <IAnimatedMesh.h>
26 #include <SAnimatedMesh.h>
27
28 // In Irrlicht 1.8 the signature of ITexture::lock was changed from
29 // (bool, u32) to (E_TEXTURE_LOCK_MODE, u32).
30 #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7
31 #define MY_ETLM_READ_ONLY true
32 #else
33 #define MY_ETLM_READ_ONLY video::ETLM_READ_ONLY
34 #endif
35
36 static void applyFacesShading(video::SColor& color, float factor)
37 {
38         color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
39         color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
40         color.setBlue(core::clamp(core::round32(color.getBlue()*factor), 0, 255));
41 }
42
43 scene::IAnimatedMesh* createCubeMesh(v3f scale)
44 {
45         video::SColor c(255,255,255,255);
46         video::S3DVertex vertices[24] =
47         {
48                 // Up
49                 video::S3DVertex(-0.5,+0.5,-0.5, 0,1,0, c, 0,1),
50                 video::S3DVertex(-0.5,+0.5,+0.5, 0,1,0, c, 0,0),
51                 video::S3DVertex(+0.5,+0.5,+0.5, 0,1,0, c, 1,0),
52                 video::S3DVertex(+0.5,+0.5,-0.5, 0,1,0, c, 1,1),
53                 // Down
54                 video::S3DVertex(-0.5,-0.5,-0.5, 0,-1,0, c, 0,0),
55                 video::S3DVertex(+0.5,-0.5,-0.5, 0,-1,0, c, 1,0),
56                 video::S3DVertex(+0.5,-0.5,+0.5, 0,-1,0, c, 1,1),
57                 video::S3DVertex(-0.5,-0.5,+0.5, 0,-1,0, c, 0,1),
58                 // Right
59                 video::S3DVertex(+0.5,-0.5,-0.5, 1,0,0, c, 0,1),
60                 video::S3DVertex(+0.5,+0.5,-0.5, 1,0,0, c, 0,0),
61                 video::S3DVertex(+0.5,+0.5,+0.5, 1,0,0, c, 1,0),
62                 video::S3DVertex(+0.5,-0.5,+0.5, 1,0,0, c, 1,1),
63                 // Left
64                 video::S3DVertex(-0.5,-0.5,-0.5, -1,0,0, c, 1,1),
65                 video::S3DVertex(-0.5,-0.5,+0.5, -1,0,0, c, 0,1),
66                 video::S3DVertex(-0.5,+0.5,+0.5, -1,0,0, c, 0,0),
67                 video::S3DVertex(-0.5,+0.5,-0.5, -1,0,0, c, 1,0),
68                 // Back
69                 video::S3DVertex(-0.5,-0.5,+0.5, 0,0,1, c, 1,1),
70                 video::S3DVertex(+0.5,-0.5,+0.5, 0,0,1, c, 0,1),
71                 video::S3DVertex(+0.5,+0.5,+0.5, 0,0,1, c, 0,0),
72                 video::S3DVertex(-0.5,+0.5,+0.5, 0,0,1, c, 1,0),
73                 // Front
74                 video::S3DVertex(-0.5,-0.5,-0.5, 0,0,-1, c, 0,1),
75                 video::S3DVertex(-0.5,+0.5,-0.5, 0,0,-1, c, 0,0),
76                 video::S3DVertex(+0.5,+0.5,-0.5, 0,0,-1, c, 1,0),
77                 video::S3DVertex(+0.5,-0.5,-0.5, 0,0,-1, c, 1,1),
78         };
79
80         u16 indices[6] = {0,1,2,2,3,0};
81
82         scene::SMesh *mesh = new scene::SMesh();
83         for (u32 i=0; i<6; ++i)
84         {
85                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
86                 buf->append(vertices + 4 * i, 4, indices, 6);
87                 // Set default material
88                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
89                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
90                 buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
91                 // Add mesh buffer to mesh
92                 mesh->addMeshBuffer(buf);
93                 buf->drop();
94         }
95
96         scene::SAnimatedMesh *anim_mesh = new scene::SAnimatedMesh(mesh);
97         mesh->drop();
98         scaleMesh(anim_mesh, scale);  // also recalculates bounding box
99         return anim_mesh;
100 }
101
102 void scaleMesh(scene::IMesh *mesh, v3f scale)
103 {
104         if (mesh == NULL)
105                 return;
106
107         aabb3f bbox;
108         bbox.reset(0, 0, 0);
109
110         u32 mc = mesh->getMeshBufferCount();
111         for (u32 j = 0; j < mc; j++) {
112                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
113                 const u32 stride = getVertexPitchFromType(buf->getVertexType());
114                 u32 vertex_count = buf->getVertexCount();
115                 u8 *vertices = (u8 *)buf->getVertices();
116                 for (u32 i = 0; i < vertex_count; i++)
117                         ((video::S3DVertex *)(vertices + i * stride))->Pos *= scale;
118
119                 buf->recalculateBoundingBox();
120
121                 // calculate total bounding box
122                 if (j == 0)
123                         bbox = buf->getBoundingBox();
124                 else
125                         bbox.addInternalBox(buf->getBoundingBox());
126         }
127         mesh->setBoundingBox(bbox);
128 }
129
130 void translateMesh(scene::IMesh *mesh, v3f vec)
131 {
132         if (mesh == NULL)
133                 return;
134
135         aabb3f bbox;
136         bbox.reset(0, 0, 0);
137
138         u32 mc = mesh->getMeshBufferCount();
139         for (u32 j = 0; j < mc; j++) {
140                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
141                 const u32 stride = getVertexPitchFromType(buf->getVertexType());
142                 u32 vertex_count = buf->getVertexCount();
143                 u8 *vertices = (u8 *)buf->getVertices();
144                 for (u32 i = 0; i < vertex_count; i++)
145                         ((video::S3DVertex *)(vertices + i * stride))->Pos += vec;
146
147                 buf->recalculateBoundingBox();
148
149                 // calculate total bounding box
150                 if (j == 0)
151                         bbox = buf->getBoundingBox();
152                 else
153                         bbox.addInternalBox(buf->getBoundingBox());
154         }
155         mesh->setBoundingBox(bbox);
156 }
157
158
159 void setMeshColor(scene::IMesh *mesh, const video::SColor &color)
160 {
161         if (mesh == NULL)
162                 return;
163
164         u32 mc = mesh->getMeshBufferCount();
165         for (u32 j = 0; j < mc; j++) {
166                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
167                 const u32 stride = getVertexPitchFromType(buf->getVertexType());
168                 u32 vertex_count = buf->getVertexCount();
169                 u8 *vertices = (u8 *)buf->getVertices();
170                 for (u32 i = 0; i < vertex_count; i++)
171                         ((video::S3DVertex *)(vertices + i * stride))->Color = color;
172         }
173 }
174
175 void shadeMeshFaces(scene::IMesh *mesh)
176 {
177         if (mesh == NULL)
178                 return;
179
180         u32 mc = mesh->getMeshBufferCount();
181         for (u32 j = 0; j < mc; j++) {
182                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
183                 const u32 stride = getVertexPitchFromType(buf->getVertexType());
184                 u32 vertex_count = buf->getVertexCount();
185                 u8 *vertices = (u8 *)buf->getVertices();
186                 for (u32 i = 0; i < vertex_count; i++) {
187                         video::S3DVertex *vertex = (video::S3DVertex *)(vertices + i * stride);
188                         video::SColor &vc = vertex->Color;
189                         if (vertex->Normal.Y < -0.5) {
190                                 applyFacesShading (vc, 0.447213);
191                         } else if (vertex->Normal.Z > 0.5) {
192                                 applyFacesShading (vc, 0.670820);
193                         } else if (vertex->Normal.Z < -0.5) {
194                                 applyFacesShading (vc, 0.670820);
195                         } else if (vertex->Normal.X > 0.5) {
196                                 applyFacesShading (vc, 0.836660);
197                         } else if (vertex->Normal.X < -0.5) {
198                                 applyFacesShading (vc, 0.836660);
199                         }
200                 }
201         }
202 }
203
204 void setMeshColorByNormalXYZ(scene::IMesh *mesh,
205                 const video::SColor &colorX,
206                 const video::SColor &colorY,
207                 const video::SColor &colorZ)
208 {
209         if (mesh == NULL)
210                 return;
211
212         u16 mc = mesh->getMeshBufferCount();
213         for (u16 j = 0; j < mc; j++) {
214                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
215                 const u32 stride = getVertexPitchFromType(buf->getVertexType());
216                 u32 vertex_count = buf->getVertexCount();
217                 u8 *vertices = (u8 *)buf->getVertices();
218                 for (u32 i = 0; i < vertex_count; i++) {
219                         video::S3DVertex *vertex = (video::S3DVertex *)(vertices + i * stride);
220                         f32 x = fabs(vertex->Normal.X);
221                         f32 y = fabs(vertex->Normal.Y);
222                         f32 z = fabs(vertex->Normal.Z);
223                         if (x >= y && x >= z)
224                                 vertex->Color = colorX;
225                         else if (y >= z)
226                                 vertex->Color = colorY;
227                         else
228                                 vertex->Color = colorZ;
229
230                 }
231         }
232 }
233
234 void rotateMeshXYby(scene::IMesh *mesh, f64 degrees)
235 {
236         u16 mc = mesh->getMeshBufferCount();
237         for (u16 j = 0; j < mc; j++) {
238                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
239                 const u32 stride = getVertexPitchFromType(buf->getVertexType());
240                 u32 vertex_count = buf->getVertexCount();
241                 u8 *vertices = (u8 *)buf->getVertices();
242                 for (u32 i = 0; i < vertex_count; i++)
243                         ((video::S3DVertex *)(vertices + i * stride))->Pos.rotateXYBy(degrees);
244         }
245 }
246
247 void rotateMeshXZby(scene::IMesh *mesh, f64 degrees)
248 {
249         u16 mc = mesh->getMeshBufferCount();
250         for (u16 j = 0; j < mc; j++) {
251                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
252                 const u32 stride = getVertexPitchFromType(buf->getVertexType());
253                 u32 vertex_count = buf->getVertexCount();
254                 u8 *vertices = (u8 *)buf->getVertices();
255                 for (u32 i = 0; i < vertex_count; i++)
256                         ((video::S3DVertex *)(vertices + i * stride))->Pos.rotateXZBy(degrees);
257         }
258 }
259
260 void rotateMeshYZby(scene::IMesh *mesh, f64 degrees)
261 {
262         u16 mc = mesh->getMeshBufferCount();
263         for (u16 j = 0; j < mc; j++) {
264                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
265                 const u32 stride = getVertexPitchFromType(buf->getVertexType());
266                 u32 vertex_count = buf->getVertexCount();
267                 u8 *vertices = (u8 *)buf->getVertices();
268                 for (u32 i = 0; i < vertex_count; i++)
269                         ((video::S3DVertex *)(vertices + i * stride))->Pos.rotateYZBy(degrees);
270         }
271 }
272
273 void rotateMeshBy6dFacedir(scene::IMesh *mesh, int facedir)
274 {
275         int axisdir = facedir >> 2;
276         facedir &= 0x03;
277
278         u16 mc = mesh->getMeshBufferCount();
279         for (u16 j = 0; j < mc; j++) {
280                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
281                 const u32 stride = getVertexPitchFromType(buf->getVertexType());
282                 u32 vertex_count = buf->getVertexCount();
283                 u8 *vertices = (u8 *)buf->getVertices();
284                 for (u32 i = 0; i < vertex_count; i++) {
285                         video::S3DVertex *vertex = (video::S3DVertex *)(vertices + i * stride);
286                         switch (axisdir) {
287                                 case 0:
288                                         if (facedir == 1)
289                                                 vertex->Pos.rotateXZBy(-90);
290                                         else if (facedir == 2)
291                                                 vertex->Pos.rotateXZBy(180);
292                                         else if (facedir == 3)
293                                                 vertex->Pos.rotateXZBy(90);
294                                         break;
295                                 case 1: // z+
296                                         vertex->Pos.rotateYZBy(90);
297                                         if (facedir == 1)
298                                                 vertex->Pos.rotateXYBy(90);
299                                         else if (facedir == 2)
300                                                 vertex->Pos.rotateXYBy(180);
301                                         else if (facedir == 3)
302                                                 vertex->Pos.rotateXYBy(-90);
303                                         break;
304                                 case 2: //z-
305                                         vertex->Pos.rotateYZBy(-90);
306                                         if (facedir == 1)
307                                                 vertex->Pos.rotateXYBy(-90);
308                                         else if (facedir == 2)
309                                                 vertex->Pos.rotateXYBy(180);
310                                         else if (facedir == 3)
311                                                 vertex->Pos.rotateXYBy(90);
312                                         break;
313                                 case 3:  //x+
314                                         vertex->Pos.rotateXYBy(-90);
315                                         if (facedir == 1)
316                                                 vertex->Pos.rotateYZBy(90);
317                                         else if (facedir == 2)
318                                                 vertex->Pos.rotateYZBy(180);
319                                         else if (facedir == 3)
320                                                 vertex->Pos.rotateYZBy(-90);
321                                         break;
322                                 case 4:  //x-
323                                         vertex->Pos.rotateXYBy(90);
324                                         if (facedir == 1)
325                                                 vertex->Pos.rotateYZBy(-90);
326                                         else if (facedir == 2)
327                                                 vertex->Pos.rotateYZBy(180);
328                                         else if (facedir == 3)
329                                                 vertex->Pos.rotateYZBy(90);
330                                         break;
331                                 case 5:
332                                         vertex->Pos.rotateXYBy(-180);
333                                         if (facedir == 1)
334                                                 vertex->Pos.rotateXZBy(90);
335                                         else if (facedir == 2)
336                                                 vertex->Pos.rotateXZBy(180);
337                                         else if (facedir == 3)
338                                                 vertex->Pos.rotateXZBy(-90);
339                                         break;
340                                 default:
341                                         break;
342                         }
343                 }
344         }
345 }
346
347 void recalculateBoundingBox(scene::IMesh *src_mesh)
348 {
349         aabb3f bbox;
350         bbox.reset(0,0,0);
351         for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
352                 scene::IMeshBuffer *buf = src_mesh->getMeshBuffer(j);
353                 buf->recalculateBoundingBox();
354                 if (j == 0)
355                         bbox = buf->getBoundingBox();
356                 else
357                         bbox.addInternalBox(buf->getBoundingBox());
358         }
359         src_mesh->setBoundingBox(bbox);
360 }
361
362 scene::IMesh* cloneMesh(scene::IMesh *src_mesh)
363 {
364         scene::SMesh* dst_mesh = new scene::SMesh();
365         for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
366                 scene::IMeshBuffer *buf = src_mesh->getMeshBuffer(j);
367                 switch (buf->getVertexType()) {
368                         case video::EVT_STANDARD: {
369                                 video::S3DVertex *v =
370                                         (video::S3DVertex *) buf->getVertices();
371                                 u16 *indices = (u16*)buf->getIndices();
372                                 scene::SMeshBuffer *temp_buf = new scene::SMeshBuffer();
373                                 temp_buf->append(v, buf->getVertexCount(),
374                                         indices, buf->getIndexCount());
375                                 dst_mesh->addMeshBuffer(temp_buf);
376                                 temp_buf->drop();
377                                 break;
378                         }
379                         case video::EVT_2TCOORDS: {
380                                 video::S3DVertex2TCoords *v =
381                                         (video::S3DVertex2TCoords *) buf->getVertices();
382                                 u16 *indices = (u16*)buf->getIndices();
383                                 scene::SMeshBufferTangents *temp_buf =
384                                         new scene::SMeshBufferTangents();
385                                 temp_buf->append(v, buf->getVertexCount(),
386                                         indices, buf->getIndexCount());
387                                 dst_mesh->addMeshBuffer(temp_buf);
388                                 temp_buf->drop();
389                                 break;
390                         }
391                         case video::EVT_TANGENTS: {
392                                 video::S3DVertexTangents *v =
393                                         (video::S3DVertexTangents *) buf->getVertices();
394                                 u16 *indices = (u16*)buf->getIndices();
395                                 scene::SMeshBufferTangents *temp_buf =
396                                         new scene::SMeshBufferTangents();
397                                 temp_buf->append(v, buf->getVertexCount(),
398                                         indices, buf->getIndexCount());
399                                 dst_mesh->addMeshBuffer(temp_buf);
400                                 temp_buf->drop();
401                                 break;
402                         }
403                 }
404         }
405         return dst_mesh;
406 }
407
408 scene::IMesh* convertNodeboxesToMesh(const std::vector<aabb3f> &boxes,
409                 const f32 *uv_coords, float expand)
410 {
411         scene::SMesh* dst_mesh = new scene::SMesh();
412
413         for (u16 j = 0; j < 6; j++)
414         {
415                 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
416                 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
417                 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
418                 dst_mesh->addMeshBuffer(buf);
419                 buf->drop();
420         }
421
422         video::SColor c(255,255,255,255);       
423
424         for (std::vector<aabb3f>::const_iterator
425                         i = boxes.begin();
426                         i != boxes.end(); ++i)
427         {
428                 aabb3f box = *i;
429                 box.repair();
430
431                 box.MinEdge.X -= expand;
432                 box.MinEdge.Y -= expand;
433                 box.MinEdge.Z -= expand;
434                 box.MaxEdge.X += expand;
435                 box.MaxEdge.Y += expand;
436                 box.MaxEdge.Z += expand;
437
438                 // Compute texture UV coords
439                 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
440                 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
441                 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
442                 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
443                 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
444                 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
445
446                 f32 txc_default[24] = {
447                         // up
448                         tx1, 1 - tz2, tx2, 1 - tz1,
449                         // down
450                         tx1, tz1, tx2, tz2,
451                         // right
452                         tz1, 1 - ty2, tz2, 1 - ty1,
453                         // left
454                         1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1,
455                         // back
456                         1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1,
457                         // front
458                         tx1, 1 - ty2, tx2, 1 - ty1,
459                 };
460
461                 // use default texture UV mapping if not provided
462                 const f32 *txc = uv_coords ? uv_coords : txc_default;
463
464                 v3f min = box.MinEdge;
465                 v3f max = box.MaxEdge;
466
467                 video::S3DVertex vertices[24] =
468                 {
469                         // up
470                         video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]),
471                         video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]),
472                         video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]),
473                         video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]),
474                         // down
475                         video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]),
476                         video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]),
477                         video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]),
478                         video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]),
479                         // right
480                         video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]),
481                         video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]),
482                         video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]),
483                         video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]),
484                         // left
485                         video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]),
486                         video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]),
487                         video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]),
488                         video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]),
489                         // back
490                         video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[16],txc[17]),
491                         video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[18],txc[17]),
492                         video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[18],txc[19]),
493                         video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[16],txc[19]),
494                         // front
495                         video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[20],txc[21]),
496                         video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[22],txc[21]),
497                         video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[22],txc[23]),
498                         video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]),
499                 };
500
501                 u16 indices[] = {0,1,2,2,3,0};
502
503                 for(u16 j = 0; j < 24; j += 4)
504                 {
505                         scene::IMeshBuffer *buf = dst_mesh->getMeshBuffer(j / 4);
506                         buf->append(vertices + j, 4, indices, 6);
507                 }
508         }
509         return dst_mesh;                                        
510 }
511
512 struct vcache
513 {
514         core::array<u32> tris;
515         float score;
516         s16 cachepos;
517         u16 NumActiveTris;
518 };
519
520 struct tcache
521 {
522         u16 ind[3];
523         float score;
524         bool drawn;
525 };
526
527 const u16 cachesize = 32;
528
529 float FindVertexScore(vcache *v)
530 {
531         const float CacheDecayPower = 1.5f;
532         const float LastTriScore = 0.75f;
533         const float ValenceBoostScale = 2.0f;
534         const float ValenceBoostPower = 0.5f;
535         const float MaxSizeVertexCache = 32.0f;
536
537         if (v->NumActiveTris == 0)
538         {
539                 // No tri needs this vertex!
540                 return -1.0f;
541         }
542
543         float Score = 0.0f;
544         int CachePosition = v->cachepos;
545         if (CachePosition < 0)
546         {
547                 // Vertex is not in FIFO cache - no score.
548         }
549         else
550         {
551                 if (CachePosition < 3)
552                 {
553                         // This vertex was used in the last triangle,
554                         // so it has a fixed score.
555                         Score = LastTriScore;
556                 }
557                 else
558                 {
559                         // Points for being high in the cache.
560                         const float Scaler = 1.0f / (MaxSizeVertexCache - 3);
561                         Score = 1.0f - (CachePosition - 3) * Scaler;
562                         Score = powf(Score, CacheDecayPower);
563                 }
564         }
565
566         // Bonus points for having a low number of tris still to
567         // use the vert, so we get rid of lone verts quickly.
568         float ValenceBoost = powf(v->NumActiveTris,
569                                 -ValenceBoostPower);
570         Score += ValenceBoostScale * ValenceBoost;
571
572         return Score;
573 }
574
575 /*
576         A specialized LRU cache for the Forsyth algorithm.
577 */
578
579 class f_lru
580 {
581
582 public:
583         f_lru(vcache *v, tcache *t): vc(v), tc(t)
584         {
585                 for (u16 i = 0; i < cachesize; i++)
586                 {
587                         cache[i] = -1;
588                 }
589         }
590
591         // Adds this vertex index and returns the highest-scoring triangle index
592         u32 add(u16 vert, bool updatetris = false)
593         {
594                 bool found = false;
595
596                 // Mark existing pos as empty
597                 for (u16 i = 0; i < cachesize; i++)
598                 {
599                         if (cache[i] == vert)
600                         {
601                                 // Move everything down
602                                 for (u16 j = i; j; j--)
603                                 {
604                                         cache[j] = cache[j - 1];
605                                 }
606
607                                 found = true;
608                                 break;
609                         }
610                 }
611
612                 if (!found)
613                 {
614                         if (cache[cachesize-1] != -1)
615                                 vc[cache[cachesize-1]].cachepos = -1;
616
617                         // Move everything down
618                         for (u16 i = cachesize - 1; i; i--)
619                         {
620                                 cache[i] = cache[i - 1];
621                         }
622                 }
623
624                 cache[0] = vert;
625
626                 u32 highest = 0;
627                 float hiscore = 0;
628
629                 if (updatetris)
630                 {
631                         // Update cache positions
632                         for (u16 i = 0; i < cachesize; i++)
633                         {
634                                 if (cache[i] == -1)
635                                         break;
636
637                                 vc[cache[i]].cachepos = i;
638                                 vc[cache[i]].score = FindVertexScore(&vc[cache[i]]);
639                         }
640
641                         // Update triangle scores
642                         for (u16 i = 0; i < cachesize; i++)
643                         {
644                                 if (cache[i] == -1)
645                                         break;
646
647                                 const u16 trisize = vc[cache[i]].tris.size();
648                                 for (u16 t = 0; t < trisize; t++)
649                                 {
650                                         tcache *tri = &tc[vc[cache[i]].tris[t]];
651
652                                         tri->score =
653                                                 vc[tri->ind[0]].score +
654                                                 vc[tri->ind[1]].score +
655                                                 vc[tri->ind[2]].score;
656
657                                         if (tri->score > hiscore)
658                                         {
659                                                 hiscore = tri->score;
660                                                 highest = vc[cache[i]].tris[t];
661                                         }
662                                 }
663                         }
664                 }
665
666                 return highest;
667         }
668
669 private:
670         s32 cache[cachesize];
671         vcache *vc;
672         tcache *tc;
673 };
674
675 /**
676 Vertex cache optimization according to the Forsyth paper:
677 http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
678
679 The function is thread-safe (read: you can optimize several meshes in different threads)
680
681 \param mesh Source mesh for the operation.  */
682 scene::IMesh* createForsythOptimizedMesh(const scene::IMesh *mesh)
683 {
684         if (!mesh)
685                 return 0;
686
687         scene::SMesh *newmesh = new scene::SMesh();
688         newmesh->BoundingBox = mesh->getBoundingBox();
689
690         const u32 mbcount = mesh->getMeshBufferCount();
691
692         for (u32 b = 0; b < mbcount; ++b)
693         {
694                 const scene::IMeshBuffer *mb = mesh->getMeshBuffer(b);
695
696                 if (mb->getIndexType() != video::EIT_16BIT)
697                 {
698                         //os::Printer::log("Cannot optimize a mesh with 32bit indices", ELL_ERROR);
699                         newmesh->drop();
700                         return 0;
701                 }
702
703                 const u32 icount = mb->getIndexCount();
704                 const u32 tcount = icount / 3;
705                 const u32 vcount = mb->getVertexCount();
706                 const u16 *ind = mb->getIndices();
707
708                 vcache *vc = new vcache[vcount];
709                 tcache *tc = new tcache[tcount];
710
711                 f_lru lru(vc, tc);
712
713                 // init
714                 for (u16 i = 0; i < vcount; i++)
715                 {
716                         vc[i].score = 0;
717                         vc[i].cachepos = -1;
718                         vc[i].NumActiveTris = 0;
719                 }
720
721                 // First pass: count how many times a vert is used
722                 for (u32 i = 0; i < icount; i += 3)
723                 {
724                         vc[ind[i]].NumActiveTris++;
725                         vc[ind[i + 1]].NumActiveTris++;
726                         vc[ind[i + 2]].NumActiveTris++;
727
728                         const u32 tri_ind = i/3;
729                         tc[tri_ind].ind[0] = ind[i];
730                         tc[tri_ind].ind[1] = ind[i + 1];
731                         tc[tri_ind].ind[2] = ind[i + 2];
732                 }
733
734                 // Second pass: list of each triangle
735                 for (u32 i = 0; i < tcount; i++)
736                 {
737                         vc[tc[i].ind[0]].tris.push_back(i);
738                         vc[tc[i].ind[1]].tris.push_back(i);
739                         vc[tc[i].ind[2]].tris.push_back(i);
740
741                         tc[i].drawn = false;
742                 }
743
744                 // Give initial scores
745                 for (u16 i = 0; i < vcount; i++)
746                 {
747                         vc[i].score = FindVertexScore(&vc[i]);
748                 }
749                 for (u32 i = 0; i < tcount; i++)
750                 {
751                         tc[i].score =
752                                         vc[tc[i].ind[0]].score +
753                                         vc[tc[i].ind[1]].score +
754                                         vc[tc[i].ind[2]].score;
755                 }
756
757                 switch(mb->getVertexType())
758                 {
759                         case video::EVT_STANDARD:
760                         {
761                                 video::S3DVertex *v = (video::S3DVertex *) mb->getVertices();
762
763                                 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
764                                 buf->Material = mb->getMaterial();
765
766                                 buf->Vertices.reallocate(vcount);
767                                 buf->Indices.reallocate(icount);
768
769                                 core::map<const video::S3DVertex, const u16> sind; // search index for fast operation
770                                 typedef core::map<const video::S3DVertex, const u16>::Node snode;
771
772                                 // Main algorithm
773                                 u32 highest = 0;
774                                 u32 drawcalls = 0;
775                                 for (;;)
776                                 {
777                                         if (tc[highest].drawn)
778                                         {
779                                                 bool found = false;
780                                                 float hiscore = 0;
781                                                 for (u32 t = 0; t < tcount; t++)
782                                                 {
783                                                         if (!tc[t].drawn)
784                                                         {
785                                                                 if (tc[t].score > hiscore)
786                                                                 {
787                                                                         highest = t;
788                                                                         hiscore = tc[t].score;
789                                                                         found = true;
790                                                                 }
791                                                         }
792                                                 }
793                                                 if (!found)
794                                                         break;
795                                         }
796
797                                         // Output the best triangle
798                                         u16 newind = buf->Vertices.size();
799
800                                         snode *s = sind.find(v[tc[highest].ind[0]]);
801
802                                         if (!s)
803                                         {
804                                                 buf->Vertices.push_back(v[tc[highest].ind[0]]);
805                                                 buf->Indices.push_back(newind);
806                                                 sind.insert(v[tc[highest].ind[0]], newind);
807                                                 newind++;
808                                         }
809                                         else
810                                         {
811                                                 buf->Indices.push_back(s->getValue());
812                                         }
813
814                                         s = sind.find(v[tc[highest].ind[1]]);
815
816                                         if (!s)
817                                         {
818                                                 buf->Vertices.push_back(v[tc[highest].ind[1]]);
819                                                 buf->Indices.push_back(newind);
820                                                 sind.insert(v[tc[highest].ind[1]], newind);
821                                                 newind++;
822                                         }
823                                         else
824                                         {
825                                                 buf->Indices.push_back(s->getValue());
826                                         }
827
828                                         s = sind.find(v[tc[highest].ind[2]]);
829
830                                         if (!s)
831                                         {
832                                                 buf->Vertices.push_back(v[tc[highest].ind[2]]);
833                                                 buf->Indices.push_back(newind);
834                                                 sind.insert(v[tc[highest].ind[2]], newind);
835                                         }
836                                         else
837                                         {
838                                                 buf->Indices.push_back(s->getValue());
839                                         }
840
841                                         vc[tc[highest].ind[0]].NumActiveTris--;
842                                         vc[tc[highest].ind[1]].NumActiveTris--;
843                                         vc[tc[highest].ind[2]].NumActiveTris--;
844
845                                         tc[highest].drawn = true;
846
847                                         for (u16 j = 0; j < 3; j++)
848                                         {
849                                                 vcache *vert = &vc[tc[highest].ind[j]];
850                                                 for (u16 t = 0; t < vert->tris.size(); t++)
851                                                 {
852                                                         if (highest == vert->tris[t])
853                                                         {
854                                                                 vert->tris.erase(t);
855                                                                 break;
856                                                         }
857                                                 }
858                                         }
859
860                                         lru.add(tc[highest].ind[0]);
861                                         lru.add(tc[highest].ind[1]);
862                                         highest = lru.add(tc[highest].ind[2], true);
863                                         drawcalls++;
864                                 }
865
866                                 buf->setBoundingBox(mb->getBoundingBox());
867                                 newmesh->addMeshBuffer(buf);
868                                 buf->drop();
869                         }
870                         break;
871                         case video::EVT_2TCOORDS:
872                         {
873                                 video::S3DVertex2TCoords *v = (video::S3DVertex2TCoords *) mb->getVertices();
874
875                                 scene::SMeshBufferLightMap *buf = new scene::SMeshBufferLightMap();
876                                 buf->Material = mb->getMaterial();
877
878                                 buf->Vertices.reallocate(vcount);
879                                 buf->Indices.reallocate(icount);
880
881                                 core::map<const video::S3DVertex2TCoords, const u16> sind; // search index for fast operation
882                                 typedef core::map<const video::S3DVertex2TCoords, const u16>::Node snode;
883
884                                 // Main algorithm
885                                 u32 highest = 0;
886                                 u32 drawcalls = 0;
887                                 for (;;)
888                                 {
889                                         if (tc[highest].drawn)
890                                         {
891                                                 bool found = false;
892                                                 float hiscore = 0;
893                                                 for (u32 t = 0; t < tcount; t++)
894                                                 {
895                                                         if (!tc[t].drawn)
896                                                         {
897                                                                 if (tc[t].score > hiscore)
898                                                                 {
899                                                                         highest = t;
900                                                                         hiscore = tc[t].score;
901                                                                         found = true;
902                                                                 }
903                                                         }
904                                                 }
905                                                 if (!found)
906                                                         break;
907                                         }
908
909                                         // Output the best triangle
910                                         u16 newind = buf->Vertices.size();
911
912                                         snode *s = sind.find(v[tc[highest].ind[0]]);
913
914                                         if (!s)
915                                         {
916                                                 buf->Vertices.push_back(v[tc[highest].ind[0]]);
917                                                 buf->Indices.push_back(newind);
918                                                 sind.insert(v[tc[highest].ind[0]], newind);
919                                                 newind++;
920                                         }
921                                         else
922                                         {
923                                                 buf->Indices.push_back(s->getValue());
924                                         }
925
926                                         s = sind.find(v[tc[highest].ind[1]]);
927
928                                         if (!s)
929                                         {
930                                                 buf->Vertices.push_back(v[tc[highest].ind[1]]);
931                                                 buf->Indices.push_back(newind);
932                                                 sind.insert(v[tc[highest].ind[1]], newind);
933                                                 newind++;
934                                         }
935                                         else
936                                         {
937                                                 buf->Indices.push_back(s->getValue());
938                                         }
939
940                                         s = sind.find(v[tc[highest].ind[2]]);
941
942                                         if (!s)
943                                         {
944                                                 buf->Vertices.push_back(v[tc[highest].ind[2]]);
945                                                 buf->Indices.push_back(newind);
946                                                 sind.insert(v[tc[highest].ind[2]], newind);
947                                         }
948                                         else
949                                         {
950                                                 buf->Indices.push_back(s->getValue());
951                                         }
952
953                                         vc[tc[highest].ind[0]].NumActiveTris--;
954                                         vc[tc[highest].ind[1]].NumActiveTris--;
955                                         vc[tc[highest].ind[2]].NumActiveTris--;
956
957                                         tc[highest].drawn = true;
958
959                                         for (u16 j = 0; j < 3; j++)
960                                         {
961                                                 vcache *vert = &vc[tc[highest].ind[j]];
962                                                 for (u16 t = 0; t < vert->tris.size(); t++)
963                                                 {
964                                                         if (highest == vert->tris[t])
965                                                         {
966                                                                 vert->tris.erase(t);
967                                                                 break;
968                                                         }
969                                                 }
970                                         }
971
972                                         lru.add(tc[highest].ind[0]);
973                                         lru.add(tc[highest].ind[1]);
974                                         highest = lru.add(tc[highest].ind[2]);
975                                         drawcalls++;
976                                 }
977
978                                 buf->setBoundingBox(mb->getBoundingBox());
979                                 newmesh->addMeshBuffer(buf);
980                                 buf->drop();
981
982                         }
983                         break;
984                         case video::EVT_TANGENTS:
985                         {
986                                 video::S3DVertexTangents *v = (video::S3DVertexTangents *) mb->getVertices();
987
988                                 scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
989                                 buf->Material = mb->getMaterial();
990
991                                 buf->Vertices.reallocate(vcount);
992                                 buf->Indices.reallocate(icount);
993
994                                 core::map<const video::S3DVertexTangents, const u16> sind; // search index for fast operation
995                                 typedef core::map<const video::S3DVertexTangents, const u16>::Node snode;
996
997                                 // Main algorithm
998                                 u32 highest = 0;
999                                 u32 drawcalls = 0;
1000                                 for (;;)
1001                                 {
1002                                         if (tc[highest].drawn)
1003                                         {
1004                                                 bool found = false;
1005                                                 float hiscore = 0;
1006                                                 for (u32 t = 0; t < tcount; t++)
1007                                                 {
1008                                                         if (!tc[t].drawn)
1009                                                         {
1010                                                                 if (tc[t].score > hiscore)
1011                                                                 {
1012                                                                         highest = t;
1013                                                                         hiscore = tc[t].score;
1014                                                                         found = true;
1015                                                                 }
1016                                                         }
1017                                                 }
1018                                                 if (!found)
1019                                                         break;
1020                                         }
1021
1022                                         // Output the best triangle
1023                                         u16 newind = buf->Vertices.size();
1024
1025                                         snode *s = sind.find(v[tc[highest].ind[0]]);
1026
1027                                         if (!s)
1028                                         {
1029                                                 buf->Vertices.push_back(v[tc[highest].ind[0]]);
1030                                                 buf->Indices.push_back(newind);
1031                                                 sind.insert(v[tc[highest].ind[0]], newind);
1032                                                 newind++;
1033                                         }
1034                                         else
1035                                         {
1036                                                 buf->Indices.push_back(s->getValue());
1037                                         }
1038
1039                                         s = sind.find(v[tc[highest].ind[1]]);
1040
1041                                         if (!s)
1042                                         {
1043                                                 buf->Vertices.push_back(v[tc[highest].ind[1]]);
1044                                                 buf->Indices.push_back(newind);
1045                                                 sind.insert(v[tc[highest].ind[1]], newind);
1046                                                 newind++;
1047                                         }
1048                                         else
1049                                         {
1050                                                 buf->Indices.push_back(s->getValue());
1051                                         }
1052
1053                                         s = sind.find(v[tc[highest].ind[2]]);
1054
1055                                         if (!s)
1056                                         {
1057                                                 buf->Vertices.push_back(v[tc[highest].ind[2]]);
1058                                                 buf->Indices.push_back(newind);
1059                                                 sind.insert(v[tc[highest].ind[2]], newind);
1060                                         }
1061                                         else
1062                                         {
1063                                                 buf->Indices.push_back(s->getValue());
1064                                         }
1065
1066                                         vc[tc[highest].ind[0]].NumActiveTris--;
1067                                         vc[tc[highest].ind[1]].NumActiveTris--;
1068                                         vc[tc[highest].ind[2]].NumActiveTris--;
1069
1070                                         tc[highest].drawn = true;
1071
1072                                         for (u16 j = 0; j < 3; j++)
1073                                         {
1074                                                 vcache *vert = &vc[tc[highest].ind[j]];
1075                                                 for (u16 t = 0; t < vert->tris.size(); t++)
1076                                                 {
1077                                                         if (highest == vert->tris[t])
1078                                                         {
1079                                                                 vert->tris.erase(t);
1080                                                                 break;
1081                                                         }
1082                                                 }
1083                                         }
1084
1085                                         lru.add(tc[highest].ind[0]);
1086                                         lru.add(tc[highest].ind[1]);
1087                                         highest = lru.add(tc[highest].ind[2]);
1088                                         drawcalls++;
1089                                 }
1090
1091                                 buf->setBoundingBox(mb->getBoundingBox());
1092                                 newmesh->addMeshBuffer(buf);
1093                                 buf->drop();
1094                         }
1095                         break;
1096                 }
1097
1098                 delete [] vc;
1099                 delete [] tc;
1100
1101         } // for each meshbuffer
1102
1103         return newmesh;
1104 }