2020-12-21 15:39:32 +00:00
/**************************************************************************/
/* gltf_document.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
# include "gltf_document.h"
2021-07-14 22:46:35 +00:00
2022-07-24 19:20:50 +00:00
# include "extensions/gltf_spec_gloss.h"
2024-02-16 13:25:15 +00:00
# include "gltf_state.h"
# include "skin_tool.h"
2020-12-21 15:39:32 +00:00
2023-01-31 07:37:55 +00:00
# include "core/config/project_settings.h"
2020-12-21 15:39:32 +00:00
# include "core/crypto/crypto_core.h"
2022-07-19 00:58:27 +00:00
# include "core/io/config_file.h"
2021-07-14 22:46:35 +00:00
# include "core/io/dir_access.h"
2021-06-11 12:51:48 +00:00
# include "core/io/file_access.h"
2021-10-19 16:06:23 +00:00
# include "core/io/file_access_memory.h"
2020-12-21 15:39:32 +00:00
# include "core/io/json.h"
2021-10-08 12:13:06 +00:00
# include "core/io/stream_peer.h"
2024-02-16 13:25:15 +00:00
# include "core/object/object_id.h"
2020-12-21 15:39:32 +00:00
# include "core/version.h"
2022-11-29 06:50:26 +00:00
# include "scene/3d/bone_attachment_3d.h"
# include "scene/3d/camera_3d.h"
# include "scene/3d/importer_mesh_instance_3d.h"
# include "scene/3d/light_3d.h"
2021-09-21 01:24:31 +00:00
# include "scene/3d/mesh_instance_3d.h"
2021-07-14 22:46:35 +00:00
# include "scene/3d/multimesh_instance_3d.h"
2021-08-13 16:42:45 +00:00
# include "scene/resources/3d/skin.h"
2023-07-11 20:29:09 +00:00
# include "scene/resources/image_texture.h"
# include "scene/resources/portable_compressed_texture.h"
2021-07-14 22:46:35 +00:00
# include "scene/resources/surface_tool.h"
2023-02-02 21:41:04 +00:00
# ifdef TOOLS_ENABLED
# include "editor/editor_file_system.h"
# endif
2021-07-14 22:46:35 +00:00
2022-03-28 13:39:24 +00:00
// FIXME: Hardcoded to avoid editor dependency.
2023-08-29 19:04:32 +00:00
# define GLTF_IMPORT_GENERATE_TANGENT_ARRAYS 8
2022-03-28 13:39:24 +00:00
# define GLTF_IMPORT_USE_NAMED_SKIN_BINDS 16
2022-04-13 12:14:26 +00:00
# define GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS 32
2023-08-29 19:04:32 +00:00
# define GLTF_IMPORT_FORCE_DISABLE_MESH_COMPRESSION 64
2022-03-28 13:39:24 +00:00
2021-07-14 22:46:35 +00:00
# include <stdio.h>
# include <stdlib.h>
2021-09-09 04:29:14 +00:00
# include <cstdint>
2020-12-21 15:39:32 +00:00
2024-07-10 08:02:36 +00:00
constexpr int COMPONENT_COUNT_FOR_ACCESSOR_TYPE [ 7 ] = {
1 , 2 , 3 , 4 , 4 , 9 , 16
} ;
2024-01-11 19:47:31 +00:00
static void _attach_extras_to_meta ( const Dictionary & p_extras , Ref < Resource > p_node ) {
if ( ! p_extras . is_empty ( ) ) {
p_node - > set_meta ( " extras " , p_extras ) ;
}
}
static void _attach_meta_to_extras ( Ref < Resource > p_node , Dictionary & p_json ) {
if ( p_node - > has_meta ( " extras " ) ) {
Dictionary node_extras = p_node - > get_meta ( " extras " ) ;
if ( p_json . has ( " extras " ) ) {
Dictionary extras = p_json [ " extras " ] ;
extras . merge ( node_extras ) ;
} else {
p_json [ " extras " ] = node_extras ;
}
}
}
2022-02-20 13:39:11 +00:00
static Ref < ImporterMesh > _mesh_to_importer_mesh ( Ref < Mesh > p_mesh ) {
Ref < ImporterMesh > importer_mesh ;
importer_mesh . instantiate ( ) ;
if ( p_mesh . is_null ( ) ) {
return importer_mesh ;
}
Ref < ArrayMesh > array_mesh = p_mesh ;
if ( p_mesh - > get_blend_shape_count ( ) ) {
ArrayMesh : : BlendShapeMode shape_mode = ArrayMesh : : BLEND_SHAPE_MODE_NORMALIZED ;
if ( array_mesh . is_valid ( ) ) {
shape_mode = array_mesh - > get_blend_shape_mode ( ) ;
}
importer_mesh - > set_blend_shape_mode ( shape_mode ) ;
for ( int morph_i = 0 ; morph_i < p_mesh - > get_blend_shape_count ( ) ; morph_i + + ) {
importer_mesh - > add_blend_shape ( p_mesh - > get_blend_shape_name ( morph_i ) ) ;
}
}
for ( int32_t surface_i = 0 ; surface_i < p_mesh - > get_surface_count ( ) ; surface_i + + ) {
Array array = p_mesh - > surface_get_arrays ( surface_i ) ;
Ref < Material > mat = p_mesh - > surface_get_material ( surface_i ) ;
String mat_name ;
if ( mat . is_valid ( ) ) {
mat_name = mat - > get_name ( ) ;
} else {
// Assign default material when no material is assigned.
2024-06-09 20:21:41 +00:00
mat . instantiate ( ) ;
2022-02-20 13:39:11 +00:00
}
importer_mesh - > add_surface ( p_mesh - > surface_get_primitive_type ( surface_i ) ,
array , p_mesh - > surface_get_blend_shape_arrays ( surface_i ) , p_mesh - > surface_get_lods ( surface_i ) , mat ,
mat_name , p_mesh - > surface_get_format ( surface_i ) ) ;
}
2024-01-11 19:47:31 +00:00
importer_mesh - > merge_meta_from ( * p_mesh ) ;
2022-02-20 13:39:11 +00:00
return importer_mesh ;
}
2023-07-18 19:42:22 +00:00
Error GLTFDocument : : _serialize ( Ref < GLTFState > p_state ) {
2023-07-18 16:15:58 +00:00
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
Error err = ext - > export_preserialize ( p_state ) ;
ERR_CONTINUE ( err ! = OK ) ;
}
2022-04-09 09:35:50 +00:00
/* STEP CONVERT MESH INSTANCES */
2022-12-10 21:05:13 +00:00
_convert_mesh_instances ( p_state ) ;
2020-12-21 15:39:32 +00:00
2022-04-09 09:35:50 +00:00
/* STEP SERIALIZE CAMERAS */
2022-12-10 21:05:13 +00:00
Error err = _serialize_cameras ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
/* STEP 3 CREATE SKINS */
2022-12-10 21:05:13 +00:00
err = _serialize_skins ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2021-09-04 01:00:59 +00:00
2022-04-09 09:35:50 +00:00
/* STEP SERIALIZE MESHES (we have enough info now) */
2022-12-10 21:05:13 +00:00
err = _serialize_meshes ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2022-04-09 09:35:50 +00:00
/* STEP SERIALIZE TEXTURES */
2022-12-10 21:05:13 +00:00
err = _serialize_materials ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2021-10-04 15:49:42 +00:00
/* STEP SERIALIZE TEXTURE SAMPLERS */
2022-12-10 21:05:13 +00:00
err = _serialize_texture_samplers ( p_state ) ;
2021-10-04 15:49:42 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2022-04-09 09:35:50 +00:00
/* STEP SERIALIZE ANIMATIONS */
2022-12-10 21:05:13 +00:00
err = _serialize_animations ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2022-04-09 09:35:50 +00:00
/* STEP SERIALIZE ACCESSORS */
2022-12-10 21:05:13 +00:00
err = _encode_accessors ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2022-04-09 09:35:50 +00:00
/* STEP SERIALIZE IMAGES */
2023-07-18 19:42:22 +00:00
err = _serialize_images ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2022-04-09 09:35:50 +00:00
/* STEP SERIALIZE TEXTURES */
2022-12-10 21:05:13 +00:00
err = _serialize_textures ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2022-12-10 21:05:13 +00:00
for ( GLTFBufferViewIndex i = 0 ; i < p_state - > buffer_views . size ( ) ; i + + ) {
p_state - > buffer_views . write [ i ] - > buffer = 0 ;
2020-12-21 15:39:32 +00:00
}
2022-04-09 09:35:50 +00:00
/* STEP SERIALIZE BUFFER VIEWS */
2022-12-10 21:05:13 +00:00
err = _encode_buffer_views ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2022-04-09 09:35:50 +00:00
/* STEP SERIALIZE NODES */
2022-12-10 21:05:13 +00:00
err = _serialize_nodes ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2022-04-09 09:35:50 +00:00
/* STEP SERIALIZE SCENE */
2022-12-10 21:05:13 +00:00
err = _serialize_scenes ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2022-09-18 03:16:22 +00:00
/* STEP SERIALIZE LIGHTS */
2022-12-10 21:05:13 +00:00
err = _serialize_lights ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2022-04-09 09:35:50 +00:00
/* STEP SERIALIZE EXTENSIONS */
2022-12-10 21:05:13 +00:00
err = _serialize_gltf_extensions ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2022-04-09 09:35:50 +00:00
/* STEP SERIALIZE VERSION */
2023-07-10 05:18:55 +00:00
err = _serialize_asset_header ( p_state ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return Error : : FAILED ;
}
2022-09-19 01:35:13 +00:00
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
2022-04-09 09:35:50 +00:00
ERR_CONTINUE ( ext . is_null ( ) ) ;
2022-12-10 21:05:13 +00:00
err = ext - > export_post ( p_state ) ;
2022-04-09 09:35:50 +00:00
ERR_FAIL_COND_V ( err ! = OK , err ) ;
}
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _serialize_gltf_extensions ( Ref < GLTFState > p_state ) const {
Vector < String > extensions_used = p_state - > extensions_used ;
Vector < String > extensions_required = p_state - > extensions_required ;
if ( ! p_state - > lights . is_empty ( ) ) {
2022-07-23 23:52:46 +00:00
extensions_used . push_back ( " KHR_lights_punctual " ) ;
}
2022-12-10 21:05:13 +00:00
if ( p_state - > use_khr_texture_transform ) {
2022-07-23 23:52:46 +00:00
extensions_used . push_back ( " KHR_texture_transform " ) ;
extensions_required . push_back ( " KHR_texture_transform " ) ;
}
if ( ! extensions_used . is_empty ( ) ) {
2022-09-18 03:06:23 +00:00
extensions_used . sort ( ) ;
2022-12-10 21:05:13 +00:00
p_state - > json [ " extensionsUsed " ] = extensions_used ;
2022-07-23 23:52:46 +00:00
}
if ( ! extensions_required . is_empty ( ) ) {
2022-09-18 03:06:23 +00:00
extensions_required . sort ( ) ;
2022-12-10 21:05:13 +00:00
p_state - > json [ " extensionsRequired " ] = extensions_required ;
2022-07-23 23:52:46 +00:00
}
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _serialize_scenes ( Ref < GLTFState > p_state ) {
2023-07-22 19:42:34 +00:00
// Godot only supports one scene per glTF file.
2020-12-21 15:39:32 +00:00
Array scenes ;
2023-07-22 19:42:34 +00:00
Dictionary scene_dict ;
scenes . append ( scene_dict ) ;
2022-12-10 21:05:13 +00:00
p_state - > json [ " scenes " ] = scenes ;
2023-07-22 19:42:34 +00:00
p_state - > json [ " scene " ] = 0 ;
// Add nodes to the scene dict.
2024-05-10 02:23:16 +00:00
if ( ! p_state - > root_nodes . is_empty ( ) ) {
scene_dict [ " nodes " ] = p_state - > root_nodes ;
}
2023-07-22 19:42:34 +00:00
if ( ! p_state - > scene_name . is_empty ( ) ) {
scene_dict [ " name " ] = p_state - > scene_name ;
}
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_json ( const String & p_path , Ref < GLTFState > p_state ) {
2020-12-21 15:39:32 +00:00
Error err ;
2022-12-10 21:05:13 +00:00
Ref < FileAccess > file = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
if ( file . is_null ( ) ) {
2020-12-21 15:39:32 +00:00
return err ;
}
Vector < uint8_t > array ;
2022-12-10 21:05:13 +00:00
array . resize ( file - > get_length ( ) ) ;
file - > get_buffer ( array . ptrw ( ) , array . size ( ) ) ;
2020-12-21 15:39:32 +00:00
String text ;
text . parse_utf8 ( ( const char * ) array . ptr ( ) , array . size ( ) ) ;
2020-12-29 18:12:33 +00:00
JSON json ;
err = json . parse ( text ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
2021-09-22 15:36:40 +00:00
_err_print_error ( " " , p_path . utf8 ( ) . get_data ( ) , json . get_error_line ( ) , json . get_error_message ( ) . utf8 ( ) . get_data ( ) , false , ERR_HANDLER_SCRIPT ) ;
2020-12-21 15:39:32 +00:00
return err ;
}
2022-12-10 21:05:13 +00:00
p_state - > json = json . get_data ( ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_glb ( Ref < FileAccess > p_file , Ref < GLTFState > p_state ) {
2024-07-26 09:52:26 +00:00
ERR_FAIL_COND_V ( p_file . is_null ( ) , ERR_INVALID_PARAMETER ) ;
ERR_FAIL_COND_V ( p_state . is_null ( ) , ERR_INVALID_PARAMETER ) ;
2022-12-10 21:05:13 +00:00
ERR_FAIL_COND_V ( p_file - > get_position ( ) ! = 0 , ERR_FILE_CANT_READ ) ;
uint32_t magic = p_file - > get_32 ( ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( magic ! = 0x46546C67 , ERR_FILE_UNRECOGNIZED ) ; //glTF
2022-12-10 21:05:13 +00:00
p_file - > get_32 ( ) ; // version
p_file - > get_32 ( ) ; // length
uint32_t chunk_length = p_file - > get_32 ( ) ;
uint32_t chunk_type = p_file - > get_32 ( ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( chunk_type ! = 0x4E4F534A , ERR_PARSE_ERROR ) ; //JSON
Vector < uint8_t > json_data ;
json_data . resize ( chunk_length ) ;
2022-12-10 21:05:13 +00:00
uint32_t len = p_file - > get_buffer ( json_data . ptrw ( ) , chunk_length ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( len ! = chunk_length , ERR_FILE_CORRUPT ) ;
String text ;
text . parse_utf8 ( ( const char * ) json_data . ptr ( ) , json_data . size ( ) ) ;
2020-12-29 18:12:33 +00:00
JSON json ;
2021-10-19 16:06:23 +00:00
Error err = json . parse ( text ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
2021-10-19 16:06:23 +00:00
_err_print_error ( " " , " " , json . get_error_line ( ) , json . get_error_message ( ) . utf8 ( ) . get_data ( ) , false , ERR_HANDLER_SCRIPT ) ;
2020-12-21 15:39:32 +00:00
return err ;
}
2022-12-10 21:05:13 +00:00
p_state - > json = json . get_data ( ) ;
2020-12-21 15:39:32 +00:00
//data?
2022-12-10 21:05:13 +00:00
chunk_length = p_file - > get_32 ( ) ;
chunk_type = p_file - > get_32 ( ) ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
if ( p_file - > eof_reached ( ) ) {
2020-12-21 15:39:32 +00:00
return OK ; //all good
}
ERR_FAIL_COND_V ( chunk_type ! = 0x004E4942 , ERR_PARSE_ERROR ) ; //BIN
2022-12-10 21:05:13 +00:00
p_state - > glb_data . resize ( chunk_length ) ;
len = p_file - > get_buffer ( p_state - > glb_data . ptrw ( ) , chunk_length ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( len ! = chunk_length , ERR_FILE_CORRUPT ) ;
return OK ;
}
static Array _vec3_to_arr ( const Vector3 & p_vec3 ) {
Array array ;
array . resize ( 3 ) ;
array [ 0 ] = p_vec3 . x ;
array [ 1 ] = p_vec3 . y ;
array [ 2 ] = p_vec3 . z ;
return array ;
}
static Vector3 _arr_to_vec3 ( const Array & p_array ) {
ERR_FAIL_COND_V ( p_array . size ( ) ! = 3 , Vector3 ( ) ) ;
return Vector3 ( p_array [ 0 ] , p_array [ 1 ] , p_array [ 2 ] ) ;
}
2021-01-20 07:02:02 +00:00
static Array _quaternion_to_array ( const Quaternion & p_quaternion ) {
2020-12-21 15:39:32 +00:00
Array array ;
array . resize ( 4 ) ;
2021-01-20 07:02:02 +00:00
array [ 0 ] = p_quaternion . x ;
array [ 1 ] = p_quaternion . y ;
array [ 2 ] = p_quaternion . z ;
array [ 3 ] = p_quaternion . w ;
2020-12-21 15:39:32 +00:00
return array ;
}
2021-01-20 07:02:02 +00:00
static Quaternion _arr_to_quaternion ( const Array & p_array ) {
ERR_FAIL_COND_V ( p_array . size ( ) ! = 4 , Quaternion ( ) ) ;
return Quaternion ( p_array [ 0 ] , p_array [ 1 ] , p_array [ 2 ] , p_array [ 3 ] ) ;
2020-12-21 15:39:32 +00:00
}
2020-10-17 05:08:21 +00:00
static Transform3D _arr_to_xform ( const Array & p_array ) {
ERR_FAIL_COND_V ( p_array . size ( ) ! = 16 , Transform3D ( ) ) ;
2020-12-21 15:39:32 +00:00
2020-10-17 05:08:21 +00:00
Transform3D xform ;
2022-05-03 12:50:35 +00:00
xform . basis . set_column ( Vector3 : : AXIS_X , Vector3 ( p_array [ 0 ] , p_array [ 1 ] , p_array [ 2 ] ) ) ;
xform . basis . set_column ( Vector3 : : AXIS_Y , Vector3 ( p_array [ 4 ] , p_array [ 5 ] , p_array [ 6 ] ) ) ;
xform . basis . set_column ( Vector3 : : AXIS_Z , Vector3 ( p_array [ 8 ] , p_array [ 9 ] , p_array [ 10 ] ) ) ;
2020-12-21 15:39:32 +00:00
xform . set_origin ( Vector3 ( p_array [ 12 ] , p_array [ 13 ] , p_array [ 14 ] ) ) ;
return xform ;
}
2020-10-17 05:08:21 +00:00
static Vector < real_t > _xform_to_array ( const Transform3D p_transform ) {
2020-12-21 15:39:32 +00:00
Vector < real_t > array ;
array . resize ( 16 ) ;
2022-05-03 12:50:35 +00:00
Vector3 axis_x = p_transform . get_basis ( ) . get_column ( Vector3 : : AXIS_X ) ;
2020-12-21 15:39:32 +00:00
array . write [ 0 ] = axis_x . x ;
array . write [ 1 ] = axis_x . y ;
array . write [ 2 ] = axis_x . z ;
array . write [ 3 ] = 0.0f ;
2022-05-03 12:50:35 +00:00
Vector3 axis_y = p_transform . get_basis ( ) . get_column ( Vector3 : : AXIS_Y ) ;
2020-12-21 15:39:32 +00:00
array . write [ 4 ] = axis_y . x ;
array . write [ 5 ] = axis_y . y ;
array . write [ 6 ] = axis_y . z ;
array . write [ 7 ] = 0.0f ;
2022-05-03 12:50:35 +00:00
Vector3 axis_z = p_transform . get_basis ( ) . get_column ( Vector3 : : AXIS_Z ) ;
2020-12-21 15:39:32 +00:00
array . write [ 8 ] = axis_z . x ;
array . write [ 9 ] = axis_z . y ;
array . write [ 10 ] = axis_z . z ;
array . write [ 11 ] = 0.0f ;
Vector3 origin = p_transform . get_origin ( ) ;
array . write [ 12 ] = origin . x ;
array . write [ 13 ] = origin . y ;
array . write [ 14 ] = origin . z ;
array . write [ 15 ] = 1.0f ;
return array ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _serialize_nodes ( Ref < GLTFState > p_state ) {
2020-12-21 15:39:32 +00:00
Array nodes ;
2022-12-10 21:05:13 +00:00
for ( int i = 0 ; i < p_state - > nodes . size ( ) ; i + + ) {
2020-12-21 15:39:32 +00:00
Dictionary node ;
2022-12-10 21:05:13 +00:00
Ref < GLTFNode > gltf_node = p_state - > nodes [ i ] ;
2020-12-21 15:39:32 +00:00
Dictionary extensions ;
node [ " extensions " ] = extensions ;
2022-09-18 03:16:22 +00:00
if ( ! gltf_node - > get_name ( ) . is_empty ( ) ) {
node [ " name " ] = gltf_node - > get_name ( ) ;
2020-12-21 15:39:32 +00:00
}
2022-09-18 03:16:22 +00:00
if ( gltf_node - > camera ! = - 1 ) {
node [ " camera " ] = gltf_node - > camera ;
2020-12-21 15:39:32 +00:00
}
2022-09-18 03:16:22 +00:00
if ( gltf_node - > light ! = - 1 ) {
2020-12-21 15:39:32 +00:00
Dictionary lights_punctual ;
extensions [ " KHR_lights_punctual " ] = lights_punctual ;
2022-09-18 03:16:22 +00:00
lights_punctual [ " light " ] = gltf_node - > light ;
2020-12-21 15:39:32 +00:00
}
2022-09-18 03:16:22 +00:00
if ( gltf_node - > mesh ! = - 1 ) {
node [ " mesh " ] = gltf_node - > mesh ;
2020-12-21 15:39:32 +00:00
}
2022-09-18 03:16:22 +00:00
if ( gltf_node - > skin ! = - 1 ) {
node [ " skin " ] = gltf_node - > skin ;
2020-12-21 15:39:32 +00:00
}
2022-09-18 03:16:22 +00:00
if ( gltf_node - > skeleton ! = - 1 & & gltf_node - > skin < 0 ) {
2020-12-21 15:39:32 +00:00
}
2023-10-12 22:37:03 +00:00
if ( gltf_node - > transform . basis . is_orthogonal ( ) ) {
// An orthogonal transform is decomposable into TRS, so prefer that.
const Vector3 position = gltf_node - > get_position ( ) ;
if ( ! position . is_zero_approx ( ) ) {
node [ " translation " ] = _vec3_to_arr ( position ) ;
}
const Quaternion rotation = gltf_node - > get_rotation ( ) ;
if ( ! rotation . is_equal_approx ( Quaternion ( ) ) ) {
node [ " rotation " ] = _quaternion_to_array ( rotation ) ;
}
const Vector3 scale = gltf_node - > get_scale ( ) ;
if ( ! scale . is_equal_approx ( Vector3 ( 1.0f , 1.0f , 1.0f ) ) ) {
node [ " scale " ] = _vec3_to_arr ( scale ) ;
}
} else {
node [ " matrix " ] = _xform_to_array ( gltf_node - > transform ) ;
2020-12-21 15:39:32 +00:00
}
2022-09-18 03:16:22 +00:00
if ( gltf_node - > children . size ( ) ) {
2020-12-21 15:39:32 +00:00
Array children ;
2022-09-18 03:16:22 +00:00
for ( int j = 0 ; j < gltf_node - > children . size ( ) ; j + + ) {
children . push_back ( gltf_node - > children [ j ] ) ;
2020-12-21 15:39:32 +00:00
}
node [ " children " ] = children ;
}
2022-04-09 09:35:50 +00:00
2024-01-10 22:08:25 +00:00
Node * scene_node = nullptr ;
2024-09-02 08:28:07 +00:00
if ( i < ( int ) p_state - > scene_nodes . size ( ) ) {
2024-01-10 22:08:25 +00:00
scene_node = p_state - > scene_nodes [ i ] ;
}
2022-09-19 01:35:13 +00:00
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
2022-04-09 09:35:50 +00:00
ERR_CONTINUE ( ext . is_null ( ) ) ;
2024-01-10 22:08:25 +00:00
Error err = ext - > export_node ( p_state , gltf_node , node , scene_node ) ;
2022-04-09 09:35:50 +00:00
ERR_CONTINUE ( err ! = OK ) ;
}
2024-05-10 02:23:16 +00:00
if ( extensions . is_empty ( ) ) {
node . erase ( " extensions " ) ;
}
2024-01-11 19:47:31 +00:00
_attach_meta_to_extras ( gltf_node , node ) ;
2020-12-21 15:39:32 +00:00
nodes . push_back ( node ) ;
}
2024-05-10 02:23:16 +00:00
if ( ! nodes . is_empty ( ) ) {
p_state - > json [ " nodes " ] = nodes ;
}
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
String GLTFDocument : : _gen_unique_name ( Ref < GLTFState > p_state , const String & p_name ) {
2024-02-16 13:25:15 +00:00
return _gen_unique_name_static ( p_state - > unique_names , p_name ) ;
2020-12-21 15:39:32 +00:00
}
2021-01-28 20:48:12 +00:00
String GLTFDocument : : _sanitize_animation_name ( const String & p_name ) {
2022-09-29 09:53:28 +00:00
String anim_name = p_name . validate_node_name ( ) ;
2024-07-26 08:04:56 +00:00
return AnimationLibrary : : validate_library_name ( anim_name ) ;
2021-01-28 20:48:12 +00:00
}
2022-12-10 21:05:13 +00:00
String GLTFDocument : : _gen_unique_animation_name ( Ref < GLTFState > p_state , const String & p_name ) {
2021-01-28 20:48:12 +00:00
const String s_name = _sanitize_animation_name ( p_name ) ;
2022-09-29 09:53:28 +00:00
String u_name ;
2021-01-28 20:48:12 +00:00
int index = 1 ;
while ( true ) {
2022-09-29 09:53:28 +00:00
u_name = s_name ;
2021-01-28 20:48:12 +00:00
if ( index > 1 ) {
2022-09-29 09:53:28 +00:00
u_name + = itos ( index ) ;
2021-01-28 20:48:12 +00:00
}
2022-12-10 21:05:13 +00:00
if ( ! p_state - > unique_animation_names . has ( u_name ) ) {
2021-01-28 20:48:12 +00:00
break ;
}
index + + ;
}
2022-12-10 21:05:13 +00:00
p_state - > unique_animation_names . insert ( u_name ) ;
2021-01-28 20:48:12 +00:00
2022-09-29 09:53:28 +00:00
return u_name ;
2021-01-28 20:48:12 +00:00
}
2021-03-16 19:18:32 +00:00
String GLTFDocument : : _sanitize_bone_name ( const String & p_name ) {
2022-09-29 09:53:28 +00:00
String bone_name = p_name ;
bone_name = bone_name . replace ( " : " , " _ " ) ;
bone_name = bone_name . replace ( " / " , " _ " ) ;
return bone_name ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
String GLTFDocument : : _gen_unique_bone_name ( Ref < GLTFState > p_state , const GLTFSkeletonIndex p_skel_i , const String & p_name ) {
2020-12-21 15:39:32 +00:00
String s_name = _sanitize_bone_name ( p_name ) ;
2020-12-15 12:04:21 +00:00
if ( s_name . is_empty ( ) ) {
2020-12-21 15:39:32 +00:00
s_name = " bone " ;
}
2022-09-29 09:53:28 +00:00
String u_name ;
2020-12-21 15:39:32 +00:00
int index = 1 ;
while ( true ) {
2022-09-29 09:53:28 +00:00
u_name = s_name ;
2020-12-21 15:39:32 +00:00
if ( index > 1 ) {
2022-09-29 09:53:28 +00:00
u_name + = " _ " + itos ( index ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
if ( ! p_state - > skeletons [ p_skel_i ] - > unique_names . has ( u_name ) ) {
2020-12-21 15:39:32 +00:00
break ;
}
index + + ;
}
2022-12-10 21:05:13 +00:00
p_state - > skeletons . write [ p_skel_i ] - > unique_names . insert ( u_name ) ;
2020-12-21 15:39:32 +00:00
2022-09-29 09:53:28 +00:00
return u_name ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_scenes ( Ref < GLTFState > p_state ) {
2023-01-26 17:14:07 +00:00
p_state - > unique_names . insert ( " Skeleton3D " ) ; // Reserve skeleton name.
2022-12-10 21:05:13 +00:00
ERR_FAIL_COND_V ( ! p_state - > json . has ( " scenes " ) , ERR_FILE_CORRUPT ) ;
const Array & scenes = p_state - > json [ " scenes " ] ;
2020-12-21 15:39:32 +00:00
int loaded_scene = 0 ;
2022-12-10 21:05:13 +00:00
if ( p_state - > json . has ( " scene " ) ) {
loaded_scene = p_state - > json [ " scene " ] ;
2020-12-21 15:39:32 +00:00
} else {
WARN_PRINT ( " The load-time scene is not defined in the glTF2 file. Picking the first scene. " ) ;
}
if ( scenes . size ( ) ) {
ERR_FAIL_COND_V ( loaded_scene > = scenes . size ( ) , ERR_FILE_CORRUPT ) ;
2023-07-23 02:17:28 +00:00
const Dictionary & scene_dict = scenes [ loaded_scene ] ;
ERR_FAIL_COND_V ( ! scene_dict . has ( " nodes " ) , ERR_UNAVAILABLE ) ;
const Array & nodes = scene_dict [ " nodes " ] ;
2020-12-21 15:39:32 +00:00
for ( int j = 0 ; j < nodes . size ( ) ; j + + ) {
2022-12-10 21:05:13 +00:00
p_state - > root_nodes . push_back ( nodes [ j ] ) ;
2020-12-21 15:39:32 +00:00
}
2023-07-23 02:17:28 +00:00
// Determine what to use for the scene name.
if ( scene_dict . has ( " name " ) & & ! String ( scene_dict [ " name " ] ) . is_empty ( ) & & ! ( ( String ) scene_dict [ " name " ] ) . begins_with ( " Scene " ) ) {
p_state - > scene_name = scene_dict [ " name " ] ;
2023-11-09 16:15:50 +00:00
} else if ( p_state - > scene_name . is_empty ( ) ) {
2023-07-23 02:17:28 +00:00
p_state - > scene_name = p_state - > filename ;
2020-12-21 15:39:32 +00:00
}
2023-10-31 22:55:15 +00:00
if ( _naming_version = = 0 ) {
p_state - > scene_name = _gen_unique_name ( p_state , p_state - > scene_name ) ;
}
2020-12-21 15:39:32 +00:00
}
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_nodes ( Ref < GLTFState > p_state ) {
ERR_FAIL_COND_V ( ! p_state - > json . has ( " nodes " ) , ERR_FILE_CORRUPT ) ;
const Array & nodes = p_state - > json [ " nodes " ] ;
2020-12-21 15:39:32 +00:00
for ( int i = 0 ; i < nodes . size ( ) ; i + + ) {
Ref < GLTFNode > node ;
2021-06-17 22:03:09 +00:00
node . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
const Dictionary & n = nodes [ i ] ;
if ( n . has ( " name " ) ) {
2024-02-16 13:25:15 +00:00
node - > set_original_name ( n [ " name " ] ) ;
2020-12-21 15:39:32 +00:00
node - > set_name ( n [ " name " ] ) ;
}
if ( n . has ( " camera " ) ) {
node - > camera = n [ " camera " ] ;
}
if ( n . has ( " mesh " ) ) {
node - > mesh = n [ " mesh " ] ;
}
if ( n . has ( " skin " ) ) {
node - > skin = n [ " skin " ] ;
}
if ( n . has ( " matrix " ) ) {
2023-10-12 22:37:03 +00:00
node - > transform = _arr_to_xform ( n [ " matrix " ] ) ;
2020-12-21 15:39:32 +00:00
} else {
if ( n . has ( " translation " ) ) {
2023-10-12 22:37:03 +00:00
node - > set_position ( _arr_to_vec3 ( n [ " translation " ] ) ) ;
2020-12-21 15:39:32 +00:00
}
if ( n . has ( " rotation " ) ) {
2023-10-12 22:37:03 +00:00
node - > set_rotation ( _arr_to_quaternion ( n [ " rotation " ] ) ) ;
2020-12-21 15:39:32 +00:00
}
if ( n . has ( " scale " ) ) {
2023-10-12 22:37:03 +00:00
node - > set_scale ( _arr_to_vec3 ( n [ " scale " ] ) ) ;
2020-12-21 15:39:32 +00:00
}
}
2024-10-07 16:28:57 +00:00
node - > set_additional_data ( " GODOT_rest_transform " , node - > transform ) ;
2020-12-21 15:39:32 +00:00
if ( n . has ( " extensions " ) ) {
Dictionary extensions = n [ " extensions " ] ;
if ( extensions . has ( " KHR_lights_punctual " ) ) {
Dictionary lights_punctual = extensions [ " KHR_lights_punctual " ] ;
if ( lights_punctual . has ( " light " ) ) {
GLTFLightIndex light = lights_punctual [ " light " ] ;
node - > light = light ;
}
}
2022-09-17 22:48:06 +00:00
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
2022-12-10 21:05:13 +00:00
Error err = ext - > parse_node_extensions ( p_state , node , extensions ) ;
2024-08-16 01:07:30 +00:00
ERR_CONTINUE_MSG ( err ! = OK , " glTF: Encountered error " + itos ( err ) + " when parsing node extensions for node " + node - > get_name ( ) + " in file " + p_state - > filename + " . Continuing. " ) ;
2022-09-17 22:48:06 +00:00
}
2020-12-21 15:39:32 +00:00
}
2024-01-11 19:47:31 +00:00
if ( n . has ( " extras " ) ) {
_attach_extras_to_meta ( n [ " extras " ] , node ) ;
}
2020-12-21 15:39:32 +00:00
if ( n . has ( " children " ) ) {
const Array & children = n [ " children " ] ;
for ( int j = 0 ; j < children . size ( ) ; j + + ) {
node - > children . push_back ( children [ j ] ) ;
}
}
2022-12-10 21:05:13 +00:00
p_state - > nodes . push_back ( node ) ;
2020-12-21 15:39:32 +00:00
}
// build the hierarchy
2022-12-10 21:05:13 +00:00
for ( GLTFNodeIndex node_i = 0 ; node_i < p_state - > nodes . size ( ) ; node_i + + ) {
for ( int j = 0 ; j < p_state - > nodes [ node_i ] - > children . size ( ) ; j + + ) {
GLTFNodeIndex child_i = p_state - > nodes [ node_i ] - > children [ j ] ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
ERR_FAIL_INDEX_V ( child_i , p_state - > nodes . size ( ) , ERR_FILE_CORRUPT ) ;
ERR_CONTINUE ( p_state - > nodes [ child_i ] - > parent ! = - 1 ) ; //node already has a parent, wtf.
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
p_state - > nodes . write [ child_i ] - > parent = node_i ;
2020-12-21 15:39:32 +00:00
}
}
2022-12-10 21:05:13 +00:00
_compute_node_heights ( p_state ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _compute_node_heights ( Ref < GLTFState > p_state ) {
p_state - > root_nodes . clear ( ) ;
for ( GLTFNodeIndex node_i = 0 ; node_i < p_state - > nodes . size ( ) ; + + node_i ) {
Ref < GLTFNode > node = p_state - > nodes [ node_i ] ;
2020-12-21 15:39:32 +00:00
node - > height = 0 ;
GLTFNodeIndex current_i = node_i ;
while ( current_i > = 0 ) {
2022-12-10 21:05:13 +00:00
const GLTFNodeIndex parent_i = p_state - > nodes [ current_i ] - > parent ;
2020-12-21 15:39:32 +00:00
if ( parent_i > = 0 ) {
+ + node - > height ;
}
current_i = parent_i ;
}
if ( node - > height = = 0 ) {
2022-12-10 21:05:13 +00:00
p_state - > root_nodes . push_back ( node_i ) ;
2020-12-21 15:39:32 +00:00
}
}
}
2023-05-10 02:28:40 +00:00
static Vector < uint8_t > _parse_base64_uri ( const String & p_uri ) {
int start = p_uri . find ( " , " ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( start = = - 1 , Vector < uint8_t > ( ) ) ;
2023-05-10 02:28:40 +00:00
CharString substr = p_uri . substr ( start + 1 ) . ascii ( ) ;
2020-12-21 15:39:32 +00:00
int strlen = substr . length ( ) ;
Vector < uint8_t > buf ;
buf . resize ( strlen / 4 * 3 + 1 + 1 ) ;
size_t len = 0 ;
ERR_FAIL_COND_V ( CryptoCore : : b64_decode ( buf . ptrw ( ) , buf . size ( ) , & len , ( unsigned char * ) substr . get_data ( ) , strlen ) ! = OK , Vector < uint8_t > ( ) ) ;
buf . resize ( len ) ;
return buf ;
}
2023-05-10 02:28:40 +00:00
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _encode_buffer_glb ( Ref < GLTFState > p_state , const String & p_path ) {
print_verbose ( " glTF: Total buffers: " + itos ( p_state - > buffers . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . is_empty ( ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
}
Array buffers ;
2024-05-10 02:23:16 +00:00
if ( ! p_state - > buffers . is_empty ( ) ) {
2022-12-10 21:05:13 +00:00
Vector < uint8_t > buffer_data = p_state - > buffers [ 0 ] ;
2020-12-21 15:39:32 +00:00
Dictionary gltf_buffer ;
gltf_buffer [ " byteLength " ] = buffer_data . size ( ) ;
buffers . push_back ( gltf_buffer ) ;
}
2022-12-10 21:05:13 +00:00
for ( GLTFBufferIndex i = 1 ; i < p_state - > buffers . size ( ) - 1 ; i + + ) {
Vector < uint8_t > buffer_data = p_state - > buffers [ i ] ;
2020-12-21 15:39:32 +00:00
Dictionary gltf_buffer ;
String filename = p_path . get_basename ( ) . get_file ( ) + itos ( i ) + " .bin " ;
String path = p_path . get_base_dir ( ) + " / " + filename ;
Error err ;
2022-12-10 21:05:13 +00:00
Ref < FileAccess > file = FileAccess : : open ( path , FileAccess : : WRITE , & err ) ;
if ( file . is_null ( ) ) {
2020-12-21 15:39:32 +00:00
return err ;
}
if ( buffer_data . size ( ) = = 0 ) {
return OK ;
}
2022-12-10 21:05:13 +00:00
file - > create ( FileAccess : : ACCESS_RESOURCES ) ;
file - > store_buffer ( buffer_data . ptr ( ) , buffer_data . size ( ) ) ;
2020-12-21 15:39:32 +00:00
gltf_buffer [ " uri " ] = filename ;
gltf_buffer [ " byteLength " ] = buffer_data . size ( ) ;
buffers . push_back ( gltf_buffer ) ;
}
2022-12-10 21:05:13 +00:00
p_state - > json [ " buffers " ] = buffers ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _encode_buffer_bins ( Ref < GLTFState > p_state , const String & p_path ) {
print_verbose ( " glTF: Total buffers: " + itos ( p_state - > buffers . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . is_empty ( ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
}
Array buffers ;
2022-12-10 21:05:13 +00:00
for ( GLTFBufferIndex i = 0 ; i < p_state - > buffers . size ( ) ; i + + ) {
Vector < uint8_t > buffer_data = p_state - > buffers [ i ] ;
2020-12-21 15:39:32 +00:00
Dictionary gltf_buffer ;
String filename = p_path . get_basename ( ) . get_file ( ) + itos ( i ) + " .bin " ;
String path = p_path . get_base_dir ( ) + " / " + filename ;
Error err ;
2022-12-10 21:05:13 +00:00
Ref < FileAccess > file = FileAccess : : open ( path , FileAccess : : WRITE , & err ) ;
if ( file . is_null ( ) ) {
2020-12-21 15:39:32 +00:00
return err ;
}
if ( buffer_data . size ( ) = = 0 ) {
return OK ;
}
2022-12-10 21:05:13 +00:00
file - > create ( FileAccess : : ACCESS_RESOURCES ) ;
file - > store_buffer ( buffer_data . ptr ( ) , buffer_data . size ( ) ) ;
2020-12-21 15:39:32 +00:00
gltf_buffer [ " uri " ] = filename ;
gltf_buffer [ " byteLength " ] = buffer_data . size ( ) ;
buffers . push_back ( gltf_buffer ) ;
}
2022-12-10 21:05:13 +00:00
p_state - > json [ " buffers " ] = buffers ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_buffers ( Ref < GLTFState > p_state , const String & p_base_path ) {
if ( ! p_state - > json . has ( " buffers " ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
const Array & buffers = p_state - > json [ " buffers " ] ;
2020-12-21 15:39:32 +00:00
for ( GLTFBufferIndex i = 0 ; i < buffers . size ( ) ; i + + ) {
2022-12-10 21:05:13 +00:00
if ( i = = 0 & & p_state - > glb_data . size ( ) ) {
p_state - > buffers . push_back ( p_state - > glb_data ) ;
2020-12-21 15:39:32 +00:00
} else {
const Dictionary & buffer = buffers [ i ] ;
if ( buffer . has ( " uri " ) ) {
Vector < uint8_t > buffer_data ;
String uri = buffer [ " uri " ] ;
if ( uri . begins_with ( " data: " ) ) { // Embedded data using base64.
// Validate data MIME types and throw an error if it's one we don't know/support.
if ( ! uri . begins_with ( " data:application/octet-stream;base64 " ) & &
! uri . begins_with ( " data:application/gltf-buffer;base64 " ) ) {
ERR_PRINT ( " glTF: Got buffer with an unknown URI data type: " + uri ) ;
}
buffer_data = _parse_base64_uri ( uri ) ;
} else { // Relative path to an external image file.
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( p_base_path . is_empty ( ) , ERR_INVALID_PARAMETER ) ;
uri = uri . uri_decode ( ) ;
2022-08-30 00:34:01 +00:00
uri = p_base_path . path_join ( uri ) . replace ( " \\ " , " / " ) ; // Fix for Windows.
2024-07-18 11:04:16 +00:00
ERR_FAIL_COND_V_MSG ( ! FileAccess : : exists ( uri ) , ERR_FILE_NOT_FOUND , " glTF: Binary file not found: " + uri ) ;
2022-11-21 13:04:01 +00:00
buffer_data = FileAccess : : get_file_as_bytes ( uri ) ;
2024-07-18 11:04:16 +00:00
ERR_FAIL_COND_V_MSG ( buffer_data . is_empty ( ) , ERR_PARSE_ERROR , " glTF: Couldn't load binary file as an array: " + uri ) ;
2020-12-21 15:39:32 +00:00
}
ERR_FAIL_COND_V ( ! buffer . has ( " byteLength " ) , ERR_PARSE_ERROR ) ;
int byteLength = buffer [ " byteLength " ] ;
ERR_FAIL_COND_V ( byteLength < buffer_data . size ( ) , ERR_PARSE_ERROR ) ;
2022-12-10 21:05:13 +00:00
p_state - > buffers . push_back ( buffer_data ) ;
2020-12-21 15:39:32 +00:00
}
}
}
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: Total buffers: " + itos ( p_state - > buffers . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _encode_buffer_views ( Ref < GLTFState > p_state ) {
2020-12-21 15:39:32 +00:00
Array buffers ;
2022-12-10 21:05:13 +00:00
for ( GLTFBufferViewIndex i = 0 ; i < p_state - > buffer_views . size ( ) ; i + + ) {
2020-12-21 15:39:32 +00:00
Dictionary d ;
2022-12-10 21:05:13 +00:00
Ref < GLTFBufferView > buffer_view = p_state - > buffer_views [ i ] ;
2020-12-21 15:39:32 +00:00
d [ " buffer " ] = buffer_view - > buffer ;
d [ " byteLength " ] = buffer_view - > byte_length ;
d [ " byteOffset " ] = buffer_view - > byte_offset ;
if ( buffer_view - > byte_stride ! = - 1 ) {
d [ " byteStride " ] = buffer_view - > byte_stride ;
}
2024-03-10 07:51:52 +00:00
if ( buffer_view - > indices ) {
d [ " target " ] = GLTFDocument : : ELEMENT_ARRAY_BUFFER ;
} else if ( buffer_view - > vertex_attributes ) {
d [ " target " ] = GLTFDocument : : ARRAY_BUFFER ;
}
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( ! d . has ( " buffer " ) , ERR_INVALID_DATA ) ;
ERR_FAIL_COND_V ( ! d . has ( " byteLength " ) , ERR_INVALID_DATA ) ;
buffers . push_back ( d ) ;
}
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: Total buffer views: " + itos ( p_state - > buffer_views . size ( ) ) ) ;
2021-11-02 00:31:59 +00:00
if ( ! buffers . size ( ) ) {
return OK ;
}
2022-12-10 21:05:13 +00:00
p_state - > json [ " bufferViews " ] = buffers ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_buffer_views ( Ref < GLTFState > p_state ) {
if ( ! p_state - > json . has ( " bufferViews " ) ) {
2021-01-14 18:13:47 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
const Array & buffers = p_state - > json [ " bufferViews " ] ;
2020-12-21 15:39:32 +00:00
for ( GLTFBufferViewIndex i = 0 ; i < buffers . size ( ) ; i + + ) {
const Dictionary & d = buffers [ i ] ;
Ref < GLTFBufferView > buffer_view ;
2021-06-17 22:03:09 +00:00
buffer_view . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( ! d . has ( " buffer " ) , ERR_PARSE_ERROR ) ;
buffer_view - > buffer = d [ " buffer " ] ;
ERR_FAIL_COND_V ( ! d . has ( " byteLength " ) , ERR_PARSE_ERROR ) ;
buffer_view - > byte_length = d [ " byteLength " ] ;
if ( d . has ( " byteOffset " ) ) {
buffer_view - > byte_offset = d [ " byteOffset " ] ;
}
if ( d . has ( " byteStride " ) ) {
buffer_view - > byte_stride = d [ " byteStride " ] ;
}
if ( d . has ( " target " ) ) {
const int target = d [ " target " ] ;
buffer_view - > indices = target = = GLTFDocument : : ELEMENT_ARRAY_BUFFER ;
2024-03-10 07:51:52 +00:00
buffer_view - > vertex_attributes = target = = GLTFDocument : : ARRAY_BUFFER ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
p_state - > buffer_views . push_back ( buffer_view ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: Total buffer views: " + itos ( p_state - > buffer_views . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _encode_accessors ( Ref < GLTFState > p_state ) {
2020-12-21 15:39:32 +00:00
Array accessors ;
2022-12-10 21:05:13 +00:00
for ( GLTFAccessorIndex i = 0 ; i < p_state - > accessors . size ( ) ; i + + ) {
2020-12-21 15:39:32 +00:00
Dictionary d ;
2022-12-10 21:05:13 +00:00
Ref < GLTFAccessor > accessor = p_state - > accessors [ i ] ;
2020-12-21 15:39:32 +00:00
d [ " componentType " ] = accessor - > component_type ;
d [ " count " ] = accessor - > count ;
2024-04-12 04:29:51 +00:00
d [ " type " ] = _get_accessor_type_name ( accessor - > accessor_type ) ;
2020-12-28 06:29:35 +00:00
d [ " normalized " ] = accessor - > normalized ;
2020-12-21 15:39:32 +00:00
d [ " max " ] = accessor - > max ;
d [ " min " ] = accessor - > min ;
2024-03-01 21:07:07 +00:00
if ( accessor - > buffer_view ! = - 1 ) {
// bufferView may be omitted to zero-initialize the buffer. When this happens, byteOffset MUST also be omitted.
d [ " byteOffset " ] = accessor - > byte_offset ;
d [ " bufferView " ] = accessor - > buffer_view ;
}
2020-12-21 15:39:32 +00:00
2024-03-01 21:07:07 +00:00
if ( accessor - > sparse_count > 0 ) {
Dictionary s ;
s [ " count " ] = accessor - > sparse_count ;
2020-12-21 15:39:32 +00:00
2024-03-01 21:07:07 +00:00
Dictionary si ;
si [ " bufferView " ] = accessor - > sparse_indices_buffer_view ;
si [ " componentType " ] = accessor - > sparse_indices_component_type ;
if ( accessor - > sparse_indices_byte_offset ! = - 1 ) {
si [ " byteOffset " ] = accessor - > sparse_indices_byte_offset ;
}
ERR_FAIL_COND_V ( ! si . has ( " bufferView " ) | | ! si . has ( " componentType " ) , ERR_PARSE_ERROR ) ;
s [ " indices " ] = si ;
2020-12-21 15:39:32 +00:00
2024-03-01 21:07:07 +00:00
Dictionary sv ;
sv [ " bufferView " ] = accessor - > sparse_values_buffer_view ;
if ( accessor - > sparse_values_byte_offset ! = - 1 ) {
sv [ " byteOffset " ] = accessor - > sparse_values_byte_offset ;
}
ERR_FAIL_COND_V ( ! sv . has ( " bufferView " ) , ERR_PARSE_ERROR ) ;
s [ " values " ] = sv ;
2020-12-21 15:39:32 +00:00
2024-03-01 21:07:07 +00:00
ERR_FAIL_COND_V ( ! s . has ( " count " ) | | ! s . has ( " indices " ) | | ! s . has ( " values " ) , ERR_PARSE_ERROR ) ;
d [ " sparse " ] = s ;
}
2020-12-21 15:39:32 +00:00
accessors . push_back ( d ) ;
}
2021-11-02 00:31:59 +00:00
if ( ! accessors . size ( ) ) {
return OK ;
}
2022-12-10 21:05:13 +00:00
p_state - > json [ " accessors " ] = accessors ;
ERR_FAIL_COND_V ( ! p_state - > json . has ( " accessors " ) , ERR_FILE_CORRUPT ) ;
print_verbose ( " glTF: Total accessors: " + itos ( p_state - > accessors . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2024-07-04 02:57:17 +00:00
String GLTFDocument : : _get_accessor_type_name ( const GLTFAccessor : : GLTFAccessorType p_accessor_type ) {
if ( p_accessor_type = = GLTFAccessor : : TYPE_SCALAR ) {
2020-12-21 15:39:32 +00:00
return " SCALAR " ;
}
2024-07-04 02:57:17 +00:00
if ( p_accessor_type = = GLTFAccessor : : TYPE_VEC2 ) {
2020-12-21 15:39:32 +00:00
return " VEC2 " ;
}
2024-07-04 02:57:17 +00:00
if ( p_accessor_type = = GLTFAccessor : : TYPE_VEC3 ) {
2020-12-21 15:39:32 +00:00
return " VEC3 " ;
}
2024-07-04 02:57:17 +00:00
if ( p_accessor_type = = GLTFAccessor : : TYPE_VEC4 ) {
2020-12-21 15:39:32 +00:00
return " VEC4 " ;
}
2024-07-04 02:57:17 +00:00
if ( p_accessor_type = = GLTFAccessor : : TYPE_MAT2 ) {
2020-12-21 15:39:32 +00:00
return " MAT2 " ;
}
2024-07-04 02:57:17 +00:00
if ( p_accessor_type = = GLTFAccessor : : TYPE_MAT3 ) {
2020-12-21 15:39:32 +00:00
return " MAT3 " ;
}
2024-07-04 02:57:17 +00:00
if ( p_accessor_type = = GLTFAccessor : : TYPE_MAT4 ) {
2020-12-21 15:39:32 +00:00
return " MAT4 " ;
}
ERR_FAIL_V ( " SCALAR " ) ;
}
2024-07-04 02:57:17 +00:00
GLTFAccessor : : GLTFAccessorType GLTFDocument : : _get_accessor_type_from_str ( const String & p_string ) {
2021-04-05 12:09:59 +00:00
if ( p_string = = " SCALAR " ) {
2024-07-04 02:57:17 +00:00
return GLTFAccessor : : TYPE_SCALAR ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
2021-04-05 12:09:59 +00:00
if ( p_string = = " VEC2 " ) {
2024-07-04 02:57:17 +00:00
return GLTFAccessor : : TYPE_VEC2 ;
2021-04-05 12:09:59 +00:00
}
if ( p_string = = " VEC3 " ) {
2024-07-04 02:57:17 +00:00
return GLTFAccessor : : TYPE_VEC3 ;
2021-04-05 12:09:59 +00:00
}
if ( p_string = = " VEC4 " ) {
2024-07-04 02:57:17 +00:00
return GLTFAccessor : : TYPE_VEC4 ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
2021-04-05 12:09:59 +00:00
if ( p_string = = " MAT2 " ) {
2024-07-04 02:57:17 +00:00
return GLTFAccessor : : TYPE_MAT2 ;
2021-04-05 12:09:59 +00:00
}
if ( p_string = = " MAT3 " ) {
2024-07-04 02:57:17 +00:00
return GLTFAccessor : : TYPE_MAT3 ;
2021-04-05 12:09:59 +00:00
}
if ( p_string = = " MAT4 " ) {
2024-07-04 02:57:17 +00:00
return GLTFAccessor : : TYPE_MAT4 ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
2024-07-04 02:57:17 +00:00
ERR_FAIL_V ( GLTFAccessor : : TYPE_SCALAR ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_accessors ( Ref < GLTFState > p_state ) {
if ( ! p_state - > json . has ( " accessors " ) ) {
2021-01-14 18:13:47 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
const Array & accessors = p_state - > json [ " accessors " ] ;
2020-12-21 15:39:32 +00:00
for ( GLTFAccessorIndex i = 0 ; i < accessors . size ( ) ; i + + ) {
const Dictionary & d = accessors [ i ] ;
Ref < GLTFAccessor > accessor ;
2021-06-17 22:03:09 +00:00
accessor . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( ! d . has ( " componentType " ) , ERR_PARSE_ERROR ) ;
2024-07-10 08:02:36 +00:00
accessor - > component_type = ( GLTFAccessor : : GLTFComponentType ) ( int32_t ) d [ " componentType " ] ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( ! d . has ( " count " ) , ERR_PARSE_ERROR ) ;
accessor - > count = d [ " count " ] ;
ERR_FAIL_COND_V ( ! d . has ( " type " ) , ERR_PARSE_ERROR ) ;
2024-04-12 04:29:51 +00:00
accessor - > accessor_type = _get_accessor_type_from_str ( d [ " type " ] ) ;
2020-12-21 15:39:32 +00:00
if ( d . has ( " bufferView " ) ) {
accessor - > buffer_view = d [ " bufferView " ] ; //optional because it may be sparse...
}
if ( d . has ( " byteOffset " ) ) {
accessor - > byte_offset = d [ " byteOffset " ] ;
}
2020-12-28 06:29:35 +00:00
if ( d . has ( " normalized " ) ) {
accessor - > normalized = d [ " normalized " ] ;
}
2020-12-21 15:39:32 +00:00
if ( d . has ( " max " ) ) {
accessor - > max = d [ " max " ] ;
}
if ( d . has ( " min " ) ) {
accessor - > min = d [ " min " ] ;
}
if ( d . has ( " sparse " ) ) {
const Dictionary & s = d [ " sparse " ] ;
ERR_FAIL_COND_V ( ! s . has ( " count " ) , ERR_PARSE_ERROR ) ;
accessor - > sparse_count = s [ " count " ] ;
ERR_FAIL_COND_V ( ! s . has ( " indices " ) , ERR_PARSE_ERROR ) ;
const Dictionary & si = s [ " indices " ] ;
ERR_FAIL_COND_V ( ! si . has ( " bufferView " ) , ERR_PARSE_ERROR ) ;
accessor - > sparse_indices_buffer_view = si [ " bufferView " ] ;
ERR_FAIL_COND_V ( ! si . has ( " componentType " ) , ERR_PARSE_ERROR ) ;
2024-07-10 08:02:36 +00:00
accessor - > sparse_indices_component_type = ( GLTFAccessor : : GLTFComponentType ) ( int32_t ) si [ " componentType " ] ;
2020-12-21 15:39:32 +00:00
if ( si . has ( " byteOffset " ) ) {
accessor - > sparse_indices_byte_offset = si [ " byteOffset " ] ;
}
ERR_FAIL_COND_V ( ! s . has ( " values " ) , ERR_PARSE_ERROR ) ;
const Dictionary & sv = s [ " values " ] ;
ERR_FAIL_COND_V ( ! sv . has ( " bufferView " ) , ERR_PARSE_ERROR ) ;
accessor - > sparse_values_buffer_view = sv [ " bufferView " ] ;
if ( sv . has ( " byteOffset " ) ) {
accessor - > sparse_values_byte_offset = sv [ " byteOffset " ] ;
}
}
2022-12-10 21:05:13 +00:00
p_state - > accessors . push_back ( accessor ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: Total accessors: " + itos ( p_state - > accessors . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
double GLTFDocument : : _filter_number ( double p_float ) {
2024-03-10 07:51:52 +00:00
if ( ! Math : : is_finite ( p_float ) ) {
// 3.6.2.2. "Values of NaN, +Infinity, and -Infinity MUST NOT be present."
2020-12-21 15:39:32 +00:00
return 0.0f ;
}
2024-03-10 07:51:52 +00:00
return ( double ) ( float ) p_float ;
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:02:36 +00:00
String GLTFDocument : : _get_component_type_name ( const GLTFAccessor : : GLTFComponentType p_component ) {
2020-12-21 15:39:32 +00:00
switch ( p_component ) {
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_NONE :
return " None " ;
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_BYTE :
2020-12-21 15:39:32 +00:00
return " Byte " ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_BYTE :
2020-12-21 15:39:32 +00:00
return " UByte " ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_SHORT :
2020-12-21 15:39:32 +00:00
return " Short " ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_SHORT :
2020-12-21 15:39:32 +00:00
return " UShort " ;
2024-07-10 08:39:04 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_INT :
2020-12-21 15:39:32 +00:00
return " Int " ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_INT :
return " UInt " ;
case GLTFAccessor : : COMPONENT_TYPE_SINGLE_FLOAT :
2020-12-21 15:39:32 +00:00
return " Float " ;
2024-07-10 08:39:04 +00:00
case GLTFAccessor : : COMPONENT_TYPE_DOUBLE_FLOAT :
return " Double " ;
case GLTFAccessor : : COMPONENT_TYPE_HALF_FLOAT :
return " Half " ;
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_LONG :
return " Long " ;
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_LONG :
return " ULong " ;
2020-12-21 15:39:32 +00:00
}
return " <Error> " ;
}
2024-07-10 08:02:36 +00:00
Error GLTFDocument : : _encode_buffer_view ( Ref < GLTFState > p_state , const double * p_src , const int p_count , const GLTFAccessor : : GLTFAccessorType p_accessor_type , const GLTFAccessor : : GLTFComponentType p_component_type , const bool p_normalized , const int p_byte_offset , const bool p_for_vertex , GLTFBufferViewIndex & r_accessor , const bool p_for_vertex_indices ) {
const int component_count = COMPONENT_COUNT_FOR_ACCESSOR_TYPE [ p_accessor_type ] ;
2022-12-10 21:05:13 +00:00
const int component_size = _get_component_type_size ( p_component_type ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( component_size = = 0 , FAILED ) ;
int skip_every = 0 ;
int skip_bytes = 0 ;
//special case of alignments, as described in spec
2022-12-10 21:05:13 +00:00
switch ( p_component_type ) {
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_BYTE :
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_BYTE : {
2024-07-04 02:57:17 +00:00
if ( p_accessor_type = = GLTFAccessor : : TYPE_MAT2 ) {
2020-12-21 15:39:32 +00:00
skip_every = 2 ;
skip_bytes = 2 ;
}
2024-07-04 02:57:17 +00:00
if ( p_accessor_type = = GLTFAccessor : : TYPE_MAT3 ) {
2020-12-21 15:39:32 +00:00
skip_every = 3 ;
skip_bytes = 1 ;
}
} break ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_SHORT :
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_SHORT : {
2024-07-04 02:57:17 +00:00
if ( p_accessor_type = = GLTFAccessor : : TYPE_MAT3 ) {
2020-12-21 15:39:32 +00:00
skip_every = 6 ;
skip_bytes = 4 ;
}
} break ;
default : {
}
}
Ref < GLTFBufferView > bv ;
2021-06-17 22:03:09 +00:00
bv . instantiate ( ) ;
2022-12-10 21:05:13 +00:00
const uint32_t offset = bv - > byte_offset = p_byte_offset ;
Vector < uint8_t > & gltf_buffer = p_state - > buffers . write [ 0 ] ;
2020-12-21 15:39:32 +00:00
2024-03-01 21:07:07 +00:00
int stride = component_count * component_size ;
2022-12-10 21:05:13 +00:00
if ( p_for_vertex & & stride % 4 ) {
2020-12-21 15:39:32 +00:00
stride + = 4 - ( stride % 4 ) ; //according to spec must be multiple of 4
}
//use to debug
2024-04-12 04:29:51 +00:00
print_verbose ( " glTF: encoding accessor type " + _get_accessor_type_name ( p_accessor_type ) + " component type: " + _get_component_type_name ( p_component_type ) + " stride: " + itos ( stride ) + " amount " + itos ( p_count ) ) ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: encoding accessor offset " + itos ( p_byte_offset ) + " view offset: " + itos ( bv - > byte_offset ) + " total buffer len: " + itos ( gltf_buffer . size ( ) ) + " view len " + itos ( bv - > byte_length ) ) ;
2020-12-21 15:39:32 +00:00
2024-03-01 21:07:07 +00:00
const int buffer_end = ( stride * ( p_count - 1 ) ) + component_size ;
2020-12-21 15:39:32 +00:00
// TODO define bv->byte_stride
bv - > byte_offset = gltf_buffer . size ( ) ;
2024-03-10 07:51:52 +00:00
if ( p_for_vertex_indices ) {
bv - > indices = true ;
} else if ( p_for_vertex ) {
bv - > vertex_attributes = true ;
2024-03-01 21:07:07 +00:00
bv - > byte_stride = stride ;
2024-03-10 07:51:52 +00:00
}
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
switch ( p_component_type ) {
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_NONE : {
ERR_FAIL_V_MSG ( ERR_INVALID_DATA , " glTF: Failed to encode buffer view, component type not set. " ) ;
}
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_BYTE : {
2020-12-21 15:39:32 +00:00
Vector < int8_t > buffer ;
2022-12-10 21:05:13 +00:00
buffer . resize ( p_count * component_count ) ;
2020-12-21 15:39:32 +00:00
int32_t dst_i = 0 ;
2022-12-10 21:05:13 +00:00
for ( int i = 0 ; i < p_count ; i + + ) {
2020-12-21 15:39:32 +00:00
for ( int j = 0 ; j < component_count ; j + + ) {
if ( skip_every & & j > 0 & & ( j % skip_every ) = = 0 ) {
dst_i + = skip_bytes ;
}
2022-12-10 21:05:13 +00:00
double d = * p_src ;
if ( p_normalized ) {
2020-12-21 15:39:32 +00:00
buffer . write [ dst_i ] = d * 128.0 ;
} else {
buffer . write [ dst_i ] = d ;
}
2022-12-10 21:05:13 +00:00
p_src + + ;
2020-12-21 15:39:32 +00:00
dst_i + + ;
}
}
int64_t old_size = gltf_buffer . size ( ) ;
gltf_buffer . resize ( old_size + ( buffer . size ( ) * sizeof ( int8_t ) ) ) ;
2021-04-27 14:19:21 +00:00
memcpy ( gltf_buffer . ptrw ( ) + old_size , buffer . ptrw ( ) , buffer . size ( ) * sizeof ( int8_t ) ) ;
2020-12-21 15:39:32 +00:00
bv - > byte_length = buffer . size ( ) * sizeof ( int8_t ) ;
} break ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_BYTE : {
2020-12-21 15:39:32 +00:00
Vector < uint8_t > buffer ;
2022-12-10 21:05:13 +00:00
buffer . resize ( p_count * component_count ) ;
2020-12-21 15:39:32 +00:00
int32_t dst_i = 0 ;
2022-12-10 21:05:13 +00:00
for ( int i = 0 ; i < p_count ; i + + ) {
2020-12-21 15:39:32 +00:00
for ( int j = 0 ; j < component_count ; j + + ) {
if ( skip_every & & j > 0 & & ( j % skip_every ) = = 0 ) {
dst_i + = skip_bytes ;
}
2022-12-10 21:05:13 +00:00
double d = * p_src ;
if ( p_normalized ) {
2020-12-21 15:39:32 +00:00
buffer . write [ dst_i ] = d * 255.0 ;
} else {
buffer . write [ dst_i ] = d ;
}
2022-12-10 21:05:13 +00:00
p_src + + ;
2020-12-21 15:39:32 +00:00
dst_i + + ;
}
}
gltf_buffer . append_array ( buffer ) ;
bv - > byte_length = buffer . size ( ) * sizeof ( uint8_t ) ;
} break ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_SHORT : {
2020-12-21 15:39:32 +00:00
Vector < int16_t > buffer ;
2022-12-10 21:05:13 +00:00
buffer . resize ( p_count * component_count ) ;
2020-12-21 15:39:32 +00:00
int32_t dst_i = 0 ;
2022-12-10 21:05:13 +00:00
for ( int i = 0 ; i < p_count ; i + + ) {
2020-12-21 15:39:32 +00:00
for ( int j = 0 ; j < component_count ; j + + ) {
if ( skip_every & & j > 0 & & ( j % skip_every ) = = 0 ) {
dst_i + = skip_bytes ;
}
2022-12-10 21:05:13 +00:00
double d = * p_src ;
if ( p_normalized ) {
2020-12-21 15:39:32 +00:00
buffer . write [ dst_i ] = d * 32768.0 ;
} else {
buffer . write [ dst_i ] = d ;
}
2022-12-10 21:05:13 +00:00
p_src + + ;
2020-12-21 15:39:32 +00:00
dst_i + + ;
}
}
int64_t old_size = gltf_buffer . size ( ) ;
gltf_buffer . resize ( old_size + ( buffer . size ( ) * sizeof ( int16_t ) ) ) ;
2021-04-27 14:19:21 +00:00
memcpy ( gltf_buffer . ptrw ( ) + old_size , buffer . ptrw ( ) , buffer . size ( ) * sizeof ( int16_t ) ) ;
2020-12-21 15:39:32 +00:00
bv - > byte_length = buffer . size ( ) * sizeof ( int16_t ) ;
} break ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_SHORT : {
2020-12-21 15:39:32 +00:00
Vector < uint16_t > buffer ;
2022-12-10 21:05:13 +00:00
buffer . resize ( p_count * component_count ) ;
2020-12-21 15:39:32 +00:00
int32_t dst_i = 0 ;
2022-12-10 21:05:13 +00:00
for ( int i = 0 ; i < p_count ; i + + ) {
2020-12-21 15:39:32 +00:00
for ( int j = 0 ; j < component_count ; j + + ) {
if ( skip_every & & j > 0 & & ( j % skip_every ) = = 0 ) {
dst_i + = skip_bytes ;
}
2022-12-10 21:05:13 +00:00
double d = * p_src ;
if ( p_normalized ) {
2020-12-21 15:39:32 +00:00
buffer . write [ dst_i ] = d * 65535.0 ;
} else {
buffer . write [ dst_i ] = d ;
}
2022-12-10 21:05:13 +00:00
p_src + + ;
2020-12-21 15:39:32 +00:00
dst_i + + ;
}
}
int64_t old_size = gltf_buffer . size ( ) ;
gltf_buffer . resize ( old_size + ( buffer . size ( ) * sizeof ( uint16_t ) ) ) ;
2021-04-27 14:19:21 +00:00
memcpy ( gltf_buffer . ptrw ( ) + old_size , buffer . ptrw ( ) , buffer . size ( ) * sizeof ( uint16_t ) ) ;
2020-12-21 15:39:32 +00:00
bv - > byte_length = buffer . size ( ) * sizeof ( uint16_t ) ;
} break ;
2024-07-10 08:39:04 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_INT : {
Vector < int32_t > buffer ;
buffer . resize ( p_count * component_count ) ;
int32_t dst_i = 0 ;
for ( int i = 0 ; i < p_count ; i + + ) {
for ( int j = 0 ; j < component_count ; j + + ) {
if ( skip_every & & j > 0 & & ( j % skip_every ) = = 0 ) {
dst_i + = skip_bytes ;
}
double d = * p_src ;
buffer . write [ dst_i ] = d ;
p_src + + ;
dst_i + + ;
}
}
int64_t old_size = gltf_buffer . size ( ) ;
gltf_buffer . resize ( old_size + ( buffer . size ( ) * sizeof ( uint32_t ) ) ) ;
memcpy ( gltf_buffer . ptrw ( ) + old_size , buffer . ptrw ( ) , buffer . size ( ) * sizeof ( uint32_t ) ) ;
bv - > byte_length = buffer . size ( ) * sizeof ( uint32_t ) ;
} break ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_INT : {
Vector < uint32_t > buffer ;
2022-12-10 21:05:13 +00:00
buffer . resize ( p_count * component_count ) ;
2020-12-21 15:39:32 +00:00
int32_t dst_i = 0 ;
2022-12-10 21:05:13 +00:00
for ( int i = 0 ; i < p_count ; i + + ) {
2020-12-21 15:39:32 +00:00
for ( int j = 0 ; j < component_count ; j + + ) {
if ( skip_every & & j > 0 & & ( j % skip_every ) = = 0 ) {
dst_i + = skip_bytes ;
}
2022-12-10 21:05:13 +00:00
double d = * p_src ;
2020-12-21 15:39:32 +00:00
buffer . write [ dst_i ] = d ;
2022-12-10 21:05:13 +00:00
p_src + + ;
2020-12-21 15:39:32 +00:00
dst_i + + ;
}
}
int64_t old_size = gltf_buffer . size ( ) ;
2024-07-10 08:02:36 +00:00
gltf_buffer . resize ( old_size + ( buffer . size ( ) * sizeof ( uint32_t ) ) ) ;
memcpy ( gltf_buffer . ptrw ( ) + old_size , buffer . ptrw ( ) , buffer . size ( ) * sizeof ( uint32_t ) ) ;
bv - > byte_length = buffer . size ( ) * sizeof ( uint32_t ) ;
2020-12-21 15:39:32 +00:00
} break ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SINGLE_FLOAT : {
2020-12-21 15:39:32 +00:00
Vector < float > buffer ;
2022-12-10 21:05:13 +00:00
buffer . resize ( p_count * component_count ) ;
2020-12-21 15:39:32 +00:00
int32_t dst_i = 0 ;
2022-12-10 21:05:13 +00:00
for ( int i = 0 ; i < p_count ; i + + ) {
2020-12-21 15:39:32 +00:00
for ( int j = 0 ; j < component_count ; j + + ) {
if ( skip_every & & j > 0 & & ( j % skip_every ) = = 0 ) {
dst_i + = skip_bytes ;
}
2022-12-10 21:05:13 +00:00
double d = * p_src ;
2020-12-21 15:39:32 +00:00
buffer . write [ dst_i ] = d ;
2022-12-10 21:05:13 +00:00
p_src + + ;
2020-12-21 15:39:32 +00:00
dst_i + + ;
}
}
int64_t old_size = gltf_buffer . size ( ) ;
gltf_buffer . resize ( old_size + ( buffer . size ( ) * sizeof ( float ) ) ) ;
2021-04-27 14:19:21 +00:00
memcpy ( gltf_buffer . ptrw ( ) + old_size , buffer . ptrw ( ) , buffer . size ( ) * sizeof ( float ) ) ;
2020-12-21 15:39:32 +00:00
bv - > byte_length = buffer . size ( ) * sizeof ( float ) ;
} break ;
2024-07-10 08:39:04 +00:00
case GLTFAccessor : : COMPONENT_TYPE_DOUBLE_FLOAT : {
Vector < double > buffer ;
buffer . resize ( p_count * component_count ) ;
int32_t dst_i = 0 ;
for ( int i = 0 ; i < p_count ; i + + ) {
for ( int j = 0 ; j < component_count ; j + + ) {
if ( skip_every & & j > 0 & & ( j % skip_every ) = = 0 ) {
dst_i + = skip_bytes ;
}
double d = * p_src ;
buffer . write [ dst_i ] = d ;
p_src + + ;
dst_i + + ;
}
}
int64_t old_size = gltf_buffer . size ( ) ;
gltf_buffer . resize ( old_size + ( buffer . size ( ) * sizeof ( double ) ) ) ;
memcpy ( gltf_buffer . ptrw ( ) + old_size , buffer . ptrw ( ) , buffer . size ( ) * sizeof ( double ) ) ;
bv - > byte_length = buffer . size ( ) * sizeof ( double ) ;
} break ;
case GLTFAccessor : : COMPONENT_TYPE_HALF_FLOAT : {
ERR_FAIL_V_MSG ( ERR_UNAVAILABLE , " glTF: Half float not supported yet. " ) ;
} break ;
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_LONG : {
Vector < int64_t > buffer ;
buffer . resize ( p_count * component_count ) ;
int32_t dst_i = 0 ;
for ( int i = 0 ; i < p_count ; i + + ) {
for ( int j = 0 ; j < component_count ; j + + ) {
if ( skip_every & & j > 0 & & ( j % skip_every ) = = 0 ) {
dst_i + = skip_bytes ;
}
// FIXME: This can result in precision loss because int64_t can store some values that double can't.
double d = * p_src ;
buffer . write [ dst_i ] = d ;
p_src + + ;
dst_i + + ;
}
}
int64_t old_size = gltf_buffer . size ( ) ;
gltf_buffer . resize ( old_size + ( buffer . size ( ) * sizeof ( int64_t ) ) ) ;
memcpy ( gltf_buffer . ptrw ( ) + old_size , buffer . ptrw ( ) , buffer . size ( ) * sizeof ( int64_t ) ) ;
bv - > byte_length = buffer . size ( ) * sizeof ( int64_t ) ;
} break ;
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_LONG : {
Vector < uint64_t > buffer ;
buffer . resize ( p_count * component_count ) ;
int32_t dst_i = 0 ;
for ( int i = 0 ; i < p_count ; i + + ) {
for ( int j = 0 ; j < component_count ; j + + ) {
if ( skip_every & & j > 0 & & ( j % skip_every ) = = 0 ) {
dst_i + = skip_bytes ;
}
// FIXME: This can result in precision loss because int64_t can store some values that double can't.
double d = * p_src ;
buffer . write [ dst_i ] = d ;
p_src + + ;
dst_i + + ;
}
}
int64_t old_size = gltf_buffer . size ( ) ;
gltf_buffer . resize ( old_size + ( buffer . size ( ) * sizeof ( uint64_t ) ) ) ;
memcpy ( gltf_buffer . ptrw ( ) + old_size , buffer . ptrw ( ) , buffer . size ( ) * sizeof ( uint64_t ) ) ;
bv - > byte_length = buffer . size ( ) * sizeof ( uint64_t ) ;
} break ;
2020-12-21 15:39:32 +00:00
}
ERR_FAIL_COND_V ( buffer_end > bv - > byte_length , ERR_INVALID_DATA ) ;
ERR_FAIL_COND_V ( ( int ) ( offset + buffer_end ) > gltf_buffer . size ( ) , ERR_INVALID_DATA ) ;
2024-03-01 21:07:07 +00:00
int pad_bytes = ( 4 - gltf_buffer . size ( ) ) & 3 ;
for ( int i = 0 ; i < pad_bytes ; i + + ) {
gltf_buffer . push_back ( 0 ) ;
}
2022-12-10 21:05:13 +00:00
r_accessor = bv - > buffer = p_state - > buffer_views . size ( ) ;
p_state - > buffer_views . push_back ( bv ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2024-07-10 08:02:36 +00:00
Error GLTFDocument : : _decode_buffer_view ( Ref < GLTFState > p_state , double * p_dst , const GLTFBufferViewIndex p_buffer_view , const int p_skip_every , const int p_skip_bytes , const int p_element_size , const int p_count , const GLTFAccessor : : GLTFAccessorType p_accessor_type , const int p_component_count , const GLTFAccessor : : GLTFComponentType p_component_type , const int p_component_size , const bool p_normalized , const int p_byte_offset , const bool p_for_vertex ) {
2022-12-10 21:05:13 +00:00
const Ref < GLTFBufferView > bv = p_state - > buffer_views [ p_buffer_view ] ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
int stride = p_element_size ;
2020-12-21 15:39:32 +00:00
if ( bv - > byte_stride ! = - 1 ) {
stride = bv - > byte_stride ;
}
2022-12-10 21:05:13 +00:00
if ( p_for_vertex & & stride % 4 ) {
2020-12-21 15:39:32 +00:00
stride + = 4 - ( stride % 4 ) ; //according to spec must be multiple of 4
}
2022-12-10 21:05:13 +00:00
ERR_FAIL_INDEX_V ( bv - > buffer , p_state - > buffers . size ( ) , ERR_PARSE_ERROR ) ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
const uint32_t offset = bv - > byte_offset + p_byte_offset ;
Vector < uint8_t > buffer = p_state - > buffers [ bv - > buffer ] ; //copy on write, so no performance hit
2020-12-21 15:39:32 +00:00
const uint8_t * bufptr = buffer . ptr ( ) ;
//use to debug
2024-04-12 04:29:51 +00:00
print_verbose ( " glTF: accessor type " + _get_accessor_type_name ( p_accessor_type ) + " component type: " + _get_component_type_name ( p_component_type ) + " stride: " + itos ( stride ) + " amount " + itos ( p_count ) ) ;
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: accessor offset " + itos ( p_byte_offset ) + " view offset: " + itos ( bv - > byte_offset ) + " total buffer len: " + itos ( buffer . size ( ) ) + " view len " + itos ( bv - > byte_length ) ) ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
const int buffer_end = ( stride * ( p_count - 1 ) ) + p_element_size ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( buffer_end > bv - > byte_length , ERR_PARSE_ERROR ) ;
ERR_FAIL_COND_V ( ( int ) ( offset + buffer_end ) > buffer . size ( ) , ERR_PARSE_ERROR ) ;
//fill everything as doubles
2022-12-10 21:05:13 +00:00
for ( int i = 0 ; i < p_count ; i + + ) {
2020-12-21 15:39:32 +00:00
const uint8_t * src = & bufptr [ offset + i * stride ] ;
2022-12-10 21:05:13 +00:00
for ( int j = 0 ; j < p_component_count ; j + + ) {
if ( p_skip_every & & j > 0 & & ( j % p_skip_every ) = = 0 ) {
src + = p_skip_bytes ;
2020-12-21 15:39:32 +00:00
}
double d = 0 ;
2022-12-10 21:05:13 +00:00
switch ( p_component_type ) {
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_NONE : {
ERR_FAIL_V_MSG ( ERR_INVALID_DATA , " glTF: Failed to decode buffer view, component type not set. " ) ;
} break ;
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_BYTE : {
2020-12-21 15:39:32 +00:00
int8_t b = int8_t ( * src ) ;
2022-12-10 21:05:13 +00:00
if ( p_normalized ) {
2020-12-21 15:39:32 +00:00
d = ( double ( b ) / 128.0 ) ;
} else {
d = double ( b ) ;
}
} break ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_BYTE : {
2020-12-21 15:39:32 +00:00
uint8_t b = * src ;
2022-12-10 21:05:13 +00:00
if ( p_normalized ) {
2020-12-21 15:39:32 +00:00
d = ( double ( b ) / 255.0 ) ;
} else {
d = double ( b ) ;
}
} break ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_SHORT : {
2020-12-21 15:39:32 +00:00
int16_t s = * ( int16_t * ) src ;
2022-12-10 21:05:13 +00:00
if ( p_normalized ) {
2020-12-21 15:39:32 +00:00
d = ( double ( s ) / 32768.0 ) ;
} else {
d = double ( s ) ;
}
} break ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_SHORT : {
2020-12-21 15:39:32 +00:00
uint16_t s = * ( uint16_t * ) src ;
2022-12-10 21:05:13 +00:00
if ( p_normalized ) {
2020-12-21 15:39:32 +00:00
d = ( double ( s ) / 65535.0 ) ;
} else {
d = double ( s ) ;
}
} break ;
2024-07-10 08:39:04 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_INT : {
d = * ( int32_t * ) src ;
2020-12-21 15:39:32 +00:00
} break ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_INT : {
d = * ( uint32_t * ) src ;
2020-12-21 15:39:32 +00:00
} break ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SINGLE_FLOAT : {
2020-12-21 15:39:32 +00:00
d = * ( float * ) src ;
} break ;
2024-07-10 08:39:04 +00:00
case GLTFAccessor : : COMPONENT_TYPE_DOUBLE_FLOAT : {
d = * ( double * ) src ;
} break ;
case GLTFAccessor : : COMPONENT_TYPE_HALF_FLOAT : {
ERR_FAIL_V_MSG ( ERR_UNAVAILABLE , " glTF: Half float not supported yet. " ) ;
} break ;
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_LONG : {
d = * ( int64_t * ) src ;
} break ;
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_LONG : {
d = * ( uint64_t * ) src ;
} break ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
* p_dst + + = d ;
src + = p_component_size ;
2020-12-21 15:39:32 +00:00
}
}
return OK ;
}
2024-07-10 08:02:36 +00:00
int GLTFDocument : : _get_component_type_size ( const GLTFAccessor : : GLTFComponentType p_component_type ) {
2022-12-10 21:05:13 +00:00
switch ( p_component_type ) {
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_NONE :
ERR_FAIL_V ( 0 ) ;
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_BYTE :
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_BYTE :
2020-12-21 15:39:32 +00:00
return 1 ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_SHORT :
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_SHORT :
2024-07-10 08:39:04 +00:00
case GLTFAccessor : : COMPONENT_TYPE_HALF_FLOAT :
2020-12-21 15:39:32 +00:00
return 2 ;
2024-07-10 08:39:04 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_INT :
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_INT :
case GLTFAccessor : : COMPONENT_TYPE_SINGLE_FLOAT :
2020-12-21 15:39:32 +00:00
return 4 ;
2024-07-10 08:39:04 +00:00
case GLTFAccessor : : COMPONENT_TYPE_DOUBLE_FLOAT :
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_LONG :
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_LONG :
return 8 ;
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:02:36 +00:00
ERR_FAIL_V ( 0 ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
Vector < double > GLTFDocument : : _decode_accessor ( Ref < GLTFState > p_state , const GLTFAccessorIndex p_accessor , const bool p_for_vertex ) {
2020-12-21 15:39:32 +00:00
//spec, for reference:
//https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment
2022-12-10 21:05:13 +00:00
ERR_FAIL_INDEX_V ( p_accessor , p_state - > accessors . size ( ) , Vector < double > ( ) ) ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
const Ref < GLTFAccessor > a = p_state - > accessors [ p_accessor ] ;
2020-12-21 15:39:32 +00:00
2024-07-10 08:02:36 +00:00
const int component_count = COMPONENT_COUNT_FOR_ACCESSOR_TYPE [ a - > accessor_type ] ;
2020-12-21 15:39:32 +00:00
const int component_size = _get_component_type_size ( a - > component_type ) ;
ERR_FAIL_COND_V ( component_size = = 0 , Vector < double > ( ) ) ;
int element_size = component_count * component_size ;
int skip_every = 0 ;
int skip_bytes = 0 ;
//special case of alignments, as described in spec
switch ( a - > component_type ) {
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_BYTE :
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_BYTE : {
2024-07-04 02:57:17 +00:00
if ( a - > accessor_type = = GLTFAccessor : : TYPE_MAT2 ) {
2020-12-21 15:39:32 +00:00
skip_every = 2 ;
skip_bytes = 2 ;
element_size = 8 ; //override for this case
}
2024-07-04 02:57:17 +00:00
if ( a - > accessor_type = = GLTFAccessor : : TYPE_MAT3 ) {
2020-12-21 15:39:32 +00:00
skip_every = 3 ;
skip_bytes = 1 ;
element_size = 12 ; //override for this case
}
} break ;
2024-07-10 08:02:36 +00:00
case GLTFAccessor : : COMPONENT_TYPE_SIGNED_SHORT :
case GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_SHORT : {
2024-07-04 02:57:17 +00:00
if ( a - > accessor_type = = GLTFAccessor : : TYPE_MAT3 ) {
2020-12-21 15:39:32 +00:00
skip_every = 6 ;
skip_bytes = 4 ;
element_size = 16 ; //override for this case
}
} break ;
default : {
}
}
Vector < double > dst_buffer ;
dst_buffer . resize ( component_count * a - > count ) ;
double * dst = dst_buffer . ptrw ( ) ;
if ( a - > buffer_view > = 0 ) {
2022-12-10 21:05:13 +00:00
ERR_FAIL_INDEX_V ( a - > buffer_view , p_state - > buffer_views . size ( ) , Vector < double > ( ) ) ;
2020-12-21 15:39:32 +00:00
2024-04-12 04:29:51 +00:00
const Error err = _decode_buffer_view ( p_state , dst , a - > buffer_view , skip_every , skip_bytes , element_size , a - > count , a - > accessor_type , component_count , a - > component_type , component_size , a - > normalized , a - > byte_offset , p_for_vertex ) ;
2021-04-05 12:09:59 +00:00
if ( err ! = OK ) {
2020-12-21 15:39:32 +00:00
return Vector < double > ( ) ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
} else {
//fill with zeros, as bufferview is not defined.
for ( int i = 0 ; i < ( a - > count * component_count ) ; i + + ) {
dst_buffer . write [ i ] = 0 ;
}
}
if ( a - > sparse_count > 0 ) {
// I could not find any file using this, so this code is so far untested
Vector < double > indices ;
indices . resize ( a - > sparse_count ) ;
const int indices_component_size = _get_component_type_size ( a - > sparse_indices_component_type ) ;
2024-07-04 02:57:17 +00:00
Error err = _decode_buffer_view ( p_state , indices . ptrw ( ) , a - > sparse_indices_buffer_view , 0 , 0 , indices_component_size , a - > sparse_count , GLTFAccessor : : TYPE_SCALAR , 1 , a - > sparse_indices_component_type , indices_component_size , false , a - > sparse_indices_byte_offset , false ) ;
2021-04-05 12:09:59 +00:00
if ( err ! = OK ) {
2020-12-21 15:39:32 +00:00
return Vector < double > ( ) ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
Vector < double > data ;
data . resize ( component_count * a - > sparse_count ) ;
2024-04-12 04:29:51 +00:00
err = _decode_buffer_view ( p_state , data . ptrw ( ) , a - > sparse_values_buffer_view , skip_every , skip_bytes , element_size , a - > sparse_count , a - > accessor_type , component_count , a - > component_type , component_size , a - > normalized , a - > sparse_values_byte_offset , p_for_vertex ) ;
2021-04-05 12:09:59 +00:00
if ( err ! = OK ) {
2020-12-21 15:39:32 +00:00
return Vector < double > ( ) ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
for ( int i = 0 ; i < indices . size ( ) ; i + + ) {
const int write_offset = int ( indices [ i ] ) * component_count ;
for ( int j = 0 ; j < component_count ; j + + ) {
dst [ write_offset + j ] = data [ i * component_count + j ] ;
}
}
}
return dst_buffer ;
}
2024-03-10 07:51:52 +00:00
GLTFAccessorIndex GLTFDocument : : _encode_accessor_as_ints ( Ref < GLTFState > p_state , const Vector < int32_t > p_attribs , const bool p_for_vertex , const bool p_for_vertex_indices ) {
2020-12-21 15:39:32 +00:00
if ( p_attribs . size ( ) = = 0 ) {
return - 1 ;
}
const int element_count = 1 ;
const int ret_size = p_attribs . size ( ) ;
Vector < double > attribs ;
attribs . resize ( ret_size ) ;
Vector < double > type_max ;
type_max . resize ( element_count ) ;
Vector < double > type_min ;
type_min . resize ( element_count ) ;
2024-03-01 21:07:07 +00:00
int max_index = 0 ;
2020-12-21 15:39:32 +00:00
for ( int i = 0 ; i < p_attribs . size ( ) ; i + + ) {
2024-03-10 07:51:52 +00:00
attribs . write [ i ] = p_attribs [ i ] ;
2024-03-01 21:07:07 +00:00
if ( p_attribs [ i ] > max_index ) {
max_index = p_attribs [ i ] ;
}
2020-12-21 15:39:32 +00:00
if ( i = = 0 ) {
for ( int32_t type_i = 0 ; type_i < element_count ; type_i + + ) {
type_max . write [ type_i ] = attribs [ ( i * element_count ) + type_i ] ;
type_min . write [ type_i ] = attribs [ ( i * element_count ) + type_i ] ;
}
}
for ( int32_t type_i = 0 ; type_i < element_count ; type_i + + ) {
type_max . write [ type_i ] = MAX ( attribs [ ( i * element_count ) + type_i ] , type_max [ type_i ] ) ;
type_min . write [ type_i ] = MIN ( attribs [ ( i * element_count ) + type_i ] , type_min [ type_i ] ) ;
}
}
2024-01-19 12:21:39 +00:00
ERR_FAIL_COND_V ( attribs . is_empty ( ) , - 1 ) ;
2020-12-21 15:39:32 +00:00
Ref < GLTFAccessor > accessor ;
2021-06-17 22:03:09 +00:00
accessor . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
GLTFBufferIndex buffer_view_i ;
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . is_empty ( ) ) {
p_state - > buffers . push_back ( Vector < uint8_t > ( ) ) ;
}
2022-12-10 21:05:13 +00:00
int64_t size = p_state - > buffers [ 0 ] . size ( ) ;
2024-07-04 02:57:17 +00:00
const GLTFAccessor : : GLTFAccessorType accessor_type = GLTFAccessor : : TYPE_SCALAR ;
2024-07-10 08:02:36 +00:00
GLTFAccessor : : GLTFComponentType component_type ;
2024-03-01 21:07:07 +00:00
if ( max_index > 65535 | | p_for_vertex ) {
2024-07-10 08:02:36 +00:00
component_type = GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_INT ;
2024-03-01 21:07:07 +00:00
} else {
2024-07-10 08:02:36 +00:00
component_type = GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_SHORT ;
2024-03-01 21:07:07 +00:00
}
2020-12-21 15:39:32 +00:00
accessor - > max = type_max ;
accessor - > min = type_min ;
accessor - > normalized = false ;
accessor - > count = ret_size ;
2024-04-12 04:29:51 +00:00
accessor - > accessor_type = accessor_type ;
2020-12-21 15:39:32 +00:00
accessor - > component_type = component_type ;
accessor - > byte_offset = 0 ;
2024-04-12 04:29:51 +00:00
Error err = _encode_buffer_view ( p_state , attribs . ptr ( ) , attribs . size ( ) , accessor_type , component_type , accessor - > normalized , size , p_for_vertex , buffer_view_i , p_for_vertex_indices ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return - 1 ;
}
accessor - > buffer_view = buffer_view_i ;
2022-12-10 21:05:13 +00:00
p_state - > accessors . push_back ( accessor ) ;
return p_state - > accessors . size ( ) - 1 ;
2020-12-21 15:39:32 +00:00
}
2024-03-12 11:50:54 +00:00
Vector < int > GLTFDocument : : _decode_accessor_as_ints ( Ref < GLTFState > p_state , const GLTFAccessorIndex p_accessor , const bool p_for_vertex , const Vector < int > & p_packed_vertex_ids ) {
2022-12-10 21:05:13 +00:00
const Vector < double > attribs = _decode_accessor ( p_state , p_accessor , p_for_vertex ) ;
2020-12-21 15:39:32 +00:00
Vector < int > ret ;
2021-04-05 12:09:59 +00:00
if ( attribs . size ( ) = = 0 ) {
2020-12-21 15:39:32 +00:00
return ret ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
const double * attribs_ptr = attribs . ptr ( ) ;
2024-03-12 11:50:54 +00:00
int ret_size = attribs . size ( ) ;
if ( ! p_packed_vertex_ids . is_empty ( ) ) {
ERR_FAIL_COND_V ( p_packed_vertex_ids [ p_packed_vertex_ids . size ( ) - 1 ] > = ret_size , ret ) ;
ret_size = p_packed_vertex_ids . size ( ) ;
}
2020-12-21 15:39:32 +00:00
ret . resize ( ret_size ) ;
2024-03-12 11:50:54 +00:00
for ( int i = 0 ; i < ret_size ; i + + ) {
int src_i = i ;
if ( ! p_packed_vertex_ids . is_empty ( ) ) {
src_i = p_packed_vertex_ids [ i ] ;
2020-12-21 15:39:32 +00:00
}
2024-03-12 11:50:54 +00:00
ret . write [ i ] = int ( attribs_ptr [ src_i ] ) ;
2020-12-21 15:39:32 +00:00
}
return ret ;
}
2024-03-12 11:50:54 +00:00
Vector < float > GLTFDocument : : _decode_accessor_as_floats ( Ref < GLTFState > p_state , const GLTFAccessorIndex p_accessor , const bool p_for_vertex , const Vector < int > & p_packed_vertex_ids ) {
2022-12-10 21:05:13 +00:00
const Vector < double > attribs = _decode_accessor ( p_state , p_accessor , p_for_vertex ) ;
2020-12-21 15:39:32 +00:00
Vector < float > ret ;
2021-04-05 12:09:59 +00:00
if ( attribs . size ( ) = = 0 ) {
2020-12-21 15:39:32 +00:00
return ret ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
const double * attribs_ptr = attribs . ptr ( ) ;
2024-03-12 11:50:54 +00:00
int ret_size = attribs . size ( ) ;
if ( ! p_packed_vertex_ids . is_empty ( ) ) {
ERR_FAIL_COND_V ( p_packed_vertex_ids [ p_packed_vertex_ids . size ( ) - 1 ] > = ret_size , ret ) ;
ret_size = p_packed_vertex_ids . size ( ) ;
}
2020-12-21 15:39:32 +00:00
ret . resize ( ret_size ) ;
2024-03-12 11:50:54 +00:00
for ( int i = 0 ; i < ret_size ; i + + ) {
int src_i = i ;
if ( ! p_packed_vertex_ids . is_empty ( ) ) {
src_i = p_packed_vertex_ids [ i ] ;
2020-12-21 15:39:32 +00:00
}
2024-03-12 11:50:54 +00:00
ret . write [ i ] = float ( attribs_ptr [ src_i ] ) ;
2020-12-21 15:39:32 +00:00
}
return ret ;
}
2024-03-10 07:51:52 +00:00
void GLTFDocument : : _round_min_max_components ( Vector < double > & r_type_min , Vector < double > & r_type_max ) {
// 3.6.2.5: For floating-point components, JSON-stored minimum and maximum values represent single precision
// floats and SHOULD be rounded to single precision before usage to avoid any potential boundary mismatches.
for ( int32_t type_i = 0 ; type_i < r_type_min . size ( ) ; type_i + + ) {
r_type_min . write [ type_i ] = ( double ) ( float ) r_type_min [ type_i ] ;
r_type_max . write [ type_i ] = ( double ) ( float ) r_type_max [ type_i ] ;
}
}
2022-12-10 21:05:13 +00:00
GLTFAccessorIndex GLTFDocument : : _encode_accessor_as_vec2 ( Ref < GLTFState > p_state , const Vector < Vector2 > p_attribs , const bool p_for_vertex ) {
2020-12-21 15:39:32 +00:00
if ( p_attribs . size ( ) = = 0 ) {
return - 1 ;
}
const int element_count = 2 ;
const int ret_size = p_attribs . size ( ) * element_count ;
Vector < double > attribs ;
attribs . resize ( ret_size ) ;
Vector < double > type_max ;
type_max . resize ( element_count ) ;
Vector < double > type_min ;
type_min . resize ( element_count ) ;
for ( int i = 0 ; i < p_attribs . size ( ) ; i + + ) {
Vector2 attrib = p_attribs [ i ] ;
2024-03-10 07:51:52 +00:00
attribs . write [ ( i * element_count ) + 0 ] = _filter_number ( attrib . x ) ;
attribs . write [ ( i * element_count ) + 1 ] = _filter_number ( attrib . y ) ;
2020-12-21 15:39:32 +00:00
_calc_accessor_min_max ( i , element_count , type_max , attribs , type_min ) ;
}
2024-03-10 07:51:52 +00:00
_round_min_max_components ( type_min , type_max ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( attribs . size ( ) % element_count ! = 0 , - 1 ) ;
Ref < GLTFAccessor > accessor ;
2021-06-17 22:03:09 +00:00
accessor . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
GLTFBufferIndex buffer_view_i ;
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . is_empty ( ) ) {
p_state - > buffers . push_back ( Vector < uint8_t > ( ) ) ;
}
2022-12-10 21:05:13 +00:00
int64_t size = p_state - > buffers [ 0 ] . size ( ) ;
2024-07-04 02:57:17 +00:00
const GLTFAccessor : : GLTFAccessorType accessor_type = GLTFAccessor : : TYPE_VEC2 ;
2024-07-10 08:02:36 +00:00
const GLTFAccessor : : GLTFComponentType component_type = GLTFAccessor : : COMPONENT_TYPE_SINGLE_FLOAT ;
2020-12-21 15:39:32 +00:00
accessor - > max = type_max ;
accessor - > min = type_min ;
accessor - > normalized = false ;
accessor - > count = p_attribs . size ( ) ;
2024-04-12 04:29:51 +00:00
accessor - > accessor_type = accessor_type ;
2020-12-21 15:39:32 +00:00
accessor - > component_type = component_type ;
accessor - > byte_offset = 0 ;
2024-04-12 04:29:51 +00:00
Error err = _encode_buffer_view ( p_state , attribs . ptr ( ) , p_attribs . size ( ) , accessor_type , component_type , accessor - > normalized , size , p_for_vertex , buffer_view_i ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return - 1 ;
}
accessor - > buffer_view = buffer_view_i ;
2022-12-10 21:05:13 +00:00
p_state - > accessors . push_back ( accessor ) ;
return p_state - > accessors . size ( ) - 1 ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
GLTFAccessorIndex GLTFDocument : : _encode_accessor_as_color ( Ref < GLTFState > p_state , const Vector < Color > p_attribs , const bool p_for_vertex ) {
2020-12-21 15:39:32 +00:00
if ( p_attribs . size ( ) = = 0 ) {
return - 1 ;
}
const int ret_size = p_attribs . size ( ) * 4 ;
Vector < double > attribs ;
attribs . resize ( ret_size ) ;
const int element_count = 4 ;
Vector < double > type_max ;
type_max . resize ( element_count ) ;
Vector < double > type_min ;
type_min . resize ( element_count ) ;
for ( int i = 0 ; i < p_attribs . size ( ) ; i + + ) {
Color attrib = p_attribs [ i ] ;
2024-03-10 07:51:52 +00:00
attribs . write [ ( i * element_count ) + 0 ] = _filter_number ( attrib . r ) ;
attribs . write [ ( i * element_count ) + 1 ] = _filter_number ( attrib . g ) ;
attribs . write [ ( i * element_count ) + 2 ] = _filter_number ( attrib . b ) ;
attribs . write [ ( i * element_count ) + 3 ] = _filter_number ( attrib . a ) ;
2020-12-21 15:39:32 +00:00
_calc_accessor_min_max ( i , element_count , type_max , attribs , type_min ) ;
}
2024-03-10 07:51:52 +00:00
_round_min_max_components ( type_min , type_max ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( attribs . size ( ) % element_count ! = 0 , - 1 ) ;
Ref < GLTFAccessor > accessor ;
2021-06-17 22:03:09 +00:00
accessor . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
GLTFBufferIndex buffer_view_i ;
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . is_empty ( ) ) {
p_state - > buffers . push_back ( Vector < uint8_t > ( ) ) ;
}
2022-12-10 21:05:13 +00:00
int64_t size = p_state - > buffers [ 0 ] . size ( ) ;
2024-07-04 02:57:17 +00:00
const GLTFAccessor : : GLTFAccessorType accessor_type = GLTFAccessor : : TYPE_VEC4 ;
2024-07-10 08:02:36 +00:00
const GLTFAccessor : : GLTFComponentType component_type = GLTFAccessor : : COMPONENT_TYPE_SINGLE_FLOAT ;
2020-12-21 15:39:32 +00:00
accessor - > max = type_max ;
accessor - > min = type_min ;
accessor - > normalized = false ;
accessor - > count = p_attribs . size ( ) ;
2024-04-12 04:29:51 +00:00
accessor - > accessor_type = accessor_type ;
2020-12-21 15:39:32 +00:00
accessor - > component_type = component_type ;
accessor - > byte_offset = 0 ;
2024-04-12 04:29:51 +00:00
Error err = _encode_buffer_view ( p_state , attribs . ptr ( ) , p_attribs . size ( ) , accessor_type , component_type , accessor - > normalized , size , p_for_vertex , buffer_view_i ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return - 1 ;
}
accessor - > buffer_view = buffer_view_i ;
2022-12-10 21:05:13 +00:00
p_state - > accessors . push_back ( accessor ) ;
return p_state - > accessors . size ( ) - 1 ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _calc_accessor_min_max ( int p_i , const int p_element_count , Vector < double > & p_type_max , Vector < double > p_attribs , Vector < double > & p_type_min ) {
if ( p_i = = 0 ) {
for ( int32_t type_i = 0 ; type_i < p_element_count ; type_i + + ) {
p_type_max . write [ type_i ] = p_attribs [ ( p_i * p_element_count ) + type_i ] ;
p_type_min . write [ type_i ] = p_attribs [ ( p_i * p_element_count ) + type_i ] ;
2020-12-21 15:39:32 +00:00
}
}
2022-12-10 21:05:13 +00:00
for ( int32_t type_i = 0 ; type_i < p_element_count ; type_i + + ) {
p_type_max . write [ type_i ] = MAX ( p_attribs [ ( p_i * p_element_count ) + type_i ] , p_type_max [ type_i ] ) ;
p_type_min . write [ type_i ] = MIN ( p_attribs [ ( p_i * p_element_count ) + type_i ] , p_type_min [ type_i ] ) ;
2020-12-21 15:39:32 +00:00
}
}
2022-12-10 21:05:13 +00:00
GLTFAccessorIndex GLTFDocument : : _encode_accessor_as_weights ( Ref < GLTFState > p_state , const Vector < Color > p_attribs , const bool p_for_vertex ) {
2020-12-21 15:39:32 +00:00
if ( p_attribs . size ( ) = = 0 ) {
return - 1 ;
}
const int ret_size = p_attribs . size ( ) * 4 ;
Vector < double > attribs ;
attribs . resize ( ret_size ) ;
const int element_count = 4 ;
Vector < double > type_max ;
type_max . resize ( element_count ) ;
Vector < double > type_min ;
type_min . resize ( element_count ) ;
for ( int i = 0 ; i < p_attribs . size ( ) ; i + + ) {
Color attrib = p_attribs [ i ] ;
2024-03-10 07:51:52 +00:00
attribs . write [ ( i * element_count ) + 0 ] = _filter_number ( attrib . r ) ;
attribs . write [ ( i * element_count ) + 1 ] = _filter_number ( attrib . g ) ;
attribs . write [ ( i * element_count ) + 2 ] = _filter_number ( attrib . b ) ;
attribs . write [ ( i * element_count ) + 3 ] = _filter_number ( attrib . a ) ;
2020-12-21 15:39:32 +00:00
_calc_accessor_min_max ( i , element_count , type_max , attribs , type_min ) ;
}
2024-03-10 07:51:52 +00:00
_round_min_max_components ( type_min , type_max ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( attribs . size ( ) % element_count ! = 0 , - 1 ) ;
Ref < GLTFAccessor > accessor ;
2021-06-17 22:03:09 +00:00
accessor . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
GLTFBufferIndex buffer_view_i ;
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . is_empty ( ) ) {
p_state - > buffers . push_back ( Vector < uint8_t > ( ) ) ;
}
2022-12-10 21:05:13 +00:00
int64_t size = p_state - > buffers [ 0 ] . size ( ) ;
2024-07-04 02:57:17 +00:00
const GLTFAccessor : : GLTFAccessorType accessor_type = GLTFAccessor : : TYPE_VEC4 ;
2024-07-10 08:02:36 +00:00
const GLTFAccessor : : GLTFComponentType component_type = GLTFAccessor : : COMPONENT_TYPE_SINGLE_FLOAT ;
2020-12-21 15:39:32 +00:00
accessor - > max = type_max ;
accessor - > min = type_min ;
accessor - > normalized = false ;
accessor - > count = p_attribs . size ( ) ;
2024-04-12 04:29:51 +00:00
accessor - > accessor_type = accessor_type ;
2020-12-21 15:39:32 +00:00
accessor - > component_type = component_type ;
accessor - > byte_offset = 0 ;
2024-04-12 04:29:51 +00:00
Error err = _encode_buffer_view ( p_state , attribs . ptr ( ) , p_attribs . size ( ) , accessor_type , component_type , accessor - > normalized , size , p_for_vertex , buffer_view_i ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return - 1 ;
}
accessor - > buffer_view = buffer_view_i ;
2022-12-10 21:05:13 +00:00
p_state - > accessors . push_back ( accessor ) ;
return p_state - > accessors . size ( ) - 1 ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
GLTFAccessorIndex GLTFDocument : : _encode_accessor_as_joints ( Ref < GLTFState > p_state , const Vector < Color > p_attribs , const bool p_for_vertex ) {
2020-12-21 15:39:32 +00:00
if ( p_attribs . size ( ) = = 0 ) {
return - 1 ;
}
const int element_count = 4 ;
const int ret_size = p_attribs . size ( ) * element_count ;
Vector < double > attribs ;
attribs . resize ( ret_size ) ;
Vector < double > type_max ;
type_max . resize ( element_count ) ;
Vector < double > type_min ;
type_min . resize ( element_count ) ;
for ( int i = 0 ; i < p_attribs . size ( ) ; i + + ) {
Color attrib = p_attribs [ i ] ;
2024-03-10 07:51:52 +00:00
attribs . write [ ( i * element_count ) + 0 ] = _filter_number ( attrib . r ) ;
attribs . write [ ( i * element_count ) + 1 ] = _filter_number ( attrib . g ) ;
attribs . write [ ( i * element_count ) + 2 ] = _filter_number ( attrib . b ) ;
attribs . write [ ( i * element_count ) + 3 ] = _filter_number ( attrib . a ) ;
2020-12-21 15:39:32 +00:00
_calc_accessor_min_max ( i , element_count , type_max , attribs , type_min ) ;
}
2024-03-10 07:51:52 +00:00
_round_min_max_components ( type_min , type_max ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( attribs . size ( ) % element_count ! = 0 , - 1 ) ;
Ref < GLTFAccessor > accessor ;
2021-06-17 22:03:09 +00:00
accessor . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
GLTFBufferIndex buffer_view_i ;
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . is_empty ( ) ) {
p_state - > buffers . push_back ( Vector < uint8_t > ( ) ) ;
}
2022-12-10 21:05:13 +00:00
int64_t size = p_state - > buffers [ 0 ] . size ( ) ;
2024-07-04 02:57:17 +00:00
const GLTFAccessor : : GLTFAccessorType accessor_type = GLTFAccessor : : TYPE_VEC4 ;
2024-07-10 08:02:36 +00:00
const GLTFAccessor : : GLTFComponentType component_type = GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_SHORT ;
2020-12-21 15:39:32 +00:00
accessor - > max = type_max ;
accessor - > min = type_min ;
accessor - > normalized = false ;
accessor - > count = p_attribs . size ( ) ;
2024-04-12 04:29:51 +00:00
accessor - > accessor_type = accessor_type ;
2020-12-21 15:39:32 +00:00
accessor - > component_type = component_type ;
accessor - > byte_offset = 0 ;
2024-04-12 04:29:51 +00:00
Error err = _encode_buffer_view ( p_state , attribs . ptr ( ) , p_attribs . size ( ) , accessor_type , component_type , accessor - > normalized , size , p_for_vertex , buffer_view_i ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return - 1 ;
}
accessor - > buffer_view = buffer_view_i ;
2022-12-10 21:05:13 +00:00
p_state - > accessors . push_back ( accessor ) ;
return p_state - > accessors . size ( ) - 1 ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
GLTFAccessorIndex GLTFDocument : : _encode_accessor_as_quaternions ( Ref < GLTFState > p_state , const Vector < Quaternion > p_attribs , const bool p_for_vertex ) {
2020-12-21 15:39:32 +00:00
if ( p_attribs . size ( ) = = 0 ) {
return - 1 ;
}
const int element_count = 4 ;
const int ret_size = p_attribs . size ( ) * element_count ;
Vector < double > attribs ;
attribs . resize ( ret_size ) ;
Vector < double > type_max ;
type_max . resize ( element_count ) ;
Vector < double > type_min ;
type_min . resize ( element_count ) ;
for ( int i = 0 ; i < p_attribs . size ( ) ; i + + ) {
2021-01-20 07:02:02 +00:00
Quaternion quaternion = p_attribs [ i ] ;
2024-03-10 07:51:52 +00:00
attribs . write [ ( i * element_count ) + 0 ] = _filter_number ( quaternion . x ) ;
attribs . write [ ( i * element_count ) + 1 ] = _filter_number ( quaternion . y ) ;
attribs . write [ ( i * element_count ) + 2 ] = _filter_number ( quaternion . z ) ;
attribs . write [ ( i * element_count ) + 3 ] = _filter_number ( quaternion . w ) ;
2020-12-21 15:39:32 +00:00
_calc_accessor_min_max ( i , element_count , type_max , attribs , type_min ) ;
}
2024-03-10 07:51:52 +00:00
_round_min_max_components ( type_min , type_max ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( attribs . size ( ) % element_count ! = 0 , - 1 ) ;
Ref < GLTFAccessor > accessor ;
2021-06-17 22:03:09 +00:00
accessor . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
GLTFBufferIndex buffer_view_i ;
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . is_empty ( ) ) {
p_state - > buffers . push_back ( Vector < uint8_t > ( ) ) ;
}
2022-12-10 21:05:13 +00:00
int64_t size = p_state - > buffers [ 0 ] . size ( ) ;
2024-07-04 02:57:17 +00:00
const GLTFAccessor : : GLTFAccessorType accessor_type = GLTFAccessor : : TYPE_VEC4 ;
2024-07-10 08:02:36 +00:00
const GLTFAccessor : : GLTFComponentType component_type = GLTFAccessor : : COMPONENT_TYPE_SINGLE_FLOAT ;
2020-12-21 15:39:32 +00:00
accessor - > max = type_max ;
accessor - > min = type_min ;
accessor - > normalized = false ;
accessor - > count = p_attribs . size ( ) ;
2024-04-12 04:29:51 +00:00
accessor - > accessor_type = accessor_type ;
2020-12-21 15:39:32 +00:00
accessor - > component_type = component_type ;
accessor - > byte_offset = 0 ;
2024-04-12 04:29:51 +00:00
Error err = _encode_buffer_view ( p_state , attribs . ptr ( ) , p_attribs . size ( ) , accessor_type , component_type , accessor - > normalized , size , p_for_vertex , buffer_view_i ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return - 1 ;
}
accessor - > buffer_view = buffer_view_i ;
2022-12-10 21:05:13 +00:00
p_state - > accessors . push_back ( accessor ) ;
return p_state - > accessors . size ( ) - 1 ;
2020-12-21 15:39:32 +00:00
}
2024-03-12 11:50:54 +00:00
Vector < Vector2 > GLTFDocument : : _decode_accessor_as_vec2 ( Ref < GLTFState > p_state , const GLTFAccessorIndex p_accessor , const bool p_for_vertex , const Vector < int > & p_packed_vertex_ids ) {
2022-12-10 21:05:13 +00:00
const Vector < double > attribs = _decode_accessor ( p_state , p_accessor , p_for_vertex ) ;
2020-12-21 15:39:32 +00:00
Vector < Vector2 > ret ;
2021-04-05 12:09:59 +00:00
if ( attribs . size ( ) = = 0 ) {
2020-12-21 15:39:32 +00:00
return ret ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( attribs . size ( ) % 2 ! = 0 , ret ) ;
const double * attribs_ptr = attribs . ptr ( ) ;
2024-03-12 11:50:54 +00:00
int ret_size = attribs . size ( ) / 2 ;
if ( ! p_packed_vertex_ids . is_empty ( ) ) {
ERR_FAIL_COND_V ( p_packed_vertex_ids [ p_packed_vertex_ids . size ( ) - 1 ] > = ret_size , ret ) ;
ret_size = p_packed_vertex_ids . size ( ) ;
}
2020-12-21 15:39:32 +00:00
ret . resize ( ret_size ) ;
2024-03-12 11:50:54 +00:00
for ( int i = 0 ; i < ret_size ; i + + ) {
int src_i = i ;
if ( ! p_packed_vertex_ids . is_empty ( ) ) {
src_i = p_packed_vertex_ids [ i ] ;
2020-12-21 15:39:32 +00:00
}
2024-03-12 11:50:54 +00:00
ret . write [ i ] = Vector2 ( attribs_ptr [ src_i * 2 + 0 ] , attribs_ptr [ src_i * 2 + 1 ] ) ;
2020-12-21 15:39:32 +00:00
}
return ret ;
}
2024-07-10 08:26:35 +00:00
GLTFAccessorIndex GLTFDocument : : _encode_accessor_as_floats ( Ref < GLTFState > p_state , const Vector < double > p_attribs , const bool p_for_vertex ) {
2020-12-21 15:39:32 +00:00
if ( p_attribs . size ( ) = = 0 ) {
return - 1 ;
}
const int element_count = 1 ;
const int ret_size = p_attribs . size ( ) ;
Vector < double > attribs ;
attribs . resize ( ret_size ) ;
Vector < double > type_max ;
type_max . resize ( element_count ) ;
Vector < double > type_min ;
type_min . resize ( element_count ) ;
for ( int i = 0 ; i < p_attribs . size ( ) ; i + + ) {
2024-03-10 07:51:52 +00:00
attribs . write [ i ] = _filter_number ( p_attribs [ i ] ) ;
2020-12-21 15:39:32 +00:00
_calc_accessor_min_max ( i , element_count , type_max , attribs , type_min ) ;
}
2024-03-10 07:51:52 +00:00
_round_min_max_components ( type_min , type_max ) ;
2020-12-21 15:39:32 +00:00
2024-01-19 12:21:39 +00:00
ERR_FAIL_COND_V ( attribs . is_empty ( ) , - 1 ) ;
2020-12-21 15:39:32 +00:00
Ref < GLTFAccessor > accessor ;
2021-06-17 22:03:09 +00:00
accessor . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
GLTFBufferIndex buffer_view_i ;
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . is_empty ( ) ) {
p_state - > buffers . push_back ( Vector < uint8_t > ( ) ) ;
}
2022-12-10 21:05:13 +00:00
int64_t size = p_state - > buffers [ 0 ] . size ( ) ;
2024-07-04 02:57:17 +00:00
const GLTFAccessor : : GLTFAccessorType accessor_type = GLTFAccessor : : TYPE_SCALAR ;
2024-07-10 08:02:36 +00:00
const GLTFAccessor : : GLTFComponentType component_type = GLTFAccessor : : COMPONENT_TYPE_SINGLE_FLOAT ;
2020-12-21 15:39:32 +00:00
accessor - > max = type_max ;
accessor - > min = type_min ;
accessor - > normalized = false ;
accessor - > count = ret_size ;
2024-04-12 04:29:51 +00:00
accessor - > accessor_type = accessor_type ;
2020-12-21 15:39:32 +00:00
accessor - > component_type = component_type ;
accessor - > byte_offset = 0 ;
2024-04-12 04:29:51 +00:00
Error err = _encode_buffer_view ( p_state , attribs . ptr ( ) , attribs . size ( ) , accessor_type , component_type , accessor - > normalized , size , p_for_vertex , buffer_view_i ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return - 1 ;
}
accessor - > buffer_view = buffer_view_i ;
2022-12-10 21:05:13 +00:00
p_state - > accessors . push_back ( accessor ) ;
return p_state - > accessors . size ( ) - 1 ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
GLTFAccessorIndex GLTFDocument : : _encode_accessor_as_vec3 ( Ref < GLTFState > p_state , const Vector < Vector3 > p_attribs , const bool p_for_vertex ) {
2020-12-21 15:39:32 +00:00
if ( p_attribs . size ( ) = = 0 ) {
return - 1 ;
}
const int element_count = 3 ;
const int ret_size = p_attribs . size ( ) * element_count ;
Vector < double > attribs ;
attribs . resize ( ret_size ) ;
Vector < double > type_max ;
type_max . resize ( element_count ) ;
Vector < double > type_min ;
type_min . resize ( element_count ) ;
for ( int i = 0 ; i < p_attribs . size ( ) ; i + + ) {
Vector3 attrib = p_attribs [ i ] ;
2024-03-10 07:51:52 +00:00
attribs . write [ ( i * element_count ) + 0 ] = _filter_number ( attrib . x ) ;
attribs . write [ ( i * element_count ) + 1 ] = _filter_number ( attrib . y ) ;
attribs . write [ ( i * element_count ) + 2 ] = _filter_number ( attrib . z ) ;
2020-12-21 15:39:32 +00:00
_calc_accessor_min_max ( i , element_count , type_max , attribs , type_min ) ;
}
2024-03-10 07:51:52 +00:00
_round_min_max_components ( type_min , type_max ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( attribs . size ( ) % element_count ! = 0 , - 1 ) ;
Ref < GLTFAccessor > accessor ;
2021-06-17 22:03:09 +00:00
accessor . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
GLTFBufferIndex buffer_view_i ;
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . is_empty ( ) ) {
p_state - > buffers . push_back ( Vector < uint8_t > ( ) ) ;
}
2022-12-10 21:05:13 +00:00
int64_t size = p_state - > buffers [ 0 ] . size ( ) ;
2024-07-04 02:57:17 +00:00
const GLTFAccessor : : GLTFAccessorType accessor_type = GLTFAccessor : : TYPE_VEC3 ;
2024-07-10 08:02:36 +00:00
const GLTFAccessor : : GLTFComponentType component_type = GLTFAccessor : : COMPONENT_TYPE_SINGLE_FLOAT ;
2020-12-21 15:39:32 +00:00
accessor - > max = type_max ;
accessor - > min = type_min ;
accessor - > normalized = false ;
accessor - > count = p_attribs . size ( ) ;
2024-04-12 04:29:51 +00:00
accessor - > accessor_type = accessor_type ;
2020-12-21 15:39:32 +00:00
accessor - > component_type = component_type ;
accessor - > byte_offset = 0 ;
2024-04-12 04:29:51 +00:00
Error err = _encode_buffer_view ( p_state , attribs . ptr ( ) , p_attribs . size ( ) , accessor_type , component_type , accessor - > normalized , size , p_for_vertex , buffer_view_i ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return - 1 ;
}
accessor - > buffer_view = buffer_view_i ;
2022-12-10 21:05:13 +00:00
p_state - > accessors . push_back ( accessor ) ;
return p_state - > accessors . size ( ) - 1 ;
2020-12-21 15:39:32 +00:00
}
2024-03-12 09:39:15 +00:00
GLTFAccessorIndex GLTFDocument : : _encode_sparse_accessor_as_vec3 ( Ref < GLTFState > p_state , const Vector < Vector3 > p_attribs , const Vector < Vector3 > p_reference_attribs , const float p_reference_multiplier , const bool p_for_vertex , const GLTFAccessorIndex p_reference_accessor ) {
2024-03-01 21:07:07 +00:00
if ( p_attribs . size ( ) = = 0 ) {
return - 1 ;
}
const int element_count = 3 ;
Vector < double > attribs ;
Vector < double > type_max ;
Vector < double > type_min ;
attribs . resize ( p_attribs . size ( ) * element_count ) ;
type_max . resize ( element_count ) ;
type_min . resize ( element_count ) ;
Vector < double > changed_indices ;
Vector < double > changed_values ;
int max_changed_index = 0 ;
for ( int i = 0 ; i < p_attribs . size ( ) ; i + + ) {
Vector3 attrib = p_attribs [ i ] ;
bool is_different = false ;
if ( i < p_reference_attribs . size ( ) ) {
2024-03-12 09:39:15 +00:00
is_different = ! ( attrib * p_reference_multiplier ) . is_equal_approx ( p_reference_attribs [ i ] ) ;
if ( ! is_different ) {
attrib = p_reference_attribs [ i ] ;
}
2024-03-01 21:07:07 +00:00
} else {
2024-03-12 09:39:15 +00:00
is_different = ! ( attrib * p_reference_multiplier ) . is_zero_approx ( ) ;
if ( ! is_different ) {
attrib = Vector3 ( ) ;
}
2024-03-01 21:07:07 +00:00
}
2024-03-12 09:39:15 +00:00
attribs . write [ ( i * element_count ) + 0 ] = _filter_number ( attrib . x ) ;
attribs . write [ ( i * element_count ) + 1 ] = _filter_number ( attrib . y ) ;
attribs . write [ ( i * element_count ) + 2 ] = _filter_number ( attrib . z ) ;
2024-03-01 21:07:07 +00:00
if ( is_different ) {
changed_indices . push_back ( i ) ;
if ( i > max_changed_index ) {
max_changed_index = i ;
}
changed_values . push_back ( _filter_number ( attrib . x ) ) ;
changed_values . push_back ( _filter_number ( attrib . y ) ) ;
changed_values . push_back ( _filter_number ( attrib . z ) ) ;
}
_calc_accessor_min_max ( i , element_count , type_max , attribs , type_min ) ;
}
_round_min_max_components ( type_min , type_max ) ;
if ( attribs . size ( ) % element_count ! = 0 ) {
return - 1 ;
}
Ref < GLTFAccessor > sparse_accessor ;
sparse_accessor . instantiate ( ) ;
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . is_empty ( ) ) {
p_state - > buffers . push_back ( Vector < uint8_t > ( ) ) ;
}
2024-03-01 21:07:07 +00:00
int64_t size = p_state - > buffers [ 0 ] . size ( ) ;
2024-07-04 02:57:17 +00:00
const GLTFAccessor : : GLTFAccessorType accessor_type = GLTFAccessor : : TYPE_VEC3 ;
2024-07-10 08:02:36 +00:00
const GLTFAccessor : : GLTFComponentType component_type = GLTFAccessor : : COMPONENT_TYPE_SINGLE_FLOAT ;
2024-03-01 21:07:07 +00:00
sparse_accessor - > normalized = false ;
sparse_accessor - > count = p_attribs . size ( ) ;
2024-04-12 04:29:51 +00:00
sparse_accessor - > accessor_type = accessor_type ;
2024-03-01 21:07:07 +00:00
sparse_accessor - > component_type = component_type ;
if ( p_reference_accessor < p_state - > accessors . size ( ) & & p_reference_accessor > = 0 & & p_state - > accessors [ p_reference_accessor ] . is_valid ( ) ) {
sparse_accessor - > byte_offset = p_state - > accessors [ p_reference_accessor ] - > byte_offset ;
sparse_accessor - > buffer_view = p_state - > accessors [ p_reference_accessor ] - > buffer_view ;
}
sparse_accessor - > max = type_max ;
sparse_accessor - > min = type_min ;
int sparse_accessor_index_stride = max_changed_index > 65535 ? 4 : 2 ;
int sparse_accessor_storage_size = changed_indices . size ( ) * ( sparse_accessor_index_stride + element_count * sizeof ( float ) ) ;
int conventional_storage_size = p_attribs . size ( ) * element_count * sizeof ( float ) ;
if ( changed_indices . size ( ) > 0 & & sparse_accessor_storage_size < conventional_storage_size ) {
// It must be worthwhile to use a sparse accessor.
GLTFBufferIndex buffer_view_i_indices = - 1 ;
GLTFBufferIndex buffer_view_i_values = - 1 ;
if ( sparse_accessor_index_stride = = 4 ) {
2024-07-10 08:02:36 +00:00
sparse_accessor - > sparse_indices_component_type = GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_INT ;
2024-03-01 21:07:07 +00:00
} else {
2024-07-10 08:02:36 +00:00
sparse_accessor - > sparse_indices_component_type = GLTFAccessor : : COMPONENT_TYPE_UNSIGNED_SHORT ;
2024-03-01 21:07:07 +00:00
}
2024-07-04 02:57:17 +00:00
if ( _encode_buffer_view ( p_state , changed_indices . ptr ( ) , changed_indices . size ( ) , GLTFAccessor : : TYPE_SCALAR , sparse_accessor - > sparse_indices_component_type , sparse_accessor - > normalized , sparse_accessor - > sparse_indices_byte_offset , false , buffer_view_i_indices ) ! = OK ) {
2024-03-01 21:07:07 +00:00
return - 1 ;
}
// We use changed_indices.size() here, because we must pass the number of vec3 values rather than the number of components.
2024-04-12 04:29:51 +00:00
if ( _encode_buffer_view ( p_state , changed_values . ptr ( ) , changed_indices . size ( ) , sparse_accessor - > accessor_type , sparse_accessor - > component_type , sparse_accessor - > normalized , sparse_accessor - > sparse_values_byte_offset , false , buffer_view_i_values ) ! = OK ) {
2024-03-01 21:07:07 +00:00
return - 1 ;
}
sparse_accessor - > sparse_indices_buffer_view = buffer_view_i_indices ;
sparse_accessor - > sparse_values_buffer_view = buffer_view_i_values ;
sparse_accessor - > sparse_count = changed_indices . size ( ) ;
} else if ( changed_indices . size ( ) > 0 ) {
GLTFBufferIndex buffer_view_i ;
sparse_accessor - > byte_offset = 0 ;
2024-04-12 04:29:51 +00:00
Error err = _encode_buffer_view ( p_state , attribs . ptr ( ) , p_attribs . size ( ) , accessor_type , component_type , sparse_accessor - > normalized , size , p_for_vertex , buffer_view_i ) ;
2024-03-01 21:07:07 +00:00
if ( err ! = OK ) {
return - 1 ;
}
sparse_accessor - > buffer_view = buffer_view_i ;
}
p_state - > accessors . push_back ( sparse_accessor ) ;
return p_state - > accessors . size ( ) - 1 ;
}
2022-12-10 21:05:13 +00:00
GLTFAccessorIndex GLTFDocument : : _encode_accessor_as_xform ( Ref < GLTFState > p_state , const Vector < Transform3D > p_attribs , const bool p_for_vertex ) {
2020-12-21 15:39:32 +00:00
if ( p_attribs . size ( ) = = 0 ) {
return - 1 ;
}
const int element_count = 16 ;
const int ret_size = p_attribs . size ( ) * element_count ;
Vector < double > attribs ;
attribs . resize ( ret_size ) ;
Vector < double > type_max ;
type_max . resize ( element_count ) ;
Vector < double > type_min ;
type_min . resize ( element_count ) ;
for ( int i = 0 ; i < p_attribs . size ( ) ; i + + ) {
2020-10-17 05:08:21 +00:00
Transform3D attrib = p_attribs [ i ] ;
2020-12-21 15:39:32 +00:00
Basis basis = attrib . get_basis ( ) ;
2022-05-03 12:50:35 +00:00
Vector3 axis_0 = basis . get_column ( Vector3 : : AXIS_X ) ;
2020-12-21 15:39:32 +00:00
2024-03-10 07:51:52 +00:00
attribs . write [ i * element_count + 0 ] = _filter_number ( axis_0 . x ) ;
attribs . write [ i * element_count + 1 ] = _filter_number ( axis_0 . y ) ;
attribs . write [ i * element_count + 2 ] = _filter_number ( axis_0 . z ) ;
2020-12-21 15:39:32 +00:00
attribs . write [ i * element_count + 3 ] = 0.0 ;
2022-05-03 12:50:35 +00:00
Vector3 axis_1 = basis . get_column ( Vector3 : : AXIS_Y ) ;
2024-03-10 07:51:52 +00:00
attribs . write [ i * element_count + 4 ] = _filter_number ( axis_1 . x ) ;
attribs . write [ i * element_count + 5 ] = _filter_number ( axis_1 . y ) ;
attribs . write [ i * element_count + 6 ] = _filter_number ( axis_1 . z ) ;
2020-12-21 15:39:32 +00:00
attribs . write [ i * element_count + 7 ] = 0.0 ;
2022-05-03 12:50:35 +00:00
Vector3 axis_2 = basis . get_column ( Vector3 : : AXIS_Z ) ;
2024-03-10 07:51:52 +00:00
attribs . write [ i * element_count + 8 ] = _filter_number ( axis_2 . x ) ;
attribs . write [ i * element_count + 9 ] = _filter_number ( axis_2 . y ) ;
attribs . write [ i * element_count + 10 ] = _filter_number ( axis_2 . z ) ;
2020-12-21 15:39:32 +00:00
attribs . write [ i * element_count + 11 ] = 0.0 ;
Vector3 origin = attrib . get_origin ( ) ;
2024-03-10 07:51:52 +00:00
attribs . write [ i * element_count + 12 ] = _filter_number ( origin . x ) ;
attribs . write [ i * element_count + 13 ] = _filter_number ( origin . y ) ;
attribs . write [ i * element_count + 14 ] = _filter_number ( origin . z ) ;
2020-12-21 15:39:32 +00:00
attribs . write [ i * element_count + 15 ] = 1.0 ;
_calc_accessor_min_max ( i , element_count , type_max , attribs , type_min ) ;
}
2024-03-10 07:51:52 +00:00
_round_min_max_components ( type_min , type_max ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( attribs . size ( ) % element_count ! = 0 , - 1 ) ;
Ref < GLTFAccessor > accessor ;
2021-06-17 22:03:09 +00:00
accessor . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
GLTFBufferIndex buffer_view_i ;
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . is_empty ( ) ) {
p_state - > buffers . push_back ( Vector < uint8_t > ( ) ) ;
}
2022-12-10 21:05:13 +00:00
int64_t size = p_state - > buffers [ 0 ] . size ( ) ;
2024-07-04 02:57:17 +00:00
const GLTFAccessor : : GLTFAccessorType accessor_type = GLTFAccessor : : TYPE_MAT4 ;
2024-07-10 08:02:36 +00:00
const GLTFAccessor : : GLTFComponentType component_type = GLTFAccessor : : COMPONENT_TYPE_SINGLE_FLOAT ;
2020-12-21 15:39:32 +00:00
accessor - > max = type_max ;
accessor - > min = type_min ;
accessor - > normalized = false ;
accessor - > count = p_attribs . size ( ) ;
2024-04-12 04:29:51 +00:00
accessor - > accessor_type = accessor_type ;
2020-12-21 15:39:32 +00:00
accessor - > component_type = component_type ;
accessor - > byte_offset = 0 ;
2024-04-12 04:29:51 +00:00
Error err = _encode_buffer_view ( p_state , attribs . ptr ( ) , p_attribs . size ( ) , accessor_type , component_type , accessor - > normalized , size , p_for_vertex , buffer_view_i ) ;
2020-12-21 15:39:32 +00:00
if ( err ! = OK ) {
return - 1 ;
}
accessor - > buffer_view = buffer_view_i ;
2022-12-10 21:05:13 +00:00
p_state - > accessors . push_back ( accessor ) ;
return p_state - > accessors . size ( ) - 1 ;
2020-12-21 15:39:32 +00:00
}
2024-03-12 11:50:54 +00:00
Vector < Vector3 > GLTFDocument : : _decode_accessor_as_vec3 ( Ref < GLTFState > p_state , const GLTFAccessorIndex p_accessor , const bool p_for_vertex , const Vector < int > & p_packed_vertex_ids ) {
2022-12-10 21:05:13 +00:00
const Vector < double > attribs = _decode_accessor ( p_state , p_accessor , p_for_vertex ) ;
2020-12-21 15:39:32 +00:00
Vector < Vector3 > ret ;
2021-04-05 12:09:59 +00:00
if ( attribs . size ( ) = = 0 ) {
2020-12-21 15:39:32 +00:00
return ret ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( attribs . size ( ) % 3 ! = 0 , ret ) ;
const double * attribs_ptr = attribs . ptr ( ) ;
2024-03-12 11:50:54 +00:00
int ret_size = attribs . size ( ) / 3 ;
if ( ! p_packed_vertex_ids . is_empty ( ) ) {
ERR_FAIL_COND_V ( p_packed_vertex_ids [ p_packed_vertex_ids . size ( ) - 1 ] > = ret_size , ret ) ;
ret_size = p_packed_vertex_ids . size ( ) ;
}
2020-12-21 15:39:32 +00:00
ret . resize ( ret_size ) ;
2024-03-12 11:50:54 +00:00
for ( int i = 0 ; i < ret_size ; i + + ) {
int src_i = i ;
if ( ! p_packed_vertex_ids . is_empty ( ) ) {
src_i = p_packed_vertex_ids [ i ] ;
2020-12-21 15:39:32 +00:00
}
2024-03-12 11:50:54 +00:00
ret . write [ i ] = Vector3 ( attribs_ptr [ src_i * 3 + 0 ] , attribs_ptr [ src_i * 3 + 1 ] , attribs_ptr [ src_i * 3 + 2 ] ) ;
2020-12-21 15:39:32 +00:00
}
return ret ;
}
2024-03-12 11:50:54 +00:00
Vector < Color > GLTFDocument : : _decode_accessor_as_color ( Ref < GLTFState > p_state , const GLTFAccessorIndex p_accessor , const bool p_for_vertex , const Vector < int > & p_packed_vertex_ids ) {
2022-12-10 21:05:13 +00:00
const Vector < double > attribs = _decode_accessor ( p_state , p_accessor , p_for_vertex ) ;
2020-12-21 15:39:32 +00:00
Vector < Color > ret ;
2021-04-05 12:09:59 +00:00
if ( attribs . size ( ) = = 0 ) {
2020-12-21 15:39:32 +00:00
return ret ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
2024-04-12 04:29:51 +00:00
const int accessor_type = p_state - > accessors [ p_accessor ] - > accessor_type ;
2024-07-04 02:57:17 +00:00
ERR_FAIL_COND_V ( ! ( accessor_type = = GLTFAccessor : : TYPE_VEC3 | | accessor_type = = GLTFAccessor : : TYPE_VEC4 ) , ret ) ;
2020-12-21 15:39:32 +00:00
int vec_len = 3 ;
2024-07-04 02:57:17 +00:00
if ( accessor_type = = GLTFAccessor : : TYPE_VEC4 ) {
2020-12-21 15:39:32 +00:00
vec_len = 4 ;
}
ERR_FAIL_COND_V ( attribs . size ( ) % vec_len ! = 0 , ret ) ;
const double * attribs_ptr = attribs . ptr ( ) ;
2024-03-12 11:50:54 +00:00
int ret_size = attribs . size ( ) / vec_len ;
if ( ! p_packed_vertex_ids . is_empty ( ) ) {
ERR_FAIL_COND_V ( p_packed_vertex_ids [ p_packed_vertex_ids . size ( ) - 1 ] > = ret_size , ret ) ;
ret_size = p_packed_vertex_ids . size ( ) ;
}
2020-12-21 15:39:32 +00:00
ret . resize ( ret_size ) ;
2024-03-12 11:50:54 +00:00
for ( int i = 0 ; i < ret_size ; i + + ) {
int src_i = i ;
if ( ! p_packed_vertex_ids . is_empty ( ) ) {
src_i = p_packed_vertex_ids [ i ] ;
2020-12-21 15:39:32 +00:00
}
2024-03-12 11:50:54 +00:00
ret . write [ i ] = Color ( attribs_ptr [ src_i * vec_len + 0 ] , attribs_ptr [ src_i * vec_len + 1 ] , attribs_ptr [ src_i * vec_len + 2 ] , vec_len = = 4 ? attribs_ptr [ src_i * 4 + 3 ] : 1.0 ) ;
2020-12-21 15:39:32 +00:00
}
return ret ;
}
2022-12-10 21:05:13 +00:00
Vector < Quaternion > GLTFDocument : : _decode_accessor_as_quaternion ( Ref < GLTFState > p_state , const GLTFAccessorIndex p_accessor , const bool p_for_vertex ) {
const Vector < double > attribs = _decode_accessor ( p_state , p_accessor , p_for_vertex ) ;
2021-01-20 07:02:02 +00:00
Vector < Quaternion > ret ;
2020-12-21 15:39:32 +00:00
2021-04-05 12:09:59 +00:00
if ( attribs . size ( ) = = 0 ) {
2020-12-21 15:39:32 +00:00
return ret ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( attribs . size ( ) % 4 ! = 0 , ret ) ;
const double * attribs_ptr = attribs . ptr ( ) ;
const int ret_size = attribs . size ( ) / 4 ;
ret . resize ( ret_size ) ;
{
for ( int i = 0 ; i < ret_size ; i + + ) {
2021-01-20 07:02:02 +00:00
ret . write [ i ] = Quaternion ( attribs_ptr [ i * 4 + 0 ] , attribs_ptr [ i * 4 + 1 ] , attribs_ptr [ i * 4 + 2 ] , attribs_ptr [ i * 4 + 3 ] ) . normalized ( ) ;
2020-12-21 15:39:32 +00:00
}
}
return ret ;
}
2022-12-10 21:05:13 +00:00
Vector < Transform2D > GLTFDocument : : _decode_accessor_as_xform2d ( Ref < GLTFState > p_state , const GLTFAccessorIndex p_accessor , const bool p_for_vertex ) {
const Vector < double > attribs = _decode_accessor ( p_state , p_accessor , p_for_vertex ) ;
2020-12-21 15:39:32 +00:00
Vector < Transform2D > ret ;
2021-04-05 12:09:59 +00:00
if ( attribs . size ( ) = = 0 ) {
2020-12-21 15:39:32 +00:00
return ret ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( attribs . size ( ) % 4 ! = 0 , ret ) ;
ret . resize ( attribs . size ( ) / 4 ) ;
for ( int i = 0 ; i < ret . size ( ) ; i + + ) {
ret . write [ i ] [ 0 ] = Vector2 ( attribs [ i * 4 + 0 ] , attribs [ i * 4 + 1 ] ) ;
ret . write [ i ] [ 1 ] = Vector2 ( attribs [ i * 4 + 2 ] , attribs [ i * 4 + 3 ] ) ;
}
return ret ;
}
2022-12-10 21:05:13 +00:00
Vector < Basis > GLTFDocument : : _decode_accessor_as_basis ( Ref < GLTFState > p_state , const GLTFAccessorIndex p_accessor , const bool p_for_vertex ) {
const Vector < double > attribs = _decode_accessor ( p_state , p_accessor , p_for_vertex ) ;
2020-12-21 15:39:32 +00:00
Vector < Basis > ret ;
2021-04-05 12:09:59 +00:00
if ( attribs . size ( ) = = 0 ) {
2020-12-21 15:39:32 +00:00
return ret ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( attribs . size ( ) % 9 ! = 0 , ret ) ;
ret . resize ( attribs . size ( ) / 9 ) ;
for ( int i = 0 ; i < ret . size ( ) ; i + + ) {
2022-05-03 12:50:35 +00:00
ret . write [ i ] . set_column ( 0 , Vector3 ( attribs [ i * 9 + 0 ] , attribs [ i * 9 + 1 ] , attribs [ i * 9 + 2 ] ) ) ;
ret . write [ i ] . set_column ( 1 , Vector3 ( attribs [ i * 9 + 3 ] , attribs [ i * 9 + 4 ] , attribs [ i * 9 + 5 ] ) ) ;
ret . write [ i ] . set_column ( 2 , Vector3 ( attribs [ i * 9 + 6 ] , attribs [ i * 9 + 7 ] , attribs [ i * 9 + 8 ] ) ) ;
2020-12-21 15:39:32 +00:00
}
return ret ;
}
2022-12-10 21:05:13 +00:00
Vector < Transform3D > GLTFDocument : : _decode_accessor_as_xform ( Ref < GLTFState > p_state , const GLTFAccessorIndex p_accessor , const bool p_for_vertex ) {
const Vector < double > attribs = _decode_accessor ( p_state , p_accessor , p_for_vertex ) ;
2020-10-17 05:08:21 +00:00
Vector < Transform3D > ret ;
2020-12-21 15:39:32 +00:00
2021-04-05 12:09:59 +00:00
if ( attribs . size ( ) = = 0 ) {
2020-12-21 15:39:32 +00:00
return ret ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( attribs . size ( ) % 16 ! = 0 , ret ) ;
ret . resize ( attribs . size ( ) / 16 ) ;
for ( int i = 0 ; i < ret . size ( ) ; i + + ) {
2022-05-03 12:50:35 +00:00
ret . write [ i ] . basis . set_column ( 0 , Vector3 ( attribs [ i * 16 + 0 ] , attribs [ i * 16 + 1 ] , attribs [ i * 16 + 2 ] ) ) ;
ret . write [ i ] . basis . set_column ( 1 , Vector3 ( attribs [ i * 16 + 4 ] , attribs [ i * 16 + 5 ] , attribs [ i * 16 + 6 ] ) ) ;
ret . write [ i ] . basis . set_column ( 2 , Vector3 ( attribs [ i * 16 + 8 ] , attribs [ i * 16 + 9 ] , attribs [ i * 16 + 10 ] ) ) ;
2020-12-21 15:39:32 +00:00
ret . write [ i ] . set_origin ( Vector3 ( attribs [ i * 16 + 12 ] , attribs [ i * 16 + 13 ] , attribs [ i * 16 + 14 ] ) ) ;
}
return ret ;
}
2024-06-29 18:14:09 +00:00
Vector < Variant > GLTFDocument : : _decode_accessor_as_variant ( Ref < GLTFState > p_state , const GLTFAccessorIndex p_accessor , Variant : : Type p_variant_type , GLTFAccessor : : GLTFAccessorType p_accessor_type ) {
const Vector < double > attribs = _decode_accessor ( p_state , p_accessor , false ) ;
Vector < Variant > ret ;
ERR_FAIL_COND_V_MSG ( attribs . is_empty ( ) , ret , " glTF: The accessor was empty. " ) ;
const int component_count = COMPONENT_COUNT_FOR_ACCESSOR_TYPE [ p_accessor_type ] ;
ERR_FAIL_COND_V_MSG ( attribs . size ( ) % component_count ! = 0 , ret , " glTF: The accessor size was not a multiple of the component count. " ) ;
const int ret_size = attribs . size ( ) / component_count ;
ret . resize ( ret_size ) ;
for ( int i = 0 ; i < ret_size ; i + + ) {
switch ( p_variant_type ) {
case Variant : : BOOL : {
ret . write [ i ] = attribs [ i * component_count ] ! = 0.0 ;
} break ;
case Variant : : INT : {
ret . write [ i ] = ( int64_t ) attribs [ i * component_count ] ;
} break ;
case Variant : : FLOAT : {
ret . write [ i ] = attribs [ i * component_count ] ;
} break ;
case Variant : : VECTOR2 :
case Variant : : RECT2 :
case Variant : : VECTOR3 :
case Variant : : VECTOR4 :
case Variant : : PLANE :
case Variant : : QUATERNION : {
// General-purpose code for importing glTF accessor data with any component count into structs up to 4 `real_t`s in size.
Variant v ;
switch ( component_count ) {
case 1 : {
v = Vector4 ( attribs [ i * component_count ] , 0.0f , 0.0f , 0.0f ) ;
} break ;
case 2 : {
v = Vector4 ( attribs [ i * component_count ] , attribs [ i * component_count + 1 ] , 0.0f , 0.0f ) ;
} break ;
case 3 : {
v = Vector4 ( attribs [ i * component_count ] , attribs [ i * component_count + 1 ] , attribs [ i * component_count + 2 ] , 0.0f ) ;
} break ;
default : {
v = Vector4 ( attribs [ i * component_count ] , attribs [ i * component_count + 1 ] , attribs [ i * component_count + 2 ] , attribs [ i * component_count + 3 ] ) ;
} break ;
}
// Evil hack that relies on the structure of Variant, but it's the
// only way to accomplish this without a ton of code duplication.
* ( Variant : : Type * ) & v = p_variant_type ;
ret . write [ i ] = v ;
} break ;
case Variant : : VECTOR2I :
case Variant : : RECT2I :
case Variant : : VECTOR3I :
case Variant : : VECTOR4I : {
// General-purpose code for importing glTF accessor data with any component count into structs up to 4 `int32_t`s in size.
Variant v ;
switch ( component_count ) {
case 1 : {
v = Vector4i ( ( int32_t ) attribs [ i * component_count ] , 0 , 0 , 0 ) ;
} break ;
case 2 : {
v = Vector4i ( ( int32_t ) attribs [ i * component_count ] , ( int32_t ) attribs [ i * component_count + 1 ] , 0 , 0 ) ;
} break ;
case 3 : {
v = Vector4i ( ( int32_t ) attribs [ i * component_count ] , ( int32_t ) attribs [ i * component_count + 1 ] , ( int32_t ) attribs [ i * component_count + 2 ] , 0 ) ;
} break ;
default : {
v = Vector4i ( ( int32_t ) attribs [ i * component_count ] , ( int32_t ) attribs [ i * component_count + 1 ] , ( int32_t ) attribs [ i * component_count + 2 ] , ( int32_t ) attribs [ i * component_count + 3 ] ) ;
} break ;
}
// Evil hack that relies on the structure of Variant, but it's the
// only way to accomplish this without a ton of code duplication.
* ( Variant : : Type * ) & v = p_variant_type ;
ret . write [ i ] = v ;
} break ;
// No more generalized hacks, each of the below types needs a lot of repetitive code.
case Variant : : COLOR : {
Variant v ;
switch ( component_count ) {
case 1 : {
v = Color ( attribs [ i * component_count ] , 0.0f , 0.0f , 1.0f ) ;
} break ;
case 2 : {
v = Color ( attribs [ i * component_count ] , attribs [ i * component_count + 1 ] , 0.0f , 1.0f ) ;
} break ;
case 3 : {
v = Color ( attribs [ i * component_count ] , attribs [ i * component_count + 1 ] , attribs [ i * component_count + 2 ] , 1.0f ) ;
} break ;
default : {
v = Color ( attribs [ i * component_count ] , attribs [ i * component_count + 1 ] , attribs [ i * component_count + 2 ] , attribs [ i * component_count + 3 ] ) ;
} break ;
}
ret . write [ i ] = v ;
} break ;
case Variant : : TRANSFORM2D : {
Transform2D t ;
switch ( component_count ) {
case 4 : {
t . columns [ 0 ] = Vector2 ( attribs [ i * component_count + 0 ] , attribs [ i * component_count + 1 ] ) ;
t . columns [ 1 ] = Vector2 ( attribs [ i * component_count + 2 ] , attribs [ i * component_count + 3 ] ) ;
} break ;
case 9 : {
t . columns [ 0 ] = Vector2 ( attribs [ i * component_count + 0 ] , attribs [ i * component_count + 1 ] ) ;
t . columns [ 1 ] = Vector2 ( attribs [ i * component_count + 3 ] , attribs [ i * component_count + 4 ] ) ;
t . columns [ 2 ] = Vector2 ( attribs [ i * component_count + 6 ] , attribs [ i * component_count + 7 ] ) ;
} break ;
case 16 : {
t . columns [ 0 ] = Vector2 ( attribs [ i * component_count + 0 ] , attribs [ i * component_count + 1 ] ) ;
t . columns [ 1 ] = Vector2 ( attribs [ i * component_count + 4 ] , attribs [ i * component_count + 5 ] ) ;
t . columns [ 2 ] = Vector2 ( attribs [ i * component_count + 12 ] , attribs [ i * component_count + 13 ] ) ;
} break ;
}
ret . write [ i ] = t ;
} break ;
case Variant : : BASIS : {
Basis b ;
switch ( component_count ) {
case 4 : {
b . rows [ 0 ] = Vector3 ( attribs [ i * component_count + 0 ] , attribs [ i * component_count + 2 ] , 0.0f ) ;
b . rows [ 1 ] = Vector3 ( attribs [ i * component_count + 1 ] , attribs [ i * component_count + 3 ] , 0.0f ) ;
} break ;
case 9 : {
b . rows [ 0 ] = Vector3 ( attribs [ i * component_count + 0 ] , attribs [ i * component_count + 3 ] , attribs [ i * component_count + 6 ] ) ;
b . rows [ 1 ] = Vector3 ( attribs [ i * component_count + 1 ] , attribs [ i * component_count + 4 ] , attribs [ i * component_count + 7 ] ) ;
b . rows [ 2 ] = Vector3 ( attribs [ i * component_count + 2 ] , attribs [ i * component_count + 5 ] , attribs [ i * component_count + 8 ] ) ;
} break ;
case 16 : {
b . rows [ 0 ] = Vector3 ( attribs [ i * component_count + 0 ] , attribs [ i * component_count + 4 ] , attribs [ i * component_count + 8 ] ) ;
b . rows [ 1 ] = Vector3 ( attribs [ i * component_count + 1 ] , attribs [ i * component_count + 5 ] , attribs [ i * component_count + 9 ] ) ;
b . rows [ 2 ] = Vector3 ( attribs [ i * component_count + 2 ] , attribs [ i * component_count + 6 ] , attribs [ i * component_count + 10 ] ) ;
} break ;
}
ret . write [ i ] = b ;
} break ;
case Variant : : TRANSFORM3D : {
Transform3D t ;
switch ( component_count ) {
case 4 : {
t . basis . rows [ 0 ] = Vector3 ( attribs [ i * component_count + 0 ] , attribs [ i * component_count + 2 ] , 0.0f ) ;
t . basis . rows [ 1 ] = Vector3 ( attribs [ i * component_count + 1 ] , attribs [ i * component_count + 3 ] , 0.0f ) ;
} break ;
case 9 : {
t . basis . rows [ 0 ] = Vector3 ( attribs [ i * component_count + 0 ] , attribs [ i * component_count + 3 ] , attribs [ i * component_count + 6 ] ) ;
t . basis . rows [ 1 ] = Vector3 ( attribs [ i * component_count + 1 ] , attribs [ i * component_count + 4 ] , attribs [ i * component_count + 7 ] ) ;
t . basis . rows [ 2 ] = Vector3 ( attribs [ i * component_count + 2 ] , attribs [ i * component_count + 5 ] , attribs [ i * component_count + 8 ] ) ;
} break ;
case 16 : {
t . basis . rows [ 0 ] = Vector3 ( attribs [ i * component_count + 0 ] , attribs [ i * component_count + 4 ] , attribs [ i * component_count + 8 ] ) ;
t . basis . rows [ 1 ] = Vector3 ( attribs [ i * component_count + 1 ] , attribs [ i * component_count + 5 ] , attribs [ i * component_count + 9 ] ) ;
t . basis . rows [ 2 ] = Vector3 ( attribs [ i * component_count + 2 ] , attribs [ i * component_count + 6 ] , attribs [ i * component_count + 10 ] ) ;
t . origin = Vector3 ( attribs [ i * component_count + 12 ] , attribs [ i * component_count + 13 ] , attribs [ i * component_count + 14 ] ) ;
} break ;
}
ret . write [ i ] = t ;
} break ;
case Variant : : PROJECTION : {
Projection p ;
switch ( component_count ) {
case 4 : {
p . columns [ 0 ] = Vector4 ( attribs [ i * component_count + 0 ] , attribs [ i * component_count + 1 ] , 0.0f , 0.0f ) ;
p . columns [ 1 ] = Vector4 ( attribs [ i * component_count + 4 ] , attribs [ i * component_count + 5 ] , 0.0f , 0.0f ) ;
} break ;
case 9 : {
p . columns [ 0 ] = Vector4 ( attribs [ i * component_count + 0 ] , attribs [ i * component_count + 1 ] , attribs [ i * component_count + 2 ] , 0.0f ) ;
p . columns [ 1 ] = Vector4 ( attribs [ i * component_count + 4 ] , attribs [ i * component_count + 5 ] , attribs [ i * component_count + 6 ] , 0.0f ) ;
p . columns [ 2 ] = Vector4 ( attribs [ i * component_count + 8 ] , attribs [ i * component_count + 9 ] , attribs [ i * component_count + 10 ] , 0.0f ) ;
} break ;
case 16 : {
p . columns [ 0 ] = Vector4 ( attribs [ i * component_count + 0 ] , attribs [ i * component_count + 1 ] , attribs [ i * component_count + 2 ] , attribs [ i * component_count + 3 ] ) ;
p . columns [ 1 ] = Vector4 ( attribs [ i * component_count + 4 ] , attribs [ i * component_count + 5 ] , attribs [ i * component_count + 6 ] , attribs [ i * component_count + 7 ] ) ;
p . columns [ 2 ] = Vector4 ( attribs [ i * component_count + 8 ] , attribs [ i * component_count + 9 ] , attribs [ i * component_count + 10 ] , attribs [ i * component_count + 11 ] ) ;
p . columns [ 3 ] = Vector4 ( attribs [ i * component_count + 12 ] , attribs [ i * component_count + 13 ] , attribs [ i * component_count + 14 ] , attribs [ i * component_count + 15 ] ) ;
} break ;
}
ret . write [ i ] = p ;
} break ;
default : {
ERR_FAIL_V_MSG ( ret , " glTF: Cannot decode accessor as Variant of type " + Variant : : get_type_name ( p_variant_type ) + " . " ) ;
}
}
}
return ret ;
}
GLTFAccessorIndex GLTFDocument : : _encode_accessor_as_variant ( Ref < GLTFState > p_state , Vector < Variant > p_attribs , Variant : : Type p_variant_type , GLTFAccessor : : GLTFAccessorType p_accessor_type , GLTFAccessor : : GLTFComponentType p_component_type ) {
const int accessor_component_count = COMPONENT_COUNT_FOR_ACCESSOR_TYPE [ p_accessor_type ] ;
Vector < double > encoded_attribs ;
for ( const Variant & v : p_attribs ) {
switch ( p_variant_type ) {
case Variant : : NIL :
case Variant : : BOOL :
case Variant : : INT :
case Variant : : FLOAT : {
// For scalar values, just append them. Variant can convert all of these to double. Some padding may also be needed.
encoded_attribs . append ( v ) ;
if ( unlikely ( accessor_component_count > 1 ) ) {
for ( int i = 1 ; i < accessor_component_count ; i + + ) {
encoded_attribs . append ( 0.0 ) ;
}
}
} break ;
case Variant : : VECTOR2 :
case Variant : : VECTOR2I :
case Variant : : VECTOR3 :
case Variant : : VECTOR3I :
case Variant : : VECTOR4 :
case Variant : : VECTOR4I : {
// Variant can handle converting Vector2/2i/3/3i/4/4i to Vector4 for us.
Vector4 vec = v ;
if ( likely ( accessor_component_count < 5 ) ) {
for ( int i = 0 ; i < accessor_component_count ; i + + ) {
encoded_attribs . append ( vec [ i ] ) ;
}
}
} break ;
case Variant : : PLANE : {
Plane p = v ;
if ( likely ( accessor_component_count = = 4 ) ) {
encoded_attribs . append ( p . normal . x ) ;
encoded_attribs . append ( p . normal . y ) ;
encoded_attribs . append ( p . normal . z ) ;
encoded_attribs . append ( p . d ) ;
}
} break ;
case Variant : : QUATERNION : {
Quaternion q = v ;
if ( likely ( accessor_component_count < 5 ) ) {
for ( int i = 0 ; i < accessor_component_count ; i + + ) {
encoded_attribs . append ( q [ i ] ) ;
}
}
} break ;
case Variant : : COLOR : {
Color c = v ;
if ( likely ( accessor_component_count < 5 ) ) {
for ( int i = 0 ; i < accessor_component_count ; i + + ) {
encoded_attribs . append ( c [ i ] ) ;
}
}
} break ;
case Variant : : RECT2 :
case Variant : : RECT2I : {
// Variant can handle converting Rect2i to Rect2 for us.
Rect2 r = v ;
if ( likely ( accessor_component_count = = 4 ) ) {
encoded_attribs . append ( r . position . x ) ;
encoded_attribs . append ( r . position . y ) ;
encoded_attribs . append ( r . size . x ) ;
encoded_attribs . append ( r . size . y ) ;
}
} break ;
case Variant : : TRANSFORM2D :
case Variant : : BASIS :
case Variant : : TRANSFORM3D :
case Variant : : PROJECTION : {
// Variant can handle converting Transform2D/Transform3D/Basis to Projection for us.
Projection p = v ;
if ( accessor_component_count = = 16 ) {
for ( int i = 0 ; i < 4 ; i + + ) {
encoded_attribs . append ( p . columns [ i ] [ 0 ] ) ;
encoded_attribs . append ( p . columns [ i ] [ 1 ] ) ;
encoded_attribs . append ( p . columns [ i ] [ 2 ] ) ;
encoded_attribs . append ( p . columns [ i ] [ 3 ] ) ;
}
} else if ( accessor_component_count = = 9 ) {
for ( int i = 0 ; i < 3 ; i + + ) {
encoded_attribs . append ( p . columns [ i ] [ 0 ] ) ;
encoded_attribs . append ( p . columns [ i ] [ 1 ] ) ;
encoded_attribs . append ( p . columns [ i ] [ 2 ] ) ;
}
} else if ( accessor_component_count = = 4 ) {
encoded_attribs . append ( p . columns [ 0 ] [ 0 ] ) ;
encoded_attribs . append ( p . columns [ 0 ] [ 1 ] ) ;
encoded_attribs . append ( p . columns [ 1 ] [ 0 ] ) ;
encoded_attribs . append ( p . columns [ 1 ] [ 1 ] ) ;
}
} break ;
default : {
ERR_FAIL_V_MSG ( - 1 , " glTF: Cannot encode accessor from Variant of type " + Variant : : get_type_name ( p_variant_type ) + " . " ) ;
}
}
}
// Determine the min and max values for the accessor.
Vector < double > type_max ;
type_max . resize ( accessor_component_count ) ;
Vector < double > type_min ;
type_min . resize ( accessor_component_count ) ;
for ( int i = 0 ; i < encoded_attribs . size ( ) ; i + + ) {
if ( Math : : is_zero_approx ( encoded_attribs [ i ] ) ) {
encoded_attribs . write [ i ] = 0.0 ;
} else {
encoded_attribs . write [ i ] = _filter_number ( encoded_attribs [ i ] ) ;
}
}
for ( int i = 0 ; i < p_attribs . size ( ) ; i + + ) {
_calc_accessor_min_max ( i , accessor_component_count , type_max , encoded_attribs , type_min ) ;
}
_round_min_max_components ( type_min , type_max ) ;
// Encode the data in a buffer view.
GLTFBufferIndex buffer_view_index = 0 ;
if ( p_state - > buffers . is_empty ( ) ) {
p_state - > buffers . push_back ( Vector < uint8_t > ( ) ) ;
}
const int64_t buffer_size = p_state - > buffers [ buffer_view_index ] . size ( ) ;
Error err = _encode_buffer_view ( p_state , encoded_attribs . ptr ( ) , p_attribs . size ( ) , p_accessor_type , p_component_type , false , buffer_size , false , buffer_view_index ) ;
if ( err ! = OK ) {
return - 1 ;
}
// Create the accessor and fill it with the data.
Ref < GLTFAccessor > accessor ;
accessor . instantiate ( ) ;
accessor - > max = type_max ;
accessor - > min = type_min ;
accessor - > count = p_attribs . size ( ) ;
accessor - > accessor_type = p_accessor_type ;
accessor - > component_type = p_component_type ;
accessor - > byte_offset = 0 ;
accessor - > buffer_view = buffer_view_index ;
const GLTFAccessorIndex new_accessor_index = p_state - > accessors . size ( ) ;
p_state - > accessors . push_back ( accessor ) ;
return new_accessor_index ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _serialize_meshes ( Ref < GLTFState > p_state ) {
2020-12-21 15:39:32 +00:00
Array meshes ;
2022-12-10 21:05:13 +00:00
for ( GLTFMeshIndex gltf_mesh_i = 0 ; gltf_mesh_i < p_state - > meshes . size ( ) ; gltf_mesh_i + + ) {
2020-12-21 15:39:32 +00:00
print_verbose ( " glTF: Serializing mesh: " + itos ( gltf_mesh_i ) ) ;
2022-12-10 21:05:13 +00:00
Ref < ImporterMesh > import_mesh = p_state - > meshes . write [ gltf_mesh_i ] - > get_mesh ( ) ;
2020-12-21 15:39:32 +00:00
if ( import_mesh . is_null ( ) ) {
continue ;
}
2022-12-10 21:05:13 +00:00
Array instance_materials = p_state - > meshes . write [ gltf_mesh_i ] - > get_instance_materials ( ) ;
2020-12-21 15:39:32 +00:00
Array primitives ;
Dictionary gltf_mesh ;
Array target_names ;
Array weights ;
2021-09-04 01:00:59 +00:00
for ( int morph_i = 0 ; morph_i < import_mesh - > get_blend_shape_count ( ) ; morph_i + + ) {
target_names . push_back ( import_mesh - > get_blend_shape_name ( morph_i ) ) ;
}
2020-12-21 15:39:32 +00:00
for ( int surface_i = 0 ; surface_i < import_mesh - > get_surface_count ( ) ; surface_i + + ) {
2021-09-04 01:00:59 +00:00
Array targets ;
2020-12-21 15:39:32 +00:00
Dictionary primitive ;
Mesh : : PrimitiveType primitive_type = import_mesh - > get_surface_primitive_type ( surface_i ) ;
switch ( primitive_type ) {
case Mesh : : PRIMITIVE_POINTS : {
primitive [ " mode " ] = 0 ;
break ;
}
case Mesh : : PRIMITIVE_LINES : {
primitive [ " mode " ] = 1 ;
break ;
}
// case Mesh::PRIMITIVE_LINE_LOOP: {
// primitive["mode"] = 2;
// break;
// }
case Mesh : : PRIMITIVE_LINE_STRIP : {
primitive [ " mode " ] = 3 ;
break ;
}
case Mesh : : PRIMITIVE_TRIANGLES : {
primitive [ " mode " ] = 4 ;
break ;
}
case Mesh : : PRIMITIVE_TRIANGLE_STRIP : {
primitive [ " mode " ] = 5 ;
break ;
}
// case Mesh::PRIMITIVE_TRIANGLE_FAN: {
// primitive["mode"] = 6;
// break;
// }
default : {
ERR_FAIL_V ( FAILED ) ;
}
}
Array array = import_mesh - > get_surface_arrays ( surface_i ) ;
2023-08-29 19:04:32 +00:00
uint64_t format = import_mesh - > get_surface_format ( surface_i ) ;
2021-09-09 04:29:14 +00:00
int32_t vertex_num = 0 ;
2020-12-21 15:39:32 +00:00
Dictionary attributes ;
{
Vector < Vector3 > a = array [ Mesh : : ARRAY_VERTEX ] ;
2024-01-19 12:21:39 +00:00
ERR_FAIL_COND_V ( a . is_empty ( ) , ERR_INVALID_DATA ) ;
2022-12-10 21:05:13 +00:00
attributes [ " POSITION " ] = _encode_accessor_as_vec3 ( p_state , a , true ) ;
2021-09-09 04:29:14 +00:00
vertex_num = a . size ( ) ;
2020-12-21 15:39:32 +00:00
}
{
Vector < real_t > a = array [ Mesh : : ARRAY_TANGENT ] ;
if ( a . size ( ) ) {
const int ret_size = a . size ( ) / 4 ;
Vector < Color > attribs ;
attribs . resize ( ret_size ) ;
for ( int i = 0 ; i < ret_size ; i + + ) {
Color out ;
out . r = a [ ( i * 4 ) + 0 ] ;
out . g = a [ ( i * 4 ) + 1 ] ;
out . b = a [ ( i * 4 ) + 2 ] ;
out . a = a [ ( i * 4 ) + 3 ] ;
attribs . write [ i ] = out ;
}
2022-12-10 21:05:13 +00:00
attributes [ " TANGENT " ] = _encode_accessor_as_color ( p_state , attribs , true ) ;
2020-12-21 15:39:32 +00:00
}
}
{
Vector < Vector3 > a = array [ Mesh : : ARRAY_NORMAL ] ;
if ( a . size ( ) ) {
const int ret_size = a . size ( ) ;
Vector < Vector3 > attribs ;
attribs . resize ( ret_size ) ;
for ( int i = 0 ; i < ret_size ; i + + ) {
attribs . write [ i ] = Vector3 ( a [ i ] ) . normalized ( ) ;
}
2022-12-10 21:05:13 +00:00
attributes [ " NORMAL " ] = _encode_accessor_as_vec3 ( p_state , attribs , true ) ;
2020-12-21 15:39:32 +00:00
}
}
{
Vector < Vector2 > a = array [ Mesh : : ARRAY_TEX_UV ] ;
if ( a . size ( ) ) {
2022-12-10 21:05:13 +00:00
attributes [ " TEXCOORD_0 " ] = _encode_accessor_as_vec2 ( p_state , a , true ) ;
2020-12-21 15:39:32 +00:00
}
}
{
Vector < Vector2 > a = array [ Mesh : : ARRAY_TEX_UV2 ] ;
if ( a . size ( ) ) {
2022-12-10 21:05:13 +00:00
attributes [ " TEXCOORD_1 " ] = _encode_accessor_as_vec2 ( p_state , a , true ) ;
2020-12-21 15:39:32 +00:00
}
}
2021-09-09 04:29:14 +00:00
for ( int custom_i = 0 ; custom_i < 3 ; custom_i + + ) {
Vector < float > a = array [ Mesh : : ARRAY_CUSTOM0 + custom_i ] ;
if ( a . size ( ) ) {
int num_channels = 4 ;
int custom_shift = Mesh : : ARRAY_FORMAT_CUSTOM0_SHIFT + custom_i * Mesh : : ARRAY_FORMAT_CUSTOM_BITS ;
switch ( ( format > > custom_shift ) & Mesh : : ARRAY_FORMAT_CUSTOM_MASK ) {
case Mesh : : ARRAY_CUSTOM_R_FLOAT :
num_channels = 1 ;
break ;
case Mesh : : ARRAY_CUSTOM_RG_FLOAT :
num_channels = 2 ;
break ;
case Mesh : : ARRAY_CUSTOM_RGB_FLOAT :
num_channels = 3 ;
break ;
case Mesh : : ARRAY_CUSTOM_RGBA_FLOAT :
num_channels = 4 ;
break ;
}
int texcoord_i = 2 + 2 * custom_i ;
String gltf_texcoord_key ;
for ( int prev_texcoord_i = 0 ; prev_texcoord_i < texcoord_i ; prev_texcoord_i + + ) {
gltf_texcoord_key = vformat ( " TEXCOORD_%d " , prev_texcoord_i ) ;
if ( ! attributes . has ( gltf_texcoord_key ) ) {
Vector < Vector2 > empty ;
empty . resize ( vertex_num ) ;
2022-12-10 21:05:13 +00:00
attributes [ gltf_texcoord_key ] = _encode_accessor_as_vec2 ( p_state , empty , true ) ;
2021-09-09 04:29:14 +00:00
}
}
LocalVector < Vector2 > first_channel ;
first_channel . resize ( vertex_num ) ;
LocalVector < Vector2 > second_channel ;
second_channel . resize ( vertex_num ) ;
for ( int32_t vert_i = 0 ; vert_i < vertex_num ; vert_i + + ) {
float u = a [ vert_i * num_channels + 0 ] ;
float v = ( num_channels = = 1 ? 0.0f : a [ vert_i * num_channels + 1 ] ) ;
first_channel [ vert_i ] = Vector2 ( u , v ) ;
u = 0 ;
v = 0 ;
if ( num_channels > = 3 ) {
u = a [ vert_i * num_channels + 2 ] ;
v = ( num_channels = = 3 ? 0.0f : a [ vert_i * num_channels + 3 ] ) ;
second_channel [ vert_i ] = Vector2 ( u , v ) ;
}
}
gltf_texcoord_key = vformat ( " TEXCOORD_%d " , texcoord_i ) ;
2022-12-10 21:05:13 +00:00
attributes [ gltf_texcoord_key ] = _encode_accessor_as_vec2 ( p_state , first_channel , true ) ;
2021-09-09 04:29:14 +00:00
gltf_texcoord_key = vformat ( " TEXCOORD_%d " , texcoord_i + 1 ) ;
2022-12-10 21:05:13 +00:00
attributes [ gltf_texcoord_key ] = _encode_accessor_as_vec2 ( p_state , second_channel , true ) ;
2021-09-09 04:29:14 +00:00
}
}
2020-12-21 15:39:32 +00:00
{
Vector < Color > a = array [ Mesh : : ARRAY_COLOR ] ;
if ( a . size ( ) ) {
2022-12-10 21:05:13 +00:00
attributes [ " COLOR_0 " ] = _encode_accessor_as_color ( p_state , a , true ) ;
2020-12-21 15:39:32 +00:00
}
}
2022-05-13 13:04:37 +00:00
HashMap < int , int > joint_i_to_bone_i ;
2022-12-10 21:05:13 +00:00
for ( GLTFNodeIndex node_i = 0 ; node_i < p_state - > nodes . size ( ) ; node_i + + ) {
2020-12-21 15:39:32 +00:00
GLTFSkinIndex skin_i = - 1 ;
2022-12-10 21:05:13 +00:00
if ( p_state - > nodes [ node_i ] - > mesh = = gltf_mesh_i ) {
skin_i = p_state - > nodes [ node_i ] - > skin ;
2020-12-21 15:39:32 +00:00
}
if ( skin_i ! = - 1 ) {
2022-12-10 21:05:13 +00:00
joint_i_to_bone_i = p_state - > skins [ skin_i ] - > joint_i_to_bone_i ;
2020-12-21 15:39:32 +00:00
break ;
}
}
{
2020-12-28 19:51:48 +00:00
const Array & a = array [ Mesh : : ARRAY_BONES ] ;
const Vector < Vector3 > & vertex_array = array [ Mesh : : ARRAY_VERTEX ] ;
if ( ( a . size ( ) / JOINT_GROUP_SIZE ) = = vertex_array . size ( ) ) {
const int ret_size = a . size ( ) / JOINT_GROUP_SIZE ;
2020-12-21 15:39:32 +00:00
Vector < Color > attribs ;
attribs . resize ( ret_size ) ;
{
for ( int array_i = 0 ; array_i < attribs . size ( ) ; array_i + + ) {
2020-12-28 19:51:48 +00:00
int32_t joint_0 = a [ ( array_i * JOINT_GROUP_SIZE ) + 0 ] ;
int32_t joint_1 = a [ ( array_i * JOINT_GROUP_SIZE ) + 1 ] ;
int32_t joint_2 = a [ ( array_i * JOINT_GROUP_SIZE ) + 2 ] ;
int32_t joint_3 = a [ ( array_i * JOINT_GROUP_SIZE ) + 3 ] ;
2020-12-21 15:39:32 +00:00
attribs . write [ array_i ] = Color ( joint_0 , joint_1 , joint_2 , joint_3 ) ;
}
}
2022-12-10 21:05:13 +00:00
attributes [ " JOINTS_0 " ] = _encode_accessor_as_joints ( p_state , attribs , true ) ;
2020-12-28 19:51:48 +00:00
} else if ( ( a . size ( ) / ( JOINT_GROUP_SIZE * 2 ) ) > = vertex_array . size ( ) ) {
Vector < Color > joints_0 ;
2021-09-09 04:29:14 +00:00
joints_0 . resize ( vertex_num ) ;
2020-12-28 19:51:48 +00:00
Vector < Color > joints_1 ;
2021-09-09 04:29:14 +00:00
joints_1 . resize ( vertex_num ) ;
2020-12-28 19:51:48 +00:00
int32_t weights_8_count = JOINT_GROUP_SIZE * 2 ;
2021-09-09 04:29:14 +00:00
for ( int32_t vertex_i = 0 ; vertex_i < vertex_num ; vertex_i + + ) {
2020-12-28 19:51:48 +00:00
Color joint_0 ;
joint_0 . r = a [ vertex_i * weights_8_count + 0 ] ;
joint_0 . g = a [ vertex_i * weights_8_count + 1 ] ;
joint_0 . b = a [ vertex_i * weights_8_count + 2 ] ;
joint_0 . a = a [ vertex_i * weights_8_count + 3 ] ;
joints_0 . write [ vertex_i ] = joint_0 ;
Color joint_1 ;
joint_1 . r = a [ vertex_i * weights_8_count + 4 ] ;
joint_1 . g = a [ vertex_i * weights_8_count + 5 ] ;
joint_1 . b = a [ vertex_i * weights_8_count + 6 ] ;
joint_1 . a = a [ vertex_i * weights_8_count + 7 ] ;
joints_1 . write [ vertex_i ] = joint_1 ;
}
2022-12-10 21:05:13 +00:00
attributes [ " JOINTS_0 " ] = _encode_accessor_as_joints ( p_state , joints_0 , true ) ;
attributes [ " JOINTS_1 " ] = _encode_accessor_as_joints ( p_state , joints_1 , true ) ;
2020-12-21 15:39:32 +00:00
}
}
{
2020-12-28 19:51:48 +00:00
const Array & a = array [ Mesh : : ARRAY_WEIGHTS ] ;
const Vector < Vector3 > & vertex_array = array [ Mesh : : ARRAY_VERTEX ] ;
if ( ( a . size ( ) / JOINT_GROUP_SIZE ) = = vertex_array . size ( ) ) {
2021-09-04 01:00:59 +00:00
int32_t vertex_count = vertex_array . size ( ) ;
2020-12-21 15:39:32 +00:00
Vector < Color > attribs ;
2021-09-04 01:00:59 +00:00
attribs . resize ( vertex_count ) ;
for ( int i = 0 ; i < vertex_count ; i + + ) {
2024-03-10 07:51:52 +00:00
Color weight_0 ( a [ ( i * JOINT_GROUP_SIZE ) + 0 ] , a [ ( i * JOINT_GROUP_SIZE ) + 1 ] , a [ ( i * JOINT_GROUP_SIZE ) + 2 ] , a [ ( i * JOINT_GROUP_SIZE ) + 3 ] ) ;
float divisor = weight_0 . r + weight_0 . g + weight_0 . b + weight_0 . a ;
if ( Math : : is_zero_approx ( divisor ) | | ! Math : : is_finite ( divisor ) ) {
divisor = 1.0 ;
weight_0 = Color ( 1 , 0 , 0 , 0 ) ;
}
attribs . write [ i ] = weight_0 / divisor ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
attributes [ " WEIGHTS_0 " ] = _encode_accessor_as_weights ( p_state , attribs , true ) ;
2020-12-28 19:51:48 +00:00
} else if ( ( a . size ( ) / ( JOINT_GROUP_SIZE * 2 ) ) > = vertex_array . size ( ) ) {
Vector < Color > weights_0 ;
2021-09-09 04:29:14 +00:00
weights_0 . resize ( vertex_num ) ;
2020-12-28 19:51:48 +00:00
Vector < Color > weights_1 ;
2021-09-09 04:29:14 +00:00
weights_1 . resize ( vertex_num ) ;
2020-12-28 19:51:48 +00:00
int32_t weights_8_count = JOINT_GROUP_SIZE * 2 ;
2021-09-09 04:29:14 +00:00
for ( int32_t vertex_i = 0 ; vertex_i < vertex_num ; vertex_i + + ) {
2020-12-28 19:51:48 +00:00
Color weight_0 ;
weight_0 . r = a [ vertex_i * weights_8_count + 0 ] ;
weight_0 . g = a [ vertex_i * weights_8_count + 1 ] ;
weight_0 . b = a [ vertex_i * weights_8_count + 2 ] ;
weight_0 . a = a [ vertex_i * weights_8_count + 3 ] ;
Color weight_1 ;
weight_1 . r = a [ vertex_i * weights_8_count + 4 ] ;
weight_1 . g = a [ vertex_i * weights_8_count + 5 ] ;
weight_1 . b = a [ vertex_i * weights_8_count + 6 ] ;
weight_1 . a = a [ vertex_i * weights_8_count + 7 ] ;
2024-03-10 07:51:52 +00:00
float divisor = weight_0 . r + weight_0 . g + weight_0 . b + weight_0 . a + weight_1 . r + weight_1 . g + weight_1 . b + weight_1 . a ;
if ( Math : : is_zero_approx ( divisor ) | | ! Math : : is_finite ( divisor ) ) {
divisor = 1.0f ;
weight_0 = Color ( 1 , 0 , 0 , 0 ) ;
weight_1 = Color ( 0 , 0 , 0 , 0 ) ;
}
weights_0 . write [ vertex_i ] = weight_0 / divisor ;
weights_1 . write [ vertex_i ] = weight_1 / divisor ;
2020-12-28 19:51:48 +00:00
}
2022-12-10 21:05:13 +00:00
attributes [ " WEIGHTS_0 " ] = _encode_accessor_as_weights ( p_state , weights_0 , true ) ;
attributes [ " WEIGHTS_1 " ] = _encode_accessor_as_weights ( p_state , weights_1 , true ) ;
2020-12-21 15:39:32 +00:00
}
}
{
Vector < int32_t > mesh_indices = array [ Mesh : : ARRAY_INDEX ] ;
if ( mesh_indices . size ( ) ) {
if ( primitive_type = = Mesh : : PRIMITIVE_TRIANGLES ) {
2024-03-12 11:50:54 +00:00
// Swap around indices, convert ccw to cw for front face.
2020-12-21 15:39:32 +00:00
const int is = mesh_indices . size ( ) ;
for ( int k = 0 ; k < is ; k + = 3 ) {
SWAP ( mesh_indices . write [ k + 0 ] , mesh_indices . write [ k + 2 ] ) ;
}
}
2024-03-01 21:07:07 +00:00
primitive [ " indices " ] = _encode_accessor_as_ints ( p_state , mesh_indices , false , true ) ;
2020-12-21 15:39:32 +00:00
} else {
if ( primitive_type = = Mesh : : PRIMITIVE_TRIANGLES ) {
2024-03-12 11:50:54 +00:00
// Generate indices because they need to be swapped for CW/CCW.
2020-12-21 15:39:32 +00:00
const Vector < Vector3 > & vertices = array [ Mesh : : ARRAY_VERTEX ] ;
Ref < SurfaceTool > st ;
2021-06-17 22:03:09 +00:00
st . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
st - > create_from_triangle_arrays ( array ) ;
st - > index ( ) ;
Vector < int32_t > generated_indices = st - > commit_to_arrays ( ) [ Mesh : : ARRAY_INDEX ] ;
const int vs = vertices . size ( ) ;
generated_indices . resize ( vs ) ;
{
for ( int k = 0 ; k < vs ; k + = 3 ) {
generated_indices . write [ k ] = k ;
generated_indices . write [ k + 1 ] = k + 2 ;
generated_indices . write [ k + 2 ] = k + 1 ;
}
}
2024-03-01 21:07:07 +00:00
primitive [ " indices " ] = _encode_accessor_as_ints ( p_state , generated_indices , false , true ) ;
2020-12-21 15:39:32 +00:00
}
}
}
primitive [ " attributes " ] = attributes ;
2024-03-12 11:50:54 +00:00
// Blend shapes
2020-12-21 15:39:32 +00:00
print_verbose ( " glTF: Mesh has targets " ) ;
if ( import_mesh - > get_blend_shape_count ( ) ) {
ArrayMesh : : BlendShapeMode shape_mode = import_mesh - > get_blend_shape_mode ( ) ;
2024-03-12 09:39:15 +00:00
const float normal_tangent_sparse_rounding = 0.001 ;
2020-12-21 15:39:32 +00:00
for ( int morph_i = 0 ; morph_i < import_mesh - > get_blend_shape_count ( ) ; morph_i + + ) {
Array array_morph = import_mesh - > get_surface_blend_shape_arrays ( surface_i , morph_i ) ;
Dictionary t ;
Vector < Vector3 > varr = array_morph [ Mesh : : ARRAY_VERTEX ] ;
2024-03-12 09:39:15 +00:00
Vector < Vector3 > src_varr = array [ Mesh : : ARRAY_VERTEX ] ;
2020-12-21 15:39:32 +00:00
Array mesh_arrays = import_mesh - > get_surface_arrays ( surface_i ) ;
2024-03-12 09:39:15 +00:00
if ( varr . size ( ) & & varr . size ( ) = = src_varr . size ( ) ) {
2020-12-21 15:39:32 +00:00
if ( shape_mode = = ArrayMesh : : BlendShapeMode : : BLEND_SHAPE_MODE_NORMALIZED ) {
const int max_idx = src_varr . size ( ) ;
for ( int blend_i = 0 ; blend_i < max_idx ; blend_i + + ) {
2024-03-12 09:39:15 +00:00
varr . write [ blend_i ] = varr [ blend_i ] - src_varr [ blend_i ] ;
2020-12-21 15:39:32 +00:00
}
}
2024-03-01 21:07:07 +00:00
GLTFAccessorIndex position_accessor = attributes [ " POSITION " ] ;
if ( position_accessor ! = - 1 ) {
2024-03-12 09:39:15 +00:00
int new_accessor = _encode_sparse_accessor_as_vec3 ( p_state , varr , Vector < Vector3 > ( ) , 1.0 , true , - 1 ) ;
2024-03-01 21:07:07 +00:00
if ( new_accessor ! = - 1 ) {
t [ " POSITION " ] = new_accessor ;
2020-12-21 15:39:32 +00:00
}
}
}
Vector < Vector3 > narr = array_morph [ Mesh : : ARRAY_NORMAL ] ;
2024-03-12 09:39:15 +00:00
Vector < Vector3 > src_narr = array [ Mesh : : ARRAY_NORMAL ] ;
if ( narr . size ( ) & & narr . size ( ) = = src_narr . size ( ) ) {
if ( shape_mode = = ArrayMesh : : BlendShapeMode : : BLEND_SHAPE_MODE_NORMALIZED ) {
const int max_idx = src_narr . size ( ) ;
for ( int blend_i = 0 ; blend_i < max_idx ; blend_i + + ) {
narr . write [ blend_i ] = narr [ blend_i ] - src_narr [ blend_i ] ;
}
}
2024-03-01 21:07:07 +00:00
GLTFAccessorIndex normal_accessor = attributes [ " NORMAL " ] ;
if ( normal_accessor ! = - 1 ) {
2024-03-12 09:39:15 +00:00
int new_accessor = _encode_sparse_accessor_as_vec3 ( p_state , narr , Vector < Vector3 > ( ) , normal_tangent_sparse_rounding , true , - 1 ) ;
2024-03-01 21:07:07 +00:00
if ( new_accessor ! = - 1 ) {
t [ " NORMAL " ] = new_accessor ;
}
}
2020-12-21 15:39:32 +00:00
}
Vector < real_t > tarr = array_morph [ Mesh : : ARRAY_TANGENT ] ;
2024-03-12 09:39:15 +00:00
Vector < real_t > src_tarr = array [ Mesh : : ARRAY_TANGENT ] ;
if ( tarr . size ( ) & & tarr . size ( ) = = src_tarr . size ( ) ) {
2020-12-21 15:39:32 +00:00
const int ret_size = tarr . size ( ) / 4 ;
2021-09-04 01:00:59 +00:00
Vector < Vector3 > attribs ;
2020-12-21 15:39:32 +00:00
attribs . resize ( ret_size ) ;
for ( int i = 0 ; i < ret_size ; i + + ) {
2021-09-04 01:00:59 +00:00
Vector3 vec3 ;
2024-03-12 09:39:15 +00:00
vec3 . x = tarr [ ( i * 4 ) + 0 ] - src_tarr [ ( i * 4 ) + 0 ] ;
vec3 . y = tarr [ ( i * 4 ) + 1 ] - src_tarr [ ( i * 4 ) + 1 ] ;
vec3 . z = tarr [ ( i * 4 ) + 2 ] - src_tarr [ ( i * 4 ) + 2 ] ;
2024-03-01 21:07:07 +00:00
attribs . write [ i ] = vec3 ;
}
GLTFAccessorIndex tangent_accessor = attributes [ " TANGENT " ] ;
if ( tangent_accessor ! = - 1 ) {
2024-03-12 09:39:15 +00:00
int new_accessor = _encode_sparse_accessor_as_vec3 ( p_state , attribs , Vector < Vector3 > ( ) , normal_tangent_sparse_rounding , true , - 1 ) ;
2024-03-01 21:07:07 +00:00
if ( new_accessor ! = - 1 ) {
t [ " TANGENT " ] = new_accessor ;
}
2020-12-21 15:39:32 +00:00
}
}
targets . push_back ( t ) ;
}
}
2021-11-02 00:31:59 +00:00
Variant v ;
if ( surface_i < instance_materials . size ( ) ) {
v = instance_materials . get ( surface_i ) ;
}
2022-11-23 23:29:01 +00:00
Ref < Material > mat = v ;
2021-11-02 00:31:59 +00:00
if ( ! mat . is_valid ( ) ) {
mat = import_mesh - > get_surface_material ( surface_i ) ;
}
2020-12-21 15:39:32 +00:00
if ( mat . is_valid ( ) ) {
2022-12-10 21:05:13 +00:00
HashMap < Ref < Material > , GLTFMaterialIndex > : : Iterator material_cache_i = p_state - > material_cache . find ( mat ) ;
2022-05-13 13:04:37 +00:00
if ( material_cache_i & & material_cache_i - > value ! = - 1 ) {
primitive [ " material " ] = material_cache_i - > value ;
2020-12-21 15:39:32 +00:00
} else {
2022-12-10 21:05:13 +00:00
GLTFMaterialIndex mat_i = p_state - > materials . size ( ) ;
p_state - > materials . push_back ( mat ) ;
2020-12-21 15:39:32 +00:00
primitive [ " material " ] = mat_i ;
2022-12-10 21:05:13 +00:00
p_state - > material_cache . insert ( mat , mat_i ) ;
2020-12-21 15:39:32 +00:00
}
}
if ( targets . size ( ) ) {
primitive [ " targets " ] = targets ;
}
primitives . push_back ( primitive ) ;
}
Dictionary e ;
e [ " targetNames " ] = target_names ;
2024-01-11 19:47:31 +00:00
gltf_mesh [ " extras " ] = e ;
_attach_meta_to_extras ( import_mesh , gltf_mesh ) ;
2020-12-21 15:39:32 +00:00
2021-09-04 01:00:59 +00:00
weights . resize ( target_names . size ( ) ) ;
for ( int name_i = 0 ; name_i < target_names . size ( ) ; name_i + + ) {
2021-02-08 09:57:18 +00:00
real_t weight = 0.0 ;
2022-12-10 21:05:13 +00:00
if ( name_i < p_state - > meshes . write [ gltf_mesh_i ] - > get_blend_weights ( ) . size ( ) ) {
weight = p_state - > meshes . write [ gltf_mesh_i ] - > get_blend_weights ( ) [ name_i ] ;
2020-12-21 15:39:32 +00:00
}
2021-09-04 01:00:59 +00:00
weights [ name_i ] = weight ;
2020-12-21 15:39:32 +00:00
}
if ( weights . size ( ) ) {
gltf_mesh [ " weights " ] = weights ;
}
ERR_FAIL_COND_V ( target_names . size ( ) ! = weights . size ( ) , FAILED ) ;
gltf_mesh [ " primitives " ] = primitives ;
meshes . push_back ( gltf_mesh ) ;
}
2021-11-02 00:31:59 +00:00
if ( ! meshes . size ( ) ) {
return OK ;
}
2022-12-10 21:05:13 +00:00
p_state - > json [ " meshes " ] = meshes ;
2020-12-21 15:39:32 +00:00
print_verbose ( " glTF: Total meshes: " + itos ( meshes . size ( ) ) ) ;
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_meshes ( Ref < GLTFState > p_state ) {
if ( ! p_state - > json . has ( " meshes " ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Array meshes = p_state - > json [ " meshes " ] ;
2020-12-21 15:39:32 +00:00
for ( GLTFMeshIndex i = 0 ; i < meshes . size ( ) ; i + + ) {
print_verbose ( " glTF: Parsing mesh: " + itos ( i ) ) ;
2024-07-10 09:02:03 +00:00
Dictionary mesh_dict = meshes [ i ] ;
2020-12-21 15:39:32 +00:00
Ref < GLTFMesh > mesh ;
2021-06-17 22:03:09 +00:00
mesh . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
bool has_vertex_color = false ;
2024-07-10 09:02:03 +00:00
ERR_FAIL_COND_V ( ! mesh_dict . has ( " primitives " ) , ERR_PARSE_ERROR ) ;
2020-12-21 15:39:32 +00:00
2024-07-10 09:02:03 +00:00
Array primitives = mesh_dict [ " primitives " ] ;
const Dictionary & extras = mesh_dict . has ( " extras " ) ? ( Dictionary ) mesh_dict [ " extras " ] : Dictionary ( ) ;
2024-01-11 19:47:31 +00:00
_attach_extras_to_meta ( extras , mesh ) ;
2021-09-21 01:24:31 +00:00
Ref < ImporterMesh > import_mesh ;
2021-06-17 22:03:09 +00:00
import_mesh . instantiate ( ) ;
2021-03-24 07:29:20 +00:00
String mesh_name = " mesh " ;
2024-07-10 09:02:03 +00:00
if ( mesh_dict . has ( " name " ) & & ! String ( mesh_dict [ " name " ] ) . is_empty ( ) ) {
mesh_name = mesh_dict [ " name " ] ;
2024-02-16 13:25:15 +00:00
mesh - > set_original_name ( mesh_name ) ;
2021-03-24 07:29:20 +00:00
}
2022-12-10 21:05:13 +00:00
import_mesh - > set_name ( _gen_unique_name ( p_state , vformat ( " %s_%s " , p_state - > scene_name , mesh_name ) ) ) ;
2024-02-16 13:25:15 +00:00
mesh - > set_name ( import_mesh - > get_name ( ) ) ;
2024-07-10 09:02:03 +00:00
TypedArray < Material > instance_materials ;
2021-03-24 07:29:20 +00:00
2020-12-21 15:39:32 +00:00
for ( int j = 0 ; j < primitives . size ( ) ; j + + ) {
2023-08-29 19:04:32 +00:00
uint64_t flags = RS : : ARRAY_FLAG_COMPRESS_ATTRIBUTES ;
2024-07-10 09:02:03 +00:00
Dictionary mesh_prim = primitives [ j ] ;
2020-12-21 15:39:32 +00:00
Array array ;
array . resize ( Mesh : : ARRAY_MAX ) ;
2024-07-10 09:02:03 +00:00
ERR_FAIL_COND_V ( ! mesh_prim . has ( " attributes " ) , ERR_PARSE_ERROR ) ;
2020-12-21 15:39:32 +00:00
2024-07-10 09:02:03 +00:00
Dictionary a = mesh_prim [ " attributes " ] ;
2020-12-21 15:39:32 +00:00
Mesh : : PrimitiveType primitive = Mesh : : PRIMITIVE_TRIANGLES ;
2024-07-10 09:02:03 +00:00
if ( mesh_prim . has ( " mode " ) ) {
const int mode = mesh_prim [ " mode " ] ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_INDEX_V ( mode , 7 , ERR_FILE_CORRUPT ) ;
2021-12-28 01:36:58 +00:00
// Convert mesh.primitive.mode to Godot Mesh enum. See:
// https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#_mesh_primitive_mode
2020-12-21 15:39:32 +00:00
static const Mesh : : PrimitiveType primitives2 [ 7 ] = {
2021-12-28 01:36:58 +00:00
Mesh : : PRIMITIVE_POINTS , // 0 POINTS
Mesh : : PRIMITIVE_LINES , // 1 LINES
Mesh : : PRIMITIVE_LINES , // 2 LINE_LOOP; loop not supported, should be converted
Mesh : : PRIMITIVE_LINE_STRIP , // 3 LINE_STRIP
Mesh : : PRIMITIVE_TRIANGLES , // 4 TRIANGLES
Mesh : : PRIMITIVE_TRIANGLE_STRIP , // 5 TRIANGLE_STRIP
Mesh : : PRIMITIVE_TRIANGLES , // 6 TRIANGLE_FAN fan not supported, should be converted
2022-10-10 11:04:01 +00:00
// TODO: Line loop and triangle fan are not supported and need to be converted to lines and triangles.
2020-12-21 15:39:32 +00:00
} ;
primitive = primitives2 [ mode ] ;
}
2024-03-12 11:50:54 +00:00
int32_t orig_vertex_num = 0 ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( ! a . has ( " POSITION " ) , ERR_PARSE_ERROR ) ;
if ( a . has ( " POSITION " ) ) {
2022-12-10 21:05:13 +00:00
PackedVector3Array vertices = _decode_accessor_as_vec3 ( p_state , a [ " POSITION " ] , true ) ;
2021-09-09 04:29:14 +00:00
array [ Mesh : : ARRAY_VERTEX ] = vertices ;
2024-03-12 11:50:54 +00:00
orig_vertex_num = vertices . size ( ) ;
}
int32_t vertex_num = orig_vertex_num ;
Vector < int > indices ;
Vector < int > indices_mapping ;
Vector < int > indices_rev_mapping ;
Vector < int > indices_vec4_mapping ;
2024-07-10 09:02:03 +00:00
if ( mesh_prim . has ( " indices " ) ) {
indices = _decode_accessor_as_ints ( p_state , mesh_prim [ " indices " ] , false ) ;
2024-03-12 11:50:54 +00:00
const int is = indices . size ( ) ;
if ( primitive = = Mesh : : PRIMITIVE_TRIANGLES ) {
// Swap around indices, convert ccw to cw for front face.
int * w = indices . ptrw ( ) ;
for ( int k = 0 ; k < is ; k + = 3 ) {
SWAP ( w [ k + 1 ] , w [ k + 2 ] ) ;
}
}
const int * indices_w = indices . ptrw ( ) ;
Vector < bool > used_indices ;
used_indices . resize_zeroed ( orig_vertex_num ) ;
bool * used_w = used_indices . ptrw ( ) ;
for ( int idx_i = 0 ; idx_i < is ; idx_i + + ) {
ERR_FAIL_INDEX_V ( indices_w [ idx_i ] , orig_vertex_num , ERR_INVALID_DATA ) ;
used_w [ indices_w [ idx_i ] ] = true ;
}
indices_rev_mapping . resize_zeroed ( orig_vertex_num ) ;
int * rev_w = indices_rev_mapping . ptrw ( ) ;
vertex_num = 0 ;
for ( int vert_i = 0 ; vert_i < orig_vertex_num ; vert_i + + ) {
if ( used_w [ vert_i ] ) {
rev_w [ vert_i ] = indices_mapping . size ( ) ;
indices_mapping . push_back ( vert_i ) ;
indices_vec4_mapping . push_back ( vert_i * 4 + 0 ) ;
indices_vec4_mapping . push_back ( vert_i * 4 + 1 ) ;
indices_vec4_mapping . push_back ( vert_i * 4 + 2 ) ;
indices_vec4_mapping . push_back ( vert_i * 4 + 3 ) ;
vertex_num + + ;
}
}
}
ERR_FAIL_COND_V ( vertex_num < = 0 , ERR_INVALID_DECLARATION ) ;
if ( a . has ( " POSITION " ) ) {
PackedVector3Array vertices = _decode_accessor_as_vec3 ( p_state , a [ " POSITION " ] , true , indices_mapping ) ;
array [ Mesh : : ARRAY_VERTEX ] = vertices ;
2020-12-21 15:39:32 +00:00
}
if ( a . has ( " NORMAL " ) ) {
2024-03-12 11:50:54 +00:00
array [ Mesh : : ARRAY_NORMAL ] = _decode_accessor_as_vec3 ( p_state , a [ " NORMAL " ] , true , indices_mapping ) ;
2020-12-21 15:39:32 +00:00
}
if ( a . has ( " TANGENT " ) ) {
2024-03-12 11:50:54 +00:00
array [ Mesh : : ARRAY_TANGENT ] = _decode_accessor_as_floats ( p_state , a [ " TANGENT " ] , true , indices_vec4_mapping ) ;
2020-12-21 15:39:32 +00:00
}
if ( a . has ( " TEXCOORD_0 " ) ) {
2024-03-12 11:50:54 +00:00
array [ Mesh : : ARRAY_TEX_UV ] = _decode_accessor_as_vec2 ( p_state , a [ " TEXCOORD_0 " ] , true , indices_mapping ) ;
2020-12-21 15:39:32 +00:00
}
if ( a . has ( " TEXCOORD_1 " ) ) {
2024-03-12 11:50:54 +00:00
array [ Mesh : : ARRAY_TEX_UV2 ] = _decode_accessor_as_vec2 ( p_state , a [ " TEXCOORD_1 " ] , true , indices_mapping ) ;
2020-12-21 15:39:32 +00:00
}
2021-09-09 04:29:14 +00:00
for ( int custom_i = 0 ; custom_i < 3 ; custom_i + + ) {
Vector < float > cur_custom ;
Vector < Vector2 > texcoord_first ;
Vector < Vector2 > texcoord_second ;
int texcoord_i = 2 + 2 * custom_i ;
String gltf_texcoord_key = vformat ( " TEXCOORD_%d " , texcoord_i ) ;
int num_channels = 0 ;
if ( a . has ( gltf_texcoord_key ) ) {
2024-03-12 11:50:54 +00:00
texcoord_first = _decode_accessor_as_vec2 ( p_state , a [ gltf_texcoord_key ] , true , indices_mapping ) ;
2021-09-09 04:29:14 +00:00
num_channels = 2 ;
}
gltf_texcoord_key = vformat ( " TEXCOORD_%d " , texcoord_i + 1 ) ;
if ( a . has ( gltf_texcoord_key ) ) {
2024-03-12 11:50:54 +00:00
texcoord_second = _decode_accessor_as_vec2 ( p_state , a [ gltf_texcoord_key ] , true , indices_mapping ) ;
2021-09-09 04:29:14 +00:00
num_channels = 4 ;
}
if ( ! num_channels ) {
break ;
}
if ( num_channels = = 2 | | num_channels = = 4 ) {
cur_custom . resize ( vertex_num * num_channels ) ;
for ( int32_t uv_i = 0 ; uv_i < texcoord_first . size ( ) & & uv_i < vertex_num ; uv_i + + ) {
cur_custom . write [ uv_i * num_channels + 0 ] = texcoord_first [ uv_i ] . x ;
cur_custom . write [ uv_i * num_channels + 1 ] = texcoord_first [ uv_i ] . y ;
}
// Vector.resize seems to not zero-initialize. Ensure all unused elements are 0:
for ( int32_t uv_i = texcoord_first . size ( ) ; uv_i < vertex_num ; uv_i + + ) {
cur_custom . write [ uv_i * num_channels + 0 ] = 0 ;
cur_custom . write [ uv_i * num_channels + 1 ] = 0 ;
}
}
if ( num_channels = = 4 ) {
for ( int32_t uv_i = 0 ; uv_i < texcoord_second . size ( ) & & uv_i < vertex_num ; uv_i + + ) {
// num_channels must be 4
cur_custom . write [ uv_i * num_channels + 2 ] = texcoord_second [ uv_i ] . x ;
cur_custom . write [ uv_i * num_channels + 3 ] = texcoord_second [ uv_i ] . y ;
}
// Vector.resize seems to not zero-initialize. Ensure all unused elements are 0:
for ( int32_t uv_i = texcoord_second . size ( ) ; uv_i < vertex_num ; uv_i + + ) {
cur_custom . write [ uv_i * num_channels + 2 ] = 0 ;
cur_custom . write [ uv_i * num_channels + 3 ] = 0 ;
}
}
if ( cur_custom . size ( ) > 0 ) {
array [ Mesh : : ARRAY_CUSTOM0 + custom_i ] = cur_custom ;
int custom_shift = Mesh : : ARRAY_FORMAT_CUSTOM0_SHIFT + custom_i * Mesh : : ARRAY_FORMAT_CUSTOM_BITS ;
if ( num_channels = = 2 ) {
flags | = Mesh : : ARRAY_CUSTOM_RG_FLOAT < < custom_shift ;
} else {
flags | = Mesh : : ARRAY_CUSTOM_RGBA_FLOAT < < custom_shift ;
}
}
}
2020-12-21 15:39:32 +00:00
if ( a . has ( " COLOR_0 " ) ) {
2024-03-12 11:50:54 +00:00
array [ Mesh : : ARRAY_COLOR ] = _decode_accessor_as_color ( p_state , a [ " COLOR_0 " ] , true , indices_mapping ) ;
2020-12-21 15:39:32 +00:00
has_vertex_color = true ;
}
2020-12-28 19:51:48 +00:00
if ( a . has ( " JOINTS_0 " ) & & ! a . has ( " JOINTS_1 " ) ) {
2024-03-12 11:50:54 +00:00
PackedInt32Array joints_0 = _decode_accessor_as_ints ( p_state , a [ " JOINTS_0 " ] , true , indices_vec4_mapping ) ;
ERR_FAIL_COND_V ( joints_0 . size ( ) ! = 4 * vertex_num , ERR_INVALID_DATA ) ;
array [ Mesh : : ARRAY_BONES ] = joints_0 ;
2020-12-28 19:51:48 +00:00
} else if ( a . has ( " JOINTS_0 " ) & & a . has ( " JOINTS_1 " ) ) {
2024-03-12 11:50:54 +00:00
PackedInt32Array joints_0 = _decode_accessor_as_ints ( p_state , a [ " JOINTS_0 " ] , true , indices_vec4_mapping ) ;
PackedInt32Array joints_1 = _decode_accessor_as_ints ( p_state , a [ " JOINTS_1 " ] , true , indices_vec4_mapping ) ;
2022-09-10 11:17:55 +00:00
ERR_FAIL_COND_V ( joints_0 . size ( ) ! = joints_1 . size ( ) , ERR_INVALID_DATA ) ;
2024-03-12 11:50:54 +00:00
ERR_FAIL_COND_V ( joints_0 . size ( ) ! = 4 * vertex_num , ERR_INVALID_DATA ) ;
2020-12-28 19:51:48 +00:00
int32_t weight_8_count = JOINT_GROUP_SIZE * 2 ;
Vector < int > joints ;
2021-09-09 04:29:14 +00:00
joints . resize ( vertex_num * weight_8_count ) ;
for ( int32_t vertex_i = 0 ; vertex_i < vertex_num ; vertex_i + + ) {
2020-12-28 19:51:48 +00:00
joints . write [ vertex_i * weight_8_count + 0 ] = joints_0 [ vertex_i * JOINT_GROUP_SIZE + 0 ] ;
joints . write [ vertex_i * weight_8_count + 1 ] = joints_0 [ vertex_i * JOINT_GROUP_SIZE + 1 ] ;
joints . write [ vertex_i * weight_8_count + 2 ] = joints_0 [ vertex_i * JOINT_GROUP_SIZE + 2 ] ;
joints . write [ vertex_i * weight_8_count + 3 ] = joints_0 [ vertex_i * JOINT_GROUP_SIZE + 3 ] ;
joints . write [ vertex_i * weight_8_count + 4 ] = joints_1 [ vertex_i * JOINT_GROUP_SIZE + 0 ] ;
joints . write [ vertex_i * weight_8_count + 5 ] = joints_1 [ vertex_i * JOINT_GROUP_SIZE + 1 ] ;
joints . write [ vertex_i * weight_8_count + 6 ] = joints_1 [ vertex_i * JOINT_GROUP_SIZE + 2 ] ;
joints . write [ vertex_i * weight_8_count + 7 ] = joints_1 [ vertex_i * JOINT_GROUP_SIZE + 3 ] ;
}
array [ Mesh : : ARRAY_BONES ] = joints ;
2020-12-21 15:39:32 +00:00
}
2020-12-28 19:51:48 +00:00
if ( a . has ( " WEIGHTS_0 " ) & & ! a . has ( " WEIGHTS_1 " ) ) {
2024-03-12 11:50:54 +00:00
Vector < float > weights = _decode_accessor_as_floats ( p_state , a [ " WEIGHTS_0 " ] , true , indices_vec4_mapping ) ;
ERR_FAIL_COND_V ( weights . size ( ) ! = 4 * vertex_num , ERR_INVALID_DATA ) ;
{ // glTF does not seem to normalize the weights for some reason.
2020-12-21 15:39:32 +00:00
int wc = weights . size ( ) ;
float * w = weights . ptrw ( ) ;
for ( int k = 0 ; k < wc ; k + = 4 ) {
float total = 0.0 ;
total + = w [ k + 0 ] ;
total + = w [ k + 1 ] ;
total + = w [ k + 2 ] ;
total + = w [ k + 3 ] ;
if ( total > 0.0 ) {
w [ k + 0 ] / = total ;
w [ k + 1 ] / = total ;
w [ k + 2 ] / = total ;
w [ k + 3 ] / = total ;
}
}
}
array [ Mesh : : ARRAY_WEIGHTS ] = weights ;
2020-12-28 19:51:48 +00:00
} else if ( a . has ( " WEIGHTS_0 " ) & & a . has ( " WEIGHTS_1 " ) ) {
2024-03-12 11:50:54 +00:00
Vector < float > weights_0 = _decode_accessor_as_floats ( p_state , a [ " WEIGHTS_0 " ] , true , indices_vec4_mapping ) ;
Vector < float > weights_1 = _decode_accessor_as_floats ( p_state , a [ " WEIGHTS_1 " ] , true , indices_vec4_mapping ) ;
2020-12-28 19:51:48 +00:00
Vector < float > weights ;
ERR_FAIL_COND_V ( weights_0 . size ( ) ! = weights_1 . size ( ) , ERR_INVALID_DATA ) ;
2024-03-12 11:50:54 +00:00
ERR_FAIL_COND_V ( weights_0 . size ( ) ! = 4 * vertex_num , ERR_INVALID_DATA ) ;
2020-12-28 19:51:48 +00:00
int32_t weight_8_count = JOINT_GROUP_SIZE * 2 ;
2021-09-09 04:29:14 +00:00
weights . resize ( vertex_num * weight_8_count ) ;
for ( int32_t vertex_i = 0 ; vertex_i < vertex_num ; vertex_i + + ) {
2020-12-28 19:51:48 +00:00
weights . write [ vertex_i * weight_8_count + 0 ] = weights_0 [ vertex_i * JOINT_GROUP_SIZE + 0 ] ;
weights . write [ vertex_i * weight_8_count + 1 ] = weights_0 [ vertex_i * JOINT_GROUP_SIZE + 1 ] ;
weights . write [ vertex_i * weight_8_count + 2 ] = weights_0 [ vertex_i * JOINT_GROUP_SIZE + 2 ] ;
weights . write [ vertex_i * weight_8_count + 3 ] = weights_0 [ vertex_i * JOINT_GROUP_SIZE + 3 ] ;
weights . write [ vertex_i * weight_8_count + 4 ] = weights_1 [ vertex_i * JOINT_GROUP_SIZE + 0 ] ;
weights . write [ vertex_i * weight_8_count + 5 ] = weights_1 [ vertex_i * JOINT_GROUP_SIZE + 1 ] ;
weights . write [ vertex_i * weight_8_count + 6 ] = weights_1 [ vertex_i * JOINT_GROUP_SIZE + 2 ] ;
weights . write [ vertex_i * weight_8_count + 7 ] = weights_1 [ vertex_i * JOINT_GROUP_SIZE + 3 ] ;
}
2024-03-12 11:50:54 +00:00
{ // glTF does not seem to normalize the weights for some reason.
2020-12-28 19:51:48 +00:00
int wc = weights . size ( ) ;
float * w = weights . ptrw ( ) ;
for ( int k = 0 ; k < wc ; k + = weight_8_count ) {
float total = 0.0 ;
total + = w [ k + 0 ] ;
total + = w [ k + 1 ] ;
total + = w [ k + 2 ] ;
total + = w [ k + 3 ] ;
total + = w [ k + 4 ] ;
total + = w [ k + 5 ] ;
total + = w [ k + 6 ] ;
total + = w [ k + 7 ] ;
if ( total > 0.0 ) {
w [ k + 0 ] / = total ;
w [ k + 1 ] / = total ;
w [ k + 2 ] / = total ;
w [ k + 3 ] / = total ;
w [ k + 4 ] / = total ;
w [ k + 5 ] / = total ;
w [ k + 6 ] / = total ;
w [ k + 7 ] / = total ;
}
}
}
array [ Mesh : : ARRAY_WEIGHTS ] = weights ;
2024-10-31 16:04:25 +00:00
flags | = Mesh : : ARRAY_FLAG_USE_8_BONE_WEIGHTS ;
2020-12-21 15:39:32 +00:00
}
2024-03-12 11:50:54 +00:00
if ( ! indices . is_empty ( ) ) {
int * w = indices . ptrw ( ) ;
const int is = indices . size ( ) ;
for ( int ind_i = 0 ; ind_i < is ; ind_i + + ) {
w [ ind_i ] = indices_rev_mapping [ indices [ ind_i ] ] ;
2020-12-21 15:39:32 +00:00
}
array [ Mesh : : ARRAY_INDEX ] = indices ;
} else if ( primitive = = Mesh : : PRIMITIVE_TRIANGLES ) {
2024-03-12 11:50:54 +00:00
// Generate indices because they need to be swapped for CW/CCW.
2020-12-21 15:39:32 +00:00
const Vector < Vector3 > & vertices = array [ Mesh : : ARRAY_VERTEX ] ;
2024-01-19 12:21:39 +00:00
ERR_FAIL_COND_V ( vertices . is_empty ( ) , ERR_PARSE_ERROR ) ;
2020-12-21 15:39:32 +00:00
const int vs = vertices . size ( ) ;
indices . resize ( vs ) ;
{
int * w = indices . ptrw ( ) ;
for ( int k = 0 ; k < vs ; k + = 3 ) {
w [ k ] = k ;
w [ k + 1 ] = k + 2 ;
w [ k + 2 ] = k + 1 ;
}
}
array [ Mesh : : ARRAY_INDEX ] = indices ;
}
2023-10-31 14:51:07 +00:00
bool generate_tangents = p_state - > force_generate_tangents & & ( primitive = = Mesh : : PRIMITIVE_TRIANGLES & & ! a . has ( " TANGENT " ) & & a . has ( " NORMAL " ) ) ;
if ( generate_tangents & & ! a . has ( " TEXCOORD_0 " ) ) {
// If we don't have UVs we provide a dummy tangent array.
Vector < float > tangents ;
tangents . resize ( vertex_num * 4 ) ;
float * tangentsw = tangents . ptrw ( ) ;
Vector < Vector3 > normals = array [ Mesh : : ARRAY_NORMAL ] ;
for ( int k = 0 ; k < vertex_num ; k + + ) {
2024-02-27 19:44:57 +00:00
Vector3 tan = Vector3 ( normals [ k ] . z , - normals [ k ] . x , normals [ k ] . y ) . cross ( normals [ k ] . normalized ( ) ) . normalized ( ) ;
2023-10-31 14:51:07 +00:00
tangentsw [ k * 4 + 0 ] = tan . x ;
tangentsw [ k * 4 + 1 ] = tan . y ;
tangentsw [ k * 4 + 2 ] = tan . z ;
tangentsw [ k * 4 + 3 ] = 1.0 ;
}
array [ Mesh : : ARRAY_TANGENT ] = tangents ;
}
2023-08-29 19:04:32 +00:00
2023-12-13 11:18:50 +00:00
// Disable compression if all z equals 0 (the mesh is 2D).
const Vector < Vector3 > & vertices = array [ Mesh : : ARRAY_VERTEX ] ;
bool is_mesh_2d = true ;
for ( int k = 0 ; k < vertices . size ( ) ; k + + ) {
if ( ! Math : : is_zero_approx ( vertices [ k ] . z ) ) {
is_mesh_2d = false ;
break ;
}
}
2024-07-10 09:02:03 +00:00
if ( p_state - > force_disable_compression | | is_mesh_2d | | ! a . has ( " POSITION " ) | | ! a . has ( " NORMAL " ) | | mesh_prim . has ( " targets " ) | | ( a . has ( " JOINTS_0 " ) | | a . has ( " JOINTS_1 " ) ) ) {
2023-08-29 19:04:32 +00:00
flags & = ~ RS : : ARRAY_FLAG_COMPRESS_ATTRIBUTES ;
}
2020-12-21 15:39:32 +00:00
2021-09-21 01:24:31 +00:00
Ref < SurfaceTool > mesh_surface_tool ;
mesh_surface_tool . instantiate ( ) ;
mesh_surface_tool - > create_from_triangle_arrays ( array ) ;
if ( a . has ( " JOINTS_0 " ) & & a . has ( " JOINTS_1 " ) ) {
mesh_surface_tool - > set_skin_weight_count ( SurfaceTool : : SKIN_8_WEIGHTS ) ;
}
mesh_surface_tool - > index ( ) ;
2023-10-31 14:51:07 +00:00
if ( generate_tangents & & a . has ( " TEXCOORD_0 " ) ) {
2020-12-21 15:39:32 +00:00
//must generate mikktspace tangents.. ergh..
2021-09-21 01:24:31 +00:00
mesh_surface_tool - > generate_tangents ( ) ;
2020-12-21 15:39:32 +00:00
}
2021-09-21 01:24:31 +00:00
array = mesh_surface_tool - > commit_to_arrays ( ) ;
2020-12-21 15:39:32 +00:00
2024-02-23 21:54:20 +00:00
if ( ( flags & RS : : ARRAY_FLAG_COMPRESS_ATTRIBUTES ) & & a . has ( " NORMAL " ) & & ( a . has ( " TANGENT " ) | | generate_tangents ) ) {
// Compression is enabled, so let's validate that the normals and tangents are correct.
Vector < Vector3 > normals = array [ Mesh : : ARRAY_NORMAL ] ;
Vector < float > tangents = array [ Mesh : : ARRAY_TANGENT ] ;
for ( int vert = 0 ; vert < normals . size ( ) ; vert + + ) {
Vector3 tan = Vector3 ( tangents [ vert * 4 + 0 ] , tangents [ vert * 4 + 1 ] , tangents [ vert * 4 + 2 ] ) ;
if ( abs ( tan . dot ( normals [ vert ] ) ) > 0.0001 ) {
// Tangent is not perpendicular to the normal, so we can't use compression.
flags & = ~ RS : : ARRAY_FLAG_COMPRESS_ATTRIBUTES ;
}
}
}
2020-12-21 15:39:32 +00:00
Array morphs ;
2024-03-12 11:50:54 +00:00
// Blend shapes
2024-07-10 09:02:03 +00:00
if ( mesh_prim . has ( " targets " ) ) {
2020-12-21 15:39:32 +00:00
print_verbose ( " glTF: Mesh has targets " ) ;
2024-07-10 09:02:03 +00:00
const Array & targets = mesh_prim [ " targets " ] ;
2020-12-21 15:39:32 +00:00
import_mesh - > set_blend_shape_mode ( Mesh : : BLEND_SHAPE_MODE_NORMALIZED ) ;
if ( j = = 0 ) {
const Array & target_names = extras . has ( " targetNames " ) ? ( Array ) extras [ " targetNames " ] : Array ( ) ;
for ( int k = 0 ; k < targets . size ( ) ; k + + ) {
2023-04-12 14:05:31 +00:00
String bs_name ;
if ( k < target_names . size ( ) & & ( ( String ) target_names [ k ] ) . size ( ) ! = 0 ) {
bs_name = ( String ) target_names [ k ] ;
} else {
bs_name = String ( " morph_ " ) + itos ( k ) ;
}
import_mesh - > add_blend_shape ( bs_name ) ;
2020-12-21 15:39:32 +00:00
}
}
for ( int k = 0 ; k < targets . size ( ) ; k + + ) {
const Dictionary & t = targets [ k ] ;
Array array_copy ;
array_copy . resize ( Mesh : : ARRAY_MAX ) ;
for ( int l = 0 ; l < Mesh : : ARRAY_MAX ; l + + ) {
array_copy [ l ] = array [ l ] ;
}
if ( t . has ( " POSITION " ) ) {
2024-03-12 11:50:54 +00:00
Vector < Vector3 > varr = _decode_accessor_as_vec3 ( p_state , t [ " POSITION " ] , true , indices_mapping ) ;
2020-12-21 15:39:32 +00:00
const Vector < Vector3 > src_varr = array [ Mesh : : ARRAY_VERTEX ] ;
const int size = src_varr . size ( ) ;
ERR_FAIL_COND_V ( size = = 0 , ERR_PARSE_ERROR ) ;
{
const int max_idx = varr . size ( ) ;
varr . resize ( size ) ;
Vector3 * w_varr = varr . ptrw ( ) ;
const Vector3 * r_varr = varr . ptr ( ) ;
const Vector3 * r_src_varr = src_varr . ptr ( ) ;
for ( int l = 0 ; l < size ; l + + ) {
if ( l < max_idx ) {
w_varr [ l ] = r_varr [ l ] + r_src_varr [ l ] ;
} else {
w_varr [ l ] = r_src_varr [ l ] ;
}
}
}
array_copy [ Mesh : : ARRAY_VERTEX ] = varr ;
}
if ( t . has ( " NORMAL " ) ) {
2024-03-12 11:50:54 +00:00
Vector < Vector3 > narr = _decode_accessor_as_vec3 ( p_state , t [ " NORMAL " ] , true , indices_mapping ) ;
2020-12-21 15:39:32 +00:00
const Vector < Vector3 > src_narr = array [ Mesh : : ARRAY_NORMAL ] ;
int size = src_narr . size ( ) ;
ERR_FAIL_COND_V ( size = = 0 , ERR_PARSE_ERROR ) ;
{
int max_idx = narr . size ( ) ;
narr . resize ( size ) ;
Vector3 * w_narr = narr . ptrw ( ) ;
const Vector3 * r_narr = narr . ptr ( ) ;
const Vector3 * r_src_narr = src_narr . ptr ( ) ;
for ( int l = 0 ; l < size ; l + + ) {
if ( l < max_idx ) {
w_narr [ l ] = r_narr [ l ] + r_src_narr [ l ] ;
} else {
w_narr [ l ] = r_src_narr [ l ] ;
}
}
}
array_copy [ Mesh : : ARRAY_NORMAL ] = narr ;
}
if ( t . has ( " TANGENT " ) ) {
2024-03-12 11:50:54 +00:00
const Vector < Vector3 > tangents_v3 = _decode_accessor_as_vec3 ( p_state , t [ " TANGENT " ] , true , indices_mapping ) ;
2020-12-21 15:39:32 +00:00
const Vector < float > src_tangents = array [ Mesh : : ARRAY_TANGENT ] ;
2024-01-19 12:21:39 +00:00
ERR_FAIL_COND_V ( src_tangents . is_empty ( ) , ERR_PARSE_ERROR ) ;
2020-12-21 15:39:32 +00:00
Vector < float > tangents_v4 ;
{
int max_idx = tangents_v3 . size ( ) ;
int size4 = src_tangents . size ( ) ;
tangents_v4 . resize ( size4 ) ;
float * w4 = tangents_v4 . ptrw ( ) ;
const Vector3 * r3 = tangents_v3 . ptr ( ) ;
const float * r4 = src_tangents . ptr ( ) ;
for ( int l = 0 ; l < size4 / 4 ; l + + ) {
if ( l < max_idx ) {
w4 [ l * 4 + 0 ] = r3 [ l ] . x + r4 [ l * 4 + 0 ] ;
w4 [ l * 4 + 1 ] = r3 [ l ] . y + r4 [ l * 4 + 1 ] ;
w4 [ l * 4 + 2 ] = r3 [ l ] . z + r4 [ l * 4 + 2 ] ;
} else {
w4 [ l * 4 + 0 ] = r4 [ l * 4 + 0 ] ;
w4 [ l * 4 + 1 ] = r4 [ l * 4 + 1 ] ;
w4 [ l * 4 + 2 ] = r4 [ l * 4 + 2 ] ;
}
w4 [ l * 4 + 3 ] = r4 [ l * 4 + 3 ] ; //copy flip value
}
}
array_copy [ Mesh : : ARRAY_TANGENT ] = tangents_v4 ;
}
2021-09-21 01:24:31 +00:00
Ref < SurfaceTool > blend_surface_tool ;
blend_surface_tool . instantiate ( ) ;
blend_surface_tool - > create_from_triangle_arrays ( array_copy ) ;
if ( a . has ( " JOINTS_0 " ) & & a . has ( " JOINTS_1 " ) ) {
blend_surface_tool - > set_skin_weight_count ( SurfaceTool : : SKIN_8_WEIGHTS ) ;
}
blend_surface_tool - > index ( ) ;
2020-12-21 15:39:32 +00:00
if ( generate_tangents ) {
2021-09-21 01:24:31 +00:00
blend_surface_tool - > generate_tangents ( ) ;
2020-12-21 15:39:32 +00:00
}
2021-09-21 01:24:31 +00:00
array_copy = blend_surface_tool - > commit_to_arrays ( ) ;
2020-12-21 15:39:32 +00:00
2022-03-24 21:15:23 +00:00
// Enforce blend shape mask array format
for ( int l = 0 ; l < Mesh : : ARRAY_MAX ; l + + ) {
2023-08-29 19:04:32 +00:00
if ( ! ( Mesh : : ARRAY_FORMAT_BLEND_SHAPE_MASK & ( 1ULL < < l ) ) ) {
2022-03-24 21:15:23 +00:00
array_copy [ l ] = Variant ( ) ;
}
}
2020-12-21 15:39:32 +00:00
morphs . push_back ( array_copy ) ;
}
}
2022-11-23 23:29:01 +00:00
Ref < Material > mat ;
2022-04-13 12:14:26 +00:00
String mat_name ;
2022-12-10 21:05:13 +00:00
if ( ! p_state - > discard_meshes_and_materials ) {
2024-07-10 09:02:03 +00:00
if ( mesh_prim . has ( " material " ) ) {
const int material = mesh_prim [ " material " ] ;
2022-12-10 21:05:13 +00:00
ERR_FAIL_INDEX_V ( material , p_state - > materials . size ( ) , ERR_FILE_CORRUPT ) ;
Ref < Material > mat3d = p_state - > materials [ material ] ;
2024-07-26 09:52:26 +00:00
ERR_FAIL_COND_V ( mat3d . is_null ( ) , ERR_FILE_CORRUPT ) ;
2022-11-23 23:29:01 +00:00
Ref < BaseMaterial3D > base_material = mat3d ;
if ( has_vertex_color & & base_material . is_valid ( ) ) {
base_material - > set_flag ( BaseMaterial3D : : FLAG_ALBEDO_FROM_VERTEX_COLOR , true ) ;
2022-04-13 12:14:26 +00:00
}
mat = mat3d ;
2020-12-21 15:39:32 +00:00
2022-04-13 12:14:26 +00:00
} else {
Ref < StandardMaterial3D > mat3d ;
mat3d . instantiate ( ) ;
if ( has_vertex_color ) {
2022-11-23 23:29:01 +00:00
mat3d - > set_flag ( StandardMaterial3D : : FLAG_ALBEDO_FROM_VERTEX_COLOR , true ) ;
2022-04-13 12:14:26 +00:00
}
mat = mat3d ;
2021-09-21 01:24:31 +00:00
}
2024-07-26 09:52:26 +00:00
ERR_FAIL_COND_V ( mat . is_null ( ) , ERR_FILE_CORRUPT ) ;
2024-07-10 09:02:03 +00:00
instance_materials . append ( mat ) ;
2022-04-13 12:14:26 +00:00
mat_name = mat - > get_name ( ) ;
2020-12-21 15:39:32 +00:00
}
2021-09-21 01:24:31 +00:00
import_mesh - > add_surface ( primitive , array , morphs ,
2022-04-13 12:14:26 +00:00
Dictionary ( ) , mat , mat_name , flags ) ;
2020-12-21 15:39:32 +00:00
}
Vector < float > blend_weights ;
blend_weights . resize ( import_mesh - > get_blend_shape_count ( ) ) ;
for ( int32_t weight_i = 0 ; weight_i < blend_weights . size ( ) ; weight_i + + ) {
blend_weights . write [ weight_i ] = 0.0f ;
}
2024-07-10 09:02:03 +00:00
if ( mesh_dict . has ( " weights " ) ) {
const Array & weights = mesh_dict [ " weights " ] ;
2020-12-21 15:39:32 +00:00
for ( int j = 0 ; j < weights . size ( ) ; j + + ) {
2020-12-28 19:51:48 +00:00
if ( j > = blend_weights . size ( ) ) {
break ;
}
2020-12-21 15:39:32 +00:00
blend_weights . write [ j ] = weights [ j ] ;
}
}
2021-05-21 07:03:06 +00:00
mesh - > set_blend_weights ( blend_weights ) ;
2024-07-10 09:02:03 +00:00
mesh - > set_instance_materials ( instance_materials ) ;
2020-12-21 15:39:32 +00:00
mesh - > set_mesh ( import_mesh ) ;
2022-12-10 21:05:13 +00:00
p_state - > meshes . push_back ( mesh ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: Total meshes: " + itos ( p_state - > meshes . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2023-10-31 22:55:15 +00:00
void GLTFDocument : : set_naming_version ( int p_version ) {
_naming_version = p_version ;
}
int GLTFDocument : : get_naming_version ( ) const {
return _naming_version ;
}
2023-07-16 06:03:49 +00:00
void GLTFDocument : : set_image_format ( const String & p_image_format ) {
_image_format = p_image_format ;
}
String GLTFDocument : : get_image_format ( ) const {
return _image_format ;
}
void GLTFDocument : : set_lossy_quality ( float p_lossy_quality ) {
_lossy_quality = p_lossy_quality ;
}
float GLTFDocument : : get_lossy_quality ( ) const {
return _lossy_quality ;
}
2023-07-18 19:42:22 +00:00
Error GLTFDocument : : _serialize_images ( Ref < GLTFState > p_state ) {
2020-12-21 15:39:32 +00:00
Array images ;
2023-07-16 06:03:49 +00:00
// Check if any extension wants to be the image saver.
_image_save_extension = Ref < GLTFDocumentExtension > ( ) ;
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
Vector < String > image_formats = ext - > get_saveable_image_formats ( ) ;
if ( image_formats . has ( _image_format ) ) {
_image_save_extension = ext ;
break ;
}
}
// Serialize every image in the state's images array.
2022-12-10 21:05:13 +00:00
for ( int i = 0 ; i < p_state - > images . size ( ) ; i + + ) {
2023-08-03 06:18:45 +00:00
Dictionary image_dict ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
ERR_CONTINUE ( p_state - > images [ i ] . is_null ( ) ) ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
Ref < Image > image = p_state - > images [ i ] - > get_image ( ) ;
2020-12-21 15:39:32 +00:00
ERR_CONTINUE ( image . is_null ( ) ) ;
2023-07-16 06:03:49 +00:00
if ( image - > is_compressed ( ) ) {
image - > decompress ( ) ;
2024-08-16 01:07:30 +00:00
ERR_FAIL_COND_V_MSG ( image - > is_compressed ( ) , ERR_INVALID_DATA , " glTF: Image was compressed, but could not be decompressed. " ) ;
2023-07-16 06:03:49 +00:00
}
2020-12-21 15:39:32 +00:00
2023-07-18 19:42:22 +00:00
if ( p_state - > filename . to_lower ( ) . ends_with ( " gltf " ) ) {
String img_name = p_state - > images [ i ] - > get_name ( ) ;
if ( img_name . is_empty ( ) ) {
img_name = itos ( i ) ;
}
img_name = _gen_unique_name ( p_state , img_name ) ;
2023-07-16 06:03:49 +00:00
img_name = img_name . pad_zeros ( 3 ) ;
2023-07-18 19:42:22 +00:00
String relative_texture_dir = " textures " ;
String full_texture_dir = p_state - > base_path . path_join ( relative_texture_dir ) ;
Ref < DirAccess > da = DirAccess : : open ( p_state - > base_path ) ;
2023-09-26 02:07:43 +00:00
ERR_FAIL_COND_V ( da . is_null ( ) , FAILED ) ;
2023-07-18 19:42:22 +00:00
if ( ! da - > dir_exists ( full_texture_dir ) ) {
da - > make_dir ( full_texture_dir ) ;
}
2023-07-16 06:03:49 +00:00
if ( _image_save_extension . is_valid ( ) ) {
img_name = img_name + _image_save_extension - > get_image_file_extension ( ) ;
Error err = _image_save_extension - > save_image_at_path ( p_state , image , full_texture_dir . path_join ( img_name ) , _image_format , _lossy_quality ) ;
2024-08-16 01:07:30 +00:00
ERR_FAIL_COND_V_MSG ( err ! = OK , err , " glTF: Failed to save image in ' " + _image_format + " ' format as a separate file. " ) ;
2023-07-16 06:03:49 +00:00
} else if ( _image_format = = " PNG " ) {
img_name = img_name + " .png " ;
image - > save_png ( full_texture_dir . path_join ( img_name ) ) ;
} else if ( _image_format = = " JPEG " ) {
img_name = img_name + " .jpg " ;
image - > save_jpg ( full_texture_dir . path_join ( img_name ) , _lossy_quality ) ;
} else {
2024-08-16 01:07:30 +00:00
ERR_FAIL_V_MSG ( ERR_UNAVAILABLE , " glTF: Unknown image format ' " + _image_format + " '. " ) ;
2023-07-16 06:03:49 +00:00
}
2023-07-18 19:42:22 +00:00
image_dict [ " uri " ] = relative_texture_dir . path_join ( img_name ) . uri_encode ( ) ;
} else {
2020-12-21 15:39:32 +00:00
GLTFBufferViewIndex bvi ;
Ref < GLTFBufferView > bv ;
2021-06-17 22:03:09 +00:00
bv . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
const GLTFBufferIndex bi = 0 ;
bv - > buffer = bi ;
2022-12-10 21:05:13 +00:00
bv - > byte_offset = p_state - > buffers [ bi ] . size ( ) ;
ERR_FAIL_INDEX_V ( bi , p_state - > buffers . size ( ) , ERR_PARAMETER_RANGE_ERROR ) ;
2020-12-21 15:39:32 +00:00
Vector < uint8_t > buffer ;
Ref < ImageTexture > img_tex = image ;
if ( img_tex . is_valid ( ) ) {
2021-03-28 11:32:17 +00:00
image = img_tex - > get_image ( ) ;
2020-12-21 15:39:32 +00:00
}
2023-07-16 06:03:49 +00:00
// Save in various image formats. Note that if the format is "None",
// the state's images will be empty, so this code will not be reached.
if ( _image_save_extension . is_valid ( ) ) {
buffer = _image_save_extension - > serialize_image_to_bytes ( p_state , image , image_dict , _image_format , _lossy_quality ) ;
} else if ( _image_format = = " PNG " ) {
buffer = image - > save_png_to_buffer ( ) ;
image_dict [ " mimeType " ] = " image/png " ;
} else if ( _image_format = = " JPEG " ) {
buffer = image - > save_jpg_to_buffer ( _lossy_quality ) ;
image_dict [ " mimeType " ] = " image/jpeg " ;
} else {
2024-08-16 01:07:30 +00:00
ERR_FAIL_V_MSG ( ERR_UNAVAILABLE , " glTF: Unknown image format ' " + _image_format + " '. " ) ;
2023-07-16 06:03:49 +00:00
}
2024-08-16 01:07:30 +00:00
ERR_FAIL_COND_V_MSG ( buffer . is_empty ( ) , ERR_INVALID_DATA , " glTF: Failed to save image in ' " + _image_format + " ' format. " ) ;
2020-12-21 15:39:32 +00:00
bv - > byte_length = buffer . size ( ) ;
2022-12-10 21:05:13 +00:00
p_state - > buffers . write [ bi ] . resize ( p_state - > buffers [ bi ] . size ( ) + bv - > byte_length ) ;
memcpy ( & p_state - > buffers . write [ bi ] . write [ bv - > byte_offset ] , buffer . ptr ( ) , buffer . size ( ) ) ;
ERR_FAIL_COND_V ( bv - > byte_offset + bv - > byte_length > p_state - > buffers [ bi ] . size ( ) , ERR_FILE_CORRUPT ) ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
p_state - > buffer_views . push_back ( bv ) ;
bvi = p_state - > buffer_views . size ( ) - 1 ;
2023-08-03 06:18:45 +00:00
image_dict [ " bufferView " ] = bvi ;
2020-12-21 15:39:32 +00:00
}
2023-08-03 06:18:45 +00:00
images . push_back ( image_dict ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
print_verbose ( " Total images: " + itos ( p_state - > images . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
if ( ! images . size ( ) ) {
return OK ;
}
2022-12-10 21:05:13 +00:00
p_state - > json [ " images " ] = images ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2023-07-16 03:23:43 +00:00
Ref < Image > GLTFDocument : : _parse_image_bytes_into_image ( Ref < GLTFState > p_state , const Vector < uint8_t > & p_bytes , const String & p_mime_type , int p_index , String & r_file_extension ) {
2023-05-10 02:28:40 +00:00
Ref < Image > r_image ;
r_image . instantiate ( ) ;
// Check if any GLTFDocumentExtensions want to import this data as an image.
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
Error err = ext - > parse_image_data ( p_state , p_bytes , p_mime_type , r_image ) ;
2024-08-16 01:07:30 +00:00
ERR_CONTINUE_MSG ( err ! = OK , " glTF: Encountered error " + itos ( err ) + " when parsing image " + itos ( p_index ) + " in file " + p_state - > filename + " . Continuing. " ) ;
2023-05-10 02:28:40 +00:00
if ( ! r_image - > is_empty ( ) ) {
2023-07-16 03:23:43 +00:00
r_file_extension = ext - > get_image_file_extension ( ) ;
2023-05-10 02:28:40 +00:00
return r_image ;
}
}
// If no extension wanted to import this data as an image, try to load a PNG or JPEG.
// First we honor the mime types if they were defined.
if ( p_mime_type = = " image/png " ) { // Load buffer as PNG.
r_image - > load_png_from_buffer ( p_bytes ) ;
2023-07-16 03:23:43 +00:00
r_file_extension = " .png " ;
2023-05-10 02:28:40 +00:00
} else if ( p_mime_type = = " image/jpeg " ) { // Loader buffer as JPEG.
r_image - > load_jpg_from_buffer ( p_bytes ) ;
2023-07-16 03:23:43 +00:00
r_file_extension = " .jpg " ;
2023-05-10 02:28:40 +00:00
}
// If we didn't pass the above tests, we attempt loading as PNG and then JPEG directly.
// This covers URIs with base64-encoded data with application/* type but
// no optional mimeType property, or bufferViews with a bogus mimeType
// (e.g. `image/jpeg` but the data is actually PNG).
// That's not *exactly* what the spec mandates but this lets us be
// lenient with bogus glb files which do exist in production.
if ( r_image - > is_empty ( ) ) { // Try PNG first.
r_image - > load_png_from_buffer ( p_bytes ) ;
}
if ( r_image - > is_empty ( ) ) { // And then JPEG.
r_image - > load_jpg_from_buffer ( p_bytes ) ;
}
// If it still can't be loaded, give up and insert an empty image as placeholder.
if ( r_image - > is_empty ( ) ) {
ERR_PRINT ( vformat ( " glTF: Couldn't load image index '%d' with its given mimetype: %s. " , p_index , p_mime_type ) ) ;
}
return r_image ;
}
2023-07-16 03:23:43 +00:00
void GLTFDocument : : _parse_image_save_image ( Ref < GLTFState > p_state , const Vector < uint8_t > & p_bytes , const String & p_file_extension , int p_index , Ref < Image > p_image ) {
2023-05-10 02:28:40 +00:00
GLTFState : : GLTFHandleBinary handling = GLTFState : : GLTFHandleBinary ( p_state - > handle_binary_image ) ;
if ( p_image - > is_empty ( ) | | handling = = GLTFState : : GLTFHandleBinary : : HANDLE_BINARY_DISCARD_TEXTURES ) {
p_state - > images . push_back ( Ref < Texture2D > ( ) ) ;
p_state - > source_images . push_back ( Ref < Image > ( ) ) ;
return ;
}
# ifdef TOOLS_ENABLED
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) & & handling = = GLTFState : : GLTFHandleBinary : : HANDLE_BINARY_EXTRACT_TEXTURES ) {
2024-10-30 09:36:47 +00:00
if ( p_state - > extract_path . is_empty ( ) ) {
WARN_PRINT ( " glTF: Couldn't extract image because the base and extract paths are empty. It will be loaded directly instead, uncompressed. " ) ;
} else if ( p_state - > extract_path . begins_with ( " res://.godot/imported " ) ) {
WARN_PRINT ( vformat ( " glTF: Extract path is in the imported directory. Image index '%d' will be loaded directly, uncompressed. " , p_index ) ) ;
2023-05-10 02:28:40 +00:00
} else {
2024-09-10 07:15:31 +00:00
if ( p_image - > get_name ( ) . is_empty ( ) ) {
WARN_PRINT ( vformat ( " glTF: Image index '%d' did not have a name. It will be automatically given a name based on its index. " , p_index ) ) ;
p_image - > set_name ( itos ( p_index ) ) ;
}
2023-05-10 02:28:40 +00:00
bool must_import = true ;
Vector < uint8_t > img_data = p_image - > get_data ( ) ;
Dictionary generator_parameters ;
2024-10-30 09:36:47 +00:00
String file_path = p_state - > get_extract_path ( ) . path_join ( p_state - > get_extract_prefix ( ) + " _ " + p_image - > get_name ( ) ) ;
2023-07-16 03:23:43 +00:00
file_path + = p_file_extension . is_empty ( ) ? " .png " : p_file_extension ;
2023-05-10 02:28:40 +00:00
if ( FileAccess : : exists ( file_path + " .import " ) ) {
Ref < ConfigFile > config ;
config . instantiate ( ) ;
config - > load ( file_path + " .import " ) ;
if ( config - > has_section_key ( " remap " , " generator_parameters " ) ) {
generator_parameters = ( Dictionary ) config - > get_value ( " remap " , " generator_parameters " ) ;
}
if ( ! generator_parameters . has ( " md5 " ) ) {
must_import = false ; // Didn't come from a gltf document; don't overwrite.
}
2023-12-25 08:58:38 +00:00
}
if ( must_import ) {
2023-05-10 02:28:40 +00:00
String existing_md5 = generator_parameters [ " md5 " ] ;
unsigned char md5_hash [ 16 ] ;
CryptoCore : : md5 ( img_data . ptr ( ) , img_data . size ( ) , md5_hash ) ;
String new_md5 = String : : hex_encode_buffer ( md5_hash , 16 ) ;
generator_parameters [ " md5 " ] = new_md5 ;
if ( new_md5 = = existing_md5 ) {
must_import = false ;
}
}
if ( must_import ) {
2023-07-16 03:23:43 +00:00
Error err = OK ;
if ( p_file_extension . is_empty ( ) ) {
// If a file extension was not specified, save the image data to a PNG file.
err = p_image - > save_png ( file_path ) ;
ERR_FAIL_COND ( err ! = OK ) ;
} else {
// If a file extension was specified, save the original bytes to a file with that extension.
Ref < FileAccess > file = FileAccess : : open ( file_path , FileAccess : : WRITE , & err ) ;
ERR_FAIL_COND ( err ! = OK ) ;
file - > store_buffer ( p_bytes ) ;
file - > close ( ) ;
}
2023-05-10 02:28:40 +00:00
// ResourceLoader::import will crash if not is_editor_hint(), so this case is protected above and will fall through to uncompressed.
HashMap < StringName , Variant > custom_options ;
custom_options [ SNAME ( " mipmaps/generate " ) ] = true ;
// Will only use project settings defaults if custom_importer is empty.
EditorFileSystem : : get_singleton ( ) - > update_file ( file_path ) ;
EditorFileSystem : : get_singleton ( ) - > reimport_append ( file_path , custom_options , String ( ) , generator_parameters ) ;
}
Ref < Texture2D > saved_image = ResourceLoader : : load ( file_path , " Texture2D " ) ;
if ( saved_image . is_valid ( ) ) {
p_state - > images . push_back ( saved_image ) ;
p_state - > source_images . push_back ( saved_image - > get_image ( ) ) ;
2024-09-10 07:15:31 +00:00
return ;
2023-05-10 02:28:40 +00:00
} else {
2024-09-10 07:15:31 +00:00
WARN_PRINT ( vformat ( " glTF: Image index '%d' with the name '%s' couldn't be imported. It will be loaded directly instead, uncompressed. " , p_index , p_image - > get_name ( ) ) ) ;
2023-05-10 02:28:40 +00:00
}
}
}
# endif // TOOLS_ENABLED
if ( handling = = GLTFState : : GLTFHandleBinary : : HANDLE_BINARY_EMBED_AS_BASISU ) {
Ref < PortableCompressedTexture2D > tex ;
tex . instantiate ( ) ;
tex - > set_name ( p_image - > get_name ( ) ) ;
tex - > set_keep_compressed_buffer ( true ) ;
tex - > create_from_image ( p_image , PortableCompressedTexture2D : : COMPRESSION_MODE_BASIS_UNIVERSAL ) ;
p_state - > images . push_back ( tex ) ;
p_state - > source_images . push_back ( p_image ) ;
return ;
}
// This handles the case of HANDLE_BINARY_EMBED_AS_UNCOMPRESSED, and it also serves
// as a fallback for HANDLE_BINARY_EXTRACT_TEXTURES when this is not the editor.
Ref < ImageTexture > tex ;
tex . instantiate ( ) ;
tex - > set_name ( p_image - > get_name ( ) ) ;
tex - > set_image ( p_image ) ;
p_state - > images . push_back ( tex ) ;
p_state - > source_images . push_back ( p_image ) ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_images ( Ref < GLTFState > p_state , const String & p_base_path ) {
2024-07-26 09:52:26 +00:00
ERR_FAIL_COND_V ( p_state . is_null ( ) , ERR_INVALID_PARAMETER ) ;
2022-12-10 21:05:13 +00:00
if ( ! p_state - > json . has ( " images " ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
}
// Ref: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#images
2022-12-10 21:05:13 +00:00
const Array & images = p_state - > json [ " images " ] ;
2023-01-27 19:38:15 +00:00
HashSet < String > used_names ;
2020-12-21 15:39:32 +00:00
for ( int i = 0 ; i < images . size ( ) ; i + + ) {
2023-05-10 02:28:40 +00:00
const Dictionary & dict = images [ i ] ;
2020-12-21 15:39:32 +00:00
// glTF 2.0 supports PNG and JPEG types, which can be specified as (from spec):
// "- a URI to an external file in one of the supported images formats, or
// - a URI with embedded base64-encoded data, or
// - a reference to a bufferView; in that case mimeType must be defined."
// Since mimeType is optional for external files and base64 data, we'll have to
// fall back on letting Godot parse the data to figure out if it's PNG or JPEG.
// We'll assume that we use either URI or bufferView, so let's warn the user
// if their image somehow uses both. And fail if it has neither.
2023-05-10 02:28:40 +00:00
ERR_CONTINUE_MSG ( ! dict . has ( " uri " ) & & ! dict . has ( " bufferView " ) , " Invalid image definition in glTF file, it should specify an 'uri' or 'bufferView'. " ) ;
if ( dict . has ( " uri " ) & & dict . has ( " bufferView " ) ) {
2022-07-03 00:55:31 +00:00
WARN_PRINT ( " Invalid image definition in glTF file using both 'uri' and 'bufferView'. 'uri' will take precedence. " ) ;
2020-12-21 15:39:32 +00:00
}
2023-05-10 02:28:40 +00:00
String mime_type ;
if ( dict . has ( " mimeType " ) ) { // Should be "image/png", "image/jpeg", or something handled by an extension.
mime_type = dict [ " mimeType " ] ;
2020-12-21 15:39:32 +00:00
}
2022-07-19 00:58:27 +00:00
String image_name ;
2023-05-10 02:28:40 +00:00
if ( dict . has ( " name " ) ) {
image_name = dict [ " name " ] ;
2023-01-27 19:38:15 +00:00
image_name = image_name . get_file ( ) . get_basename ( ) . validate_filename ( ) ;
}
if ( image_name . is_empty ( ) ) {
image_name = itos ( i ) ;
}
while ( used_names . has ( image_name ) ) {
image_name + = " _ " + itos ( i ) ;
}
used_names . insert ( image_name ) ;
2023-05-10 02:28:40 +00:00
// Load the image data. If we get a byte array, store here for later.
Vector < uint8_t > data ;
if ( dict . has ( " uri " ) ) {
2020-12-21 15:39:32 +00:00
// Handles the first two bullet points from the spec (embedded data, or external file).
2023-05-10 02:28:40 +00:00
String uri = dict [ " uri " ] ;
2020-12-21 15:39:32 +00:00
if ( uri . begins_with ( " data: " ) ) { // Embedded data using base64.
data = _parse_base64_uri ( uri ) ;
// mimeType is optional, but if we have it defined in the URI, let's use it.
2023-05-10 02:28:40 +00:00
if ( mime_type . is_empty ( ) & & uri . contains ( " ; " ) ) {
// Trim "data:" prefix which is 5 characters long, and end at ";base64".
mime_type = uri . substr ( 5 , uri . find ( " ;base64 " ) - 5 ) ;
2020-12-21 15:39:32 +00:00
}
} else { // Relative path to an external image file.
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( p_base_path . is_empty ( ) , ERR_INVALID_PARAMETER ) ;
2021-09-09 04:29:14 +00:00
uri = uri . uri_decode ( ) ;
2022-08-30 00:34:01 +00:00
uri = p_base_path . path_join ( uri ) . replace ( " \\ " , " / " ) ; // Fix for Windows.
2024-09-10 07:15:31 +00:00
// If the image is in the .godot/imported directory, we can't use ResourceLoader.
if ( ! p_base_path . begins_with ( " res://.godot/imported " ) ) {
// ResourceLoader will rely on the file extension to use the relevant loader.
// The spec says that if mimeType is defined, it should take precedence (e.g.
// there could be a `.png` image which is actually JPEG), but there's no easy
// API for that in Godot, so we'd have to load as a buffer (i.e. embedded in
// the material), so we only do that only as fallback.
Ref < Texture2D > texture = ResourceLoader : : load ( uri , " Texture2D " ) ;
if ( texture . is_valid ( ) ) {
p_state - > images . push_back ( texture ) ;
p_state - > source_images . push_back ( texture - > get_image ( ) ) ;
continue ;
}
2023-05-10 02:28:40 +00:00
}
// mimeType is optional, but if we have it in the file extension, let's use it.
// If the mimeType does not match with the file extension, either it should be
// specified in the file, or the GLTFDocumentExtension should handle it.
if ( mime_type . is_empty ( ) ) {
mime_type = " image/ " + uri . get_extension ( ) ;
}
// Fallback to loading as byte array. This enables us to support the
// spec's requirement that we honor mimetype regardless of file URI.
data = FileAccess : : get_file_as_bytes ( uri ) ;
if ( data . size ( ) = = 0 ) {
WARN_PRINT ( vformat ( " glTF: Image index '%d' couldn't be loaded as a buffer of MIME type '%s' from URI: %s because there was no data to load. Skipping it. " , i , mime_type , uri ) ) ;
2022-12-10 21:05:13 +00:00
p_state - > images . push_back ( Ref < Texture2D > ( ) ) ; // Placeholder to keep count.
2023-05-10 02:28:40 +00:00
p_state - > source_images . push_back ( Ref < Image > ( ) ) ;
2020-12-21 15:39:32 +00:00
continue ;
}
}
2023-05-10 02:28:40 +00:00
} else if ( dict . has ( " bufferView " ) ) {
2020-12-21 15:39:32 +00:00
// Handles the third bullet point from the spec (bufferView).
2023-05-10 02:28:40 +00:00
ERR_FAIL_COND_V_MSG ( mime_type . is_empty ( ) , ERR_FILE_CORRUPT , vformat ( " glTF: Image index '%d' specifies 'bufferView' but no 'mimeType', which is invalid. " , i ) ) ;
const GLTFBufferViewIndex bvi = dict [ " bufferView " ] ;
2022-12-10 21:05:13 +00:00
ERR_FAIL_INDEX_V ( bvi , p_state - > buffer_views . size ( ) , ERR_PARAMETER_RANGE_ERROR ) ;
Ref < GLTFBufferView > bv = p_state - > buffer_views [ bvi ] ;
2020-12-21 15:39:32 +00:00
const GLTFBufferIndex bi = bv - > buffer ;
2022-12-10 21:05:13 +00:00
ERR_FAIL_INDEX_V ( bi , p_state - > buffers . size ( ) , ERR_PARAMETER_RANGE_ERROR ) ;
ERR_FAIL_COND_V ( bv - > byte_offset + bv - > byte_length > p_state - > buffers [ bi ] . size ( ) , ERR_FILE_CORRUPT ) ;
2023-05-10 02:28:40 +00:00
const PackedByteArray & buffer = p_state - > buffers [ bi ] ;
data = buffer . slice ( bv - > byte_offset , bv - > byte_offset + bv - > byte_length ) ;
}
// Done loading the image data bytes. Check that we actually got data to parse.
// Note: There are paths above that return early, so this point might not be reached.
if ( data . is_empty ( ) ) {
WARN_PRINT ( vformat ( " glTF: Image index '%d' couldn't be loaded, no data found. Skipping it. " , i ) ) ;
p_state - > images . push_back ( Ref < Texture2D > ( ) ) ; // Placeholder to keep count.
2022-07-19 00:58:27 +00:00
p_state - > source_images . push_back ( Ref < Image > ( ) ) ;
continue ;
}
2023-05-10 02:28:40 +00:00
// Parse the image data from bytes into an Image resource and save if needed.
2023-07-16 03:23:43 +00:00
String file_extension ;
Ref < Image > img = _parse_image_bytes_into_image ( p_state , data , mime_type , i , file_extension ) ;
2023-01-27 19:38:15 +00:00
img - > set_name ( image_name ) ;
2023-07-16 03:23:43 +00:00
_parse_image_save_image ( p_state , data , file_extension , i , img ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: Total images: " + itos ( p_state - > images . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _serialize_textures ( Ref < GLTFState > p_state ) {
if ( ! p_state - > textures . size ( ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
}
Array textures ;
2022-12-10 21:05:13 +00:00
for ( int32_t i = 0 ; i < p_state - > textures . size ( ) ; i + + ) {
2023-08-03 06:18:45 +00:00
Dictionary texture_dict ;
Ref < GLTFTexture > gltf_texture = p_state - > textures [ i ] ;
2023-07-16 06:03:49 +00:00
if ( _image_save_extension . is_valid ( ) ) {
Error err = _image_save_extension - > serialize_texture_json ( p_state , texture_dict , gltf_texture , _image_format ) ;
ERR_FAIL_COND_V ( err ! = OK , err ) ;
} else {
ERR_CONTINUE ( gltf_texture - > get_src_image ( ) = = - 1 ) ;
texture_dict [ " source " ] = gltf_texture - > get_src_image ( ) ;
}
2023-08-03 06:18:45 +00:00
GLTFTextureSamplerIndex sampler_index = gltf_texture - > get_sampler ( ) ;
2021-10-04 15:49:42 +00:00
if ( sampler_index ! = - 1 ) {
2023-08-03 06:18:45 +00:00
texture_dict [ " sampler " ] = sampler_index ;
2021-10-04 15:49:42 +00:00
}
2023-08-03 06:18:45 +00:00
textures . push_back ( texture_dict ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
p_state - > json [ " textures " ] = textures ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_textures ( Ref < GLTFState > p_state ) {
if ( ! p_state - > json . has ( " textures " ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
const Array & textures = p_state - > json [ " textures " ] ;
2020-12-21 15:39:32 +00:00
for ( GLTFTextureIndex i = 0 ; i < textures . size ( ) ; i + + ) {
2023-08-03 06:18:45 +00:00
const Dictionary & texture_dict = textures [ i ] ;
Ref < GLTFTexture > gltf_texture ;
gltf_texture . instantiate ( ) ;
2023-05-10 02:28:40 +00:00
// Check if any GLTFDocumentExtensions want to handle this texture JSON.
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
2023-08-03 06:18:45 +00:00
Error err = ext - > parse_texture_json ( p_state , texture_dict , gltf_texture ) ;
2024-08-16 01:07:30 +00:00
ERR_CONTINUE_MSG ( err ! = OK , " glTF: Encountered error " + itos ( err ) + " when parsing texture JSON " + String ( Variant ( texture_dict ) ) + " in file " + p_state - > filename + " . Continuing. " ) ;
2023-08-03 06:18:45 +00:00
if ( gltf_texture - > get_src_image ( ) ! = - 1 ) {
2023-05-10 02:28:40 +00:00
break ;
}
}
2023-08-03 06:18:45 +00:00
if ( gltf_texture - > get_src_image ( ) = = - 1 ) {
2024-08-16 01:07:30 +00:00
// No extensions handled it, so use the base glTF source.
2023-05-10 02:28:40 +00:00
// This may be the fallback, or the only option anyway.
2023-08-03 06:18:45 +00:00
ERR_FAIL_COND_V ( ! texture_dict . has ( " source " ) , ERR_PARSE_ERROR ) ;
gltf_texture - > set_src_image ( texture_dict [ " source " ] ) ;
2023-05-10 02:28:40 +00:00
}
2023-08-03 06:18:45 +00:00
if ( gltf_texture - > get_sampler ( ) = = - 1 & & texture_dict . has ( " sampler " ) ) {
gltf_texture - > set_sampler ( texture_dict [ " sampler " ] ) ;
2021-10-04 15:49:42 +00:00
}
2023-08-03 06:18:45 +00:00
p_state - > textures . push_back ( gltf_texture ) ;
2020-12-21 15:39:32 +00:00
}
return OK ;
}
2022-12-10 21:05:13 +00:00
GLTFTextureIndex GLTFDocument : : _set_texture ( Ref < GLTFState > p_state , Ref < Texture2D > p_texture , StandardMaterial3D : : TextureFilter p_filter_mode , bool p_repeats ) {
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( p_texture . is_null ( ) , - 1 ) ;
Ref < GLTFTexture > gltf_texture ;
2021-06-17 22:03:09 +00:00
gltf_texture . instantiate ( ) ;
2021-03-28 11:32:17 +00:00
ERR_FAIL_COND_V ( p_texture - > get_image ( ) . is_null ( ) , - 1 ) ;
2022-12-10 21:05:13 +00:00
GLTFImageIndex gltf_src_image_i = p_state - > images . size ( ) ;
p_state - > images . push_back ( p_texture ) ;
2023-05-10 02:28:40 +00:00
p_state - > source_images . push_back ( p_texture - > get_image ( ) ) ;
2020-12-21 15:39:32 +00:00
gltf_texture - > set_src_image ( gltf_src_image_i ) ;
2022-12-10 21:05:13 +00:00
gltf_texture - > set_sampler ( _set_sampler_for_mode ( p_state , p_filter_mode , p_repeats ) ) ;
GLTFTextureIndex gltf_texture_i = p_state - > textures . size ( ) ;
p_state - > textures . push_back ( gltf_texture ) ;
2020-12-21 15:39:32 +00:00
return gltf_texture_i ;
}
2022-07-19 00:58:27 +00:00
Ref < Texture2D > GLTFDocument : : _get_texture ( Ref < GLTFState > p_state , const GLTFTextureIndex p_texture , int p_texture_types ) {
2022-12-10 21:05:13 +00:00
ERR_FAIL_INDEX_V ( p_texture , p_state - > textures . size ( ) , Ref < Texture2D > ( ) ) ;
const GLTFImageIndex image = p_state - > textures [ p_texture ] - > get_src_image ( ) ;
ERR_FAIL_INDEX_V ( image , p_state - > images . size ( ) , Ref < Texture2D > ( ) ) ;
2022-07-19 00:58:27 +00:00
if ( GLTFState : : GLTFHandleBinary ( p_state - > handle_binary_image ) = = GLTFState : : GLTFHandleBinary : : HANDLE_BINARY_EMBED_AS_BASISU ) {
2023-08-05 23:52:51 +00:00
ERR_FAIL_INDEX_V ( image , p_state - > source_images . size ( ) , Ref < Texture2D > ( ) ) ;
2022-07-19 00:58:27 +00:00
Ref < PortableCompressedTexture2D > portable_texture ;
portable_texture . instantiate ( ) ;
portable_texture - > set_keep_compressed_buffer ( true ) ;
2023-08-05 23:52:51 +00:00
Ref < Image > new_img = p_state - > source_images [ image ] - > duplicate ( ) ;
2022-07-19 00:58:27 +00:00
ERR_FAIL_COND_V ( new_img . is_null ( ) , Ref < Texture2D > ( ) ) ;
new_img - > generate_mipmaps ( ) ;
if ( p_texture_types ) {
portable_texture - > create_from_image ( new_img , PortableCompressedTexture2D : : COMPRESSION_MODE_BASIS_UNIVERSAL , true ) ;
} else {
portable_texture - > create_from_image ( new_img , PortableCompressedTexture2D : : COMPRESSION_MODE_BASIS_UNIVERSAL , false ) ;
}
p_state - > images . write [ image ] = portable_texture ;
2023-05-10 02:28:40 +00:00
p_state - > source_images . write [ image ] = new_img ;
2022-07-19 00:58:27 +00:00
}
2022-12-10 21:05:13 +00:00
return p_state - > images [ image ] ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
GLTFTextureSamplerIndex GLTFDocument : : _set_sampler_for_mode ( Ref < GLTFState > p_state , StandardMaterial3D : : TextureFilter p_filter_mode , bool p_repeats ) {
for ( int i = 0 ; i < p_state - > texture_samplers . size ( ) ; + + i ) {
if ( p_state - > texture_samplers [ i ] - > get_filter_mode ( ) = = p_filter_mode ) {
2021-10-04 15:49:42 +00:00
return i ;
}
}
2022-12-10 21:05:13 +00:00
GLTFTextureSamplerIndex gltf_sampler_i = p_state - > texture_samplers . size ( ) ;
2021-10-04 15:49:42 +00:00
Ref < GLTFTextureSampler > gltf_sampler ;
gltf_sampler . instantiate ( ) ;
gltf_sampler - > set_filter_mode ( p_filter_mode ) ;
gltf_sampler - > set_wrap_mode ( p_repeats ) ;
2022-12-10 21:05:13 +00:00
p_state - > texture_samplers . push_back ( gltf_sampler ) ;
2021-10-04 15:49:42 +00:00
return gltf_sampler_i ;
}
2022-12-10 21:05:13 +00:00
Ref < GLTFTextureSampler > GLTFDocument : : _get_sampler_for_texture ( Ref < GLTFState > p_state , const GLTFTextureIndex p_texture ) {
ERR_FAIL_INDEX_V ( p_texture , p_state - > textures . size ( ) , Ref < Texture2D > ( ) ) ;
const GLTFTextureSamplerIndex sampler = p_state - > textures [ p_texture ] - > get_sampler ( ) ;
2021-10-04 15:49:42 +00:00
if ( sampler = = - 1 ) {
2022-12-10 21:05:13 +00:00
return p_state - > default_texture_sampler ;
2021-10-04 15:49:42 +00:00
} else {
2022-12-10 21:05:13 +00:00
ERR_FAIL_INDEX_V ( sampler , p_state - > texture_samplers . size ( ) , Ref < GLTFTextureSampler > ( ) ) ;
2021-10-04 15:49:42 +00:00
2022-12-10 21:05:13 +00:00
return p_state - > texture_samplers [ sampler ] ;
2021-10-04 15:49:42 +00:00
}
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _serialize_texture_samplers ( Ref < GLTFState > p_state ) {
if ( ! p_state - > texture_samplers . size ( ) ) {
2021-10-04 15:49:42 +00:00
return OK ;
}
Array samplers ;
2022-12-10 21:05:13 +00:00
for ( int32_t i = 0 ; i < p_state - > texture_samplers . size ( ) ; + + i ) {
2021-10-04 15:49:42 +00:00
Dictionary d ;
2022-12-10 21:05:13 +00:00
Ref < GLTFTextureSampler > s = p_state - > texture_samplers [ i ] ;
2021-10-04 15:49:42 +00:00
d [ " magFilter " ] = s - > get_mag_filter ( ) ;
d [ " minFilter " ] = s - > get_min_filter ( ) ;
d [ " wrapS " ] = s - > get_wrap_s ( ) ;
d [ " wrapT " ] = s - > get_wrap_t ( ) ;
samplers . push_back ( d ) ;
}
2022-12-10 21:05:13 +00:00
p_state - > json [ " samplers " ] = samplers ;
2021-10-04 15:49:42 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_texture_samplers ( Ref < GLTFState > p_state ) {
p_state - > default_texture_sampler . instantiate ( ) ;
p_state - > default_texture_sampler - > set_min_filter ( GLTFTextureSampler : : FilterMode : : LINEAR_MIPMAP_LINEAR ) ;
p_state - > default_texture_sampler - > set_mag_filter ( GLTFTextureSampler : : FilterMode : : LINEAR ) ;
p_state - > default_texture_sampler - > set_wrap_s ( GLTFTextureSampler : : WrapMode : : REPEAT ) ;
p_state - > default_texture_sampler - > set_wrap_t ( GLTFTextureSampler : : WrapMode : : REPEAT ) ;
2021-10-04 15:49:42 +00:00
2022-12-10 21:05:13 +00:00
if ( ! p_state - > json . has ( " samplers " ) ) {
2021-10-04 15:49:42 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
const Array & samplers = p_state - > json [ " samplers " ] ;
2021-10-04 15:49:42 +00:00
for ( int i = 0 ; i < samplers . size ( ) ; + + i ) {
const Dictionary & d = samplers [ i ] ;
Ref < GLTFTextureSampler > sampler ;
sampler . instantiate ( ) ;
if ( d . has ( " minFilter " ) ) {
sampler - > set_min_filter ( d [ " minFilter " ] ) ;
} else {
sampler - > set_min_filter ( GLTFTextureSampler : : FilterMode : : LINEAR_MIPMAP_LINEAR ) ;
}
if ( d . has ( " magFilter " ) ) {
sampler - > set_mag_filter ( d [ " magFilter " ] ) ;
} else {
sampler - > set_mag_filter ( GLTFTextureSampler : : FilterMode : : LINEAR ) ;
}
if ( d . has ( " wrapS " ) ) {
sampler - > set_wrap_s ( d [ " wrapS " ] ) ;
} else {
sampler - > set_wrap_s ( GLTFTextureSampler : : WrapMode : : DEFAULT ) ;
}
if ( d . has ( " wrapT " ) ) {
sampler - > set_wrap_t ( d [ " wrapT " ] ) ;
} else {
sampler - > set_wrap_t ( GLTFTextureSampler : : WrapMode : : DEFAULT ) ;
}
2022-12-10 21:05:13 +00:00
p_state - > texture_samplers . push_back ( sampler ) ;
2021-10-04 15:49:42 +00:00
}
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _serialize_materials ( Ref < GLTFState > p_state ) {
2020-12-21 15:39:32 +00:00
Array materials ;
2022-12-10 21:05:13 +00:00
for ( int32_t i = 0 ; i < p_state - > materials . size ( ) ; i + + ) {
2020-12-21 15:39:32 +00:00
Dictionary d ;
2022-12-10 21:05:13 +00:00
Ref < Material > material = p_state - > materials [ i ] ;
2020-12-21 15:39:32 +00:00
if ( material . is_null ( ) ) {
materials . push_back ( d ) ;
continue ;
}
2020-12-15 12:04:21 +00:00
if ( ! material - > get_name ( ) . is_empty ( ) ) {
2022-12-10 21:05:13 +00:00
d [ " name " ] = _gen_unique_name ( p_state , material - > get_name ( ) ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-17 04:36:01 +00:00
2022-11-23 23:29:01 +00:00
Ref < BaseMaterial3D > base_material = material ;
2022-12-17 04:36:01 +00:00
if ( base_material . is_null ( ) ) {
materials . push_back ( d ) ;
continue ;
}
Dictionary mr ;
{
Array arr ;
const Color c = base_material - > get_albedo ( ) . srgb_to_linear ( ) ;
arr . push_back ( c . r ) ;
arr . push_back ( c . g ) ;
arr . push_back ( c . b ) ;
arr . push_back ( c . a ) ;
mr [ " baseColorFactor " ] = arr ;
}
2023-07-16 06:03:49 +00:00
if ( _image_format ! = " None " ) {
2022-12-17 04:36:01 +00:00
Dictionary bct ;
Ref < Texture2D > albedo_texture = base_material - > get_texture ( BaseMaterial3D : : TEXTURE_ALBEDO ) ;
GLTFTextureIndex gltf_texture_index = - 1 ;
if ( albedo_texture . is_valid ( ) & & albedo_texture - > get_image ( ) . is_valid ( ) ) {
albedo_texture - > set_name ( material - > get_name ( ) + " _albedo " ) ;
gltf_texture_index = _set_texture ( p_state , albedo_texture , base_material - > get_texture_filter ( ) , base_material - > get_flag ( BaseMaterial3D : : FLAG_USE_TEXTURE_REPEAT ) ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-17 04:36:01 +00:00
if ( gltf_texture_index ! = - 1 ) {
bct [ " index " ] = gltf_texture_index ;
Dictionary extensions = _serialize_texture_transform_uv1 ( material ) ;
if ( ! extensions . is_empty ( ) ) {
bct [ " extensions " ] = extensions ;
p_state - > use_khr_texture_transform = true ;
}
mr [ " baseColorTexture " ] = bct ;
}
}
mr [ " metallicFactor " ] = base_material - > get_metallic ( ) ;
mr [ " roughnessFactor " ] = base_material - > get_roughness ( ) ;
2023-09-26 17:01:26 +00:00
if ( _image_format ! = " None " ) {
bool has_roughness = base_material - > get_texture ( BaseMaterial3D : : TEXTURE_ROUGHNESS ) . is_valid ( ) & & base_material - > get_texture ( BaseMaterial3D : : TEXTURE_ROUGHNESS ) - > get_image ( ) . is_valid ( ) ;
bool has_ao = base_material - > get_feature ( BaseMaterial3D : : FEATURE_AMBIENT_OCCLUSION ) & & base_material - > get_texture ( BaseMaterial3D : : TEXTURE_AMBIENT_OCCLUSION ) . is_valid ( ) ;
bool has_metalness = base_material - > get_texture ( BaseMaterial3D : : TEXTURE_METALLIC ) . is_valid ( ) & & base_material - > get_texture ( BaseMaterial3D : : TEXTURE_METALLIC ) - > get_image ( ) . is_valid ( ) ;
if ( has_ao | | has_roughness | | has_metalness ) {
Dictionary mrt ;
Ref < Texture2D > roughness_texture = base_material - > get_texture ( BaseMaterial3D : : TEXTURE_ROUGHNESS ) ;
BaseMaterial3D : : TextureChannel roughness_channel = base_material - > get_roughness_texture_channel ( ) ;
Ref < Texture2D > metallic_texture = base_material - > get_texture ( BaseMaterial3D : : TEXTURE_METALLIC ) ;
BaseMaterial3D : : TextureChannel metalness_channel = base_material - > get_metallic_texture_channel ( ) ;
Ref < Texture2D > ao_texture = base_material - > get_texture ( BaseMaterial3D : : TEXTURE_AMBIENT_OCCLUSION ) ;
BaseMaterial3D : : TextureChannel ao_channel = base_material - > get_ao_texture_channel ( ) ;
Ref < ImageTexture > orm_texture ;
orm_texture . instantiate ( ) ;
Ref < Image > orm_image ;
orm_image . instantiate ( ) ;
int32_t height = 0 ;
int32_t width = 0 ;
Ref < Image > ao_image ;
if ( has_ao ) {
height = ao_texture - > get_height ( ) ;
width = ao_texture - > get_width ( ) ;
ao_image = ao_texture - > get_image ( ) ;
Ref < ImageTexture > img_tex = ao_image ;
if ( img_tex . is_valid ( ) ) {
ao_image = img_tex - > get_image ( ) ;
}
if ( ao_image - > is_compressed ( ) ) {
ao_image - > decompress ( ) ;
}
2022-12-17 04:36:01 +00:00
}
2023-09-26 17:01:26 +00:00
Ref < Image > roughness_image ;
if ( has_roughness ) {
height = roughness_texture - > get_height ( ) ;
width = roughness_texture - > get_width ( ) ;
roughness_image = roughness_texture - > get_image ( ) ;
Ref < ImageTexture > img_tex = roughness_image ;
if ( img_tex . is_valid ( ) ) {
roughness_image = img_tex - > get_image ( ) ;
2020-12-21 15:39:32 +00:00
}
2023-09-26 17:01:26 +00:00
if ( roughness_image - > is_compressed ( ) ) {
roughness_image - > decompress ( ) ;
}
}
Ref < Image > metallness_image ;
if ( has_metalness ) {
height = metallic_texture - > get_height ( ) ;
width = metallic_texture - > get_width ( ) ;
metallness_image = metallic_texture - > get_image ( ) ;
Ref < ImageTexture > img_tex = metallness_image ;
if ( img_tex . is_valid ( ) ) {
metallness_image = img_tex - > get_image ( ) ;
}
if ( metallness_image - > is_compressed ( ) ) {
metallness_image - > decompress ( ) ;
2020-12-21 15:39:32 +00:00
}
2023-09-26 17:01:26 +00:00
}
Ref < Texture2D > albedo_texture = base_material - > get_texture ( BaseMaterial3D : : TEXTURE_ALBEDO ) ;
if ( albedo_texture . is_valid ( ) & & albedo_texture - > get_image ( ) . is_valid ( ) ) {
height = albedo_texture - > get_height ( ) ;
width = albedo_texture - > get_width ( ) ;
}
orm_image - > initialize_data ( width , height , false , Image : : FORMAT_RGBA8 ) ;
if ( ao_image . is_valid ( ) & & ao_image - > get_size ( ) ! = Vector2 ( width , height ) ) {
ao_image - > resize ( width , height , Image : : INTERPOLATE_LANCZOS ) ;
}
if ( roughness_image . is_valid ( ) & & roughness_image - > get_size ( ) ! = Vector2 ( width , height ) ) {
roughness_image - > resize ( width , height , Image : : INTERPOLATE_LANCZOS ) ;
}
if ( metallness_image . is_valid ( ) & & metallness_image - > get_size ( ) ! = Vector2 ( width , height ) ) {
metallness_image - > resize ( width , height , Image : : INTERPOLATE_LANCZOS ) ;
}
for ( int32_t h = 0 ; h < height ; h + + ) {
for ( int32_t w = 0 ; w < width ; w + + ) {
Color c = Color ( 1.0f , 1.0f , 1.0f ) ;
if ( has_ao ) {
if ( BaseMaterial3D : : TextureChannel : : TEXTURE_CHANNEL_RED = = ao_channel ) {
c . r = ao_image - > get_pixel ( w , h ) . r ;
} else if ( BaseMaterial3D : : TextureChannel : : TEXTURE_CHANNEL_GREEN = = ao_channel ) {
c . r = ao_image - > get_pixel ( w , h ) . g ;
} else if ( BaseMaterial3D : : TextureChannel : : TEXTURE_CHANNEL_BLUE = = ao_channel ) {
c . r = ao_image - > get_pixel ( w , h ) . b ;
} else if ( BaseMaterial3D : : TextureChannel : : TEXTURE_CHANNEL_ALPHA = = ao_channel ) {
c . r = ao_image - > get_pixel ( w , h ) . a ;
}
2022-11-23 23:29:01 +00:00
}
2023-09-26 17:01:26 +00:00
if ( has_roughness ) {
if ( BaseMaterial3D : : TextureChannel : : TEXTURE_CHANNEL_RED = = roughness_channel ) {
c . g = roughness_image - > get_pixel ( w , h ) . r ;
} else if ( BaseMaterial3D : : TextureChannel : : TEXTURE_CHANNEL_GREEN = = roughness_channel ) {
c . g = roughness_image - > get_pixel ( w , h ) . g ;
} else if ( BaseMaterial3D : : TextureChannel : : TEXTURE_CHANNEL_BLUE = = roughness_channel ) {
c . g = roughness_image - > get_pixel ( w , h ) . b ;
} else if ( BaseMaterial3D : : TextureChannel : : TEXTURE_CHANNEL_ALPHA = = roughness_channel ) {
c . g = roughness_image - > get_pixel ( w , h ) . a ;
}
}
if ( has_metalness ) {
if ( BaseMaterial3D : : TextureChannel : : TEXTURE_CHANNEL_RED = = metalness_channel ) {
c . b = metallness_image - > get_pixel ( w , h ) . r ;
} else if ( BaseMaterial3D : : TextureChannel : : TEXTURE_CHANNEL_GREEN = = metalness_channel ) {
c . b = metallness_image - > get_pixel ( w , h ) . g ;
} else if ( BaseMaterial3D : : TextureChannel : : TEXTURE_CHANNEL_BLUE = = metalness_channel ) {
c . b = metallness_image - > get_pixel ( w , h ) . b ;
} else if ( BaseMaterial3D : : TextureChannel : : TEXTURE_CHANNEL_ALPHA = = metalness_channel ) {
c . b = metallness_image - > get_pixel ( w , h ) . a ;
}
}
orm_image - > set_pixel ( w , h , c ) ;
2022-07-23 23:52:46 +00:00
}
2020-12-21 15:39:32 +00:00
}
2023-09-26 17:01:26 +00:00
orm_image - > generate_mipmaps ( ) ;
orm_texture - > set_image ( orm_image ) ;
GLTFTextureIndex orm_texture_index = - 1 ;
if ( has_ao | | has_roughness | | has_metalness ) {
orm_texture - > set_name ( material - > get_name ( ) + " _orm " ) ;
orm_texture_index = _set_texture ( p_state , orm_texture , base_material - > get_texture_filter ( ) , base_material - > get_flag ( BaseMaterial3D : : FLAG_USE_TEXTURE_REPEAT ) ) ;
}
if ( has_ao ) {
Dictionary occt ;
occt [ " index " ] = orm_texture_index ;
d [ " occlusionTexture " ] = occt ;
}
if ( has_roughness | | has_metalness ) {
mrt [ " index " ] = orm_texture_index ;
Dictionary extensions = _serialize_texture_transform_uv1 ( material ) ;
if ( ! extensions . is_empty ( ) ) {
mrt [ " extensions " ] = extensions ;
p_state - > use_khr_texture_transform = true ;
}
mr [ " metallicRoughnessTexture " ] = mrt ;
2022-12-17 04:36:01 +00:00
}
}
2020-12-21 15:39:32 +00:00
}
2022-12-17 04:36:01 +00:00
d [ " pbrMetallicRoughness " ] = mr ;
2023-09-26 17:01:26 +00:00
if ( base_material - > get_feature ( BaseMaterial3D : : FEATURE_NORMAL_MAPPING ) & & _image_format ! = " None " ) {
2020-12-21 15:39:32 +00:00
Dictionary nt ;
Ref < ImageTexture > tex ;
2021-06-17 22:03:09 +00:00
tex . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
{
2022-11-23 23:29:01 +00:00
Ref < Texture2D > normal_texture = base_material - > get_texture ( BaseMaterial3D : : TEXTURE_NORMAL ) ;
2021-12-31 16:43:15 +00:00
if ( normal_texture . is_valid ( ) ) {
// Code for uncompressing RG normal maps
Ref < Image > img = normal_texture - > get_image ( ) ;
if ( img . is_valid ( ) ) {
Ref < ImageTexture > img_tex = img ;
if ( img_tex . is_valid ( ) ) {
img = img_tex - > get_image ( ) ;
}
img - > decompress ( ) ;
img - > convert ( Image : : FORMAT_RGBA8 ) ;
for ( int32_t y = 0 ; y < img - > get_height ( ) ; y + + ) {
for ( int32_t x = 0 ; x < img - > get_width ( ) ; x + + ) {
Color c = img - > get_pixel ( x , y ) ;
Vector2 red_green = Vector2 ( c . r , c . g ) ;
red_green = red_green * Vector2 ( 2.0f , 2.0f ) - Vector2 ( 1.0f , 1.0f ) ;
float blue = 1.0f - red_green . dot ( red_green ) ;
blue = MAX ( 0.0f , blue ) ;
c . b = Math : : sqrt ( blue ) ;
img - > set_pixel ( x , y , c ) ;
}
}
2022-05-03 23:49:20 +00:00
tex - > set_image ( img ) ;
2020-12-21 15:39:32 +00:00
}
}
}
GLTFTextureIndex gltf_texture_index = - 1 ;
2021-03-28 11:32:17 +00:00
if ( tex . is_valid ( ) & & tex - > get_image ( ) . is_valid ( ) ) {
2020-12-21 15:39:32 +00:00
tex - > set_name ( material - > get_name ( ) + " _normal " ) ;
2022-12-10 21:05:13 +00:00
gltf_texture_index = _set_texture ( p_state , tex , base_material - > get_texture_filter ( ) , base_material - > get_flag ( BaseMaterial3D : : FLAG_USE_TEXTURE_REPEAT ) ) ;
2020-12-21 15:39:32 +00:00
}
2022-11-23 23:29:01 +00:00
nt [ " scale " ] = base_material - > get_normal_scale ( ) ;
2020-12-21 15:39:32 +00:00
if ( gltf_texture_index ! = - 1 ) {
nt [ " index " ] = gltf_texture_index ;
d [ " normalTexture " ] = nt ;
}
}
2022-11-23 23:29:01 +00:00
if ( base_material - > get_feature ( BaseMaterial3D : : FEATURE_EMISSION ) ) {
const Color c = base_material - > get_emission ( ) . linear_to_srgb ( ) ;
2020-12-21 15:39:32 +00:00
Array arr ;
arr . push_back ( c . r ) ;
arr . push_back ( c . g ) ;
arr . push_back ( c . b ) ;
d [ " emissiveFactor " ] = arr ;
}
2022-12-17 04:36:01 +00:00
2023-09-26 17:01:26 +00:00
if ( base_material - > get_feature ( BaseMaterial3D : : FEATURE_EMISSION ) & & _image_format ! = " None " ) {
2020-12-21 15:39:32 +00:00
Dictionary et ;
2022-11-23 23:29:01 +00:00
Ref < Texture2D > emission_texture = base_material - > get_texture ( BaseMaterial3D : : TEXTURE_EMISSION ) ;
2020-12-21 15:39:32 +00:00
GLTFTextureIndex gltf_texture_index = - 1 ;
2021-03-28 11:32:17 +00:00
if ( emission_texture . is_valid ( ) & & emission_texture - > get_image ( ) . is_valid ( ) ) {
2020-12-21 15:39:32 +00:00
emission_texture - > set_name ( material - > get_name ( ) + " _emission " ) ;
2022-12-10 21:05:13 +00:00
gltf_texture_index = _set_texture ( p_state , emission_texture , base_material - > get_texture_filter ( ) , base_material - > get_flag ( BaseMaterial3D : : FLAG_USE_TEXTURE_REPEAT ) ) ;
2020-12-21 15:39:32 +00:00
}
if ( gltf_texture_index ! = - 1 ) {
et [ " index " ] = gltf_texture_index ;
d [ " emissiveTexture " ] = et ;
}
}
2022-12-17 04:36:01 +00:00
2022-11-23 23:29:01 +00:00
const bool ds = base_material - > get_cull_mode ( ) = = BaseMaterial3D : : CULL_DISABLED ;
2020-12-21 15:39:32 +00:00
if ( ds ) {
d [ " doubleSided " ] = ds ;
}
2022-12-17 04:36:01 +00:00
2022-11-23 23:29:01 +00:00
if ( base_material - > get_transparency ( ) = = BaseMaterial3D : : TRANSPARENCY_ALPHA_SCISSOR ) {
2020-12-21 15:39:32 +00:00
d [ " alphaMode " ] = " MASK " ;
2022-11-23 23:29:01 +00:00
d [ " alphaCutoff " ] = base_material - > get_alpha_scissor_threshold ( ) ;
} else if ( base_material - > get_transparency ( ) ! = BaseMaterial3D : : TRANSPARENCY_DISABLED ) {
2020-12-21 15:39:32 +00:00
d [ " alphaMode " ] = " BLEND " ;
}
2022-12-17 04:36:01 +00:00
2023-03-03 11:10:42 +00:00
Dictionary extensions ;
if ( base_material - > get_shading_mode ( ) = = BaseMaterial3D : : SHADING_MODE_UNSHADED ) {
Dictionary mat_unlit ;
extensions [ " KHR_materials_unlit " ] = mat_unlit ;
p_state - > add_used_extension ( " KHR_materials_unlit " ) ;
}
2023-07-08 20:56:11 +00:00
if ( base_material - > get_feature ( BaseMaterial3D : : FEATURE_EMISSION ) & & ! Math : : is_equal_approx ( base_material - > get_emission_energy_multiplier ( ) , 1.0f ) ) {
Dictionary mat_emissive_strength ;
mat_emissive_strength [ " emissiveStrength " ] = base_material - > get_emission_energy_multiplier ( ) ;
extensions [ " KHR_materials_emissive_strength " ] = mat_emissive_strength ;
p_state - > add_used_extension ( " KHR_materials_emissive_strength " ) ;
}
2023-03-03 11:10:42 +00:00
d [ " extensions " ] = extensions ;
2024-01-11 19:47:31 +00:00
_attach_meta_to_extras ( material , d ) ;
2020-12-21 15:39:32 +00:00
materials . push_back ( d ) ;
}
2021-11-02 00:31:59 +00:00
if ( ! materials . size ( ) ) {
return OK ;
}
2022-12-10 21:05:13 +00:00
p_state - > json [ " materials " ] = materials ;
print_verbose ( " Total materials: " + itos ( p_state - > materials . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_materials ( Ref < GLTFState > p_state ) {
if ( ! p_state - > json . has ( " materials " ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
const Array & materials = p_state - > json [ " materials " ] ;
2020-12-21 15:39:32 +00:00
for ( GLTFMaterialIndex i = 0 ; i < materials . size ( ) ; i + + ) {
2023-06-23 19:01:07 +00:00
const Dictionary & material_dict = materials [ i ] ;
2020-12-21 15:39:32 +00:00
Ref < StandardMaterial3D > material ;
2021-06-17 22:03:09 +00:00
material . instantiate ( ) ;
2023-06-23 19:01:07 +00:00
if ( material_dict . has ( " name " ) & & ! String ( material_dict [ " name " ] ) . is_empty ( ) ) {
material - > set_name ( material_dict [ " name " ] ) ;
2021-03-24 07:29:20 +00:00
} else {
material - > set_name ( vformat ( " material_%s " , itos ( i ) ) ) ;
2020-12-21 15:39:32 +00:00
}
2023-06-23 19:01:07 +00:00
Dictionary material_extensions ;
if ( material_dict . has ( " extensions " ) ) {
material_extensions = material_dict [ " extensions " ] ;
2020-12-21 15:39:32 +00:00
}
2023-03-03 11:10:42 +00:00
2023-06-23 19:01:07 +00:00
if ( material_extensions . has ( " KHR_materials_unlit " ) ) {
2023-03-03 11:10:42 +00:00
material - > set_shading_mode ( BaseMaterial3D : : SHADING_MODE_UNSHADED ) ;
}
2023-06-23 19:01:07 +00:00
if ( material_extensions . has ( " KHR_materials_emissive_strength " ) ) {
Dictionary emissive_strength = material_extensions [ " KHR_materials_emissive_strength " ] ;
2023-06-23 18:43:34 +00:00
if ( emissive_strength . has ( " emissiveStrength " ) ) {
material - > set_emission_energy_multiplier ( emissive_strength [ " emissiveStrength " ] ) ;
}
}
2023-06-23 19:01:07 +00:00
if ( material_extensions . has ( " KHR_materials_pbrSpecularGlossiness " ) ) {
2020-12-21 15:39:32 +00:00
WARN_PRINT ( " Material uses a specular and glossiness workflow. Textures will be converted to roughness and metallic workflow, which may not be 100% accurate. " ) ;
2023-06-23 19:01:07 +00:00
Dictionary sgm = material_extensions [ " KHR_materials_pbrSpecularGlossiness " ] ;
2020-12-21 15:39:32 +00:00
Ref < GLTFSpecGloss > spec_gloss ;
2021-06-17 22:03:09 +00:00
spec_gloss . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
if ( sgm . has ( " diffuseTexture " ) ) {
const Dictionary & diffuse_texture_dict = sgm [ " diffuseTexture " ] ;
if ( diffuse_texture_dict . has ( " index " ) ) {
2022-12-10 21:05:13 +00:00
Ref < GLTFTextureSampler > diffuse_sampler = _get_sampler_for_texture ( p_state , diffuse_texture_dict [ " index " ] ) ;
2021-10-04 15:49:42 +00:00
if ( diffuse_sampler . is_valid ( ) ) {
material - > set_texture_filter ( diffuse_sampler - > get_filter_mode ( ) ) ;
material - > set_flag ( BaseMaterial3D : : FLAG_USE_TEXTURE_REPEAT , diffuse_sampler - > get_wrap_mode ( ) ) ;
}
2022-07-19 00:58:27 +00:00
Ref < Texture2D > diffuse_texture = _get_texture ( p_state , diffuse_texture_dict [ " index " ] , TEXTURE_TYPE_GENERIC ) ;
2020-12-21 15:39:32 +00:00
if ( diffuse_texture . is_valid ( ) ) {
2021-03-28 11:32:17 +00:00
spec_gloss - > diffuse_img = diffuse_texture - > get_image ( ) ;
2020-12-21 15:39:32 +00:00
material - > set_texture ( BaseMaterial3D : : TEXTURE_ALBEDO , diffuse_texture ) ;
}
}
}
if ( sgm . has ( " diffuseFactor " ) ) {
const Array & arr = sgm [ " diffuseFactor " ] ;
ERR_FAIL_COND_V ( arr . size ( ) ! = 4 , ERR_PARSE_ERROR ) ;
2022-04-13 08:37:22 +00:00
const Color c = Color ( arr [ 0 ] , arr [ 1 ] , arr [ 2 ] , arr [ 3 ] ) . linear_to_srgb ( ) ;
2020-12-21 15:39:32 +00:00
spec_gloss - > diffuse_factor = c ;
material - > set_albedo ( spec_gloss - > diffuse_factor ) ;
}
if ( sgm . has ( " specularFactor " ) ) {
const Array & arr = sgm [ " specularFactor " ] ;
ERR_FAIL_COND_V ( arr . size ( ) ! = 3 , ERR_PARSE_ERROR ) ;
spec_gloss - > specular_factor = Color ( arr [ 0 ] , arr [ 1 ] , arr [ 2 ] ) ;
}
if ( sgm . has ( " glossinessFactor " ) ) {
spec_gloss - > gloss_factor = sgm [ " glossinessFactor " ] ;
material - > set_roughness ( 1.0f - CLAMP ( spec_gloss - > gloss_factor , 0.0f , 1.0f ) ) ;
}
if ( sgm . has ( " specularGlossinessTexture " ) ) {
const Dictionary & spec_gloss_texture = sgm [ " specularGlossinessTexture " ] ;
if ( spec_gloss_texture . has ( " index " ) ) {
2022-07-19 00:58:27 +00:00
const Ref < Texture2D > orig_texture = _get_texture ( p_state , spec_gloss_texture [ " index " ] , TEXTURE_TYPE_GENERIC ) ;
2020-12-21 15:39:32 +00:00
if ( orig_texture . is_valid ( ) ) {
2021-03-28 11:32:17 +00:00
spec_gloss - > spec_gloss_img = orig_texture - > get_image ( ) ;
2020-12-21 15:39:32 +00:00
}
}
}
spec_gloss_to_rough_metal ( spec_gloss , material ) ;
2023-06-23 19:01:07 +00:00
} else if ( material_dict . has ( " pbrMetallicRoughness " ) ) {
const Dictionary & mr = material_dict [ " pbrMetallicRoughness " ] ;
2020-12-21 15:39:32 +00:00
if ( mr . has ( " baseColorFactor " ) ) {
const Array & arr = mr [ " baseColorFactor " ] ;
ERR_FAIL_COND_V ( arr . size ( ) ! = 4 , ERR_PARSE_ERROR ) ;
2022-04-13 08:37:22 +00:00
const Color c = Color ( arr [ 0 ] , arr [ 1 ] , arr [ 2 ] , arr [ 3 ] ) . linear_to_srgb ( ) ;
2020-12-21 15:39:32 +00:00
material - > set_albedo ( c ) ;
}
if ( mr . has ( " baseColorTexture " ) ) {
const Dictionary & bct = mr [ " baseColorTexture " ] ;
if ( bct . has ( " index " ) ) {
2022-12-10 21:05:13 +00:00
Ref < GLTFTextureSampler > bct_sampler = _get_sampler_for_texture ( p_state , bct [ " index " ] ) ;
2021-10-04 15:49:42 +00:00
material - > set_texture_filter ( bct_sampler - > get_filter_mode ( ) ) ;
material - > set_flag ( BaseMaterial3D : : FLAG_USE_TEXTURE_REPEAT , bct_sampler - > get_wrap_mode ( ) ) ;
2022-07-19 00:58:27 +00:00
material - > set_texture ( BaseMaterial3D : : TEXTURE_ALBEDO , _get_texture ( p_state , bct [ " index " ] , TEXTURE_TYPE_GENERIC ) ) ;
2020-12-21 15:39:32 +00:00
}
if ( ! mr . has ( " baseColorFactor " ) ) {
material - > set_albedo ( Color ( 1 , 1 , 1 ) ) ;
}
_set_texture_transform_uv1 ( bct , material ) ;
}
if ( mr . has ( " metallicFactor " ) ) {
material - > set_metallic ( mr [ " metallicFactor " ] ) ;
} else {
material - > set_metallic ( 1.0 ) ;
}
if ( mr . has ( " roughnessFactor " ) ) {
material - > set_roughness ( mr [ " roughnessFactor " ] ) ;
} else {
material - > set_roughness ( 1.0 ) ;
}
if ( mr . has ( " metallicRoughnessTexture " ) ) {
const Dictionary & bct = mr [ " metallicRoughnessTexture " ] ;
if ( bct . has ( " index " ) ) {
2022-07-19 00:58:27 +00:00
const Ref < Texture2D > t = _get_texture ( p_state , bct [ " index " ] , TEXTURE_TYPE_GENERIC ) ;
2020-12-21 15:39:32 +00:00
material - > set_texture ( BaseMaterial3D : : TEXTURE_METALLIC , t ) ;
material - > set_metallic_texture_channel ( BaseMaterial3D : : TEXTURE_CHANNEL_BLUE ) ;
material - > set_texture ( BaseMaterial3D : : TEXTURE_ROUGHNESS , t ) ;
material - > set_roughness_texture_channel ( BaseMaterial3D : : TEXTURE_CHANNEL_GREEN ) ;
if ( ! mr . has ( " metallicFactor " ) ) {
material - > set_metallic ( 1 ) ;
}
if ( ! mr . has ( " roughnessFactor " ) ) {
material - > set_roughness ( 1 ) ;
}
}
}
}
2023-06-23 19:01:07 +00:00
if ( material_dict . has ( " normalTexture " ) ) {
const Dictionary & bct = material_dict [ " normalTexture " ] ;
2020-12-21 15:39:32 +00:00
if ( bct . has ( " index " ) ) {
2022-07-19 00:58:27 +00:00
material - > set_texture ( BaseMaterial3D : : TEXTURE_NORMAL , _get_texture ( p_state , bct [ " index " ] , TEXTURE_TYPE_NORMAL ) ) ;
2020-12-21 15:39:32 +00:00
material - > set_feature ( BaseMaterial3D : : FEATURE_NORMAL_MAPPING , true ) ;
}
if ( bct . has ( " scale " ) ) {
material - > set_normal_scale ( bct [ " scale " ] ) ;
}
}
2023-06-23 19:01:07 +00:00
if ( material_dict . has ( " occlusionTexture " ) ) {
const Dictionary & bct = material_dict [ " occlusionTexture " ] ;
2020-12-21 15:39:32 +00:00
if ( bct . has ( " index " ) ) {
2022-07-19 00:58:27 +00:00
material - > set_texture ( BaseMaterial3D : : TEXTURE_AMBIENT_OCCLUSION , _get_texture ( p_state , bct [ " index " ] , TEXTURE_TYPE_GENERIC ) ) ;
2020-12-21 15:39:32 +00:00
material - > set_ao_texture_channel ( BaseMaterial3D : : TEXTURE_CHANNEL_RED ) ;
material - > set_feature ( BaseMaterial3D : : FEATURE_AMBIENT_OCCLUSION , true ) ;
}
}
2023-06-23 19:01:07 +00:00
if ( material_dict . has ( " emissiveFactor " ) ) {
const Array & arr = material_dict [ " emissiveFactor " ] ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( arr . size ( ) ! = 3 , ERR_PARSE_ERROR ) ;
2022-04-13 08:37:22 +00:00
const Color c = Color ( arr [ 0 ] , arr [ 1 ] , arr [ 2 ] ) . linear_to_srgb ( ) ;
2020-12-21 15:39:32 +00:00
material - > set_feature ( BaseMaterial3D : : FEATURE_EMISSION , true ) ;
material - > set_emission ( c ) ;
}
2023-06-23 19:01:07 +00:00
if ( material_dict . has ( " emissiveTexture " ) ) {
const Dictionary & bct = material_dict [ " emissiveTexture " ] ;
2020-12-21 15:39:32 +00:00
if ( bct . has ( " index " ) ) {
2022-07-19 00:58:27 +00:00
material - > set_texture ( BaseMaterial3D : : TEXTURE_EMISSION , _get_texture ( p_state , bct [ " index " ] , TEXTURE_TYPE_GENERIC ) ) ;
2020-12-21 15:39:32 +00:00
material - > set_feature ( BaseMaterial3D : : FEATURE_EMISSION , true ) ;
material - > set_emission ( Color ( 0 , 0 , 0 ) ) ;
}
}
2023-06-23 19:01:07 +00:00
if ( material_dict . has ( " doubleSided " ) ) {
const bool ds = material_dict [ " doubleSided " ] ;
2020-12-21 15:39:32 +00:00
if ( ds ) {
material - > set_cull_mode ( BaseMaterial3D : : CULL_DISABLED ) ;
}
}
2023-06-23 19:01:07 +00:00
if ( material_dict . has ( " alphaMode " ) ) {
const String & am = material_dict [ " alphaMode " ] ;
2020-12-21 15:39:32 +00:00
if ( am = = " BLEND " ) {
material - > set_transparency ( BaseMaterial3D : : TRANSPARENCY_ALPHA_DEPTH_PRE_PASS ) ;
} else if ( am = = " MASK " ) {
material - > set_transparency ( BaseMaterial3D : : TRANSPARENCY_ALPHA_SCISSOR ) ;
2023-06-23 19:01:07 +00:00
if ( material_dict . has ( " alphaCutoff " ) ) {
material - > set_alpha_scissor_threshold ( material_dict [ " alphaCutoff " ] ) ;
2020-12-21 15:39:32 +00:00
} else {
material - > set_alpha_scissor_threshold ( 0.5f ) ;
}
}
}
2024-01-11 19:47:31 +00:00
if ( material_dict . has ( " extras " ) ) {
_attach_extras_to_meta ( material_dict [ " extras " ] , material ) ;
}
2022-12-10 21:05:13 +00:00
p_state - > materials . push_back ( material ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
print_verbose ( " Total materials: " + itos ( p_state - > materials . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _set_texture_transform_uv1 ( const Dictionary & p_dict , Ref < BaseMaterial3D > p_material ) {
if ( p_dict . has ( " extensions " ) ) {
const Dictionary & extensions = p_dict [ " extensions " ] ;
2020-12-21 15:39:32 +00:00
if ( extensions . has ( " KHR_texture_transform " ) ) {
2022-12-10 21:05:13 +00:00
if ( p_material . is_valid ( ) ) {
2022-11-23 23:29:01 +00:00
const Dictionary & texture_transform = extensions [ " KHR_texture_transform " ] ;
const Array & offset_arr = texture_transform [ " offset " ] ;
if ( offset_arr . size ( ) = = 2 ) {
const Vector3 offset_vector3 = Vector3 ( offset_arr [ 0 ] , offset_arr [ 1 ] , 0.0f ) ;
2022-12-10 21:05:13 +00:00
p_material - > set_uv1_offset ( offset_vector3 ) ;
2022-11-23 23:29:01 +00:00
}
2020-12-21 15:39:32 +00:00
2022-11-23 23:29:01 +00:00
const Array & scale_arr = texture_transform [ " scale " ] ;
if ( scale_arr . size ( ) = = 2 ) {
const Vector3 scale_vector3 = Vector3 ( scale_arr [ 0 ] , scale_arr [ 1 ] , 1.0f ) ;
2022-12-10 21:05:13 +00:00
p_material - > set_uv1_scale ( scale_vector3 ) ;
2022-11-23 23:29:01 +00:00
}
2020-12-21 15:39:32 +00:00
}
}
}
}
void GLTFDocument : : spec_gloss_to_rough_metal ( Ref < GLTFSpecGloss > r_spec_gloss , Ref < BaseMaterial3D > p_material ) {
2022-11-23 23:29:01 +00:00
if ( r_spec_gloss . is_null ( ) ) {
return ;
}
2020-12-21 15:39:32 +00:00
if ( r_spec_gloss - > spec_gloss_img . is_null ( ) ) {
return ;
}
if ( r_spec_gloss - > diffuse_img . is_null ( ) ) {
return ;
}
2022-11-23 23:29:01 +00:00
if ( p_material . is_null ( ) ) {
return ;
}
2020-12-21 15:39:32 +00:00
bool has_roughness = false ;
bool has_metal = false ;
p_material - > set_roughness ( 1.0f ) ;
p_material - > set_metallic ( 1.0f ) ;
2022-07-22 18:06:19 +00:00
Ref < Image > rm_img = Image : : create_empty ( r_spec_gloss - > spec_gloss_img - > get_width ( ) , r_spec_gloss - > spec_gloss_img - > get_height ( ) , false , Image : : FORMAT_RGBA8 ) ;
2020-12-21 15:39:32 +00:00
r_spec_gloss - > spec_gloss_img - > decompress ( ) ;
if ( r_spec_gloss - > diffuse_img . is_valid ( ) ) {
r_spec_gloss - > diffuse_img - > decompress ( ) ;
r_spec_gloss - > diffuse_img - > resize ( r_spec_gloss - > spec_gloss_img - > get_width ( ) , r_spec_gloss - > spec_gloss_img - > get_height ( ) , Image : : INTERPOLATE_LANCZOS ) ;
r_spec_gloss - > spec_gloss_img - > resize ( r_spec_gloss - > diffuse_img - > get_width ( ) , r_spec_gloss - > diffuse_img - > get_height ( ) , Image : : INTERPOLATE_LANCZOS ) ;
}
for ( int32_t y = 0 ; y < r_spec_gloss - > spec_gloss_img - > get_height ( ) ; y + + ) {
for ( int32_t x = 0 ; x < r_spec_gloss - > spec_gloss_img - > get_width ( ) ; x + + ) {
2022-04-13 08:37:22 +00:00
const Color specular_pixel = r_spec_gloss - > spec_gloss_img - > get_pixel ( x , y ) . srgb_to_linear ( ) ;
2020-12-21 15:39:32 +00:00
Color specular = Color ( specular_pixel . r , specular_pixel . g , specular_pixel . b ) ;
specular * = r_spec_gloss - > specular_factor ;
Color diffuse = Color ( 1.0f , 1.0f , 1.0f ) ;
2022-04-13 08:37:22 +00:00
diffuse * = r_spec_gloss - > diffuse_img - > get_pixel ( x , y ) . srgb_to_linear ( ) ;
2020-12-21 15:39:32 +00:00
float metallic = 0.0f ;
Color base_color ;
spec_gloss_to_metal_base_color ( specular , diffuse , base_color , metallic ) ;
Color mr = Color ( 1.0f , 1.0f , 1.0f ) ;
mr . g = specular_pixel . a ;
mr . b = metallic ;
if ( ! Math : : is_equal_approx ( mr . g , 1.0f ) ) {
has_roughness = true ;
}
2021-06-20 07:03:06 +00:00
if ( ! Math : : is_zero_approx ( mr . b ) ) {
2020-12-21 15:39:32 +00:00
has_metal = true ;
}
mr . g * = r_spec_gloss - > gloss_factor ;
mr . g = 1.0f - mr . g ;
rm_img - > set_pixel ( x , y , mr ) ;
if ( r_spec_gloss - > diffuse_img . is_valid ( ) ) {
2022-04-13 08:37:22 +00:00
r_spec_gloss - > diffuse_img - > set_pixel ( x , y , base_color . linear_to_srgb ( ) ) ;
2020-12-21 15:39:32 +00:00
}
}
}
rm_img - > generate_mipmaps ( ) ;
r_spec_gloss - > diffuse_img - > generate_mipmaps ( ) ;
2022-05-03 23:49:20 +00:00
p_material - > set_texture ( BaseMaterial3D : : TEXTURE_ALBEDO , ImageTexture : : create_from_image ( r_spec_gloss - > diffuse_img ) ) ;
Ref < ImageTexture > rm_image_texture = ImageTexture : : create_from_image ( rm_img ) ;
2020-12-21 15:39:32 +00:00
if ( has_roughness ) {
p_material - > set_texture ( BaseMaterial3D : : TEXTURE_ROUGHNESS , rm_image_texture ) ;
p_material - > set_roughness_texture_channel ( BaseMaterial3D : : TEXTURE_CHANNEL_GREEN ) ;
}
if ( has_metal ) {
p_material - > set_texture ( BaseMaterial3D : : TEXTURE_METALLIC , rm_image_texture ) ;
p_material - > set_metallic_texture_channel ( BaseMaterial3D : : TEXTURE_CHANNEL_BLUE ) ;
}
}
void GLTFDocument : : spec_gloss_to_metal_base_color ( const Color & p_specular_factor , const Color & p_diffuse , Color & r_base_color , float & r_metallic ) {
const Color DIELECTRIC_SPECULAR = Color ( 0.04f , 0.04f , 0.04f ) ;
Color specular = Color ( p_specular_factor . r , p_specular_factor . g , p_specular_factor . b ) ;
const float one_minus_specular_strength = 1.0f - get_max_component ( specular ) ;
const float dielectric_specular_red = DIELECTRIC_SPECULAR . r ;
float brightness_diffuse = get_perceived_brightness ( p_diffuse ) ;
const float brightness_specular = get_perceived_brightness ( specular ) ;
r_metallic = solve_metallic ( dielectric_specular_red , brightness_diffuse , brightness_specular , one_minus_specular_strength ) ;
const float one_minus_metallic = 1.0f - r_metallic ;
const Color base_color_from_diffuse = p_diffuse * ( one_minus_specular_strength / ( 1.0f - dielectric_specular_red ) / MAX ( one_minus_metallic , CMP_EPSILON ) ) ;
const Color base_color_from_specular = ( specular - ( DIELECTRIC_SPECULAR * ( one_minus_metallic ) ) ) * ( 1.0f / MAX ( r_metallic , CMP_EPSILON ) ) ;
r_base_color . r = Math : : lerp ( base_color_from_diffuse . r , base_color_from_specular . r , r_metallic * r_metallic ) ;
r_base_color . g = Math : : lerp ( base_color_from_diffuse . g , base_color_from_specular . g , r_metallic * r_metallic ) ;
r_base_color . b = Math : : lerp ( base_color_from_diffuse . b , base_color_from_specular . b , r_metallic * r_metallic ) ;
r_base_color . a = p_diffuse . a ;
2021-09-29 03:51:34 +00:00
r_base_color = r_base_color . clamp ( ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_skins ( Ref < GLTFState > p_state ) {
if ( ! p_state - > json . has ( " skins " ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
const Array & skins = p_state - > json [ " skins " ] ;
2020-12-21 15:39:32 +00:00
// Create the base skins, and mark nodes that are joints
for ( int i = 0 ; i < skins . size ( ) ; i + + ) {
const Dictionary & d = skins [ i ] ;
Ref < GLTFSkin > skin ;
2021-06-17 22:03:09 +00:00
skin . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( ! d . has ( " joints " ) , ERR_PARSE_ERROR ) ;
const Array & joints = d [ " joints " ] ;
if ( d . has ( " inverseBindMatrices " ) ) {
2022-12-10 21:05:13 +00:00
skin - > inverse_binds = _decode_accessor_as_xform ( p_state , d [ " inverseBindMatrices " ] , false ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( skin - > inverse_binds . size ( ) ! = joints . size ( ) , ERR_PARSE_ERROR ) ;
}
for ( int j = 0 ; j < joints . size ( ) ; j + + ) {
const GLTFNodeIndex node = joints [ j ] ;
2022-12-10 21:05:13 +00:00
ERR_FAIL_INDEX_V ( node , p_state - > nodes . size ( ) , ERR_PARSE_ERROR ) ;
2020-12-21 15:39:32 +00:00
skin - > joints . push_back ( node ) ;
skin - > joints_original . push_back ( node ) ;
2022-12-10 21:05:13 +00:00
p_state - > nodes . write [ node ] - > joint = true ;
2020-12-21 15:39:32 +00:00
}
2021-03-24 07:29:20 +00:00
if ( d . has ( " name " ) & & ! String ( d [ " name " ] ) . is_empty ( ) ) {
2020-12-21 15:39:32 +00:00
skin - > set_name ( d [ " name " ] ) ;
2021-03-24 07:29:20 +00:00
} else {
skin - > set_name ( vformat ( " skin_%s " , itos ( i ) ) ) ;
2020-12-21 15:39:32 +00:00
}
if ( d . has ( " skeleton " ) ) {
skin - > skin_root = d [ " skeleton " ] ;
}
2022-12-10 21:05:13 +00:00
p_state - > skins . push_back ( skin ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
for ( GLTFSkinIndex i = 0 ; i < p_state - > skins . size ( ) ; + + i ) {
Ref < GLTFSkin > skin = p_state - > skins . write [ i ] ;
2020-12-21 15:39:32 +00:00
// Expand the skin to capture all the extra non-joints that lie in between the actual joints,
// and expand the hierarchy to ensure multi-rooted trees lie on the same height level
2024-02-16 13:25:15 +00:00
ERR_FAIL_COND_V ( SkinTool : : _expand_skin ( p_state - > nodes , skin ) , ERR_PARSE_ERROR ) ;
ERR_FAIL_COND_V ( SkinTool : : _verify_skin ( p_state - > nodes , skin ) , ERR_PARSE_ERROR ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: Total skins: " + itos ( p_state - > skins . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _serialize_skins ( Ref < GLTFState > p_state ) {
_remove_duplicate_skins ( p_state ) ;
2021-09-04 01:00:59 +00:00
Array json_skins ;
2022-12-10 21:05:13 +00:00
for ( int skin_i = 0 ; skin_i < p_state - > skins . size ( ) ; skin_i + + ) {
Ref < GLTFSkin > gltf_skin = p_state - > skins [ skin_i ] ;
2021-09-04 01:00:59 +00:00
Dictionary json_skin ;
2022-12-10 21:05:13 +00:00
json_skin [ " inverseBindMatrices " ] = _encode_accessor_as_xform ( p_state , gltf_skin - > inverse_binds , false ) ;
2021-09-04 01:00:59 +00:00
json_skin [ " joints " ] = gltf_skin - > get_joints ( ) ;
json_skin [ " name " ] = gltf_skin - > get_name ( ) ;
json_skins . push_back ( json_skin ) ;
}
2022-12-10 21:05:13 +00:00
if ( ! p_state - > skins . size ( ) ) {
2021-10-26 03:14:12 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
p_state - > json [ " skins " ] = json_skins ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _create_skins ( Ref < GLTFState > p_state ) {
for ( GLTFSkinIndex skin_i = 0 ; skin_i < p_state - > skins . size ( ) ; + + skin_i ) {
Ref < GLTFSkin > gltf_skin = p_state - > skins . write [ skin_i ] ;
2020-12-21 15:39:32 +00:00
Ref < Skin > skin ;
2021-06-17 22:03:09 +00:00
skin . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
// Some skins don't have IBM's! What absolute monsters!
2020-12-15 12:04:21 +00:00
const bool has_ibms = ! gltf_skin - > inverse_binds . is_empty ( ) ;
2020-12-21 15:39:32 +00:00
for ( int joint_i = 0 ; joint_i < gltf_skin - > joints_original . size ( ) ; + + joint_i ) {
GLTFNodeIndex node = gltf_skin - > joints_original [ joint_i ] ;
2022-12-10 21:05:13 +00:00
String bone_name = p_state - > nodes [ node ] - > get_name ( ) ;
2020-12-21 15:39:32 +00:00
2020-10-17 05:08:21 +00:00
Transform3D xform ;
2020-12-21 15:39:32 +00:00
if ( has_ibms ) {
xform = gltf_skin - > inverse_binds [ joint_i ] ;
}
2022-12-10 21:05:13 +00:00
if ( p_state - > use_named_skin_binds ) {
2020-12-21 15:39:32 +00:00
skin - > add_named_bind ( bone_name , xform ) ;
} else {
int32_t bone_i = gltf_skin - > joint_i_to_bone_i [ joint_i ] ;
skin - > add_bind ( bone_i , xform ) ;
}
}
gltf_skin - > godot_skin = skin ;
}
// Purge the duplicates!
2022-12-10 21:05:13 +00:00
_remove_duplicate_skins ( p_state ) ;
2020-12-21 15:39:32 +00:00
// Create unique names now, after removing duplicates
2022-12-10 21:05:13 +00:00
for ( GLTFSkinIndex skin_i = 0 ; skin_i < p_state - > skins . size ( ) ; + + skin_i ) {
Ref < Skin > skin = p_state - > skins . write [ skin_i ] - > godot_skin ;
2020-12-15 12:04:21 +00:00
if ( skin - > get_name ( ) . is_empty ( ) ) {
2020-12-21 15:39:32 +00:00
// Make a unique name, no gltf node represents this skin
2022-12-10 21:05:13 +00:00
skin - > set_name ( _gen_unique_name ( p_state , " Skin " ) ) ;
2020-12-21 15:39:32 +00:00
}
}
return OK ;
}
2022-12-10 21:05:13 +00:00
bool GLTFDocument : : _skins_are_same ( const Ref < Skin > p_skin_a , const Ref < Skin > p_skin_b ) {
if ( p_skin_a - > get_bind_count ( ) ! = p_skin_b - > get_bind_count ( ) ) {
2020-12-21 15:39:32 +00:00
return false ;
}
2022-12-10 21:05:13 +00:00
for ( int i = 0 ; i < p_skin_a - > get_bind_count ( ) ; + + i ) {
if ( p_skin_a - > get_bind_bone ( i ) ! = p_skin_b - > get_bind_bone ( i ) ) {
2020-12-21 15:39:32 +00:00
return false ;
}
2022-12-10 21:05:13 +00:00
if ( p_skin_a - > get_bind_name ( i ) ! = p_skin_b - > get_bind_name ( i ) ) {
2021-05-21 03:26:11 +00:00
return false ;
}
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
Transform3D a_xform = p_skin_a - > get_bind_pose ( i ) ;
Transform3D b_xform = p_skin_b - > get_bind_pose ( i ) ;
2020-12-21 15:39:32 +00:00
if ( a_xform ! = b_xform ) {
return false ;
}
}
return true ;
}
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _remove_duplicate_skins ( Ref < GLTFState > p_state ) {
for ( int i = 0 ; i < p_state - > skins . size ( ) ; + + i ) {
for ( int j = i + 1 ; j < p_state - > skins . size ( ) ; + + j ) {
const Ref < Skin > skin_i = p_state - > skins [ i ] - > godot_skin ;
const Ref < Skin > skin_j = p_state - > skins [ j ] - > godot_skin ;
2020-12-21 15:39:32 +00:00
if ( _skins_are_same ( skin_i , skin_j ) ) {
// replace it and delete the old
2022-12-10 21:05:13 +00:00
p_state - > skins . write [ j ] - > godot_skin = skin_i ;
2020-12-21 15:39:32 +00:00
}
}
}
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _serialize_lights ( Ref < GLTFState > p_state ) {
if ( p_state - > lights . is_empty ( ) ) {
2022-07-23 23:52:46 +00:00
return OK ;
}
2020-12-21 15:39:32 +00:00
Array lights ;
2022-12-10 21:05:13 +00:00
for ( GLTFLightIndex i = 0 ; i < p_state - > lights . size ( ) ; i + + ) {
lights . push_back ( p_state - > lights [ i ] - > to_dictionary ( ) ) ;
2020-12-21 15:39:32 +00:00
}
Dictionary extensions ;
2022-12-10 21:05:13 +00:00
if ( p_state - > json . has ( " extensions " ) ) {
extensions = p_state - > json [ " extensions " ] ;
2020-12-21 15:39:32 +00:00
} else {
2022-12-10 21:05:13 +00:00
p_state - > json [ " extensions " ] = extensions ;
2020-12-21 15:39:32 +00:00
}
Dictionary lights_punctual ;
extensions [ " KHR_lights_punctual " ] = lights_punctual ;
lights_punctual [ " lights " ] = lights ;
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: Total lights: " + itos ( p_state - > lights . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _serialize_cameras ( Ref < GLTFState > p_state ) {
2020-12-21 15:39:32 +00:00
Array cameras ;
2022-12-10 21:05:13 +00:00
cameras . resize ( p_state - > cameras . size ( ) ) ;
for ( GLTFCameraIndex i = 0 ; i < p_state - > cameras . size ( ) ; i + + ) {
cameras [ i ] = p_state - > cameras [ i ] - > to_dictionary ( ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
if ( ! p_state - > cameras . size ( ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
p_state - > json [ " cameras " ] = cameras ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: Total cameras: " + itos ( p_state - > cameras . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_lights ( Ref < GLTFState > p_state ) {
if ( ! p_state - > json . has ( " extensions " ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Dictionary extensions = p_state - > json [ " extensions " ] ;
2020-12-21 15:39:32 +00:00
if ( ! extensions . has ( " KHR_lights_punctual " ) ) {
return OK ;
}
Dictionary lights_punctual = extensions [ " KHR_lights_punctual " ] ;
if ( ! lights_punctual . has ( " lights " ) ) {
return OK ;
}
const Array & lights = lights_punctual [ " lights " ] ;
for ( GLTFLightIndex light_i = 0 ; light_i < lights . size ( ) ; light_i + + ) {
2022-07-26 05:51:53 +00:00
Ref < GLTFLight > light = GLTFLight : : from_dictionary ( lights [ light_i ] ) ;
if ( light . is_null ( ) ) {
return Error : : ERR_PARSE_ERROR ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
p_state - > lights . push_back ( light ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: Total lights: " + itos ( p_state - > lights . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_cameras ( Ref < GLTFState > p_state ) {
if ( ! p_state - > json . has ( " cameras " ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
const Array cameras = p_state - > json [ " cameras " ] ;
2020-12-21 15:39:32 +00:00
for ( GLTFCameraIndex i = 0 ; i < cameras . size ( ) ; i + + ) {
2022-12-10 21:05:13 +00:00
p_state - > cameras . push_back ( GLTFCamera : : from_dictionary ( cameras [ i ] ) ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: Total cameras: " + itos ( p_state - > cameras . size ( ) ) ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
String GLTFDocument : : interpolation_to_string ( const GLTFAnimation : : Interpolation p_interp ) {
String interp = " LINEAR " ;
if ( p_interp = = GLTFAnimation : : INTERP_STEP ) {
interp = " STEP " ;
} else if ( p_interp = = GLTFAnimation : : INTERP_LINEAR ) {
interp = " LINEAR " ;
} else if ( p_interp = = GLTFAnimation : : INTERP_CATMULLROMSPLINE ) {
interp = " CATMULLROMSPLINE " ;
} else if ( p_interp = = GLTFAnimation : : INTERP_CUBIC_SPLINE ) {
interp = " CUBICSPLINE " ;
}
return interp ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _serialize_animations ( Ref < GLTFState > p_state ) {
if ( ! p_state - > animation_players . size ( ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
for ( int32_t player_i = 0 ; player_i < p_state - > animation_players . size ( ) ; player_i + + ) {
AnimationPlayer * animation_player = p_state - > animation_players [ player_i ] ;
2023-02-04 02:06:29 +00:00
List < StringName > animations ;
animation_player - > get_animation_list ( & animations ) ;
2021-08-01 19:47:20 +00:00
for ( const StringName & animation_name : animations ) {
2023-02-04 02:06:29 +00:00
_convert_animation ( p_state , animation_player , animation_name ) ;
2020-12-21 15:39:32 +00:00
}
}
Array animations ;
2022-12-10 21:05:13 +00:00
for ( GLTFAnimationIndex animation_i = 0 ; animation_i < p_state - > animations . size ( ) ; animation_i + + ) {
2020-12-21 15:39:32 +00:00
Dictionary d ;
2022-12-10 21:05:13 +00:00
Ref < GLTFAnimation > gltf_animation = p_state - > animations [ animation_i ] ;
2024-07-10 08:26:35 +00:00
if ( gltf_animation - > is_empty_of_tracks ( ) ) {
2020-12-21 15:39:32 +00:00
continue ;
}
2020-12-15 12:04:21 +00:00
if ( ! gltf_animation - > get_name ( ) . is_empty ( ) ) {
2020-12-21 15:39:32 +00:00
d [ " name " ] = gltf_animation - > get_name ( ) ;
}
Array channels ;
Array samplers ;
2024-07-10 08:26:35 +00:00
// Serialize glTF node tracks with the vanilla glTF animation system.
for ( KeyValue < int , GLTFAnimation : : NodeTrack > & track_i : gltf_animation - > get_node_tracks ( ) ) {
GLTFAnimation : : NodeTrack track = track_i . value ;
2021-08-31 03:18:12 +00:00
if ( track . position_track . times . size ( ) ) {
2020-12-21 15:39:32 +00:00
Dictionary t ;
t [ " sampler " ] = samplers . size ( ) ;
Dictionary s ;
2021-08-31 03:18:12 +00:00
s [ " interpolation " ] = interpolation_to_string ( track . position_track . interpolation ) ;
2024-07-10 08:26:35 +00:00
Vector < double > times = track . position_track . times ;
2022-12-10 21:05:13 +00:00
s [ " input " ] = _encode_accessor_as_floats ( p_state , times , false ) ;
2024-07-10 08:26:35 +00:00
Vector < Vector3 > values = track . position_track . values ;
2022-12-10 21:05:13 +00:00
s [ " output " ] = _encode_accessor_as_vec3 ( p_state , values , false ) ;
2020-12-21 15:39:32 +00:00
samplers . push_back ( s ) ;
Dictionary target ;
target [ " path " ] = " translation " ;
2021-08-09 20:13:42 +00:00
target [ " node " ] = track_i . key ;
2020-12-21 15:39:32 +00:00
t [ " target " ] = target ;
channels . push_back ( t ) ;
}
if ( track . rotation_track . times . size ( ) ) {
Dictionary t ;
t [ " sampler " ] = samplers . size ( ) ;
Dictionary s ;
s [ " interpolation " ] = interpolation_to_string ( track . rotation_track . interpolation ) ;
2024-07-10 08:26:35 +00:00
Vector < double > times = track . rotation_track . times ;
2022-12-10 21:05:13 +00:00
s [ " input " ] = _encode_accessor_as_floats ( p_state , times , false ) ;
2021-01-20 07:02:02 +00:00
Vector < Quaternion > values = track . rotation_track . values ;
2022-12-10 21:05:13 +00:00
s [ " output " ] = _encode_accessor_as_quaternions ( p_state , values , false ) ;
2020-12-21 15:39:32 +00:00
samplers . push_back ( s ) ;
Dictionary target ;
target [ " path " ] = " rotation " ;
2021-08-09 20:13:42 +00:00
target [ " node " ] = track_i . key ;
2020-12-21 15:39:32 +00:00
t [ " target " ] = target ;
channels . push_back ( t ) ;
}
if ( track . scale_track . times . size ( ) ) {
Dictionary t ;
t [ " sampler " ] = samplers . size ( ) ;
Dictionary s ;
s [ " interpolation " ] = interpolation_to_string ( track . scale_track . interpolation ) ;
2024-07-10 08:26:35 +00:00
Vector < double > times = track . scale_track . times ;
2022-12-10 21:05:13 +00:00
s [ " input " ] = _encode_accessor_as_floats ( p_state , times , false ) ;
2024-07-10 08:26:35 +00:00
Vector < Vector3 > values = track . scale_track . values ;
2022-12-10 21:05:13 +00:00
s [ " output " ] = _encode_accessor_as_vec3 ( p_state , values , false ) ;
2020-12-21 15:39:32 +00:00
samplers . push_back ( s ) ;
Dictionary target ;
target [ " path " ] = " scale " ;
2021-08-09 20:13:42 +00:00
target [ " node " ] = track_i . key ;
2020-12-21 15:39:32 +00:00
t [ " target " ] = target ;
channels . push_back ( t ) ;
}
if ( track . weight_tracks . size ( ) ) {
2021-09-04 01:00:59 +00:00
double length = 0.0f ;
for ( int32_t track_idx = 0 ; track_idx < track . weight_tracks . size ( ) ; track_idx + + ) {
int32_t last_time_index = track . weight_tracks [ track_idx ] . times . size ( ) - 1 ;
length = MAX ( length , track . weight_tracks [ track_idx ] . times [ last_time_index ] ) ;
}
2020-12-21 15:39:32 +00:00
Dictionary t ;
t [ " sampler " ] = samplers . size ( ) ;
Dictionary s ;
2024-07-10 08:26:35 +00:00
Vector < double > times ;
2024-05-23 15:40:26 +00:00
const double increment = 1.0 / p_state - > get_bake_fps ( ) ;
2021-09-04 01:00:59 +00:00
{
double time = 0.0 ;
bool last = false ;
while ( true ) {
times . push_back ( time ) ;
if ( last ) {
break ;
}
time + = increment ;
if ( time > = length ) {
last = true ;
time = length ;
}
}
}
2020-12-21 15:39:32 +00:00
2021-09-04 01:00:59 +00:00
for ( int32_t track_idx = 0 ; track_idx < track . weight_tracks . size ( ) ; track_idx + + ) {
double time = 0.0 ;
bool last = false ;
Vector < real_t > weight_track ;
while ( true ) {
2021-09-16 06:03:50 +00:00
float weight = _interpolate_track < real_t > ( track . weight_tracks [ track_idx ] . times ,
2021-09-04 01:00:59 +00:00
track . weight_tracks [ track_idx ] . values ,
time ,
track . weight_tracks [ track_idx ] . interpolation ) ;
weight_track . push_back ( weight ) ;
if ( last ) {
break ;
}
time + = increment ;
if ( time > = length ) {
last = true ;
time = length ;
}
}
track . weight_tracks . write [ track_idx ] . times = times ;
track . weight_tracks . write [ track_idx ] . values = weight_track ;
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:26:35 +00:00
Vector < double > all_track_times = times ;
Vector < double > all_track_values ;
2021-09-04 01:00:59 +00:00
int32_t values_size = track . weight_tracks [ 0 ] . values . size ( ) ;
int32_t weight_tracks_size = track . weight_tracks . size ( ) ;
all_track_values . resize ( weight_tracks_size * values_size ) ;
2020-12-21 15:39:32 +00:00
for ( int k = 0 ; k < track . weight_tracks . size ( ) ; k + + ) {
2021-09-16 06:03:50 +00:00
Vector < real_t > wdata = track . weight_tracks [ k ] . values ;
2020-12-21 15:39:32 +00:00
for ( int l = 0 ; l < wdata . size ( ) ; l + + ) {
2021-09-04 01:00:59 +00:00
int32_t index = l * weight_tracks_size + k ;
ERR_BREAK ( index > = all_track_values . size ( ) ) ;
all_track_values . write [ index ] = wdata . write [ l ] ;
2020-12-21 15:39:32 +00:00
}
}
s [ " interpolation " ] = interpolation_to_string ( track . weight_tracks [ track . weight_tracks . size ( ) - 1 ] . interpolation ) ;
2022-12-10 21:05:13 +00:00
s [ " input " ] = _encode_accessor_as_floats ( p_state , all_track_times , false ) ;
s [ " output " ] = _encode_accessor_as_floats ( p_state , all_track_values , false ) ;
2020-12-21 15:39:32 +00:00
samplers . push_back ( s ) ;
Dictionary target ;
target [ " path " ] = " weights " ;
2021-08-09 20:13:42 +00:00
target [ " node " ] = track_i . key ;
2020-12-21 15:39:32 +00:00
t [ " target " ] = target ;
channels . push_back ( t ) ;
}
}
2024-10-04 07:37:44 +00:00
if ( ! gltf_animation - > get_pointer_tracks ( ) . is_empty ( ) ) {
// Serialize glTF pointer tracks with the KHR_animation_pointer extension.
if ( ! p_state - > extensions_used . has ( " KHR_animation_pointer " ) ) {
p_state - > extensions_used . push_back ( " KHR_animation_pointer " ) ;
}
for ( KeyValue < String , GLTFAnimation : : Channel < Variant > > & pointer_track_iter : gltf_animation - > get_pointer_tracks ( ) ) {
const String & json_pointer = pointer_track_iter . key ;
const GLTFAnimation : : Channel < Variant > & pointer_track = pointer_track_iter . value ;
const Ref < GLTFObjectModelProperty > & obj_model_prop = p_state - > object_model_properties [ json_pointer ] ;
Dictionary channel ;
channel [ " sampler " ] = samplers . size ( ) ;
Dictionary channel_target ;
channel_target [ " path " ] = " pointer " ;
Dictionary channel_target_ext ;
Dictionary channel_target_ext_khr_anim_ptr ;
channel_target_ext_khr_anim_ptr [ " pointer " ] = json_pointer ;
channel_target_ext [ " KHR_animation_pointer " ] = channel_target_ext_khr_anim_ptr ;
channel_target [ " extensions " ] = channel_target_ext ;
channel [ " target " ] = channel_target ;
channels . push_back ( channel ) ;
Dictionary sampler ;
sampler [ " input " ] = _encode_accessor_as_floats ( p_state , pointer_track . times , false ) ;
sampler [ " interpolation " ] = interpolation_to_string ( pointer_track . interpolation ) ;
sampler [ " output " ] = _encode_accessor_as_variant ( p_state , pointer_track . values , obj_model_prop - > get_variant_type ( ) , obj_model_prop - > get_accessor_type ( ) ) ;
samplers . push_back ( sampler ) ;
}
}
2020-12-21 15:39:32 +00:00
if ( channels . size ( ) & & samplers . size ( ) ) {
d [ " channels " ] = channels ;
d [ " samplers " ] = samplers ;
animations . push_back ( d ) ;
}
}
2021-11-02 00:31:59 +00:00
if ( ! animations . size ( ) ) {
return OK ;
}
2022-12-10 21:05:13 +00:00
p_state - > json [ " animations " ] = animations ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: Total animations ' " + itos ( p_state - > animations . size ( ) ) + " '. " ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_animations ( Ref < GLTFState > p_state ) {
if ( ! p_state - > json . has ( " animations " ) ) {
2020-12-21 15:39:32 +00:00
return OK ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
const Array & animations = p_state - > json [ " animations " ] ;
2020-12-21 15:39:32 +00:00
2024-07-10 08:26:35 +00:00
for ( GLTFAnimationIndex anim_index = 0 ; anim_index < animations . size ( ) ; anim_index + + ) {
const Dictionary & anim_dict = animations [ anim_index ] ;
2020-12-21 15:39:32 +00:00
Ref < GLTFAnimation > animation ;
2021-06-17 22:03:09 +00:00
animation . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
2024-07-10 08:26:35 +00:00
if ( ! anim_dict . has ( " channels " ) | | ! anim_dict . has ( " samplers " ) ) {
2020-12-21 15:39:32 +00:00
continue ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
2024-07-10 08:26:35 +00:00
Array channels = anim_dict [ " channels " ] ;
Array samplers = anim_dict [ " samplers " ] ;
2020-12-21 15:39:32 +00:00
2024-07-10 08:26:35 +00:00
if ( anim_dict . has ( " name " ) ) {
const String anim_name = anim_dict [ " name " ] ;
2022-09-29 09:53:28 +00:00
const String anim_name_lower = anim_name . to_lower ( ) ;
if ( anim_name_lower . begins_with ( " loop " ) | | anim_name_lower . ends_with ( " loop " ) | | anim_name_lower . begins_with ( " cycle " ) | | anim_name_lower . ends_with ( " cycle " ) ) {
2020-12-21 15:39:32 +00:00
animation - > set_loop ( true ) ;
}
2024-02-16 13:25:15 +00:00
animation - > set_original_name ( anim_name ) ;
2022-12-10 21:05:13 +00:00
animation - > set_name ( _gen_unique_animation_name ( p_state , anim_name ) ) ;
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:26:35 +00:00
for ( int channel_index = 0 ; channel_index < channels . size ( ) ; channel_index + + ) {
const Dictionary & anim_channel = channels [ channel_index ] ;
ERR_FAIL_COND_V_MSG ( ! anim_channel . has ( " sampler " ) , ERR_PARSE_ERROR , " glTF: Animation channel missing required 'sampler' property. " ) ;
ERR_FAIL_COND_V_MSG ( ! anim_channel . has ( " target " ) , ERR_PARSE_ERROR , " glTF: Animation channel missing required 'target' property. " ) ;
// Parse sampler.
const int sampler_index = anim_channel [ " sampler " ] ;
ERR_FAIL_INDEX_V ( sampler_index , samplers . size ( ) , ERR_PARSE_ERROR ) ;
const Dictionary & sampler_dict = samplers [ sampler_index ] ;
ERR_FAIL_COND_V ( ! sampler_dict . has ( " input " ) , ERR_PARSE_ERROR ) ;
ERR_FAIL_COND_V ( ! sampler_dict . has ( " output " ) , ERR_PARSE_ERROR ) ;
const int input_time_accessor_index = sampler_dict [ " input " ] ;
const int output_value_accessor_index = sampler_dict [ " output " ] ;
2020-12-21 15:39:32 +00:00
GLTFAnimation : : Interpolation interp = GLTFAnimation : : INTERP_LINEAR ;
int output_count = 1 ;
2024-07-10 08:26:35 +00:00
if ( sampler_dict . has ( " interpolation " ) ) {
const String & in = sampler_dict [ " interpolation " ] ;
2020-12-21 15:39:32 +00:00
if ( in = = " STEP " ) {
interp = GLTFAnimation : : INTERP_STEP ;
} else if ( in = = " LINEAR " ) {
interp = GLTFAnimation : : INTERP_LINEAR ;
} else if ( in = = " CATMULLROMSPLINE " ) {
interp = GLTFAnimation : : INTERP_CATMULLROMSPLINE ;
output_count = 3 ;
} else if ( in = = " CUBICSPLINE " ) {
interp = GLTFAnimation : : INTERP_CUBIC_SPLINE ;
output_count = 3 ;
}
}
2024-07-10 08:26:35 +00:00
const Vector < double > times = _decode_accessor ( p_state , input_time_accessor_index , false ) ;
// Parse target.
const Dictionary & anim_target = anim_channel [ " target " ] ;
ERR_FAIL_COND_V_MSG ( ! anim_target . has ( " path " ) , ERR_PARSE_ERROR , " glTF: Animation channel target missing required 'path' property. " ) ;
String path = anim_target [ " path " ] ;
2024-10-04 07:37:44 +00:00
if ( path = = " pointer " ) {
ERR_FAIL_COND_V ( ! anim_target . has ( " extensions " ) , ERR_PARSE_ERROR ) ;
Dictionary target_extensions = anim_target [ " extensions " ] ;
ERR_FAIL_COND_V ( ! target_extensions . has ( " KHR_animation_pointer " ) , ERR_PARSE_ERROR ) ;
Dictionary khr_anim_ptr = target_extensions [ " KHR_animation_pointer " ] ;
ERR_FAIL_COND_V ( ! khr_anim_ptr . has ( " pointer " ) , ERR_PARSE_ERROR ) ;
String anim_json_ptr = khr_anim_ptr [ " pointer " ] ;
_parse_animation_pointer ( p_state , anim_json_ptr , animation , interp , times , output_value_accessor_index ) ;
} else {
// If it's not a pointer, it's a regular animation channel from vanilla glTF (pos/rot/scale/weights).
if ( ! anim_target . has ( " node " ) ) {
WARN_PRINT ( " glTF: Animation channel target missing 'node' property. Ignoring this channel. " ) ;
continue ;
}
2020-12-21 15:39:32 +00:00
2024-10-04 07:37:44 +00:00
GLTFNodeIndex node = anim_target [ " node " ] ;
2024-07-10 08:26:35 +00:00
2024-10-04 07:37:44 +00:00
ERR_FAIL_INDEX_V ( node , p_state - > nodes . size ( ) , ERR_PARSE_ERROR ) ;
2020-12-21 15:39:32 +00:00
2024-10-04 07:37:44 +00:00
GLTFAnimation : : NodeTrack * track = nullptr ;
2020-12-21 15:39:32 +00:00
2024-10-04 07:37:44 +00:00
if ( ! animation - > get_node_tracks ( ) . has ( node ) ) {
animation - > get_node_tracks ( ) [ node ] = GLTFAnimation : : NodeTrack ( ) ;
}
track = & animation - > get_node_tracks ( ) [ node ] ;
if ( path = = " translation " ) {
const Vector < Vector3 > positions = _decode_accessor_as_vec3 ( p_state , output_value_accessor_index , false ) ;
track - > position_track . interpolation = interp ;
track - > position_track . times = times ;
track - > position_track . values = positions ;
} else if ( path = = " rotation " ) {
const Vector < Quaternion > rotations = _decode_accessor_as_quaternion ( p_state , output_value_accessor_index , false ) ;
track - > rotation_track . interpolation = interp ;
track - > rotation_track . times = times ;
track - > rotation_track . values = rotations ;
} else if ( path = = " scale " ) {
const Vector < Vector3 > scales = _decode_accessor_as_vec3 ( p_state , output_value_accessor_index , false ) ;
track - > scale_track . interpolation = interp ;
track - > scale_track . times = times ;
track - > scale_track . values = scales ;
} else if ( path = = " weights " ) {
const Vector < float > weights = _decode_accessor_as_floats ( p_state , output_value_accessor_index , false ) ;
ERR_FAIL_INDEX_V ( p_state - > nodes [ node ] - > mesh , p_state - > meshes . size ( ) , ERR_PARSE_ERROR ) ;
Ref < GLTFMesh > mesh = p_state - > meshes [ p_state - > nodes [ node ] - > mesh ] ;
const int wc = mesh - > get_blend_weights ( ) . size ( ) ;
ERR_CONTINUE_MSG ( wc = = 0 , " glTF: Animation tried to animate weights, but mesh has no weights. " ) ;
track - > weight_tracks . resize ( wc ) ;
const int expected_value_count = times . size ( ) * output_count * wc ;
ERR_CONTINUE_MSG ( weights . size ( ) ! = expected_value_count , " Invalid weight data, expected " + itos ( expected_value_count ) + " weight values, got " + itos ( weights . size ( ) ) + " instead. " ) ;
const int wlen = weights . size ( ) / wc ;
for ( int k = 0 ; k < wc ; k + + ) { //separate tracks, having them together is not such a good idea
GLTFAnimation : : Channel < real_t > cf ;
cf . interpolation = interp ;
cf . times = Variant ( times ) ;
Vector < real_t > wdata ;
wdata . resize ( wlen ) ;
for ( int l = 0 ; l < wlen ; l + + ) {
wdata . write [ l ] = weights [ l * wc + k ] ;
}
cf . values = wdata ;
track - > weight_tracks . write [ k ] = cf ;
}
} else {
WARN_PRINT ( " Invalid path ' " + path + " '. " ) ;
2020-12-21 15:39:32 +00:00
}
}
}
2022-12-10 21:05:13 +00:00
p_state - > animations . push_back ( animation ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
print_verbose ( " glTF: Total animations ' " + itos ( p_state - > animations . size ( ) ) + " '. " ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2024-10-04 07:37:44 +00:00
void GLTFDocument : : _parse_animation_pointer ( Ref < GLTFState > p_state , const String & p_animation_json_pointer , const Ref < GLTFAnimation > p_gltf_animation , const GLTFAnimation : : Interpolation p_interp , const Vector < double > & p_times , const int p_output_value_accessor_index ) {
// Special case: Convert TRS animation pointers to node track pos/rot/scale.
// This is required to handle skeleton bones, and improves performance for regular nodes.
// Mark this as unlikely because TRS animation pointers are not recommended,
// since vanilla glTF animations can already animate TRS properties directly.
// But having this code exist is required to be spec-compliant and handle all test files.
// Note that TRS still needs to be handled in the general case as well, for KHR_interactivity.
const PackedStringArray split = p_animation_json_pointer . split ( " / " , false , 3 ) ;
if ( unlikely ( split . size ( ) = = 3 & & split [ 0 ] = = " nodes " & & ( split [ 2 ] = = " translation " | | split [ 2 ] = = " rotation " | | split [ 2 ] = = " scale " | | split [ 2 ] = = " matrix " | | split [ 2 ] = = " weights " ) ) ) {
const GLTFNodeIndex node_index = split [ 1 ] . to_int ( ) ;
HashMap < int , GLTFAnimation : : NodeTrack > & node_tracks = p_gltf_animation - > get_node_tracks ( ) ;
if ( ! node_tracks . has ( node_index ) ) {
node_tracks [ node_index ] = GLTFAnimation : : NodeTrack ( ) ;
}
GLTFAnimation : : NodeTrack * track = & node_tracks [ node_index ] ;
if ( split [ 2 ] = = " translation " ) {
const Vector < Vector3 > positions = _decode_accessor_as_vec3 ( p_state , p_output_value_accessor_index , false ) ;
track - > position_track . interpolation = p_interp ;
track - > position_track . times = p_times ;
track - > position_track . values = positions ;
} else if ( split [ 2 ] = = " rotation " ) {
const Vector < Quaternion > rotations = _decode_accessor_as_quaternion ( p_state , p_output_value_accessor_index , false ) ;
track - > rotation_track . interpolation = p_interp ;
track - > rotation_track . times = p_times ;
track - > rotation_track . values = rotations ;
} else if ( split [ 2 ] = = " scale " ) {
const Vector < Vector3 > scales = _decode_accessor_as_vec3 ( p_state , p_output_value_accessor_index , false ) ;
track - > scale_track . interpolation = p_interp ;
track - > scale_track . times = p_times ;
track - > scale_track . values = scales ;
} else if ( split [ 2 ] = = " matrix " ) {
const Vector < Transform3D > transforms = _decode_accessor_as_xform ( p_state , p_output_value_accessor_index , false ) ;
track - > position_track . interpolation = p_interp ;
track - > position_track . times = p_times ;
track - > position_track . values . resize ( transforms . size ( ) ) ;
track - > rotation_track . interpolation = p_interp ;
track - > rotation_track . times = p_times ;
track - > rotation_track . values . resize ( transforms . size ( ) ) ;
track - > scale_track . interpolation = p_interp ;
track - > scale_track . times = p_times ;
track - > scale_track . values . resize ( transforms . size ( ) ) ;
for ( int i = 0 ; i < transforms . size ( ) ; i + + ) {
track - > position_track . values . write [ i ] = transforms [ i ] . get_origin ( ) ;
track - > rotation_track . values . write [ i ] = transforms [ i ] . basis . get_rotation_quaternion ( ) ;
track - > scale_track . values . write [ i ] = transforms [ i ] . basis . get_scale ( ) ;
}
} else { // if (split[2] == "weights")
const Vector < float > accessor_weights = _decode_accessor_as_floats ( p_state , p_output_value_accessor_index , false ) ;
const GLTFMeshIndex mesh_index = p_state - > nodes [ node_index ] - > mesh ;
ERR_FAIL_INDEX ( mesh_index , p_state - > meshes . size ( ) ) ;
const Ref < GLTFMesh > gltf_mesh = p_state - > meshes [ mesh_index ] ;
const Vector < float > & blend_weights = gltf_mesh - > get_blend_weights ( ) ;
const int blend_weight_count = gltf_mesh - > get_blend_weights ( ) . size ( ) ;
const int anim_weights_size = accessor_weights . size ( ) ;
// For example, if a mesh has 2 blend weights, and the accessor provides 10 values, then there are 5 frames of animation, each with 2 blend weights.
ERR_FAIL_COND_MSG ( blend_weight_count = = 0 | | ( ( anim_weights_size % blend_weight_count ) ! = 0 ) , " glTF: Cannot apply " + itos ( accessor_weights . size ( ) ) + " weights to a mesh with " + itos ( blend_weights . size ( ) ) + " blend weights. " ) ;
const int frame_count = anim_weights_size / blend_weight_count ;
track - > weight_tracks . resize ( blend_weight_count ) ;
for ( int blend_weight_index = 0 ; blend_weight_index < blend_weight_count ; blend_weight_index + + ) {
GLTFAnimation : : Channel < real_t > weight_track ;
weight_track . interpolation = p_interp ;
weight_track . times = p_times ;
weight_track . values . resize ( frame_count ) ;
for ( int frame_index = 0 ; frame_index < frame_count ; frame_index + + ) {
// For example, if a mesh has 2 blend weights, and the accessor provides 10 values,
// then the first frame has indices [0, 1], the second frame has [2, 3], and so on.
// Here we process all frames of one blend weight, so we want [0, 2, 4, 6, 8] or [1, 3, 5, 7, 9].
// For the fist one we calculate 0 * 2 + 0, 1 * 2 + 0, 2 * 2 + 0, etc, then for the second 0 * 2 + 1, 1 * 2 + 1, 2 * 2 + 1, etc.
weight_track . values . write [ frame_index ] = accessor_weights [ frame_index * blend_weight_count + blend_weight_index ] ;
}
track - > weight_tracks . write [ blend_weight_index ] = weight_track ;
}
}
// The special case was handled, return to skip the general case.
return ;
}
// General case: Convert animation pointers to Variant value pointer tracks.
Ref < GLTFObjectModelProperty > obj_model_prop = import_object_model_property ( p_state , p_animation_json_pointer ) ;
if ( obj_model_prop . is_null ( ) | | ! obj_model_prop - > has_node_paths ( ) ) {
// Exit quietly, `import_object_model_property` already prints a warning if the property is not found.
return ;
}
HashMap < String , GLTFAnimation : : Channel < Variant > > & anim_ptr_map = p_gltf_animation - > get_pointer_tracks ( ) ;
GLTFAnimation : : Channel < Variant > channel ;
channel . interpolation = p_interp ;
channel . times = p_times ;
channel . values = _decode_accessor_as_variant ( p_state , p_output_value_accessor_index , obj_model_prop - > get_variant_type ( ) , obj_model_prop - > get_accessor_type ( ) ) ;
anim_ptr_map [ p_animation_json_pointer ] = channel ;
}
2023-07-22 03:06:50 +00:00
void GLTFDocument : : _assign_node_names ( Ref < GLTFState > p_state ) {
2022-12-10 21:05:13 +00:00
for ( int i = 0 ; i < p_state - > nodes . size ( ) ; i + + ) {
2023-07-22 03:06:50 +00:00
Ref < GLTFNode > gltf_node = p_state - > nodes [ i ] ;
2020-12-21 15:39:32 +00:00
// Any joints get unique names generated when the skeleton is made, unique to the skeleton
2023-07-22 03:06:50 +00:00
if ( gltf_node - > skeleton > = 0 ) {
2020-12-21 15:39:32 +00:00
continue ;
2021-04-05 12:09:59 +00:00
}
2023-07-23 02:17:28 +00:00
String gltf_node_name = gltf_node - > get_name ( ) ;
if ( gltf_node_name . is_empty ( ) ) {
2023-10-31 22:55:15 +00:00
if ( _naming_version = = 0 ) {
if ( gltf_node - > mesh > = 0 ) {
gltf_node_name = _gen_unique_name ( p_state , " Mesh " ) ;
} else if ( gltf_node - > camera > = 0 ) {
gltf_node_name = _gen_unique_name ( p_state , " Camera3D " ) ;
} else {
gltf_node_name = _gen_unique_name ( p_state , " Node " ) ;
}
2020-12-21 15:39:32 +00:00
} else {
2023-10-31 22:55:15 +00:00
if ( gltf_node - > mesh > = 0 ) {
gltf_node_name = " Mesh " ;
} else if ( gltf_node - > camera > = 0 ) {
gltf_node_name = " Camera " ;
} else {
gltf_node_name = " Node " ;
}
2020-12-21 15:39:32 +00:00
}
}
2023-07-23 02:17:28 +00:00
gltf_node - > set_name ( _gen_unique_name ( p_state , gltf_node_name ) ) ;
2020-12-21 15:39:32 +00:00
}
}
2022-12-10 21:05:13 +00:00
BoneAttachment3D * GLTFDocument : : _generate_bone_attachment ( Ref < GLTFState > p_state , Skeleton3D * p_skeleton , const GLTFNodeIndex p_node_index , const GLTFNodeIndex p_bone_index ) {
Ref < GLTFNode > gltf_node = p_state - > nodes [ p_node_index ] ;
Ref < GLTFNode > bone_node = p_state - > nodes [ p_bone_index ] ;
2020-12-21 15:39:32 +00:00
BoneAttachment3D * bone_attachment = memnew ( BoneAttachment3D ) ;
print_verbose ( " glTF: Creating bone attachment for: " + gltf_node - > get_name ( ) ) ;
ERR_FAIL_COND_V ( ! bone_node - > joint , nullptr ) ;
bone_attachment - > set_bone_name ( bone_node - > get_name ( ) ) ;
return bone_attachment ;
}
2022-12-10 21:05:13 +00:00
GLTFMeshIndex GLTFDocument : : _convert_mesh_to_gltf ( Ref < GLTFState > p_state , MeshInstance3D * p_mesh_instance ) {
2020-12-21 15:39:32 +00:00
ERR_FAIL_NULL_V ( p_mesh_instance , - 1 ) ;
2024-02-04 03:41:00 +00:00
ERR_FAIL_COND_V_MSG ( p_mesh_instance - > get_mesh ( ) . is_null ( ) , - 1 , " glTF: Tried to export a MeshInstance3D node named " + p_mesh_instance - > get_name ( ) + " , but it has no mesh. This node will be exported without a mesh. " ) ;
Ref < Mesh > mesh_resource = p_mesh_instance - > get_mesh ( ) ;
ERR_FAIL_COND_V_MSG ( mesh_resource - > get_surface_count ( ) = = 0 , - 1 , " glTF: Tried to export a MeshInstance3D node named " + p_mesh_instance - > get_name ( ) + " , but its mesh has no surfaces. This node will be exported without a mesh. " ) ;
2024-05-29 14:09:42 +00:00
TypedArray < Material > instance_materials ;
for ( int32_t surface_i = 0 ; surface_i < mesh_resource - > get_surface_count ( ) ; surface_i + + ) {
Ref < Material > mat = p_mesh_instance - > get_active_material ( surface_i ) ;
instance_materials . append ( mat ) ;
}
2024-02-04 03:41:00 +00:00
Ref < ImporterMesh > current_mesh = _mesh_to_importer_mesh ( mesh_resource ) ;
2020-12-21 15:39:32 +00:00
Vector < float > blend_weights ;
2024-02-04 03:41:00 +00:00
int32_t blend_count = mesh_resource - > get_blend_shape_count ( ) ;
2022-02-20 13:39:11 +00:00
blend_weights . resize ( blend_count ) ;
for ( int32_t blend_i = 0 ; blend_i < blend_count ; blend_i + + ) {
blend_weights . write [ blend_i ] = 0.0f ;
2020-12-21 15:39:32 +00:00
}
2022-02-20 13:39:11 +00:00
2020-12-21 15:39:32 +00:00
Ref < GLTFMesh > gltf_mesh ;
2021-06-17 22:03:09 +00:00
gltf_mesh . instantiate ( ) ;
2021-11-02 00:31:59 +00:00
gltf_mesh - > set_instance_materials ( instance_materials ) ;
2021-09-21 01:24:31 +00:00
gltf_mesh - > set_mesh ( current_mesh ) ;
2020-12-21 15:39:32 +00:00
gltf_mesh - > set_blend_weights ( blend_weights ) ;
2022-12-10 21:05:13 +00:00
GLTFMeshIndex mesh_i = p_state - > meshes . size ( ) ;
p_state - > meshes . push_back ( gltf_mesh ) ;
2020-12-21 15:39:32 +00:00
return mesh_i ;
}
2022-12-10 21:05:13 +00:00
ImporterMeshInstance3D * GLTFDocument : : _generate_mesh_instance ( Ref < GLTFState > p_state , const GLTFNodeIndex p_node_index ) {
Ref < GLTFNode > gltf_node = p_state - > nodes [ p_node_index ] ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
ERR_FAIL_INDEX_V ( gltf_node - > mesh , p_state - > meshes . size ( ) , nullptr ) ;
2020-12-21 15:39:32 +00:00
2021-09-21 01:24:31 +00:00
ImporterMeshInstance3D * mi = memnew ( ImporterMeshInstance3D ) ;
2020-12-21 15:39:32 +00:00
print_verbose ( " glTF: Creating mesh for: " + gltf_node - > get_name ( ) ) ;
2023-01-27 06:41:58 +00:00
p_state - > scene_mesh_instances . insert ( p_node_index , mi ) ;
2022-12-10 21:05:13 +00:00
Ref < GLTFMesh > mesh = p_state - > meshes . write [ gltf_node - > mesh ] ;
2020-12-21 15:39:32 +00:00
if ( mesh . is_null ( ) ) {
return mi ;
}
2021-09-21 01:24:31 +00:00
Ref < ImporterMesh > import_mesh = mesh - > get_mesh ( ) ;
2020-12-21 15:39:32 +00:00
if ( import_mesh . is_null ( ) ) {
return mi ;
}
mi - > set_mesh ( import_mesh ) ;
2024-01-11 19:47:31 +00:00
import_mesh - > merge_meta_from ( * mesh ) ;
2020-12-21 15:39:32 +00:00
return mi ;
}
2022-12-10 21:05:13 +00:00
Light3D * GLTFDocument : : _generate_light ( Ref < GLTFState > p_state , const GLTFNodeIndex p_node_index ) {
Ref < GLTFNode > gltf_node = p_state - > nodes [ p_node_index ] ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
ERR_FAIL_INDEX_V ( gltf_node - > light , p_state - > lights . size ( ) , nullptr ) ;
2020-12-21 15:39:32 +00:00
print_verbose ( " glTF: Creating light for: " + gltf_node - > get_name ( ) ) ;
2022-12-10 21:05:13 +00:00
Ref < GLTFLight > l = p_state - > lights [ gltf_node - > light ] ;
2022-07-26 05:51:53 +00:00
return l - > to_node ( ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
Camera3D * GLTFDocument : : _generate_camera ( Ref < GLTFState > p_state , const GLTFNodeIndex p_node_index ) {
Ref < GLTFNode > gltf_node = p_state - > nodes [ p_node_index ] ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
ERR_FAIL_INDEX_V ( gltf_node - > camera , p_state - > cameras . size ( ) , nullptr ) ;
2020-12-21 15:39:32 +00:00
print_verbose ( " glTF: Creating camera for: " + gltf_node - > get_name ( ) ) ;
2022-12-10 21:05:13 +00:00
Ref < GLTFCamera > c = p_state - > cameras [ gltf_node - > camera ] ;
2022-08-13 21:27:15 +00:00
return c - > to_node ( ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
GLTFCameraIndex GLTFDocument : : _convert_camera ( Ref < GLTFState > p_state , Camera3D * p_camera ) {
2020-12-21 15:39:32 +00:00
print_verbose ( " glTF: Converting camera: " + p_camera - > get_name ( ) ) ;
2022-08-13 21:27:15 +00:00
Ref < GLTFCamera > c = GLTFCamera : : from_node ( p_camera ) ;
2022-12-10 21:05:13 +00:00
GLTFCameraIndex camera_index = p_state - > cameras . size ( ) ;
p_state - > cameras . push_back ( c ) ;
2020-12-21 15:39:32 +00:00
return camera_index ;
}
2022-12-10 21:05:13 +00:00
GLTFLightIndex GLTFDocument : : _convert_light ( Ref < GLTFState > p_state , Light3D * p_light ) {
2020-12-21 15:39:32 +00:00
print_verbose ( " glTF: Converting light: " + p_light - > get_name ( ) ) ;
2022-07-26 05:51:53 +00:00
Ref < GLTFLight > l = GLTFLight : : from_node ( p_light ) ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
GLTFLightIndex light_index = p_state - > lights . size ( ) ;
p_state - > lights . push_back ( l ) ;
2020-12-21 15:39:32 +00:00
return light_index ;
}
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _convert_spatial ( Ref < GLTFState > p_state , Node3D * p_spatial , Ref < GLTFNode > p_node ) {
2023-10-12 22:37:03 +00:00
p_node - > transform = p_spatial - > get_transform ( ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
Node3D * GLTFDocument : : _generate_spatial ( Ref < GLTFState > p_state , const GLTFNodeIndex p_node_index ) {
Ref < GLTFNode > gltf_node = p_state - > nodes [ p_node_index ] ;
2020-12-21 15:39:32 +00:00
Node3D * spatial = memnew ( Node3D ) ;
print_verbose ( " glTF: Converting spatial: " + gltf_node - > get_name ( ) ) ;
return spatial ;
}
2022-09-18 03:16:22 +00:00
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _convert_scene_node ( Ref < GLTFState > p_state , Node * p_current , const GLTFNodeIndex p_gltf_parent , const GLTFNodeIndex p_gltf_root ) {
2020-12-21 15:39:32 +00:00
bool retflag = true ;
_check_visibility ( p_current , retflag ) ;
if ( retflag ) {
return ;
}
2024-02-04 03:41:00 +00:00
# ifdef TOOLS_ENABLED
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) & & p_gltf_root ! = - 1 & & p_current - > get_owner ( ) = = nullptr ) {
WARN_VERBOSE ( " glTF export warning: Node ' " + p_current - > get_name ( ) + " ' has no owner. This is likely a temporary node generated by a @tool script. This would not be saved when saving the Godot scene, therefore it will not be exported to glTF. " ) ;
return ;
}
# endif // TOOLS_ENABLED
2020-12-21 15:39:32 +00:00
Ref < GLTFNode > gltf_node ;
2021-06-17 22:03:09 +00:00
gltf_node . instantiate ( ) ;
2024-02-16 13:25:15 +00:00
gltf_node - > set_original_name ( p_current - > get_name ( ) ) ;
2022-12-10 21:05:13 +00:00
gltf_node - > set_name ( _gen_unique_name ( p_state , p_current - > get_name ( ) ) ) ;
2024-09-02 08:28:07 +00:00
gltf_node - > merge_meta_from ( p_current ) ;
2024-07-10 09:02:03 +00:00
if ( Object : : cast_to < Node3D > ( p_current ) ) {
Node3D * spatial = Object : : cast_to < Node3D > ( p_current ) ;
2022-12-10 21:05:13 +00:00
_convert_spatial ( p_state , spatial , gltf_node ) ;
2020-12-21 15:39:32 +00:00
}
2024-07-10 09:02:03 +00:00
if ( Object : : cast_to < MeshInstance3D > ( p_current ) ) {
MeshInstance3D * mi = Object : : cast_to < MeshInstance3D > ( p_current ) ;
2022-12-10 21:05:13 +00:00
_convert_mesh_instance_to_gltf ( mi , p_state , gltf_node ) ;
2024-07-10 09:02:03 +00:00
} else if ( Object : : cast_to < BoneAttachment3D > ( p_current ) ) {
BoneAttachment3D * bone = Object : : cast_to < BoneAttachment3D > ( p_current ) ;
2022-12-10 21:05:13 +00:00
_convert_bone_attachment_to_gltf ( bone , p_state , p_gltf_parent , p_gltf_root , gltf_node ) ;
2020-12-21 15:39:32 +00:00
return ;
2024-07-10 09:02:03 +00:00
} else if ( Object : : cast_to < Skeleton3D > ( p_current ) ) {
Skeleton3D * skel = Object : : cast_to < Skeleton3D > ( p_current ) ;
2022-12-10 21:05:13 +00:00
_convert_skeleton_to_gltf ( skel , p_state , p_gltf_parent , p_gltf_root , gltf_node ) ;
2020-12-21 15:39:32 +00:00
// We ignore the Godot Engine node that is the skeleton.
return ;
2024-07-10 09:02:03 +00:00
} else if ( Object : : cast_to < MultiMeshInstance3D > ( p_current ) ) {
MultiMeshInstance3D * multi = Object : : cast_to < MultiMeshInstance3D > ( p_current ) ;
2022-12-10 21:05:13 +00:00
_convert_multi_mesh_instance_to_gltf ( multi , p_gltf_parent , p_gltf_root , gltf_node , p_state ) ;
2021-01-22 21:52:32 +00:00
# ifdef MODULE_CSG_ENABLED
2024-07-10 09:02:03 +00:00
} else if ( Object : : cast_to < CSGShape3D > ( p_current ) ) {
CSGShape3D * shape = Object : : cast_to < CSGShape3D > ( p_current ) ;
2021-09-04 01:00:59 +00:00
if ( shape - > get_parent ( ) & & shape - > is_root_shape ( ) ) {
2022-12-10 21:05:13 +00:00
_convert_csg_shape_to_gltf ( shape , p_gltf_parent , gltf_node , p_state ) ;
2020-12-21 15:39:32 +00:00
}
2021-01-22 21:52:32 +00:00
# endif // MODULE_CSG_ENABLED
# ifdef MODULE_GRIDMAP_ENABLED
2024-07-10 09:02:03 +00:00
} else if ( Object : : cast_to < GridMap > ( p_current ) ) {
2021-09-04 01:00:59 +00:00
GridMap * gridmap = Object : : cast_to < GridMap > ( p_current ) ;
2022-12-10 21:05:13 +00:00
_convert_grid_map_to_gltf ( gridmap , p_gltf_parent , p_gltf_root , gltf_node , p_state ) ;
2021-01-22 21:52:32 +00:00
# endif // MODULE_GRIDMAP_ENABLED
2024-07-10 09:02:03 +00:00
} else if ( Object : : cast_to < Camera3D > ( p_current ) ) {
2020-12-21 15:39:32 +00:00
Camera3D * camera = Object : : cast_to < Camera3D > ( p_current ) ;
2022-12-10 21:05:13 +00:00
_convert_camera_to_gltf ( camera , p_state , gltf_node ) ;
2024-07-10 09:02:03 +00:00
} else if ( Object : : cast_to < Light3D > ( p_current ) ) {
2020-12-21 15:39:32 +00:00
Light3D * light = Object : : cast_to < Light3D > ( p_current ) ;
2022-12-10 21:05:13 +00:00
_convert_light_to_gltf ( light , p_state , gltf_node ) ;
2024-07-10 09:02:03 +00:00
} else if ( Object : : cast_to < AnimationPlayer > ( p_current ) ) {
2020-12-21 15:39:32 +00:00
AnimationPlayer * animation_player = Object : : cast_to < AnimationPlayer > ( p_current ) ;
2024-07-10 08:26:35 +00:00
p_state - > animation_players . push_back ( animation_player ) ;
2020-12-21 15:39:32 +00:00
}
2022-09-17 22:48:06 +00:00
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
2022-12-10 21:05:13 +00:00
ext - > convert_scene_node ( p_state , gltf_node , p_current ) ;
2022-09-17 22:48:06 +00:00
}
2024-09-02 08:28:07 +00:00
GLTFNodeIndex current_node_i ;
if ( gltf_node - > get_parent ( ) = = - 1 ) {
current_node_i = p_state - > append_gltf_node ( gltf_node , p_current , p_gltf_parent ) ;
} else if ( gltf_node - > get_parent ( ) < - 1 ) {
return ;
} else {
current_node_i = p_state - > nodes . size ( ) - 1 ;
while ( gltf_node ! = p_state - > nodes [ current_node_i ] ) {
current_node_i - - ;
}
2020-12-21 15:39:32 +00:00
}
2024-09-02 08:28:07 +00:00
const GLTFNodeIndex gltf_root = ( p_gltf_root = = - 1 ) ? current_node_i : p_gltf_root ;
2020-12-21 15:39:32 +00:00
for ( int node_i = 0 ; node_i < p_current - > get_child_count ( ) ; node_i + + ) {
2022-12-10 21:05:13 +00:00
_convert_scene_node ( p_state , p_current - > get_child ( node_i ) , current_node_i , gltf_root ) ;
2020-12-21 15:39:32 +00:00
}
}
2021-01-22 21:52:32 +00:00
# ifdef MODULE_CSG_ENABLED
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _convert_csg_shape_to_gltf ( CSGShape3D * p_current , GLTFNodeIndex p_gltf_parent , Ref < GLTFNode > p_gltf_node , Ref < GLTFState > p_state ) {
2021-09-04 01:00:59 +00:00
CSGShape3D * csg = p_current ;
2020-12-21 15:39:32 +00:00
csg - > call ( " _update_shape " ) ;
Array meshes = csg - > get_meshes ( ) ;
if ( meshes . size ( ) ! = 2 ) {
return ;
}
2022-02-10 18:31:25 +00:00
Ref < ImporterMesh > mesh ;
mesh . instantiate ( ) ;
{
2024-05-29 14:09:42 +00:00
Ref < ArrayMesh > csg_mesh = csg - > get_meshes ( ) [ 1 ] ;
2022-02-10 18:31:25 +00:00
for ( int32_t surface_i = 0 ; surface_i < csg_mesh - > get_surface_count ( ) ; surface_i + + ) {
Array array = csg_mesh - > surface_get_arrays ( surface_i ) ;
2024-05-29 14:09:42 +00:00
Ref < Material > mat ;
Ref < Material > mat_override = csg - > get_material_override ( ) ;
if ( mat_override . is_valid ( ) ) {
mat = mat_override ;
}
Ref < Material > mat_surface_override = csg_mesh - > surface_get_material ( surface_i ) ;
if ( mat_surface_override . is_valid ( ) & & mat . is_null ( ) ) {
mat = mat_surface_override ;
}
2022-02-10 18:31:25 +00:00
String mat_name ;
if ( mat . is_valid ( ) ) {
mat_name = mat - > get_name ( ) ;
} else {
// Assign default material when no material is assigned.
2024-06-09 20:21:41 +00:00
mat . instantiate ( ) ;
2022-02-10 18:31:25 +00:00
}
2024-05-29 14:09:42 +00:00
2022-02-10 18:31:25 +00:00
mesh - > add_surface ( csg_mesh - > surface_get_primitive_type ( surface_i ) ,
array , csg_mesh - > surface_get_blend_shape_arrays ( surface_i ) , csg_mesh - > surface_get_lods ( surface_i ) , mat ,
mat_name , csg_mesh - > surface_get_format ( surface_i ) ) ;
}
2020-12-21 15:39:32 +00:00
}
2022-02-10 18:31:25 +00:00
2020-12-21 15:39:32 +00:00
Ref < GLTFMesh > gltf_mesh ;
2021-06-17 22:03:09 +00:00
gltf_mesh . instantiate ( ) ;
2022-02-10 18:31:25 +00:00
gltf_mesh - > set_mesh ( mesh ) ;
2024-02-16 13:25:15 +00:00
gltf_mesh - > set_original_name ( csg - > get_name ( ) ) ;
2022-12-10 21:05:13 +00:00
GLTFMeshIndex mesh_i = p_state - > meshes . size ( ) ;
p_state - > meshes . push_back ( gltf_mesh ) ;
p_gltf_node - > mesh = mesh_i ;
2024-05-29 14:09:42 +00:00
p_gltf_node - > transform = csg - > get_transform ( ) ;
2024-02-16 13:25:15 +00:00
p_gltf_node - > set_original_name ( csg - > get_name ( ) ) ;
2022-12-10 21:05:13 +00:00
p_gltf_node - > set_name ( _gen_unique_name ( p_state , csg - > get_name ( ) ) ) ;
2020-12-21 15:39:32 +00:00
}
2021-01-22 21:52:32 +00:00
# endif // MODULE_CSG_ENABLED
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _check_visibility ( Node * p_node , bool & r_retflag ) {
r_retflag = true ;
2020-12-21 15:39:32 +00:00
Node3D * spatial = Object : : cast_to < Node3D > ( p_node ) ;
Node2D * node_2d = Object : : cast_to < Node2D > ( p_node ) ;
if ( node_2d & & ! node_2d - > is_visible ( ) ) {
return ;
}
if ( spatial & & ! spatial - > is_visible ( ) ) {
return ;
}
2022-12-10 21:05:13 +00:00
r_retflag = false ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _convert_camera_to_gltf ( Camera3D * camera , Ref < GLTFState > p_state , Ref < GLTFNode > p_gltf_node ) {
2023-09-09 15:40:07 +00:00
ERR_FAIL_NULL ( camera ) ;
2022-12-10 21:05:13 +00:00
GLTFCameraIndex camera_index = _convert_camera ( p_state , camera ) ;
2020-12-21 15:39:32 +00:00
if ( camera_index ! = - 1 ) {
2022-12-10 21:05:13 +00:00
p_gltf_node - > camera = camera_index ;
2020-12-21 15:39:32 +00:00
}
}
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _convert_light_to_gltf ( Light3D * light , Ref < GLTFState > p_state , Ref < GLTFNode > p_gltf_node ) {
2023-09-09 15:40:07 +00:00
ERR_FAIL_NULL ( light ) ;
2022-12-10 21:05:13 +00:00
GLTFLightIndex light_index = _convert_light ( p_state , light ) ;
2020-12-21 15:39:32 +00:00
if ( light_index ! = - 1 ) {
2022-12-10 21:05:13 +00:00
p_gltf_node - > light = light_index ;
2020-12-21 15:39:32 +00:00
}
}
2021-01-22 21:52:32 +00:00
# ifdef MODULE_GRIDMAP_ENABLED
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _convert_grid_map_to_gltf ( GridMap * p_grid_map , GLTFNodeIndex p_parent_node_index , GLTFNodeIndex p_root_node_index , Ref < GLTFNode > p_gltf_node , Ref < GLTFState > p_state ) {
2021-09-04 01:00:59 +00:00
Array cells = p_grid_map - > get_used_cells ( ) ;
2020-12-21 15:39:32 +00:00
for ( int32_t k = 0 ; k < cells . size ( ) ; k + + ) {
GLTFNode * new_gltf_node = memnew ( GLTFNode ) ;
2022-12-10 21:05:13 +00:00
p_gltf_node - > children . push_back ( p_state - > nodes . size ( ) ) ;
p_state - > nodes . push_back ( new_gltf_node ) ;
2020-12-21 15:39:32 +00:00
Vector3 cell_location = cells [ k ] ;
2021-09-04 01:00:59 +00:00
int32_t cell = p_grid_map - > get_cell_item (
2020-12-21 15:39:32 +00:00
Vector3 ( cell_location . x , cell_location . y , cell_location . z ) ) ;
2020-10-17 05:08:21 +00:00
Transform3D cell_xform ;
2022-01-27 05:44:36 +00:00
cell_xform . basis = p_grid_map - > get_basis_with_orthogonal_index (
2021-09-04 01:00:59 +00:00
p_grid_map - > get_cell_item_orientation (
2020-12-21 15:39:32 +00:00
Vector3 ( cell_location . x , cell_location . y , cell_location . z ) ) ) ;
2021-09-04 01:00:59 +00:00
cell_xform . basis . scale ( Vector3 ( p_grid_map - > get_cell_scale ( ) ,
p_grid_map - > get_cell_scale ( ) ,
p_grid_map - > get_cell_scale ( ) ) ) ;
2022-08-20 16:39:05 +00:00
cell_xform . set_origin ( p_grid_map - > map_to_local (
2020-12-21 15:39:32 +00:00
Vector3 ( cell_location . x , cell_location . y , cell_location . z ) ) ) ;
Ref < GLTFMesh > gltf_mesh ;
2021-06-17 22:03:09 +00:00
gltf_mesh . instantiate ( ) ;
2022-02-20 13:39:11 +00:00
gltf_mesh - > set_mesh ( _mesh_to_importer_mesh ( p_grid_map - > get_mesh_library ( ) - > get_item_mesh ( cell ) ) ) ;
2024-02-16 13:25:15 +00:00
gltf_mesh - > set_original_name ( p_grid_map - > get_mesh_library ( ) - > get_item_name ( cell ) ) ;
2022-12-10 21:05:13 +00:00
new_gltf_node - > mesh = p_state - > meshes . size ( ) ;
p_state - > meshes . push_back ( gltf_mesh ) ;
2023-10-12 22:37:03 +00:00
new_gltf_node - > transform = cell_xform * p_grid_map - > get_transform ( ) ;
2024-02-16 13:25:15 +00:00
new_gltf_node - > set_original_name ( p_grid_map - > get_mesh_library ( ) - > get_item_name ( cell ) ) ;
2022-12-10 21:05:13 +00:00
new_gltf_node - > set_name ( _gen_unique_name ( p_state , p_grid_map - > get_mesh_library ( ) - > get_item_name ( cell ) ) ) ;
2020-12-21 15:39:32 +00:00
}
}
2021-01-22 21:52:32 +00:00
# endif // MODULE_GRIDMAP_ENABLED
2020-12-21 15:39:32 +00:00
2021-09-21 01:24:31 +00:00
void GLTFDocument : : _convert_multi_mesh_instance_to_gltf (
MultiMeshInstance3D * p_multi_mesh_instance ,
GLTFNodeIndex p_parent_node_index ,
GLTFNodeIndex p_root_node_index ,
2022-12-10 21:05:13 +00:00
Ref < GLTFNode > p_gltf_node , Ref < GLTFState > p_state ) {
2023-09-09 15:40:07 +00:00
ERR_FAIL_NULL ( p_multi_mesh_instance ) ;
2021-09-04 01:00:59 +00:00
Ref < MultiMesh > multi_mesh = p_multi_mesh_instance - > get_multimesh ( ) ;
2021-09-21 01:24:31 +00:00
if ( multi_mesh . is_null ( ) ) {
return ;
}
Ref < GLTFMesh > gltf_mesh ;
gltf_mesh . instantiate ( ) ;
Ref < Mesh > mesh = multi_mesh - > get_mesh ( ) ;
if ( mesh . is_null ( ) ) {
return ;
}
2024-02-16 13:25:15 +00:00
gltf_mesh - > set_original_name ( multi_mesh - > get_name ( ) ) ;
2021-09-21 01:24:31 +00:00
gltf_mesh - > set_name ( multi_mesh - > get_name ( ) ) ;
Ref < ImporterMesh > importer_mesh ;
importer_mesh . instantiate ( ) ;
Ref < ArrayMesh > array_mesh = multi_mesh - > get_mesh ( ) ;
if ( array_mesh . is_valid ( ) ) {
importer_mesh - > set_blend_shape_mode ( array_mesh - > get_blend_shape_mode ( ) ) ;
for ( int32_t blend_i = 0 ; blend_i < array_mesh - > get_blend_shape_count ( ) ; blend_i + + ) {
importer_mesh - > add_blend_shape ( array_mesh - > get_blend_shape_name ( blend_i ) ) ;
}
}
for ( int32_t surface_i = 0 ; surface_i < mesh - > get_surface_count ( ) ; surface_i + + ) {
Ref < Material > mat = mesh - > surface_get_material ( surface_i ) ;
String material_name ;
if ( mat . is_valid ( ) ) {
material_name = mat - > get_name ( ) ;
2020-12-21 15:39:32 +00:00
}
2021-09-21 01:24:31 +00:00
Array blend_arrays ;
if ( array_mesh . is_valid ( ) ) {
blend_arrays = array_mesh - > surface_get_blend_shape_arrays ( surface_i ) ;
}
importer_mesh - > add_surface ( mesh - > surface_get_primitive_type ( surface_i ) , mesh - > surface_get_arrays ( surface_i ) ,
blend_arrays , mesh - > surface_get_lods ( surface_i ) , mat , material_name , mesh - > surface_get_format ( surface_i ) ) ;
}
gltf_mesh - > set_mesh ( importer_mesh ) ;
2022-12-10 21:05:13 +00:00
GLTFMeshIndex mesh_index = p_state - > meshes . size ( ) ;
p_state - > meshes . push_back ( gltf_mesh ) ;
2021-09-21 01:24:31 +00:00
for ( int32_t instance_i = 0 ; instance_i < multi_mesh - > get_instance_count ( ) ;
instance_i + + ) {
Transform3D transform ;
if ( multi_mesh - > get_transform_format ( ) = = MultiMesh : : TRANSFORM_2D ) {
Transform2D xform_2d = multi_mesh - > get_instance_transform_2d ( instance_i ) ;
transform . origin =
Vector3 ( xform_2d . get_origin ( ) . x , 0 , xform_2d . get_origin ( ) . y ) ;
real_t rotation = xform_2d . get_rotation ( ) ;
Quaternion quaternion ( Vector3 ( 0 , 1 , 0 ) , rotation ) ;
Size2 scale = xform_2d . get_scale ( ) ;
transform . basis . set_quaternion_scale ( quaternion ,
Vector3 ( scale . x , 0 , scale . y ) ) ;
transform = p_multi_mesh_instance - > get_transform ( ) * transform ;
} else if ( multi_mesh - > get_transform_format ( ) = = MultiMesh : : TRANSFORM_3D ) {
transform = p_multi_mesh_instance - > get_transform ( ) *
2021-10-28 13:19:35 +00:00
multi_mesh - > get_instance_transform ( instance_i ) ;
2021-09-21 01:24:31 +00:00
}
Ref < GLTFNode > new_gltf_node ;
new_gltf_node . instantiate ( ) ;
new_gltf_node - > mesh = mesh_index ;
2023-10-12 22:37:03 +00:00
new_gltf_node - > transform = transform ;
2024-02-16 13:25:15 +00:00
new_gltf_node - > set_original_name ( p_multi_mesh_instance - > get_name ( ) ) ;
2022-12-10 21:05:13 +00:00
new_gltf_node - > set_name ( _gen_unique_name ( p_state , p_multi_mesh_instance - > get_name ( ) ) ) ;
p_gltf_node - > children . push_back ( p_state - > nodes . size ( ) ) ;
p_state - > nodes . push_back ( new_gltf_node ) ;
2020-12-21 15:39:32 +00:00
}
}
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _convert_skeleton_to_gltf ( Skeleton3D * p_skeleton3d , Ref < GLTFState > p_state , GLTFNodeIndex p_parent_node_index , GLTFNodeIndex p_root_node_index , Ref < GLTFNode > p_gltf_node ) {
2021-09-04 01:00:59 +00:00
Skeleton3D * skeleton = p_skeleton3d ;
Ref < GLTFSkeleton > gltf_skeleton ;
gltf_skeleton . instantiate ( ) ;
2022-12-10 21:05:13 +00:00
// GLTFSkeleton is only used to hold internal p_state data. It will not be written to the document.
2021-09-04 01:00:59 +00:00
//
gltf_skeleton - > godot_skeleton = skeleton ;
2022-12-10 21:05:13 +00:00
GLTFSkeletonIndex skeleton_i = p_state - > skeletons . size ( ) ;
p_state - > skeleton3d_to_gltf_skeleton [ skeleton - > get_instance_id ( ) ] = skeleton_i ;
p_state - > skeletons . push_back ( gltf_skeleton ) ;
2021-09-04 01:00:59 +00:00
BoneId bone_count = skeleton - > get_bone_count ( ) ;
for ( BoneId bone_i = 0 ; bone_i < bone_count ; bone_i + + ) {
Ref < GLTFNode > joint_node ;
joint_node . instantiate ( ) ;
// Note that we cannot use _gen_unique_bone_name here, because glTF spec requires all node
// names to be unique regardless of whether or not they are used as joints.
2024-02-16 13:25:15 +00:00
joint_node - > set_original_name ( skeleton - > get_bone_name ( bone_i ) ) ;
2022-12-10 21:05:13 +00:00
joint_node - > set_name ( _gen_unique_name ( p_state , skeleton - > get_bone_name ( bone_i ) ) ) ;
2023-10-12 22:37:03 +00:00
joint_node - > transform = skeleton - > get_bone_pose ( bone_i ) ;
2021-09-04 01:00:59 +00:00
joint_node - > joint = true ;
2024-08-30 20:40:11 +00:00
if ( p_skeleton3d - > has_bone_meta ( bone_i , " extras " ) ) {
joint_node - > set_meta ( " extras " , p_skeleton3d - > get_bone_meta ( bone_i , " extras " ) ) ;
}
2022-12-10 21:05:13 +00:00
GLTFNodeIndex current_node_i = p_state - > nodes . size ( ) ;
p_state - > scene_nodes . insert ( current_node_i , skeleton ) ;
p_state - > nodes . push_back ( joint_node ) ;
2021-09-04 01:00:59 +00:00
gltf_skeleton - > joints . push_back ( current_node_i ) ;
if ( skeleton - > get_bone_parent ( bone_i ) = = - 1 ) {
gltf_skeleton - > roots . push_back ( current_node_i ) ;
}
gltf_skeleton - > godot_bone_node . insert ( bone_i , current_node_i ) ;
}
for ( BoneId bone_i = 0 ; bone_i < bone_count ; bone_i + + ) {
GLTFNodeIndex current_node_i = gltf_skeleton - > godot_bone_node [ bone_i ] ;
BoneId parent_bone_id = skeleton - > get_bone_parent ( bone_i ) ;
if ( parent_bone_id = = - 1 ) {
if ( p_parent_node_index ! = - 1 ) {
2022-12-10 21:05:13 +00:00
p_state - > nodes . write [ current_node_i ] - > parent = p_parent_node_index ;
p_state - > nodes . write [ p_parent_node_index ] - > children . push_back ( current_node_i ) ;
2021-09-04 01:00:59 +00:00
}
} else {
GLTFNodeIndex parent_node_i = gltf_skeleton - > godot_bone_node [ parent_bone_id ] ;
2022-12-10 21:05:13 +00:00
p_state - > nodes . write [ current_node_i ] - > parent = parent_node_i ;
p_state - > nodes . write [ parent_node_i ] - > children . push_back ( current_node_i ) ;
2020-12-21 15:39:32 +00:00
}
}
2021-09-04 01:00:59 +00:00
// Remove placeholder skeleton3d node by not creating the gltf node
// Skins are per mesh
for ( int node_i = 0 ; node_i < skeleton - > get_child_count ( ) ; node_i + + ) {
2022-12-10 21:05:13 +00:00
_convert_scene_node ( p_state , skeleton - > get_child ( node_i ) , p_parent_node_index , p_root_node_index ) ;
2021-09-04 01:00:59 +00:00
}
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _convert_bone_attachment_to_gltf ( BoneAttachment3D * p_bone_attachment , Ref < GLTFState > p_state , GLTFNodeIndex p_parent_node_index , GLTFNodeIndex p_root_node_index , Ref < GLTFNode > p_gltf_node ) {
2021-09-04 01:00:59 +00:00
Skeleton3D * skeleton ;
// Note that relative transforms to external skeletons and pose overrides are not supported.
if ( p_bone_attachment - > get_use_external_skeleton ( ) ) {
2024-07-10 09:02:03 +00:00
skeleton = Object : : cast_to < Skeleton3D > ( p_bone_attachment - > get_node_or_null ( p_bone_attachment - > get_external_skeleton ( ) ) ) ;
2021-09-04 01:00:59 +00:00
} else {
2024-07-10 09:02:03 +00:00
skeleton = Object : : cast_to < Skeleton3D > ( p_bone_attachment - > get_parent ( ) ) ;
2021-09-04 01:00:59 +00:00
}
GLTFSkeletonIndex skel_gltf_i = - 1 ;
2022-12-10 21:05:13 +00:00
if ( skeleton ! = nullptr & & p_state - > skeleton3d_to_gltf_skeleton . has ( skeleton - > get_instance_id ( ) ) ) {
skel_gltf_i = p_state - > skeleton3d_to_gltf_skeleton [ skeleton - > get_instance_id ( ) ] ;
2021-09-04 01:00:59 +00:00
}
int bone_idx = - 1 ;
if ( skeleton ! = nullptr ) {
bone_idx = p_bone_attachment - > get_bone_idx ( ) ;
if ( bone_idx = = - 1 ) {
bone_idx = skeleton - > find_bone ( p_bone_attachment - > get_bone_name ( ) ) ;
2020-12-21 15:39:32 +00:00
}
}
2021-09-04 01:00:59 +00:00
GLTFNodeIndex par_node_index = p_parent_node_index ;
if ( skeleton ! = nullptr & & bone_idx ! = - 1 & & skel_gltf_i ! = - 1 ) {
2022-12-10 21:05:13 +00:00
Ref < GLTFSkeleton > gltf_skeleton = p_state - > skeletons . write [ skel_gltf_i ] ;
2021-09-04 01:00:59 +00:00
gltf_skeleton - > bone_attachments . push_back ( p_bone_attachment ) ;
par_node_index = gltf_skeleton - > joints [ bone_idx ] ;
}
for ( int node_i = 0 ; node_i < p_bone_attachment - > get_child_count ( ) ; node_i + + ) {
2022-12-10 21:05:13 +00:00
_convert_scene_node ( p_state , p_bone_attachment - > get_child ( node_i ) , par_node_index , p_root_node_index ) ;
2021-09-04 01:00:59 +00:00
}
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _convert_mesh_instance_to_gltf ( MeshInstance3D * p_scene_parent , Ref < GLTFState > p_state , Ref < GLTFNode > p_gltf_node ) {
GLTFMeshIndex gltf_mesh_index = _convert_mesh_to_gltf ( p_state , p_scene_parent ) ;
2021-09-04 01:00:59 +00:00
if ( gltf_mesh_index ! = - 1 ) {
2022-12-10 21:05:13 +00:00
p_gltf_node - > mesh = gltf_mesh_index ;
2020-12-21 15:39:32 +00:00
}
}
2023-07-22 03:06:50 +00:00
void GLTFDocument : : _generate_scene_node ( Ref < GLTFState > p_state , const GLTFNodeIndex p_node_index , Node * p_scene_parent , Node * p_scene_root ) {
Ref < GLTFNode > gltf_node = p_state - > nodes [ p_node_index ] ;
2020-12-21 15:39:32 +00:00
2021-05-21 07:03:06 +00:00
if ( gltf_node - > skeleton > = 0 ) {
2023-07-22 03:06:50 +00:00
_generate_skeleton_bone_node ( p_state , p_node_index , p_scene_parent , p_scene_root ) ;
2021-05-21 07:03:06 +00:00
return ;
}
2020-12-21 15:39:32 +00:00
Node3D * current_node = nullptr ;
// Is our parent a skeleton
2023-07-22 03:06:50 +00:00
Skeleton3D * active_skeleton = Object : : cast_to < Skeleton3D > ( p_scene_parent ) ;
2020-12-21 15:39:32 +00:00
2021-05-21 07:03:06 +00:00
const bool non_bone_parented_to_skeleton = active_skeleton ;
2020-12-21 15:39:32 +00:00
2021-05-21 07:03:06 +00:00
// skinned meshes must not be placed in a bone attachment.
if ( non_bone_parented_to_skeleton & & gltf_node - > skin < 0 ) {
// Bone Attachment - Parent Case
2023-07-22 03:06:50 +00:00
BoneAttachment3D * bone_attachment = _generate_bone_attachment ( p_state , active_skeleton , p_node_index , gltf_node - > parent ) ;
2020-12-21 15:39:32 +00:00
2023-07-22 03:06:50 +00:00
p_scene_parent - > add_child ( bone_attachment , true ) ;
2023-09-28 04:32:32 +00:00
// Find the correct bone_idx so we can properly serialize it.
bone_attachment - > set_bone_idx ( active_skeleton - > find_bone ( gltf_node - > get_name ( ) ) ) ;
2023-07-22 03:06:50 +00:00
bone_attachment - > set_owner ( p_scene_root ) ;
2020-12-21 15:39:32 +00:00
// There is no gltf_node that represent this, so just directly create a unique name
2023-01-26 17:14:07 +00:00
bone_attachment - > set_name ( gltf_node - > get_name ( ) ) ;
2020-12-21 15:39:32 +00:00
// We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
// and attach it to the bone_attachment
2023-07-22 03:06:50 +00:00
p_scene_parent = bone_attachment ;
2020-12-21 15:39:32 +00:00
}
2022-09-17 22:48:06 +00:00
// Check if any GLTFDocumentExtension classes want to generate a node for us.
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
2023-07-22 03:06:50 +00:00
current_node = ext - > generate_scene_node ( p_state , gltf_node , p_scene_parent ) ;
2022-09-17 22:48:06 +00:00
if ( current_node ) {
break ;
}
2021-08-06 22:54:10 +00:00
}
2022-09-17 22:48:06 +00:00
// If none of our GLTFDocumentExtension classes generated us a node, we generate one.
2021-08-06 22:54:10 +00:00
if ( ! current_node ) {
2023-01-27 06:41:58 +00:00
if ( gltf_node - > skin > = 0 & & gltf_node - > mesh > = 0 & & ! gltf_node - > children . is_empty ( ) ) {
2024-08-16 01:07:30 +00:00
// glTF specifies that skinned meshes should ignore their node transforms,
2023-09-05 17:19:22 +00:00
// only being controlled by the skeleton, so Godot will reparent a skinned
// mesh to its skeleton. However, we still need to ensure any child nodes
// keep their place in the tree, so if there are any child nodes, the skinned
// mesh must not be the base node, so generate an empty spatial base.
2023-07-22 03:06:50 +00:00
current_node = _generate_spatial ( p_state , p_node_index ) ;
Node3D * mesh_inst = _generate_mesh_instance ( p_state , p_node_index ) ;
2023-01-27 06:41:58 +00:00
mesh_inst - > set_name ( gltf_node - > get_name ( ) ) ;
current_node - > add_child ( mesh_inst , true ) ;
} else if ( gltf_node - > mesh > = 0 ) {
2023-07-22 03:06:50 +00:00
current_node = _generate_mesh_instance ( p_state , p_node_index ) ;
2022-09-17 22:48:06 +00:00
} else if ( gltf_node - > camera > = 0 ) {
2023-07-22 03:06:50 +00:00
current_node = _generate_camera ( p_state , p_node_index ) ;
2022-09-17 22:48:06 +00:00
} else if ( gltf_node - > light > = 0 ) {
2023-07-22 03:06:50 +00:00
current_node = _generate_light ( p_state , p_node_index ) ;
2022-09-17 22:48:06 +00:00
} else {
2023-07-22 03:06:50 +00:00
current_node = _generate_spatial ( p_state , p_node_index ) ;
2022-09-17 22:48:06 +00:00
}
2021-05-21 07:03:06 +00:00
}
2023-07-22 02:59:39 +00:00
String gltf_node_name = gltf_node - > get_name ( ) ;
if ( ! gltf_node_name . is_empty ( ) ) {
current_node - > set_name ( gltf_node_name ) ;
}
2023-07-23 03:36:57 +00:00
// Note: p_scene_parent and p_scene_root must either both be null or both be valid.
if ( p_scene_root = = nullptr ) {
// If the root node argument is null, this is the root node.
p_scene_root = current_node ;
2024-08-04 09:23:01 +00:00
// If multiple nodes were generated under the root node, ensure they have the owner set.
if ( unlikely ( current_node - > get_child_count ( ) > 0 ) ) {
Array args ;
args . append ( p_scene_root ) ;
for ( int i = 0 ; i < current_node - > get_child_count ( ) ; i + + ) {
Node * child = current_node - > get_child ( i ) ;
child - > propagate_call ( StringName ( " set_owner " ) , args ) ;
}
}
2023-07-23 03:36:57 +00:00
} else {
// Add the node we generated and set the owner to the scene root.
p_scene_parent - > add_child ( current_node , true ) ;
2022-09-17 22:48:06 +00:00
Array args ;
2023-07-22 03:06:50 +00:00
args . append ( p_scene_root ) ;
2022-09-17 22:48:06 +00:00
current_node - > propagate_call ( StringName ( " set_owner " ) , args ) ;
2023-10-12 22:37:03 +00:00
current_node - > set_transform ( gltf_node - > transform ) ;
2021-05-21 07:03:06 +00:00
}
2024-01-11 19:47:31 +00:00
current_node - > merge_meta_from ( * gltf_node ) ;
2023-07-22 03:06:50 +00:00
p_state - > scene_nodes . insert ( p_node_index , current_node ) ;
2021-05-21 07:03:06 +00:00
for ( int i = 0 ; i < gltf_node - > children . size ( ) ; + + i ) {
2023-07-22 03:06:50 +00:00
_generate_scene_node ( p_state , gltf_node - > children [ i ] , current_node , p_scene_root ) ;
2021-05-21 07:03:06 +00:00
}
}
2023-07-22 03:06:50 +00:00
void GLTFDocument : : _generate_skeleton_bone_node ( Ref < GLTFState > p_state , const GLTFNodeIndex p_node_index , Node * p_scene_parent , Node * p_scene_root ) {
2022-12-10 21:05:13 +00:00
Ref < GLTFNode > gltf_node = p_state - > nodes [ p_node_index ] ;
2021-05-21 07:03:06 +00:00
Node3D * current_node = nullptr ;
2022-12-10 21:05:13 +00:00
Skeleton3D * skeleton = p_state - > skeletons [ gltf_node - > skeleton ] - > godot_skeleton ;
2021-05-21 07:03:06 +00:00
// In this case, this node is already a bone in skeleton.
const bool is_skinned_mesh = ( gltf_node - > skin > = 0 & & gltf_node - > mesh > = 0 ) ;
const bool requires_extra_node = ( gltf_node - > mesh > = 0 | | gltf_node - > camera > = 0 | | gltf_node - > light > = 0 ) ;
2022-12-10 21:05:13 +00:00
Skeleton3D * active_skeleton = Object : : cast_to < Skeleton3D > ( p_scene_parent ) ;
2021-05-21 07:03:06 +00:00
if ( active_skeleton ! = skeleton ) {
if ( active_skeleton ) {
2023-01-27 06:41:58 +00:00
// Should no longer be possible.
ERR_PRINT ( vformat ( " glTF: Generating scene detected direct parented Skeletons at node %d " , p_node_index ) ) ;
2022-12-10 21:05:13 +00:00
BoneAttachment3D * bone_attachment = _generate_bone_attachment ( p_state , active_skeleton , p_node_index , gltf_node - > parent ) ;
p_scene_parent - > add_child ( bone_attachment , true ) ;
bone_attachment - > set_owner ( p_scene_root ) ;
2021-05-21 07:03:06 +00:00
// There is no gltf_node that represent this, so just directly create a unique name
2022-12-10 21:05:13 +00:00
bone_attachment - > set_name ( _gen_unique_name ( p_state , " BoneAttachment3D " ) ) ;
2021-05-21 07:03:06 +00:00
// We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
// and attach it to the bone_attachment
2022-12-10 21:05:13 +00:00
p_scene_parent = bone_attachment ;
2021-05-21 07:03:06 +00:00
}
if ( skeleton - > get_parent ( ) = = nullptr ) {
2024-02-04 08:47:44 +00:00
if ( p_scene_root ) {
p_scene_parent - > add_child ( skeleton , true ) ;
skeleton - > set_owner ( p_scene_root ) ;
} else {
p_scene_parent = skeleton ;
p_scene_root = skeleton ;
}
2021-05-21 07:03:06 +00:00
}
}
active_skeleton = skeleton ;
2023-01-27 06:41:58 +00:00
current_node = active_skeleton ;
2024-04-17 11:11:16 +00:00
if ( active_skeleton ) {
p_scene_parent = active_skeleton ;
}
2021-05-21 07:03:06 +00:00
if ( requires_extra_node ) {
2023-01-27 06:41:58 +00:00
current_node = nullptr ;
2021-05-21 07:03:06 +00:00
// skinned meshes must not be placed in a bone attachment.
if ( ! is_skinned_mesh ) {
// Bone Attachment - Same Node Case
2022-12-10 21:05:13 +00:00
BoneAttachment3D * bone_attachment = _generate_bone_attachment ( p_state , active_skeleton , p_node_index , p_node_index ) ;
2021-05-21 07:03:06 +00:00
2022-12-10 21:05:13 +00:00
p_scene_parent - > add_child ( bone_attachment , true ) ;
2023-09-28 04:32:32 +00:00
// Find the correct bone_idx so we can properly serialize it.
bone_attachment - > set_bone_idx ( active_skeleton - > find_bone ( gltf_node - > get_name ( ) ) ) ;
2022-12-10 21:05:13 +00:00
bone_attachment - > set_owner ( p_scene_root ) ;
2021-05-21 07:03:06 +00:00
// There is no gltf_node that represent this, so just directly create a unique name
2023-01-26 17:14:07 +00:00
bone_attachment - > set_name ( gltf_node - > get_name ( ) ) ;
2021-05-21 07:03:06 +00:00
// We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
// and attach it to the bone_attachment
2022-12-10 21:05:13 +00:00
p_scene_parent = bone_attachment ;
2021-05-21 07:03:06 +00:00
}
2022-09-17 22:48:06 +00:00
// Check if any GLTFDocumentExtension classes want to generate a node for us.
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
2022-12-10 21:05:13 +00:00
current_node = ext - > generate_scene_node ( p_state , gltf_node , p_scene_parent ) ;
2022-09-17 22:48:06 +00:00
if ( current_node ) {
break ;
}
2020-12-21 15:39:32 +00:00
}
2022-09-17 22:48:06 +00:00
// If none of our GLTFDocumentExtension classes generated us a node, we generate one.
if ( ! current_node ) {
if ( gltf_node - > mesh > = 0 ) {
2022-12-10 21:05:13 +00:00
current_node = _generate_mesh_instance ( p_state , p_node_index ) ;
2022-09-17 22:48:06 +00:00
} else if ( gltf_node - > camera > = 0 ) {
2022-12-10 21:05:13 +00:00
current_node = _generate_camera ( p_state , p_node_index ) ;
2022-09-17 22:48:06 +00:00
} else if ( gltf_node - > light > = 0 ) {
2022-12-10 21:05:13 +00:00
current_node = _generate_light ( p_state , p_node_index ) ;
2022-09-17 22:48:06 +00:00
} else {
2022-12-10 21:05:13 +00:00
current_node = _generate_spatial ( p_state , p_node_index ) ;
2022-09-17 22:48:06 +00:00
}
}
// Add the node we generated and set the owner to the scene root.
2022-12-10 21:05:13 +00:00
p_scene_parent - > add_child ( current_node , true ) ;
if ( current_node ! = p_scene_root ) {
2022-09-17 22:48:06 +00:00
Array args ;
2022-12-10 21:05:13 +00:00
args . append ( p_scene_root ) ;
2022-09-17 22:48:06 +00:00
current_node - > propagate_call ( StringName ( " set_owner " ) , args ) ;
2020-12-21 15:39:32 +00:00
}
2021-05-21 07:03:06 +00:00
// Do not set transform here. Transform is already applied to our bone.
2020-12-21 15:39:32 +00:00
current_node - > set_name ( gltf_node - > get_name ( ) ) ;
}
2022-12-10 21:05:13 +00:00
p_state - > scene_nodes . insert ( p_node_index , current_node ) ;
2020-12-21 15:39:32 +00:00
for ( int i = 0 ; i < gltf_node - > children . size ( ) ; + + i ) {
2023-07-22 03:06:50 +00:00
_generate_scene_node ( p_state , gltf_node - > children [ i ] , active_skeleton , p_scene_root ) ;
2020-12-21 15:39:32 +00:00
}
}
template < typename T >
2022-03-28 13:39:24 +00:00
struct SceneFormatImporterGLTFInterpolate {
2020-12-21 15:39:32 +00:00
T lerp ( const T & a , const T & b , float c ) const {
return a + ( b - a ) * c ;
}
T catmull_rom ( const T & p0 , const T & p1 , const T & p2 , const T & p3 , float t ) {
const float t2 = t * t ;
const float t3 = t2 * t ;
return 0.5f * ( ( 2.0f * p1 ) + ( - p0 + p2 ) * t + ( 2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3 ) * t2 + ( - p0 + 3.0f * p1 - 3.0f * p2 + p3 ) * t3 ) ;
}
2024-06-25 15:01:32 +00:00
T hermite ( T start , T tan_start , T end , T tan_end , float t ) {
/* Formula from the glTF 2.0 specification. */
2020-12-21 15:39:32 +00:00
const real_t t2 = t * t ;
const real_t t3 = t2 * t ;
2024-06-25 15:01:32 +00:00
const real_t h00 = 2.0 * t3 - 3.0 * t2 + 1.0 ;
const real_t h10 = t3 - 2.0 * t2 + t ;
const real_t h01 = - 2.0 * t3 + 3.0 * t2 ;
const real_t h11 = t3 - t2 ;
return start * h00 + tan_start * h10 + end * h01 + tan_end * h11 ;
2020-12-21 15:39:32 +00:00
}
} ;
// thank you for existing, partial specialization
template < >
2022-03-28 13:39:24 +00:00
struct SceneFormatImporterGLTFInterpolate < Quaternion > {
2021-01-20 07:02:02 +00:00
Quaternion lerp ( const Quaternion & a , const Quaternion & b , const float c ) const {
2024-01-17 16:30:04 +00:00
ERR_FAIL_COND_V_MSG ( ! a . is_normalized ( ) , Quaternion ( ) , vformat ( " The quaternion \" a \" %s must be normalized. " , a ) ) ;
ERR_FAIL_COND_V_MSG ( ! b . is_normalized ( ) , Quaternion ( ) , vformat ( " The quaternion \" b \" %s must be normalized. " , b ) ) ;
2020-12-21 15:39:32 +00:00
return a . slerp ( b , c ) . normalized ( ) ;
}
2021-01-20 07:02:02 +00:00
Quaternion catmull_rom ( const Quaternion & p0 , const Quaternion & p1 , const Quaternion & p2 , const Quaternion & p3 , const float c ) {
2024-01-17 16:30:04 +00:00
ERR_FAIL_COND_V_MSG ( ! p1 . is_normalized ( ) , Quaternion ( ) , vformat ( " The quaternion \" p1 \" (%s) must be normalized. " , p1 ) ) ;
ERR_FAIL_COND_V_MSG ( ! p2 . is_normalized ( ) , Quaternion ( ) , vformat ( " The quaternion \" p2 \" (%s) must be normalized. " , p2 ) ) ;
2020-12-21 15:39:32 +00:00
return p1 . slerp ( p2 , c ) . normalized ( ) ;
}
2024-06-25 15:01:32 +00:00
Quaternion hermite ( const Quaternion start , const Quaternion tan_start , const Quaternion end , const Quaternion tan_end , const float t ) {
2024-01-17 16:30:04 +00:00
ERR_FAIL_COND_V_MSG ( ! start . is_normalized ( ) , Quaternion ( ) , vformat ( " The start quaternion %s must be normalized. " , start ) ) ;
ERR_FAIL_COND_V_MSG ( ! end . is_normalized ( ) , Quaternion ( ) , vformat ( " The end quaternion %s must be normalized. " , end ) ) ;
2020-12-21 15:39:32 +00:00
return start . slerp ( end , t ) . normalized ( ) ;
}
} ;
template < typename T >
2024-07-10 08:26:35 +00:00
T GLTFDocument : : _interpolate_track ( const Vector < double > & p_times , const Vector < T > & p_values , const float p_time , const GLTFAnimation : : Interpolation p_interp ) {
2024-01-19 12:21:39 +00:00
ERR_FAIL_COND_V ( p_values . is_empty ( ) , T ( ) ) ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
if ( p_times . size ( ) ! = ( p_values . size ( ) / ( p_interp = = GLTFAnimation : : INTERP_CUBIC_SPLINE ? 3 : 1 ) ) ) {
2021-07-30 05:56:19 +00:00
ERR_PRINT_ONCE ( " The interpolated values are not corresponding to its times. " ) ;
return p_values [ 0 ] ;
}
2020-12-21 15:39:32 +00:00
//could use binary search, worth it?
int idx = - 1 ;
for ( int i = 0 ; i < p_times . size ( ) ; i + + ) {
2021-04-05 12:09:59 +00:00
if ( p_times [ i ] > p_time ) {
2020-12-21 15:39:32 +00:00
break ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
idx + + ;
}
2022-03-28 13:39:24 +00:00
SceneFormatImporterGLTFInterpolate < T > interp ;
2020-12-21 15:39:32 +00:00
switch ( p_interp ) {
case GLTFAnimation : : INTERP_LINEAR : {
if ( idx = = - 1 ) {
return p_values [ 0 ] ;
} else if ( idx > = p_times . size ( ) - 1 ) {
return p_values [ p_times . size ( ) - 1 ] ;
}
const float c = ( p_time - p_times [ idx ] ) / ( p_times [ idx + 1 ] - p_times [ idx ] ) ;
return interp . lerp ( p_values [ idx ] , p_values [ idx + 1 ] , c ) ;
} break ;
case GLTFAnimation : : INTERP_STEP : {
if ( idx = = - 1 ) {
return p_values [ 0 ] ;
} else if ( idx > = p_times . size ( ) - 1 ) {
return p_values [ p_times . size ( ) - 1 ] ;
}
return p_values [ idx ] ;
} break ;
case GLTFAnimation : : INTERP_CATMULLROMSPLINE : {
if ( idx = = - 1 ) {
return p_values [ 1 ] ;
} else if ( idx > = p_times . size ( ) - 1 ) {
return p_values [ 1 + p_times . size ( ) - 1 ] ;
}
const float c = ( p_time - p_times [ idx ] ) / ( p_times [ idx + 1 ] - p_times [ idx ] ) ;
return interp . catmull_rom ( p_values [ idx - 1 ] , p_values [ idx ] , p_values [ idx + 1 ] , p_values [ idx + 3 ] , c ) ;
} break ;
case GLTFAnimation : : INTERP_CUBIC_SPLINE : {
if ( idx = = - 1 ) {
return p_values [ 1 ] ;
} else if ( idx > = p_times . size ( ) - 1 ) {
return p_values [ ( p_times . size ( ) - 1 ) * 3 + 1 ] ;
}
2024-06-25 15:01:32 +00:00
const float td = ( p_times [ idx + 1 ] - p_times [ idx ] ) ;
const float c = ( p_time - p_times [ idx ] ) / td ;
2020-12-21 15:39:32 +00:00
2023-11-18 22:40:56 +00:00
const T & from = p_values [ idx * 3 + 1 ] ;
2024-06-25 15:01:32 +00:00
const T tan_from = td * p_values [ idx * 3 + 2 ] ;
2023-11-18 22:40:56 +00:00
const T & to = p_values [ idx * 3 + 4 ] ;
2024-06-25 15:01:32 +00:00
const T tan_to = td * p_values [ idx * 3 + 3 ] ;
2020-12-21 15:39:32 +00:00
2024-06-25 15:01:32 +00:00
return interp . hermite ( from , tan_from , to , tan_to , c ) ;
2020-12-21 15:39:32 +00:00
} break ;
}
ERR_FAIL_V ( p_values [ 0 ] ) ;
}
2024-10-04 07:19:12 +00:00
NodePath GLTFDocument : : _find_material_node_path ( Ref < GLTFState > p_state , Ref < Material > p_material ) {
int mesh_index = 0 ;
for ( Ref < GLTFMesh > gltf_mesh : p_state - > meshes ) {
TypedArray < Material > materials = gltf_mesh - > get_instance_materials ( ) ;
for ( int mat_index = 0 ; mat_index < materials . size ( ) ; mat_index + + ) {
if ( materials [ mat_index ] = = p_material ) {
for ( Ref < GLTFNode > gltf_node : p_state - > nodes ) {
if ( gltf_node - > mesh = = mesh_index ) {
NodePath node_path = gltf_node - > get_scene_node_path ( p_state ) ;
// Example: MyNode:mesh:surface_0/material:albedo_color, so we want the mesh:surface_0/material part.
Vector < StringName > subpath ;
subpath . append ( " mesh " ) ;
subpath . append ( " surface_ " + itos ( mat_index ) + " /material " ) ;
return NodePath ( node_path . get_names ( ) , subpath , false ) ;
}
}
}
}
mesh_index + + ;
}
return NodePath ( ) ;
}
Ref < GLTFObjectModelProperty > GLTFDocument : : import_object_model_property ( Ref < GLTFState > p_state , const String & p_json_pointer ) {
if ( p_state - > object_model_properties . has ( p_json_pointer ) ) {
return p_state - > object_model_properties [ p_json_pointer ] ;
}
Ref < GLTFObjectModelProperty > ret ;
// Split the JSON pointer into its components.
const PackedStringArray split = p_json_pointer . split ( " / " , false ) ;
ERR_FAIL_COND_V_MSG ( split . size ( ) < 3 , ret , " glTF: Cannot use JSON pointer ' " + p_json_pointer + " ' because it does not contain enough elements. The only animatable properties are at least 3 levels deep (ex: '/nodes/0/translation' or '/materials/0/emissiveFactor'). " ) ;
ret . instantiate ( ) ;
ret - > set_json_pointers ( { split } ) ;
// Partial paths are passed to GLTFDocumentExtension classes if GLTFDocument cannot handle a given JSON pointer.
TypedArray < NodePath > partial_paths ;
// Note: This might not be an integer, but in that case, we don't use this value anyway.
const int top_level_index = split [ 1 ] . to_int ( ) ;
// For JSON pointers present in the core glTF Object Model, hard-code them in GLTFDocument.
// https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/ObjectModel.adoc
if ( split [ 0 ] = = " nodes " ) {
ERR_FAIL_INDEX_V_MSG ( top_level_index , p_state - > nodes . size ( ) , ret , vformat ( " glTF: Unable to find node %d for JSON pointer '%s'. " , top_level_index , p_json_pointer ) ) ;
Ref < GLTFNode > pointed_gltf_node = p_state - > nodes [ top_level_index ] ;
NodePath node_path = pointed_gltf_node - > get_scene_node_path ( p_state ) ;
partial_paths . append ( node_path ) ;
// Check if it's something we should be able to handle.
const String & node_prop = split [ 2 ] ;
if ( node_prop = = " translation " ) {
ret - > append_path_to_property ( node_path , " position " ) ;
ret - > set_types ( Variant : : VECTOR3 , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT3 ) ;
} else if ( node_prop = = " rotation " ) {
ret - > append_path_to_property ( node_path , " quaternion " ) ;
ret - > set_types ( Variant : : QUATERNION , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT4 ) ;
} else if ( node_prop = = " scale " ) {
ret - > append_path_to_property ( node_path , " scale " ) ;
ret - > set_types ( Variant : : VECTOR3 , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT3 ) ;
} else if ( node_prop = = " matrix " ) {
ret - > append_path_to_property ( node_path , " transform " ) ;
ret - > set_types ( Variant : : TRANSFORM3D , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT4X4 ) ;
} else if ( node_prop = = " globalMatrix " ) {
ret - > append_path_to_property ( node_path , " global_transform " ) ;
ret - > set_types ( Variant : : TRANSFORM3D , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT4X4 ) ;
} else if ( node_prop = = " weights " ) {
if ( split . size ( ) > 3 ) {
const String & weight_index_string = split [ 3 ] ;
ret - > append_path_to_property ( node_path , " blend_shapes/morph_ " + weight_index_string ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
}
// Else, Godot's MeshInstance3D does not expose the blend shape weights as one property.
// But that's fine, we handle this case in _parse_animation_pointer instead.
}
} else if ( split [ 0 ] = = " cameras " ) {
const String & camera_prop = split [ 2 ] ;
for ( Ref < GLTFNode > gltf_node : p_state - > nodes ) {
if ( gltf_node - > camera = = top_level_index ) {
NodePath node_path = gltf_node - > get_scene_node_path ( p_state ) ;
partial_paths . append ( node_path ) ;
// Check if it's something we should be able to handle.
if ( camera_prop = = " orthographic " | | camera_prop = = " perspective " ) {
ERR_FAIL_COND_V ( split . size ( ) < 4 , ret ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
const String & sub_prop = split [ 3 ] ;
if ( sub_prop = = " xmag " | | sub_prop = = " ymag " ) {
ret - > append_path_to_property ( node_path , " size " ) ;
} else if ( sub_prop = = " yfov " ) {
ret - > append_path_to_property ( node_path , " fov " ) ;
GLTFCamera : : set_fov_conversion_expressions ( ret ) ;
} else if ( sub_prop = = " zfar " ) {
ret - > append_path_to_property ( node_path , " far " ) ;
} else if ( sub_prop = = " znear " ) {
ret - > append_path_to_property ( node_path , " near " ) ;
}
}
}
}
} else if ( split [ 0 ] = = " materials " ) {
ERR_FAIL_INDEX_V_MSG ( top_level_index , p_state - > materials . size ( ) , ret , vformat ( " glTF: Unable to find material %d for JSON pointer '%s'. " , top_level_index , p_json_pointer ) ) ;
Ref < Material > pointed_material = p_state - > materials [ top_level_index ] ;
NodePath mat_path = _find_material_node_path ( p_state , pointed_material ) ;
if ( mat_path . is_empty ( ) ) {
WARN_PRINT ( vformat ( " glTF: Unable to find a path to the material %d for JSON pointer '%s'. This is likely bad data but it's also possible this is intentional. Continuing anyway. " , top_level_index , p_json_pointer ) ) ;
} else {
partial_paths . append ( mat_path ) ;
const String & mat_prop = split [ 2 ] ;
if ( mat_prop = = " alphaCutoff " ) {
ret - > append_path_to_property ( mat_path , " alpha_scissor_threshold " ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
} else if ( mat_prop = = " emissiveFactor " ) {
ret - > append_path_to_property ( mat_path , " emission " ) ;
ret - > set_types ( Variant : : COLOR , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT3 ) ;
} else if ( mat_prop = = " extensions " ) {
ERR_FAIL_COND_V ( split . size ( ) < 5 , ret ) ;
const String & ext_name = split [ 3 ] ;
const String & ext_prop = split [ 4 ] ;
if ( ext_name = = " KHR_materials_emissive_strength " & & ext_prop = = " emissiveStrength " ) {
ret - > append_path_to_property ( mat_path , " emission_energy_multiplier " ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
}
} else {
ERR_FAIL_COND_V ( split . size ( ) < 4 , ret ) ;
const String & sub_prop = split [ 3 ] ;
if ( mat_prop = = " normalTexture " ) {
if ( sub_prop = = " scale " ) {
ret - > append_path_to_property ( mat_path , " normal_scale " ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
}
} else if ( mat_prop = = " occlusionTexture " ) {
if ( sub_prop = = " strength " ) {
// This is the closest thing Godot has to an occlusion strength property.
ret - > append_path_to_property ( mat_path , " ao_light_affect " ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
}
} else if ( mat_prop = = " pbrMetallicRoughness " ) {
if ( sub_prop = = " baseColorFactor " ) {
ret - > append_path_to_property ( mat_path , " albedo_color " ) ;
ret - > set_types ( Variant : : COLOR , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT4 ) ;
} else if ( sub_prop = = " metallicFactor " ) {
ret - > append_path_to_property ( mat_path , " metallic " ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
} else if ( sub_prop = = " roughnessFactor " ) {
ret - > append_path_to_property ( mat_path , " roughness " ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
} else if ( sub_prop = = " baseColorTexture " ) {
ERR_FAIL_COND_V ( split . size ( ) < 6 , ret ) ;
const String & tex_ext_dict = split [ 4 ] ;
const String & tex_ext_name = split [ 5 ] ;
const String & tex_ext_prop = split [ 6 ] ;
if ( tex_ext_dict = = " extensions " & & tex_ext_name = = " KHR_texture_transform " ) {
// Godot only supports UVs for the whole material, not per texture.
// We treat the albedo texture as the main texture, and import as UV1.
// Godot does not support texture rotation, only offset and scale.
if ( tex_ext_prop = = " offset " ) {
ret - > append_path_to_property ( mat_path , " uv1_offset " ) ;
ret - > set_types ( Variant : : VECTOR3 , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT2 ) ;
} else if ( tex_ext_prop = = " scale " ) {
ret - > append_path_to_property ( mat_path , " uv1_scale " ) ;
ret - > set_types ( Variant : : VECTOR3 , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT2 ) ;
}
}
}
}
}
}
} else if ( split [ 0 ] = = " meshes " ) {
for ( Ref < GLTFNode > gltf_node : p_state - > nodes ) {
if ( gltf_node - > mesh = = top_level_index ) {
NodePath node_path = gltf_node - > get_scene_node_path ( p_state ) ;
Vector < StringName > subpath ;
subpath . append ( " mesh " ) ;
partial_paths . append ( NodePath ( node_path . get_names ( ) , subpath , false ) ) ;
break ;
}
}
} else if ( split [ 0 ] = = " extensions " ) {
if ( split [ 1 ] = = " KHR_lights_punctual " & & split [ 2 ] = = " lights " & & split . size ( ) > 4 ) {
const int light_index = split [ 3 ] . to_int ( ) ;
ERR_FAIL_INDEX_V_MSG ( light_index , p_state - > lights . size ( ) , ret , vformat ( " glTF: Unable to find light %d for JSON pointer '%s'. " , light_index , p_json_pointer ) ) ;
const String & light_prop = split [ 4 ] ;
const Ref < GLTFLight > pointed_light = p_state - > lights [ light_index ] ;
for ( Ref < GLTFNode > gltf_node : p_state - > nodes ) {
if ( gltf_node - > light = = light_index ) {
NodePath node_path = gltf_node - > get_scene_node_path ( p_state ) ;
partial_paths . append ( node_path ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
// Check if it's something we should be able to handle.
if ( light_prop = = " color " ) {
ret - > append_path_to_property ( node_path , " light_color " ) ;
ret - > set_types ( Variant : : COLOR , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT3 ) ;
} else if ( light_prop = = " intensity " ) {
ret - > append_path_to_property ( node_path , " light_energy " ) ;
} else if ( light_prop = = " range " ) {
const String & light_type = p_state - > lights [ light_index ] - > light_type ;
if ( light_type = = " spot " ) {
ret - > append_path_to_property ( node_path , " spot_range " ) ;
} else {
ret - > append_path_to_property ( node_path , " omni_range " ) ;
}
} else if ( light_prop = = " spot " ) {
ERR_FAIL_COND_V ( split . size ( ) < 6 , ret ) ;
const String & sub_prop = split [ 5 ] ;
if ( sub_prop = = " innerConeAngle " ) {
ret - > append_path_to_property ( node_path , " spot_angle_attenuation " ) ;
GLTFLight : : set_cone_inner_attenuation_conversion_expressions ( ret ) ;
} else if ( sub_prop = = " outerConeAngle " ) {
ret - > append_path_to_property ( node_path , " spot_angle " ) ;
}
}
}
}
}
}
// Additional JSON pointers can be added by GLTFDocumentExtension classes.
// We only need this if no mapping has been found yet from GLTFDocument's internal code.
// When available, we pass the partial paths to the extension to help it generate the full path.
// For example, for `/nodes/3/extensions/MY_ext/prop`, we pass a NodePath that leads to node 3,
// so the GLTFDocumentExtension class only needs to resolve the last `MY_ext/prop` part of the path.
// It should check `split.size() > 4 and split[0] == "nodes" and split[2] == "extensions" and split[3] == "MY_ext"`
// at the start of the function to check if this JSON pointer applies to it, then it can handle `split[4]`.
if ( ! ret - > has_node_paths ( ) ) {
for ( Ref < GLTFDocumentExtension > ext : all_document_extensions ) {
ret = ext - > import_object_model_property ( p_state , split , partial_paths ) ;
if ( ret . is_valid ( ) & & ret - > has_node_paths ( ) ) {
if ( ! ret - > has_json_pointers ( ) ) {
ret - > set_json_pointers ( { split } ) ;
}
break ;
}
}
if ( ret . is_null ( ) | | ! ret - > has_node_paths ( ) ) {
if ( split . has ( " KHR_texture_transform " ) ) {
WARN_VERBOSE ( vformat ( " glTF: Texture transforms are only supported per material in Godot. All KHR_texture_transform properties will be ignored except for the albedo texture. Ignoring JSON pointer '%s'. " , p_json_pointer ) ) ;
} else {
WARN_PRINT ( vformat ( " glTF: Animation contained JSON pointer '%s' which could not be resolved. This property will not be animated. " , p_json_pointer ) ) ;
}
}
}
p_state - > object_model_properties [ p_json_pointer ] = ret ;
return ret ;
}
Ref < GLTFObjectModelProperty > GLTFDocument : : export_object_model_property ( Ref < GLTFState > p_state , const NodePath & p_node_path , const Node * p_godot_node , GLTFNodeIndex p_gltf_node_index ) {
Ref < GLTFObjectModelProperty > ret ;
const Object * target_object = p_godot_node ;
const Vector < StringName > subpath = p_node_path . get_subnames ( ) ;
ERR_FAIL_COND_V_MSG ( subpath . is_empty ( ) , ret , " glTF: Cannot export empty property. No property was specified in the NodePath: " + p_node_path ) ;
int target_prop_depth = 0 ;
for ( StringName subname : subpath ) {
Variant target_property = target_object - > get ( subname ) ;
if ( target_property . get_type ( ) = = Variant : : OBJECT ) {
target_object = target_property ;
if ( target_object ) {
target_prop_depth + + ;
continue ;
}
}
break ;
}
const String & target_prop = subpath [ target_prop_depth ] ;
ret . instantiate ( ) ;
ret - > set_node_paths ( { p_node_path } ) ;
Vector < PackedStringArray > split_json_pointers ;
PackedStringArray split_json_pointer ;
if ( Object : : cast_to < BaseMaterial3D > ( target_object ) ) {
for ( int i = 0 ; i < p_state - > materials . size ( ) ; i + + ) {
if ( p_state - > materials [ i ] . ptr ( ) = = target_object ) {
split_json_pointer . append ( " materials " ) ;
split_json_pointer . append ( itos ( i ) ) ;
if ( target_prop = = " alpha_scissor_threshold " ) {
split_json_pointer . append ( " alphaCutoff " ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
} else if ( target_prop = = " emission " ) {
split_json_pointer . append ( " emissiveFactor " ) ;
ret - > set_types ( Variant : : COLOR , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT3 ) ;
} else if ( target_prop = = " emission_energy_multiplier " ) {
split_json_pointer . append ( " extensions " ) ;
split_json_pointer . append ( " KHR_materials_emissive_strength " ) ;
split_json_pointer . append ( " emissiveStrength " ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
} else if ( target_prop = = " normal_scale " ) {
split_json_pointer . append ( " normalTexture " ) ;
split_json_pointer . append ( " scale " ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
} else if ( target_prop = = " ao_light_affect " ) {
split_json_pointer . append ( " occlusionTexture " ) ;
split_json_pointer . append ( " strength " ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
} else if ( target_prop = = " albedo_color " ) {
split_json_pointer . append ( " pbrMetallicRoughness " ) ;
split_json_pointer . append ( " baseColorFactor " ) ;
ret - > set_types ( Variant : : COLOR , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT4 ) ;
} else if ( target_prop = = " metallic " ) {
split_json_pointer . append ( " pbrMetallicRoughness " ) ;
split_json_pointer . append ( " metallicFactor " ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
} else if ( target_prop = = " roughness " ) {
split_json_pointer . append ( " pbrMetallicRoughness " ) ;
split_json_pointer . append ( " roughnessFactor " ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
} else if ( target_prop = = " uv1_offset " | | target_prop = = " uv1_scale " ) {
split_json_pointer . append ( " pbrMetallicRoughness " ) ;
split_json_pointer . append ( " baseColorTexture " ) ;
split_json_pointer . append ( " extensions " ) ;
split_json_pointer . append ( " KHR_texture_transform " ) ;
if ( target_prop = = " uv1_offset " ) {
split_json_pointer . append ( " offset " ) ;
} else {
split_json_pointer . append ( " scale " ) ;
}
ret - > set_types ( Variant : : VECTOR3 , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT2 ) ;
} else {
split_json_pointer . clear ( ) ;
}
break ;
}
}
} else {
// Properties directly on Godot nodes.
Ref < GLTFNode > gltf_node = p_state - > nodes [ p_gltf_node_index ] ;
if ( Object : : cast_to < Camera3D > ( target_object ) & & gltf_node - > camera > = 0 ) {
split_json_pointer . append ( " cameras " ) ;
split_json_pointer . append ( itos ( gltf_node - > camera ) ) ;
const Camera3D * camera_node = Object : : cast_to < Camera3D > ( target_object ) ;
const Camera3D : : ProjectionType projection_type = camera_node - > get_projection ( ) ;
if ( projection_type = = Camera3D : : PROJECTION_PERSPECTIVE ) {
split_json_pointer . append ( " perspective " ) ;
} else {
split_json_pointer . append ( " orthographic " ) ;
}
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
if ( target_prop = = " size " ) {
PackedStringArray xmag = split_json_pointer . duplicate ( ) ;
xmag . append ( " xmag " ) ;
split_json_pointers . append ( xmag ) ;
split_json_pointer . append ( " ymag " ) ;
} else if ( target_prop = = " fov " ) {
split_json_pointer . append ( " yfov " ) ;
GLTFCamera : : set_fov_conversion_expressions ( ret ) ;
} else if ( target_prop = = " far " ) {
split_json_pointer . append ( " zfar " ) ;
} else if ( target_prop = = " near " ) {
split_json_pointer . append ( " znear " ) ;
} else {
split_json_pointer . clear ( ) ;
}
} else if ( Object : : cast_to < Light3D > ( target_object ) & & gltf_node - > light > = 0 ) {
split_json_pointer . append ( " extensions " ) ;
split_json_pointer . append ( " KHR_lights_punctual " ) ;
split_json_pointer . append ( " lights " ) ;
split_json_pointer . append ( itos ( gltf_node - > light ) ) ;
ret - > set_types ( Variant : : FLOAT , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT ) ;
if ( target_prop = = " light_color " ) {
split_json_pointer . append ( " color " ) ;
ret - > set_types ( Variant : : COLOR , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT3 ) ;
} else if ( target_prop = = " light_energy " ) {
split_json_pointer . append ( " intensity " ) ;
} else if ( target_prop = = " spot_range " ) {
split_json_pointer . append ( " range " ) ;
} else if ( target_prop = = " omni_range " ) {
split_json_pointer . append ( " range " ) ;
} else if ( target_prop = = " spot_angle " ) {
split_json_pointer . append ( " spot " ) ;
split_json_pointer . append ( " outerConeAngle " ) ;
} else if ( target_prop = = " spot_angle_attenuation " ) {
split_json_pointer . append ( " spot " ) ;
split_json_pointer . append ( " innerConeAngle " ) ;
GLTFLight : : set_cone_inner_attenuation_conversion_expressions ( ret ) ;
} else {
split_json_pointer . clear ( ) ;
}
} else if ( Object : : cast_to < MeshInstance3D > ( target_object ) & & target_prop . begins_with ( " blend_shapes/morph_ " ) ) {
const String & weight_index_string = target_prop . trim_prefix ( " blend_shapes/morph_ " ) ;
split_json_pointer . append ( " nodes " ) ;
split_json_pointer . append ( itos ( p_gltf_node_index ) ) ;
split_json_pointer . append ( " weights " ) ;
split_json_pointer . append ( weight_index_string ) ;
}
// Transform properties. Check for all 3D nodes if we haven't resolved the JSON pointer yet.
// Note: Do not put this in an `else`, because otherwise this will not be reached.
if ( split_json_pointer . is_empty ( ) & & Object : : cast_to < Node3D > ( target_object ) ) {
split_json_pointer . append ( " nodes " ) ;
split_json_pointer . append ( itos ( p_gltf_node_index ) ) ;
if ( target_prop = = " position " ) {
split_json_pointer . append ( " translation " ) ;
ret - > set_types ( Variant : : VECTOR3 , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT3 ) ;
} else if ( target_prop = = " quaternion " ) {
// Note: Only Quaternion rotation can be converted from Godot in this mapping.
// Struct methods like from_euler are not accessible from the Expression class. :(
split_json_pointer . append ( " rotation " ) ;
ret - > set_types ( Variant : : QUATERNION , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT4 ) ;
} else if ( target_prop = = " scale " ) {
split_json_pointer . append ( " scale " ) ;
ret - > set_types ( Variant : : VECTOR3 , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT3 ) ;
} else if ( target_prop = = " transform " ) {
split_json_pointer . append ( " matrix " ) ;
ret - > set_types ( Variant : : TRANSFORM3D , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT4X4 ) ;
} else if ( target_prop = = " global_transform " ) {
split_json_pointer . append ( " globalMatrix " ) ;
ret - > set_types ( Variant : : TRANSFORM3D , GLTFObjectModelProperty : : GLTF_OBJECT_MODEL_TYPE_FLOAT4X4 ) ;
} else {
split_json_pointer . clear ( ) ;
}
}
}
// Additional JSON pointers can be added by GLTFDocumentExtension classes.
// We only need this if no mapping has been found yet from GLTFDocument's internal code.
// We pass as many pieces of information as we can to the extension to give it lots of context.
if ( split_json_pointer . is_empty ( ) ) {
for ( Ref < GLTFDocumentExtension > ext : all_document_extensions ) {
ret = ext - > export_object_model_property ( p_state , p_node_path , p_godot_node , p_gltf_node_index , target_object , target_prop_depth ) ;
if ( ret . is_valid ( ) & & ret - > has_json_pointers ( ) ) {
if ( ! ret - > has_node_paths ( ) ) {
ret - > set_node_paths ( { p_node_path } ) ;
}
break ;
}
}
} else {
// GLTFDocument's internal code found a mapping, so set it and return it.
split_json_pointers . append ( split_json_pointer ) ;
ret - > set_json_pointers ( split_json_pointers ) ;
}
return ret ;
}
2024-05-23 15:40:26 +00:00
void GLTFDocument : : _import_animation ( Ref < GLTFState > p_state , AnimationPlayer * p_animation_player , const GLTFAnimationIndex p_index , const bool p_trimming , const bool p_remove_immutable_tracks ) {
ERR_FAIL_COND ( p_state . is_null ( ) ) ;
2024-07-10 08:26:35 +00:00
Node * scene_root = p_animation_player - > get_parent ( ) ;
ERR_FAIL_NULL ( scene_root ) ;
2022-12-10 21:05:13 +00:00
Ref < GLTFAnimation > anim = p_state - > animations [ p_index ] ;
2020-12-21 15:39:32 +00:00
2022-09-29 09:53:28 +00:00
String anim_name = anim - > get_name ( ) ;
if ( anim_name . is_empty ( ) ) {
2020-12-21 15:39:32 +00:00
// No node represent these, and they are not in the hierarchy, so just make a unique name
2022-12-10 21:05:13 +00:00
anim_name = _gen_unique_name ( p_state , " Animation " ) ;
2020-12-21 15:39:32 +00:00
}
Ref < Animation > animation ;
2021-06-17 22:03:09 +00:00
animation . instantiate ( ) ;
2022-09-29 09:53:28 +00:00
animation - > set_name ( anim_name ) ;
2024-05-23 15:40:26 +00:00
animation - > set_step ( 1.0 / p_state - > get_bake_fps ( ) ) ;
2020-12-21 15:39:32 +00:00
if ( anim - > get_loop ( ) ) {
2021-10-15 13:25:00 +00:00
animation - > set_loop_mode ( Animation : : LOOP_LINEAR ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
double anim_start = p_trimming ? INFINITY : 0.0 ;
2022-11-14 19:14:52 +00:00
double anim_end = 0.0 ;
2020-12-21 15:39:32 +00:00
2024-07-10 08:26:35 +00:00
for ( const KeyValue < int , GLTFAnimation : : NodeTrack > & track_i : anim - > get_node_tracks ( ) ) {
const GLTFAnimation : : NodeTrack & track = track_i . value ;
2021-05-21 07:03:06 +00:00
//need to find the path: for skeletons, weight tracks will affect the mesh
2020-12-21 15:39:32 +00:00
NodePath node_path ;
2021-05-21 07:03:06 +00:00
//for skeletons, transform tracks always affect bones
NodePath transform_node_path ;
2023-01-27 06:41:58 +00:00
//for meshes, especially skinned meshes, there are cases where it will be added as a child
NodePath mesh_instance_node_path ;
2020-12-21 15:39:32 +00:00
2021-08-09 20:13:42 +00:00
GLTFNodeIndex node_index = track_i . key ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
const Ref < GLTFNode > gltf_node = p_state - > nodes [ track_i . key ] ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
HashMap < GLTFNodeIndex , Node * > : : Iterator node_element = p_state - > scene_nodes . find ( node_index ) ;
2023-02-04 02:06:29 +00:00
ERR_CONTINUE_MSG ( ! node_element , vformat ( " Unable to find node %d for animation. " , node_index ) ) ;
2024-07-10 08:26:35 +00:00
node_path = scene_root - > get_path_to ( node_element - > value ) ;
2023-01-27 06:41:58 +00:00
HashMap < GLTFNodeIndex , ImporterMeshInstance3D * > : : Iterator mesh_instance_element = p_state - > scene_mesh_instances . find ( node_index ) ;
if ( mesh_instance_element ) {
2024-07-10 08:26:35 +00:00
mesh_instance_node_path = scene_root - > get_path_to ( mesh_instance_element - > value ) ;
2023-01-27 06:41:58 +00:00
} else {
mesh_instance_node_path = node_path ;
}
2021-05-21 07:03:06 +00:00
2020-12-21 15:39:32 +00:00
if ( gltf_node - > skeleton > = 0 ) {
2022-12-10 21:05:13 +00:00
const Skeleton3D * sk = p_state - > skeletons [ gltf_node - > skeleton ] - > godot_skeleton ;
2023-09-09 15:40:07 +00:00
ERR_FAIL_NULL ( sk ) ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
const String path = p_animation_player - > get_parent ( ) - > get_path_to ( sk ) ;
2020-12-21 15:39:32 +00:00
const String bone = gltf_node - > get_name ( ) ;
2021-05-21 07:03:06 +00:00
transform_node_path = path + " : " + bone ;
2020-12-21 15:39:32 +00:00
} else {
2021-05-21 07:03:06 +00:00
transform_node_path = node_path ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
if ( p_trimming ) {
2022-11-14 19:14:52 +00:00
for ( int i = 0 ; i < track . rotation_track . times . size ( ) ; i + + ) {
anim_start = MIN ( anim_start , track . rotation_track . times [ i ] ) ;
anim_end = MAX ( anim_end , track . rotation_track . times [ i ] ) ;
}
for ( int i = 0 ; i < track . position_track . times . size ( ) ; i + + ) {
anim_start = MIN ( anim_start , track . position_track . times [ i ] ) ;
anim_end = MAX ( anim_end , track . position_track . times [ i ] ) ;
}
for ( int i = 0 ; i < track . scale_track . times . size ( ) ; i + + ) {
anim_start = MIN ( anim_start , track . scale_track . times [ i ] ) ;
anim_end = MAX ( anim_end , track . scale_track . times [ i ] ) ;
}
for ( int i = 0 ; i < track . weight_tracks . size ( ) ; i + + ) {
for ( int j = 0 ; j < track . weight_tracks [ i ] . times . size ( ) ; j + + ) {
anim_start = MIN ( anim_start , track . weight_tracks [ i ] . times [ j ] ) ;
anim_end = MAX ( anim_end , track . weight_tracks [ i ] . times [ j ] ) ;
}
}
} else {
// If you don't use trimming and the first key time is not at 0.0, fake keys will be inserted.
for ( int i = 0 ; i < track . rotation_track . times . size ( ) ; i + + ) {
anim_end = MAX ( anim_end , track . rotation_track . times [ i ] ) ;
}
for ( int i = 0 ; i < track . position_track . times . size ( ) ; i + + ) {
anim_end = MAX ( anim_end , track . position_track . times [ i ] ) ;
}
for ( int i = 0 ; i < track . scale_track . times . size ( ) ; i + + ) {
anim_end = MAX ( anim_end , track . scale_track . times [ i ] ) ;
}
for ( int i = 0 ; i < track . weight_tracks . size ( ) ; i + + ) {
for ( int j = 0 ; j < track . weight_tracks [ i ] . times . size ( ) ; j + + ) {
anim_end = MAX ( anim_end , track . weight_tracks [ i ] . times [ j ] ) ;
}
2020-12-21 15:39:32 +00:00
}
}
2021-05-21 07:03:06 +00:00
// Animated TRS properties will not affect a skinned mesh.
const bool transform_affects_skinned_mesh_instance = gltf_node - > skeleton < 0 & & gltf_node - > skin > = 0 ;
2021-08-31 03:18:12 +00:00
if ( ( track . rotation_track . values . size ( ) | | track . position_track . values . size ( ) | | track . scale_track . values . size ( ) ) & & ! transform_affects_skinned_mesh_instance ) {
2020-12-21 15:39:32 +00:00
//make transform track
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
int base_idx = animation - > get_track_count ( ) ;
int position_idx = - 1 ;
int rotation_idx = - 1 ;
int scale_idx = - 1 ;
if ( track . position_track . values . size ( ) ) {
2023-01-29 23:49:55 +00:00
bool is_default = true ; //discard the track if all it contains is default values
if ( p_remove_immutable_tracks ) {
2023-10-12 22:37:03 +00:00
Vector3 base_pos = gltf_node - > get_position ( ) ;
2023-01-29 23:49:55 +00:00
for ( int i = 0 ; i < track . position_track . times . size ( ) ; i + + ) {
2023-10-20 08:56:49 +00:00
int value_index = track . position_track . interpolation = = GLTFAnimation : : INTERP_CUBIC_SPLINE ? ( 1 + i * 3 ) : i ;
ERR_FAIL_COND_MSG ( value_index > = track . position_track . values . size ( ) , " Animation sampler output accessor with 'CUBICSPLINE' interpolation doesn't have enough elements. " ) ;
Vector3 value = track . position_track . values [ value_index ] ;
2023-01-29 23:49:55 +00:00
if ( ! value . is_equal_approx ( base_pos ) ) {
is_default = false ;
break ;
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
}
}
2023-01-29 23:49:55 +00:00
if ( ! p_remove_immutable_tracks | | ! is_default ) {
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
position_idx = base_idx ;
animation - > add_track ( Animation : : TYPE_POSITION_3D ) ;
animation - > track_set_path ( position_idx , transform_node_path ) ;
animation - > track_set_imported ( position_idx , true ) ; //helps merging later
2023-12-11 00:59:22 +00:00
if ( track . position_track . interpolation = = GLTFAnimation : : INTERP_STEP ) {
animation - > track_set_interpolation_type ( position_idx , Animation : : InterpolationType : : INTERPOLATION_NEAREST ) ;
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
base_idx + + ;
}
}
if ( track . rotation_track . values . size ( ) ) {
2023-01-29 23:49:55 +00:00
bool is_default = true ; //discard the track if all it contains is default values
if ( p_remove_immutable_tracks ) {
2023-10-12 22:37:03 +00:00
Quaternion base_rot = gltf_node - > get_rotation ( ) ;
2023-01-29 23:49:55 +00:00
for ( int i = 0 ; i < track . rotation_track . times . size ( ) ; i + + ) {
2023-10-20 08:56:49 +00:00
int value_index = track . rotation_track . interpolation = = GLTFAnimation : : INTERP_CUBIC_SPLINE ? ( 1 + i * 3 ) : i ;
ERR_FAIL_COND_MSG ( value_index > = track . rotation_track . values . size ( ) , " Animation sampler output accessor with 'CUBICSPLINE' interpolation doesn't have enough elements. " ) ;
Quaternion value = track . rotation_track . values [ value_index ] . normalized ( ) ;
2023-01-29 23:49:55 +00:00
if ( ! value . is_equal_approx ( base_rot ) ) {
is_default = false ;
break ;
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
}
}
2023-01-29 23:49:55 +00:00
if ( ! p_remove_immutable_tracks | | ! is_default ) {
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
rotation_idx = base_idx ;
animation - > add_track ( Animation : : TYPE_ROTATION_3D ) ;
animation - > track_set_path ( rotation_idx , transform_node_path ) ;
animation - > track_set_imported ( rotation_idx , true ) ; //helps merging later
2023-12-11 00:59:22 +00:00
if ( track . rotation_track . interpolation = = GLTFAnimation : : INTERP_STEP ) {
animation - > track_set_interpolation_type ( rotation_idx , Animation : : InterpolationType : : INTERPOLATION_NEAREST ) ;
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
base_idx + + ;
}
}
if ( track . scale_track . values . size ( ) ) {
2023-01-29 23:49:55 +00:00
bool is_default = true ; //discard the track if all it contains is default values
if ( p_remove_immutable_tracks ) {
2023-10-12 22:37:03 +00:00
Vector3 base_scale = gltf_node - > get_scale ( ) ;
2023-01-29 23:49:55 +00:00
for ( int i = 0 ; i < track . scale_track . times . size ( ) ; i + + ) {
2023-10-20 08:56:49 +00:00
int value_index = track . scale_track . interpolation = = GLTFAnimation : : INTERP_CUBIC_SPLINE ? ( 1 + i * 3 ) : i ;
ERR_FAIL_COND_MSG ( value_index > = track . scale_track . values . size ( ) , " Animation sampler output accessor with 'CUBICSPLINE' interpolation doesn't have enough elements. " ) ;
Vector3 value = track . scale_track . values [ value_index ] ;
2023-01-29 23:49:55 +00:00
if ( ! value . is_equal_approx ( base_scale ) ) {
is_default = false ;
break ;
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
}
}
2023-01-29 23:49:55 +00:00
if ( ! p_remove_immutable_tracks | | ! is_default ) {
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
scale_idx = base_idx ;
animation - > add_track ( Animation : : TYPE_SCALE_3D ) ;
animation - > track_set_path ( scale_idx , transform_node_path ) ;
animation - > track_set_imported ( scale_idx , true ) ; //helps merging later
2023-12-11 00:59:22 +00:00
if ( track . scale_track . interpolation = = GLTFAnimation : : INTERP_STEP ) {
animation - > track_set_interpolation_type ( scale_idx , Animation : : InterpolationType : : INTERPOLATION_NEAREST ) ;
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
base_idx + + ;
}
}
2024-05-23 15:40:26 +00:00
const double increment = 1.0 / p_state - > get_bake_fps ( ) ;
2022-11-14 19:14:52 +00:00
double time = anim_start ;
2020-12-21 15:39:32 +00:00
Vector3 base_pos ;
2021-01-20 07:02:02 +00:00
Quaternion base_rot ;
2020-12-21 15:39:32 +00:00
Vector3 base_scale = Vector3 ( 1 , 1 , 1 ) ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
if ( rotation_idx = = - 1 ) {
2023-10-12 22:37:03 +00:00
base_rot = gltf_node - > get_rotation ( ) ;
2020-12-21 15:39:32 +00:00
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
if ( position_idx = = - 1 ) {
2023-10-12 22:37:03 +00:00
base_pos = gltf_node - > get_position ( ) ;
2020-12-21 15:39:32 +00:00
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
if ( scale_idx = = - 1 ) {
2023-10-12 22:37:03 +00:00
base_scale = gltf_node - > get_scale ( ) ;
2020-12-21 15:39:32 +00:00
}
bool last = false ;
while ( true ) {
Vector3 pos = base_pos ;
2021-01-20 07:02:02 +00:00
Quaternion rot = base_rot ;
2020-12-21 15:39:32 +00:00
Vector3 scale = base_scale ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
if ( position_idx > = 0 ) {
2021-08-31 03:18:12 +00:00
pos = _interpolate_track < Vector3 > ( track . position_track . times , track . position_track . values , time , track . position_track . interpolation ) ;
2022-11-14 19:14:52 +00:00
animation - > position_track_insert_key ( position_idx , time - anim_start , pos ) ;
2020-12-21 15:39:32 +00:00
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
if ( rotation_idx > = 0 ) {
2021-01-20 07:02:02 +00:00
rot = _interpolate_track < Quaternion > ( track . rotation_track . times , track . rotation_track . values , time , track . rotation_track . interpolation ) ;
2022-11-14 19:14:52 +00:00
animation - > rotation_track_insert_key ( rotation_idx , time - anim_start , rot ) ;
2020-12-21 15:39:32 +00:00
}
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
if ( scale_idx > = 0 ) {
2020-12-21 15:39:32 +00:00
scale = _interpolate_track < Vector3 > ( track . scale_track . times , track . scale_track . values , time , track . scale_track . interpolation ) ;
2022-11-14 19:14:52 +00:00
animation - > scale_track_insert_key ( scale_idx , time - anim_start , scale ) ;
Remove animation 3D transform track, replace by loc/rot/scale tracks.
* `Animation.TYPE_TRANSFORM3D` track is gone.
* Added POSITION_3D, ROTATION_3D, SCALE_3D tracks.
* GLTF2, Collada, FBX importers will only import the track types found.
* Skeleton3D bone poses are now Pos/Rot/Scale, pose matrix removed.
* AnimationPlayer and AnimationTree animate these tracks separately, only when found.
* Removed BakeReset code, is useless with these changes.
This is the first in a series of commits designed to make the animation system in Godot more useful, which includes:
* Better compatibility with Autodesk products
* Better reusability of animations across models (including retargeting).
* Proper animation compression.
* etc.
*Note* GLTF2 animation saving went broken with this PR, needs to be fixed in a subsequent one.
2021-10-11 22:20:58 +00:00
}
2020-12-21 15:39:32 +00:00
if ( last ) {
break ;
}
time + = increment ;
2022-11-14 19:14:52 +00:00
if ( time > = anim_end ) {
2020-12-21 15:39:32 +00:00
last = true ;
2022-11-14 19:14:52 +00:00
time = anim_end ;
2020-12-21 15:39:32 +00:00
}
}
}
for ( int i = 0 ; i < track . weight_tracks . size ( ) ; i + + ) {
2022-12-10 21:05:13 +00:00
ERR_CONTINUE ( gltf_node - > mesh < 0 | | gltf_node - > mesh > = p_state - > meshes . size ( ) ) ;
Ref < GLTFMesh > mesh = p_state - > meshes [ gltf_node - > mesh ] ;
2020-12-21 15:39:32 +00:00
ERR_CONTINUE ( mesh . is_null ( ) ) ;
ERR_CONTINUE ( mesh - > get_mesh ( ) . is_null ( ) ) ;
ERR_CONTINUE ( mesh - > get_mesh ( ) - > get_mesh ( ) . is_null ( ) ) ;
2023-01-27 06:41:58 +00:00
const String blend_path = String ( mesh_instance_node_path ) + " : " + String ( mesh - > get_mesh ( ) - > get_blend_shape_name ( i ) ) ;
2020-12-21 15:39:32 +00:00
const int track_idx = animation - > get_track_count ( ) ;
2021-10-15 22:04:35 +00:00
animation - > add_track ( Animation : : TYPE_BLEND_SHAPE ) ;
2020-12-21 15:39:32 +00:00
animation - > track_set_path ( track_idx , blend_path ) ;
2023-01-23 07:40:06 +00:00
animation - > track_set_imported ( track_idx , true ) ; //helps merging later
2020-12-21 15:39:32 +00:00
// Only LINEAR and STEP (NEAREST) can be supported out of the box by Godot's Animation,
// the other modes have to be baked.
GLTFAnimation : : Interpolation gltf_interp = track . weight_tracks [ i ] . interpolation ;
if ( gltf_interp = = GLTFAnimation : : INTERP_LINEAR | | gltf_interp = = GLTFAnimation : : INTERP_STEP ) {
animation - > track_set_interpolation_type ( track_idx , gltf_interp = = GLTFAnimation : : INTERP_STEP ? Animation : : INTERPOLATION_NEAREST : Animation : : INTERPOLATION_LINEAR ) ;
for ( int j = 0 ; j < track . weight_tracks [ i ] . times . size ( ) ; j + + ) {
const float t = track . weight_tracks [ i ] . times [ j ] ;
const float attribs = track . weight_tracks [ i ] . values [ j ] ;
2021-10-15 22:04:35 +00:00
animation - > blend_shape_track_insert_key ( track_idx , t , attribs ) ;
2020-12-21 15:39:32 +00:00
}
} else {
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
2024-05-23 15:40:26 +00:00
const double increment = 1.0 / p_state - > get_bake_fps ( ) ;
2021-03-18 08:50:28 +00:00
double time = 0.0 ;
2020-12-21 15:39:32 +00:00
bool last = false ;
while ( true ) {
2021-09-16 06:03:50 +00:00
real_t blend = _interpolate_track < real_t > ( track . weight_tracks [ i ] . times , track . weight_tracks [ i ] . values , time , gltf_interp ) ;
2022-11-14 19:14:52 +00:00
animation - > blend_shape_track_insert_key ( track_idx , time - anim_start , blend ) ;
2020-12-21 15:39:32 +00:00
if ( last ) {
break ;
}
time + = increment ;
2022-11-14 19:14:52 +00:00
if ( time > = anim_end ) {
2020-12-21 15:39:32 +00:00
last = true ;
2022-11-14 19:14:52 +00:00
time = anim_end ;
2020-12-21 15:39:32 +00:00
}
}
}
}
}
2024-10-04 07:37:44 +00:00
for ( const KeyValue < String , GLTFAnimation : : Channel < Variant > > & track_iter : anim - > get_pointer_tracks ( ) ) {
// Determine the property to animate.
const String json_pointer = track_iter . key ;
const Ref < GLTFObjectModelProperty > prop = import_object_model_property ( p_state , json_pointer ) ;
ERR_FAIL_COND ( prop . is_null ( ) ) ;
// Adjust the animation duration to encompass all keyframes.
const GLTFAnimation : : Channel < Variant > & channel = track_iter . value ;
ERR_CONTINUE_MSG ( channel . times . size ( ) ! = channel . values . size ( ) , vformat ( " glTF: Animation pointer '%s' has mismatched keyframe times and values. " , json_pointer ) ) ;
if ( p_trimming ) {
for ( int i = 0 ; i < channel . times . size ( ) ; i + + ) {
anim_start = MIN ( anim_start , channel . times [ i ] ) ;
anim_end = MAX ( anim_end , channel . times [ i ] ) ;
}
} else {
for ( int i = 0 ; i < channel . times . size ( ) ; i + + ) {
anim_end = MAX ( anim_end , channel . times [ i ] ) ;
}
}
// Begin converting the glTF animation to a Godot animation.
const Ref < Expression > gltf_to_godot_expr = prop - > get_gltf_to_godot_expression ( ) ;
const bool is_gltf_to_godot_expr_valid = gltf_to_godot_expr . is_valid ( ) ;
for ( const NodePath node_path : prop - > get_node_paths ( ) ) {
// If using an expression, determine the base instance to pass to the expression.
Object * base_instance = nullptr ;
if ( is_gltf_to_godot_expr_valid ) {
Ref < Resource > resource ;
Vector < StringName > leftover_subpath ;
base_instance = scene_root - > get_node_and_resource ( node_path , resource , leftover_subpath ) ;
if ( resource . is_valid ( ) ) {
base_instance = resource . ptr ( ) ;
}
}
// Add a track and insert all keys and values.
const int track_index = animation - > get_track_count ( ) ;
animation - > add_track ( Animation : : TYPE_VALUE ) ;
animation - > track_set_interpolation_type ( track_index , GLTFAnimation : : gltf_to_godot_interpolation ( channel . interpolation ) ) ;
animation - > track_set_path ( track_index , node_path ) ;
for ( int i = 0 ; i < channel . times . size ( ) ; i + + ) {
const double time = channel . times [ i ] ;
Variant value = channel . values [ i ] ;
if ( is_gltf_to_godot_expr_valid ) {
Array inputs ;
inputs . append ( value ) ;
value = gltf_to_godot_expr - > execute ( inputs , base_instance ) ;
}
animation - > track_insert_key ( track_index , time , value ) ;
}
}
}
2022-11-14 19:14:52 +00:00
animation - > set_length ( anim_end - anim_start ) ;
2020-12-21 15:39:32 +00:00
2022-04-07 11:49:28 +00:00
Ref < AnimationLibrary > library ;
2022-12-10 21:05:13 +00:00
if ( ! p_animation_player - > has_animation_library ( " " ) ) {
2022-04-07 11:49:28 +00:00
library . instantiate ( ) ;
2022-12-10 21:05:13 +00:00
p_animation_player - > add_animation_library ( " " , library ) ;
2022-04-07 11:49:28 +00:00
} else {
2022-12-10 21:05:13 +00:00
library = p_animation_player - > get_animation_library ( " " ) ;
2022-04-07 11:49:28 +00:00
}
2022-09-29 09:53:28 +00:00
library - > add_animation ( anim_name , animation ) ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _convert_mesh_instances ( Ref < GLTFState > p_state ) {
for ( GLTFNodeIndex mi_node_i = 0 ; mi_node_i < p_state - > nodes . size ( ) ; + + mi_node_i ) {
Ref < GLTFNode > node = p_state - > nodes [ mi_node_i ] ;
2020-12-21 15:39:32 +00:00
if ( node - > mesh < 0 ) {
continue ;
}
2022-12-10 21:05:13 +00:00
HashMap < GLTFNodeIndex , Node * > : : Iterator mi_element = p_state - > scene_nodes . find ( mi_node_i ) ;
2020-12-21 15:39:32 +00:00
if ( ! mi_element ) {
continue ;
}
2022-05-13 13:04:37 +00:00
MeshInstance3D * mi = Object : : cast_to < MeshInstance3D > ( mi_element - > value ) ;
2021-10-19 16:06:23 +00:00
if ( ! mi ) {
continue ;
}
2023-10-12 22:37:03 +00:00
node - > transform = mi - > get_transform ( ) ;
2020-12-21 15:39:32 +00:00
2023-05-27 08:08:44 +00:00
Node * skel_node = mi - > get_node_or_null ( mi - > get_skeleton_path ( ) ) ;
Skeleton3D * godot_skeleton = Object : : cast_to < Skeleton3D > ( skel_node ) ;
if ( ! godot_skeleton | | godot_skeleton - > get_bone_count ( ) = = 0 ) {
2020-12-21 15:39:32 +00:00
continue ;
}
2023-05-27 08:08:44 +00:00
// At this point in the code, we know we have a Skeleton3D with at least one bone.
2020-12-21 15:39:32 +00:00
Ref < Skin > skin = mi - > get_skin ( ) ;
Ref < GLTFSkin > gltf_skin ;
2021-06-17 22:03:09 +00:00
gltf_skin . instantiate ( ) ;
2020-12-21 15:39:32 +00:00
Array json_joints ;
2023-05-27 08:08:44 +00:00
if ( p_state - > skeleton3d_to_gltf_skeleton . has ( godot_skeleton - > get_instance_id ( ) ) ) {
2021-09-04 01:00:59 +00:00
// This is a skinned mesh. If the mesh has no ARRAY_WEIGHTS or ARRAY_BONES, it will be invisible.
2022-12-10 21:05:13 +00:00
const GLTFSkeletonIndex skeleton_gltf_i = p_state - > skeleton3d_to_gltf_skeleton [ godot_skeleton - > get_instance_id ( ) ] ;
Ref < GLTFSkeleton > gltf_skeleton = p_state - > skeletons [ skeleton_gltf_i ] ;
2023-05-27 08:08:44 +00:00
int bone_cnt = godot_skeleton - > get_bone_count ( ) ;
2021-09-04 01:00:59 +00:00
ERR_FAIL_COND ( bone_cnt ! = gltf_skeleton - > joints . size ( ) ) ;
2021-11-23 03:40:47 +00:00
ObjectID gltf_skin_key ;
if ( skin . is_valid ( ) ) {
gltf_skin_key = skin - > get_instance_id ( ) ;
}
2021-09-04 01:00:59 +00:00
ObjectID gltf_skel_key = godot_skeleton - > get_instance_id ( ) ;
GLTFSkinIndex skin_gltf_i = - 1 ;
GLTFNodeIndex root_gltf_i = - 1 ;
if ( ! gltf_skeleton - > roots . is_empty ( ) ) {
root_gltf_i = gltf_skeleton - > roots [ 0 ] ;
}
2022-12-10 21:05:13 +00:00
if ( p_state - > skin_and_skeleton3d_to_gltf_skin . has ( gltf_skin_key ) & & p_state - > skin_and_skeleton3d_to_gltf_skin [ gltf_skin_key ] . has ( gltf_skel_key ) ) {
skin_gltf_i = p_state - > skin_and_skeleton3d_to_gltf_skin [ gltf_skin_key ] [ gltf_skel_key ] ;
2020-12-21 15:39:32 +00:00
} else {
2021-09-04 01:00:59 +00:00
if ( skin . is_null ( ) ) {
// Note that gltf_skin_key should remain null, so these can share a reference.
2023-05-27 08:08:44 +00:00
skin = godot_skeleton - > create_skin_from_rest_transforms ( ) ;
2021-09-04 01:00:59 +00:00
}
gltf_skin . instantiate ( ) ;
gltf_skin - > godot_skin = skin ;
gltf_skin - > set_name ( skin - > get_name ( ) ) ;
gltf_skin - > skeleton = skeleton_gltf_i ;
gltf_skin - > skin_root = root_gltf_i ;
//gltf_state->godot_to_gltf_node[skel_node]
HashMap < StringName , int > bone_name_to_idx ;
for ( int bone_i = 0 ; bone_i < bone_cnt ; bone_i + + ) {
2023-05-27 08:08:44 +00:00
bone_name_to_idx [ godot_skeleton - > get_bone_name ( bone_i ) ] = bone_i ;
2021-09-04 01:00:59 +00:00
}
for ( int bind_i = 0 , cnt = skin - > get_bind_count ( ) ; bind_i < cnt ; bind_i + + ) {
int bone_i = skin - > get_bind_bone ( bind_i ) ;
Transform3D bind_pose = skin - > get_bind_pose ( bind_i ) ;
StringName bind_name = skin - > get_bind_name ( bind_i ) ;
if ( bind_name ! = StringName ( ) ) {
bone_i = bone_name_to_idx [ bind_name ] ;
}
ERR_CONTINUE ( bone_i < 0 | | bone_i > = bone_cnt ) ;
if ( bind_name = = StringName ( ) ) {
2023-05-27 08:08:44 +00:00
bind_name = godot_skeleton - > get_bone_name ( bone_i ) ;
2020-12-21 15:39:32 +00:00
}
2021-09-04 01:00:59 +00:00
GLTFNodeIndex skeleton_bone_i = gltf_skeleton - > joints [ bone_i ] ;
gltf_skin - > joints_original . push_back ( skeleton_bone_i ) ;
gltf_skin - > joints . push_back ( skeleton_bone_i ) ;
gltf_skin - > inverse_binds . push_back ( bind_pose ) ;
2023-05-27 08:08:44 +00:00
if ( godot_skeleton - > get_bone_parent ( bone_i ) = = - 1 ) {
2021-09-04 01:00:59 +00:00
gltf_skin - > roots . push_back ( skeleton_bone_i ) ;
}
gltf_skin - > joint_i_to_bone_i [ bind_i ] = bone_i ;
gltf_skin - > joint_i_to_name [ bind_i ] = bind_name ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
skin_gltf_i = p_state - > skins . size ( ) ;
p_state - > skins . push_back ( gltf_skin ) ;
p_state - > skin_and_skeleton3d_to_gltf_skin [ gltf_skin_key ] [ gltf_skel_key ] = skin_gltf_i ;
2020-12-21 15:39:32 +00:00
}
2021-09-04 01:00:59 +00:00
node - > skin = skin_gltf_i ;
node - > skeleton = skeleton_gltf_i ;
2020-12-21 15:39:32 +00:00
}
}
}
2022-12-10 21:05:13 +00:00
float GLTFDocument : : solve_metallic ( float p_dielectric_specular , float p_diffuse , float p_specular , float p_one_minus_specular_strength ) {
if ( p_specular < = p_dielectric_specular ) {
2020-12-21 15:39:32 +00:00
return 0.0f ;
}
const float a = p_dielectric_specular ;
2022-12-10 21:05:13 +00:00
const float b = p_diffuse * p_one_minus_specular_strength / ( 1.0f - p_dielectric_specular ) + p_specular - 2.0f * p_dielectric_specular ;
const float c = p_dielectric_specular - p_specular ;
2020-12-21 15:39:32 +00:00
const float D = b * b - 4.0f * a * c ;
return CLAMP ( ( - b + Math : : sqrt ( D ) ) / ( 2.0f * a ) , 0.0f , 1.0f ) ;
}
float GLTFDocument : : get_perceived_brightness ( const Color p_color ) {
const Color coeff = Color ( R_BRIGHTNESS_COEFF , G_BRIGHTNESS_COEFF , B_BRIGHTNESS_COEFF ) ;
const Color value = coeff * ( p_color * p_color ) ;
const float r = value . r ;
const float g = value . g ;
const float b = value . b ;
return Math : : sqrt ( r + g + b ) ;
}
float GLTFDocument : : get_max_component ( const Color & p_color ) {
const float r = p_color . r ;
const float g = p_color . g ;
const float b = p_color . b ;
return MAX ( MAX ( r , g ) , b ) ;
}
2024-02-04 08:47:44 +00:00
void GLTFDocument : : _process_mesh_instances ( Ref < GLTFState > p_state , Node * p_scene_root ) {
2022-12-10 21:05:13 +00:00
for ( GLTFNodeIndex node_i = 0 ; node_i < p_state - > nodes . size ( ) ; + + node_i ) {
Ref < GLTFNode > node = p_state - > nodes [ node_i ] ;
2020-12-21 15:39:32 +00:00
if ( node - > skin > = 0 & & node - > mesh > = 0 ) {
const GLTFSkinIndex skin_i = node - > skin ;
2023-01-27 06:41:58 +00:00
ImporterMeshInstance3D * mi = nullptr ;
HashMap < GLTFNodeIndex , ImporterMeshInstance3D * > : : Iterator mi_element = p_state - > scene_mesh_instances . find ( node_i ) ;
if ( mi_element ) {
mi = mi_element - > value ;
} else {
HashMap < GLTFNodeIndex , Node * > : : Iterator si_element = p_state - > scene_nodes . find ( node_i ) ;
ERR_CONTINUE_MSG ( ! si_element , vformat ( " Unable to find node %d " , node_i ) ) ;
mi = Object : : cast_to < ImporterMeshInstance3D > ( si_element - > value ) ;
ERR_CONTINUE_MSG ( mi = = nullptr , vformat ( " Unable to cast node %d of type %s to ImporterMeshInstance3D " , node_i , si_element - > value - > get_class_name ( ) ) ) ;
}
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
const GLTFSkeletonIndex skel_i = p_state - > skins . write [ node - > skin ] - > skeleton ;
Ref < GLTFSkeleton > gltf_skeleton = p_state - > skeletons . write [ skel_i ] ;
2020-12-21 15:39:32 +00:00
Skeleton3D * skeleton = gltf_skeleton - > godot_skeleton ;
2021-05-21 02:12:16 +00:00
ERR_CONTINUE_MSG ( skeleton = = nullptr , vformat ( " Unable to find Skeleton for node %d skin %d " , node_i , skin_i ) ) ;
2020-12-21 15:39:32 +00:00
mi - > get_parent ( ) - > remove_child ( mi ) ;
2024-06-13 04:45:55 +00:00
mi - > set_owner ( nullptr ) ;
2021-10-21 14:46:07 +00:00
skeleton - > add_child ( mi , true ) ;
2024-02-04 08:47:44 +00:00
mi - > set_owner ( p_scene_root ) ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
mi - > set_skin ( p_state - > skins . write [ skin_i ] - > godot_skin ) ;
2020-12-21 15:39:32 +00:00
mi - > set_skeleton_path ( mi - > get_path_to ( skeleton ) ) ;
2020-10-17 05:08:21 +00:00
mi - > set_transform ( Transform3D ( ) ) ;
2020-12-21 15:39:32 +00:00
}
}
}
2024-07-10 08:26:35 +00:00
GLTFNodeIndex GLTFDocument : : _node_and_or_bone_to_gltf_node_index ( Ref < GLTFState > p_state , const Vector < StringName > & p_node_subpath , const Node * p_godot_node ) {
2024-07-10 09:02:03 +00:00
const Skeleton3D * skeleton = Object : : cast_to < Skeleton3D > ( p_godot_node ) ;
2024-07-10 08:26:35 +00:00
if ( skeleton & & p_node_subpath . size ( ) = = 1 ) {
// Special case: Handle skeleton bone TRS tracks. They use the format `A/B/C/Skeleton3D:bone_name`.
// We have a Skeleton3D, check if it has a bone with the same name as this subpath.
const String & bone_name = p_node_subpath [ 0 ] ;
const int32_t bone_index = skeleton - > find_bone ( bone_name ) ;
if ( bone_index ! = - 1 ) {
// A bone was found! But we still need to figure out which glTF node it corresponds to.
for ( GLTFSkeletonIndex skeleton_i = 0 ; skeleton_i < p_state - > skeletons . size ( ) ; skeleton_i + + ) {
const Ref < GLTFSkeleton > & skeleton_gltf = p_state - > skeletons [ skeleton_i ] ;
if ( skeleton = = skeleton_gltf - > godot_skeleton ) {
GLTFNodeIndex node_i = skeleton_gltf - > godot_bone_node [ bone_index ] ;
return node_i ;
}
}
ERR_FAIL_V_MSG ( - 1 , vformat ( " glTF: Found a bone %s in a Skeleton3D that wasn't in the GLTFState. Ensure that all nodes referenced by the AnimationPlayer are in the scene you are exporting. " , bone_name ) ) ;
}
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:26:35 +00:00
// General case: Not a skeleton bone, usually this means a normal node, or it could be the Skeleton3D itself.
for ( const KeyValue < GLTFNodeIndex , Node * > & scene_node_i : p_state - > scene_nodes ) {
if ( scene_node_i . value = = p_godot_node ) {
return scene_node_i . key ;
}
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:26:35 +00:00
ERR_FAIL_V_MSG ( - 1 , vformat ( " glTF: A node was animated, but it wasn't found in the GLTFState. Ensure that all nodes referenced by the AnimationPlayer are in the scene you are exporting. " ) ) ;
}
bool GLTFDocument : : _convert_animation_node_track ( Ref < GLTFState > p_state , GLTFAnimation : : NodeTrack & p_gltf_node_track , const Ref < Animation > & p_godot_animation , int32_t p_godot_anim_track_index , Vector < double > & p_times ) {
GLTFAnimation : : Interpolation gltf_interpolation = GLTFAnimation : : godot_to_gltf_interpolation ( p_godot_animation , p_godot_anim_track_index ) ;
const Animation : : TrackType track_type = p_godot_animation - > track_get_type ( p_godot_anim_track_index ) ;
const int32_t key_count = p_godot_animation - > track_get_key_count ( p_godot_anim_track_index ) ;
const NodePath node_path = p_godot_animation - > track_get_path ( p_godot_anim_track_index ) ;
const Vector < StringName > subpath = node_path . get_subnames ( ) ;
double anim_end = p_godot_animation - > get_length ( ) ;
2021-10-13 09:18:43 +00:00
if ( track_type = = Animation : : TYPE_SCALE_3D ) {
2023-02-04 02:06:29 +00:00
if ( gltf_interpolation = = GLTFAnimation : : INTERP_CUBIC_SPLINE ) {
gltf_interpolation = GLTFAnimation : : INTERP_LINEAR ;
2024-07-10 08:26:35 +00:00
p_gltf_node_track . scale_track . times . clear ( ) ;
p_gltf_node_track . scale_track . values . clear ( ) ;
2023-02-04 02:06:29 +00:00
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
2024-05-23 15:40:26 +00:00
const double increment = 1.0 / p_state - > get_bake_fps ( ) ;
2023-02-04 02:06:29 +00:00
double time = 0.0 ;
bool last = false ;
while ( true ) {
Vector3 scale ;
2024-07-10 08:26:35 +00:00
Error err = p_godot_animation - > try_scale_track_interpolate ( p_godot_anim_track_index , time , & scale ) ;
2023-02-04 02:06:29 +00:00
ERR_CONTINUE ( err ! = OK ) ;
2024-07-10 08:26:35 +00:00
p_gltf_node_track . scale_track . values . push_back ( scale ) ;
p_gltf_node_track . scale_track . times . push_back ( time ) ;
2023-02-04 02:06:29 +00:00
if ( last ) {
break ;
}
time + = increment ;
if ( time > = anim_end ) {
last = true ;
time = anim_end ;
}
}
} else {
2024-07-10 08:26:35 +00:00
p_gltf_node_track . scale_track . times = p_times ;
p_gltf_node_track . scale_track . interpolation = gltf_interpolation ;
p_gltf_node_track . scale_track . values . resize ( key_count ) ;
2023-02-04 02:06:29 +00:00
for ( int32_t key_i = 0 ; key_i < key_count ; key_i + + ) {
Vector3 scale ;
2024-07-10 08:26:35 +00:00
Error err = p_godot_animation - > scale_track_get_key ( p_godot_anim_track_index , key_i , & scale ) ;
2023-02-04 02:06:29 +00:00
ERR_CONTINUE ( err ! = OK ) ;
2024-07-10 08:26:35 +00:00
p_gltf_node_track . scale_track . values . write [ key_i ] = scale ;
2023-02-04 02:06:29 +00:00
}
2021-10-13 09:18:43 +00:00
}
} else if ( track_type = = Animation : : TYPE_POSITION_3D ) {
2023-02-04 02:06:29 +00:00
if ( gltf_interpolation = = GLTFAnimation : : INTERP_CUBIC_SPLINE ) {
gltf_interpolation = GLTFAnimation : : INTERP_LINEAR ;
2024-07-10 08:26:35 +00:00
p_gltf_node_track . position_track . times . clear ( ) ;
p_gltf_node_track . position_track . values . clear ( ) ;
2023-02-04 02:06:29 +00:00
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
2024-05-23 15:40:26 +00:00
const double increment = 1.0 / p_state - > get_bake_fps ( ) ;
2023-02-04 02:06:29 +00:00
double time = 0.0 ;
bool last = false ;
while ( true ) {
Vector3 scale ;
2024-07-10 08:26:35 +00:00
Error err = p_godot_animation - > try_position_track_interpolate ( p_godot_anim_track_index , time , & scale ) ;
2023-02-04 02:06:29 +00:00
ERR_CONTINUE ( err ! = OK ) ;
2024-07-10 08:26:35 +00:00
p_gltf_node_track . position_track . values . push_back ( scale ) ;
p_gltf_node_track . position_track . times . push_back ( time ) ;
2023-02-04 02:06:29 +00:00
if ( last ) {
break ;
}
time + = increment ;
if ( time > = anim_end ) {
last = true ;
time = anim_end ;
}
}
} else {
2024-07-10 08:26:35 +00:00
p_gltf_node_track . position_track . times = p_times ;
p_gltf_node_track . position_track . values . resize ( key_count ) ;
p_gltf_node_track . position_track . interpolation = gltf_interpolation ;
2023-02-04 02:06:29 +00:00
for ( int32_t key_i = 0 ; key_i < key_count ; key_i + + ) {
Vector3 position ;
2024-07-10 08:26:35 +00:00
Error err = p_godot_animation - > position_track_get_key ( p_godot_anim_track_index , key_i , & position ) ;
2023-02-04 02:06:29 +00:00
ERR_CONTINUE ( err ! = OK ) ;
2024-07-10 08:26:35 +00:00
p_gltf_node_track . position_track . values . write [ key_i ] = position ;
2023-02-04 02:06:29 +00:00
}
2021-10-13 09:18:43 +00:00
}
} else if ( track_type = = Animation : : TYPE_ROTATION_3D ) {
2023-02-04 02:06:29 +00:00
if ( gltf_interpolation = = GLTFAnimation : : INTERP_CUBIC_SPLINE ) {
gltf_interpolation = GLTFAnimation : : INTERP_LINEAR ;
2024-07-10 08:26:35 +00:00
p_gltf_node_track . rotation_track . times . clear ( ) ;
p_gltf_node_track . rotation_track . values . clear ( ) ;
2023-02-04 02:06:29 +00:00
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
2024-05-23 15:40:26 +00:00
const double increment = 1.0 / p_state - > get_bake_fps ( ) ;
2023-02-04 02:06:29 +00:00
double time = 0.0 ;
bool last = false ;
while ( true ) {
Quaternion rotation ;
2024-07-10 08:26:35 +00:00
Error err = p_godot_animation - > try_rotation_track_interpolate ( p_godot_anim_track_index , time , & rotation ) ;
2023-02-04 02:06:29 +00:00
ERR_CONTINUE ( err ! = OK ) ;
2024-07-10 08:26:35 +00:00
p_gltf_node_track . rotation_track . values . push_back ( rotation ) ;
p_gltf_node_track . rotation_track . times . push_back ( time ) ;
2023-02-04 02:06:29 +00:00
if ( last ) {
break ;
}
time + = increment ;
if ( time > = anim_end ) {
last = true ;
time = anim_end ;
}
}
} else {
2024-07-10 08:26:35 +00:00
p_gltf_node_track . rotation_track . times = p_times ;
p_gltf_node_track . rotation_track . values . resize ( key_count ) ;
p_gltf_node_track . rotation_track . interpolation = gltf_interpolation ;
2023-02-04 02:06:29 +00:00
for ( int32_t key_i = 0 ; key_i < key_count ; key_i + + ) {
Quaternion rotation ;
2024-07-10 08:26:35 +00:00
Error err = p_godot_animation - > rotation_track_get_key ( p_godot_anim_track_index , key_i , & rotation ) ;
2023-02-04 02:06:29 +00:00
ERR_CONTINUE ( err ! = OK ) ;
2024-07-10 08:26:35 +00:00
p_gltf_node_track . rotation_track . values . write [ key_i ] = rotation ;
}
}
} else if ( subpath . size ( ) > 0 ) {
const StringName & node_prop = subpath [ 0 ] ;
if ( track_type = = Animation : : TYPE_VALUE ) {
if ( node_prop = = " position " ) {
p_gltf_node_track . position_track . interpolation = gltf_interpolation ;
p_gltf_node_track . position_track . times = p_times ;
p_gltf_node_track . position_track . values . resize ( key_count ) ;
if ( gltf_interpolation = = GLTFAnimation : : INTERP_CUBIC_SPLINE ) {
gltf_interpolation = GLTFAnimation : : INTERP_LINEAR ;
p_gltf_node_track . position_track . times . clear ( ) ;
p_gltf_node_track . position_track . values . clear ( ) ;
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / p_state - > get_bake_fps ( ) ;
double time = 0.0 ;
bool last = false ;
while ( true ) {
Vector3 position ;
Error err = p_godot_animation - > try_position_track_interpolate ( p_godot_anim_track_index , time , & position ) ;
ERR_CONTINUE ( err ! = OK ) ;
p_gltf_node_track . position_track . values . push_back ( position ) ;
p_gltf_node_track . position_track . times . push_back ( time ) ;
if ( last ) {
break ;
}
time + = increment ;
if ( time > = anim_end ) {
last = true ;
time = anim_end ;
}
2023-02-04 02:06:29 +00:00
}
2024-07-10 08:26:35 +00:00
} else {
for ( int32_t key_i = 0 ; key_i < key_count ; key_i + + ) {
Vector3 position = p_godot_animation - > track_get_key_value ( p_godot_anim_track_index , key_i ) ;
p_gltf_node_track . position_track . values . write [ key_i ] = position ;
2023-02-04 02:06:29 +00:00
}
}
2024-07-10 08:26:35 +00:00
} else if ( node_prop = = " rotation " | | node_prop = = " rotation_degrees " | | node_prop = = " quaternion " ) {
p_gltf_node_track . rotation_track . interpolation = gltf_interpolation ;
p_gltf_node_track . rotation_track . times = p_times ;
p_gltf_node_track . rotation_track . values . resize ( key_count ) ;
if ( gltf_interpolation = = GLTFAnimation : : INTERP_CUBIC_SPLINE ) {
gltf_interpolation = GLTFAnimation : : INTERP_LINEAR ;
p_gltf_node_track . rotation_track . times . clear ( ) ;
p_gltf_node_track . rotation_track . values . clear ( ) ;
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / p_state - > get_bake_fps ( ) ;
double time = 0.0 ;
bool last = false ;
while ( true ) {
Quaternion rotation ;
Error err = p_godot_animation - > try_rotation_track_interpolate ( p_godot_anim_track_index , time , & rotation ) ;
ERR_CONTINUE ( err ! = OK ) ;
p_gltf_node_track . rotation_track . values . push_back ( rotation ) ;
p_gltf_node_track . rotation_track . times . push_back ( time ) ;
if ( last ) {
break ;
}
time + = increment ;
if ( time > = anim_end ) {
last = true ;
time = anim_end ;
}
2023-02-04 02:06:29 +00:00
}
2024-07-10 08:26:35 +00:00
} else {
for ( int32_t key_i = 0 ; key_i < key_count ; key_i + + ) {
Quaternion rotation_quaternion ;
if ( node_prop = = " quaternion " ) {
rotation_quaternion = p_godot_animation - > track_get_key_value ( p_godot_anim_track_index , key_i ) ;
} else {
Vector3 rotation_euler = p_godot_animation - > track_get_key_value ( p_godot_anim_track_index , key_i ) ;
if ( node_prop = = " rotation_degrees " ) {
rotation_euler * = Math_TAU / 360.0 ;
}
rotation_quaternion = Quaternion : : from_euler ( rotation_euler ) ;
}
p_gltf_node_track . rotation_track . values . write [ key_i ] = rotation_quaternion ;
2023-02-04 02:06:29 +00:00
}
}
2024-07-10 08:26:35 +00:00
} else if ( node_prop = = " scale " ) {
p_gltf_node_track . scale_track . interpolation = gltf_interpolation ;
p_gltf_node_track . scale_track . times = p_times ;
p_gltf_node_track . scale_track . values . resize ( key_count ) ;
if ( gltf_interpolation = = GLTFAnimation : : INTERP_CUBIC_SPLINE ) {
gltf_interpolation = GLTFAnimation : : INTERP_LINEAR ;
p_gltf_node_track . scale_track . times . clear ( ) ;
p_gltf_node_track . scale_track . values . clear ( ) ;
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / p_state - > get_bake_fps ( ) ;
double time = 0.0 ;
bool last = false ;
while ( true ) {
Vector3 scale ;
Error err = p_godot_animation - > try_scale_track_interpolate ( p_godot_anim_track_index , time , & scale ) ;
ERR_CONTINUE ( err ! = OK ) ;
p_gltf_node_track . scale_track . values . push_back ( scale ) ;
p_gltf_node_track . scale_track . times . push_back ( time ) ;
if ( last ) {
break ;
}
time + = increment ;
if ( time > = anim_end ) {
last = true ;
time = anim_end ;
}
}
} else {
for ( int32_t key_i = 0 ; key_i < key_count ; key_i + + ) {
Vector3 scale_track = p_godot_animation - > track_get_key_value ( p_godot_anim_track_index , key_i ) ;
p_gltf_node_track . scale_track . values . write [ key_i ] = scale_track ;
}
2023-02-04 02:06:29 +00:00
}
2024-07-10 08:26:35 +00:00
} else if ( node_prop = = " transform " ) {
p_gltf_node_track . position_track . interpolation = gltf_interpolation ;
p_gltf_node_track . position_track . times = p_times ;
p_gltf_node_track . position_track . values . resize ( key_count ) ;
p_gltf_node_track . rotation_track . interpolation = gltf_interpolation ;
p_gltf_node_track . rotation_track . times = p_times ;
p_gltf_node_track . rotation_track . values . resize ( key_count ) ;
p_gltf_node_track . scale_track . interpolation = gltf_interpolation ;
p_gltf_node_track . scale_track . times = p_times ;
p_gltf_node_track . scale_track . values . resize ( key_count ) ;
if ( gltf_interpolation = = GLTFAnimation : : INTERP_CUBIC_SPLINE ) {
gltf_interpolation = GLTFAnimation : : INTERP_LINEAR ;
p_gltf_node_track . position_track . times . clear ( ) ;
p_gltf_node_track . position_track . values . clear ( ) ;
p_gltf_node_track . rotation_track . times . clear ( ) ;
p_gltf_node_track . rotation_track . values . clear ( ) ;
p_gltf_node_track . scale_track . times . clear ( ) ;
p_gltf_node_track . scale_track . values . clear ( ) ;
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / p_state - > get_bake_fps ( ) ;
double time = 0.0 ;
bool last = false ;
while ( true ) {
Vector3 position ;
Quaternion rotation ;
Vector3 scale ;
Error err = p_godot_animation - > try_position_track_interpolate ( p_godot_anim_track_index , time , & position ) ;
ERR_CONTINUE ( err ! = OK ) ;
err = p_godot_animation - > try_rotation_track_interpolate ( p_godot_anim_track_index , time , & rotation ) ;
ERR_CONTINUE ( err ! = OK ) ;
err = p_godot_animation - > try_scale_track_interpolate ( p_godot_anim_track_index , time , & scale ) ;
ERR_CONTINUE ( err ! = OK ) ;
p_gltf_node_track . position_track . values . push_back ( position ) ;
p_gltf_node_track . position_track . times . push_back ( time ) ;
p_gltf_node_track . rotation_track . values . push_back ( rotation ) ;
p_gltf_node_track . rotation_track . times . push_back ( time ) ;
p_gltf_node_track . scale_track . values . push_back ( scale ) ;
p_gltf_node_track . scale_track . times . push_back ( time ) ;
if ( last ) {
break ;
}
time + = increment ;
if ( time > = anim_end ) {
last = true ;
time = anim_end ;
}
2023-02-04 02:06:29 +00:00
}
2024-07-10 08:26:35 +00:00
} else {
for ( int32_t key_i = 0 ; key_i < key_count ; key_i + + ) {
Transform3D transform = p_godot_animation - > track_get_key_value ( p_godot_anim_track_index , key_i ) ;
p_gltf_node_track . position_track . values . write [ key_i ] = transform . get_origin ( ) ;
p_gltf_node_track . rotation_track . values . write [ key_i ] = transform . basis . get_rotation_quaternion ( ) ;
p_gltf_node_track . scale_track . values . write [ key_i ] = transform . basis . get_scale ( ) ;
2023-02-04 02:06:29 +00:00
}
}
} else {
2024-07-10 08:26:35 +00:00
// This is a Value track animating a property, but not a TRS property, so it can't be converted into a node track.
return false ;
}
} else if ( track_type = = Animation : : TYPE_BEZIER ) {
const int32_t keys = anim_end * p_state - > get_bake_fps ( ) ;
if ( node_prop = = " scale " ) {
if ( p_gltf_node_track . scale_track . times . is_empty ( ) ) {
p_gltf_node_track . scale_track . interpolation = gltf_interpolation ;
Vector < double > new_times ;
new_times . resize ( keys ) ;
for ( int32_t key_i = 0 ; key_i < keys ; key_i + + ) {
new_times . write [ key_i ] = key_i / p_state - > get_bake_fps ( ) ;
}
p_gltf_node_track . scale_track . times = new_times ;
2020-12-21 15:39:32 +00:00
2024-07-10 08:26:35 +00:00
p_gltf_node_track . scale_track . values . resize ( keys ) ;
2020-12-21 15:39:32 +00:00
2024-07-10 08:26:35 +00:00
for ( int32_t key_i = 0 ; key_i < keys ; key_i + + ) {
p_gltf_node_track . scale_track . values . write [ key_i ] = Vector3 ( 1.0f , 1.0f , 1.0f ) ;
}
2020-12-21 15:39:32 +00:00
2024-07-10 08:26:35 +00:00
for ( int32_t key_i = 0 ; key_i < keys ; key_i + + ) {
Vector3 bezier_track = p_gltf_node_track . scale_track . values [ key_i ] ;
if ( subpath . size ( ) = = 2 ) {
if ( subpath [ 1 ] = = StringName ( " x " ) ) {
bezier_track . x = p_godot_animation - > bezier_track_interpolate ( p_godot_anim_track_index , key_i / p_state - > get_bake_fps ( ) ) ;
} else if ( subpath [ 1 ] = = StringName ( " y " ) ) {
bezier_track . y = p_godot_animation - > bezier_track_interpolate ( p_godot_anim_track_index , key_i / p_state - > get_bake_fps ( ) ) ;
} else if ( subpath [ 1 ] = = StringName ( " z " ) ) {
bezier_track . z = p_godot_animation - > bezier_track_interpolate ( p_godot_anim_track_index , key_i / p_state - > get_bake_fps ( ) ) ;
}
}
p_gltf_node_track . scale_track . values . write [ key_i ] = bezier_track ;
2023-02-04 02:06:29 +00:00
}
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:26:35 +00:00
} else if ( node_prop = = " position " ) {
if ( p_gltf_node_track . position_track . times . is_empty ( ) ) {
p_gltf_node_track . position_track . interpolation = gltf_interpolation ;
Vector < double > new_times ;
new_times . resize ( keys ) ;
for ( int32_t key_i = 0 ; key_i < keys ; key_i + + ) {
new_times . write [ key_i ] = key_i / p_state - > get_bake_fps ( ) ;
}
p_gltf_node_track . position_track . times = new_times ;
p_gltf_node_track . position_track . values . resize ( keys ) ;
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:26:35 +00:00
for ( int32_t key_i = 0 ; key_i < keys ; key_i + + ) {
Vector3 bezier_track = p_gltf_node_track . position_track . values [ key_i ] ;
if ( subpath . size ( ) = = 2 ) {
if ( subpath [ 1 ] = = StringName ( " x " ) ) {
bezier_track . x = p_godot_animation - > bezier_track_interpolate ( p_godot_anim_track_index , key_i / p_state - > get_bake_fps ( ) ) ;
} else if ( subpath [ 1 ] = = StringName ( " y " ) ) {
bezier_track . y = p_godot_animation - > bezier_track_interpolate ( p_godot_anim_track_index , key_i / p_state - > get_bake_fps ( ) ) ;
} else if ( subpath [ 1 ] = = StringName ( " z " ) ) {
bezier_track . z = p_godot_animation - > bezier_track_interpolate ( p_godot_anim_track_index , key_i / p_state - > get_bake_fps ( ) ) ;
}
}
p_gltf_node_track . position_track . values . write [ key_i ] = bezier_track ;
}
} else if ( node_prop = = " quaternion " ) {
if ( p_gltf_node_track . rotation_track . times . is_empty ( ) ) {
p_gltf_node_track . rotation_track . interpolation = gltf_interpolation ;
Vector < double > new_times ;
new_times . resize ( keys ) ;
for ( int32_t key_i = 0 ; key_i < keys ; key_i + + ) {
new_times . write [ key_i ] = key_i / p_state - > get_bake_fps ( ) ;
}
p_gltf_node_track . rotation_track . times = new_times ;
2020-12-21 15:39:32 +00:00
2024-07-10 08:26:35 +00:00
p_gltf_node_track . rotation_track . values . resize ( keys ) ;
2020-12-21 15:39:32 +00:00
}
2023-02-04 02:06:29 +00:00
for ( int32_t key_i = 0 ; key_i < keys ; key_i + + ) {
2024-07-10 08:26:35 +00:00
Quaternion bezier_track = p_gltf_node_track . rotation_track . values [ key_i ] ;
if ( subpath . size ( ) = = 2 ) {
if ( subpath [ 1 ] = = StringName ( " x " ) ) {
bezier_track . x = p_godot_animation - > bezier_track_interpolate ( p_godot_anim_track_index , key_i / p_state - > get_bake_fps ( ) ) ;
} else if ( subpath [ 1 ] = = StringName ( " y " ) ) {
bezier_track . y = p_godot_animation - > bezier_track_interpolate ( p_godot_anim_track_index , key_i / p_state - > get_bake_fps ( ) ) ;
} else if ( subpath [ 1 ] = = StringName ( " z " ) ) {
bezier_track . z = p_godot_animation - > bezier_track_interpolate ( p_godot_anim_track_index , key_i / p_state - > get_bake_fps ( ) ) ;
} else if ( subpath [ 1 ] = = StringName ( " w " ) ) {
bezier_track . w = p_godot_animation - > bezier_track_interpolate ( p_godot_anim_track_index , key_i / p_state - > get_bake_fps ( ) ) ;
}
}
p_gltf_node_track . rotation_track . values . write [ key_i ] = bezier_track ;
2023-02-04 02:06:29 +00:00
}
2024-07-10 08:26:35 +00:00
} else {
// This is a Bezier track animating a property, but not a TRS property, so it can't be converted into a node track.
return false ;
2023-02-04 02:06:29 +00:00
}
2024-07-10 08:26:35 +00:00
} else {
// This property track isn't a Value track or Bezier track, so it can't be converted into a node track.
return false ;
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:26:35 +00:00
} else {
// This isn't a TRS track or a property track, so it can't be converted into a node track.
return false ;
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:26:35 +00:00
// If we reached this point, the track was some kind of TRS track and was successfully converted.
2024-10-04 07:37:44 +00:00
// All failure paths should return false before this point to indicate this
// isn't a node track so it can be handled by KHR_animation_pointer instead.
2024-07-10 08:26:35 +00:00
return true ;
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:26:35 +00:00
void GLTFDocument : : _convert_animation ( Ref < GLTFState > p_state , AnimationPlayer * p_animation_player , const String & p_animation_track_name ) {
2022-12-10 21:05:13 +00:00
Ref < Animation > animation = p_animation_player - > get_animation ( p_animation_track_name ) ;
2020-12-21 15:39:32 +00:00
Ref < GLTFAnimation > gltf_animation ;
2021-06-17 22:03:09 +00:00
gltf_animation . instantiate ( ) ;
2024-02-16 13:25:15 +00:00
gltf_animation - > set_original_name ( p_animation_track_name ) ;
2022-12-10 21:05:13 +00:00
gltf_animation - > set_name ( _gen_unique_name ( p_state , p_animation_track_name ) ) ;
2024-07-10 08:26:35 +00:00
HashMap < int , GLTFAnimation : : NodeTrack > & node_tracks = gltf_animation - > get_node_tracks ( ) ;
for ( int32_t track_index = 0 ; track_index < animation - > get_track_count ( ) ; track_index + + ) {
if ( ! animation - > track_is_enabled ( track_index ) ) {
2020-12-21 15:39:32 +00:00
continue ;
}
2024-07-10 08:26:35 +00:00
// Get the Godot node and the glTF node index for the animation track.
const NodePath track_path = animation - > track_get_path ( track_index ) ;
const Node * anim_player_parent = p_animation_player - > get_parent ( ) ;
const Node * animated_node = anim_player_parent - > get_node_or_null ( track_path ) ;
ERR_CONTINUE_MSG ( ! animated_node , " glTF: Cannot get node for animated track using path: " + String ( track_path ) ) ;
const GLTFAnimation : : Interpolation gltf_interpolation = GLTFAnimation : : godot_to_gltf_interpolation ( animation , track_index ) ;
// First, check if it's a Blend Shape track.
if ( animation - > track_get_type ( track_index ) = = Animation : : TYPE_BLEND_SHAPE ) {
2024-07-10 09:02:03 +00:00
const MeshInstance3D * mesh_instance = Object : : cast_to < MeshInstance3D > ( animated_node ) ;
2024-07-10 08:26:35 +00:00
ERR_CONTINUE_MSG ( ! mesh_instance , " glTF: Animation had a Blend Shape track, but the node wasn't a MeshInstance3D. Ignoring this track. " ) ;
Ref < Mesh > mesh = mesh_instance - > get_mesh ( ) ;
2021-09-04 01:00:59 +00:00
ERR_CONTINUE ( mesh . is_null ( ) ) ;
int32_t mesh_index = - 1 ;
2022-12-10 21:05:13 +00:00
for ( const KeyValue < GLTFNodeIndex , Node * > & mesh_track_i : p_state - > scene_nodes ) {
2024-07-10 08:26:35 +00:00
if ( mesh_track_i . value = = animated_node ) {
2021-08-09 20:13:42 +00:00
mesh_index = mesh_track_i . key ;
2021-09-04 01:00:59 +00:00
}
}
ERR_CONTINUE ( mesh_index = = - 1 ) ;
2024-07-10 08:26:35 +00:00
GLTFAnimation : : NodeTrack track = node_tracks . has ( mesh_index ) ? node_tracks [ mesh_index ] : GLTFAnimation : : NodeTrack ( ) ;
if ( ! node_tracks . has ( mesh_index ) ) {
2021-09-04 01:00:59 +00:00
for ( int32_t shape_i = 0 ; shape_i < mesh - > get_blend_shape_count ( ) ; shape_i + + ) {
String shape_name = mesh - > get_blend_shape_name ( shape_i ) ;
2024-07-10 08:26:35 +00:00
NodePath shape_path = NodePath ( track_path . get_names ( ) , { shape_name } , false ) ;
2021-10-16 01:04:09 +00:00
int32_t shape_track_i = animation - > find_track ( shape_path , Animation : : TYPE_BLEND_SHAPE ) ;
2021-09-04 01:00:59 +00:00
if ( shape_track_i = = - 1 ) {
2021-09-16 06:03:50 +00:00
GLTFAnimation : : Channel < real_t > weight ;
2021-09-04 01:00:59 +00:00
weight . interpolation = GLTFAnimation : : INTERP_LINEAR ;
weight . times . push_back ( 0.0f ) ;
weight . times . push_back ( 0.0f ) ;
weight . values . push_back ( 0.0f ) ;
weight . values . push_back ( 0.0f ) ;
track . weight_tracks . push_back ( weight ) ;
2020-12-21 15:39:32 +00:00
continue ;
}
2021-09-04 01:00:59 +00:00
int32_t key_count = animation - > track_get_key_count ( shape_track_i ) ;
2021-09-16 06:03:50 +00:00
GLTFAnimation : : Channel < real_t > weight ;
2021-09-04 01:00:59 +00:00
weight . interpolation = gltf_interpolation ;
weight . times . resize ( key_count ) ;
for ( int32_t time_i = 0 ; time_i < key_count ; time_i + + ) {
weight . times . write [ time_i ] = animation - > track_get_key_time ( shape_track_i , time_i ) ;
2020-12-21 15:39:32 +00:00
}
2021-09-04 01:00:59 +00:00
weight . values . resize ( key_count ) ;
for ( int32_t value_i = 0 ; value_i < key_count ; value_i + + ) {
weight . values . write [ value_i ] = animation - > track_get_key_value ( shape_track_i , value_i ) ;
2020-12-21 15:39:32 +00:00
}
2021-09-04 01:00:59 +00:00
track . weight_tracks . push_back ( weight ) ;
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:26:35 +00:00
node_tracks [ mesh_index ] = track ;
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:26:35 +00:00
continue ;
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:26:35 +00:00
// If it's not a Blend Shape track, it must either be a TRS track, a property Value track, or something we can't handle.
// For the cases we can handle, we will need to know the glTF node index, glTF interpolation, and the times of the track.
const Vector < StringName > subnames = track_path . get_subnames ( ) ;
const GLTFNodeIndex node_i = _node_and_or_bone_to_gltf_node_index ( p_state , subnames , animated_node ) ;
ERR_CONTINUE_MSG ( node_i = = - 1 , " glTF: Cannot get glTF node index for animated track using path: " + String ( track_path ) ) ;
const int anim_key_count = animation - > track_get_key_count ( track_index ) ;
Vector < double > times ;
times . resize ( anim_key_count ) ;
for ( int32_t key_i = 0 ; key_i < anim_key_count ; key_i + + ) {
times . write [ key_i ] = animation - > track_get_key_time ( track_index , key_i ) ;
}
// Try converting the track to a TRS glTF node track. This will only succeed if the Godot animation is a TRS track.
const HashMap < int , GLTFAnimation : : NodeTrack > : : Iterator node_track_iter = node_tracks . find ( node_i ) ;
GLTFAnimation : : NodeTrack track ;
if ( node_track_iter ) {
track = node_track_iter - > value ;
}
if ( _convert_animation_node_track ( p_state , track , animation , track_index , times ) ) {
// If the track was successfully converted, save it and continue to the next track.
node_tracks [ node_i ] = track ;
continue ;
2020-12-21 15:39:32 +00:00
}
2024-10-04 07:37:44 +00:00
// If the track wasn't a TRS track or Blend Shape track, it might be a Value track animating a property.
// Then this is something that we need to handle with KHR_animation_pointer.
Ref < GLTFObjectModelProperty > obj_model_prop = export_object_model_property ( p_state , track_path , animated_node , node_i ) ;
if ( obj_model_prop . is_valid ( ) & & obj_model_prop - > has_json_pointers ( ) ) {
// Insert the property track into the KHR_animation_pointer pointer tracks.
GLTFAnimation : : Channel < Variant > channel ;
channel . interpolation = gltf_interpolation ;
channel . times = times ;
channel . values . resize ( anim_key_count ) ;
// If using an expression, determine the base instance to pass to the expression.
const Ref < Expression > godot_to_gltf_expr = obj_model_prop - > get_godot_to_gltf_expression ( ) ;
const bool is_godot_to_gltf_expr_valid = godot_to_gltf_expr . is_valid ( ) ;
Object * base_instance = nullptr ;
if ( is_godot_to_gltf_expr_valid ) {
Ref < Resource > resource ;
Vector < StringName > leftover_subpath ;
base_instance = anim_player_parent - > get_node_and_resource ( track_path , resource , leftover_subpath ) ;
if ( resource . is_valid ( ) ) {
base_instance = resource . ptr ( ) ;
}
}
// Convert the Godot animation values into glTF animation values (still Variant).
for ( int32_t key_i = 0 ; key_i < anim_key_count ; key_i + + ) {
Variant value = animation - > track_get_key_value ( track_index , key_i ) ;
if ( is_godot_to_gltf_expr_valid ) {
Array inputs ;
inputs . append ( value ) ;
value = godot_to_gltf_expr - > execute ( inputs , base_instance ) ;
}
channel . values . write [ key_i ] = value ;
}
// Use the JSON pointer to insert the property track into the pointer tracks. There will usually be just one JSON pointer.
HashMap < String , GLTFAnimation : : Channel < Variant > > & pointer_tracks = gltf_animation - > get_pointer_tracks ( ) ;
Vector < PackedStringArray > split_json_pointers = obj_model_prop - > get_json_pointers ( ) ;
for ( const PackedStringArray & split_json_pointer : split_json_pointers ) {
String json_pointer_str = " / " + String ( " / " ) . join ( split_json_pointer ) ;
p_state - > object_model_properties [ json_pointer_str ] = obj_model_prop ;
pointer_tracks [ json_pointer_str ] = channel ;
}
}
2020-12-21 15:39:32 +00:00
}
2024-07-10 08:26:35 +00:00
if ( ! gltf_animation - > is_empty_of_tracks ( ) ) {
2022-12-10 21:05:13 +00:00
p_state - > animations . push_back ( gltf_animation ) ;
2020-12-21 15:39:32 +00:00
}
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse ( Ref < GLTFState > p_state , String p_path , Ref < FileAccess > p_file ) {
2020-12-21 15:39:32 +00:00
Error err ;
2022-12-10 21:05:13 +00:00
if ( p_file . is_null ( ) ) {
2021-10-19 16:06:23 +00:00
return FAILED ;
2020-12-21 15:39:32 +00:00
}
2022-12-10 21:05:13 +00:00
p_file - > seek ( 0 ) ;
uint32_t magic = p_file - > get_32 ( ) ;
2020-12-21 15:39:32 +00:00
if ( magic = = 0x46546C67 ) {
//binary file
//text file
2022-12-10 21:05:13 +00:00
p_file - > seek ( 0 ) ;
err = _parse_glb ( p_file , p_state ) ;
2021-10-19 16:06:23 +00:00
if ( err ! = OK ) {
return err ;
2021-04-05 12:09:59 +00:00
}
2020-12-21 15:39:32 +00:00
} else {
2022-12-10 21:05:13 +00:00
p_file - > seek ( 0 ) ;
String text = p_file - > get_as_utf8_string ( ) ;
2021-10-19 16:06:23 +00:00
JSON json ;
err = json . parse ( text ) ;
if ( err ! = OK ) {
_err_print_error ( " " , " " , json . get_error_line ( ) , json . get_error_message ( ) . utf8 ( ) . get_data ( ) , false , ERR_HANDLER_SCRIPT ) ;
2021-04-05 12:09:59 +00:00
}
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2022-12-10 21:05:13 +00:00
p_state - > json = json . get_data ( ) ;
2020-12-21 15:39:32 +00:00
}
2023-07-10 05:18:55 +00:00
err = _parse_asset_header ( p_state ) ;
ERR_FAIL_COND_V ( err ! = OK , err ) ;
2020-12-21 15:39:32 +00:00
2022-09-19 01:35:13 +00:00
document_extensions . clear ( ) ;
for ( Ref < GLTFDocumentExtension > ext : all_document_extensions ) {
2021-10-19 16:06:23 +00:00
ERR_CONTINUE ( ext . is_null ( ) ) ;
2022-12-10 21:05:13 +00:00
err = ext - > import_preflight ( p_state , p_state - > json [ " extensionsUsed " ] ) ;
2022-09-19 01:35:13 +00:00
if ( err = = OK ) {
document_extensions . push_back ( ext ) ;
}
2021-04-05 12:09:59 +00:00
}
2021-10-04 15:49:42 +00:00
2022-12-10 21:05:13 +00:00
err = _parse_gltf_state ( p_state , p_path ) ;
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( err ! = OK , err ) ;
2021-10-04 15:49:42 +00:00
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-07-23 23:52:46 +00:00
Dictionary _serialize_texture_transform_uv ( Vector2 p_offset , Vector2 p_scale ) {
Dictionary texture_transform ;
bool is_offset = p_offset ! = Vector2 ( 0.0 , 0.0 ) ;
if ( is_offset ) {
2020-12-21 15:39:32 +00:00
Array offset ;
offset . resize ( 2 ) ;
2022-07-23 23:52:46 +00:00
offset [ 0 ] = p_offset . x ;
offset [ 1 ] = p_offset . y ;
2020-12-21 15:39:32 +00:00
texture_transform [ " offset " ] = offset ;
2022-07-23 23:52:46 +00:00
}
bool is_scaled = p_scale ! = Vector2 ( 1.0 , 1.0 ) ;
if ( is_scaled ) {
2020-12-21 15:39:32 +00:00
Array scale ;
scale . resize ( 2 ) ;
2022-07-23 23:52:46 +00:00
scale [ 0 ] = p_scale . x ;
scale [ 1 ] = p_scale . y ;
2020-12-21 15:39:32 +00:00
texture_transform [ " scale " ] = scale ;
2022-07-23 23:52:46 +00:00
}
Dictionary extension ;
// Note: Godot doesn't support texture rotation.
if ( is_offset | | is_scaled ) {
2020-12-21 15:39:32 +00:00
extension [ " KHR_texture_transform " ] = texture_transform ;
}
return extension ;
}
Dictionary GLTFDocument : : _serialize_texture_transform_uv1 ( Ref < BaseMaterial3D > p_material ) {
2024-07-26 09:52:26 +00:00
ERR_FAIL_COND_V ( p_material . is_null ( ) , Dictionary ( ) ) ;
2022-11-23 23:29:01 +00:00
Vector3 offset = p_material - > get_uv1_offset ( ) ;
Vector3 scale = p_material - > get_uv1_scale ( ) ;
return _serialize_texture_transform_uv ( Vector2 ( offset . x , offset . y ) , Vector2 ( scale . x , scale . y ) ) ;
2022-07-23 23:52:46 +00:00
}
Dictionary GLTFDocument : : _serialize_texture_transform_uv2 ( Ref < BaseMaterial3D > p_material ) {
2024-07-26 09:52:26 +00:00
ERR_FAIL_COND_V ( p_material . is_null ( ) , Dictionary ( ) ) ;
2022-11-23 23:29:01 +00:00
Vector3 offset = p_material - > get_uv2_offset ( ) ;
Vector3 scale = p_material - > get_uv2_scale ( ) ;
return _serialize_texture_transform_uv ( Vector2 ( offset . x , offset . y ) , Vector2 ( scale . x , scale . y ) ) ;
2020-12-21 15:39:32 +00:00
}
2023-07-10 05:18:55 +00:00
Error GLTFDocument : : _serialize_asset_header ( Ref < GLTFState > p_state ) {
2020-12-21 15:39:32 +00:00
const String version = " 2.0 " ;
2022-12-10 21:05:13 +00:00
p_state - > major_version = version . get_slice ( " . " , 0 ) . to_int ( ) ;
p_state - > minor_version = version . get_slice ( " . " , 1 ) . to_int ( ) ;
2020-12-21 15:39:32 +00:00
Dictionary asset ;
asset [ " version " ] = version ;
2023-07-10 05:18:55 +00:00
if ( ! p_state - > copyright . is_empty ( ) ) {
asset [ " copyright " ] = p_state - > copyright ;
}
2022-02-08 19:50:37 +00:00
String hash = String ( VERSION_HASH ) ;
asset [ " generator " ] = String ( VERSION_FULL_NAME ) + String ( " @ " ) + ( hash . is_empty ( ) ? String ( " unknown " ) : hash ) ;
2022-12-10 21:05:13 +00:00
p_state - > json [ " asset " ] = asset ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( ! asset . has ( " version " ) , Error : : FAILED ) ;
2022-12-10 21:05:13 +00:00
ERR_FAIL_COND_V ( ! p_state - > json . has ( " asset " ) , Error : : FAILED ) ;
2020-12-21 15:39:32 +00:00
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _serialize_file ( Ref < GLTFState > p_state , const String p_path ) {
2020-12-21 15:39:32 +00:00
Error err = FAILED ;
if ( p_path . to_lower ( ) . ends_with ( " glb " ) ) {
2022-12-10 21:05:13 +00:00
err = _encode_buffer_glb ( p_state , p_path ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( err ! = OK , err ) ;
2022-12-10 21:05:13 +00:00
Ref < FileAccess > file = FileAccess : : open ( p_path , FileAccess : : WRITE , & err ) ;
ERR_FAIL_COND_V ( file . is_null ( ) , FAILED ) ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
String json = Variant ( p_state - > json ) . to_json_string ( ) ;
2020-12-21 15:39:32 +00:00
const uint32_t magic = 0x46546C67 ; // GLTF
const int32_t header_size = 12 ;
const int32_t chunk_header_size = 8 ;
CharString cs = json . utf8 ( ) ;
2021-10-26 03:14:12 +00:00
const uint32_t text_data_length = cs . length ( ) ;
const uint32_t text_chunk_length = ( ( text_data_length + 3 ) & ( ~ 3 ) ) ;
2020-12-21 15:39:32 +00:00
const uint32_t text_chunk_type = 0x4E4F534A ; //JSON
2021-10-26 03:14:12 +00:00
uint32_t binary_data_length = 0 ;
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . size ( ) > 0 ) {
2022-12-10 21:05:13 +00:00
binary_data_length = p_state - > buffers [ 0 ] . size ( ) ;
2020-12-21 15:39:32 +00:00
}
2021-10-26 03:14:12 +00:00
const uint32_t binary_chunk_length = ( ( binary_data_length + 3 ) & ( ~ 3 ) ) ;
const uint32_t binary_chunk_type = 0x004E4942 ; //BIN
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
file - > create ( FileAccess : : ACCESS_RESOURCES ) ;
file - > store_32 ( magic ) ;
file - > store_32 ( p_state - > major_version ) ; // version
2024-05-10 02:23:16 +00:00
uint32_t total_length = header_size + chunk_header_size + text_chunk_length ;
if ( binary_chunk_length ) {
total_length + = chunk_header_size + binary_chunk_length ;
}
file - > store_32 ( total_length ) ;
// Write the JSON text chunk.
2022-12-10 21:05:13 +00:00
file - > store_32 ( text_chunk_length ) ;
file - > store_32 ( text_chunk_type ) ;
file - > store_buffer ( ( uint8_t * ) & cs [ 0 ] , cs . length ( ) ) ;
2021-10-26 03:14:12 +00:00
for ( uint32_t pad_i = text_data_length ; pad_i < text_chunk_length ; pad_i + + ) {
2022-12-10 21:05:13 +00:00
file - > store_8 ( ' ' ) ;
2021-10-26 03:14:12 +00:00
}
2024-05-10 02:23:16 +00:00
// Write a single binary chunk.
2020-12-21 15:39:32 +00:00
if ( binary_chunk_length ) {
2022-12-10 21:05:13 +00:00
file - > store_32 ( binary_chunk_length ) ;
file - > store_32 ( binary_chunk_type ) ;
file - > store_buffer ( p_state - > buffers [ 0 ] . ptr ( ) , binary_data_length ) ;
2024-05-10 02:23:16 +00:00
for ( uint32_t pad_i = binary_data_length ; pad_i < binary_chunk_length ; pad_i + + ) {
file - > store_8 ( 0 ) ;
}
2021-10-26 03:14:12 +00:00
}
2020-12-21 15:39:32 +00:00
} else {
2022-12-10 21:05:13 +00:00
err = _encode_buffer_bins ( p_state , p_path ) ;
2020-12-21 15:39:32 +00:00
ERR_FAIL_COND_V ( err ! = OK , err ) ;
2022-12-10 21:05:13 +00:00
Ref < FileAccess > file = FileAccess : : open ( p_path , FileAccess : : WRITE , & err ) ;
ERR_FAIL_COND_V ( file . is_null ( ) , FAILED ) ;
2020-12-21 15:39:32 +00:00
2022-12-10 21:05:13 +00:00
file - > create ( FileAccess : : ACCESS_RESOURCES ) ;
String json = Variant ( p_state - > json ) . to_json_string ( ) ;
file - > store_string ( json ) ;
2020-12-21 15:39:32 +00:00
}
return err ;
}
2021-09-10 07:54:10 +00:00
void GLTFDocument : : _bind_methods ( ) {
2024-02-16 13:25:15 +00:00
BIND_ENUM_CONSTANT ( ROOT_NODE_MODE_SINGLE_ROOT ) ;
BIND_ENUM_CONSTANT ( ROOT_NODE_MODE_KEEP_ROOT ) ;
BIND_ENUM_CONSTANT ( ROOT_NODE_MODE_MULTI_ROOT ) ;
ClassDB : : bind_method ( D_METHOD ( " set_image_format " , " image_format " ) , & GLTFDocument : : set_image_format ) ;
ClassDB : : bind_method ( D_METHOD ( " get_image_format " ) , & GLTFDocument : : get_image_format ) ;
ClassDB : : bind_method ( D_METHOD ( " set_lossy_quality " , " lossy_quality " ) , & GLTFDocument : : set_lossy_quality ) ;
ClassDB : : bind_method ( D_METHOD ( " get_lossy_quality " ) , & GLTFDocument : : get_lossy_quality ) ;
ClassDB : : bind_method ( D_METHOD ( " set_root_node_mode " , " root_node_mode " ) , & GLTFDocument : : set_root_node_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_root_node_mode " ) , & GLTFDocument : : get_root_node_mode ) ;
2022-11-14 19:14:52 +00:00
ClassDB : : bind_method ( D_METHOD ( " append_from_file " , " path " , " state " , " flags " , " base_path " ) ,
& GLTFDocument : : append_from_file , DEFVAL ( 0 ) , DEFVAL ( String ( ) ) ) ;
ClassDB : : bind_method ( D_METHOD ( " append_from_buffer " , " bytes " , " base_path " , " state " , " flags " ) ,
& GLTFDocument : : append_from_buffer , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " append_from_scene " , " node " , " state " , " flags " ) ,
& GLTFDocument : : append_from_scene , DEFVAL ( 0 ) ) ;
2023-01-29 23:49:55 +00:00
ClassDB : : bind_method ( D_METHOD ( " generate_scene " , " state " , " bake_fps " , " trimming " , " remove_immutable_tracks " ) ,
& GLTFDocument : : generate_scene , DEFVAL ( 30 ) , DEFVAL ( false ) , DEFVAL ( true ) ) ;
2021-10-19 16:06:23 +00:00
ClassDB : : bind_method ( D_METHOD ( " generate_buffer " , " state " ) ,
& GLTFDocument : : generate_buffer ) ;
ClassDB : : bind_method ( D_METHOD ( " write_to_filesystem " , " state " , " path " ) ,
& GLTFDocument : : write_to_filesystem ) ;
2023-07-16 06:03:49 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " image_format " ) , " set_image_format " , " get_image_format " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " lossy_quality " ) , " set_lossy_quality " , " get_lossy_quality " ) ;
2023-07-23 03:36:57 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " root_node_mode " ) , " set_root_node_mode " , " get_root_node_mode " ) ;
2023-07-16 06:03:49 +00:00
2024-10-04 07:19:12 +00:00
ClassDB : : bind_static_method ( " GLTFDocument " , D_METHOD ( " import_object_model_property " , " state " , " json_pointer " ) , & GLTFDocument : : import_object_model_property ) ;
ClassDB : : bind_static_method ( " GLTFDocument " , D_METHOD ( " export_object_model_property " , " state " , " node_path " , " godot_node " , " gltf_node_index " ) , & GLTFDocument : : export_object_model_property ) ;
2022-09-19 01:35:13 +00:00
ClassDB : : bind_static_method ( " GLTFDocument " , D_METHOD ( " register_gltf_document_extension " , " extension " , " first_priority " ) ,
& GLTFDocument : : register_gltf_document_extension , DEFVAL ( false ) ) ;
2022-11-22 20:07:56 +00:00
ClassDB : : bind_static_method ( " GLTFDocument " , D_METHOD ( " unregister_gltf_document_extension " , " extension " ) ,
& GLTFDocument : : unregister_gltf_document_extension ) ;
2024-08-29 00:46:01 +00:00
ClassDB : : bind_static_method ( " GLTFDocument " , D_METHOD ( " get_supported_gltf_extensions " ) ,
& GLTFDocument : : get_supported_gltf_extensions ) ;
2021-09-10 07:54:10 +00:00
}
2022-12-10 21:05:13 +00:00
void GLTFDocument : : _build_parent_hierachy ( Ref < GLTFState > p_state ) {
2021-09-10 07:54:10 +00:00
// build the hierarchy
2022-12-10 21:05:13 +00:00
for ( GLTFNodeIndex node_i = 0 ; node_i < p_state - > nodes . size ( ) ; node_i + + ) {
for ( int j = 0 ; j < p_state - > nodes [ node_i ] - > children . size ( ) ; j + + ) {
GLTFNodeIndex child_i = p_state - > nodes [ node_i ] - > children [ j ] ;
ERR_FAIL_INDEX ( child_i , p_state - > nodes . size ( ) ) ;
if ( p_state - > nodes . write [ child_i ] - > parent ! = - 1 ) {
2021-09-10 07:54:10 +00:00
continue ;
}
2022-12-10 21:05:13 +00:00
p_state - > nodes . write [ child_i ] - > parent = node_i ;
2021-09-10 07:54:10 +00:00
}
}
}
2022-09-19 01:35:13 +00:00
Vector < Ref < GLTFDocumentExtension > > GLTFDocument : : all_document_extensions ;
2021-09-21 01:24:31 +00:00
2022-09-19 01:35:13 +00:00
void GLTFDocument : : register_gltf_document_extension ( Ref < GLTFDocumentExtension > p_extension , bool p_first_priority ) {
2024-05-06 14:20:20 +00:00
if ( ! all_document_extensions . has ( p_extension ) ) {
2022-09-19 01:35:13 +00:00
if ( p_first_priority ) {
all_document_extensions . insert ( 0 , p_extension ) ;
} else {
all_document_extensions . push_back ( p_extension ) ;
}
}
2021-09-21 01:24:31 +00:00
}
2022-11-22 20:07:56 +00:00
void GLTFDocument : : unregister_gltf_document_extension ( Ref < GLTFDocumentExtension > p_extension ) {
all_document_extensions . erase ( p_extension ) ;
}
2022-09-19 01:35:13 +00:00
void GLTFDocument : : unregister_all_gltf_document_extensions ( ) {
all_document_extensions . clear ( ) ;
2021-09-21 01:24:31 +00:00
}
2021-10-19 16:06:23 +00:00
2023-09-26 17:01:26 +00:00
Vector < Ref < GLTFDocumentExtension > > GLTFDocument : : get_all_gltf_document_extensions ( ) {
return all_document_extensions ;
}
2024-08-29 00:46:01 +00:00
Vector < String > GLTFDocument : : get_supported_gltf_extensions ( ) {
HashSet < String > set = get_supported_gltf_extensions_hashset ( ) ;
Vector < String > vec ;
for ( const String & s : set ) {
vec . append ( s ) ;
}
vec . sort ( ) ;
return vec ;
}
HashSet < String > GLTFDocument : : get_supported_gltf_extensions_hashset ( ) {
HashSet < String > supported_extensions ;
// If the extension is supported directly in GLTFDocument, list it here.
// Other built-in extensions are supported by GLTFDocumentExtension classes.
supported_extensions . insert ( " GODOT_single_root " ) ;
2024-10-04 07:37:44 +00:00
supported_extensions . insert ( " KHR_animation_pointer " ) ;
2024-08-29 00:46:01 +00:00
supported_extensions . insert ( " KHR_lights_punctual " ) ;
supported_extensions . insert ( " KHR_materials_emissive_strength " ) ;
supported_extensions . insert ( " KHR_materials_pbrSpecularGlossiness " ) ;
supported_extensions . insert ( " KHR_materials_unlit " ) ;
supported_extensions . insert ( " KHR_texture_transform " ) ;
for ( Ref < GLTFDocumentExtension > ext : all_document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
Vector < String > ext_supported_extensions = ext - > get_supported_extensions ( ) ;
for ( int i = 0 ; i < ext_supported_extensions . size ( ) ; + + i ) {
supported_extensions . insert ( ext_supported_extensions [ i ] ) ;
}
}
return supported_extensions ;
}
2022-12-10 21:05:13 +00:00
PackedByteArray GLTFDocument : : _serialize_glb_buffer ( Ref < GLTFState > p_state , Error * r_err ) {
Error err = _encode_buffer_glb ( p_state , " " ) ;
2021-10-19 16:06:23 +00:00
if ( r_err ) {
* r_err = err ;
}
ERR_FAIL_COND_V ( err ! = OK , PackedByteArray ( ) ) ;
2022-12-10 21:05:13 +00:00
String json = Variant ( p_state - > json ) . to_json_string ( ) ;
2021-10-19 16:06:23 +00:00
const uint32_t magic = 0x46546C67 ; // GLTF
const int32_t header_size = 12 ;
const int32_t chunk_header_size = 8 ;
2023-01-28 23:52:05 +00:00
int32_t padding = ( chunk_header_size + json . utf8 ( ) . length ( ) ) % 4 ;
json + = String ( " " ) . repeat ( padding ) ;
2021-10-19 16:06:23 +00:00
CharString cs = json . utf8 ( ) ;
const uint32_t text_chunk_length = cs . length ( ) ;
const uint32_t text_chunk_type = 0x4E4F534A ; //JSON
int32_t binary_data_length = 0 ;
2024-05-10 02:23:16 +00:00
if ( p_state - > buffers . size ( ) > 0 ) {
2022-12-10 21:05:13 +00:00
binary_data_length = p_state - > buffers [ 0 ] . size ( ) ;
2021-10-19 16:06:23 +00:00
}
const int32_t binary_chunk_length = binary_data_length ;
const int32_t binary_chunk_type = 0x004E4942 ; //BIN
Ref < StreamPeerBuffer > buffer ;
buffer . instantiate ( ) ;
buffer - > put_32 ( magic ) ;
2022-12-10 21:05:13 +00:00
buffer - > put_32 ( p_state - > major_version ) ; // version
2021-10-19 16:06:23 +00:00
buffer - > put_32 ( header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_data_length ) ; // length
buffer - > put_32 ( text_chunk_length ) ;
buffer - > put_32 ( text_chunk_type ) ;
buffer - > put_data ( ( uint8_t * ) & cs [ 0 ] , cs . length ( ) ) ;
if ( binary_chunk_length ) {
buffer - > put_32 ( binary_chunk_length ) ;
buffer - > put_32 ( binary_chunk_type ) ;
2022-12-10 21:05:13 +00:00
buffer - > put_data ( p_state - > buffers [ 0 ] . ptr ( ) , binary_data_length ) ;
2021-10-19 16:06:23 +00:00
}
return buffer - > get_data_array ( ) ;
}
2023-07-22 02:59:39 +00:00
Node * GLTFDocument : : _generate_scene_node_tree ( Ref < GLTFState > p_state ) {
2023-08-20 20:21:12 +00:00
// Generate the skeletons and skins (if any).
2024-02-16 13:25:15 +00:00
HashMap < ObjectID , SkinSkeletonIndex > skeleton_map ;
Error err = SkinTool : : _create_skeletons ( p_state - > unique_names , p_state - > skins , p_state - > nodes ,
skeleton_map , p_state - > skeletons , p_state - > scene_nodes ) ;
2024-08-16 01:07:30 +00:00
ERR_FAIL_COND_V_MSG ( err ! = OK , nullptr , " glTF: Failed to create skeletons. " ) ;
2023-08-20 20:21:12 +00:00
err = _create_skins ( p_state ) ;
2024-08-16 01:07:30 +00:00
ERR_FAIL_COND_V_MSG ( err ! = OK , nullptr , " glTF: Failed to create skins. " ) ;
2024-09-02 10:27:06 +00:00
// Run pre-generate for each extension, in case an extension needs to do something before generating the scene.
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
err = ext - > import_pre_generate ( p_state ) ;
ERR_CONTINUE ( err ! = OK ) ;
}
2023-08-20 20:21:12 +00:00
// Generate the node tree.
2023-07-23 03:36:57 +00:00
Node * single_root ;
if ( p_state - > extensions_used . has ( " GODOT_single_root " ) ) {
_generate_scene_node ( p_state , 0 , nullptr , nullptr ) ;
single_root = p_state - > scene_nodes [ 0 ] ;
2024-04-17 11:11:16 +00:00
if ( single_root & & single_root - > get_owner ( ) & & single_root - > get_owner ( ) ! = single_root ) {
single_root = single_root - > get_owner ( ) ;
}
2023-07-23 03:36:57 +00:00
} else {
single_root = memnew ( Node3D ) ;
for ( int32_t root_i = 0 ; root_i < p_state - > root_nodes . size ( ) ; root_i + + ) {
_generate_scene_node ( p_state , p_state - > root_nodes [ root_i ] , single_root , single_root ) ;
}
2023-07-22 02:59:39 +00:00
}
// Assign the scene name and single root name to each other
// if one is missing, or do nothing if both are already set.
if ( unlikely ( p_state - > scene_name . is_empty ( ) ) ) {
p_state - > scene_name = single_root - > get_name ( ) ;
} else if ( single_root - > get_name ( ) = = StringName ( ) ) {
2023-10-31 22:55:15 +00:00
if ( _naming_version = = 0 ) {
single_root - > set_name ( p_state - > scene_name ) ;
} else {
single_root - > set_name ( _gen_unique_name ( p_state , p_state - > scene_name ) ) ;
}
2023-07-22 02:59:39 +00:00
}
return single_root ;
}
2023-07-10 05:18:55 +00:00
Error GLTFDocument : : _parse_asset_header ( Ref < GLTFState > p_state ) {
if ( ! p_state - > json . has ( " asset " ) ) {
return ERR_PARSE_ERROR ;
}
Dictionary asset = p_state - > json [ " asset " ] ;
if ( ! asset . has ( " version " ) ) {
return ERR_PARSE_ERROR ;
}
String version = asset [ " version " ] ;
p_state - > major_version = version . get_slice ( " . " , 0 ) . to_int ( ) ;
p_state - > minor_version = version . get_slice ( " . " , 1 ) . to_int ( ) ;
if ( asset . has ( " copyright " ) ) {
p_state - > copyright = asset [ " copyright " ] ;
}
return OK ;
}
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_gltf_state ( Ref < GLTFState > p_state , const String & p_search_path ) {
2021-10-19 16:06:23 +00:00
Error err ;
2022-01-08 03:01:09 +00:00
/* PARSE EXTENSIONS */
2022-12-10 21:05:13 +00:00
err = _parse_gltf_extensions ( p_state ) ;
2022-01-08 03:01:09 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
/* PARSE SCENE */
2022-12-10 21:05:13 +00:00
err = _parse_scenes ( p_state ) ;
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2022-01-08 03:01:09 +00:00
/* PARSE NODES */
2022-12-10 21:05:13 +00:00
err = _parse_nodes ( p_state ) ;
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2022-01-08 03:01:09 +00:00
/* PARSE BUFFERS */
2022-12-10 21:05:13 +00:00
err = _parse_buffers ( p_state , p_search_path ) ;
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2022-01-08 03:01:09 +00:00
/* PARSE BUFFER VIEWS */
2022-12-10 21:05:13 +00:00
err = _parse_buffer_views ( p_state ) ;
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2022-01-08 03:01:09 +00:00
/* PARSE ACCESSORS */
2022-12-10 21:05:13 +00:00
err = _parse_accessors ( p_state ) ;
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2022-12-10 21:05:13 +00:00
if ( ! p_state - > discard_meshes_and_materials ) {
2022-04-13 12:14:26 +00:00
/* PARSE IMAGES */
2022-12-10 21:05:13 +00:00
err = _parse_images ( p_state , p_search_path ) ;
2021-10-19 16:06:23 +00:00
2022-04-13 12:14:26 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2021-10-19 16:06:23 +00:00
2021-10-04 15:49:42 +00:00
/* PARSE TEXTURE SAMPLERS */
2022-12-10 21:05:13 +00:00
err = _parse_texture_samplers ( p_state ) ;
2021-10-04 15:49:42 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2022-04-13 12:14:26 +00:00
/* PARSE TEXTURES */
2022-12-10 21:05:13 +00:00
err = _parse_textures ( p_state ) ;
2021-10-19 16:06:23 +00:00
2022-04-13 12:14:26 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2021-10-19 16:06:23 +00:00
2022-04-13 12:14:26 +00:00
/* PARSE TEXTURES */
2022-12-10 21:05:13 +00:00
err = _parse_materials ( p_state ) ;
2021-10-19 16:06:23 +00:00
2022-04-13 12:14:26 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
}
2021-10-19 16:06:23 +00:00
2022-01-08 03:01:09 +00:00
/* PARSE SKINS */
2022-12-10 21:05:13 +00:00
err = _parse_skins ( p_state ) ;
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2022-01-08 03:01:09 +00:00
/* DETERMINE SKELETONS */
2024-02-25 08:36:39 +00:00
err = SkinTool : : _determine_skeletons ( p_state - > skins , p_state - > nodes , p_state - > skeletons , p_state - > get_import_as_skeleton_bones ( ) ? p_state - > root_nodes : Vector < GLTFNodeIndex > ( ) ) ;
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2024-07-10 08:26:35 +00:00
/* ASSIGN SCENE NODE NAMES */
// This must be run AFTER determining skeletons, and BEFORE parsing animations.
_assign_node_names ( p_state ) ;
2022-01-08 03:01:09 +00:00
/* PARSE MESHES (we have enough info now) */
2022-12-10 21:05:13 +00:00
err = _parse_meshes ( p_state ) ;
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2022-01-08 03:01:09 +00:00
/* PARSE LIGHTS */
2022-12-10 21:05:13 +00:00
err = _parse_lights ( p_state ) ;
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2022-01-08 03:01:09 +00:00
/* PARSE CAMERAS */
2022-12-10 21:05:13 +00:00
err = _parse_cameras ( p_state ) ;
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
2022-01-08 03:01:09 +00:00
/* PARSE ANIMATIONS */
2022-12-10 21:05:13 +00:00
err = _parse_animations ( p_state ) ;
2021-10-19 16:06:23 +00:00
ERR_FAIL_COND_V ( err ! = OK , ERR_PARSE_ERROR ) ;
return OK ;
}
2024-02-16 13:25:15 +00:00
PackedByteArray GLTFDocument : : generate_buffer ( Ref < GLTFState > p_state ) {
Ref < GLTFState > state = p_state ;
2024-07-26 09:52:26 +00:00
ERR_FAIL_COND_V ( state . is_null ( ) , PackedByteArray ( ) ) ;
2024-02-16 13:25:15 +00:00
// For buffers, set the state filename to an empty string, but
// don't touch the base path, in case the user set it manually.
state - > filename = " " ;
Error err = _serialize ( state ) ;
ERR_FAIL_COND_V ( err ! = OK , PackedByteArray ( ) ) ;
PackedByteArray bytes = _serialize_glb_buffer ( state , & err ) ;
return bytes ;
}
Error GLTFDocument : : write_to_filesystem ( Ref < GLTFState > p_state , const String & p_path ) {
Ref < GLTFState > state = p_state ;
2024-07-26 09:52:26 +00:00
ERR_FAIL_COND_V ( state . is_null ( ) , ERR_INVALID_PARAMETER ) ;
2024-10-30 09:36:47 +00:00
state - > set_base_path ( p_path . get_base_dir ( ) ) ;
2024-02-16 13:25:15 +00:00
state - > filename = p_path . get_file ( ) ;
Error err = _serialize ( state ) ;
if ( err ! = OK ) {
return err ;
}
err = _serialize_file ( state , p_path ) ;
if ( err ! = OK ) {
return Error : : FAILED ;
}
return OK ;
}
Node * GLTFDocument : : generate_scene ( Ref < GLTFState > p_state , float p_bake_fps , bool p_trimming , bool p_remove_immutable_tracks ) {
Ref < GLTFState > state = p_state ;
2024-07-26 09:52:26 +00:00
ERR_FAIL_COND_V ( state . is_null ( ) , nullptr ) ;
2024-02-16 13:25:15 +00:00
ERR_FAIL_INDEX_V ( 0 , state - > root_nodes . size ( ) , nullptr ) ;
Error err = OK ;
2024-05-23 15:40:26 +00:00
p_state - > set_bake_fps ( p_bake_fps ) ;
2024-02-16 13:25:15 +00:00
Node * root = _generate_scene_node_tree ( state ) ;
ERR_FAIL_NULL_V ( root , nullptr ) ;
_process_mesh_instances ( state , root ) ;
if ( state - > get_create_animations ( ) & & state - > animations . size ( ) ) {
AnimationPlayer * ap = memnew ( AnimationPlayer ) ;
root - > add_child ( ap , true ) ;
ap - > set_owner ( root ) ;
for ( int i = 0 ; i < state - > animations . size ( ) ; i + + ) {
2024-05-23 15:40:26 +00:00
_import_animation ( state , ap , i , p_trimming , p_remove_immutable_tracks ) ;
2024-02-16 13:25:15 +00:00
}
}
for ( KeyValue < GLTFNodeIndex , Node * > E : state - > scene_nodes ) {
ERR_CONTINUE ( ! E . value ) ;
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
Dictionary node_json ;
if ( state - > json . has ( " nodes " ) ) {
Array nodes = state - > json [ " nodes " ] ;
if ( 0 < = E . key & & E . key < nodes . size ( ) ) {
node_json = nodes [ E . key ] ;
}
}
Ref < GLTFNode > gltf_node = state - > nodes [ E . key ] ;
err = ext - > import_node ( p_state , gltf_node , node_json , E . value ) ;
ERR_CONTINUE ( err ! = OK ) ;
}
}
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
err = ext - > import_post ( p_state , root ) ;
ERR_CONTINUE ( err ! = OK ) ;
}
ERR_FAIL_NULL_V ( root , nullptr ) ;
return root ;
}
Error GLTFDocument : : append_from_scene ( Node * p_node , Ref < GLTFState > p_state , uint32_t p_flags ) {
2024-04-10 21:05:54 +00:00
ERR_FAIL_NULL_V ( p_node , FAILED ) ;
2024-02-16 13:25:15 +00:00
Ref < GLTFState > state = p_state ;
ERR_FAIL_COND_V ( state . is_null ( ) , FAILED ) ;
state - > use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS ;
state - > discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS ;
state - > force_generate_tangents = p_flags & GLTF_IMPORT_GENERATE_TANGENT_ARRAYS ;
state - > force_disable_compression = p_flags & GLTF_IMPORT_FORCE_DISABLE_MESH_COMPRESSION ;
if ( ! state - > buffers . size ( ) ) {
state - > buffers . push_back ( Vector < uint8_t > ( ) ) ;
}
// Perform export preflight for document extensions. Only extensions that
// return OK will be used for the rest of the export steps.
document_extensions . clear ( ) ;
for ( Ref < GLTFDocumentExtension > ext : all_document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
Error err = ext - > export_preflight ( state , p_node ) ;
if ( err = = OK ) {
document_extensions . push_back ( ext ) ;
}
}
// Add the root node(s) and their descendants to the state.
if ( _root_node_mode = = RootNodeMode : : ROOT_NODE_MODE_MULTI_ROOT ) {
const int child_count = p_node - > get_child_count ( ) ;
if ( child_count > 0 ) {
for ( int i = 0 ; i < child_count ; i + + ) {
_convert_scene_node ( state , p_node - > get_child ( i ) , - 1 , - 1 ) ;
}
state - > scene_name = p_node - > get_name ( ) ;
return OK ;
}
}
if ( _root_node_mode = = RootNodeMode : : ROOT_NODE_MODE_SINGLE_ROOT ) {
state - > extensions_used . append ( " GODOT_single_root " ) ;
}
_convert_scene_node ( state , p_node , - 1 , - 1 ) ;
2024-09-02 10:27:06 +00:00
// Run post-convert for each extension, in case an extension needs to do something after converting the scene.
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
Error err = ext - > export_post_convert ( p_state , p_node ) ;
ERR_CONTINUE ( err ! = OK ) ;
}
2024-02-16 13:25:15 +00:00
return OK ;
}
Error GLTFDocument : : append_from_buffer ( PackedByteArray p_bytes , String p_base_path , Ref < GLTFState > p_state , uint32_t p_flags ) {
Ref < GLTFState > state = p_state ;
ERR_FAIL_COND_V ( state . is_null ( ) , FAILED ) ;
// TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire
Error err = FAILED ;
state - > use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS ;
state - > discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS ;
state - > force_generate_tangents = p_flags & GLTF_IMPORT_GENERATE_TANGENT_ARRAYS ;
state - > force_disable_compression = p_flags & GLTF_IMPORT_FORCE_DISABLE_MESH_COMPRESSION ;
Ref < FileAccessMemory > file_access ;
file_access . instantiate ( ) ;
file_access - > open_custom ( p_bytes . ptr ( ) , p_bytes . size ( ) ) ;
2024-10-30 09:36:47 +00:00
state - > set_base_path ( p_base_path . get_base_dir ( ) ) ;
2024-02-16 13:25:15 +00:00
err = _parse ( p_state , state - > base_path , file_access ) ;
ERR_FAIL_COND_V ( err ! = OK , err ) ;
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
ERR_CONTINUE ( ext . is_null ( ) ) ;
err = ext - > import_post_parse ( state ) ;
ERR_FAIL_COND_V ( err ! = OK , err ) ;
}
return OK ;
}
2023-08-03 06:18:45 +00:00
Error GLTFDocument : : append_from_file ( String p_path , Ref < GLTFState > p_state , uint32_t p_flags , String p_base_path ) {
2024-02-16 13:25:15 +00:00
Ref < GLTFState > state = p_state ;
2021-10-19 16:06:23 +00:00
// TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire
2024-02-16 13:25:15 +00:00
if ( state = = Ref < GLTFState > ( ) ) {
state . instantiate ( ) ;
2021-10-19 16:06:23 +00:00
}
2024-10-30 09:36:47 +00:00
state - > set_filename ( p_path . get_file ( ) . get_basename ( ) ) ;
2024-02-16 13:25:15 +00:00
state - > use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS ;
state - > discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS ;
state - > force_generate_tangents = p_flags & GLTF_IMPORT_GENERATE_TANGENT_ARRAYS ;
state - > force_disable_compression = p_flags & GLTF_IMPORT_FORCE_DISABLE_MESH_COMPRESSION ;
2023-08-29 19:04:32 +00:00
2021-10-19 16:06:23 +00:00
Error err ;
2022-12-10 21:05:13 +00:00
Ref < FileAccess > file = FileAccess : : open ( p_path , FileAccess : : READ , & err ) ;
2024-10-30 21:42:10 +00:00
ERR_FAIL_COND_V_MSG ( err ! = OK , err , vformat ( R " (Can't open file at path " % s " ) " , p_path ) ) ;
2024-07-26 09:52:26 +00:00
ERR_FAIL_COND_V ( file . is_null ( ) , ERR_FILE_CANT_OPEN ) ;
2021-12-14 05:34:18 +00:00
String base_path = p_base_path ;
if ( base_path . is_empty ( ) ) {
base_path = p_path . get_base_dir ( ) ;
}
2024-10-30 09:36:47 +00:00
state - > set_base_path ( base_path ) ;
2023-08-03 06:18:45 +00:00
err = _parse ( p_state , base_path , file ) ;
2022-04-09 09:35:50 +00:00
ERR_FAIL_COND_V ( err ! = OK , err ) ;
2022-09-19 01:35:13 +00:00
for ( Ref < GLTFDocumentExtension > ext : document_extensions ) {
2022-04-09 09:35:50 +00:00
ERR_CONTINUE ( ext . is_null ( ) ) ;
2023-08-03 06:18:45 +00:00
err = ext - > import_post_parse ( p_state ) ;
2022-04-09 09:35:50 +00:00
ERR_FAIL_COND_V ( err ! = OK , err ) ;
}
return OK ;
2021-10-19 16:06:23 +00:00
}
2022-01-08 03:01:09 +00:00
2022-12-10 21:05:13 +00:00
Error GLTFDocument : : _parse_gltf_extensions ( Ref < GLTFState > p_state ) {
2024-07-26 09:52:26 +00:00
ERR_FAIL_COND_V ( p_state . is_null ( ) , ERR_PARSE_ERROR ) ;
2022-12-10 21:05:13 +00:00
if ( p_state - > json . has ( " extensionsUsed " ) ) {
Vector < String > ext_array = p_state - > json [ " extensionsUsed " ] ;
p_state - > extensions_used = ext_array ;
2022-09-18 03:06:23 +00:00
}
2022-12-10 21:05:13 +00:00
if ( p_state - > json . has ( " extensionsRequired " ) ) {
Vector < String > ext_array = p_state - > json [ " extensionsRequired " ] ;
p_state - > extensions_required = ext_array ;
2022-09-18 03:06:23 +00:00
}
2024-08-29 00:46:01 +00:00
HashSet < String > supported_extensions = get_supported_gltf_extensions_hashset ( ) ;
2022-11-22 15:29:56 +00:00
Error ret = OK ;
2022-12-10 21:05:13 +00:00
for ( int i = 0 ; i < p_state - > extensions_required . size ( ) ; i + + ) {
if ( ! supported_extensions . has ( p_state - > extensions_required [ i ] ) ) {
2024-08-16 01:07:30 +00:00
ERR_PRINT ( " glTF: Can't import file ' " + p_state - > filename + " ', required extension ' " + String ( p_state - > extensions_required [ i ] ) + " ' is not supported. Are you missing a GLTFDocumentExtension plugin? " ) ;
2022-09-19 01:35:13 +00:00
ret = ERR_UNAVAILABLE ;
}
}
return ret ;
2022-01-08 03:01:09 +00:00
}
2023-07-23 03:36:57 +00:00
void GLTFDocument : : set_root_node_mode ( GLTFDocument : : RootNodeMode p_root_node_mode ) {
_root_node_mode = p_root_node_mode ;
}
GLTFDocument : : RootNodeMode GLTFDocument : : get_root_node_mode ( ) const {
return _root_node_mode ;
}
2024-02-16 13:25:15 +00:00
String GLTFDocument : : _gen_unique_name_static ( HashSet < String > & r_unique_names , const String & p_name ) {
const String s_name = p_name . validate_node_name ( ) ;
String u_name ;
int index = 1 ;
while ( true ) {
u_name = s_name ;
if ( index > 1 ) {
u_name + = itos ( index ) ;
}
if ( ! r_unique_names . has ( u_name ) ) {
break ;
}
index + + ;
}
r_unique_names . insert ( u_name ) ;
return u_name ;
}