Browse Source

GLTF imports & exports material texture filters

The Tophat Demon 3 years ago
parent
commit
bb34c55b3c

+ 1 - 0
modules/gltf/config.py

@@ -22,6 +22,7 @@ def get_doc_classes():
         "GLTFSpecGloss",
         "GLTFState",
         "GLTFTexture",
+        "GLTFTextureSampler",
         "PackedSceneGLTF",
     ]
 

+ 13 - 0
modules/gltf/doc_classes/GLTFState.xml

@@ -86,6 +86,12 @@
 			<description>
 			</description>
 		</method>
+		<method name="get_texture_samplers">
+			<return type="Array" />
+			<description>
+				Retrieves the array of texture samplers that are used by the textures contained in the GLTF.
+			</description>
+		</method>
 		<method name="get_textures">
 			<return type="Array" />
 			<description>
@@ -173,6 +179,13 @@
 			<description>
 			</description>
 		</method>
+		<method name="set_texture_samplers">
+			<return type="void" />
+			<argument index="0" name="texture_samplers" type="Array" />
+			<description>
+				Sets the array of texture samplers that are used by the textures contained in the GLTF.
+			</description>
+		</method>
 		<method name="set_textures">
 			<return type="void" />
 			<argument index="0" name="textures" type="Array" />

+ 3 - 0
modules/gltf/doc_classes/GLTFTexture.xml

@@ -10,6 +10,9 @@
 	<methods>
 	</methods>
 	<members>
+		<member name="sampler" type="int" setter="set_sampler" getter="get_sampler" default="-1">
+			ID of the texture sampler to use when sampling the image. If -1, then the default texture sampler is used (linear filtering, and repeat wrapping in both axes).
+		</member>
 		<member name="src_image" type="int" setter="set_src_image" getter="get_src_image" default="0">
 		</member>
 	</members>

+ 29 - 0
modules/gltf/doc_classes/GLTFTextureSampler.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFTextureSampler" inherits="Resource" version="3.6" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+	<brief_description>
+		Represents a GLTF texture sampler
+	</brief_description>
+	<description>
+		Represents a texture sampler as defined by the base GLTF spec. Texture samplers in GLTF specify how to sample data from the texture's base image, when rendering the texture on an object.
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+	</methods>
+	<members>
+		<member name="mag_filter" type="int" setter="set_mag_filter" getter="get_mag_filter" default="9729">
+			Texture's magnification filter, used when the texture appears larger on screen than the source image.
+		</member>
+		<member name="min_filter" type="int" setter="set_min_filter" getter="get_min_filter" default="9987">
+			Texture's minification filter, used when the texture appears smaller on screen than the source image.
+		</member>
+		<member name="wrap_s" type="int" setter="set_wrap_s" getter="get_wrap_s" default="10497">
+			Wrapping mode to use for S-axis (horizontal) texture coordinates.
+		</member>
+		<member name="wrap_t" type="int" setter="set_wrap_t" getter="get_wrap_t" default="10497">
+			Wrapping mode to use for T-axis (vertical) texture coordinates.
+		</member>
+	</members>
+	<constants>
+	</constants>
+</class>

+ 151 - 27
modules/gltf/gltf_document.cpp

@@ -131,26 +131,26 @@ Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &
 		return Error::FAILED;
 	}
 
-	/* STEP 7 SERIALIZE ANIMATIONS */
-	err = _serialize_animations(state);
+	/* STEP 7 SERIALIZE TEXTURE SAMPLERS */
+	err = _serialize_texture_samplers(state);
 	if (err != OK) {
 		return Error::FAILED;
 	}
 
-	/* STEP 8 SERIALIZE ACCESSORS */
-	err = _encode_accessors(state);
+	/* STEP 8 SERIALIZE ANIMATIONS */
+	err = _serialize_animations(state);
 	if (err != OK) {
 		return Error::FAILED;
 	}
 
-	/* STEP 9 SERIALIZE IMAGES */
-	err = _serialize_images(state, p_path);
+	/* STEP 9 SERIALIZE ACCESSORS */
+	err = _encode_accessors(state);
 	if (err != OK) {
 		return Error::FAILED;
 	}
 
