#include "RendererConfig.h" #ifdef USE_SOFTWARE_TNL #if defined(WIN32) #include #endif #include #include "dyna_gl.h" #include "IMeshBuilder.h" #include "pserror.h" #include #include #if defined(WIN32) #include "win/arb_extensions.h" #endif #if defined(WIN32) extern PFNGLACTIVETEXTUREARBPROC oglActiveTextureARB; extern PFNGLCLIENTACTIVETEXTUREARBPROC oglClientActiveTextureARB; extern PFNGLMULTITEXCOORD4FARBPROC oglMultiTexCoord4f; #endif namespace RZ { namespace Renderer { class OpenGLMeshBuilder : public IMeshBuilder { public: OpenGLMeshBuilder(); // Begin // Begins the generation of a mesh. You must supply // the primitive type, size of the vertex buffer, and // the number of primitives. // vertexFlags are a combination of VertexType values // that represent what data should be stored in the verts. void Begin(PrimitiveType type, unsigned int vertexFlags, unsigned int numPrims, unsigned int numVerts, bool autogenNormals); // End // Ends the build process which will cause the generation of the mesh data. // Returned will be a handle to the mesh RZ::Renderer::MeshHandle End(void); // SetStreamData // Stores stream data for one or more vertex fields. // streamId: Which field we are setting data for // startVertIndex: Which vertex in vertex buffer are we starting at // numVerts: How many full verts of data are we processing // dataPtr: Incoming data. All data is float based. // elementsPerField: How many floats-per-field for this data. // NOTE: This is for verification purposes. Positions and // Normals are 3 elements. Texture UVs are 2 elements. Colors // are 4 elements. // stride: The number of bytes from the START of one field to the next on the incoming stream. void SetStreamData(VertexType streamId, unsigned int startVertIndex, unsigned int numVerts, const float *dataPtr, unsigned int elementsPerField, unsigned int stride); // AddPrimitive // Adds another primitive to the buffer. void AddPrimitive(unsigned int numVerts, unsigned int *vertexIndices); private: void Reset(); struct RawVertexData { float m_position[3]; float m_color[4]; float m_uv[2]; float m_normal[3]; }; typedef std::vector RawVertexDataVector; typedef std::vector PrimitiveIndexVector; typedef std::vector> PrimitiveDefinitionVector; RawVertexDataVector m_rawVertex; PrimitiveIndexVector m_rawIndicies; PrimitiveDefinitionVector m_rawPrims; PrimitiveType m_primType; unsigned int m_vertexFlags; unsigned int m_numPrims; unsigned int m_numVerts; bool m_autoGenNormals; bool m_isBuilding; }; class OpenGLMesh : public IMeshHandle { public: // execute the drawing process void Draw(void); private: friend class OpenGLMeshBuilder; typedef boost::scoped_array ScopedFloatArray; typedef boost::scoped_array ScopedUIntArray; ScopedFloatArray m_arrayPositions; ScopedFloatArray m_arrayNormals; ScopedFloatArray m_arrayColors; ScopedFloatArray m_arrayTexture1; ScopedUIntArray m_arrayPrimSizes; ScopedUIntArray m_arrayIndices; GLenum m_mode; unsigned int m_numPrims; }; } // namespace Renderer } // namespace RZ using namespace RZ; using namespace RZ::Renderer; void OpenGLMesh::Draw(void) { float *ptr; // positions if (ptr = m_arrayPositions.get()) { dglEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, ptr); } else { dglDisableClientState(GL_VERTEX_ARRAY); } // colors if (ptr = m_arrayColors.get()) { dglEnableClientState(GL_COLOR_ARRAY); dglColorPointer(4, GL_FLOAT, 0, ptr); } else { dglDisableClientState(GL_COLOR_ARRAY); } // normals if (ptr = m_arrayNormals.get()) { dglEnableClientState(GL_NORMAL_ARRAY); dglNormalPointer(3, GL_FLOAT, 0, ptr); } else { dglDisableClientState(GL_NORMAL_ARRAY); } // texture if (ptr = m_arrayTexture1.get()) { oglClientActiveTextureARB(GL_TEXTURE0_ARB + 0); dglEnableClientState(GL_TEXTURE_COORD_ARRAY); dglTexCoordPointer(4, GL_FLOAT, 0, ptr); } else { oglClientActiveTextureARB(GL_TEXTURE0_ARB + 0); dglDisableClientState(GL_TEXTURE_COORD_ARRAY); } // draw all the primitives unsigned int i, indexOffset = 0, *indexList = m_arrayIndices.get(); for (i = 0; i < m_numPrims; ++i) { unsigned int numVerts = m_arrayPrimSizes[i]; dglDrawElements(m_mode, numVerts, GL_UNSIGNED_INT, &indexList[indexOffset]); indexOffset += numVerts; } } OpenGLMeshBuilder::OpenGLMeshBuilder() : m_isBuilding(false) {} void OpenGLMeshBuilder::Reset(void) { m_isBuilding = false; m_rawVertex.clear(); m_rawIndicies.clear(); m_rawPrims.clear(); } // Begin // Begins the generation of a mesh. You must supply // the primitive type, size of the vertex buffer, and // the number of primitives. // vertexFlags are a combination of VertexType values // that represent what data should be stored in the verts. void OpenGLMeshBuilder::Begin(PrimitiveType type, unsigned int vertexFlags, unsigned int numPrims, unsigned int numVerts, bool autogenNormals) { ASSERT(m_isBuilding == false); ASSERT(vertexFlags & kPosition); // position must be set Reset(); // initialize m_primType = type; m_vertexFlags = vertexFlags; m_numPrims = numPrims; m_numVerts = numVerts; m_autoGenNormals = autogenNormals; m_isBuilding = true; // prepare our data m_rawVertex.reserve(m_numVerts); m_rawVertex.resize(m_numVerts); m_rawPrims.reserve(m_numPrims); m_rawIndicies.reserve(m_numPrims * 4); // rough estimate } // End // Ends the build process which will cause the generation of the mesh data. // Returned will be a handle to the mesh RZ::Renderer::MeshHandle OpenGLMeshBuilder::End(void) { // make sure we are building ASSERT(m_isBuilding); // TODO: auto-generate normals if we are supposed to // create a mesh OpenGLMesh *glMesh = new OpenGLMesh; MeshHandle meshHandle(glMesh); // allocate the data if (m_vertexFlags & kPosition) { glMesh->m_arrayPositions.reset(new float[m_numVerts * 3]); } if (m_vertexFlags & kNormal) { glMesh->m_arrayNormals.reset(new float[m_numVerts * 3]); } if (m_vertexFlags & kColor) { glMesh->m_arrayColors.reset(new float[m_numVerts * 4]); } if (m_vertexFlags & kTexture1) { glMesh->m_arrayTexture1.reset(new float[m_numVerts * 2]); } glMesh->m_arrayPrimSizes.reset(new unsigned int[m_numPrims]); glMesh->m_arrayIndices.reset(new unsigned int[m_rawIndicies.size()]); // initialize the data unsigned int i; for (i = 0; i < m_numVerts; ++i) { RawVertexData &rvd = m_rawVertex[i]; if (m_vertexFlags & kPosition) { memcpy(glMesh->m_arrayPositions.get() + (m_numVerts * 3), rvd.m_position, 3 * sizeof(float)); } if (m_vertexFlags & kNormal) { memcpy(glMesh->m_arrayNormals.get() + (m_numVerts * 3), rvd.m_normal, 3 * sizeof(float)); } if (m_vertexFlags & kColor) { memcpy(glMesh->m_arrayColors.get() + (m_numVerts * 4), rvd.m_color, 4 * sizeof(float)); } if (m_vertexFlags & kTexture1) { memcpy(glMesh->m_arrayTexture1.get() + (m_numVerts * 2), rvd.m_uv, 2 * sizeof(float)); } } for (i = 0; i < m_numPrims; ++i) { glMesh->m_arrayPrimSizes.get()[i] = m_rawPrims[i].second; } memcpy(glMesh->m_arrayIndices.get(), &m_rawIndicies[0], m_rawIndicies.size() * sizeof(unsigned int)); switch (m_primType) { case kTriangle: glMesh->m_mode = GL_TRIANGLES; break; case kTriangleFan: glMesh->m_mode = GL_TRIANGLE_FAN; break; case kTriangleStrip: glMesh->m_mode = GL_TRIANGLE_STRIP; break; } m_numPrims = m_numPrims; // we don't need the temporary data anymore Reset(); // return the mesh handle return meshHandle; } // SetStreamData // Stores stream data for one or more vertex fields. // streamId: Which field we are setting data for // startVertIndex: Which vertex in vertex buffer are we starting at // numVerts: How many full verts of data are we processing // dataPtr: Incoming data. All data is float based. // elementsPerField: How many floats-per-field for this data. // NOTE: This is for verification purposes. Positions and // Normals are 3 elements. Texture UVs are 2 elements. Colors // are 4 elements. // stride: The number of bytes from the START of one field to the next on the incoming stream. void OpenGLMeshBuilder::SetStreamData(VertexType streamId, unsigned int startVertIndex, unsigned int numVerts, const float *dataPtr, unsigned int elementsPerField, unsigned int stride) { // make sure we are building ASSERT(m_isBuilding); // make sure one, and only one, bit is set in the stream Id ASSERT((streamId != 0) && ((streamId & (streamId - 1)) == 0)); // make sure it is expected ASSERT(streamId & m_vertexFlags); // make sure we don't go beyond our vertex limit ASSERT(((startVertIndex + numVerts) <= m_numVerts)); // check input pointers ASSERT(dataPtr != NULL); ASSERT(stride != 0); unsigned int offset = 0; switch (streamId) { case kPosition: offset = 0; ASSERT(elementsPerField == 3); break; case kColor: offset = 3; ASSERT(elementsPerField == 4); break; case kTexture1: offset = 7; ASSERT(elementsPerField == 2); break; case kNormal: offset = 9; ASSERT(elementsPerField == 3); break; } // start looping through and updating the verts unsigned int vertIdx; for (vertIdx = 0; vertIdx < numVerts; ++vertIdx, dataPtr = (const float *)(((const unsigned char *)dataPtr) + stride)) { // get to the vert RawVertexData &rvd = m_rawVertex[vertIdx + startVertIndex]; float *rawFloat = &rvd.m_position[0]; // fill in the data memcpy(rawFloat, dataPtr, elementsPerField * sizeof(float)); } } // AddPrimitive // Adds another primitive to the buffer. void OpenGLMeshBuilder::AddPrimitive(unsigned int numVerts, unsigned int *vertexIndices) { // make sure we are building ASSERT(m_isBuilding); // don't blow the buffer ASSERT(m_rawPrims.size() < m_numPrims); // check input pointers ASSERT(vertexIndices != NULL); ASSERT(numVerts >= 3); // what is the index offset? unsigned int indexOffset = m_rawIndicies.size(); // add all the vertex indices unsigned int i; for (i = 0; i < numVerts; ++i) { unsigned int index = vertexIndices[i]; ASSERT(index < m_numVerts); m_rawIndicies.push_back(index); } // add the primitive m_rawPrims.push_back(std::pair(numVerts, indexOffset)); } RZ::Renderer::IMeshBuilder *rend_CreateMeshBuilder(void) { return new RZ::Renderer::OpenGLMeshBuilder; } #endif