Skip to content

Commit

Permalink
Merge pull request #81138 from clayjohn/attribute-compression
Browse files Browse the repository at this point in the history
Vertex and attribute compression
  • Loading branch information
akien-mga committed Oct 5, 2023
2 parents 7ee2eb5 + 51ed3ae commit f02695c
Show file tree
Hide file tree
Showing 59 changed files with 1,753 additions and 671 deletions.
2 changes: 2 additions & 0 deletions doc/classes/EditorSceneFormatImporter.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,7 @@
</constant>
<constant name="IMPORT_DISCARD_MESHES_AND_MATERIALS" value="32">
</constant>
<constant name="IMPORT_FORCE_DISABLE_MESH_COMPRESSION" value="64">
</constant>
</constants>
</class>
1 change: 1 addition & 0 deletions doc/classes/MeshDataTool.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
<method name="commit_to_surface">
<return type="int" enum="Error" />
<param index="0" name="mesh" type="ArrayMesh" />
<param index="1" name="compression_flags" type="int" default="0" />
<description>
Adds a new surface to specified [Mesh] with edited data.
</description>
Expand Down
34 changes: 34 additions & 0 deletions doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2210,6 +2210,15 @@
<param index="0" name="format" type="int" enum="RenderingServer.ArrayFormat" is_bitfield="true" />
<param index="1" name="vertex_count" type="int" />
<description>
Returns the stride of the attribute buffer for a mesh with given [param format].
</description>
</method>
<method name="mesh_surface_get_format_normal_tangent_stride" qualifiers="const">
<return type="int" />
<param index="0" name="format" type="int" enum="RenderingServer.ArrayFormat" is_bitfield="true" />
<param index="1" name="vertex_count" type="int" />
<description>
Returns the stride of the combined normals and tangents for a mesh with given [param format]. Note importantly that, while normals and tangents are in the vertex buffer with vertices, they are only interleaved with each other and so have a different stride than vertex positions.
</description>
</method>
<method name="mesh_surface_get_format_offset" qualifiers="const">
Expand All @@ -2218,20 +2227,23 @@
<param index="1" name="vertex_count" type="int" />
<param index="2" name="array_index" type="int" />
<description>
Returns the offset of a given attribute by [param array_index] in the start of its respective buffer.
</description>
</method>
<method name="mesh_surface_get_format_skin_stride" qualifiers="const">
<return type="int" />
<param index="0" name="format" type="int" enum="RenderingServer.ArrayFormat" is_bitfield="true" />
<param index="1" name="vertex_count" type="int" />
<description>
Returns the stride of the skin buffer for a mesh with given [param format].
</description>
</method>
<method name="mesh_surface_get_format_vertex_stride" qualifiers="const">
<return type="int" />
<param index="0" name="format" type="int" enum="RenderingServer.ArrayFormat" is_bitfield="true" />
<param index="1" name="vertex_count" type="int" />
<description>
Returns the stride of the vertex positions for a mesh with given [param format]. Note importantly that vertex positions are stored consecutively and are not interleaved with the other attributes in the vertex buffer (normals and tangents).
</description>
</method>
<method name="mesh_surface_get_material" qualifiers="const">
Expand Down Expand Up @@ -4187,6 +4199,28 @@
Flag used to mark that the array uses 8 bone weights instead of 4.
</constant>
<constant name="ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY" value="268435456" enum="ArrayFormat" is_bitfield="true">
Flag used to mark that the mesh does not have a vertex array and instead will infer vertex positions in the shader using indices and other information.
</constant>
<constant name="ARRAY_FLAG_COMPRESS_ATTRIBUTES" value="536870912" enum="ArrayFormat" is_bitfield="true">
Flag used to mark that a mesh is using compressed attributes (vertices, normals, tangents, uvs). When this form of compression is enabled, vertex positions will be packed into into an RGBA16UNORM attribute and scaled in the vertex shader. The normal and tangent will be packed into a RG16UNORM representing an axis, and an 16 bit float stored in the A-channel of the vertex. UVs will use 16-bit normalized floats instead of full 32 bit signed floats. When using this compression mode you must either use vertices, normals, and tangents or only vertices. You cannot use normals without tangents. Importers will automatically enable this compression if they can.
</constant>
<constant name="ARRAY_FLAG_FORMAT_VERSION_BASE" value="35" enum="ArrayFormat" is_bitfield="true">
Flag used to mark the start of the bits used to store the mesh version.
</constant>
<constant name="ARRAY_FLAG_FORMAT_VERSION_SHIFT" value="35" enum="ArrayFormat" is_bitfield="true">
Flag used to shift a mesh format int to bring the version into the lowest digits.
</constant>
<constant name="ARRAY_FLAG_FORMAT_VERSION_1" value="0" enum="ArrayFormat" is_bitfield="true">
Flag used to record the format used by prior mesh versions before the introduction of a version.
</constant>
<constant name="ARRAY_FLAG_FORMAT_VERSION_2" value="34359738368" enum="ArrayFormat" is_bitfield="true">
Flag used to record the second iteration of the mesh version flag. The primary difference between this and [constant ARRAY_FLAG_FORMAT_VERSION_1] is that this version supports [constant ARRAY_FLAG_COMPRESS_ATTRIBUTES] and in this version vertex positions are de-interleaved from normals and tangents.
</constant>
<constant name="ARRAY_FLAG_FORMAT_CURRENT_VERSION" value="34359738368" enum="ArrayFormat" is_bitfield="true">
Flag used to record the current version that the engine expects. Currently this is the same as [constant ARRAY_FLAG_FORMAT_VERSION_2].
</constant>
<constant name="ARRAY_FLAG_FORMAT_VERSION_MASK" value="255" enum="ArrayFormat" is_bitfield="true">
Flag used to isolate the bits used for mesh version after using [constant ARRAY_FLAG_FORMAT_VERSION_SHIFT] to shift them into place.
</constant>
<constant name="PRIMITIVE_POINTS" value="0" enum="PrimitiveType">
Primitive to draw consists of points.
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/ResourceImporterOBJ.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
<link title="Importing 3D scenes">$DOCS_URL/tutorials/assets_pipeline/importing_scenes.html</link>
</tutorials>
<members>
<member name="force_disable_mesh_compression" type="bool" setter="" getter="" default="false">
If [code]true[/code], mesh compression will not be used. Consider enabling if you notice blocky artifacts in your mesh normals or UVs, or if you have meshes that are larger than a few thousand meters in each direction.
</member>
<member name="generate_tangents" type="bool" setter="" getter="" default="true">
If [code]true[/code], generate vertex tangents using [url=http://www.mikktspace.com/]Mikktspace[/url] if the source mesh doesn't have tangent data. When possible, it's recommended to let the 3D modeling software generate tangents on export instead on relying on this option. Tangents are required for correct display of normal and height maps, along with any material/shader features that require tangents.
If you don't need material features that require tangents, disabling this can reduce output file size and speed up importing if the source 3D file doesn't contain tangents.
Expand Down
3 changes: 3 additions & 0 deletions doc/classes/ResourceImporterScene.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
If [code]true[/code], generate vertex tangents using [url=http://www.mikktspace.com/]Mikktspace[/url] if the input meshes don't have tangent data. When possible, it's recommended to let the 3D modeling software generate tangents on export instead on relying on this option. Tangents are required for correct display of normal and height maps, along with any material/shader features that require tangents.
If you don't need material features that require tangents, disabling this can reduce output file size and speed up importing if the source 3D file doesn't contain tangents.
</member>
<member name="meshes/force_disable_compression" type="bool" setter="" getter="" default="false">
If [code]true[/code], mesh compression will not be used. Consider enabling if you notice blocky artifacts in your mesh normals or UVs, or if you have meshes that are larger than a few thousand meters in each direction.
</member>
<member name="meshes/generate_lods" type="bool" setter="" getter="" default="true">
If [code]true[/code], generates lower detail variants of the mesh which will be displayed in the distance to improve rendering performance. Not all meshes benefit from LOD, especially if they are never rendered from far away. Disabling this can reduce output file size and speed up importing. See [url=$DOCS_URL/tutorials/3d/mesh_lod.html#doc-mesh-lod]Mesh level of detail (LOD)[/url] for more information.
</member>
Expand Down
2 changes: 1 addition & 1 deletion drivers/gles3/rasterizer_canvas_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1383,7 +1383,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) {
GLuint vertex_array_gl = 0;
GLuint index_array_gl = 0;

uint32_t input_mask = 0; // 2D meshes always use the same vertex format
uint64_t input_mask = 0; // 2D meshes always use the same vertex format.
if (mesh_instance.is_valid()) {
mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array_gl);
} else {
Expand Down
12 changes: 12 additions & 0 deletions drivers/gles3/rasterizer_scene_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2908,6 +2908,18 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
}

material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant, spec_constants);
{
GLES3::Mesh::Surface *s = reinterpret_cast<GLES3::Mesh::Surface *>(surf->surface);
if (s->format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::COMPRESSED_AABB_POSITION, s->aabb.position, shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::COMPRESSED_AABB_SIZE, s->aabb.size, shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::UV_SCALE, s->uv_scale, shader->version, instance_variant, spec_constants);
} else {
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::COMPRESSED_AABB_POSITION, Vector3(0.0, 0.0, 0.0), shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::COMPRESSED_AABB_SIZE, Vector3(1.0, 1.0, 1.0), shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::UV_SCALE, Vector4(0.0, 0.0, 0.0, 0.0), shader->version, instance_variant, spec_constants);
}
}