-	/* STEP 10 SERIALIZE TEXTURES */
-	err = _serialize_textures(state);
+	/* STEP 10 SERIALIZE IMAGES */
+	err = _serialize_images(state, p_path);
 	if (err != OK) {
 		return Error::FAILED;
 	}
@@ -159,43 +159,43 @@ Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &
 		state->buffer_views.write[i]->buffer = 0;
 	}
 
-	/* STEP 11 SERIALIZE BUFFER VIEWS */
+	/* STEP 12 SERIALIZE BUFFER VIEWS */
 	err = _encode_buffer_views(state);
 	if (err != OK) {
 		return Error::FAILED;
 	}
 
-	/* STEP 12 SERIALIZE NODES */
+	/* STEP 13 SERIALIZE NODES */
 	err = _serialize_nodes(state);
 	if (err != OK) {
 		return Error::FAILED;
 	}
 
-	/* STEP 13 SERIALIZE SCENE */
+	/* STEP 15 SERIALIZE SCENE */
 	err = _serialize_scenes(state);
 	if (err != OK) {
 		return Error::FAILED;
 	}
 
-	/* STEP 14 SERIALIZE SCENE */
+	/* STEP 16 SERIALIZE SCENE */
 	err = _serialize_lights(state);
 	if (err != OK) {
 		return Error::FAILED;
 	}
 
-	/* STEP 15 SERIALIZE EXTENSIONS */
+	/* STEP 17 SERIALIZE EXTENSIONS */
 	err = _serialize_extensions(state);
 	if (err != OK) {
 		return Error::FAILED;
 	}
 
-	/* STEP 16 SERIALIZE VERSION */
+	/* STEP 18 SERIALIZE VERSION */
 	err = _serialize_version(state);
 	if (err != OK) {
 		return Error::FAILED;
 	}
 
-	/* STEP 17 SERIALIZE FILE */
+	/* STEP 19 SERIALIZE FILE */
 	err = _serialize_file(state, p_path);
 	if (err != OK) {
 		return Error::FAILED;
@@ -2946,7 +2946,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> state, const String &p_path
 
 		ERR_CONTINUE(state->images[i].is_null());
 
-		Ref<Image> image = state->images[i]->get_data();
+		Ref<Image> image = state->images[i];
 		ERR_CONTINUE(image.is_null());
 
 		if (p_path.to_lower().ends_with("glb")) {
@@ -3151,11 +3151,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat
 			continue;
 		}
 
-		Ref<ImageTexture> t;
-		t.instance();
-		t->create_from_image(img);
-
-		state->images.push_back(t);
+		state->images.push_back(img);
 	}
 
 	print_verbose("glTF: Total images: " + itos(state->images.size()));
@@ -3174,6 +3170,11 @@ Error GLTFDocument::_serialize_textures(Ref<GLTFState> state) {
 		Ref<GLTFTexture> t = state->textures[i];
 		ERR_CONTINUE(t->get_src_image() == -1);
 		d["source"] = t->get_src_image();
+
+		GLTFTextureSamplerIndex sampler_index = t->get_sampler();
+		if (sampler_index != -1) {
+			d["sampler"] = sampler_index;
+		}
 		textures.push_back(d);
 	}
 	state->json["textures"] = textures;
@@ -3195,7 +3196,24 @@ Error GLTFDocument::_parse_textures(Ref<GLTFState> state) {
 		Ref<GLTFTexture> t;
 		t.instance();
 		t->set_src_image(d["source"]);
+		if (d.has("sampler")) {
+			t->set_sampler(d["sampler"]);
+		} else {
+			t->set_sampler(-1);
+		}
 		state->textures.push_back(t);
+
+		// Create and cache the texture used in the engine
+		Ref<ImageTexture> imgTex;
+		imgTex.instance();
+		imgTex->create_from_image(state->images[t->get_src_image()]);
+
+		// Set texture filter and repeat based on sampler settings
+		const Ref<GLTFTextureSampler> sampler = _get_sampler_for_texture(state, t->get_sampler());
+		Texture::Flags flags = sampler->get_texture_flags();
+		imgTex->set_flags(flags);
+
+		state->texture_cache.insert(i, imgTex);
 	}
 
 	return OK;
