LCOV - code coverage report
Current view: top level - ezlibs - ezVoxWriter.hpp (source / functions) Coverage Total Hit
Test: Coverage (llvm-cov → lcov → genhtml) Lines: 80.8 % 463 374
Test Date: 2025-09-16 22:55:37 Functions: 81.7 % 60 49
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 69.7 % 76 53

             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
        

Generated by: LCOV version 2.0-1