22#include <QRandomGenerator>
51 m_instanceCount = ecdSet.
size();
52 m_instanceData.resize(m_instanceCount *
sizeof(InstanceData));
53 InstanceData *data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
55 QVector3D from(0.0f, 1.0f, 0.0f);
57 if (ecdSet.
size() > 0) {
58 qDebug() <<
"DipoleObject: First dipole raw pos:" << ecdSet[0].rd(0) << ecdSet[0].rd(1) << ecdSet[0].rd(2);
64 float unitScale = 1.0f;
65 float maxCoord = 0.0f;
68 for(
int i=0; i < ecdSet.
size(); ++i) {
69 maxCoord = std::max(maxCoord, std::abs(ecdSet[i].rd(0)));
70 maxCoord = std::max(maxCoord, std::abs(ecdSet[i].rd(1)));
71 maxCoord = std::max(maxCoord, std::abs(ecdSet[i].rd(2)));
73 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));
74 maxMag = std::max(maxMag, mag);
77 if (maxCoord > 5.0f) {
79 qDebug() <<
"DipoleObject: Detected large coordinates (max" << maxCoord <<
"), applying mm->m scale (0.001).";
82 qDebug() <<
"DipoleObject: Loading" << m_instanceCount <<
"dipoles with scale" << unitScale;
84 for (
int i = 0; i < ecdSet.
size(); ++i) {
85 const auto &dip = ecdSet[i];
87 QVector3D pos(dip.rd(0) * unitScale, dip.rd(1) * unitScale, dip.rd(2) * unitScale);
88 QVector3D Q(dip.Q(0), dip.Q(1), dip.Q(2));
89 float mag = Q.length();
91 QVector3D to = Q.normalized();
94 if (QVector3D::dotProduct(from, to) > 0.99f) {
96 }
else if (QVector3D::dotProduct(from, to) < -0.99f) {
97 rot = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 180.0f);
99 rot = QQuaternion::rotationTo(from, to);
103 float scaleFactor = (maxMag > 0.0f) ? (0.2f + 0.8f * (mag / maxMag)) : 1.0f;
108 m.scale(scaleFactor);
110 const float *mPtr = m.constData();
111 for (
int j = 0; j < 16; ++j) {
112 data[i].model[j] = mPtr[j];
116 data[i].color[0] = QRandomGenerator::global()->generateDouble();
117 data[i].color[1] = QRandomGenerator::global()->generateDouble();
118 data[i].color[2] = QRandomGenerator::global()->generateDouble();
119 data[i].color[3] = 1.0f;
122 data[i].isSelected = 0.0f;
125 if (m_instanceCount > 0) {
126 InstanceData *data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
127 float x = data[0].model[12];
128 float y = data[0].model[13];
129 float z = data[0].model[14];
130 qDebug() <<
"DipoleObject: First dipole initial pos (Scaled):" << x << y << z;
133 m_instancesDirty =
true;
138 if (m_instanceCount == 0)
return;
140 InstanceData *data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
142 for (
int i = 0; i < m_instanceCount; ++i) {
144 QMatrix4x4 currentModel;
145 const float *src = data[i].model;
146 float *dst = currentModel.data();
147 for(
int j=0; j<16; ++j) dst[j] = src[j];
154 QMatrix4x4 newModel = trans * currentModel;
157 const float *newPtr = newModel.constData();
158 for (
int j = 0; j < 16; ++j) {
159 data[i].model[j] = newPtr[j];
163 if (m_instanceCount > 0) {
165 InstanceData *data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
168 float x = data[0].model[12];
169 float y = data[0].model[13];
170 float z = data[0].model[14];
171 qDebug() <<
"DipoleObject: First dipole transformed pos (Meters):" << x << y << z;
174 m_instancesDirty =
true;
179 if (m_instanceCount == 0)
return QVector3D();
180 const InstanceData *data =
reinterpret_cast<const InstanceData*
>(m_instanceData.constData());
182 return QVector3D(data[0].model[12], data[0].model[13], data[0].model[14]);
185void DipoleObject::createGeometry()
187 if (!m_vertexData.isEmpty())
return;
193 float radius = 0.005f;
194 float height = 0.01f;
197 std::vector<VertexData> vertices;
198 std::vector<uint32_t> indices;
202 vertices.push_back(tip);
205 VertexData baseCenter = {0, 0, 0, 0, -1, 0};
206 vertices.push_back(baseCenter);
212 for (
int i = 0; i < segments; ++i) {
213 float angle = 2.0f *
M_PI * i / segments;
214 float x = radius * cos(angle);
215 float z = radius * sin(angle);
219 QVector3D n(x, radius/height, z);
222 VertexData vSide = {x, 0, z, n.x(), n.y(), n.z()};
223 vertices.push_back(vSide);
225 VertexData vBase = {x, 0, z, 0, -1, 0};
226 vertices.push_back(vBase);
230 for (
int i = 0; i < segments; ++i) {
231 int next = (i + 1) % segments;
247 vertices.push_back(tip);
248 vertices.push_back(baseCenter);
250 for (
int i=0; i<segments; ++i) {
251 float angle = 2.0f *
M_PI * i / segments;
252 float x = radius * cos(angle);
253 float z = radius * sin(angle);
255 QVector3D n(x, 0, z);
257 QVector3D slope(x, -height, z);
258 QVector3D tangent(-sin(angle), 0, cos(angle));
259 QVector3D sideNormal = QVector3D::crossProduct(tangent, slope).normalized();
262 vertices.push_back({x, 0, z, sideNormal.x(), sideNormal.y(), sideNormal.z()});
265 for (
int i=0; i<segments; ++i) {
266 float angle = 2.0f *
M_PI * i / segments;
267 float x = radius * cos(angle);
268 float z = radius * sin(angle);
269 vertices.push_back({x, 0, z, 0, -1, 0});
273 int baseStart = 2 + segments;
275 for (
int i = 0; i < segments; ++i) {
276 int next = (i + 1) % segments;
279 indices.push_back(0);
280 indices.push_back(sideStart + next);
281 indices.push_back(sideStart + i);
284 indices.push_back(1);
285 indices.push_back(baseStart + i);
286 indices.push_back(baseStart + next);
289 m_indexCount = indices.size();
291 m_vertexData.resize(vertices.size() *
sizeof(VertexData));
292 memcpy(m_vertexData.data(), vertices.data(), m_vertexData.size());
294 m_indexData.resize(indices.size() *
sizeof(uint32_t));
295 memcpy(m_indexData.data(), indices.data(), m_indexData.size());
297 m_geometryDirty =
true;
302 if (m_geometryDirty) {
303 if (!m_gpu->vertexBuffer) {
304 m_gpu->vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, m_vertexData.size()));
305 m_gpu->vertexBuffer->create();
306 qDebug() <<
"DipoleObject: Created vertex buffer size" << m_vertexData.size();
308 if (!m_gpu->indexBuffer) {
309 m_gpu->indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, m_indexData.size()));
310 m_gpu->indexBuffer->create();
311 qDebug() <<
"DipoleObject: Created index buffer size" << m_indexData.size();
313 u->uploadStaticBuffer(m_gpu->vertexBuffer.get(), m_vertexData.constData());
314 u->uploadStaticBuffer(m_gpu->indexBuffer.get(), m_indexData.constData());
315 m_geometryDirty =
false;
318 if (m_instancesDirty && m_instanceCount > 0) {
319 if (!m_gpu->instanceBuffer || m_gpu->instanceBuffer->size() < m_instanceData.size()) {
320 m_gpu->instanceBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, m_instanceData.size()));
321 m_gpu->instanceBuffer->create();
322 qDebug() <<
"DipoleObject: Created instance buffer size" << m_instanceData.size();
324 u->updateDynamicBuffer(m_gpu->instanceBuffer.get(), 0, m_instanceData.size(), m_instanceData.constData());
325 m_instancesDirty =
false;
331 if (m_instanceCount == 0)
return -1;
334 float closestDist = std::numeric_limits<float>::max();
336 const InstanceData *data =
reinterpret_cast<const InstanceData*
>(m_instanceData.constData());
340 const float baseRadius = 0.02f;
342 for (
int i = 0; i < m_instanceCount; ++i) {
344 QVector3D center(data[i].model[12], data[i].model[13], data[i].model[14]);
347 QVector3D col0(data[i].model[0], data[i].model[1], data[i].model[2]);
348 float scale = col0.length();
350 float radius = baseRadius * scale;
353 QVector3D L = center - rayOrigin;
354 float tca = QVector3D::dotProduct(L, rayDir);
356 if (tca < 0)
continue;
358 float d2 = QVector3D::dotProduct(L, L) - tca * tca;
359 if (d2 > radius * radius)
continue;
361 float thc = std::sqrt(radius * radius - d2);
362 float t0 = tca - thc;
363 float t1 = tca + thc;
369 if (t < closestDist) {
375 if (closestIdx != -1) {
385 if (index < 0 || index >= m_instanceCount)
return;
387 InstanceData *data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
389 data[index].isSelected = selected ? 1.0f : 0.0f;
391 m_instancesDirty =
true;
Instanced-arrow renderable for fitted equivalent current dipoles, driven by QRhi instancing.
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 INVLIB::InvEcdSet &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.