@@ -3203,24 +3221,124 @@ Error GLTFDocument::_parse_textures(Ref<GLTFState> state) {
 
 GLTFTextureIndex GLTFDocument::_set_texture(Ref<GLTFState> state, Ref<Texture> p_texture) {
 	ERR_FAIL_COND_V(p_texture.is_null(), -1);
+	ERR_FAIL_COND_V(p_texture->get_data().is_null(), -1);
+
+	// Create GLTF data structures for the new texture
 	Ref<GLTFTexture> gltf_texture;
 	gltf_texture.instance();
-	ERR_FAIL_COND_V(p_texture->get_data().is_null(), -1);
 	GLTFImageIndex gltf_src_image_i = state->images.size();
-	state->images.push_back(p_texture);
+
+	state->images.push_back(p_texture->get_data());
+
+	GLTFTextureSamplerIndex gltf_sampler_i = _set_sampler_for_mode(state, p_texture->get_flags());
+
 	gltf_texture->set_src_image(gltf_src_image_i);
+	gltf_texture->set_sampler(gltf_sampler_i);
+
 	GLTFTextureIndex gltf_texture_i = state->textures.size();
 	state->textures.push_back(gltf_texture);
+	state->texture_cache[gltf_texture_i] = p_texture;
 	return gltf_texture_i;
 }
 
 Ref<Texture> GLTFDocument::_get_texture(Ref<GLTFState> state, const GLTFTextureIndex p_texture) {
 	ERR_FAIL_INDEX_V(p_texture, state->textures.size(), Ref<Texture>());
-	const GLTFImageIndex image = state->textures[p_texture]->get_src_image();
+	return state->texture_cache[p_texture];
+}
+
+GLTFTextureSamplerIndex GLTFDocument::_set_sampler_for_mode(Ref<GLTFState> state, uint32_t p_mode) {
+	for (int i = 0; i < state->texture_samplers.size(); ++i) {
+		if (state->texture_samplers[i]->get_texture_flags() == p_mode) {
+			return i;
+		}
+	}
+
+	GLTFTextureSamplerIndex gltf_sampler_i = state->texture_samplers.size();
+	Ref<GLTFTextureSampler> gltf_sampler;
+	gltf_sampler.instance();
+	gltf_sampler->set_texture_flags(p_mode);
+	state->texture_samplers.push_back(gltf_sampler);
+	return gltf_sampler_i;
+}
+
+Ref<GLTFTextureSampler> GLTFDocument::_get_sampler_for_texture(Ref<GLTFState> state, const GLTFTextureIndex p_texture) {
+	ERR_FAIL_INDEX_V(p_texture, state->textures.size(), Ref<Texture>());
+	const GLTFTextureSamplerIndex sampler = state->textures[p_texture]->get_sampler();
+
+	if (sampler == -1) {
+		return state->default_texture_sampler;
+	} else {
+		ERR_FAIL_INDEX_V(sampler, state->texture_samplers.size(), Ref<GLTFTextureSampler>());
+
+		return state->texture_samplers[sampler];
+	}
+}
+
+Error GLTFDocument::_serialize_texture_samplers(Ref<GLTFState> state) {
+	if (!state->texture_samplers.size()) {
+		return OK;
+	}
+
+	Array samplers;
+	for (int32_t i = 0; i < state->texture_samplers.size(); ++i) {
+		Dictionary d;
+		Ref<GLTFTextureSampler> s = state->texture_samplers[i];
+		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);
+	}
+	state->json["samplers"] = samplers;
+
+	return OK;
+}
+
+Error GLTFDocument::_parse_texture_samplers(Ref<GLTFState> state) {
+	state->default_texture_sampler.instance();
+	state->default_texture_sampler->set_min_filter(GLTFTextureSampler::FilterMode::LINEAR_MIPMAP_LINEAR);
+	state->default_texture_sampler->set_mag_filter(GLTFTextureSampler::FilterMode::LINEAR);
+	state->default_texture_sampler->set_wrap_s(GLTFTextureSampler::WrapMode::REPEAT);
+	state->default_texture_sampler->set_wrap_t(GLTFTextureSampler::WrapMode::REPEAT);
+
+	if (!state->json.has("samplers")) {
+		return OK;
+	}
+
+	const Array &samplers = state->json["samplers"];
+	for (int i = 0; i < samplers.size(); ++i) {
+		const Dictionary &d = samplers[i];
+
+		Ref<GLTFTextureSampler> sampler;
+		sampler.instance();
+
+		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);
+		}
 
