Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optional texture embedding #172

Merged
merged 5 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/vsgXchange/models.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ namespace vsgXchange
static constexpr const char* crease_angle = "crease_angle"; /// float
static constexpr const char* two_sided = "two_sided"; /// bool
static constexpr const char* discard_empty_nodes = "discard_empty_nodes"; /// bool
static constexpr const char* external_textures = "external_textures"; /// bool
static constexpr const char* external_texture_format = "external_texture_format"; /// TextureFormat enum

bool readOptions(vsg::Options& options, vsg::CommandLine& arguments) const override;

Expand Down
89 changes: 84 additions & 5 deletions src/assimp/assimp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,31 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
#include <sstream>
#include <stack>

enum class TextureFormat
{
native,
vsgt,
vsgb
};

// this needs to be defined before 'vsg/commandline.h' has been included
std::istream& operator>> (std::istream& is, TextureFormat& textureFormat)
{
std::string value;
is >> value;

if (value == "native")
textureFormat = TextureFormat::native;
else if (value == "vsgb")
textureFormat = TextureFormat::vsgb;
else if ((value == "vsgt")||(value == "vsga"))
textureFormat = TextureFormat::vsgt;
else
textureFormat = TextureFormat::native;

return is;
}

#include <vsg/all.h>

#include <assimp/Importer.hpp>
Expand Down Expand Up @@ -117,6 +142,8 @@ bool assimp::getFeatures(Features& features) const
features.optionNameTypeMap[assimp::crease_angle] = vsg::type_name<float>();
features.optionNameTypeMap[assimp::two_sided] = vsg::type_name<bool>();
features.optionNameTypeMap[assimp::discard_empty_nodes] = vsg::type_name<bool>();
features.optionNameTypeMap[assimp::external_textures] = vsg::type_name<bool>();
features.optionNameTypeMap[assimp::external_texture_format] = vsg::type_name<TextureFormat>();

return true;
}
Expand All @@ -128,6 +155,9 @@ bool assimp::readOptions(vsg::Options& options, vsg::CommandLine& arguments) con
result = arguments.readAndAssign<float>(assimp::crease_angle, &options) || result;
result = arguments.readAndAssign<bool>(assimp::two_sided, &options) || result;
result = arguments.readAndAssign<bool>(assimp::discard_empty_nodes, &options) || result;
result = arguments.readAndAssign<bool>(assimp::external_textures, &options) || result;
result = arguments.readAndAssign<TextureFormat>(assimp::external_texture_format, &options) || result;

return result;
}

Expand All @@ -149,11 +179,14 @@ struct SceneConverter

bool useViewDependentState = true;
bool discardEmptyNodes = true;
bool externalTextures = false;
TextureFormat externalTextureFormat = TextureFormat::native;

// TODO flatShadedShaderSet?
vsg::ref_ptr<vsg::ShaderSet> pbrShaderSet;
vsg::ref_ptr<vsg::ShaderSet> phongShaderSet;
vsg::ref_ptr<vsg::SharedObjects> sharedObjects;
vsg::ref_ptr<vsg::External> externalObjects;

