diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml
index 03123097490..9461fe90a2c 100644
--- a/doc/classes/ArrayMesh.xml
+++ b/doc/classes/ArrayMesh.xml
@@ -47,7 +47,7 @@
-
+
Creates a new surface.
diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml
index 78bd3106ece..fbf0233074c 100644
--- a/doc/classes/Mesh.xml
+++ b/doc/classes/Mesh.xml
@@ -207,8 +207,11 @@
Flag used to mark that the array uses 16-bit bones instead of 8-bit.
-
- Used to set flags [constant ARRAY_COMPRESS_VERTEX], [constant ARRAY_COMPRESS_NORMAL], [constant ARRAY_COMPRESS_TANGENT], [constant ARRAY_COMPRESS_COLOR], [constant ARRAY_COMPRESS_TEX_UV], [constant ARRAY_COMPRESS_TEX_UV2] and [constant ARRAY_COMPRESS_WEIGHTS] quickly.
+
+ Flag used to mark that the array uses an octahedral representation of normal and tangent vectors rather than cartesian.
+
+
+ Used to set flags [constant ARRAY_COMPRESS_VERTEX], [constant ARRAY_COMPRESS_NORMAL], [constant ARRAY_COMPRESS_TANGENT], [constant ARRAY_COMPRESS_COLOR], [constant ARRAY_COMPRESS_TEX_UV], [constant ARRAY_COMPRESS_TEX_UV2], [constant ARRAY_COMPRESS_WEIGHTS], and [constant ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION] quickly.
Array of vertices.
diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml
index 77afc403013..02f5fc9c526 100644
--- a/doc/classes/SurfaceTool.xml
+++ b/doc/classes/SurfaceTool.xml
@@ -167,11 +167,11 @@
-
+
Returns a constructed [ArrayMesh] from current information passed in. If an existing [ArrayMesh] is passed in as an argument, will add an extra surface to the existing [ArrayMesh].
- Default flag is [constant Mesh.ARRAY_COMPRESS_DEFAULT]. See [code]ARRAY_COMPRESS_*[/code] constants in [enum Mesh.ArrayFormat] for other flags.
+ Default flag is [constant Mesh.ARRAY_COMPRESS_DEFAULT] if compression is enabled. If compression is disabled the default flag is [constant Mesh.ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION]. See [code]ARRAY_COMPRESS_*[/code] constants in [enum Mesh.ArrayFormat] for other flags.
diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml
index 8ec4baf8f10..34528ef47a8 100644
--- a/doc/classes/VisualServer.xml
+++ b/doc/classes/VisualServer.xml
@@ -2518,7 +2518,7 @@
-
+
Adds a surface generated from the Arrays to a mesh. See [enum PrimitiveType] constants for types.
@@ -4489,8 +4489,11 @@
Flag used to mark that the array uses 16-bit bones instead of 8-bit.
-
- Used to set flags [constant ARRAY_COMPRESS_NORMAL], [constant ARRAY_COMPRESS_TANGENT], [constant ARRAY_COMPRESS_COLOR], [constant ARRAY_COMPRESS_TEX_UV], [constant ARRAY_COMPRESS_TEX_UV2] and [constant ARRAY_COMPRESS_WEIGHTS] quickly.
+
+ Flag used to mark that the array uses an octahedral representation of normal and tangent vectors rather than cartesian.
+
+
+ Used to set flags [constant ARRAY_COMPRESS_NORMAL], [constant ARRAY_COMPRESS_TANGENT], [constant ARRAY_COMPRESS_COLOR], [constant ARRAY_COMPRESS_TEX_UV], [constant ARRAY_COMPRESS_TEX_UV2], [constant ARRAY_COMPRESS_WEIGHTS], and [constant ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION] quickly.
Primitive to draw consists of points.
diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp
index f6d12371aa5..9574d8f2809 100644
--- a/drivers/gles2/rasterizer_scene_gles2.cpp
+++ b/drivers/gles2/rasterizer_scene_gles2.cpp
@@ -2417,6 +2417,8 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
RasterizerStorageGLES2::Skeleton *prev_skeleton = nullptr;
RasterizerStorageGLES2::GeometryOwner *prev_owner = nullptr;
+ bool prev_octahedral_compression = false;
+
Transform view_transform_inverse = p_view_transform.inverse();
CameraMatrix projection_inverse = p_projection.inverse();
@@ -2666,6 +2668,12 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
storage->info.render.surface_switch_count++;
}
+ bool octahedral_compression = ((RasterizerStorageGLES2::Surface *)e->geometry)->format & VisualServer::ArrayFormat::ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION;
+ if (octahedral_compression != prev_octahedral_compression) {
+ state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_OCTAHEDRAL_COMPRESSION, octahedral_compression);
+ rebind = true;
+ }
+
bool shader_rebind = false;
if (rebind || material != prev_material) {
storage->info.render.material_switch_count++;
@@ -2783,6 +2791,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
prev_material = material;
prev_skeleton = skeleton;
prev_instancing = instancing;
+ prev_octahedral_compression = octahedral_compression;
prev_light = light;
prev_refprobe_1 = refprobe_1;
prev_refprobe_2 = refprobe_2;
@@ -2791,6 +2800,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements,
}
_setup_light_type(nullptr, nullptr); //clear light stuff
+ state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_OCTAHEDRAL_COMPRESSION, false);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, false);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADELESS, false);
state.scene_shader.set_conditional(SceneShaderGLES2::BASE_PASS, false);
diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp
index 17a59c0fdab..42aea361800 100644
--- a/drivers/gles2/rasterizer_storage_gles2.cpp
+++ b/drivers/gles2/rasterizer_storage_gles2.cpp
@@ -2098,22 +2098,42 @@ static PoolVector _unpack_half_floats(const PoolVector &array,
} break;
case VS::ARRAY_NORMAL: {
- if (p_format & VS::ARRAY_COMPRESS_NORMAL) {
- src_size[i] = 4;
- dst_size[i] = 4;
+ if (p_format & VS::ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
+ if (p_format & VS::ARRAY_COMPRESS_NORMAL) {
+ src_size[i] = 2;
+ dst_size[i] = 2;
+ } else {
+ src_size[i] = 4;
+ dst_size[i] = 4;
+ }
} else {
- src_size[i] = 12;
- dst_size[i] = 12;
+ if (p_format & VS::ARRAY_COMPRESS_NORMAL) {
+ src_size[i] = 4;
+ dst_size[i] = 4;
+ } else {
+ src_size[i] = 12;
+ dst_size[i] = 12;
+ }
}
} break;
case VS::ARRAY_TANGENT: {
- if (p_format & VS::ARRAY_COMPRESS_TANGENT) {
- src_size[i] = 4;
- dst_size[i] = 4;
+ if (p_format & VS::ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
+ if (p_format & VS::ARRAY_COMPRESS_TANGENT) {
+ src_size[i] = 2;
+ dst_size[i] = 2;
+ } else {
+ src_size[i] = 4;
+ dst_size[i] = 4;
+ }
} else {
- src_size[i] = 16;
- dst_size[i] = 16;
+ if (p_format & VS::ARRAY_COMPRESS_TANGENT) {
+ src_size[i] = 4;
+ dst_size[i] = 4;
+ } else {
+ src_size[i] = 16;
+ dst_size[i] = 16;
+ }
}
} break;
@@ -2288,30 +2308,54 @@ void RasterizerStorageGLES2::mesh_add_surface(RID p_mesh, uint32_t p_format, VS:
} break;
case VS::ARRAY_NORMAL: {
- attribs[i].size = 3;
-
- if (p_format & VS::ARRAY_COMPRESS_NORMAL) {
- attribs[i].type = GL_BYTE;
- attributes_stride += 4; //pad extra byte
+ if (p_format & VS::ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
attribs[i].normalized = GL_TRUE;
+ attribs[i].size = 2;
+ if (p_format & VS::ARRAY_COMPRESS_NORMAL) {
+ attribs[i].type = GL_BYTE;
+ attributes_stride += 2;
+ } else {
+ attribs[i].type = GL_SHORT;
+ attributes_stride += 4;
+ }
} else {
- attribs[i].type = GL_FLOAT;
- attributes_stride += 12;
- attribs[i].normalized = GL_FALSE;
+ attribs[i].size = 3;
+
+ if (p_format & VS::ARRAY_COMPRESS_NORMAL) {
+ attribs[i].type = GL_BYTE;
+ attributes_stride += 4; //pad extra byte
+ attribs[i].normalized = GL_TRUE;
+ } else {
+ attribs[i].type = GL_FLOAT;
+ attributes_stride += 12;
+ attribs[i].normalized = GL_FALSE;
+ }
}
} break;
case VS::ARRAY_TANGENT: {
- attribs[i].size = 4;
-
- if (p_format & VS::ARRAY_COMPRESS_TANGENT) {
- attribs[i].type = GL_BYTE;
- attributes_stride += 4;
+ if (p_format & VS::ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
attribs[i].normalized = GL_TRUE;
+ attribs[i].size = 2;
+ if (p_format & VS::ARRAY_COMPRESS_TANGENT) {
+ attribs[i].type = GL_BYTE;
+ attributes_stride += 2;
+ } else {
+ attribs[i].type = GL_SHORT;
+ attributes_stride += 4;
+ }
} else {
- attribs[i].type = GL_FLOAT;
- attributes_stride += 16;
- attribs[i].normalized = GL_FALSE;
+ attribs[i].size = 4;
+
+ if (p_format & VS::ARRAY_COMPRESS_TANGENT) {
+ attribs[i].type = GL_BYTE;
+ attributes_stride += 4;
+ attribs[i].normalized = GL_TRUE;
+ } else {
+ attribs[i].type = GL_FLOAT;
+ attributes_stride += 16;
+ attribs[i].normalized = GL_FALSE;
+ }
}
} break;
diff --git a/drivers/gles2/shader_gles2.cpp b/drivers/gles2/shader_gles2.cpp
index 845a1d5e0bc..09ccfcc4737 100644
--- a/drivers/gles2/shader_gles2.cpp
+++ b/drivers/gles2/shader_gles2.cpp
@@ -195,7 +195,7 @@ ShaderGLES2::Version *ShaderGLES2::get_current_version() {
}
for (int j = 0; j < conditional_count; j++) {
- bool enable = (conditional_version.version & (1 << j)) > 0;
+ bool enable = (conditional_version.version & (uint64_t(1) << j)) > 0;
if (enable) {
strings.push_back(conditional_defines[j]);
@@ -488,8 +488,8 @@ void ShaderGLES2::setup(
int p_fragment_code_start) {
ERR_FAIL_COND(version);
- conditional_version.key = 0;
- new_conditional_version.key = 0;
+ memset(conditional_version.key, 0, sizeof(conditional_version.key));
+ memset(new_conditional_version.key, 0, sizeof(new_conditional_version.key));
uniform_count = p_uniform_count;
conditional_count = p_conditional_count;
conditional_defines = p_conditional_defines;
@@ -634,7 +634,7 @@ void ShaderGLES2::free_custom_shader(uint32_t p_code_id) {
VersionKey key;
key.code_version = p_code_id;
- for (Set::Element *E = custom_code_map[p_code_id].versions.front(); E; E = E->next()) {
+ for (Set::Element *E = custom_code_map[p_code_id].versions.front(); E; E = E->next()) {
key.version = E->get();
ERR_CONTINUE(!version_map.has(key));
Version &v = version_map[key];
diff --git a/drivers/gles2/shader_gles2.h b/drivers/gles2/shader_gles2.h
index a74f5531f03..0300e8b7031 100644
--- a/drivers/gles2/shader_gles2.h
+++ b/drivers/gles2/shader_gles2.h
@@ -99,7 +99,7 @@ private:
Vector texture_uniforms;
Vector custom_uniforms;
Vector custom_defines;
- Set versions;
+ Set versions;
};
struct Version {
@@ -125,16 +125,16 @@ private:
union VersionKey {
struct {
- uint32_t version;
+ uint64_t version;
uint32_t code_version;
};
- uint64_t key;
- bool operator==(const VersionKey &p_key) const { return key == p_key.key; }
- bool operator<(const VersionKey &p_key) const { return key < p_key.key; }
+ unsigned char key[12];
+ bool operator==(const VersionKey &p_key) const { return version == p_key.version && code_version == p_key.code_version; }
+ bool operator<(const VersionKey &p_key) const { return version < p_key.version || (version == p_key.version && code_version < p_key.code_version); }
};
struct VersionKeyHash {
- static _FORCE_INLINE_ uint32_t hash(const VersionKey &p_key) { return HashMapHasherDefault::hash(p_key.key); }
+ static _FORCE_INLINE_ uint32_t hash(const VersionKey &p_key) { return hash_djb2_buffer(p_key.key, sizeof(p_key.key)); }
};
//this should use a way more cachefriendly version..
@@ -222,13 +222,13 @@ public:
void set_custom_shader(uint32_t p_code_id);
void free_custom_shader(uint32_t p_code_id);
- uint32_t get_version_key() const { return conditional_version.version; }
+ uint64_t get_version_key() const { return conditional_version.version; }
// this void* is actually a RasterizerStorageGLES2::Material, but C++ doesn't
// like forward declared nested classes.
void use_material(void *p_material);
- _FORCE_INLINE_ uint32_t get_version() const { return new_conditional_version.version; }
+ _FORCE_INLINE_ uint64_t get_version() const { return new_conditional_version.version; }
_FORCE_INLINE_ bool is_version_valid() const { return version && version->ok; }
virtual void init() = 0;
@@ -261,10 +261,12 @@ int ShaderGLES2::_get_uniform(int p_which) const {
void ShaderGLES2::_set_conditional(int p_which, bool p_value) {
ERR_FAIL_INDEX(p_which, conditional_count);
+ ERR_FAIL_INDEX(static_cast(p_which), sizeof(new_conditional_version.version) * 8)
+
if (p_value) {
- new_conditional_version.version |= (1 << p_which);
+ new_conditional_version.version |= (uint64_t(1) << p_which);
} else {
- new_conditional_version.version &= ~(1 << p_which);
+ new_conditional_version.version &= ~(uint64_t(1) << p_which);
}
}
diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl
index 9f8d1cbf5a8..12a6bbb55ac 100644
--- a/drivers/gles2/shaders/scene.glsl
+++ b/drivers/gles2/shaders/scene.glsl
@@ -31,11 +31,19 @@ precision highp int;
attribute highp vec4 vertex_attrib; // attrib:0
/* clang-format on */
+#ifdef ENABLE_OCTAHEDRAL_COMPRESSION
+attribute vec2 normal_attrib; // attrib:1
+#else
attribute vec3 normal_attrib; // attrib:1
+#endif
#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
+#ifdef ENABLE_OCTAHEDRAL_COMPRESSION
+attribute vec2 tangent_attrib; // attrib:2
+#else
attribute vec4 tangent_attrib; // attrib:2
#endif
+#endif
#if defined(ENABLE_COLOR_INTERP)
attribute vec4 color_attrib; // attrib:3
@@ -102,6 +110,15 @@ uniform float light_normal_bias;
uniform highp int view_index;
+#ifdef ENABLE_OCTAHEDRAL_COMPRESSION
+vec3 oct_to_vec3(vec2 e) {
+ vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
+ float t = max(-v.z, 0.0);
+ v.xy += t * -sign(v.xy);
+ return v;
+}
+#endif
+
//
// varyings
//
@@ -341,11 +358,20 @@ void main() {
#endif
+#ifdef ENABLE_OCTAHEDRAL_COMPRESSION
+ vec3 normal = oct_to_vec3(normal_attrib);
+#else
vec3 normal = normal_attrib;
+#endif
#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP)
+#ifdef ENABLE_OCTAHEDRAL_COMPRESSION
+ vec3 tangent = oct_to_vec3(vec2(tangent_attrib.x, abs(tangent_attrib.y) * 2.0 - 1.0));
+ float binormalf = sign(tangent_attrib.y);
+#else
vec3 tangent = tangent_attrib.xyz;
float binormalf = tangent_attrib.a;
+#endif
vec3 binormal = normalize(cross(normal, tangent) * binormalf);
#endif
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 43db3a13313..c91f92c0762 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -1944,6 +1944,7 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
bool first = true;
bool prev_use_instancing = false;
+ bool prev_octahedral_compression = false;
storage->info.render.draw_call_count += p_element_count;
bool prev_opaque_prepass = false;
@@ -2108,6 +2109,12 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
}
}
+ bool octahedral_compression = ((RasterizerStorageGLES3::Surface *)e->geometry)->format & VisualServer::ArrayFormat::ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION;
+ if (octahedral_compression != prev_octahedral_compression) {
+ state.scene_shader.set_conditional(SceneShaderGLES3::ENABLE_OCTAHEDRAL_COMPRESSION, octahedral_compression);
+ rebind = true;
+ }
+
if (material != prev_material || rebind) {
storage->info.render.material_switch_count++;
@@ -2140,12 +2147,14 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
prev_shading = shading;
prev_skeleton = skeleton;
prev_use_instancing = use_instancing;
+ prev_octahedral_compression = octahedral_compression;
prev_opaque_prepass = use_opaque_prepass;
first = false;
}
glBindVertexArray(0);
+ state.scene_shader.remove_custom_define("#define ENABLE_OCTAHEDRAL_COMPRESSION\n");
state.scene_shader.set_conditional(SceneShaderGLES3::USE_INSTANCING, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_SKELETON, false);
state.scene_shader.set_conditional(SceneShaderGLES3::USE_RADIANCE_MAP, false);
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index d6233db3572..802af73017e 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -3396,30 +3396,54 @@ void RasterizerStorageGLES3::mesh_add_surface(RID p_mesh, uint32_t p_format, VS:
} break;
case VS::ARRAY_NORMAL: {
- attribs[i].size = 3;
-
- if (p_format & VS::ARRAY_COMPRESS_NORMAL) {
- attribs[i].type = GL_BYTE;
- attributes_stride += 4; //pad extra byte
+ if (p_format & VS::ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
attribs[i].normalized = GL_TRUE;
+ attribs[i].size = 2;
+ if (p_format & VS::ARRAY_COMPRESS_NORMAL) {
+ attribs[i].type = GL_BYTE;
+ attributes_stride += 2;
+ } else {
+ attribs[i].type = GL_SHORT;
+ attributes_stride += 4;
+ }
} else {
- attribs[i].type = GL_FLOAT;
- attributes_stride += 12;
- attribs[i].normalized = GL_FALSE;
+ attribs[i].size = 3;
+
+ if (p_format & VS::ARRAY_COMPRESS_NORMAL) {
+ attribs[i].type = GL_BYTE;
+ attributes_stride += 4; //pad extra byte
+ attribs[i].normalized = GL_TRUE;
+ } else {
+ attribs[i].type = GL_FLOAT;
+ attributes_stride += 12;
+ attribs[i].normalized = GL_FALSE;
+ }
}
} break;
case VS::ARRAY_TANGENT: {
- attribs[i].size = 4;
-
- if (p_format & VS::ARRAY_COMPRESS_TANGENT) {
- attribs[i].type = GL_BYTE;
- attributes_stride += 4;
+ if (p_format & VS::ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
attribs[i].normalized = GL_TRUE;
+ attribs[i].size = 2;
+ if (p_format & VS::ARRAY_COMPRESS_TANGENT) {
+ attribs[i].type = GL_BYTE;
+ attributes_stride += 2;
+ } else {
+ attribs[i].type = GL_SHORT;
+ attributes_stride += 4;
+ }
} else {
- attribs[i].type = GL_FLOAT;
- attributes_stride += 16;
- attribs[i].normalized = GL_FALSE;
+ attribs[i].size = 4;
+
+ if (p_format & VS::ARRAY_COMPRESS_TANGENT) {
+ attribs[i].type = GL_BYTE;
+ attributes_stride += 4;
+ attribs[i].normalized = GL_TRUE;
+ } else {
+ attribs[i].type = GL_FLOAT;
+ attributes_stride += 16;
+ attribs[i].normalized = GL_FALSE;
+ }
}
} break;
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index e894beb159f..8de8b478b7d 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -25,10 +25,18 @@ ARRAY_INDEX=8,
layout(location = 0) in highp vec4 vertex_attrib;
/* clang-format on */
+#ifdef ENABLE_OCTAHEDRAL_COMPRESSION
+layout(location = 1) in vec2 normal_attrib;
+#else
layout(location = 1) in vec3 normal_attrib;
+#endif
#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
+#ifdef ENABLE_OCTAHEDRAL_COMPRESSION
+layout(location = 2) in vec2 tangent_attrib;
+#else
layout(location = 2) in vec4 tangent_attrib;
#endif
+#endif
#if defined(ENABLE_COLOR_INTERP)
layout(location = 3) in vec4 color_attrib;
@@ -251,6 +259,15 @@ void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, float r
#endif
+#ifdef ENABLE_OCTAHEDRAL_COMPRESSION
+vec3 oct_to_vec3(vec2 e) {
+ vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
+ float t = max(-v.z, 0.0);
+ v.xy += t * -sign(v.xy);
+ return v;
+}
+#endif
+
/* Varyings */
out highp vec3 vertex_interp;
@@ -322,12 +339,21 @@ void main() {
}
#endif
+#ifdef ENABLE_OCTAHEDRAL_COMPRESSION
+ vec3 normal = oct_to_vec3(normal_attrib);
+#else
vec3 normal = normal_attrib;
+#endif
#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
+#ifdef ENABLE_OCTAHEDRAL_COMPRESSION
+ vec3 tangent = oct_to_vec3(vec2(tangent_attrib.x, abs(tangent_attrib.y) * 2.0 - 1.0));
+ float binormalf = sign(tangent_attrib.y);
+#else
vec3 tangent = tangent_attrib.xyz;
float binormalf = tangent_attrib.a;
#endif
+#endif
#if defined(ENABLE_COLOR_INTERP)
color_interp = color_attrib;
@@ -338,7 +364,6 @@ void main() {
#endif
#if defined(ENABLE_TANGENT_INTERP) || defined(ENABLE_NORMALMAP) || defined(LIGHT_USE_ANISOTROPY)
-
vec3 binormal = normalize(cross(normal, tangent) * binormalf);
#endif
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index 13802974ab7..cdf6c6a807c 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -928,7 +928,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref &p_me
mr.push_back(a);
}
- p_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, d, mr, p_use_compression ? Mesh::ARRAY_COMPRESS_DEFAULT : 0);
+ p_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, d, mr, p_use_compression ? Mesh::ARRAY_COMPRESS_DEFAULT : Mesh::ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION);
if (material.is_valid()) {
if (p_use_mesh_material) {
diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp
index b6d86f10801..882d43f79b8 100644
--- a/editor/import/resource_importer_obj.cpp
+++ b/editor/import/resource_importer_obj.cpp
@@ -210,7 +210,7 @@ static Error _parse_obj(const String &p_path, List[> &r_meshes, bool p_
bool generate_tangents = p_generate_tangents;
Vector3 scale_mesh = p_scale_mesh;
Vector3 offset_mesh = p_offset_mesh;
- int mesh_flags = p_optimize ? Mesh::ARRAY_COMPRESS_DEFAULT : 0;
+ int mesh_flags = p_optimize ? Mesh::ARRAY_COMPRESS_DEFAULT : Mesh::ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION;
Vector vertices;
Vector normals;
diff --git a/modules/fbx/data/fbx_mesh_data.cpp b/modules/fbx/data/fbx_mesh_data.cpp
index bae75edd0fb..75c28bf4f44 100644
--- a/modules/fbx/data/fbx_mesh_data.cpp
+++ b/modules/fbx/data/fbx_mesh_data.cpp
@@ -386,7 +386,7 @@ MeshInstance *FBXMeshData::create_fbx_mesh(const ImportState &state, const FBXDo
Mesh::PRIMITIVE_TRIANGLES,
surface->surface_tool->commit_to_arrays(),
surface->morphs,
- use_compression ? Mesh::ARRAY_COMPRESS_DEFAULT : 0);
+ use_compression ? Mesh::ARRAY_COMPRESS_DEFAULT : Mesh::ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION);
if (surface->material.is_valid()) {
mesh->surface_set_name(in_mesh_surface_id, surface->material->get_name());
diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp
index 389d581be90..ede29ecad0d 100644
--- a/scene/3d/soft_body.cpp
+++ b/scene/3d/soft_body.cpp
@@ -84,7 +84,12 @@ void SoftBodyVisualServerHandler::set_vertex(int p_vertex_id, const void *p_vect
}
void SoftBodyVisualServerHandler::set_normal(int p_vertex_id, const void *p_vector3) {
- memcpy(&write_buffer[p_vertex_id * stride + offset_normal], p_vector3, sizeof(float) * 3);
+ Vector2 normal_oct = VisualServer::get_singleton()->norm_to_oct(*(Vector3 *)p_vector3);
+ int16_t v_normal[2] = {
+ (int16_t)CLAMP(normal_oct.x * 32767, -32768, 32767),
+ (int16_t)CLAMP(normal_oct.y * 32767, -32768, 32767),
+ };
+ memcpy(&write_buffer[p_vertex_id * stride + offset_normal], v_normal, sizeof(uint16_t) * 2);
}
void SoftBodyVisualServerHandler::set_aabb(const AABB &p_aabb) {
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 21fd0e0fe00..6f2eeae8afe 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -534,18 +534,16 @@ void Sprite3D::_draw() {
// Everything except position and UV is compressed
PoolVector::Write write_buffer = mesh_buffer.write();
- int8_t v_normal[4] = {
- (int8_t)CLAMP(normal.x * 127, -128, 127),
- (int8_t)CLAMP(normal.y * 127, -128, 127),
- (int8_t)CLAMP(normal.z * 127, -128, 127),
- 0,
+ Vector2 normal_oct = VisualServer::get_singleton()->norm_to_oct(normal);
+ int8_t v_normal[2] = {
+ (int8_t)CLAMP(normal_oct.x * 127, -128, 127),
+ (int8_t)CLAMP(normal_oct.y * 127, -128, 127),
};
- int8_t v_tangent[4] = {
- (int8_t)CLAMP(tangent.normal.x * 127, -128, 127),
- (int8_t)CLAMP(tangent.normal.y * 127, -128, 127),
- (int8_t)CLAMP(tangent.normal.z * 127, -128, 127),
- (int8_t)CLAMP(tangent.d * 127, -128, 127)
+ Vector2 tangent_oct = VisualServer::get_singleton()->tangent_to_oct(tangent.normal, tangent.d, false);
+ int8_t v_tangent[2] = {
+ (int8_t)CLAMP(tangent_oct.x * 127, -128, 127),
+ (int8_t)CLAMP(tangent_oct.y * 127, -128, 127),
};
uint8_t v_color[4] = {
@@ -571,8 +569,8 @@ void Sprite3D::_draw() {
float v_vertex[3] = { vtx.x, vtx.y, vtx.z };
memcpy(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3);
- memcpy(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_NORMAL]], v_normal, 4);
- memcpy(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_TANGENT]], v_tangent, 4);
+ memcpy(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_NORMAL]], v_normal, 2);
+ memcpy(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_TANGENT]], v_tangent, 2);
memcpy(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_COLOR]], v_color, 4);
}
@@ -888,18 +886,16 @@ void AnimatedSprite3D::_draw() {
// Everything except position and UV is compressed
PoolVector::Write write_buffer = mesh_buffer.write();
- int8_t v_normal[4] = {
- (int8_t)CLAMP(normal.x * 127, -128, 127),
- (int8_t)CLAMP(normal.y * 127, -128, 127),
- (int8_t)CLAMP(normal.z * 127, -128, 127),
- 0,
+ Vector2 normal_oct = VisualServer::get_singleton()->norm_to_oct(normal);
+ int8_t v_normal[2] = {
+ (int8_t)CLAMP(normal_oct.x * 127, -128, 127),
+ (int8_t)CLAMP(normal_oct.y * 127, -128, 127),
};
- int8_t v_tangent[4] = {
- (int8_t)CLAMP(tangent.normal.x * 127, -128, 127),
- (int8_t)CLAMP(tangent.normal.y * 127, -128, 127),
- (int8_t)CLAMP(tangent.normal.z * 127, -128, 127),
- (int8_t)CLAMP(tangent.d * 127, -128, 127)
+ Vector2 tangent_oct = VisualServer::get_singleton()->tangent_to_oct(tangent.normal, tangent.d, false);
+ int8_t v_tangent[2] = {
+ (int8_t)CLAMP(tangent_oct.x * 127, -128, 127),
+ (int8_t)CLAMP(tangent_oct.y * 127, -128, 127),
};
uint8_t v_color[4] = {
@@ -925,8 +921,8 @@ void AnimatedSprite3D::_draw() {
float v_vertex[3] = { vtx.x, vtx.y, vtx.z };
memcpy(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3);
- memcpy(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_NORMAL]], v_normal, 4);
- memcpy(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_TANGENT]], v_tangent, 4);
+ memcpy(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_NORMAL]], v_normal, 2);
+ memcpy(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_TANGENT]], v_tangent, 2);
memcpy(&write_buffer[i * mesh_stride + mesh_surface_offsets[VS::ARRAY_COLOR]], v_color, 4);
}
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 628601ec706..7bb1e7136d3 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -546,6 +546,7 @@ void Mesh::_bind_methods() {
BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_2D_VERTICES);
BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_16_BIT_BONES);
+ BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION);
BIND_ENUM_CONSTANT(ARRAY_COMPRESS_DEFAULT);
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index 0d05fd85bb3..a5249e94d82 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -96,8 +96,9 @@ public:
ARRAY_FLAG_USE_2D_VERTICES = ARRAY_COMPRESS_INDEX << 1,
ARRAY_FLAG_USE_16_BIT_BONES = ARRAY_COMPRESS_INDEX << 2,
ARRAY_FLAG_USE_DYNAMIC_UPDATE = ARRAY_COMPRESS_INDEX << 3,
+ ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION = ARRAY_COMPRESS_INDEX << 4,
- ARRAY_COMPRESS_DEFAULT = ARRAY_COMPRESS_NORMAL | ARRAY_COMPRESS_TANGENT | ARRAY_COMPRESS_COLOR | ARRAY_COMPRESS_TEX_UV | ARRAY_COMPRESS_TEX_UV2 | ARRAY_COMPRESS_WEIGHTS
+ ARRAY_COMPRESS_DEFAULT = ARRAY_COMPRESS_NORMAL | ARRAY_COMPRESS_TANGENT | ARRAY_COMPRESS_COLOR | ARRAY_COMPRESS_TEX_UV | ARRAY_COMPRESS_TEX_UV2 | ARRAY_COMPRESS_WEIGHTS | ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION
};
diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp
index fac03115916..424e7ce0585 100644
--- a/servers/visual_server.cpp
+++ b/servers/visual_server.cpp
@@ -329,6 +329,59 @@ RID VisualServer::get_white_texture() {
#define SMALL_VEC2 Vector2(0.00001, 0.00001)
#define SMALL_VEC3 Vector3(0.00001, 0.00001, 0.00001)
+// Maps normalized vector to an octohedron projected onto the cartesian plane
+// Resulting 2D vector in range [-1, 1]
+// See http://jcgt.org/published/0003/02/01/ for details
+Vector2 VisualServer::norm_to_oct(const Vector3 v) {
+ const float invL1Norm = (1.0f) / (Math::absf(v.x) + Math::absf(v.y) + Math::absf(v.z));
+
+ Vector2 res;
+
+ if (v.z < 0.0f) {
+ res.x = (1.0f - Math::absf(v.y * invL1Norm)) * SGN(v.x);
+ res.y = (1.0f - Math::absf(v.x * invL1Norm)) * SGN(v.y);
+ } else {
+ res.x = v.x * invL1Norm;
+ res.y = v.y * invL1Norm;
+ }
+
+ return res;
+}
+
+// Maps normalized tangent vector to an octahedron projected onto the cartesian plane
+// Encodes the tangent vector sign in the second componenet of the returned Vector2 for use in shaders
+// high_precision specifies whether the encoding will be 32 bit (true) or 16 bit (false)
+// Resulting 2D vector in range [-1, 1]
+// See http://jcgt.org/published/0003/02/01/ for details
+Vector2 VisualServer::tangent_to_oct(const Vector3 v, const float sign, const bool high_precision) {
+ float bias = high_precision ? 1.0f / 32767 : 1.0f / 127;
+ Vector2 res = norm_to_oct(v);
+ res.y = res.y * 0.5f + 0.5f;
+ res.y = MAX(res.y, bias) * SGN(sign);
+ return res;
+}
+
+// Convert Octohedron-mapped normalized vector back to Cartesian
+// Assumes normalized format (elements of v within range [-1, 1])
+Vector3 VisualServer::oct_to_norm(const Vector2 v) {
+ Vector3 res(v.x, v.y, 1 - (Math::absf(v.x) + Math::absf(v.y)));
+ float t = MAX(-res.z, 0.0f);
+ res.x += t * -SGN(res.x);
+ res.y += t * -SGN(res.y);
+ return res;
+}
+
+// Convert Octohedron-mapped normalized tangent vector back to Cartesian
+// out_sign provides the direction for the original cartesian tangent
+// Assumes normalized format (elements of v within range [-1, 1])
+Vector3 VisualServer::oct_to_tangent(const Vector2 v, float *out_sign) {
+ Vector2 v_decompressed = v;
+ v_decompressed.y = Math::absf(v_decompressed.y) * 2 - 1;
+ Vector3 res = oct_to_norm(v_decompressed);
+ *out_sign = SGN(v[1]);
+ return res;
+}
+
Error VisualServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint32_t *p_offsets, uint32_t *p_stride, PoolVector &r_vertex_array, int p_vertex_array_len, PoolVector &r_index_array, int p_index_array_len, AABB &r_aabb, Vector &r_bone_aabb) {
PoolVector::Write vw = r_vertex_array.write();
@@ -437,22 +490,47 @@ Error VisualServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint32_
// setting vertices means regenerating the AABB
- if (p_format & ARRAY_COMPRESS_NORMAL) {
- for (int i = 0; i < p_vertex_array_len; i++) {
- int8_t vector[4] = {
- (int8_t)CLAMP(src[i].x * 127, -128, 127),
- (int8_t)CLAMP(src[i].y * 127, -128, 127),
- (int8_t)CLAMP(src[i].z * 127, -128, 127),
- 0,
- };
+ if (p_format & ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
+ if (p_format & ARRAY_COMPRESS_NORMAL) {
+ for (int i = 0; i < p_vertex_array_len; i++) {
+ Vector2 res = norm_to_oct(src[i]);
+ int8_t vector[2] = {
+ (int8_t)CLAMP(res.x * 127, -128, 127),
+ (int8_t)CLAMP(res.y * 127, -128, 127),
+ };
- memcpy(&vw[p_offsets[ai] + i * p_stride[ai]], vector, 4);
+ memcpy(&vw[p_offsets[ai] + i * p_stride[ai]], vector, 2);
+ }
+
+ } else {
+ for (int i = 0; i < p_vertex_array_len; i++) {
+ Vector2 res = norm_to_oct(src[i]);
+ int16_t vector[2] = {
+ (int16_t)CLAMP(res.x * 32767, -32768, 32767),
+ (int16_t)CLAMP(res.y * 32767, -32768, 32767),
+ };
+
+ memcpy(&vw[p_offsets[ai] + i * p_stride[ai]], vector, 4);
+ }
}
-
} else {
- for (int i = 0; i < p_vertex_array_len; i++) {
- float vector[3] = { src[i].x, src[i].y, src[i].z };
- memcpy(&vw[p_offsets[ai] + i * p_stride[ai]], vector, 3 * 4);
+ if (p_format & ARRAY_COMPRESS_NORMAL) {
+ for (int i = 0; i < p_vertex_array_len; i++) {
+ int8_t vector[4] = {
+ (int8_t)CLAMP(src[i].x * 127, -128, 127),
+ (int8_t)CLAMP(src[i].y * 127, -128, 127),
+ (int8_t)CLAMP(src[i].z * 127, -128, 127),
+ 0,
+ };
+
+ memcpy(&vw[p_offsets[ai] + i * p_stride[ai]], vector, 4);
+ }
+
+ } else {
+ for (int i = 0; i < p_vertex_array_len; i++) {
+ float vector[3] = { src[i].x, src[i].y, src[i].z };
+ memcpy(&vw[p_offsets[ai] + i * p_stride[ai]], vector, 3 * 4);
+ }
}
}
@@ -468,28 +546,57 @@ Error VisualServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint32_
PoolVector::Read read = array.read();
const real_t *src = read.ptr();
- if (p_format & ARRAY_COMPRESS_TANGENT) {
- for (int i = 0; i < p_vertex_array_len; i++) {
- int8_t xyzw[4] = {
- (int8_t)CLAMP(src[i * 4 + 0] * 127, -128, 127),
- (int8_t)CLAMP(src[i * 4 + 1] * 127, -128, 127),
- (int8_t)CLAMP(src[i * 4 + 2] * 127, -128, 127),
- (int8_t)CLAMP(src[i * 4 + 3] * 127, -128, 127)
- };
+ if (p_format & ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
+ if (p_format & ARRAY_COMPRESS_TANGENT) {
+ for (int i = 0; i < p_vertex_array_len; i++) {
+ Vector3 source(src[i * 4 + 0], src[i * 4 + 1], src[i * 4 + 2]);
+ Vector2 res = tangent_to_oct(source, src[i * 4 + 3], false);
- memcpy(&vw[p_offsets[ai] + i * p_stride[ai]], xyzw, 4);
+ int8_t vector[2] = {
+ (int8_t)CLAMP(res.x * 127, -128, 127),
+ (int8_t)CLAMP(res.y * 127, -128, 127)
+ };
+
+ memcpy(&vw[p_offsets[ai] + i * p_stride[ai]], vector, 2);
+ }
+
+ } else {
+ for (int i = 0; i < p_vertex_array_len; i++) {
+ Vector3 source(src[i * 4 + 0], src[i * 4 + 1], src[i * 4 + 2]);
+ Vector2 res = tangent_to_oct(source, src[i * 4 + 3], true);
+
+ int16_t vector[2] = {
+ (int16_t)CLAMP(res.x * 32767, -32768, 32767),
+ (int16_t)CLAMP(res.y * 32767, -32768, 32767)
+ };
+
+ memcpy(&vw[p_offsets[ai] + i * p_stride[ai]], vector, 4);
+ }
}
-
} else {
- for (int i = 0; i < p_vertex_array_len; i++) {
- float xyzw[4] = {
- src[i * 4 + 0],
- src[i * 4 + 1],
- src[i * 4 + 2],
- src[i * 4 + 3]
- };
+ if (p_format & ARRAY_COMPRESS_TANGENT) {
+ for (int i = 0; i < p_vertex_array_len; i++) {
+ int8_t xyzw[4] = {
+ (int8_t)CLAMP(src[i * 4 + 0] * 127, -128, 127),
+ (int8_t)CLAMP(src[i * 4 + 1] * 127, -128, 127),
+ (int8_t)CLAMP(src[i * 4 + 2] * 127, -128, 127),
+ (int8_t)CLAMP(src[i * 4 + 3] * 127, -128, 127)
+ };
- memcpy(&vw[p_offsets[ai] + i * p_stride[ai]], xyzw, 4 * 4);
+ memcpy(&vw[p_offsets[ai] + i * p_stride[ai]], xyzw, 4);
+ }
+
+ } else {
+ for (int i = 0; i < p_vertex_array_len; i++) {
+ float xyzw[4] = {
+ src[i * 4 + 0],
+ src[i * 4 + 1],
+ src[i * 4 + 2],
+ src[i * 4 + 3]
+ };
+
+ memcpy(&vw[p_offsets[ai] + i * p_stride[ai]], xyzw, 4 * 4);
+ }
}
}
@@ -770,19 +877,35 @@ uint32_t VisualServer::mesh_surface_make_offsets_from_format(uint32_t p_format,
} break;
case VS::ARRAY_NORMAL: {
- if (p_format & ARRAY_COMPRESS_NORMAL) {
- elem_size = sizeof(uint32_t);
+ if (p_format & ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
+ if (p_format & ARRAY_COMPRESS_NORMAL) {
+ elem_size = sizeof(uint8_t) * 2;
+ } else {
+ elem_size = sizeof(uint16_t) * 2;
+ }
} else {
- elem_size = sizeof(float) * 3;
+ if (p_format & ARRAY_COMPRESS_NORMAL) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 3;
+ }
}
} break;
case VS::ARRAY_TANGENT: {
- if (p_format & ARRAY_COMPRESS_TANGENT) {
- elem_size = sizeof(uint32_t);
+ if (p_format & ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
+ if (p_format & ARRAY_COMPRESS_TANGENT) {
+ elem_size = sizeof(uint8_t) * 2;
+ } else {
+ elem_size = sizeof(uint16_t) * 2;
+ }
} else {
- elem_size = sizeof(float) * 4;
+ if (p_format & ARRAY_COMPRESS_TANGENT) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 4;
+ }
}
} break;
@@ -959,10 +1082,18 @@ void VisualServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_prim
} break;
case VS::ARRAY_NORMAL: {
- if (p_compress_format & ARRAY_COMPRESS_NORMAL) {
- elem_size = sizeof(uint32_t);
+ if (p_compress_format & ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
+ if (p_compress_format & ARRAY_COMPRESS_NORMAL) {
+ elem_size = sizeof(uint8_t) * 2;
+ } else {
+ elem_size = sizeof(uint16_t) * 2;
+ }
} else {
- elem_size = sizeof(float) * 3;
+ if (p_compress_format & ARRAY_COMPRESS_NORMAL) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 3;
+ }
}
offsets[i] = attributes_base_offset + attributes_stride;
attributes_stride += elem_size;
@@ -970,10 +1101,18 @@ void VisualServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_prim
} break;
case VS::ARRAY_TANGENT: {
- if (p_compress_format & ARRAY_COMPRESS_TANGENT) {
- elem_size = sizeof(uint32_t);
+ if (p_compress_format & ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
+ if (p_compress_format & ARRAY_COMPRESS_TANGENT) {
+ elem_size = sizeof(uint8_t) * 2;
+ } else {
+ elem_size = sizeof(uint16_t) * 2;
+ }
} else {
- elem_size = sizeof(float) * 4;
+ if (p_compress_format & ARRAY_COMPRESS_TANGENT) {
+ elem_size = sizeof(uint32_t);
+ } else {
+ elem_size = sizeof(float) * 4;
+ }
}
offsets[i] = attributes_base_offset + attributes_stride;
attributes_stride += elem_size;
@@ -1146,19 +1285,35 @@ Array VisualServer::_get_array_from_surface(uint32_t p_format, PoolVector arr;
arr.resize(p_vertex_len);
- if (p_format & ARRAY_COMPRESS_NORMAL) {
- PoolVector::Write w = arr.write();
- const float multiplier = 1.f / 127.f;
+ if (p_format & ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
+ if (p_format & ARRAY_COMPRESS_NORMAL) {
+ PoolVector::Write w = arr.write();
- for (int j = 0; j < p_vertex_len; j++) {
- const int8_t *v = (const int8_t *)&r[j * total_elem_size + offsets[i]];
- w[j] = Vector3(float(v[0]) * multiplier, float(v[1]) * multiplier, float(v[2]) * multiplier);
+ for (int j = 0; j < p_vertex_len; j++) {
+ const int8_t *n = (const int8_t *)&r[j * total_elem_size + offsets[i]];
+ Vector2 enc(n[0] / 127.0f, n[1] / 127.0f);
+
+ w[j] = oct_to_norm(enc);
+ }
+ } else {
+ PoolVector::Write w = arr.write();
+
+ for (int j = 0; j < p_vertex_len; j++) {
+ const int16_t *n = (const int16_t *)&r[j * total_elem_size + offsets[i]];
+ Vector2 enc(n[0] / 32767.0f, n[1] / 32767.0f);
+
+ w[j] = oct_to_norm(enc);
+ }
}
} else {
- PoolVector::Write w = arr.write();
+ if (p_format & ARRAY_COMPRESS_NORMAL) {
+ PoolVector::Write w = arr.write();
+ const float multiplier = 1.f / 127.f;
- for (int j = 0; j < p_vertex_len; j++) {
- const float *v = (const float *)&r[j * total_elem_size + offsets[i]];
- w[j] = Vector3(v[0], v[1], v[2]);
+ for (int j = 0; j < p_vertex_len; j++) {
+ const int8_t *v = (const int8_t *)&r[j * total_elem_size + offsets[i]];
+ w[j] = Vector3(float(v[0]) * multiplier, float(v[1]) * multiplier, float(v[2]) * multiplier);
+ }
+ } else {
+ PoolVector::Write w = arr.write();
+
+ for (int j = 0; j < p_vertex_len; j++) {
+ const float *v = (const float *)&r[j * total_elem_size + offsets[i]];
+ w[j] = Vector3(v[0], v[1], v[2]);
+ }
}
}
@@ -1311,22 +1488,51 @@ Array VisualServer::_get_array_from_surface(uint32_t p_format, PoolVector arr;
arr.resize(p_vertex_len * 4);
- if (p_format & ARRAY_COMPRESS_TANGENT) {
- PoolVector::Write w = arr.write();
- for (int j = 0; j < p_vertex_len; j++) {
- const int8_t *v = (const int8_t *)&r[j * total_elem_size + offsets[i]];
- for (int k = 0; k < 4; k++) {
- w[j * 4 + k] = float(v[k] / 127.0);
+ if (p_format & ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION) {
+ if (p_format & ARRAY_COMPRESS_TANGENT) {
+ PoolVector::Write w = arr.write();
+
+ for (int j = 0; j < p_vertex_len; j++) {
+ const int8_t *t = (const int8_t *)&r[j * total_elem_size + offsets[i]];
+ Vector2 enc(t[0] / 127.0f, t[1] / 127.0f);
+ Vector3 dec = oct_to_tangent(enc, &w[j * 3 + 2]);
+
+ w[j * 3 + 0] = dec.x;
+ w[j * 3 + 1] = dec.y;
+ w[j * 3 + 2] = dec.z;
+ }
+ } else {
+ PoolVector::Write w = arr.write();
+
+ for (int j = 0; j < p_vertex_len; j++) {
+ const int16_t *t = (const int16_t *)&r[j * total_elem_size + offsets[i]];
+ Vector2 enc(t[0] / 32767.0f, t[1] / 32767.0f);
+ Vector3 dec = oct_to_tangent(enc, &w[j * 3 + 2]);
+
+ w[j * 3 + 0] = dec.x;
+ w[j * 3 + 1] = dec.y;
+ w[j * 3 + 2] = dec.z;
}
}
} else {
- PoolVector::Write w = arr.write();
+ if (p_format & ARRAY_COMPRESS_TANGENT) {
+ PoolVector::Write w = arr.write();
- for (int j = 0; j < p_vertex_len; j++) {
- const float *v = (const float *)&r[j * total_elem_size + offsets[i]];
- for (int k = 0; k < 4; k++) {
- w[j * 4 + k] = v[k];
+ for (int j = 0; j < p_vertex_len; j++) {
+ const int8_t *v = (const int8_t *)&r[j * total_elem_size + offsets[i]];
+ for (int k = 0; k < 4; k++) {
+ w[j * 4 + k] = float(v[k] / 127.0);
+ }
+ }
+ } else {
+ PoolVector::Write w = arr.write();
+
+ for (int j = 0; j < p_vertex_len; j++) {
+ const float *v = (const float *)&r[j * total_elem_size + offsets[i]];
+ for (int k = 0; k < 4; k++) {
+ w[j * 4 + k] = v[k];
+ }
}
}
}
@@ -2019,6 +2225,7 @@ void VisualServer::_bind_methods() {
BIND_ENUM_CONSTANT(ARRAY_COMPRESS_INDEX);
BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_2D_VERTICES);
BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_16_BIT_BONES);
+ BIND_ENUM_CONSTANT(ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION);
BIND_ENUM_CONSTANT(ARRAY_COMPRESS_DEFAULT);
BIND_ENUM_CONSTANT(PRIMITIVE_POINTS);
diff --git a/servers/visual_server.h b/servers/visual_server.h
index a9ea43835be..8596255fc29 100644
--- a/servers/visual_server.h
+++ b/servers/visual_server.h
@@ -68,6 +68,10 @@ protected:
public:
static VisualServer *get_singleton();
static VisualServer *create();
+ static Vector2 norm_to_oct(const Vector3 v);
+ static Vector2 tangent_to_oct(const Vector3 v, const float sign, const bool high_precision);
+ static Vector3 oct_to_norm(const Vector2 v);
+ static Vector3 oct_to_tangent(const Vector2 v, float *out_sign);
enum {
@@ -263,8 +267,9 @@ public:
ARRAY_FLAG_USE_2D_VERTICES = ARRAY_COMPRESS_INDEX << 1,
ARRAY_FLAG_USE_16_BIT_BONES = ARRAY_COMPRESS_INDEX << 2,
ARRAY_FLAG_USE_DYNAMIC_UPDATE = ARRAY_COMPRESS_INDEX << 3,
+ ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION = ARRAY_COMPRESS_INDEX << 4,
- ARRAY_COMPRESS_DEFAULT = ARRAY_COMPRESS_NORMAL | ARRAY_COMPRESS_TANGENT | ARRAY_COMPRESS_COLOR | ARRAY_COMPRESS_TEX_UV | ARRAY_COMPRESS_TEX_UV2 | ARRAY_COMPRESS_WEIGHTS
+ ARRAY_COMPRESS_DEFAULT = ARRAY_COMPRESS_NORMAL | ARRAY_COMPRESS_TANGENT | ARRAY_COMPRESS_COLOR | ARRAY_COMPRESS_TEX_UV | ARRAY_COMPRESS_TEX_UV2 | ARRAY_COMPRESS_WEIGHTS | ARRAY_FLAG_USE_OCTAHEDRAL_COMPRESSION
};
]