-	ERR_FAIL_INDEX_V(image, state->images.size(), Ref<Texture>());
+		if (d.has("wrapS")) {
+			sampler->set_wrap_s(d["wrapS"]);
+		} else {
+			sampler->set_wrap_s(GLTFTextureSampler::WrapMode::REPEAT);
+		}
+
+		if (d.has("wrapT")) {
+			sampler->set_wrap_t(d["wrapT"]);
+		} else {
+			sampler->set_wrap_t(GLTFTextureSampler::WrapMode::REPEAT);
+		}
 
-	return state->images[image];
+		state->texture_samplers.push_back(sampler);
+	}
+
+	return OK;
 }
 
 Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
@@ -6645,13 +6763,19 @@ Error GLTFDocument::parse(Ref<GLTFState> state, String p_path, bool p_read_binar
 		return Error::FAILED;
 	}
 
+	/* PARSE TEXTURE SAMPLERS */
+	err = _parse_texture_samplers(state);
+	if (err != OK) {
+		return Error::FAILED;
+	}
+
 	/* PARSE TEXTURES */
 	err = _parse_textures(state);
 	if (err != OK) {
 		return Error::FAILED;
 	}
 
-	/* PARSE TEXTURES */
+	/* PARSE MATERIALS */
 	err = _parse_materials(state);
 	if (err != OK) {
 		return Error::FAILED;

+ 8 - 0
modules/gltf/gltf_document.h

@@ -50,6 +50,7 @@ class GLTFSkin;
 class GLTFNode;
 class GLTFSpecGloss;
 class GLTFSkeleton;
+class GLTFTextureSampler;
 class MultiMeshInstance;
 
 #ifdef MODULE_CSG_ENABLED
@@ -72,6 +73,7 @@ using GLTFNodeIndex = int;
 using GLTFSkeletonIndex = int;
 using GLTFSkinIndex = int;
 using GLTFTextureIndex = int;
+using GLTFTextureSamplerIndex = int;
 
 class GLTFDocument : public Resource {
 	GDCLASS(GLTFDocument, Resource);
@@ -183,6 +185,10 @@ private:
 	GLTFTextureIndex _set_texture(Ref<GLTFState> state, Ref<Texture> p_texture);
 	Ref<Texture> _get_texture(Ref<GLTFState> state,
 			const GLTFTextureIndex p_texture);
+	GLTFTextureSamplerIndex _set_sampler_for_mode(Ref<GLTFState> state,
+			uint32_t p_mode);
+	Ref<GLTFTextureSampler> _get_sampler_for_texture(Ref<GLTFState> state,
+			const GLTFTextureIndex p_texture);
 	Error _parse_json(const String &p_path, Ref<GLTFState> state);
 	Error _parse_glb(const String &p_path, Ref<GLTFState> state);
 	void _compute_node_heights(Ref<GLTFState> state);
@@ -230,10 +236,12 @@ private:
 			const bool p_for_vertex);
 	Error _parse_meshes(Ref<GLTFState> state);
 	Error _serialize_textures(Ref<GLTFState> state);
+	Error _serialize_texture_samplers(Ref<GLTFState> state);
 	Error _serialize_images(Ref<GLTFState> state, const String &p_path);
 	Error _serialize_lights(Ref<GLTFState> state);
 	Error _parse_images(Ref<GLTFState> state, const String &p_base_path);
 	Error _parse_textures(Ref<GLTFState> state);
+	Error _parse_texture_samplers(Ref<GLTFState> state);
 	Error _parse_materials(Ref<GLTFState> state);
 	void _set_texture_transform_uv1(const Dictionary &d, Ref<SpatialMaterial> material);
 	void spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss,

+ 11 - 0
modules/gltf/gltf_state.cpp

@@ -61,6 +61,8 @@ void GLTFState::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_root_nodes", "root_nodes"), &GLTFState::set_root_nodes);
 	ClassDB::bind_method(D_METHOD("get_textures"), &GLTFState::get_textures);
 	ClassDB::bind_method(D_METHOD("set_textures", "textures"), &GLTFState::set_textures);
+	ClassDB::bind_method(D_METHOD("get_texture_samplers"), &GLTFState::get_texture_samplers);
+	ClassDB::bind_method(D_METHOD("set_texture_samplers", "texture_samplers"), &GLTFState::set_texture_samplers);
 	ClassDB::bind_method(D_METHOD("get_images"), &GLTFState::get_images);
 	ClassDB::bind_method(D_METHOD("set_images", "images"), &GLTFState::set_images);
 	ClassDB::bind_method(D_METHOD("get_skins"), &GLTFState::get_skins);
@@ -95,6 +97,7 @@ void GLTFState::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_name"), "set_scene_name", "get_scene_name"); // String
 	ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "root_nodes"), "set_root_nodes", "get_root_nodes"); // Vector<int>
 	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_textures", "get_textures"); // Vector<Ref<GLTFTexture>>