// Can be index count or vertex count
uint32_t count = 0;
Expand Down
79 changes: 52 additions & 27 deletions drivers/gles3/shaders/scene.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ ADDITIVE_SPOT = false

/*
from RenderingServer:
ARRAY_VERTEX = 0, // RG32F or RGB32F (depending on 2D bit)
ARRAY_NORMAL = 1, // RG16 octahedral compression
ARRAY_VERTEX = 0, // RGB32F or RGBA16
ARRAY_NORMAL = 1, // RG16 octahedral compression or RGBA16 normal + angle
ARRAY_TANGENT = 2, // RG16 octahedral compression, sign stored in sign of G
ARRAY_COLOR = 3, // RGBA8
ARRAY_TEX_UV = 4, // RG32F
Expand All @@ -68,16 +68,16 @@ ARRAY_WEIGHTS = 11, // RGBA16UNORM (x2 if 8 weights)

/* INPUT ATTRIBS */

layout(location = 0) in highp vec3 vertex_attrib;
// Always contains vertex position in XYZ, can contain tangent angle in W.
layout(location = 0) in highp vec4 vertex_angle_attrib;
/* clang-format on */

#ifdef NORMAL_USED
layout(location = 1) in vec2 normal_attrib;
// Contains Normal/Axis in RG, can contain tangent in BA.
layout(location = 1) in vec4 axis_tangent_attrib;
#endif

