diff --git a/src/celengine/modelgeometry.cpp b/src/celengine/modelgeometry.cpp index 51100da60..4617f1eb6 100644 --- a/src/celengine/modelgeometry.cpp +++ b/src/celengine/modelgeometry.cpp @@ -37,14 +37,14 @@ public: { for (auto vboId : vbos) { - if (vboId != 0) + if (vboId.second) { - glDeleteBuffers(1, &vboId); + glDeleteBuffers(1, &vboId.second); } } } - std::vector vbos; // vertex buffer objects + std::map vbos; // vertex buffer objects }; @@ -99,10 +99,27 @@ ModelGeometry::render(RenderContext& rc, double /* t */) mesh->getVertexData(), GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); + m_glData->vbos[mesh->getVertexData()] = vboId; } } - m_glData->vbos.push_back(vboId); + for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex) + { + const Mesh::PrimitiveGroup* group = mesh->getGroup(groupIndex); + if (group->vertexOverride != nullptr && group->vertexCountOverride * group->vertexDescriptionOverride.stride > MinVBOSize) + { + glGenBuffers(1, &vboId); + if (vboId != 0) + { + glBindBuffer(GL_ARRAY_BUFFER, vboId); + glBufferData(GL_ARRAY_BUFFER, + group->vertexCountOverride * group->vertexDescriptionOverride.stride, + group->vertexOverride, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + m_glData->vbos[group->vertexOverride] = vboId; + } + } + } } } @@ -113,30 +130,42 @@ ModelGeometry::render(RenderContext& rc, double /* t */) for (unsigned int meshIndex = 0; meshIndex < m_model->getMeshCount(); ++meshIndex) { Mesh* mesh = m_model->getMesh(meshIndex); - GLuint vboId = 0; - if (meshIndex < m_glData->vbos.size()) - { - vboId = m_glData->vbos[meshIndex]; - } - - if (vboId != 0) - { - // Bind the vertex buffer object. - glBindBuffer(GL_ARRAY_BUFFER, vboId); - rc.setVertexArrays(mesh->getVertexDescription(), nullptr); - } - else - { - // No vertex buffer object; just use normal vertex arrays - rc.setVertexArrays(mesh->getVertexDescription(), mesh->getVertexData()); - } + const void* currentData = nullptr; + GLuint currentVboId = 0; // Iterate over all primitive groups in the mesh for (unsigned int groupIndex = 0; groupIndex < mesh->getGroupCount(); ++groupIndex) { const Mesh::PrimitiveGroup* group = mesh->getGroup(groupIndex); - rc.updateShader(mesh->getVertexDescription(), group->prim); + bool useOverrideValue = group->vertexOverride != nullptr && rc.shouldDrawLineAsTriangles(); + + const void* data = useOverrideValue ? group->vertexOverride : mesh->getVertexData(); + auto vertexDescription = useOverrideValue ? group->vertexDescriptionOverride : mesh->getVertexDescription(); + + if (currentData != data) + { + GLuint vboId = m_glData->vbos[data]; + if (vboId != 0) + { + // Bind the vertex buffer object. + glBindBuffer(GL_ARRAY_BUFFER, vboId); + rc.setVertexArrays(vertexDescription, nullptr); + } + else + { + if (currentVboId != 0) + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + // No vertex buffer object; just use normal vertex arrays + rc.setVertexArrays(vertexDescription, data); + } + currentData = data; + currentVboId = vboId; + } + + rc.updateShader(vertexDescription, group->prim); // Set up the material const Material* material = nullptr; @@ -147,11 +176,11 @@ ModelGeometry::render(RenderContext& rc, double /* t */) } rc.setMaterial(material); - rc.drawGroup(*group); + rc.drawGroup(*group, useOverrideValue); } // If we set a VBO, unbind it. - if (vboId != 0) + if (currentVboId != 0) { glBindBuffer(GL_ARRAY_BUFFER, 0); } diff --git a/src/celengine/rendcontext.cpp b/src/celengine/rendcontext.cpp index 34f4a0ee9..c7294a7c8 100644 --- a/src/celengine/rendcontext.cpp +++ b/src/celengine/rendcontext.cpp @@ -122,6 +122,13 @@ RenderContext::setMaterial(const Material* newMaterial) } +bool +RenderContext::shouldDrawLineAsTriangles() const +{ + return renderer->shouldDrawLineAsTriangles(); +} + + void RenderContext::setPointScale(float _pointScale) { @@ -151,7 +158,7 @@ RenderContext::getCameraOrientation() const void -RenderContext::drawGroup(const Mesh::PrimitiveGroup& group) +RenderContext::drawGroup(const Mesh::PrimitiveGroup& group, bool useOverride) { // Skip rendering if this is the emissive pass but there's no // emissive texture. @@ -175,10 +182,10 @@ RenderContext::drawGroup(const Mesh::PrimitiveGroup& group) glActiveTexture(GL_TEXTURE0); } - glDrawElements(GLPrimitiveModes[(int) group.prim], - group.nIndices, + glDrawElements(GLPrimitiveModes[(int)(useOverride ? group.primOverride : group.prim)], + useOverride ? group.nIndicesOverride : group.nIndices, GL_UNSIGNED_INT, - group.indices); + useOverride ? group.indicesOverride : group.indices); #ifndef GL_ES if (drawPoints) { @@ -208,19 +215,22 @@ RenderContext::updateShader(const cmod::Mesh::VertexDescription& desc, Mesh::Pri bool useNormalsNow = (desc.getAttribute(Mesh::Normal).format == Mesh::Float3); bool useColorsNow = (desc.getAttribute(Mesh::Color0).format != Mesh::InvalidFormat); bool useTexCoordsNow = (desc.getAttribute(Mesh::Texture0).format != Mesh::InvalidFormat); + bool drawLineNow = (desc.getAttribute(Mesh::NextPosition).format == Mesh::Float3); bool useStaticPointSizeNow = primType == Mesh::PointList; if (usePointSizeNow != usePointSize || useStaticPointSizeNow != useStaticPointSize || useNormalsNow != useNormals || useColorsNow != useColors || - useTexCoordsNow != useTexCoords) + useTexCoordsNow != useTexCoords || + drawLineNow != drawLine) { usePointSize = usePointSizeNow; useStaticPointSize = useStaticPointSizeNow; useNormals = useNormalsNow; useColors = useColorsNow; useTexCoords = useTexCoordsNow; + drawLine = drawLineNow; if (getMaterial() != nullptr) makeCurrent(*getMaterial()); } @@ -353,6 +363,40 @@ setExtendedVertexArrays(const Mesh::VertexDescription& desc, glDisableVertexAttribArray(CelestiaGLProgram::PointSizeAttributeIndex); break; } + + const Mesh::VertexAttribute& nextPos = desc.getAttribute(Mesh::NextPosition); + switch (nextPos.format) + { + case Mesh::Float3: + glEnableVertexAttribArray(CelestiaGLProgram::NextVCoordAttributeIndex); + glVertexAttribPointer(CelestiaGLProgram::NextVCoordAttributeIndex, + GLComponentCounts[(int) nextPos.format], + GLComponentTypes[(int) nextPos.format], + GL_FALSE, + desc.stride, + vertices + nextPos.offset); + break; + default: + glDisableVertexAttribArray(CelestiaGLProgram::NextVCoordAttributeIndex); + break; + } + + const Mesh::VertexAttribute& scaleFac = desc.getAttribute(Mesh::ScaleFactor); + switch (scaleFac.format) + { + case Mesh::Float1: + glEnableVertexAttribArray(CelestiaGLProgram::ScaleFactorAttributeIndex); + glVertexAttribPointer(CelestiaGLProgram::ScaleFactorAttributeIndex, + GLComponentCounts[(int) scaleFac.format], + GLComponentTypes[(int) scaleFac.format], + GL_FALSE, + desc.stride, + vertices + scaleFac.offset); + break; + default: + glDisableVertexAttribArray(CelestiaGLProgram::ScaleFactorAttributeIndex); + break; + } } @@ -551,6 +595,9 @@ GLSL_RenderContext::makeCurrent(const Material& m) else if (useStaticPointSize) shaderProps.texUsage |= ShaderProperties::StaticPointSize; + if (drawLine) + shaderProps.texUsage |= ShaderProperties::LineAsTriangles; + if (useColors) shaderProps.texUsage |= ShaderProperties::VertexColors; @@ -638,6 +685,12 @@ GLSL_RenderContext::makeCurrent(const Material& m) prog->pointScale = renderer->getScreenDpi() / 96.0f; } + if (drawLine) + { + prog->lineWidthX = renderer->getLineWidthX(); + prog->lineWidthY = renderer->getLineWidthY(); + } + // Ring shadow parameters if ((shaderProps.texUsage & ShaderProperties::RingShadowTexture) != 0) { @@ -783,6 +836,9 @@ GLSLUnlit_RenderContext::makeCurrent(const Material& m) else if (useStaticPointSize) shaderProps.texUsage |= ShaderProperties::StaticPointSize; + if (drawLine) + shaderProps.texUsage |= ShaderProperties::LineAsTriangles; + if (useColors) shaderProps.texUsage |= ShaderProperties::VertexColors; @@ -817,6 +873,12 @@ GLSLUnlit_RenderContext::makeCurrent(const Material& m) prog->pointScale = renderer->getScreenDpi() / 96.0f; } + if (drawLine) + { + prog->lineWidthX = renderer->getLineWidthX(); + prog->lineWidthY = renderer->getLineWidthY(); + } + Material::BlendMode newBlendMode = Material::InvalidBlend; if (m.opacity != 1.0f || m.blend == Material::AdditiveBlend || diff --git a/src/celengine/rendcontext.h b/src/celengine/rendcontext.h index 8710c537a..67777efbe 100644 --- a/src/celengine/rendcontext.h +++ b/src/celengine/rendcontext.h @@ -30,13 +30,14 @@ class RenderContext virtual void setVertexArrays(const cmod::Mesh::VertexDescription& desc, const void* vertexData); virtual void updateShader(const cmod::Mesh::VertexDescription& desc, cmod::Mesh::PrimitiveGroupType primType); - virtual void drawGroup(const cmod::Mesh::PrimitiveGroup& group); + virtual void drawGroup(const cmod::Mesh::PrimitiveGroup& group, bool useOverride); const cmod::Material* getMaterial() const; void setMaterial(const cmod::Material*); void lock() { locked = true; } void unlock() { locked = false; } bool isLocked() const { return locked; } + bool shouldDrawLineAsTriangles() const; enum RenderPass { @@ -70,6 +71,7 @@ class RenderContext bool useNormals{ true }; bool useColors{ false }; bool useTexCoords{ true }; + bool drawLine { false }; const Eigen::Matrix4f *modelViewMatrix; const Eigen::Matrix4f *projectionMatrix; }; diff --git a/src/celmodel/mesh.cpp b/src/celmodel/mesh.cpp index 331777c4c..c7118f56d 100644 --- a/src/celmodel/mesh.cpp +++ b/src/celmodel/mesh.cpp @@ -109,6 +109,23 @@ Mesh::VertexDescription::validate() const return true; } +Mesh::VertexDescription Mesh::VertexDescription::appendingAttributes(const VertexAttribute* newAttributes, int count) const +{ + unsigned int totalAttributeCount = nAttributes + count; + VertexAttribute* allAttributes = new VertexAttribute[totalAttributeCount]; + memcpy(allAttributes, attributes, sizeof(VertexAttribute) * nAttributes); + unsigned int newStride = stride; + for (int i = 0; i < count; ++i) + { + VertexAttribute attribute = newAttributes[i]; + allAttributes[nAttributes + i] = attribute; + newStride += Mesh::getVertexAttributeSize(attribute.format); + } + memcpy(allAttributes + nAttributes, newAttributes, sizeof(VertexAttribute) * count); + VertexDescription desc = VertexDescription(newStride, totalAttributeCount, allAttributes); + delete[] allAttributes; + return desc; +} Mesh::VertexDescription::~VertexDescription() { @@ -236,17 +253,113 @@ Mesh::addGroup(PrimitiveGroup* group) } +Mesh::PrimitiveGroup* Mesh::createLinePrimitiveGroup(bool lineStrip, unsigned int nIndices, index32* indices) +{ + // Transform LINE_STRIP/LINES to triangle vertices + int transformedVertCount = lineStrip ? (nIndices - 1) * 4 : nIndices * 2; + // Get information of the position attributes + auto positionAttributes = vertexDesc.getAttribute(Position); + int positionSize = getVertexAttributeSize(positionAttributes.format); + int positionOffset = positionAttributes.offset; + + int originalStride = vertexDesc.stride; + // Add another position (next line end), and scale factor + // ORIGINAL ATTRIBUTES | NextVCoordAttributeIndex | ScaleFactorAttributeIndex + int stride = originalStride + positionSize + sizeof(float); + + // Get line count + int lineCount = lineStrip ? nIndices - 1 : nIndices / 2; + int lineIndexCount = 6 * lineCount; + int lineVertexCount = 4 * lineCount; + + // Create buffer to hold the transformed vertices/indices + unsigned char* data = new unsigned char[stride * lineVertexCount]; + index32* newIndices = new index32[lineIndexCount]; + + unsigned char* originalData = (unsigned char *)vertices; + auto ptr = data; + for (int i = 0; i < lineCount; ++i) + { + int thisIndex = indices[lineStrip ? i : i * 2]; + int nextIndex = indices[lineStrip ? i + 1 : i * 2 + 1]; + + unsigned char* origThisVertLoc = originalData + thisIndex * originalStride; + unsigned char* origNextVertLoc = originalData + nextIndex * originalStride; + float *ff = (float *)origThisVertLoc; + float *ffn = (float *)origNextVertLoc; + + // Fill the info for the 4 vertices + memcpy(ptr, origThisVertLoc, originalStride); + memcpy(ptr + originalStride, origNextVertLoc + positionOffset, positionSize); + *(float *)&ptr[originalStride + positionSize] = -0.5f; + ptr += stride; + + memcpy(ptr, origThisVertLoc, originalStride); + memcpy(ptr + originalStride, origNextVertLoc + positionOffset, positionSize); + *(float *)&ptr[originalStride + positionSize] = 0.5f; + ptr += stride; + + memcpy(ptr, origNextVertLoc, originalStride); + memcpy(ptr + originalStride, origThisVertLoc + positionOffset, positionSize); + *(float *)&ptr[originalStride + positionSize] = -0.5f; + ptr += stride; + + memcpy(ptr, origNextVertLoc, originalStride); + memcpy(ptr + originalStride, origThisVertLoc + positionOffset, positionSize); + *(float *)&ptr[originalStride + positionSize] = 0.5f; + ptr += stride; + + int lineIndex = 6 * i; + index32 newIndex = 4 * i; + + // Fill info for the 6 indices + newIndices[lineIndex] = newIndex; + newIndices[lineIndex + 1] = newIndex + 1; + newIndices[lineIndex + 2] = newIndex + 2; + newIndices[lineIndex + 3] = newIndex + 2; + newIndices[lineIndex + 4] = newIndex + 3; + newIndices[lineIndex + 5] = newIndex; + } + + array newAttributes = { + VertexAttribute(NextPosition, positionAttributes.format, originalStride), + VertexAttribute(ScaleFactor, Float1, originalStride + positionSize), + }; + auto* g = new PrimitiveGroup(); + g->vertexOverride = data; + g->vertexCountOverride = lineVertexCount; + g->vertexDescriptionOverride = vertexDesc.appendingAttributes(newAttributes.data(), 2); + g->indicesOverride = newIndices; + g->nIndicesOverride = lineIndexCount; + g->primOverride = PrimitiveGroupType::TriList; + return g; +} + + unsigned int Mesh::addGroup(PrimitiveGroupType prim, unsigned int materialIndex, unsigned int nIndices, index32* indices) { - auto* g = new PrimitiveGroup(); - g->prim = prim; - g->materialIndex = materialIndex; + PrimitiveGroup* g; + if (prim == LineStrip || prim == LineList) + { + g = createLinePrimitiveGroup(prim == LineStrip, nIndices, indices); + } + else + { + g = new PrimitiveGroup; + g->vertexOverride = nullptr; + g->vertexCountOverride = 0; + g->indicesOverride = nullptr; + g->nIndicesOverride = 0; + g->primOverride = prim; + } g->nIndices = nIndices; g->indices = indices; + g->prim = prim; + g->materialIndex = materialIndex; return addGroup(g); } @@ -518,6 +631,27 @@ Mesh::transform(const Vector3f& translation, float scale) Map(reinterpret_cast(vdata)) = tv; } + // Scale and translate the overriden vertex values + for (i = 0; i < getGroupCount(); i++) + { + PrimitiveGroup* group = getGroup(i); + char* vdata = reinterpret_cast(group->vertexOverride); + if (!vdata) + return; + + auto vertexDesc = group->vertexDescriptionOverride; + int positionOffset = vertexDesc.getAttribute(Position).offset; + int nextPositionOffset = vertexDesc.getAttribute(NextPosition).offset; + for (int j = 0; j < group->vertexCountOverride; j++, vdata += vertexDesc.stride) + { + Vector3f tv = (Map(reinterpret_cast(vdata + positionOffset)) + translation) * scale; + Map(reinterpret_cast(vdata + positionOffset)) = tv; + + tv = (Map(reinterpret_cast(vdata + nextPositionOffset)) + translation) * scale; + Map(reinterpret_cast(vdata + nextPositionOffset)) = tv; + } + } + // Point sizes need to be scaled as well if (vertexDesc.getAttribute(PointSize).format == Float1) { diff --git a/src/celmodel/mesh.h b/src/celmodel/mesh.h index f83fb5783..d70f3d52e 100644 --- a/src/celmodel/mesh.h +++ b/src/celmodel/mesh.h @@ -42,7 +42,9 @@ class Mesh Texture2 = 7, Texture3 = 8, PointSize = 9, - SemanticMax = 10, + NextPosition = 10, + ScaleFactor = 11, + SemanticMax = 12, InvalidSemantic = -1, }; @@ -93,6 +95,8 @@ class Mesh return semanticMap[semantic]; } + VertexDescription appendingAttributes(const VertexAttribute* newAttributes, int count) const; + bool validate() const; VertexDescription& operator=(const VertexDescription&); @@ -134,6 +138,12 @@ class Mesh unsigned int materialIndex; index32* indices; unsigned int nIndices; + PrimitiveGroupType primOverride; + void* vertexOverride; + unsigned int vertexCountOverride; + index32* indicesOverride; + unsigned int nIndicesOverride; + VertexDescription vertexDescriptionOverride { 0, 0, nullptr }; }; class PickResult @@ -154,6 +164,7 @@ class Mesh bool setVertexDescription(const VertexDescription& desc); const VertexDescription& getVertexDescription() const; + PrimitiveGroup* createLinePrimitiveGroup(bool lineStrip, unsigned int nIndices, Mesh::index32* indices); const PrimitiveGroup* getGroup(unsigned int index) const; PrimitiveGroup* getGroup(unsigned int index); unsigned int addGroup(PrimitiveGroup* group);