+	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "texture_samplers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_texture_samplers", "get_texture_samplers"); //Vector<Ref<GLTFTextureSampler>>
 	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "images", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_images", "get_images"); // Vector<Ref<Texture>
 	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "skins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skins", "get_skins"); // Vector<Ref<GLTFSkin>>
 	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "cameras", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_cameras", "get_cameras"); // Vector<Ref<GLTFCamera>>
@@ -218,6 +221,14 @@ void GLTFState::set_textures(Array p_textures) {
 	GLTFDocument::set_from_array(textures, p_textures);
 }
 
+Array GLTFState::get_texture_samplers() {
+	return GLTFDocument::to_array(texture_samplers);
+}
+
+void GLTFState::set_texture_samplers(Array p_texture_samplers) {
+	GLTFDocument::set_from_array(texture_samplers, p_texture_samplers);
+}
+
 Array GLTFState::get_images() {
 	return GLTFDocument::to_array(images);
 }

+ 10 - 1
modules/gltf/gltf_state.h

@@ -48,6 +48,9 @@
 #include "gltf_skeleton.h"
 #include "gltf_skin.h"
 #include "gltf_texture.h"
+#include "gltf_texture_sampler.h"
+#include "scene/animation/animation_player.h"
+#include "scene/resources/texture.h"
 
 class GLTFState : public Resource {
 	GDCLASS(GLTFState, Resource);
@@ -78,7 +81,10 @@ class GLTFState : public Resource {
 	String scene_name;
 	Vector<int> root_nodes;
 	Vector<Ref<GLTFTexture>> textures;
-	Vector<Ref<Texture>> images;
+	Vector<Ref<GLTFTextureSampler>> texture_samplers;
+	Ref<GLTFTextureSampler> default_texture_sampler;
+	Vector<Ref<Image>> images;
+	Map<GLTFTextureIndex, Ref<Texture>> texture_cache;
 
 	Vector<Ref<GLTFSkin>> skins;
 	Vector<Ref<GLTFCamera>> cameras;
@@ -140,6 +146,9 @@ public:
 	Array get_textures();
 	void set_textures(Array p_textures);
 
+	Array get_texture_samplers();
+	void set_texture_samplers(Array p_texture_samplers);
+
 	Array get_images();
 	void set_images(Array p_images);
 

+ 11 - 0
modules/gltf/gltf_texture.cpp

@@ -33,8 +33,11 @@
 void GLTFTexture::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_src_image"), &GLTFTexture::get_src_image);
 	ClassDB::bind_method(D_METHOD("set_src_image", "src_image"), &GLTFTexture::set_src_image);
+	ClassDB::bind_method(D_METHOD("get_sampler"), &GLTFTexture::get_sampler);
+	ClassDB::bind_method(D_METHOD("set_sampler", "sampler"), &GLTFTexture::set_sampler);
 
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "src_image"), "set_src_image", "get_src_image"); // int
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "sampler"), "set_sampler", "get_sampler"); // int
 }
 
 GLTFImageIndex GLTFTexture::get_src_image() const {
@@ -44,3 +47,11 @@ GLTFImageIndex GLTFTexture::get_src_image() const {
 void GLTFTexture::set_src_image(GLTFImageIndex val) {
 	src_image = val;
 }
+
+GLTFTextureSamplerIndex GLTFTexture::get_sampler() const {
+	return sampler;
+}
+
+void GLTFTexture::set_sampler(GLTFTextureSamplerIndex val) {
+	sampler = val;
+}

+ 3 - 0
modules/gltf/gltf_texture.h

@@ -40,6 +40,7 @@ class GLTFTexture : public Resource {
 
 private:
 	GLTFImageIndex src_image = 0;
+	GLTFTextureSamplerIndex sampler = -1;
 
 protected:
 	static void _bind_methods();
@@ -47,6 +48,8 @@ protected:
 public:
 	GLTFImageIndex get_src_image() const;
 	void set_src_image(GLTFImageIndex val);
+	GLTFTextureSamplerIndex get_sampler() const;
+	void set_sampler(GLTFTextureSamplerIndex val);
 };
 
 #endif // GLTF_TEXTURE_H

+ 47 - 0
modules/gltf/gltf_texture_sampler.cpp

@@ -0,0 +1,47 @@
+/*************************************************************************/
+/*  gltf_texture_sampler.cpp                                             */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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_texture_sampler.h"
+
+void GLTFTextureSampler::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("get_mag_filter"), &GLTFTextureSampler::get_mag_filter);
+	ClassDB::bind_method(D_METHOD("set_mag_filter", "filter_mode"), &GLTFTextureSampler::set_mag_filter);
+	ClassDB::bind_method(D_METHOD("get_min_filter"), &GLTFTextureSampler::get_min_filter);
+	ClassDB::bind_method(D_METHOD("set_min_filter", "filter_mode"), &GLTFTextureSampler::set_min_filter);
+	ClassDB::bind_method(D_METHOD("get_wrap_s"), &GLTFTextureSampler::get_wrap_s);
+	ClassDB::bind_method(D_METHOD("set_wrap_s", "wrap_mode"), &GLTFTextureSampler::set_wrap_s);
+	ClassDB::bind_method(D_METHOD("get_wrap_t"), &GLTFTextureSampler::get_wrap_t);
+	ClassDB::bind_method(D_METHOD("set_wrap_t", "wrap_mode"), &GLTFTextureSampler::set_wrap_t);
+
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "mag_filter"), "set_mag_filter", "get_mag_filter");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "min_filter"), "set_min_filter", "get_min_filter");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_s"), "set_wrap_s", "get_wrap_s");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_t"), "set_wrap_t", "get_wrap_t");
+}

