22#include <QRandomGenerator>
47 return m_gpu->vertexBuffer.get();
51 return m_gpu->indexBuffer.get();
55 return m_gpu->instanceBuffer.get();
62 m_instanceCount = ecdSet.
size();
63 m_instanceData.resize(m_instanceCount *
sizeof(InstanceData));
64 m_loadedModels.resize(m_instanceCount);
65 InstanceData* data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
67 QVector3D from(0.0f, 1.0f, 0.0f);
70 for (
int i = 0; i < ecdSet.
size(); ++i) {
71 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));
72 maxMag = std::max(maxMag, mag);
75 for (
int i = 0; i < ecdSet.
size(); ++i) {
76 const auto& dip = ecdSet[i];
78 QVector3D pos(dip.rd(0), dip.rd(1), dip.rd(2));
79 QVector3D Q(dip.Q(0), dip.Q(1), dip.Q(2));
80 float mag = Q.length();
82 QVector3D to = Q.normalized();
85 if (QVector3D::dotProduct(from, to) > 0.99f) {
87 }
else if (QVector3D::dotProduct(from, to) < -0.99f) {
88 rot = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 180.0f);
90 rot = QQuaternion::rotationTo(from, to);
94 float scaleFactor = (maxMag > 0.0f) ? (0.2f + 0.8f * (mag / maxMag)) : 1.0f;
100 m_loadedModels[i] = m;
102 const float* mPtr = m.constData();
103 for (
int j = 0; j < 16; ++j) {
104 data[i].model[j] = mPtr[j];
108 data[i].color[0] = QRandomGenerator::global()->generateDouble();
109 data[i].color[1] = QRandomGenerator::global()->generateDouble();
110 data[i].color[2] = QRandomGenerator::global()->generateDouble();
111 data[i].color[3] = 1.0f;
114 data[i].isSelected = 0.0f;
117 m_instancesDirty =
true;
122 if (m_instanceCount == 0)
125 InstanceData* data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
127 for (
int i = 0; i < m_instanceCount; ++i) {
128 const QMatrix4x4 newModel = trans * m_loadedModels[i];
129 const float* newPtr = newModel.constData();
130 for (
int j = 0; j < 16; ++j) {
131 data[i].model[j] = newPtr[j];
135 m_instancesDirty =
true;
140 if (m_instanceCount == 0)
142 const InstanceData* data =
reinterpret_cast<const InstanceData*
>(m_instanceData.constData());
143 for (
int i = 0; i < m_instanceCount; ++i) {
145 const QVector3D pos(data[i].model[12], data[i].model[13], data[i].model[14]);
146 min = i == 0 ? pos : QVector3D(std::min(min.x(), pos.x()), std::min(min.y(), pos.y()), std::min(min.z(), pos.z()));
147 max = i == 0 ? pos : QVector3D(std::max(max.x(), pos.x()), std::max(max.y(), pos.y()), std::max(max.z(), pos.z()));
152void DipoleObject::createGeometry()
154 if (!m_vertexData.isEmpty())
161 float radius = 0.005f;
162 float height = 0.01f;
165 std::vector<VertexData> vertices;
166 std::vector<uint32_t> indices;
170 vertices.push_back(tip);
173 VertexData baseCenter = {0, 0, 0, 0, -1, 0};
174 vertices.push_back(baseCenter);
177 for (
int i = 0; i < segments; ++i) {
178 float angle = 2.0f *
M_PI * i / segments;
179 float x = radius * cos(angle);
180 float z = radius * sin(angle);
184 QVector3D n(x, radius / height, z);
187 VertexData vSide = {x, 0, z, n.x(), n.y(), n.z()};
188 vertices.push_back(vSide);
190 VertexData vBase = {x, 0, z, 0, -1, 0};
191 vertices.push_back(vBase);
195 for (
int i = 0; i < segments; ++i) {
210 vertices.push_back(tip);
211 vertices.push_back(baseCenter);
213 for (
int i = 0; i < segments; ++i) {
214 float angle = 2.0f *
M_PI * i / segments;
215 float x = radius * cos(angle);
216 float z = radius * sin(angle);
218 QVector3D n(x, 0, z);
220 QVector3D slope(x, -height, z);
221 QVector3D tangent(-sin(angle), 0, cos(angle));
222 QVector3D sideNormal = QVector3D::crossProduct(tangent, slope).normalized();
225 vertices.push_back({x, 0, z, sideNormal.x(), sideNormal.y(), sideNormal.z()});
228 for (
int i = 0; i < segments; ++i) {
229 float angle = 2.0f *
M_PI * i / segments;
230 float x = radius * cos(angle);
231 float z = radius * sin(angle);
232 vertices.push_back({x, 0, z, 0, -1, 0});
236 int baseStart = 2 + segments;
238 for (
int i = 0; i < segments; ++i) {
239 int next = (i + 1) % segments;
242 indices.push_back(0);
243 indices.push_back(sideStart + next);
244 indices.push_back(sideStart + i);
247 indices.push_back(1);
248 indices.push_back(baseStart + i);
249 indices.push_back(baseStart + next);
252 m_indexCount =
static_cast<int>(indices.size());
254 m_vertexData.resize(vertices.size() *
sizeof(VertexData));
255 memcpy(m_vertexData.data(), vertices.data(), m_vertexData.size());
257 m_indexData.resize(indices.size() *
sizeof(uint32_t));
258 memcpy(m_indexData.data(), indices.data(), m_indexData.size());
260 m_geometryDirty =
true;
265 if (m_geometryDirty) {
266 if (!m_gpu->vertexBuffer) {
267 m_gpu->vertexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, m_vertexData.size()));
268 m_gpu->vertexBuffer->create();
270 if (!m_gpu->indexBuffer) {
271 m_gpu->indexBuffer.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, m_indexData.size()));
272 m_gpu->indexBuffer->create();
274 u->uploadStaticBuffer(m_gpu->vertexBuffer.get(), m_vertexData.constData());
275 u->uploadStaticBuffer(m_gpu->indexBuffer.get(), m_indexData.constData());
276 m_geometryDirty =
false;
279 if (m_instancesDirty && m_instanceCount > 0) {
280 if (!m_gpu->instanceBuffer ||
static_cast<qsizetype
>(m_gpu->instanceBuffer->size()) < m_instanceData.size()) {
281 m_gpu->instanceBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, m_instanceData.size()));
282 m_gpu->instanceBuffer->create();
284 u->updateDynamicBuffer(m_gpu->instanceBuffer.get(), 0, m_instanceData.size(), m_instanceData.constData());
285 m_instancesDirty =
false;
291 if (m_instanceCount == 0)
295 float closestDist = std::numeric_limits<float>::max();
297 const InstanceData* data =
reinterpret_cast<const InstanceData*
>(m_instanceData.constData());
301 const float baseRadius = 0.02f;
303 for (
int i = 0; i < m_instanceCount; ++i) {
305 QVector3D center(data[i].model[12], data[i].model[13], data[i].model[14]);
308 QVector3D col0(data[i].model[0], data[i].model[1], data[i].model[2]);
309 float scale = col0.length();
311 float radius = baseRadius * scale;
314 QVector3D L = center - rayOrigin;
315 float tca = QVector3D::dotProduct(L, rayDir);
320 float d2 = QVector3D::dotProduct(L, L) - tca * tca;
321 if (d2 > radius * radius)
324 float thc = std::sqrt(radius * radius - d2);
325 float t0 = tca - thc;
326 float t1 = tca + thc;
334 if (t < closestDist) {
340 if (closestIdx != -1) {
350 if (index < 0 || index >= m_instanceCount)
353 InstanceData* data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
355 data[index].isSelected = selected ? 1.0f : 0.0f;
357 m_instancesDirty =
true;
Instanced-arrow renderable for fitted equivalent current dipoles, driven by QRhi instancing.
3-D brain visualisation using the Qt RHI rendering backend.
Interleaved vertex attributes (position, normal, color, curvature) for brain surface GPU upload.
std::unique_ptr< QRhiBuffer > instanceBuffer
std::unique_ptr< QRhiBuffer > indexBuffer
std::unique_ptr< QRhiBuffer > vertexBuffer
QRhiBuffer * indexBuffer() const
void load(const INVLIB::InvEcdSet &ecdSet)
void setSelected(int index, bool selected)
void updateBuffers(QRhi *rhi, QRhiResourceUpdateBatch *u)
int intersect(const QVector3D &rayOrigin, const QVector3D &rayDir, float &dist) const
void applyTransform(const QMatrix4x4 &trans)
QRhiBuffer * vertexBuffer() const
bool boundingBox(QVector3D &min, QVector3D &max) const
QRhiBuffer * instanceBuffer() const
Holds a set of Electric Current Dipoles.