diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..952df7d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.8) +cmake_policy(SET CMP0048 NEW) +cmake_policy(SET CMP0054 NEW) +cmake_policy(SET CMP0012 NEW) +cmake_policy(SET CMP0023 NEW) + +set(PROJECT VoxWriter) + +enable_language(C CXX) + +project(${PROJECT} CXX) + +add_executable(${PROJECT} main.cpp VoxWriter.cpp VoxWriter.h) + diff --git a/VoxWriter.cpp b/VoxWriter.cpp index 739e110..c653c88 100644 --- a/VoxWriter.cpp +++ b/VoxWriter.cpp @@ -1,3 +1,6 @@ +// This is an independent project of an individual developer. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + // Copyright 2018 Stephane Cuillerdier @Aiekick // Permission is hereby granted, free of charge, to any person obtaining a @@ -28,6 +31,8 @@ #include "VoxWriter.h" #include +//#define VERBOSE + namespace vox { DICTstring::DICTstring() @@ -342,9 +347,9 @@ namespace vox { return sizeof(uint8_t) * 4 * 256; } - + ////////////////////////////////////////////////////////////////// - + VoxCube::VoxCube() { id = 0; @@ -373,6 +378,8 @@ namespace vox } else { + printf("Vox file creation failed, err : %s", GetErrnoMsg(*vError).c_str()); + SAFE_DELETE(vox); } @@ -469,10 +476,11 @@ namespace vox } ////////////////////////////////////////////////////////////////// - - VoxWriter::VoxWriter(int vLimitX, int vLimitY, int vLimitZ) + // the limit of magicavoxel is 127 for one cube, is 127 voxels (indexs : 0 -> 126) + // vMaxVoxelPerCubeX,Y,Z define the limit of one cube + VoxWriter::VoxWriter(int vMaxVoxelPerCubeX, int vMaxVoxelPerCubeY, int vMaxVoxelPerCubeZ) { - MV_VERSION = 150; + MV_VERSION = 150; // the old version of MV not open another file than if version is 150 (answer by @ephtracy ID_VOX = GetID('V', 'O', 'X', ' '); ID_PACK = GetID('P', 'A', 'C', 'K'); @@ -486,19 +494,16 @@ namespace vox maxCubeId = 0; - minCubeX = (int)1e5; - minCubeY = (int)1e5; - minCubeZ = (int)1e5; - - maxCubeX = (int)-1e5; - maxCubeY = (int)-1e5; - maxCubeZ = (int)-1e5; + minCubeX = (int)1e7; + minCubeY = (int)1e7; + minCubeZ = (int)1e7; // the limit of magicavoxel is 127 because the first is 1 not 0 // so this is 0 to 126 - m_LimitX = min(vLimitX, 126); - m_LimitY = min(vLimitY, 126); - m_LimitZ = min(vLimitZ, 126); + // index limit, size is 127 + m_MaxVoxelPerCubeX = ct::mini(vMaxVoxelPerCubeX, 126); + m_MaxVoxelPerCubeY = ct::mini(vMaxVoxelPerCubeY, 126); + m_MaxVoxelPerCubeZ = ct::mini(vMaxVoxelPerCubeZ, 126); maxVolume.lowerBound = 1e7f; maxVolume.upperBound = 0.0f; @@ -525,7 +530,7 @@ namespace vox void VoxWriter::ClearColors() { - colors.clear(); + } void VoxWriter::AddColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t index) @@ -538,17 +543,13 @@ namespace vox void VoxWriter::AddVoxel(uint32_t vX, uint32_t vY, uint32_t vZ, uint8_t vColorIndex) { // cube pos - int ox = (int)floor(vX / m_LimitX); - int oy = (int)floor(vY / m_LimitY); - int oz = (int)floor(vZ / m_LimitZ); - - minCubeX = min(minCubeX, ox); - minCubeY = min(minCubeX, oy); - minCubeZ = min(minCubeX, oz); - - maxCubeX = max(maxCubeX, ox + 1); - maxCubeY = max(maxCubeX, oy + 1); - maxCubeZ = max(maxCubeX, oz + 1); + int ox =(int)std::floor((double)vX / (double)m_MaxVoxelPerCubeX); + int oy = (int)std::floor((double)vY / (double)m_MaxVoxelPerCubeY); + int oz = (int)std::floor((double)vZ / (double)m_MaxVoxelPerCubeZ); + + minCubeX = ct::mini(minCubeX, ox); + minCubeY = ct::mini(minCubeX, oy); + minCubeZ = ct::mini(minCubeX, oz); auto cube = GetCube(ox, oy, oz); @@ -559,13 +560,6 @@ namespace vox { if (OpenFileForWriting(vFilePathName)) { - //LogStr("----"); - //LogStr("minCube " + toStr(minCubeX) + " " + toStr(minCubeY) + " " + toStr(minCubeZ)); - //LogStr("maxVolume inf " + toStr(maxVolume.lowerBound.x) + " " + toStr(maxVolume.lowerBound.y) + " " + toStr(maxVolume.lowerBound.z)); - //LogStr("maxVolume sup " + toStr(maxVolume.upperBound.x) + " " + toStr(maxVolume.upperBound.y) + " " + toStr(maxVolume.upperBound.z)); - //LogStr("maxVolume size " + toStr(maxVolume.upperBound.x - maxVolume.lowerBound.x) + " " + toStr(maxVolume.upperBound.y - maxVolume.lowerBound.y) + " " + toStr(maxVolume.upperBound.z - maxVolume.lowerBound.z)); - //LogStr("-----"); - int32_t zero = 0; fwrite(&ID_VOX, sizeof(int32_t), 1, m_File); @@ -605,16 +599,18 @@ namespace vox trans.childNodeId = ++nodeIds; trans.layerId = 0; - int limX = m_LimitX; - int limY = m_LimitY; - int limZ = m_LimitZ; + int limX = m_MaxVoxelPerCubeX; + int limY = m_MaxVoxelPerCubeY; + int limZ = m_MaxVoxelPerCubeZ; - c->tx = (int)((c->tx - minCubeX + 0.5f) * limX) - maxVolume.lowerBound.x - maxVolume.Size().x * 0.5f; - c->ty = (int)((c->ty - minCubeY + 0.5f) * limY) - maxVolume.lowerBound.y - maxVolume.Size().y * 0.5f; + c->tx = (int)((c->tx - minCubeX + 0.5f) * limX); + c->ty = (int)((c->ty - minCubeY + 0.5f) * limY); c->tz = (int)((c->tz - minCubeZ + 0.5f) * limZ); + c->tx = c->tx + (int)(maxVolume.lowerBound.x - maxVolume.Size().x * 0.5); + c->ty = c->ty + (int)(maxVolume.lowerBound.y - maxVolume.Size().y * 0.5); // not an animation in my case so only first frame frames[0] - trans.frames[0].Add("_t", toStr(c->tx) + " " + toStr(c->ty) + " " + toStr(c->tz)); + trans.frames[0].Add("_t", ct::toStr(c->tx) + " " + ct::toStr(c->ty) + " " + ct::toStr(c->tz)); shapeTransforms.push_back(trans); @@ -641,7 +637,7 @@ namespace vox { LAYR layr; layr.nodeId = i; - layr.nodeAttribs.Add("_name", toStr(i)); + layr.nodeAttribs.Add("_name", ct::toStr(i)); layr.write(m_File); }*/ @@ -660,9 +656,10 @@ namespace vox palette.colors[i] = 0; } } + palette.write(m_File); } - + const long mainChildChunkSize = GetFilePos() - headerSize; SetFilePos(numBytesMainChunkPos); uint32_t size = (uint32_t)mainChildChunkSize; @@ -723,7 +720,7 @@ namespace vox void VoxWriter::MergeVoxelInCube(uint32_t vX, uint32_t vY, uint32_t vZ, uint32_t vColorIndex, VoxCube *vCube) { - maxVolume.Combine(cVec3((float)vX, (float)vY, (float)vZ)); + maxVolume.Combine(ct::dvec3((double)vX, (double)vY, (double)vZ)); bool exist = false; @@ -738,21 +735,13 @@ namespace vox } } - // voxel - uint8_t x = (uint8_t)(vX % m_LimitX); - uint8_t y = (uint8_t)(vY % m_LimitY); - uint8_t z = (uint8_t)(vZ % m_LimitZ); - - vCube->size.sizex = cMax(vCube->size.sizex, x); - vCube->size.sizey = cMax(vCube->size.sizey, y); - vCube->size.sizez = cMax(vCube->size.sizez, z); - - vCube->size.sizex = m_LimitX; - vCube->size.sizey = m_LimitY; - vCube->size.sizez = m_LimitZ; - - if (exist == false) + if (!exist) { + // voxel + uint8_t x = (uint8_t)(vX % m_MaxVoxelPerCubeX); + uint8_t y = (uint8_t)(vY % m_MaxVoxelPerCubeY); + uint8_t z = (uint8_t)(vZ % m_MaxVoxelPerCubeZ); + vCube->xyzi.voxels.push_back(x); // x vCube->xyzi.voxels.push_back(y); // y vCube->xyzi.voxels.push_back(z); // z @@ -762,10 +751,6 @@ namespace vox vCube->xyzi.voxels.push_back(vColorIndex); // color index } - /*else //if (exportColors) - { - // on va merger la couleur - }*/ } VoxCube* VoxWriter::GetCube(int vX, int vY, int vZ) @@ -782,11 +767,9 @@ namespace vox c.ty = vY; c.tz = vZ; - c.size.sizex = 0; - c.size.sizey = 0; - c.size.sizez = 0; - - //LogStr("Cube " + toStr(id) + " : s " + toStr(c.size.sizex) + " " + toStr(c.size.sizey) + " " + toStr(c.size.sizez)); + c.size.sizex = m_MaxVoxelPerCubeX + 1; + c.size.sizey = m_MaxVoxelPerCubeY + 1; + c.size.sizez = m_MaxVoxelPerCubeZ + 1; cubes.push_back(c); } diff --git a/VoxWriter.h b/VoxWriter.h index 3c5841f..b1bda47 100644 --- a/VoxWriter.h +++ b/VoxWriter.h @@ -24,15 +24,287 @@ // that's all, the file was initially created for my Proecedural soft // "SdfMesher" cf :https://twitter.com/hashtag/sdfmesher?src=hash // it support just my needs for the moment, but i put here because its a basis for more i thinck - #ifndef __VOX_WRITER_H__ #define __VOX_WRITER_H__ -#include "Tools.hpp" #include #include #include #include +#include +// extracted and adapted from https://github.com/aiekick/cTools (LICENSE MIT) +// for make VoxWriter lib free +namespace ct +{ +#define SAFE_DELETE(a) if (a != 0) delete a, a = 0 + template ::std::string toStr(const T& t) + { + ::std::ostringstream os; + os << t; + return os.str(); + } + template inline T mini(const T& a, const T& b) { return a < b ? a : b; } + template inline T maxi(const T& a, const T& b) { return a > b ? a : b; } + template + struct vec3 + { + T x, y, z; + vec3() : x((T)0), y((T)0), z((T)0) {} + vec3(T xyz) : x(xyz), y(xyz), z(xyz) {} + vec3(T x, T y, T z) : x(x), y(y), z(z) {} + vec3(::std::string vec, char c = ';', vec3 *def = 0)//may be in format "0.2f,0.3f,0.4f" + { + if (def) + { + x = def->x; + y = def->y; + z = def->z; + } + ::std::vector result = StringToNumberVector(vec, c); + size_t s = result.size(); + if (s > 0) x = result[0]; + if (s > 1) y = result[1]; + if (s > 2) z = result[2]; + } + vec3 Offset(T vX, T vY, T vZ) { return vec3(x + vX, y + vY, z + vZ); } + void Set(T vX, T vY, T vZ) { x = vX; y = vY; z = vZ; } + vec3 operator -() const { return vec3(-x, -y, -z); } + vec3 yzx() { return vec3(y, z, x); } + void operator += (const vec3& v) { x += v.x; y += v.y; z += v.z; } + bool operator == (const vec3& v) { return (x == v.x && y == v.y && z == v.z); } + bool operator != (const vec3& v) { return (x != v.x || y != v.y || z != v.z); } + void operator -= (const vec3& v) { x -= v.x; y -= v.y; z -= v.z; } + void operator *= (T a) { x *= a; y *= a; z *= a; } + void operator /= (T a) { x /= a; y /= a; z /= a; } + T length() const { return sqrtf(lengthSquared()); } + T lengthSquared() const { return x * x + y * y + z * z; } + T normalize() { T _length = length(); if (_length < (T)1e-5) return (T)0; T _invLength = (T)1 / _length; x *= _invLength; y *= _invLength; z *= _invLength; return _length; } + vec3 getNormalized() { vec3 n = vec3(x, y, z); n.normalize(); return n; } + T sum() { return x + y + z; } + T sumAbs() { return abs(x) + abs(y) + abs(z); } + bool empty() { if (x == (T)0 && y == (T)0 && z == (T)0) return true; else return false; } + std::string string(char c = ';') { return toStr(x) + c + toStr(y) + c + toStr(z); } + }; + template inline vec3 operator + (const vec3& v, const T& f) { return vec3(v.x + f, v.y + f, v.z + f); } + template inline vec3 operator + (const vec3& v, vec3 f) { return vec3(v.x + f.x, v.y + f.y, v.z + f.z); } + template inline vec3 operator - (const vec3& v, const T& f) { return vec3(v.x - f, v.y - f, v.z - f); } + template inline vec3 operator - (const vec3& v, vec3 f) { return vec3(v.x - f.x, v.y - f.y, v.z - f.z); } + template inline vec3 operator * (const vec3& v, const T& f) { return vec3(v.x * f, v.y * f, v.z * f); } + template inline vec3 operator * (const vec3& v, vec3 f) { return vec3(v.x * f.x, v.y * f.y, v.z * f.z); } + template inline vec3 operator / (const vec3& v, const T& f) { return vec3(v.x / f, v.y / f, v.z / f); } + template inline vec3 operator / (vec3& v, const T& f) { return vec3(v.x / f, v.y / f, v.z / f); } + template inline vec3 operator / (const T& f, vec3& v) { return vec3(f / v.x, f / v.y, f / v.z); } + template inline vec3 operator / (const vec3& v, vec3 f) { return vec3(v.x / f.x, v.y / f.y, v.z / f.z); } + template inline bool operator < (const vec3& v, vec3 f) { return v.x < f.x && v.y < f.y && v.z < f.z; } + template inline bool operator < (const vec3& v, const T& f) { return v.x < f && v.y < f && v.z < f; } + template inline bool operator > (const vec3& v, vec3 f) { return v.x > f.x && v.y > f.y && v.z > f.z; } + template inline bool operator > (const vec3& v, const T& f) { return v.x > f && v.y > f && v.z > f; } + template inline bool operator <= (const vec3& v, vec3 f) { return v.x <= f.x && v.y <= f.y && v.z <= f.z; } + template inline bool operator <= (const vec3& v, const T& f) { return v.x <= f && v.y <= f && v.z <= f; } + template inline bool operator >= (const vec3& v, vec3 f) { return v.x >= f.x && v.y >= f.y && v.z >= f.z; } + template inline bool operator >= (const vec3& v, const T& f) { return v.x >= f && v.y >= f && v.z >= f; } + template inline bool operator != (const vec3& v, vec3 f) { return (f.x != v.x) && (f.y != v.y) && (f.z != v.z); } + template inline bool operator == (const vec3& v, vec3 f) { return (f.x == v.x) && (f.y == v.y) && (f.z == v.z); } + template inline vec3 mini(const vec3& a, const vec3& b) { return vec3(mini(a.x, b.x), mini(a.y, b.y), mini(a.z, b.z)); } + template inline vec3 maxi(const vec3& a, const vec3& b) { return vec3(maxi(a.x, b.x), maxi(a.y, b.y), maxi(a.z, b.z)); } + template inline T dotS(vec3 a, vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } + typedef vec3 dvec3; + typedef vec3 fvec3; + typedef vec3 bvec3; + typedef vec3 ivec3; + typedef vec3 uvec3; + template + struct AABBCC // copy of b2AABB struct + { + vec3 lowerBound; ///< the lower left vertex + vec3 upperBound; ///< the upper right vertex + + AABBCC() : lowerBound((T)0), upperBound((T)0) {} + AABBCC(vec3 vlowerBound, vec3 vUpperBound) + //: lowerBound(vlowerBound), upperBound(vUpperBound) + { + lowerBound.x = mini(vlowerBound.x, vUpperBound.x); + lowerBound.y = mini(vlowerBound.y, vUpperBound.y); + lowerBound.z = mini(vlowerBound.z, vUpperBound.z); + upperBound.x = maxi(vlowerBound.x, vUpperBound.x); + upperBound.y = maxi(vlowerBound.y, vUpperBound.y); + upperBound.z = maxi(vlowerBound.z, vUpperBound.z); + } + + /// Verify that the bounds are sorted. + //bool IsValid() const; + + /// Add a vector to this vector. + void operator += (const vec3& v) + { + lowerBound += v; upperBound += v; + } + + /// Subtract a vector from this vector. + void operator -= (const vec3& v) + { + lowerBound -= v; upperBound -= v; + } + + /// Multiply this vector by a scalar. + void operator *= (T a) + { + lowerBound *= a; upperBound *= a; + } + + /// Divide this vector by a scalar. + void operator /= (T a) + { + lowerBound /= a; upperBound /= a; + } + + /// Get the center of the AABB. + vec3 GetCenter() const + { + return (lowerBound + upperBound) / (T)2; + } + + /// Get the extents of the AABB (half-widths). + vec3 GetExtents() const + { + return (upperBound - lowerBound) / (T)2; + } + + /// Get the perimeter length + T GetPerimeter() const + { + float wx = upperBound.x - lowerBound.x; + float wy = upperBound.y - lowerBound.y; + float wz = upperBound.z - lowerBound.z; + return (T)2 * (wx + wy + wz); + } + + /// Combine an AABB into this one. + void Combine(const AABBCC& aabb) + { + lowerBound = mini(lowerBound, aabb.lowerBound); + upperBound = maxi(upperBound, aabb.upperBound); + } + + /// Combine two AABBs into this one. + void Combine(const AABBCC& aabb1, const AABBCC& aabb2) + { + lowerBound = mini(aabb1.lowerBound, aabb2.lowerBound); + upperBound = maxi(aabb1.upperBound, aabb2.upperBound); + } + + /// Combine a point into this one. + void Combine(const vec3& pt) + { + lowerBound.x = mini(lowerBound.x, pt.x); + lowerBound.y = mini(lowerBound.y, pt.y); + lowerBound.z = mini(lowerBound.z, pt.z); + upperBound.x = maxi(upperBound.x, pt.x); + upperBound.y = maxi(upperBound.y, pt.y); + upperBound.z = maxi(upperBound.z, pt.z); + } + + /// Does this aabb contain the provided AABB. + bool Contains(const AABBCC& aabb) const + { + bool result = true; + result = result && lowerBound.x <= aabb.lowerBound.x; + result = result && lowerBound.y <= aabb.lowerBound.y; + result = result && lowerBound.z <= aabb.lowerBound.z; + result = result && aabb.upperBound.x <= upperBound.x; + result = result && aabb.upperBound.y <= upperBound.y; + result = result && aabb.upperBound.z <= upperBound.z; + return result; + } + + /// Does this aabb contain the provided vec2. + bool ContainsPoint(const vec3& pt) const + { + bool result = true; + result = result && lowerBound.x <= pt.x; + result = result && lowerBound.y <= pt.y; + result = result && lowerBound.z <= pt.z; + result = result && pt.x <= upperBound.x; + result = result && pt.y <= upperBound.y; + result = result && pt.z <= upperBound.z; + return result; + } + + bool Intersects(const AABBCC& other) + { + bool result = true; + result = result || lowerBound.x <= other.lowerBound.x; + result = result || lowerBound.y <= other.lowerBound.y; + result = result || lowerBound.z <= other.lowerBound.z; + result = result || other.upperBound.x <= upperBound.x; + result = result || other.upperBound.y <= upperBound.y; + result = result || other.upperBound.z <= upperBound.z; + return result; + } + + const vec3 Size() + { + return vec3(upperBound - lowerBound); + } + }; + typedef AABBCC dAABBCC; + typedef AABBCC fAABBCC; + typedef AABBCC iAABBCC; + + /// Add a float to a AABBCC. + template inline AABBCC operator + (const AABBCC& v, float f) + { + return AABBCC(v.lowerBound + f, v.upperBound + f); + } + + /// Add a AABBCC to a AABBCC. + template inline AABBCC operator + (const AABBCC& v, AABBCC f) + { + return AABBCC(v.lowerBound + f.lowerBound, v.upperBound + f.upperBound); + } + + /// Substract a float from a AABBCC. + template inline AABBCC operator - (const AABBCC& v, float f) + { + return AABBCC(v.lowerBound - f, v.upperBound - f); + } + + /// Substract a AABBCC to a AABBCC. + template inline AABBCC operator - (const AABBCC& v, AABBCC f) + { + return AABBCC(v.lowerBound - f.lowerBound, v.upperBound - f.upperBound); + } + + /// Multiply a float with a AABBCC. + template inline AABBCC operator * (const AABBCC& v, float f) + { + return AABBCC(v.lowerBound * f, v.upperBound * f); + } + + /// Multiply a AABBCC with a AABBCC. + template inline AABBCC operator * (const AABBCC& v, AABBCC f) + { + return AABBCC(v.lowerBound * f.lowerBound, v.upperBound * f.upperBound); + } + + /// Divide a AABBCC by a float. + template inline AABBCC operator / (const AABBCC& v, float f) + { + return AABBCC(v.lowerBound / f, v.upperBound / f); + } + + /// Divide a AABBCC by a float. + template inline AABBCC operator / (AABBCC& v, float f) + { + return AABBCC(v.lowerBound / f, v.upperBound / f); + } + + /// Divide a AABBCC by a AABBCC. + template inline AABBCC operator / (const AABBCC& v, AABBCC f) + { + return AABBCC(v.lowerBound / f.lowerBound, v.upperBound / f.upperBound); + } +} namespace vox { @@ -168,7 +440,7 @@ namespace vox RGBA(); void write(FILE *fp); size_t getSize(); - }; + }; struct VoxCube { @@ -208,28 +480,25 @@ namespace vox int ID_NGRP; int ID_NSHP; - int m_LimitX; - int m_LimitY; - int m_LimitZ; + int m_MaxVoxelPerCubeX; + int m_MaxVoxelPerCubeY; + int m_MaxVoxelPerCubeZ; FILE * m_File; - cAABBCC maxVolume; + ct::dAABBCC maxVolume; std::vector colors; std::vector cubes; int maxCubeId; int minCubeX; int minCubeY; int minCubeZ; - int maxCubeX; - int maxCubeY; - int maxCubeZ; std::map>> cubesId; std::map>> voxelId; errno_t lastError; public: - VoxWriter(int vLimitX, int vLimitY, int vLimitZ); + VoxWriter(int vLimitX = 126, int vLimitY = 126, int vLimitZ = 126); ~VoxWriter(); errno_t IsOk(std::string vFilePathName); void ClearVoxels(); diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..b4bf05e --- /dev/null +++ b/main.cpp @@ -0,0 +1,15 @@ +#include "VoxWriter.h" + +// example from @unphased on this topic : https://github.com/aiekick/MagicaVoxel_File_Writer/issues/2 + +int main() +{ + vox::VoxWriter vox; + for (int i = 0; i < 1000; ++i) { + for (int j = 0; j < 1000; ++j) { + vox.AddVoxel(i, j, floor(sin((float)(i * i + j * j) / 50000) * 150) + 150, (i + j) % 255 + 1); + } + } + + vox.SaveToFile("output_voxwriter.vox"); +} \ No newline at end of file