#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
layout(location = 2) in vec2 tangent_attrib;
#endif
// location 2 is unused.

#if defined(COLOR_USED)
layout(location = 3) in vec4 color_attrib;
Expand Down Expand Up @@ -122,6 +122,16 @@ vec3 oct_to_vec3(vec2 e) {
return normalize(v);
}

void axis_angle_to_tbn(vec3 axis, float angle, out vec3 tangent, out vec3 binormal, out vec3 normal) {
float c = cos(angle);
float s = sin(angle);
vec3 omc_axis = (1.0 - c) * axis;
vec3 s_axis = s * axis;
tangent = omc_axis.xxx * axis + vec3(c, -s_axis.z, s_axis.y);
binormal = omc_axis.yyy * axis + vec3(s_axis.z, c, -s_axis.x);
normal = omc_axis.zzz * axis + vec3(-s_axis.y, s_axis.x, c);
}

#ifdef USE_INSTANCING
layout(location = 12) in highp vec4 instance_xform0;
layout(location = 13) in highp vec4 instance_xform1;
Expand Down Expand Up @@ -228,10 +238,9 @@ multiview_data;
#endif

uniform highp mat4 world_transform;

#ifdef USE_LIGHTMAP
uniform highp vec4 lightmap_uv_rect;
#endif
uniform highp vec3 compressed_aabb_position;
uniform highp vec3 compressed_aabb_size;
uniform highp vec4 uv_scale;

/* Varyings */

Expand All @@ -248,13 +257,9 @@ out vec4 color_interp;
out vec2 uv_interp;
#endif

#if defined(UV2_USED)
out vec2 uv2_interp;
#else
#ifdef USE_LIGHTMAP
#if defined(UV2_USED) || defined(USE_LIGHTMAP)
out vec2 uv2_interp;
#endif
#endif

