draw a sphere at a local maximum with the associated color

This commit is contained in:
Paul Leroy
2024-03-19 11:21:37 +01:00
parent e489498151
commit 99d93ef114
12 changed files with 685 additions and 0 deletions
+364
View File
@@ -0,0 +1,364 @@
#include "GrainsAsEllipsoids.h"
#include <ccPointCloud.h>
#include <QOpenGLShaderProgram>
#include <iostream>
GrainsAsEllipsoids::GrainsAsEllipsoids(ccPointCloud *cloud, ccMainAppInterface *app)
: m_cloud(cloud)
, m_app(app)
{
setShaderPath("C:/dev/CloudCompare/plugins/private/qG3POINT/shaders");
}
void GrainsAsEllipsoids::setShaderPath(const QString& path)
{
m_shaderPath = path;
}
void GrainsAsEllipsoids::releaseShaders()
{
m_program.clear();
}
bool GrainsAsEllipsoids::initProgram(QOpenGLContext* context)
{
if (m_program.isNull())
{
QString error;
if (!context)
{
assert(false);
return false;
}
m_program.reset(new QOpenGLShaderProgram(context));
// create vertex shader
// QString vertexShaderFile(m_shaderPath + "/DrawGrains.vs");
QString vertexShaderFile(m_shaderPath + "/ellipsoid.vs");
if (!m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, vertexShaderFile))
{
error = m_program->log();
ccLog::Error(error);
return false;
}
// create geometry shader
// QString geometryShaderFile(m_shaderPath + "/DrawGrains.gs");
// if (!m_program->addShaderFromSourceFile(QOpenGLShader::Geometry, geometryShaderFile))
// {
// error = m_program->log();
// ccLog::Error(error);
// return false;
// }
// create fragment shader
// QString fragmentShaderFile(m_shaderPath + "/DrawGrains.fs");
QString fragmentShaderFile(m_shaderPath + "/ellipsoid.fs");
if (!m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, fragmentShaderFile))
{
error = m_program->log();
ccLog::Error(error);
return false;
}
if (!m_program->link())
{
error = m_program->log();
ccLog::Error(error);
return false;
}
// initialize the ellipsoid instance
m_ellipsoidInstance.resize(401);
float stepTheta = M_PI / 21;
float stepPhi = 2 * M_PI / 21;
int count = 0;
CCVector3f center = *m_cloud->getPoint(m_localMaximumIndexes[0]);
m_ellipsoidInstance[count++] = CCVector3f(0, 0, 1);
for (int thetaI = 1; thetaI < 20; thetaI++)
{
for (int phiI = 0; phiI < 21; phiI++)
{
m_ellipsoidInstance[count++] = CCVector3f(center.x + sin(thetaI * stepTheta) * cos(phiI * stepPhi) * 1,
center.y + sin(thetaI * stepTheta) * sin(phiI * stepPhi) * 1,
center.z + cos(thetaI * stepTheta) * 1);
}
}
m_ellipsoidInstance[count++] = CCVector3f(0, 0, -1);
// initialize sphere
initSphereVertices();
initSphereIndexes();
buildInterleavedVertices();
}
return true;
}
void GrainsAsEllipsoids::initSphereVertices()
{
float radius = 0.2;
// clear memory of prev arrays
std::vector<float>().swap(vertices);
std::vector<float>().swap(normals);
std::vector<float>().swap(texCoords);
float x, y, z, xy; // vertex position
float nx, ny, nz, lengthInv = 1.0f / radius; // vertex normal
float s, t; // vertex texCoord
float sectorStep = 2 * M_PI / sectorCount;
float stackStep = M_PI / stackCount;
float sectorAngle, stackAngle;
for(int i = 0; i <= stackCount; ++i)
{
stackAngle = M_PI / 2 - i * stackStep; // starting from pi/2 to -pi/2
xy = radius * cosf(stackAngle); // r * cos(u)
z = radius * sinf(stackAngle); // r * sin(u)
// add (sectorCount+1) vertices per stack
// first and last vertices have same position and normal, but different tex coords
for(int j = 0; j <= sectorCount; ++j)
{
sectorAngle = j * sectorStep; // starting from 0 to 2pi
// vertex position (x, y, z)
x = xy * cosf(sectorAngle); // r * cos(u) * cos(v)
y = xy * sinf(sectorAngle); // r * cos(u) * sin(v)
vertices.push_back(x);
vertices.push_back(y);
vertices.push_back(z);
// normalized vertex normal (nx, ny, nz)
nx = x * lengthInv;
ny = y * lengthInv;
nz = z * lengthInv;
normals.push_back(nx);
normals.push_back(ny);
normals.push_back(nz);
// vertex tex coord (s, t) range between [0, 1]
s = (float)j / sectorCount;
t = (float)i / stackCount;
texCoords.push_back(s);
texCoords.push_back(t);
}
}
ccLog::Print("vertices.size() " + QString::number(vertices.size()));
}
void GrainsAsEllipsoids::initSphereIndexes()
{
// generate CCW index list of sphere triangles
// k1--k1+1
// | / |
// | / |
// k2--k2+1
int k1, k2;
for(int i = 0; i < stackCount; ++i)
{
k1 = i * (sectorCount + 1); // beginning of current stack
k2 = k1 + sectorCount + 1; // beginning of next stack
for(int j = 0; j < sectorCount; ++j, ++k1, ++k2)
{
// 2 triangles per sector excluding first and last stacks
// k1 => k2 => k1+1
if(i != 0)
{
indices.push_back(k1);
indices.push_back(k2);
indices.push_back(k1 + 1);
}
// k1+1 => k2 => k2+1
if(i != (stackCount-1))
{
indices.push_back(k1 + 1);
indices.push_back(k2);
indices.push_back(k2 + 1);
}
// store indices for lines
// vertical lines for all stacks, k1 => k2
lineIndices.push_back(k1);
lineIndices.push_back(k2);
if(i != 0) // horizontal lines except 1st stack, k1 => k+1
{
lineIndices.push_back(k1);
lineIndices.push_back(k1 + 1);
}
}
}
}
void GrainsAsEllipsoids::buildInterleavedVertices()
{
std::vector<float>().swap(interleavedVertices);
std::size_t i, j;
std::size_t count = vertices.size();
for(i = 0, j = 0; i < count; i += 3, j += 2)
{
interleavedVertices.push_back(vertices[i]);
interleavedVertices.push_back(vertices[i+1]);
interleavedVertices.push_back(vertices[i+2]);
interleavedVertices.push_back(normals[i]);
interleavedVertices.push_back(normals[i+1]);
interleavedVertices.push_back(normals[i+2]);
interleavedVertices.push_back(texCoords[j]);
interleavedVertices.push_back(texCoords[j+1]);
}
}
bool GrainsAsEllipsoids::drawSphere(CC_DRAW_CONTEXT& context, int colorIndex)
{
QOpenGLFunctions_2_1* glFunc = context.glFunctions<QOpenGLFunctions_2_1>();
assert(glFunc != nullptr);
const CCVector3f* center = m_cloud->getPoint(m_localMaximumIndexes[0]);
ccColor::Rgba color;
if (colorIndex < m_grainColors->size())
{
color = m_grainColors->data()[colorIndex];
}
else
{
ccLog::Error("[GrainsAsEllipsoids::drawSphere] index is larger than the color table");
return false;
}
// set uniforms
m_program->setUniformValue("center", center->x, center->y, center->z);
QVector4D lightPosition(0, 0, 1, 0);
QVector4D lightAmbient(0.3f, 0.3f, 0.3f, 1); // grey
QVector4D lightDiffuse(0.7f, 0.7f, 0.7f, 1); // light grey
QVector4D lightSpecular(1.0f, 1.0f, 1.0f, 1); // RGB white
// QVector4D materialAmbient(0.5f, 0.5f, 0.5f, 1);
QVector4D materialDiffuse(0.7f, 0.7f, 0.7f, 1);
QVector4D materialSpecular(0.4f, 0.4f, 0.4f, 1);
QVector4D materialAmbient(static_cast<float>(color.r) / ccColor::MAX, static_cast<float>(color.g) / ccColor::MAX, static_cast<float>(color.b) / ccColor::MAX, 1);
// QVector4D materialDiffuse(color.r / ccColor::MAX, color.g / ccColor::MAX, color.b / ccColor::MAX, 1);
// QVector4D materialSpecular(color.r / ccColor::MAX, color.g / ccColor::MAX, color.b / ccColor::MAX, 1);
float materialShininess = 16;
m_program->setUniformValue("lightPosition", lightPosition);
m_program->setUniformValue("lightAmbient", lightAmbient);
m_program->setUniformValue("lightDiffuse", lightDiffuse);
m_program->setUniformValue("lightSpecular", lightSpecular);
m_program->setUniformValue("materialAmbient", materialAmbient);
m_program->setUniformValue("materialDiffuse", materialDiffuse);
m_program->setUniformValue("materialSpecular", materialSpecular);
m_program->setUniformValue("materialShininess", materialShininess);
m_program->setAttributeArray("vertexPosition", static_cast<GLfloat*>(vertices.data()), 3);
m_program->setAttributeArray("vertexNormal", static_cast<GLfloat*>(normals.data()), 3);
m_program->setAttributeArray("vertexTexCoord", static_cast<GLfloat*>(texCoords.data()), 2);
m_program->enableAttributeArray("vertexPosition");
m_program->enableAttributeArray("vertexNormal");
m_program->enableAttributeArray("vertexTexCoord");
glFunc->glEnable(GL_PROGRAM_POINT_SIZE);
setUniformValueColor(ccColor::yellow);
glFunc->glDrawElements(GL_TRIANGLES, (unsigned int)indices.size(), GL_UNSIGNED_INT, indices.data());
m_program->disableAttributeArray("vertexPosition");
m_program->disableAttributeArray("vertexNormal");
m_program->disableAttributeArray("vertexTexCoord");
return true;
}
void GrainsAsEllipsoids::setGrainColorsTable(QSharedPointer<RGBAColorsTableType> colorTable)
{
m_grainColors = colorTable;
}
void GrainsAsEllipsoids::setUniformValueColor(const ccColor::Rgba &color)
{
m_program->setUniformValue("color", color.r, color.g, color.b, color.a);
}
void GrainsAsEllipsoids::drawGrains(CC_DRAW_CONTEXT& context)
{
if (!initProgram(context.qGLContext))
{
ccLog::Warning("[GrainsAsEllipsoids::drawGrains] impossible to init shader program");
return;
}
QOpenGLFunctions_2_1* glFunc = context.glFunctions<QOpenGLFunctions_2_1>();
assert(glFunc != nullptr);
QMatrix4x4 projection;
QMatrix4x4 modelView;
glFunc->glGetFloatv(GL_PROJECTION_MATRIX, projection.data());
glFunc->glGetFloatv(GL_MODELVIEW_MATRIX, modelView.data());
QMatrix4x4 projectionModelView = projection * modelView; // QMatrix4x4 expects row major data
m_program->bind();
// set uniforms
m_program->setUniformValue("modelViewProjectionMatrix", projectionModelView);
m_program->setUniformValue("normalLength", GLfloat(m_normalLineParameters.length));
// get the coordinates of the local maxima
std::vector<CCVector3> semiAxisLengths(m_localMaximumIndexes.size(), CCVector3(1, 1, 1));
std::vector<CCVector3f> centers(m_localMaximumIndexes.size());
// initialize the vector containing the centers of the grains
for (int index = 0; index < m_localMaximumIndexes.size(); index++)
{
centers[index] = *m_cloud->getPoint(m_localMaximumIndexes[index]);
}
if (false)
{
m_program->setAttributeArray("vertexIn", static_cast<GLfloat*>(vertices.data()), 3);
m_program->setAttributeArray("semiAxisLengths", static_cast<GLfloat*>(semiAxisLengths.front().u), 3);
m_program->enableAttributeArray("vertexIn"); // enable the vertex locations array
m_program->enableAttributeArray("semiAxisLengths"); // enable the semi-axis lenghts array
m_program->setUniformValue("center", centers[0].x, centers[0].y, centers[0].z);
glFunc->glEnable(GL_PROGRAM_POINT_SIZE);
setUniformValueColor(ccColor::yellow);
glFunc->glDrawElements(GL_TRIANGLES, (unsigned int)indices.size(), GL_UNSIGNED_INT, indices.data());
m_program->disableAttributeArray("vertexIn");
m_program->disableAttributeArray("semiAxesLengths");
}
else
{
QMatrix4x4 matrixNormal = modelView;
matrixNormal.setColumn(3, QVector4D(0,0,0,1));
m_program->setUniformValue("modelViewMatrix", modelView);
m_program->setUniformValue("normalMatrix", matrixNormal);
drawSphere(context, 0);
}
m_program->release();
m_app->redrawAll();
}
void GrainsAsEllipsoids::setLocalMaximumIndexes(const Eigen::ArrayXi &localMaximumIndexes)
{
m_localMaximumIndexes = localMaximumIndexes;
}
void GrainsAsEllipsoids::draw(CC_DRAW_CONTEXT& context)
{
drawGrains(context);
}