mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-20 08:05:45 +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 tdef_align(128) float float_align128;
|
||||||
|
|
||||||
|
typedef uint16_t ScriptString;
|
||||||
|
|
||||||
struct dvar_t;
|
struct dvar_t;
|
||||||
struct MenuCell;
|
struct MenuCell;
|
||||||
struct cplane_s;
|
struct cplane_s;
|
||||||
@ -591,14 +593,22 @@ namespace T6
|
|||||||
int partBits[5];
|
int partBits[5];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum XModelLodRampType : unsigned char
|
||||||
|
{
|
||||||
|
XMODEL_LOD_RAMP_RIGID = 0x0,
|
||||||
|
XMODEL_LOD_RAMP_SKINNED = 0x1,
|
||||||
|
|
||||||
|
XMODEL_LOD_RAMP_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
struct XModel
|
struct XModel
|
||||||
{
|
{
|
||||||
const char* name;
|
const char* name;
|
||||||
unsigned char numBones;
|
unsigned char numBones;
|
||||||
unsigned char numRootBones;
|
unsigned char numRootBones;
|
||||||
unsigned char numsurfs;
|
unsigned char numsurfs;
|
||||||
char lodRampType;
|
XModelLodRampType lodRampType;
|
||||||
uint16_t* boneNames;
|
ScriptString* boneNames;
|
||||||
unsigned char* parentList;
|
unsigned char* parentList;
|
||||||
uint16_t (*quats)[4];
|
uint16_t (*quats)[4];
|
||||||
float (*trans)[4];
|
float (*trans)[4];
|
||||||
@ -618,7 +628,7 @@ namespace T6
|
|||||||
uint16_t collLod;
|
uint16_t collLod;
|
||||||
float* himipInvSqRadii;
|
float* himipInvSqRadii;
|
||||||
int memUsage;
|
int memUsage;
|
||||||
int flags;
|
unsigned int flags;
|
||||||
bool bad;
|
bool bad;
|
||||||
PhysPreset* physPreset;
|
PhysPreset* physPreset;
|
||||||
unsigned char numCollmaps;
|
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 "Math/Quaternion.h"
|
||||||
#include "Utils/DistinctMapper.h"
|
#include "Utils/DistinctMapper.h"
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
struct XModelObject
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct XModelBone
|
struct XModelBone
|
||||||
{
|
{
|
||||||
@ -30,8 +25,7 @@ struct XModelBoneWeight
|
|||||||
|
|
||||||
struct XModelVertexBoneWeightCollection
|
struct XModelVertexBoneWeightCollection
|
||||||
{
|
{
|
||||||
std::unique_ptr<XModelBoneWeight[]> weights;
|
std::vector<XModelBoneWeight> weights;
|
||||||
size_t totalWeightCount;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct XModelVertexBoneWeights
|
struct XModelVertexBoneWeights
|
||||||
@ -51,8 +45,6 @@ struct XModelVertex
|
|||||||
struct XModelFace
|
struct XModelFace
|
||||||
{
|
{
|
||||||
int vertexIndex[3];
|
int vertexIndex[3];
|
||||||
int objectIndex;
|
|
||||||
int materialIndex;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct XModelMaterial
|
struct XModelMaterial
|
||||||
@ -89,10 +81,30 @@ struct XModelMaterial
|
|||||||
float blinn[2];
|
float blinn[2];
|
||||||
float phong;
|
float phong;
|
||||||
std::string colorMapName;
|
std::string colorMapName;
|
||||||
|
std::string normalMapName;
|
||||||
|
std::string specularMapName;
|
||||||
|
|
||||||
void ApplyDefaults();
|
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
|
struct VertexMergerPos
|
||||||
{
|
{
|
||||||
float x;
|
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(ZoneCommon)
|
||||||
links:linkto(minilzo)
|
links:linkto(minilzo)
|
||||||
links:linkto(minizip)
|
links:linkto(minizip)
|
||||||
|
links:linkto(libtomcrypt)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ObjWriting:use()
|
function ObjWriting:use()
|
||||||
@ -55,5 +56,6 @@ function ObjWriting:project()
|
|||||||
minilzo:include(includes)
|
minilzo:include(includes)
|
||||||
minizip:include(includes)
|
minizip:include(includes)
|
||||||
json:include(includes)
|
json:include(includes)
|
||||||
|
libtomcrypt:include(includes)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,37 +2,11 @@
|
|||||||
|
|
||||||
#include "Dumping/AbstractAssetDumper.h"
|
#include "Dumping/AbstractAssetDumper.h"
|
||||||
#include "Game/IW3/IW3.h"
|
#include "Game/IW3/IW3.h"
|
||||||
#include "Model/Obj/ObjWriter.h"
|
|
||||||
#include "Model/XModel/AbstractXModelWriter.h"
|
|
||||||
#include "Utils/DistinctMapper.h"
|
|
||||||
|
|
||||||
namespace IW3
|
namespace IW3
|
||||||
{
|
{
|
||||||
class AssetDumperXModel final : public AbstractAssetDumper<XModel>
|
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:
|
protected:
|
||||||
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
||||||
void DumpAsset(AssetDumpingContext& context, 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 "Dumping/AbstractAssetDumper.h"
|
||||||
#include "Game/IW4/IW4.h"
|
#include "Game/IW4/IW4.h"
|
||||||
#include "Model/Obj/ObjWriter.h"
|
|
||||||
#include "Model/XModel/AbstractXModelWriter.h"
|
|
||||||
#include "Utils/DistinctMapper.h"
|
|
||||||
|
|
||||||
namespace IW4
|
namespace IW4
|
||||||
{
|
{
|
||||||
class AssetDumperXModel final : public AbstractAssetDumper<XModel>
|
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:
|
protected:
|
||||||
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
||||||
void DumpAsset(AssetDumpingContext& context, 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 "Dumping/AbstractAssetDumper.h"
|
||||||
#include "Game/IW5/IW5.h"
|
#include "Game/IW5/IW5.h"
|
||||||
#include "Model/Obj/ObjWriter.h"
|
|
||||||
#include "Model/XModel/AbstractXModelWriter.h"
|
|
||||||
#include "Utils/DistinctMapper.h"
|
|
||||||
|
|
||||||
namespace IW5
|
namespace IW5
|
||||||
{
|
{
|
||||||
class AssetDumperXModel final : public AbstractAssetDumper<XModel>
|
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:
|
protected:
|
||||||
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
||||||
void DumpAsset(AssetDumpingContext& context, 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 "Dumping/AbstractAssetDumper.h"
|
||||||
#include "Game/T5/T5.h"
|
#include "Game/T5/T5.h"
|
||||||
#include "Model/Obj/ObjWriter.h"
|
|
||||||
#include "Model/XModel/AbstractXModelWriter.h"
|
|
||||||
#include "Utils/DistinctMapper.h"
|
|
||||||
|
|
||||||
namespace T5
|
namespace T5
|
||||||
{
|
{
|
||||||
class AssetDumperXModel final : public AbstractAssetDumper<XModel>
|
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:
|
protected:
|
||||||
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
||||||
void DumpAsset(AssetDumpingContext& context, 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 "Dumping/AbstractAssetDumper.h"
|
||||||
#include "Game/T6/T6.h"
|
#include "Game/T6/T6.h"
|
||||||
#include "Model/Obj/ObjWriter.h"
|
|
||||||
#include "Model/XModel/AbstractXModelWriter.h"
|
|
||||||
#include "Utils/DistinctMapper.h"
|
|
||||||
|
|
||||||
namespace T6
|
namespace T6
|
||||||
{
|
{
|
||||||
class AssetDumperXModel final : public AbstractAssetDumper<XModel>
|
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:
|
protected:
|
||||||
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
||||||
void DumpAsset(AssetDumpingContext& context, 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
|
enum class ModelOutputFormat_e
|
||||||
{
|
{
|
||||||
XMODEL_EXPORT,
|
XMODEL_EXPORT,
|
||||||
OBJ
|
OBJ,
|
||||||
|
GLTF,
|
||||||
|
GLB
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Verbose = false;
|
bool Verbose = false;
|
||||||
std::vector<bool> AssetTypesToHandleBitfield;
|
std::vector<bool> AssetTypesToHandleBitfield;
|
||||||
|
|
||||||
ImageOutputFormat_e ImageOutputFormat = ImageOutputFormat_e::DDS;
|
ImageOutputFormat_e ImageOutputFormat = ImageOutputFormat_e::DDS;
|
||||||
ModelOutputFormat_e ModelOutputFormat = ModelOutputFormat_e::XMODEL_EXPORT;
|
ModelOutputFormat_e ModelOutputFormat = ModelOutputFormat_e::GLB;
|
||||||
bool MenuLegacyMode = false;
|
bool MenuLegacyMode = false;
|
||||||
|
|
||||||
} Configuration;
|
} 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 "ObjWriting.h"
|
||||||
#include "Utils/Arguments/UsageInformation.h"
|
#include "Utils/Arguments/UsageInformation.h"
|
||||||
#include "Utils/FileUtils.h"
|
#include "Utils/FileUtils.h"
|
||||||
|
#include "Utils/StringUtils.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
@ -79,7 +80,7 @@ const CommandLineOption* const OPTION_IMAGE_FORMAT =
|
|||||||
const CommandLineOption* const OPTION_MODEL_FORMAT =
|
const CommandLineOption* const OPTION_MODEL_FORMAT =
|
||||||
CommandLineOption::Builder::Create()
|
CommandLineOption::Builder::Create()
|
||||||
.WithLongName("model-format")
|
.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")
|
.WithParameter("modelFormatValue")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
@ -179,8 +180,7 @@ void UnlinkerArgs::SetVerbose(const bool isVerbose)
|
|||||||
bool UnlinkerArgs::SetImageDumpingMode()
|
bool UnlinkerArgs::SetImageDumpingMode()
|
||||||
{
|
{
|
||||||
auto specifiedValue = m_argument_parser.GetValueForOption(OPTION_IMAGE_FORMAT);
|
auto specifiedValue = m_argument_parser.GetValueForOption(OPTION_IMAGE_FORMAT);
|
||||||
for (auto& c : specifiedValue)
|
utils::MakeStringLowerCase(specifiedValue);
|
||||||
c = static_cast<char>(tolower(c));
|
|
||||||
|
|
||||||
if (specifiedValue == "dds")
|
if (specifiedValue == "dds")
|
||||||
{
|
{
|
||||||
@ -202,8 +202,7 @@ bool UnlinkerArgs::SetImageDumpingMode()
|
|||||||
bool UnlinkerArgs::SetModelDumpingMode()
|
bool UnlinkerArgs::SetModelDumpingMode()
|
||||||
{
|
{
|
||||||
auto specifiedValue = m_argument_parser.GetValueForOption(OPTION_MODEL_FORMAT);
|
auto specifiedValue = m_argument_parser.GetValueForOption(OPTION_MODEL_FORMAT);
|
||||||
for (auto& c : specifiedValue)
|
utils::MakeStringLowerCase(specifiedValue);
|
||||||
c = static_cast<char>(tolower(c));
|
|
||||||
|
|
||||||
if (specifiedValue == "xmodel_export")
|
if (specifiedValue == "xmodel_export")
|
||||||
{
|
{
|
||||||
@ -217,6 +216,18 @@ bool UnlinkerArgs::SetModelDumpingMode()
|
|||||||
return true;
|
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);
|
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());
|
printf("Illegal value: \"%s\" is not a valid model output format. Use -? to see usage information.\n", originalValue.c_str());
|
||||||
return false;
|
return false;
|
||||||
@ -238,8 +249,7 @@ void UnlinkerArgs::ParseCommaSeparatedAssetTypeString(const std::string& input)
|
|||||||
size_t endPos;
|
size_t endPos;
|
||||||
|
|
||||||
std::string lowerInput(input);
|
std::string lowerInput(input);
|
||||||
for (auto& c : lowerInput)
|
utils::MakeStringLowerCase(lowerInput);
|
||||||
c = static_cast<char>(tolower(c));
|
|
||||||
|
|
||||||
while (currentPos < lowerInput.size() && (endPos = lowerInput.find_first_of(',', currentPos)) != std::string::npos)
|
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));
|
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;
|
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)
|
_NODISCARD T& operator()(const size_t index)
|
||||||
{
|
{
|
||||||
assert(index < 2);
|
assert(index < 2);
|
||||||
@ -55,6 +61,32 @@ public:
|
|||||||
{
|
{
|
||||||
return m_value[1];
|
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;
|
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)
|
_NODISCARD T& operator()(const size_t index)
|
||||||
{
|
{
|
||||||
assert(index < 3);
|
assert(index < 3);
|
||||||
@ -151,6 +189,34 @@ public:
|
|||||||
{
|
{
|
||||||
return m_value[2];
|
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;
|
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)
|
_NODISCARD T& operator()(const size_t index)
|
||||||
{
|
{
|
||||||
assert(index < 4);
|
assert(index < 4);
|
||||||
@ -267,6 +339,36 @@ public:
|
|||||||
{
|
{
|
||||||
return m_value[3];
|
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;
|
typedef Vector4<float> Vector4f;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user