21#include <QRandomGenerator>
36 return {{m_vertexData.constData(), quint32(m_vertexData.size()), m_geometryGeneration},
37 {m_indexData.constData(), quint32(m_indexData.size()), m_geometryGeneration},
38 {m_instanceData.constData(), quint32(m_instanceCount *
sizeof(InstanceData)), m_instanceGeneration},
47 m_instanceCount = ecdSet.
size();
48 m_instanceData.resize(m_instanceCount *
sizeof(InstanceData));
49 m_loadedModels.resize(m_instanceCount);
50 InstanceData* data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
52 QVector3D from(0.0f, 1.0f, 0.0f);
55 for (
int i = 0; i < ecdSet.
size(); ++i) {
56 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));
57 maxMag = std::max(maxMag, mag);
60 for (
int i = 0; i < ecdSet.
size(); ++i) {
61 const auto& dip = ecdSet[i];
63 QVector3D pos(dip.rd(0), dip.rd(1), dip.rd(2));
64 QVector3D Q(dip.Q(0), dip.Q(1), dip.Q(2));
65 float mag = Q.length();
67 QVector3D to = Q.normalized();
70 if (QVector3D::dotProduct(from, to) > 0.99f) {
72 }
else if (QVector3D::dotProduct(from, to) < -0.99f) {
73 rot = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 180.0f);
75 rot = QQuaternion::rotationTo(from, to);
79 float scaleFactor = (maxMag > 0.0f) ? (0.2f + 0.8f * (mag / maxMag)) : 1.0f;
85 m_loadedModels[i] = m;
87 const float* mPtr = m.constData();
88 for (
int j = 0; j < 16; ++j) {
89 data[i].model[j] = mPtr[j];
93 data[i].color[0] = QRandomGenerator::global()->generateDouble();
94 data[i].color[1] = QRandomGenerator::global()->generateDouble();
95 data[i].color[2] = QRandomGenerator::global()->generateDouble();
96 data[i].color[3] = 1.0f;
99 data[i].isSelected = 0.0f;
102 ++m_instanceGeneration;
107 if (m_instanceCount == 0)
110 InstanceData* data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
112 for (
int i = 0; i < m_instanceCount; ++i) {
113 const QMatrix4x4 newModel = trans * m_loadedModels[i];
114 const float* newPtr = newModel.constData();
115 for (
int j = 0; j < 16; ++j) {
116 data[i].model[j] = newPtr[j];
120 ++m_instanceGeneration;
125 if (m_instanceCount == 0)
127 const InstanceData* data =
reinterpret_cast<const InstanceData*
>(m_instanceData.constData());
128 for (
int i = 0; i < m_instanceCount; ++i) {
130 const QVector3D pos(data[i].model[12], data[i].model[13], data[i].model[14]);
131 min = i == 0 ? pos : QVector3D(std::min(min.x(), pos.x()), std::min(min.y(), pos.y()), std::min(min.z(), pos.z()));
132 max = i == 0 ? pos : QVector3D(std::max(max.x(), pos.x()), std::max(max.y(), pos.y()), std::max(max.z(), pos.z()));
137void DipoleObject::createGeometry()
139 if (!m_vertexData.isEmpty())
146 float radius = 0.005f;
147 float height = 0.01f;
150 std::vector<VertexData> vertices;
151 std::vector<uint32_t> indices;
155 vertices.push_back(tip);
158 VertexData baseCenter = {0, 0, 0, 0, -1, 0};
159 vertices.push_back(baseCenter);
162 for (
int i = 0; i < segments; ++i) {
163 float angle = 2.0f * M_PI * i / segments;
164 float x = radius * cos(angle);
165 float z = radius * sin(angle);
169 QVector3D n(x, radius / height, z);
172 VertexData vSide = {x, 0, z, n.x(), n.y(), n.z()};
173 vertices.push_back(vSide);
175 VertexData vBase = {x, 0, z, 0, -1, 0};
176 vertices.push_back(vBase);
180 for (
int i = 0; i < segments; ++i) {
195 vertices.push_back(tip);
196 vertices.push_back(baseCenter);
198 for (
int i = 0; i < segments; ++i) {
199 float angle = 2.0f * M_PI * i / segments;
200 float x = radius * cos(angle);
201 float z = radius * sin(angle);
203 QVector3D n(x, 0, z);
205 QVector3D slope(x, -height, z);
206 QVector3D tangent(-sin(angle), 0, cos(angle));
207 QVector3D sideNormal = QVector3D::crossProduct(tangent, slope).normalized();
210 vertices.push_back({x, 0, z, sideNormal.x(), sideNormal.y(), sideNormal.z()});
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);
217 vertices.push_back({x, 0, z, 0, -1, 0});
221 int baseStart = 2 + segments;
223 for (
int i = 0; i < segments; ++i) {
224 int next = (i + 1) % segments;
227 indices.push_back(0);
228 indices.push_back(sideStart + next);
229 indices.push_back(sideStart + i);
232 indices.push_back(1);
233 indices.push_back(baseStart + i);
234 indices.push_back(baseStart + next);
237 m_indexCount =
static_cast<int>(indices.size());
239 m_vertexData.resize(vertices.size() *
sizeof(VertexData));
240 memcpy(m_vertexData.data(), vertices.data(), m_vertexData.size());
242 m_indexData.resize(indices.size() *
sizeof(uint32_t));
243 memcpy(m_indexData.data(), indices.data(), m_indexData.size());
245 ++m_geometryGeneration;
250 if (m_instanceCount == 0)
254 float closestDist = std::numeric_limits<float>::max();
256 const InstanceData* data =
reinterpret_cast<const InstanceData*
>(m_instanceData.constData());
260 const float baseRadius = 0.02f;
262 for (
int i = 0; i < m_instanceCount; ++i) {
264 QVector3D center(data[i].model[12], data[i].model[13], data[i].model[14]);
267 QVector3D col0(data[i].model[0], data[i].model[1], data[i].model[2]);
268 float scale = col0.length();
270 float radius = baseRadius * scale;
273 QVector3D L = center - rayOrigin;
274 float tca = QVector3D::dotProduct(L, rayDir);
279 float d2 = QVector3D::dotProduct(L, L) - tca * tca;
280 if (d2 > radius * radius)
283 float thc = std::sqrt(radius * radius - d2);
284 float t0 = tca - thc;
285 float t1 = tca + thc;
293 if (t < closestDist) {
299 if (closestIdx != -1) {
309 if (index < 0 || index >= m_instanceCount)
312 InstanceData* data =
reinterpret_cast<InstanceData*
>(m_instanceData.data());
314 data[index].isSelected = selected ? 1.0f : 0.0f;
316 ++m_instanceGeneration;
Instanced-arrow renderable for fitted equivalent current dipoles.
3-D brain visualisation using the Qt RHI rendering backend.
CPU streams of an instanced mesh: one shape (vertices, indices) drawn once per instance.
Geometry of one brain surface vertex: position and normal.
void load(const INVLIB::InvEcdSet &ecdSet)
void setSelected(int index, bool selected)
InstancedStreams streams() const
int intersect(const QVector3D &rayOrigin, const QVector3D &rayDir, float &dist) const
void applyTransform(const QMatrix4x4 &trans)
bool boundingBox(QVector3D &min, QVector3D &max) const
Holds a set of Electric Current Dipoles.