Initial support for glTF models

This commit is contained in:
Fernando García Liñán
2021-08-16 17:00:57 +02:00
parent 65483a373f
commit 910225532e
12 changed files with 28867 additions and 1 deletions

View File

@@ -5,3 +5,5 @@ endif()
if (NOT SYSTEM_UDNS)
add_subdirectory(udns)
endif()
add_subdirectory(tiny_gltf)

16
3rdparty/tiny_gltf/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,16 @@
add_library(FGTinyGLTF INTERFACE)
target_include_directories(FGTinyGLTF
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>)
set_target_properties(FGTinyGLTF PROPERTIES LINKER_LANGUAGE CXX)
# in the non-shared case, we need to export these as well
if (NOT SIMGEAR_SHARED)
install(TARGETS FGTinyGLTF
EXPORT SimGearTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()

20406
3rdparty/tiny_gltf/json.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

7760
3rdparty/tiny_gltf/tiny_gltf.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -169,7 +169,8 @@ if(NOT SIMGEAR_HEADLESS)
target_link_libraries(SimGearScene PRIVATE
ZLIB::ZLIB
${OPENGL_LIBRARY}
${JPEG_LIBRARY})
${JPEG_LIBRARY}
FGTinyGLTF)
# ToDo: define an ALIAS target for the sound backend, to get
# rid of this logic here

View File

