diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index 505b56c4870..d855d69b646 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 ac18e3f1ced..e5b752f4ca4 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 };