44#include <QRandomGenerator>
73 m_instanceCount = ecdSet.
size();
74 m_instanceData.resize(m_instanceCount *
sizeof(InstanceData));
75 InstanceData *data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
77 QVector3D from(0.0f, 1.0f, 0.0f);
79 if (ecdSet.
size() > 0) {
80 qDebug() <<
"DipoleObject: First dipole raw pos:" << ecdSet[0].rd(0) << ecdSet[0].rd(1) << ecdSet[0].rd(2);
86 float unitScale = 1.0f;
87 float maxCoord = 0.0f;
90 for(
int i=0; i < ecdSet.
size(); ++i) {
91 maxCoord = std::max(maxCoord, std::abs(ecdSet[i].rd(0)));
92 maxCoord = std::max(maxCoord, std::abs(ecdSet[i].rd(1)));
93 maxCoord = std::max(maxCoord, std::abs(ecdSet[i].rd(2)));
95 float mag = std::sqrt(std::pow(ecdSet[i].Q(0), 2) + std::pow(ecdSet[i].Q(1), 2) + std::pow(ecdSet[i].Q(2), 2));
96 maxMag = std::max(maxMag, mag);
99 if (maxCoord > 5.0f) {
101 qDebug() <<
"DipoleObject: Detected large coordinates (max" << maxCoord <<
"), applying mm->m scale (0.001).";
104 qDebug() <<
"DipoleObject: Loading" << m_instanceCount <<
"dipoles with scale" << unitScale;
106 for (
int i = 0; i < ecdSet.
size(); ++i) {
107 const auto &dip = ecdSet[i];
109 QVector3D pos(dip.rd(0) * unitScale, dip.rd(1) * unitScale, dip.rd(2) * unitScale);
110 QVector3D Q(dip.Q(0), dip.Q(1), dip.Q(2));
111 float mag = Q.length();
113 QVector3D to = Q.normalized();
116 if (QVector3D::dotProduct(from, to) > 0.99f) {
118 }
else if (QVector3D::dotProduct(from, to) < -0.99f) {
119 rot = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 180.0f);
121 rot = QQuaternion::rotationTo(from, to);
125 float scaleFactor = (maxMag > 0.0f) ? (0.2f + 0.8f * (mag / maxMag)) : 1.0f;
130 m.scale(scaleFactor);
132 const float *mPtr = m.constData();
133 for (
int j = 0; j < 16; ++j) {
134 data[i].model[j] = mPtr[j];
138 data[i].color[0] = QRandomGenerator::global()->generateDouble();
139 data[i].color[1] = QRandomGenerator::global()->generateDouble();
140 data[i].color[2] = QRandomGenerator::global()->generateDouble();
141 data[i].color[3] = 1.0f;
144 data[i].isSelected = 0.0f;
147 if (m_instanceCount > 0) {
148 InstanceData *data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
149 float x = data[0].model[12];
150 float y = data[0].model[13];
151 float z = data[0].model[14];
152 qDebug() <<
"DipoleObject: First dipole initial pos (Scaled):" << x << y << z;
155 m_instancesDirty =
true;
160 if (m_instanceCount == 0)
return;
162 InstanceData *data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
164 for (
int i = 0; i < m_instanceCount; ++i) {
166 QMatrix4x4 currentModel;
167 const float *src = data[i].model;
168 float *dst = currentModel.data();
169 for(
int j=0; j<16; ++j) dst[j] = src[j];
176 QMatrix4x4 newModel = trans * currentModel;
179 const float *newPtr = newModel.constData();
180 for (
int j = 0; j < 16; ++j) {
181 data[i].model[j] = newPtr[j];
185 if (m_instanceCount > 0) {
187 InstanceData *data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
190 float x = data[0].model[12];
191 float y = data[0].model[13];
192 float z = data[0].model[14];
193 qDebug() <<
"DipoleObject: First dipole transformed pos (Meters):" << x << y << z;
196 m_instancesDirty =
true;
201 if (m_instanceCount == 0)
return QVector3D();
202 const InstanceData *data =
reinterpret_cast<const InstanceData*
>(m_instanceData.constData());
204 return QVector3D(data[0].model[12], data[0].model[13], data[0].model[14]);
207void DipoleObject::createGeometry()
209 if (!m_vertexData.isEmpty())
return;
215 float radius = 0.005f;
216 float height = 0.01f;
219 std::vector<VertexData> vertices;
220 std::vector<uint32_t> indices;
224 vertices.push_back(tip);
227 VertexData baseCenter = {0, 0, 0, 0, -1, 0};
228 vertices.push_back(baseCenter);
234 for (
int i = 0; i < segments; ++i) {
235 float angle = 2.0f *
M_PI * i / segments;
236 float x = radius * cos(angle);
237 float z = radius * sin(angle);
241 QVector3D n(x, radius/height, z);
244 VertexData vSide = {x, 0, z, n.x(), n.y(), n.z()};
245 vertices.push_back(vSide);
247 VertexData vBase = {x, 0, z, 0, -1, 0};
248 vertices.push_back(vBase);
252 for (
int i = 0; i < segments; ++i) {
253 int next = (i + 1) % segments;
269 vertices.push_back(tip);
270 vertices.push_back(baseCenter);
272 for (
int i=0; i<segments; ++i) {
273 float angle = 2.0f *
M_PI * i / segments;
274 float x = radius * cos(angle);
275 float z = radius * sin(angle);
277 QVector3D n(x, 0, z);
279 QVector3D slope(x, -height, z);
280 QVector3D tangent(-sin(angle), 0, cos(angle));
281 QVector3D sideNormal = QVector3D::crossProduct(tangent, slope).normalized();
284 vertices.push_back({x, 0, z, sideNormal.x(), sideNormal.y(), sideNormal.z()});
287 for (
int i=0; i<segments; ++i) {
288 float angle = 2.0f *
M_PI * i / segments;
289 float x = radius * cos(angle);
290 float z = radius * sin(angle);
291 vertices.push_back({x, 0, z, 0, -1, 0});
295 int baseStart = 2 + segments;
297 for (
int i = 0; i < segments; ++i) {
298 int next = (i + 1) % segments;
301 indices.push_back(0);
302 indices.push_back(sideStart + next);
303 indices.push_back(sideStart + i);
306 indices.push_back(1);
307 indices.push_back(baseStart + i);
308 indices.push_back(baseStart + next);
311 m_indexCount = indices.size();
313 m_vertexData.resize(vertices.size() *
sizeof(VertexData));
314 memcpy(m_vertexData.data(), vertices.data(), m_vertexData.size());
316 m_indexData.resize(indices.size() *
sizeof(uint32_t));
317 memcpy(m_indexData.data(), indices.data(), m_indexData.size());
319 m_geometryDirty =
true;
324 if (m_geometryDirty) {
325 if (!m_gpu->vertexBuffer) {
326 m_gpu->vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, m_vertexData.size()));
327 m_gpu->vertexBuffer->create();
328 qDebug() <<
"DipoleObject: Created vertex buffer size" << m_vertexData.size();
330 if (!m_gpu->indexBuffer) {
331 m_gpu->indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, m_indexData.size()));
332 m_gpu->indexBuffer->create();
333 qDebug() <<
"DipoleObject: Created index buffer size" << m_indexData.size();
335 u->uploadStaticBuffer(m_gpu->vertexBuffer.get(), m_vertexData.constData());
336 u->uploadStaticBuffer(m_gpu->indexBuffer.get(), m_indexData.constData());
337 m_geometryDirty =
false;
340 if (m_instancesDirty && m_instanceCount > 0) {
341 if (!m_gpu->instanceBuffer || m_gpu->instanceBuffer->size() < m_instanceData.size()) {
342 m_gpu->instanceBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, m_instanceData.size()));
343 m_gpu->instanceBuffer->create();
344 qDebug() <<
"DipoleObject: Created instance buffer size" << m_instanceData.size();
346 u->updateDynamicBuffer(m_gpu->instanceBuffer.get(), 0, m_instanceData.size(), m_instanceData.constData());
347 m_instancesDirty =
false;
353 if (m_instanceCount == 0)
return -1;
356 float closestDist = std::numeric_limits<float>::max();
358 const InstanceData *data =
reinterpret_cast<const InstanceData*
>(m_instanceData.constData());
362 const float baseRadius = 0.02f;
364 for (
int i = 0; i < m_instanceCount; ++i) {
366 QVector3D center(data[i].model[12], data[i].model[13], data[i].model[14]);
369 QVector3D col0(data[i].model[0], data[i].model[1], data[i].model[2]);
370 float scale = col0.length();
372 float radius = baseRadius * scale;
375 QVector3D L = center - rayOrigin;
376 float tca = QVector3D::dotProduct(L, rayDir);
378 if (tca < 0)
continue;
380 float d2 = QVector3D::dotProduct(L, L) - tca * tca;
381 if (d2 > radius * radius)
continue;
383 float thc = std::sqrt(radius * radius - d2);
384 float t0 = tca - thc;
385 float t1 = tca + thc;
391 if (t < closestDist) {
397 if (closestIdx != -1) {
407 if (index < 0 || index >= m_instanceCount)
return;
409 InstanceData *data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
411 data[index].isSelected = selected ? 1.0f : 0.0f;
413 m_instancesDirty =
true;
DipoleObject class declaration.
Interleaved vertex attributes (position, normal, color, curvature) for brain surface GPU upload.
std::unique_ptr< QRhiBuffer > instanceBuffer
std::unique_ptr< QRhiBuffer > vertexBuffer
std::unique_ptr< QRhiBuffer > indexBuffer
QRhiBuffer * instanceBuffer() const
int intersect(const QVector3D &rayOrigin, const QVector3D &rayDir, float &dist) const
QVector3D debugFirstDipolePosition() const
void updateBuffers(QRhi *rhi, QRhiResourceUpdateBatch *u)
void load(const INVERSELIB::ECDSet &ecdSet)
QRhiBuffer * indexBuffer() const
void setSelected(int index, bool selected)
QRhiBuffer * vertexBuffer() const
void applyTransform(const QMatrix4x4 &trans)
Holds a set of Electric Current Dipoles.