@@ -403,6 +403,25 @@ TextureBuilder::Registrar installTransparent("transparent",
new TransparentTextureBuilder);
}
class NullNormalmapTextureBuilder : public TextureBuilder
{
public:
Texture* build(Effect* effect, const SGPropertyNode*,
const SGReaderWriterOptions* options);
};
Texture* NullNormalmapTextureBuilder::build(Effect* effect, const SGPropertyNode*,
const SGReaderWriterOptions* options)
{
return StateAttributeFactory::instance()->getNullNormalmapTexture();
}
namespace
{
TextureBuilder::Registrar installNullNormalmap("null-normalmap",
new NullNormalmapTextureBuilder);
}
class NoiseBuilder : public TextureBuilder
{
public:

View File

@@ -8,6 +8,7 @@ set(HEADERS
ConditionNode.hxx
ModelRegistry.hxx
PrimitiveCollector.hxx
ReaderWriterGLTF.hxx
SGClipGroup.hxx
SGInteractionAnimation.hxx
SGLight.hxx
@@ -34,6 +35,7 @@ set(SOURCES
ConditionNode.cxx
ModelRegistry.cxx
PrimitiveCollector.cxx
ReaderWriterGLTF.cxx
SGClipGroup.cxx
SGInteractionAnimation.cxx
SGLight.cxx

View File

@@ -0,0 +1,604 @@
// Copyright (C) 2021 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include "ReaderWriterGLTF.hxx"
#include <osg/Texture2D>
#include <osg/MatrixTransform>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/ReadFile>
#include <osgUtil/SmoothingVisitor>
#include <simgear/math/SGMath.hxx>
#include <simgear/props/vectorPropTemplates.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#define TINYGLTF_IMPLEMENTATION
#define TINYGLTF_NO_STB_IMAGE
#define TINYGLTF_NO_STB_IMAGE_WRITE
#define TINYGLTF_NO_INCLUDE_STB_IMAGE
#define TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
#define TINYGLTF_NO_EXTERNAL_IMAGE
#define TINYGLTF_NOEXCEPTION
#define TINYGLTF_USE_CPP14
#include "tiny_gltf.h"
namespace simgear {
struct GLTFBuilder {
const tinygltf::Model &model;
const SGReaderWriterOptions *opts;
std::vector<osg::ref_ptr<osg::Array>> arrays;
GLTFBuilder(const tinygltf::Model &model_,
const SGReaderWriterOptions *opts_) :
model(model_),
opts(opts_) {
extractArrays();
}
osg::Node *makeModel() const {
osg::MatrixTransform* transform = new osg::MatrixTransform;
// In glTF +Y is up, but we use +Z up
transform->setMatrix(osg::Matrixd::rotate(osg::Vec3d(0.0, 1.0, 0.0),
osg::Vec3d(0.0, 0.0, 1.0)));
for (const auto &scene : model.scenes) {
for (const int nodeIndex : scene.nodes) {
osg::Node *node = makeNode(model.nodes[nodeIndex]);
if (node)
transform->addChild(node);
}
}
return transform;
}
osg::Node *makeNode(const tinygltf::Node &node) const {
osg::MatrixTransform* mt = new osg::MatrixTransform;
// This is the name used later by animations
mt->setName(node.name);
if (node.matrix.size() == 16) {
osg::Matrixd mat;
mat.set(node.matrix.data());
mt->setMatrix(mat);
} else {
osg::Matrixd scale, translation, rotation;
if (node.scale.size() == 3) {
scale = osg::Matrixd::scale(node.scale[0],
node.scale[1],
node.scale[2]);
}
if (node.rotation.size() == 4) {
osg::Quat quat(node.rotation[0],
node.rotation[1],
node.rotation[2],
node.rotation[3]);
rotation.makeRotate(quat);
}
if (node.translation.size() == 3) {
translation = osg::Matrixd::translate(node.translation[0],
node.translation[1],
node.translation[2]);
}
mt->setMatrix(scale * rotation * translation);
}
if (node.mesh >= 0) {
osg::Group *mesh = makeMesh(model.meshes[node.mesh]);
if (mesh)
mt->addChild(mesh);
}
for (const int nodeIndex : node.children) {
osg::Node *child = makeNode(model.nodes[nodeIndex]);
if (child)
mt->addChild(child);
}
return mt;
}
osg::Group *makeMesh(const tinygltf::Mesh &mesh) const {
osg::Group *group = new osg::Group;
for (const auto &primitive : mesh.primitives) {
if (primitive.indices < 0) {
// Currently we don't support primitives without index data.
// The glTF spec says that we should be using drawArrays to
// render geometry without index data.
continue;
}
EffectGeode *geode = new EffectGeode;
group->addChild(geode);
SGPropertyNode_ptr effectRoot = new SGPropertyNode;
makeChild(effectRoot, "inherits-from")->setStringValue("Effects/model-pbr");
if (primitive.material >= 0) {
// We have a material assigned to the primitive, add all the
// required material info as parameters to the Effect.
makeMaterialParameters(makeChild(effectRoot, "parameters"),
model.materials[primitive.material]);
}
Effect *effect = makeEffect(effectRoot, true, opts);
if (effect)
geode->setEffect(effect);
osg::Geometry *geom = new osg::Geometry;
geode->addDrawable(geom);
geom->setDataVariance(osg::Object::STATIC);
geom->setUseDisplayList(false);
geom->setUseVertexBufferObjects(true);
for (const auto &attr : primitive.attributes) {
if (attr.first == "POSITION") {
geom->setVertexArray(arrays[attr.second].get());
} else if (attr.first == "NORMAL") {
geom->setNormalArray(arrays[attr.second].get());
} else if (attr.first == "TEXCOORD_0") {
geom->setTexCoordArray(0, arrays[attr.second].get());
} else if (attr.first == "TEXCOORD_1") {
geom->setTexCoordArray(1, arrays[attr.second].get());
} else if (attr.first == "COLOR_0") {
geom->setColorArray(arrays[attr.second].get());
}
}
int mode = -1;
switch (primitive.mode) {
case TINYGLTF_MODE_TRIANGLES: mode = GL_TRIANGLES; break;
case TINYGLTF_MODE_TRIANGLE_STRIP: mode = GL_TRIANGLE_STRIP; break;
case TINYGLTF_MODE_TRIANGLE_FAN: mode = GL_TRIANGLE_FAN; break;
case TINYGLTF_MODE_POINTS: mode = GL_POINTS; break;
case TINYGLTF_MODE_LINE: mode = GL_LINES; break;
case TINYGLTF_MODE_LINE_LOOP: mode = GL_LINE_LOOP; break;
default: break;
}
const tinygltf::Accessor &indexAccessor = model.accessors[primitive.indices];
if (indexAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
auto *indices = static_cast<osg::UShortArray *>(
arrays[primitive.indices].get());
auto *drawElements = new osg::DrawElementsUShort(
mode, indices->begin(), indices->end());
geom->addPrimitiveSet(drawElements);
} else if (indexAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
auto *indices = static_cast<osg::UIntArray *>(
arrays[primitive.indices].get());
auto *drawElements = new osg::DrawElementsUInt(
mode, indices->begin(), indices->end());
geom->addPrimitiveSet(drawElements);
} else if (indexAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
auto *indices = static_cast<osg::UByteArray *>(
arrays[primitive.indices].get());
auto *drawElements = new osg::DrawElementsUByte(
mode, indexAccessor.count);
std::copy(indices->begin(), indices->end(), drawElements->begin());
geom->addPrimitiveSet(drawElements);
}
// Compute the normals if the file doesn't already contain them
if (!geom->getNormalArray()) {
osgUtil::SmoothingVisitor sv;
geode->accept(sv);
}
// Generate tangent vectors etc if needed
geode->runGenerators(geom);
}
return group;
}
bool makeMaterialParameters(SGPropertyNode *params,
const tinygltf::Material &material) const {
const tinygltf::PbrMetallicRoughness &pbr = material.pbrMetallicRoughness;
makeChild(params, "base-color-factor")->setValue(
SGVec4d(pbr.baseColorFactor[0],
pbr.baseColorFactor[1],
pbr.baseColorFactor[2],
pbr.baseColorFactor[3]));
makeChild(params, "metallic-factor")->setValue(pbr.metallicFactor);
makeChild(params, "roughness-factor")->setValue(pbr.roughnessFactor);
makeChild(params, "emissive-factor")->setValue(
SGVec3d(material.emissiveFactor[0],
material.emissiveFactor[1],
material.emissiveFactor[2]));
SGPropertyNode *blendNode = makeChild(params, "blend");
if (material.alphaMode == "OPAQUE") {
makeChild(blendNode, "active")->setValue(false);
makeChild(params, "rendering-hint")->setStringValue("opaque");
makeChild(params, "alpha-cutoff")->setValue(-1.0);
} else if (material.alphaMode == "MASK") {
makeChild(blendNode, "active")->setValue(false);
makeChild(params, "rendering-hint")->setStringValue("opaque");
makeChild(params, "alpha-cutoff")->setValue(material.alphaCutoff);
} else if (material.alphaMode == "BLEND") {
makeChild(blendNode, "active")->setValue(true);
makeChild(params, "rendering-hint")->setStringValue("transparent");
makeChild(params, "alpha-cutoff")->setValue(-1.0);
}
std::string cullFaceString;
if (material.doubleSided) {
cullFaceString = "off";
} else {
cullFaceString = "back";
}
makeChild(params, "cull-face")->setStringValue(cullFaceString);
SGPropertyNode *baseColorTexNode = makeChild(params, "texture", 0);
if (!makeTextureParameters(baseColorTexNode, pbr.baseColorTexture.index))
makeChild(baseColorTexNode, "type")->setValue("white");
SGPropertyNode *normalTexNode = makeChild(params, "texture", 1);
if (!makeTextureParameters(normalTexNode, material.normalTexture.index))
makeChild(normalTexNode, "type")->setValue("null-normalmap");
SGPropertyNode *metRoughTexNode = makeChild(params, "texture", 2);
if (!makeTextureParameters(metRoughTexNode, pbr.metallicRoughnessTexture.index))
makeChild(metRoughTexNode, "type")->setValue("white");
SGPropertyNode *occlusionTexNode = makeChild(params, "texture", 3);
if (!makeTextureParameters(occlusionTexNode, material.occlusionTexture.index))
makeChild(occlusionTexNode, "type")->setValue("white");
SGPropertyNode *emissiveTexNode = makeChild(params, "texture", 4);
if (!makeTextureParameters(emissiveTexNode, material.emissiveTexture.index))
makeChild(emissiveTexNode, "type")->setValue("white");
makeChild(params, "flip-vertically")->setValue(true);
return true;
}
bool makeTextureParameters(SGPropertyNode *texNode, int textureIndex) const {
if (textureIndex < 0) {
// The material doesn't define this texture
return false;
}
const tinygltf::Texture &texture = model.textures[textureIndex];
const tinygltf::Image &image = model.images[texture.source];
if (tinygltf::IsDataURI(image.uri) || image.image.size() > 0) {
// This is an embedded image
// Since we rely on the Effects framework to load the images from
// a file for us, we can't support these for now.
SG_LOG(SG_INPUT, SG_DEV_ALERT, "glTF loader: embedded images are not supported");
return false;
}
// This is an URI to a external image
std::string absFileName = osgDB::findDataFile(image.uri, opts);
if (absFileName.empty()) {
SG_LOG(SG_INPUT, SG_ALERT, "glTF loader: could not find external texture '"
<< image.uri << "'");
return false;
}
makeChild(texNode, "type")->setValue("2d");
makeChild(texNode, "image")->setStringValue(absFileName);
// Use default sampler settings
makeChild(texNode, "filter")->setStringValue("linear-mipmap-linear");
makeChild(texNode, "mag-filter")->setStringValue("linear");
makeChild(texNode, "wrap-s")->setStringValue("repeat");
makeChild(texNode, "wrap-t")->setStringValue("repeat");
return true;
}
// Create an OSG array from a glTF accessor
// Copied from osgEarth's glTF reader plugin
template<typename OSGArray, int ComponentType, int AccessorType>
class ArrayBuilder {
public:
static OSGArray* makeArray(unsigned int size) {
return new OSGArray(size);
}
static void copyData(OSGArray* dest, const unsigned char* src, size_t viewOffset,
size_t byteStride, size_t accessorOffset, size_t count) {
int32_t componentSize = tinygltf::GetComponentSizeInBytes(ComponentType);
int32_t numComponents = tinygltf::GetNumComponentsInType(AccessorType);
if (byteStride == 0) {
memcpy(&(*dest)[0], src + accessorOffset + viewOffset, componentSize * numComponents * count);
} else {
const unsigned char* ptr = src + accessorOffset + viewOffset;
for (size_t i = 0; i < count; ++i, ptr += byteStride) {
memcpy(&(*dest)[i], ptr, componentSize * numComponents);
}
}
}
static void copyData(OSGArray* dest, const tinygltf::Buffer& buffer, const tinygltf::BufferView& bufferView,
const tinygltf::Accessor& accessor) {
copyData(dest, &buffer.data.at(0), bufferView.byteOffset,
bufferView.byteStride, accessor.byteOffset, accessor.count);
}
static OSGArray* makeArray(const tinygltf::Buffer& buffer, const tinygltf::BufferView& bufferView,
const tinygltf::Accessor& accessor) {
OSGArray* result = new OSGArray(accessor.count);
copyData(result, buffer, bufferView, accessor);
return result;
}
};
// Take all of the accessors and turn them into arrays
// Copied from osgEarth's glTF reader plugin
void extractArrays() {
for (const auto &accessor : model.accessors) {
const auto &bufferView = model.bufferViews[accessor.bufferView];
const auto &buffer = model.buffers[bufferView.buffer];
osg::ref_ptr<osg::Array> osgArray;
switch (accessor.componentType) {
case TINYGLTF_COMPONENT_TYPE_BYTE:
switch (accessor.type) {
case TINYGLTF_TYPE_SCALAR:
osgArray = ArrayBuilder<osg::ByteArray,
TINYGLTF_COMPONENT_TYPE_BYTE,
TINYGLTF_TYPE_SCALAR>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC2:
osgArray = ArrayBuilder<osg::Vec2bArray,
TINYGLTF_COMPONENT_TYPE_BYTE,
TINYGLTF_TYPE_VEC2>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC3:
osgArray = ArrayBuilder<osg::Vec3bArray,
TINYGLTF_COMPONENT_TYPE_BYTE,
TINYGLTF_TYPE_VEC3>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC4:
osgArray = ArrayBuilder<osg::Vec4bArray,
TINYGLTF_COMPONENT_TYPE_BYTE,
TINYGLTF_TYPE_VEC4>::makeArray(buffer, bufferView, accessor);
break;
default:
break;
}
break;
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
switch (accessor.type) {
case TINYGLTF_TYPE_SCALAR:
osgArray = ArrayBuilder<osg::UByteArray,
TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE,
TINYGLTF_TYPE_SCALAR>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC2:
osgArray = ArrayBuilder<osg::Vec2ubArray,
TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE,
TINYGLTF_TYPE_VEC2>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC3:
osgArray = ArrayBuilder<osg::Vec3ubArray,
TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE,
TINYGLTF_TYPE_VEC3>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC4:
osgArray = ArrayBuilder<osg::Vec4ubArray,
TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE,
TINYGLTF_TYPE_VEC4>::makeArray(buffer, bufferView, accessor);
break;
default:
break;
}
break;
case TINYGLTF_COMPONENT_TYPE_SHORT:
switch (accessor.type) {
case TINYGLTF_TYPE_SCALAR:
osgArray = ArrayBuilder<osg::ShortArray,
TINYGLTF_COMPONENT_TYPE_SHORT,
TINYGLTF_TYPE_SCALAR>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC2:
osgArray = ArrayBuilder<osg::Vec2sArray,
TINYGLTF_COMPONENT_TYPE_SHORT,
TINYGLTF_TYPE_VEC2>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC3:
osgArray = ArrayBuilder<osg::Vec3sArray,
TINYGLTF_COMPONENT_TYPE_SHORT,
TINYGLTF_TYPE_VEC3>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC4:
osgArray = ArrayBuilder<osg::Vec4sArray,
TINYGLTF_COMPONENT_TYPE_SHORT,
TINYGLTF_TYPE_VEC4>::makeArray(buffer, bufferView, accessor);
break;
default:
break;
}
break;
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
switch (accessor.type) {
case TINYGLTF_TYPE_SCALAR:
osgArray = ArrayBuilder<osg::UShortArray,
TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT,
TINYGLTF_TYPE_SCALAR>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC2:
osgArray = ArrayBuilder<osg::Vec2usArray,
TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT,
TINYGLTF_TYPE_VEC2>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC3:
osgArray = ArrayBuilder<osg::Vec3usArray,
TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT,
TINYGLTF_TYPE_VEC3>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC4:
osgArray = ArrayBuilder<osg::Vec4usArray,
TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT,
TINYGLTF_TYPE_VEC4>::makeArray(buffer, bufferView, accessor);
break;
default:
break;
}
break;
case TINYGLTF_COMPONENT_TYPE_INT:
switch (accessor.type) {
case TINYGLTF_TYPE_SCALAR:
osgArray = ArrayBuilder<osg::IntArray,
TINYGLTF_COMPONENT_TYPE_INT,
TINYGLTF_TYPE_SCALAR>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC2:
osgArray = ArrayBuilder<osg::Vec2uiArray,
TINYGLTF_COMPONENT_TYPE_INT,
TINYGLTF_TYPE_VEC2>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC3:
osgArray = ArrayBuilder<osg::Vec3uiArray,
TINYGLTF_COMPONENT_TYPE_INT,
TINYGLTF_TYPE_VEC3>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC4:
osgArray = ArrayBuilder<osg::Vec4uiArray,
TINYGLTF_COMPONENT_TYPE_INT,
TINYGLTF_TYPE_VEC4>::makeArray(buffer, bufferView, accessor);
break;
default:
break;
}
break;
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
switch (accessor.type) {
case TINYGLTF_TYPE_SCALAR:
osgArray = ArrayBuilder<osg::UIntArray,
TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT,
TINYGLTF_TYPE_SCALAR>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC2:
osgArray = ArrayBuilder<osg::Vec2iArray,
TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT,
TINYGLTF_TYPE_VEC2>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC3:
osgArray = ArrayBuilder<osg::Vec3iArray,
TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT,
TINYGLTF_TYPE_VEC3>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC4:
osgArray = ArrayBuilder<osg::Vec4iArray,
TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT,
TINYGLTF_TYPE_VEC4>::makeArray(buffer, bufferView, accessor);
break;
default:
break;
}
break;
case TINYGLTF_COMPONENT_TYPE_FLOAT:
switch (accessor.type) {
case TINYGLTF_TYPE_SCALAR:
osgArray = ArrayBuilder<osg::FloatArray,
TINYGLTF_COMPONENT_TYPE_FLOAT,
TINYGLTF_TYPE_SCALAR>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC2:
osgArray = ArrayBuilder<osg::Vec2Array,
TINYGLTF_COMPONENT_TYPE_FLOAT,
TINYGLTF_TYPE_VEC2>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC3:
osgArray = ArrayBuilder<osg::Vec3Array,
TINYGLTF_COMPONENT_TYPE_FLOAT,
TINYGLTF_TYPE_VEC3>::makeArray(buffer, bufferView, accessor);
break;
case TINYGLTF_TYPE_VEC4:
osgArray = ArrayBuilder<osg::Vec4Array,
TINYGLTF_COMPONENT_TYPE_FLOAT,
TINYGLTF_TYPE_VEC4>::makeArray(buffer, bufferView, accessor);
break;
default:
break;
}
default:
break;
}
if (osgArray.valid()) {
osgArray->setBinding(osg::Array::BIND_PER_VERTEX);
osgArray->setNormalize(accessor.normalized);
}
arrays.push_back(osgArray);
}
}
};
ReaderWriterGLTF::ReaderWriterGLTF()
{
supportsExtension("gltf", "glTF ASCII loader");
supportsExtension("glb", "glTF binary loader");
}
ReaderWriterGLTF::~ReaderWriterGLTF()
{
}
const char *ReaderWriterGLTF::className() const
{
return "glTF loader";
}
osgDB::ReaderWriter::ReadResult
ReaderWriterGLTF::readNode(const std::string &location,
const osgDB::Options *options) const
{
std::string ext = osgDB::getFileExtension(location);
if (!acceptsExtension(ext))
return ReadResult::FILE_NOT_HANDLED;
const auto sgOpts = dynamic_cast<const SGReaderWriterOptions *>(options);
if (!sgOpts) {
// We need a SGReaderWriterOptions for Effects, so we can't parse this
// properly. I'm not sure if this can happen at all though.
return ReadResult::FILE_NOT_FOUND;
}
tinygltf::Model model;
tinygltf::TinyGLTF loader;
std::string err, warn;
bool ret;
if (ext == "gltf")
ret = loader.LoadASCIIFromFile(&model, &err, &warn, location);
else if (ext == "glb")
ret = loader.LoadBinaryFromFile(&model, &err, &warn, location);
else
return ReadResult::FILE_NOT_HANDLED;
if (!warn.empty())
SG_LOG(SG_INPUT, SG_WARN, "glTF loader: " << warn);
if (!err.empty())
SG_LOG(SG_INPUT, SG_ALERT, "glTF loader: " << err);
if (!ret)
return osgDB::ReaderWriter::ReadResult::ERROR_IN_READING_FILE;
GLTFBuilder builder(model, sgOpts);
return builder.makeModel();
}
} // namespace simgear

View File

@@ -0,0 +1,37 @@
// Copyright (C) 2021 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_READERWRITERGLTF_HXX
#define SG_READERWRITERGLTF_HXX
#include <osgDB/ReaderWriter>
namespace simgear {
class ReaderWriterGLTF : public osgDB::ReaderWriter {
public:
ReaderWriterGLTF();
virtual ~ReaderWriterGLTF();
virtual const char *className() const;
virtual ReadResult readNode(const std::string &location,
const osgDB::Options *options) const;
};
} // namespace simgear
#endif /* SG_READERWRITERGLTF_HXX */

View File

@@ -39,6 +39,7 @@
#include "ReaderWriterSPT.hxx"
#include "ReaderWriterSTG.hxx"
#include <simgear/scene/dem/ReaderWriterPGT.hxx>
#include <simgear/scene/model/ReaderWriterGLTF.hxx>
// the following are static values needed by the runtime object
// loader. However, the loading is done via a call back so these
@@ -68,6 +69,9 @@ simgear::ModelRegistryCallbackProxy<simgear::LoadOnlyCallback> g_sptCallbackProx
osgDB::RegisterReaderWriterProxy<simgear::ReaderWriterPGT> g_readerWriterPGTProxy;
simgear::ModelRegistryCallbackProxy<simgear::LoadOnlyCallback> g_pgtCallbackProxy("pgt");
#endif
osgDB::RegisterReaderWriterProxy<simgear::ReaderWriterGLTF> g_readerWriterGLTFProxy;
simgear::ModelRegistryCallbackProxy<simgear::LoadOnlyCallback> g_gltfCallbackProxy("gltf");
}
void sgUserDataInit( SGPropertyNode *p ) {

View File

@@ -81,6 +81,18 @@ StateAttributeFactory::StateAttributeFactory()
_transparentTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
_transparentTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
_transparentTexture->setDataVariance(osg::Object::STATIC);
// And a null normal map texture
dummyImage = new osg::Image;
dummyImage->allocateImage(1, 1, 1, GL_RGB, GL_UNSIGNED_BYTE);
imageBytes = dummyImage->data(0, 0);
imageBytes[0] = 127;
imageBytes[1] = 127;
imageBytes[2] = 255;
_nullNormalmapTexture = new osg::Texture2D;
_nullNormalmapTexture->setImage(dummyImage);
_nullNormalmapTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
_nullNormalmapTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
_nullNormalmapTexture->setDataVariance(osg::Object::STATIC);
_white = new Vec4Array(1);
(*_white)[0].set(1.0f, 1.0f, 1.0f, 1.0f);
_white->setDataVariance(Object::STATIC);

View File

@@ -66,6 +66,8 @@ public:
{
return _transparentTexture.get();
}
// Null normalmap texture vec3(0.5, 0.5, 1.0)
osg::Texture2D* getNullNormalmapTexture() { return _nullNormalmapTexture.get(); }
// cull front and back facing polygons
osg::CullFace* getCullFaceFront() { return _cullFaceFront.get(); }
osg::CullFace* getCullFaceBack() { return _cullFaceBack.get(); }
@@ -81,6 +83,7 @@ protected:
osg::ref_ptr<osg::TexEnv> _standardTexEnv;
osg::ref_ptr<osg::Texture2D> _whiteTexture;
osg::ref_ptr<osg::Texture2D> _transparentTexture;
osg::ref_ptr<osg::Texture2D> _nullNormalmapTexture;
osg::ref_ptr<osg::Vec4Array> _white;
osg::ref_ptr<osg::CullFace> _cullFaceFront;
osg::ref_ptr<osg::CullFace> _cullFaceBack;