Ejemplo n.º 1
0
	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();
	}
Ejemplo n.º 2
0
	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);
		}

		Mesh->recalculateBoundingBox();
	}
Ejemplo n.º 3
0
//! Creates/loads an animated mesh from the file.
IAnimatedMesh* CSMFMeshFileLoader::createMesh(io::IReadFile* 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;
}
Ejemplo n.º 4
0
IMesh* MeshTools::createMeshFromSoftBody(btSoftBody* softBody)
{
	SMeshBuffer* buffer = new SMeshBuffer();
	video::S3DVertex vtx;
	vtx.Color.set(255,255,255,255);

    /* Each soft body contain an array of vertices (nodes/particles_mass)   */
    btSoftBody::tNodeArray&   nodes(softBody->m_nodes);

    // Convert bullet nodes to vertices
    for(int i=0;i<nodes.size();++i)
    {
        const btSoftBody::Node&	n=nodes[i];
        const btVector3 normal=n.m_n;
        const btVector3 pos=n.m_x;

        vtx.Pos.set(pos.getX(), pos.getY(), pos.getZ());
		vtx.Normal.set(normal.getX(), normal.getY(), normal.getZ());

        // TODO: calculate texture coords
        //vtx.TCoords.set(tsx, tsy);

        buffer->Vertices.push_back(vtx);
    }

    // Convert triangles of the softbody to an index list
    for(int i=0;i<softBody->m_faces.size();++i)
    {
        auto	faces = softBody->m_faces;

        btSoftBody::Node*   node_0=faces[i].m_n[0];
        btSoftBody::Node*   node_1=faces[i].m_n[1];
        btSoftBody::Node*   node_2=faces[i].m_n[2];

        const int indices[] =
        {
            int(node_0-&nodes[0]),
            int(node_1-&nodes[0]),
            int(node_2-&nodes[0])
        };

        for(int j=0;j<3;++j)
            buffer->Indices.push_back(indices[j]);
    }

	buffer->recalculateBoundingBox();

	// Default the mesh to stream because most likely we will be updating
	// the vertex positions every frame to deal with softbody movement.
	buffer->setHardwareMappingHint(EHM_STREAM);

	SMesh* mesh = new SMesh();
	mesh->addMeshBuffer(buffer);
	mesh->recalculateBoundingBox();
	buffer->drop();
	return mesh;
}
Ejemplo n.º 5
0
//! 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;
}
Ejemplo n.º 6
0
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;
}
// 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;
}
Ejemplo n.º 8
0
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;
}
Ejemplo n.º 9
0
//! 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;
}
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;
}
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;
}
Ejemplo n.º 12
0
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;
}
Ejemplo n.º 13
0
    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));
            }
        }
    }
Ejemplo n.º 14
0
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};
}
Ejemplo n.º 15
0
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;
}
Ejemplo n.º 16
0
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.");
}
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;
}
Ejemplo n.º 18
0
/**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;
}
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;
}
Ejemplo n.º 20
0
//! 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;
}
Ejemplo n.º 21
0
//! 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 core::vector3df normal =
			GetFaceNormal(verts[faces[i].firstVert].pos,
					verts[faces[i].firstVert+1].pos,
					verts[faces[i].firstVert+2].pos);

		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.
		u32 h = faces[i].numVerts - 1, l = 0, c; // High, Low, Center
		for (v = 0; v < faces[i].numVerts - 2; ++v)
		{
			if (v & 1)
				c = h - 1;
			else
				c = l + 1;

			meshBuffer->Indices.push_back(base + h);
			meshBuffer->Indices.push_back(base + l);
			meshBuffer->Indices.push_back(base + c);

			if (v & 1)
				--h;
			else
				++l;
		}
	} 


	// load textures
	core::array<video::ITexture*> tex;
	tex.set_used(header.numTextures + 1);
	tex[0] = 0;
	
	for (i = 1; i < (header.numTextures + 1); i++)
	{
		tex[i] = Driver->getTexture(textures[i-1].fileName);
	}


	// prepare lightmaps
	core::array<video::ITexture*> lig;
	lig.set_used(header.numLightmaps + 1);

	u32 lightmapWidth = 128, lightmapHeight = 128;
	lig[0] = 0;
	core::dimension2d<s32> lmapsize(lightmapWidth, lightmapHeight);

	bool oldMipMapState = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
	Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);

	for (i = 1; i < (header.numLightmaps + 1); i++)
	{
		core::stringc lightmapname = file->getFileName();
		lightmapname += ".lightmap.";
		lightmapname += (int)i;
		lig[i] = Driver->addTexture(lmapsize, lightmapname.c_str());

		if (lig[i]->getSize() != lmapsize)
			os::Printer::log("OCTLoader: Created lightmap is not of the requested size", ELL_ERROR);

		if (lig[i])
		{
			void* pp = lig[i]->lock();

			if (pp)
			{
				video::ECOLOR_FORMAT format = lig[i]->getColorFormat();
				if (format == video::ECF_A1R5G5B5)
				{
					s16* p = (s16*)pp;

					octLightmap * lm;					
					lm = &lightmaps[i-1];

					for (u32 x=0; x<lightmapWidth; ++x)
						for (u32 y=0; y<lightmapHeight; ++y)
						{
							p[x*128 + y] = video::RGB16(
								lm->data[x][y][2],
								lm->data[x][y][1],
								lm->data[x][y][0]);
						}
				}
				else
				if (format == video::ECF_A8R8G8B8)
				{
					s32* p = (s32*)pp;

					octLightmap* lm;
					lm = &lightmaps[i-1];

					for (u32 x=0; x<lightmapWidth; ++x)
						for (u32 y=0; y<lightmapHeight; ++y)
						{
							p[x*128 + y] = video::SColor(255,
								lm->data[x][y][2],
								lm->data[x][y][1],
								lm->data[x][y][0]).color;
						}
				}
				else
					os::Printer::log(
						"OCTLoader: Could not create lightmap, unsupported texture format.", ELL_ERROR);
			}

			lig[i]->unlock();
		}
		else
			os::Printer::log("OCTLoader: Could not create lightmap, driver created no texture.", ELL_ERROR);
	}
	Driver->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;
}