+ 165 - 0
modules/gltf/gltf_texture_sampler.h

@@ -0,0 +1,165 @@
+/*************************************************************************/
+/*  gltf_texture_sampler.h                                               */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* 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.                */
+/*************************************************************************/
+
+#ifndef GLTF_TEXTURE_SAMPLER_H
+#define GLTF_TEXTURE_SAMPLER_H
+
+#include "core/resource.h"
+#include "scene/resources/texture.h"
+
+class GLTFTextureSampler : public Resource {
+	GDCLASS(GLTFTextureSampler, Resource);
+
+public:
+	enum FilterMode {
+		NEAREST = 9728,
+		LINEAR = 9729,
+		NEAREST_MIPMAP_NEAREST = 9984,
+		LINEAR_MIPMAP_NEAREST = 9985,
+		NEAREST_MIPMAP_LINEAR = 9986,
+		LINEAR_MIPMAP_LINEAR = 9987
+	};
+
+	enum WrapMode {
+		CLAMP_TO_EDGE = 33071,
+		MIRRORED_REPEAT = 33648,
+		REPEAT = 10497
+	};
+
+	int get_mag_filter() const {
+		return mag_filter;
+	}
+
+	void set_mag_filter(const int filter_mode) {
+		mag_filter = (FilterMode)filter_mode;
+	}
+
+	int get_min_filter() const {
+		return min_filter;
+	}
+
+	void set_min_filter(const int filter_mode) {
+		min_filter = (FilterMode)filter_mode;
+	}
+
+	int get_wrap_s() const {
+		return wrap_s;
+	}
+
+	void set_wrap_s(const int wrap_mode) {
+		wrap_s = (WrapMode)wrap_mode;
+	}
+
+	int get_wrap_t() const {
+		return wrap_t;
+	}
+
+	void set_wrap_t(const int wrap_mode) {
+		wrap_s = (WrapMode)wrap_mode;
+	}
+
+	Texture::Flags get_filter_mode() const {
+		switch (min_filter) {
+			case NEAREST:
+				return (Texture::Flags)0;
+			case LINEAR:
+				return Texture::Flags::FLAG_FILTER;
+			case NEAREST_MIPMAP_NEAREST:
+			case NEAREST_MIPMAP_LINEAR:
+				return Texture::Flags::FLAG_MIPMAPS;
+			case LINEAR_MIPMAP_NEAREST:
+			case LINEAR_MIPMAP_LINEAR:
+			default:
+				return (Texture::Flags)(Texture::Flags::FLAG_FILTER | Texture::Flags::FLAG_MIPMAPS);
+		}
+	}
+
+	void set_filter_mode(uint32_t flags) {
+		const bool filter = (flags & Texture::Flags::FLAG_FILTER);
+		const bool mipmaps = (flags & Texture::Flags::FLAG_MIPMAPS);
+
+		if (filter && mipmaps) {
+			min_filter = FilterMode::LINEAR_MIPMAP_LINEAR;
+			mag_filter = FilterMode::LINEAR;
+		} else if (filter) {
+			min_filter = FilterMode::LINEAR;
+			mag_filter = FilterMode::LINEAR;
+		} else if (mipmaps) {
+			min_filter = FilterMode::NEAREST_MIPMAP_LINEAR;
+			mag_filter = FilterMode::NEAREST;
+		} else {
+			min_filter = FilterMode::NEAREST;
+			mag_filter = FilterMode::NEAREST;
+		}
+	}
+
+	Texture::Flags get_wrap_mode() const {
+		if ((wrap_s == WrapMode::MIRRORED_REPEAT) && (wrap_t == WrapMode::MIRRORED_REPEAT)) {
+			return Texture::Flags::FLAG_MIRRORED_REPEAT;
+		} else if ((wrap_s == WrapMode::REPEAT) && (wrap_t == WrapMode::REPEAT)) {
+			return Texture::Flags::FLAG_REPEAT;
+		} else {
+			return (Texture::Flags)0;
+		}
+	}
+
+	void set_wrap_mode(uint32_t flags) {
+		if (flags & Texture::Flags::FLAG_MIRRORED_REPEAT) {
+			wrap_s = WrapMode::MIRRORED_REPEAT;
+			wrap_t = WrapMode::MIRRORED_REPEAT;
+		} else if (flags & Texture::Flags::FLAG_REPEAT) {
+			wrap_s = WrapMode::REPEAT;
+			wrap_t = WrapMode::REPEAT;
+		} else {
+			wrap_s = WrapMode::CLAMP_TO_EDGE;
+			wrap_t = WrapMode::CLAMP_TO_EDGE;
+		}
+	}
+
+	Texture::Flags get_texture_flags() const {
+		return (Texture::Flags)(get_filter_mode() | get_wrap_mode());
+	}
+
+	void set_texture_flags(uint32_t flags) {
+		set_filter_mode(flags);
+		set_wrap_mode(flags);
+	}
+
+protected:
+	static void _bind_methods();
+
+private:
+	FilterMode mag_filter = FilterMode::LINEAR;
+	FilterMode min_filter = FilterMode::LINEAR_MIPMAP_LINEAR;
+	WrapMode wrap_s = WrapMode::REPEAT;
+	WrapMode wrap_t = WrapMode::REPEAT;
+};
+
+#endif // GLTF_TEXTURE_SAMPLER_H

+ 1 - 0
modules/gltf/register_types.cpp

@@ -78,6 +78,7 @@ void register_gltf_types() {
 	ClassDB::register_class<GLTFBufferView>();
 	ClassDB::register_class<GLTFAccessor>();
 	ClassDB::register_class<GLTFTexture>();
+	ClassDB::register_class<GLTFTextureSampler>();
 	ClassDB::register_class<GLTFSkeleton>();
 	ClassDB::register_class<GLTFSkin>();
 	ClassDB::register_class<GLTFCamera>();