GLTF: Move accessor and buffer view Dictionary conversion into classes

This commit is contained in:
Aaron Franke 2025-07-05 15:01:00 -07:00
parent fc7065d2b4
commit 02182b1087
No known key found for this signature in database
GPG key ID: 40A1750B977E56BF
9 changed files with 274 additions and 169 deletions

View file

@ -31,6 +31,8 @@
#include "gltf_accessor.h"
#include "gltf_accessor.compat.inc"
#include "../gltf_state.h"
void GLTFAccessor::_bind_methods() {
BIND_ENUM_CONSTANT(TYPE_SCALAR);
BIND_ENUM_CONSTANT(TYPE_VEC2);
@ -53,6 +55,9 @@ void GLTFAccessor::_bind_methods() {
BIND_ENUM_CONSTANT(COMPONENT_TYPE_SIGNED_LONG);
BIND_ENUM_CONSTANT(COMPONENT_TYPE_UNSIGNED_LONG);
ClassDB::bind_static_method("GLTFAccessor", D_METHOD("from_dictionary", "dictionary"), &GLTFAccessor::from_dictionary);
ClassDB::bind_method(D_METHOD("to_dictionary"), &GLTFAccessor::to_dictionary);
ClassDB::bind_method(D_METHOD("get_buffer_view"), &GLTFAccessor::get_buffer_view);
ClassDB::bind_method(D_METHOD("set_buffer_view", "buffer_view"), &GLTFAccessor::set_buffer_view);
ClassDB::bind_method(D_METHOD("get_byte_offset"), &GLTFAccessor::get_byte_offset);
@ -101,6 +106,8 @@ void GLTFAccessor::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_values_byte_offset"), "set_sparse_values_byte_offset", "get_sparse_values_byte_offset"); // int
}
// Property getters and setters.
GLTFBufferViewIndex GLTFAccessor::get_buffer_view() const {
return buffer_view;
}
@ -220,3 +227,146 @@ int64_t GLTFAccessor::get_sparse_values_byte_offset() const {
void GLTFAccessor::set_sparse_values_byte_offset(int64_t p_sparse_values_byte_offset) {
sparse_values_byte_offset = p_sparse_values_byte_offset;
}
// Trivial helper functions.
GLTFAccessor::GLTFAccessorType GLTFAccessor::_get_accessor_type_from_str(const String &p_string) {
if (p_string == "SCALAR") {
return GLTFAccessor::TYPE_SCALAR;
}
if (p_string == "VEC2") {
return GLTFAccessor::TYPE_VEC2;
}
if (p_string == "VEC3") {
return GLTFAccessor::TYPE_VEC3;
}
if (p_string == "VEC4") {
return GLTFAccessor::TYPE_VEC4;
}
if (p_string == "MAT2") {
return GLTFAccessor::TYPE_MAT2;
}
if (p_string == "MAT3") {
return GLTFAccessor::TYPE_MAT3;
}
if (p_string == "MAT4") {
return GLTFAccessor::TYPE_MAT4;
}
ERR_FAIL_V(GLTFAccessor::TYPE_SCALAR);
}
String GLTFAccessor::_get_accessor_type_name() const {
switch (accessor_type) {
case GLTFAccessor::TYPE_SCALAR:
return "SCALAR";
case GLTFAccessor::TYPE_VEC2:
return "VEC2";
case GLTFAccessor::TYPE_VEC3:
return "VEC3";
case GLTFAccessor::TYPE_VEC4:
return "VEC4";
case GLTFAccessor::TYPE_MAT2:
return "MAT2";
case GLTFAccessor::TYPE_MAT3:
return "MAT3";
case GLTFAccessor::TYPE_MAT4:
return "MAT4";
default:
break;
}
ERR_FAIL_V("SCALAR");
}
// Dictionary conversion.
Ref<GLTFAccessor> GLTFAccessor::from_dictionary(const Dictionary &p_dict) {
// See https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/accessor.schema.json
Ref<GLTFAccessor> accessor;
accessor.instantiate();
if (p_dict.has("bufferView")) {
// bufferView is optional. If not present, the accessor is considered to be zero-initialized.
accessor->buffer_view = p_dict["bufferView"];
}
if (p_dict.has("byteOffset")) {
accessor->byte_offset = p_dict["byteOffset"];
}
if (p_dict.has("componentType")) {
accessor->component_type = (GLTFAccessor::GLTFComponentType)(int32_t)p_dict["componentType"];
}
if (p_dict.has("count")) {
accessor->count = p_dict["count"];
}
if (accessor->count <= 0) {
ERR_PRINT("glTF import: Invalid accessor count " + itos(accessor->count) + " for accessor. Accessor count must be greater than 0.");
}
if (p_dict.has("max")) {
accessor->max = p_dict["max"];
}
if (p_dict.has("min")) {
accessor->min = p_dict["min"];
}
if (p_dict.has("normalized")) {
accessor->normalized = p_dict["normalized"];
}
if (p_dict.has("sparse")) {
// See https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/accessor.sparse.schema.json
const Dictionary &sparse_dict = p_dict["sparse"];
ERR_FAIL_COND_V(!sparse_dict.has("count"), accessor);
accessor->sparse_count = sparse_dict["count"];
ERR_FAIL_COND_V(!sparse_dict.has("indices"), accessor);
const Dictionary &sparse_indices_dict = sparse_dict["indices"];
ERR_FAIL_COND_V(!sparse_indices_dict.has("bufferView"), accessor);
accessor->sparse_indices_buffer_view = sparse_indices_dict["bufferView"];
ERR_FAIL_COND_V(!sparse_indices_dict.has("componentType"), accessor);
accessor->sparse_indices_component_type = (GLTFAccessor::GLTFComponentType)(int32_t)sparse_indices_dict["componentType"];
if (sparse_indices_dict.has("byteOffset")) {
accessor->sparse_indices_byte_offset = sparse_indices_dict["byteOffset"];
}
ERR_FAIL_COND_V(!sparse_dict.has("values"), accessor);
const Dictionary &sparse_values_dict = sparse_dict["values"];
ERR_FAIL_COND_V(!sparse_values_dict.has("bufferView"), accessor);
accessor->sparse_values_buffer_view = sparse_values_dict["bufferView"];
if (sparse_values_dict.has("byteOffset")) {
accessor->sparse_values_byte_offset = sparse_values_dict["byteOffset"];
}
}
accessor->accessor_type = _get_accessor_type_from_str(p_dict["type"]);
return accessor;
}
Dictionary GLTFAccessor::to_dictionary() const {
Dictionary dict;
if (buffer_view != -1) {
// bufferView may be omitted to zero-initialize the buffer. When this happens, byteOffset MUST also be omitted.
if (byte_offset > 0) {
dict["byteOffset"] = byte_offset;
}
dict["bufferView"] = buffer_view;
}
dict["componentType"] = component_type;
dict["count"] = count;
dict["max"] = max;
dict["min"] = min;
dict["normalized"] = normalized;
dict["type"] = _get_accessor_type_name();
if (sparse_count > 0) {
Dictionary sparse_indices_dict;
sparse_indices_dict["bufferView"] = sparse_indices_buffer_view;
sparse_indices_dict["componentType"] = sparse_indices_component_type;
if (sparse_indices_byte_offset > 0) {
sparse_indices_dict["byteOffset"] = sparse_indices_byte_offset;
}
Dictionary sparse_values_dict;
sparse_values_dict["bufferView"] = sparse_values_buffer_view;
if (sparse_values_byte_offset > 0) {
sparse_values_dict["byteOffset"] = sparse_values_byte_offset;
}
Dictionary sparse_dict;
sparse_dict["count"] = sparse_count;
sparse_dict["indices"] = sparse_indices_dict;
sparse_dict["values"] = sparse_values_dict;
dict["sparse"] = sparse_dict;
}
return dict;
}

View file

@ -32,9 +32,9 @@
#include "../gltf_defines.h"
#include "core/io/resource.h"
#include "gltf_buffer_view.h"
struct GLTFAccessor : public Resource {
class GLTFAccessor : public Resource {
GDCLASS(GLTFAccessor, Resource);
friend class GLTFDocument;
@ -80,6 +80,10 @@ private:
GLTFBufferViewIndex sparse_values_buffer_view = 0;
int64_t sparse_values_byte_offset = 0;
// Trivial helper functions.
static GLTFAccessor::GLTFAccessorType _get_accessor_type_from_str(const String &p_string);
String _get_accessor_type_name() const;
protected:
static void _bind_methods();
@ -106,6 +110,7 @@ protected:
#endif // DISABLE_DEPRECATED
public:
// Property getters and setters.
GLTFBufferViewIndex get_buffer_view() const;
void set_buffer_view(GLTFBufferViewIndex p_buffer_view);
@ -150,6 +155,10 @@ public:
int64_t get_sparse_values_byte_offset() const;
void set_sparse_values_byte_offset(int64_t p_sparse_values_byte_offset);
// Dictionary conversion.
static Ref<GLTFAccessor> from_dictionary(const Dictionary &p_dict);
Dictionary to_dictionary() const;
};
VARIANT_ENUM_CAST(GLTFAccessor::GLTFAccessorType);

View file

@ -36,6 +36,9 @@
void GLTFBufferView::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_buffer_view_data", "state"), &GLTFBufferView::load_buffer_view_data);
ClassDB::bind_static_method("GLTFBufferView", D_METHOD("from_dictionary", "dictionary"), &GLTFBufferView::from_dictionary);
ClassDB::bind_method(D_METHOD("to_dictionary"), &GLTFBufferView::to_dictionary);
ClassDB::bind_method(D_METHOD("get_buffer"), &GLTFBufferView::get_buffer);
ClassDB::bind_method(D_METHOD("set_buffer", "buffer"), &GLTFBufferView::set_buffer);
ClassDB::bind_method(D_METHOD("get_byte_offset"), &GLTFBufferView::get_byte_offset);
@ -105,12 +108,58 @@ void GLTFBufferView::set_vertex_attributes(bool p_attributes) {
vertex_attributes = p_attributes;
}
Vector<uint8_t> GLTFBufferView::load_buffer_view_data(const Ref<GLTFState> p_state) const {
ERR_FAIL_COND_V(p_state.is_null(), Vector<uint8_t>());
Vector<uint8_t> GLTFBufferView::load_buffer_view_data(const Ref<GLTFState> p_gltf_state) const {
ERR_FAIL_COND_V(p_gltf_state.is_null(), Vector<uint8_t>());
ERR_FAIL_COND_V_MSG(byte_stride > 0, Vector<uint8_t>(), "Buffer views with byte stride are not yet supported by this method.");
const TypedArray<Vector<uint8_t>> &buffers = p_state->get_buffers();
const TypedArray<Vector<uint8_t>> &buffers = p_gltf_state->get_buffers();
ERR_FAIL_INDEX_V(buffer, buffers.size(), Vector<uint8_t>());
const PackedByteArray &buffer_data = buffers[buffer];
const int64_t byte_end = byte_offset + byte_length;
return buffer_data.slice(byte_offset, byte_end);
}
Ref<GLTFBufferView> GLTFBufferView::from_dictionary(const Dictionary &p_dict) {
// See https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/bufferView.schema.json
Ref<GLTFBufferView> buffer_view;
buffer_view.instantiate();
if (p_dict.has("buffer")) {
buffer_view->set_buffer(p_dict["buffer"]);
}
if (p_dict.has("byteLength")) {
buffer_view->set_byte_length(p_dict["byteLength"]);
}
if (p_dict.has("byteOffset")) {
buffer_view->set_byte_offset(p_dict["byteOffset"]);
}
if (p_dict.has("byteStride")) {
buffer_view->byte_stride = p_dict["byteStride"];
if (buffer_view->byte_stride < 4 || buffer_view->byte_stride > 252 || buffer_view->byte_stride % 4 != 0) {
ERR_PRINT("glTF import: Invalid byte stride " + itos(buffer_view->byte_stride) + " for buffer view. If defined, byte stride must be a multiple of 4 and between 4 and 252.");
}
}
if (p_dict.has("target")) {
const int target = p_dict["target"];
buffer_view->indices = target == ArrayBufferTarget::TARGET_ELEMENT_ARRAY_BUFFER;
buffer_view->vertex_attributes = target == ArrayBufferTarget::TARGET_ARRAY_BUFFER;
}
return buffer_view;
}
Dictionary GLTFBufferView::to_dictionary() const {
Dictionary dict;
ERR_FAIL_COND_V_MSG(buffer == -1, dict, "Buffer index must be set to a valid buffer before converting to Dictionary.");
dict["buffer"] = buffer;
dict["byteLength"] = byte_length;
if (byte_offset != 0) {
dict["byteOffset"] = byte_offset;
}
if (byte_stride != -1) {
dict["byteStride"] = byte_stride;
}
if (indices) {
dict["target"] = ArrayBufferTarget::TARGET_ELEMENT_ARRAY_BUFFER;
} else if (vertex_attributes) {
dict["target"] = ArrayBufferTarget::TARGET_ARRAY_BUFFER;
}
return dict;
}

View file

@ -38,13 +38,24 @@ class GLTFBufferView : public Resource {
GDCLASS(GLTFBufferView, Resource);
friend class GLTFDocument;
public:
// When a buffer view is used by vertex indices or attribute accessors it SHOULD specify
// "target" with a value of ELEMENT_ARRAY_BUFFER (34963) or ARRAY_BUFFER (34962) respectively.
// This is only used for mesh data. For non-mesh buffer views, the target should be left blank.
// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#buffers-and-buffer-views-overview
enum ArrayBufferTarget {
TARGET_NONE = 0,
TARGET_ARRAY_BUFFER = 34962,
TARGET_ELEMENT_ARRAY_BUFFER = 34963,
};
private:
GLTFBufferIndex buffer = -1;
int64_t byte_offset = 0;
int64_t byte_length = 0;
int64_t byte_stride = -1;
bool indices = false;
bool vertex_attributes = false;
bool indices = false; // True for TARGET_ELEMENT_ARRAY_BUFFER.
bool vertex_attributes = false; // True for TARGET_ARRAY_BUFFER.
protected:
static void _bind_methods();
@ -78,5 +89,8 @@ public:
bool get_vertex_attributes() const;
void set_vertex_attributes(bool p_attributes);
Vector<uint8_t> load_buffer_view_data(const Ref<GLTFState> p_state) const;
Vector<uint8_t> load_buffer_view_data(const Ref<GLTFState> p_gltf_state) const;
static Ref<GLTFBufferView> from_dictionary(const Dictionary &p_dict);
Dictionary to_dictionary() const;
};