std::vector<vsg::ref_ptr<vsg::DescriptorConfigurator>> convertedMaterials;
std::vector<vsg::ref_ptr<vsg::Node>> convertedMeshes;
Expand Down Expand Up @@ -250,6 +283,7 @@ SamplerData SceneConverter::convertTexture(const aiMaterial& material, aiTexture
if (material.GetTexture(type, 0, &texPath, nullptr, nullptr, nullptr, nullptr, wrapMode) == AI_SUCCESS)
{
SamplerData samplerImage;
vsg::Path externalTextureFilename;

if (auto texture = scene->GetEmbeddedTexture(texPath.C_Str()))
{
Expand Down Expand Up @@ -288,10 +322,10 @@ SamplerData SceneConverter::convertTexture(const aiMaterial& material, aiTexture
}
else
{
auto textureFilename = vsg::findFile(texPath.C_Str(), options);
if (samplerImage.data = vsg::read_cast<vsg::Data>(textureFilename, options); !samplerImage.data.valid())
externalTextureFilename = vsg::findFile(texPath.C_Str(), options);
if (samplerImage.data = vsg::read_cast<vsg::Data>(externalTextureFilename, options); !samplerImage.data.valid())
{
vsg::warn("Failed to load texture: ", textureFilename, " texPath = ", texPath.C_Str());
vsg::warn("Failed to load texture: ", externalTextureFilename, " texPath = ", texPath.C_Str());
return {};
}
}
Expand All @@ -317,6 +351,40 @@ SamplerData SceneConverter::convertTexture(const aiMaterial& material, aiTexture
sharedObjects->share(samplerImage.sampler);
}

if (externalTextures && externalObjects)
{
// calculate the texture filename
switch (externalTextureFormat)
{
case TextureFormat::native:
break; // nothing to do
case TextureFormat::vsgt:
externalTextureFilename = vsg::removeExtension(externalTextureFilename).concat(".vsgt");
break;
case TextureFormat::vsgb:
externalTextureFilename = vsg::removeExtension(externalTextureFilename).concat(".vsgb");
break;
}

// actually write out the texture.. this need only be done once per texture!
if (externalObjects->entries.count(externalTextureFilename) == 0)
{
switch (externalTextureFormat)
{
case TextureFormat::native:
break; // nothing to do
case TextureFormat::vsgt:
vsg::write(samplerImage.data, externalTextureFilename, options);
break;
case TextureFormat::vsgb:
vsg::write(samplerImage.data, externalTextureFilename, options);
break;
}

externalObjects->add(externalTextureFilename, samplerImage.data);
}
}

return samplerImage;
}
else
Expand Down Expand Up @@ -717,11 +785,14 @@ vsg::ref_ptr<vsg::Node> SceneConverter::visit(const aiScene* in_scene, vsg::ref_
scene = in_scene;
options = in_options;
discardEmptyNodes = vsg::value<bool>(true, assimp::discard_empty_nodes, options);
externalTextures = vsg::value<bool>(false, assimp::external_textures, options);
externalTextureFormat = vsg::value<TextureFormat>(TextureFormat::native, assimp::external_texture_format, options);

std::string name = scene->mName.C_Str();

if (options) sharedObjects = options->sharedObjects;
if (!sharedObjects) sharedObjects = vsg::SharedObjects::create();
if (externalTextures && !externalObjects) externalObjects = vsg::External::create();

processCameras();
processLights();
Expand Down Expand Up @@ -1023,7 +1094,15 @@ vsg::ref_ptr<vsg::Object> assimp::Implementation::read(const vsg::Path& filename

SceneConverter converter;
converter.filename = filename;
return converter.visit(scene, opt, ext);

auto root = converter.visit(scene, opt, ext);
if (root)
{
if (converter.externalTextures && converter.externalObjects && !converter.externalObjects->entries.empty())
root->setObject("external", converter.externalObjects);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above code block changes what happens when a null ref_ptr is returned from convert,visit(), and would cause the code to fall through to the vsg::read() at the end of method. Is this intended?

I would have thought a else return {} would keep the original behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you are correct. I mentally completely blanked out the #if .. #endif block and its contained 'return' statement (without that block the behaviour is actually unchanged, but still probably less easy to read).

I hope to get around to updating this PR beginning of next week.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the PR now


return root;
}
else
{
Expand All @@ -1038,7 +1117,7 @@ vsg::ref_ptr<vsg::Object> assimp::Implementation::read(const vsg::Path& filename
opt->paths.push_back(vsg::filePath(filename));
opt->extensionHint = vsg::lowerCaseFileExtension(filename);

return read(file, opt);
return vsg::read(file, opt);
#endif

return {};
Expand Down