- Edited
I'm new to godot-cpp and I'm encountering a rendering issue after converting GLTF material textures to StandardMaterial3D and Texture in my project. Specifically, the textures are not rendering correctly after the conversion.
I've tried to look for an API in godot-cpp to set the WrapS and WrapT properties for the textures, but I couldn't find any.
And I've been inspired by the CesiumForUnity codebase and have borrowed several ideas from it for my project.
Now The baseColorTexture is not rendering correctly, while the baseColor is fine. When this amateur project is nearly completed, I want to share it with the open-source community for everyone to learn and progress together.
Could anyone more experienced with godot-cpp help me out? Below are the relevant code snippets and screenshots for reference.
Ref<StandardMaterial3D> material =
Ref<StandardMaterial3D>( memnew( StandardMaterial3D ) );
const CesiumGltf::Material *pMaterial =
Model::getSafe( &gltf.materials, primitive.material );
if ( pMaterial )
{
setGltfMaterialParameterValues( gltf, primitiveInfo, *pMaterial, material,
materialProperties );
}
Color color = material->get_albedo();
SPDLOG_INFO( "material get_albedo {0}, {1}, {2}, {3}", color.r, color.g, color.b,
color.a );
SPDLOG_INFO( "material_get_metallic {0}", material->get_metallic() );
meshInstance->set_material_override( material );`
`static const CesiumGltf::MaterialPBRMetallicRoughness defaultPbrMetallicRoughness;
void setGltfMaterialParameterValues( const CesiumGltf::Model &model,
const CesiumPrimitiveInfo &primitiveInfo,
const CesiumGltf::Material &gltfMaterial,
const Ref<StandardMaterial3D> material,
const TilesetMaterialProperties &materialProperties )
{
CESIUM_TRACE( "Cesium::CreateMaterials" );
const CesiumGltf::MaterialPBRMetallicRoughness &pbr =
gltfMaterial.pbrMetallicRoughness ? gltfMaterial.pbrMetallicRoughness.value()
: defaultPbrMetallicRoughness;
// Add base color factor and metallic-roughness factor regardless
// of whether the textures are present.
const std::vector<double> &baseColorFactor = pbr.baseColorFactor;
material->set_albedo(
Color( baseColorFactor[0], baseColorFactor[1], baseColorFactor[2], baseColorFactor[3] ) );
material->set_metallic( pbr.metallicFactor );
material->set_roughness( pbr.roughnessFactor );
const std::optional<CesiumGltf::TextureInfo> &baseColorTexture = pbr.baseColorTexture;
if ( baseColorTexture )
{
auto texCoordIndexIt = primitiveInfo.uvIndexMap.find( baseColorTexture->texCoord );
if ( texCoordIndexIt != primitiveInfo.uvIndexMap.end() )
{
Ref<godot::Texture> gTexture = loadTexture( model, baseColorTexture->index, true );
if ( gTexture.is_valid() )
{
material->set_texture( StandardMaterial3D::TextureParam::TEXTURE_ALBEDO, gTexture );
const CesiumGltf::Texture *pTexture =
Model::getSafe( &model.textures, baseColorTexture->index );
const CesiumGltf::Sampler *pSampler =
CesiumGltf::Model::getSafe( &model.samplers, pTexture->source );
if ( !pSampler->minFilter )
{
if ( pSampler->magFilter &&
*pSampler->magFilter == Sampler::MagFilter::NEAREST )
{
material->set_texture_filter(
BaseMaterial3D::TextureFilter::TEXTURE_FILTER_NEAREST );
}
else
{
material->set_texture_filter(
BaseMaterial3D::TextureFilter::TEXTURE_FILTER_LINEAR );
}
}
else
{
switch ( *pSampler->minFilter )
{
case Sampler::MinFilter::NEAREST:
material->set_texture_filter(
BaseMaterial3D::TextureFilter::TEXTURE_FILTER_NEAREST );
break;
case Sampler::MinFilter::NEAREST_MIPMAP_NEAREST:
material->set_texture_filter( BaseMaterial3D::TextureFilter::
TEXTURE_FILTER_NEAREST_WITH_MIPMAPS );
break;
case Sampler::MinFilter::LINEAR:
material->set_texture_filter(
BaseMaterial3D::TextureFilter::TEXTURE_FILTER_LINEAR );
break;
case Sampler::MinFilter::LINEAR_MIPMAP_NEAREST:
material->set_texture_filter(
BaseMaterial3D::TextureFilter::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS );
break;
default:
material->set_texture_filter(
BaseMaterial3D::TextureFilter::TEXTURE_FILTER_MAX );
}
}
}
}
}
const std::optional<TextureInfo> &metallicRoughness = pbr.metallicRoughnessTexture;
if ( metallicRoughness )
{
auto texCoordIndexIt = primitiveInfo.uvIndexMap.find( metallicRoughness->texCoord );
if ( texCoordIndexIt != primitiveInfo.uvIndexMap.end() )
{
Ref<godot::Texture> gTexture = loadTexture( model, metallicRoughness->index, false );
if ( gTexture.is_valid() )
{
material->set_texture( StandardMaterial3D::TextureParam::TEXTURE_METALLIC,
gTexture );
}
}
}
const std::vector<double> &emissiveFactor = gltfMaterial.emissiveFactor;
if ( gltfMaterial.emissiveTexture )
{
auto texCoordIndexIt =
primitiveInfo.uvIndexMap.find( gltfMaterial.emissiveTexture->texCoord );
if ( texCoordIndexIt != primitiveInfo.uvIndexMap.end() )
{
Ref<godot::Texture> gTexture =
loadTexture( model, gltfMaterial.emissiveTexture->index, true );
if ( gTexture.is_valid() )
{
material->set_texture( StandardMaterial3D::TextureParam::TEXTURE_EMISSION,
gTexture );
}
}
}
if ( gltfMaterial.normalTexture )
{
auto texCoordIndexIt =
primitiveInfo.uvIndexMap.find( gltfMaterial.normalTexture->texCoord );
if ( texCoordIndexIt != primitiveInfo.uvIndexMap.end() )
{
Ref<godot::Texture> gTexture =
loadTexture( model, gltfMaterial.normalTexture->index, true );
if ( gTexture.is_valid() )
{
material->set_texture( StandardMaterial3D::TextureParam::TEXTURE_NORMAL, gTexture );
}
}
}
if ( gltfMaterial.occlusionTexture )
{
auto texCoordIndexIt =
primitiveInfo.uvIndexMap.find( gltfMaterial.occlusionTexture->texCoord );
if ( texCoordIndexIt != primitiveInfo.uvIndexMap.end() )
{
Ref<godot::Texture> gTexture =
loadTexture( model, gltfMaterial.occlusionTexture->index, true );
if ( gTexture.is_valid() )
{
material->set_texture( StandardMaterial3D::TextureParam::TEXTURE_AMBIENT_OCCLUSION,
gTexture );
}
}
}
// Handle KHR_texture_transform for each available texture.
CesiumGltf::KhrTextureTransform textureTransform;
const CesiumGltf::ExtensionKhrTextureTransform *pBaseColorTextureTransform =
pbr.baseColorTexture
? pbr.baseColorTexture->getExtension<CesiumGltf::ExtensionKhrTextureTransform>()
: nullptr;
if ( pBaseColorTextureTransform )
{
textureTransform = CesiumGltf::KhrTextureTransform( *pBaseColorTextureTransform );
if ( textureTransform.status() == CesiumGltf::KhrTextureTransformStatus::Valid )
{
const glm::dvec2 &scale = textureTransform.scale();
const glm::dvec2 &offset = textureTransform.offset();
material->set_uv1_scale( Vector3( scale[0], scale[1], 0 ) );
material->set_uv1_offset( Vector3( offset[0], offset[1], 0 ) );
// const glm::dvec2& rotationSineCosine =
// textureTransform.rotationSineCosine();
// unityMaterial.SetVector(
// materialProperties.getBaseColorTextureRotationID(),
// { static_cast<float>(rotationSineCosine[0]),
// static_cast<float>(rotationSineCosine[1]),
// 0.0f,
// 0.0f });
}
}
const CesiumGltf::ExtensionKhrTextureTransform *pMetallicRoughnessTextureTransform =
pbr.metallicRoughnessTexture
? pbr.metallicRoughnessTexture->getExtension<CesiumGltf::ExtensionKhrTextureTransform>()
: nullptr;
if ( pMetallicRoughnessTextureTransform )
{
textureTransform = CesiumGltf::KhrTextureTransform( *pMetallicRoughnessTextureTransform );
if ( textureTransform.status() == CesiumGltf::KhrTextureTransformStatus::Valid )
{
const glm::dvec2 &scale = textureTransform.scale();
const glm::dvec2 &offset = textureTransform.offset();
material->set_uv1_scale( Vector3( scale[0], scale[1], 0 ) );
material->set_uv1_offset( Vector3( offset[0], offset[1], 0 ) );
// const glm::dvec2& rotationSineCosine =
// textureTransform.rotationSineCosine();
// unityMaterial.SetVector(
// materialProperties.getMetallicRoughnessTextureRotationID(),
// { static_cast<float>(rotationSineCosine[0]),
// static_cast<float>(rotationSineCosine[1]),
// 0.0f,
// 0.0f });
}
}
const CesiumGltf::ExtensionKhrTextureTransform *pNormalTextureTransform =
gltfMaterial.normalTexture
? gltfMaterial.normalTexture->getExtension<CesiumGltf::ExtensionKhrTextureTransform>()
: nullptr;
if ( pNormalTextureTransform )
{
textureTransform = CesiumGltf::KhrTextureTransform( *pNormalTextureTransform );
if ( textureTransform.status() == CesiumGltf::KhrTextureTransformStatus::Valid )
{
const glm::dvec2 &scale = textureTransform.scale();
const glm::dvec2 &offset = textureTransform.offset();
material->set_uv1_scale( Vector3( scale[0], scale[1], 0 ) );
material->set_uv1_offset( Vector3( offset[0], offset[1], 0 ) );
// const glm::dvec2& rotationSineCosine =
// textureTransform.rotationSineCosine();
// unityMaterial.SetVector(
// materialProperties.getNormalMapTextureRotationID(),
// { static_cast<float>(rotationSineCosine[0]),
// static_cast<float>(rotationSineCosine[1]),
// 0.0f,
// 0.0f });
}
}
const CesiumGltf::ExtensionKhrTextureTransform *pEmissiveTextureTransform =
gltfMaterial.emissiveTexture
? gltfMaterial.emissiveTexture->getExtension<CesiumGltf::ExtensionKhrTextureTransform>()
: nullptr;
if ( pEmissiveTextureTransform )
{
textureTransform = CesiumGltf::KhrTextureTransform( *pEmissiveTextureTransform );
if ( textureTransform.status() == CesiumGltf::KhrTextureTransformStatus::Valid )
{
const glm::dvec2 &scale = textureTransform.scale();
const glm::dvec2 &offset = textureTransform.offset();
material->set_uv1_scale( Vector3( scale[0], scale[1], 0 ) );
material->set_uv1_offset( Vector3( offset[0], offset[1], 0 ) );
// const glm::dvec2& rotationSineCosine =
// textureTransform.rotationSineCosine();
// unityMaterial.SetVector(
// materialProperties.getEmissiveTextureRotationID(),
// { static_cast<float>(rotationSineCosine[0]),
// static_cast<float>(rotationSineCosine[1]),
// 0.0f,
// 0.0f });
}
}
const CesiumGltf::ExtensionKhrTextureTransform *pOcclusionTextureTransform =
gltfMaterial.occlusionTexture
? gltfMaterial.occlusionTexture
->getExtension<CesiumGltf::ExtensionKhrTextureTransform>()
: nullptr;
if ( pOcclusionTextureTransform )
{
textureTransform = CesiumGltf::KhrTextureTransform( *pOcclusionTextureTransform );
if ( textureTransform.status() == CesiumGltf::KhrTextureTransformStatus::Valid )
{
const glm::dvec2 &scale = textureTransform.scale();
const glm::dvec2 &offset = textureTransform.offset();
material->set_uv1_scale( Vector3( scale[0], scale[1], 0 ) );
material->set_uv1_offset( Vector3( offset[0], offset[1], 0 ) );
// const glm::dvec2& rotationSineCosine =
// textureTransform.rotationSineCosine();
// unityMaterial.SetVector(
// materialProperties.getOcclusionTextureRotationID(),
// { static_cast<float>(rotationSineCosine[0]),
// static_cast<float>(rotationSineCosine[1]),
// 0.0f,
// 0.0f });
}
}
}
Ref<godot::Image> loadImageFromCesiumImage( const CesiumGltf::ImageCesium &imageCesium, bool sRGB )
{
int32_t width = imageCesium.width;
int32_t height = imageCesium.height;
int32_t channels = imageCesium.channels;
godot::Image::Format format;
Ref<godot::Image> image;
switch ( channels )
{
case 1:
format = godot::Image::FORMAT_L8; // 灰度图
break;
case 2:
format = godot::Image::FORMAT_LA8; // 灰度+透明度
break;
case 3:
format = godot::Image::FORMAT_RGB8; // RGB
break;
case 4:
format = godot::Image::FORMAT_RGBA8; // RGBA
break;
default:
format = godot::Image::FORMAT_RGBA8;
break;
}
std::vector<uint8_t> pixelDataBuffer( width * height * channels * imageCesium.bytesPerChannel );
if ( imageCesium.mipPositions.empty() )
{
std::memcpy( pixelDataBuffer.data(), imageCesium.pixelData.data(),
imageCesium.pixelData.size() );
godot::PackedByteArray packedData;
packedData.resize( pixelDataBuffer.size() );
std::memcpy( packedData.ptrw(), pixelDataBuffer.data(), pixelDataBuffer.size() );
image.instantiate();
image->set_data( width, height, false, format, packedData );
}
else
{
size_t totalMipSize = 0;
for ( const auto &mip : imageCesium.mipPositions )
{
totalMipSize += mip.byteSize;
}
if ( totalMipSize > pixelDataBuffer.size() )
{
pixelDataBuffer.resize( totalMipSize, 0 );
}
uint8_t *writePos = pixelDataBuffer.data();
for ( const auto &mip : imageCesium.mipPositions )
{
std::memcpy( writePos, imageCesium.pixelData.data() + mip.byteOffset, mip.byteSize );
writePos += mip.byteSize;
}
godot::PackedByteArray packedData;
packedData.resize( imageCesium.mipPositions[0].byteSize );
std::memcpy( packedData.ptrw(), pixelDataBuffer.data(),
imageCesium.mipPositions[0].byteSize );
image.instantiate();
image->set_data( width, height, false, format, packedData );
}
return image;
}
Ref<godot::Texture> loadTexture( const CesiumGltf::Model &model, int32_t textureInfoIndex,
bool sRGB )
{
const CesiumGltf::Texture *pTexture = Model::getSafe( &model.textures, textureInfoIndex );
const CesiumGltf::Image *pImage = CesiumGltf::Model::getSafe( &model.images, pTexture->source );
if ( !pImage )
{
return Ref<godot::Texture>( nullptr );
}
const ImageCesium &imageCesium = pImage->cesium;
Ref<godot::Image> image = loadImageFromCesiumImage( imageCesium, sRGB );
if ( !image.is_valid() )
{
return Ref<godot::Texture>();
}
Ref<ImageTexture> godotTexture = ImageTexture::create_from_image( image );
return godotTexture;
}