//################################################################################################## // // Custom Visualization Core library // Copyright (C) 2011-2012 Ceetron AS // // This library is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This library is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at <> // for more details. // //################################################################################################## #include "cvfBase.h" #include "cvfDrawableVectors.h" #include "cvfOpenGL.h" #include "cvfOpenGLResourceManager.h" #include "cvfBufferObjectManaged.h" #include "cvfDrawableGeo.h" #include "cvfQuat.h" #include "cvfShaderProgram.h" #include "cvfUniform.h" #include "cvfPrimitiveSetIndexedUShort.h" #include "cvfRay.h" #ifndef CVF_OPENGL_ES #include "cvfRenderState_FF.h" #endif namespace cvf { //================================================================================================== /// /// \class cvf::DrawableVectors /// \ingroup Render /// /// DrawableVectors implements drawing of vector arrows. /// //================================================================================================== //-------------------------------------------------------------------------------------------------- /// Constructor to use when using fixed function rendering //-------------------------------------------------------------------------------------------------- DrawableVectors::DrawableVectors() : m_singleColor(Color3f(Color3::WHITE)), m_renderWithVBO(true) { } //-------------------------------------------------------------------------------------------------- /// Constructor to use when using shader based rendering //-------------------------------------------------------------------------------------------------- DrawableVectors::DrawableVectors(String vectorMatrixUniformName, String colorUniformName) : m_vectorMatrixUniformName(vectorMatrixUniformName), m_colorUniformName(colorUniformName), m_singleColor(Color3f(Color3::WHITE)), m_renderWithVBO(true) { } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- DrawableVectors::~DrawableVectors() { releaseBufferObjectsGPU(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void DrawableVectors::setGlyph(UShortArray* triangles, Vec3fArray* vertices) { m_vectorGlyph = new DrawableGeo; m_vectorGlyphPrimSet = new PrimitiveSetIndexedUShort(PT_TRIANGLES); m_vectorGlyphPrimSet->setIndices(triangles); m_vectorGlyph->setVertexArray(vertices); m_vectorGlyph->addPrimitiveSet(m_vectorGlyphPrimSet.p()); m_vectorGlyph->computeNormals(); if (m_renderWithVBO) { m_vectorGlyph->setRenderMode(DrawableGeo::BUFFER_OBJECT); } releaseBufferObjectsGPU(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void DrawableVectors::setSingleColor(Color3f color) { m_singleColor = color; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void DrawableVectors::setVectors(Vec3fArray* vertexArray, Vec3fArray* vectorArray) { CVF_ASSERT(vertexArray && vectorArray); CVF_ASSERT(vertexArray->size() && vectorArray->size()); m_vertexArray = vertexArray; m_vectorArray = vectorArray; m_boundingBox.add(*m_vertexArray); } //-------------------------------------------------------------------------------------------------- /// Set color of each corresponding vector /// /// \param vectorColorArray Array of colors to be mapped one-by-one to the vector array /// \param colorUniformName Name of uniform when used by a shader program. Ignored if not. /// /// \sa setVectors //-------------------------------------------------------------------------------------------------- void DrawableVectors::setColors(Color3fArray* vectorColorArray) { m_colorArray = vectorColorArray; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t DrawableVectors::vectorCount() const { CVF_ASSERT(m_vertexArray->size() == m_vectorArray->size()); return m_vertexArray->size(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void DrawableVectors::render(OpenGLContext* oglContext, ShaderProgram* shaderProgram, const MatrixState&) { CVF_CALLSITE_OPENGL(oglContext); CVF_ASSERT(shaderProgram); CVF_ASSERT(shaderProgram->isProgramUsed(oglContext)); CVF_ASSERT(m_vertexArray->size() == m_vectorArray->size()); CVF_ASSERT(m_colorArray.isNull() || (m_colorArray->size() == m_vectorArray->size())); CVF_ASSERT(m_vectorGlyph.notNull()); CVF_ASSERT(m_vectorGlyph->primitiveSetCount() == 1); // Setup Vertex Arrays/vbos const GLvoid* ptrOrOffset = 0; if (m_renderWithVBO && m_glyphVerticesAndNormalsBO.notNull() && m_glyphVerticesAndNormalsBO->isUploaded()) { // Bind VBO for vertex and normal data m_glyphVerticesAndNormalsBO->bindBuffer(oglContext); glVertexAttribPointer(ShaderProgram::NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(float)*6, (void*)(sizeof(float)*3)); glVertexAttribPointer(ShaderProgram::VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof(float)*6, 0); glEnableVertexAttribArray(ShaderProgram::NORMAL); glEnableVertexAttribArray(ShaderProgram::VERTEX); m_indicesBO->bindBuffer(oglContext); } else { glBindBuffer(GL_ARRAY_BUFFER, 0); glVertexAttribPointer(ShaderProgram::NORMAL, 3, GL_FLOAT, GL_FALSE, 0, m_vectorGlyph->normalArray()->ptr()->ptr()); glEnableVertexAttribArray(ShaderProgram::NORMAL); glVertexAttribPointer(ShaderProgram::VERTEX, 3, GL_FLOAT, GL_FALSE, 0, m_vectorGlyph->vertexArray()->ptr()->ptr()); glEnableVertexAttribArray(ShaderProgram::VERTEX); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); ptrOrOffset = m_vectorGlyphPrimSet->indices()->ptr(); } // Must use manual uniform setting, as setting the uniform through the Uniform* class requires a lookup for location // every time, and this is always the same GLint vectorMatrixUniformLocation = shaderProgram->uniformLocation(m_vectorMatrixUniformName.toAscii().ptr()); CVF_ASSERT(vectorMatrixUniformLocation != -1); GLint colorUniformLocation = shaderProgram->uniformLocation(m_colorUniformName.toAscii().ptr()); CVF_ASSERT(colorUniformLocation != -1); #ifndef CVF_OPENGL_ES uint minIndex = m_vectorGlyphPrimSet->minIndex(); uint maxIndex = m_vectorGlyphPrimSet->maxIndex(); #endif GLsizei indexCount = static_cast(m_vectorGlyphPrimSet->indexCount()); // Set the single color to use if (m_colorArray.isNull()) { glUniform3fv(colorUniformLocation, 1, m_singleColor.ptr()); } float vectorMat[16]; size_t numVectors = m_vectorArray->size(); size_t i; for (i = 0; i < numVectors; i++) { // Compute the transformation matrix vectorMatrix(i, vectorMat); // Set this as a uniform to the shader program glUniformMatrix4fv(vectorMatrixUniformLocation, 1, GL_FALSE, vectorMat); if (m_colorArray.notNull()) { glUniform3fv(colorUniformLocation, 1, m_colorArray->get(i).ptr()); } // Draw the arrow #ifdef CVF_OPENGL_ES glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, ptrOrOffset); #else glDrawRangeElements(GL_TRIANGLES, minIndex, maxIndex, indexCount, GL_UNSIGNED_SHORT, ptrOrOffset); #endif } // Cleanup glDisableVertexAttribArray(ShaderProgram::VERTEX); glDisableVertexAttribArray(ShaderProgram::NORMAL); CVF_CHECK_OGL(oglContext); // Things to consider for performance: // 1) Uniform arrays evt. Uniform buffer objects // 2) Texture // 3) Texture array // GL_ARB_draw_instanced(); // glDrawElementsInstanced } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void DrawableVectors::renderFixedFunction(OpenGLContext* oglContext, const MatrixState& matrixState) { #ifdef CVF_OPENGL_ES CVF_FAIL_MSG("Not supported on OpenGL ES"); #else CVF_ASSERT(oglContext); CVF_ASSERT(m_vertexArray->size() == m_vectorArray->size()); CVF_ASSERT(m_vectorGlyph.notNull()); glEnable(GL_NORMALIZE); glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); glEnable(GL_COLOR_MATERIAL); if (m_colorArray.isNull()) { glColor3fv(m_singleColor.ptr()); } float vectorMat[16]; size_t numVectors = m_vectorArray->size(); size_t i; for (i = 0; i < numVectors; i++) { // Compute/retrieve "matrix" vectorMatrix(i, vectorMat); glPushMatrix(); glMultMatrixf(vectorMat); if (m_colorArray.notNull()) { glColor3fv(m_colorArray->get(i).ptr()); } // Draw the geometry m_vectorGlyph->renderFixedFunction(oglContext, matrixState); glPopMatrix(); } glDisable(GL_NORMALIZE); CVF_CHECK_OGL(oglContext); #endif // CVF_OPENGL_ES } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void DrawableVectors::renderImmediateMode(OpenGLContext* oglContext, const MatrixState& matrixState) { #ifdef CVF_OPENGL_ES CVF_FAIL_MSG("Not supported on OpenGL ES"); #else CVF_ASSERT(oglContext); CVF_ASSERT(m_vertexArray->size() == m_vectorArray->size()); CVF_ASSERT(m_vectorGlyph.notNull()); glEnable(GL_NORMALIZE); glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); glEnable(GL_COLOR_MATERIAL); if (m_colorArray.isNull()) { glColor3fv(m_singleColor.ptr()); } float vectorMat[16]; size_t numVectors = m_vectorArray->size(); size_t i; for (i = 0; i < numVectors; i++) { // Compute/retrieve "matrix" vectorMatrix(i, vectorMat); glPushMatrix(); glMultMatrixf(vectorMat); if (m_colorArray.notNull()) { glColor3fv(m_colorArray->get(i).ptr()); } // Draw the geometry m_vectorGlyph->renderImmediateMode(oglContext, matrixState); glPopMatrix(); } glDisable(GL_NORMALIZE); CVF_CHECK_OGL(oglContext); #endif // CVF_OPENGL_ES } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void DrawableVectors::createUploadBufferObjectsGPU(OpenGLContext* oglContext) { if (!m_renderWithVBO || m_vertexArray->size() == 0 || m_vectorGlyph.isNull() || m_vectorGlyphPrimSet.isNull()) { return; } if (m_glyphVerticesAndNormalsBO.isNull() || !m_glyphVerticesAndNormalsBO->isUploaded()) { // Build a interleaved VBO for glyphs vertices and normals to get better performance // during the main shader based draw path size_t numVertices = m_vectorGlyph->vertexArray()->size(); Vec3fArray data; data.reserve(numVertices*2); size_t i; for (i = 0; i < numVertices; i++) { data.add(m_vectorGlyph->vertexArray()->get(i)); data.add(m_vectorGlyph->normalArray()->get(i)); } GLuint uiSizeInBytes = static_cast(data.size()*3*sizeof(float)); m_glyphVerticesAndNormalsBO = oglContext->resourceManager()->getOrCreateManagedBufferObject(oglContext, GL_ARRAY_BUFFER, uiSizeInBytes, data.ptr()->ptr()); } if (m_indicesBO.isNull() || !m_indicesBO->isUploaded()) { const UShortArray* indices = m_vectorGlyphPrimSet->indices(); size_t numIndices = indices->size(); if (numIndices > 0) { GLuint uiSizeInBytes = static_cast(numIndices*sizeof(GLushort)); m_indicesBO = oglContext->resourceManager()->getOrCreateManagedBufferObject(oglContext, GL_ELEMENT_ARRAY_BUFFER, uiSizeInBytes, indices->ptr()); CVF_CHECK_OGL(oglContext); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void DrawableVectors::releaseBufferObjectsGPU() { m_glyphVerticesAndNormalsBO = NULL; if (m_vectorGlyph.notNull()) { m_vectorGlyph->releaseBufferObjectsGPU(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t DrawableVectors::vertexCount() const { if (m_vectorGlyph.isNull()) { return 0; } return m_vectorGlyph->vertexCount()*vectorCount(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t DrawableVectors::triangleCount() const { if (m_vectorGlyph.isNull()) { return 0; } return m_vectorGlyph->triangleCount()*vectorCount(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t DrawableVectors::faceCount() const { if (m_vectorGlyph.isNull()) { return 0; } return m_vectorGlyph->faceCount()*vectorCount(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- BoundingBox DrawableVectors::boundingBox() const { return m_boundingBox; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool DrawableVectors::rayIntersectCreateDetail(const Ray& ray, Vec3d* intersectionPoint, ref* hitDetail) const { CVF_UNUSED(ray); CVF_UNUSED(intersectionPoint); CVF_UNUSED(hitDetail); return false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void DrawableVectors::vectorMatrix(size_t vectorIndex, float vectorMatArray[16]) { const Vec3f& anchorPt = m_vertexArray->get(vectorIndex); Vec3f vec = m_vectorArray->get(vectorIndex); float length = vec.length(); Quatf rotQuat; // This should really be a ratio eval based on length of vector // Copied from VTOVectorDrawer.cpp. TODO: refactor static const float TINY_VEC_COMP = 0.0000000001f; bool negZOnly = vec.z() < 0.0f && Math::abs(vec.x()) < TINY_VEC_COMP && Math::abs(vec.y()) < TINY_VEC_COMP; if (negZOnly) { rotQuat.set(0.0f, 1.0f, 0.0f, 0.0f); } else { const Vec3f s = Vec3f::Z_AXIS; Vec3f d = vec/length; Vec3f v = s + d; Vec3f cross = v ^ d; float dot = v*d; rotQuat.set(cross.x(), cross.y(), cross.z(), dot); } float Nq = rotQuat.x()*rotQuat.x() + rotQuat.y()*rotQuat.y() + rotQuat.z()*rotQuat.z() + rotQuat.w()*rotQuat.w(); float s = (Nq > 0.0f) ? (2.0f/Nq) : 0.0f; float xs = rotQuat.x()*s; float ys = rotQuat.y()*s; float zs = rotQuat.z()*s; float wx = rotQuat.w()*xs; float wy = rotQuat.w()*ys; float wz = rotQuat.w()*zs; float xx = rotQuat.x()*xs; float xy = rotQuat.x()*ys; float xz = rotQuat.x()*zs; float yy = rotQuat.y()*ys; float yz = rotQuat.y()*zs; float zz = rotQuat.z()*zs; vectorMatArray[0] = length*(1.0f - (yy + zz)); vectorMatArray[1] = length*(xy + wz); vectorMatArray[2] = length*(xz - wy); vectorMatArray[3] = 0.0f; vectorMatArray[4] = length*(xy - wz); vectorMatArray[5] = length*(1.0f - (xx + zz)); vectorMatArray[6] = length*(yz + wx); vectorMatArray[7] = 0.0f; vectorMatArray[8] = length*(xz + wy); vectorMatArray[9] = length*(yz - wx); vectorMatArray[10] = length*(1.0f - (xx + yy)); vectorMatArray[11] = 0.0f; // Set the translation vectorMatArray[12] = anchorPt.x(); vectorMatArray[13] = anchorPt.y(); vectorMatArray[14] = anchorPt.z(); vectorMatArray[15] = 1.0f; } } // namespace cvf