1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "shapemanager.h"
5#include <QtQuick3DParticles/private/qquick3dparticleshapedatautils_p.h>
6
7#include <QtCore/qdir.h>
8#include <QCborStreamWriter>
9#include <QCborStreamReader>
10#include <QRandomGenerator>
11
12ShapeManager::ShapeManager(QObject *parent) : QObject(parent)
13{
14
15}
16
17void ShapeManager::setImage(const QString &filename)
18{
19 if (m_imageFilename == filename)
20 return;
21
22 m_imageFilename = filename;
23 loadImage();
24}
25
26void ShapeManager::setDepth(float depth)
27{
28 m_depth = depth;
29}
30
31void ShapeManager::setScale(float scale)
32{
33 m_scale = scale;
34}
35
36// Set the amount of positions CBOR should contain.
37// When -1, then amount is same as the amount of pixels in the image.
38// Default value -1.
39void ShapeManager::setAmount(int amount)
40{
41 m_amount = amount;
42}
43
44void ShapeManager::setSortingMode(SortingMode mode)
45{
46 m_sortingMode = mode;
47}
48
49void ShapeManager::setSortingPosition(const QVector3D &position)
50{
51 m_sortingPosition = position;
52}
53
54bool ShapeManager::loadImage()
55{
56 QFileInfo fileInfo(m_imageFilename);
57
58 // Is this a real file?
59 if (!fileInfo.exists()) {
60 qWarning() << "Imagefile not found:" << qPrintable(m_imageFilename);
61 return false;
62 }
63
64 if (!m_image.load(fileName: m_imageFilename)) {
65 qWarning() << "Not able to load image:" << qPrintable(m_imageFilename);
66 return false;
67 }
68
69 // Make sure image is in proper format
70 if (m_image.format() != QImage::Format_ARGB32 && m_image.format() != QImage::Format_ARGB32_Premultiplied)
71 m_image = m_image.convertToFormat(f: QImage::Format_ARGB32_Premultiplied);
72
73 // Flip the image
74 m_image.mirror();
75
76 return true;
77}
78
79bool ShapeManager::generateData()
80{
81 if (m_image.isNull()) {
82 qWarning() << "Can't generate data, no image loaded";
83 return false;
84 }
85
86 QRect r(0, 0, m_image.width(), m_image.height());
87 // resample on the fly using 16-bit
88 int sx = (m_image.width() << 16) / r.width();
89 int sy = (m_image.height() << 16) / r.height();
90 int w = r.width();
91 int h = r.height();
92 for (int y = 0; y < h; ++y) {
93 const uint *sl = (const uint *) m_image.constScanLine((y * sy) >> 16);
94 for (int x = 0; x < w; ++x) {
95 if (sl[(x * sx) >> 16] & 0xff000000) {
96 QVector3D pos(x - (w * 0.5f), y - (h * 0.5f), 0.0f);
97 pos *= m_scale;
98 // Get random z based on depth
99 float posZ = float(QRandomGenerator::global()->generateDouble() - 0.5f) * m_depth * m_scale;
100 pos.setZ(posZ);
101 m_data << pos;
102 }
103 }
104 }
105 // Shuffle the data into random order
106 auto rd = std::random_device{};
107 std::shuffle(first: m_data.begin(),
108 last: m_data.end(),
109 g: std::default_random_engine(rd()));
110
111 return true;
112}
113
114bool ShapeManager::saveShapeData(const QString &filename)
115{
116 m_outputData.clear();
117
118 QFile dataFile(filename);
119 if (!dataFile.open(flags: QIODevice::WriteOnly)) {
120 // Invalid file
121 qWarning() << "Unable to open file:" << filename;
122 return false;
123 }
124
125 QCborStreamWriter writer(&dataFile);
126 QQuick3DParticleShapeDataUtils::writeShapeHeader(writer);
127
128 // Start positions array
129 writer.startArray();
130
131 // Collect correct amount of positions
132 if (m_amount < 0) {
133 m_outputData = m_data;
134 } else {
135 // m_data is shuffled so we can just pick m_amount from there
136 int index = 0;
137 while (m_outputData.size() < m_amount) {
138 m_outputData << m_data[index];
139 index = index < (m_data.size() - 1) ? index + 1 : 0;
140 }
141 }
142
143 // Sort
144 if (m_sortingMode == SortingMode::DistanceClosestFirst) {
145 std::sort(first: m_outputData.begin(),
146 last: m_outputData.end(),
147 comp: [this](const QVector3D& lhs, const QVector3D& rhs) {
148 return m_sortingPosition.distanceToPoint(point: lhs) < m_sortingPosition.distanceToPoint(point: rhs);
149 });
150 } else if (m_sortingMode == SortingMode::DistanceClosestLast) {
151 std::sort(first: m_outputData.begin(),
152 last: m_outputData.end(),
153 comp: [this](const QVector3D& lhs, const QVector3D& rhs) {
154 return m_sortingPosition.distanceToPoint(point: rhs) < m_sortingPosition.distanceToPoint(point: lhs);
155 });
156 }
157
158 // Write positions
159 for (auto position : m_outputData)
160 QQuick3DParticleShapeDataUtils::writeValue(writer, value: position);
161
162 // Leave positions array
163 writer.endArray();
164
165 // Leave root array
166 writer.endArray();
167
168 return true;
169}
170
171void ShapeManager::dumpOutput()
172{
173 qDebug() << "Particle Shape";
174 qDebug() << m_outputData;
175 qDebug() << "Image positions:" << m_data.size();
176 qDebug() << "Generated positions:" << m_outputData.size();
177}
178

source code of qtquick3d/tools/shapegen/shapemanager.cpp