//! reads a mesh sections and creates a mesh from it IAnimatedMesh* CIrrMeshFileLoader::readMesh(io::IXMLReader* reader) { SAnimatedMesh* animatedmesh = new SAnimatedMesh(); SMesh* mesh = new SMesh(); animatedmesh->addMesh(mesh); mesh->drop(); core::stringc bbSectionName = "boundingBox"; core::stringc bufferSectionName = "buffer"; core::stringc meshSectionName = "mesh"; if (!reader->isEmptyElement()) while(reader->read()) { if (reader->getNodeType() == io::EXN_ELEMENT) { const wchar_t* nodeName = reader->getNodeName(); if (bbSectionName == nodeName) { // inside a bounding box, ignore it for now because // we are calculating this anyway ourselves later. } else if (bufferSectionName == nodeName) { // we've got a mesh buffer IMeshBuffer* buffer = readMeshBuffer(reader); if (buffer) { mesh->addMeshBuffer(buffer); buffer->drop(); } } else skipSection(reader, true); // unknown section } // end if node type is element else if (reader->getNodeType() == io::EXN_ELEMENT_END) { if (meshSectionName == reader->getNodeName()) { // end of mesh section reached, cancel out break; } } } // end while reader->read(); mesh->recalculateBoundingBox(); animatedmesh->recalculateBoundingBox(); return animatedmesh; }
void addstrip(const HeightMap &hm, colour_func cf, u16 y0, u16 y1, u32 bufNum) { SMeshBuffer *buf = 0; if (bufNum<Mesh->getMeshBufferCount()) { buf = (SMeshBuffer*)Mesh->getMeshBuffer(bufNum); } else { // create new buffer buf = new SMeshBuffer(); Mesh->addMeshBuffer(buf); // to simplify things we drop here but continue using buf buf->drop(); } buf->Vertices.set_used((1 + y1 - y0) * Width); u32 i=0; for (u16 y = y0; y <= y1; ++y) { for (u16 x = 0; x < Width; ++x) { const f32 z = hm.get(x, y); const f32 xx = (f32)x/(f32)Width; const f32 yy = (f32)y/(f32)Height; S3DVertex& v = buf->Vertices[i++]; v.Pos.set(x, Scale * z, y); v.Normal.set(hm.getnormal(x, y, Scale)); v.Color=cf(xx, yy, z); v.TCoords.set(xx, yy); } } buf->Indices.set_used(6 * (Width - 1) * (y1 - y0)); i=0; for(u16 y = y0; y < y1; ++y) { for(u16 x = 0; x < Width - 1; ++x) { const u16 n = (y-y0) * Width + x; buf->Indices[i]=n; buf->Indices[++i]=n + Width; buf->Indices[++i]=n + Width + 1; buf->Indices[++i]=n + Width + 1; buf->Indices[++i]=n + 1; buf->Indices[++i]=n; ++i; } } buf->recalculateBoundingBox(); }
irr::scene::IMesh* CGWIC_Cell::TerrainToMesh(int LOD) { SMesh* out = new SMesh(); CDynamicMeshBuffer* buff = new CDynamicMeshBuffer(EVT_2TCOORDS,EIT_16BIT); terrain->getMeshBufferForLOD(*buff,LOD); const u32 vertCnt = buff->getVertexCount(); S3DVertex2TCoords* mbv = reinterpret_cast<S3DVertex2TCoords*> (buff->getVertexBuffer().getData()); vector3df scale = terrain->getScale(); for (u32 i=0; i<vertCnt; i++) mbv[i].Pos *= scale; out->addMeshBuffer(buff); out->recalculateBoundingBox(); buff->drop(); terrain->setPosition(terrain->getPosition()); return out; }
void init(const HeightMap &hm, f32 scale, colour_func cf, IVideoDriver *driver) { Scale = scale; const u32 mp = driver -> getMaximalPrimitiveCount(); Width = hm.width(); Height = hm.height(); const u32 sw = mp / (6 * Height); // the width of each piece u32 i=0; for(u32 y0 = 0; y0 < Height; y0 += sw) { u16 y1 = y0 + sw; if (y1 >= Height) y1 = Height - 1; // the last one might be narrower addstrip(hm, cf, y0, y1, i); ++i; } if (i<Mesh->getMeshBufferCount()) { // clear the rest for (u32 j=i; j<Mesh->getMeshBufferCount(); ++j) { Mesh->getMeshBuffer(j)->drop(); } Mesh->MeshBuffers.erase(i,Mesh->getMeshBufferCount()-i); } // set dirty flag to make sure that hardware copies of this // buffer are also updated, see IMesh::setHardwareMappingHint Mesh->setDirty(); Mesh->recalculateBoundingBox(); }
CGAL::Three::Scene_item* Polyhedron_demo_stl_plugin::load(QFileInfo fileinfo) { // Open file std::ifstream in(fileinfo.filePath().toUtf8(), std::ios::in | std::ios::binary); if(!in) { std::cerr << "Error! Cannot open file " << (const char*)fileinfo.filePath().toUtf8() << std::endl; return NULL; } std::vector<CGAL::cpp11::array<double, 3> > points; std::vector<CGAL::cpp11::array<int, 3> > triangles; if (!CGAL::read_STL(in, points, triangles)) { std::cerr << "Error: invalid STL file" << std::endl; return NULL; } try{ // Try building a surface_mesh SMesh* SM = new SMesh(); if (CGAL::Polygon_mesh_processing::is_polygon_soup_a_polygon_mesh(triangles)) CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, triangles, *SM); if(!SM->is_valid() || SM->is_empty()){ std::cerr << "Error: Invalid facegraph" << std::endl; } else{ Scene_surface_mesh_item* item = new Scene_surface_mesh_item(SM); item->setName(fileinfo.completeBaseName()); return item; } } catch(...){} Scene_polygon_soup_item* item = new Scene_polygon_soup_item(); item->setName(fileinfo.completeBaseName()); item->load(points, triangles); return item; }
//! Creates/loads an animated mesh from the file. IAnimatedMesh* CSMFMeshFileLoader::createMesh(io::IReadFile* file) { if ( !file ) return 0; if ( getMeshTextureLoader() ) getMeshTextureLoader()->setMeshFile(file); // create empty mesh SMesh *mesh = new SMesh(); // load file u16 version; u8 flags; s32 limbCount; s32 i; io::BinaryFile::read(file, version); io::BinaryFile::read(file, flags); io::BinaryFile::read(file, limbCount); // load mesh data core::matrix4 identity; for (i=0; i < limbCount; ++i) loadLimb(file, mesh, identity); // recalculate buffer bounding boxes for (i=0; i < (s32)mesh->getMeshBufferCount(); ++i) mesh->getMeshBuffer(i)->recalculateBoundingBox(); mesh->recalculateBoundingBox(); SAnimatedMesh *am = new SAnimatedMesh(); am->addMesh(mesh); mesh->drop(); am->recalculateBoundingBox(); return am; }
SBsp3 *SBsp3::FromMesh(SMesh *m) { SBsp3 *bsp3 = NULL; int i; SMesh mc; ZERO(&mc); for(i = 0; i < m->l.n; i++) { mc.AddTriangle(&(m->l.elem[i])); } srand(0); // Let's be deterministic, at least! int n = mc.l.n; while(n > 1) { int k = rand() % n; n--; SWAP(STriangle, mc.l.elem[k], mc.l.elem[n]); } for(i = 0; i < mc.l.n; i++) { bsp3 = bsp3->Insert(&(mc.l.elem[i]), NULL); } mc.Clear(); return bsp3; }
//----------------------------------------------------------------------------- // Export a triangle mesh, in the requested format. //----------------------------------------------------------------------------- void SolveSpace::ExportMeshTo(char *filename) { SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh); if(m->IsEmpty()) { Error("Active group mesh is empty; nothing to export."); return; } FILE *f = fopen(filename, "wb"); if(!f) { Error("Couldn't write to '%s'", filename); return; } if(StringEndsIn(filename, ".stl")) { ExportMeshAsStlTo(f, m); } else if(StringEndsIn(filename, ".obj")) { ExportMeshAsObjTo(f, m); } else { Error("Can't identify output file type from file extension of " "filename '%s'; try .stl, .obj.", filename); } fclose(f); }
//! creates/loads an animated mesh from the file. //! \return Pointer to the created mesh. Returns 0 if loading failed. //! If you no longer need the mesh, you should call IAnimatedMesh::drop(). //! See IReferenceCounted::drop() for more information. IAnimatedMesh* CSTLMeshFileLoader::createMesh(io::IReadFile* file) { const long filesize = file->getSize(); if (filesize < 6) // we need a header return 0; const u32 WORD_BUFFER_LENGTH = 512; SMesh* mesh = new SMesh(); SMeshBuffer* meshBuffer = new SMeshBuffer(); mesh->addMeshBuffer(meshBuffer); meshBuffer->drop(); core::vector3df vertex[3]; core::vector3df normal; c8 buffer[WORD_BUFFER_LENGTH]; bool binary = false; file->read(buffer, 5); if (strncmp("solid", buffer, 5)) binary = true; // read/skip header u32 binFaceCount = 0; if (binary) { file->seek(80); file->read(&binFaceCount, 4); #ifdef __BIG_ENDIAN__ binFaceCount = os::Byteswap::byteswap(binFaceCount); #endif } else goNextLine(file); u16 attrib=0; core::stringc token; token.reserve(32); while (file->getPos() < filesize) { if (!binary) { if (getNextToken(file, token) != "facet") { if (token=="endsolid") break; mesh->drop(); return 0; } if (getNextToken(file, token) != "normal") { mesh->drop(); return 0; } } getNextVector(file, normal, binary); if (!binary) { if (getNextToken(file, token) != "outer") { mesh->drop(); return 0; } if (getNextToken(file, token) != "loop") { mesh->drop(); return 0; } } for (u32 i=0; i<3; ++i) { if (!binary) { if (getNextToken(file, token) != "vertex") { mesh->drop(); return 0; } } getNextVector(file, vertex[i], binary); } if (!binary) { if (getNextToken(file, token) != "endloop") { mesh->drop(); return 0; } if (getNextToken(file, token) != "endfacet") { mesh->drop(); return 0; } } else { file->read(&attrib, 2); #ifdef __BIG_ENDIAN__ attrib = os::Byteswap::byteswap(attrib); #endif } SMeshBuffer* mb = reinterpret_cast<SMeshBuffer*>(mesh->getMeshBuffer(mesh->getMeshBufferCount()-1)); u32 vCount = mb->getVertexCount(); video::SColor color(0xffffffff); if (attrib & 0x8000) color = video::A1R5G5B5toA8R8G8B8(attrib); if (normal==core::vector3df()) normal=core::plane3df(vertex[2],vertex[1],vertex[0]).Normal; mb->Vertices.push_back(video::S3DVertex(vertex[2],normal,color, core::vector2df())); mb->Vertices.push_back(video::S3DVertex(vertex[1],normal,color, core::vector2df())); mb->Vertices.push_back(video::S3DVertex(vertex[0],normal,color, core::vector2df())); mb->Indices.push_back(vCount); mb->Indices.push_back(vCount+1); mb->Indices.push_back(vCount+2); } // end while (file->getPos() < filesize) mesh->getMeshBuffer(0)->recalculateBoundingBox(); // Create the Animated mesh if there's anything in the mesh SAnimatedMesh* pAM = 0; if ( 0 != mesh->getMeshBufferCount() ) { mesh->recalculateBoundingBox(); pAM = new SAnimatedMesh(); pAM->Type = EAMT_OBJ; pAM->addMesh(mesh); pAM->recalculateBoundingBox(); } mesh->drop(); return pAM; }
void SolveSpace::ExportViewOrWireframeTo(char *filename, bool wireframe) { int i; SEdgeList edges; ZERO(&edges); SBezierList beziers; ZERO(&beziers); SMesh *sm = NULL; if(SS.GW.showShaded) { Group *g = SK.GetGroup(SS.GW.activeGroup); g->GenerateDisplayItems(); sm = &(g->displayMesh); } if(sm && sm->IsEmpty()) { sm = NULL; } for(i = 0; i < SK.entity.n; i++) { Entity *e = &(SK.entity.elem[i]); if(!e->IsVisible()) continue; if(e->construction) continue; if(SS.exportPwlCurves || (sm && !SS.GW.showHdnLines) || fabs(SS.exportOffset) > LENGTH_EPS) { // We will be doing hidden line removal, which we can't do on // exact curves; so we need things broken down to pwls. Same // problem with cutter radius compensation. e->GenerateEdges(&edges); } else { e->GenerateBezierCurves(&beziers); } } if(SS.GW.showEdges) { Group *g = SK.GetGroup(SS.GW.activeGroup); g->GenerateDisplayItems(); SEdgeList *selr = &(g->displayEdges); SEdge *se; for(se = selr->l.First(); se; se = selr->l.NextAfter(se)) { edges.AddEdge(se->a, se->b, Style::SOLID_EDGE); } } if(SS.GW.showConstraints) { Constraint *c; for(c = SK.constraint.First(); c; c = SK.constraint.NextAfter(c)) { c->GetEdges(&edges); } } if(wireframe) { VectorFileWriter *out = VectorFileWriter::ForFile(filename); if(out) { ExportWireframeCurves(&edges, &beziers, out); } } else { Vector u = SS.GW.projRight, v = SS.GW.projUp, n = u.Cross(v), origin = SS.GW.offset.ScaledBy(-1); VectorFileWriter *out = VectorFileWriter::ForFile(filename); if(out) { ExportLinesAndMesh(&edges, &beziers, sm, u, v, n, origin, SS.CameraTangent()*SS.GW.scale, out); } if(out && !out->HasCanvasSize()) { // These file formats don't have a canvas size, so they just // get exported in the raw coordinate system. So indicate what // that was on-screen. SS.justExportedInfo.draw = true; SS.justExportedInfo.pt = origin; SS.justExportedInfo.u = u; SS.justExportedInfo.v = v; InvalidateGraphics(); } } edges.Clear(); beziers.Clear(); }
// ---------------------------------------------------------------------------- void Track::build() { IrrlichtDevice* device = Editor::getEditor()->getDevice(); PHYSFS_setWriteDir(Editor::getEditor()->getTrackDir().c_str()); PHYSFS_mkdir(m_file_name.c_str()); path p = Editor::getEditor()->getTrackDir() + m_file_name; CMeshBuffer<S3DVertex2TCoords>* mb = m_terrain->build(p); SMesh smesh; smesh.addMeshBuffer(mb); for (u32 i = 1; i < m_roads.size(); i++) { IRoad* r = m_roads[i]; if (r->getSpline()->getPointNum()>1) smesh.addMeshBuffer(((Road*)r)->getMeshBuffer()); } B3DMeshWriter* writer = new B3DMeshWriter(device->getFileSystem()); IWriteFile *file; file = device->getFileSystem()->createAndWriteFile((p + "/track.b3d").c_str()); writer->writeMesh(file, &smesh); file->drop(); delete writer; m_driveline->build(p); std::ofstream mat; mat.open((p + "/materials.xml").c_str()); mat << "<materials>\n"; mat << " <material name=\"splatt.png\" graphical-effect=\"splatting\""; SMaterial m = m_terrain->getMaterial(0); for (int i = 1; i < 5; i++) { mat << " splatting-texture-" << i << "=\""; mat << Editor::toRelative(m.getTexture(i+1)->getName()).c_str(); mat << "\""; } mat << "/>\n"; if (m_gravity_road) { for (u32 i = 1; i < m_roads.size(); i++) { stringc tex = m_roads[i]->getTexName(); if (tex.size()>0) { mat << " <material name=\""; mat << tex.c_str(); mat << "\" has-gravity=\"yes\" />\n"; } } // roads } // gravity road mode mat <<"</materials>\n"; mat.close(); stringw track; track += "<track name = \""; track += m_track_name + L"\"\n"; track += " version = \"5\"\n"; track += " groups = \"made-by-STK-TE\"\n"; track += " designer = \""; track += m_designer + "\"\n"; track += " music = \""; track += m_music.c_str(); track += "\"\n"; track += " screenshot = \"screenshot.jpg\"\n"; track += " smooth-normals = \"true\"\n"; track += " reverse = \"Y\"\n>\n"; track += "</track>\n"; PHYSFS_uint64 len = 4 * track.size(); char* dst = new char[len]; #ifdef _WIN32 PHYSFS_utf8FromUcs2((PHYSFS_uint16*)track.c_str(),dst,len); #else PHYSFS_utf8FromUcs4((PHYSFS_uint32*)track.c_str(), dst, len); #endif FILE* f; f = fopen((p + "/track.xml").c_str(), "wb"); fwrite(dst, sizeof(char), strlen(dst), f); fclose(f); delete[] dst; std::ofstream scene; scene.open((p + "/scene.xml").c_str()); scene << "<scene>\n"; scene << " <track model=\"track.b3d\" x=\"0\" y=\"0\" z=\"0\">\n"; exportElements(scene, true); scene << " </track>\n"; exportElements(scene, false); ISceneManager* sm = Editor::getEditor()->getSceneManager(); ISceneNode* node; int i = 1; stringc name; while ((node = sm->getSceneNodeFromId(MAGIC_NUMBER + i))) { name = node->getName(); vector3df pos; if (node->isVisible() && (name == "banana" || name == "item" || name == "small-nitro" || name == "big-nitro")) { pos = node->getPosition(); scene << " <" << name.c_str() << " x=\"" << pos.X << "\" y=\"" << pos.Y << "\" z=\"" << pos.Z << "\" />\n"; } i++; } scene << Viewport::get()->getSky()->getXmlString().c_str(); Viewport::get()->printCheckLine(&scene); scene << " <default-start karts-per-row = \"3\"\n"; scene << " forwards-distance =\"1.50\"\n"; scene << " sidewards-distance=\"3.00\"\n"; scene << " upwards-distance =\"0.10\"/>\n"; scene << "</scene>\n"; scene.close(); MsgWndw::get()->showMsg(_("Track exported!")); } // build
IMesh* MeshTools::createMeshFromHeightmap(IImage* image, dimension2du tileSizeInPixels, vector2di tilePosInTiles, bool extraStripsOnEdges) { // Use top corner of image to get bias for centering the Z axis. float imageZeroBias = image->getPixel(0,0).getAverage(); dimension2du imageDimension = image->getDimension(); // Easier to think of script working in whole tile units, so handle conversion to pixel pos in this method: vector2di startAtPixel = tilePosInTiles * vector2di(tileSizeInPixels.Width, tileSizeInPixels.Height); // However we're going to invert the image Y axis so that the bottom of the picture will be at 0,0 in world space, // and the top of the picture will be at +Y in world space. startAtPixel.Y = (imageDimension.Height-1) - startAtPixel.Y; // Return nothing if the tile is outside the image, or if the resulting mesh would have only 0 or 1 rows or columns. // Doing this check early prevents getting underflow when we clip the tile size to the image dimensions a few statements down. if (startAtPixel.X < 0 || startAtPixel.X >= (imageDimension.Width-1) || startAtPixel.Y <= 0 || startAtPixel.Y > (imageDimension.Height-1)) return NULL; // Calculate whether to use tile size or tile size + 1 (for generating overlap) // Adding the extra strip amount before clipping to image dimensions prevents overflowing the image edges after clipping. dimension2du useTileSize = tileSizeInPixels + (extraStripsOnEdges? dimension2du(1,1) : dimension2du(0,0)); // Clip the tile size if we're dealing with a "leftover" amount at the edge that's not a whole tile's width or height. // Most heightmap implementations require exactly a power of 2 + 1 square dimensions like 257x257, but supporting // image sizes that are not whole multiples of tile size is as simple as this one line of code and supporting // image sizes that are not square is a nice feature for a platformer game so you can have levels that are longer // than they are tall. useTileSize.set(min(useTileSize.Width, imageDimension.Width-startAtPixel.X), min(useTileSize.Height, (u32)startAtPixel.Y+1)); SMeshBuffer* buffer = new SMeshBuffer(); // Preallocate vertex buffer to the size we know we'll need. buffer->Vertices.set_used(useTileSize.Width*useTileSize.Height); // Amount of TCoord per unit of x,y in image. vector2df tCoordsScale(1.0f / useTileSize.Width, 1.0f / useTileSize.Height); // Whether we put y on the outside or x on the outside, doesn't matter, because we are // using getIndex() to find where x and y map too. However, I happen to know I made getIndex // put x's together, y in rows, and looping in the same rank order might have better cache performance // because it should access the vertices in the same order they are in the array. for (u32 y=0; y < useTileSize.Height; ++y) { for (u32 x=0; x < useTileSize.Width; ++x) { u32 index = getIndex(useTileSize, x, y); S3DVertex& vert = buffer->Vertices[index]; // No special coloration vert.Color = SColor(255,255,255,255); // Scale texture to tile. vert.TCoords = vector2df(x,y) * tCoordsScale; // y axis starts with 0 at image bottom and goes up. vector2di pixelPos( startAtPixel.X+x, startAtPixel.Y-y ); SColor heightColor = image->getPixel(pixelPos.X, pixelPos.Y); float thisHeight = imageZeroBias - heightColor.getAverage(); vert.Pos.set(x, y, thisHeight); // I'm only averaging normals along the 4 compass directions. It's // arguable whether diagonals should count; I chose to ignore diagonals. // Ignoring diagonals allows me to assume the "run" in rise/run is always // just one unit; if you add diagonals here you'll also need to change // the slope calculation to use the actual distance instead of assuming 1. vector2di offsetsArray[] = { vector2di( 1, 0), // 3 o'clock vector2di( 0, 1), // 12 o'clock vector2di(-1, 0), // 9 o'clock vector2di( 0,-1) // 6 o'clock }; // Calculate the normals of the surrounding slopes. // Uses the image, not just the tile patch size, so it will // calculate correct normals for vertices on tile edges. for (size_t i=-0; i < 4; ++i) { vector2di offset = vector2di(x,y) + offsetsArray[i]; // Skip this offset if it's outside the image if (offset.X < 0 || offset.Y < 0 || offset.X >= (s32)imageDimension.Width || offset.Y >= (s32)imageDimension.Height) continue; vector2di otherPixelPos( startAtPixel.X+x+offset.X, startAtPixel.Y-y-offset.Y ); float otherHeight = 255 - image->getPixel((u32)otherPixelPos.X, (u32)otherPixelPos.Y).getAverage(); // The code Irrlicht's in terrain scene node does all kinds of complicated // business with cross products and such - waaay over complicated. You don't need // all that stuff. Dude it' s a heightmap: all you need to worray about is // rise over run on unit intervals! Algebra 1 type stuff, y = mx + c // Calculate the rise of the line over the run, taking into account the fact // that the offset could be in either direction. float rise = (offset.X < 0 || offset.Y < 0)? thisHeight - otherHeight : otherHeight - thisHeight; // Assuming that run = 1, m = rise / run is just m = rise. float m = rise; // The the slope of the normal is just the negative of the reciprocal of the line slope. float n = -1.0f / rise; // The X,Y of the normal vector is just going to be the X and Y of the offset // (think about it - obviously the normal of the slope must tilt in the direction of the run) // and the Z of the normal vector is just the slope of the normal over that run. vector3df normVect(offset.X, offset.Y, n); //vert.Normal += normVect; } //vert.Normal.normalize(); vert.Normal.set(0,0,-1.0f); } } // Pre-allocate index data to 2*3*Width*Height for triangles. // There is actually 1 less square per count of vertices though, // for instance if you had 2x2 vertices, you only have 1 square. buffer->Indices.set_used(2*3*(useTileSize.Width-1)*(useTileSize.Height-1)); // Start with 1 and generate all the triangles from their top right corners. // Like this (A is top right corner at x,y): // // y=1 B---A // | / | // y=0 C---D // x=0 x=1 for (u32 dst=0, x=1; x < useTileSize.Width; ++x) { for (u32 y=1; y < useTileSize.Height; ++y) { u32 A = getIndex(useTileSize, x, y ); u32 B = getIndex(useTileSize, x-1, y ); u32 C = getIndex(useTileSize, x-1, y-1 ); u32 D = getIndex(useTileSize, x, y-1 ); buffer->Indices[dst++] = C; buffer->Indices[dst++] = B; buffer->Indices[dst++] = A; buffer->Indices[dst++] = D; buffer->Indices[dst++] = C; buffer->Indices[dst++] = A; } } buffer->recalculateBoundingBox(); buffer->setHardwareMappingHint(EHM_STATIC); SMesh* mesh = new SMesh(); mesh->addMeshBuffer(buffer); mesh->recalculateBoundingBox(); buffer->drop(); return mesh; }
MeshTools::SplitMeshResult MeshTools::splitMeshZ(IMesh* oldMesh, float zCut, float zInsert, bool marginalTrisInLeft, bool marginalTrisInRight) { IMeshBuffer* oldMeshBuf = oldMesh->getMeshBuffer(0); u16* oldInd = oldMeshBuf->getIndices(); SMeshBuffer* leftMeshBuf = new SMeshBuffer(); SMeshBuffer* rightMeshBuf = new SMeshBuffer(); LinkSplitter linkSplitter(oldMeshBuf, zCut); vector3df const offset(0,0, zInsert); set<pair<PsblVertPtr, PsblVertPtr>> leftEdgeLinks; set<pair<PsblVertPtr, PsblVertPtr>> rightEdgeLinks; for (u32 i=0; i < oldMeshBuf->getIndexCount(); i += 3) { // Calling these shapes instead of triangles because they could be triangle or a quad after slice, // (it could also be a degenerate case of a line or a point - those will be discarded by addConvexShape) vector<PsblVertPtr> leftShape; vector<PsblVertPtr> rightShape; int a = oldInd[i]; int b = oldInd[i+1]; int c = oldInd[i+2]; PsblVertPtr mid1; PsblVertPtr mid2; PsblVertPtr mid3; // Don't create a copy just for a triangle that is just kissing the edge. Leave it in the background. if (linkSplitter.compareZ(a)==0 && linkSplitter.compareZ(b)==0 && linkSplitter.compareZ(c)==0) { if (marginalTrisInLeft) { leftShape.push_back(linkSplitter.getVert(a)); leftShape.push_back(linkSplitter.getVert(b)); leftShape.push_back(linkSplitter.getVert(c)); } if (marginalTrisInRight) { rightShape.push_back(linkSplitter.getVert(a)); rightShape.push_back(linkSplitter.getVert(b)); rightShape.push_back(linkSplitter.getVert(c)); } } else { // This is something I had to think hard about so I'm writing this comment so I don't // have to think it through again or forget it: // // Both the left shape and the right shape, and both the triangle and the quad, "magically" // come out with correct winding order when the triangle is split. Here's why: // Let the original triangle vertices be A-B-C. // Let the split midpoints be AB' and CA' as A-B is split and C-A are split respectively. // The shapes (ignore which shape is left or right, call them P and Q instead) // will be filled in the following order: // // round P Q Comment // 1 A - A is not in Q at all // 1 cont. AB' AB' AB' replaces B in P // 2 - B B proper is in Q and not in P // 2 cont. - - Link B-C is not split // 3 - C C proper is in Q and not in P // 3 cont. CA' CA' CA' replaces C in P // // Now, read the columns P and Q vertically top to bottom to see the resultant vertex order. // The triangle P is trivially still the correct winding order; it is just a smaller triangle now. // The new quad Q retains the old triangle's winding order property for the following reason: // As you wrapped around a full circle in old triangle A-B-C you would have traversed C-A then A-B. // The link C-A has been cut and vertex CA' inserted. // The link A-B has been cut and vertex AB' inserted. // If you traverse the new shape starting from the _middle_ you follow the following path: // B-C C-CA' CA'-AB' AB'-B B-C (again) // Compare to old triangle: // B-C C-A A-B B-C (again) // Even though vertex A has been cut off and replaced with two vertices, the two // new vertices lie along the path that the old links took and are in the new shape in the right order. mid1 = linkSplitter.processLink(leftShape, rightShape, a, b); mid2 = linkSplitter.processLink(leftShape, rightShape, b, c); mid3 = linkSplitter.processLink(leftShape, rightShape, c, a); } // If a triangle was split then two of those three midpoints are inhabited by a vertex. // We want to get them both in mid1 and mid2 AND we need them in correct order. PsblVertPtr cut1 = (mid1 && mid2)? mid1 : mid2; PsblVertPtr cut2 = (mid2 && mid3)? mid3 : mid2; if (cut1 && cut2) { vector<PsblVertPtr> chain = linkSplitter.chopLink(cut1, cut2, 1); linkSplitter.insertPoints(leftShape, cut1, cut2, chain); linkSplitter.insertPoints(rightShape, cut1, cut2, chain); } linkSplitter.addConvexShape(leftShape, leftMeshBuf, -offset/2); linkSplitter.addConvexShape(rightShape, rightMeshBuf, offset/2); // Add any edges of the left shape that lie along the border. linkSplitter.addEdgeLinks(leftShape, leftEdgeLinks); // Right side polys that share a border with left side ones will have // opposite winding order. In order for set_intersection to consider them // as matches, we'll store the right side links in reversed order. std::reverse(rightShape.begin(), rightShape.end()); linkSplitter.addEdgeLinks(rightShape, rightEdgeLinks); } SMesh* leftMesh = new SMesh(); leftMeshBuf->recalculateBoundingBox(); leftMeshBuf->setHardwareMappingHint(EHM_STATIC); leftMesh->addMeshBuffer(leftMeshBuf); leftMesh->recalculateBoundingBox(); leftMeshBuf->drop(); // we drop the buf, mesh obj has it now SMesh* rightMesh = new SMesh(); rightMeshBuf->recalculateBoundingBox(); rightMeshBuf->setHardwareMappingHint(EHM_STATIC); rightMesh->addMeshBuffer(rightMeshBuf); rightMesh->recalculateBoundingBox(); rightMeshBuf->drop(); // we drop the buf, mesh obj has it now SMesh* middleMesh = NULL; if (zInsert > 0) { set<pair<PsblVertPtr,PsblVertPtr>> result; std::set_intersection( leftEdgeLinks.begin(), leftEdgeLinks.end(), rightEdgeLinks.begin(), rightEdgeLinks.end(), std::inserter(result, result.begin()) ); size_t debugsize = result.size(); if (result.size() > 0) { SMeshBuffer* middleMeshBuf = new SMeshBuffer(); vector<PsblVertPtr> shape(4); for (auto it=result.begin(); it!=result.end(); ++it) { shape[0] = it->second; shape[1] = it->first; shape[2] = it->first->duplicate(offset); shape[3] = it->second->duplicate(offset); linkSplitter.addConvexShape(shape, middleMeshBuf, -offset/2); } middleMesh = new SMesh(); middleMeshBuf->recalculateBoundingBox(); middleMeshBuf->setHardwareMappingHint(EHM_STATIC); middleMesh->addMeshBuffer(middleMeshBuf); middleMesh->recalculateBoundingBox(); middleMeshBuf->drop(); // we drop the buf, mesh obj has it now } } return SplitMeshResult {leftMesh, middleMesh, rightMesh}; }
GLlink(ISceneNode *i_parent, ISceneManager *i_mgr, s32 i_id, const LinkInfo &i_li, BodyInfo_var i_binfo) : ISceneNode(i_parent, i_mgr, i_id), m_jointId(i_li.jointId) { setAutomaticCulling(scene::EAC_OFF); setPosition(vector3df( i_li.translation[0], -i_li.translation[1], i_li.translation[2])); Vector3 axis(i_li.rotation[0], i_li.rotation[1], i_li.rotation[2]); Matrix33 R; hrp::calcRodrigues(R, axis, i_li.rotation[3]); Vector3 rpy(rpyFromRot(R)); //std::cout << "rpy:" << rpy << std::endl; setRotation(vector3df(-180/M_PI*rpy[0], 180/M_PI*rpy[1], -180/M_PI*rpy[2])); m_axis << i_li.jointAxis[0], i_li.jointAxis[1], i_li.jointAxis[2]; ShapeInfoSequence_var sis = i_binfo->shapes(); AppearanceInfoSequence_var ais = i_binfo->appearances(); MaterialInfoSequence_var mis = i_binfo->materials(); TextureInfoSequence_var txs = i_binfo->textures(); const TransformedShapeIndexSequence& tsis = i_li.shapeIndices; core::vector3df vertex; core::vector3df normal; for (unsigned int l=0; l<tsis.length(); l++) { SMesh* mesh = new SMesh(); SMeshBuffer* meshBuffer = new SMeshBuffer(); mesh->addMeshBuffer(meshBuffer); meshBuffer->drop(); const TransformedShapeIndex &tsi = tsis[l]; short index = tsi.shapeIndex; ShapeInfo& si = sis[index]; const float *vertices = si.vertices.get_buffer(); const LongSequence& triangles = si.triangles; const AppearanceInfo& ai = ais[si.appearanceIndex]; const float *normals = ai.normals.get_buffer(); //std::cout << "length of normals = " << ai.normals.length() << std::endl; const LongSequence& normalIndices = ai.normalIndices; //std::cout << "length of normalIndices = " << normalIndices.length() << std::endl; const int numTriangles = triangles.length() / 3; //std::cout << "numTriangles = " << numTriangles << std::endl; video::SColor color(0xffffffff); if (ai.colors.length()) { color.set(0xff, 0xff*ai.colors[0], 0xff*ai.colors[1], 0xff*ai.colors[2]); } else if (ai.materialIndex >= 0) { const MaterialInfo& mi = mis[ai.materialIndex]; color.set(0xff, 0xff*mi.diffuseColor[0], 0xff*mi.diffuseColor[1], 0xff*mi.diffuseColor[2]); } else { std::cout << "no material" << std::endl; } SMeshBuffer* mb = reinterpret_cast<SMeshBuffer*>(mesh->getMeshBuffer(mesh->getMeshBufferCount()-1)); u32 vCount = mb->getVertexCount(); const DblArray12& tfm = tsi.transformMatrix; CMatrix4<f32> cmat; for (int i=0; i<3; i++) { for (int j=0; j<4; j++) { cmat[j*4+i] = tfm[i*4+j]; } } cmat[3] = cmat[7] = cmat[11] = 0.0; cmat[15] = 1.0; vector3df pos = cmat.getTranslation(); pos.Y *= -1; vector3df rpy = cmat.getRotationDegrees(); rpy.X *= -1; rpy.Z *= -1; vector3df scale = cmat.getScale(); const float *textureCoordinate = NULL; if (ai.textureIndex >= 0) { textureCoordinate = ai.textureCoordinate.get_buffer(); //std::cout << "length of textureCoordinate:" << ai.textureCoordinate.length() << std::endl; //std::cout << "length of vertices:" << si.vertices.length() << std::endl; } for(int j=0; j < numTriangles; ++j) { if (!ai.normalPerVertex) { int p; if (normalIndices.length() == 0) { p = j*3; } else { p = normalIndices[j]*3; } if ( normals != NULL ) { normal.X = normals[p]; normal.Y = -normals[p+1]; //left-handed->right-handed normal.Z = normals[p+2]; } else { normal.X = 0; normal.Y = 0; normal.Z = 1; } } for(int k=0; k < 3; ++k) { long orgVertexIndex = si.triangles[j * 3 + k]; if (ai.normalPerVertex) { int p; if (normalIndices.length()) { p = normalIndices[j*3+k]*3; } else { p = orgVertexIndex*3; } normal.X = normals[p]; normal.Y = -normals[p+1]; //left-handed -> right-handed normal.Z = normals[p+2]; } int p = orgVertexIndex * 3; vertex.X = scale.X*vertices[p]; vertex.Y = -scale.Y*vertices[p+1]; // left-handed -> right-handed vertex.Z = scale.Z*vertices[p+2]; //std::cout << vertices[p] <<"," << vertices[p+1] << "," << vertices[p+2] << std::endl; vector2df texc; if (textureCoordinate) { texc.X = textureCoordinate[ai.textureCoordIndices[j*3+k]*2]; texc.Y = textureCoordinate[ai.textureCoordIndices[j*3+k]*2+1]; } // redundant vertices mb->Vertices.push_back(video::S3DVertex(vertex,normal,color, texc)); } mb->Indices.push_back(vCount); mb->Indices.push_back(vCount+2); mb->Indices.push_back(vCount+1); vCount += 3; } mesh->getMeshBuffer(0)->recalculateBoundingBox(); // Create the Animated mesh if there's anything in the mesh SAnimatedMesh* pAM = 0; if ( 0 != mesh->getMeshBufferCount() ) { mesh->recalculateBoundingBox(); pAM = new SAnimatedMesh(); pAM->Type = EAMT_OBJ; pAM->addMesh(mesh); pAM->recalculateBoundingBox(); } mesh->drop(); vector3df noscale(1,1,1); IMeshSceneNode *node = i_mgr->addMeshSceneNode(mesh, this, -1, pos, rpy, noscale); if (ai.textureIndex >= 0) { const TextureInfo& ti = txs[ai.textureIndex]; //std::cout << "url:" << ti.url << std::endl; video::IVideoDriver* driver = i_mgr->getVideoDriver(); const char *path = ti.url; SMaterial& mat = node->getMaterial(0); ITexture *texture = driver->getTexture(path); mat.setTexture( 0, texture); } } const SensorInfoSequence& sensors = i_li.sensors; for (unsigned int i=0; i<sensors.length(); i++) { const SensorInfo& si = sensors[i]; std::string type(si.type); if (type == "Vision") { //std::cout << si.name << std::endl; ISceneNode *camera = i_mgr->addEmptySceneNode(this); camera->setName(si.name); camera->setPosition(vector3df( si.translation[0], -si.translation[1], si.translation[2])); Vector3 axis(si.rotation[0], si.rotation[1], si.rotation[2]); Matrix33 R; hrp::calcRodrigues(R, axis, si.rotation[3]); Vector3 rpy(rpyFromRot(R)); camera->setRotation(vector3df(-180/M_PI*rpy[0], 180/M_PI*rpy[1], -180/M_PI*rpy[2])); m_cameraInfos.push_back(new GLcamera(si, camera)); } } }
// creates a hill plane IAnimatedMesh* CGeometryCreator::createHillPlaneMesh(const core::dimension2d<f32>& tileSize, const core::dimension2d<s32>& tc, video::SMaterial* material, f32 hillHeight, const core::dimension2d<f32>& ch, const core::dimension2d<f32>& textureRepeatCount) { core::dimension2d<s32> tileCount = tc; tileCount.Height += 1; tileCount.Width += 1; core::dimension2d<f32> countHills = ch; SMeshBuffer* buffer = new SMeshBuffer(); SMesh* mesh = new SMesh(); video::S3DVertex vtx; vtx.Color.set(255,255,255,255); vtx.Normal.set(0,0,0); if (countHills.Width < 0.01f) countHills.Width = 1; if (countHills.Height < 0.01f) countHills.Height = 1; f32 halfX = (tileSize.Width * tileCount.Width) / 2; f32 halfY = (tileSize.Height * tileCount.Height) / 2; // create vertices s32 x = 0; s32 y = 0; core::dimension2d<f32> tx; tx.Width = 1.0f / (tileCount.Width / textureRepeatCount.Width); tx.Height = 1.0f / (tileCount.Height / textureRepeatCount.Height); for (x=0; x<tileCount.Width; ++x) for (y=0; y<tileCount.Height; ++y) { vtx.Pos.set(tileSize.Width * x - halfX, 0, tileSize.Height * y - halfY); vtx.TCoords.set(-(f32)x * tx.Width, (f32)y * tx.Height); if (hillHeight) vtx.Pos.Y = (f32)(sin(vtx.Pos.X * countHills.Width * engine::core::PI / halfX) * cos(vtx.Pos.Z * countHills.Height * engine::core::PI / halfY)) *hillHeight; buffer->Vertices.push_back(vtx); } // create indices for (x=0; x<tileCount.Width-1; ++x) for (y=0; y<tileCount.Height-1; ++y) { s32 current = y*tileCount.Width + x; buffer->Indices.push_back(current); buffer->Indices.push_back(current + 1); buffer->Indices.push_back(current + tileCount.Width); buffer->Indices.push_back(current + 1); buffer->Indices.push_back(current + 1 + tileCount.Width); buffer->Indices.push_back(current + tileCount.Width); } // recalculate normals for (s32 i=0; i<(s32)buffer->Indices.size(); i+=3) { core::plane3d<f32> p( buffer->Vertices[buffer->Indices[i+0]].Pos, buffer->Vertices[buffer->Indices[i+1]].Pos, buffer->Vertices[buffer->Indices[i+2]].Pos); p.Normal.normalize(); buffer->Vertices[buffer->Indices[i+0]].Normal = p.Normal; buffer->Vertices[buffer->Indices[i+1]].Normal = p.Normal; buffer->Vertices[buffer->Indices[i+2]].Normal = p.Normal; } if (material) buffer->Material = *material; buffer->recalculateBoundingBox(); SAnimatedMesh* animatedMesh = new SAnimatedMesh(); mesh->addMeshBuffer(buffer); mesh->recalculateBoundingBox(); animatedMesh->addMesh(mesh); animatedMesh->recalculateBoundingBox(); mesh->drop(); buffer->drop(); return animatedMesh; }
IMeshSceneNode* CustomSceneNodeManager::CreateCylinderSceneNode(scene::ISceneManager* sceneManager, s32 id, SColor& color, unsigned int resolution, float radius, float height) { /*if (!cylinderMesh) {*/ if (resolution >= 4) { SMesh* newCylinderMesh = new SMesh(); SMeshBuffer* buf = new SMeshBuffer(); newCylinderMesh->addMeshBuffer(buf); buf->MappingHint_Vertex = EHM_STATIC; buf->MappingHint_Index = EHM_STATIC; buf->drop(); int noWarningSignedResolution = resolution; float currentTheta = 0.0f; float skipAmount = 2.0f*PI / (float)resolution; float halfHeight = height / 2.0f; S3DVertex temp1 = S3DVertex(Vector3(0.0f, halfHeight, 0.0f), Vector3_Zero, color, vector2d<f32>(0.0f, 0.0f)); S3DVertex temp2 = S3DVertex(Vector3(0.0f, -halfHeight, 0.0f), Vector3_Zero, color, vector2d<f32>(0.0f, 1.0f)); for(int i = 0; i < noWarningSignedResolution; i++) { float x = cosf(currentTheta) * radius; float z = sinf(currentTheta) * radius; temp1.Pos.X = x; temp1.Pos.Z = z; temp1.TCoords.X = currentTheta / 2.0f*PI; temp2.Pos.X = x; temp2.Pos.Z = z; temp2.TCoords.X = currentTheta / 2.0f*PI; buf->Vertices.push_back(temp1); buf->Vertices.push_back(temp2); currentTheta += skipAmount; } temp1.Pos.X = 0.0f; temp1.Pos.Z = 0.0f; temp1.TCoords.X = 0.0f; temp2.Pos.X = 0.0f; temp2.Pos.Z = 0.0f; temp1.TCoords.X = 0.0f; buf->Vertices.push_back(temp1); buf->Vertices.push_back(temp2); //Get indices for(int i = 0; i < noWarningSignedResolution - 1; i++) { buf->Indices.push_back(i*2); buf->Indices.push_back(i*2+2); buf->Indices.push_back(i*2+1); buf->Indices.push_back(i*2+1); buf->Indices.push_back(i*2+2); buf->Indices.push_back(i*2+3); buf->Indices.push_back(i*2); buf->Indices.push_back(buf->Vertices.size()-2); buf->Indices.push_back(i*2+2); buf->Indices.push_back(i*2+1); buf->Indices.push_back(i*2+3); buf->Indices.push_back(buf->Vertices.size()-1); } buf->Indices.push_back(buf->Vertices.size()-4); buf->Indices.push_back(0); buf->Indices.push_back(buf->Vertices.size()-3); buf->Indices.push_back(buf->Vertices.size()-3); buf->Indices.push_back(0); buf->Indices.push_back(1); buf->Indices.push_back(buf->Vertices.size()-4); buf->Indices.push_back(buf->Vertices.size()-2); buf->Indices.push_back(0); buf->Indices.push_back(buf->Vertices.size()-3); buf->Indices.push_back(1); buf->Indices.push_back(buf->Vertices.size()-1); //Calculate normals CalculateNormals(buf->Vertices, buf->Indices); buf->recalculateBoundingBox(); newCylinderMesh->recalculateBoundingBox(); IMeshSceneNode* node = sceneManager->addMeshSceneNode(newCylinderMesh); newCylinderMesh->drop(); return node; } /* return NULL; } else { IMeshSceneNode* node = sceneManager->addMeshSceneNode(cylinderMesh); node->setScale(Vector3(radius, height, radius)); return node; }*/ return NULL; }
~TMesh() { Mesh->drop(); }
IMeshSceneNode* CustomSceneNodeManager::CreateConeSceneNode(scene::ISceneManager* sceneManager, s32 id, SColor& color, unsigned int resolution, float radius, float height) { if (resolution >= 4) { /*IMesh* newConeMesh = sceneManager->getGeometryCreator()->createConeMesh(radius, height, resolution, color, color); IMeshSceneNode* node = sceneManager->addMeshSceneNode(newConeMesh); sceneManager->getMeshCache()->addMesh(irr::io::path("ConeMesh"), (irr::scene::IAnimatedMesh*)newConeMesh); newConeMesh->drop();*/ SMesh* newConeMesh = new SMesh(); SMeshBuffer* buf = new SMeshBuffer(); newConeMesh->addMeshBuffer(buf); buf->MappingHint_Vertex = EHM_STATIC; buf->MappingHint_Index = EHM_STATIC; buf->drop(); int noWarningSignedResolution = resolution; float currentTheta = 0.0f; float skipAmount = 2.0f*PI / (float)resolution; float halfHeight = height / 2.0f; S3DVertex temp1 = S3DVertex(Vector3(0.0f, halfHeight, 0.0f), Vector3_Zero, color, vector2d<f32>(0.0f, 0.0f)); S3DVertex temp2 = S3DVertex(Vector3(0.0f, -halfHeight, 0.0f), Vector3_Zero, color, vector2d<f32>(0.0f, 1.0f)); for(int i = 0; i < noWarningSignedResolution; i++) { float x = cosf(currentTheta) * radius; float z = sinf(currentTheta) * radius; temp2.Pos.X = x; temp2.Pos.Z = z; temp2.TCoords.X = currentTheta / 2.0f*PI; buf->Vertices.push_back(temp2); currentTheta += skipAmount; } buf->Vertices.push_back(temp1); //Get side indices for(int i = 0; i < noWarningSignedResolution - 1; i++) { buf->Indices.push_back(i); buf->Indices.push_back(buf->Vertices.size()-1); buf->Indices.push_back(i+1); } buf->Indices.push_back(buf->Vertices.size()-2); buf->Indices.push_back(buf->Vertices.size()-1); buf->Indices.push_back(0); temp2.Pos.X = 0.0f; temp2.Pos.Z = 0.0f; buf->Vertices.push_back(temp2); //Get bottom indices for(int i = 0; i < noWarningSignedResolution - 1; i++) { buf->Indices.push_back(i); buf->Indices.push_back(i+1); buf->Indices.push_back(buf->Vertices.size()-1); } buf->Indices.push_back(buf->Vertices.size()-1); buf->Indices.push_back(buf->Vertices.size()-3); buf->Indices.push_back(0); //Calculate normals CalculateNormals(buf->Vertices, buf->Indices); buf->recalculateBoundingBox(); newConeMesh->recalculateBoundingBox(); IMeshSceneNode* node = sceneManager->addMeshSceneNode(newConeMesh); newConeMesh->drop(); return node; } return NULL; }
//! creates/loads an animated mesh from the file. //! \return Pointer to the created mesh. Returns 0 if loading failed. //! If you no longer need the mesh, you should call IAnimatedMesh::drop(). //! See IUnknown::drop() for more information. SMesh* MDLFileLoader::CreateMesh(FileReader* file) { studiohdr_t* pMDLFile = (studiohdr_t*)new byte[file->GetSize()]; g_pActiveStudioHdr = pMDLFile; file->Read(pMDLFile, file->GetSize()); mstudiobodyparts_t* pBodyparts = pMDLFile->pBodypart(0); mstudiomodel_t* pModel = pBodyparts->pModel(0); const mstudio_modelvertexdata_t* pVertexdata = pModel->GetVertexData(); stringc vtxFile = file->GetFileName(); vtxFile = vtxFile.subString(0, vtxFile.findLast('.') + 1); vtxFile += "dx90.vtx"; file = m_pFileSystem->CreateAndOpenFile(vtxFile.c_str()); FileHeader_t* pVtxFile = (FileHeader_t*)new byte[file->GetSize()]; file->Read(pVtxFile, file->GetSize()); BodyPartHeader_t* pBodypart = pVtxFile->pBodyPart(0); ModelHeader_t* pVtxModel = pBodypart->pModel(0); ModelLODHeader_t* pLod = pVtxModel->pLOD(0); SMesh* pPirateMesh = new SMesh(); IDirect3DVertexBuffer9* pVertexBuffer = NULL; for (int i=0; i<pLod->numMeshes; i++) { MeshHeader_t* pMesh = pLod->pMesh(i); StripGroupHeader_t* pStripGroup = pMesh->pStripGroup(0); // StripHeader_t* pStrip = pStripGroup->pStrip(0); SD3D9MeshBuffer* pMeshBuffer = new SD3D9MeshBuffer(m_pDriver, MdlVertexDecl); if (i==0) { pMeshBuffer->CreateVertexBuffer(0, sizeof(MDL_VT), pModel->numvertices, D3DUSAGE_WRITEONLY); pVertexBuffer = pMeshBuffer->GetVertexBuffer(0); MDL_VT* pVert = NULL; pVertexBuffer->Lock(0, 0, (void**)&pVert, 0); for (int j=0; j<pModel->numvertices; j++) { const Vector* p = pVertexdata->Position(j); const Vector* n = pVertexdata->Normal(j); const Vector2D* t = pVertexdata->Texcoord(j); pVert[j].Position[0] = p->x; pVert[j].Position[1] = p->y; pVert[j].Position[2] = p->z; pVert[j].Normal[0] = n->x; pVert[j].Normal[1] = n->y; pVert[j].Normal[2] = n->z; pVert[j].Texcoord[0] = t->x; pVert[j].Texcoord[1] = t->y; } pVertexBuffer->Unlock(); } else { pMeshBuffer->SetVertexBuffer(0, pModel->numvertices, pVertexBuffer); } mstudiomesh_t* pStudioMesh = pModel->pMesh(i); pMeshBuffer->CreateIndexBuffer(pStripGroup->numIndices, D3DUSAGE_WRITEONLY, TRUE); u16* pIndices = NULL; pMeshBuffer->GetIndexBuffer()->Lock(0, 0, (void**)&pIndices, 0); for (int j=0; j<pStripGroup->numIndices; j++) { pIndices[j] = pStripGroup->pVertex(*(pStripGroup->pIndex(j)))->origMeshVertID + pStudioMesh->vertexoffset; } pMeshBuffer->GetIndexBuffer()->Unlock(); mstudiotexture_t* pTexture = pMDLFile->pTexture(i); stringc textureName = file->GetFileName(); textureName = textureName.subString(0, textureName.findLast('/') + 1); textureName = textureName + pTexture->pszName() + ".tga"; D3D9Texture* pD3DTexture = m_pDriver->GetTexture(textureName.c_str()); pMeshBuffer->m_Material.Textures[0] = pD3DTexture; pPirateMesh->AddMeshBuffer(pMeshBuffer); pMeshBuffer->Drop(); } delete[] pVtxFile; if (pMDLFile->pVertexBase) delete pMDLFile->pVertexBase; delete[] pMDLFile; file->Drop(); return pPirateMesh; }
IAnimatedMesh* CMY3DMeshFileLoader::createMesh(io::IReadFile* file) { MaterialEntry.clear(); MeshBufferEntry.clear(); ChildNodes.clear(); // working directory (from which we load the scene) core::stringc filepath = FileSystem->getFileDir(file->getFileName()); if (filepath==".") filepath=""; else filepath.append("/"); // read file into memory SMyFileHeader fileHeader; file->read(&fileHeader, sizeof(SMyFileHeader)); #ifdef __BIG_ENDIAN__ fileHeader.MyId = os::Byteswap::byteswap(fileHeader.MyId); fileHeader.Ver = os::Byteswap::byteswap(fileHeader.Ver); #endif if (fileHeader.MyId!=MY3D_ID || fileHeader.Ver!=MY3D_VER) { os::Printer::log("Bad MY3D file header, loading failed!", ELL_ERROR); return 0; } u16 id; file->read(&id, sizeof(id)); #ifdef __BIG_ENDIAN__ id = os::Byteswap::byteswap(id); #endif if (id!=MY3D_SCENE_HEADER_ID) { os::Printer::log("Cannot find MY3D_SCENE_HEADER_ID, loading failed!", ELL_ERROR); return 0; } SMySceneHeader sceneHeader; file->read(&sceneHeader, sizeof(SMySceneHeader)); #ifdef __BIG_ENDIAN__ sceneHeader.MaterialCount = os::Byteswap::byteswap(sceneHeader.MaterialCount); sceneHeader.MeshCount = os::Byteswap::byteswap(sceneHeader.MeshCount); #endif file->read(&id, sizeof(id)); #ifdef __BIG_ENDIAN__ id = os::Byteswap::byteswap(id); #endif if (id!=MY3D_MAT_LIST_ID) { os::Printer::log("Can not find MY3D_MAT_LIST_ID, loading failed!", ELL_ERROR); return 0; } core::stringc texturePath = SceneManager->getParameters()->getAttributeAsString(MY3D_TEXTURE_PATH); file->read(&id, sizeof(id)); #ifdef __BIG_ENDIAN__ id = os::Byteswap::byteswap(id); #endif c8 namebuf[256]; for (s32 m=0; m<sceneHeader.MaterialCount; ++m) { if (id != MY3D_MAT_HEADER_ID) { os::Printer::log("Cannot find MY3D_MAT_HEADER_ID, loading failed!", ELL_ERROR); return 0; } // read material header MaterialEntry.push_back(SMyMaterialEntry()); SMyMaterialEntry& me=MaterialEntry.getLast(); file->read(&(me.Header), sizeof(SMyMaterialHeader)); // read next identificator file->read(&id, sizeof(id)); #ifdef __BIG_ENDIAN__ id = os::Byteswap::byteswap(id); #endif bool gotLightMap=false, gotMainMap=false; for (u32 t=0; t<me.Header.TextureCount; ++t) { if (id==MY3D_TEX_FNAME_ID) file->read(namebuf, 256); else { me.Texture2 = readEmbeddedLightmap(file, namebuf); if (!me.Texture2) return 0; gotLightMap = true; } const core::stringc name(namebuf); const s32 pos = name.findLast('.'); const core::stringc LightingMapStr = "LightingMap"; const s32 ls = LightingMapStr.size(); const bool isSubString = (LightingMapStr == name.subString(core::max_(0, (pos - ls)), ls)); if ((isSubString || (name[pos-1]=='m' && name[pos-2]=='l' && name[pos-3]=='_')) && !gotLightMap) { const bool oldMipMapState = SceneManager->getVideoDriver()->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS); SceneManager->getVideoDriver()->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false); me.Texture2FileName = texturePath.size() ? texturePath : filepath; me.Texture2FileName.append("Lightmaps/"); me.Texture2FileName.append(name); if (name.size()) me.Texture2 = SceneManager->getVideoDriver()->getTexture(me.Texture2FileName); me.MaterialType = video::EMT_LIGHTMAP_M2; gotLightMap = true; SceneManager->getVideoDriver()->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, oldMipMapState); } else if (!gotLightMap && gotMainMap) { me.Texture2FileName = texturePath.size() ? texturePath : filepath; me.Texture2FileName.append(name); if (name.size()) me.Texture2 = SceneManager->getVideoDriver()->getTexture(me.Texture2FileName); me.MaterialType = video::EMT_REFLECTION_2_LAYER; } else if (!gotMainMap && !gotLightMap) { me.Texture1FileName = filepath; me.Texture1FileName.append(name); if (name.size()) me.Texture1 = SceneManager->getVideoDriver()->getTexture(me.Texture1FileName); gotMainMap = true; me.MaterialType = video::EMT_SOLID; } else if (gotLightMap) { me.MaterialType = video::EMT_LIGHTMAP_M2; } file->read(&id, sizeof(id)); #ifdef __BIG_ENDIAN__ id = os::Byteswap::byteswap(id); #endif } // override material types based on their names if (!strncmp(me.Header.Name, "AlphaChannel-", 13)) me.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; else if (!strncmp(me.Header.Name, "SphereMap-", 10)) me.MaterialType = video::EMT_SPHERE_MAP; } // loading meshes if (id!=MY3D_MESH_LIST_ID) { os::Printer::log("Can not find MY3D_MESH_LIST_ID, loading failed!", ELL_ERROR); return 0; } file->read(&id, sizeof(id)); #ifdef __BIG_ENDIAN__ id = os::Byteswap::byteswap(id); #endif for (s32 mesh_id=0; mesh_id<sceneHeader.MeshCount; mesh_id++) { // Warning!!! In some cases MY3D exporter uncorrectly calculates // MeshCount (it's a problem, has to be solved) thats why // i added this code line if (id!=MY3D_MESH_HEADER_ID) break; if (id!=MY3D_MESH_HEADER_ID) { os::Printer::log("Can not find MY3D_MESH_HEADER_ID, loading failed!", ELL_ERROR); return 0; } SMyMeshHeader meshHeader; file->read(&meshHeader, sizeof(SMyMeshHeader)); core::array <SMyVertex> Vertex; core::array <SMyFace> Face; core::array <SMyTVertex> TVertex1, TVertex2; core::array <SMyFace> TFace1, TFace2; s32 vertsNum=0; s32 facesNum=0; // vertices file->read(&id, sizeof(id)); #ifdef __BIG_ENDIAN__ id = os::Byteswap::byteswap(id); #endif if (id!=MY3D_VERTS_ID) { os::Printer::log("Can not find MY3D_VERTS_ID, loading failed!", ELL_ERROR); return 0; } file->read(&vertsNum, sizeof(vertsNum)); Vertex.set_used(vertsNum); file->read(Vertex.pointer(), sizeof(SMyVertex)*vertsNum); // faces file->read(&id, sizeof(id)); #ifdef __BIG_ENDIAN__ id = os::Byteswap::byteswap(id); #endif if (id!=MY3D_FACES_ID) { os::Printer::log("Can not find MY3D_FACES_ID, loading failed!", ELL_ERROR); return 0; } file->read(&facesNum, sizeof(facesNum)); Face.set_used(facesNum); file->read(Face.pointer(), sizeof(SMyFace)*facesNum); // reading texture channels for (s32 tex=0; tex<(s32)meshHeader.TChannelCnt; tex++) { // Max 2 texture channels allowed (but in format .my3d can be more) s32 tVertsNum=0, tFacesNum=0; // reading texture coords file->read(&id, sizeof(id)); #ifdef __BIG_ENDIAN__ id = os::Byteswap::byteswap(id); #endif if (id!=MY3D_TVERTS_ID) { core::stringc msg="Can not find MY3D_TVERTS_ID ("; msg.append(core::stringc(tex)); msg.append("texture channel), loading failed!"); os::Printer::log(msg.c_str(), ELL_ERROR); return 0; } file->read(&tVertsNum, sizeof(tVertsNum)); if (tex==0) { // 1st texture channel TVertex1.set_used(tVertsNum); file->read(TVertex1.pointer(), sizeof(SMyTVertex)*tVertsNum); } else if (tex==1) { // 2nd texture channel TVertex2.set_used(tVertsNum); file->read(TVertex2.pointer(), sizeof(SMyTVertex)*tVertsNum); } else { // skip other texture channels file->seek(file->getPos()+sizeof(SMyTVertex)*tVertsNum); } // reading texture faces file->read(&id, sizeof(id)); #ifdef __BIG_ENDIAN__ id = os::Byteswap::byteswap(id); #endif if (id!=MY3D_TFACES_ID) { core::stringc msg="Can not find MY3D_TFACES_ID ("; msg.append(core::stringc(tex)); msg.append("texture channel), loading failed!"); os::Printer::log(msg.c_str(), ELL_ERROR); return 0; } file->read(&tFacesNum, sizeof(tFacesNum)); if (tex==0) { // 1st texture channel TFace1.set_used(tFacesNum); file->read(TFace1.pointer(), sizeof(SMyFace)*tFacesNum); } else if (tex==1) { // 2nd texture channel TFace2.set_used(tFacesNum); file->read(TFace2.pointer(), sizeof(SMyFace)*tFacesNum); } else { // skip other texture channels file->seek(file->getPos()+sizeof(SMyFace)*tFacesNum); } } // trying to find material SMyMaterialEntry* matEnt = getMaterialEntryByIndex(meshHeader.MatIndex); // creating geometry for the mesh // trying to find mesh buffer for this material CMeshBuffer<video::S3DVertex2TCoords>* buffer = getMeshBufferByMaterialIndex(meshHeader.MatIndex); if (!buffer || (buffer->getVertexBuffer()->getVertexCount()+vertsNum) > SceneManager->getVideoDriver()->getMaximalPrimitiveCount()) { // creating new mesh buffer for this material buffer = new CMeshBuffer<video::S3DVertex2TCoords>(SceneManager->getVideoDriver()->getVertexDescriptor(1)); buffer->Material.MaterialType = video::EMT_LIGHTMAP_M2; // EMT_LIGHTMAP_M4 also possible buffer->Material.Wireframe = false; buffer->Material.Lighting = false; if (matEnt) { buffer->Material.MaterialType = matEnt->MaterialType; if (buffer->Material.MaterialType == video::EMT_REFLECTION_2_LAYER) { buffer->Material.Lighting = true; buffer->Material.setTexture(1, matEnt->Texture1); buffer->Material.setTexture(0, matEnt->Texture2); } else { buffer->Material.setTexture(0, matEnt->Texture1); buffer->Material.setTexture(1, matEnt->Texture2); } if (buffer->Material.MaterialType == video::EMT_TRANSPARENT_ALPHA_CHANNEL) { buffer->Material.BackfaceCulling = true; buffer->Material.Lighting = true; } else if (buffer->Material.MaterialType == video::EMT_SPHERE_MAP) { buffer->Material.Lighting = true; } buffer->Material.AmbientColor = video::SColor( matEnt->Header.AmbientColor.A, matEnt->Header.AmbientColor.R, matEnt->Header.AmbientColor.G, matEnt->Header.AmbientColor.B ); buffer->Material.DiffuseColor = video::SColor( matEnt->Header.DiffuseColor.A, matEnt->Header.DiffuseColor.R, matEnt->Header.DiffuseColor.G, matEnt->Header.DiffuseColor.B ); buffer->Material.EmissiveColor = video::SColor( matEnt->Header.EmissiveColor.A, matEnt->Header.EmissiveColor.R, matEnt->Header.EmissiveColor.G, matEnt->Header.EmissiveColor.B ); buffer->Material.SpecularColor = video::SColor( matEnt->Header.SpecularColor.A, matEnt->Header.SpecularColor.R, matEnt->Header.SpecularColor.G, matEnt->Header.SpecularColor.B ); } else { buffer->Material.setTexture(0, 0); buffer->Material.setTexture(1, 0); buffer->Material.AmbientColor = video::SColor(255, 255, 255, 255); buffer->Material.DiffuseColor = video::SColor(255, 255, 255, 255); buffer->Material.EmissiveColor = video::SColor(0, 0, 0, 0); buffer->Material.SpecularColor = video::SColor(0, 0, 0, 0); } if (matEnt && matEnt->Header.Transparency!=0) { if (buffer->Material.MaterialType == video::EMT_REFLECTION_2_LAYER ) { buffer->Material.MaterialType = video::EMT_TRANSPARENT_REFLECTION_2_LAYER; buffer->Material.Lighting = true; buffer->Material.BackfaceCulling = true; } else { buffer->Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; buffer->Material.Lighting = false; buffer->Material.BackfaceCulling = false; } } else if ( !buffer->Material.getTexture(1) && buffer->Material.MaterialType != video::EMT_TRANSPARENT_ALPHA_CHANNEL && buffer->Material.MaterialType != video::EMT_SPHERE_MAP) { buffer->Material.MaterialType = video::EMT_SOLID; buffer->Material.Lighting = true; } MeshBufferEntry.push_back( SMyMeshBufferEntry(meshHeader.MatIndex, buffer)); } video::S3DVertex2TCoords VertexA, VertexB, VertexC; // vertices (A, B, C) color video::SColor vert_color; if (matEnt && (buffer->Material.MaterialType == video::EMT_TRANSPARENT_VERTEX_ALPHA || buffer->Material.MaterialType == video::EMT_TRANSPARENT_REFLECTION_2_LAYER)) { video::SColor color( matEnt->Header.DiffuseColor.A, matEnt->Header.DiffuseColor.R, matEnt->Header.DiffuseColor.G, matEnt->Header.DiffuseColor.B); vert_color = color.getInterpolated(video::SColor(0,0,0,0), 1-matEnt->Header.Transparency); } else { vert_color = buffer->Material.DiffuseColor; } VertexA.Color = VertexB.Color = VertexC.Color = vert_color; if (buffer->Material.MaterialType == video::EMT_TRANSPARENT_ALPHA_CHANNEL) { buffer->getIndexBuffer()->reallocate(buffer->getIndexBuffer()->getIndexCount()+6*facesNum); buffer->getVertexBuffer()->reallocate(buffer->getVertexBuffer()->getVertexCount()+6*facesNum); } else { buffer->getIndexBuffer()->reallocate(buffer->getIndexBuffer()->getIndexCount()+3*facesNum); buffer->getVertexBuffer()->reallocate(buffer->getVertexBuffer()->getVertexCount()+3*facesNum); } for (int f=0; f<facesNum; f++) { // vertex A VertexA.Pos.X = Vertex[Face[f].C].Coord.X; VertexA.Pos.Y = Vertex[Face[f].C].Coord.Y; VertexA.Pos.Z = Vertex[Face[f].C].Coord.Z; VertexA.Normal.X = Vertex[Face[f].C].Normal.X; VertexA.Normal.Y = Vertex[Face[f].C].Normal.Y; VertexA.Normal.Z = Vertex[Face[f].C].Normal.Z; if (meshHeader.TChannelCnt>0) { VertexA.TCoords.X = TVertex1[TFace1[f].C].TCoord.X; VertexA.TCoords.Y = TVertex1[TFace1[f].C].TCoord.Y; } if (meshHeader.TChannelCnt>1) { VertexA.TCoords2.X = TVertex2[TFace2[f].C].TCoord.X; VertexA.TCoords2.Y = TVertex2[TFace2[f].C].TCoord.Y; } // vertex B VertexB.Pos.X = Vertex[Face[f].B].Coord.X; VertexB.Pos.Y = Vertex[Face[f].B].Coord.Y; VertexB.Pos.Z = Vertex[Face[f].B].Coord.Z; VertexB.Normal.X = Vertex[Face[f].B].Normal.X; VertexB.Normal.Y = Vertex[Face[f].B].Normal.Y; VertexB.Normal.Z = Vertex[Face[f].B].Normal.Z; if (meshHeader.TChannelCnt>0) { VertexB.TCoords.X = TVertex1[TFace1[f].B].TCoord.X; VertexB.TCoords.Y = TVertex1[TFace1[f].B].TCoord.Y; } if (meshHeader.TChannelCnt>1) { VertexB.TCoords2.X = TVertex2[TFace2[f].B].TCoord.X; VertexB.TCoords2.Y = TVertex2[TFace2[f].B].TCoord.Y; } // vertex C VertexC.Pos.X = Vertex[Face[f].A].Coord.X; VertexC.Pos.Y = Vertex[Face[f].A].Coord.Y; VertexC.Pos.Z = Vertex[Face[f].A].Coord.Z; VertexC.Normal.X = Vertex[Face[f].A].Normal.X; VertexC.Normal.Y = Vertex[Face[f].A].Normal.Y; VertexC.Normal.Z = Vertex[Face[f].A].Normal.Z; if (meshHeader.TChannelCnt>0) { VertexC.TCoords.X = TVertex1[TFace1[f].A].TCoord.X; VertexC.TCoords.Y = TVertex1[TFace1[f].A].TCoord.Y; } if (meshHeader.TChannelCnt>1) { VertexC.TCoords2.X = TVertex2[TFace2[f].A].TCoord.X; VertexC.TCoords2.Y = TVertex2[TFace2[f].A].TCoord.Y; } // store 3d data in mesh buffer buffer->getIndexBuffer()->addIndex(buffer->getVertexBuffer()->getVertexCount()); buffer->getVertexBuffer()->addVertex(&VertexA); buffer->getIndexBuffer()->addIndex(buffer->getVertexBuffer()->getVertexCount()); buffer->getVertexBuffer()->addVertex(&VertexB); buffer->getIndexBuffer()->addIndex(buffer->getVertexBuffer()->getVertexCount()); buffer->getVertexBuffer()->addVertex(&VertexC); //***************************************************************** // !!!!!! W A R N I N G !!!!!!! //***************************************************************** // For materials with alpha channel we duplicate all faces. // This has be done for proper lighting calculation of the back faces. // So you must remember this while you creating your models !!!!! //***************************************************************** // !!!!!! W A R N I N G !!!!!!! //***************************************************************** if (buffer->Material.MaterialType == video::EMT_TRANSPARENT_ALPHA_CHANNEL) { VertexA.Normal = core::vector3df(-VertexA.Normal.X, -VertexA.Normal.Y, -VertexA.Normal.Z); VertexB.Normal = core::vector3df(-VertexB.Normal.X, -VertexB.Normal.Y, -VertexB.Normal.Z); VertexC.Normal = core::vector3df(-VertexC.Normal.X, -VertexC.Normal.Y, -VertexC.Normal.Z); buffer->getIndexBuffer()->addIndex(buffer->getVertexBuffer()->getVertexCount()); buffer->getVertexBuffer()->addVertex(&VertexC); buffer->getIndexBuffer()->addIndex(buffer->getVertexBuffer()->getVertexCount()); buffer->getVertexBuffer()->addVertex(&VertexB); buffer->getIndexBuffer()->addIndex(buffer->getVertexBuffer()->getVertexCount()); buffer->getVertexBuffer()->addVertex(&VertexA); } } file->read(&id, sizeof(id)); #ifdef __BIG_ENDIAN__ id = os::Byteswap::byteswap(id); #endif } // creating mesh SMesh* mesh = new SMesh(); for (u32 num=0; num<MeshBufferEntry.size(); ++num) { CMeshBuffer<video::S3DVertex2TCoords>* buffer = MeshBufferEntry[num].MeshBuffer; if (!buffer) continue; mesh->addMeshBuffer(buffer); buffer->recalculateBoundingBox(); buffer->drop(); } mesh->recalculateBoundingBox(); if (id != MY3D_FILE_END_ID) os::Printer::log("Loading finished, but can not find MY3D_FILE_END_ID token.", ELL_WARNING); SAnimatedMesh* am = new SAnimatedMesh(); am->addMesh(mesh); mesh->drop(); am->recalculateBoundingBox(); return am; }
/**Creates/loads an animated mesh from the file. \return Pointer to the created mesh. Returns 0 if loading failed. If you no longer need the mesh, you should call IAnimatedMesh::drop(). See IReferenceCounted::drop() for more information.*/ IAnimatedMesh* CDMFLoader::createMesh(io::IReadFile* file) { if (!file) return 0; video::IVideoDriver* driver = SceneMgr->getVideoDriver(); //Load stringlist StringList dmfRawFile; LoadFromFile(file, dmfRawFile); if (dmfRawFile.size()==0) return 0; SMesh * mesh = new SMesh(); u32 i; dmfHeader header; //load header core::array<dmfMaterial> materiali; if (GetDMFHeader(dmfRawFile, header)) { //let's set ambient light SceneMgr->setAmbientLight(header.dmfAmbient); //let's create the correct number of materials, vertices and faces dmfVert *verts=new dmfVert[header.numVertices]; dmfFace *faces=new dmfFace[header.numFaces]; //let's get the materials #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Loading materials", core::stringc(header.numMaterials).c_str()); #endif GetDMFMaterials(dmfRawFile, materiali, header.numMaterials); //let's get vertices and faces #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Loading geometry"); #endif GetDMFVerticesFaces(dmfRawFile, verts, faces); //create a meshbuffer for each material, then we'll remove empty ones #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Creating meshbuffers."); #endif for (i=0; i<header.numMaterials; i++) { //create a new SMeshBufferLightMap for each material SSkinMeshBuffer* buffer = new SSkinMeshBuffer(); buffer->Material.MaterialType = video::EMT_LIGHTMAP_LIGHTING; buffer->Material.Wireframe = false; buffer->Material.Lighting = true; mesh->addMeshBuffer(buffer); buffer->drop(); } // Build the mesh buffers #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Adding geometry to mesh."); #endif for (i = 0; i < header.numFaces; i++) { #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Polygon with #vertices", core::stringc(faces[i].numVerts).c_str()); #endif if (faces[i].numVerts < 3) continue; const core::vector3df normal = core::triangle3df(verts[faces[i].firstVert].pos, verts[faces[i].firstVert+1].pos, verts[faces[i].firstVert+2].pos).getNormal().normalize(); SSkinMeshBuffer* meshBuffer = (SSkinMeshBuffer*)mesh->getMeshBuffer( faces[i].materialID); const bool use2TCoords = meshBuffer->Vertices_2TCoords.size() || materiali[faces[i].materialID].lightmapName.size(); if (use2TCoords && meshBuffer->Vertices_Standard.size()) meshBuffer->convertTo2TCoords(); const u32 base = meshBuffer->Vertices_2TCoords.size()?meshBuffer->Vertices_2TCoords.size():meshBuffer->Vertices_Standard.size(); // Add this face's verts if (use2TCoords) { // make sure we have the proper type set meshBuffer->VertexType=video::EVT_2TCOORDS; for (u32 v = 0; v < faces[i].numVerts; v++) { const dmfVert& vv = verts[faces[i].firstVert + v]; video::S3DVertex2TCoords vert(vv.pos, normal, video::SColor(255,255,255,255), vv.tc, vv.lc); if (materiali[faces[i].materialID].textureBlend==4 && SceneMgr->getParameters()->getAttributeAsBool(DMF_FLIP_ALPHA_TEXTURES)) { vert.TCoords.set(vv.tc.X,-vv.tc.Y); } meshBuffer->Vertices_2TCoords.push_back(vert); } } else { for (u32 v = 0; v < faces[i].numVerts; v++) { const dmfVert& vv = verts[faces[i].firstVert + v]; video::S3DVertex vert(vv.pos, normal, video::SColor(255,255,255,255), vv.tc); if (materiali[faces[i].materialID].textureBlend==4 && SceneMgr->getParameters()->getAttributeAsBool(DMF_FLIP_ALPHA_TEXTURES)) { vert.TCoords.set(vv.tc.X,-vv.tc.Y); } meshBuffer->Vertices_Standard.push_back(vert); } } // Now add the indices // This weird loop turns convex polygons into triangle strips. // I do it this way instead of a simple fan because it usually // looks a lot better in wireframe, for example. u32 h = faces[i].numVerts - 1, l = 0, c; // High, Low, Center for (u32 v = 0; v < faces[i].numVerts - 2; v++) { if (v & 1) // odd c = h - 1; else // even c = l + 1; meshBuffer->Indices.push_back(base + h); meshBuffer->Indices.push_back(base + l); meshBuffer->Indices.push_back(base + c); if (v & 1) // odd h--; else // even l++; } } delete [] verts; delete [] faces; } // delete all buffers without geometry in it. #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Cleaning meshbuffers."); #endif i = 0; while(i < mesh->MeshBuffers.size()) { if (mesh->MeshBuffers[i]->getVertexCount() == 0 || mesh->MeshBuffers[i]->getIndexCount() == 0) { // Meshbuffer is empty -- drop it mesh->MeshBuffers[i]->drop(); mesh->MeshBuffers.erase(i); materiali.erase(i); } else { i++; } } { //load textures and lightmaps in materials. //don't worry if you receive a could not load texture, cause if you don't need //a particular material in your scene it will be loaded and then destroyed. #ifdef _IRR_DMF_DEBUG_ os::Printer::log("Loading textures."); #endif const bool use_mat_dirs=!SceneMgr->getParameters()->getAttributeAsBool(DMF_IGNORE_MATERIALS_DIRS); core::stringc path; if ( SceneMgr->getParameters()->existsAttribute(DMF_TEXTURE_PATH) ) path = SceneMgr->getParameters()->getAttributeAsString(DMF_TEXTURE_PATH); else path = FileSystem->getFileDir(file->getFileName()); path += ('/'); for (i=0; i<mesh->getMeshBufferCount(); i++) { //texture and lightmap video::ITexture *tex = 0; video::ITexture *lig = 0; //current buffer to apply material video::SMaterial& mat = mesh->getMeshBuffer(i)->getMaterial(); //Primary texture is normal if (materiali[i].textureFlag==0) { if (materiali[i].textureBlend==4) driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT,true); findFile(use_mat_dirs, path, materiali[i].pathName, materiali[i].textureName); tex = driver->getTexture(materiali[i].textureName); } //Primary texture is just a colour else if(materiali[i].textureFlag==1) { video::SColor color(axtoi(materiali[i].textureName.c_str())); //just for compatibility with older Irrlicht versions //to support transparent materials if (color.getAlpha()!=255 && materiali[i].textureBlend==4) driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT,true); video::IImage *immagine= driver->createImage(video::ECF_A8R8G8B8, core::dimension2d<u32>(8,8)); immagine->fill(color); tex = driver->addTexture("", immagine); immagine->drop(); //to support transparent materials if (color.getAlpha()!=255 && materiali[i].textureBlend==4) { mat.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; mat.MaterialTypeParam =(((f32) (color.getAlpha()-1))/255.0f); } } //Lightmap is present if (materiali[i].lightmapFlag == 0) { findFile(use_mat_dirs, path, materiali[i].pathName, materiali[i].lightmapName); lig = driver->getTexture(materiali[i].lightmapName); } else //no lightmap { mat.MaterialType = video::EMT_SOLID; const f32 mult = 100.0f - header.dmfShadow; mat.AmbientColor=header.dmfAmbient.getInterpolated(video::SColor(255,0,0,0),mult/100.f); } if (materiali[i].textureBlend==4) { mat.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; mat.MaterialTypeParam = SceneMgr->getParameters()->getAttributeAsFloat(DMF_ALPHA_CHANNEL_REF); } //if texture is present mirror vertically owing to DeleD representation if (tex && header.dmfVersion<1.1) { const core::dimension2d<u32> texsize = tex->getSize(); void* pp = tex->lock(); if (pp) { const video::ECOLOR_FORMAT format = tex->getColorFormat(); if (format == video::ECF_A1R5G5B5) { s16* p = (s16*)pp; s16 tmp=0; for (u32 x=0; x<texsize.Width; x++) for (u32 y=0; y<texsize.Height/2; y++) { tmp=p[y*texsize.Width + x]; p[y*texsize.Width + x] = p[(texsize.Height-y-1)*texsize.Width + x]; p[(texsize.Height-y-1)*texsize.Width + x]=tmp; } } else if (format == video::ECF_A8R8G8B8) { s32* p = (s32*)pp; s32 tmp=0; for (u32 x=0; x<texsize.Width; x++) for (u32 y=0; y<texsize.Height/2; y++) { tmp=p[y*texsize.Width + x]; p[y*texsize.Width + x] = p[(texsize.Height-y-1)*texsize.Width + x]; p[(texsize.Height-y-1)*texsize.Width + x]=tmp; } } } tex->unlock(); tex->regenerateMipMapLevels(); } //if lightmap is present mirror vertically owing to DeleD rapresentation if (lig && header.dmfVersion<1.1) { const core::dimension2d<u32> ligsize=lig->getSize(); void* pp = lig->lock(); if (pp) { video::ECOLOR_FORMAT format = lig->getColorFormat(); if (format == video::ECF_A1R5G5B5) { s16* p = (s16*)pp; s16 tmp=0; for (u32 x=0; x<ligsize.Width; x++) { for (u32 y=0; y<ligsize.Height/2; y++) { tmp=p[y*ligsize.Width + x]; p[y*ligsize.Width + x] = p[(ligsize.Height-y-1)*ligsize.Width + x]; p[(ligsize.Height-y-1)*ligsize.Width + x]=tmp; } } } else if (format == video::ECF_A8R8G8B8) { s32* p = (s32*)pp; s32 tmp=0; for (u32 x=0; x<ligsize.Width; x++) { for (u32 y=0; y<ligsize.Height/2; y++) { tmp=p[y*ligsize.Width + x]; p[y*ligsize.Width + x] = p[(ligsize.Height-y-1)*ligsize.Width + x]; p[(ligsize.Height-y-1)*ligsize.Width + x]=tmp; } } } } lig->unlock(); lig->regenerateMipMapLevels(); } mat.setTexture(0, tex); mat.setTexture(1, lig); } } // create bounding box for (i = 0; i < mesh->MeshBuffers.size(); ++i) { mesh->MeshBuffers[i]->recalculateBoundingBox(); } mesh->recalculateBoundingBox(); // Set up an animated mesh to hold the mesh SAnimatedMesh* AMesh = new SAnimatedMesh(); AMesh->Type = EAMT_UNKNOWN; AMesh->addMesh(mesh); AMesh->recalculateBoundingBox(); mesh->drop(); return AMesh; }
void SolveSpace::ExportLinesAndMesh(SEdgeList *sel, SBezierList *sbl, SMesh *sm, Vector u, Vector v, Vector n, Vector origin, double cameraTan, VectorFileWriter *out) { double s = 1.0 / SS.exportScale; // Project into the export plane; so when we're done, z doesn't matter, // and x and y are what goes in the DXF. SEdge *e; for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) { // project into the specified csys, and apply export scale (e->a) = e->a.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s); (e->b) = e->b.InPerspective(u, v, n, origin, cameraTan).ScaledBy(s); } SBezier *b; if(sbl) { for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) { *b = b->InPerspective(u, v, n, origin, cameraTan); int i; for(i = 0; i <= b->deg; i++) { b->ctrl[i] = (b->ctrl[i]).ScaledBy(s); } } } // If cutter radius compensation is requested, then perform it now if(fabs(SS.exportOffset) > LENGTH_EPS) { // assemble those edges into a polygon, and clear the edge list SPolygon sp; ZERO(&sp); sel->AssemblePolygon(&sp, NULL); sel->Clear(); SPolygon compd; ZERO(&compd); sp.normal = Vector::From(0, 0, -1); sp.FixContourDirections(); sp.OffsetInto(&compd, SS.exportOffset*s); sp.Clear(); compd.MakeEdgesInto(sel); compd.Clear(); } // Now the triangle mesh; project, then build a BSP to perform // occlusion testing and generated the shaded surfaces. SMesh smp; ZERO(&smp); if(sm) { Vector l0 = (SS.lightDir[0]).WithMagnitude(1), l1 = (SS.lightDir[1]).WithMagnitude(1); STriangle *tr; for(tr = sm->l.First(); tr; tr = sm->l.NextAfter(tr)) { STriangle tt = *tr; tt.a = (tt.a).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s); tt.b = (tt.b).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s); tt.c = (tt.c).InPerspective(u, v, n, origin, cameraTan).ScaledBy(s); // And calculate lighting for the triangle Vector n = tt.Normal().WithMagnitude(1); double lighting = SS.ambientIntensity + max(0, (SS.lightIntensity[0])*(n.Dot(l0))) + max(0, (SS.lightIntensity[1])*(n.Dot(l1))); double r = min(1, REDf (tt.meta.color)*lighting), g = min(1, GREENf(tt.meta.color)*lighting), b = min(1, BLUEf (tt.meta.color)*lighting); tt.meta.color = RGBf(r, g, b); smp.AddTriangle(&tt); } } // Use the BSP routines to generate the split triangles in paint order. SBsp3 *bsp = SBsp3::FromMesh(&smp); SMesh sms; ZERO(&sms); bsp->GenerateInPaintOrder(&sms); // And cull the back-facing triangles STriangle *tr; sms.l.ClearTags(); for(tr = sms.l.First(); tr; tr = sms.l.NextAfter(tr)) { Vector n = tr->Normal(); if(n.z < 0) { tr->tag = 1; } } sms.l.RemoveTagged(); // And now we perform hidden line removal if requested SEdgeList hlrd; ZERO(&hlrd); if(sm && !SS.GW.showHdnLines) { SKdNode *root = SKdNode::From(&smp); // Generate the edges where a curved surface turns from front-facing // to back-facing. if(SS.GW.showEdges) { root->MakeCertainEdgesInto(sel, SKdNode::TURNING_EDGES, false, NULL, NULL); } root->ClearTags(); int cnt = 1234; SEdge *se; for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) { if(se->auxA == Style::CONSTRAINT) { // Constraints should not get hidden line removed; they're // always on top. hlrd.AddEdge(se->a, se->b, se->auxA); continue; } SEdgeList out; ZERO(&out); // Split the original edge against the mesh out.AddEdge(se->a, se->b, se->auxA); root->OcclusionTestLine(*se, &out, cnt); // the occlusion test splits unnecessarily; so fix those out.MergeCollinearSegments(se->a, se->b); cnt++; // And add the results to our output SEdge *sen; for(sen = out.l.First(); sen; sen = out.l.NextAfter(sen)) { hlrd.AddEdge(sen->a, sen->b, sen->auxA); } out.Clear(); } sel = &hlrd; } // We kept the line segments and Beziers separate until now; but put them // all together, and also project everything into the xy plane, since not // all export targets ignore the z component of the points. for(e = sel->l.First(); e; e = sel->l.NextAfter(e)) { SBezier sb = SBezier::From(e->a, e->b); sb.auxA = e->auxA; sbl->l.Add(&sb); } for(b = sbl->l.First(); b; b = sbl->l.NextAfter(b)) { for(int i = 0; i <= b->deg; i++) { b->ctrl[i].z = 0; } } // If possible, then we will assemble these output curves into loops. They // will then get exported as closed paths. SBezierLoopSetSet sblss; ZERO(&sblss); SBezierList leftovers; ZERO(&leftovers); SSurface srf = SSurface::FromPlane(Vector::From(0, 0, 0), Vector::From(1, 0, 0), Vector::From(0, 1, 0)); SPolygon spxyz; ZERO(&spxyz); bool allClosed; SEdge notClosedAt; sbl->l.ClearTags(); sblss.FindOuterFacesFrom(sbl, &spxyz, &srf, SS.ChordTolMm()*s, &allClosed, ¬ClosedAt, NULL, NULL, &leftovers); for(b = leftovers.l.First(); b; b = leftovers.l.NextAfter(b)) { sblss.AddOpenPath(b); } // Now write the lines and triangles to the output file out->Output(&sblss, &sms); leftovers.Clear(); spxyz.Clear(); sblss.Clear(); smp.Clear(); sms.Clear(); hlrd.Clear(); }
void Group::GenerateShellAndMesh(void) { bool prevBooleanFailed = booleanFailed; booleanFailed = false; Group *srcg = this; thisShell.Clear(); thisMesh.Clear(); runningShell.Clear(); runningMesh.Clear(); // Don't attempt a lathe or extrusion unless the source section is good: // planar and not self-intersecting. bool haveSrc = true; if(type == EXTRUDE || type == LATHE) { Group *src = SK.GetGroup(opA); if(src->polyError.how != POLY_GOOD) { haveSrc = false; } } if(type == TRANSLATE || type == ROTATE) { // A step and repeat gets merged against the group's prevous group, // not our own previous group. srcg = SK.GetGroup(opA); GenerateForStepAndRepeat<SShell>(&(srcg->thisShell), &thisShell); GenerateForStepAndRepeat<SMesh> (&(srcg->thisMesh), &thisMesh); } else if(type == EXTRUDE && haveSrc) { Group *src = SK.GetGroup(opA); Vector translate = Vector::From(h.param(0), h.param(1), h.param(2)); Vector tbot, ttop; if(subtype == ONE_SIDED) { tbot = Vector::From(0, 0, 0); ttop = translate.ScaledBy(2); } else { tbot = translate.ScaledBy(-1); ttop = translate.ScaledBy(1); } SBezierLoopSetSet *sblss = &(src->bezierLoops); SBezierLoopSet *sbls; for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) { int is = thisShell.surface.n; // Extrude this outer contour (plus its inner contours, if present) thisShell.MakeFromExtrusionOf(sbls, tbot, ttop, color); // And for any plane faces, annotate the model with the entity for // that face, so that the user can select them with the mouse. Vector onOrig = sbls->point; int i; for(i = is; i < thisShell.surface.n; i++) { SSurface *ss = &(thisShell.surface.elem[i]); hEntity face = Entity::NO_ENTITY; Vector p = ss->PointAt(0, 0), n = ss->NormalAt(0, 0).WithMagnitude(1); double d = n.Dot(p); if(i == is || i == (is + 1)) { // These are the top and bottom of the shell. if(fabs((onOrig.Plus(ttop)).Dot(n) - d) < LENGTH_EPS) { face = Remap(Entity::NO_ENTITY, REMAP_TOP); ss->face = face.v; } if(fabs((onOrig.Plus(tbot)).Dot(n) - d) < LENGTH_EPS) { face = Remap(Entity::NO_ENTITY, REMAP_BOTTOM); ss->face = face.v; } continue; } // So these are the sides if(ss->degm != 1 || ss->degn != 1) continue; Entity *e; for(e = SK.entity.First(); e; e = SK.entity.NextAfter(e)) { if(e->group.v != opA.v) continue; if(e->type != Entity::LINE_SEGMENT) continue; Vector a = SK.GetEntity(e->point[0])->PointGetNum(), b = SK.GetEntity(e->point[1])->PointGetNum(); a = a.Plus(ttop); b = b.Plus(ttop); // Could get taken backwards, so check all cases. if((a.Equals(ss->ctrl[0][0]) && b.Equals(ss->ctrl[1][0])) || (b.Equals(ss->ctrl[0][0]) && a.Equals(ss->ctrl[1][0])) || (a.Equals(ss->ctrl[0][1]) && b.Equals(ss->ctrl[1][1])) || (b.Equals(ss->ctrl[0][1]) && a.Equals(ss->ctrl[1][1]))) { face = Remap(e->h, REMAP_LINE_TO_FACE); ss->face = face.v; break; } } } } } else if(type == LATHE && haveSrc) { Group *src = SK.GetGroup(opA); Vector pt = SK.GetEntity(predef.origin)->PointGetNum(), axis = SK.GetEntity(predef.entityB)->VectorGetNum(); axis = axis.WithMagnitude(1); SBezierLoopSetSet *sblss = &(src->bezierLoops); SBezierLoopSet *sbls; for(sbls = sblss->l.First(); sbls; sbls = sblss->l.NextAfter(sbls)) { thisShell.MakeFromRevolutionOf(sbls, pt, axis, color, this); } } else if(type == LINKED) { // The imported shell or mesh are copied over, with the appropriate // transformation applied. We also must remap the face entities. Vector offset = { SK.GetParam(h.param(0))->val, SK.GetParam(h.param(1))->val, SK.GetParam(h.param(2))->val }; Quaternion q = { SK.GetParam(h.param(3))->val, SK.GetParam(h.param(4))->val, SK.GetParam(h.param(5))->val, SK.GetParam(h.param(6))->val }; thisMesh.MakeFromTransformationOf(&impMesh, offset, q, scale); thisMesh.RemapFaces(this, 0); thisShell.MakeFromTransformationOf(&impShell, offset, q, scale); thisShell.RemapFaces(this, 0); } if(srcg->meshCombine != COMBINE_AS_ASSEMBLE) { thisShell.MergeCoincidentSurfaces(); } // So now we've got the mesh or shell for this group. Combine it with // the previous group's mesh or shell with the requested Boolean, and // we're done. Group *prevg = srcg->RunningMeshGroup(); if(prevg->runningMesh.IsEmpty() && thisMesh.IsEmpty() && !forceToMesh) { SShell *prevs = &(prevg->runningShell); GenerateForBoolean<SShell>(prevs, &thisShell, &runningShell, srcg->meshCombine); if(srcg->meshCombine != COMBINE_AS_ASSEMBLE) { runningShell.MergeCoincidentSurfaces(); } // If the Boolean failed, then we should note that in the text screen // for this group. booleanFailed = runningShell.booleanFailed; if(booleanFailed != prevBooleanFailed) { SS.ScheduleShowTW(); } } else { SMesh prevm, thism; prevm = {}; thism = {}; prevm.MakeFromCopyOf(&(prevg->runningMesh)); prevg->runningShell.TriangulateInto(&prevm); thism.MakeFromCopyOf(&thisMesh); thisShell.TriangulateInto(&thism); SMesh outm = {}; GenerateForBoolean<SMesh>(&prevm, &thism, &outm, srcg->meshCombine); // And make sure that the output mesh is vertex-to-vertex. SKdNode *root = SKdNode::From(&outm); root->SnapToMesh(&outm); root->MakeMeshInto(&runningMesh); outm.Clear(); thism.Clear(); prevm.Clear(); } displayDirty = true; }
IMeshSceneNode* CustomSceneNodeManager::CreateCapsuleSceneNode(scene::ISceneManager* sceneManager, s32 id, SColor& color, unsigned int resolution, float radius, float heightFromSphereCenters) { if (resolution >= 4) { SMesh* newCapsuleMesh = new SMesh(); SMeshBuffer* buf = new SMeshBuffer(); newCapsuleMesh->addMeshBuffer(buf); buf->MappingHint_Vertex = EHM_STATIC; buf->MappingHint_Index = EHM_STATIC; buf->drop(); int noWarningSignedResolution = resolution; float thetaSkipAmount = 2.0f*PI / (float)resolution; float halfHeight = heightFromSphereCenters / 2.0f; float phiSkipAmount = PI*0.5f / (float)resolution; S3DVertex temp1 = S3DVertex(Vector3(0.0f, halfHeight, 0.0f), Vector3_Zero, color, vector2d<f32>(0.0f, 0.0f)); S3DVertex temp2 = S3DVertex(Vector3(0.0f, -halfHeight, 0.0f), Vector3_Zero, color, vector2d<f32>(0.0f, 1.0f)); float currentTheta = 0.0f; float currentPhi = phiSkipAmount; temp1.Pos.Y = halfHeight + radius; buf->Vertices.push_back(temp1); //Semi-sphere Tips for(unsigned int i = 1; i < resolution; i++) { for(unsigned int j = 0; j < resolution; j++) { float x = sinf(currentPhi) * cosf(currentTheta) * radius; float y = cosf(currentPhi) * radius; float z = sinf(currentPhi) * sinf(currentTheta) * radius; temp1.Pos.X = x; temp1.Pos.Y = y + halfHeight; temp1.Pos.Z = z; temp1.TCoords.X = currentTheta / 2.0f*PI; temp1.TCoords.Y = currentPhi / PI; buf->Vertices.push_back(temp1); currentTheta += thetaSkipAmount; } currentTheta = 0.0f; currentPhi += phiSkipAmount; } currentTheta = 0.0f; currentPhi = PI/2.0f; //Semi-sphere Tips for(unsigned int i = 1; i < resolution; i++) { for(unsigned int j = 0; j < resolution; j++) { float x = sinf(currentPhi) * cosf(currentTheta) * radius; float y = cosf(currentPhi) * radius; float z = sinf(currentPhi) * sinf(currentTheta) * radius; temp1.Pos.X = x; temp1.Pos.Y = y - halfHeight; temp1.Pos.Z = z; temp1.TCoords.X = currentTheta / 2.0f*PI; temp1.TCoords.Y = currentPhi / PI; buf->Vertices.push_back(temp1); currentTheta += thetaSkipAmount; } currentTheta = 0.0f; currentPhi += phiSkipAmount; } temp1.Pos.X = 0.0f; temp1.Pos.Y = -(halfHeight + radius); temp1.Pos.Z = 0.0f; buf->Vertices.push_back(temp1); //Top vertex indices for(unsigned int i = 1; i <= resolution; i++) { if (i == resolution) { buf->Indices.push_back(i); buf->Indices.push_back(0); buf->Indices.push_back(1); } else { buf->Indices.push_back(i); buf->Indices.push_back(0); buf->Indices.push_back(i + 1); } } //Get indices int i = 1 + resolution; while(i < buf->Vertices.size() - 1) { for(unsigned int j = 1; j < resolution; j++) { buf->Indices.push_back(i); buf->Indices.push_back(i - noWarningSignedResolution); buf->Indices.push_back(i - noWarningSignedResolution + 1); buf->Indices.push_back(i); buf->Indices.push_back(i - noWarningSignedResolution + 1); buf->Indices.push_back(i + 1); i++; } buf->Indices.push_back(i); buf->Indices.push_back(i - noWarningSignedResolution); buf->Indices.push_back(i - noWarningSignedResolution + 1 - resolution); buf->Indices.push_back(i); buf->Indices.push_back(i - noWarningSignedResolution + 1 - resolution); buf->Indices.push_back(i + 1 - resolution); i++; } //Bottom vertex indices for(int i = resolution; i >= 1 ; i--) { if (i == 1) { /*buf->Indices.push_back(i); buf->Indices.push_back(0); buf->Indices.push_back(1);*/ buf->Indices.push_back(buf->Vertices.size() -1); buf->Indices.push_back(buf->Vertices.size() -1 - i); buf->Indices.push_back(buf->Vertices.size() - 1 - resolution); } else { buf->Indices.push_back(buf->Vertices.size() -1); buf->Indices.push_back(buf->Vertices.size() -1 - i); buf->Indices.push_back(buf->Vertices.size() - i); } } //Calculate normals CalculateNormals(buf->Vertices, buf->Indices); buf->recalculateBoundingBox(); newCapsuleMesh->recalculateBoundingBox(); IMeshSceneNode* node = sceneManager->addMeshSceneNode(newCapsuleMesh); newCapsuleMesh->drop(); return node; } return NULL; }
IAnimatedMesh* CLMTSMeshFileLoader::createMesh(io::IReadFile* file) { u32 i; u32 id; // HEADER file->read(&Header, sizeof(SLMTSHeader)); if (Header.MagicID == 0x4C4D5354) { FlipEndianess = true; Header.MagicID = os::Byteswap::byteswap(Header.MagicID); Header.Version = os::Byteswap::byteswap(Header.Version); Header.HeaderSize = os::Byteswap::byteswap(Header.HeaderSize); Header.TextureCount = os::Byteswap::byteswap(Header.TextureCount); Header.SubsetCount = os::Byteswap::byteswap(Header.SubsetCount); Header.TriangleCount = os::Byteswap::byteswap(Header.TriangleCount); Header.SubsetSize = os::Byteswap::byteswap(Header.SubsetSize); Header.VertexSize = os::Byteswap::byteswap(Header.VertexSize); } if (Header.MagicID != 0x53544D4C) { // "LMTS" os::Printer::log("LMTS ERROR: wrong header magic id!", ELL_ERROR); return 0; } //Skip any User Data (arbitrary app specific data) const s32 userSize = Header.HeaderSize - sizeof(SLMTSHeader); if (userSize>0) file->seek(userSize,true); // TEXTURES file->read(&id, sizeof(u32)); if (FlipEndianess) id = os::Byteswap::byteswap(id); if (id != 0x54584554) { // "TEXT" os::Printer::log("LMTS ERROR: wrong texture magic id!", ELL_ERROR); return 0; } Textures = new SLMTSTextureInfoEntry[Header.TextureCount]; file->read(Textures, sizeof(SLMTSTextureInfoEntry)*Header.TextureCount); if (FlipEndianess) { for (i=0; i<Header.TextureCount; ++i) Textures[i].Flags = os::Byteswap::byteswap(Textures[i].Flags); } // SUBSETS file->read(&id, sizeof(u32)); if (FlipEndianess) id = os::Byteswap::byteswap(id); if (id != 0x53425553) // "SUBS" { os::Printer::log("LMTS ERROR: wrong subset magic id!", ELL_ERROR); cleanup(); return 0; } Subsets = new SLMTSSubsetInfoEntry[Header.SubsetCount]; const s32 subsetUserSize = Header.SubsetSize - sizeof(SLMTSSubsetInfoEntry); for (i=0; i<Header.SubsetCount; ++i) { file->read(&Subsets[i], sizeof(SLMTSSubsetInfoEntry)); if (FlipEndianess) { Subsets[i].Offset = os::Byteswap::byteswap(Subsets[i].Offset); Subsets[i].Count = os::Byteswap::byteswap(Subsets[i].Count); Subsets[i].TextID1 = os::Byteswap::byteswap(Subsets[i].TextID1); Subsets[i].TextID2 = os::Byteswap::byteswap(Subsets[i].TextID2); } if (subsetUserSize>0) file->seek(subsetUserSize,true); } // TRIANGLES file->read(&id, sizeof(u32)); if (FlipEndianess) id = os::Byteswap::byteswap(id); if (id != 0x53495254) // "TRIS" { os::Printer::log("LMTS ERROR: wrong triangle magic id!", ELL_ERROR); cleanup(); return 0; } Triangles = new SLMTSTriangleDataEntry[(Header.TriangleCount*3)]; const s32 triUserSize = Header.VertexSize - sizeof(SLMTSTriangleDataEntry); for (i=0; i<(Header.TriangleCount*3); ++i) { file->read(&Triangles[i], sizeof(SLMTSTriangleDataEntry)); if (FlipEndianess) { Triangles[i].X = os::Byteswap::byteswap(Triangles[i].X); Triangles[i].Y = os::Byteswap::byteswap(Triangles[i].Y); Triangles[i].Z = os::Byteswap::byteswap(Triangles[i].Z); Triangles[i].U1 = os::Byteswap::byteswap(Triangles[i].U1); Triangles[i].V1 = os::Byteswap::byteswap(Triangles[i].U2); Triangles[i].U2 = os::Byteswap::byteswap(Triangles[i].V1); Triangles[i].V2 = os::Byteswap::byteswap(Triangles[i].V2); } if (triUserSize>0) file->seek(triUserSize,true); } ///////////////////////////////////////////////////////////////// SMesh* mesh = new SMesh(); constructMesh(mesh); loadTextures(mesh); cleanup(); SAnimatedMesh* am = new SAnimatedMesh(); am->Type = EAMT_LMTS; // not unknown to irrlicht anymore am->addMesh(mesh); am->recalculateBoundingBox(); mesh->drop(); return am; }
irr::scene::IMesh* MeshTools::createHillMesh(SurfaceQuadTree& tree, rectf section, double min_height, double max_height) { std::vector<PsblVertPtr> triangles; /* tree.split(1); auto ptr = tree.child[1]; for (int i=0; i<2; ++i) { ptr->split(1); ptr = ptr->child[3*(i+1)%4]; } ptr = tree.child[2]; for (int i=0; i<5; ++i) { ptr->split(1); ptr = ptr->child[3*(i+1)%4]; } */ struct Visitor : public SurfaceQuadTree::Visitor { vector<pair<QuadTreePtr, QuadTreePtr>> pairs; typedef tuple<QuadTreePtr, QuadTreePtr> edge; typedef vector<edge> triangle; vector<triangle> triangles; typedef vector<triangle>::const_iterator triangle_iter; std::map<edge, vector<triangle>> edge_to_triangle; bool crosses(edge e1, edge e2) { // Ignore if the edges share a vertex; that is not a crossing. if ( get<0>(e1)->vert == get<0>(e2)->vert || get<0>(e1)->vert == get<1>(e2)->vert || get<1>(e1)->vert == get<0>(e2)->vert || get<1>(e1)->vert == get<1>(e2)->vert ) return false; // line2d(T xa, T ya, T xb, T yb) vector3df pos1a = get<0>(e1)->vert->getPos(); vector3df pos1b = get<1>(e1)->vert->getPos(); line2df line1(pos1a.X, pos1a.Y, pos1b.X, pos1b.Y); vector3df pos2a = get<0>(e2)->vert->getPos(); vector3df pos2b = get<1>(e2)->vert->getPos(); line2df line2(pos2a.X, pos2a.Y, pos2b.X, pos2b.Y); vector2df out; return line1.intersectWith(line2, out); } bool compareEdge(edge e, std::string from, std::string to) { return get<0>(e)->getPath() == from && get<1>(e)->getPath() == to; } bool findEdge(triangle t, std::string from, std::string to) { return compareEdge(t[0], from, to) || compareEdge(t[1], from, to) || compareEdge(t[2], from, to); } virtual void addTriangle(QuadTreePtr a, QuadTreePtr b, QuadTreePtr c) { // If a,b,c already in triangles, duplicate triangle error. // foreach edge in the new triangle, // find other triangles that share one edge with this one // test the other 2x2 edges for intersection. triangle new_triangle( { make_tuple(a,b), make_tuple(b,c), make_tuple(c,a) }); //if (findEdge(new_triangle, "Root SW SW SW NW SE", "Root SW SW SW NW NW")) // cout << "found" << endl; //if (findEdge(new_triangle, "Root SW SW SW NW NE", "Root SW SW SW NW SW")) // cout << "found" << endl; for (edge e_same : new_triangle) for (edge e_check : new_triangle) if (e_same != e_check) for (triangle contiguous : edge_to_triangle[e_same]) for (edge e_existing : contiguous) if (e_existing != e_same) if (crosses(e_check, e_existing)) { cout << "detected crossing, new is " << get<0>(e_check)->getPath() << "-" << get<1>(e_check)->getPath() << " old is " << get<0>(e_existing)->getPath() << "-" << get<1>(e_existing)->getPath() << endl; } triangles.push_back(new_triangle); edge_to_triangle[make_tuple(a,b)].push_back(new_triangle); edge_to_triangle[make_tuple(b,c)].push_back(new_triangle); edge_to_triangle[make_tuple(c,a)].push_back(new_triangle); edge_to_triangle[make_tuple(b,a)].push_back(new_triangle); edge_to_triangle[make_tuple(c,b)].push_back(new_triangle); edge_to_triangle[make_tuple(a,c)].push_back(new_triangle); } virtual void check(QuadTreePtr a1, QuadTreePtr a2, QuadTreePtr b1, QuadTreePtr b2) { } }; Visitor visitor; tree.triangulate(triangles, section, min_height, max_height, &visitor); //tree.sweep(triangles, section, &visitor); //cout << "num triangle points: " << triangles.size() << endl; vector<QuadTreePtr> leaves; tree.dumpLeaves(leaves); //cout << "num leaves: " << leaves.size() << endl; /* for (QuadTreePtr p1 : leaves) for (QuadTreePtr p2 : leaves) if (p1 != p2) if (p1->isAdjacent(p2)) if (!visitor.has(p1, p2)) { auto sz1 = p1->region.getSize(); auto sz2 = p2->region.getSize(); auto c1 = p1->region.getCenter(); auto c2 = p2->region.getCenter(); char const* path1 = p1->getPath().c_str(); char const* path2 = p2->getPath().c_str(); cout << path1 << endl; cout << path2 << endl; cout << "Missing pair of adjacent vertices." << endl; } float x1 = section.UpperLeftCorner.X; float x2 = section.LowerRightCorner.X; float y1 = section.UpperLeftCorner.Y; float y2 = section.LowerRightCorner.Y; */ /* d---c | / | a---b */ /* S3DVertex sa; sa.Pos = vector3df(x1, y1, 0); PsblVertPtr a = new PossibleVertex(sa); S3DVertex sb; sb.Pos = vector3df(x2, y1, 0); PsblVertPtr b = new PossibleVertex(sb); S3DVertex sc; sc.Pos = vector3df(x2, y2, 0); PsblVertPtr c = new PossibleVertex(sc); S3DVertex sd; sd.Pos = vector3df(x1, y2, 0); PsblVertPtr d = new PossibleVertex(sd); // a-b-c // a-c-d triangles.push_back(a); triangles.push_back(b); triangles.push_back(c); triangles.push_back(a); triangles.push_back(c); triangles.push_back(d); triangles.push_back(c); triangles.push_back(b); triangles.push_back(a); triangles.push_back(d); triangles.push_back(c); triangles.push_back(a); */ SMeshBuffer* buffer = new SMeshBuffer(); for (auto pv : triangles) pv->addToMeshBuf(buffer, vector3df()); if (buffer->getIndexCount() % 3 > 0) throw std::logic_error("SurfaceQuadTree triangulation added a 'triangle' with less than 3 vertices in it."); //cout << "num vertices " << buffer->getVertexCount() << endl; //cout << "num indices " << buffer->getIndexCount() << endl; buffer->recalculateBoundingBox(); buffer->setHardwareMappingHint(EHM_STATIC); SMesh* mesh = new SMesh(); mesh->addMeshBuffer(buffer); mesh->recalculateBoundingBox(); buffer->drop(); return mesh; }
PlayerFrame::PlayerFrame(FrameInfo info, bool load_volumic) { // increase total number of loaded frames PlayerFrame::totalLoadedFrames++; device->getLogger()->log((stringw("Loading frame number ")+stringw(info.id)+L". Total of frames loaded : "+stringw(PlayerFrame::totalLoadedFrames)).c_str()); core::array<stringc> _lastEleFileNames; core::array<stringc> _lastFaceFileNames; core::array<scene::SMeshBuffer*> _lastBuffers; // initialize some variables this->id = info.id; for (u32 o=0; o < info.nodefiles.size(); o++) { SMeshBuffer* buffer = new SMeshBuffer(); IMeshSceneNode* node = NULL; stringc nodeFileName = info.nodefiles[o]; stringc faceFileName = info.facefiles[o]; stringc eleFileName = info.elefiles[o]; // LOADING ifstream innode; ifstream inele; ifstream inface; int nb_of_points, nb_of_tetrahedra, nb_of_faces; int p1, p2, p3, p4; yohan::base::DATA x, y, z; // used to know if an error occured in the while loop bool error = false; // ----------------------------------------------------------------------- // - POINTS -------------------------------------------------------------- // ----------------------------------------------------------------------- innode.open(nodeFileName.c_str(), ios::in | ios::binary); // opens the nodes file if (!innode || !innode.good()) { device->getLogger()->log(( stringc("ERROR: This node file could not be opened : ") + nodeFileName ).c_str()); buffer->drop(); continue; } // first line data : innode >> nb_of_points >> dim >> nb_of_attr >> boundary_marker; innode.read(reinterpret_cast < char * > (&nb_of_points), sizeof(int)); // we should have at least one tetrahedra (4 points) and each point should have 3 coordinates if (nb_of_points > 65535 || nb_of_points < 4) { device->getLogger()->log("ERROR: a node file should not contain more than 65535 points and less than 4 points."); buffer->drop(); continue; } device->getLogger()->log((stringw("Loading ")+stringw(nb_of_points)+L" points from "+stringw(nodeFileName.c_str())+L"...").c_str()); // default color video::SColor clr(255,100,100,200); // lets add the vertices to the buffer buffer->Vertices.reallocate( nb_of_points ); // this is one line : innode >> index >> x >> y >> z; innode.read(reinterpret_cast < char * > (&x), sizeof(yohan::base::DATA)); innode.read(reinterpret_cast < char * > (&y), sizeof(yohan::base::DATA)); innode.read(reinterpret_cast < char * > (&z), sizeof(yohan::base::DATA)); while (!innode.eof() && innode.good())// && (int)buffer->Vertices.size() < nb_of_points) { buffer->Vertices.push_back(video::S3DVertex((f32)x, (f32)y, (f32)z, 1,0,0, clr, 0,0)); // this is one line : innode >> index >> x >> y >> z; innode.read(reinterpret_cast < char * > (&x), sizeof(yohan::base::DATA)); innode.read(reinterpret_cast < char * > (&y), sizeof(yohan::base::DATA)); innode.read(reinterpret_cast < char * > (&z), sizeof(yohan::base::DATA)); } innode.close(); innode.clear(); // ----------------------------------------------------------------------- // lets check if verticies have been added well if (buffer->Vertices.size() != nb_of_points) { device->getLogger()->log("ERROR: the node file does not seem to be valid."); buffer->drop(); continue; } if (load_volumic) { // ----------------------------------------------------------------------- // - TETRAHEDRAS --------------------------------------------------------- // ----------------------------------------------------------------------- // at first we check if the ele file has not been already opened s32 eleLoadedIndex = -1; for (u32 e=0; e < PlayerFrame::lastEleFileNames.size(); e++) { if (PlayerFrame::lastEleFileNames[e] == eleFileName) { eleLoadedIndex = e; device->getLogger()->log("The ele file is already in memory. We do not need to reload it."); break; } } // if is not in memory, load it from file if (!PlayerFrame::last_was_volumic || eleLoadedIndex == -1 || PlayerFrame::lastBuffers[eleLoadedIndex]->Vertices.size() != buffer->Vertices.size()) { if (eleLoadedIndex != -1) device->getLogger()->log((stringc(PlayerFrame::lastBuffers[eleLoadedIndex]->Vertices.size())+ " This is weird: the ele file which is already in memory don't have the same number of vertices than this one.").c_str()); inele.open(eleFileName.c_str(), ios::in | ios::binary); // opens the ele file if (!inele || !inele.good()) { device->getLogger()->log(( stringc("ERROR: This ele file could not be opened : ") + eleFileName ).c_str()); buffer->drop(); continue; } // first line data : inele >> nb_of_tetrahedra >> dim >> nb_of_attr; inele.read(reinterpret_cast < char * > (&nb_of_tetrahedra), sizeof(int)); device->getLogger()->log((stringw("Loading ")+stringw(nb_of_tetrahedra)+L" tetrahedras from "+stringw(eleFileName.c_str())+L"...").c_str()); // we should have at least one tetrahedra and each tetrahedra should have 4 points if (nb_of_tetrahedra < 1) { buffer->drop(); continue; } // lets add the indices to the buffer buffer->Indices.set_used( (u32)(3 * 4 * nb_of_tetrahedra) ); u32 i = 0; // this is one line : inele >> index >> p1 >> p2 >> p3 >> p4; inele.read(reinterpret_cast < char * > (&p1), sizeof(int)); inele.read(reinterpret_cast < char * > (&p2), sizeof(int)); inele.read(reinterpret_cast < char * > (&p3), sizeof(int)); inele.read(reinterpret_cast < char * > (&p4), sizeof(int)); while (!inele.eof() && inele.good())// && i < 3 * 4 * nb_of_tetrahedra - 12) { // check if we are not out of bounds if (i > (u32)(3 * 4 * nb_of_tetrahedra - 12) || p1 > nb_of_points || p2 > nb_of_points || p3 > nb_of_points || p4 > nb_of_points) { device->getLogger()->log("ERROR: the ele file does not seem to be valid. "); buffer->drop(); error = true; break; } // add 4 polygons per tetrahedra. Not optimized ! s32 ajust_index = 0; buffer->Indices[(u32)(i+0)] = (u32)(p1 + ajust_index); buffer->Indices[(u32)(i+1)] = (u32)(p3 + ajust_index); buffer->Indices[(u32)(i+2)] = (u32)(p2 + ajust_index); core::triangle3df t4( buffer->Vertices[(u32)(p1 + ajust_index)].Pos, buffer->Vertices[(u32)(p3 + ajust_index)].Pos, buffer->Vertices[(u32)(p2 + ajust_index)].Pos); i += 3; buffer->Indices[(u32)(i+0)] = (u32)(p1 + ajust_index); buffer->Indices[(u32)(i+1)] = (u32)(p2 + ajust_index); buffer->Indices[(u32)(i+2)] = (u32)(p4 + ajust_index); core::triangle3df t3( buffer->Vertices[(u32)(p1 + ajust_index)].Pos, buffer->Vertices[(u32)(p2 + ajust_index)].Pos, buffer->Vertices[(u32)(p4 + ajust_index)].Pos); i += 3; buffer->Indices[(u32)(i+0)] = (u32)(p3 + ajust_index); buffer->Indices[(u32)(i+1)] = (u32)(p4 + ajust_index); buffer->Indices[(u32)(i+2)] = (u32)(p2 + ajust_index); core::triangle3df t1( buffer->Vertices[(u32)(p3 + ajust_index)].Pos, buffer->Vertices[(u32)(p4 + ajust_index)].Pos, buffer->Vertices[(u32)(p2 + ajust_index)].Pos); i += 3; buffer->Indices[(u32)(i+0)] = (u32)(p1 + ajust_index); buffer->Indices[(u32)(i+1)] = (u32)(p4 + ajust_index); buffer->Indices[(u32)(i+2)] = (u32)(p3 + ajust_index); core::triangle3df t2( buffer->Vertices[(u32)(p1 + ajust_index)].Pos, buffer->Vertices[(u32)(p4 + ajust_index)].Pos, buffer->Vertices[(u32)(p3 + ajust_index)].Pos); i += 3; buffer->Vertices[(u32)(p1 + ajust_index)].Normal = t1.getNormal(); buffer->Vertices[(u32)(p2 + ajust_index)].Normal = t2.getNormal(); buffer->Vertices[(u32)(p3 + ajust_index)].Normal = t3.getNormal(); buffer->Vertices[(u32)(p4 + ajust_index)].Normal = t4.getNormal(); // this is one line : inele >> index >> p1 >> p2 >> p3 >> p4; inele.read(reinterpret_cast < char * > (&p1), sizeof(int)); inele.read(reinterpret_cast < char * > (&p2), sizeof(int)); inele.read(reinterpret_cast < char * > (&p3), sizeof(int)); inele.read(reinterpret_cast < char * > (&p4), sizeof(int)); } inele.close(); inele.clear(); } else // we do not need to reload from the file ! { buffer->Indices.reallocate(PlayerFrame::lastBuffers[eleLoadedIndex]->Indices.size()); buffer->Indices = PlayerFrame::lastBuffers[eleLoadedIndex]->Indices; for (u32 j=0; j < buffer->Vertices.size(); j++) buffer->Vertices[j].Normal = PlayerFrame::lastBuffers[eleLoadedIndex]->Vertices[j].Normal; } // ----------------------------------------------------------------------- } else // load_volumic == false { // ----------------------------------------------------------------------- // - FACES --------------------------------------------------------------- // ----------------------------------------------------------------------- // at first we check if the ele file has not been already opened s32 faceLoadedIndex = -1; for (u32 e=0; e < PlayerFrame::lastFaceFileNames.size(); e++) { if (PlayerFrame::lastFaceFileNames[e] == faceFileName) { faceLoadedIndex = e; device->getLogger()->log("The face file is already in memory. We do not need to reload it."); break; } } // if is not in memory, load it from file if (PlayerFrame::last_was_volumic || faceLoadedIndex == -1 || PlayerFrame::lastBuffers[faceLoadedIndex]->Vertices.size() != buffer->Vertices.size()) { inface.open(faceFileName.c_str(), ios::in | ios::binary); // opens the face file if (!inface || !inface.good()) { device->getLogger()->log(( stringc("ERROR: This face file could not be opened : ") + faceFileName ).c_str()); buffer->drop(); continue; } // first line data : inface >> nb_of_tetrahedra >> dim >> nb_of_attr; inface.read(reinterpret_cast < char * > (&nb_of_faces), sizeof(int)); device->getLogger()->log((stringw("Loading ")+stringw(nb_of_faces)+L" faces from "+stringw(faceFileName.c_str())+L"...").c_str()); // we should have at least one face if (nb_of_faces < 1) { buffer->drop(); continue; } // lets add the indices to the buffer buffer->Indices.set_used( (u32)(3 * nb_of_faces) ); u32 i = 0; // this is one line : inface >> index >> p1 >> p2 >> p3; inface.read(reinterpret_cast < char * > (&p1), sizeof(int)); inface.read(reinterpret_cast < char * > (&p2), sizeof(int)); inface.read(reinterpret_cast < char * > (&p3), sizeof(int)); while (!inface.eof() && inface.good()) { // check if we are not out of bounds if (i > (u32)(3 * nb_of_faces - 3) || p1 > nb_of_points || p2 > nb_of_points || p3 > nb_of_points) { device->getLogger()->log("ERROR: the face file does not seem to be valid. "); buffer->drop(); error = true; break; } // add 1 polygon per face. s32 ajust_index = -1; buffer->Indices[(u32)(i+0)] = (u32)(p1 + ajust_index); buffer->Indices[(u32)(i+1)] = (u32)(p3 + ajust_index); buffer->Indices[(u32)(i+2)] = (u32)(p2 + ajust_index); core::triangle3df t( buffer->Vertices[(u32)(p1 + ajust_index)].Pos, buffer->Vertices[(u32)(p3 + ajust_index)].Pos, buffer->Vertices[(u32)(p2 + ajust_index)].Pos); i += 3; buffer->Vertices[(u32)(p1 + ajust_index)].Normal = t.getNormal(); buffer->Vertices[(u32)(p2 + ajust_index)].Normal = t.getNormal(); buffer->Vertices[(u32)(p3 + ajust_index)].Normal = t.getNormal(); // this is one line : inface >> index >> p1 >> p2 >> p3; inface.read(reinterpret_cast < char * > (&p1), sizeof(int)); inface.read(reinterpret_cast < char * > (&p2), sizeof(int)); inface.read(reinterpret_cast < char * > (&p3), sizeof(int)); } inface.close(); inface.clear(); } else // we do not need to reload from the file ! { buffer->Indices.reallocate(PlayerFrame::lastBuffers[faceLoadedIndex]->Indices.size()); buffer->Indices = PlayerFrame::lastBuffers[faceLoadedIndex]->Indices; for (u32 j=0; j < buffer->Vertices.size(); j++) buffer->Vertices[j].Normal = PlayerFrame::lastBuffers[faceLoadedIndex]->Vertices[j].Normal; } // ----------------------------------------------------------------------- } if (error) continue; // lets recalculate the bounding box and create the mesh for (u32 j=0; j < buffer->Vertices.size(); ++j) buffer->BoundingBox.addInternalPoint(buffer->Vertices[j].Pos); SMesh* mesh = new SMesh; mesh->addMeshBuffer(buffer); mesh->recalculateBoundingBox(); // here we go, lets create the node that will owns the mesh data node = smgr->addMeshSceneNode( mesh ); mesh->drop(); if (!node) { node = NULL; continue; } node->setVisible(false); //node->setMaterialFlag(video::EMF_WIREFRAME, true); this->nodes.push_back( node ); // updating last _lastEleFileNames.push_back( eleFileName ); _lastFaceFileNames.push_back( faceFileName ); _lastBuffers.push_back( buffer ); } // clean history and update it PlayerFrame::lastEleFileNames = _lastEleFileNames; PlayerFrame::lastFaceFileNames = _lastFaceFileNames; for (u32 e=0; e < lastBuffers.size(); e++) { if (_lastBuffers.binary_search(PlayerFrame::lastBuffers[e]) == -1) { PlayerFrame::lastBuffers[e]->drop(); } } PlayerFrame::lastBuffers = _lastBuffers; PlayerFrame::last_was_volumic = load_volumic; // that's it ! device->getLogger()->log("Loaded."); }
// Create a new tile. From 0.3+ Tiles will be parametric and will not use the reference mesh. void TerrainTile::createTerrain(ISceneNode* parent, vector3df pos, stringc name, bool param) { stringc tilename = TerrainManager::getInstance()->getTileMeshName(); if (tilename=="") tilename="../media/land.obj"; IMesh* baseMesh = NULL; if (!param) { baseMesh = smgr->getMesh(tilename.c_str()); } else { //Tries to create a mesh that is 1024 in size (mesh size), scaled then by IRB. //"tilesegment" is used to determine the density of the mesh, smaller=less dense f32 size = TerrainManager::getInstance()->getScale(); f32 calc = App::getInstance()->terraindensity/size; u32 tilesegment = (u32)(calc*size); if (tilesegment < 10) tilesegment = 10; //u32 tilesegment = (u32)(0.024414f*size); Old fixed density baseMesh = smgr->addHillPlaneMesh( "myHill", core::dimension2d<f32>(f32(1024/tilesegment),f32(1024/tilesegment)), core::dimension2d<u32>(tilesegment,tilesegment), 0, 0, core::dimension2d<f32>(0,0), core::dimension2d<f32>(1,1)); } SMesh* newMesh = NULL; newMesh = smgr->getMeshManipulator()->createMeshCopy(baseMesh); newMesh->setHardwareMappingHint(EHM_STATIC); if (node) node->drop(); // Create the terrain mesh node node = smgr->addMeshSceneNode(newMesh,parent,100); node->setMaterialFlag(EMF_LIGHTING,false); //node->setMaterialFlag(EMF_WIREFRAME,true); // node->setMaterialFlag(EMF_BLEND_OPERATION,true); // Create the terrain mesh node //Add shadow to this in the XEffect if (EffectsManager::getInstance()->isXEffectsEnabled()) { node->setMaterialFlag(EMF_LIGHTING,false); EffectsManager::getInstance()->addShadowToNode(node); } nodescale = node->getBoundingBox().getExtent().X; TerrainManager::getInstance()->setTileMeshSize(nodescale); node->setName(name); node->setScale(vector3df(scale/nodescale,scale/nodescale,scale/nodescale)); node->setPosition(pos*scale); selector = smgr->createTriangleSelector(newMesh,node); node->setTriangleSelector(selector); assignTerrainShader(node); // Create the water mesh, using the same reference as the terrain, applied shader will use the vertices informations to set the transparency of the water. ocean=smgr->addMeshSceneNode(newMesh,node,0); // use "newMesh" as the same reference. Will use the vertices height to get the transparency for the water. // assignWaterShader(ocean); ocean->getMaterial(0).MaterialType = video::EMT_SOLID; ocean->getMaterial(0).BlendOperation = EBO_ADD; ocean->getMaterial(0).BlendFactor = pack_textureBlendFunc(EBF_SRC_ALPHA, EBF_ONE_MINUS_SRC_ALPHA); meshBuffer = ((IMeshSceneNode*)node)->getMesh()->getMeshBuffer(0); mb_vertices = (S3DVertex*) meshBuffer->getVertices(); mb_indices = meshBuffer->getIndices(); /* // Reset the vertices height of the mesh to 0.0f (Y axis) for (unsigned int j = 0; j < meshBuffer->getVertexCount(); j += 1) { mb_vertices[j].Pos.Y = 0.0f; } */ driver = smgr->getGUIEnvironment()->getVideoDriver(); recalculate(); custom = false; storeUndo(); }
//! creates/loads an animated mesh from the file. //! \return Pointer to the created mesh. Returns 0 if loading failed. //! If you no longer need the mesh, you should call IAnimatedMesh::drop(). //! See IReferenceCounted::drop() for more information. IAnimatedMesh* COCTLoader::createMesh(io::IReadFile* file) { if (!file) return 0; octHeader header; file->read(&header, sizeof(octHeader)); octVert * verts = new octVert[header.numVerts]; octFace * faces = new octFace[header.numFaces]; octTexture * textures = new octTexture[header.numTextures]; octLightmap * lightmaps = new octLightmap[header.numLightmaps]; octLight * lights = new octLight[header.numLights]; file->read(verts, sizeof(octVert) * header.numVerts); file->read(faces, sizeof(octFace) * header.numFaces); //TODO: Make sure id is in the legal range for Textures and Lightmaps u32 i; for (i = 0; i < header.numTextures; i++) { octTexture t; file->read(&t, sizeof(octTexture)); textures[t.id] = t; } for (i = 0; i < header.numLightmaps; i++) { octLightmap t; file->read(&t, sizeof(octLightmap)); lightmaps[t.id] = t; } file->read(lights, sizeof(octLight) * header.numLights); //TODO: Now read in my extended OCT header (flexible lightmaps and vertex normals) // This is the method Nikolaus Gebhardt used in the Q3 loader -- create a // meshbuffer for every possible combination of lightmap and texture including // a "null" texture and "null" lightmap. Ones that end up with nothing in them // will be removed later. SMesh * Mesh = new SMesh(); for (i=0; i<(header.numTextures+1) * (header.numLightmaps+1); ++i) { scene::SMeshBufferLightMap* buffer = new scene::SMeshBufferLightMap(); buffer->Material.MaterialType = video::EMT_LIGHTMAP; buffer->Material.Lighting = false; Mesh->addMeshBuffer(buffer); buffer->drop(); } // Build the mesh buffers for (i = 0; i < header.numFaces; i++) { if (faces[i].numVerts < 3) continue; const f32* const a = verts[faces[i].firstVert].pos; const f32* const b = verts[faces[i].firstVert+1].pos; const f32* const c = verts[faces[i].firstVert+2].pos; const core::vector3df normal = core::plane3df(core::vector3df(a[0],a[1],a[2]), core::vector3df(b[0],c[1],c[2]), core::vector3df(c[0],c[1],c[2])).Normal; const u32 textureID = core::min_(s32(faces[i].textureID), s32(header.numTextures - 1)) + 1; const u32 lightmapID = core::min_(s32(faces[i].lightmapID),s32(header.numLightmaps - 1)) + 1; SMeshBufferLightMap * meshBuffer = (SMeshBufferLightMap*)Mesh->getMeshBuffer(lightmapID * (header.numTextures + 1) + textureID); const u32 base = meshBuffer->Vertices.size(); // Add this face's verts u32 v; for (v = 0; v < faces[i].numVerts; ++v) { octVert * vv = &verts[faces[i].firstVert + v]; video::S3DVertex2TCoords vert; vert.Pos.set(vv->pos[0], vv->pos[1], vv->pos[2]); vert.Color = video::SColor(0,255,255,255); vert.Normal.set(normal); if (textureID == 0) { // No texture -- just a lightmap. Thus, use lightmap coords for texture 1. // (the actual texture will be swapped later) vert.TCoords.set(vv->lc[0], vv->lc[1]); } else { vert.TCoords.set(vv->tc[0], vv->tc[1]); vert.TCoords2.set(vv->lc[0], vv->lc[1]); } meshBuffer->Vertices.push_back(vert); } // Now add the indices // This weird loop turns convex polygons into triangle strips. // I do it this way instead of a simple fan because it usually looks a lot better in wireframe, for example. // High, Low u32 h = faces[i].numVerts - 1; u32 l = 0; for (v = 0; v < faces[i].numVerts - 2; ++v) { const u32 center = (v & 1)? h - 1: l + 1; meshBuffer->Indices.push_back(base + h); meshBuffer->Indices.push_back(base + l); meshBuffer->Indices.push_back(base + center); if (v & 1) --h; else ++l; } } // load textures core::array<video::ITexture*> tex; tex.reallocate(header.numTextures + 1); tex.push_back(0); const core::stringc relpath = FileSystem->getFileDir(file->getFileName())+"/"; for (i = 1; i < (header.numTextures + 1); i++) { core::stringc path(textures[i-1].fileName); path.replace('\\','/'); if (FileSystem->existFile(path)) tex.push_back(SceneManager->getVideoDriver()->getTexture(path)); else // try to read in the relative path of the OCT file tex.push_back(SceneManager->getVideoDriver()->getTexture( (relpath + path) )); } // prepare lightmaps core::array<video::ITexture*> lig; lig.set_used(header.numLightmaps + 1); lig[0] = 0; const u32 lightmapWidth = 128; const u32 lightmapHeight = 128; const core::dimension2d<u32> lmapsize(lightmapWidth, lightmapHeight); bool oldMipMapState = SceneManager->getVideoDriver()->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS); SceneManager->getVideoDriver()->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false); video::CImage tmpImage(video::ECF_R8G8B8, lmapsize); for (i = 1; i < (header.numLightmaps + 1); ++i) { core::stringc lightmapname = file->getFileName(); lightmapname += ".lightmap."; lightmapname += (int)i; const octLightmap* lm = &lightmaps[i-1]; for (u32 x=0; x<lightmapWidth; ++x) { for (u32 y=0; y<lightmapHeight; ++y) { tmpImage.setPixel(x, y, video::SColor(255, lm->data[x][y][2], lm->data[x][y][1], lm->data[x][y][0])); } } lig[i] = SceneManager->getVideoDriver()->addTexture(lightmapname.c_str(), &tmpImage); } SceneManager->getVideoDriver()->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, oldMipMapState); // Free stuff delete [] verts; delete [] faces; delete [] textures; delete [] lightmaps; delete [] lights; // attach materials for (i = 0; i < header.numLightmaps + 1; i++) { for (u32 j = 0; j < header.numTextures + 1; j++) { u32 mb = i * (header.numTextures + 1) + j; SMeshBufferLightMap * meshBuffer = (SMeshBufferLightMap*)Mesh->getMeshBuffer(mb); meshBuffer->Material.setTexture(0, tex[j]); meshBuffer->Material.setTexture(1, lig[i]); if (meshBuffer->Material.getTexture(0) == 0) { // This material has no texture, so we'll just show the lightmap if there is one. // We swapped the texture coordinates earlier. meshBuffer->Material.setTexture(0, meshBuffer->Material.getTexture(1)); meshBuffer->Material.setTexture(1, 0); } if (meshBuffer->Material.getTexture(1) == 0) { // If there is only one texture, it should be solid and lit. // Among other things, this way you can preview OCT lights. meshBuffer->Material.MaterialType = video::EMT_SOLID; meshBuffer->Material.Lighting = true; } } } // delete all buffers without geometry in it. i = 0; while(i < Mesh->MeshBuffers.size()) { if (Mesh->MeshBuffers[i]->getVertexCount() == 0 || Mesh->MeshBuffers[i]->getIndexCount() == 0 || Mesh->MeshBuffers[i]->getMaterial().getTexture(0) == 0) { // Meshbuffer is empty -- drop it Mesh->MeshBuffers[i]->drop(); Mesh->MeshBuffers.erase(i); } else { ++i; } } // create bounding box for (i = 0; i < Mesh->MeshBuffers.size(); ++i) { Mesh->MeshBuffers[i]->recalculateBoundingBox(); } Mesh->recalculateBoundingBox(); // Set up an animated mesh to hold the mesh SAnimatedMesh* AMesh = new SAnimatedMesh(); AMesh->Type = EAMT_OCT; AMesh->addMesh(Mesh); AMesh->recalculateBoundingBox(); Mesh->drop(); return AMesh; }
IAnimatedMesh* CGeometryCreator::createTerrainMesh(video::IImage* texture, video::IImage* heightmap, const core::dimension2d<f32>& stretchSize, f32 maxHeight, video::IVideoDriver* driver, const core::dimension2d<s32> maxVtxBlockSize, bool debugBorders) { u32 tm = os::Timer::getRealTime()/1000; if (!texture || !heightmap) return 0; video::SMaterial material; c8 textureName[64]; c8 tmp[255]; // debug border s32 borderSkip = debugBorders ? 0 : 1; video::S3DVertex vtx; vtx.Color.set(255,255,255,255); SMesh* mesh = new SMesh(); core::dimension2d<s32> hMapSize= heightmap->getDimension(); core::dimension2d<s32> tMapSize= texture->getDimension(); core::position2d<f32> thRel((f32)tMapSize.Width / (s32)hMapSize.Width, (f32)tMapSize.Height / (s32)hMapSize.Height); core::position2d<s32> processed(0,0); while (processed.Y<hMapSize.Height) { while(processed.X<hMapSize.Width) { core::dimension2d<s32> blockSize = maxVtxBlockSize; if (processed.X + blockSize.Width > hMapSize.Width) blockSize.Width = hMapSize.Width - processed.X; if (processed.Y + blockSize.Height > hMapSize.Height) blockSize.Height = hMapSize.Height - processed.Y; SMeshBuffer* buffer = new SMeshBuffer(); s32 x,y; // add vertices of vertex block for (y=0; y<blockSize.Height; ++y) for (x=0; x<blockSize.Width; ++x) { video::SColor clr = heightmap->getPixel(x+processed.X, y+processed.Y); f32 height = ((clr.getRed() + clr.getGreen() + clr.getBlue()) / 3.0f)/255.0f * maxHeight; vtx.Pos.set((f32)(x+processed.X) * stretchSize.Width, height, (f32)(y+processed.Y) * stretchSize.Height); vtx.TCoords.set((f32)(x+0.5f) / ((f32)blockSize.Width), (f32)(y+0.5f) / ((f32)blockSize.Height)); buffer->Vertices.push_back(vtx); } // add indices of vertex block for (y=0; y<blockSize.Height-1; ++y) for (x=0; x<blockSize.Width-1; ++x) { s32 c = (y*blockSize.Width) + x; buffer->Indices.push_back(c); buffer->Indices.push_back(c + blockSize.Width); buffer->Indices.push_back(c + 1); buffer->Indices.push_back(c + 1); buffer->Indices.push_back(c + blockSize.Width); buffer->Indices.push_back(c + 1 + blockSize.Width); } // recalculate normals for (s32 i=0; i<(s32)buffer->Indices.size(); i+=3) { core::plane3d<f32> p( buffer->Vertices[buffer->Indices[i+0]].Pos, buffer->Vertices[buffer->Indices[i+1]].Pos, buffer->Vertices[buffer->Indices[i+2]].Pos); p.Normal.normalize(); buffer->Vertices[buffer->Indices[i+0]].Normal = p.Normal; buffer->Vertices[buffer->Indices[i+1]].Normal = p.Normal; buffer->Vertices[buffer->Indices[i+2]].Normal = p.Normal; } if (buffer->Vertices.size()) { // create texture for this block video::IImage* img = new video::CImage(texture, core::position2d<s32>((s32)(processed.X*thRel.X), (s32)(processed.Y*thRel.Y)), core::dimension2d<s32>((s32)(blockSize.Width*thRel.X), (s32)(blockSize.Height*thRel.Y))); sprintf(textureName, "terrain%d_%d", tm, mesh->getMeshBufferCount()); material.Texture1 = driver->addTexture(textureName, img); if (material.Texture1) { sprintf(tmp, "Generated terrain texture (%dx%d): %s", material.Texture1->getSize().Width, material.Texture1->getSize().Height, textureName); os::Printer::log(tmp); } else os::Printer::log("Could not create terrain texture.", textureName, ELL_ERROR); buffer->Material = material; img->drop(); } buffer->recalculateBoundingBox(); mesh->addMeshBuffer(buffer); buffer->drop(); // keep on processing processed.X += maxVtxBlockSize.Width - borderSkip; } // keep on processing processed.X = 0; processed.Y += maxVtxBlockSize.Height - borderSkip; } SAnimatedMesh* animatedMesh = new SAnimatedMesh(); mesh->recalculateBoundingBox(); animatedMesh->addMesh(mesh); animatedMesh->recalculateBoundingBox(); mesh->drop(); return animatedMesh; }