Make file paths work cross platform
This commit is contained in:
parent
de09acb789
commit
15d0516fd3
12
npm/index.js
12
npm/index.js
|
@ -38,12 +38,11 @@ function convert(srcFile, destFile, opts = []) {
|
||||||
throw new Error(`Unsupported file extension: ${destFile}`);
|
throw new Error(`Unsupported file extension: ${destFile}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let srcPath = fs.realpathSync(srcFile);
|
let destDir = path.dirname(destFile);
|
||||||
let destDir = fs.realpathSync(path.dirname(destFile));
|
|
||||||
let destPath = path.join(destDir, path.basename(destFile, destExt));
|
let destPath = path.join(destDir, path.basename(destFile, destExt));
|
||||||
|
|
||||||
let args = opts.slice(0);
|
let args = opts.slice(0);
|
||||||
args.push('--input', srcPath, '--output', destPath);
|
args.push('--input', srcFile, '--output', destPath);
|
||||||
let child = childProcess.spawn(tool, args);
|
let child = childProcess.spawn(tool, args);
|
||||||
|
|
||||||
let output = '';
|
let output = '';
|
||||||
|
@ -52,7 +51,7 @@ function convert(srcFile, destFile, opts = []) {
|
||||||
child.on('error', reject);
|
child.on('error', reject);
|
||||||
child.on('close', code => {
|
child.on('close', code => {
|
||||||
// the FBX SDK may create an .fbm dir during conversion; delete!
|
// the FBX SDK may create an .fbm dir during conversion; delete!
|
||||||
let fbmCruft = srcPath.replace(/.fbx$/i, '.fbm');
|
let fbmCruft = srcFile.replace(/.fbx$/i, '.fbm');
|
||||||
// don't stick a fork in things if this fails, just log a warning
|
// don't stick a fork in things if this fails, just log a warning
|
||||||
const onError = error =>
|
const onError = error =>
|
||||||
error && console.warn(`Failed to delete ${fbmCruft}: ${error}`);
|
error && console.warn(`Failed to delete ${fbmCruft}: ${error}`);
|
||||||
|
@ -67,6 +66,11 @@ function convert(srcFile, destFile, opts = []) {
|
||||||
reject(new Error(`Converter output:\n` +
|
reject(new Error(`Converter output:\n` +
|
||||||
(output.length ? output : "<none>")));
|
(output.length ? output : "<none>")));
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
if (destExt === ".gltf") {
|
||||||
|
destPath = path.join(destPath + "_out", path.basename(destFile, destExt));
|
||||||
|
}
|
||||||
|
|
||||||
resolve(destPath + destExt);
|
resolve(destPath + destExt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
417
src/Raw2Gltf.cpp
417
src/Raw2Gltf.cpp
|
@ -76,7 +76,8 @@ struct GLTFData
|
||||||
std::shared_ptr<BufferViewData> GetAlignedBufferView(BufferData &buffer, const BufferViewData::GL_ArrayType target)
|
std::shared_ptr<BufferViewData> GetAlignedBufferView(BufferData &buffer, const BufferViewData::GL_ArrayType target)
|
||||||
{
|
{
|
||||||
unsigned long bufferSize = this->binary->size();
|
unsigned long bufferSize = this->binary->size();
|
||||||
if ((bufferSize % 4) > 0) {
|
if ((bufferSize % 4) > 0)
|
||||||
|
{
|
||||||
bufferSize += (4 - (bufferSize % 4));
|
bufferSize += (4 - (bufferSize % 4));
|
||||||
this->binary->resize(bufferSize);
|
this->binary->resize(bufferSize);
|
||||||
}
|
}
|
||||||
|
@ -102,23 +103,30 @@ struct GLTFData
|
||||||
{
|
{
|
||||||
// see if we've already created a BufferViewData for this precise file
|
// see if we've already created a BufferViewData for this precise file
|
||||||
auto iter = filenameToBufferView.find(filename);
|
auto iter = filenameToBufferView.find(filename);
|
||||||
if (iter != filenameToBufferView.end()) {
|
if (iter != filenameToBufferView.end())
|
||||||
|
{
|
||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<BufferViewData> result;
|
std::shared_ptr<BufferViewData> result;
|
||||||
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
||||||
if (file) {
|
if (file)
|
||||||
|
{
|
||||||
std::streamsize size = file.tellg();
|
std::streamsize size = file.tellg();
|
||||||
file.seekg(0, std::ios::beg);
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
std::vector<char> fileBuffer(size);
|
std::vector<char> fileBuffer(size);
|
||||||
if (file.read(fileBuffer.data(), size)) {
|
if (file.read(fileBuffer.data(), size))
|
||||||
|
{
|
||||||
result = AddRawBufferView(buffer, fileBuffer.data(), size);
|
result = AddRawBufferView(buffer, fileBuffer.data(), size);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
fmt::printf("Warning: Couldn't read %lu bytes from %s, skipping file.\n", size, filename);
|
fmt::printf("Warning: Couldn't read %lu bytes from %s, skipping file.\n", size, filename);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
fmt::printf("Warning: Couldn't open file %s, skipping file.\n", filename);
|
fmt::printf("Warning: Couldn't open file %s, skipping file.\n", filename);
|
||||||
}
|
}
|
||||||
// note that we persist here not only success, but also failure, as nullptr
|
// note that we persist here not only success, but also failure, as nullptr
|
||||||
|
@ -126,7 +134,6 @@ struct GLTFData
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
std::shared_ptr<AccessorData> AddAccessorWithView(
|
std::shared_ptr<AccessorData> AddAccessorWithView(
|
||||||
BufferViewData &bufferView, const GLType &type, const std::vector<T> &source)
|
BufferViewData &bufferView, const GLType &type, const std::vector<T> &source)
|
||||||
|
@ -155,12 +162,15 @@ struct GLTFData
|
||||||
surfaceModel.GetAttributeArray<T>(attribArr, attrDef.rawAttributeIx);
|
surfaceModel.GetAttributeArray<T>(attribArr, attrDef.rawAttributeIx);
|
||||||
|
|
||||||
std::shared_ptr<AccessorData> accessor;
|
std::shared_ptr<AccessorData> accessor;
|
||||||
if (attrDef.dracoComponentType != draco::DT_INVALID && primitive.dracoMesh != nullptr) {
|
if (attrDef.dracoComponentType != draco::DT_INVALID && primitive.dracoMesh != nullptr)
|
||||||
|
{
|
||||||
primitive.AddDracoAttrib(attrDef, attribArr);
|
primitive.AddDracoAttrib(attrDef, attribArr);
|
||||||
|
|
||||||
accessor = accessors.hold(new AccessorData(attrDef.glType));
|
accessor = accessors.hold(new AccessorData(attrDef.glType));
|
||||||
accessor->count = attribArr.size();
|
accessor->count = attribArr.size();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER);
|
auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER);
|
||||||
accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr);
|
accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr);
|
||||||
}
|
}
|
||||||
|
@ -171,9 +181,11 @@ struct GLTFData
|
||||||
template <class T>
|
template <class T>
|
||||||
void serializeHolder(json &glTFJson, std::string key, const Holder<T> holder)
|
void serializeHolder(json &glTFJson, std::string key, const Holder<T> holder)
|
||||||
{
|
{
|
||||||
if (!holder.ptrs.empty()) {
|
if (!holder.ptrs.empty())
|
||||||
|
{
|
||||||
std::vector<json> bits;
|
std::vector<json> bits;
|
||||||
for (const auto &ptr : holder.ptrs) {
|
for (const auto &ptr : holder.ptrs)
|
||||||
|
{
|
||||||
bits.push_back(ptr->serialize());
|
bits.push_back(ptr->serialize());
|
||||||
}
|
}
|
||||||
glTFJson[key] = bits;
|
glTFJson[key] = bits;
|
||||||
|
@ -204,7 +216,6 @@ struct GLTFData
|
||||||
|
|
||||||
std::shared_ptr<std::vector<uint8_t>> binary;
|
std::shared_ptr<std::vector<uint8_t>> binary;
|
||||||
|
|
||||||
|
|
||||||
Holder<BufferData> buffers;
|
Holder<BufferData> buffers;
|
||||||
Holder<BufferViewData> bufferViews;
|
Holder<BufferViewData> bufferViews;
|
||||||
Holder<AccessorData> accessors;
|
Holder<AccessorData> accessors;
|
||||||
|
@ -220,9 +231,11 @@ struct GLTFData
|
||||||
Holder<SceneData> scenes;
|
Holder<SceneData> scenes;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void WriteToVectorContext(void *context, void *data, int size) {
|
static void WriteToVectorContext(void *context, void *data, int size)
|
||||||
|
{
|
||||||
auto *vec = static_cast<std::vector<char> *>(context);
|
auto *vec = static_cast<std::vector<char> *>(context);
|
||||||
for (int ii = 0; ii < size; ii ++) {
|
for (int ii = 0; ii < size; ii++)
|
||||||
|
{
|
||||||
vec->push_back(((char *)data)[ii]);
|
vec->push_back(((char *)data)[ii]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,7 +267,8 @@ static const std::vector<TriangleIndex> getIndexArray(const RawModel &raw)
|
||||||
{
|
{
|
||||||
std::vector<TriangleIndex> result;
|
std::vector<TriangleIndex> result;
|
||||||
|
|
||||||
for (int i = 0; i < raw.GetTriangleCount(); i++) {
|
for (int i = 0; i < raw.GetTriangleCount(); i++)
|
||||||
|
{
|
||||||
result.push_back((TriangleIndex)raw.GetTriangle(i).verts[0]);
|
result.push_back((TriangleIndex)raw.GetTriangle(i).verts[0]);
|
||||||
result.push_back((TriangleIndex)raw.GetTriangle(i).verts[1]);
|
result.push_back((TriangleIndex)raw.GetTriangle(i).verts[1]);
|
||||||
result.push_back((TriangleIndex)raw.GetTriangle(i).verts[2]);
|
result.push_back((TriangleIndex)raw.GetTriangle(i).verts[2]);
|
||||||
|
@ -263,7 +277,8 @@ static const std::vector<TriangleIndex> getIndexArray(const RawModel &raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: replace with a proper MaterialHasher class
|
// TODO: replace with a proper MaterialHasher class
|
||||||
static const std::string materialHash(const RawMaterial &m) {
|
static const std::string materialHash(const RawMaterial &m)
|
||||||
|
{
|
||||||
return m.name + "_" + std::to_string(m.type);
|
return m.name + "_" + std::to_string(m.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,17 +286,19 @@ ModelData *Raw2Gltf(
|
||||||
std::ofstream &gltfOutStream,
|
std::ofstream &gltfOutStream,
|
||||||
const std::string &outputFolder,
|
const std::string &outputFolder,
|
||||||
const RawModel &raw,
|
const RawModel &raw,
|
||||||
const GltfOptions &options
|
const GltfOptions &options)
|
||||||
)
|
{
|
||||||
|
if (verboseOutput)
|
||||||
{
|
{
|
||||||
if (verboseOutput) {
|
|
||||||
fmt::printf("Building render model...\n");
|
fmt::printf("Building render model...\n");
|
||||||
for (int i = 0; i < raw.GetMaterialCount(); i++) {
|
for (int i = 0; i < raw.GetMaterialCount(); i++)
|
||||||
|
{
|
||||||
fmt::printf(
|
fmt::printf(
|
||||||
"Material %d: %s [shading: %s]\n", i, raw.GetMaterial(i).name.c_str(),
|
"Material %d: %s [shading: %s]\n", i, raw.GetMaterial(i).name.c_str(),
|
||||||
Describe(raw.GetMaterial(i).info->shadingModel));
|
Describe(raw.GetMaterial(i).info->shadingModel));
|
||||||
}
|
}
|
||||||
if (raw.GetVertexCount() > 2 * raw.GetTriangleCount()) {
|
if (raw.GetVertexCount() > 2 * raw.GetTriangleCount())
|
||||||
|
{
|
||||||
fmt::printf(
|
fmt::printf(
|
||||||
"Warning: High vertex count. Make sure there are no unnecessary vertex attributes. (see -keepAttribute cmd-line option)");
|
"Warning: High vertex count. Make sure there are no unnecessary vertex attributes. (see -keepAttribute cmd-line option)");
|
||||||
}
|
}
|
||||||
|
@ -294,7 +311,8 @@ ModelData *Raw2Gltf(
|
||||||
options.keepAttribs,
|
options.keepAttribs,
|
||||||
true);
|
true);
|
||||||
|
|
||||||
if (verboseOutput) {
|
if (verboseOutput)
|
||||||
|
{
|
||||||
fmt::printf("%7d vertices\n", raw.GetVertexCount());
|
fmt::printf("%7d vertices\n", raw.GetVertexCount());
|
||||||
fmt::printf("%7d triangles\n", raw.GetTriangleCount());
|
fmt::printf("%7d triangles\n", raw.GetTriangleCount());
|
||||||
fmt::printf("%7d textures\n", raw.GetTextureCount());
|
fmt::printf("%7d textures\n", raw.GetTextureCount());
|
||||||
|
@ -312,22 +330,22 @@ ModelData *Raw2Gltf(
|
||||||
|
|
||||||
// for now, we only have one buffer; data->binary points to the same vector as that BufferData does.
|
// for now, we only have one buffer; data->binary points to the same vector as that BufferData does.
|
||||||
BufferData &buffer = *gltf->buffers.hold(
|
BufferData &buffer = *gltf->buffers.hold(
|
||||||
options.outputBinary ?
|
options.outputBinary ? new BufferData(gltf->binary) : new BufferData(extBufferFilename, gltf->binary, options.embedResources));
|
||||||
new BufferData(gltf->binary) :
|
|
||||||
new BufferData(extBufferFilename, gltf->binary, options.embedResources));
|
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// nodes
|
// nodes
|
||||||
//
|
//
|
||||||
|
|
||||||
for (int i = 0; i < raw.GetNodeCount(); i++) {
|
for (int i = 0; i < raw.GetNodeCount(); i++)
|
||||||
|
{
|
||||||
// assumption: RawNode index == NodeData index
|
// assumption: RawNode index == NodeData index
|
||||||
const RawNode &node = raw.GetNode(i);
|
const RawNode &node = raw.GetNode(i);
|
||||||
|
|
||||||
auto nodeData = gltf->nodes.hold(
|
auto nodeData = gltf->nodes.hold(
|
||||||
new NodeData(node.name, node.translation, node.rotation, node.scale, node.isJoint));
|
new NodeData(node.name, node.translation, node.rotation, node.scale, node.isJoint));
|
||||||
|
|
||||||
for (const auto &childId : node.childIds) {
|
for (const auto &childId : node.childIds)
|
||||||
|
{
|
||||||
int childIx = raw.GetNodeById(childId);
|
int childIx = raw.GetNodeById(childId);
|
||||||
assert(childIx >= 0);
|
assert(childIx >= 0);
|
||||||
nodeData->AddChildNode(childIx);
|
nodeData->AddChildNode(childIx);
|
||||||
|
@ -340,10 +358,12 @@ ModelData *Raw2Gltf(
|
||||||
// animations
|
// animations
|
||||||
//
|
//
|
||||||
|
|
||||||
for (int i = 0; i < raw.GetAnimationCount(); i++) {
|
for (int i = 0; i < raw.GetAnimationCount(); i++)
|
||||||
|
{
|
||||||
const RawAnimation &animation = raw.GetAnimation(i);
|
const RawAnimation &animation = raw.GetAnimation(i);
|
||||||
|
|
||||||
if (animation.channels.size() == 0) {
|
if (animation.channels.size() == 0)
|
||||||
|
{
|
||||||
fmt::printf("Warning: animation '%s' has zero channels. Skipping.\n", animation.name.c_str());
|
fmt::printf("Warning: animation '%s' has zero channels. Skipping.\n", animation.name.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -353,15 +373,18 @@ ModelData *Raw2Gltf(
|
||||||
accessor->max = {*std::max_element(std::begin(animation.times), std::end(animation.times))};
|
accessor->max = {*std::max_element(std::begin(animation.times), std::end(animation.times))};
|
||||||
|
|
||||||
AnimationData &aDat = *gltf->animations.hold(new AnimationData(animation.name, *accessor));
|
AnimationData &aDat = *gltf->animations.hold(new AnimationData(animation.name, *accessor));
|
||||||
if (verboseOutput) {
|
if (verboseOutput)
|
||||||
|
{
|
||||||
fmt::printf("Animation '%s' has %lu channels:\n", animation.name.c_str(), animation.channels.size());
|
fmt::printf("Animation '%s' has %lu channels:\n", animation.name.c_str(), animation.channels.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t channelIx = 0; channelIx < animation.channels.size(); channelIx++) {
|
for (size_t channelIx = 0; channelIx < animation.channels.size(); channelIx++)
|
||||||
|
{
|
||||||
const RawChannel &channel = animation.channels[channelIx];
|
const RawChannel &channel = animation.channels[channelIx];
|
||||||
const RawNode &node = raw.GetNode(channel.nodeIndex);
|
const RawNode &node = raw.GetNode(channel.nodeIndex);
|
||||||
|
|
||||||
if (verboseOutput) {
|
if (verboseOutput)
|
||||||
|
{
|
||||||
fmt::printf(
|
fmt::printf(
|
||||||
" Channel %lu (%s) has translations/rotations/scales/weights: [%lu, %lu, %lu, %lu]\n",
|
" Channel %lu (%s) has translations/rotations/scales/weights: [%lu, %lu, %lu, %lu]\n",
|
||||||
channelIx, node.name.c_str(), channel.translations.size(), channel.rotations.size(),
|
channelIx, node.name.c_str(), channel.translations.size(), channel.rotations.size(),
|
||||||
|
@ -369,16 +392,20 @@ ModelData *Raw2Gltf(
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeData &nDat = require(nodesById, node.id);
|
NodeData &nDat = require(nodesById, node.id);
|
||||||
if (!channel.translations.empty()) {
|
if (!channel.translations.empty())
|
||||||
|
{
|
||||||
aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, GLT_VEC3F, channel.translations), "translation");
|
aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, GLT_VEC3F, channel.translations), "translation");
|
||||||
}
|
}
|
||||||
if (!channel.rotations.empty()) {
|
if (!channel.rotations.empty())
|
||||||
|
{
|
||||||
aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, GLT_QUATF, channel.rotations), "rotation");
|
aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, GLT_QUATF, channel.rotations), "rotation");
|
||||||
}
|
}
|
||||||
if (!channel.scales.empty()) {
|
if (!channel.scales.empty())
|
||||||
|
{
|
||||||
aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, GLT_VEC3F, channel.scales), "scale");
|
aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, GLT_VEC3F, channel.scales), "scale");
|
||||||
}
|
}
|
||||||
if (!channel.weights.empty()) {
|
if (!channel.weights.empty())
|
||||||
|
{
|
||||||
aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, {CT_FLOAT, 1, "SCALAR"}, channel.weights), "weights");
|
aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, {CT_FLOAT, 1, "SCALAR"}, channel.weights), "weights");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -399,7 +426,8 @@ ModelData *Raw2Gltf(
|
||||||
|
|
||||||
auto texIndicesKey = [&](std::vector<int> ixVec, std::string tag) -> std::string {
|
auto texIndicesKey = [&](std::vector<int> ixVec, std::string tag) -> std::string {
|
||||||
std::string result = tag;
|
std::string result = tag;
|
||||||
for (int ix : ixVec) {
|
for (int ix : ixVec)
|
||||||
|
{
|
||||||
result += "_" + std::to_string(ix);
|
result += "_" + std::to_string(ix);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -414,28 +442,33 @@ ModelData *Raw2Gltf(
|
||||||
std::vector<int> rawTexIndices,
|
std::vector<int> rawTexIndices,
|
||||||
const pixel_merger &combine,
|
const pixel_merger &combine,
|
||||||
const std::string &tag,
|
const std::string &tag,
|
||||||
bool transparentOutput
|
bool transparentOutput) -> std::shared_ptr<TextureData> {
|
||||||
) -> std::shared_ptr<TextureData>
|
|
||||||
{
|
|
||||||
const std::string key = texIndicesKey(rawTexIndices, tag);
|
const std::string key = texIndicesKey(rawTexIndices, tag);
|
||||||
auto iter = textureByIndicesKey.find(key);
|
auto iter = textureByIndicesKey.find(key);
|
||||||
if (iter != textureByIndicesKey.end()) {
|
if (iter != textureByIndicesKey.end())
|
||||||
|
{
|
||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto describeChannel = [&](int channels) -> std::string {
|
auto describeChannel = [&](int channels) -> std::string {
|
||||||
switch(channels) {
|
switch (channels)
|
||||||
case 1: return "G";
|
{
|
||||||
case 2: return "GA";
|
case 1:
|
||||||
case 3: return "RGB";
|
return "G";
|
||||||
case 4: return "RGBA";
|
case 2:
|
||||||
|
return "GA";
|
||||||
|
case 3:
|
||||||
|
return "RGB";
|
||||||
|
case 4:
|
||||||
|
return "RGBA";
|
||||||
default:
|
default:
|
||||||
return fmt::format("?%d?", channels);
|
return fmt::format("?%d?", channels);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// keep track of some texture data as we load them
|
// keep track of some texture data as we load them
|
||||||
struct TexInfo {
|
struct TexInfo
|
||||||
|
{
|
||||||
explicit TexInfo(int rawTexIx) : rawTexIx(rawTexIx) {}
|
explicit TexInfo(int rawTexIx) : rawTexIx(rawTexIx) {}
|
||||||
|
|
||||||
const int rawTexIx;
|
const int rawTexIx;
|
||||||
|
@ -448,23 +481,32 @@ ModelData *Raw2Gltf(
|
||||||
int width = -1, height = -1;
|
int width = -1, height = -1;
|
||||||
std::string mergedFilename = tag;
|
std::string mergedFilename = tag;
|
||||||
std::vector<TexInfo> texes{};
|
std::vector<TexInfo> texes{};
|
||||||
for (const int rawTexIx : rawTexIndices) {
|
for (const int rawTexIx : rawTexIndices)
|
||||||
|
{
|
||||||
TexInfo info(rawTexIx);
|
TexInfo info(rawTexIx);
|
||||||
if (rawTexIx >= 0) {
|
if (rawTexIx >= 0)
|
||||||
|
{
|
||||||
const RawTexture &rawTex = raw.GetTexture(rawTexIx);
|
const RawTexture &rawTex = raw.GetTexture(rawTexIx);
|
||||||
const std::string &fileLoc = rawTex.fileLocation;
|
const std::string &fileLoc = rawTex.fileLocation;
|
||||||
const std::string &name = StringUtils::GetFileBaseString(StringUtils::GetFileNameString(fileLoc));
|
const std::string &name = StringUtils::GetFileBaseString(StringUtils::GetFileNameString(fileLoc));
|
||||||
if (!fileLoc.empty()) {
|
if (!fileLoc.empty())
|
||||||
|
{
|
||||||
info.pixels = stbi_load(fileLoc.c_str(), &info.width, &info.height, &info.channels, 0);
|
info.pixels = stbi_load(fileLoc.c_str(), &info.width, &info.height, &info.channels, 0);
|
||||||
if (!info.pixels) {
|
if (!info.pixels)
|
||||||
|
{
|
||||||
fmt::printf("Warning: merge texture [%d](%s) could not be loaded.\n",
|
fmt::printf("Warning: merge texture [%d](%s) could not be loaded.\n",
|
||||||
rawTexIx,
|
rawTexIx,
|
||||||
name);
|
name);
|
||||||
} else {
|
}
|
||||||
if (width < 0) {
|
else
|
||||||
|
{
|
||||||
|
if (width < 0)
|
||||||
|
{
|
||||||
width = info.width;
|
width = info.width;
|
||||||
height = info.height;
|
height = info.height;
|
||||||
} else if (width != info.width || height != info.height) {
|
}
|
||||||
|
else if (width != info.width || height != info.height)
|
||||||
|
{
|
||||||
fmt::printf("Warning: texture %s (%d, %d) can't be merged with previous texture(s) of dimension (%d, %d)\n",
|
fmt::printf("Warning: texture %s (%d, %d) can't be merged with previous texture(s) of dimension (%d, %d)\n",
|
||||||
name,
|
name,
|
||||||
info.width, info.height, width, height);
|
info.width, info.height, width, height);
|
||||||
|
@ -480,7 +522,8 @@ ModelData *Raw2Gltf(
|
||||||
// at the moment, the best choice of filename is also the best choice of name
|
// at the moment, the best choice of filename is also the best choice of name
|
||||||
const std::string mergedName = mergedFilename;
|
const std::string mergedName = mergedFilename;
|
||||||
|
|
||||||
if (width < 0) {
|
if (width < 0)
|
||||||
|
{
|
||||||
// no textures to merge; bail
|
// no textures to merge; bail
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -492,27 +535,34 @@ ModelData *Raw2Gltf(
|
||||||
std::vector<uint8_t> mergedPixels(static_cast<size_t>(channels * width * height));
|
std::vector<uint8_t> mergedPixels(static_cast<size_t>(channels * width * height));
|
||||||
std::vector<pixel> pixels(texes.size());
|
std::vector<pixel> pixels(texes.size());
|
||||||
std::vector<const pixel *> pixelPointers(texes.size());
|
std::vector<const pixel *> pixelPointers(texes.size());
|
||||||
for (int xx = 0; xx < width; xx ++) {
|
for (int xx = 0; xx < width; xx++)
|
||||||
for (int yy = 0; yy < height; yy ++) {
|
{
|
||||||
|
for (int yy = 0; yy < height; yy++)
|
||||||
|
{
|
||||||
pixels.clear();
|
pixels.clear();
|
||||||
for (int jj = 0; jj < texes.size(); jj ++) {
|
for (int jj = 0; jj < texes.size(); jj++)
|
||||||
|
{
|
||||||
const TexInfo &tex = texes[jj];
|
const TexInfo &tex = texes[jj];
|
||||||
// each texture's structure will depend on its channel count
|
// each texture's structure will depend on its channel count
|
||||||
int ii = tex.channels * (xx + yy * width);
|
int ii = tex.channels * (xx + yy * width);
|
||||||
int kk = 0;
|
int kk = 0;
|
||||||
if (tex.pixels != nullptr) {
|
if (tex.pixels != nullptr)
|
||||||
for (; kk < tex.channels; kk ++) {
|
{
|
||||||
|
for (; kk < tex.channels; kk++)
|
||||||
|
{
|
||||||
pixels[jj][kk] = tex.pixels[ii++] / 255.0f;
|
pixels[jj][kk] = tex.pixels[ii++] / 255.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (; kk < pixels[jj].size(); kk ++) {
|
for (; kk < pixels[jj].size(); kk++)
|
||||||
|
{
|
||||||
pixels[jj][kk] = 1.0f;
|
pixels[jj][kk] = 1.0f;
|
||||||
}
|
}
|
||||||
pixelPointers[jj] = &pixels[jj];
|
pixelPointers[jj] = &pixels[jj];
|
||||||
}
|
}
|
||||||
const pixel merged = combine(pixelPointers);
|
const pixel merged = combine(pixelPointers);
|
||||||
int ii = channels * (xx + yy * width);
|
int ii = channels * (xx + yy * width);
|
||||||
for (int jj = 0; jj < channels; jj ++) {
|
for (int jj = 0; jj < channels; jj++)
|
||||||
|
{
|
||||||
mergedPixels[ii + jj] = static_cast<uint8_t>(fmax(0, fmin(255.0f, merged[jj] * 255.0f)));
|
mergedPixels[ii + jj] = static_cast<uint8_t>(fmax(0, fmin(255.0f, merged[jj] * 255.0f)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -523,39 +573,48 @@ ModelData *Raw2Gltf(
|
||||||
|
|
||||||
std::vector<char> imgBuffer;
|
std::vector<char> imgBuffer;
|
||||||
int res;
|
int res;
|
||||||
if (png) {
|
if (png)
|
||||||
|
{
|
||||||
res = stbi_write_png_to_func(WriteToVectorContext, &imgBuffer,
|
res = stbi_write_png_to_func(WriteToVectorContext, &imgBuffer,
|
||||||
width, height, channels, mergedPixels.data(), width * channels);
|
width, height, channels, mergedPixels.data(), width * channels);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
res = stbi_write_jpg_to_func(WriteToVectorContext, &imgBuffer,
|
res = stbi_write_jpg_to_func(WriteToVectorContext, &imgBuffer,
|
||||||
width, height, channels, mergedPixels.data(), 80);
|
width, height, channels, mergedPixels.data(), 80);
|
||||||
}
|
}
|
||||||
if (!res) {
|
if (!res)
|
||||||
|
{
|
||||||
fmt::printf("Warning: failed to generate merge texture '%s'.\n", mergedFilename);
|
fmt::printf("Warning: failed to generate merge texture '%s'.\n", mergedFilename);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageData *image;
|
ImageData *image;
|
||||||
if (options.outputBinary) {
|
if (options.outputBinary)
|
||||||
|
{
|
||||||
const auto bufferView = gltf->AddRawBufferView(buffer, imgBuffer.data(), imgBuffer.size());
|
const auto bufferView = gltf->AddRawBufferView(buffer, imgBuffer.data(), imgBuffer.size());
|
||||||
image = new ImageData(mergedName, *bufferView, png ? "image/png" : "image/jpeg");
|
image = new ImageData(mergedName, *bufferView, png ? "image/png" : "image/jpeg");
|
||||||
|
}
|
||||||
} else {
|
else
|
||||||
|
{
|
||||||
const std::string imageFilename = mergedFilename + (png ? ".png" : ".jpg");
|
const std::string imageFilename = mergedFilename + (png ? ".png" : ".jpg");
|
||||||
const std::string imagePath = outputFolder + imageFilename;
|
const std::string imagePath = outputFolder + imageFilename;
|
||||||
FILE *fp = fopen(imagePath.c_str(), "wb");
|
FILE *fp = fopen(imagePath.c_str(), "wb");
|
||||||
if (fp == nullptr) {
|
if (fp == nullptr)
|
||||||
|
{
|
||||||
fmt::printf("Warning:: Couldn't write file '%s' for writing.\n", imagePath);
|
fmt::printf("Warning:: Couldn't write file '%s' for writing.\n", imagePath);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fwrite(imgBuffer.data(), imgBuffer.size(), 1, fp) != 1) {
|
if (fwrite(imgBuffer.data(), imgBuffer.size(), 1, fp) != 1)
|
||||||
|
{
|
||||||
fmt::printf("Warning: Failed to write %lu bytes to file '%s'.\n", imgBuffer.size(), imagePath);
|
fmt::printf("Warning: Failed to write %lu bytes to file '%s'.\n", imgBuffer.size(), imagePath);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
if (verboseOutput) {
|
if (verboseOutput)
|
||||||
|
{
|
||||||
fmt::printf("Wrote %lu bytes to texture '%s'.\n", imgBuffer.size(), imagePath);
|
fmt::printf("Wrote %lu bytes to texture '%s'.\n", imgBuffer.size(), imagePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,7 +631,8 @@ ModelData *Raw2Gltf(
|
||||||
auto getSimpleTexture = [&](int rawTexIndex, const std::string &tag) {
|
auto getSimpleTexture = [&](int rawTexIndex, const std::string &tag) {
|
||||||
const std::string key = texIndicesKey({rawTexIndex}, tag);
|
const std::string key = texIndicesKey({rawTexIndex}, tag);
|
||||||
auto iter = textureByIndicesKey.find(key);
|
auto iter = textureByIndicesKey.find(key);
|
||||||
if (iter != textureByIndicesKey.end()) {
|
if (iter != textureByIndicesKey.end())
|
||||||
|
{
|
||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -581,32 +641,41 @@ ModelData *Raw2Gltf(
|
||||||
const std::string relativeFilename = StringUtils::GetFileNameString(rawTexture.fileLocation);
|
const std::string relativeFilename = StringUtils::GetFileNameString(rawTexture.fileLocation);
|
||||||
|
|
||||||
ImageData *image = nullptr;
|
ImageData *image = nullptr;
|
||||||
if (options.outputBinary) {
|
if (options.outputBinary)
|
||||||
|
{
|
||||||
auto bufferView = gltf->AddBufferViewForFile(buffer, rawTexture.fileLocation);
|
auto bufferView = gltf->AddBufferViewForFile(buffer, rawTexture.fileLocation);
|
||||||
if (bufferView) {
|
if (bufferView)
|
||||||
|
{
|
||||||
std::string suffix = StringUtils::GetFileSuffixString(rawTexture.fileLocation);
|
std::string suffix = StringUtils::GetFileSuffixString(rawTexture.fileLocation);
|
||||||
image = new ImageData(relativeFilename, *bufferView, suffixToMimeType(suffix));
|
image = new ImageData(relativeFilename, *bufferView, suffixToMimeType(suffix));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if (!relativeFilename.empty()) {
|
else if (!relativeFilename.empty())
|
||||||
|
{
|
||||||
image = new ImageData(relativeFilename, relativeFilename);
|
image = new ImageData(relativeFilename, relativeFilename);
|
||||||
std::string outputPath = outputFolder + relativeFilename;
|
|
||||||
if (FileUtils::CopyFile(rawTexture.fileLocation, outputPath)) {
|
std::string outputPath = outputFolder + StringUtils::NormalizePath(relativeFilename);
|
||||||
if (verboseOutput) {
|
|
||||||
|
if (FileUtils::CopyFile(rawTexture.fileLocation, outputPath, true))
|
||||||
|
{
|
||||||
|
if (verboseOutput)
|
||||||
|
{
|
||||||
fmt::printf("Copied texture '%s' to output folder: %s\n", textureName, outputPath);
|
fmt::printf("Copied texture '%s' to output folder: %s\n", textureName, outputPath);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// no point commenting further on read/write error; CopyFile() does enough of that, and we
|
// no point commenting further on read/write error; CopyFile() does enough of that, and we
|
||||||
// certainly want to to add an image struct to the glTF JSON, with the correct relative path
|
// certainly want to to add an image struct to the glTF JSON, with the correct relative path
|
||||||
// reference, even if the copy failed.
|
// reference, even if the copy failed.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!image) {
|
if (!image)
|
||||||
|
{
|
||||||
// fallback is tiny transparent PNG
|
// fallback is tiny transparent PNG
|
||||||
image = new ImageData(
|
image = new ImageData(
|
||||||
textureName,
|
textureName,
|
||||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg=="
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==");
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TextureData> texDat = gltf->textures.hold(
|
std::shared_ptr<TextureData> texDat = gltf->textures.hold(
|
||||||
|
@ -619,7 +688,8 @@ ModelData *Raw2Gltf(
|
||||||
// materials
|
// materials
|
||||||
//
|
//
|
||||||
|
|
||||||
for (int materialIndex = 0; materialIndex < raw.GetMaterialCount(); materialIndex++) {
|
for (int materialIndex = 0; materialIndex < raw.GetMaterialCount(); materialIndex++)
|
||||||
|
{
|
||||||
const RawMaterial &material = raw.GetMaterial(materialIndex);
|
const RawMaterial &material = raw.GetMaterial(materialIndex);
|
||||||
const bool isTransparent =
|
const bool isTransparent =
|
||||||
material.type == RAW_MATERIAL_TYPE_TRANSPARENT ||
|
material.type == RAW_MATERIAL_TYPE_TRANSPARENT ||
|
||||||
|
@ -644,8 +714,7 @@ ModelData *Raw2Gltf(
|
||||||
const std::string tag,
|
const std::string tag,
|
||||||
RawTextureUsage usage,
|
RawTextureUsage usage,
|
||||||
const pixel_merger &combine,
|
const pixel_merger &combine,
|
||||||
bool outputHasAlpha
|
bool outputHasAlpha) -> std::shared_ptr<TextureData> {
|
||||||
) -> std::shared_ptr<TextureData> {
|
|
||||||
return getDerivedTexture({material.textures[usage]}, combine, tag, outputHasAlpha);
|
return getDerivedTexture({material.textures[usage]}, combine, tag, outputHasAlpha);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -655,8 +724,7 @@ ModelData *Raw2Gltf(
|
||||||
RawTextureUsage u1,
|
RawTextureUsage u1,
|
||||||
RawTextureUsage u2,
|
RawTextureUsage u2,
|
||||||
const pixel_merger &combine,
|
const pixel_merger &combine,
|
||||||
bool outputHasAlpha
|
bool outputHasAlpha) -> std::shared_ptr<TextureData> {
|
||||||
) -> std::shared_ptr<TextureData> {
|
|
||||||
return getDerivedTexture(
|
return getDerivedTexture(
|
||||||
{material.textures[u1], material.textures[u2]},
|
{material.textures[u1], material.textures[u2]},
|
||||||
combine, tag, outputHasAlpha);
|
combine, tag, outputHasAlpha);
|
||||||
|
@ -669,21 +737,22 @@ ModelData *Raw2Gltf(
|
||||||
RawTextureUsage u2,
|
RawTextureUsage u2,
|
||||||
RawTextureUsage u3,
|
RawTextureUsage u3,
|
||||||
const pixel_merger &combine,
|
const pixel_merger &combine,
|
||||||
bool outputHasAlpha
|
bool outputHasAlpha) -> std::shared_ptr<TextureData> {
|
||||||
) -> std::shared_ptr<TextureData> {
|
|
||||||
return getDerivedTexture(
|
return getDerivedTexture(
|
||||||
{material.textures[u1], material.textures[u2], material.textures[u3]},
|
{material.textures[u1], material.textures[u2], material.textures[u3]},
|
||||||
combine, tag, outputHasAlpha);
|
combine, tag, outputHasAlpha);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<PBRMetallicRoughness> pbrMetRough;
|
std::shared_ptr<PBRMetallicRoughness> pbrMetRough;
|
||||||
if (options.usePBRMetRough) {
|
if (options.usePBRMetRough)
|
||||||
|
{
|
||||||
// albedo is a basic texture, no merging needed
|
// albedo is a basic texture, no merging needed
|
||||||
std::shared_ptr<TextureData> baseColorTex, aoMetRoughTex;
|
std::shared_ptr<TextureData> baseColorTex, aoMetRoughTex;
|
||||||
|
|
||||||
Vec4f diffuseFactor;
|
Vec4f diffuseFactor;
|
||||||
float metallic, roughness;
|
float metallic, roughness;
|
||||||
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) {
|
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH)
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* PBR FBX Material -> PBR Met/Rough glTF.
|
* PBR FBX Material -> PBR Met/Rough glTF.
|
||||||
*
|
*
|
||||||
|
@ -705,10 +774,13 @@ ModelData *Raw2Gltf(
|
||||||
emissiveFactor = props->emissiveFactor;
|
emissiveFactor = props->emissiveFactor;
|
||||||
emissiveIntensity = props->emissiveIntensity;
|
emissiveIntensity = props->emissiveIntensity;
|
||||||
// add the occlusion texture only if actual occlusion pixels exist in the aoNetRough texture.
|
// add the occlusion texture only if actual occlusion pixels exist in the aoNetRough texture.
|
||||||
if (material.textures[RAW_TEXTURE_USAGE_OCCLUSION] >= 0) {
|
if (material.textures[RAW_TEXTURE_USAGE_OCCLUSION] >= 0)
|
||||||
|
{
|
||||||
occlusionTexture = aoMetRoughTex.get();
|
occlusionTexture = aoMetRoughTex.get();
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* Traditional FBX Material -> PBR Met/Rough glTF.
|
* Traditional FBX Material -> PBR Met/Rough glTF.
|
||||||
*
|
*
|
||||||
|
@ -741,15 +813,19 @@ ModelData *Raw2Gltf(
|
||||||
return {0, getRoughness(shininess), metallic, 1};
|
return {0, getRoughness(shininess), metallic, 1};
|
||||||
},
|
},
|
||||||
false);
|
false);
|
||||||
if (aoMetRoughTex != nullptr) {
|
if (aoMetRoughTex != nullptr)
|
||||||
|
{
|
||||||
// if we successfully built a texture, factors are just multiplicative identity
|
// if we successfully built a texture, factors are just multiplicative identity
|
||||||
metallic = roughness = 1.0f;
|
metallic = roughness = 1.0f;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// no shininess texture,
|
// no shininess texture,
|
||||||
roughness = getRoughness(props->shininess);
|
roughness = getRoughness(props->shininess);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
else
|
||||||
|
{
|
||||||
metallic = 0.2f;
|
metallic = 0.2f;
|
||||||
roughness = 0.8f;
|
roughness = 0.8f;
|
||||||
}
|
}
|
||||||
|
@ -763,7 +839,8 @@ ModelData *Raw2Gltf(
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<KHRCmnUnlitMaterial> khrCmnUnlitMat;
|
std::shared_ptr<KHRCmnUnlitMaterial> khrCmnUnlitMat;
|
||||||
if (options.useKHRMatUnlit) {
|
if (options.useKHRMatUnlit)
|
||||||
|
{
|
||||||
normalTexture = nullptr;
|
normalTexture = nullptr;
|
||||||
|
|
||||||
emissiveTexture = nullptr;
|
emissiveTexture = nullptr;
|
||||||
|
@ -772,11 +849,14 @@ ModelData *Raw2Gltf(
|
||||||
Vec4f diffuseFactor;
|
Vec4f diffuseFactor;
|
||||||
std::shared_ptr<TextureData> baseColorTex;
|
std::shared_ptr<TextureData> baseColorTex;
|
||||||
|
|
||||||
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) {
|
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH)
|
||||||
|
{
|
||||||
RawMetRoughMatProps *props = (RawMetRoughMatProps *)material.info.get();
|
RawMetRoughMatProps *props = (RawMetRoughMatProps *)material.info.get();
|
||||||
diffuseFactor = props->diffuseFactor;
|
diffuseFactor = props->diffuseFactor;
|
||||||
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
|
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
RawTraditionalMatProps *props = ((RawTraditionalMatProps *)material.info.get());
|
RawTraditionalMatProps *props = ((RawTraditionalMatProps *)material.info.get());
|
||||||
diffuseFactor = props->diffuseFactor;
|
diffuseFactor = props->diffuseFactor;
|
||||||
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_DIFFUSE);
|
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_DIFFUSE);
|
||||||
|
@ -786,7 +866,8 @@ ModelData *Raw2Gltf(
|
||||||
|
|
||||||
khrCmnUnlitMat.reset(new KHRCmnUnlitMaterial());
|
khrCmnUnlitMat.reset(new KHRCmnUnlitMaterial());
|
||||||
}
|
}
|
||||||
if (!occlusionTexture) {
|
if (!occlusionTexture)
|
||||||
|
{
|
||||||
occlusionTexture = simpleTex(RAW_TEXTURE_USAGE_OCCLUSION).get();
|
occlusionTexture = simpleTex(RAW_TEXTURE_USAGE_OCCLUSION).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -798,7 +879,8 @@ ModelData *Raw2Gltf(
|
||||||
materialsByName[materialHash(material)] = mData;
|
materialsByName[materialHash(material)] = mData;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &surfaceModel : materialModels) {
|
for (const auto &surfaceModel : materialModels)
|
||||||
|
{
|
||||||
assert(surfaceModel.GetSurfaceCount() == 1);
|
assert(surfaceModel.GetSurfaceCount() == 1);
|
||||||
const RawSurface &rawSurface = surfaceModel.GetSurface(0);
|
const RawSurface &rawSurface = surfaceModel.GetSurface(0);
|
||||||
const long surfaceId = rawSurface.id;
|
const long surfaceId = rawSurface.id;
|
||||||
|
@ -808,12 +890,15 @@ ModelData *Raw2Gltf(
|
||||||
|
|
||||||
MeshData *mesh = nullptr;
|
MeshData *mesh = nullptr;
|
||||||
auto meshIter = meshBySurfaceId.find(surfaceId);
|
auto meshIter = meshBySurfaceId.find(surfaceId);
|
||||||
if (meshIter != meshBySurfaceId.end()) {
|
if (meshIter != meshBySurfaceId.end())
|
||||||
|
{
|
||||||
mesh = meshIter->second.get();
|
mesh = meshIter->second.get();
|
||||||
|
}
|
||||||
} else {
|
else
|
||||||
|
{
|
||||||
std::vector<float> defaultDeforms;
|
std::vector<float> defaultDeforms;
|
||||||
for (const auto &channel : rawSurface.blendChannels) {
|
for (const auto &channel : rawSurface.blendChannels)
|
||||||
|
{
|
||||||
defaultDeforms.push_back(channel.defaultDeform);
|
defaultDeforms.push_back(channel.defaultDeform);
|
||||||
}
|
}
|
||||||
auto meshPtr = gltf->meshes.hold(new MeshData(rawSurface.name, defaultDeforms));
|
auto meshPtr = gltf->meshes.hold(new MeshData(rawSurface.name, defaultDeforms));
|
||||||
|
@ -822,19 +907,19 @@ ModelData *Raw2Gltf(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool useLongIndices =
|
bool useLongIndices =
|
||||||
(options.useLongIndices == UseLongIndicesOptions::ALWAYS)
|
(options.useLongIndices == UseLongIndicesOptions::ALWAYS) || (options.useLongIndices == UseLongIndicesOptions::AUTO && surfaceModel.GetVertexCount() > 65535);
|
||||||
|| (options.useLongIndices == UseLongIndicesOptions::AUTO
|
|
||||||
&& surfaceModel.GetVertexCount() > 65535);
|
|
||||||
|
|
||||||
std::shared_ptr<PrimitiveData> primitive;
|
std::shared_ptr<PrimitiveData> primitive;
|
||||||
if (options.useDraco) {
|
if (options.useDraco)
|
||||||
|
{
|
||||||
int triangleCount = surfaceModel.GetTriangleCount();
|
int triangleCount = surfaceModel.GetTriangleCount();
|
||||||
|
|
||||||
// initialize Draco mesh with vertex index information
|
// initialize Draco mesh with vertex index information
|
||||||
auto dracoMesh(std::make_shared<draco::Mesh>());
|
auto dracoMesh(std::make_shared<draco::Mesh>());
|
||||||
dracoMesh->SetNumFaces(static_cast<size_t>(triangleCount));
|
dracoMesh->SetNumFaces(static_cast<size_t>(triangleCount));
|
||||||
|
|
||||||
for (uint32_t ii = 0; ii < triangleCount; ii++) {
|
for (uint32_t ii = 0; ii < triangleCount; ii++)
|
||||||
|
{
|
||||||
draco::Mesh::Face face;
|
draco::Mesh::Face face;
|
||||||
face[0] = surfaceModel.GetTriangle(ii).verts[0];
|
face[0] = surfaceModel.GetTriangle(ii).verts[0];
|
||||||
face[1] = surfaceModel.GetTriangle(ii).verts[1];
|
face[1] = surfaceModel.GetTriangle(ii).verts[1];
|
||||||
|
@ -845,7 +930,9 @@ ModelData *Raw2Gltf(
|
||||||
AccessorData &indexes = *gltf->accessors.hold(new AccessorData(useLongIndices ? GLT_UINT : GLT_USHORT));
|
AccessorData &indexes = *gltf->accessors.hold(new AccessorData(useLongIndices ? GLT_UINT : GLT_USHORT));
|
||||||
indexes.count = 3 * triangleCount;
|
indexes.count = 3 * triangleCount;
|
||||||
primitive.reset(new PrimitiveData(indexes, mData, dracoMesh));
|
primitive.reset(new PrimitiveData(indexes, mData, dracoMesh));
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
const AccessorData &indexes = *gltf->AddAccessorWithView(
|
const AccessorData &indexes = *gltf->AddAccessorWithView(
|
||||||
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ELEMENT_ARRAY_BUFFER),
|
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ELEMENT_ARRAY_BUFFER),
|
||||||
useLongIndices ? GLT_UINT : GLT_USHORT, getIndexArray(surfaceModel));
|
useLongIndices ? GLT_UINT : GLT_USHORT, getIndexArray(surfaceModel));
|
||||||
|
@ -856,7 +943,8 @@ ModelData *Raw2Gltf(
|
||||||
// surface vertices
|
// surface vertices
|
||||||
//
|
//
|
||||||
{
|
{
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_POSITION) != 0)
|
||||||
|
{
|
||||||
const AttributeDefinition<Vec3f> ATTR_POSITION("POSITION", &RawVertex::position,
|
const AttributeDefinition<Vec3f> ATTR_POSITION("POSITION", &RawVertex::position,
|
||||||
GLT_VEC3F, draco::GeometryAttribute::POSITION, draco::DT_FLOAT32);
|
GLT_VEC3F, draco::GeometryAttribute::POSITION, draco::DT_FLOAT32);
|
||||||
auto accessor = gltf->AddAttributeToPrimitive<Vec3f>(
|
auto accessor = gltf->AddAttributeToPrimitive<Vec3f>(
|
||||||
|
@ -865,43 +953,51 @@ ModelData *Raw2Gltf(
|
||||||
accessor->min = toStdVec(rawSurface.bounds.min);
|
accessor->min = toStdVec(rawSurface.bounds.min);
|
||||||
accessor->max = toStdVec(rawSurface.bounds.max);
|
accessor->max = toStdVec(rawSurface.bounds.max);
|
||||||
}
|
}
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_NORMAL) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_NORMAL) != 0)
|
||||||
|
{
|
||||||
const AttributeDefinition<Vec3f> ATTR_NORMAL("NORMAL", &RawVertex::normal,
|
const AttributeDefinition<Vec3f> ATTR_NORMAL("NORMAL", &RawVertex::normal,
|
||||||
GLT_VEC3F, draco::GeometryAttribute::NORMAL, draco::DT_FLOAT32);
|
GLT_VEC3F, draco::GeometryAttribute::NORMAL, draco::DT_FLOAT32);
|
||||||
gltf->AddAttributeToPrimitive<Vec3f>(buffer, surfaceModel, *primitive, ATTR_NORMAL);
|
gltf->AddAttributeToPrimitive<Vec3f>(buffer, surfaceModel, *primitive, ATTR_NORMAL);
|
||||||
}
|
}
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_TANGENT) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_TANGENT) != 0)
|
||||||
|
{
|
||||||
const AttributeDefinition<Vec4f> ATTR_TANGENT("TANGENT", &RawVertex::tangent, GLT_VEC4F);
|
const AttributeDefinition<Vec4f> ATTR_TANGENT("TANGENT", &RawVertex::tangent, GLT_VEC4F);
|
||||||
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_TANGENT);
|
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_TANGENT);
|
||||||
}
|
}
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0)
|
||||||
|
{
|
||||||
const AttributeDefinition<Vec4f> ATTR_COLOR("COLOR_0", &RawVertex::color, GLT_VEC4F,
|
const AttributeDefinition<Vec4f> ATTR_COLOR("COLOR_0", &RawVertex::color, GLT_VEC4F,
|
||||||
draco::GeometryAttribute::COLOR, draco::DT_FLOAT32);
|
draco::GeometryAttribute::COLOR, draco::DT_FLOAT32);
|
||||||
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_COLOR);
|
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_COLOR);
|
||||||
}
|
}
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV0) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV0) != 0)
|
||||||
|
{
|
||||||
const AttributeDefinition<Vec2f> ATTR_TEXCOORD_0("TEXCOORD_0", &RawVertex::uv0,
|
const AttributeDefinition<Vec2f> ATTR_TEXCOORD_0("TEXCOORD_0", &RawVertex::uv0,
|
||||||
GLT_VEC2F, draco::GeometryAttribute::TEX_COORD, draco::DT_FLOAT32);
|
GLT_VEC2F, draco::GeometryAttribute::TEX_COORD, draco::DT_FLOAT32);
|
||||||
gltf->AddAttributeToPrimitive<Vec2f>(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_0);
|
gltf->AddAttributeToPrimitive<Vec2f>(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_0);
|
||||||
}
|
}
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV1) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV1) != 0)
|
||||||
|
{
|
||||||
const AttributeDefinition<Vec2f> ATTR_TEXCOORD_1("TEXCOORD_1", &RawVertex::uv1,
|
const AttributeDefinition<Vec2f> ATTR_TEXCOORD_1("TEXCOORD_1", &RawVertex::uv1,
|
||||||
GLT_VEC2F, draco::GeometryAttribute::TEX_COORD, draco::DT_FLOAT32);
|
GLT_VEC2F, draco::GeometryAttribute::TEX_COORD, draco::DT_FLOAT32);
|
||||||
gltf->AddAttributeToPrimitive<Vec2f>(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1);
|
gltf->AddAttributeToPrimitive<Vec2f>(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1);
|
||||||
}
|
}
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0)
|
||||||
|
{
|
||||||
const AttributeDefinition<Vec4i> ATTR_JOINTS("JOINTS_0", &RawVertex::jointIndices,
|
const AttributeDefinition<Vec4i> ATTR_JOINTS("JOINTS_0", &RawVertex::jointIndices,
|
||||||
GLT_VEC4I, draco::GeometryAttribute::GENERIC, draco::DT_UINT16);
|
GLT_VEC4I, draco::GeometryAttribute::GENERIC, draco::DT_UINT16);
|
||||||
gltf->AddAttributeToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS);
|
gltf->AddAttributeToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS);
|
||||||
}
|
}
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0)
|
||||||
|
{
|
||||||
const AttributeDefinition<Vec4f> ATTR_WEIGHTS("WEIGHTS_0", &RawVertex::jointWeights,
|
const AttributeDefinition<Vec4f> ATTR_WEIGHTS("WEIGHTS_0", &RawVertex::jointWeights,
|
||||||
GLT_VEC4F, draco::GeometryAttribute::GENERIC, draco::DT_FLOAT32);
|
GLT_VEC4F, draco::GeometryAttribute::GENERIC, draco::DT_FLOAT32);
|
||||||
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_WEIGHTS);
|
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_WEIGHTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// each channel present in the mesh always ends up a target in the primitive
|
// each channel present in the mesh always ends up a target in the primitive
|
||||||
for (int channelIx = 0; channelIx < rawSurface.blendChannels.size(); channelIx ++) {
|
for (int channelIx = 0; channelIx < rawSurface.blendChannels.size(); channelIx++)
|
||||||
|
{
|
||||||
const auto &channel = rawSurface.blendChannels[channelIx];
|
const auto &channel = rawSurface.blendChannels[channelIx];
|
||||||
|
|
||||||
// track the bounds of each shape channel
|
// track the bounds of each shape channel
|
||||||
|
@ -909,14 +1005,17 @@ ModelData *Raw2Gltf(
|
||||||
|
|
||||||
std::vector<Vec3f> positions, normals;
|
std::vector<Vec3f> positions, normals;
|
||||||
std::vector<Vec4f> tangents;
|
std::vector<Vec4f> tangents;
|
||||||
for (int jj = 0; jj < surfaceModel.GetVertexCount(); jj ++) {
|
for (int jj = 0; jj < surfaceModel.GetVertexCount(); jj++)
|
||||||
|
{
|
||||||
auto blendVertex = surfaceModel.GetVertex(jj).blends[channelIx];
|
auto blendVertex = surfaceModel.GetVertex(jj).blends[channelIx];
|
||||||
shapeBounds.AddPoint(blendVertex.position);
|
shapeBounds.AddPoint(blendVertex.position);
|
||||||
positions.push_back(blendVertex.position);
|
positions.push_back(blendVertex.position);
|
||||||
if (options.useBlendShapeTangents && channel.hasNormals) {
|
if (options.useBlendShapeTangents && channel.hasNormals)
|
||||||
|
{
|
||||||
normals.push_back(blendVertex.normal);
|
normals.push_back(blendVertex.normal);
|
||||||
}
|
}
|
||||||
if (options.useBlendShapeTangents && channel.hasTangents) {
|
if (options.useBlendShapeTangents && channel.hasTangents)
|
||||||
|
{
|
||||||
tangents.push_back(blendVertex.tangent);
|
tangents.push_back(blendVertex.tangent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -927,14 +1026,16 @@ ModelData *Raw2Gltf(
|
||||||
pAcc->max = toStdVec(shapeBounds.max);
|
pAcc->max = toStdVec(shapeBounds.max);
|
||||||
|
|
||||||
std::shared_ptr<AccessorData> nAcc;
|
std::shared_ptr<AccessorData> nAcc;
|
||||||
if (!normals.empty()) {
|
if (!normals.empty())
|
||||||
|
{
|
||||||
nAcc = gltf->AddAccessorWithView(
|
nAcc = gltf->AddAccessorWithView(
|
||||||
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER),
|
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER),
|
||||||
GLT_VEC3F, normals);
|
GLT_VEC3F, normals);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<AccessorData> tAcc;
|
std::shared_ptr<AccessorData> tAcc;
|
||||||
if (!tangents.empty()) {
|
if (!tangents.empty())
|
||||||
|
{
|
||||||
nAcc = gltf->AddAccessorWithView(
|
nAcc = gltf->AddAccessorWithView(
|
||||||
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER),
|
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER),
|
||||||
GLT_VEC4F, tangents);
|
GLT_VEC4F, tangents);
|
||||||
|
@ -943,7 +1044,8 @@ ModelData *Raw2Gltf(
|
||||||
primitive->AddTarget(pAcc.get(), nAcc.get(), tAcc.get());
|
primitive->AddTarget(pAcc.get(), nAcc.get(), tAcc.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (options.useDraco) {
|
if (options.useDraco)
|
||||||
|
{
|
||||||
// Set up the encoder.
|
// Set up the encoder.
|
||||||
draco::Encoder encoder;
|
draco::Encoder encoder;
|
||||||
|
|
||||||
|
@ -969,7 +1071,8 @@ ModelData *Raw2Gltf(
|
||||||
// Assign meshes to node
|
// Assign meshes to node
|
||||||
//
|
//
|
||||||
|
|
||||||
for (int i = 0; i < raw.GetNodeCount(); i++) {
|
for (int i = 0; i < raw.GetNodeCount(); i++)
|
||||||
|
{
|
||||||
|
|
||||||
const RawNode &node = raw.GetNode(i);
|
const RawNode &node = raw.GetNode(i);
|
||||||
auto nodeData = gltf->nodes.ptrs[i];
|
auto nodeData = gltf->nodes.ptrs[i];
|
||||||
|
@ -988,16 +1091,20 @@ ModelData *Raw2Gltf(
|
||||||
//
|
//
|
||||||
// surface skin
|
// surface skin
|
||||||
//
|
//
|
||||||
if (!rawSurface.jointIds.empty()) {
|
if (!rawSurface.jointIds.empty())
|
||||||
if (nodeData->skin == -1) {
|
{
|
||||||
|
if (nodeData->skin == -1)
|
||||||
|
{
|
||||||
// glTF uses column-major matrices
|
// glTF uses column-major matrices
|
||||||
std::vector<Mat4f> inverseBindMatrices;
|
std::vector<Mat4f> inverseBindMatrices;
|
||||||
for (const auto &inverseBindMatrice : rawSurface.inverseBindMatrices) {
|
for (const auto &inverseBindMatrice : rawSurface.inverseBindMatrices)
|
||||||
|
{
|
||||||
inverseBindMatrices.push_back(inverseBindMatrice.Transpose());
|
inverseBindMatrices.push_back(inverseBindMatrice.Transpose());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint32_t> jointIndexes;
|
std::vector<uint32_t> jointIndexes;
|
||||||
for (const auto &jointId : rawSurface.jointIds) {
|
for (const auto &jointId : rawSurface.jointIds)
|
||||||
|
{
|
||||||
jointIndexes.push_back(require(nodesById, jointId).ix);
|
jointIndexes.push_back(require(nodesById, jointId).ix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1016,18 +1123,22 @@ ModelData *Raw2Gltf(
|
||||||
// cameras
|
// cameras
|
||||||
//
|
//
|
||||||
|
|
||||||
for (int i = 0; i < raw.GetCameraCount(); i++) {
|
for (int i = 0; i < raw.GetCameraCount(); i++)
|
||||||
|
{
|
||||||
const RawCamera &cam = raw.GetCamera(i);
|
const RawCamera &cam = raw.GetCamera(i);
|
||||||
CameraData &camera = *gltf->cameras.hold(new CameraData());
|
CameraData &camera = *gltf->cameras.hold(new CameraData());
|
||||||
camera.name = cam.name;
|
camera.name = cam.name;
|
||||||
|
|
||||||
if (cam.mode == RawCamera::CAMERA_MODE_PERSPECTIVE) {
|
if (cam.mode == RawCamera::CAMERA_MODE_PERSPECTIVE)
|
||||||
|
{
|
||||||
camera.type = "perspective";
|
camera.type = "perspective";
|
||||||
camera.aspectRatio = cam.perspective.aspectRatio;
|
camera.aspectRatio = cam.perspective.aspectRatio;
|
||||||
camera.yfov = cam.perspective.fovDegreesY * ((float)M_PI / 180.0f);
|
camera.yfov = cam.perspective.fovDegreesY * ((float)M_PI / 180.0f);
|
||||||
camera.znear = cam.perspective.nearZ;
|
camera.znear = cam.perspective.nearZ;
|
||||||
camera.zfar = cam.perspective.farZ;
|
camera.zfar = cam.perspective.farZ;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
camera.type = "orthographic";
|
camera.type = "orthographic";
|
||||||
camera.xmag = cam.orthographic.magX;
|
camera.xmag = cam.orthographic.magX;
|
||||||
camera.ymag = cam.orthographic.magY;
|
camera.ymag = cam.orthographic.magY;
|
||||||
|
@ -1037,7 +1148,8 @@ ModelData *Raw2Gltf(
|
||||||
// Add the camera to the node hierarchy.
|
// Add the camera to the node hierarchy.
|
||||||
|
|
||||||
auto iter = nodesById.find(cam.nodeId);
|
auto iter = nodesById.find(cam.nodeId);
|
||||||
if (iter == nodesById.end()) {
|
if (iter == nodesById.end())
|
||||||
|
{
|
||||||
fmt::printf("Warning: Camera node id %lu does not exist.\n", cam.nodeId);
|
fmt::printf("Warning: Camera node id %lu does not exist.\n", cam.nodeId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1048,7 +1160,8 @@ ModelData *Raw2Gltf(
|
||||||
NodeData &rootNode = require(nodesById, raw.GetRootNode());
|
NodeData &rootNode = require(nodesById, raw.GetRootNode());
|
||||||
const SceneData &rootScene = *gltf->scenes.hold(new SceneData(defaultSceneName, rootNode));
|
const SceneData &rootScene = *gltf->scenes.hold(new SceneData(defaultSceneName, rootNode));
|
||||||
|
|
||||||
if (options.outputBinary) {
|
if (options.outputBinary)
|
||||||
|
{
|
||||||
// note: glTF binary is little-endian
|
// note: glTF binary is little-endian
|
||||||
const char glbHeader[] = {
|
const char glbHeader[] = {
|
||||||
'g', 'l', 'T', 'F', // magic
|
'g', 'l', 'T', 'F', // magic
|
||||||
|
@ -1067,34 +1180,37 @@ ModelData *Raw2Gltf(
|
||||||
|
|
||||||
{
|
{
|
||||||
std::vector<std::string> extensionsUsed, extensionsRequired;
|
std::vector<std::string> extensionsUsed, extensionsRequired;
|
||||||
if (options.useKHRMatUnlit) {
|
if (options.useKHRMatUnlit)
|
||||||
|
{
|
||||||
extensionsUsed.push_back(KHR_MATERIALS_CMN_UNLIT);
|
extensionsUsed.push_back(KHR_MATERIALS_CMN_UNLIT);
|
||||||
}
|
}
|
||||||
if (options.useDraco) {
|
if (options.useDraco)
|
||||||
|
{
|
||||||
extensionsUsed.push_back(KHR_DRACO_MESH_COMPRESSION);
|
extensionsUsed.push_back(KHR_DRACO_MESH_COMPRESSION);
|
||||||
extensionsRequired.push_back(KHR_DRACO_MESH_COMPRESSION);
|
extensionsRequired.push_back(KHR_DRACO_MESH_COMPRESSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
json glTFJson{
|
json glTFJson{
|
||||||
{ "asset", {
|
{"asset", {{"generator", "FBX2glTF"}, {"version", "2.0"}}},
|
||||||
{ "generator", "FBX2glTF" },
|
{"scene", rootScene.ix}};
|
||||||
{ "version", "2.0" }}},
|
if (!extensionsUsed.empty())
|
||||||
{ "scene", rootScene.ix }
|
{
|
||||||
};
|
|
||||||
if (!extensionsUsed.empty()) {
|
|
||||||
glTFJson["extensionsUsed"] = extensionsUsed;
|
glTFJson["extensionsUsed"] = extensionsUsed;
|
||||||
}
|
}
|
||||||
if (!extensionsRequired.empty()) {
|
if (!extensionsRequired.empty())
|
||||||
|
{
|
||||||
glTFJson["extensionsRequired"] = extensionsRequired;
|
glTFJson["extensionsRequired"] = extensionsRequired;
|
||||||
}
|
}
|
||||||
|
|
||||||
gltf->serializeHolders(glTFJson);
|
gltf->serializeHolders(glTFJson);
|
||||||
gltfOutStream << glTFJson.dump(options.outputBinary ? 0 : 4);
|
gltfOutStream << glTFJson.dump(options.outputBinary ? 0 : 4);
|
||||||
}
|
}
|
||||||
if (options.outputBinary) {
|
if (options.outputBinary)
|
||||||
|
{
|
||||||
uint32_t jsonLength = (uint32_t)gltfOutStream.tellp() - 20;
|
uint32_t jsonLength = (uint32_t)gltfOutStream.tellp() - 20;
|
||||||
// the binary body must begin on a 4-aligned address, so pad json with spaces if necessary
|
// the binary body must begin on a 4-aligned address, so pad json with spaces if necessary
|
||||||
while ((jsonLength % 4) != 0) {
|
while ((jsonLength % 4) != 0)
|
||||||
|
{
|
||||||
gltfOutStream.put(' ');
|
gltfOutStream.put(' ');
|
||||||
jsonLength++;
|
jsonLength++;
|
||||||
}
|
}
|
||||||
|
@ -1110,7 +1226,8 @@ ModelData *Raw2Gltf(
|
||||||
// append binary buffer directly to .glb file
|
// append binary buffer directly to .glb file
|
||||||
uint32_t binaryLength = gltf->binary->size();
|
uint32_t binaryLength = gltf->binary->size();
|
||||||
gltfOutStream.write((const char *)&(*gltf->binary)[0], binaryLength);
|
gltfOutStream.write((const char *)&(*gltf->binary)[0], binaryLength);
|
||||||
while ((binaryLength % 4) != 0) {
|
while ((binaryLength % 4) != 0)
|
||||||
|
{
|
||||||
gltfOutStream.put('\0');
|
gltfOutStream.put('\0');
|
||||||
binaryLength++;
|
binaryLength++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -197,7 +197,7 @@ Copyright (c) 2016-2017 Oculus VR, LLC.
|
||||||
|
|
||||||
if (options.count("output") == 0) {
|
if (options.count("output") == 0) {
|
||||||
// if -o is not given, default to the basename of the .fbx
|
// if -o is not given, default to the basename of the .fbx
|
||||||
outputPath = "./" + StringUtils::GetFileBaseString(inputPath);
|
outputPath = "." + (const char)StringUtils::GetPathSeparator() + StringUtils::GetFileBaseString(inputPath);
|
||||||
}
|
}
|
||||||
std::string outputFolder; // the output folder in .gltf mode, not used for .glb
|
std::string outputFolder; // the output folder in .gltf mode, not used for .glb
|
||||||
std::string modelPath; // the path of the actual .glb or .gltf file
|
std::string modelPath; // the path of the actual .glb or .gltf file
|
||||||
|
@ -207,7 +207,7 @@ Copyright (c) 2016-2017 Oculus VR, LLC.
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// in gltf mode, we create a folder and write into that
|
// in gltf mode, we create a folder and write into that
|
||||||
outputFolder = outputPath + "_out/";
|
outputFolder = outputPath + "_out" + (const char)StringUtils::GetPathSeparator();
|
||||||
modelPath = outputFolder + StringUtils::GetFileNameString(outputPath) + ".gltf";
|
modelPath = outputFolder + StringUtils::GetFileNameString(outputPath) + ".gltf";
|
||||||
}
|
}
|
||||||
if (!FileUtils::CreatePath(modelPath.c_str())) {
|
if (!FileUtils::CreatePath(modelPath.c_str())) {
|
||||||
|
|
|
@ -182,7 +182,7 @@ namespace FileUtils {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CopyFile(const std::string &srcFilename, const std::string &dstFilename) {
|
bool CopyFile(const std::string &srcFilename, const std::string &dstFilename, bool createPath = false) {
|
||||||
std::ifstream srcFile(srcFilename, std::ios::binary);
|
std::ifstream srcFile(srcFilename, std::ios::binary);
|
||||||
if (!srcFile) {
|
if (!srcFile) {
|
||||||
fmt::printf("Warning: Couldn't open file %s for reading.\n", srcFilename);
|
fmt::printf("Warning: Couldn't open file %s for reading.\n", srcFilename);
|
||||||
|
@ -193,9 +193,14 @@ namespace FileUtils {
|
||||||
std::streamsize srcSize = srcFile.tellg();
|
std::streamsize srcSize = srcFile.tellg();
|
||||||
srcFile.seekg(0, std::ios::beg);
|
srcFile.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
if (createPath && !CreatePath(dstFilename.c_str())) {
|
||||||
|
fmt::printf("Warning: Couldn't create directory %s.\n", dstFilename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::ofstream dstFile(dstFilename, std::ios::binary | std::ios::trunc);
|
std::ofstream dstFile(dstFilename, std::ios::binary | std::ios::trunc);
|
||||||
if (!dstFile) {
|
if (!dstFile) {
|
||||||
fmt::printf("Warning: Couldn't open file %s for writing.\n", srcFilename);
|
fmt::printf("Warning: Couldn't open file %s for writing.\n", dstFilename);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
dstFile << srcFile.rdbuf();
|
dstFile << srcFile.rdbuf();
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace FileUtils {
|
||||||
|
|
||||||
bool CreatePath(const char *path);
|
bool CreatePath(const char *path);
|
||||||
|
|
||||||
bool CopyFile(const std::string &srcFilename, const std::string &dstFilename);
|
bool CopyFile(const std::string &srcFilename, const std::string &dstFilename, bool createPath = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !__FILE_UTILS_H__
|
#endif // !__FILE_UTILS_H__
|
||||||
|
|
|
@ -16,4 +16,32 @@ namespace StringUtils {
|
||||||
return (s == PATH_WIN) ? PATH_UNIX : PATH_WIN;
|
return (s == PATH_WIN) ? PATH_UNIX : PATH_WIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PathSeparator GetPathSeparator() {
|
||||||
|
#if defined( __unix__ ) || defined( __APPLE__ )
|
||||||
|
return PATH_UNIX;
|
||||||
|
#else
|
||||||
|
return PATH_WIN;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string NormalizePath(const std::string &path)
|
||||||
|
{
|
||||||
|
PathSeparator separator = GetPathSeparator();
|
||||||
|
|
||||||
|
char replace;
|
||||||
|
|
||||||
|
if (separator == PATH_WIN) {
|
||||||
|
replace = PATH_UNIX;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
replace = PATH_WIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string normalizedPath = path;
|
||||||
|
for (size_t s = normalizedPath.find(replace, 0); s != std::string::npos; s = normalizedPath.find(replace, s)) {
|
||||||
|
normalizedPath[s] = separator;
|
||||||
|
}
|
||||||
|
return normalizedPath;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,10 @@ namespace StringUtils {
|
||||||
|
|
||||||
PathSeparator operator!(const PathSeparator &s);
|
PathSeparator operator!(const PathSeparator &s);
|
||||||
|
|
||||||
|
PathSeparator GetPathSeparator();
|
||||||
|
|
||||||
|
const std::string NormalizePath(const std::string &path);
|
||||||
|
|
||||||
inline const std::string GetCleanPathString(const std::string &path, const PathSeparator separator = PATH_WIN)
|
inline const std::string GetCleanPathString(const std::string &path, const PathSeparator separator = PATH_WIN)
|
||||||
{
|
{
|
||||||
std::string cleanPath = path;
|
std::string cleanPath = path;
|
||||||
|
|
Loading…
Reference in New Issue