mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-20 00:02:55 +00:00
Merge pull request #182 from Laupetin/feature/gltf-xmodel-dumping
feat: dump xmodels as gltf/glb
This commit is contained in:
commit
35474c217e
@ -22,6 +22,8 @@ namespace T6
|
||||
|
||||
typedef tdef_align(128) float float_align128;
|
||||
|
||||
typedef uint16_t ScriptString;
|
||||
|
||||
struct dvar_t;
|
||||
struct MenuCell;
|
||||
struct cplane_s;
|
||||
@ -591,14 +593,22 @@ namespace T6
|
||||
int partBits[5];
|
||||
};
|
||||
|
||||
enum XModelLodRampType : unsigned char
|
||||
{
|
||||
XMODEL_LOD_RAMP_RIGID = 0x0,
|
||||
XMODEL_LOD_RAMP_SKINNED = 0x1,
|
||||
|
||||
XMODEL_LOD_RAMP_COUNT
|
||||
};
|
||||
|
||||
struct XModel
|
||||
{
|
||||
const char* name;
|
||||
unsigned char numBones;
|
||||
unsigned char numRootBones;
|
||||
unsigned char numsurfs;
|
||||
char lodRampType;
|
||||
uint16_t* boneNames;
|
||||
XModelLodRampType lodRampType;
|
||||
ScriptString* boneNames;
|
||||
unsigned char* parentList;
|
||||
uint16_t (*quats)[4];
|
||||
float (*trans)[4];
|
||||
@ -618,7 +628,7 @@ namespace T6
|
||||
uint16_t collLod;
|
||||
float* himipInvSqRadii;
|
||||
int memUsage;
|
||||
int flags;
|
||||
unsigned int flags;
|
||||
bool bad;
|
||||
PhysPreset* physPreset;
|
||||
unsigned char numCollmaps;
|
||||
|
36
src/ObjCommon/Game/T6/Json/JsonXModel.h
Normal file
36
src/ObjCommon/Game/T6/Json/JsonXModel.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/T6/T6.h"
|
||||
|
||||
#include "Json/JsonCommon.h"
|
||||
#include "Json/JsonExtension.h"
|
||||
#include <memory>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace T6
|
||||
{
|
||||
class JsonXModelLod
|
||||
{
|
||||
public:
|
||||
std::string file;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModelLod, file);
|
||||
|
||||
class JsonXModel
|
||||
{
|
||||
public:
|
||||
std::vector<JsonXModelLod> lods;
|
||||
unsigned collLod;
|
||||
std::optional<std::string> physPreset;
|
||||
std::optional<std::string> physConstraints;
|
||||
unsigned flags;
|
||||
JsonVec3 lightingOriginOffset;
|
||||
float lightingOriginRange;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonXModel, lods, collLod, physPreset, physConstraints, flags, lightingOriginOffset, lightingOriginRange);
|
||||
} // namespace T6
|
27
src/ObjCommon/XModel/Gltf/GltfConstants.h
Normal file
27
src/ObjCommon/XModel/Gltf/GltfConstants.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils/FileUtils.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
constexpr uint32_t GLTF_MAGIC = FileUtils::MakeMagic32('g', 'l', 'T', 'F');
|
||||
constexpr uint32_t GLTF_VERSION = 2u;
|
||||
constexpr auto GLTF_VERSION_STRING = "2.0";
|
||||
|
||||
constexpr uint32_t CHUNK_MAGIC_JSON = FileUtils::MakeMagic32('J', 'S', 'O', 'N');
|
||||
constexpr uint32_t CHUNK_MAGIC_BIN = FileUtils::MakeMagic32('B', 'I', 'N', '\x00');
|
||||
|
||||
constexpr auto GLTF_LENGTH_OFFSET = 8u;
|
||||
constexpr auto GLTF_JSON_CHUNK_LENGTH_OFFSET = 12u;
|
||||
constexpr auto GLTF_JSON_CHUNK_DATA_OFFSET = 20u;
|
||||
|
||||
constexpr auto GLTF_DATA_URI_PREFIX = "data:application/octet-stream;base64,";
|
||||
|
||||
constexpr auto GLTF_ATTRIBUTE_POSITION = "POSITION";
|
||||
constexpr auto GLTF_ATTRIBUTE_NORMAL = "NORMAL";
|
||||
constexpr auto GLTF_ATTRIBUTE_TEXCOORD_0 = "TEXCOORD_0";
|
||||
constexpr auto GLTF_ATTRIBUTE_JOINTS_0 = "JOINTS_0";
|
||||
constexpr auto GLTF_ATTRIBUTE_WEIGHTS_0 = "WEIGHTS_0";
|
||||
} // namespace gltf
|
343
src/ObjCommon/XModel/Gltf/JsonGltf.h
Normal file
343
src/ObjCommon/XModel/Gltf/JsonGltf.h
Normal file
@ -0,0 +1,343 @@
|
||||
#pragma once
|
||||
|
||||
#include "Json/JsonExtension.h"
|
||||
#include <array>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
class JsonAsset
|
||||
{
|
||||
public:
|
||||
std::string version;
|
||||
std::optional<std::string> generator;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAsset, version, generator);
|
||||
|
||||
class JsonNode
|
||||
{
|
||||
public:
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::array<float, 3>> translation;
|
||||
std::optional<std::array<float, 4>> rotation;
|
||||
std::optional<std::array<float, 3>> scale;
|
||||
std::optional<std::vector<unsigned>> children;
|
||||
std::optional<unsigned> skin;
|
||||
std::optional<unsigned> mesh;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonNode, name, translation, rotation, scale, children, skin, mesh);
|
||||
|
||||
class JsonBuffer
|
||||
{
|
||||
public:
|
||||
unsigned byteLength;
|
||||
std::optional<std::string> uri;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonBuffer, byteLength, uri);
|
||||
|
||||
enum class JsonAccessorComponentType
|
||||
{
|
||||
SIGNED_BYTE = 5120,
|
||||
UNSIGNED_BYTE = 5121,
|
||||
SIGNED_SHORT = 5122,
|
||||
UNSIGNED_SHORT = 5123,
|
||||
UNSIGNED_INT = 5125,
|
||||
FLOAT = 5126
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(JsonAccessorComponentType,
|
||||
{
|
||||
{JsonAccessorComponentType::SIGNED_BYTE, static_cast<unsigned>(JsonAccessorComponentType::SIGNED_BYTE) },
|
||||
{JsonAccessorComponentType::UNSIGNED_BYTE, static_cast<unsigned>(JsonAccessorComponentType::UNSIGNED_BYTE) },
|
||||
{JsonAccessorComponentType::SIGNED_SHORT, static_cast<unsigned>(JsonAccessorComponentType::SIGNED_SHORT) },
|
||||
{JsonAccessorComponentType::UNSIGNED_SHORT, static_cast<unsigned>(JsonAccessorComponentType::UNSIGNED_SHORT)},
|
||||
{JsonAccessorComponentType::UNSIGNED_INT, static_cast<unsigned>(JsonAccessorComponentType::UNSIGNED_INT) },
|
||||
{JsonAccessorComponentType::FLOAT, static_cast<unsigned>(JsonAccessorComponentType::FLOAT) },
|
||||
});
|
||||
|
||||
enum class JsonAccessorType
|
||||
{
|
||||
SCALAR,
|
||||
VEC2,
|
||||
VEC3,
|
||||
VEC4,
|
||||
MAT2,
|
||||
MAT3,
|
||||
MAT4
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(JsonAccessorType,
|
||||
{
|
||||
{JsonAccessorType::SCALAR, "SCALAR"},
|
||||
{JsonAccessorType::VEC2, "VEC2" },
|
||||
{JsonAccessorType::VEC3, "VEC3" },
|
||||
{JsonAccessorType::VEC4, "VEC4" },
|
||||
{JsonAccessorType::MAT2, "MAT2" },
|
||||
{JsonAccessorType::MAT3, "MAT3" },
|
||||
{JsonAccessorType::MAT4, "MAT4" },
|
||||
});
|
||||
|
||||
class JsonAccessor
|
||||
{
|
||||
public:
|
||||
std::optional<unsigned> bufferView;
|
||||
std::optional<unsigned> byteOffset;
|
||||
JsonAccessorComponentType componentType;
|
||||
// std::optional<boolean> normalized
|
||||
unsigned count;
|
||||
JsonAccessorType type;
|
||||
std::optional<std::vector<float>> max;
|
||||
std::optional<std::vector<float>> min;
|
||||
// std::optional<JsonAccessorSparse> sparse;
|
||||
// std::optional<std::string> name;
|
||||
// extensions
|
||||
// extras
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAccessor, bufferView, byteOffset, componentType, count, type, min, max);
|
||||
|
||||
enum class JsonBufferViewTarget
|
||||
{
|
||||
ARRAY_BUFFER = 34962,
|
||||
ELEMENT_ARRAY_BUFFER = 34963
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(JsonBufferViewTarget,
|
||||
{
|
||||
{JsonBufferViewTarget::ARRAY_BUFFER, static_cast<unsigned>(JsonBufferViewTarget::ARRAY_BUFFER) },
|
||||
{JsonBufferViewTarget::ELEMENT_ARRAY_BUFFER, static_cast<unsigned>(JsonBufferViewTarget::ELEMENT_ARRAY_BUFFER)},
|
||||
});
|
||||
|
||||
class JsonBufferView
|
||||
{
|
||||
public:
|
||||
unsigned buffer;
|
||||
unsigned byteLength;
|
||||
std::optional<unsigned> byteOffset;
|
||||
std::optional<unsigned> byteStride;
|
||||
std::optional<JsonBufferViewTarget> target;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonBufferView, buffer, byteLength, byteOffset, byteStride, target);
|
||||
|
||||
enum class JsonAnimationChannelTargetPath
|
||||
{
|
||||
TRANSLATION,
|
||||
ROTATION,
|
||||
SCALE,
|
||||
WEIGHTS
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(JsonAnimationChannelTargetPath,
|
||||
{
|
||||
{JsonAnimationChannelTargetPath::TRANSLATION, "translation"},
|
||||
{JsonAnimationChannelTargetPath::ROTATION, "rotation" },
|
||||
{JsonAnimationChannelTargetPath::SCALE, "scale" },
|
||||
{JsonAnimationChannelTargetPath::WEIGHTS, "weights" },
|
||||
});
|
||||
|
||||
class JsonAnimationChannelTarget
|
||||
{
|
||||
public:
|
||||
unsigned node;
|
||||
JsonAnimationChannelTargetPath path;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAnimationChannelTarget, node, path);
|
||||
|
||||
class JsonAnimationChannel
|
||||
{
|
||||
public:
|
||||
unsigned sampler;
|
||||
JsonAnimationChannelTarget target;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAnimationChannel, sampler, target);
|
||||
|
||||
enum class JsonAnimationSamplerInterpolation
|
||||
{
|
||||
LINEAR,
|
||||
STEP,
|
||||
CUBIC_SPLINE
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(JsonAnimationSamplerInterpolation,
|
||||
{
|
||||
{JsonAnimationSamplerInterpolation::LINEAR, "LINEAR" },
|
||||
{JsonAnimationSamplerInterpolation::STEP, "STEP" },
|
||||
{JsonAnimationSamplerInterpolation::CUBIC_SPLINE, "CUBICSPLINE"},
|
||||
});
|
||||
|
||||
class JsonAnimationSampler
|
||||
{
|
||||
public:
|
||||
unsigned input;
|
||||
std::optional<JsonAnimationSamplerInterpolation> interpolation;
|
||||
unsigned output;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAnimationSampler, input, interpolation, output);
|
||||
|
||||
class JsonAnimation
|
||||
{
|
||||
public:
|
||||
std::vector<JsonAnimationChannel> channels;
|
||||
std::vector<JsonAnimationSampler> samplers;
|
||||
std::optional<std::string> name;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonAnimation, channels, samplers, name);
|
||||
|
||||
class JsonTextureInfo
|
||||
{
|
||||
public:
|
||||
unsigned index;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonTextureInfo, index);
|
||||
|
||||
class JsonPbrMetallicRoughness
|
||||
{
|
||||
public:
|
||||
std::optional<JsonTextureInfo> baseColorTexture;
|
||||
std::optional<float> metallicFactor;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonPbrMetallicRoughness, baseColorTexture, metallicFactor);
|
||||
|
||||
class JsonNormalTextureInfo
|
||||
{
|
||||
public:
|
||||
unsigned index;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonNormalTextureInfo, index);
|
||||
|
||||
class JsonMaterial
|
||||
{
|
||||
public:
|
||||
std::optional<std::string> name;
|
||||
std::optional<JsonPbrMetallicRoughness> pbrMetallicRoughness;
|
||||
std::optional<JsonNormalTextureInfo> normalTexture;
|
||||
std::optional<bool> doubleSided;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonMaterial, name, pbrMetallicRoughness, normalTexture, doubleSided);
|
||||
|
||||
enum class JsonMeshPrimitivesMode
|
||||
{
|
||||
POINTS = 0,
|
||||
LINES = 1,
|
||||
LINE_LOOP = 2,
|
||||
LINE_STRIP = 3,
|
||||
TRIANGLES = 4,
|
||||
TRIANGLES_STRIP = 5,
|
||||
TRIANGLE_FAN = 6
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(JsonMeshPrimitivesMode,
|
||||
{
|
||||
{JsonMeshPrimitivesMode::POINTS, static_cast<unsigned>(JsonMeshPrimitivesMode::POINTS) },
|
||||
{JsonMeshPrimitivesMode::LINES, static_cast<unsigned>(JsonMeshPrimitivesMode::LINES) },
|
||||
{JsonMeshPrimitivesMode::LINE_LOOP, static_cast<unsigned>(JsonMeshPrimitivesMode::LINE_LOOP) },
|
||||
{JsonMeshPrimitivesMode::LINE_STRIP, static_cast<unsigned>(JsonMeshPrimitivesMode::LINE_STRIP) },
|
||||
{JsonMeshPrimitivesMode::TRIANGLES, static_cast<unsigned>(JsonMeshPrimitivesMode::TRIANGLES) },
|
||||
{JsonMeshPrimitivesMode::TRIANGLES_STRIP, static_cast<unsigned>(JsonMeshPrimitivesMode::TRIANGLES_STRIP)},
|
||||
{JsonMeshPrimitivesMode::TRIANGLE_FAN, static_cast<unsigned>(JsonMeshPrimitivesMode::TRIANGLE_FAN) },
|
||||
});
|
||||
|
||||
// This should probably be a map, but the supported models do not have arbitrary primitives anyway
|
||||
class JsonMeshPrimitivesAttributes
|
||||
{
|
||||
public:
|
||||
std::optional<unsigned> POSITION;
|
||||
std::optional<unsigned> NORMAL;
|
||||
std::optional<unsigned> TEXCOORD_0;
|
||||
std::optional<unsigned> JOINTS_0;
|
||||
std::optional<unsigned> WEIGHTS_0;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonMeshPrimitivesAttributes, POSITION, NORMAL, TEXCOORD_0, JOINTS_0, WEIGHTS_0);
|
||||
|
||||
class JsonMeshPrimitives
|
||||
{
|
||||
public:
|
||||
JsonMeshPrimitivesAttributes attributes;
|
||||
std::optional<unsigned> indices;
|
||||
std::optional<unsigned> material;
|
||||
std::optional<JsonMeshPrimitivesMode> mode;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonMeshPrimitives, attributes, indices, material, mode);
|
||||
|
||||
class JsonMesh
|
||||
{
|
||||
public:
|
||||
std::vector<JsonMeshPrimitives> primitives;
|
||||
std::optional<std::vector<float>> weights;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonMesh, primitives, weights);
|
||||
|
||||
class JsonSkin
|
||||
{
|
||||
public:
|
||||
std::optional<unsigned> inverseBindMatrices;
|
||||
std::optional<unsigned> skeleton;
|
||||
std::vector<unsigned> joints;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonSkin, inverseBindMatrices, skeleton, joints);
|
||||
|
||||
class JsonScene
|
||||
{
|
||||
public:
|
||||
std::vector<unsigned> nodes;
|
||||
std::optional<std::string> name;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonScene, nodes, name);
|
||||
|
||||
class JsonTexture
|
||||
{
|
||||
public:
|
||||
unsigned source;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonTexture, source);
|
||||
|
||||
class JsonImage
|
||||
{
|
||||
public:
|
||||
std::optional<std::string> uri;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(JsonImage, uri);
|
||||
|
||||
class JsonRoot
|
||||
{
|
||||
public:
|
||||
std::optional<std::vector<JsonAccessor>> accessors;
|
||||
std::optional<std::vector<JsonAnimation>> animations;
|
||||
JsonAsset asset;
|
||||
std::optional<std::vector<JsonBuffer>> buffers;
|
||||
std::optional<std::vector<JsonBufferView>> bufferViews;
|
||||
std::optional<std::vector<JsonImage>> images;
|
||||
std::optional<std::vector<JsonMaterial>> materials;
|
||||
std::optional<std::vector<JsonMesh>> meshes;
|
||||
std::optional<std::vector<JsonNode>> nodes;
|
||||
std::optional<std::vector<JsonSkin>> skins;
|
||||
std::optional<unsigned> scene;
|
||||
std::optional<std::vector<JsonScene>> scenes;
|
||||
std::optional<std::vector<JsonTexture>> textures;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(
|
||||
JsonRoot, accessors, animations, asset, buffers, bufferViews, images, materials, meshes, nodes, skins, scene, scenes, textures);
|
||||
} // namespace gltf
|
@ -3,13 +3,8 @@
|
||||
#include "Math/Quaternion.h"
|
||||
#include "Utils/DistinctMapper.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
struct XModelObject
|
||||
{
|
||||
std::string name;
|
||||
};
|
||||
#include <vector>
|
||||
|
||||
struct XModelBone
|
||||
{
|
||||
@ -30,8 +25,7 @@ struct XModelBoneWeight
|
||||
|
||||
struct XModelVertexBoneWeightCollection
|
||||
{
|
||||
std::unique_ptr<XModelBoneWeight[]> weights;
|
||||
size_t totalWeightCount;
|
||||
std::vector<XModelBoneWeight> weights;
|
||||
};
|
||||
|
||||
struct XModelVertexBoneWeights
|
||||
@ -51,8 +45,6 @@ struct XModelVertex
|
||||
struct XModelFace
|
||||
{
|
||||
int vertexIndex[3];
|
||||
int objectIndex;
|
||||
int materialIndex;
|
||||
};
|
||||
|
||||
struct XModelMaterial
|
||||
@ -89,10 +81,30 @@ struct XModelMaterial
|
||||
float blinn[2];
|
||||
float phong;
|
||||
std::string colorMapName;
|
||||
std::string normalMapName;
|
||||
std::string specularMapName;
|
||||
|
||||
void ApplyDefaults();
|
||||
};
|
||||
|
||||
struct XModelObject
|
||||
{
|
||||
std::string name;
|
||||
int materialIndex;
|
||||
std::vector<XModelFace> m_faces;
|
||||
};
|
||||
|
||||
struct XModelCommon
|
||||
{
|
||||
std::string m_name;
|
||||
std::vector<XModelObject> m_objects;
|
||||
std::vector<XModelBone> m_bones;
|
||||
std::vector<XModelMaterial> m_materials;
|
||||
std::vector<XModelVertex> m_vertices;
|
||||
std::vector<XModelVertexBoneWeights> m_vertex_bone_weights;
|
||||
XModelVertexBoneWeightCollection m_bone_weight_data;
|
||||
};
|
||||
|
||||
struct VertexMergerPos
|
||||
{
|
||||
float x;
|
44
src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp
Normal file
44
src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include "AssetLoaderXModel.h"
|
||||
|
||||
#include "Game/T6/T6.h"
|
||||
#include "Game/T6/XModel/JsonXModelLoader.h"
|
||||
#include "Pool/GlobalAssetPool.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
|
||||
using namespace T6;
|
||||
|
||||
void* AssetLoaderXModel::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory)
|
||||
{
|
||||
auto* xmodel = memory->Create<XModel>();
|
||||
memset(xmodel, 0, sizeof(XModel));
|
||||
xmodel->name = memory->Dup(assetName.c_str());
|
||||
|
||||
return xmodel;
|
||||
}
|
||||
|
||||
bool AssetLoaderXModel::CanLoadFromRaw() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AssetLoaderXModel::LoadFromRaw(
|
||||
const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const
|
||||
{
|
||||
const auto file = searchPath->Open(std::format("xmodel/{}.json", assetName));
|
||||
if (!file.IsOpen())
|
||||
return false;
|
||||
|
||||
auto* xmodel = memory->Alloc<XModel>();
|
||||
xmodel->name = memory->Dup(assetName.c_str());
|
||||
|
||||
std::vector<XAssetInfoGeneric*> dependencies;
|
||||
if (LoadXModelAsJson(*file.m_stream, *xmodel, memory, manager, dependencies))
|
||||
manager->AddAsset<AssetXModel>(assetName, xmodel, std::move(dependencies));
|
||||
else
|
||||
std::cerr << "Failed to load xmodel \"" << assetName << "\"\n";
|
||||
|
||||
return true;
|
||||
}
|
19
src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.h
Normal file
19
src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderXModel.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "AssetLoading/BasicAssetLoader.h"
|
||||
#include "AssetLoading/IAssetLoadingManager.h"
|
||||
#include "Game/T6/T6.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
|
||||
namespace T6
|
||||
{
|
||||
class AssetLoaderXModel final : public BasicAssetLoader<ASSET_TYPE_XMODEL, XModel>
|
||||
{
|
||||
static std::string GetFileNameForAsset(const std::string& assetName);
|
||||
|
||||
public:
|
||||
_NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override;
|
||||
_NODISCARD bool CanLoadFromRaw() const override;
|
||||
bool
|
||||
LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const override;
|
||||
};
|
||||
} // namespace T6
|
117
src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp
Normal file
117
src/ObjLoading/Game/T6/XModel/JsonXModelLoader.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
#include "JsonXModelLoader.h"
|
||||
|
||||
#include "Game/T6/CommonT6.h"
|
||||
#include "Game/T6/Json/JsonXModel.h"
|
||||
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <vector>
|
||||
|
||||
using namespace nlohmann;
|
||||
using namespace T6;
|
||||
|
||||
namespace
|
||||
{
|
||||
class JsonLoader
|
||||
{
|
||||
public:
|
||||
JsonLoader(std::istream& stream, MemoryManager& memory, IAssetLoadingManager& manager, std::set<XAssetInfoGeneric*>& dependencies)
|
||||
: m_stream(stream),
|
||||
m_memory(memory),
|
||||
m_manager(manager),
|
||||
m_dependencies(dependencies)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
bool Load(XModel& xmodel) const
|
||||
{
|
||||
const auto jRoot = json::parse(m_stream);
|
||||
std::string type;
|
||||
unsigned version;
|
||||
|
||||
jRoot.at("_type").get_to(type);
|
||||
jRoot.at("_version").get_to(version);
|
||||
|
||||
if (type != "xmodel" || version != 1u)
|
||||
{
|
||||
std::cerr << "Tried to load xmodel \"" << xmodel.name << "\" but did not find expected type material of version 1\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto jXModel = jRoot.get<JsonXModel>();
|
||||
return CreateXModelFromJson(jXModel, xmodel);
|
||||
}
|
||||
|
||||
private:
|
||||
static void PrintError(const XModel& xmodel, const std::string& message)
|
||||
{
|
||||
std::cerr << "Cannot load xmodel \"" << xmodel.name << "\": " << message << "\n";
|
||||
}
|
||||
|
||||
bool CreateXModelFromJson(const JsonXModel& jXModel, XModel& xmodel) const
|
||||
{
|
||||
xmodel.collLod = static_cast<uint16_t>(jXModel.collLod);
|
||||
|
||||
if (jXModel.physPreset)
|
||||
{
|
||||
auto* physPreset = m_manager.LoadDependency<AssetPhysPreset>(jXModel.physPreset.value());
|
||||
if (!physPreset)
|
||||
{
|
||||
PrintError(xmodel, "Could not find phys preset");
|
||||
return false;
|
||||
}
|
||||
m_dependencies.emplace(physPreset);
|
||||
xmodel.physPreset = physPreset->Asset();
|
||||
}
|
||||
else
|
||||
{
|
||||
xmodel.physPreset = nullptr;
|
||||
}
|
||||
|
||||
if (jXModel.physConstraints)
|
||||
{
|
||||
auto* physConstraints = m_manager.LoadDependency<AssetPhysConstraints>(jXModel.physConstraints.value());
|
||||
if (!physConstraints)
|
||||
{
|
||||
PrintError(xmodel, "Could not find phys constraints");
|
||||
return false;
|
||||
}
|
||||
m_dependencies.emplace(physConstraints);
|
||||
xmodel.physConstraints = physConstraints->Asset();
|
||||
}
|
||||
else
|
||||
{
|
||||
xmodel.physConstraints = nullptr;
|
||||
}
|
||||
|
||||
xmodel.flags = jXModel.flags;
|
||||
xmodel.lightingOriginOffset.x = jXModel.lightingOriginOffset.x;
|
||||
xmodel.lightingOriginOffset.y = jXModel.lightingOriginOffset.y;
|
||||
xmodel.lightingOriginOffset.z = jXModel.lightingOriginOffset.z;
|
||||
xmodel.lightingOriginRange = jXModel.lightingOriginRange;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::istream& m_stream;
|
||||
MemoryManager& m_memory;
|
||||
IAssetLoadingManager& m_manager;
|
||||
std::set<XAssetInfoGeneric*>& m_dependencies;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace T6
|
||||
{
|
||||
bool LoadXModelAsJson(
|
||||
std::istream& stream, XModel& xmodel, MemoryManager* memory, IAssetLoadingManager* manager, std::vector<XAssetInfoGeneric*>& dependencies)
|
||||
{
|
||||
std::set<XAssetInfoGeneric*> dependenciesSet;
|
||||
const JsonLoader loader(stream, *memory, *manager, dependenciesSet);
|
||||
|
||||
dependencies.assign(dependenciesSet.cbegin(), dependenciesSet.cend());
|
||||
|
||||
return loader.Load(xmodel);
|
||||
}
|
||||
} // namespace T6
|
13
src/ObjLoading/Game/T6/XModel/JsonXModelLoader.h
Normal file
13
src/ObjLoading/Game/T6/XModel/JsonXModelLoader.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "AssetLoading/IAssetLoadingManager.h"
|
||||
#include "Game/T6/T6.h"
|
||||
#include "Utils/MemoryManager.h"
|
||||
|
||||
#include <istream>
|
||||
|
||||
namespace T6
|
||||
{
|
||||
bool LoadXModelAsJson(
|
||||
std::istream& stream, XModel& xmodel, MemoryManager* memory, IAssetLoadingManager* manager, std::vector<XAssetInfoGeneric*>& dependencies);
|
||||
} // namespace T6
|
@ -19,6 +19,7 @@ function ObjWriting:link(links)
|
||||
links:linkto(ZoneCommon)
|
||||
links:linkto(minilzo)
|
||||
links:linkto(minizip)
|
||||
links:linkto(libtomcrypt)
|
||||
end
|
||||
|
||||
function ObjWriting:use()
|
||||
@ -55,5 +56,6 @@ function ObjWriting:project()
|
||||
minilzo:include(includes)
|
||||
minizip:include(includes)
|
||||
json:include(includes)
|
||||
libtomcrypt:include(includes)
|
||||
|
||||
end
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,37 +2,11 @@
|
||||
|
||||
#include "Dumping/AbstractAssetDumper.h"
|
||||
#include "Game/IW3/IW3.h"
|
||||
#include "Model/Obj/ObjWriter.h"
|
||||
#include "Model/XModel/AbstractXModelWriter.h"
|
||||
#include "Utils/DistinctMapper.h"
|
||||
|
||||
namespace IW3
|
||||
{
|
||||
class AssetDumperXModel final : public AbstractAssetDumper<XModel>
|
||||
{
|
||||
static GfxImage* GetMaterialColorMap(const Material* material);
|
||||
static GfxImage* GetMaterialNormalMap(const Material* material);
|
||||
static GfxImage* GetMaterialSpecularMap(const Material* material);
|
||||
|
||||
static void AddObjMaterials(ObjWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model);
|
||||
static void AddObjObjects(ObjWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModel* model, unsigned lod);
|
||||
static void AddObjVertices(ObjWriter& writer, const XModel* model, unsigned lod);
|
||||
static void AddObjFaces(ObjWriter& writer, const XModel* model, unsigned lod);
|
||||
static void DumpObjLod(AssetDumpingContext& context, XAssetInfo<XModel>* asset, unsigned lod);
|
||||
static void DumpObjMat(const AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
static void DumpObj(AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
|
||||
static void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model);
|
||||
static void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model);
|
||||
static void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, unsigned lod);
|
||||
static void AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, unsigned lod);
|
||||
static void AllocateXModelBoneWeights(const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection);
|
||||
static void
|
||||
AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection);
|
||||
static void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModel* model, unsigned lod);
|
||||
static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo<XModel>* asset, unsigned lod);
|
||||
static void DumpXModelExport(const AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
|
||||
protected:
|
||||
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
||||
void DumpAsset(AssetDumpingContext& context, XAssetInfo<XModel>* asset) override;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,37 +2,11 @@
|
||||
|
||||
#include "Dumping/AbstractAssetDumper.h"
|
||||
#include "Game/IW4/IW4.h"
|
||||
#include "Model/Obj/ObjWriter.h"
|
||||
#include "Model/XModel/AbstractXModelWriter.h"
|
||||
#include "Utils/DistinctMapper.h"
|
||||
|
||||
namespace IW4
|
||||
{
|
||||
class AssetDumperXModel final : public AbstractAssetDumper<XModel>
|
||||
{
|
||||
static GfxImage* GetMaterialColorMap(const Material* material);
|
||||
static GfxImage* GetMaterialNormalMap(const Material* material);
|
||||
static GfxImage* GetMaterialSpecularMap(const Material* material);
|
||||
|
||||
static void AddObjMaterials(ObjWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model);
|
||||
static void AddObjObjects(ObjWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex);
|
||||
static void AddObjVertices(ObjWriter& writer, const XModelSurfs* modelSurfs);
|
||||
static void AddObjFaces(ObjWriter& writer, const XModelSurfs* modelSurfs);
|
||||
static void DumpObjLod(const AssetDumpingContext& context, XAssetInfo<XModel>* asset, const unsigned lod);
|
||||
static void DumpObjMat(const AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
static void DumpObj(AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
|
||||
static void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model);
|
||||
static void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model);
|
||||
static void AddXModelObjects(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs);
|
||||
static void AddXModelVertices(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs);
|
||||
static void AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection);
|
||||
static void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection);
|
||||
static void
|
||||
AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex);
|
||||
static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo<XModel>* asset, unsigned lod);
|
||||
static void DumpXModelExport(AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
|
||||
protected:
|
||||
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
||||
void DumpAsset(AssetDumpingContext& context, XAssetInfo<XModel>* asset) override;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,37 +2,11 @@
|
||||
|
||||
#include "Dumping/AbstractAssetDumper.h"
|
||||
#include "Game/IW5/IW5.h"
|
||||
#include "Model/Obj/ObjWriter.h"
|
||||
#include "Model/XModel/AbstractXModelWriter.h"
|
||||
#include "Utils/DistinctMapper.h"
|
||||
|
||||
namespace IW5
|
||||
{
|
||||
class AssetDumperXModel final : public AbstractAssetDumper<XModel>
|
||||
{
|
||||
static GfxImage* GetMaterialColorMap(const Material* material);
|
||||
static GfxImage* GetMaterialNormalMap(const Material* material);
|
||||
static GfxImage* GetMaterialSpecularMap(const Material* material);
|
||||
|
||||
static void AddObjMaterials(ObjWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model);
|
||||
static void AddObjObjects(ObjWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex);
|
||||
static void AddObjVertices(ObjWriter& writer, const XModelSurfs* modelSurfs);
|
||||
static void AddObjFaces(ObjWriter& writer, const XModelSurfs* modelSurfs);
|
||||
static void DumpObjLod(const AssetDumpingContext& context, XAssetInfo<XModel>* asset, const unsigned lod);
|
||||
static void DumpObjMat(const AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
static void DumpObj(AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
|
||||
static void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model);
|
||||
static void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model);
|
||||
static void AddXModelObjects(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs);
|
||||
static void AddXModelVertices(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs);
|
||||
static void AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection);
|
||||
static void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection);
|
||||
static void
|
||||
AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex);
|
||||
static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo<XModel>* asset, unsigned lod);
|
||||
static void DumpXModelExport(AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
|
||||
protected:
|
||||
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
||||
void DumpAsset(AssetDumpingContext& context, XAssetInfo<XModel>* asset) override;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,37 +2,11 @@
|
||||
|
||||
#include "Dumping/AbstractAssetDumper.h"
|
||||
#include "Game/T5/T5.h"
|
||||
#include "Model/Obj/ObjWriter.h"
|
||||
#include "Model/XModel/AbstractXModelWriter.h"
|
||||
#include "Utils/DistinctMapper.h"
|
||||
|
||||
namespace T5
|
||||
{
|
||||
class AssetDumperXModel final : public AbstractAssetDumper<XModel>
|
||||
{
|
||||
static GfxImage* GetMaterialColorMap(const Material* material);
|
||||
static GfxImage* GetMaterialNormalMap(const Material* material);
|
||||
static GfxImage* GetMaterialSpecularMap(const Material* material);
|
||||
|
||||
static void AddObjMaterials(ObjWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model);
|
||||
static void AddObjObjects(ObjWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModel* model, unsigned lod);
|
||||
static void AddObjVertices(ObjWriter& writer, const XModel* model, unsigned lod);
|
||||
static void AddObjFaces(ObjWriter& writer, const XModel* model, unsigned lod);
|
||||
static void DumpObjLod(AssetDumpingContext& context, XAssetInfo<XModel>* asset, unsigned lod);
|
||||
static void DumpObjMat(const AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
static void DumpObj(AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
|
||||
static void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model);
|
||||
static void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model);
|
||||
static void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, unsigned lod);
|
||||
static void AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, unsigned lod);
|
||||
static void AllocateXModelBoneWeights(const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection);
|
||||
static void
|
||||
AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection);
|
||||
static void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModel* model, unsigned lod);
|
||||
static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo<XModel>* asset, unsigned lod);
|
||||
static void DumpXModelExport(const AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
|
||||
protected:
|
||||
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
||||
void DumpAsset(AssetDumpingContext& context, XAssetInfo<XModel>* asset) override;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,37 +2,11 @@
|
||||
|
||||
#include "Dumping/AbstractAssetDumper.h"
|
||||
#include "Game/T6/T6.h"
|
||||
#include "Model/Obj/ObjWriter.h"
|
||||
#include "Model/XModel/AbstractXModelWriter.h"
|
||||
#include "Utils/DistinctMapper.h"
|
||||
|
||||
namespace T6
|
||||
{
|
||||
class AssetDumperXModel final : public AbstractAssetDumper<XModel>
|
||||
{
|
||||
static GfxImage* GetMaterialColorMap(const Material* material);
|
||||
static GfxImage* GetMaterialNormalMap(const Material* material);
|
||||
static GfxImage* GetMaterialSpecularMap(const Material* material);
|
||||
|
||||
static void AddObjMaterials(ObjWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model);
|
||||
static void AddObjObjects(ObjWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModel* model, unsigned lod);
|
||||
static void AddObjVertices(ObjWriter& writer, const XModel* model, unsigned lod);
|
||||
static void AddObjFaces(ObjWriter& writer, const XModel* model, unsigned lod);
|
||||
static void DumpObjLod(AssetDumpingContext& context, XAssetInfo<XModel>* asset, unsigned lod);
|
||||
static void DumpObjMat(const AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
static void DumpObj(AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
|
||||
static void AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model);
|
||||
static void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model);
|
||||
static void AddXModelObjects(AbstractXModelWriter& writer, const XModel* model, unsigned lod);
|
||||
static void AddXModelVertices(AbstractXModelWriter& writer, const XModel* model, unsigned lod);
|
||||
static void AllocateXModelBoneWeights(const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection);
|
||||
static void
|
||||
AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModel* model, unsigned lod, XModelVertexBoneWeightCollection& weightCollection);
|
||||
static void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModel* model, unsigned lod);
|
||||
static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo<XModel>* asset, unsigned lod);
|
||||
static void DumpXModelExport(const AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
|
||||
protected:
|
||||
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
||||
void DumpAsset(AssetDumpingContext& context, XAssetInfo<XModel>* asset) override;
|
||||
|
71
src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp
Normal file
71
src/ObjWriting/Game/T6/XModel/JsonXModelWriter.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include "JsonXModelWriter.h"
|
||||
|
||||
#include "Game/T6/CommonT6.h"
|
||||
#include "Game/T6/Json/JsonXModel.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace nlohmann;
|
||||
using namespace T6;
|
||||
|
||||
namespace
|
||||
{
|
||||
class JsonDumper
|
||||
{
|
||||
public:
|
||||
JsonDumper(AssetDumpingContext& context, std::ostream& stream)
|
||||
: m_stream(stream)
|
||||
{
|
||||
}
|
||||
|
||||
void Dump(const XModel* xmodel) const
|
||||
{
|
||||
JsonXModel jsonXModel;
|
||||
CreateJsonXModel(jsonXModel, *xmodel);
|
||||
json jRoot = jsonXModel;
|
||||
|
||||
jRoot["_type"] = "xmodel";
|
||||
jRoot["_version"] = 1;
|
||||
|
||||
m_stream << std::setw(4) << jRoot << "\n";
|
||||
}
|
||||
|
||||
private:
|
||||
static const char* AssetName(const char* input)
|
||||
{
|
||||
if (input && input[0] == ',')
|
||||
return &input[1];
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
void CreateJsonXModel(JsonXModel& jXModel, const XModel& xmodel) const
|
||||
{
|
||||
jXModel.collLod = xmodel.collLod;
|
||||
|
||||
if (xmodel.physPreset && xmodel.physPreset->name)
|
||||
jXModel.physPreset = AssetName(xmodel.physPreset->name);
|
||||
|
||||
if (xmodel.physConstraints && xmodel.physConstraints->name)
|
||||
jXModel.physConstraints = AssetName(xmodel.physConstraints->name);
|
||||
|
||||
jXModel.flags = xmodel.flags;
|
||||
jXModel.lightingOriginOffset.x = xmodel.lightingOriginOffset.x;
|
||||
jXModel.lightingOriginOffset.y = xmodel.lightingOriginOffset.y;
|
||||
jXModel.lightingOriginOffset.z = xmodel.lightingOriginOffset.z;
|
||||
jXModel.lightingOriginRange = xmodel.lightingOriginRange;
|
||||
}
|
||||
|
||||
std::ostream& m_stream;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace T6
|
||||
{
|
||||
void DumpXModelAsJson(std::ostream& stream, const XModel* xmodel, AssetDumpingContext& context)
|
||||
{
|
||||
const JsonDumper dumper(context, stream);
|
||||
dumper.Dump(xmodel);
|
||||
}
|
||||
} // namespace T6
|
11
src/ObjWriting/Game/T6/XModel/JsonXModelWriter.h
Normal file
11
src/ObjWriting/Game/T6/XModel/JsonXModelWriter.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "Dumping/AssetDumpingContext.h"
|
||||
#include "Game/T6/T6.h"
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace T6
|
||||
{
|
||||
void DumpXModelAsJson(std::ostream& stream, const XModel* xmodel, AssetDumpingContext& context);
|
||||
} // namespace T6
|
@ -1,157 +0,0 @@
|
||||
#include "ObjWriter.h"
|
||||
|
||||
#include "Game/IW4/CommonIW4.h"
|
||||
|
||||
ObjWriter::ObjWriter(std::string gameName, std::string zoneName)
|
||||
: m_game_name(std::move(gameName)),
|
||||
m_zone_name(std::move(zoneName))
|
||||
{
|
||||
}
|
||||
|
||||
void ObjWriter::AddObject(ObjObject object)
|
||||
{
|
||||
m_objects.emplace_back(std::move(object));
|
||||
m_object_data.emplace_back();
|
||||
}
|
||||
|
||||
void ObjWriter::AddMaterial(MtlMaterial material)
|
||||
{
|
||||
m_materials.emplace_back(std::move(material));
|
||||
}
|
||||
|
||||
void ObjWriter::AddVertex(const int objectId, const ObjVertex vertex)
|
||||
{
|
||||
if (objectId < 0 || static_cast<unsigned>(objectId) >= m_object_data.size())
|
||||
return;
|
||||
|
||||
m_object_data[objectId].m_vertices.Add(vertex);
|
||||
}
|
||||
|
||||
void ObjWriter::AddNormal(const int objectId, const ObjNormal normal)
|
||||
{
|
||||
if (objectId < 0 || static_cast<unsigned>(objectId) >= m_object_data.size())
|
||||
return;
|
||||
|
||||
m_object_data[objectId].m_normals.Add(normal);
|
||||
}
|
||||
|
||||
void ObjWriter::AddUv(const int objectId, const ObjUv uv)
|
||||
{
|
||||
if (objectId < 0 || static_cast<unsigned>(objectId) >= m_object_data.size())
|
||||
return;
|
||||
|
||||
m_object_data[objectId].m_uvs.Add(uv);
|
||||
}
|
||||
|
||||
void ObjWriter::AddFace(const int objectId, const ObjFace face)
|
||||
{
|
||||
if (objectId < 0 || static_cast<unsigned>(objectId) >= m_object_data.size())
|
||||
return;
|
||||
|
||||
m_object_data[objectId].m_faces.push_back(face);
|
||||
}
|
||||
|
||||
void ObjWriter::GetObjObjectDataOffsets(std::vector<ObjObjectDataOffsets>& inputOffsets, std::vector<ObjObjectDataOffsets>& distinctOffsets)
|
||||
{
|
||||
ObjObjectDataOffsets currentInputOffsets{};
|
||||
ObjObjectDataOffsets currentDistinctOffsets{};
|
||||
|
||||
for (const auto& objectData : m_object_data)
|
||||
{
|
||||
inputOffsets.push_back(currentInputOffsets);
|
||||
distinctOffsets.push_back(currentDistinctOffsets);
|
||||
|
||||
currentInputOffsets.vertexOffset += objectData.m_vertices.GetInputValueCount();
|
||||
currentInputOffsets.normalOffset += objectData.m_normals.GetInputValueCount();
|
||||
currentInputOffsets.uvOffset += objectData.m_uvs.GetInputValueCount();
|
||||
currentDistinctOffsets.vertexOffset += objectData.m_vertices.GetDistinctValueCount();
|
||||
currentDistinctOffsets.normalOffset += objectData.m_normals.GetDistinctValueCount();
|
||||
currentDistinctOffsets.uvOffset += objectData.m_uvs.GetDistinctValueCount();
|
||||
}
|
||||
}
|
||||
|
||||
void ObjWriter::WriteObj(std::ostream& stream)
|
||||
{
|
||||
WriteObj(stream, std::string());
|
||||
}
|
||||
|
||||
void ObjWriter::WriteObj(std::ostream& stream, const std::string& mtlName)
|
||||
{
|
||||
stream << "# OpenAssetTools OBJ File ( " << m_game_name << ")\n";
|
||||
stream << "# Game Origin: " << m_game_name << "\n";
|
||||
stream << "# Zone Origin: " << m_zone_name << "\n";
|
||||
|
||||
if (!mtlName.empty())
|
||||
stream << "mtllib " << mtlName << "\n";
|
||||
|
||||
std::vector<ObjObjectDataOffsets> inputOffsetsByObject;
|
||||
std::vector<ObjObjectDataOffsets> distinctOffsetsByObject;
|
||||
GetObjObjectDataOffsets(inputOffsetsByObject, distinctOffsetsByObject);
|
||||
|
||||
auto objectIndex = 0;
|
||||
for (const auto& object : m_objects)
|
||||
{
|
||||
const auto& objectData = m_object_data[objectIndex];
|
||||
stream << "o " << object.name << "\n";
|
||||
|
||||
for (const auto& v : objectData.m_vertices.GetDistinctValues())
|
||||
stream << "v " << v.coordinates[0] << " " << v.coordinates[1] << " " << v.coordinates[2] << "\n";
|
||||
for (const auto& uv : objectData.m_uvs.GetDistinctValues())
|
||||
stream << "vt " << uv.uv[0] << " " << uv.uv[1] << "\n";
|
||||
for (const auto& n : objectData.m_normals.GetDistinctValues())
|
||||
stream << "vn " << n.normal[0] << " " << n.normal[1] << " " << n.normal[2] << "\n";
|
||||
|
||||
if (object.materialIndex >= 0 && static_cast<unsigned>(object.materialIndex) < m_materials.size())
|
||||
stream << "usemtl " << m_materials[object.materialIndex].materialName << "\n";
|
||||
|
||||
for (const auto& f : objectData.m_faces)
|
||||
{
|
||||
const size_t v[3]{objectData.m_vertices.GetDistinctPositionByInputPosition(f.vertexIndex[0] - inputOffsetsByObject[objectIndex].vertexOffset)
|
||||
+ distinctOffsetsByObject[objectIndex].vertexOffset + 1,
|
||||
objectData.m_vertices.GetDistinctPositionByInputPosition(f.vertexIndex[1] - inputOffsetsByObject[objectIndex].vertexOffset)
|
||||
+ distinctOffsetsByObject[objectIndex].vertexOffset + 1,
|
||||
objectData.m_vertices.GetDistinctPositionByInputPosition(f.vertexIndex[2] - inputOffsetsByObject[objectIndex].vertexOffset)
|
||||
+ distinctOffsetsByObject[objectIndex].vertexOffset + 1};
|
||||
const size_t n[3]{objectData.m_normals.GetDistinctPositionByInputPosition(f.normalIndex[0] - inputOffsetsByObject[objectIndex].normalOffset)
|
||||
+ distinctOffsetsByObject[objectIndex].normalOffset + 1,
|
||||
objectData.m_normals.GetDistinctPositionByInputPosition(f.normalIndex[1] - inputOffsetsByObject[objectIndex].normalOffset)
|
||||
+ distinctOffsetsByObject[objectIndex].normalOffset + 1,
|
||||
objectData.m_normals.GetDistinctPositionByInputPosition(f.normalIndex[2] - inputOffsetsByObject[objectIndex].normalOffset)
|
||||
+ distinctOffsetsByObject[objectIndex].normalOffset + 1};
|
||||
const size_t uv[3]{objectData.m_uvs.GetDistinctPositionByInputPosition(f.uvIndex[0] - inputOffsetsByObject[objectIndex].uvOffset)
|
||||
+ distinctOffsetsByObject[objectIndex].uvOffset + 1,
|
||||
objectData.m_uvs.GetDistinctPositionByInputPosition(f.uvIndex[1] - inputOffsetsByObject[objectIndex].uvOffset)
|
||||
+ distinctOffsetsByObject[objectIndex].uvOffset + 1,
|
||||
objectData.m_uvs.GetDistinctPositionByInputPosition(f.uvIndex[2] - inputOffsetsByObject[objectIndex].uvOffset)
|
||||
+ distinctOffsetsByObject[objectIndex].uvOffset + 1};
|
||||
|
||||
stream << "f " << v[0] << "/" << uv[0] << "/" << n[0] << " " << v[1] << "/" << uv[1] << "/" << n[1] << " " << v[2] << "/" << uv[2] << "/" << n[2]
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
objectIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjWriter::WriteMtl(std::ostream& stream)
|
||||
{
|
||||
stream << "# OpenAssetTools MAT File ( " << m_game_name << ")\n";
|
||||
stream << "# Game Origin: " << m_game_name << "\n";
|
||||
stream << "# Zone Origin: " << m_zone_name << "\n";
|
||||
stream << "# Material count: " << m_materials.size() << "\n";
|
||||
|
||||
for (const auto& material : m_materials)
|
||||
{
|
||||
stream << "\n";
|
||||
stream << "newmtl " << material.materialName << "\n";
|
||||
|
||||
if (!material.colorMapName.empty())
|
||||
stream << "map_Kd ../images/" << material.colorMapName << ".dds\n";
|
||||
|
||||
if (!material.normalMapName.empty())
|
||||
stream << "map_bump ../images/" << material.normalMapName << ".dds\n";
|
||||
|
||||
if (!material.specularMapName.empty())
|
||||
stream << "map_Ks ../images/" << material.specularMapName << ".dds\n";
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Model/Obj/ObjCommon.h"
|
||||
#include "Utils/DistinctMapper.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
|
||||
class ObjWriter
|
||||
{
|
||||
protected:
|
||||
struct ObjObjectData
|
||||
{
|
||||
DistinctMapper<ObjVertex> m_vertices;
|
||||
DistinctMapper<ObjNormal> m_normals;
|
||||
DistinctMapper<ObjUv> m_uvs;
|
||||
std::vector<ObjFace> m_faces;
|
||||
};
|
||||
|
||||
struct ObjObjectDataOffsets
|
||||
{
|
||||
size_t vertexOffset;
|
||||
size_t normalOffset;
|
||||
size_t uvOffset;
|
||||
};
|
||||
|
||||
std::string m_game_name;
|
||||
std::string m_zone_name;
|
||||
std::vector<ObjObject> m_objects;
|
||||
std::vector<ObjObjectData> m_object_data;
|
||||
std::vector<MtlMaterial> m_materials;
|
||||
|
||||
void GetObjObjectDataOffsets(std::vector<ObjObjectDataOffsets>& inputOffsets, std::vector<ObjObjectDataOffsets>& distinctOffsets);
|
||||
|
||||
public:
|
||||
ObjWriter(std::string gameName, std::string zoneName);
|
||||
|
||||
void AddObject(ObjObject object);
|
||||
void AddMaterial(MtlMaterial material);
|
||||
void AddVertex(int objectId, ObjVertex vertex);
|
||||
void AddNormal(int objectId, ObjNormal normal);
|
||||
void AddUv(int objectId, ObjUv uv);
|
||||
void AddFace(int objectId, ObjFace face);
|
||||
|
||||
void WriteObj(std::ostream& stream);
|
||||
void WriteObj(std::ostream& stream, const std::string& mtlName);
|
||||
void WriteMtl(std::ostream& stream);
|
||||
};
|
@ -1,33 +0,0 @@
|
||||
#include "AbstractXModelWriter.h"
|
||||
|
||||
AbstractXModelWriter::AbstractXModelWriter() = default;
|
||||
|
||||
void AbstractXModelWriter::AddObject(XModelObject object)
|
||||
{
|
||||
m_objects.emplace_back(std::move(object));
|
||||
}
|
||||
|
||||
void AbstractXModelWriter::AddBone(XModelBone bone)
|
||||
{
|
||||
m_bones.emplace_back(std::move(bone));
|
||||
}
|
||||
|
||||
void AbstractXModelWriter::AddMaterial(XModelMaterial material)
|
||||
{
|
||||
m_materials.emplace_back(std::move(material));
|
||||
}
|
||||
|
||||
void AbstractXModelWriter::AddVertex(XModelVertex vertex)
|
||||
{
|
||||
m_vertices.emplace_back(vertex);
|
||||
}
|
||||
|
||||
void AbstractXModelWriter::AddVertexBoneWeights(XModelVertexBoneWeights vertexBoneWeights)
|
||||
{
|
||||
m_vertex_bone_weights.emplace_back(vertexBoneWeights);
|
||||
}
|
||||
|
||||
void AbstractXModelWriter::AddFace(XModelFace face)
|
||||
{
|
||||
m_faces.emplace_back(face);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Model/XModel/XModelCommon.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class AbstractXModelWriter
|
||||
{
|
||||
protected:
|
||||
std::vector<XModelObject> m_objects;
|
||||
std::vector<XModelBone> m_bones;
|
||||
std::vector<XModelMaterial> m_materials;
|
||||
std::vector<XModelVertex> m_vertices;
|
||||
std::vector<XModelVertexBoneWeights> m_vertex_bone_weights;
|
||||
std::vector<XModelFace> m_faces;
|
||||
|
||||
public:
|
||||
AbstractXModelWriter();
|
||||
|
||||
void AddObject(XModelObject object);
|
||||
void AddBone(XModelBone bone);
|
||||
void AddMaterial(XModelMaterial material);
|
||||
void AddVertex(XModelVertex vertex);
|
||||
void AddVertexBoneWeights(XModelVertexBoneWeights vertexBoneWeights);
|
||||
void AddFace(XModelFace face);
|
||||
};
|
@ -1,221 +0,0 @@
|
||||
#include "XModelExportWriter.h"
|
||||
|
||||
#include "Math/Quaternion.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
class XModelExportWriterBase : public XModelExportWriter
|
||||
{
|
||||
protected:
|
||||
std::string m_game_name;
|
||||
std::string m_zone_name;
|
||||
|
||||
VertexMerger m_vertex_merger;
|
||||
|
||||
void PrepareVertexMerger()
|
||||
{
|
||||
m_vertex_merger = VertexMerger(m_vertices.size());
|
||||
|
||||
auto vertexOffset = 0u;
|
||||
for (const auto& vertex : m_vertices)
|
||||
{
|
||||
XModelVertexBoneWeights weights{nullptr, 0};
|
||||
|
||||
if (vertexOffset < m_vertex_bone_weights.size())
|
||||
weights = m_vertex_bone_weights[vertexOffset];
|
||||
|
||||
m_vertex_merger.Add(VertexMergerPos{vertex.coordinates[0], vertex.coordinates[1], vertex.coordinates[2], weights.weights, weights.weightCount});
|
||||
|
||||
vertexOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteHeader(std::ostream& stream, const int version) const
|
||||
{
|
||||
stream << "// OpenAssetTools XMODEL_EXPORT File\n";
|
||||
stream << "// Game Origin: " << m_game_name << "\n";
|
||||
stream << "// Zone Origin: " << m_zone_name << "\n";
|
||||
stream << "MODEL\n";
|
||||
stream << "VERSION " << version << "\n";
|
||||
stream << "\n";
|
||||
}
|
||||
|
||||
void WriteBones(std::ostream& stream) const
|
||||
{
|
||||
stream << "NUMBONES " << m_bones.size() << "\n";
|
||||
size_t boneNum = 0u;
|
||||
for (const auto& bone : m_bones)
|
||||
{
|
||||
stream << "BONE " << boneNum << " ";
|
||||
if (bone.parentIndex < 0)
|
||||
stream << "-1";
|
||||
else
|
||||
stream << bone.parentIndex;
|
||||
|
||||
stream << " \"" << bone.name << "\"\n";
|
||||
boneNum++;
|
||||
}
|
||||
stream << "\n";
|
||||
|
||||
boneNum = 0u;
|
||||
for (const auto& bone : m_bones)
|
||||
{
|
||||
stream << "BONE " << boneNum << "\n";
|
||||
stream << "OFFSET ";
|
||||
stream << std::setprecision(6) << std::fixed << bone.globalOffset[0] << ", " << std::setprecision(6) << std::fixed << bone.globalOffset[1] << ", "
|
||||
<< std::setprecision(6) << std::fixed << bone.globalOffset[2] << "\n";
|
||||
|
||||
stream << "SCALE ";
|
||||
stream << std::setprecision(6) << std::fixed << bone.scale[0] << ", " << std::setprecision(6) << std::fixed << bone.scale[1] << ", "
|
||||
<< std::setprecision(6) << std::fixed << bone.scale[2] << "\n";
|
||||
|
||||
const Matrix32 mat = bone.globalRotation.ToMatrix();
|
||||
stream << "X " << std::setprecision(6) << std::fixed << mat.m_data[0][0] << ", " << std::setprecision(6) << std::fixed << mat.m_data[1][0] << ", "
|
||||
<< std::setprecision(6) << std::fixed << mat.m_data[2][0] << "\n";
|
||||
stream << "Y " << std::setprecision(6) << std::fixed << mat.m_data[0][1] << ", " << std::setprecision(6) << std::fixed << mat.m_data[1][1] << ", "
|
||||
<< std::setprecision(6) << std::fixed << mat.m_data[2][1] << "\n";
|
||||
stream << "Z " << std::setprecision(6) << std::fixed << mat.m_data[0][2] << ", " << std::setprecision(6) << std::fixed << mat.m_data[1][2] << ", "
|
||||
<< std::setprecision(6) << std::fixed << mat.m_data[2][2] << "\n";
|
||||
stream << "\n";
|
||||
boneNum++;
|
||||
}
|
||||
}
|
||||
|
||||
XModelExportWriterBase(std::string gameName, std::string zoneName)
|
||||
: m_game_name(std::move(gameName)),
|
||||
m_zone_name(std::move(zoneName))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class XModelExportWriter6 final : public XModelExportWriterBase
|
||||
{
|
||||
void WriteVertices(std::ostream& stream) const
|
||||
{
|
||||
const auto& distinctVertexValues = m_vertex_merger.GetDistinctValues();
|
||||
stream << "NUMVERTS " << distinctVertexValues.size() << "\n";
|
||||
size_t vertexNum = 0u;
|
||||
for (const auto& vertexPos : distinctVertexValues)
|
||||
{
|
||||
stream << "VERT " << vertexNum << "\n";
|
||||
stream << "OFFSET ";
|
||||
stream << std::setprecision(6) << std::fixed << vertexPos.x << ", " << std::setprecision(6) << std::fixed << vertexPos.y << ", "
|
||||
<< std::setprecision(6) << std::fixed << vertexPos.z << "\n";
|
||||
stream << "BONES " << vertexPos.weightCount << "\n";
|
||||
|
||||
for (auto weightIndex = 0u; weightIndex < vertexPos.weightCount; weightIndex++)
|
||||
{
|
||||
stream << "BONE " << vertexPos.weights[weightIndex].boneIndex << " " << std::setprecision(6) << std::fixed
|
||||
<< vertexPos.weights[weightIndex].weight << "\n";
|
||||
}
|
||||
stream << "\n";
|
||||
vertexNum++;
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteFaceVertex(std::ostream& stream, const size_t index, const XModelVertex& vertex)
|
||||
{
|
||||
stream << "VERT " << index << "\n";
|
||||
stream << "NORMAL " << std::setprecision(6) << std::fixed << vertex.normal[0] << " " << std::setprecision(6) << std::fixed << vertex.normal[1] << " "
|
||||
<< std::setprecision(6) << std::fixed << vertex.normal[2] << "\n";
|
||||
stream << "COLOR " << std::setprecision(6) << std::fixed << vertex.color[0] << " " << std::setprecision(6) << std::fixed << vertex.color[1] << " "
|
||||
<< std::setprecision(6) << std::fixed << vertex.color[2] << " " << std::setprecision(6) << std::fixed << vertex.color[3] << "\n";
|
||||
stream << "UV 1 " << std::setprecision(6) << std::fixed << vertex.uv[0] << " " << std::setprecision(6) << std::fixed << vertex.uv[1] << "\n";
|
||||
}
|
||||
|
||||
void WriteFaces(std::ostream& stream) const
|
||||
{
|
||||
stream << "NUMFACES " << m_faces.size() << "\n";
|
||||
for (const auto& face : m_faces)
|
||||
{
|
||||
const size_t distinctPositions[3]{
|
||||
m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[0]),
|
||||
m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[1]),
|
||||
m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[2]),
|
||||
};
|
||||
|
||||
const XModelVertex& v0 = m_vertices[face.vertexIndex[0]];
|
||||
const XModelVertex& v1 = m_vertices[face.vertexIndex[1]];
|
||||
const XModelVertex& v2 = m_vertices[face.vertexIndex[2]];
|
||||
|
||||
stream << "TRI " << face.objectIndex << " " << face.materialIndex << " 0 0\n";
|
||||
WriteFaceVertex(stream, distinctPositions[0], v0);
|
||||
WriteFaceVertex(stream, distinctPositions[1], v1);
|
||||
WriteFaceVertex(stream, distinctPositions[2], v2);
|
||||
stream << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void WriteObjects(std::ostream& stream) const
|
||||
{
|
||||
stream << "NUMOBJECTS " << m_objects.size() << "\n";
|
||||
size_t objectNum = 0u;
|
||||
for (const auto& object : m_objects)
|
||||
{
|
||||
stream << "OBJECT " << objectNum << " \"" << object.name << "\"\n";
|
||||
objectNum++;
|
||||
}
|
||||
stream << "\n";
|
||||
}
|
||||
|
||||
void WriteMaterials(std::ostream& stream) const
|
||||
{
|
||||
stream << "NUMMATERIALS " << m_materials.size() << "\n";
|
||||
size_t materialNum = 0u;
|
||||
for (const auto& material : m_materials)
|
||||
{
|
||||
const auto colorMapPath = "../images/" + material.colorMapName + ".dds";
|
||||
stream << "MATERIAL " << materialNum << " \"" << material.name << "\" \"" << material.materialTypeName << "\" \"" << colorMapPath << "\"\n";
|
||||
stream << "COLOR " << std::setprecision(6) << std::fixed << material.color[0] << " " << std::setprecision(6) << std::fixed << material.color[1]
|
||||
<< " " << std::setprecision(6) << std::fixed << material.color[2] << " " << std::setprecision(6) << std::fixed << material.color[3] << "\n";
|
||||
stream << "TRANSPARENCY " << std::setprecision(6) << std::fixed << material.transparency[0] << " " << std::setprecision(6) << std::fixed
|
||||
<< material.transparency[1] << " " << std::setprecision(6) << std::fixed << material.transparency[2] << " " << std::setprecision(6)
|
||||
<< std::fixed << material.transparency[3] << "\n";
|
||||
stream << "AMBIENTCOLOR " << std::setprecision(6) << std::fixed << material.ambientColor[0] << " " << std::setprecision(6) << std::fixed
|
||||
<< material.ambientColor[1] << " " << std::setprecision(6) << std::fixed << material.ambientColor[2] << " " << std::setprecision(6)
|
||||
<< std::fixed << material.ambientColor[3] << "\n";
|
||||
stream << "INCANDESCENCE " << std::setprecision(6) << std::fixed << material.incandescence[0] << " " << std::setprecision(6) << std::fixed
|
||||
<< material.incandescence[1] << " " << std::setprecision(6) << std::fixed << material.incandescence[2] << " " << std::setprecision(6)
|
||||
<< std::fixed << material.incandescence[3] << "\n";
|
||||
stream << "COEFFS " << std::setprecision(6) << std::fixed << material.coeffs[0] << " " << std::setprecision(6) << std::fixed << material.coeffs[1]
|
||||
<< "\n";
|
||||
stream << "GLOW " << std::setprecision(6) << std::fixed << material.glow.x << " " << material.glow.y << "\n";
|
||||
stream << "REFRACTIVE " << material.refractive.x << " " << std::setprecision(6) << std::fixed << material.refractive.y << "\n";
|
||||
stream << "SPECULARCOLOR " << std::setprecision(6) << std::fixed << material.specularColor[0] << " " << std::setprecision(6) << std::fixed
|
||||
<< material.specularColor[1] << " " << std::setprecision(6) << std::fixed << material.specularColor[2] << " " << std::setprecision(6)
|
||||
<< std::fixed << material.specularColor[3] << "\n";
|
||||
stream << "REFLECTIVECOLOR " << std::setprecision(6) << std::fixed << material.reflectiveColor[0] << " " << std::setprecision(6) << std::fixed
|
||||
<< material.reflectiveColor[1] << " " << std::setprecision(6) << std::fixed << material.reflectiveColor[2] << " " << std::setprecision(6)
|
||||
<< std::fixed << material.reflectiveColor[3] << "\n";
|
||||
stream << "REFLECTIVE " << material.reflective.x << " " << std::setprecision(6) << std::fixed << material.reflective.y << "\n";
|
||||
stream << "BLINN " << std::setprecision(6) << std::fixed << material.blinn[0] << " " << std::setprecision(6) << std::fixed << material.blinn[1]
|
||||
<< "\n";
|
||||
stream << "PHONG " << std::setprecision(6) << std::fixed << material.phong << "\n";
|
||||
stream << "\n";
|
||||
materialNum++;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
XModelExportWriter6(std::string gameName, std::string zoneName)
|
||||
: XModelExportWriterBase(std::move(gameName), std::move(zoneName))
|
||||
{
|
||||
}
|
||||
|
||||
void Write(std::ostream& stream) override
|
||||
{
|
||||
PrepareVertexMerger();
|
||||
WriteHeader(stream, 6);
|
||||
WriteBones(stream);
|
||||
WriteVertices(stream);
|
||||
WriteFaces(stream);
|
||||
WriteObjects(stream);
|
||||
WriteMaterials(stream);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<XModelExportWriter> XModelExportWriter::CreateWriterForVersion6(std::string gameName, std::string zoneName)
|
||||
{
|
||||
return std::make_unique<XModelExportWriter6>(std::move(gameName), std::move(zoneName));
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "AbstractXModelWriter.h"
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
class XModelExportWriter : public AbstractXModelWriter
|
||||
{
|
||||
public:
|
||||
XModelExportWriter() = default;
|
||||
virtual ~XModelExportWriter() = default;
|
||||
XModelExportWriter(const XModelExportWriter& other) = default;
|
||||
XModelExportWriter(XModelExportWriter&& other) noexcept = default;
|
||||
XModelExportWriter& operator=(const XModelExportWriter& other) = default;
|
||||
XModelExportWriter& operator=(XModelExportWriter&& other) noexcept = default;
|
||||
|
||||
virtual void Write(std::ostream& stream) = 0;
|
||||
|
||||
static std::unique_ptr<XModelExportWriter> CreateWriterForVersion6(std::string gameName, std::string zoneName);
|
||||
};
|
@ -20,14 +20,16 @@ public:
|
||||
enum class ModelOutputFormat_e
|
||||
{
|
||||
XMODEL_EXPORT,
|
||||
OBJ
|
||||
OBJ,
|
||||
GLTF,
|
||||
GLB
|
||||
};
|
||||
|
||||
bool Verbose = false;
|
||||
std::vector<bool> AssetTypesToHandleBitfield;
|
||||
|
||||
ImageOutputFormat_e ImageOutputFormat = ImageOutputFormat_e::DDS;
|
||||
ModelOutputFormat_e ModelOutputFormat = ModelOutputFormat_e::XMODEL_EXPORT;
|
||||
ModelOutputFormat_e ModelOutputFormat = ModelOutputFormat_e::GLB;
|
||||
bool MenuLegacyMode = false;
|
||||
|
||||
} Configuration;
|
||||
|
232
src/ObjWriting/XModel/Export/XModelExportWriter.cpp
Normal file
232
src/ObjWriting/XModel/Export/XModelExportWriter.cpp
Normal file
@ -0,0 +1,232 @@
|
||||
#include "XModelExportWriter.h"
|
||||
|
||||
#include "Math/Quaternion.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
class XModelExportWriterBase : public XModelWriter
|
||||
{
|
||||
protected:
|
||||
void PrepareVertexMerger(const XModelCommon& xmodel)
|
||||
{
|
||||
m_vertex_merger = VertexMerger(xmodel.m_vertices.size());
|
||||
|
||||
auto vertexOffset = 0u;
|
||||
for (const auto& vertex : xmodel.m_vertices)
|
||||
{
|
||||
XModelVertexBoneWeights weights{nullptr, 0};
|
||||
|
||||
if (vertexOffset < xmodel.m_vertex_bone_weights.size())
|
||||
weights = xmodel.m_vertex_bone_weights[vertexOffset];
|
||||
|
||||
m_vertex_merger.Add(VertexMergerPos{vertex.coordinates[0], vertex.coordinates[1], vertex.coordinates[2], weights.weights, weights.weightCount});
|
||||
|
||||
vertexOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteHeader(const int version) const
|
||||
{
|
||||
m_stream << "// OpenAssetTools XMODEL_EXPORT File\n";
|
||||
m_stream << "// Game Origin: " << m_game_name << "\n";
|
||||
m_stream << "// Zone Origin: " << m_zone_name << "\n";
|
||||
m_stream << "MODEL\n";
|
||||
m_stream << "VERSION " << version << "\n";
|
||||
m_stream << "\n";
|
||||
}
|
||||
|
||||
void WriteBones(const XModelCommon& xmodel) const
|
||||
{
|
||||
m_stream << "NUMBONES " << xmodel.m_bones.size() << "\n";
|
||||
size_t boneNum = 0u;
|
||||
for (const auto& bone : xmodel.m_bones)
|
||||
{
|
||||
m_stream << "BONE " << boneNum << " ";
|
||||
if (bone.parentIndex < 0)
|
||||
m_stream << "-1";
|
||||
else
|
||||
m_stream << bone.parentIndex;
|
||||
|
||||
m_stream << " \"" << bone.name << "\"\n";
|
||||
boneNum++;
|
||||
}
|
||||
m_stream << "\n";
|
||||
|
||||
boneNum = 0u;
|
||||
for (const auto& bone : xmodel.m_bones)
|
||||
{
|
||||
m_stream << "BONE " << boneNum << "\n";
|
||||
m_stream << std::format("OFFSET {:.6f}, {:.6f}, {:.6f}\n", bone.globalOffset[0], bone.globalOffset[1], bone.globalOffset[2]);
|
||||
m_stream << std::format("SCALE {:.6f}, {:.6f}, {:.6f}\n", bone.scale[0], bone.scale[1], bone.scale[2]);
|
||||
|
||||
const Matrix32 mat = bone.globalRotation.ToMatrix();
|
||||
m_stream << std::format("X {:.6f}, {:.6f}, {:.6f}\n", mat.m_data[0][0], mat.m_data[1][0], mat.m_data[2][0]);
|
||||
m_stream << std::format("Y {:.6f}, {:.6f}, {:.6f}\n", mat.m_data[0][1], mat.m_data[1][1], mat.m_data[2][1]);
|
||||
m_stream << std::format("Z {:.6f}, {:.6f}, {:.6f}\n", mat.m_data[0][2], mat.m_data[1][2], mat.m_data[2][2]);
|
||||
m_stream << '\n';
|
||||
boneNum++;
|
||||
}
|
||||
}
|
||||
|
||||
XModelExportWriterBase(std::ostream& stream, std::string gameName, std::string zoneName)
|
||||
: m_stream(stream),
|
||||
m_game_name(std::move(gameName)),
|
||||
m_zone_name(std::move(zoneName))
|
||||
{
|
||||
}
|
||||
|
||||
std::ostream& m_stream;
|
||||
std::string m_game_name;
|
||||
std::string m_zone_name;
|
||||
VertexMerger m_vertex_merger;
|
||||
};
|
||||
|
||||
class XModelExportWriter6 final : public XModelExportWriterBase
|
||||
{
|
||||
void WriteVertices(const XModelCommon& xmodel) const
|
||||
{
|
||||
const auto& distinctVertexValues = m_vertex_merger.GetDistinctValues();
|
||||
m_stream << "NUMVERTS " << distinctVertexValues.size() << "\n";
|
||||
size_t vertexNum = 0u;
|
||||
for (const auto& vertexPos : distinctVertexValues)
|
||||
{
|
||||
m_stream << "VERT " << vertexNum << "\n";
|
||||
m_stream << std::format("OFFSET {:.6f}, {:.6f}, {:.6f}\n", vertexPos.x, vertexPos.y, vertexPos.z);
|
||||
m_stream << "BONES " << vertexPos.weightCount << "\n";
|
||||
|
||||
for (auto weightIndex = 0u; weightIndex < vertexPos.weightCount; weightIndex++)
|
||||
{
|
||||
const auto& weight = vertexPos.weights[weightIndex];
|
||||
m_stream << std::format("BONE {} {:.6f}\n", weight.boneIndex, weight.weight);
|
||||
}
|
||||
m_stream << "\n";
|
||||
vertexNum++;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteFaceVertex(const size_t index, const XModelVertex& vertex) const
|
||||
{
|
||||
m_stream << "VERT " << index << "\n";
|
||||
m_stream << std::format("NORMAL {:.6f} {:.6f} {:.6f}\n", vertex.normal[0], vertex.normal[1], vertex.normal[2]);
|
||||
m_stream << std::format("COLOR {:.6f} {:.6f} {:.6f} {:.6f}\n", vertex.color[0], vertex.color[1], vertex.color[2], vertex.color[3]);
|
||||
m_stream << std::format("UV 1 {:.6f} {:.6f}\n", vertex.uv[0], vertex.uv[1]);
|
||||
}
|
||||
|
||||
void WriteFaces(const XModelCommon& xmodel) const
|
||||
{
|
||||
auto totalFaceCount = 0u;
|
||||
for (const auto& object : xmodel.m_objects)
|
||||
totalFaceCount += object.m_faces.size();
|
||||
|
||||
m_stream << "NUMFACES " << totalFaceCount << "\n";
|
||||
|
||||
auto objectIndex = 0u;
|
||||
for (const auto& object : xmodel.m_objects)
|
||||
{
|
||||
for (const auto& face : object.m_faces)
|
||||
{
|
||||
const size_t distinctPositions[3]{
|
||||
m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[0]),
|
||||
m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[1]),
|
||||
m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[2]),
|
||||
};
|
||||
|
||||
const XModelVertex& v0 = xmodel.m_vertices[face.vertexIndex[0]];
|
||||
const XModelVertex& v1 = xmodel.m_vertices[face.vertexIndex[1]];
|
||||
const XModelVertex& v2 = xmodel.m_vertices[face.vertexIndex[2]];
|
||||
|
||||
m_stream << "TRI " << objectIndex << " " << object.materialIndex << " 0 0\n";
|
||||
WriteFaceVertex(distinctPositions[0], v0);
|
||||
WriteFaceVertex(distinctPositions[1], v1);
|
||||
WriteFaceVertex(distinctPositions[2], v2);
|
||||
m_stream << "\n";
|
||||
}
|
||||
|
||||
objectIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteObjects(const XModelCommon& xmodel) const
|
||||
{
|
||||
m_stream << "NUMOBJECTS " << xmodel.m_objects.size() << "\n";
|
||||
size_t objectNum = 0u;
|
||||
for (const auto& object : xmodel.m_objects)
|
||||
{
|
||||
m_stream << "OBJECT " << objectNum << " \"" << object.name << "\"\n";
|
||||
objectNum++;
|
||||
}
|
||||
m_stream << "\n";
|
||||
}
|
||||
|
||||
void WriteMaterials(const XModelCommon& xmodel) const
|
||||
{
|
||||
m_stream << "NUMMATERIALS " << xmodel.m_materials.size() << "\n";
|
||||
size_t materialNum = 0u;
|
||||
for (const auto& material : xmodel.m_materials)
|
||||
{
|
||||
const auto colorMapPath = "../images/" + material.colorMapName + ".dds";
|
||||
m_stream << "MATERIAL " << materialNum << " \"" << material.name << "\" \"" << material.materialTypeName << "\" \"" << colorMapPath << "\"\n";
|
||||
m_stream << std::format("COLOR {:.6f} {:.6f} {:.6f} {:.6f}\n", material.color[0], material.color[1], material.color[2], material.color[3]);
|
||||
m_stream << std::format("TRANSPARENCY {:.6f} {:.6f} {:.6f} {:.6f}\n",
|
||||
material.transparency[0],
|
||||
material.transparency[1],
|
||||
material.transparency[2],
|
||||
material.transparency[3]);
|
||||
m_stream << std::format("AMBIENTCOLOR {:.6f} {:.6f} {:.6f} {:.6f}\n",
|
||||
material.ambientColor[0],
|
||||
material.ambientColor[1],
|
||||
material.ambientColor[2],
|
||||
material.ambientColor[3]);
|
||||
m_stream << std::format("INCANDESCENCE {:.6f} {:.6f} {:.6f} {:.6f}\n",
|
||||
material.incandescence[0],
|
||||
material.incandescence[1],
|
||||
material.incandescence[2],
|
||||
material.incandescence[3]);
|
||||
m_stream << std::format("COEFFS {:.6f} {:.6f}\n", material.coeffs[0], material.coeffs[1]);
|
||||
m_stream << std::format("GLOW {:.6f} {}\n", material.glow.x, material.glow.y);
|
||||
m_stream << std::format("REFRACTIVE {} {:.6f}\n", material.refractive.x, material.refractive.y);
|
||||
m_stream << std::format("SPECULARCOLOR {:.6f} {:.6f} {:.6f} {:.6f}\n",
|
||||
material.specularColor[0],
|
||||
material.specularColor[1],
|
||||
material.specularColor[2],
|
||||
material.specularColor[3]);
|
||||
m_stream << std::format("REFLECTIVECOLOR {:.6f} {:.6f} {:.6f} {:.6f}\n",
|
||||
material.reflectiveColor[0],
|
||||
material.reflectiveColor[1],
|
||||
material.reflectiveColor[2],
|
||||
material.reflectiveColor[3]);
|
||||
m_stream << std::format("REFLECTIVE {} {:.6f}\n", material.reflective.x, material.reflective.y);
|
||||
m_stream << std::format("BLINN {:.6f} {:.6f}\n", material.blinn[0], material.blinn[1]);
|
||||
m_stream << std::format("PHONG {:.6f}\n", material.phong);
|
||||
m_stream << '\n';
|
||||
materialNum++;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
XModelExportWriter6(std::ostream& stream, std::string gameName, std::string zoneName)
|
||||
: XModelExportWriterBase(stream, std::move(gameName), std::move(zoneName))
|
||||
{
|
||||
}
|
||||
|
||||
void Write(const XModelCommon& xmodel) override
|
||||
{
|
||||
PrepareVertexMerger(xmodel);
|
||||
WriteHeader(6);
|
||||
WriteBones(xmodel);
|
||||
WriteVertices(xmodel);
|
||||
WriteFaces(xmodel);
|
||||
WriteObjects(xmodel);
|
||||
WriteMaterials(xmodel);
|
||||
}
|
||||
};
|
||||
|
||||
namespace xmodel_export
|
||||
{
|
||||
std::unique_ptr<XModelWriter> CreateWriterForVersion6(std::ostream& stream, std::string gameName, std::string zoneName)
|
||||
{
|
||||
return std::make_unique<XModelExportWriter6>(stream, std::move(gameName), std::move(zoneName));
|
||||
}
|
||||
} // namespace xmodel_export
|
11
src/ObjWriting/XModel/Export/XModelExportWriter.h
Normal file
11
src/ObjWriting/XModel/Export/XModelExportWriter.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "XModel/XModelWriter.h"
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
namespace xmodel_export
|
||||
{
|
||||
std::unique_ptr<XModelWriter> CreateWriterForVersion6(std::ostream& stream, std::string gameName, std::string zoneName);
|
||||
}
|
86
src/ObjWriting/XModel/Gltf/GltfBinOutput.cpp
Normal file
86
src/ObjWriting/XModel/Gltf/GltfBinOutput.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "GltfBinOutput.h"
|
||||
|
||||
#include "Utils/Alignment.h"
|
||||
#include "XModel/Gltf/GltfConstants.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace gltf;
|
||||
|
||||
BinOutput::BinOutput(std::ostream& stream)
|
||||
: m_stream(stream)
|
||||
{
|
||||
}
|
||||
|
||||
void BinOutput::Write(const void* data, const size_t dataSize) const
|
||||
{
|
||||
m_stream.write(static_cast<const char*>(data), dataSize);
|
||||
}
|
||||
|
||||
void BinOutput::AlignToFour(const char value) const
|
||||
{
|
||||
const auto offset = m_stream.tellp();
|
||||
if (offset % 4 > 0)
|
||||
{
|
||||
const uint32_t alignmentValue = FileUtils::MakeMagic32(value, value, value, value);
|
||||
Write(&alignmentValue, 4u - (offset % 4u));
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> BinOutput::CreateBufferUri(const void* buffer, size_t bufferSize) const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void BinOutput::EmitJson(const nlohmann::json& json) const
|
||||
{
|
||||
static constexpr uint32_t ZERO = 0u;
|
||||
|
||||
Write(&GLTF_MAGIC, sizeof(GLTF_MAGIC));
|
||||
Write(&GLTF_VERSION, sizeof(GLTF_VERSION));
|
||||
|
||||
// File length will be filled later
|
||||
Write(&ZERO, sizeof(ZERO));
|
||||
|
||||
// Chunk length will be filled after writing json
|
||||
Write(&ZERO, sizeof(ZERO));
|
||||
Write(&CHUNK_MAGIC_JSON, sizeof(CHUNK_MAGIC_JSON));
|
||||
|
||||
m_stream << json;
|
||||
|
||||
AlignToFour(' ');
|
||||
|
||||
const auto offsetAfterData = m_stream.tellp();
|
||||
const auto jsonDataLength = static_cast<uint32_t>(static_cast<unsigned>(offsetAfterData) - GLTF_JSON_CHUNK_DATA_OFFSET);
|
||||
|
||||
// Fill chunk length now
|
||||
m_stream.seekp(GLTF_JSON_CHUNK_LENGTH_OFFSET, std::ios::beg);
|
||||
Write(&jsonDataLength, sizeof(jsonDataLength));
|
||||
|
||||
// Return to previous pos
|
||||
m_stream.seekp(offsetAfterData, std::ios::beg);
|
||||
}
|
||||
|
||||
void BinOutput::EmitBuffer(const void* buffer, const size_t bufferSize) const
|
||||
{
|
||||
const auto chunkLength = utils::Align<uint32_t>(bufferSize, 4u);
|
||||
Write(&chunkLength, sizeof(chunkLength));
|
||||
Write(&CHUNK_MAGIC_BIN, sizeof(CHUNK_MAGIC_BIN));
|
||||
|
||||
Write(buffer, bufferSize);
|
||||
AlignToFour('\0');
|
||||
}
|
||||
|
||||
void BinOutput::Finalize() const
|
||||
{
|
||||
const auto fileEndOffset = m_stream.tellp();
|
||||
const auto fileSize = static_cast<uint32_t>(fileEndOffset);
|
||||
|
||||
// Fill chunk length now
|
||||
m_stream.seekp(GLTF_LENGTH_OFFSET, std::ios::beg);
|
||||
Write(&fileSize, sizeof(fileSize));
|
||||
|
||||
// Return to file end
|
||||
m_stream.seekp(fileEndOffset, std::ios::beg);
|
||||
}
|
25
src/ObjWriting/XModel/Gltf/GltfBinOutput.h
Normal file
25
src/ObjWriting/XModel/Gltf/GltfBinOutput.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "GltfOutput.h"
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
class BinOutput final : public Output
|
||||
{
|
||||
public:
|
||||
explicit BinOutput(std::ostream& stream);
|
||||
|
||||
std::optional<std::string> CreateBufferUri(const void* buffer, size_t bufferSize) const override;
|
||||
void EmitJson(const nlohmann::json& json) const override;
|
||||
void EmitBuffer(const void* buffer, size_t bufferSize) const override;
|
||||
void Finalize() const override;
|
||||
|
||||
private:
|
||||
void Write(const void* data, size_t dataSize) const;
|
||||
void AlignToFour(char value) const;
|
||||
|
||||
std::ostream& m_stream;
|
||||
};
|
||||
} // namespace gltf
|
25
src/ObjWriting/XModel/Gltf/GltfOutput.h
Normal file
25
src/ObjWriting/XModel/Gltf/GltfOutput.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
class Output
|
||||
{
|
||||
protected:
|
||||
Output() = default;
|
||||
virtual ~Output() = default;
|
||||
Output(const Output& other) = default;
|
||||
Output(Output&& other) noexcept = default;
|
||||
Output& operator=(const Output& other) = default;
|
||||
Output& operator=(Output&& other) noexcept = default;
|
||||
|
||||
public:
|
||||
virtual std::optional<std::string> CreateBufferUri(const void* buffer, size_t bufferSize) const = 0;
|
||||
virtual void EmitJson(const nlohmann::json& json) const = 0;
|
||||
virtual void EmitBuffer(const void* buffer, size_t bufferSize) const = 0;
|
||||
virtual void Finalize() const = 0;
|
||||
};
|
||||
} // namespace gltf
|
51
src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp
Normal file
51
src/ObjWriting/XModel/Gltf/GltfTextOutput.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include "GltfTextOutput.h"
|
||||
|
||||
#include "Utils/Alignment.h"
|
||||
#include "XModel/Gltf/GltfConstants.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#define LTC_NO_PROTOTYPES
|
||||
#include <tomcrypt.h>
|
||||
|
||||
using namespace gltf;
|
||||
|
||||
TextOutput::TextOutput(std::ostream& stream)
|
||||
: m_stream(stream)
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<std::string> TextOutput::CreateBufferUri(const void* buffer, const size_t bufferSize) const
|
||||
{
|
||||
static constexpr auto URI_PREFIX_LENGTH = std::char_traits<char>::length(GLTF_DATA_URI_PREFIX);
|
||||
const auto base64Length = 4u * ((bufferSize + 2u) / 3u);
|
||||
const auto base64BufferSize = URI_PREFIX_LENGTH + base64Length;
|
||||
|
||||
std::string output(base64BufferSize, '\0');
|
||||
|
||||
std::memcpy(output.data(), GLTF_DATA_URI_PREFIX, URI_PREFIX_LENGTH);
|
||||
|
||||
unsigned long outLength = base64Length + 1u;
|
||||
const auto result = base64_encode(static_cast<const unsigned char*>(buffer), bufferSize, &output[URI_PREFIX_LENGTH], &outLength);
|
||||
|
||||
assert(result == CRYPT_OK);
|
||||
assert(outLength == base64Length);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void TextOutput::EmitJson(const nlohmann::json& json) const
|
||||
{
|
||||
m_stream << std::setw(4) << json;
|
||||
}
|
||||
|
||||
void TextOutput::EmitBuffer(const void* buffer, const size_t bufferSize) const
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
void TextOutput::Finalize() const
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
22
src/ObjWriting/XModel/Gltf/GltfTextOutput.h
Normal file
22
src/ObjWriting/XModel/Gltf/GltfTextOutput.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "GltfOutput.h"
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
class TextOutput final : public Output
|
||||
{
|
||||
public:
|
||||
explicit TextOutput(std::ostream& stream);
|
||||
|
||||
std::optional<std::string> CreateBufferUri(const void* buffer, size_t bufferSize) const override;
|
||||
void EmitJson(const nlohmann::json& json) const override;
|
||||
void EmitBuffer(const void* buffer, size_t bufferSize) const override;
|
||||
void Finalize() const override;
|
||||
|
||||
private:
|
||||
std::ostream& m_stream;
|
||||
};
|
||||
} // namespace gltf
|
621
src/ObjWriting/XModel/Gltf/GltfWriter.cpp
Normal file
621
src/ObjWriting/XModel/Gltf/GltfWriter.cpp
Normal file
@ -0,0 +1,621 @@
|
||||
#include "GltfWriter.h"
|
||||
|
||||
#include "GitVersion.h"
|
||||
#include "Math/Vector.h"
|
||||
#include "XModel/Gltf/GltfConstants.h"
|
||||
#include "XModel/Gltf/JsonGltf.h"
|
||||
|
||||
#include <format>
|
||||
|
||||
using namespace gltf;
|
||||
using namespace nlohmann;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr auto GLTF_GENERATOR = "OpenAssetTools " GIT_VERSION;
|
||||
|
||||
struct GltfVertex
|
||||
{
|
||||
float coordinates[3];
|
||||
float normal[3];
|
||||
float uv[2];
|
||||
};
|
||||
|
||||
class GltfWriterImpl final : public gltf::Writer
|
||||
{
|
||||
public:
|
||||
GltfWriterImpl(const Output* output, std::string gameName, std::string zoneName)
|
||||
: m_output(output),
|
||||
m_game_name(std::move(gameName)),
|
||||
m_zone_name(std::move(zoneName))
|
||||
{
|
||||
}
|
||||
|
||||
void Write(const XModelCommon& xmodel) override
|
||||
{
|
||||
JsonRoot gltf;
|
||||
std::vector<uint8_t> bufferData;
|
||||
|
||||
CreateJsonAsset(gltf.asset);
|
||||
CreateSkeletonNodes(gltf, xmodel);
|
||||
CreateMeshNode(gltf, xmodel);
|
||||
CreateRootNode(gltf, xmodel);
|
||||
CreateMaterials(gltf, xmodel);
|
||||
CreateBufferViews(gltf, xmodel);
|
||||
CreateAccessors(gltf, xmodel);
|
||||
CreateSkin(gltf, xmodel);
|
||||
CreateMesh(gltf, xmodel);
|
||||
CreateScene(gltf, xmodel);
|
||||
FillBufferData(gltf, xmodel, bufferData);
|
||||
CreateBuffer(gltf, xmodel, bufferData);
|
||||
|
||||
const json jRoot = gltf;
|
||||
m_output->EmitJson(jRoot);
|
||||
|
||||
if (!bufferData.empty())
|
||||
m_output->EmitBuffer(bufferData.data(), bufferData.size());
|
||||
|
||||
m_output->Finalize();
|
||||
}
|
||||
|
||||
private:
|
||||
static void CreateJsonAsset(JsonAsset& asset)
|
||||
{
|
||||
asset.version = GLTF_VERSION_STRING;
|
||||
asset.generator = GLTF_GENERATOR;
|
||||
}
|
||||
|
||||
void CreateMeshNode(JsonRoot& gltf, const XModelCommon& xmodel)
|
||||
{
|
||||
JsonNode meshNode;
|
||||
|
||||
if (!xmodel.m_name.empty())
|
||||
meshNode.name = xmodel.m_name;
|
||||
|
||||
// We only have one mesh
|
||||
meshNode.mesh = 0u;
|
||||
|
||||
// Only add skin if the model has bones
|
||||
if (!xmodel.m_bones.empty())
|
||||
{
|
||||
// We only have one skin
|
||||
meshNode.skin = 0u;
|
||||
}
|
||||
|
||||
if (!gltf.nodes.has_value())
|
||||
gltf.nodes.emplace();
|
||||
|
||||
m_mesh_node = gltf.nodes->size();
|
||||
gltf.nodes->emplace_back(std::move(meshNode));
|
||||
}
|
||||
|
||||
void CreateRootNode(JsonRoot& gltf, const XModelCommon& xmodel)
|
||||
{
|
||||
JsonNode rootNode;
|
||||
|
||||
if (!xmodel.m_name.empty())
|
||||
rootNode.name = std::format("{}_skel", xmodel.m_name);
|
||||
|
||||
if (!gltf.nodes.has_value())
|
||||
gltf.nodes.emplace();
|
||||
|
||||
rootNode.children.emplace();
|
||||
rootNode.children->push_back(m_mesh_node);
|
||||
|
||||
if (!xmodel.m_bones.empty())
|
||||
rootNode.children->push_back(m_first_bone_node);
|
||||
|
||||
m_root_node = gltf.nodes->size();
|
||||
gltf.nodes->emplace_back(std::move(rootNode));
|
||||
}
|
||||
|
||||
void CreateMesh(JsonRoot& gltf, const XModelCommon& xmodel)
|
||||
{
|
||||
if (!gltf.meshes.has_value())
|
||||
gltf.meshes.emplace();
|
||||
|
||||
JsonMesh mesh;
|
||||
|
||||
const auto hasBoneWeightData = !xmodel.m_vertex_bone_weights.empty();
|
||||
|
||||
auto objectIndex = 0u;
|
||||
for (const auto& object : xmodel.m_objects)
|
||||
{
|
||||
JsonMeshPrimitives primitives;
|
||||
|
||||
if (object.materialIndex >= 0)
|
||||
primitives.material = static_cast<unsigned>(object.materialIndex);
|
||||
|
||||
primitives.attributes.POSITION = m_position_accessor;
|
||||
primitives.attributes.NORMAL = m_normal_accessor;
|
||||
primitives.attributes.TEXCOORD_0 = m_uv_accessor;
|
||||
|
||||
if (hasBoneWeightData)
|
||||
{
|
||||
primitives.attributes.JOINTS_0 = m_joints_accessor;
|
||||
primitives.attributes.WEIGHTS_0 = m_weights_accessor;
|
||||
}
|
||||
|
||||
primitives.mode = JsonMeshPrimitivesMode::TRIANGLES;
|
||||
primitives.indices = m_first_index_accessor + objectIndex;
|
||||
|
||||
mesh.primitives.emplace_back(primitives);
|
||||
objectIndex++;
|
||||
}
|
||||
|
||||
gltf.meshes->emplace_back(std::move(mesh));
|
||||
}
|
||||
|
||||
static void CreateMaterials(JsonRoot& gltf, const XModelCommon& xmodel)
|
||||
{
|
||||
if (!gltf.materials.has_value())
|
||||
gltf.materials.emplace();
|
||||
|
||||
for (const auto& modelMaterial : xmodel.m_materials)
|
||||
{
|
||||
JsonMaterial material;
|
||||
|
||||
material.name = modelMaterial.name;
|
||||
|
||||
if (!modelMaterial.colorMapName.empty())
|
||||
{
|
||||
material.pbrMetallicRoughness.emplace();
|
||||
material.pbrMetallicRoughness->baseColorTexture.emplace();
|
||||
|
||||
material.pbrMetallicRoughness->baseColorTexture->index = CreateTexture(gltf, modelMaterial.colorMapName);
|
||||
material.pbrMetallicRoughness->metallicFactor = 0.0f;
|
||||
}
|
||||
|
||||
if (!modelMaterial.normalMapName.empty())
|
||||
material.normalTexture.emplace().index = CreateTexture(gltf, modelMaterial.normalMapName);
|
||||
|
||||
material.doubleSided = true;
|
||||
gltf.materials->emplace_back(material);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned CreateTexture(JsonRoot& gltf, const std::string& textureName)
|
||||
{
|
||||
if (!gltf.textures.has_value())
|
||||
gltf.textures.emplace();
|
||||
if (!gltf.images.has_value())
|
||||
gltf.images.emplace();
|
||||
|
||||
auto uri = std::format("../images/{}.dds", textureName);
|
||||
|
||||
auto existingTexIndex = 0u;
|
||||
for (const auto& existingTex : gltf.textures.value())
|
||||
{
|
||||
const auto& existingImage = gltf.images.value()[existingTex.source];
|
||||
|
||||
if (existingImage.uri == uri)
|
||||
return existingTexIndex;
|
||||
|
||||
existingTexIndex++;
|
||||
}
|
||||
|
||||
JsonImage image;
|
||||
image.uri = std::move(uri);
|
||||
const auto imageIndex = gltf.images->size();
|
||||
gltf.images->emplace_back(std::move(image));
|
||||
|
||||
JsonTexture texture;
|
||||
texture.source = imageIndex;
|
||||
const auto textureIndex = gltf.textures->size();
|
||||
gltf.textures->emplace_back(texture);
|
||||
|
||||
return textureIndex;
|
||||
}
|
||||
|
||||
void CreateSkeletonNodes(JsonRoot& gltf, const XModelCommon& xmodel)
|
||||
{
|
||||
if (xmodel.m_bones.empty())
|
||||
return;
|
||||
|
||||
if (!gltf.nodes.has_value())
|
||||
gltf.nodes.emplace();
|
||||
|
||||
const auto boneCount = xmodel.m_bones.size();
|
||||
m_first_bone_node = gltf.nodes->size();
|
||||
for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++)
|
||||
{
|
||||
JsonNode boneNode;
|
||||
const auto& bone = xmodel.m_bones[boneIndex];
|
||||
|
||||
Vector3f translation(bone.globalOffset[0], bone.globalOffset[2], -bone.globalOffset[1]);
|
||||
Quaternion32 rotation(bone.globalRotation.m_x, bone.globalRotation.m_z, -bone.globalRotation.m_y, bone.globalRotation.m_w);
|
||||
if (bone.parentIndex >= 0)
|
||||
{
|
||||
const auto& parentBone = xmodel.m_bones[bone.parentIndex];
|
||||
translation -= Vector3f(parentBone.globalOffset[0], parentBone.globalOffset[2], -parentBone.globalOffset[1]);
|
||||
rotation /= Quaternion32(
|
||||
parentBone.globalRotation.m_x, parentBone.globalRotation.m_z, -parentBone.globalRotation.m_y, parentBone.globalRotation.m_w);
|
||||
}
|
||||
rotation.Normalize();
|
||||
|
||||
boneNode.name = bone.name;
|
||||
boneNode.translation = std::to_array({translation.x(), translation.y(), translation.z()});
|
||||
boneNode.rotation = std::to_array({rotation.m_x, rotation.m_y, rotation.m_z, rotation.m_w});
|
||||
|
||||
std::vector<unsigned> children;
|
||||
for (auto maybeChildIndex = 0u; maybeChildIndex < boneCount; maybeChildIndex++)
|
||||
{
|
||||
if (xmodel.m_bones[maybeChildIndex].parentIndex == static_cast<int>(boneIndex))
|
||||
children.emplace_back(maybeChildIndex + m_first_bone_node);
|
||||
}
|
||||
if (!children.empty())
|
||||
boneNode.children = std::move(children);
|
||||
|
||||
gltf.nodes->emplace_back(std::move(boneNode));
|
||||
}
|
||||
}
|
||||
|
||||
void CreateSkin(JsonRoot& gltf, const XModelCommon& xmodel) const
|
||||
{
|
||||
if (xmodel.m_bones.empty())
|
||||
return;
|
||||
|
||||
if (!gltf.skins.has_value())
|
||||
gltf.skins.emplace();
|
||||
|
||||
JsonSkin skin;
|
||||
|
||||
const auto boneCount = xmodel.m_bones.size();
|
||||
|
||||
skin.joints.reserve(boneCount);
|
||||
for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++)
|
||||
skin.joints.emplace_back(boneIndex + m_first_bone_node);
|
||||
|
||||
skin.inverseBindMatrices = m_inverse_bind_matrices_accessor;
|
||||
|
||||
gltf.skins->emplace_back(std::move(skin));
|
||||
}
|
||||
|
||||
void CreateScene(JsonRoot& gltf, const XModelCommon& xmodel) const
|
||||
{
|
||||
JsonScene scene;
|
||||
|
||||
// Only add skin if the model has bones
|
||||
scene.nodes.emplace_back(m_root_node);
|
||||
|
||||
if (!gltf.scenes.has_value())
|
||||
gltf.scenes.emplace();
|
||||
|
||||
gltf.scenes->emplace_back(std::move(scene));
|
||||
gltf.scene = 0u;
|
||||
}
|
||||
|
||||
void CreateBufferViews(JsonRoot& gltf, const XModelCommon& xmodel)
|
||||
{
|
||||
if (!gltf.bufferViews.has_value())
|
||||
gltf.bufferViews.emplace();
|
||||
|
||||
unsigned bufferOffset = 0u;
|
||||
|
||||
JsonBufferView vertexBufferView;
|
||||
vertexBufferView.buffer = 0u;
|
||||
vertexBufferView.byteOffset = bufferOffset;
|
||||
vertexBufferView.byteStride = sizeof(GltfVertex);
|
||||
vertexBufferView.byteLength = sizeof(GltfVertex) * xmodel.m_vertices.size();
|
||||
vertexBufferView.target = JsonBufferViewTarget::ARRAY_BUFFER;
|
||||
bufferOffset += vertexBufferView.byteLength;
|
||||
|
||||
m_vertex_buffer_view = gltf.bufferViews->size();
|
||||
gltf.bufferViews->emplace_back(vertexBufferView);
|
||||
|
||||
if (!xmodel.m_vertex_bone_weights.empty())
|
||||
{
|
||||
JsonBufferView jointsBufferView;
|
||||
jointsBufferView.buffer = 0u;
|
||||
jointsBufferView.byteOffset = bufferOffset;
|
||||
jointsBufferView.byteLength = sizeof(uint8_t) * xmodel.m_vertices.size() * 4u;
|
||||
jointsBufferView.target = JsonBufferViewTarget::ARRAY_BUFFER;
|
||||
bufferOffset += jointsBufferView.byteLength;
|
||||
|
||||
m_joints_buffer_view = gltf.bufferViews->size();
|
||||
gltf.bufferViews->emplace_back(jointsBufferView);
|
||||
|
||||
JsonBufferView weightsBufferView;
|
||||
weightsBufferView.buffer = 0u;
|
||||
weightsBufferView.byteOffset = bufferOffset;
|
||||
weightsBufferView.byteLength = sizeof(float) * xmodel.m_vertices.size() * 4u;
|
||||
weightsBufferView.target = JsonBufferViewTarget::ARRAY_BUFFER;
|
||||
bufferOffset += weightsBufferView.byteLength;
|
||||
|
||||
m_weights_buffer_view = gltf.bufferViews->size();
|
||||
gltf.bufferViews->emplace_back(weightsBufferView);
|
||||
|
||||
JsonBufferView inverseBindMatricesBufferView;
|
||||
inverseBindMatricesBufferView.buffer = 0u;
|
||||
inverseBindMatricesBufferView.byteOffset = bufferOffset;
|
||||
inverseBindMatricesBufferView.byteLength = sizeof(float) * xmodel.m_bones.size() * 16u;
|
||||
bufferOffset += inverseBindMatricesBufferView.byteLength;
|
||||
|
||||
m_inverse_bind_matrices_buffer_view = gltf.bufferViews->size();
|
||||
gltf.bufferViews->emplace_back(inverseBindMatricesBufferView);
|
||||
}
|
||||
|
||||
m_first_index_buffer_view = gltf.bufferViews->size();
|
||||
for (const auto& object : xmodel.m_objects)
|
||||
{
|
||||
JsonBufferView indicesBufferView;
|
||||
indicesBufferView.buffer = 0u;
|
||||
indicesBufferView.byteOffset = bufferOffset;
|
||||
indicesBufferView.byteLength = sizeof(unsigned short) * object.m_faces.size() * 3u;
|
||||
indicesBufferView.target = JsonBufferViewTarget::ELEMENT_ARRAY_BUFFER;
|
||||
bufferOffset += indicesBufferView.byteLength;
|
||||
|
||||
gltf.bufferViews->emplace_back(indicesBufferView);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateAccessors(JsonRoot& gltf, const XModelCommon& xmodel)
|
||||
{
|
||||
if (!gltf.accessors.has_value())
|
||||
gltf.accessors.emplace();
|
||||
|
||||
JsonAccessor positionAccessor;
|
||||
positionAccessor.bufferView = m_vertex_buffer_view;
|
||||
positionAccessor.byteOffset = offsetof(GltfVertex, coordinates);
|
||||
positionAccessor.componentType = JsonAccessorComponentType::FLOAT;
|
||||
positionAccessor.count = xmodel.m_vertices.size();
|
||||
positionAccessor.type = JsonAccessorType::VEC3;
|
||||
m_position_accessor = gltf.accessors->size();
|
||||
gltf.accessors->emplace_back(positionAccessor);
|
||||
|
||||
JsonAccessor normalAccessor;
|
||||
normalAccessor.bufferView = m_vertex_buffer_view;
|
||||
normalAccessor.byteOffset = offsetof(GltfVertex, normal);
|
||||
normalAccessor.componentType = JsonAccessorComponentType::FLOAT;
|
||||
normalAccessor.count = xmodel.m_vertices.size();
|
||||
normalAccessor.type = JsonAccessorType::VEC3;
|
||||
m_normal_accessor = gltf.accessors->size();
|
||||
gltf.accessors->emplace_back(normalAccessor);
|
||||
|
||||
JsonAccessor uvAccessor;
|
||||
uvAccessor.bufferView = m_vertex_buffer_view;
|
||||
uvAccessor.byteOffset = offsetof(GltfVertex, uv);
|
||||
uvAccessor.componentType = JsonAccessorComponentType::FLOAT;
|
||||
uvAccessor.count = xmodel.m_vertices.size();
|
||||
uvAccessor.type = JsonAccessorType::VEC2;
|
||||
m_uv_accessor = gltf.accessors->size();
|
||||
gltf.accessors->emplace_back(uvAccessor);
|
||||
|
||||
if (!xmodel.m_vertex_bone_weights.empty())
|
||||
{
|
||||
JsonAccessor jointsAccessor;
|
||||
jointsAccessor.bufferView = m_joints_buffer_view;
|
||||
jointsAccessor.componentType = JsonAccessorComponentType::UNSIGNED_BYTE;
|
||||
jointsAccessor.count = xmodel.m_vertices.size();
|
||||
jointsAccessor.type = JsonAccessorType::VEC4;
|
||||
m_joints_accessor = gltf.accessors->size();
|
||||
gltf.accessors->emplace_back(jointsAccessor);
|
||||
|
||||
JsonAccessor weightsAccessor;
|
||||
weightsAccessor.bufferView = m_weights_buffer_view;
|
||||
weightsAccessor.componentType = JsonAccessorComponentType::FLOAT;
|
||||
weightsAccessor.count = xmodel.m_vertices.size();
|
||||
weightsAccessor.type = JsonAccessorType::VEC4;
|
||||
m_weights_accessor = gltf.accessors->size();
|
||||
gltf.accessors->emplace_back(weightsAccessor);
|
||||
|
||||
JsonAccessor inverseBindMatricesAccessor;
|
||||
inverseBindMatricesAccessor.bufferView = m_inverse_bind_matrices_buffer_view;
|
||||
inverseBindMatricesAccessor.componentType = JsonAccessorComponentType::FLOAT;
|
||||
inverseBindMatricesAccessor.count = xmodel.m_bones.size();
|
||||
inverseBindMatricesAccessor.type = JsonAccessorType::MAT4;
|
||||
m_inverse_bind_matrices_accessor = gltf.accessors->size();
|
||||
gltf.accessors->emplace_back(inverseBindMatricesAccessor);
|
||||
}
|
||||
|
||||
m_first_index_accessor = gltf.accessors->size();
|
||||
for (auto i = 0u; i < xmodel.m_objects.size(); i++)
|
||||
{
|
||||
const auto& object = xmodel.m_objects[i];
|
||||
|
||||
JsonAccessor indicesAccessor;
|
||||
indicesAccessor.bufferView = m_first_index_buffer_view + i;
|
||||
indicesAccessor.componentType = JsonAccessorComponentType::UNSIGNED_SHORT;
|
||||
indicesAccessor.count = object.m_faces.size() * 3u;
|
||||
indicesAccessor.type = JsonAccessorType::SCALAR;
|
||||
|
||||
gltf.accessors->emplace_back(indicesAccessor);
|
||||
}
|
||||
}
|
||||
|
||||
void FillBufferData(JsonRoot& gltf, const XModelCommon& xmodel, std::vector<uint8_t>& bufferData) const
|
||||
{
|
||||
const auto expectedBufferSize = GetExpectedBufferSize(xmodel);
|
||||
bufferData.resize(expectedBufferSize);
|
||||
|
||||
auto currentBufferOffset = 0u;
|
||||
|
||||
float minPosition[3]{
|
||||
std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max(),
|
||||
std::numeric_limits<float>::max(),
|
||||
};
|
||||
float maxPosition[3]{
|
||||
-std::numeric_limits<float>::max(),
|
||||
-std::numeric_limits<float>::max(),
|
||||
-std::numeric_limits<float>::max(),
|
||||
};
|
||||
|
||||
for (const auto& commonVertex : xmodel.m_vertices)
|
||||
{
|
||||
auto* vertex = reinterpret_cast<GltfVertex*>(&bufferData[currentBufferOffset]);
|
||||
|
||||
vertex->coordinates[0] = commonVertex.coordinates[0];
|
||||
vertex->coordinates[1] = commonVertex.coordinates[2];
|
||||
vertex->coordinates[2] = -commonVertex.coordinates[1];
|
||||
|
||||
if (minPosition[0] > vertex->coordinates[0])
|
||||
minPosition[0] = vertex->coordinates[0];
|
||||
if (minPosition[1] > vertex->coordinates[1])
|
||||
minPosition[1] = vertex->coordinates[1];
|
||||
if (minPosition[2] > vertex->coordinates[2])
|
||||
minPosition[2] = vertex->coordinates[2];
|
||||
if (maxPosition[0] < vertex->coordinates[0])
|
||||
maxPosition[0] = vertex->coordinates[0];
|
||||
if (maxPosition[1] < vertex->coordinates[1])
|
||||
maxPosition[1] = vertex->coordinates[1];
|
||||
if (maxPosition[2] < vertex->coordinates[2])
|
||||
maxPosition[2] = vertex->coordinates[2];
|
||||
|
||||
vertex->normal[0] = commonVertex.normal[0];
|
||||
vertex->normal[1] = commonVertex.normal[2];
|
||||
vertex->normal[2] = -commonVertex.normal[1];
|
||||
|
||||
vertex->uv[0] = commonVertex.uv[0];
|
||||
vertex->uv[1] = commonVertex.uv[1];
|
||||
|
||||
currentBufferOffset += sizeof(GltfVertex);
|
||||
}
|
||||
|
||||
if (gltf.accessors.has_value())
|
||||
{
|
||||
gltf.accessors.value()[m_position_accessor].min = std::vector({minPosition[0], minPosition[1], minPosition[2]});
|
||||
gltf.accessors.value()[m_position_accessor].max = std::vector({maxPosition[0], maxPosition[1], maxPosition[2]});
|
||||
}
|
||||
|
||||
if (!xmodel.m_vertex_bone_weights.empty())
|
||||
{
|
||||
assert(xmodel.m_vertex_bone_weights.size() == xmodel.m_vertices.size());
|
||||
|
||||
auto* joints = reinterpret_cast<uint8_t*>(&bufferData[currentBufferOffset]);
|
||||
auto* weights = reinterpret_cast<float*>(&bufferData[currentBufferOffset + sizeof(uint8_t) * 4u * xmodel.m_vertex_bone_weights.size()]);
|
||||
for (const auto& commonVertexWeights : xmodel.m_vertex_bone_weights)
|
||||
{
|
||||
assert(commonVertexWeights.weights != nullptr);
|
||||
assert(commonVertexWeights.weightCount <= 4u);
|
||||
|
||||
const auto commonVertexWeightCount = std::min(commonVertexWeights.weightCount, 4u);
|
||||
for (auto i = 0u; i < commonVertexWeightCount; i++)
|
||||
{
|
||||
joints[i] = static_cast<unsigned char>(commonVertexWeights.weights[i].boneIndex);
|
||||
weights[i] = commonVertexWeights.weights[i].weight;
|
||||
}
|
||||
|
||||
for (auto i = commonVertexWeightCount; i < 4u; i++)
|
||||
{
|
||||
joints[i] = 0u;
|
||||
weights[i] = 0.0f;
|
||||
}
|
||||
|
||||
joints += 4u;
|
||||
weights += 4u;
|
||||
}
|
||||
|
||||
currentBufferOffset += (sizeof(uint8_t) + sizeof(float)) * 4u * xmodel.m_vertex_bone_weights.size();
|
||||
|
||||
auto* inverseBindMatrixData = reinterpret_cast<float*>(&bufferData[currentBufferOffset]);
|
||||
for (const auto& bone : xmodel.m_bones)
|
||||
{
|
||||
Matrix32 inverseBindMatrix;
|
||||
inverseBindMatrix.m_data[0][3] = -bone.globalOffset[0];
|
||||
inverseBindMatrix.m_data[1][3] = -bone.globalOffset[2];
|
||||
inverseBindMatrix.m_data[2][3] = bone.globalOffset[1];
|
||||
|
||||
// In-memory = row major
|
||||
// gltf = column major
|
||||
inverseBindMatrixData[0] = inverseBindMatrix.m_data[0][0];
|
||||
inverseBindMatrixData[1] = inverseBindMatrix.m_data[1][0];
|
||||
inverseBindMatrixData[2] = inverseBindMatrix.m_data[2][0];
|
||||
inverseBindMatrixData[3] = inverseBindMatrix.m_data[3][0];
|
||||
inverseBindMatrixData[4] = inverseBindMatrix.m_data[0][1];
|
||||
inverseBindMatrixData[5] = inverseBindMatrix.m_data[1][1];
|
||||
inverseBindMatrixData[6] = inverseBindMatrix.m_data[2][1];
|
||||
inverseBindMatrixData[7] = inverseBindMatrix.m_data[3][1];
|
||||
inverseBindMatrixData[8] = inverseBindMatrix.m_data[0][2];
|
||||
inverseBindMatrixData[9] = inverseBindMatrix.m_data[1][2];
|
||||
inverseBindMatrixData[10] = inverseBindMatrix.m_data[2][2];
|
||||
inverseBindMatrixData[11] = inverseBindMatrix.m_data[3][2];
|
||||
inverseBindMatrixData[12] = inverseBindMatrix.m_data[0][3];
|
||||
inverseBindMatrixData[13] = inverseBindMatrix.m_data[1][3];
|
||||
inverseBindMatrixData[14] = inverseBindMatrix.m_data[2][3];
|
||||
inverseBindMatrixData[15] = inverseBindMatrix.m_data[3][3];
|
||||
|
||||
inverseBindMatrixData += 16u;
|
||||
}
|
||||
currentBufferOffset += sizeof(float) * 16u * xmodel.m_bones.size();
|
||||
}
|
||||
|
||||
for (const auto& object : xmodel.m_objects)
|
||||
{
|
||||
for (const auto& face : object.m_faces)
|
||||
{
|
||||
auto* faceIndices = reinterpret_cast<unsigned short*>(&bufferData[currentBufferOffset]);
|
||||
faceIndices[0] = static_cast<unsigned short>(face.vertexIndex[2]);
|
||||
faceIndices[1] = static_cast<unsigned short>(face.vertexIndex[1]);
|
||||
faceIndices[2] = static_cast<unsigned short>(face.vertexIndex[0]);
|
||||
|
||||
currentBufferOffset += sizeof(unsigned short) * 3u;
|
||||
}
|
||||
}
|
||||
|
||||
assert(expectedBufferSize == currentBufferOffset);
|
||||
}
|
||||
|
||||
static size_t GetExpectedBufferSize(const XModelCommon& xmodel)
|
||||
{
|
||||
auto result = 0u;
|
||||
|
||||
result += xmodel.m_vertices.size() * sizeof(GltfVertex);
|
||||
|
||||
if (!xmodel.m_vertex_bone_weights.empty())
|
||||
{
|
||||
// Joints and weights
|
||||
result += xmodel.m_vertices.size() * 4u * (sizeof(uint8_t) + sizeof(float));
|
||||
|
||||
// Inverse bind matrices
|
||||
result += xmodel.m_bones.size() * 16u * sizeof(float);
|
||||
}
|
||||
|
||||
for (const auto& object : xmodel.m_objects)
|
||||
{
|
||||
result += object.m_faces.size() * sizeof(unsigned short) * 3u;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void CreateBuffer(JsonRoot& gltf, const XModelCommon& xmodel, const std::vector<uint8_t>& bufferData) const
|
||||
{
|
||||
if (!gltf.buffers.has_value())
|
||||
gltf.buffers.emplace();
|
||||
|
||||
JsonBuffer jsonBuffer;
|
||||
jsonBuffer.byteLength = bufferData.size();
|
||||
|
||||
if (!bufferData.empty())
|
||||
jsonBuffer.uri = m_output->CreateBufferUri(bufferData.data(), bufferData.size());
|
||||
|
||||
gltf.buffers->emplace_back(std::move(jsonBuffer));
|
||||
}
|
||||
|
||||
unsigned m_mesh_node = 0u;
|
||||
unsigned m_root_node = 0u;
|
||||
unsigned m_first_bone_node = 0u;
|
||||
unsigned m_position_accessor = 0u;
|
||||
unsigned m_normal_accessor = 0u;
|
||||
unsigned m_uv_accessor = 0u;
|
||||
unsigned m_joints_accessor = 0u;
|
||||
unsigned m_weights_accessor = 0u;
|
||||
unsigned m_inverse_bind_matrices_accessor = 0u;
|
||||
unsigned m_vertex_buffer_view = 0u;
|
||||
unsigned m_joints_buffer_view = 0u;
|
||||
unsigned m_weights_buffer_view = 0u;
|
||||
unsigned m_inverse_bind_matrices_buffer_view = 0u;
|
||||
unsigned m_first_index_buffer_view = 0u;
|
||||
unsigned m_first_index_accessor = 0u;
|
||||
|
||||
const Output* m_output;
|
||||
std::string m_game_name;
|
||||
std::string m_zone_name;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Writer> Writer::CreateWriter(const Output* output, std::string gameName, std::string zoneName)
|
||||
{
|
||||
return std::make_unique<GltfWriterImpl>(output, std::move(gameName), std::move(zoneName));
|
||||
}
|
24
src/ObjWriting/XModel/Gltf/GltfWriter.h
Normal file
24
src/ObjWriting/XModel/Gltf/GltfWriter.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "GltfOutput.h"
|
||||
#include "XModel/Gltf/JsonGltf.h"
|
||||
#include "XModel/XModelWriter.h"
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
namespace gltf
|
||||
{
|
||||
class Writer : public XModelWriter
|
||||
{
|
||||
public:
|
||||
Writer() = default;
|
||||
~Writer() override = default;
|
||||
Writer(const Writer& other) = default;
|
||||
Writer(Writer&& other) noexcept = default;
|
||||
Writer& operator=(const Writer& other) = default;
|
||||
Writer& operator=(Writer&& other) noexcept = default;
|
||||
|
||||
static std::unique_ptr<Writer> CreateWriter(const Output* output, std::string gameName, std::string zoneName);
|
||||
};
|
||||
} // namespace gltf
|
227
src/ObjWriting/XModel/Obj/ObjWriter.cpp
Normal file
227
src/ObjWriting/XModel/Obj/ObjWriter.cpp
Normal file
@ -0,0 +1,227 @@
|
||||
#include "ObjWriter.h"
|
||||
|
||||
#include "Utils/DistinctMapper.h"
|
||||
#include "XModel/Obj/ObjCommon.h"
|
||||
|
||||
#include <format>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct ObjObjectData
|
||||
{
|
||||
DistinctMapper<ObjVertex> m_vertices;
|
||||
DistinctMapper<ObjNormal> m_normals;
|
||||
DistinctMapper<ObjUv> m_uvs;
|
||||
};
|
||||
|
||||
struct ObjObjectDataOffsets
|
||||
{
|
||||
size_t vertexOffset;
|
||||
size_t normalOffset;
|
||||
size_t uvOffset;
|
||||
};
|
||||
|
||||
class ObjWriter final : public XModelWriter
|
||||
{
|
||||
public:
|
||||
ObjWriter(std::ostream& stream, std::string gameName, std::string zoneName, std::string mtlName)
|
||||
: m_stream(stream),
|
||||
m_mtl_name(std::move(mtlName)),
|
||||
m_game_name(std::move(gameName)),
|
||||
m_zone_name(std::move(zoneName))
|
||||
{
|
||||
}
|
||||
|
||||
void Write(const XModelCommon& xmodel) override
|
||||
{
|
||||
m_stream << "# OpenAssetTools OBJ File ( " << m_game_name << ")\n";
|
||||
m_stream << "# Game Origin: " << m_game_name << "\n";
|
||||
m_stream << "# Zone Origin: " << m_zone_name << "\n";
|
||||
|
||||
if (!m_mtl_name.empty())
|
||||
m_stream << "mtllib " << m_mtl_name << "\n";
|
||||
|
||||
std::vector<ObjObjectDataOffsets> inputOffsetsByObject;
|
||||
std::vector<ObjObjectDataOffsets> distinctOffsetsByObject;
|
||||
GetObjObjectDataOffsets(xmodel, inputOffsetsByObject, distinctOffsetsByObject);
|
||||
|
||||
auto objectIndex = 0;
|
||||
for (const auto& object : xmodel.m_objects)
|
||||
{
|
||||
const auto& objectData = m_object_data[objectIndex];
|
||||
m_stream << "o " << object.name << "\n";
|
||||
|
||||
for (const auto& v : objectData.m_vertices.GetDistinctValues())
|
||||
m_stream << "v " << v.coordinates[0] << " " << v.coordinates[1] << " " << v.coordinates[2] << "\n";
|
||||
for (const auto& uv : objectData.m_uvs.GetDistinctValues())
|
||||
m_stream << "vt " << uv.uv[0] << " " << uv.uv[1] << "\n";
|
||||
for (const auto& n : objectData.m_normals.GetDistinctValues())
|
||||
m_stream << "vn " << n.normal[0] << " " << n.normal[1] << " " << n.normal[2] << "\n";
|
||||
|
||||
if (object.materialIndex >= 0 && static_cast<unsigned>(object.materialIndex) < xmodel.m_materials.size())
|
||||
m_stream << "usemtl " << xmodel.m_materials[object.materialIndex].name << "\n";
|
||||
|
||||
auto faceIndex = 0u;
|
||||
for (const auto& f : object.m_faces)
|
||||
{
|
||||
const auto faceVertexOffset = 3u * faceIndex;
|
||||
const size_t v[3]{
|
||||
objectData.m_vertices.GetDistinctPositionByInputPosition(faceVertexOffset + 0) + distinctOffsetsByObject[objectIndex].vertexOffset + 1,
|
||||
objectData.m_vertices.GetDistinctPositionByInputPosition(faceVertexOffset + 1) + distinctOffsetsByObject[objectIndex].vertexOffset + 1,
|
||||
objectData.m_vertices.GetDistinctPositionByInputPosition(faceVertexOffset + 2) + distinctOffsetsByObject[objectIndex].vertexOffset + 1,
|
||||
};
|
||||
const size_t n[3]{
|
||||
objectData.m_normals.GetDistinctPositionByInputPosition(faceVertexOffset + 0) + distinctOffsetsByObject[objectIndex].normalOffset + 1,
|
||||
objectData.m_normals.GetDistinctPositionByInputPosition(faceVertexOffset + 1) + distinctOffsetsByObject[objectIndex].normalOffset + 1,
|
||||
objectData.m_normals.GetDistinctPositionByInputPosition(faceVertexOffset + 2) + distinctOffsetsByObject[objectIndex].normalOffset + 1,
|
||||
};
|
||||
const size_t uv[3]{
|
||||
objectData.m_uvs.GetDistinctPositionByInputPosition(faceVertexOffset + 0) + distinctOffsetsByObject[objectIndex].uvOffset + 1,
|
||||
objectData.m_uvs.GetDistinctPositionByInputPosition(faceVertexOffset + 1) + distinctOffsetsByObject[objectIndex].uvOffset + 1,
|
||||
objectData.m_uvs.GetDistinctPositionByInputPosition(faceVertexOffset + 2) + distinctOffsetsByObject[objectIndex].uvOffset + 1,
|
||||
};
|
||||
|
||||
m_stream << std::format("f {}/{}/{} {}/{}/{} {}/{}/{}\n", v[0], uv[0], n[0], v[1], uv[1], n[1], v[2], uv[2], n[2]);
|
||||
faceIndex++;
|
||||
}
|
||||
|
||||
objectIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
void GetObjObjectDataOffsets(const XModelCommon& xmodel,
|
||||
std::vector<ObjObjectDataOffsets>& inputOffsets,
|
||||
std::vector<ObjObjectDataOffsets>& distinctOffsets)
|
||||
{
|
||||
ObjObjectDataOffsets currentInputOffsets{};
|
||||
ObjObjectDataOffsets currentDistinctOffsets{};
|
||||
|
||||
AddObjFaces(xmodel);
|
||||
|
||||
for (const auto& objectData : m_object_data)
|
||||
{
|
||||
inputOffsets.push_back(currentInputOffsets);
|
||||
distinctOffsets.push_back(currentDistinctOffsets);
|
||||
|
||||
currentInputOffsets.vertexOffset += objectData.m_vertices.GetInputValueCount();
|
||||
currentInputOffsets.normalOffset += objectData.m_normals.GetInputValueCount();
|
||||
currentInputOffsets.uvOffset += objectData.m_uvs.GetInputValueCount();
|
||||
currentDistinctOffsets.vertexOffset += objectData.m_vertices.GetDistinctValueCount();
|
||||
currentDistinctOffsets.normalOffset += objectData.m_normals.GetDistinctValueCount();
|
||||
currentDistinctOffsets.uvOffset += objectData.m_uvs.GetDistinctValueCount();
|
||||
}
|
||||
}
|
||||
|
||||
void AddObjFaces(const XModelCommon& common)
|
||||
{
|
||||
m_object_data.resize(common.m_objects.size());
|
||||
|
||||
auto objectIndex = 0u;
|
||||
for (const auto& commonObject : common.m_objects)
|
||||
{
|
||||
auto& objectData = m_object_data[objectIndex];
|
||||
|
||||
for (const auto& commonFace : commonObject.m_faces)
|
||||
{
|
||||
ObjFace face{};
|
||||
face.vertexIndex[0] = commonFace.vertexIndex[2];
|
||||
face.vertexIndex[1] = commonFace.vertexIndex[1];
|
||||
face.vertexIndex[2] = commonFace.vertexIndex[0];
|
||||
face.normalIndex[0] = face.vertexIndex[0];
|
||||
face.normalIndex[1] = face.vertexIndex[1];
|
||||
face.normalIndex[2] = face.vertexIndex[2];
|
||||
face.uvIndex[0] = face.vertexIndex[0];
|
||||
face.uvIndex[1] = face.vertexIndex[1];
|
||||
face.uvIndex[2] = face.vertexIndex[2];
|
||||
|
||||
// Reverse order for OBJ
|
||||
AddObjVertex(objectData, common, commonFace.vertexIndex[2]);
|
||||
AddObjVertex(objectData, common, commonFace.vertexIndex[1]);
|
||||
AddObjVertex(objectData, common, commonFace.vertexIndex[0]);
|
||||
}
|
||||
|
||||
objectIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
static void AddObjVertex(ObjObjectData& objectData, const XModelCommon& common, const int commonVertexIndex)
|
||||
{
|
||||
const auto& commonVertex = common.m_vertices[commonVertexIndex];
|
||||
|
||||
ObjVertex objVertex{};
|
||||
objVertex.coordinates[0] = commonVertex.coordinates[0];
|
||||
objVertex.coordinates[1] = commonVertex.coordinates[2];
|
||||
objVertex.coordinates[2] = -commonVertex.coordinates[1];
|
||||
objectData.m_vertices.Add(objVertex);
|
||||
|
||||
ObjNormal objNormal{};
|
||||
objNormal.normal[0] = commonVertex.normal[0];
|
||||
objNormal.normal[1] = commonVertex.normal[2];
|
||||
objNormal.normal[2] = -commonVertex.normal[1];
|
||||
objectData.m_normals.Add(objNormal);
|
||||
|
||||
ObjUv objUv{};
|
||||
objUv.uv[0] = commonVertex.uv[0];
|
||||
objUv.uv[1] = 1.0f - commonVertex.uv[1];
|
||||
objectData.m_uvs.Add(objUv);
|
||||
}
|
||||
|
||||
std::ostream& m_stream;
|
||||
std::string m_mtl_name;
|
||||
std::string m_game_name;
|
||||
std::string m_zone_name;
|
||||
std::vector<ObjObjectData> m_object_data;
|
||||
};
|
||||
|
||||
class MtlWriter final : public XModelWriter
|
||||
{
|
||||
public:
|
||||
MtlWriter(std::ostream& stream, std::string gameName, std::string zoneName)
|
||||
: m_stream(stream),
|
||||
m_game_name(std::move(gameName)),
|
||||
m_zone_name(std::move(zoneName))
|
||||
{
|
||||
}
|
||||
|
||||
void Write(const XModelCommon& xmodel) override
|
||||
{
|
||||
m_stream << "# OpenAssetTools MAT File ( " << m_game_name << ")\n";
|
||||
m_stream << "# Game Origin: " << m_game_name << "\n";
|
||||
m_stream << "# Zone Origin: " << m_zone_name << "\n";
|
||||
m_stream << "# Material count: " << xmodel.m_materials.size() << "\n";
|
||||
|
||||
for (const auto& material : xmodel.m_materials)
|
||||
{
|
||||
m_stream << "\n";
|
||||
m_stream << "newmtl " << material.name << "\n";
|
||||
|
||||
if (!material.colorMapName.empty())
|
||||
m_stream << "map_Kd ../images/" << material.colorMapName << ".dds\n";
|
||||
|
||||
if (!material.normalMapName.empty())
|
||||
m_stream << "map_bump ../images/" << material.normalMapName << ".dds\n";
|
||||
|
||||
if (!material.specularMapName.empty())
|
||||
m_stream << "map_Ks ../images/" << material.specularMapName << ".dds\n";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream& m_stream;
|
||||
std::string m_game_name;
|
||||
std::string m_zone_name;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace obj
|
||||
{
|
||||
std::unique_ptr<XModelWriter> CreateObjWriter(std::ostream& stream, std::string mtlName, std::string gameName, std::string zoneName)
|
||||
{
|
||||
return std::make_unique<ObjWriter>(stream, std::move(mtlName), std::move(gameName), std::move(zoneName));
|
||||
}
|
||||
|
||||
std::unique_ptr<XModelWriter> CreateMtlWriter(std::ostream& stream, std::string gameName, std::string zoneName)
|
||||
{
|
||||
return std::make_unique<MtlWriter>(stream, std::move(gameName), std::move(zoneName));
|
||||
}
|
||||
} // namespace obj
|
12
src/ObjWriting/XModel/Obj/ObjWriter.h
Normal file
12
src/ObjWriting/XModel/Obj/ObjWriter.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "XModel/XModelWriter.h"
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
namespace obj
|
||||
{
|
||||
std::unique_ptr<XModelWriter> CreateObjWriter(std::ostream& stream, std::string mtlName, std::string gameName, std::string zoneName);
|
||||
std::unique_ptr<XModelWriter> CreateMtlWriter(std::ostream& stream, std::string gameName, std::string zoneName);
|
||||
} // namespace obj
|
18
src/ObjWriting/XModel/XModelWriter.h
Normal file
18
src/ObjWriting/XModel/XModelWriter.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "XModel/XModelCommon.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class XModelWriter
|
||||
{
|
||||
public:
|
||||
XModelWriter() = default;
|
||||
virtual ~XModelWriter() = default;
|
||||
XModelWriter(const XModelWriter& other) = default;
|
||||
XModelWriter(XModelWriter&& other) noexcept = default;
|
||||
XModelWriter& operator=(const XModelWriter& other) = default;
|
||||
XModelWriter& operator=(XModelWriter&& other) noexcept = default;
|
||||
|
||||
virtual void Write(const XModelCommon& xmodel) = 0;
|
||||
};
|
@ -5,6 +5,7 @@
|
||||
#include "ObjWriting.h"
|
||||
#include "Utils/Arguments/UsageInformation.h"
|
||||
#include "Utils/FileUtils.h"
|
||||
#include "Utils/StringUtils.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
@ -79,7 +80,7 @@ const CommandLineOption* const OPTION_IMAGE_FORMAT =
|
||||
const CommandLineOption* const OPTION_MODEL_FORMAT =
|
||||
CommandLineOption::Builder::Create()
|
||||
.WithLongName("model-format")
|
||||
.WithDescription("Specifies the format of dumped model files. Valid values are: XMODEL_EXPORT, OBJ")
|
||||
.WithDescription("Specifies the format of dumped model files. Valid values are: XMODEL_EXPORT, OBJ, GLTF, GLB")
|
||||
.WithParameter("modelFormatValue")
|
||||
.Build();
|
||||
|
||||
@ -179,8 +180,7 @@ void UnlinkerArgs::SetVerbose(const bool isVerbose)
|
||||
bool UnlinkerArgs::SetImageDumpingMode()
|
||||
{
|
||||
auto specifiedValue = m_argument_parser.GetValueForOption(OPTION_IMAGE_FORMAT);
|
||||
for (auto& c : specifiedValue)
|
||||
c = static_cast<char>(tolower(c));
|
||||
utils::MakeStringLowerCase(specifiedValue);
|
||||
|
||||
if (specifiedValue == "dds")
|
||||
{
|
||||
@ -202,8 +202,7 @@ bool UnlinkerArgs::SetImageDumpingMode()
|
||||
bool UnlinkerArgs::SetModelDumpingMode()
|
||||
{
|
||||
auto specifiedValue = m_argument_parser.GetValueForOption(OPTION_MODEL_FORMAT);
|
||||
for (auto& c : specifiedValue)
|
||||
c = static_cast<char>(tolower(c));
|
||||
utils::MakeStringLowerCase(specifiedValue);
|
||||
|
||||
if (specifiedValue == "xmodel_export")
|
||||
{
|
||||
@ -217,6 +216,18 @@ bool UnlinkerArgs::SetModelDumpingMode()
|
||||
return true;
|
||||
}
|
||||
|
||||
if (specifiedValue == "gltf")
|
||||
{
|
||||
ObjWriting::Configuration.ModelOutputFormat = ObjWriting::Configuration_t::ModelOutputFormat_e::GLTF;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (specifiedValue == "glb")
|
||||
{
|
||||
ObjWriting::Configuration.ModelOutputFormat = ObjWriting::Configuration_t::ModelOutputFormat_e::GLB;
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string originalValue = m_argument_parser.GetValueForOption(OPTION_MODEL_FORMAT);
|
||||
printf("Illegal value: \"%s\" is not a valid model output format. Use -? to see usage information.\n", originalValue.c_str());
|
||||
return false;
|
||||
@ -238,8 +249,7 @@ void UnlinkerArgs::ParseCommaSeparatedAssetTypeString(const std::string& input)
|
||||
size_t endPos;
|
||||
|
||||
std::string lowerInput(input);
|
||||
for (auto& c : lowerInput)
|
||||
c = static_cast<char>(tolower(c));
|
||||
utils::MakeStringLowerCase(lowerInput);
|
||||
|
||||
while (currentPos < lowerInput.size() && (endPos = lowerInput.find_first_of(',', currentPos)) != std::string::npos)
|
||||
{
|
||||
|
@ -51,6 +51,135 @@ public:
|
||||
|
||||
return Matrix<T>(m00, m01, m02, 0, m10, m11, m12, 0, m20, m21, m22, 0, 0, 0, 0, T(1.0));
|
||||
}
|
||||
|
||||
static T dot(const Quaternion& q1, const Quaternion& q2)
|
||||
{
|
||||
return static_cast<T>((q1.m_x * q2.m_x) + (q1.m_y * q2.m_y) + (q1.m_z * q2.m_z) + (q1.m_w * q2.m_w));
|
||||
}
|
||||
|
||||
static Quaternion& conj(Quaternion& result)
|
||||
{
|
||||
result.m_x = -result.m_x;
|
||||
result.m_y = -result.m_y;
|
||||
result.m_z = -result.m_z;
|
||||
return result;
|
||||
}
|
||||
|
||||
static Quaternion& invert(Quaternion& result)
|
||||
{
|
||||
// from game programming gems p198
|
||||
// do result = conj( q ) / norm( q )
|
||||
Quaternion::conj(result);
|
||||
|
||||
// return if norm() is near 0 (divide by 0 would result in NaN)
|
||||
T l = result.lengthSquared();
|
||||
if (l < static_cast<T>(0.0001))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
T l_inv = static_cast<T>(1.0) / l;
|
||||
result.m_x *= l_inv;
|
||||
result.m_y *= l_inv;
|
||||
result.m_z *= l_inv;
|
||||
result.m_w *= l_inv;
|
||||
return result;
|
||||
}
|
||||
|
||||
T lengthSquared() const
|
||||
{
|
||||
return Quaternion::dot(*this, *this);
|
||||
}
|
||||
|
||||
T length() const
|
||||
{
|
||||
return sqrt(lengthSquared());
|
||||
}
|
||||
|
||||
void Normalize()
|
||||
{
|
||||
const auto l = length();
|
||||
|
||||
// return if no magnitude (already as normalized as possible)
|
||||
if (l < static_cast<T>(0.0001))
|
||||
return;
|
||||
|
||||
T inverseLength = static_cast<T>(1.0) / l;
|
||||
m_x *= inverseLength;
|
||||
m_y *= inverseLength;
|
||||
m_z *= inverseLength;
|
||||
m_w *= inverseLength;
|
||||
}
|
||||
|
||||
friend Quaternion operator+(const Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
return Quaternion(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y, lhs.m_z + rhs.m_z, lhs.m_w + rhs.m_w);
|
||||
}
|
||||
|
||||
friend Quaternion operator-(const Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
return Quaternion(lhs.m_x - rhs.m_x, lhs.m_y - rhs.m_y, lhs.m_z - rhs.m_z, lhs.m_w - rhs.m_w);
|
||||
}
|
||||
|
||||
friend Quaternion& operator+=(Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
lhs.m_x += rhs.m_x;
|
||||
lhs.m_y += rhs.m_y;
|
||||
lhs.m_z += rhs.m_z;
|
||||
lhs.m_w += rhs.m_w;
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend Quaternion& operator-=(Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
lhs.m_x -= rhs.m_x;
|
||||
lhs.m_y -= rhs.m_y;
|
||||
lhs.m_z -= rhs.m_z;
|
||||
lhs.m_w -= rhs.m_w;
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend Quaternion operator*(const Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
const T x2 = lhs.m_w * rhs.m_x + lhs.m_x * rhs.m_w + lhs.m_y * rhs.m_z - lhs.m_z * rhs.m_y;
|
||||
const T y2 = lhs.m_w * rhs.m_y + lhs.m_y * rhs.m_w + lhs.m_z * rhs.m_x - lhs.m_x * rhs.m_z;
|
||||
const T z2 = lhs.m_w * rhs.m_z + lhs.m_z * rhs.m_w + lhs.m_x * rhs.m_y - lhs.m_y * rhs.m_x;
|
||||
const T w2 = lhs.m_w * rhs.m_w - lhs.m_x * rhs.m_x - lhs.m_y * rhs.m_y - lhs.m_z * rhs.m_z;
|
||||
|
||||
return Quaternion(x2, y2, z2, w2);
|
||||
}
|
||||
|
||||
friend Quaternion operator/(const Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
Quaternion rhsInv = rhs;
|
||||
Quaternion::invert(rhsInv);
|
||||
return lhs * rhsInv;
|
||||
}
|
||||
|
||||
friend Quaternion& operator*=(Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
const T x2 = lhs.m_w * rhs.m_x + lhs.m_x * rhs.m_w + lhs.m_y * rhs.m_z - lhs.m_z * rhs.m_y;
|
||||
const T y2 = lhs.m_w * rhs.m_y + lhs.m_y * rhs.m_w + lhs.m_z * rhs.m_x - lhs.m_x * rhs.m_z;
|
||||
const T z2 = lhs.m_w * rhs.m_z + lhs.m_z * rhs.m_w + lhs.m_x * rhs.m_y - lhs.m_y * rhs.m_x;
|
||||
const T w2 = lhs.m_w * rhs.m_w - lhs.m_x * rhs.m_x - lhs.m_y * rhs.m_y - lhs.m_z * rhs.m_z;
|
||||
|
||||
lhs.m_x = x2;
|
||||
lhs.m_y = y2;
|
||||
lhs.m_z = z2;
|
||||
lhs.m_w = w2;
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend Quaternion& operator/=(Quaternion& lhs, const Quaternion& rhs)
|
||||
{
|
||||
Quaternion rhsInv = rhs;
|
||||
Quaternion::invert(rhsInv);
|
||||
lhs *= rhsInv;
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
typedef Quaternion<float> Quaternion32;
|
||||
|
@ -24,6 +24,12 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
~Vector2() = default;
|
||||
Vector2(const Vector2& other) = default;
|
||||
Vector2(Vector2&& other) noexcept = default;
|
||||
Vector2& operator=(const Vector2& other) = default;
|
||||
Vector2& operator=(Vector2&& other) noexcept = default;
|
||||
|
||||
_NODISCARD T& operator()(const size_t index)
|
||||
{
|
||||
assert(index < 2);
|
||||
@ -55,6 +61,32 @@ public:
|
||||
{
|
||||
return m_value[1];
|
||||
}
|
||||
|
||||
friend Vector2 operator+(const Vector2& lhs, const Vector2& rhs)
|
||||
{
|
||||
return Vector2(lhs.m_value[0] + rhs.m_value[0], lhs.m_value[1] + rhs.m_value[1]);
|
||||
}
|
||||
|
||||
friend Vector2 operator-(const Vector2& lhs, const Vector2& rhs)
|
||||
{
|
||||
return Vector2(lhs.m_value[0] - rhs.m_value[0], lhs.m_value[1] - rhs.m_value[1]);
|
||||
}
|
||||
|
||||
friend Vector2& operator+=(Vector2& lhs, const Vector2& rhs)
|
||||
{
|
||||
lhs.m_value[0] += rhs.m_value[0];
|
||||
lhs.m_value[1] += rhs.m_value[1];
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend Vector2& operator-=(Vector2& lhs, const Vector2& rhs)
|
||||
{
|
||||
lhs.m_value[0] -= rhs.m_value[0];
|
||||
lhs.m_value[1] -= rhs.m_value[1];
|
||||
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
typedef Vector2<float> Vector2f;
|
||||
@ -80,6 +112,12 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
~Vector3() = default;
|
||||
Vector3(const Vector3& other) = default;
|
||||
Vector3(Vector3&& other) noexcept = default;
|
||||
Vector3& operator=(const Vector3& other) = default;
|
||||
Vector3& operator=(Vector3&& other) noexcept = default;
|
||||
|
||||
_NODISCARD T& operator()(const size_t index)
|
||||
{
|
||||
assert(index < 3);
|
||||
@ -151,6 +189,34 @@ public:
|
||||
{
|
||||
return m_value[2];
|
||||
}
|
||||
|
||||
friend Vector3 operator+(const Vector3& lhs, const Vector3& rhs)
|
||||
{
|
||||
return Vector3(lhs.m_value[0] + rhs.m_value[0], lhs.m_value[1] + rhs.m_value[1], lhs.m_value[2] + rhs.m_value[2]);
|
||||
}
|
||||
|
||||
friend Vector3 operator-(const Vector3& lhs, const Vector3& rhs)
|
||||
{
|
||||
return Vector3(lhs.m_value[0] - rhs.m_value[0], lhs.m_value[1] - rhs.m_value[1], lhs.m_value[2] - rhs.m_value[2]);
|
||||
}
|
||||
|
||||
friend Vector3& operator+=(Vector3& lhs, const Vector3& rhs)
|
||||
{
|
||||
lhs.m_value[0] += rhs.m_value[0];
|
||||
lhs.m_value[1] += rhs.m_value[1];
|
||||
lhs.m_value[2] += rhs.m_value[2];
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend Vector3& operator-=(Vector3& lhs, const Vector3& rhs)
|
||||
{
|
||||
lhs.m_value[0] -= rhs.m_value[0];
|
||||
lhs.m_value[1] -= rhs.m_value[1];
|
||||
lhs.m_value[2] -= rhs.m_value[2];
|
||||
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
typedef Vector3<float> Vector3f;
|
||||
@ -176,6 +242,12 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
~Vector4() = default;
|
||||
Vector4(const Vector4& other) = default;
|
||||
Vector4(Vector4&& other) noexcept = default;
|
||||
Vector4& operator=(const Vector4& other) = default;
|
||||
Vector4& operator=(Vector4&& other) noexcept = default;
|
||||
|
||||
_NODISCARD T& operator()(const size_t index)
|
||||
{
|
||||
assert(index < 4);
|
||||
@ -267,6 +339,36 @@ public:
|
||||
{
|
||||
return m_value[3];
|
||||
}
|
||||
|
||||
friend Vector4 operator+(const Vector4& lhs, const Vector4& rhs)
|
||||
{
|
||||
return Vector4(lhs.m_value[0] + rhs.m_value[0], lhs.m_value[1] + rhs.m_value[1], lhs.m_value[2] + rhs.m_value[2], lhs.m_value[3] + rhs.m_value[3]);
|
||||
}
|
||||
|
||||
friend Vector4 operator-(const Vector4& lhs, const Vector4& rhs)
|
||||
{
|
||||
return Vector4(lhs.m_value[0] - rhs.m_value[0], lhs.m_value[1] - rhs.m_value[1], lhs.m_value[2] - rhs.m_value[2], lhs.m_value[3] - rhs.m_value[3]);
|
||||
}
|
||||
|
||||
friend Vector4& operator+=(Vector4& lhs, const Vector4& rhs)
|
||||
{
|
||||
lhs.m_value[0] += rhs.m_value[0];
|
||||
lhs.m_value[1] += rhs.m_value[1];
|
||||
lhs.m_value[2] += rhs.m_value[2];
|
||||
lhs.m_value[3] += rhs.m_value[3];
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend Vector4& operator-=(Vector4& lhs, const Vector4& rhs)
|
||||
{
|
||||
lhs.m_value[0] -= rhs.m_value[0];
|
||||
lhs.m_value[1] -= rhs.m_value[1];
|
||||
lhs.m_value[2] -= rhs.m_value[2];
|
||||
lhs.m_value[3] -= rhs.m_value[3];
|
||||
|
||||
return lhs;
|
||||
}
|
||||
};
|
||||
|
||||
typedef Vector4<float> Vector4f;
|
||||
|
Loading…
x
Reference in New Issue
Block a user