VOXELFORMAT: hopefully fixed uv coordinates for palettes that don't have 256 colors

master
Martin Gerhardy 2022-05-22 18:37:20 +02:00
parent 2396a881ff
commit ad0e30841a
5 changed files with 47 additions and 34 deletions

View File

@ -152,7 +152,7 @@ Objects: {
if (withTexCoords) { if (withTexCoords) {
// 1 x 256 is the texture format that we are using for our palette // 1 x 256 is the texture format that we are using for our palette
const float texcoord = 1.0f / (float)palette.colorCount; const float texcoord = 1.0f / (float)voxel::PaletteMaxColors;
// it is only 1 pixel high - sample the middle // it is only 1 pixel high - sample the middle
const float v1 = 0.5f; const float v1 = 0.5f;
wrapBool(stream.writeString("\t\tLayerElementUV: 0 {\n", false)) wrapBool(stream.writeString("\t\tLayerElementUV: 0 {\n", false))

View File

@ -160,7 +160,7 @@ bool GLTFFormat::saveMeshes(const core::Map<int, int> &meshIdxNodeMap, const Sce
continue; continue;
} }
// 1 x 256 is the texture format that we are using for our palette // 1 x 256 is the texture format that we are using for our palette
const float texcoord = 1.0f / (float)palette.colorCount; const float texcoord = 1.0f / (float)voxel::PaletteMaxColors;
int meshExtIdx = 0; int meshExtIdx = 0;
meshIdxNodeMap.get(nodeId, meshExtIdx); meshIdxNodeMap.get(nodeId, meshExtIdx);

View File

@ -33,24 +33,18 @@ namespace voxelformat {
return false; \ return false; \
} }
bool OBJFormat::writeMtlFile(const core::String &mtlName, const core::String &paletteName) const { bool OBJFormat::writeMtlFile(io::SeekableWriteStream &stream, const core::String &mtlId, const core::String &mapKd) const {
const io::FilePtr &file = io::filesystem()->open(mtlName, io::FileMode::SysWrite); if (!stream.writeStringFormat(false, "\nnewmtl %s\n", mtlId.c_str())) {
if (!file->validHandle()) { Log::error("Failed to write obj newmtl");
Log::error("Failed to create mtl file at %s", file->name().c_str());
return false; return false;
} }
io::FileStream stream(file); wrapBool(stream.writeString("Ka 1.000000 1.000000 1.000000\n", false))
wrapBool(stream.writeStringFormat(false, "# version " PROJECT_VERSION " github.com/mgerhardy/vengi\n")) wrapBool(stream.writeString("Kd 1.000000 1.000000 1.000000\n", false))
wrapBool(stream.writeStringFormat(false, "\n")) wrapBool(stream.writeString("Ks 0.000000 0.000000 0.000000\n", false))
wrapBool(stream.writeStringFormat(false, "newmtl palette\n")) wrapBool(stream.writeString("Tr 1.000000\n", false))
wrapBool(stream.writeStringFormat(false, "Ka 1.000000 1.000000 1.000000\n")) wrapBool(stream.writeString("illum 1\n", false))
wrapBool(stream.writeStringFormat(false, "Kd 1.000000 1.000000 1.000000\n")) wrapBool(stream.writeString("Ns 0.000000\n", false))
wrapBool(stream.writeStringFormat(false, "Ks 0.000000 0.000000 0.000000\n")) if (!stream.writeStringFormat(false, "map_Kd %s\n", mapKd.c_str())) {
wrapBool(stream.writeStringFormat(false, "Tr 1.000000\n"))
wrapBool(stream.writeStringFormat(false, "illum 1\n"))
wrapBool(stream.writeStringFormat(false, "Ns 0.000000\n"))
const core::String& paletteFilename = core::string::extractFilenameWithExtension(paletteName);
if (!stream.writeStringFormat(false, "map_Kd %s\n", paletteFilename.c_str())) {
Log::error("Failed to write obj map_Kd"); Log::error("Failed to write obj map_Kd");
return false; return false;
} }
@ -66,6 +60,21 @@ bool OBJFormat::saveMeshes(const core::Map<int, int> &, const SceneGraph &sceneG
Log::debug("Exporting %i layers", (int)meshes.size()); Log::debug("Exporting %i layers", (int)meshes.size());
core::String mtlname = core::string::stripExtension(filename);
mtlname.append(".mtl");
Log::debug("Use mtl file: %s", mtlname.c_str());
const io::FilePtr &file = io::filesystem()->open(mtlname, io::FileMode::SysWrite);
if (!file->validHandle()) {
Log::error("Failed to create mtl file at %s", file->name().c_str());
return false;
}
io::FileStream matlstream(file);
wrapBool(matlstream.writeString("# version " PROJECT_VERSION " github.com/mgerhardy/vengi\n", false))
wrapBool(matlstream.writeString("\n", false))
core::Map<uint64_t, int> paletteMaterialIndices((int)sceneGraph.size());
int idxOffset = 0; int idxOffset = 0;
int texcoordOffset = 0; int texcoordOffset = 0;
for (const auto &meshExt : meshes) { for (const auto &meshExt : meshes) {
@ -80,17 +89,10 @@ bool OBJFormat::saveMeshes(const core::Map<int, int> &, const SceneGraph &sceneG
const SceneGraphNode &graphNode = sceneGraph.node(meshExt.nodeId); const SceneGraphNode &graphNode = sceneGraph.node(meshExt.nodeId);
const voxel::Palette &palette = graphNode.palette(); const voxel::Palette &palette = graphNode.palette();
core::String mtlname = core::string::stripExtension(filename);
mtlname.append(core::String::format("%" PRIu64, palette.hash()));
mtlname.append(".mtl");
const core::String hashId = core::String::format("%" PRIu64, palette.hash()); const core::String hashId = core::String::format("%" PRIu64, palette.hash());
core::String palettename = core::string::stripExtension(filename);
palettename.append(hashId);
palettename.append(".png");
// 1 x 256 is the texture format that we are using for our palette // 1 x 256 is the texture format that we are using for our palette
const float texcoord = 1.0f / (float)palette.colorCount; const float texcoord = 1.0f / (float)voxel::PaletteMaxColors;
// it is only 1 pixel high - sample the middle // it is only 1 pixel high - sample the middle
const float v1 = 0.5f; const float v1 = 0.5f;
const glm::vec3 offset(mesh->getOffset()); const glm::vec3 offset(mesh->getOffset());
@ -102,7 +104,10 @@ bool OBJFormat::saveMeshes(const core::Map<int, int> &, const SceneGraph &sceneG
} }
stream.writeStringFormat(false, "o %s\n", objectName); stream.writeStringFormat(false, "o %s\n", objectName);
stream.writeStringFormat(false, "mtllib %s\n", core::string::extractFilenameWithExtension(mtlname).c_str()); stream.writeStringFormat(false, "mtllib %s\n", core::string::extractFilenameWithExtension(mtlname).c_str());
wrapBool(stream.writeStringFormat(false, "usemtl palette\n")) if (!stream.writeStringFormat(false, "usemtl %s\n", hashId.c_str())) {
Log::error("Failed to write obj usemtl %s\n", hashId.c_str());
return false;
}
for (int i = 0; i < nv; ++i) { for (int i = 0; i < nv; ++i) {
const voxel::VoxelVertex &v = vertices[i]; const voxel::VoxelVertex &v = vertices[i];
@ -175,11 +180,18 @@ bool OBJFormat::saveMeshes(const core::Map<int, int> &, const SceneGraph &sceneG
} }
idxOffset += nv; idxOffset += nv;
if (!writeMtlFile(mtlname, palettename)) { if (paletteMaterialIndices.find(palette.hash()) == paletteMaterialIndices.end()) {
return false; core::String palettename = core::string::stripExtension(filename);
} palettename.append(hashId);
if (!palette.save(palettename.c_str())) { palettename.append(".png");
return false; paletteMaterialIndices.put(palette.hash(), 1);
const core::String &mapKd = core::string::extractFilenameWithExtension(palettename);
if (!writeMtlFile(matlstream, hashId, mapKd)) {
return false;
}
if (!palette.save(palettename.c_str())) {
return false;
}
} }
} }
return true; return true;

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include "MeshFormat.h" #include "MeshFormat.h"
#include "io/Stream.h"
namespace tinyobj { namespace tinyobj {
struct mesh_t; struct mesh_t;
@ -18,7 +19,7 @@ namespace voxelformat {
*/ */
class OBJFormat : public MeshFormat { class OBJFormat : public MeshFormat {
private: private:
bool writeMtlFile(const core::String& mtlName, const core::String &paletteName) const; bool writeMtlFile(io::SeekableWriteStream &stream, const core::String &mtlId, const core::String &mapKd) const;
static void calculateAABB(const tinyobj::mesh_t &mesh, const tinyobj::attrib_t &attrib, glm::vec3 &mins, static void calculateAABB(const tinyobj::mesh_t &mesh, const tinyobj::attrib_t &attrib, glm::vec3 &mins,
glm::vec3 &maxs); glm::vec3 &maxs);
static void subdivideShape(const tinyobj::mesh_t &mesh, const core::StringMap<image::ImagePtr> &textures, static void subdivideShape(const tinyobj::mesh_t &mesh, const core::StringMap<image::ImagePtr> &textures,

View File

@ -65,7 +65,7 @@ bool PLYFormat::saveMeshes(const core::Map<int, int> &, const SceneGraph &sceneG
const SceneGraphNode &graphNode = sceneGraph.node(meshExt.nodeId); const SceneGraphNode &graphNode = sceneGraph.node(meshExt.nodeId);
const voxel::Palette &palette = graphNode.palette(); const voxel::Palette &palette = graphNode.palette();
// 1 x 256 is the texture format that we are using for our palette // 1 x 256 is the texture format that we are using for our palette
const float texcoord = 1.0f / (float)palette.colorCount; const float texcoord = 1.0f / (float)voxel::PaletteMaxColors;
// it is only 1 pixel high - sample the middle // it is only 1 pixel high - sample the middle
const float v1 = 0.5f; const float v1 = 0.5f;