When I first decided to use Irrlicht for my project I used the information in the "Custom SceneNode" tutorial to design some basic meshes to be used by the engine I am developing. Now it seems I was barking up the wrong tree because this tutorial doesn't seem to be consistent with creating meshes using IMeshSceneNode. Furthermore, I am rather confused by some of the features of IMeshSceneNodes and my inability to alter the material for a mesh. So I am going to explain a bit about myself and my project, then present some examples of what I have so far for feedback on my misconceptions and what I am doing wrong so that I can fix it.
First: I am new to working with graphics engines so please forgive my ignorance in what are probably commonplace approaches to doing things. In all my previous work (which isn't small) I have dealt with DirectX directly using primitives and rendering things by hand. So some of these features seem counter intuitive to what I am used to.
Second: My project is completely plugin based. It is a platform designed so that plugins can be built by authoring software. Everything that is drawn by the engine is defined using XML (and files pointed to in the XML). All components. This includes meshes, textures, materials, lights, cameras, billboards, panels, ui components, custom colors, as well as the objects and scenes that use all these pieces.
Now that you know a bit about the project, lets get into my confusion. I have 3 types of meshes defined in xml:
- Static Meshes - meshes and/or animated meshes that are defined in a file created in a 3D authoring tool (such as Blender, 3D Max, Maya, etc...). They are "static" because they do not change according to XML parameters.
Coded Meshes - meshes that are completely customized using code; they are unique to a specific plugin and intentionally hard-coded (for whatever reason) by the plugin developer.
Dynamic Meshes - meshes that are familiar and generic in nature (such as a sphere, box, pyramid, etc...) and can be defined using a consistent set of parameters.
However, IMeshSceneNode seems incompatible with this approach. My biggest problem with the IMeshSceneNode is that it does not seem that I can arbitrarily change the materials. Furthermore, the IMesh on which this class seems to be based has mesh buffers that are dependent on a material... Example:
Code: Select all
virtual IMeshBuffer * getMeshBuffer (const video::SMaterial &material) const =0
Those are my basic questions I would like to start with. Next comes turning the classes I created using the tutorial into the classes that are compatible with the IMeshSceneNode class.
Here is an example of a "dynamic mesh" that I created based on the "Custom Scene Node" tutorial and the set of classes it relies on. I will work to figure this out myself once I understand the things above but just in case anyone will offer advice... how would I change this setup so that it is consistent with the IMeshSceneNode structure/interface?
First.. by basic "mesh" (node) class:
Code: Select all
class VRMesh : public ISceneNode
{
enum VRMeshType { TwoDimensional, Planar, ThreeDimensional };
public:
VRMesh(ISceneManager* manager, ISceneNode* parent = 0, int id = -1);
virtual ~VRMesh();
public /*virtual*/:
virtual const core::aabbox3d<f32>& getBoundingBox() const override;
public /*abstract*/:
virtual VRMeshType vrMeshType() = 0;
protected:
core::aabbox3d<f32> mBox;
};
Code: Select all
class _3DMesh : public VRMesh
{
public:
_3DMesh(ISceneManager* manager, ISceneNode* parent = 0, int id = -1);
virtual ~_3DMesh();
void resposition(vector3df* vertexArray, const int vertexCount, PositioningFlags3D alignmentPolicy);
void resposition(video::S3DVertex* vertexArray, const int vertexCount, PositioningFlags3D alignmentPolicy);
public /*override*/:
inline VRMeshType vrMeshType() override;
};
inline VRMeshType _3DMesh::vrMeshType() { return VRMeshType::ThreeDimensional; }
Code: Select all
class Standard3DMesh : public _3DMesh
{
public:
Standard3DMesh(ObjectInfo* info, ISceneManager* manager, ISceneNode* parent = 0, int id = -1);
virtual ~Standard3DMesh();
SMaterial* material() const;
void setMaterial(SMaterial* material);
protected:
video::S3DVertex* mVertexArray;
SMaterial* mMaterial;
};
Code: Select all
class SingleTexturedSphere : public Standard3DMesh
{
public:
SingleTexturedSphere(ObjectInfo* info, ISceneManager* manager, ISceneNode* parent = NULL, int id = -1, const vector3df& position = vector3df(0.0f, 0.0f, 0.0f),
PositioningFlags3D alignmentPolicy = PositioningFlags3D(PositioningPolicy3D::centered, PositioningPolicy3D::centered, PositioningPolicy3D::centered));
virtual ~SingleTexturedSphere();
public /*overrides*/:
virtual void OnRegisterSceneNode() override;
virtual void render() override;
private /*fields*/:
unsigned short* mIndices;
int mSlices;
int mWedges;
int mVertexCount;
int mIndexCount;
S3DVertex mCenterVertex;
float mRadius;
void createSphereVertices(ObjectInfo* info, PositioningFlags3D alignmentPolicy);
inline vector2df getTextureCoordinates(ObjectInfo* info, int faceIndex);
};
Code: Select all
SingleTexturedSphere::SingleTexturedSphere(ObjectInfo* info, ISceneManager* manager, ISceneNode* parent, int id, const vector3df& position, PositioningFlags3D alignmentPolicy) :
Standard3DMesh(info, manager, parent, id),
mSlices(8),
mWedges(8),
mRadius(1.0f)
//mMaterial(info->
{
DynamicMesh* meshInfo = static_cast<DynamicMesh*>(info->getMesh());
if (meshInfo)
{
mRadius = meshInfo->getFloatParameter("radius");
if (mRadius == FLT_MAX)
mRadius = 1.0f;
mWedges = meshInfo->getIntParameter("wedges");
if (mWedges == INT_MAX)
mWedges = 8;
mSlices = meshInfo->getIntParameter("slices");
if (mSlices == INT_MAX)
mSlices = 8;
}
createSphereVertices(info, alignmentPolicy);
}
SingleTexturedSphere::~SingleTexturedSphere()
{
delete [] mVertexArray;
delete [] mIndices;
}
void SingleTexturedSphere::createSphereVertices(ObjectInfo* info, PositioningFlags3D alignmentPolicy)
{
mCenterVertex.Pos.set(mRadius, mRadius, mRadius);
vector3df northpole(mRadius, 2 * mRadius, mRadius);
vector3df southpole(mRadius, 0.0f, mRadius);
//float uYDelta = PI / (float)mSlices;
//float uYOffset = 1.0f;
float tDelta = PI / (float)mSlices;
float theta;
float omega = 0.0f;
float oDelta = 2 * PI / mWedges;
mVertexCount = (mSlices + 1) * (mWedges + 1);
mVertexArray = new S3DVertex[mVertexCount];
mIndexCount = 6 * (mSlices - 1) * mWedges;
mIndices = new unsigned short[mIndexCount];
int vIndex = 0;
int iIndex = 0;
int verticesPerWedgeLine = mSlices + 1;
float tFactorY = 1.0f / mWedges;
//int slicePos = mSlices - 1;
// ALL WEDGES EXCEPT LAST ONE
for (int i = 0; i < mWedges; ++i)
{
theta = 0.0f;
for (int j = 0; j <= mSlices; ++j)
{
// determine coordinates and texture coordinates
if (j == 0)
{
mVertexArray[vIndex].Pos.set(northpole);
mVertexArray[vIndex].TCoords.set(mWedges * i, 0.0f);
}
else if (j == mSlices)
{
mVertexArray[vIndex].Pos.set(southpole);
mVertexArray[vIndex].TCoords.set(mWedges * i, 1.0f);
}
else
{
mVertexArray[vIndex].Pos.set(
mRadius * sinf(theta) * cosf(omega),
mRadius * cosf(theta),
mRadius * sinf(theta) * sinf(omega));
mVertexArray[vIndex].TCoords.set(mWedges * i, 0.5f - (0.5f * cosf(theta))); // attempts equal area grid
}
// figure out next index set
if (i > 0)
{
if (j == 1)
{
mIndices[iIndex] = vIndex - 1;
mIndices[iIndex + 1] = vIndex;
mIndices[iIndex + 2] = vIndex - verticesPerWedgeLine;
iIndex += 3;
}
else if (j == mSlices)
{
mIndices[iIndex] = vIndex - verticesPerWedgeLine - 1;
mIndices[iIndex + 1] = vIndex - 1;
mIndices[iIndex + 2] = vIndex;
iIndex += 3;
}
else
{
mIndices[iIndex] = vIndex - verticesPerWedgeLine - 1;
mIndices[iIndex + 1] = vIndex;
mIndices[iIndex + 2] = vIndex - verticesPerWedgeLine;
mIndices[iIndex + 3] = vIndex - verticesPerWedgeLine - 1;;
mIndices[iIndex + 4] = vIndex - 1;
mIndices[iIndex + 5] = vIndex;
iIndex += 6;
}
}
mVertexArray[vIndex].Normal.set(mVertexArray[vIndex].Pos.normalize());
++vIndex;
theta += tDelta;
}
omega += oDelta;
}
// figure out last wedge
theta = 0.0f;
for (int j = 0; j <= mSlices; ++j)
{
mVertexArray[vIndex].Pos.set(mVertexArray[j].Pos);
if (j == 1)
{
mIndices[iIndex] = vIndex - 1;
mIndices[iIndex + 1] = vIndex;
mIndices[iIndex + 2] = vIndex - verticesPerWedgeLine;
iIndex += 3;
}
else if (j == mSlices)
{
mIndices[iIndex] = vIndex - verticesPerWedgeLine - 1;
mIndices[iIndex + 1] = vIndex - 1;
mIndices[iIndex + 2] = vIndex;
iIndex += 3;
}
else
{
mIndices[iIndex] = vIndex - verticesPerWedgeLine - 1;
mIndices[iIndex + 1] = vIndex;
mIndices[iIndex + 2] = vIndex - verticesPerWedgeLine;
mIndices[iIndex + 3] = vIndex - verticesPerWedgeLine - 1;;
mIndices[iIndex + 4] = vIndex - 1;
mIndices[iIndex + 5] = vIndex;
iIndex += 6;
}
mVertexArray[vIndex].Normal.set(mVertexArray[vIndex].Pos.normalize());
++vIndex;
theta += tDelta;
}
// apply the alignment policy
this->resposition(mVertexArray, mVertexCount, alignmentPolicy);
// create bounding box
mBox.reset(mCenterVertex.Pos);
for (int i = 0; i < mVertexCount; ++i)
mBox.addInternalPoint(mVertexArray[i].Pos);
}
void SingleTexturedSphere::OnRegisterSceneNode()
{
if (IsVisible)
SceneManager->registerNodeForRendering(this);
ISceneNode::OnRegisterSceneNode();
}
void SingleTexturedSphere::render()
{
video::IVideoDriver* driver = SceneManager->getVideoDriver();
driver->setMaterial(*mMaterial);
driver->drawIndexedTriangleList(mVertexArray, mVertexCount, mIndices, mIndexCount / 3);
}