123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- /*
- Minetest
- Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- #include "mesh.h"
- #include "debug.h"
- #include "log.h"
- #include <cmath>
- #include <iostream>
- #include <IAnimatedMesh.h>
- #include <SAnimatedMesh.h>
- #include <IAnimatedMeshSceneNode.h>
- inline static void applyShadeFactor(video::SColor& color, float factor)
- {
- color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
- color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
- color.setBlue(core::clamp(core::round32(color.getBlue()*factor), 0, 255));
- }
- void applyFacesShading(video::SColor &color, const v3f &normal)
- {
- /*
- Some drawtypes have normals set to (0, 0, 0), this must result in
- maximum brightness: shade factor 1.0.
- Shade factors for aligned cube faces are:
- +Y 1.000000 sqrt(1.0)
- -Y 0.447213 sqrt(0.2)
- +-X 0.670820 sqrt(0.45)
- +-Z 0.836660 sqrt(0.7)
- */
- float x2 = normal.X * normal.X;
- float y2 = normal.Y * normal.Y;
- float z2 = normal.Z * normal.Z;
- if (normal.Y < 0)
- applyShadeFactor(color, 0.670820f * x2 + 0.447213f * y2 + 0.836660f * z2);
- else if ((x2 > 1e-3) || (z2 > 1e-3))
- applyShadeFactor(color, 0.670820f * x2 + 1.000000f * y2 + 0.836660f * z2);
- }
- scene::IAnimatedMesh* createCubeMesh(v3f scale)
- {
- video::SColor c(255,255,255,255);
- video::S3DVertex vertices[24] =
- {
- // Up
- video::S3DVertex(-0.5,+0.5,-0.5, 0,1,0, c, 0,1),
- video::S3DVertex(-0.5,+0.5,+0.5, 0,1,0, c, 0,0),
- video::S3DVertex(+0.5,+0.5,+0.5, 0,1,0, c, 1,0),
- video::S3DVertex(+0.5,+0.5,-0.5, 0,1,0, c, 1,1),
- // Down
- video::S3DVertex(-0.5,-0.5,-0.5, 0,-1,0, c, 0,0),
- video::S3DVertex(+0.5,-0.5,-0.5, 0,-1,0, c, 1,0),
- video::S3DVertex(+0.5,-0.5,+0.5, 0,-1,0, c, 1,1),
- video::S3DVertex(-0.5,-0.5,+0.5, 0,-1,0, c, 0,1),
- // Right
- video::S3DVertex(+0.5,-0.5,-0.5, 1,0,0, c, 0,1),
- video::S3DVertex(+0.5,+0.5,-0.5, 1,0,0, c, 0,0),
- video::S3DVertex(+0.5,+0.5,+0.5, 1,0,0, c, 1,0),
- video::S3DVertex(+0.5,-0.5,+0.5, 1,0,0, c, 1,1),
- // Left
- video::S3DVertex(-0.5,-0.5,-0.5, -1,0,0, c, 1,1),
- video::S3DVertex(-0.5,-0.5,+0.5, -1,0,0, c, 0,1),
- video::S3DVertex(-0.5,+0.5,+0.5, -1,0,0, c, 0,0),
- video::S3DVertex(-0.5,+0.5,-0.5, -1,0,0, c, 1,0),
- // Back
- video::S3DVertex(-0.5,-0.5,+0.5, 0,0,1, c, 1,1),
- video::S3DVertex(+0.5,-0.5,+0.5, 0,0,1, c, 0,1),
- video::S3DVertex(+0.5,+0.5,+0.5, 0,0,1, c, 0,0),
- video::S3DVertex(-0.5,+0.5,+0.5, 0,0,1, c, 1,0),
- // Front
- video::S3DVertex(-0.5,-0.5,-0.5, 0,0,-1, c, 0,1),
- video::S3DVertex(-0.5,+0.5,-0.5, 0,0,-1, c, 0,0),
- video::S3DVertex(+0.5,+0.5,-0.5, 0,0,-1, c, 1,0),
- video::S3DVertex(+0.5,-0.5,-0.5, 0,0,-1, c, 1,1),
- };
- u16 indices[6] = {0,1,2,2,3,0};
- scene::SMesh *mesh = new scene::SMesh();
- for (u32 i=0; i<6; ++i)
- {
- scene::IMeshBuffer *buf = new scene::SMeshBuffer();
- buf->append(vertices + 4 * i, 4, indices, 6);
- // Set default material
- buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
- buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
- buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
- // Add mesh buffer to mesh
- mesh->addMeshBuffer(buf);
- buf->drop();
- }
- scene::SAnimatedMesh *anim_mesh = new scene::SAnimatedMesh(mesh);
- mesh->drop();
- scaleMesh(anim_mesh, scale); // also recalculates bounding box
- return anim_mesh;
- }
- void scaleMesh(scene::IMesh *mesh, v3f scale)
- {
- if (mesh == NULL)
- return;
- aabb3f bbox;
- bbox.reset(0, 0, 0);
- u32 mc = mesh->getMeshBufferCount();
- for (u32 j = 0; j < mc; j++) {
- scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- const u32 stride = getVertexPitchFromType(buf->getVertexType());
- u32 vertex_count = buf->getVertexCount();
- u8 *vertices = (u8 *)buf->getVertices();
- for (u32 i = 0; i < vertex_count; i++)
- ((video::S3DVertex *)(vertices + i * stride))->Pos *= scale;
- buf->recalculateBoundingBox();
- // calculate total bounding box
- if (j == 0)
- bbox = buf->getBoundingBox();
- else
- bbox.addInternalBox(buf->getBoundingBox());
- }
- mesh->setBoundingBox(bbox);
- }
- void translateMesh(scene::IMesh *mesh, v3f vec)
- {
- if (mesh == NULL)
- return;
- aabb3f bbox;
- bbox.reset(0, 0, 0);
- u32 mc = mesh->getMeshBufferCount();
- for (u32 j = 0; j < mc; j++) {
- scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- const u32 stride = getVertexPitchFromType(buf->getVertexType());
- u32 vertex_count = buf->getVertexCount();
- u8 *vertices = (u8 *)buf->getVertices();
- for (u32 i = 0; i < vertex_count; i++)
- ((video::S3DVertex *)(vertices + i * stride))->Pos += vec;
- buf->recalculateBoundingBox();
- // calculate total bounding box
- if (j == 0)
- bbox = buf->getBoundingBox();
- else
- bbox.addInternalBox(buf->getBoundingBox());
- }
- mesh->setBoundingBox(bbox);
- }
- void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor &color)
- {
- const u32 stride = getVertexPitchFromType(buf->getVertexType());
- u32 vertex_count = buf->getVertexCount();
- u8 *vertices = (u8 *) buf->getVertices();
- for (u32 i = 0; i < vertex_count; i++)
- ((video::S3DVertex *) (vertices + i * stride))->Color = color;
- }
- void setAnimatedMeshColor(scene::IAnimatedMeshSceneNode *node, const video::SColor &color)
- {
- for (u32 i = 0; i < node->getMaterialCount(); ++i) {
- node->getMaterial(i).EmissiveColor = color;
- }
- }
- void setMeshColor(scene::IMesh *mesh, const video::SColor &color)
- {
- if (mesh == NULL)
- return;
- u32 mc = mesh->getMeshBufferCount();
- for (u32 j = 0; j < mc; j++)
- setMeshBufferColor(mesh->getMeshBuffer(j), color);
- }
- void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count)
- {
- const u32 stride = getVertexPitchFromType(buf->getVertexType());
- assert(buf->getVertexCount() >= count);
- u8 *vertices = (u8 *) buf->getVertices();
- for (u32 i = 0; i < count; i++)
- ((video::S3DVertex*) (vertices + i * stride))->TCoords = uv[i];
- }
- template <typename F>
- static void applyToMesh(scene::IMesh *mesh, const F &fn)
- {
- u16 mc = mesh->getMeshBufferCount();
- for (u16 j = 0; j < mc; j++) {
- scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- const u32 stride = getVertexPitchFromType(buf->getVertexType());
- u32 vertex_count = buf->getVertexCount();
- char *vertices = reinterpret_cast<char *>(buf->getVertices());
- for (u32 i = 0; i < vertex_count; i++)
- fn(reinterpret_cast<video::S3DVertex *>(vertices + i * stride));
- }
- }
- void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor)
- {
- const u32 stride = getVertexPitchFromType(buf->getVertexType());
- u32 vertex_count = buf->getVertexCount();
- u8 *vertices = (u8 *) buf->getVertices();
- for (u32 i = 0; i < vertex_count; i++) {
- video::S3DVertex *vertex = (video::S3DVertex *) (vertices + i * stride);
- video::SColor *vc = &(vertex->Color);
- // Reset color
- *vc = *buffercolor;
- // Apply shading
- applyFacesShading(*vc, vertex->Normal);
- }
- }
- void setMeshColorByNormalXYZ(scene::IMesh *mesh,
- const video::SColor &colorX,
- const video::SColor &colorY,
- const video::SColor &colorZ)
- {
- if (!mesh)
- return;
- auto colorizator = [=] (video::S3DVertex *vertex) {
- f32 x = fabs(vertex->Normal.X);
- f32 y = fabs(vertex->Normal.Y);
- f32 z = fabs(vertex->Normal.Z);
- if (x >= y && x >= z)
- vertex->Color = colorX;
- else if (y >= z)
- vertex->Color = colorY;
- else
- vertex->Color = colorZ;
- };
- applyToMesh(mesh, colorizator);
- }
- void setMeshColorByNormal(scene::IMesh *mesh, const v3f &normal,
- const video::SColor &color)
- {
- if (!mesh)
- return;
- auto colorizator = [normal, color] (video::S3DVertex *vertex) {
- if (vertex->Normal == normal)
- vertex->Color = color;
- };
- applyToMesh(mesh, colorizator);
- }
- template <float v3f::*U, float v3f::*V>
- static void rotateMesh(scene::IMesh *mesh, float degrees)
- {
- degrees *= M_PI / 180.0f;
- float c = std::cos(degrees);
- float s = std::sin(degrees);
- auto rotator = [c, s] (video::S3DVertex *vertex) {
- float u = vertex->Pos.*U;
- float v = vertex->Pos.*V;
- vertex->Pos.*U = c * u - s * v;
- vertex->Pos.*V = s * u + c * v;
- };
- applyToMesh(mesh, rotator);
- }
- void rotateMeshXYby(scene::IMesh *mesh, f64 degrees)
- {
- rotateMesh<&v3f::X, &v3f::Y>(mesh, degrees);
- }
- void rotateMeshXZby(scene::IMesh *mesh, f64 degrees)
- {
- rotateMesh<&v3f::X, &v3f::Z>(mesh, degrees);
- }
- void rotateMeshYZby(scene::IMesh *mesh, f64 degrees)
- {
- rotateMesh<&v3f::Y, &v3f::Z>(mesh, degrees);
- }
- void rotateMeshBy6dFacedir(scene::IMesh *mesh, int facedir)
- {
- int axisdir = facedir >> 2;
- facedir &= 0x03;
- switch (facedir) {
- case 1: rotateMeshXZby(mesh, -90); break;
- case 2: rotateMeshXZby(mesh, 180); break;
- case 3: rotateMeshXZby(mesh, 90); break;
- }
- switch (axisdir) {
- case 1: rotateMeshYZby(mesh, 90); break; // z+
- case 2: rotateMeshYZby(mesh, -90); break; // z-
- case 3: rotateMeshXYby(mesh, -90); break; // x+
- case 4: rotateMeshXYby(mesh, 90); break; // x-
- case 5: rotateMeshXYby(mesh, -180); break;
- }
- }
- void recalculateBoundingBox(scene::IMesh *src_mesh)
- {
- aabb3f bbox;
- bbox.reset(0,0,0);
- for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
- scene::IMeshBuffer *buf = src_mesh->getMeshBuffer(j);
- buf->recalculateBoundingBox();
- if (j == 0)
- bbox = buf->getBoundingBox();
- else
- bbox.addInternalBox(buf->getBoundingBox());
- }
- src_mesh->setBoundingBox(bbox);
- }
- bool checkMeshNormals(scene::IMesh *mesh)
- {
- // Assume correct normals if this many first faces get it right.
- static const u16 MAX_FACES_TO_CHECK = 9;
- u32 buffer_count = mesh->getMeshBufferCount();
- for (u32 i = 0; i < buffer_count; i++) {
- scene::IMeshBuffer *buffer = mesh->getMeshBuffer(i);
- // Here we intentionally check only first normal, assuming that if buffer
- // has it valid, then most likely all other ones are fine too. We can
- // check all of the normals to have length, but it seems like an overkill
- // hurting the performance and covering only really weird broken models.
- f32 length = buffer->getNormal(0).getLength();
- if (!std::isfinite(length) || length < 1e-10f)
- return false;
- const u16 count = MYMIN(MAX_FACES_TO_CHECK * 3, buffer->getIndexCount() - 3);
- for (u16 i = 0; i < count; i += 3) {
- core::plane3df plane(buffer->getPosition(buffer->getIndices()[i]),
- buffer->getPosition(buffer->getIndices()[i+1]),
- buffer->getPosition(buffer->getIndices()[i+2]));
- for (u16 j = 0; j < 3; j++)
- if (plane.Normal.dotProduct(buffer->getNormal(buffer->getIndices()[i+j])) <= 0)
- return false;
- }
- }
- return true;
- }
- scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer)
- {
- switch (mesh_buffer->getVertexType()) {
- case video::EVT_STANDARD: {
- video::S3DVertex *v = (video::S3DVertex *) mesh_buffer->getVertices();
- u16 *indices = mesh_buffer->getIndices();
- scene::SMeshBuffer *cloned_buffer = new scene::SMeshBuffer();
- cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
- mesh_buffer->getIndexCount());
- return cloned_buffer;
- }
- case video::EVT_2TCOORDS: {
- video::S3DVertex2TCoords *v =
- (video::S3DVertex2TCoords *) mesh_buffer->getVertices();
- u16 *indices = mesh_buffer->getIndices();
- scene::SMeshBufferLightMap *cloned_buffer =
- new scene::SMeshBufferLightMap();
- cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
- mesh_buffer->getIndexCount());
- return cloned_buffer;
- }
- case video::EVT_TANGENTS: {
- video::S3DVertexTangents *v =
- (video::S3DVertexTangents *) mesh_buffer->getVertices();
- u16 *indices = mesh_buffer->getIndices();
- scene::SMeshBufferTangents *cloned_buffer =
- new scene::SMeshBufferTangents();
- cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
- mesh_buffer->getIndexCount());
- return cloned_buffer;
- }
- }
- // This should not happen.
- sanity_check(false);
- return NULL;
- }
- scene::SMesh* cloneMesh(scene::IMesh *src_mesh)
- {
- scene::SMesh* dst_mesh = new scene::SMesh();
- for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
- scene::IMeshBuffer *temp_buf = cloneMeshBuffer(
- src_mesh->getMeshBuffer(j));
- dst_mesh->addMeshBuffer(temp_buf);
- temp_buf->drop();
- }
- return dst_mesh;
- }
- scene::IMesh* convertNodeboxesToMesh(const std::vector<aabb3f> &boxes,
- const f32 *uv_coords, float expand)
- {
- scene::SMesh* dst_mesh = new scene::SMesh();
- for (u16 j = 0; j < 6; j++)
- {
- scene::IMeshBuffer *buf = new scene::SMeshBuffer();
- buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
- buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
- dst_mesh->addMeshBuffer(buf);
- buf->drop();
- }
- video::SColor c(255,255,255,255);
- for (aabb3f box : boxes) {
- box.repair();
- box.MinEdge.X -= expand;
- box.MinEdge.Y -= expand;
- box.MinEdge.Z -= expand;
- box.MaxEdge.X += expand;
- box.MaxEdge.Y += expand;
- box.MaxEdge.Z += expand;
- // Compute texture UV coords
- f32 tx1 = (box.MinEdge.X / BS) + 0.5;
- f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
- f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
- f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
- f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
- f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
- f32 txc_default[24] = {
- // up
- tx1, 1 - tz2, tx2, 1 - tz1,
- // down
- tx1, tz1, tx2, tz2,
- // right
- tz1, 1 - ty2, tz2, 1 - ty1,
- // left
- 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1,
- // back
- 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1,
- // front
- tx1, 1 - ty2, tx2, 1 - ty1,
- };
- // use default texture UV mapping if not provided
- const f32 *txc = uv_coords ? uv_coords : txc_default;
- v3f min = box.MinEdge;
- v3f max = box.MaxEdge;
- video::S3DVertex vertices[24] =
- {
- // up
- video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]),
- video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]),
- video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]),
- video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]),
- // down
- video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]),
- video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]),
- video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]),
- video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]),
- // right
- video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]),
- video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]),
- video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]),
- video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]),
- // left
- video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]),
- video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]),
- video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]),
- video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]),
- // back
- video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[16],txc[17]),
- video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[18],txc[17]),
- video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[18],txc[19]),
- video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[16],txc[19]),
- // front
- video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[20],txc[21]),
- video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[22],txc[21]),
- video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[22],txc[23]),
- video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]),
- };
- u16 indices[] = {0,1,2,2,3,0};
- for(u16 j = 0; j < 24; j += 4)
- {
- scene::IMeshBuffer *buf = dst_mesh->getMeshBuffer(j / 4);
- buf->append(vertices + j, 4, indices, 6);
- }
- }
- return dst_mesh;
- }
|