#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
out vec3 tangent_interp;
Expand Down Expand Up @@ -294,7 +299,7 @@ layout(std140) uniform MaterialUniforms { // ubo:3
invariant gl_Position;

void main() {
highp vec3 vertex = vertex_attrib;
highp vec3 vertex = vertex_angle_attrib.xyz * compressed_aabb_size + compressed_aabb_position;

highp mat4 model_matrix = world_transform;
#ifdef USE_INSTANCING
Expand All @@ -303,15 +308,30 @@ void main() {
#endif

#ifdef NORMAL_USED
vec3 normal = oct_to_vec3(normal_attrib * 2.0 - 1.0);
vec3 normal = oct_to_vec3(axis_tangent_attrib.xy * 2.0 - 1.0);
#endif
highp mat3 model_normal_matrix = mat3(model_matrix);

#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
vec2 signed_tangent_attrib = tangent_attrib * 2.0 - 1.0;
vec3 tangent = oct_to_vec3(vec2(signed_tangent_attrib.x, abs(signed_tangent_attrib.y) * 2.0 - 1.0));
float binormalf = sign(signed_tangent_attrib.y);
vec3 binormal = normalize(cross(normal, tangent) * binormalf);
#if defined(NORMAL_USED) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)

vec3 binormal;
float binormal_sign;
vec3 tangent;
if (axis_tangent_attrib.z > 0.0 || axis_tangent_attrib.w < 1.0) {
// Uncompressed format.
vec2 signed_tangent_attrib = axis_tangent_attrib.zw * 2.0 - 1.0;
tangent = oct_to_vec3(vec2(signed_tangent_attrib.x, abs(signed_tangent_attrib.y) * 2.0 - 1.0));
binormal_sign = sign(signed_tangent_attrib.y);
binormal = normalize(cross(normal, tangent) * binormal_sign);
} else {
// Compressed format.
float angle = vertex_angle_attrib.w;
binormal_sign = angle > 0.5 ? 1.0 : -1.0; // 0.5 does not exist in UNORM16, so values are either greater or smaller.
angle = abs(angle * 2.0 - 1.0) * M_PI; // 0.5 is basically zero, allowing to encode both signs reliably.
vec3 axis = normal;
axis_angle_to_tbn(axis, angle, tangent, binormal, normal);
binormal *= binormal_sign;
}
#endif

#if defined(COLOR_USED)
Expand All @@ -326,13 +346,18 @@ void main() {
uv_interp = uv_attrib;
#endif

#ifdef USE_LIGHTMAP
uv2_interp = lightmap_uv_rect.zw * uv2_attrib + lightmap_uv_rect.xy;
#else
#if defined(UV2_USED)
#if defined(UV2_USED) || defined(USE_LIGHTMAP)
uv2_interp = uv2_attrib;
#endif

if (uv_scale != vec4(0.0)) { // Compression enabled
#ifdef UV_USED
uv_interp = (uv_interp - 0.5) * uv_scale.xy;
#endif
#if defined(UV2_USED) || defined(USE_LIGHTMAP)
uv2_interp = (uv2_interp - 0.5) * uv_scale.zw;
#endif
}

#if defined(OVERRIDE_POSITION)
highp vec4 position;
Expand Down
2 changes: 1 addition & 1 deletion drivers/gles3/storage/material_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2944,7 +2944,7 @@ void SceneShaderData::set_code(const String &p_code) {
cull_mode = Cull(cull_modei);
blend_mode = BlendMode(blend_modei);
alpha_antialiasing_mode = AlphaAntiAliasing(alpha_antialiasing_modei);
vertex_input_mask = uint32_t(uses_normal);
vertex_input_mask = uint64_t(uses_normal);
vertex_input_mask |= uses_tangent << 1;
vertex_input_mask |= uses_color << 2;
vertex_input_mask |= uses_uv << 3;
Expand Down
2 changes: 1 addition & 1 deletion drivers/gles3/storage/material_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ struct SceneShaderData : public ShaderData {
bool uses_bones;
bool uses_weights;

uint32_t vertex_input_mask = 0;
uint64_t vertex_input_mask = 0;

uint64_t last_pass = 0;
uint32_t index = 0;
Expand Down
Loading

0 comments on commit f02695c

Please sign in to comment.