Branch data Line data Source code
1 : : #pragma once
2 : :
3 : : /*
4 : : MIT License
5 : :
6 : : Copyright (c) 2018-2024 Stephane Cuillerdier (aka aiekick)
7 : :
8 : : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : : of this software and associated documentation files (the "Software"), to deal
10 : : in the Software without restriction, including without limitation the rights
11 : : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : : copies of the Software, and to permit persons to whom the Software is
13 : : furnished to do so, subject to the following conditions:
14 : :
15 : : The above copyright notice and this permission notice shall be included in all
16 : : copies or substantial portions of the Software.
17 : :
18 : : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : : SOFTWARE.
25 : : */
26 : :
27 : : // ezVox is part of the ezLibs project : https://github.com/aiekick/ezLibs.git
28 : :
29 : : #include <map>
30 : : #include <cmath>
31 : : #include <array>
32 : : #include <string>
33 : : #include <vector>
34 : : #include <chrono>
35 : : #include <memory>
36 : : #include <cstdint>
37 : : #include <sstream>
38 : : #include <iostream>
39 : : #include <functional>
40 : :
41 : : #include "ezMath.hpp"
42 : : #include "ezStr.hpp"
43 : :
44 : : // This File is a helper for write a vox file after 0.99 release to support
45 : : // the world mode editor
46 : : // just add all color with the color Index with AddColor
47 : : // And add all voxels with the method AddVoxel with the voxel in world position, and finally save the model
48 : : // that's all, the file was initially created for my Proecedural soft
49 : : // it support just my needs for the moment, but i put here because its a basis for more i thinck
50 : :
51 : : namespace ez {
52 : : namespace file {
53 : :
54 : : namespace vox {
55 : : typedef uint32_t KeyFrame;
56 : :
57 : : typedef size_t CubeX;
58 : : typedef size_t CubeY;
59 : : typedef size_t CubeZ;
60 : : typedef size_t CubeID;
61 : : typedef size_t VoxelX;
62 : : typedef size_t VoxelY;
63 : : typedef size_t VoxelZ;
64 : : typedef size_t VoxelID;
65 : : typedef int32_t TagID;
66 : : typedef int32_t Version;
67 : : typedef int32_t ColorID;
68 : :
69 : : typedef ez::dAABBCC Volume;
70 : :
71 : : typedef std::function<void(const KeyFrame& vKeyFrame, const double& vValue)> KeyFrameTimeLoggingFunctor;
72 : :
73 : 200 : inline uint32_t GetMVID(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
74 : 200 : return (a) | (b << 8) | (c << 16) | (d << 24);
75 : 200 : }
76 : :
77 : : struct DICTstring {
78 : : int32_t bufferSize = 0;
79 : : std::string buffer;
80 : :
81 : 198 : DICTstring() = default;
82 : :
83 : 198 : void write(FILE* fp) {
84 : 198 : bufferSize = (int32_t)buffer.size();
85 : 198 : fwrite(&bufferSize, sizeof(int32_t), 1, fp);
86 : 198 : fwrite(buffer.data(), sizeof(char), bufferSize, fp);
87 : 198 : }
88 : 198 : size_t getSize() {
89 : 198 : bufferSize = (int32_t)buffer.size();
90 : 198 : return sizeof(int32_t) + sizeof(char) * bufferSize;
91 : 198 : }
92 : : };
93 : :
94 : : struct DICTitem {
95 : : DICTstring key;
96 : : DICTstring value;
97 : :
98 : : DICTitem() = default;
99 : 99 : DICTitem(std::string vKey, std::string vValue) {
100 : 99 : key.buffer = vKey;
101 : 99 : value.buffer = vValue;
102 : 99 : }
103 : :
104 : 99 : void write(FILE* fp) {
105 : 99 : key.write(fp);
106 : 99 : value.write(fp);
107 : 99 : }
108 : :
109 : 99 : size_t getSize() {
110 : 99 : return key.getSize() + value.getSize();
111 : 99 : }
112 : : };
113 : :
114 : : struct DICT {
115 : : int32_t count = 0;
116 : : std::vector<DICTitem> keys;
117 : :
118 : 120 : DICT() = default;
119 : :
120 : 120 : void write(FILE* fp) {
121 : 120 : count = (int32_t)keys.size();
122 : 120 : fwrite(&count, sizeof(int32_t), 1, fp);
123 [ + + ]: 120 : for (auto& key : keys) {
124 : 99 : key.write(fp);
125 : 99 : }
126 : 120 : }
127 : :
128 : 120 : size_t getSize() {
129 : 120 : size_t s = sizeof(int32_t);
130 [ + + ]: 120 : for (auto& key : keys) {
131 : 99 : s += key.getSize();
132 : 99 : }
133 : 120 : return s;
134 : 120 : }
135 : :
136 : 99 : void Add(std::string vKey, std::string vValue) {
137 : 99 : keys.push_back(DICTitem(vKey, vValue));
138 : 99 : }
139 : : };
140 : :
141 : : struct nTRN {
142 : : int32_t nodeId = 0;
143 : : DICT nodeAttribs;
144 : : int32_t childNodeId = 0;
145 : : int32_t reservedId = -1;
146 : : int32_t layerId = -1;
147 : : int32_t numFrames = 1;
148 : : std::vector<DICT> frames;
149 : :
150 : 10 : nTRN(int32_t countFrames) {
151 : 10 : numFrames = countFrames;
152 : 10 : frames.resize(static_cast<size_t>(numFrames));
153 : 10 : }
154 : :
155 : 10 : void write(FILE* fp) {
156 : : // chunk header
157 : 10 : int32_t id = GetMVID('n', 'T', 'R', 'N');
158 : 10 : fwrite(&id, sizeof(int32_t), 1, fp);
159 : 10 : size_t contentSize = getSize();
160 : 10 : fwrite(&contentSize, sizeof(int32_t), 1, fp);
161 : 10 : size_t childSize = 0;
162 : 10 : fwrite(&childSize, sizeof(int32_t), 1, fp);
163 : :
164 : : // datas's
165 : 10 : fwrite(&nodeId, sizeof(int32_t), 1, fp);
166 : 10 : nodeAttribs.write(fp);
167 : 10 : fwrite(&childNodeId, sizeof(int32_t), 1, fp);
168 : 10 : fwrite(&reservedId, sizeof(int32_t), 1, fp);
169 : 10 : fwrite(&layerId, sizeof(int32_t), 1, fp);
170 : 10 : fwrite(&numFrames, sizeof(int32_t), 1, fp);
171 [ + + ]: 10 : for (auto& frame : frames) {
172 : 10 : frame.write(fp);
173 : 10 : }
174 : 10 : }
175 : :
176 : 10 : size_t getSize() {
177 : 10 : size_t s = sizeof(int32_t) * 5 + nodeAttribs.getSize();
178 [ + + ]: 10 : for (auto& frame : frames) {
179 : 10 : s += frame.getSize();
180 : 10 : }
181 : 10 : return s;
182 : 10 : }
183 : : };
184 : :
185 : : struct nGRP {
186 : : int32_t nodeId = 0;
187 : : DICT nodeAttribs;
188 : : int32_t nodeChildrenNodes;
189 : : std::vector<int32_t> childNodes;
190 : :
191 : 1 : nGRP(int32_t vCount) {
192 : 1 : nodeChildrenNodes = vCount;
193 : 1 : childNodes.resize(nodeChildrenNodes);
194 : 1 : }
195 : :
196 : 1 : void write(FILE* fp) {
197 : : // chunk header
198 : 1 : int32_t id = GetMVID('n', 'G', 'R', 'P');
199 : 1 : fwrite(&id, sizeof(int32_t), 1, fp);
200 : 1 : size_t contentSize = getSize();
201 : 1 : fwrite(&contentSize, sizeof(int32_t), 1, fp);
202 : 1 : size_t childSize = 0;
203 : 1 : fwrite(&childSize, sizeof(int32_t), 1, fp);
204 : :
205 : : // datas's
206 : 1 : fwrite(&nodeId, sizeof(int32_t), 1, fp);
207 : 1 : nodeAttribs.write(fp);
208 : 1 : fwrite(&nodeChildrenNodes, sizeof(int32_t), 1, fp);
209 : 1 : fwrite(childNodes.data(), sizeof(int32_t), nodeChildrenNodes, fp);
210 : 1 : }
211 : :
212 : 1 : size_t getSize() {
213 : 1 : return sizeof(int32_t) * (2 + nodeChildrenNodes) + nodeAttribs.getSize();
214 : 1 : }
215 : : };
216 : :
217 : : struct MODEL {
218 : : int32_t modelId = 0;
219 : : DICT modelAttribs;
220 : :
221 : 90 : MODEL() = default;
222 : :
223 : 90 : void write(FILE* fp) {
224 : 90 : fwrite(&modelId, sizeof(int32_t), 1, fp);
225 : 90 : modelAttribs.write(fp);
226 : 90 : }
227 : :
228 : 90 : size_t getSize() {
229 : 90 : return sizeof(int32_t) + modelAttribs.getSize();
230 : 90 : }
231 : : };
232 : :
233 : : struct nSHP {
234 : : int32_t nodeId = 0;
235 : : DICT nodeAttribs;
236 : : int32_t numModels;
237 : : std::vector<MODEL> models;
238 : :
239 : 9 : nSHP(int32_t vCount) {
240 : 9 : numModels = vCount;
241 : 9 : models.resize(numModels);
242 : 9 : }
243 : :
244 : 9 : void write(FILE* fp) {
245 : : // chunk header
246 : 9 : int32_t id = GetMVID('n', 'S', 'H', 'P');
247 : 9 : fwrite(&id, sizeof(int32_t), 1, fp);
248 : 9 : size_t contentSize = getSize();
249 : 9 : fwrite(&contentSize, sizeof(int32_t), 1, fp);
250 : 9 : size_t childSize = 0;
251 : 9 : fwrite(&childSize, sizeof(int32_t), 1, fp);
252 : :
253 : : // datas's
254 : 9 : fwrite(&nodeId, sizeof(int32_t), 1, fp);
255 : 9 : nodeAttribs.write(fp);
256 : 9 : fwrite(&numModels, sizeof(int32_t), 1, fp);
257 [ + + ]: 90 : for (auto& model : models) {
258 : 90 : model.write(fp);
259 : 90 : }
260 : 9 : }
261 : :
262 : 9 : size_t getSize() {
263 : 9 : size_t s = sizeof(int32_t) * 2 + nodeAttribs.getSize();
264 [ + + ]: 90 : for (auto& model : models) {
265 : 90 : s += model.getSize();
266 : 90 : }
267 : 9 : return s;
268 : 9 : }
269 : : };
270 : :
271 : : struct LAYR {
272 : : int32_t nodeId = 0;
273 : : int32_t reservedId = -1;
274 : : DICT nodeAttribs;
275 : :
276 : : LAYR() = default;
277 : :
278 : 0 : void write(FILE* fp) {
279 : 0 : // chunk header
280 : 0 : int32_t id = GetMVID('L', 'A', 'Y', 'R');
281 : 0 : fwrite(&id, sizeof(int32_t), 1, fp);
282 : 0 : size_t contentSize = getSize();
283 : 0 : fwrite(&contentSize, sizeof(int32_t), 1, fp);
284 : 0 : size_t childSize = 0;
285 : 0 : fwrite(&childSize, sizeof(int32_t), 1, fp);
286 : 0 :
287 : 0 : // datas's
288 : 0 : fwrite(&nodeId, sizeof(int32_t), 1, fp);
289 : 0 : nodeAttribs.write(fp);
290 : 0 : fwrite(&reservedId, sizeof(int32_t), 1, fp);
291 : 0 : }
292 : :
293 : 0 : size_t getSize() {
294 : 0 : return sizeof(int32_t) * 2 + nodeAttribs.getSize();
295 : 0 : }
296 : : };
297 : :
298 : : struct SIZE {
299 : : int32_t sizex = 0;
300 : : int32_t sizey = 0;
301 : : int32_t sizez = 0;
302 : :
303 : 9 : SIZE() = default;
304 : :
305 : 90 : void write(FILE* fp) {
306 : : // chunk header
307 : 90 : int32_t id = GetMVID('S', 'I', 'Z', 'E');
308 : 90 : fwrite(&id, sizeof(int32_t), 1, fp);
309 : 90 : size_t contentSize = getSize();
310 : 90 : fwrite(&contentSize, sizeof(int32_t), 1, fp);
311 : 90 : size_t childSize = 0;
312 : 90 : fwrite(&childSize, sizeof(int32_t), 1, fp);
313 : :
314 : : // datas's
315 : 90 : fwrite(&sizex, sizeof(int32_t), 1, fp);
316 : 90 : fwrite(&sizey, sizeof(int32_t), 1, fp);
317 : 90 : fwrite(&sizez, sizeof(int32_t), 1, fp);
318 : 90 : }
319 : :
320 : 90 : size_t getSize() {
321 : 90 : return sizeof(int32_t) * 3;
322 : 90 : }
323 : : };
324 : :
325 : : struct XYZI {
326 : : int32_t numVoxels = 0;
327 : : std::vector<uint8_t> voxels;
328 : :
329 : 90 : XYZI() = default;
330 : :
331 : 90 : void write(FILE* fp) {
332 : : // chunk header
333 : 90 : int32_t id = GetMVID('X', 'Y', 'Z', 'I');
334 : 90 : fwrite(&id, sizeof(int32_t), 1, fp);
335 : 90 : size_t contentSize = getSize();
336 : 90 : fwrite(&contentSize, sizeof(int32_t), 1, fp);
337 : 90 : size_t childSize = 0;
338 : 90 : fwrite(&childSize, sizeof(int32_t), 1, fp);
339 : :
340 : : // datas's
341 : 90 : fwrite(&numVoxels, sizeof(int32_t), 1, fp);
342 : 90 : fwrite(voxels.data(), sizeof(uint8_t), voxels.size(), fp);
343 : 90 : }
344 : :
345 : 90 : size_t getSize() {
346 : 90 : numVoxels = (int32_t)voxels.size() / 4;
347 : 90 : return sizeof(int32_t) * (1 + numVoxels);
348 : 90 : }
349 : : };
350 : :
351 : : struct RGBA {
352 : : std::array<int32_t, 256> colors{};
353 : :
354 : 0 : RGBA() = default;
355 : :
356 : 0 : void write(FILE* fp) {
357 : : // chunk header
358 : 0 : int32_t id = GetMVID('R', 'G', 'B', 'A');
359 : 0 : fwrite(&id, sizeof(int32_t), 1, fp);
360 : 0 : size_t contentSize = getSize();
361 : 0 : fwrite(&contentSize, sizeof(int32_t), 1, fp);
362 : 0 : size_t childSize = 0;
363 : 0 : fwrite(&childSize, sizeof(int32_t), 1, fp);
364 : :
365 : : // datas's
366 : 0 : fwrite(colors.data(), sizeof(uint8_t), contentSize, fp);
367 : 0 : }
368 : :
369 : 0 : size_t getSize() {
370 : 0 : return sizeof(int32_t) * colors.size();
371 : 0 : }
372 : : };
373 : :
374 : : struct VoxCube {
375 : : int id = 0;
376 : :
377 : : // translate
378 : : int tx = 0;
379 : : int ty = 0;
380 : : int tz = 0;
381 : :
382 : : SIZE size;
383 : : std::map<KeyFrame, XYZI> xyzis;
384 : :
385 : 9 : VoxCube() = default;
386 : :
387 : 9 : void write(FILE* fp) {
388 [ + + ]: 90 : for (auto& xyzi : xyzis) {
389 : 90 : size.write(fp);
390 : 90 : xyzi.second.write(fp);
391 : 90 : }
392 : 9 : }
393 : : };
394 : :
395 : : class Writer {
396 : : private:
397 : 9 : static const uint32_t GetID(const uint8_t& a, const uint8_t& b, const uint8_t& c, const uint8_t& d) {
398 : 9 : return (a) | (b << 8) | (c << 16) | (d << 24);
399 : 9 : }
400 : :
401 : : private:
402 : : Version MV_VERSION = 150; // the old version of MV not open another file than if version is 150 (answer by @ephtracy)
403 : :
404 : : TagID ID_VOX = GetID('V', 'O', 'X', ' ');
405 : : TagID ID_PACK = GetID('P', 'A', 'C', 'K');
406 : : TagID ID_MAIN = GetID('M', 'A', 'I', 'N');
407 : : TagID ID_SIZE = GetID('S', 'I', 'Z', 'E');
408 : : TagID ID_XYZI = GetID('X', 'Y', 'Z', 'I');
409 : : TagID ID_RGBA = GetID('R', 'G', 'B', 'A');
410 : : TagID ID_NTRN = GetID('n', 'T', 'R', 'N');
411 : : TagID ID_NGRP = GetID('n', 'G', 'R', 'P');
412 : : TagID ID_NSHP = GetID('n', 'S', 'H', 'P');
413 : :
414 : : VoxelX m_MaxVoxelPerCubeX = 0;
415 : : VoxelY m_MaxVoxelPerCubeY = 0;
416 : : VoxelZ m_MaxVoxelPerCubeZ = 0;
417 : :
418 : : CubeID maxCubeId = 0;
419 : : CubeX minCubeX = (CubeX)1e7;
420 : : CubeY minCubeY = (CubeY)1e7;
421 : : CubeZ minCubeZ = (CubeZ)1e7;
422 : :
423 : : FILE* m_file = nullptr;
424 : :
425 : : Volume maxVolume = Volume(1e7, -1e7);
426 : :
427 : : KeyFrame m_KeyFrame = 0;
428 : :
429 : : std::vector<ColorID> colors;
430 : :
431 : : std::vector<VoxCube> cubes;
432 : :
433 : : std::map<CubeX, std::map<CubeY, std::map<CubeZ, CubeID>>> cubesId;
434 : : std::map<KeyFrame, std::map<VoxelX, std::map<VoxelY, std::map<VoxelZ, VoxelID>>>> voxelId;
435 : :
436 : : int32_t lastError = 0;
437 : :
438 : : bool m_TimeLoggingEnabled = false; // for log elapsed time between key frames and total
439 : :
440 : : std::chrono::steady_clock::time_point m_StartTime;
441 : : std::chrono::steady_clock::time_point m_LastKeyFrameTime;
442 : : std::map<KeyFrame, double> m_FrameTimes;
443 : : double m_TotalTime = 0.0;
444 : :
445 : : KeyFrameTimeLoggingFunctor m_KeyFrameTimeLoggingFunctor;
446 : :
447 : : public:
448 : : //////////////////////////////////////////////////////////////////
449 : : // the limit of magicavoxel is 127 for one cube, is 127 voxels (indexs : 0 -> 126)
450 : : // vMaxVoxelPerCubeX,Y,Z define the limit of one cube
451 : 1 : Writer(const VoxelX& vMaxVoxelPerCubeX = 126, const VoxelY& vMaxVoxelPerCubeY = 126, const VoxelZ& vMaxVoxelPerCubeZ = 126) {
452 : : // the limit of magicavoxel is 127 because the first voxel is 1 not 0
453 : : // so this is 0 to 126
454 : : // index limit, size is 127
455 : 1 : m_MaxVoxelPerCubeX = ez::clamp<size_t>(vMaxVoxelPerCubeX, 0, 126);
456 : 1 : m_MaxVoxelPerCubeY = ez::clamp<size_t>(vMaxVoxelPerCubeY, 0, 126);
457 : 1 : m_MaxVoxelPerCubeZ = ez::clamp<size_t>(vMaxVoxelPerCubeZ, 0, 126);
458 : 1 : }
459 : :
460 : 1 : ~Writer() = default;
461 : :
462 : 0 : Writer& clear() {
463 : 0 : clearVoxels();
464 : 0 : clearColors();
465 : 0 : return *this;
466 : 0 : }
467 : :
468 : 0 : Writer& clearVoxels() {
469 : 0 : cubes.clear();
470 : 0 : cubesId.clear();
471 : 0 : voxelId.clear();
472 : 0 : return *this;
473 : 0 : }
474 : :
475 : 0 : Writer& clearColors() {
476 : 0 : colors.clear();
477 : 0 : return *this;
478 : 0 : }
479 : :
480 : 1 : Writer& startTimeLogging() {
481 : 1 : m_TimeLoggingEnabled = true;
482 : 1 : m_StartTime = std::chrono::steady_clock::now();
483 : 1 : m_LastKeyFrameTime = m_StartTime;
484 : 1 : return *this;
485 : 1 : };
486 : :
487 : 1 : Writer& stopTimeLogging() {
488 [ + - ]: 1 : if (m_TimeLoggingEnabled) {
489 : 1 : const auto now = std::chrono::steady_clock::now();
490 : 1 : m_FrameTimes[m_KeyFrame] = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_LastKeyFrameTime).count() * 1e-3;
491 [ + - ]: 1 : if (m_KeyFrameTimeLoggingFunctor) {
492 : 1 : m_KeyFrameTimeLoggingFunctor(m_KeyFrame, m_FrameTimes.at(m_KeyFrame));
493 : 1 : }
494 : 1 : m_TotalTime = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_StartTime).count() * 1e-3;
495 : 1 : m_TimeLoggingEnabled = false;
496 : 1 : }
497 : 1 : return *this;
498 : 1 : }
499 : :
500 : 1 : Writer& setKeyFrameTimeLoggingFunctor(const KeyFrameTimeLoggingFunctor& vKeyFrameTimeLoggingFunctor) {
501 : 1 : m_KeyFrameTimeLoggingFunctor = vKeyFrameTimeLoggingFunctor;
502 : 1 : return *this;
503 : 1 : }
504 : :
505 : 10 : Writer& setKeyFrame(uint32_t vKeyFrame) {
506 [ + + ]: 10 : if (m_KeyFrame != vKeyFrame) {
507 [ + - ]: 9 : if (m_TimeLoggingEnabled) {
508 : 9 : const auto now = std::chrono::steady_clock::now();
509 : 9 : const auto elapsed = now - m_LastKeyFrameTime;
510 : 9 : m_FrameTimes[m_KeyFrame] = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count() * 1e-3;
511 [ + - ]: 9 : if (m_KeyFrameTimeLoggingFunctor) {
512 : 9 : m_KeyFrameTimeLoggingFunctor(m_KeyFrame, m_FrameTimes.at(m_KeyFrame));
513 : 9 : }
514 : 9 : m_LastKeyFrameTime = now;
515 : 9 : }
516 : 9 : m_KeyFrame = vKeyFrame;
517 : 9 : }
518 : 10 : return *this;
519 : 10 : }
520 : :
521 : 0 : Writer& addColor(const uint8_t& r, const uint8_t& g, const uint8_t& b, const uint8_t& a, const uint8_t& index) {
522 : 0 : while (colors.size() <= index)
523 : 0 : colors.push_back(0);
524 : 0 : colors[index] = GetID(r, g, b, a);
525 : 0 : return *this;
526 : 0 : }
527 : :
528 : 1428840 : Writer& addVoxel(const size_t& vX, const size_t& vY, const size_t& vZ, const uint8_t& vColorIndex) {
529 : : // cube pos
530 : 1428840 : size_t ox = (size_t)std::floor((double)vX / (double)m_MaxVoxelPerCubeX);
531 : 1428840 : size_t oy = (size_t)std::floor((double)vY / (double)m_MaxVoxelPerCubeY);
532 : 1428840 : size_t oz = (size_t)std::floor((double)vZ / (double)m_MaxVoxelPerCubeZ);
533 : :
534 : 1428840 : minCubeX = ez::mini<size_t>(minCubeX, ox);
535 : 1428840 : minCubeY = ez::mini<size_t>(minCubeX, oy);
536 : 1428840 : minCubeZ = ez::mini<size_t>(minCubeX, oz);
537 : :
538 : 1428840 : auto cube = m_GetCube(ox, oy, oz);
539 : :
540 : 1428840 : m_MergeVoxelInCube(vX, vY, vZ, vColorIndex, cube);
541 : 1428840 : return *this;
542 : 1428840 : }
543 : :
544 : 1 : Writer& save(const std::string& vFilePathName) {
545 [ + - ]: 1 : if (m_OpenFileForWriting(vFilePathName)) {
546 : 1 : int32_t zero = 0;
547 : :
548 : 1 : fwrite(&ID_VOX, sizeof(int32_t), 1, m_file);
549 : 1 : fwrite(&MV_VERSION, sizeof(int32_t), 1, m_file);
550 : :
551 : : // MAIN CHUNCK
552 : 1 : fwrite(&ID_MAIN, sizeof(int32_t), 1, m_file);
553 : 1 : fwrite(&zero, sizeof(int32_t), 1, m_file);
554 : :
555 : 1 : long numBytesMainChunkPos = m_GetFilePos();
556 : 1 : fwrite(&zero, sizeof(int32_t), 1, m_file);
557 : :
558 : 1 : long headerSize = m_GetFilePos();
559 : :
560 : 1 : int count = (int)cubes.size();
561 : :
562 : 1 : int nodeIds = 0;
563 : 1 : nTRN rootTransform(1);
564 : 1 : rootTransform.nodeId = nodeIds;
565 : 1 : rootTransform.childNodeId = ++nodeIds;
566 : :
567 : 1 : nGRP rootGroup(count);
568 : 1 : rootGroup.nodeId = nodeIds; //
569 : 1 : rootGroup.nodeChildrenNodes = count;
570 : :
571 : 1 : std::vector<nSHP> shapes;
572 : 1 : std::vector<nTRN> shapeTransforms;
573 : 1 : size_t cube_idx = 0U;
574 : 1 : int32_t model_id = 0U;
575 [ + + ]: 9 : for (auto& cube : cubes) {
576 : 9 : cube.write(m_file);
577 : :
578 : : // trans
579 : 9 : nTRN trans(1); // not a trans anim so ony one frame
580 : 9 : trans.nodeId = ++nodeIds; //
581 : 9 : rootGroup.childNodes[cube_idx] = nodeIds;
582 : 9 : trans.childNodeId = ++nodeIds;
583 : 9 : trans.layerId = 0;
584 : 9 : cube.tx = (int)std::floor((cube.tx - minCubeX + 0.5f) * m_MaxVoxelPerCubeX - maxVolume.lowerBound.x - maxVolume.Size().x * 0.5);
585 : 9 : cube.ty = (int)std::floor((cube.ty - minCubeY + 0.5f) * m_MaxVoxelPerCubeY - maxVolume.lowerBound.y - maxVolume.Size().y * 0.5);
586 : 9 : cube.tz = (int)std::floor((cube.tz - minCubeZ + 0.5f) * m_MaxVoxelPerCubeZ);
587 : 9 : trans.frames[0].Add("_t", ez::str::toStr(cube.tx) + " " + ez::str::toStr(cube.ty) + " " + ez::str::toStr(cube.tz));
588 : 9 : shapeTransforms.push_back(trans);
589 : :
590 : : // shape
591 : 9 : nSHP shape((int32_t)cube.xyzis.size());
592 : 9 : shape.nodeId = nodeIds;
593 : 9 : size_t model_array_id = 0U;
594 [ + + ]: 90 : for (const auto& xyzi : cube.xyzis) {
595 : 90 : shape.models[model_array_id].modelId = model_id;
596 : 90 : shape.models[model_array_id].modelAttribs.Add("_f", ez::str::toStr(xyzi.first));
597 : 90 : ++model_array_id;
598 : 90 : ++model_id;
599 : 90 : }
600 : 9 : shapes.push_back(shape);
601 : :
602 : 9 : ++cube_idx;
603 : 9 : }
604 : :
605 : 1 : rootTransform.write(m_file);
606 : 1 : rootGroup.write(m_file);
607 : :
608 : : // trn & shp
609 [ + + ]: 10 : for (int i = 0; i < count; i++) {
610 : 9 : shapeTransforms[i].write(m_file);
611 : 9 : shapes[i].write(m_file);
612 : 9 : }
613 : :
614 : : // no layr in my cases
615 : :
616 : : // layr
617 : : /*for (int i = 0; i < 8; i++)
618 : : {
619 : : LAYR layr;
620 : : layr.nodeId = i;
621 : : layr.nodeAttribs.Add("_name", ez::str::toStr(i));
622 : : layr.write(m_file);
623 : : }*/
624 : :
625 : : // RGBA Palette
626 [ - + ]: 1 : if (colors.size() > 0) {
627 : 0 : RGBA palette;
628 [ # # ]: 0 : for (int32_t i = 0; i < 255; i++) {
629 [ # # ]: 0 : if (i < (int32_t)colors.size()) {
630 : 0 : palette.colors[i] = colors[i];
631 : 0 : } else {
632 : 0 : palette.colors[i] = 0;
633 : 0 : }
634 : 0 : }
635 : :
636 : 0 : palette.write(m_file);
637 : 0 : }
638 : :
639 : 1 : const long mainChildChunkSize = m_GetFilePos() - headerSize;
640 : 1 : m_SetFilePos(numBytesMainChunkPos);
641 : 1 : uint32_t size = (uint32_t)mainChildChunkSize;
642 : 1 : fwrite(&size, sizeof(uint32_t), 1, m_file);
643 : :
644 : 1 : m_CloseFile();
645 : 1 : }
646 : 1 : return *this;
647 : 1 : }
648 : :
649 : 0 : size_t getVoxelsCount(const KeyFrame& vKeyFrame) const {
650 : 0 : size_t voxel_count = 0U;
651 : 0 : for (const auto& cube : cubes) {
652 : 0 : if (cube.xyzis.find(vKeyFrame) != cube.xyzis.end()) {
653 : 0 : voxel_count += cube.xyzis.at(vKeyFrame).numVoxels;
654 : 0 : }
655 : 0 : }
656 : 0 : return voxel_count;
657 : 0 : }
658 : :
659 : 0 : size_t getVoxelsCount() const {
660 : 0 : size_t voxel_count = 0U;
661 : 0 : for (const auto& cube : cubes) {
662 : 0 : for (auto& key_xyzi : cube.xyzis) {
663 : 0 : voxel_count += key_xyzi.second.numVoxels;
664 : 0 : }
665 : 0 : }
666 : 0 : return voxel_count;
667 : 0 : }
668 : :
669 : 1 : Writer& printStats() {
670 : 1 : std::cout << "---- Stats ------------------------------" << std::endl;
671 : 1 : std::cout << "Volume : " << maxVolume.Size().x << " x " << maxVolume.Size().y << " x " << maxVolume.Size().z << std::endl;
672 : 1 : std::cout << "count cubes : " << cubes.size() << std::endl;
673 : 1 : std::map<KeyFrame, size_t> frame_counts;
674 [ + + ]: 9 : for (const auto& cube : cubes) {
675 [ + + ]: 90 : for (auto& key_xyzi : cube.xyzis) {
676 : 90 : frame_counts[key_xyzi.first] += key_xyzi.second.numVoxels;
677 : 90 : }
678 : 9 : }
679 : 1 : size_t voxels_total = 0U;
680 [ + - ]: 1 : if (frame_counts.size() > 1U) {
681 : 1 : std::cout << "count key frames : " << frame_counts.size() << std::endl;
682 : 1 : std::cout << "-----------------------------------------" << std::endl;
683 [ + + ]: 10 : for (const auto& frame_count : frame_counts) {
684 : 10 : std::cout << " o--\\-> key frame : " << frame_count.first << std::endl;
685 : 10 : std::cout << " \\-> voxels count : " << frame_count.second << std::endl;
686 [ + - ]: 10 : if (m_FrameTimes.find(frame_count.first) != m_FrameTimes.end()) {
687 : 10 : std::cout << " \\-> elapsed time : " << m_FrameTimes.at(frame_count.first) << " secs" << std::endl;
688 : 10 : }
689 : 10 : voxels_total += frame_count.second;
690 : 10 : }
691 : 1 : std::cout << "-----------------------------------------" << std::endl;
692 [ # # ]: 1 : } else if (!frame_counts.empty()) {
693 : 0 : voxels_total = frame_counts.begin()->second;
694 : 0 : }
695 : 1 : std::cout << "voxels total : " << voxels_total << std::endl;
696 : 1 : std::cout << "total elapsed time : " << m_TotalTime << " secs" << std::endl;
697 : 1 : std::cout << "-----------------------------------------" << std::endl;
698 : 1 : return *this;
699 : 1 : }
700 : :
701 : : private:
702 : 1 : bool m_OpenFileForWriting(const std::string& vFilePathName) {
703 : : #if _MSC_VER
704 : : lastError = fopen_s(&m_file, vFilePathName.c_str(), "wb");
705 : : #else
706 : 1 : m_file = fopen(vFilePathName.c_str(), "wb");
707 [ + - ]: 1 : lastError = m_file ? 0 : errno;
708 : 1 : #endif
709 [ - + ]: 1 : if (lastError != 0) return false;
710 : 1 : return true;
711 : 1 : }
712 : :
713 : 1 : void m_CloseFile() {
714 : 1 : fclose(m_file);
715 : 1 : }
716 : :
717 : 3 : long m_GetFilePos() const {
718 : 3 : return ftell(m_file);
719 : 3 : }
720 : :
721 : 1 : void m_SetFilePos(const long& vPos) {
722 : : // SEEK_SET Beginning of file
723 : : // SEEK_CUR Current position of the file pointer
724 : : // SEEK_END End of file
725 : 1 : fseek(m_file, vPos, SEEK_SET);
726 : 1 : }
727 : :
728 : 1428840 : const size_t m_GetCubeId(const VoxelX& vX, const VoxelY& vY, const VoxelZ& vZ) {
729 [ + + ]: 1428840 : if (cubesId.find(vX) != cubesId.end()) {
730 [ + + ]: 1428837 : if (cubesId[vX].find(vY) != cubesId[vX].end()) {
731 [ + - ]: 1428831 : if (cubesId[vX][vY].find(vZ) != cubesId[vX][vY].end()) {
732 : 1428831 : return cubesId[vX][vY][vZ];
733 : 1428831 : }
734 : 1428831 : }
735 : 1428837 : }
736 : :
737 : 9 : cubesId[vX][vY][vZ] = maxCubeId++;
738 : :
739 : 9 : return cubesId[vX][vY][vZ];
740 : 1428840 : }
741 : :
742 : : // Wrap a position inside a particular cube dimension
743 : 4286520 : inline uint8_t Wrap(size_t v, size_t lim) {
744 : 4286520 : v = v % lim;
745 [ - + ]: 4286520 : if (v < 0) {
746 : 0 : v += lim;
747 : 0 : }
748 : 4286520 : return (uint8_t)v;
749 : 4286520 : }
750 : :
751 : 1428840 : void m_MergeVoxelInCube(const VoxelX& vX, const VoxelY& vY, const VoxelZ& vZ, const uint8_t& vColorIndex, VoxCube* vCube) {
752 : 1428840 : maxVolume.Combine(ez::dvec3((double)vX, (double)vY, (double)vZ));
753 : :
754 : 1428840 : bool exist = false;
755 [ + + ]: 1428840 : if (voxelId.find(m_KeyFrame) != voxelId.end()) {
756 : 1428830 : auto& vidk = voxelId.at(m_KeyFrame);
757 [ + + ]: 1428830 : if (vidk.find(vX) != vidk.end()) {
758 : 1425060 : auto& vidkx = vidk.at(vX);
759 [ - + ]: 1425060 : if (vidkx.find(vY) != vidkx.end()) {
760 : 0 : auto& vidkxy = vidkx.at(vY);
761 [ # # ]: 0 : if (vidkxy.find(vZ) != vidkxy.end()) {
762 : 0 : exist = true;
763 : 0 : }
764 : 0 : }
765 : 1425060 : }
766 : 1428830 : }
767 : :
768 [ + - ]: 1428840 : if (!exist) {
769 : 1428840 : auto& xyzi = vCube->xyzis[m_KeyFrame];
770 : 1428840 : xyzi.voxels.push_back(Wrap(vX, m_MaxVoxelPerCubeX)); // x
771 : 1428840 : xyzi.voxels.push_back(Wrap(vY, m_MaxVoxelPerCubeY)); // y
772 : 1428840 : xyzi.voxels.push_back(Wrap(vZ, m_MaxVoxelPerCubeZ)); // z
773 : :
774 : : // correspond a la loc de la couleur du voxel en question
775 : 1428840 : voxelId[m_KeyFrame][vX][vY][vZ] = (int)xyzi.voxels.size();
776 : :
777 : 1428840 : xyzi.voxels.push_back(vColorIndex); // color index
778 : 1428840 : }
779 : 1428840 : }
780 : :
781 : 1428840 : VoxCube* m_GetCube(const VoxelX& vX, const VoxelY& vY, const VoxelZ& vZ) {
782 : 1428840 : const auto& id = m_GetCubeId(vX, vY, vZ);
783 : :
784 [ + + ]: 1428840 : if (id == cubes.size()) {
785 : 9 : VoxCube c;
786 : :
787 : 9 : c.id = (int32_t)id;
788 : :
789 : 9 : c.tx = (int32_t)vX;
790 : 9 : c.ty = (int32_t)vY;
791 : 9 : c.tz = (int32_t)vZ;
792 : :
793 : 9 : c.size.sizex = (int32_t)m_MaxVoxelPerCubeX;
794 : 9 : c.size.sizey = (int32_t)m_MaxVoxelPerCubeY;
795 : 9 : c.size.sizez = (int32_t)m_MaxVoxelPerCubeZ;
796 : :
797 : 9 : cubes.push_back(c);
798 : 9 : }
799 : :
800 [ + - ]: 1428840 : if (id < cubes.size()) {
801 : 1428840 : return &cubes[id];
802 : 1428840 : }
803 : :
804 : 0 : return nullptr;
805 : 1428840 : }
806 : : };
807 : :
808 : : } // namespace vox
809 : : } // namespace file
810 : : } // namespace ez
|