Vertex and attribute compression to reduce the size of the vertex format.

This allows Godot to automatically compress meshes to save a lot of bandwidth.

In general, this requires no interaction from the user and should result in
no noticable quality loss.

This scheme is not backwards compatible, so we have provided an upgrade
mechanism, and a mesh versioning mechanism.

Existing meshes can still be used as a result, but users can get a
performance boost by reimporting assets.
This commit is contained in:
clayjohn 2023-08-29 21:04:32 +02:00
parent d31794c4a2
commit 51ed3aef63
59 changed files with 1752 additions and 670 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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">
@ -2218,6 +2227,7 @@
<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">
@ -2225,6 +2235,7 @@
<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">
@ -2232,6 +2243,7 @@
<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">
@ -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.

View file

@ -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.

View file

@ -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>

View file

@ -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 {

View file

@ -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;

View file

@ -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
@ -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;
@ -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;
@ -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 */
@ -248,12 +257,8 @@ out vec4 color_interp;
out vec2 uv_interp;
#endif
#if defined(UV2_USED)
#if defined(UV2_USED) || defined(USE_LIGHTMAP)
out vec2 uv2_interp;
#else
#ifdef USE_LIGHTMAP
out vec2 uv2_interp;
#endif
#endif
#if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
@ -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
@ -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)
@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -117,34 +117,40 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
uint32_t skin_stride = 0;
for (int i = 0; i < RS::ARRAY_WEIGHTS; i++) {
if ((p_surface.format & (1 << i))) {
if ((p_surface.format & (1ULL << i))) {
switch (i) {
case RS::ARRAY_VERTEX: {
if (p_surface.format & RS::ARRAY_FLAG_USE_2D_VERTICES) {
if ((p_surface.format & RS::ARRAY_FLAG_USE_2D_VERTICES) || (p_surface.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
stride += sizeof(float) * 2;
} else {
stride += sizeof(float) * 3;
}
} break;
case RS::ARRAY_NORMAL: {
stride += sizeof(uint16_t) * 2;
} break;
case RS::ARRAY_TANGENT: {
stride += sizeof(uint16_t) * 2;
if (!(p_surface.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
stride += sizeof(uint16_t) * 2;
}
} break;
case RS::ARRAY_COLOR: {
attrib_stride += sizeof(uint32_t);
} break;
case RS::ARRAY_TEX_UV: {
attrib_stride += sizeof(float) * 2;
if (p_surface.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
attrib_stride += sizeof(uint16_t) * 2;
} else {
attrib_stride += sizeof(float) * 2;
}
} break;
case RS::ARRAY_TEX_UV2: {
attrib_stride += sizeof(float) * 2;
if (p_surface.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
attrib_stride += sizeof(uint16_t) * 2;
} else {
attrib_stride += sizeof(float) * 2;
}
} break;
case RS::ARRAY_CUSTOM0:
case RS::ARRAY_CUSTOM1:
@ -183,94 +189,123 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
}
}
#endif
uint64_t surface_version = p_surface.format & (uint64_t(RS::ARRAY_FLAG_FORMAT_VERSION_MASK) << RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT);
RS::SurfaceData new_surface = p_surface;
#ifdef DISABLE_DEPRECATED
ERR_FAIL_COND_MSG(surface_version != RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION, "Surface version provided (" + itos(int(surface_version >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT)) + ") does not match current version (" + itos(RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) + ")");
#else
if (surface_version != uint64_t(RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION)) {
RS::_fix_surface_compatibility(new_surface);
surface_version = new_surface.format & (uint64_t(RS::ARRAY_FLAG_FORMAT_VERSION_MASK) << RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT);
ERR_FAIL_COND_MSG(surface_version != uint64_t(RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION),
"Surface version provided (" +
itos((surface_version >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) & RS::ARRAY_FLAG_FORMAT_VERSION_MASK) +
") does not match current version (" +
itos((uint64_t(RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION) >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) & RS::ARRAY_FLAG_FORMAT_VERSION_MASK) +
")");
}
#endif
Mesh::Surface *s = memnew(Mesh::Surface);
s->format = p_surface.format;
s->primitive = p_surface.primitive;
s->format = new_surface.format;
s->primitive = new_surface.primitive;
if (p_surface.vertex_data.size()) {
if (new_surface.vertex_data.size()) {
glGenBuffers(1, &s->vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer);
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s->vertex_buffer, p_surface.vertex_data.size(), p_surface.vertex_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW, "Mesh vertex buffer");
s->vertex_buffer_size = p_surface.vertex_data.size();
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s->vertex_buffer, new_surface.vertex_data.size(), new_surface.vertex_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW, "Mesh vertex buffer");
s->vertex_buffer_size = new_surface.vertex_data.size();
}
if (p_surface.attribute_data.size()) {
if (new_surface.attribute_data.size()) {
glGenBuffers(1, &s->attribute_buffer);
glBindBuffer(GL_ARRAY_BUFFER, s->attribute_buffer);
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s->attribute_buffer, p_surface.attribute_data.size(), p_surface.attribute_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW, "Mesh attribute buffer");
s->attribute_buffer_size = p_surface.attribute_data.size();
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s->attribute_buffer, new_surface.attribute_data.size(), new_surface.attribute_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW, "Mesh attribute buffer");
s->attribute_buffer_size = new_surface.attribute_data.size();
}
if (p_surface.skin_data.size()) {
if (new_surface.skin_data.size()) {
glGenBuffers(1, &s->skin_buffer);
glBindBuffer(GL_ARRAY_BUFFER, s->skin_buffer);
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s->skin_buffer, p_surface.skin_data.size(), p_surface.skin_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW, "Mesh skin buffer");
s->skin_buffer_size = p_surface.skin_data.size();
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s->skin_buffer, new_surface.skin_data.size(), new_surface.skin_data.ptr(), (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW, "Mesh skin buffer");
s->skin_buffer_size = new_surface.skin_data.size();
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
s->vertex_count = p_surface.vertex_count;
s->vertex_count = new_surface.vertex_count;
if (p_surface.format & RS::ARRAY_FORMAT_BONES) {
if (new_surface.format & RS::ARRAY_FORMAT_BONES) {
mesh->has_bone_weights = true;
}
if (p_surface.index_count) {
bool is_index_16 = p_surface.vertex_count <= 65536 && p_surface.vertex_count > 0;
if (new_surface.index_count) {
bool is_index_16 = new_surface.vertex_count <= 65536 && new_surface.vertex_count > 0;
glGenBuffers(1, &s->index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_buffer);
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ELEMENT_ARRAY_BUFFER, s->index_buffer, p_surface.index_data.size(), p_surface.index_data.ptr(), GL_STATIC_DRAW, "Mesh index buffer");
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ELEMENT_ARRAY_BUFFER, s->index_buffer, new_surface.index_data.size(), new_surface.index_data.ptr(), GL_STATIC_DRAW, "Mesh index buffer");
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //unbind
s->index_count = p_surface.index_count;
s->index_buffer_size = p_surface.index_data.size();
s->index_count = new_surface.index_count;
s->index_buffer_size = new_surface.index_data.size();
if (p_surface.lods.size()) {
s->lods = memnew_arr(Mesh::Surface::LOD, p_surface.lods.size());
s->lod_count = p_surface.lods.size();
if (new_surface.lods.size()) {
s->lods = memnew_arr(Mesh::Surface::LOD, new_surface.lods.size());
s->lod_count = new_surface.lods.size();
for (int i = 0; i < p_surface.lods.size(); i++) {
for (int i = 0; i < new_surface.lods.size(); i++) {
glGenBuffers(1, &s->lods[i].index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->lods[i].index_buffer);
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ELEMENT_ARRAY_BUFFER, s->lods[i].index_buffer, p_surface.lods[i].index_data.size(), p_surface.lods[i].index_data.ptr(), GL_STATIC_DRAW, "Mesh index buffer LOD[" + itos(i) + "]");
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ELEMENT_ARRAY_BUFFER, s->lods[i].index_buffer, new_surface.lods[i].index_data.size(), new_surface.lods[i].index_data.ptr(), GL_STATIC_DRAW, "Mesh index buffer LOD[" + itos(i) + "]");
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //unbind
s->lods[i].edge_length = p_surface.lods[i].edge_length;
s->lods[i].index_count = p_surface.lods[i].index_data.size() / (is_index_16 ? 2 : 4);
s->lods[i].index_buffer_size = p_surface.lods[i].index_data.size();
s->lods[i].edge_length = new_surface.lods[i].edge_length;
s->lods[i].index_count = new_surface.lods[i].index_data.size() / (is_index_16 ? 2 : 4);
s->lods[i].index_buffer_size = new_surface.lods[i].index_data.size();
}
}
}
ERR_FAIL_COND_MSG(!p_surface.index_count && !p_surface.vertex_count, "Meshes must contain a vertex array, an index array, or both");
ERR_FAIL_COND_MSG(!new_surface.index_count && !new_surface.vertex_count, "Meshes must contain a vertex array, an index array, or both");
s->aabb = p_surface.aabb;
s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them.
s->aabb = new_surface.aabb;
s->bone_aabbs = new_surface.bone_aabbs; //only really useful for returning them.
if (p_surface.skin_data.size() || mesh->blend_shape_count > 0) {
s->uv_scale = new_surface.uv_scale;
if (new_surface.skin_data.size() || mesh->blend_shape_count > 0) {
// Size must match the size of the vertex array.
int size = p_surface.vertex_data.size();
int size = new_surface.vertex_data.size();
int vertex_size = 0;
int stride = 0;
int position_stride = 0;
int normal_tangent_stride = 0;
int normal_offset = 0;
int tangent_offset = 0;
if ((p_surface.format & (1 << RS::ARRAY_VERTEX))) {
if (p_surface.format & RS::ARRAY_FLAG_USE_2D_VERTICES) {
if ((new_surface.format & (1ULL << RS::ARRAY_VERTEX))) {
if (new_surface.format & RS::ARRAY_FLAG_USE_2D_VERTICES) {
vertex_size = 2;
position_stride = sizeof(float) * vertex_size;
} else {
vertex_size = 3;
if (new_surface.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
vertex_size = 4;
position_stride = sizeof(uint16_t) * vertex_size;
} else {
vertex_size = 3;
position_stride = sizeof(float) * vertex_size;
}
}
stride = sizeof(float) * vertex_size;
}
if ((p_surface.format & (1 << RS::ARRAY_NORMAL))) {
normal_offset = stride;
stride += sizeof(uint16_t) * 2;
if ((new_surface.format & (1ULL << RS::ARRAY_NORMAL))) {
normal_offset = position_stride * s->vertex_count;
normal_tangent_stride += sizeof(uint16_t) * 2;
}
if ((p_surface.format & (1 << RS::ARRAY_TANGENT))) {
tangent_offset = stride;
stride += sizeof(uint16_t) * 2;
if ((new_surface.format & (1ULL << RS::ARRAY_TANGENT))) {
tangent_offset = normal_offset + normal_tangent_stride;
normal_tangent_stride += sizeof(uint16_t) * 2;
}
if (mesh->blend_shape_count > 0) {
@ -282,54 +317,38 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
glBindVertexArray(s->blend_shapes[i].vertex_array);
glGenBuffers(1, &s->blend_shapes[i].vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, s->blend_shapes[i].vertex_buffer);
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s->blend_shapes[i].vertex_buffer, size, p_surface.blend_shape_data.ptr() + i * size, (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW, "Mesh blend shape buffer");
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s->blend_shapes[i].vertex_buffer, size, new_surface.blend_shape_data.ptr() + i * size, (s->format & RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW, "Mesh blend shape buffer");
if ((p_surface.format & (1 << RS::ARRAY_VERTEX))) {
if ((new_surface.format & (1ULL << RS::ARRAY_VERTEX))) {
glEnableVertexAttribArray(RS::ARRAY_VERTEX + 3);
glVertexAttribPointer(RS::ARRAY_VERTEX + 3, vertex_size, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(0));
glVertexAttribPointer(RS::ARRAY_VERTEX + 3, vertex_size, GL_FLOAT, GL_FALSE, position_stride, CAST_INT_TO_UCHAR_PTR(0));
}
if ((p_surface.format & (1 << RS::ARRAY_NORMAL))) {
if ((new_surface.format & (1ULL << RS::ARRAY_NORMAL))) {
// Normal and tangent are packed into the same attribute.
glEnableVertexAttribArray(RS::ARRAY_NORMAL + 3);
glVertexAttribPointer(RS::ARRAY_NORMAL + 3, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(normal_offset));
glVertexAttribPointer(RS::ARRAY_NORMAL + 3, 2, GL_UNSIGNED_SHORT, GL_TRUE, normal_tangent_stride, CAST_INT_TO_UCHAR_PTR(normal_offset));
}
if ((p_surface.format & (1 << RS::ARRAY_TANGENT))) {
if ((p_surface.format & (1ULL << RS::ARRAY_TANGENT))) {
glEnableVertexAttribArray(RS::ARRAY_TANGENT + 3);
glVertexAttribPointer(RS::ARRAY_TANGENT + 3, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(tangent_offset));
glVertexAttribPointer(RS::ARRAY_TANGENT + 3, 2, GL_UNSIGNED_SHORT, GL_TRUE, normal_tangent_stride, CAST_INT_TO_UCHAR_PTR(tangent_offset));
}
}
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// Create a vertex array to use for skeleton/blend shapes.
glGenVertexArrays(1, &s->skeleton_vertex_array);
glBindVertexArray(s->skeleton_vertex_array);
glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer);
if ((p_surface.format & (1 << RS::ARRAY_VERTEX))) {
glEnableVertexAttribArray(RS::ARRAY_VERTEX);
glVertexAttribPointer(RS::ARRAY_VERTEX, vertex_size, GL_FLOAT, GL_FALSE, stride, CAST_INT_TO_UCHAR_PTR(0));
}
if ((p_surface.format & (1 << RS::ARRAY_NORMAL))) {
glEnableVertexAttribArray(RS::ARRAY_NORMAL);
glVertexAttribPointer(RS::ARRAY_NORMAL, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(normal_offset));
}
if ((p_surface.format & (1 << RS::ARRAY_TANGENT))) {
glEnableVertexAttribArray(RS::ARRAY_TANGENT);
glVertexAttribPointer(RS::ARRAY_TANGENT, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, CAST_INT_TO_UCHAR_PTR(tangent_offset));
}
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
if (mesh->surface_count == 0) {
mesh->aabb = p_surface.aabb;
mesh->aabb = new_surface.aabb;
} else {
mesh->aabb.merge_with(p_surface.aabb);
mesh->aabb.merge_with(new_surface.aabb);
}
mesh->skeleton_aabb_version = 0;
s->material = p_surface.material;
s->material = new_surface.material;
mesh->surfaces = (Mesh::Surface **)memrealloc(mesh->surfaces, sizeof(Mesh::Surface *) * (mesh->surface_count + 1));
mesh->surfaces[mesh->surface_count] = s;
@ -479,6 +498,8 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const {
}
}
sd.uv_scale = s.uv_scale;
return sd;
}
@ -696,10 +717,6 @@ void MeshStorage::mesh_clear(RID p_mesh) {
}
memdelete_arr(s.blend_shapes);
}
if (s.skeleton_vertex_array != 0) {
glDeleteVertexArrays(1, &s.skeleton_vertex_array);
s.skeleton_vertex_array = 0;
}
memdelete(mesh->surfaces[i]);
}
@ -720,15 +737,16 @@ void MeshStorage::mesh_clear(RID p_mesh) {
}
}
void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, MeshInstance::Surface *mis) {
void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, MeshInstance::Surface *mis) {
Mesh::Surface::Attrib attribs[RS::ARRAY_MAX];
int position_stride = 0; // Vertex position only.
int normal_tangent_stride = 0;
int attributes_stride = 0;
int vertex_stride = 0;
int skin_stride = 0;
for (int i = 0; i < RS::ARRAY_INDEX; i++) {
if (!(s->format & (1 << i))) {
if (!(s->format & (1ULL << i))) {
attribs[i].enabled = false;
attribs[i].integer = false;
continue;
@ -739,29 +757,55 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
switch (i) {
case RS::ARRAY_VERTEX: {
attribs[i].offset = vertex_stride;
attribs[i].offset = 0;
attribs[i].type = GL_FLOAT;
attribs[i].normalized = GL_FALSE;
if (s->format & RS::ARRAY_FLAG_USE_2D_VERTICES) {
attribs[i].size = 2;
position_stride = attribs[i].size * sizeof(float);
} else {
attribs[i].size = 3;
if (!mis && (s->format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
attribs[i].size = 4;
position_stride = attribs[i].size * sizeof(uint16_t);
attribs[i].type = GL_UNSIGNED_SHORT;
attribs[i].normalized = GL_TRUE;
} else {
attribs[i].size = 3;
position_stride = attribs[i].size * sizeof(float);
}
}
attribs[i].type = GL_FLOAT;
vertex_stride += attribs[i].size * sizeof(float);
attribs[i].normalized = GL_FALSE;
} break;
case RS::ARRAY_NORMAL: {
attribs[i].offset = vertex_stride;
attribs[i].size = 2;
if (!mis && (s->format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
attribs[i].size = 2;
normal_tangent_stride += 2 * attribs[i].size;
} else {
attribs[i].size = 4;
// A small trick here: if we are uncompressed and we have normals, but no tangents. We need
// the shader to think there are 4 components to "axis_tangent_attrib". So we give a size of 4,
// but a stride based on only having 2 elements.
if (!(s->format & RS::ARRAY_FORMAT_TANGENT)) {
normal_tangent_stride += (mis ? sizeof(float) : sizeof(uint16_t)) * 2;
} else {
normal_tangent_stride += (mis ? sizeof(float) : sizeof(uint16_t)) * 4;
}
}
if (mis) {
// Transform feedback has interleave all or no attributes. It can't mix interleaving.
attribs[i].offset = position_stride;
normal_tangent_stride += position_stride;
position_stride = normal_tangent_stride;
} else {
attribs[i].offset = position_stride * s->vertex_count;
}
attribs[i].type = (mis ? GL_FLOAT : GL_UNSIGNED_SHORT);
vertex_stride += sizeof(uint16_t) * 2 * (mis ? 2 : 1);
attribs[i].normalized = GL_TRUE;
} break;
case RS::ARRAY_TANGENT: {
attribs[i].offset = vertex_stride;
attribs[i].size = 2;
attribs[i].type = (mis ? GL_FLOAT : GL_UNSIGNED_SHORT);
vertex_stride += sizeof(uint16_t) * 2 * (mis ? 2 : 1);
attribs[i].normalized = GL_TRUE;
// We never use the tangent attribute. It is always packed in ARRAY_NORMAL, or ARRAY_VERTEX.
attribs[i].enabled = false;
attribs[i].integer = false;
} break;
case RS::ARRAY_COLOR: {
attribs[i].offset = attributes_stride;
@ -773,16 +817,28 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
case RS::ARRAY_TEX_UV: {
attribs[i].offset = attributes_stride;
attribs[i].size = 2;
attribs[i].type = GL_FLOAT;
attributes_stride += 2 * sizeof(float);
attribs[i].normalized = GL_FALSE;
if (s->format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
attribs[i].type = GL_UNSIGNED_SHORT;
attributes_stride += 2 * sizeof(uint16_t);
attribs[i].normalized = GL_TRUE;
} else {
attribs[i].type = GL_FLOAT;
attributes_stride += 2 * sizeof(float);
attribs[i].normalized = GL_FALSE;
}
} break;
case RS::ARRAY_TEX_UV2: {
attribs[i].offset = attributes_stride;
attribs[i].size = 2;
attribs[i].type = GL_FLOAT;
attributes_stride += 2 * sizeof(float);
attribs[i].normalized = GL_FALSE;
if (s->format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
attribs[i].type = GL_UNSIGNED_SHORT;
attributes_stride += 2 * sizeof(uint16_t);
attribs[i].normalized = GL_TRUE;
} else {
attribs[i].type = GL_FLOAT;
attributes_stride += 2 * sizeof(float);
attribs[i].normalized = GL_FALSE;
}
} break;
case RS::ARRAY_CUSTOM0:
case RS::ARRAY_CUSTOM1:
@ -828,7 +884,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
continue;
}
if (i <= RS::ARRAY_TANGENT) {
attribs[i].stride = vertex_stride;
attribs[i].stride = (i == RS::ARRAY_VERTEX) ? position_stride : normal_tangent_stride;
if (mis) {
glBindBuffer(GL_ARRAY_BUFFER, mis->vertex_buffer);
} else {
@ -946,7 +1002,7 @@ void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint3
if ((mesh->blend_shape_count > 0 || (mesh->surfaces[p_surface]->format & RS::ARRAY_FORMAT_BONES)) && mesh->surfaces[p_surface]->vertex_buffer_size > 0) {
// Cache surface properties
s.format_cache = mesh->surfaces[p_surface]->format;
if ((s.format_cache & (1 << RS::ARRAY_VERTEX))) {
if ((s.format_cache & (1ULL << RS::ARRAY_VERTEX))) {
if (s.format_cache & RS::ARRAY_FLAG_USE_2D_VERTICES) {
s.vertex_size_cache = 2;
} else {
@ -954,25 +1010,27 @@ void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint3
}
s.vertex_stride_cache = sizeof(float) * s.vertex_size_cache;
}
if ((s.format_cache & (1 << RS::ARRAY_NORMAL))) {
if ((s.format_cache & (1ULL << RS::ARRAY_NORMAL))) {
s.vertex_normal_offset_cache = s.vertex_stride_cache;
s.vertex_stride_cache += sizeof(uint32_t) * 2;
}
if ((s.format_cache & (1 << RS::ARRAY_TANGENT))) {
if ((s.format_cache & (1ULL << RS::ARRAY_TANGENT))) {
s.vertex_tangent_offset_cache = s.vertex_stride_cache;
s.vertex_stride_cache += sizeof(uint32_t) * 2;
}
int buffer_size = s.vertex_stride_cache * mesh->surfaces[p_surface]->vertex_count;
// Buffer to be used for rendering. Final output of skeleton and blend shapes.
glGenBuffers(1, &s.vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, s.vertex_buffer);
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s.vertex_buffer, s.vertex_stride_cache * mesh->surfaces[p_surface]->vertex_count, nullptr, GL_DYNAMIC_DRAW, "MeshInstance vertex buffer");
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s.vertex_buffer, buffer_size, nullptr, GL_DYNAMIC_DRAW, "MeshInstance vertex buffer");
if (mesh->blend_shape_count > 0) {
// Ping-Pong buffers for processing blendshapes.
glGenBuffers(2, s.vertex_buffers);
for (uint32_t i = 0; i < 2; i++) {
glBindBuffer(GL_ARRAY_BUFFER, s.vertex_buffers[i]);
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s.vertex_buffers[i], s.vertex_stride_cache * mesh->surfaces[p_surface]->vertex_count, nullptr, GL_DYNAMIC_DRAW, "MeshInstance process buffer[" + itos(i) + "]");
GLES3::Utilities::get_singleton()->buffer_allocate_data(GL_ARRAY_BUFFER, s.vertex_buffers[i], buffer_size, nullptr, GL_DYNAMIC_DRAW, "MeshInstance process buffer[" + itos(i) + "]");
}
}
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
@ -1011,19 +1069,19 @@ void MeshStorage::mesh_instance_set_canvas_item_transform(RID p_mesh_instance, c
void MeshStorage::_blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface) {
glBindBuffer(GL_ARRAY_BUFFER, p_mi->surfaces[p_surface].vertex_buffers[0]);
if ((p_mi->surfaces[p_surface].format_cache & (1 << RS::ARRAY_VERTEX))) {
if ((p_mi->surfaces[p_surface].format_cache & (1ULL << RS::ARRAY_VERTEX))) {
glEnableVertexAttribArray(RS::ARRAY_VERTEX);
glVertexAttribPointer(RS::ARRAY_VERTEX, p_mi->surfaces[p_surface].vertex_size_cache, GL_FLOAT, GL_FALSE, p_mi->surfaces[p_surface].vertex_stride_cache, CAST_INT_TO_UCHAR_PTR(0));
} else {
glDisableVertexAttribArray(RS::ARRAY_VERTEX);
}
if ((p_mi->surfaces[p_surface].format_cache & (1 << RS::ARRAY_NORMAL))) {
if ((p_mi->surfaces[p_surface].format_cache & (1ULL << RS::ARRAY_NORMAL))) {
glEnableVertexAttribArray(RS::ARRAY_NORMAL);
glVertexAttribIPointer(RS::ARRAY_NORMAL, 2, GL_UNSIGNED_INT, p_mi->surfaces[p_surface].vertex_stride_cache, CAST_INT_TO_UCHAR_PTR(p_mi->surfaces[p_surface].vertex_normal_offset_cache));
} else {
glDisableVertexAttribArray(RS::ARRAY_NORMAL);
}
if ((p_mi->surfaces[p_surface].format_cache & (1 << RS::ARRAY_TANGENT))) {
if ((p_mi->surfaces[p_surface].format_cache & (1ULL << RS::ARRAY_TANGENT))) {
glEnableVertexAttribArray(RS::ARRAY_TANGENT);
glVertexAttribIPointer(RS::ARRAY_TANGENT, 2, GL_UNSIGNED_INT, p_mi->surfaces[p_surface].vertex_stride_cache, CAST_INT_TO_UCHAR_PTR(p_mi->surfaces[p_surface].vertex_tangent_offset_cache));
} else {
@ -1091,7 +1149,7 @@ void MeshStorage::update_mesh_instances() {
}
for (uint32_t i = 0; i < mi->surfaces.size(); i++) {
if (mi->surfaces[i].vertex_buffer == 0 || mi->mesh->surfaces[i]->skeleton_vertex_array == 0) {
if (mi->surfaces[i].vertex_buffer == 0) {
continue;
}
@ -1106,10 +1164,10 @@ void MeshStorage::update_mesh_instances() {
specialization |= array_is_2d ? SkeletonShaderGLES3::MODE_2D : 0;
specialization |= SkeletonShaderGLES3::USE_BLEND_SHAPES;
if (!array_is_2d) {
if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_NORMAL))) {
if ((mi->surfaces[i].format_cache & (1ULL << RS::ARRAY_NORMAL))) {
specialization |= SkeletonShaderGLES3::USE_NORMAL;
}
if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_TANGENT))) {
if ((mi->surfaces[i].format_cache & (1ULL << RS::ARRAY_TANGENT))) {
specialization |= SkeletonShaderGLES3::USE_TANGENT;
}
}
@ -1123,7 +1181,12 @@ void MeshStorage::update_mesh_instances() {
skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_SHAPE_COUNT, float(mi->mesh->blend_shape_count), skeleton_shader.shader_version, variant, specialization);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(mi->mesh->surfaces[i]->skeleton_vertex_array);
GLuint vertex_array_gl = 0;
uint64_t mask = ((1 << 10) - 1) << 3; // Mask from ARRAY_FORMAT_COLOR to ARRAY_FORMAT_INDEX.
mask = ~mask;
uint64_t format = mi->surfaces[i].format_cache & mask; // Format should only have vertex, normal, tangent (as necessary) + compressions.
mesh_surface_get_vertex_arrays_and_format(mi->mesh->surfaces[i], format, vertex_array_gl);
glBindVertexArray(vertex_array_gl);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mi->surfaces[i].vertex_buffers[0]);
glBeginTransformFeedback(GL_POINTS);
glDrawArrays(GL_POINTS, 0, mi->mesh->surfaces[i]->vertex_count);
@ -1210,10 +1273,10 @@ void MeshStorage::update_mesh_instances() {
specialization |= SkeletonShaderGLES3::FINAL_PASS;
specialization |= use_8_weights ? SkeletonShaderGLES3::USE_EIGHT_WEIGHTS : 0;
if (!array_is_2d) {
if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_NORMAL))) {
if ((mi->surfaces[i].format_cache & (1ULL << RS::ARRAY_NORMAL))) {
specialization |= SkeletonShaderGLES3::USE_NORMAL;
}
if ((mi->surfaces[i].format_cache & (1 << RS::ARRAY_TANGENT))) {
if ((mi->surfaces[i].format_cache & (1ULL << RS::ARRAY_TANGENT))) {
specialization |= SkeletonShaderGLES3::USE_TANGENT;
}
}
@ -1233,7 +1296,12 @@ void MeshStorage::update_mesh_instances() {
skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_Y, inverse_transform[1], skeleton_shader.shader_version, variant, specialization);
skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_OFFSET, inverse_transform[2], skeleton_shader.shader_version, variant, specialization);
glBindVertexArray(mi->mesh->surfaces[i]->skeleton_vertex_array);
GLuint vertex_array_gl = 0;
uint64_t mask = ((1 << 10) - 1) << 3; // Mask from ARRAY_FORMAT_COLOR to ARRAY_FORMAT_INDEX.
mask = ~mask;
uint64_t format = mi->surfaces[i].format_cache & mask; // Format should only have vertex, normal, tangent (as necessary) + compressions.
mesh_surface_get_vertex_arrays_and_format(mi->mesh->surfaces[i], format, vertex_array_gl);
glBindVertexArray(vertex_array_gl);
_compute_skeleton(mi, sk, i);
}
}

View file

@ -58,7 +58,7 @@ struct Mesh {
uint32_t offset;
};
RS::PrimitiveType primitive = RS::PRIMITIVE_POINTS;
uint32_t format = 0;
uint64_t format = 0;
GLuint vertex_buffer = 0;
GLuint attribute_buffer = 0;
@ -98,6 +98,8 @@ struct Mesh {
Vector<AABB> bone_aabbs;
Vector4 uv_scale;
struct BlendShape {
GLuint vertex_buffer = 0;
GLuint vertex_array = 0;
@ -144,7 +146,7 @@ struct MeshInstance {
int vertex_size_cache = 0;
int vertex_normal_offset_cache = 0;
int vertex_tangent_offset_cache = 0;
uint32_t format_cache = 0;
uint64_t format_cache = 0;
Mesh::Surface::Version *versions = nullptr; //allocated on demand
uint32_t version_count = 0;
@ -221,7 +223,7 @@ private:
mutable RID_Owner<Mesh, true> mesh_owner;
void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, MeshInstance::Surface *mis = nullptr);
void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, MeshInstance::Surface *mis = nullptr);
/* Mesh Instance API */
@ -381,18 +383,18 @@ public:
}
// Use this to cache Vertex Array Objects so they are only generated once
_FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint32_t p_input_mask, GLuint &r_vertex_array_gl) {
_FORCE_INLINE_ void mesh_surface_get_vertex_arrays_and_format(void *p_surface, uint64_t p_input_mask, GLuint &r_vertex_array_gl) {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
s->version_lock.lock();
//there will never be more than, at much, 3 or 4 versions, so iterating is the fastest way
// There will never be more than 3 or 4 versions, so iterating is the fastest way.
for (uint32_t i = 0; i < s->version_count; i++) {
if (s->versions[i].input_mask != p_input_mask) {
continue;
}
//we have this version, hooray
// We have this version, hooray.
r_vertex_array_gl = s->versions[i].vertex_array;
s->version_lock.unlock();
return;
@ -424,7 +426,7 @@ public:
// TODO: considering hashing versions with multimesh buffer RID.
// Doing so would allow us to avoid specifying multimesh buffer pointers every frame and may improve performance.
_FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint32_t p_surface_index, uint32_t p_input_mask, GLuint &r_vertex_array_gl) {
_FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint32_t p_surface_index, uint64_t p_input_mask, GLuint &r_vertex_array_gl) {
MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance);
ERR_FAIL_NULL(mi);
Mesh *mesh = mi->mesh;

View file

@ -4499,6 +4499,7 @@ RID RenderingDeviceVulkan::vertex_array_create(uint32_t p_vertex_count, VertexFo
if (atf.frequency == VERTEX_FREQUENCY_VERTEX) {
// Validate size for regular drawing.
uint64_t total_size = uint64_t(atf.stride) * (p_vertex_count - 1) + atf.offset + element_size;
ERR_FAIL_COND_V_MSG(total_size > buffer->size, RID(),
"Attachment (" + itos(i) + ") will read past the end of the buffer.");
@ -4665,7 +4666,7 @@ struct RenderingDeviceVulkanShaderBinarySpecializationConstant {
};
struct RenderingDeviceVulkanShaderBinaryData {
uint32_t vertex_input_mask;
uint64_t vertex_input_mask;
uint32_t fragment_output_mask;
uint32_t specialization_constants_count;
uint32_t is_compute;
@ -4881,7 +4882,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_
push_constant.size = binary_data.push_constant_size;
push_constant.vk_stages_mask = binary_data.push_constant_vk_stages_mask;
uint32_t vertex_input_mask = binary_data.vertex_input_mask;
uint64_t vertex_input_mask = binary_data.vertex_input_mask;
uint32_t fragment_output_mask = binary_data.fragment_output_mask;
@ -5209,7 +5210,7 @@ RID RenderingDeviceVulkan::shader_create_placeholder() {
return shader_owner.make_rid(shader);
}
uint32_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) {
uint64_t RenderingDeviceVulkan::shader_get_vertex_input_attribute_mask(RID p_shader) {
_THREAD_SAFE_METHOD_
const Shader *shader = shader_owner.get_or_null(p_shader);
@ -6152,8 +6153,8 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma
pipeline_vertex_input_state_create_info = vd.create_info;
// Validate with inputs.
for (uint32_t i = 0; i < 32; i++) {
if (!(shader->vertex_input_mask & (1UL << i))) {
for (uint64_t i = 0; i < 64; i++) {
if (!(shader->vertex_input_mask & (1ULL << i))) {
continue;
}
bool found = false;

View file

@ -621,7 +621,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE;
};
uint32_t vertex_input_mask = 0; // Inputs used, this is mostly for validation.
uint64_t vertex_input_mask = 0; // Inputs used, this is mostly for validation.
uint32_t fragment_output_mask = 0;
struct PushConstant {
@ -1140,7 +1140,7 @@ public:
virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder = RID());
virtual RID shader_create_placeholder();
virtual uint32_t shader_get_vertex_input_attribute_mask(RID p_shader);
virtual uint64_t shader_get_vertex_input_attribute_mask(RID p_shader);
/*****************/
/**** UNIFORM ****/

View file

@ -919,6 +919,12 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p
}
}
uint64_t mesh_flags = 0;
if (p_use_compression) {
mesh_flags = RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
}
Ref<SurfaceTool> surftool;
surftool.instantiate();
surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
@ -969,14 +975,21 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p
}
if (!normal_src) {
//should always be normals
// Should always have normals.
surftool->generate_normals();
}
if ((!binormal_src || !tangent_src) && normal_src && uv_src && force_make_tangents) {
bool generate_tangents = (!binormal_src || !tangent_src) && uv_src && force_make_tangents;
if (generate_tangents) {
surftool->generate_tangents();
}
if (!binormal_src || !(tangent_src || generate_tangents) || p_mesh->get_blend_shape_count() != 0 || p_skin_controller) {
// Can't compress if attributes missing or if using vertex weights.
mesh_flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
}
////////////////////////////
// FINALLY CREATE SUFRACE //
////////////////////////////
@ -996,7 +1009,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p
// Enforce blend shape mask array format
for (int mj = 0; mj < Mesh::ARRAY_MAX; mj++) {
if (!(Mesh::ARRAY_FORMAT_BLEND_SHAPE_MASK & (1 << mj))) {
if (!(Mesh::ARRAY_FORMAT_BLEND_SHAPE_MASK & (1ULL << mj))) {
a[mj] = Variant();
}
}
@ -1011,7 +1024,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p
}
surface_name = material->get_name();
}
p_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, d, mr, Dictionary(), mat, surface_name);
p_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, d, mr, Dictionary(), mat, surface_name, mesh_flags);
}
/*****************/
@ -1773,7 +1786,7 @@ Node *EditorSceneFormatImporterCollada::import_scene(const String &p_path, uint3
state.use_mesh_builtin_materials = true;
state.bake_fps = (float)p_options["animation/fps"];
Error err = state.load(p_path, flags, p_flags & EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, false);
Error err = state.load(p_path, flags, p_flags & EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, !bool(p_flags & EditorSceneFormatImporter::IMPORT_FORCE_DISABLE_MESH_COMPRESSION));
if (r_err) {
*r_err = err;

View file

@ -202,7 +202,7 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S
return OK;
}
static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_optimize, Vector3 p_scale_mesh, Vector3 p_offset_mesh, List<String> *r_missing_deps) {
static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_optimize, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, List<String> *r_missing_deps) {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open OBJ file '%s', it may not exist or not be readable.", p_path));
@ -226,7 +226,11 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &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 = 0;
uint64_t mesh_flags = RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
if (p_disable_compression) {
mesh_flags = 0;
}
Vector<Vector3> vertices;
Vector<Vector3> normals;
@ -287,7 +291,6 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
uv.x = v[1].to_float();
uv.y = 1.0 - v[2].to_float();
uvs.push_back(uv);
} else if (l.begins_with("vn ")) {
//normal
Vector<String> v = l.split(" ", false);
@ -380,6 +383,9 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
if (generate_tangents && uvs.size()) {
surf_tool->generate_tangents();
} else {
// We need tangents in order to compress vertex data. So disable if tangents aren't generated.
mesh_flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
}
surf_tool->index();
@ -464,7 +470,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {
List<Ref<Mesh>> meshes;
Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, Vector3(1, 1, 1), Vector3(0, 0, 0), r_missing_deps);
Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, Vector3(1, 1, 1), Vector3(0, 0, 0), p_flags & IMPORT_FORCE_DISABLE_MESH_COMPRESSION, r_missing_deps);
if (err != OK) {
if (r_err) {
@ -543,6 +549,7 @@ void ResourceImporterOBJ::get_import_options(const String &p_path, List<ImportOp
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "scale_mesh"), Vector3(1, 1, 1)));
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "offset_mesh"), Vector3(0, 0, 0)));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimize_mesh"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_disable_mesh_compression"), false));
}
bool ResourceImporterOBJ::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
@ -552,7 +559,7 @@ bool ResourceImporterOBJ::get_option_visibility(const String &p_path, const Stri
Error ResourceImporterOBJ::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
List<Ref<Mesh>> meshes;
Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["optimize_mesh"], p_options["scale_mesh"], p_options["offset_mesh"], nullptr);
Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["optimize_mesh"], p_options["scale_mesh"], p_options["offset_mesh"], p_options["force_disable_mesh_compression"], nullptr);
ERR_FAIL_COND_V(err != OK, err);
ERR_FAIL_COND_V(meshes.size() != 1, ERR_BUG);

View file

@ -112,6 +112,7 @@ void EditorSceneFormatImporter::_bind_methods() {
BIND_CONSTANT(IMPORT_GENERATE_TANGENT_ARRAYS);
BIND_CONSTANT(IMPORT_USE_NAMED_SKIN_BINDS);
BIND_CONSTANT(IMPORT_DISCARD_MESHES_AND_MATERIALS);
BIND_CONSTANT(IMPORT_FORCE_DISABLE_MESH_COMPRESSION);
}
/////////////////////////////////
@ -1934,6 +1935,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Static (VoxelGI/SDFGI),Static Lightmaps (VoxelGI/SDFGI/LightmapGI),Dynamic (VoxelGI only)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/force_disable_compression"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30));
@ -2428,6 +2430,11 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
import_flags |= EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS;
}
bool force_disable_compression = p_options["meshes/force_disable_compression"];
if (force_disable_compression) {
import_flags |= EditorSceneFormatImporter::IMPORT_FORCE_DISABLE_MESH_COMPRESSION;
}
Error err = OK;
List<String> missing_deps; // for now, not much will be done with this
Node *scene = importer->import_scene(src_path, import_flags, p_options, &missing_deps, &err);

View file

@ -71,6 +71,7 @@ public:
IMPORT_GENERATE_TANGENT_ARRAYS = 8,
IMPORT_USE_NAMED_SKIN_BINDS = 16,
IMPORT_DISCARD_MESHES_AND_MATERIALS = 32, //used for optimizing animation import
IMPORT_FORCE_DISABLE_MESH_COMPRESSION = 64,
};
virtual uint32_t get_import_flags() const;

View file

@ -364,6 +364,13 @@ def build_gles3_header(
+ str(defspec)
+ ") { _FU GLfloat vec3[3]={float(p_vec3.x),float(p_vec3.y),float(p_vec3.z)}; glUniform3fv(version_get_uniform(p_uniform,p_version,p_variant,p_specialization),1,vec3); }\n\n"
)
fd.write(
"\t_FORCE_INLINE_ void version_set_uniform(Uniforms p_uniform, const Vector4& p_vec4,RID p_version,ShaderVariant p_variant"
+ defvariant
+ ",uint64_t p_specialization="
+ str(defspec)
+ ") { _FU GLfloat vec4[4]={float(p_vec4.x),float(p_vec4.y),float(p_vec4.z),float(p_vec4.w)}; glUniform4fv(version_get_uniform(p_uniform,p_version,p_variant,p_specialization),1,vec4); }\n\n"
)
fd.write(
"\t_FORCE_INLINE_ void version_set_uniform(Uniforms p_uniform, float p_a, float p_b,RID p_version,ShaderVariant p_variant"
+ defvariant

View file

@ -274,3 +274,16 @@ Validate extension JSON: API was removed: classes/GraphEdit/methods/set_arrange_
Validate extension JSON: API was removed: classes/GraphEdit/properties/arrange_nodes_button_hidden
Make GraphEdit toolbar more customizable
GH-81138
--------
Validate extension JSON: Error: Field 'classes/ImporterMesh/methods/add_surface/arguments/6': meta changed value in new API, from "uint32" to "uint64".
Validate extension JSON: Error: Field 'classes/ImporterMesh/methods/get_surface_format/return_value': meta changed value in new API, from "uint32" to "uint64".
Validate extension JSON: Error: Field 'classes/MeshDataTool/methods/commit_to_surface/arguments': size changed value in new API, from 1 to 2.
Validate extension JSON: Error: Field 'classes/MeshDataTool/methods/get_format/return_value': meta changed value in new API, from "int32" to "uint64".
Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/shader_get_vertex_input_attribute_mask/return_value': meta changed value in new API, from "uint32" to "uint64".
Validate extension JSON: Error: Field 'classes/SurfaceTool/methods/commit/arguments/1': meta changed value in new API, from "uint32" to "uint64".
Surface format was increased to 64 bits from 32 bits. Compatibility methods registered.

View file

@ -66,8 +66,10 @@
#endif // MODULE_GRIDMAP_ENABLED
// FIXME: Hardcoded to avoid editor dependency.
#define GLTF_IMPORT_GENERATE_TANGENT_ARRAYS 8
#define GLTF_IMPORT_USE_NAMED_SKIN_BINDS 16
#define GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS 32
#define GLTF_IMPORT_FORCE_DISABLE_MESH_COMPRESSION 64
#include <stdio.h>
#include <stdlib.h>
@ -2208,7 +2210,7 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> p_state) {
}
Array array = import_mesh->get_surface_arrays(surface_i);
uint32_t format = import_mesh->get_surface_format(surface_i);
uint64_t format = import_mesh->get_surface_format(surface_i);
int32_t vertex_num = 0;
Dictionary attributes;
{
@ -2568,7 +2570,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
import_mesh->set_name(_gen_unique_name(p_state, vformat("%s_%s", p_state->scene_name, mesh_name)));
for (int j = 0; j < primitives.size(); j++) {
uint32_t flags = 0;
uint64_t flags = RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
Dictionary p = primitives[j];
Array array;
@ -2795,7 +2797,11 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
array[Mesh::ARRAY_INDEX] = indices;
}
bool generate_tangents = (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("TEXCOORD_0") && a.has("NORMAL"));
bool generate_tangents = p_state->force_generate_tangents && (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("TEXCOORD_0") && a.has("NORMAL"));
if (p_state->force_disable_compression || !a.has("POSITION") || !a.has("NORMAL") || !(a.has("TANGENT") || generate_tangents) || p.has("targets") || (a.has("JOINTS_0") || a.has("JOINTS_1"))) {
flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
}
Ref<SurfaceTool> mesh_surface_tool;
mesh_surface_tool.instantiate();
@ -2935,7 +2941,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
// Enforce blend shape mask array format
for (int l = 0; l < Mesh::ARRAY_MAX; l++) {
if (!(Mesh::ARRAY_FORMAT_BLEND_SHAPE_MASK & (1 << l))) {
if (!(Mesh::ARRAY_FORMAT_BLEND_SHAPE_MASK & (1ULL << l))) {
array_copy[l] = Variant();
}
}
@ -7415,6 +7421,8 @@ Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint
ERR_FAIL_COND_V(p_state.is_null(), FAILED);
p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
p_state->force_generate_tangents = p_flags & GLTF_IMPORT_GENERATE_TANGENT_ARRAYS;
p_state->force_disable_compression = p_flags & GLTF_IMPORT_FORCE_DISABLE_MESH_COMPRESSION;
if (!p_state->buffers.size()) {
p_state->buffers.push_back(Vector<uint8_t>());
}
@ -7452,6 +7460,8 @@ Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_pa
Error err = FAILED;
p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
p_state->force_generate_tangents = p_flags & GLTF_IMPORT_GENERATE_TANGENT_ARRAYS;
p_state->force_disable_compression = p_flags & GLTF_IMPORT_FORCE_DISABLE_MESH_COMPRESSION;
Ref<FileAccessMemory> file_access;
file_access.instantiate();
@ -7575,6 +7585,9 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> p_state, uint
p_state->filename = p_path.get_file().get_basename();
p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
p_state->force_generate_tangents = p_flags & GLTF_IMPORT_GENERATE_TANGENT_ARRAYS;
p_state->force_disable_compression = p_flags & GLTF_IMPORT_FORCE_DISABLE_MESH_COMPRESSION;
Error err;
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err);
ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN);

View file

@ -58,7 +58,9 @@ class GLTFState : public Resource {
bool use_named_skin_binds = false;
bool use_khr_texture_transform = false;
bool discard_meshes_and_materials = false;
bool force_generate_tangents = false;
bool create_animations = true;
bool force_disable_compression = false;
int handle_binary_image = HANDLE_BINARY_EXTRACT_TEXTURES;

View file

@ -128,6 +128,16 @@ partial class GraphNode
remove => DeleteRequest -= value;
}
}
partial class ImporterMesh
{
/// <inheritdoc cref="AddSurface(Mesh.PrimitiveType, Godot.Collections.Array, Godot.Collections.Array{Godot.Collections.Array}, Godot.Collections.Dictionary, Material, string, ulong)"/>
[EditorBrowsable(EditorBrowsableState.Never)]
public void AddSurface(Mesh.PrimitiveType primitive, Godot.Collections.Array arrays, Godot.Collections.Array<Godot.Collections.Array> blendShapes, Godot.Collections.Dictionary lods, Material material, string name, uint flags)
{
AddSurface(primitive, arrays, blendShapes, lods, material, name, (ulong)flags);
}
}
partial class MeshInstance3D
{
@ -191,6 +201,13 @@ partial class SurfaceTool
{
AddTriangleFan(vertices, uvs, colors, uv2S, normals, new Godot.Collections.Array<Plane>(tangents));
}
/// <inheritdoc cref="Commit(ArrayMesh, ulong)"/>
[EditorBrowsable(EditorBrowsableState.Never)]
public ArrayMesh Commit(ArrayMesh existing, uint flags)
{
return Commit(existing, (ulong)flags);
}
}
partial class TileMap

View file

@ -46,12 +46,14 @@ void SoftBodyRenderingServerHandler::prepare(RID p_mesh, int p_surface) {
uint32_t surface_offsets[RS::ARRAY_MAX];
uint32_t vertex_stride;
uint32_t normal_tangent_stride;
uint32_t attrib_stride;
uint32_t skin_stride;
RS::get_singleton()->mesh_surface_make_offsets_from_format(surface_data.format, surface_data.vertex_count, surface_data.index_count, surface_offsets, vertex_stride, attrib_stride, skin_stride);
RS::get_singleton()->mesh_surface_make_offsets_from_format(surface_data.format, surface_data.vertex_count, surface_data.index_count, surface_offsets, vertex_stride, normal_tangent_stride, attrib_stride, skin_stride);
buffer = surface_data.vertex_data;
stride = vertex_stride;
normal_stride = normal_tangent_stride;
offset_vertices = surface_offsets[RS::ARRAY_VERTEX];
offset_normal = surface_offsets[RS::ARRAY_NORMAL];
}
@ -59,6 +61,7 @@ void SoftBodyRenderingServerHandler::prepare(RID p_mesh, int p_surface) {
void SoftBodyRenderingServerHandler::clear() {
buffer.resize(0);
stride = 0;
normal_stride = 0;
offset_vertices = 0;
offset_normal = 0;
@ -83,15 +86,11 @@ void SoftBodyRenderingServerHandler::set_vertex(int p_vertex_id, const Vector3 &
}
void SoftBodyRenderingServerHandler::set_normal(int p_vertex_id, const Vector3 &p_normal) {
// Store normal vector in A2B10G10R10 format.
Vector3 n = p_normal;
n *= Vector3(0.5, 0.5, 0.5);
n += Vector3(0.5, 0.5, 0.5);
Vector2 res = n.octahedron_encode();
Vector2 res = p_normal.octahedron_encode();
uint32_t value = 0;
value |= (uint16_t)CLAMP(res.x * 65535, 0, 65535);
value |= (uint16_t)CLAMP(res.y * 65535, 0, 65535) << 16;
memcpy(&write_buffer[p_vertex_id * stride + offset_normal], &value, sizeof(uint32_t));
memcpy(&write_buffer[p_vertex_id * normal_stride + offset_normal], &value, sizeof(uint32_t));
}
void SoftBodyRenderingServerHandler::set_aabb(const AABB &p_aabb) {

View file

@ -44,6 +44,7 @@ class SoftBodyRenderingServerHandler : public PhysicsServer3DRenderingServerHand
int surface = 0;
Vector<uint8_t> buffer;
uint32_t stride = 0;
uint32_t normal_stride = 0;
uint32_t offset_vertices = 0;
uint32_t offset_normal = 0;

View file

@ -234,8 +234,8 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect,
float v_vertex[3] = { (float)vtx.x, (float)vtx.y, (float)vtx.z };
memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3);
memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_NORMAL]], &v_normal, 4);
memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_TANGENT]], &v_tangent, 4);
memcpy(&vertex_write_buffer[i * normal_tangent_stride + mesh_surface_offsets[RS::ARRAY_NORMAL]], &v_normal, 4);
memcpy(&vertex_write_buffer[i * normal_tangent_stride + mesh_surface_offsets[RS::ARRAY_TANGENT]], &v_tangent, 4);
memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4);
}
@ -682,7 +682,7 @@ SpriteBase3D::SpriteBase3D() {
sd.material = material;
RS::get_singleton()->mesh_surface_make_offsets_from_format(sd.format, sd.vertex_count, sd.index_count, mesh_surface_offsets, vertex_stride, attrib_stride, skin_stride);
RS::get_singleton()->mesh_surface_make_offsets_from_format(sd.format, sd.vertex_count, sd.index_count, mesh_surface_offsets, vertex_stride, normal_tangent_stride, attrib_stride, skin_stride);
RS::get_singleton()->mesh_add_surface(mesh, sd);
set_base(mesh);
}

View file

@ -112,6 +112,7 @@ protected:
PackedByteArray vertex_buffer;
PackedByteArray attribute_buffer;
uint32_t vertex_stride = 0;
uint32_t normal_tangent_stride = 0;
uint32_t attrib_stride = 0;
uint32_t skin_stride = 0;
uint32_t mesh_surface_format = 0;

View file

@ -149,7 +149,7 @@ void ImmediateMesh::surface_end() {
ERR_FAIL_COND_MSG(!surface_active, "Not creating any surface. Use surface_begin() to do it.");
ERR_FAIL_COND_MSG(!vertices.size(), "No vertices were added, surface can't be created.");
uint32_t format = ARRAY_FORMAT_VERTEX;
uint64_t format = ARRAY_FORMAT_VERTEX | ARRAY_FLAG_FORMAT_CURRENT_VERSION;
uint32_t vertex_stride = 0;
if (active_surface_data.vertex_2d) {
@ -158,18 +158,18 @@ void ImmediateMesh::surface_end() {
} else {
vertex_stride = sizeof(float) * 3;
}
uint32_t normal_tangent_stride = 0;
uint32_t normal_offset = 0;
if (uses_normals) {
format |= ARRAY_FORMAT_NORMAL;
normal_offset = vertex_stride;
vertex_stride += sizeof(uint32_t);
normal_offset = vertex_stride * vertices.size();
normal_tangent_stride += sizeof(uint32_t);
}
uint32_t tangent_offset = 0;
if (uses_tangents) {
format |= ARRAY_FORMAT_TANGENT;
tangent_offset += vertex_stride;
vertex_stride += sizeof(uint32_t);
tangent_offset = vertex_stride * vertices.size() + normal_tangent_stride;
normal_tangent_stride += sizeof(uint32_t);
}
AABB aabb;
@ -192,7 +192,7 @@ void ImmediateMesh::surface_end() {
}
}
if (uses_normals) {
uint32_t *normal = (uint32_t *)&surface_vertex_ptr[i * vertex_stride + normal_offset];
uint32_t *normal = (uint32_t *)&surface_vertex_ptr[i * normal_tangent_stride + normal_offset];
Vector2 n = normals[i].octahedron_encode();
@ -203,7 +203,7 @@ void ImmediateMesh::surface_end() {
*normal = value;
}
if (uses_tangents) {
uint32_t *tangent = (uint32_t *)&surface_vertex_ptr[i * vertex_stride + tangent_offset];
uint32_t *tangent = (uint32_t *)&surface_vertex_ptr[i * normal_tangent_stride + tangent_offset];
Vector2 t = tangents[i].normal.octahedron_tangent_encode(tangents[i].d);
uint32_t value = 0;
value |= (uint16_t)CLAMP(t.x * 65535, 0, 65535);

View file

@ -156,7 +156,7 @@ Mesh::BlendShapeMode ImporterMesh::get_blend_shape_mode() const {
return blend_shape_mode;
}
void ImporterMesh::add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes, const Dictionary &p_lods, const Ref<Material> &p_material, const String &p_name, const uint32_t p_flags) {
void ImporterMesh::add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes, const Dictionary &p_lods, const Ref<Material> &p_material, const String &p_name, const uint64_t p_flags) {
ERR_FAIL_COND(p_blend_shapes.size() != blend_shapes.size());
ERR_FAIL_COND(p_arrays.size() != Mesh::ARRAY_MAX);
Surface s;
@ -240,7 +240,7 @@ float ImporterMesh::get_surface_lod_size(int p_surface, int p_lod) const {
return surfaces[p_surface].lods[p_lod].distance;
}
uint32_t ImporterMesh::get_surface_format(int p_surface) const {
uint64_t ImporterMesh::get_surface_format(int p_surface) const {
ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0);
return surfaces[p_surface].flags;
}
@ -1105,7 +1105,7 @@ struct EditorSceneFormatImporterMeshLightmapSurface {
Ref<Material> material;
LocalVector<SurfaceTool::Vertex> vertices;
Mesh::PrimitiveType primitive = Mesh::PrimitiveType::PRIMITIVE_MAX;
uint32_t format = 0;
uint64_t format = 0;
String name;
};

View file

@ -61,7 +61,7 @@ class ImporterMesh : public Resource {
Vector<LOD> lods;
Ref<Material> material;
String name;
uint32_t flags = 0;
uint64_t flags = 0;
struct LODComparator {
_FORCE_INLINE_ bool operator()(const LOD &l, const LOD &r) const {
@ -93,7 +93,7 @@ public:
int get_blend_shape_count() const;
String get_blend_shape_name(int p_blend_shape) const;
void add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), const Ref<Material> &p_material = Ref<Material>(), const String &p_name = String(), const uint32_t p_flags = 0);
void add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), const Ref<Material> &p_material = Ref<Material>(), const String &p_name = String(), const uint64_t p_flags = 0);
int get_surface_count() const;
void set_blend_shape_mode(Mesh::BlendShapeMode p_blend_shape_mode);
@ -108,7 +108,7 @@ public:
Vector<int> get_surface_lod_indices(int p_surface, int p_lod) const;
float get_surface_lod_size(int p_surface, int p_lod) const;
Ref<Material> get_surface_material(int p_surface) const;
uint32_t get_surface_format(int p_surface) const;
uint64_t get_surface_format(int p_surface) const;
void set_surface_material(int p_surface, const Ref<Material> &p_material);

View file

@ -350,6 +350,8 @@ private:
}
};
size_t sss = sizeof(MaterialKey);
struct ShaderData {
RID shader;
int users = 0;

View file

@ -1011,12 +1011,13 @@ static Mesh::PrimitiveType _old_primitives[7] = {
};
#endif // DISABLE_DEPRECATED
void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_format, uint32_t p_new_format, uint32_t p_elements, Vector<uint8_t> &vertex_data, Vector<uint8_t> &attribute_data, Vector<uint8_t> &skin_data) {
void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint64_t p_old_format, uint64_t p_new_format, uint32_t p_elements, Vector<uint8_t> &vertex_data, Vector<uint8_t> &attribute_data, Vector<uint8_t> &skin_data) {
uint32_t dst_vertex_stride;
uint32_t dst_normal_tangent_stride;
uint32_t dst_attribute_stride;
uint32_t dst_skin_stride;
uint32_t dst_offsets[Mesh::ARRAY_MAX];
RenderingServer::get_singleton()->mesh_surface_make_offsets_from_format(p_new_format & (~RS::ARRAY_FORMAT_INDEX), p_elements, 0, dst_offsets, dst_vertex_stride, dst_attribute_stride, dst_skin_stride);
RenderingServer::get_singleton()->mesh_surface_make_offsets_from_format(p_new_format & (~RS::ARRAY_FORMAT_INDEX), p_elements, 0, dst_offsets, dst_vertex_stride, dst_normal_tangent_stride, dst_attribute_stride, dst_skin_stride);
vertex_data.resize(dst_vertex_stride * p_elements);
attribute_data.resize(dst_attribute_stride * p_elements);
@ -1031,7 +1032,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
uint32_t src_offset = 0;
for (uint32_t j = 0; j < OLD_ARRAY_INDEX; j++) {
if (!(p_old_format & (1 << j))) {
if (!(p_old_format & (1ULL << j))) {
continue;
}
switch (j) {
@ -1081,7 +1082,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
if ((p_old_format & OLD_ARRAY_COMPRESS_NORMAL) && (p_old_format & OLD_ARRAY_FORMAT_TANGENT) && (p_old_format & OLD_ARRAY_COMPRESS_TANGENT)) {
for (uint32_t i = 0; i < p_elements; i++) {
const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
int16_t *dst = (int16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
int16_t *dst = (int16_t *)&dst_vertex_ptr[i * dst_normal_tangent_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
dst[0] = (int16_t)CLAMP(src[0] / 127.0f * 32767, -32768, 32767);
dst[1] = (int16_t)CLAMP(src[1] / 127.0f * 32767, -32768, 32767);
@ -1090,7 +1091,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
} else {
for (uint32_t i = 0; i < p_elements; i++) {
const int16_t *src = (const int16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
int16_t *dst = (int16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
int16_t *dst = (int16_t *)&dst_vertex_ptr[i * dst_normal_tangent_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
dst[0] = src[0];
dst[1] = src[1];
@ -1104,7 +1105,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
const Vector3 original_normal(src[0], src[1], src[2]);
Vector2 res = original_normal.octahedron_encode();
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_normal_tangent_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535);
dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535);
}
@ -1115,7 +1116,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
const Vector3 original_normal(src[0], src[1], src[2]);
Vector2 res = original_normal.octahedron_encode();
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_normal_tangent_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535);
dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535);
}
@ -1129,7 +1130,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
if (p_old_format & OLD_ARRAY_COMPRESS_TANGENT) { // int8 SNORM -> uint16 UNORM
for (uint32_t i = 0; i < p_elements; i++) {
const int8_t *src = (const int8_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]];
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_normal_tangent_stride + dst_offsets[Mesh::ARRAY_TANGENT]];
dst[0] = (uint16_t)CLAMP((src[0] / 127.0f * .5f + .5f) * 65535, 0, 65535);
dst[1] = (uint16_t)CLAMP((src[1] / 127.0f * .5f + .5f) * 65535, 0, 65535);
@ -1138,7 +1139,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
} else { // int16 SNORM -> uint16 UNORM
for (uint32_t i = 0; i < p_elements; i++) {
const int16_t *src = (const int16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_TANGENT]];
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_normal_tangent_stride + dst_offsets[Mesh::ARRAY_TANGENT]];
dst[0] = (uint16_t)CLAMP((src[0] / 32767.0f * .5f + .5f) * 65535, 0, 65535);
dst[1] = (uint16_t)CLAMP((src[1] / 32767.0f * .5f + .5f) * 65535, 0, 65535);
@ -1152,7 +1153,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
const Vector3 original_tangent(src[0], src[1], src[2]);
Vector2 res = original_tangent.octahedron_tangent_encode(src[3]);
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_normal_tangent_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535);
dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535);
}
@ -1163,7 +1164,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
const Vector3 original_tangent(src[0], src[1], src[2]);
Vector2 res = original_tangent.octahedron_tangent_encode(src[3]);
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_vertex_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * dst_normal_tangent_stride + dst_offsets[Mesh::ARRAY_NORMAL]];
dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535);
dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535);
}
@ -1348,7 +1349,7 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
}
ERR_FAIL_COND_V(!d.has("format"), false);
uint32_t old_format = d["format"];
uint64_t old_format = d["format"];
uint32_t primitive = d["primitive"];
@ -1357,7 +1358,7 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) {
ERR_FAIL_COND_V(!d.has("vertex_count"), false);
int vertex_count = d["vertex_count"];
uint32_t new_format = ARRAY_FORMAT_VERTEX;
uint64_t new_format = ARRAY_FORMAT_VERTEX | ARRAY_FLAG_FORMAT_CURRENT_VERSION;
if (old_format & OLD_ARRAY_FORMAT_NORMAL) {
new_format |= ARRAY_FORMAT_NORMAL;
@ -1494,6 +1495,7 @@ Array ArrayMesh::_get_surfaces() const {
data["attribute_data"] = surface.attribute_data;
}
data["aabb"] = surface.aabb;
data["uv_scale"] = surface.uv_scale;
if (surface.index_count) {
data["index_data"] = surface.index_data;
data["index_count"] = surface.index_count;
@ -1573,6 +1575,10 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) {
}
surface.aabb = d["aabb"];
if (d.has("uv_scale")) {
surface.uv_scale = d["uv_scale"];
}
if (d.has("index_data")) {
ERR_FAIL_COND(!d.has("index_count"));
surface.index_data = d["index_data"];
@ -1619,6 +1625,13 @@ void ArrayMesh::_set_surfaces(const Array &p_surfaces) {
_2d = d["2d"];
}
#ifndef DISABLE_DEPRECATED
uint64_t surface_version = surface.format & (ARRAY_FLAG_FORMAT_VERSION_MASK << ARRAY_FLAG_FORMAT_VERSION_SHIFT);
if (surface_version != ARRAY_FLAG_FORMAT_CURRENT_VERSION) {
RS::_fix_surface_compatibility(surface);
}
#endif
surface_data.push_back(surface);
surface_materials.push_back(material);
surface_names.push_back(surf_name);
@ -1725,7 +1738,7 @@ void ArrayMesh::_recompute_aabb() {
}
// TODO: Need to add binding to add_surface using future MeshSurfaceData object.
void ArrayMesh::add_surface(BitField<ArrayFormat> p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data, const Vector<AABB> &p_bone_aabbs, const Vector<RS::SurfaceData::LOD> &p_lods) {
void ArrayMesh::add_surface(BitField<ArrayFormat> p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data, const Vector<AABB> &p_bone_aabbs, const Vector<RS::SurfaceData::LOD> &p_lods, const Vector4 p_uv_scale) {
ERR_FAIL_COND(surfaces.size() == RS::MAX_MESH_SURFACES);
_create_if_empty();
@ -1753,6 +1766,7 @@ void ArrayMesh::add_surface(BitField<ArrayFormat> p_format, PrimitiveType p_prim
sd.blend_shape_data = p_blend_shape_data;
sd.bone_aabbs = p_bone_aabbs;
sd.lods = p_lods;
sd.uv_scale = p_uv_scale;
RenderingServer::get_singleton()->mesh_add_surface(mesh, sd);
@ -1780,7 +1794,7 @@ void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &
print_line("primitive: " + itos(surface.primitive));
*/
add_surface(surface.format, PrimitiveType(surface.primitive), surface.vertex_data, surface.attribute_data, surface.skin_data, surface.vertex_count, surface.index_data, surface.index_count, surface.aabb, surface.blend_shape_data, surface.bone_aabbs, surface.lods);
add_surface(surface.format, PrimitiveType(surface.primitive), surface.vertex_data, surface.attribute_data, surface.skin_data, surface.vertex_count, surface.index_data, surface.index_count, surface.aabb, surface.blend_shape_data, surface.bone_aabbs, surface.lods, surface.uv_scale);
}
Array ArrayMesh::surface_get_arrays(int p_surface) const {
@ -2005,7 +2019,7 @@ struct ArrayMeshLightmapSurface {
Ref<Material> material;
LocalVector<SurfaceTool::Vertex> vertices;
Mesh::PrimitiveType primitive = Mesh::PrimitiveType::PRIMITIVE_MAX;
uint32_t format = 0;
uint64_t format = 0;
};
Error ArrayMesh::lightmap_unwrap(const Transform3D &p_base_transform, float p_texel_size) {

View file

@ -119,7 +119,7 @@ public:
ARRAY_CUSTOM_MAX
};
enum ArrayFormat {
enum ArrayFormat : uint64_t {
ARRAY_FORMAT_VERTEX = RS::ARRAY_FORMAT_VERTEX,
ARRAY_FORMAT_NORMAL = RS::ARRAY_FORMAT_NORMAL,
ARRAY_FORMAT_TANGENT = RS::ARRAY_FORMAT_TANGENT,
@ -151,6 +151,14 @@ public:
ARRAY_FLAG_USE_8_BONE_WEIGHTS = RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS,
ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY = RS::ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY,
ARRAY_FLAG_COMPRESS_ATTRIBUTES = RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES,
ARRAY_FLAG_FORMAT_VERSION_BASE = RS::ARRAY_FLAG_FORMAT_VERSION_BASE,
ARRAY_FLAG_FORMAT_VERSION_SHIFT = RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT,
ARRAY_FLAG_FORMAT_VERSION_1 = RS::ARRAY_FLAG_FORMAT_VERSION_1,
ARRAY_FLAG_FORMAT_VERSION_2 = (uint64_t)RS::ARRAY_FLAG_FORMAT_VERSION_2,
ARRAY_FLAG_FORMAT_CURRENT_VERSION = (uint64_t)RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION,
ARRAY_FLAG_FORMAT_VERSION_MASK = RS::ARRAY_FLAG_FORMAT_VERSION_MASK,
};
virtual int get_surface_count() const;
@ -328,7 +336,7 @@ protected:
public:
void add_surface_from_arrays(PrimitiveType p_primitive, const Array &p_arrays, const TypedArray<Array> &p_blend_shapes = TypedArray<Array>(), const Dictionary &p_lods = Dictionary(), BitField<ArrayFormat> p_flags = 0);
void add_surface(BitField<ArrayFormat> p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data = Vector<uint8_t>(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>(), const Vector<RS::SurfaceData::LOD> &p_lods = Vector<RS::SurfaceData::LOD>());
void add_surface(BitField<ArrayFormat> p_format, PrimitiveType p_primitive, const Vector<uint8_t> &p_array, const Vector<uint8_t> &p_attribute_array, const Vector<uint8_t> &p_skin_array, int p_vertex_count, const Vector<uint8_t> &p_index_array, int p_index_count, const AABB &p_aabb, const Vector<uint8_t> &p_blend_shape_data = Vector<uint8_t>(), const Vector<AABB> &p_bone_aabbs = Vector<AABB>(), const Vector<RS::SurfaceData::LOD> &p_lods = Vector<RS::SurfaceData::LOD>(), const Vector4 p_uv_scale = Vector4());
Array surface_get_arrays(int p_surface) const override;
TypedArray<Array> surface_get_blend_shape_arrays(int p_surface) const override;

View file

@ -0,0 +1,41 @@
/**************************************************************************/
/* mesh_data_tool.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DISABLE_DEPRECATED
Error MeshDataTool::commit_to_surface_bind_compat_81138(const Ref<ArrayMesh> &p_mesh) {
return commit_to_surface(p_mesh, 0);
}
void MeshDataTool::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("commit_to_surface", "mesh"), &MeshDataTool::commit_to_surface_bind_compat_81138);
}
#endif

View file

@ -29,6 +29,7 @@
/**************************************************************************/
#include "mesh_data_tool.h"
#include "mesh_data_tool.compat.inc"
void MeshDataTool::clear() {
vertices.clear();
@ -190,7 +191,7 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf
return OK;
}
Error MeshDataTool::commit_to_surface(const Ref<ArrayMesh> &p_mesh) {
Error MeshDataTool::commit_to_surface(const Ref<ArrayMesh> &p_mesh, uint64_t p_compression_flags) {
ERR_FAIL_COND_V(p_mesh.is_null(), ERR_INVALID_PARAMETER);
Array arr;
arr.resize(Mesh::ARRAY_MAX);
@ -327,13 +328,13 @@ Error MeshDataTool::commit_to_surface(const Ref<ArrayMesh> &p_mesh) {
Ref<ArrayMesh> ncmesh = p_mesh;
int sc = ncmesh->get_surface_count();
ncmesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr);
ncmesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr, TypedArray<Array>(), Dictionary(), p_compression_flags);
ncmesh->surface_set_material(sc, material);
return OK;
}
int MeshDataTool::get_format() const {
uint64_t MeshDataTool::get_format() const {
return format;
}
@ -521,7 +522,7 @@ void MeshDataTool::set_material(const Ref<Material> &p_material) {
void MeshDataTool::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &MeshDataTool::clear);
ClassDB::bind_method(D_METHOD("create_from_surface", "mesh", "surface"), &MeshDataTool::create_from_surface);
ClassDB::bind_method(D_METHOD("commit_to_surface", "mesh"), &MeshDataTool::commit_to_surface);
ClassDB::bind_method(D_METHOD("commit_to_surface", "mesh", "compression_flags"), &MeshDataTool::commit_to_surface, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_format"), &MeshDataTool::get_format);

View file

@ -36,7 +36,7 @@
class MeshDataTool : public RefCounted {
GDCLASS(MeshDataTool, RefCounted);
int format = 0;
uint64_t format = 0;
struct Vertex {
Vector3 vertex;
Color color;
@ -74,12 +74,17 @@ class MeshDataTool : public RefCounted {
protected:
static void _bind_methods();
#ifndef DISABLE_DEPRECATED
Error commit_to_surface_bind_compat_81138(const Ref<ArrayMesh> &p_mesh);
static void _bind_compatibility_methods();
#endif
public:
void clear();
Error create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surface);
Error commit_to_surface(const Ref<ArrayMesh> &p_mesh);
Error commit_to_surface(const Ref<ArrayMesh> &p_mesh, uint64_t p_compression_flags = 0);
int get_format() const;
uint64_t get_format() const;
int get_vertex_count() const;
int get_edge_count() const;

View file

@ -422,7 +422,7 @@ Array SurfaceTool::commit_to_arrays() {
a.resize(Mesh::ARRAY_MAX);
for (int i = 0; i < Mesh::ARRAY_MAX; i++) {
if (!(format & (1 << i))) {
if (!(format & (1ULL << i))) {
continue; //not in format
}
@ -711,7 +711,7 @@ Array SurfaceTool::commit_to_arrays() {
return a;
}
Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint32_t p_compress_flags) {
Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint64_t p_compress_flags) {
Ref<ArrayMesh> mesh;
if (p_existing.is_valid()) {
mesh = p_existing;
@ -787,7 +787,7 @@ void SurfaceTool::deindex() {
index_array.clear();
}
void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat) {
void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint64_t &lformat) {
ERR_FAIL_NULL_MSG(p_existing, "First argument in SurfaceTool::_create_list() must be a valid object of type Mesh");
Array arr = p_existing->surface_get_arrays(p_surface);
@ -798,7 +798,7 @@ void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, Local
const uint32_t SurfaceTool::custom_mask[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0, Mesh::ARRAY_FORMAT_CUSTOM1, Mesh::ARRAY_FORMAT_CUSTOM2, Mesh::ARRAY_FORMAT_CUSTOM3 };
const uint32_t SurfaceTool::custom_shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT };
void SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays, LocalVector<SurfaceTool::Vertex> &ret, uint32_t *r_format) {
void SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays, LocalVector<SurfaceTool::Vertex> &ret, uint64_t *r_format) {
ret.clear();
Vector<Vector3> varr = p_arrays[RS::ARRAY_VERTEX];
@ -927,7 +927,7 @@ void SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays
}
}
void SurfaceTool::_create_list_from_arrays(Array arr, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat) {
void SurfaceTool::_create_list_from_arrays(Array arr, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint64_t &lformat) {
create_vertex_array_from_triangle_arrays(arr, *r_vertex, &lformat);
ERR_FAIL_COND(r_vertex->size() == 0);
@ -1020,7 +1020,7 @@ void SurfaceTool::append_from(const Ref<Mesh> &p_existing, int p_surface, const
format = 0;
}
uint32_t nformat = 0;
uint64_t nformat = 0;
LocalVector<Vertex> nvertices;
LocalVector<int> nindices;
_create_list(p_existing, p_surface, &nvertices, &nindices, nformat);

View file

@ -136,7 +136,7 @@ private:
bool begun = false;
bool first = false;
Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_LINES;
uint32_t format = 0;
uint64_t format = 0;
Ref<Material> material;
//arrays
LocalVector<Vertex> vertex_array;
@ -158,8 +158,8 @@ private:
CustomFormat last_custom_format[RS::ARRAY_CUSTOM_COUNT];
void _create_list_from_arrays(Array arr, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat);
void _create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint32_t &lformat);
void _create_list_from_arrays(Array arr, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint64_t &lformat);
void _create_list(const Ref<Mesh> &p_existing, int p_surface, LocalVector<Vertex> *r_vertex, LocalVector<int> *r_index, uint64_t &lformat);
//mikktspace callbacks
static int mikktGetNumFaces(const SMikkTSpaceContext *pContext);
@ -219,12 +219,12 @@ public:
LocalVector<Vertex> &get_vertex_array() { return vertex_array; }
void create_from_triangle_arrays(const Array &p_arrays);
static void create_vertex_array_from_triangle_arrays(const Array &p_arrays, LocalVector<Vertex> &ret, uint32_t *r_format = nullptr);
static void create_vertex_array_from_triangle_arrays(const Array &p_arrays, LocalVector<Vertex> &ret, uint64_t *r_format = nullptr);
Array commit_to_arrays();
void create_from(const Ref<Mesh> &p_existing, int p_surface);
void create_from_blend_shape(const Ref<Mesh> &p_existing, int p_surface, const String &p_blend_shape_name);
void append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform3D &p_xform);
Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint32_t p_compress_flags = 0);
Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint64_t p_compress_flags = 0);
SurfaceTool();
};

View file

@ -728,7 +728,7 @@ void RenderForwardClustered::_fill_instance_data(RenderListType p_render_list, i
RendererRD::MaterialStorage::store_transform(inst->prev_transform, instance_data.prev_transform);
#ifdef REAL_T_IS_DOUBLE
// Split the origin into two components, the float approximation and the missing precision
// Split the origin into two components, the float approximation and the missing precision.
// In the shader we will combine these back together to restore the lost precision.
RendererRD::MaterialStorage::split_double(inst->transform.origin.x, &instance_data.transform[12], &instance_data.transform[3]);
RendererRD::MaterialStorage::split_double(inst->transform.origin.y, &instance_data.transform[13], &instance_data.transform[7]);
@ -748,6 +748,28 @@ void RenderForwardClustered::_fill_instance_data(RenderListType p_render_list, i
instance_data.lightmap_uv_scale[2] = inst->lightmap_uv_scale.size.x;
instance_data.lightmap_uv_scale[3] = inst->lightmap_uv_scale.size.y;
AABB surface_aabb = AABB(Vector3(0.0, 0.0, 0.0), Vector3(1.0, 1.0, 1.0));
uint64_t format = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_format(surface->surface);
Vector4 uv_scale = Vector4(0.0, 0.0, 0.0, 0.0);
if (format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
surface_aabb = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_aabb(surface->surface);
uv_scale = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_uv_scale(surface->surface);
}
instance_data.compressed_aabb_position[0] = surface_aabb.position.x;
instance_data.compressed_aabb_position[1] = surface_aabb.position.y;
instance_data.compressed_aabb_position[2] = surface_aabb.position.z;
instance_data.compressed_aabb_size[0] = surface_aabb.size.x;
instance_data.compressed_aabb_size[1] = surface_aabb.size.y;
instance_data.compressed_aabb_size[2] = surface_aabb.size.z;
instance_data.uv_scale[0] = uv_scale.x;
instance_data.uv_scale[1] = uv_scale.y;
instance_data.uv_scale[2] = uv_scale.z;
instance_data.uv_scale[3] = uv_scale.w;
bool cant_repeat = instance_data.flags & INSTANCE_DATA_FLAG_MULTIMESH || inst->mesh_instance.is_valid();
if (prev_surface != nullptr && !cant_repeat && prev_surface->sort.sort_key1 == surface->sort.sort_key1 && prev_surface->sort.sort_key2 == surface->sort.sort_key2 && inst->mirror == prev_surface->owner->mirror && repeats < RenderElementInfo::MAX_REPEATS) {
@ -3942,7 +3964,7 @@ RenderGeometryInstance *RenderForwardClustered::geometry_instance_create(RID p_b
return ginstance;
}
void RenderForwardClustered::GeometryInstanceForwardClustered::set_transform(const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabbb) {
void RenderForwardClustered::GeometryInstanceForwardClustered::set_transform(const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabb) {
uint64_t frame = RSG::rasterizer->get_frame_number();
if (frame != prev_transform_change_frame) {
prev_transform = transform;
@ -3950,7 +3972,7 @@ void RenderForwardClustered::GeometryInstanceForwardClustered::set_transform(con
prev_transform_dirty = true;
}
RenderGeometryInstanceBase::set_transform(p_transform, p_aabb, p_transformed_aabbb);
RenderGeometryInstanceBase::set_transform(p_transform, p_aabb, p_transformed_aabb);
}
void RenderForwardClustered::GeometryInstanceForwardClustered::set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) {

View file

@ -298,6 +298,9 @@ class RenderForwardClustered : public RendererSceneRenderRD {
uint32_t gi_offset; //GI information when using lightmapping (VCT or lightmap index)
uint32_t layer_mask;
float lightmap_uv_scale[4];
float compressed_aabb_position[4];
float compressed_aabb_size[4];
float uv_scale[4];
};
UBO ubo;
@ -479,7 +482,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
virtual void _mark_dirty() override;
virtual void set_transform(const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabbb) override;
virtual void set_transform(const Transform3D &p_transform, const AABB &p_aabb, const AABB &p_transformed_aabb) override;
virtual void set_use_lightmap(RID p_lightmap_instance, const Rect2 &p_lightmap_uv_scale, int p_lightmap_slice_index) override;
virtual void set_lightmap_capture(const Color *p_sh9) override;

View file

@ -142,7 +142,7 @@ public:
bool valid = false;
RID version;
uint32_t vertex_input_mask = 0;
uint64_t vertex_input_mask = 0;
PipelineCacheRD pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][PIPELINE_VERSION_MAX];
PipelineCacheRD color_pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][PIPELINE_COLOR_PASS_FLAG_COUNT];

View file

@ -68,53 +68,40 @@ void RenderForwardMobile::ForwardIDStorageMobile::map_forward_id(RendererRD::For
forward_id_allocators[p_type].map[p_id] = p_index;
}
void RenderForwardMobile::ForwardIDStorageMobile::fill_push_constant_instance_indices(GeometryInstanceForwardMobile::PushConstant *p_push_constant, uint32_t &spec_constants, const GeometryInstanceForwardMobile *p_instance) {
// first zero out our indices
void RenderForwardMobile::fill_push_constant_instance_indices(SceneState::InstanceData *p_instance_data, const GeometryInstanceForwardMobile *p_instance) {
// First zero out our indices.
p_push_constant->omni_lights[0] = 0xFFFFFFFF;
p_push_constant->omni_lights[1] = 0xFFFFFFFF;
p_instance_data->omni_lights[0] = 0xFFFFFFFF;
p_instance_data->omni_lights[1] = 0xFFFFFFFF;
p_push_constant->spot_lights[0] = 0xFFFFFFFF;
p_push_constant->spot_lights[1] = 0xFFFFFFFF;
p_instance_data->spot_lights[0] = 0xFFFFFFFF;
p_instance_data->spot_lights[1] = 0xFFFFFFFF;
p_push_constant->decals[0] = 0xFFFFFFFF;
p_push_constant->decals[1] = 0xFFFFFFFF;
p_instance_data->decals[0] = 0xFFFFFFFF;
p_instance_data->decals[1] = 0xFFFFFFFF;
p_push_constant->reflection_probes[0] = 0xFFFFFFFF;
p_push_constant->reflection_probes[1] = 0xFFFFFFFF;
if (p_instance->omni_light_count == 0) {
spec_constants |= 1 << SPEC_CONSTANT_DISABLE_OMNI_LIGHTS;
}
if (p_instance->spot_light_count == 0) {
spec_constants |= 1 << SPEC_CONSTANT_DISABLE_SPOT_LIGHTS;
}
if (p_instance->reflection_probe_count == 0) {
spec_constants |= 1 << SPEC_CONSTANT_DISABLE_REFLECTION_PROBES;
}
if (p_instance->decals_count == 0) {
spec_constants |= 1 << SPEC_CONSTANT_DISABLE_DECALS;
}
p_instance_data->reflection_probes[0] = 0xFFFFFFFF;
p_instance_data->reflection_probes[1] = 0xFFFFFFFF;
for (uint32_t i = 0; i < MAX_RDL_CULL; i++) {
uint32_t ofs = i < 4 ? 0 : 1;
uint32_t shift = (i & 0x3) << 3;
uint32_t mask = ~(0xFF << shift);
if (i < p_instance->omni_light_count) {
p_push_constant->omni_lights[ofs] &= mask;
p_push_constant->omni_lights[ofs] |= uint32_t(forward_id_allocators[RendererRD::FORWARD_ID_TYPE_OMNI_LIGHT].map[p_instance->omni_lights[i]]) << shift;
p_instance_data->omni_lights[ofs] &= mask;
p_instance_data->omni_lights[ofs] |= uint32_t(forward_id_storage_mobile->forward_id_allocators[RendererRD::FORWARD_ID_TYPE_OMNI_LIGHT].map[p_instance->omni_lights[i]]) << shift;
}
if (i < p_instance->spot_light_count) {
p_push_constant->spot_lights[ofs] &= mask;
p_push_constant->spot_lights[ofs] |= uint32_t(forward_id_allocators[RendererRD::FORWARD_ID_TYPE_SPOT_LIGHT].map[p_instance->spot_lights[i]]) << shift;
p_instance_data->spot_lights[ofs] &= mask;
p_instance_data->spot_lights[ofs] |= uint32_t(forward_id_storage_mobile->forward_id_allocators[RendererRD::FORWARD_ID_TYPE_SPOT_LIGHT].map[p_instance->spot_lights[i]]) << shift;
}
if (i < p_instance->decals_count) {
p_push_constant->decals[ofs] &= mask;
p_push_constant->decals[ofs] |= uint32_t(forward_id_allocators[RendererRD::FORWARD_ID_TYPE_DECAL].map[p_instance->decals[i]]) << shift;
p_instance_data->decals[ofs] &= mask;
p_instance_data->decals[ofs] |= uint32_t(forward_id_storage_mobile->forward_id_allocators[RendererRD::FORWARD_ID_TYPE_DECAL].map[p_instance->decals[i]]) << shift;
}
if (i < p_instance->reflection_probe_count) {
p_push_constant->reflection_probes[ofs] &= mask;
p_push_constant->reflection_probes[ofs] |= uint32_t(forward_id_allocators[RendererRD::FORWARD_ID_TYPE_REFLECTION_PROBE].map[p_instance->reflection_probes[i]]) << shift;
p_instance_data->reflection_probes[ofs] &= mask;
p_instance_data->reflection_probes[ofs] |= uint32_t(forward_id_storage_mobile->forward_id_allocators[RendererRD::FORWARD_ID_TYPE_REFLECTION_PROBE].map[p_instance->reflection_probes[i]]) << shift;
}
}
}
@ -366,6 +353,18 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_
uniforms.push_back(u);
}
{
RD::Uniform u;
u.binding = 1;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
RID instance_buffer = scene_state.instance_buffer[p_render_list];
if (instance_buffer == RID()) {
instance_buffer = scene_shader.default_vec4_xform_buffer; // Any buffer will do since its not used.
}
u.append_id(instance_buffer);
uniforms.push_back(u);
}
{
RID radiance_texture;
if (p_radiance_texture.is_valid()) {
@ -461,15 +460,6 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_
uniforms.push_back(u);
}
{
RD::Uniform u;
u.binding = 8;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
RID cb = p_cluster_buffer.is_valid() ? p_cluster_buffer : default_vec4_xform_buffer;
u.append_id(cb);
uniforms.push_back(u);
}
*/
{
@ -682,8 +672,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
_fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR);
render_list[RENDER_LIST_OPAQUE].sort_by_key();
render_list[RENDER_LIST_ALPHA].sort_by_reverse_depth_and_priority();
_fill_element_info(RENDER_LIST_OPAQUE);
_fill_element_info(RENDER_LIST_ALPHA);
_fill_instance_data(RENDER_LIST_OPAQUE);
_fill_instance_data(RENDER_LIST_ALPHA);
if (p_render_data->render_info) {
p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE][RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME] = p_render_data->instances->size();
@ -1290,7 +1280,7 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr
_fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode, true);
uint32_t render_list_size = render_list[RENDER_LIST_SECONDARY].elements.size() - render_list_from;
render_list[RENDER_LIST_SECONDARY].sort_by_key_range(render_list_from, render_list_size);
_fill_element_info(RENDER_LIST_SECONDARY, render_list_from, render_list_size);
_fill_instance_data(RENDER_LIST_SECONDARY, render_list_from, render_list_size);
{
//regular forward for now
@ -1373,7 +1363,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c
PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL;
_fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode);
render_list[RENDER_LIST_SECONDARY].sort_by_key();
_fill_element_info(RENDER_LIST_SECONDARY);
_fill_instance_data(RENDER_LIST_SECONDARY);
RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID());
@ -1418,7 +1408,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *>
PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL;
_fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode);
render_list[RENDER_LIST_SECONDARY].sort_by_key();
_fill_element_info(RENDER_LIST_SECONDARY);
_fill_instance_data(RENDER_LIST_SECONDARY);
RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID());
@ -1499,7 +1489,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const
_fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode);
render_list[RENDER_LIST_SECONDARY].sort_by_key();
_fill_element_info(RENDER_LIST_SECONDARY);
_fill_instance_data(RENDER_LIST_SECONDARY);
RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_SECONDARY, nullptr, RID());
@ -1691,6 +1681,91 @@ RID RenderForwardMobile::_render_buffers_get_velocity_texture(Ref<RenderSceneBuf
return RID();
}
void RenderForwardMobile::_update_instance_data_buffer(RenderListType p_render_list) {
if (scene_state.instance_data[p_render_list].size() > 0) {
if (scene_state.instance_buffer[p_render_list] == RID() || scene_state.instance_buffer_size[p_render_list] < scene_state.instance_data[p_render_list].size()) {
if (scene_state.instance_buffer[p_render_list] != RID()) {
RD::get_singleton()->free(scene_state.instance_buffer[p_render_list]);
}
uint32_t new_size = nearest_power_of_2_templated(MAX(uint64_t(INSTANCE_DATA_BUFFER_MIN_SIZE), scene_state.instance_data[p_render_list].size()));
scene_state.instance_buffer[p_render_list] = RD::get_singleton()->storage_buffer_create(new_size * sizeof(SceneState::InstanceData));
scene_state.instance_buffer_size[p_render_list] = new_size;
}
RD::get_singleton()->buffer_update(scene_state.instance_buffer[p_render_list], 0, sizeof(SceneState::InstanceData) * scene_state.instance_data[p_render_list].size(), scene_state.instance_data[p_render_list].ptr(), RD::BARRIER_MASK_RASTER);
}
}
void RenderForwardMobile::_fill_instance_data(RenderListType p_render_list, uint32_t p_offset, int32_t p_max_elements, bool p_update_buffer) {
RenderList *rl = &render_list[p_render_list];
uint32_t element_total = p_max_elements >= 0 ? uint32_t(p_max_elements) : rl->elements.size();
scene_state.instance_data[p_render_list].resize(p_offset + element_total);
rl->element_info.resize(p_offset + element_total);
for (uint32_t i = 0; i < element_total; i++) {
GeometryInstanceSurfaceDataCache *surface = rl->elements[i + p_offset];
GeometryInstanceForwardMobile *inst = surface->owner;
SceneState::InstanceData &instance_data = scene_state.instance_data[p_render_list][i + p_offset];
if (inst->store_transform_cache) {
RendererRD::MaterialStorage::store_transform(inst->transform, instance_data.transform);
#ifdef REAL_T_IS_DOUBLE
// Split the origin into two components, the float approximation and the missing precision.
// In the shader we will combine these back together to restore the lost precision.
RendererRD::MaterialStorage::split_double(inst->transform.origin.x, &instance_data.transform[12], &instance_data.transform[3]);
RendererRD::MaterialStorage::split_double(inst->transform.origin.y, &instance_data.transform[13], &instance_data.transform[7]);
RendererRD::MaterialStorage::split_double(inst->transform.origin.z, &instance_data.transform[14], &instance_data.transform[11]);
#endif
} else {
RendererRD::MaterialStorage::store_transform(Transform3D(), instance_data.transform);
}
instance_data.flags = inst->flags_cache;
instance_data.gi_offset = inst->gi_offset_cache;
instance_data.layer_mask = inst->layer_mask;
instance_data.instance_uniforms_ofs = uint32_t(inst->shader_uniforms_offset);
instance_data.lightmap_uv_scale[0] = inst->lightmap_uv_scale.position.x;
instance_data.lightmap_uv_scale[1] = inst->lightmap_uv_scale.position.y;
instance_data.lightmap_uv_scale[2] = inst->lightmap_uv_scale.size.x;
instance_data.lightmap_uv_scale[3] = inst->lightmap_uv_scale.size.y;
AABB surface_aabb = AABB(Vector3(0.0, 0.0, 0.0), Vector3(1.0, 1.0, 1.0));
uint64_t format = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_format(surface->surface);
Vector4 uv_scale = Vector4(0.0, 0.0, 0.0, 0.0);
if (format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
surface_aabb = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_aabb(surface->surface);
uv_scale = RendererRD::MeshStorage::get_singleton()->mesh_surface_get_uv_scale(surface->surface);
}
fill_push_constant_instance_indices(&instance_data, inst);
instance_data.compressed_aabb_position[0] = surface_aabb.position.x;
instance_data.compressed_aabb_position[1] = surface_aabb.position.y;
instance_data.compressed_aabb_position[2] = surface_aabb.position.z;
instance_data.compressed_aabb_size[0] = surface_aabb.size.x;
instance_data.compressed_aabb_size[1] = surface_aabb.size.y;
instance_data.compressed_aabb_size[2] = surface_aabb.size.z;
instance_data.uv_scale[0] = uv_scale.x;
instance_data.uv_scale[1] = uv_scale.y;
instance_data.uv_scale[2] = uv_scale.z;
instance_data.uv_scale[3] = uv_scale.w;
RenderElementInfo &element_info = rl->element_info[p_offset + i];
element_info.lod_index = surface->lod_index;
element_info.uses_lightmap = surface->sort.uses_lightmap;
}
if (p_update_buffer) {
_update_instance_data_buffer(p_render_list);
}
}
_FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primitive, uint32_t p_indices) {
static const uint32_t divisor[RS::PRIMITIVE_MAX] = { 1, 2, 1, 3, 1 };
static const uint32_t subtractor[RS::PRIMITIVE_MAX] = { 0, 0, 1, 0, 1 };
@ -1911,21 +1986,6 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data,
p_render_data->scene_data->update_ubo(scene_state.uniform_buffers[p_index], get_debug_draw_mode(), env, reflection_probe_instance, p_render_data->camera_attributes, p_flip_y, p_pancake_shadows, p_screen_size, p_default_bg_color, _render_buffers_get_luminance_multiplier(), p_opaque_render_buffers, false);
}
void RenderForwardMobile::_fill_element_info(RenderListType p_render_list, uint32_t p_offset, int32_t p_max_elements) {
RenderList *rl = &render_list[p_render_list];
uint32_t element_total = p_max_elements >= 0 ? uint32_t(p_max_elements) : rl->elements.size();
rl->element_info.resize(p_offset + element_total);
for (uint32_t i = 0; i < element_total; i++) {
GeometryInstanceSurfaceDataCache *surface = rl->elements[i + p_offset];
RenderElementInfo &element_info = rl->element_info[p_offset + i];
element_info.lod_index = surface->lod_index;
element_info.uses_lightmap = surface->sort.uses_lightmap;
}
}
/// RENDERING ///
void RenderForwardMobile::_render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element) {
@ -2011,39 +2071,17 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
uint32_t base_spec_constants = p_params->spec_constant_base_flags;
// GeometryInstanceForwardMobile::PushConstant push_constant = inst->push_constant;
GeometryInstanceForwardMobile::PushConstant push_constant;
SceneState::PushConstant push_constant;
push_constant.base_index = i + p_params->element_offset;
if (inst->store_transform_cache) {
RendererRD::MaterialStorage::store_transform(inst->transform, push_constant.transform);
#ifdef REAL_T_IS_DOUBLE
// Split the origin into two components, the float approximation and the missing precision
// In the shader we will combine these back together to restore the lost precision.
RendererRD::MaterialStorage::split_double(inst->transform.origin.x, &push_constant.transform[12], &push_constant.transform[3]);
RendererRD::MaterialStorage::split_double(inst->transform.origin.y, &push_constant.transform[13], &push_constant.transform[7]);
RendererRD::MaterialStorage::split_double(inst->transform.origin.z, &push_constant.transform[14], &push_constant.transform[11]);
#endif
if constexpr (p_pass_mode == PASS_MODE_DEPTH_MATERIAL) {
push_constant.uv_offset[0] = p_params->uv_offset.x;
push_constant.uv_offset[1] = p_params->uv_offset.y;
} else {
RendererRD::MaterialStorage::store_transform(Transform3D(), push_constant.transform);
push_constant.uv_offset[0] = 0.0;
push_constant.uv_offset[1] = 0.0;
}
push_constant.flags = inst->flags_cache;
push_constant.gi_offset = inst->gi_offset_cache;
push_constant.layer_mask = inst->layer_mask;
push_constant.instance_uniforms_ofs = uint32_t(inst->shader_uniforms_offset);
if (p_params->pass_mode == PASS_MODE_DEPTH_MATERIAL) {
// abuse lightmap_uv_scale[0] here, should not be needed here
push_constant.lightmap_uv_scale[0] = p_params->uv_offset.x;
push_constant.lightmap_uv_scale[1] = p_params->uv_offset.y;
} else {
push_constant.lightmap_uv_scale[0] = inst->lightmap_uv_scale.position.x;
push_constant.lightmap_uv_scale[1] = inst->lightmap_uv_scale.position.y;
push_constant.lightmap_uv_scale[2] = inst->lightmap_uv_scale.size.x;
push_constant.lightmap_uv_scale[3] = inst->lightmap_uv_scale.size.y;
};
RID material_uniform_set;
SceneShaderForwardMobile::ShaderData *shader;
void *mesh_surface;
@ -2060,7 +2098,19 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
if (inst->use_soft_shadow) {
base_spec_constants |= 1 << SPEC_CONSTANT_USING_SOFT_SHADOWS;
}
forward_id_storage_mobile->fill_push_constant_instance_indices(&push_constant, base_spec_constants, inst);
if (inst->omni_light_count == 0) {
base_spec_constants |= 1 << SPEC_CONSTANT_DISABLE_OMNI_LIGHTS;
}
if (inst->spot_light_count == 0) {
base_spec_constants |= 1 << SPEC_CONSTANT_DISABLE_SPOT_LIGHTS;
}
if (inst->reflection_probe_count == 0) {
base_spec_constants |= 1 << SPEC_CONSTANT_DISABLE_REFLECTION_PROBES;
}
if (inst->decals_count == 0) {
base_spec_constants |= 1 << SPEC_CONSTANT_DISABLE_DECALS;
}
#ifdef DEBUG_ENABLED
if (unlikely(get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_LIGHTING)) {
@ -2184,7 +2234,7 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
prev_material_uniform_set = material_uniform_set;
}
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(GeometryInstanceForwardMobile::PushConstant));
RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(SceneState::PushConstant));
uint32_t instance_count = surf->owner->instance_count > 1 ? surf->owner->instance_count : 1;
if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_USES_PARTICLE_TRAILS) {

View file

@ -202,9 +202,9 @@ private:
void _update_render_base_uniform_set(const RendererRD::MaterialStorage::Samplers &p_samplers);
void _update_instance_data_buffer(RenderListType p_render_list);
void _fill_instance_data(RenderListType p_render_list, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true);
void _fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, bool p_append = false);
void _fill_element_info(RenderListType p_render_list, uint32_t p_offset = 0, int32_t p_max_elements = -1);
// void _update_instance_data_buffer(RenderListType p_render_list);
void _setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_opaque_render_buffers = false, bool p_pancake_shadows = false, int p_index = 0);
void _setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform);
@ -229,6 +229,32 @@ private:
struct SceneState {
LocalVector<RID> uniform_buffers;
struct PushConstant {
float uv_offset[2];
uint32_t base_index;
uint32_t pad;
};
struct InstanceData {
float transform[16];
uint32_t flags;
uint32_t instance_uniforms_ofs; // Base offset in global buffer for instance variables.
uint32_t gi_offset; // GI information when using lightmapping (VCT or lightmap index).
uint32_t layer_mask = 1;
float lightmap_uv_scale[4]; // Doubles as uv_offset when needed.
uint32_t reflection_probes[2]; // Packed reflection probes.
uint32_t omni_lights[2]; // Packed omni lights.
uint32_t spot_lights[2]; // Packed spot lights.
uint32_t decals[2]; // Packed spot lights.
float compressed_aabb_position[4];
float compressed_aabb_size[4];
float uv_scale[4];
};
RID instance_buffer[RENDER_LIST_MAX];
uint32_t instance_buffer_size[RENDER_LIST_MAX] = { 0, 0, 0 };
LocalVector<InstanceData> instance_data[RENDER_LIST_MAX];
// !BAS! We need to change lightmaps, we're not going to do this with a buffer but pushing the used lightmap in
LightmapData lightmaps[MAX_LIGHTMAPS];
RID lightmap_ids[MAX_LIGHTMAPS];
@ -447,27 +473,11 @@ protected:
class GeometryInstanceForwardMobile : public RenderGeometryInstanceBase {
public:
// this structure maps to our push constant in our shader and is populated right before our draw call
struct PushConstant {
float transform[16];
uint32_t flags;
uint32_t instance_uniforms_ofs; //base offset in global buffer for instance variables
uint32_t gi_offset; //GI information when using lightmapping (VCT or lightmap index)
uint32_t layer_mask = 1;
float lightmap_uv_scale[4]; // doubles as uv_offset when needed
uint32_t reflection_probes[2]; // packed reflection probes
uint32_t omni_lights[2]; // packed omni lights
uint32_t spot_lights[2]; // packed spot lights
uint32_t decals[2]; // packed spot lights
};
// PushConstant push_constant; // we populate this from our instance data
//used during rendering
RID transforms_uniform_set;
bool use_projector = false;
bool use_soft_shadow = false;
bool store_transform_cache = true; // if true we copy our transform into our PushConstant, if false we use our transforms UBO and clear our PushConstants transform
bool store_transform_cache = true; // If true we copy our transform into our per-draw buffer, if false we use our transforms UBO and clear our per-draw transform.
uint32_t instance_count = 0;
uint32_t trail_steps = 1;
@ -534,12 +544,12 @@ protected:
virtual void free_forward_id(RendererRD::ForwardIDType p_type, RendererRD::ForwardID p_id) override;
virtual void map_forward_id(RendererRD::ForwardIDType p_type, RendererRD::ForwardID p_id, uint32_t p_index) override;
virtual bool uses_forward_ids() const override { return true; }
void fill_push_constant_instance_indices(GeometryInstanceForwardMobile::PushConstant *p_push_constant, uint32_t &spec_constants, const GeometryInstanceForwardMobile *p_instance);
};
ForwardIDStorageMobile *forward_id_storage_mobile = nullptr;
void fill_push_constant_instance_indices(SceneState::InstanceData *p_instance_data, const GeometryInstanceForwardMobile *p_instance);
virtual RendererRD::ForwardIDStorage *create_forward_id_storage() override {
forward_id_storage_mobile = memnew(ForwardIDStorageMobile);
return forward_id_storage_mobile;

View file

@ -620,7 +620,7 @@ void SceneShaderForwardMobile::init(const String p_defines) {
actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
actions.default_repeat = ShaderLanguage::REPEAT_ENABLE;
actions.global_buffer_array_variable = "global_shader_uniforms.data";
actions.instance_uniform_index_variable = "draw_call.instance_uniforms_ofs";
actions.instance_uniform_index_variable = "instances.data[instance_index].instance_uniforms_ofs";
actions.apply_luminance_multiplier = true; // apply luminance multiplier to screen texture
actions.check_multiview_samplers = RendererCompositorRD::get_singleton()->is_xr_enabled(); // Make sure we check sampling multiview textures.

View file

@ -97,7 +97,7 @@ public:
bool valid = false;
RID version;
uint32_t vertex_input_mask = 0;
uint64_t vertex_input_mask = 0;
PipelineCacheRD pipelines[CULL_VARIANT_MAX][RS::PRIMITIVE_MAX][SHADER_VERSION_MAX];
Vector<ShaderCompiler::GeneratedCode::Texture> texture_uniforms;

View file

@ -91,7 +91,7 @@ public:
return result;
}
_FORCE_INLINE_ uint32_t get_vertex_input_mask() {
_FORCE_INLINE_ uint64_t get_vertex_input_mask() {
if (input_mask == 0) {
ERR_FAIL_COND_V(shader.is_null(), 0);
input_mask = RD::get_singleton()->shader_get_vertex_input_attribute_mask(shader);

View file

@ -894,7 +894,7 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface);
ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX);
uint32_t input_mask = pipeline_variants->variants[light_mode][variant[primitive]].get_vertex_input_mask();
uint64_t input_mask = pipeline_variants->variants[light_mode][variant[primitive]].get_vertex_input_mask();
RID vertex_array;
RD::VertexFormatID vertex_format = RD::INVALID_FORMAT_ID;

View file

@ -10,21 +10,17 @@
/* INPUT ATTRIBS */
layout(location = 0) in vec3 vertex_attrib;
// Always contains vertex position in XYZ, can contain tangent angle in W.
layout(location = 0) in vec4 vertex_angle_attrib;
//only for pure render depth when normal is not used
#ifdef NORMAL_USED
layout(location = 1) in vec2 normal_attrib;
#if defined(NORMAL_USED) || defined(TANGENT_USED)
// 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))
#define TANGENT_USED
#endif
#ifdef TANGENT_USED
layout(location = 2) in vec2 tangent_attrib;
#endif
// Location 2 is unused.
#if defined(COLOR_USED)
layout(location = 3) in vec4 color_attrib;
@ -63,15 +59,12 @@ layout(location = 11) in vec4 weight_attrib;
#endif
#ifdef MOTION_VECTORS
layout(location = 12) in vec3 previous_vertex_attrib;
layout(location = 12) in vec4 previous_vertex_attrib;
#ifdef NORMAL_USED
layout(location = 13) in vec2 previous_normal_attrib;
#if defined(NORMAL_USED) || defined(TANGENT_USED)
layout(location = 13) in vec4 previous_normal_attrib;
#endif
#ifdef TANGENT_USED
layout(location = 14) in vec2 previous_tangent_attrib;
#endif
#endif // MOTION_VECTORS
vec3 oct_to_vec3(vec2 e) {
@ -81,6 +74,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);
}
/* Varyings */
layout(location = 0) out vec3 vertex_interp;
@ -179,10 +182,11 @@ vec3 double_add_vec3(vec3 base_a, vec3 prec_a, vec3 base_b, vec3 prec_b, out vec
void vertex_shader(vec3 vertex_input,
#ifdef NORMAL_USED
in vec2 normal_input,
in vec3 normal_input,
#endif
#ifdef TANGENT_USED
in vec2 tangent_input,
in vec3 tangent_input,
in vec3 binormal_input,
#endif
in uint instance_index, in bool is_multimesh, in uint multimesh_offset, in SceneData scene_data, in mat4 model_matrix, out vec4 screen_pos) {
vec4 instance_custom = vec4(0.0);
@ -314,14 +318,12 @@ void vertex_shader(vec3 vertex_input,
vec3 vertex = vertex_input;
#ifdef NORMAL_USED
vec3 normal = oct_to_vec3(normal_input * 2.0 - 1.0);
vec3 normal = normal_input;
#endif
#ifdef TANGENT_USED
vec2 signed_tangent_input = tangent_input * 2.0 - 1.0;
vec3 tangent = oct_to_vec3(vec2(signed_tangent_input.x, abs(signed_tangent_input.y) * 2.0 - 1.0));
float binormalf = sign(signed_tangent_input.y);
vec3 binormal = normalize(cross(normal, tangent) * binormalf);
vec3 tangent = tangent_input;
vec3 binormal = binormal_input;
#endif
#ifdef UV_USED
@ -332,6 +334,17 @@ void vertex_shader(vec3 vertex_input,
uv2_interp = uv2_attrib;
#endif
vec4 uv_scale = instances.data[instance_index].uv_scale;
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
}
#ifdef OVERRIDE_POSITION
vec4 position;
#endif
@ -484,6 +497,46 @@ void vertex_shader(vec3 vertex_input,
#endif
}
void _unpack_vertex_attributes(vec4 p_vertex_in, vec3 p_compressed_aabb_position, vec3 p_compressed_aabb_size,
#if defined(NORMAL_USED) || defined(TANGENT_USED)
vec4 p_normal_in,
#ifdef NORMAL_USED
out vec3 r_normal,
#endif
out vec3 r_tangent,
out vec3 r_binormal,
#endif
out vec3 r_vertex) {
r_vertex = p_vertex_in.xyz * p_compressed_aabb_size + p_compressed_aabb_position;
#ifdef NORMAL_USED
r_normal = oct_to_vec3(p_normal_in.xy * 2.0 - 1.0);
#endif
#if defined(NORMAL_USED) || defined(TANGENT_USED)
float binormal_sign;
// This works because the oct value (0, 1) maps onto (0, 0, -1) which encodes to (1, 1).
// Accordingly, if p_normal_in.z contains octahedral values, it won't equal (0, 1).
if (p_normal_in.z > 0.0 || p_normal_in.w < 1.0) {
// Uncompressed format.
vec2 signed_tangent_attrib = p_normal_in.zw * 2.0 - 1.0;
r_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);
r_binormal = normalize(cross(r_normal, r_tangent) * binormal_sign);
} else {
// Compressed format.
float angle = p_vertex_in.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 = r_normal;
axis_angle_to_tbn(axis, angle, r_tangent, r_binormal, r_normal);
r_binormal *= binormal_sign;
}
#endif
}
void main() {
uint instance_index = draw_call.instance_index;
@ -498,13 +551,38 @@ void main() {
#ifdef MOTION_VECTORS
// Previous vertex.
global_time = scene_data_block.prev_data.time;
vertex_shader(previous_vertex_attrib,
vec3 prev_vertex;
#ifdef NORMAL_USED
vec3 prev_normal;
#endif
#if defined(NORMAL_USED) || defined(TANGENT_USED)
vec3 prev_tangent;
vec3 prev_binormal;
#endif
_unpack_vertex_attributes(
previous_vertex_attrib,
instances.data[instance_index].compressed_aabb_position_pad.xyz,
instances.data[instance_index].compressed_aabb_size_pad.xyz,
#if defined(NORMAL_USED) || defined(TANGENT_USED)
previous_normal_attrib,
#ifdef NORMAL_USED
prev_normal,
#endif
prev_tangent,
prev_binormal,
#endif
prev_vertex);
global_time = scene_data_block.prev_data.time;
vertex_shader(prev_vertex,
#ifdef NORMAL_USED
prev_normal,
#endif
#ifdef TANGENT_USED
previous_tangent_attrib,
prev_tangent,
prev_binormal,
#endif
instance_index, is_multimesh, draw_call.multimesh_motion_vectors_previous_offset, scene_data_block.prev_data, instances.data[instance_index].prev_transform, prev_screen_position);
#else
@ -512,14 +590,38 @@ void main() {
vec4 screen_position;
#endif
vec3 vertex;
#ifdef NORMAL_USED
vec3 normal;
#endif
#if defined(NORMAL_USED) || defined(TANGENT_USED)
vec3 tangent;
vec3 binormal;
#endif
_unpack_vertex_attributes(
vertex_angle_attrib,
instances.data[instance_index].compressed_aabb_position_pad.xyz,
instances.data[instance_index].compressed_aabb_size_pad.xyz,
#if defined(NORMAL_USED) || defined(TANGENT_USED)
axis_tangent_attrib,
#ifdef NORMAL_USED
normal,
#endif
tangent,
binormal,
#endif
vertex);
// Current vertex.
global_time = scene_data_block.data.time;
vertex_shader(vertex_attrib,
vertex_shader(vertex,
#ifdef NORMAL_USED
normal_attrib,
normal,
#endif
#ifdef TANGENT_USED
tangent_attrib,
tangent,
binormal,
#endif
instance_index, is_multimesh, draw_call.multimesh_motion_vectors_current_offset, scene_data_block.data, model_matrix, screen_position);
}
@ -575,10 +677,6 @@ layout(location = 3) in vec2 uv_interp;
layout(location = 4) in vec2 uv2_interp;
#endif
#if !defined(TANGENT_USED) && (defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED))
#define TANGENT_USED
#endif
#ifdef TANGENT_USED
layout(location = 5) in vec3 tangent_interp;
layout(location = 6) in vec3 binormal_interp;

View file

@ -28,6 +28,10 @@
#endif
#endif
#if !defined(TANGENT_USED) && (defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED))
#define TANGENT_USED
#endif
layout(push_constant, std430) uniform DrawCall {
uint instance_index;
uint uv_offset;
@ -211,6 +215,9 @@ struct InstanceData {
uint gi_offset; //GI information when using lightmapping (VCT or lightmap index)
uint layer_mask;
vec4 lightmap_uv_scale;
vec4 compressed_aabb_position_pad; // Only .xyz is used. .w is padding.
vec4 compressed_aabb_size_pad; // Only .xyz is used. .w is padding.
vec4 uv_scale;
};
layout(set = 1, binding = 2, std430) buffer restrict readonly InstanceDataBuffer {

View file

@ -11,17 +11,17 @@
/* INPUT ATTRIBS */
layout(location = 0) in vec3 vertex_attrib;
// Always contains vertex position in XYZ, can contain tangent angle in W.
layout(location = 0) in vec4 vertex_angle_attrib;
//only for pure render depth when normal is not used
#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;
@ -66,6 +66,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);
}
/* Varyings */
layout(location = 0) highp out vec3 vertex_interp;
@ -162,9 +172,9 @@ void main() {
color_interp = color_attrib;
#endif
bool is_multimesh = bool(draw_call.flags & INSTANCE_FLAGS_MULTIMESH);
bool is_multimesh = bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH);
mat4 model_matrix = draw_call.transform;
mat4 model_matrix = instances.data[draw_call.instance_index].transform;
mat4 inv_view_matrix = scene_data.inv_view_matrix;
#ifdef USE_DOUBLE_PRECISION
vec3 model_precision = vec3(model_matrix[0][3], model_matrix[1][3], model_matrix[2][3]);
@ -178,7 +188,7 @@ void main() {
#endif
mat3 model_normal_matrix;
if (bool(draw_call.flags & INSTANCE_FLAGS_NON_UNIFORM_SCALE)) {
if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_NON_UNIFORM_SCALE)) {
model_normal_matrix = transpose(inverse(mat3(model_matrix)));
} else {
model_normal_matrix = mat3(model_matrix);
@ -191,7 +201,7 @@ void main() {
//multimesh, instances are for it
#ifdef USE_PARTICLE_TRAILS
uint trail_size = (draw_call.flags >> INSTANCE_FLAGS_PARTICLE_TRAIL_SHIFT) & INSTANCE_FLAGS_PARTICLE_TRAIL_MASK;
uint trail_size = (instances.data[draw_call.instance_index].flags >> INSTANCE_FLAGS_PARTICLE_TRAIL_SHIFT) & INSTANCE_FLAGS_PARTICLE_TRAIL_MASK;
uint stride = 3 + 1 + 1; //particles always uses this format
uint offset = trail_size * stride * gl_InstanceIndex;
@ -238,22 +248,22 @@ void main() {
uint stride = 0;
{
//TODO implement a small lookup table for the stride
if (bool(draw_call.flags & INSTANCE_FLAGS_MULTIMESH_FORMAT_2D)) {
if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH_FORMAT_2D)) {
stride += 2;
} else {
stride += 3;
}
if (bool(draw_call.flags & INSTANCE_FLAGS_MULTIMESH_HAS_COLOR)) {
if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_COLOR)) {
stride += 1;
}
if (bool(draw_call.flags & INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA)) {
if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA)) {
stride += 1;
}
}
uint offset = stride * gl_InstanceIndex;
if (bool(draw_call.flags & INSTANCE_FLAGS_MULTIMESH_FORMAT_2D)) {
if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH_FORMAT_2D)) {
matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
offset += 2;
} else {
@ -261,14 +271,14 @@ void main() {
offset += 3;
}
if (bool(draw_call.flags & INSTANCE_FLAGS_MULTIMESH_HAS_COLOR)) {
if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_COLOR)) {
#ifdef COLOR_USED
color_interp *= transforms.data[offset];
#endif
offset += 1;
}
if (bool(draw_call.flags & INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA)) {
if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA)) {
instance_custom = transforms.data[offset];
}
@ -287,16 +297,31 @@ void main() {
model_normal_matrix = model_normal_matrix * mat3(matrix);
}
vec3 vertex = vertex_attrib;
vec3 vertex = vertex_angle_attrib.xyz * instances.data[draw_call.instance_index].compressed_aabb_size_pad.xyz + instances.data[draw_call.instance_index].compressed_aabb_position_pad.xyz;
#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
#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
#ifdef UV_USED
@ -307,6 +332,17 @@ void main() {
uv2_interp = uv2_attrib;
#endif
vec4 uv_scale = instances.data[draw_call.instance_index].uv_scale;
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
}
#ifdef OVERRIDE_POSITION
vec4 position;
#endif
@ -441,8 +477,7 @@ void main() {
#endif // MODE_RENDER_DEPTH
#ifdef MODE_RENDER_MATERIAL
if (scene_data.material_uv2_mode) {
vec2 uv_offset = draw_call.lightmap_uv_scale.xy; // we are abusing lightmap_uv_scale here, we shouldn't have a lightmap during a depth pass...
gl_Position.xy = (uv2_attrib.xy + uv_offset) * 2.0 - 1.0;
gl_Position.xy = (uv2_attrib.xy + draw_call.uv_offset) * 2.0 - 1.0;
gl_Position.z = 0.00001;
gl_Position.w = 1.0;
}
@ -765,7 +800,7 @@ void main() {
#endif // ALPHA_ANTIALIASING_EDGE_USED
mat4 inv_view_matrix = scene_data.inv_view_matrix;
mat4 read_model_matrix = draw_call.transform;
mat4 read_model_matrix = instances.data[draw_call.instance_index].transform;
#ifdef USE_DOUBLE_PRECISION
read_model_matrix[0][3] = 0.0;
read_model_matrix[1][3] = 0.0;
@ -890,11 +925,11 @@ void main() {
if (!sc_disable_decals) { //Decals
// must implement
uint decal_indices = draw_call.decals.x;
uint decal_indices = instances.data[draw_call.instance_index].decals.x;
for (uint i = 0; i < 8; i++) {
uint decal_index = decal_indices & 0xFF;
if (i == 3) {
decal_indices = draw_call.decals.y;
decal_indices = instances.data[draw_call.instance_index].decals.y;
} else {
decal_indices = decal_indices >> 8;
}
@ -903,7 +938,7 @@ void main() {
break;
}
if (!bool(decals.data[decal_index].mask & draw_call.layer_mask)) {
if (!bool(decals.data[decal_index].mask & instances.data[draw_call.instance_index].layer_mask)) {
continue; //not masked
}
@ -1097,8 +1132,8 @@ void main() {
#ifdef USE_LIGHTMAP
//lightmap
if (bool(draw_call.flags & INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE)) { //has lightmap capture
uint index = draw_call.gi_offset;
if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE)) { //has lightmap capture
uint index = instances.data[draw_call.instance_index].gi_offset;
vec3 wnormal = mat3(scene_data.inv_view_matrix) * normal;
const float c1 = 0.429043;
@ -1118,12 +1153,12 @@ void main() {
2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z) *
scene_data.emissive_exposure_normalization;
} else if (bool(draw_call.flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { // has actual lightmap
bool uses_sh = bool(draw_call.flags & INSTANCE_FLAGS_USE_SH_LIGHTMAP);
uint ofs = draw_call.gi_offset & 0xFFFF;
uint slice = draw_call.gi_offset >> 16;
} else if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { // has actual lightmap
bool uses_sh = bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_USE_SH_LIGHTMAP);
uint ofs = instances.data[draw_call.instance_index].gi_offset & 0xFFFF;
uint slice = instances.data[draw_call.instance_index].gi_offset >> 16;
vec3 uvw;
uvw.xy = uv2 * draw_call.lightmap_uv_scale.zw + draw_call.lightmap_uv_scale.xy;
uvw.xy = uv2 * instances.data[draw_call.instance_index].lightmap_uv_scale.zw + instances.data[draw_call.instance_index].lightmap_uv_scale.xy;
uvw.z = float(slice);
if (uses_sh) {
@ -1162,7 +1197,7 @@ void main() {
vec4 reflection_accum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 ambient_accum = vec4(0.0, 0.0, 0.0, 0.0);
uint reflection_indices = draw_call.reflection_probes.x;
uint reflection_indices = instances.data[draw_call.instance_index].reflection_probes.x;
#ifdef LIGHT_ANISOTROPY_USED
// https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy
@ -1179,7 +1214,7 @@ void main() {
for (uint i = 0; i < 8; i++) {
uint reflection_index = reflection_indices & 0xFF;
if (i == 3) {
reflection_indices = draw_call.reflection_probes.y;
reflection_indices = instances.data[draw_call.instance_index].reflection_probes.y;
} else {
reflection_indices = reflection_indices >> 8;
}
@ -1260,7 +1295,7 @@ void main() {
break;
}
if (!bool(directional_lights.data[i].mask & draw_call.layer_mask)) {
if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
continue; //not masked
}
@ -1532,7 +1567,7 @@ void main() {
break;
}
if (!bool(directional_lights.data[i].mask & draw_call.layer_mask)) {
if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
continue; //not masked
}
@ -1601,11 +1636,11 @@ void main() {
} //directional light
if (!sc_disable_omni_lights) { //omni lights
uint light_indices = draw_call.omni_lights.x;
uint light_indices = instances.data[draw_call.instance_index].omni_lights.x;
for (uint i = 0; i < 8; i++) {
uint light_index = light_indices & 0xFF;
if (i == 3) {
light_indices = draw_call.omni_lights.y;
light_indices = instances.data[draw_call.instance_index].omni_lights.y;
} else {
light_indices = light_indices >> 8;
}
@ -1646,11 +1681,11 @@ void main() {
if (!sc_disable_spot_lights) { //spot lights
uint light_indices = draw_call.spot_lights.x;
uint light_indices = instances.data[draw_call.instance_index].spot_lights.x;
for (uint i = 0; i < 8; i++) {
uint light_index = light_indices & 0xFF;
if (i == 3) {
light_indices = draw_call.spot_lights.y;
light_indices = instances.data[draw_call.instance_index].spot_lights.y;
} else {
light_indices = light_indices >> 8;
}

View file

@ -15,20 +15,11 @@
#endif
#define USING_MOBILE_RENDERER
/* don't exceed 128 bytes!! */
/* put instance data into our push content, not a array */
layout(push_constant, std430) uniform DrawCall {
highp mat4 transform; // 64 - 64
uint flags; // 04 - 68
uint instance_uniforms_ofs; //base offset in global buffer for instance variables // 04 - 72
uint gi_offset; //GI information when using lightmapping (VCT or lightmap index) // 04 - 76
uint layer_mask; // 04 - 80
highp vec4 lightmap_uv_scale; // 16 - 96 doubles as uv_offset when needed
uvec2 reflection_probes; // 08 - 104
uvec2 omni_lights; // 08 - 112
uvec2 spot_lights; // 08 - 120
uvec2 decals; // 08 - 128
layout(push_constant, std430) uniform DrawCall {
vec2 uv_offset;
uint instance_index;
uint pad;
}
draw_call;
@ -123,6 +114,29 @@ layout(set = 1, binding = 0, std140) uniform SceneDataBlock {
}
scene_data_block;
struct InstanceData {
highp mat4 transform; // 64 - 64
uint flags; // 04 - 68
uint instance_uniforms_ofs; // Base offset in global buffer for instance variables. // 04 - 72
uint gi_offset; // GI information when using lightmapping (VCT or lightmap index). // 04 - 76
uint layer_mask; // 04 - 80
highp vec4 lightmap_uv_scale; // 16 - 96 Doubles as uv_offset when needed.
uvec2 reflection_probes; // 08 - 104
uvec2 omni_lights; // 08 - 112
uvec2 spot_lights; // 08 - 120
uvec2 decals; // 08 - 128
vec4 compressed_aabb_position_pad; // 16 - 144 // Only .xyz is used. .w is padding.
vec4 compressed_aabb_size_pad; // 16 - 160 // Only .xyz is used. .w is padding.
vec4 uv_scale; // 16 - 176
};
layout(set = 1, binding = 1, std430) buffer restrict readonly InstanceDataBuffer {
InstanceData data[];
}
instances;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
layout(set = 1, binding = 2) uniform mediump textureCubeArray radiance_cubemap;

View file

@ -71,7 +71,7 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, bool is_di
mat4 inv_view_matrix = scene_data_block.data.inv_view_matrix;
#ifdef USING_MOBILE_RENDERER
mat4 read_model_matrix = draw_call.transform;
mat4 read_model_matrix = instances.data[draw_call.instance_index].transform;
#else
mat4 read_model_matrix = instances.data[instance_index_interp].transform;
#endif

View file

@ -49,7 +49,7 @@ layout(push_constant, std430) uniform Params {
uint blend_shape_count;
bool normalized_blend_shapes;
uint pad0;
uint normal_tangent_stride;
uint pad1;
vec2 skeleton_transform_x;
@ -188,15 +188,15 @@ void main() {
vertex = uintBitsToFloat(uvec3(src_vertices.data[src_offset + 0], src_vertices.data[src_offset + 1], src_vertices.data[src_offset + 2]));
src_offset += 3;
uint src_normal = params.vertex_count * params.vertex_stride + index * params.normal_tangent_stride;
if (params.has_normal) {
normal = decode_uint_oct_to_norm(src_vertices.data[src_offset]);
src_offset++;
normal = decode_uint_oct_to_norm(src_vertices.data[src_normal]);
src_normal++;
}
if (params.has_tangent) {
tangent = decode_uint_oct_to_tang(src_vertices.data[src_offset]);
tangent = decode_uint_oct_to_tang(src_vertices.data[src_normal]);
}
if (params.has_blend_shape) {
@ -208,19 +208,19 @@ void main() {
for (uint i = 0; i < params.blend_shape_count; i++) {
float w = blend_shape_weights.data[i];
if (abs(w) > 0.0001) {
uint base_offset = (params.vertex_count * i + index) * params.vertex_stride;
uint base_offset = params.vertex_count * i * (params.vertex_stride + params.normal_tangent_stride) + index * params.vertex_stride;
blend_vertex += uintBitsToFloat(uvec3(src_blend_shapes.data[base_offset + 0], src_blend_shapes.data[base_offset + 1], src_blend_shapes.data[base_offset + 2])) * w;
base_offset += 3;
uint base_normal = params.vertex_count * i * (params.vertex_stride + params.normal_tangent_stride) + params.vertex_count * params.vertex_stride + index * params.normal_tangent_stride;
if (params.has_normal) {
blend_normal += decode_uint_oct_to_norm(src_blend_shapes.data[base_offset]) * w;
base_offset++;
blend_normal += decode_uint_oct_to_norm(src_blend_shapes.data[base_normal]) * w;
base_normal++;
}
if (params.has_tangent) {
blend_tangent += decode_uint_oct_to_tang(src_blend_shapes.data[base_offset]).rgb * w;
blend_tangent += decode_uint_oct_to_tang(src_blend_shapes.data[base_normal]).rgb * w;
}
blend_total += w;
@ -291,15 +291,15 @@ void main() {
dst_vertices.data[dst_offset + 1] = uvertex.y;
dst_vertices.data[dst_offset + 2] = uvertex.z;
dst_offset += 3;
uint dst_normal = params.vertex_count * params.vertex_stride + index * params.normal_tangent_stride;
if (params.has_normal) {
dst_vertices.data[dst_offset] = encode_norm_to_uint_oct(normal);
dst_offset++;
dst_vertices.data[dst_normal] = encode_norm_to_uint_oct(normal);
dst_normal++;
}
if (params.has_tangent) {
dst_vertices.data[dst_offset] = encode_tang_to_uint_oct(tangent);
dst_vertices.data[dst_normal] = encode_tang_to_uint_oct(tangent);
}
#endif

View file

@ -270,10 +270,10 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
uint32_t skin_stride = 0;
for (int i = 0; i < RS::ARRAY_WEIGHTS; i++) {
if ((p_surface.format & (1 << i))) {
if ((p_surface.format & (1ULL << i))) {
switch (i) {
case RS::ARRAY_VERTEX: {
if (p_surface.format & RS::ARRAY_FLAG_USE_2D_VERTICES) {
if ((p_surface.format & RS::ARRAY_FLAG_USE_2D_VERTICES) || (p_surface.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
stride += sizeof(float) * 2;
} else {
stride += sizeof(float) * 3;
@ -281,22 +281,31 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
} break;
case RS::ARRAY_NORMAL: {
stride += sizeof(int32_t);
stride += sizeof(uint16_t) * 2;
} break;
case RS::ARRAY_TANGENT: {
stride += sizeof(int32_t);
if (!(p_surface.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
stride += sizeof(uint16_t) * 2;
}
} break;
case RS::ARRAY_COLOR: {
attrib_stride += sizeof(uint32_t);
} break;
case RS::ARRAY_TEX_UV: {
attrib_stride += sizeof(float) * 2;
if (p_surface.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
attrib_stride += sizeof(uint16_t) * 2;
} else {
attrib_stride += sizeof(float) * 2;
}
} break;
case RS::ARRAY_TEX_UV2: {
attrib_stride += sizeof(float) * 2;
if (p_surface.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
attrib_stride += sizeof(uint16_t) * 2;
} else {
attrib_stride += sizeof(float) * 2;
}
} break;
case RS::ARRAY_CUSTOM0:
@ -336,61 +345,98 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
}
}
#endif
uint64_t surface_version = p_surface.format & (uint64_t(RS::ARRAY_FLAG_FORMAT_VERSION_MASK) << RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT);
RS::SurfaceData new_surface = p_surface;
#ifdef DISABLE_DEPRECATED
ERR_FAIL_COND_MSG(surface_version != RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION, "Surface version provided (" + itos(int(surface_version >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT)) + ") does not match current version (" + itos(RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) + ")");
#else
if (surface_version != uint64_t(RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION)) {
RS::_fix_surface_compatibility(new_surface);
surface_version = new_surface.format & (RS::ARRAY_FLAG_FORMAT_VERSION_MASK << RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT);
ERR_FAIL_COND_MSG(surface_version != RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION,
"Surface version provided (" +
itos((surface_version >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) & RS::ARRAY_FLAG_FORMAT_VERSION_MASK) +
") does not match current version (" +
itos((RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) & RS::ARRAY_FLAG_FORMAT_VERSION_MASK) +
")");
}
#endif
Mesh::Surface *s = memnew(Mesh::Surface);
s->format = p_surface.format;
s->primitive = p_surface.primitive;
s->format = new_surface.format;
s->primitive = new_surface.primitive;
bool use_as_storage = (p_surface.skin_data.size() || mesh->blend_shape_count > 0);
bool use_as_storage = (new_surface.skin_data.size() || mesh->blend_shape_count > 0);
if (p_surface.vertex_data.size()) {
s->vertex_buffer = RD::get_singleton()->vertex_buffer_create(p_surface.vertex_data.size(), p_surface.vertex_data, use_as_storage);
s->vertex_buffer_size = p_surface.vertex_data.size();
if (new_surface.vertex_data.size()) {
// If we have an uncompressed surface that contains normals, but not tangents, we need to differentiate the array
// from a compressed array in the shader. To do so, we allow the the normal to read 4 components out of the buffer
// But only give it 2 components per normal. So essentially, each vertex reads the next normal in normal.zw.
// This allows us to avoid adding a shader permutation, and avoid passing dummy tangents. Since the stride is kept small
// this should still be a net win for bandwidth.
// If we do this, then the last normal will read past the end of the array. So we need to pad the array with dummy data.
if (!(new_surface.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) && (new_surface.format & RS::ARRAY_FORMAT_NORMAL) && !(new_surface.format & RS::ARRAY_FORMAT_TANGENT)) {
// Unfortunately, we need to copy the buffer, which is fine as doing a resize triggers a CoW anyway.
Vector<uint8_t> new_vertex_data;
new_vertex_data.resize_zeroed(new_surface.vertex_data.size() + sizeof(uint16_t) * 2);
memcpy(new_vertex_data.ptrw(), new_surface.vertex_data.ptr(), new_surface.vertex_data.size());
s->vertex_buffer = RD::get_singleton()->vertex_buffer_create(new_vertex_data.size(), new_vertex_data, use_as_storage);
s->vertex_buffer_size = new_vertex_data.size();
} else {
s->vertex_buffer = RD::get_singleton()->vertex_buffer_create(new_surface.vertex_data.size(), new_surface.vertex_data, use_as_storage);
s->vertex_buffer_size = new_surface.vertex_data.size();
}
}
if (p_surface.attribute_data.size()) {
s->attribute_buffer = RD::get_singleton()->vertex_buffer_create(p_surface.attribute_data.size(), p_surface.attribute_data);
if (new_surface.attribute_data.size()) {
s->attribute_buffer = RD::get_singleton()->vertex_buffer_create(new_surface.attribute_data.size(), new_surface.attribute_data);
}
if (p_surface.skin_data.size()) {
s->skin_buffer = RD::get_singleton()->vertex_buffer_create(p_surface.skin_data.size(), p_surface.skin_data, use_as_storage);
s->skin_buffer_size = p_surface.skin_data.size();
if (new_surface.skin_data.size()) {
s->skin_buffer = RD::get_singleton()->vertex_buffer_create(new_surface.skin_data.size(), new_surface.skin_data, use_as_storage);
s->skin_buffer_size = new_surface.skin_data.size();
}
s->vertex_count = p_surface.vertex_count;
s->vertex_count = new_surface.vertex_count;
if (p_surface.format & RS::ARRAY_FORMAT_BONES) {
if (new_surface.format & RS::ARRAY_FORMAT_BONES) {
mesh->has_bone_weights = true;
}
if (p_surface.index_count) {
bool is_index_16 = p_surface.vertex_count <= 65536 && p_surface.vertex_count > 0;
if (new_surface.index_count) {
bool is_index_16 = new_surface.vertex_count <= 65536 && new_surface.vertex_count > 0;
s->index_buffer = RD::get_singleton()->index_buffer_create(p_surface.index_count, is_index_16 ? RD::INDEX_BUFFER_FORMAT_UINT16 : RD::INDEX_BUFFER_FORMAT_UINT32, p_surface.index_data, false);
s->index_count = p_surface.index_count;
s->index_buffer = RD::get_singleton()->index_buffer_create(new_surface.index_count, is_index_16 ? RD::INDEX_BUFFER_FORMAT_UINT16 : RD::INDEX_BUFFER_FORMAT_UINT32, new_surface.index_data, false);
s->index_count = new_surface.index_count;
s->index_array = RD::get_singleton()->index_array_create(s->index_buffer, 0, s->index_count);
if (p_surface.lods.size()) {
s->lods = memnew_arr(Mesh::Surface::LOD, p_surface.lods.size());
s->lod_count = p_surface.lods.size();
if (new_surface.lods.size()) {
s->lods = memnew_arr(Mesh::Surface::LOD, new_surface.lods.size());
s->lod_count = new_surface.lods.size();
for (int i = 0; i < p_surface.lods.size(); i++) {
uint32_t indices = p_surface.lods[i].index_data.size() / (is_index_16 ? 2 : 4);
s->lods[i].index_buffer = RD::get_singleton()->index_buffer_create(indices, is_index_16 ? RD::INDEX_BUFFER_FORMAT_UINT16 : RD::INDEX_BUFFER_FORMAT_UINT32, p_surface.lods[i].index_data);
for (int i = 0; i < new_surface.lods.size(); i++) {
uint32_t indices = new_surface.lods[i].index_data.size() / (is_index_16 ? 2 : 4);
s->lods[i].index_buffer = RD::get_singleton()->index_buffer_create(indices, is_index_16 ? RD::INDEX_BUFFER_FORMAT_UINT16 : RD::INDEX_BUFFER_FORMAT_UINT32, new_surface.lods[i].index_data);
s->lods[i].index_array = RD::get_singleton()->index_array_create(s->lods[i].index_buffer, 0, indices);
s->lods[i].edge_length = p_surface.lods[i].edge_length;
s->lods[i].edge_length = new_surface.lods[i].edge_length;
s->lods[i].index_count = indices;
}
}
}
ERR_FAIL_COND_MSG(!p_surface.index_count && !p_surface.vertex_count, "Meshes must contain a vertex array, an index array, or both");
ERR_FAIL_COND_MSG(!new_surface.index_count && !new_surface.vertex_count, "Meshes must contain a vertex array, an index array, or both");
s->aabb = p_surface.aabb;
s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them.
s->aabb = new_surface.aabb;
s->bone_aabbs = new_surface.bone_aabbs; //only really useful for returning them.
s->uv_scale = new_surface.uv_scale;
if (mesh->blend_shape_count > 0) {
s->blend_shape_buffer = RD::get_singleton()->storage_buffer_create(p_surface.blend_shape_data.size(), p_surface.blend_shape_data);
s->blend_shape_buffer = RD::get_singleton()->storage_buffer_create(new_surface.blend_shape_data.size(), new_surface.blend_shape_data);
}
if (use_as_storage) {
@ -433,13 +479,13 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
}
if (mesh->surface_count == 0) {
mesh->aabb = p_surface.aabb;
mesh->aabb = new_surface.aabb;
} else {
mesh->aabb.merge_with(p_surface.aabb);
mesh->aabb.merge_with(new_surface.aabb);
}
mesh->skeleton_aabb_version = 0;
s->material = p_surface.material;
s->material = new_surface.material;
mesh->surfaces = (Mesh::Surface **)memrealloc(mesh->surfaces, sizeof(Mesh::Surface *) * (mesh->surface_count + 1));
mesh->surfaces[mesh->surface_count] = s;
@ -545,6 +591,11 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const {
sd.format = s.format;
if (s.vertex_buffer.is_valid()) {
sd.vertex_data = RD::get_singleton()->buffer_get_data(s.vertex_buffer);
// When using an uncompressed buffer with normals, but without tangents, we have to trim the padding.
if (!(s.format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) && (s.format & RS::ARRAY_FORMAT_NORMAL) && !(s.format & RS::ARRAY_FORMAT_TANGENT)) {
Vector<uint8_t> new_vertex_data;
sd.vertex_data.resize(sd.vertex_data.size() - sizeof(uint16_t) * 2);
}
}
if (s.attribute_buffer.is_valid()) {
sd.attribute_data = RD::get_singleton()->buffer_get_data(s.attribute_buffer);
@ -560,6 +611,7 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const {
sd.index_data = RD::get_singleton()->buffer_get_data(s.index_buffer);
}
sd.aabb = s.aabb;
sd.uv_scale = s.uv_scale;
for (uint32_t i = 0; i < s.lod_count; i++) {
RS::SurfaceData::LOD lod;
lod.edge_length = s.lods[i].edge_length;
@ -1016,8 +1068,10 @@ void MeshStorage::update_mesh_instances() {
push_constant.has_skeleton = sk != nullptr && sk->use_2d == array_is_2d && (mi->mesh->surfaces[i]->format & RS::ARRAY_FORMAT_BONES);
push_constant.has_blend_shape = mi->mesh->blend_shape_count > 0;
push_constant.normal_tangent_stride = (push_constant.has_normal ? 1 : 0) + (push_constant.has_tangent ? 1 : 0);
push_constant.vertex_count = mi->mesh->surfaces[i]->vertex_count;
push_constant.vertex_stride = (mi->mesh->surfaces[i]->vertex_buffer_size / mi->mesh->surfaces[i]->vertex_count) / 4;
push_constant.vertex_stride = ((mi->mesh->surfaces[i]->vertex_buffer_size / mi->mesh->surfaces[i]->vertex_count) / 4) - push_constant.normal_tangent_stride;
push_constant.skin_stride = (mi->mesh->surfaces[i]->skin_buffer_size / mi->mesh->surfaces[i]->vertex_count) / 4;
push_constant.skin_weight_offset = (mi->mesh->surfaces[i]->format & RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS) ? 4 : 2;
@ -1042,7 +1096,6 @@ void MeshStorage::update_mesh_instances() {
push_constant.blend_shape_count = mi->mesh->blend_shape_count;
push_constant.normalized_blend_shapes = mi->mesh->blend_shape_mode == RS::BLEND_SHAPE_MODE_NORMALIZED;
push_constant.pad0 = 0;
push_constant.pad1 = 0;
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SkeletonShader::PushConstant));
@ -1061,11 +1114,13 @@ void MeshStorage::update_mesh_instances() {
RD::get_singleton()->compute_list_end();
}
void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis, uint32_t p_current_buffer, uint32_t p_previous_buffer) {
void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis, uint32_t p_current_buffer, uint32_t p_previous_buffer) {
Vector<RD::VertexAttribute> attributes;
Vector<RID> buffers;
Vector<uint64_t> offsets;
uint32_t stride = 0;
uint32_t position_stride = 0;
uint32_t normal_tangent_stride = 0;
uint32_t attribute_stride = 0;
uint32_t skin_stride = 0;
@ -1073,8 +1128,9 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
RD::VertexAttribute vd;
RID buffer;
vd.location = i;
uint64_t offset = 0;
if (!(s->format & (1 << i))) {
if (!(s->format & (1ULL << i))) {
// Not supplied by surface, use default value
buffer = mesh_default_rd_buffers[i];
vd.stride = 0;
@ -1123,14 +1179,19 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
switch (i) {
case RS::ARRAY_VERTEX: {
vd.offset = stride;
vd.offset = position_stride;
if (s->format & RS::ARRAY_FLAG_USE_2D_VERTICES) {
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
stride += sizeof(float) * 2;
position_stride = sizeof(float) * 2;
} else {
vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT;
stride += sizeof(float) * 3;
if (!mis && (s->format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
vd.format = RD::DATA_FORMAT_R16G16B16A16_UNORM;
position_stride = sizeof(uint16_t) * 4;
} else {
vd.format = RD::DATA_FORMAT_R32G32B32_SFLOAT;
position_stride = sizeof(float) * 3;
}
}
if (mis) {
@ -1141,10 +1202,22 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
} break;
case RS::ARRAY_NORMAL: {
vd.offset = stride;
vd.format = RD::DATA_FORMAT_R16G16_UNORM;
stride += sizeof(uint16_t) * 2;
vd.offset = 0;
offset = position_stride * s->vertex_count;
if (!mis && (s->format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
vd.format = RD::DATA_FORMAT_R16G16_UNORM;
normal_tangent_stride += sizeof(uint16_t) * 2;
} else {
vd.format = RD::DATA_FORMAT_R16G16B16A16_UNORM;
// A small trick here: if we are uncompressed and we have normals, but no tangents. We need
// the shader to think there are 4 components to "axis_tangent_attrib". So we give a size of 4,
// but a stride based on only having 2 elements.
if (!(s->format & RS::ARRAY_FORMAT_TANGENT)) {
normal_tangent_stride += sizeof(uint16_t) * 2;
} else {
normal_tangent_stride += sizeof(uint16_t) * 4;
}
}
if (mis) {
buffer = mis->vertex_buffer[p_current_buffer];
} else {
@ -1152,15 +1225,9 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
}
} break;
case RS::ARRAY_TANGENT: {
vd.offset = stride;
vd.format = RD::DATA_FORMAT_R16G16_UNORM;
stride += sizeof(uint16_t) * 2;
if (mis) {
buffer = mis->vertex_buffer[p_current_buffer];
} else {
buffer = s->vertex_buffer;
}
buffer = mesh_default_rd_buffers[i];
vd.stride = 0;
vd.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT;
} break;
case RS::ARRAY_COLOR: {
vd.offset = attribute_stride;
@ -1171,17 +1238,25 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
} break;
case RS::ARRAY_TEX_UV: {
vd.offset = attribute_stride;
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
attribute_stride += sizeof(float) * 2;
if (s->format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
vd.format = RD::DATA_FORMAT_R16G16_UNORM;
attribute_stride += sizeof(uint16_t) * 2;
} else {
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
attribute_stride += sizeof(float) * 2;
}
buffer = s->attribute_buffer;
} break;
case RS::ARRAY_TEX_UV2: {
vd.offset = attribute_stride;
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
attribute_stride += sizeof(float) * 2;
if (s->format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
vd.format = RD::DATA_FORMAT_R16G16_UNORM;
attribute_stride += sizeof(uint16_t) * 2;
} else {
vd.format = RD::DATA_FORMAT_R32G32_SFLOAT;
attribute_stride += sizeof(float) * 2;
}
buffer = s->attribute_buffer;
} break;
case RS::ARRAY_CUSTOM0:
@ -1216,12 +1291,13 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
}
}
if (!(p_input_mask & (1 << i))) {
if (!(p_input_mask & (1ULL << i))) {
continue; // Shader does not need this, skip it (but computing stride was important anyway)
}
attributes.push_back(vd);
buffers.push_back(buffer);
offsets.push_back(offset);
if (p_input_motion_vectors) {
// Since the previous vertex, normal and tangent can't be part of the vertex format but they are required when motion
@ -1246,6 +1322,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
attributes.push_back(vd);
buffers.push_back(buffer);
offsets.push_back(offset);
}
}
}
@ -1256,9 +1333,10 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
continue; //default location
}
int loc = attributes[i].location;
if ((loc < RS::ARRAY_COLOR) || ((loc >= ATTRIBUTE_LOCATION_PREV_VERTEX) && (loc <= ATTRIBUTE_LOCATION_PREV_TANGENT))) {
attributes.write[i].stride = stride;
if (loc == RS::ARRAY_VERTEX || loc == ATTRIBUTE_LOCATION_PREV_VERTEX) {
attributes.write[i].stride = position_stride;
} else if ((loc < RS::ARRAY_COLOR) || ((loc >= ATTRIBUTE_LOCATION_PREV_NORMAL) && (loc <= ATTRIBUTE_LOCATION_PREV_TANGENT))) {
attributes.write[i].stride = normal_tangent_stride;
} else if (loc < RS::ARRAY_BONES) {
attributes.write[i].stride = attribute_stride;
} else {
@ -1271,7 +1349,7 @@ void MeshStorage::_mesh_surface_generate_version_for_input_mask(Mesh::Surface::V
v.previous_buffer = p_previous_buffer;
v.input_motion_vectors = p_input_motion_vectors;
v.vertex_format = RD::get_singleton()->vertex_format_create(attributes);
v.vertex_array = RD::get_singleton()->vertex_array_create(s->vertex_count, v.vertex_format, buffers);
v.vertex_array = RD::get_singleton()->vertex_array_create(s->vertex_count, v.vertex_format, buffers, offsets);
}
////////////////// MULTIMESH

View file

@ -73,7 +73,7 @@ private:
struct Mesh {
struct Surface {
RS::PrimitiveType primitive = RS::PRIMITIVE_POINTS;
uint32_t format = 0;
uint64_t format = 0;
RID vertex_buffer;
RID attribute_buffer;
@ -90,7 +90,7 @@ private:
// cache-efficient structure.
struct Version {
uint32_t input_mask = 0;
uint64_t input_mask = 0;
uint32_t current_buffer = 0;
uint32_t previous_buffer = 0;
bool input_motion_vectors = false;
@ -120,6 +120,8 @@ private:
Vector<AABB> bone_aabbs;
Vector4 uv_scale;
RID blend_shape_buffer;
RID material;
@ -190,7 +192,7 @@ private:
weight_update_list(this), array_update_list(this) {}
};
void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint32_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis = nullptr, uint32_t p_current_buffer = 0, uint32_t p_previous_buffer = 0);
void _mesh_surface_generate_version_for_input_mask(Mesh::Surface::Version &v, Mesh::Surface *s, uint64_t p_input_mask, bool p_input_motion_vectors, MeshInstance::Surface *mis = nullptr, uint32_t p_current_buffer = 0, uint32_t p_previous_buffer = 0);
void _mesh_instance_clear(MeshInstance *mi);
void _mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint32_t p_surface);
@ -265,7 +267,7 @@ private:
uint32_t blend_shape_count;
uint32_t normalized_blend_shapes;
uint32_t pad0;
uint32_t normal_tangent_stride;
uint32_t pad1;
float skeleton_transform_x[2];
float skeleton_transform_y[2];
@ -422,6 +424,21 @@ public:
return s->index_count ? s->index_count : s->vertex_count;
}
_FORCE_INLINE_ AABB mesh_surface_get_aabb(void *p_surface) {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
return s->aabb;
}
_FORCE_INLINE_ uint64_t mesh_surface_get_format(void *p_surface) {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
return s->format;
}
_FORCE_INLINE_ Vector4 mesh_surface_get_uv_scale(void *p_surface) {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
return s->uv_scale;
}
_FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t &r_index_count) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
@ -484,7 +501,7 @@ public:
s->version_lock.unlock();
}
_FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint32_t p_surface_index, uint32_t p_input_mask, bool p_input_motion_vectors, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) {
_FORCE_INLINE_ void mesh_instance_surface_get_vertex_arrays_and_format(RID p_mesh_instance, uint64_t p_surface_index, uint32_t p_input_mask, bool p_input_motion_vectors, RID &r_vertex_array_rd, RD::VertexFormatID &r_vertex_format) {
MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance);
ERR_FAIL_NULL(mi);
Mesh *mesh = mi->mesh;

View file

@ -643,7 +643,7 @@ Error RenderingDevice::_reflect_spirv(const Vector<ShaderStageSPIRVData> &p_spir
for (uint32_t j = 0; j < iv_count; j++) {
if (input_vars[j] && input_vars[j]->decoration_flags == 0) { // Regular input.
r_reflection_data.vertex_input_mask |= (1 << uint32_t(input_vars[j]->location));
r_reflection_data.vertex_input_mask |= (1ULL << uint32_t(input_vars[j]->location));
}
}
}

View file

@ -758,7 +758,7 @@ public:
virtual RID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, RID p_placeholder = RID()) = 0;
virtual RID shader_create_placeholder() = 0;
virtual uint32_t shader_get_vertex_input_attribute_mask(RID p_shader) = 0;
virtual uint64_t shader_get_vertex_input_attribute_mask(RID p_shader) = 0;
/******************/
/**** UNIFORMS ****/
@ -1371,7 +1371,7 @@ protected:
struct SpirvReflectionData {
BitField<ShaderStage> stages_mask;
uint32_t vertex_input_mask;
uint64_t vertex_input_mask;
uint32_t fragment_output_mask;
bool is_compute;
uint32_t compute_local_size[3];

View file

@ -322,7 +322,40 @@ RID RenderingServer::get_white_texture() {
return white_texture;
}
Error RenderingServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint32_t *p_offsets, uint32_t p_vertex_stride, uint32_t p_attrib_stride, uint32_t p_skin_stride, Vector<uint8_t> &r_vertex_array, Vector<uint8_t> &r_attrib_array, Vector<uint8_t> &r_skin_array, int p_vertex_array_len, Vector<uint8_t> &r_index_array, int p_index_array_len, AABB &r_aabb, Vector<AABB> &r_bone_aabb) {
void _get_axis_angle(const Vector3 &p_normal, const Vector4 &p_tangent, float &r_angle, Vector3 &r_axis) {
Vector3 tangent = Vector3(p_tangent.x, p_tangent.y, p_tangent.z);
float d = p_tangent.w;
Vector3 binormal = p_normal.cross(tangent);
r_angle = Math::acos((tangent.x + binormal.y + p_normal.z - 1.0) / 2.0);
float denom = 2.0 * Math::sin(r_angle);
r_axis.x = (p_normal.y - binormal.z) / denom;
r_axis.y = (tangent.z - p_normal.x) / denom;
r_axis.z = (binormal.x - tangent.y) / denom;
r_axis.normalize();
if (d < 0.0) {
r_angle = CLAMP((1.0 - r_angle / Math_PI) * 0.5, 0.0, 0.49999);
} else {
r_angle = (r_angle / Math_PI) * 0.5 + 0.5;
}
}
// The inputs to this function should match the outputs of _get_axis_angle. I.e. p_axis is a normalized vector
// and p_angle includes the binormal direction.
void _get_tbn_from_axis_angle(const Vector3 &p_axis, float p_angle, Vector3 &r_normal, Vector4 &r_tangent) {
float binormal_sign = p_angle > 0.5 ? 1.0 : -1.0;
float angle = Math::abs(p_angle * 2.0 - 1.0) * Math_PI;
float c = cos(angle);
float s = sin(angle);
Vector3 omc_axis = (1.0 - c) * p_axis;
Vector3 s_axis = s * p_axis;
Vector3 tan = omc_axis.x * p_axis + Vector3(c, -s_axis.z, s_axis.y);
r_tangent = Vector4(tan.x, tan.y, tan.z, binormal_sign);
r_normal = omc_axis.z * p_axis + Vector3(-s_axis.y, s_axis.x, c);
}
Error RenderingServer::_surface_set_data(Array p_arrays, uint64_t p_format, uint32_t *p_offsets, uint32_t p_vertex_stride, uint32_t p_normal_stride, uint32_t p_attrib_stride, uint32_t p_skin_stride, Vector<uint8_t> &r_vertex_array, Vector<uint8_t> &r_attrib_array, Vector<uint8_t> &r_skin_array, int p_vertex_array_len, Vector<uint8_t> &r_index_array, int p_index_array_len, AABB &r_aabb, Vector<AABB> &r_bone_aabb, Vector4 &r_uv_scale) {
uint8_t *vw = r_vertex_array.ptrw();
uint8_t *aw = r_attrib_array.ptrw();
uint8_t *sw = r_skin_array.ptrw();
@ -334,8 +367,44 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint
int max_bone = 0;
// Preprocess UVs if compression is enabled
if (p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES && ((p_format & RS::ARRAY_FORMAT_TEX_UV) || (p_format & RS::ARRAY_FORMAT_TEX_UV2))) {
const Vector2 *uv_src = nullptr;
if (p_format & RS::ARRAY_FORMAT_TEX_UV) {
Vector<Vector2> array = p_arrays[RS::ARRAY_TEX_UV];
uv_src = array.ptr();
}
const Vector2 *uv2_src = nullptr;
if (p_format & RS::ARRAY_FORMAT_TEX_UV2) {
Vector<Vector2> array = p_arrays[RS::ARRAY_TEX_UV2];
uv2_src = array.ptr();
}
Vector2 max_val = Vector2(0.0, 0.0);
Vector2 min_val = Vector2(0.0, 0.0);
Vector2 max_val2 = Vector2(0.0, 0.0);
Vector2 min_val2 = Vector2(0.0, 0.0);
for (int i = 0; i < p_vertex_array_len; i++) {
if (p_format & RS::ARRAY_FORMAT_TEX_UV) {
max_val = max_val.max(uv_src[i]);
min_val = min_val.min(uv_src[i]);
}
if (p_format & RS::ARRAY_FORMAT_TEX_UV2) {
max_val2 = max_val2.max(uv2_src[i]);
min_val2 = min_val2.min(uv2_src[i]);
}
}
max_val = max_val.abs().max(min_val.abs());
max_val2 = max_val2.abs().max(min_val2.abs());
r_uv_scale = Vector4(max_val.x, max_val.y, max_val2.x, max_val2.y) * Vector4(2.0, 2.0, 2.0, 2.0);
}
for (int ai = 0; ai < RS::ARRAY_MAX; ai++) {
if (!(p_format & (1 << ai))) { // No array
if (!(p_format & (1ULL << ai))) { // No array
continue;
}
@ -375,7 +444,118 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint
// Setting vertices means regenerating the AABB.
AABB aabb;
{
if (p_format & ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
// First we need to generate the AABB for the entire surface.
for (int i = 0; i < p_vertex_array_len; i++) {
if (i == 0) {
aabb = AABB(src[i], SMALL_VEC3);
} else {
aabb.expand_to(src[i]);
}
}
bool using_normals_tangents = (p_format & RS::ARRAY_FORMAT_NORMAL) && (p_format & RS::ARRAY_FORMAT_TANGENT);
if (!using_normals_tangents) {
// Early out if we are only setting vertex positions.
for (int i = 0; i < p_vertex_array_len; i++) {
Vector3 pos = (src[i] - aabb.position) / aabb.size;
uint16_t vector[4] = {
(uint16_t)CLAMP(pos.x * 65535, 0, 65535),
(uint16_t)CLAMP(pos.y * 65535, 0, 65535),
(uint16_t)CLAMP(pos.z * 65535, 0, 65535),
(uint16_t)0
};
memcpy(&vw[p_offsets[ai] + i * p_vertex_stride], vector, sizeof(uint16_t) * 4);
}
continue;
}
// Validate normal and tangent arrays.
ERR_FAIL_COND_V(p_arrays[RS::ARRAY_NORMAL].get_type() != Variant::PACKED_VECTOR3_ARRAY, ERR_INVALID_PARAMETER);
Variant::Type tangent_type = p_arrays[RS::ARRAY_TANGENT].get_type();
ERR_FAIL_COND_V(tangent_type != Variant::PACKED_FLOAT32_ARRAY && tangent_type != Variant::PACKED_FLOAT64_ARRAY, ERR_INVALID_PARAMETER);
Vector<Vector3> normal_array = p_arrays[RS::ARRAY_NORMAL];
ERR_FAIL_COND_V(normal_array.size() != p_vertex_array_len, ERR_INVALID_PARAMETER);
const Vector3 *normal_src = normal_array.ptr();
// We need a different version if using double precision tangents.
if (tangent_type == Variant::PACKED_FLOAT32_ARRAY) {
Vector<float> tangent_array = p_arrays[RS::ARRAY_TANGENT];
ERR_FAIL_COND_V(tangent_array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER);
const float *tangent_src = tangent_array.ptr();
// Set data for vertex, normal, and tangent.
for (int i = 0; i < p_vertex_array_len; i++) {
float angle = 0.0;
Vector3 axis;
Vector4 tangent = Vector4(tangent_src[i * 4 + 0], tangent_src[i * 4 + 1], tangent_src[i * 4 + 2], tangent_src[i * 4 + 3]);
_get_axis_angle(normal_src[i], tangent, angle, axis);
// Store axis.
{
Vector2 res = axis.octahedron_encode();
uint16_t vector[2] = {
(uint16_t)CLAMP(res.x * 65535, 0, 65535),
(uint16_t)CLAMP(res.y * 65535, 0, 65535),
};
memcpy(&vw[p_offsets[RS::ARRAY_NORMAL] + i * p_normal_stride], vector, 4);
}
// Store vertex position + angle.
{
Vector3 pos = (src[i] - aabb.position) / aabb.size;
uint16_t vector[4] = {
(uint16_t)CLAMP(pos.x * 65535, 0, 65535),
(uint16_t)CLAMP(pos.y * 65535, 0, 65535),
(uint16_t)CLAMP(pos.z * 65535, 0, 65535),
(uint16_t)CLAMP(angle * 65535, 0, 65535)
};
memcpy(&vw[p_offsets[ai] + i * p_vertex_stride], vector, sizeof(uint16_t) * 4);
}
}
} else { // PACKED_FLOAT64_ARRAY
Vector<double> tangent_array = p_arrays[RS::ARRAY_TANGENT];
ERR_FAIL_COND_V(tangent_array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER);
const double *tangent_src = tangent_array.ptr();
// Set data for vertex, normal, and tangent.
for (int i = 0; i < p_vertex_array_len; i++) {
float angle;
Vector3 axis;
Vector4 tangent = Vector4(tangent_src[i * 4 + 0], tangent_src[i * 4 + 1], tangent_src[i * 4 + 2], tangent_src[i * 4 + 3]);
_get_axis_angle(normal_src[i], tangent, angle, axis);
// Store axis.
{
Vector2 res = axis.octahedron_encode();
uint16_t vector[2] = {
(uint16_t)CLAMP(res.x * 65535, 0, 65535),
(uint16_t)CLAMP(res.y * 65535, 0, 65535),
};
memcpy(&vw[p_offsets[RS::ARRAY_NORMAL] + i * p_normal_stride], vector, 4);
}
// Store vertex position + angle.
{
Vector3 pos = (src[i] - aabb.position) / aabb.size;
uint16_t vector[4] = {
(uint16_t)CLAMP(pos.x * 65535, 0, 65535),
(uint16_t)CLAMP(pos.y * 65535, 0, 65535),
(uint16_t)CLAMP(pos.z * 65535, 0, 65535),
(uint16_t)CLAMP(angle * 65535, 0, 65535)
};
memcpy(&vw[p_offsets[ai] + i * p_vertex_stride], vector, sizeof(uint16_t) * 4);
}
}
}
} else {
for (int i = 0; i < p_vertex_array_len; i++) {
float vector[3] = { (float)src[i].x, (float)src[i].y, (float)src[i].z };
@ -394,55 +574,61 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint
} break;
case RS::ARRAY_NORMAL: {
ERR_FAIL_COND_V(p_arrays[ai].get_type() != Variant::PACKED_VECTOR3_ARRAY, ERR_INVALID_PARAMETER);
// If using compression we store normal while storing vertices.
if (!(p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
ERR_FAIL_COND_V(p_arrays[ai].get_type() != Variant::PACKED_VECTOR3_ARRAY, ERR_INVALID_PARAMETER);
Vector<Vector3> array = p_arrays[ai];
ERR_FAIL_COND_V(array.size() != p_vertex_array_len, ERR_INVALID_PARAMETER);
Vector<Vector3> array = p_arrays[ai];
ERR_FAIL_COND_V(array.size() != p_vertex_array_len, ERR_INVALID_PARAMETER);
const Vector3 *src = array.ptr();
for (int i = 0; i < p_vertex_array_len; i++) {
Vector2 res = src[i].octahedron_encode();
uint16_t vector[2] = {
(uint16_t)CLAMP(res.x * 65535, 0, 65535),
(uint16_t)CLAMP(res.y * 65535, 0, 65535),
};
const Vector3 *src = array.ptr();
for (int i = 0; i < p_vertex_array_len; i++) {
Vector2 res = src[i].octahedron_encode();
uint16_t vector[2] = {
(uint16_t)CLAMP(res.x * 65535, 0, 65535),
(uint16_t)CLAMP(res.y * 65535, 0, 65535),
};
memcpy(&vw[p_offsets[ai] + i * p_vertex_stride], vector, 4);
memcpy(&vw[p_offsets[ai] + i * p_normal_stride], vector, 4);
}
}
} break;
case RS::ARRAY_TANGENT: {
Variant::Type type = p_arrays[ai].get_type();
ERR_FAIL_COND_V(type != Variant::PACKED_FLOAT32_ARRAY && type != Variant::PACKED_FLOAT64_ARRAY, ERR_INVALID_PARAMETER);
if (type == Variant::PACKED_FLOAT32_ARRAY) {
Vector<float> array = p_arrays[ai];
ERR_FAIL_COND_V(array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER);
const float *src_ptr = array.ptr();
// If using compression we store tangent while storing vertices.
if (!(p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
Variant::Type type = p_arrays[ai].get_type();
ERR_FAIL_COND_V(type != Variant::PACKED_FLOAT32_ARRAY && type != Variant::PACKED_FLOAT64_ARRAY, ERR_INVALID_PARAMETER);
if (type == Variant::PACKED_FLOAT32_ARRAY) {
Vector<float> array = p_arrays[ai];
ERR_FAIL_COND_V(array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER);
const float *src_ptr = array.ptr();
for (int i = 0; i < p_vertex_array_len; i++) {
const Vector3 src(src_ptr[i * 4 + 0], src_ptr[i * 4 + 1], src_ptr[i * 4 + 2]);
Vector2 res = src.octahedron_tangent_encode(src_ptr[i * 4 + 3]);
uint16_t vector[2] = {
(uint16_t)CLAMP(res.x * 65535, 0, 65535),
(uint16_t)CLAMP(res.y * 65535, 0, 65535),
};
for (int i = 0; i < p_vertex_array_len; i++) {
const Vector3 src(src_ptr[i * 4 + 0], src_ptr[i * 4 + 1], src_ptr[i * 4 + 2]);
Vector2 res = src.octahedron_tangent_encode(src_ptr[i * 4 + 3]);
uint16_t vector[2] = {
(uint16_t)CLAMP(res.x * 65535, 0, 65535),
(uint16_t)CLAMP(res.y * 65535, 0, 65535),
};
memcpy(&vw[p_offsets[ai] + i * p_vertex_stride], vector, 4);
}
} else { // PACKED_FLOAT64_ARRAY
Vector<double> array = p_arrays[ai];
ERR_FAIL_COND_V(array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER);
const double *src_ptr = array.ptr();
memcpy(&vw[p_offsets[ai] + i * p_normal_stride], vector, 4);
}
} else { // PACKED_FLOAT64_ARRAY
Vector<double> array = p_arrays[ai];
ERR_FAIL_COND_V(array.size() != p_vertex_array_len * 4, ERR_INVALID_PARAMETER);
const double *src_ptr = array.ptr();
for (int i = 0; i < p_vertex_array_len; i++) {
const Vector3 src(src_ptr[i * 4 + 0], src_ptr[i * 4 + 1], src_ptr[i * 4 + 2]);
Vector2 res = src.octahedron_tangent_encode(src_ptr[i * 4 + 3]);
uint16_t vector[2] = {
(uint16_t)CLAMP(res.x * 65535, 0, 65535),
(uint16_t)CLAMP(res.y * 65535, 0, 65535),
};
for (int i = 0; i < p_vertex_array_len; i++) {
const Vector3 src(src_ptr[i * 4 + 0], src_ptr[i * 4 + 1], src_ptr[i * 4 + 2]);
Vector2 res = src.octahedron_tangent_encode(src_ptr[i * 4 + 3]);
uint16_t vector[2] = {
(uint16_t)CLAMP(res.x * 65535, 0, 65535),
(uint16_t)CLAMP(res.y * 65535, 0, 65535),
};
memcpy(&vw[p_offsets[ai] + i * p_vertex_stride], vector, 4);
memcpy(&vw[p_offsets[ai] + i * p_normal_stride], vector, 4);
}
}
}
} break;
@ -472,13 +658,20 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint
ERR_FAIL_COND_V(array.size() != p_vertex_array_len, ERR_INVALID_PARAMETER);
const Vector2 *src = array.ptr();
for (int i = 0; i < p_vertex_array_len; i++) {
float uv[2] = { (float)src[i].x, (float)src[i].y };
memcpy(&aw[p_offsets[ai] + i * p_attrib_stride], uv, 2 * 4);
if (p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
for (int i = 0; i < p_vertex_array_len; i++) {
Vector2 vec = src[i];
// Normalize into 0-1 from possible range -uv_scale - uv_scale.
vec = vec / (Vector2(r_uv_scale.x, r_uv_scale.y)) + Vector2(0.5, 0.5);
uint16_t uv[2] = { (uint16_t)CLAMP(vec.x * 65535, 0, 65535), (uint16_t)CLAMP(vec.y * 65535, 0, 65535) };
memcpy(&aw[p_offsets[ai] + i * p_attrib_stride], uv, 4);
}
} else {
for (int i = 0; i < p_vertex_array_len; i++) {
float uv[2] = { (float)src[i].x, (float)src[i].y };
memcpy(&aw[p_offsets[ai] + i * p_attrib_stride], uv, 2 * 4);
}
}
} break;
case RS::ARRAY_TEX_UV2: {
@ -490,9 +683,19 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint
const Vector2 *src = array.ptr();
for (int i = 0; i < p_vertex_array_len; i++) {
float uv[2] = { (float)src[i].x, (float)src[i].y };
memcpy(&aw[p_offsets[ai] + i * p_attrib_stride], uv, 2 * 4);
if (p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
for (int i = 0; i < p_vertex_array_len; i++) {
Vector2 vec = src[i];
// Normalize into 0-1 from possible range -uv_scale - uv_scale.
vec = vec / (Vector2(r_uv_scale.z, r_uv_scale.w)) + Vector2(0.5, 0.5);
uint16_t uv[2] = { (uint16_t)CLAMP(vec.x * 65535, 0, 65535), (uint16_t)CLAMP(vec.y * 65535, 0, 65535) };
memcpy(&aw[p_offsets[ai] + i * p_attrib_stride], uv, 4);
}
} else {
for (int i = 0; i < p_vertex_array_len; i++) {
float uv[2] = { (float)src[i].x, (float)src[i].y };
memcpy(&aw[p_offsets[ai] + i * p_attrib_stride], uv, 2 * 4);
}
}
} break;
case RS::ARRAY_CUSTOM0:
@ -707,9 +910,10 @@ uint32_t RenderingServer::mesh_surface_get_format_offset(BitField<ArrayFormat> p
p_format = int64_t(p_format) & ~ARRAY_FORMAT_INDEX;
uint32_t offsets[ARRAY_MAX];
uint32_t vstr;
uint32_t ntstr;
uint32_t astr;
uint32_t sstr;
mesh_surface_make_offsets_from_format(p_format, p_vertex_len, 0, offsets, vstr, astr, sstr);
mesh_surface_make_offsets_from_format(p_format, p_vertex_len, 0, offsets, vstr, ntstr, astr, sstr);
return offsets[p_array_index];
}
@ -717,32 +921,48 @@ uint32_t RenderingServer::mesh_surface_get_format_vertex_stride(BitField<ArrayFo
p_format = int64_t(p_format) & ~ARRAY_FORMAT_INDEX;
uint32_t offsets[ARRAY_MAX];
uint32_t vstr;
uint32_t ntstr;
uint32_t astr;
uint32_t sstr;
mesh_surface_make_offsets_from_format(p_format, p_vertex_len, 0, offsets, vstr, astr, sstr);
mesh_surface_make_offsets_from_format(p_format, p_vertex_len, 0, offsets, vstr, ntstr, astr, sstr);
return vstr;
}
uint32_t RenderingServer::mesh_surface_get_format_normal_tangent_stride(BitField<ArrayFormat> p_format, int p_vertex_len) const {
p_format = int64_t(p_format) & ~ARRAY_FORMAT_INDEX;
uint32_t offsets[ARRAY_MAX];
uint32_t vstr;
uint32_t ntstr;
uint32_t astr;
uint32_t sstr;
mesh_surface_make_offsets_from_format(p_format, p_vertex_len, 0, offsets, vstr, ntstr, astr, sstr);
return vstr;
}
uint32_t RenderingServer::mesh_surface_get_format_attribute_stride(BitField<ArrayFormat> p_format, int p_vertex_len) const {
p_format = int64_t(p_format) & ~ARRAY_FORMAT_INDEX;
uint32_t offsets[ARRAY_MAX];
uint32_t vstr;
uint32_t ntstr;
uint32_t astr;
uint32_t sstr;
mesh_surface_make_offsets_from_format(p_format, p_vertex_len, 0, offsets, vstr, astr, sstr);
mesh_surface_make_offsets_from_format(p_format, p_vertex_len, 0, offsets, vstr, ntstr, astr, sstr);
return astr;
}
uint32_t RenderingServer::mesh_surface_get_format_skin_stride(BitField<ArrayFormat> p_format, int p_vertex_len) const {
p_format = int64_t(p_format) & ~ARRAY_FORMAT_INDEX;
uint32_t offsets[ARRAY_MAX];
uint32_t vstr;
uint32_t ntstr;
uint32_t astr;
uint32_t sstr;
mesh_surface_make_offsets_from_format(p_format, p_vertex_len, 0, offsets, vstr, astr, sstr);
mesh_surface_make_offsets_from_format(p_format, p_vertex_len, 0, offsets, vstr, ntstr, astr, sstr);
return sstr;
}
void RenderingServer::mesh_surface_make_offsets_from_format(uint32_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets, uint32_t &r_vertex_element_size, uint32_t &r_attrib_element_size, uint32_t &r_skin_element_size) const {
void RenderingServer::mesh_surface_make_offsets_from_format(uint64_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets, uint32_t &r_vertex_element_size, uint32_t &r_normal_element_size, uint32_t &r_attrib_element_size, uint32_t &r_skin_element_size) const {
r_vertex_element_size = 0;
r_normal_element_size = 0;
r_attrib_element_size = 0;
r_skin_element_size = 0;
@ -753,13 +973,15 @@ void RenderingServer::mesh_surface_make_offsets_from_format(uint32_t p_format, i
if (i == RS::ARRAY_VERTEX) {
size_accum = &r_vertex_element_size;
} else if (i == RS::ARRAY_NORMAL) {
size_accum = &r_normal_element_size;
} else if (i == RS::ARRAY_COLOR) {
size_accum = &r_attrib_element_size;
} else if (i == RS::ARRAY_BONES) {
size_accum = &r_skin_element_size;
}
if (!(p_format & (1 << i))) { // No array
if (!(p_format & (1ULL << i))) { // No array
continue;
}
@ -770,7 +992,7 @@ void RenderingServer::mesh_surface_make_offsets_from_format(uint32_t p_format, i
if (p_format & ARRAY_FLAG_USE_2D_VERTICES) {
elem_size = 2;
} else {
elem_size = 3;
elem_size = (p_format & ARRAY_FLAG_COMPRESS_ATTRIBUTES) ? 2 : 3;
}
elem_size *= sizeof(float);
@ -779,22 +1001,22 @@ void RenderingServer::mesh_surface_make_offsets_from_format(uint32_t p_format, i
elem_size = 4;
} break;
case RS::ARRAY_TANGENT: {
elem_size = 4;
elem_size = (p_format & ARRAY_FLAG_COMPRESS_ATTRIBUTES) ? 0 : 4;
} break;
case RS::ARRAY_COLOR: {
elem_size = 4;
} break;
case RS::ARRAY_TEX_UV: {
elem_size = 8;
elem_size = (p_format & ARRAY_FLAG_COMPRESS_ATTRIBUTES) ? 4 : 8;
} break;
case RS::ARRAY_TEX_UV2: {
elem_size = 8;
elem_size = (p_format & ARRAY_FLAG_COMPRESS_ATTRIBUTES) ? 4 : 8;
} break;
case RS::ARRAY_CUSTOM0:
case RS::ARRAY_CUSTOM1:
case RS::ARRAY_CUSTOM2:
case RS::ARRAY_CUSTOM3: {
uint32_t format = (p_format >> (ARRAY_FORMAT_CUSTOM_BASE + (ARRAY_FORMAT_CUSTOM_BITS * (i - ARRAY_CUSTOM0)))) & ARRAY_FORMAT_CUSTOM_MASK;
uint64_t format = (p_format >> (ARRAY_FORMAT_CUSTOM_BASE + (ARRAY_FORMAT_CUSTOM_BITS * (i - ARRAY_CUSTOM0)))) & ARRAY_FORMAT_CUSTOM_MASK;
switch (format) {
case ARRAY_CUSTOM_RGBA8_UNORM: {
elem_size = 4;
@ -852,6 +1074,9 @@ void RenderingServer::mesh_surface_make_offsets_from_format(uint32_t p_format, i
if (size_accum != nullptr) {
r_offsets[i] = (*size_accum);
if (i == RS::ARRAY_NORMAL || i == RS::ARRAY_TANGENT) {
r_offsets[i] += r_vertex_element_size * p_vertex_len;
}
(*size_accum) += elem_size;
} else {
r_offsets[i] = 0;
@ -859,11 +1084,11 @@ void RenderingServer::mesh_surface_make_offsets_from_format(uint32_t p_format, i
}
}
Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surface_data, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, const Dictionary &p_lods, uint32_t p_compress_format) {
Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surface_data, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, const Dictionary &p_lods, uint64_t p_compress_format) {
ERR_FAIL_INDEX_V(p_primitive, RS::PRIMITIVE_MAX, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(p_arrays.size() != RS::ARRAY_MAX, ERR_INVALID_PARAMETER);
uint32_t format = 0;
uint64_t format = 0;
// Validation
int index_array_len = 0;
@ -874,7 +1099,7 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
continue;
}
format |= (1 << i);
format |= (1ULL << i);
if (i == RS::ARRAY_VERTEX) {
switch (p_arrays[i].get_type()) {
@ -930,7 +1155,7 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
for (uint32_t i = 0; i < RS::ARRAY_CUSTOM_COUNT; ++i) {
// Include custom array format type.
if (format & (1 << (ARRAY_CUSTOM0 + i))) {
if (format & (1ULL << (ARRAY_CUSTOM0 + i))) {
format |= (RS::ARRAY_FORMAT_CUSTOM_MASK << (RS::ARRAY_FORMAT_CUSTOM_BASE + i * RS::ARRAY_FORMAT_CUSTOM_BITS)) & p_compress_format;
}
}
@ -938,21 +1163,33 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
uint32_t offsets[RS::ARRAY_MAX];
uint32_t vertex_element_size;
uint32_t normal_element_size;
uint32_t attrib_element_size;
uint32_t skin_element_size;
mesh_surface_make_offsets_from_format(format, array_len, index_array_len, offsets, vertex_element_size, attrib_element_size, skin_element_size);
uint32_t mask = (1 << ARRAY_MAX) - 1;
uint64_t mask = (1ULL << ARRAY_MAX) - 1ULL;
format |= (~mask) & p_compress_format; // Make the full format.
// Force version to the current version as this function will always return a surface with the current version.
format &= ~(ARRAY_FLAG_FORMAT_VERSION_MASK << ARRAY_FLAG_FORMAT_VERSION_SHIFT);
format |= ARRAY_FLAG_FORMAT_CURRENT_VERSION & (ARRAY_FLAG_FORMAT_VERSION_MASK << ARRAY_FLAG_FORMAT_VERSION_SHIFT);
mesh_surface_make_offsets_from_format(format, array_len, index_array_len, offsets, vertex_element_size, normal_element_size, attrib_element_size, skin_element_size);
if ((format & RS::ARRAY_FORMAT_VERTEX) == 0 && !(format & RS::ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY)) {
ERR_PRINT("Mesh created without vertex array. This mesh will not be visible with the default shader. If using an empty vertex array is intentional, create the mesh with the ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY flag to silence this error.");
// Set the flag here after warning to suppress errors down the pipeline.
format |= RS::ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY;
}
int vertex_array_size = vertex_element_size * array_len;
if (format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES && ((format & RS::ARRAY_FORMAT_NORMAL) || (format & RS::ARRAY_FORMAT_TANGENT))) {
// If using normals or tangents, then we need all three.
ERR_FAIL_COND_V_MSG(!(format & RS::ARRAY_FORMAT_VERTEX), ERR_INVALID_PARAMETER, "Can't use compression flag 'ARRAY_FLAG_COMPRESS_ATTRIBUTES' while using normals or tangents without vertex array.");
ERR_FAIL_COND_V_MSG(!(format & RS::ARRAY_FORMAT_NORMAL), ERR_INVALID_PARAMETER, "Can't use compression flag 'ARRAY_FLAG_COMPRESS_ATTRIBUTES' while using tangents without normal array.");
ERR_FAIL_COND_V_MSG(!(format & RS::ARRAY_FORMAT_TANGENT), ERR_INVALID_PARAMETER, "Can't use compression flag 'ARRAY_FLAG_COMPRESS_ATTRIBUTES' while using normals without tangent array.");
}
int vertex_array_size = (vertex_element_size + normal_element_size) * array_len;
int attrib_array_size = attrib_element_size * array_len;
int skin_array_size = skin_element_size * array_len;
int index_array_size = offsets[RS::ARRAY_INDEX] * index_array_len;
@ -972,7 +1209,9 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
AABB aabb;
Vector<AABB> bone_aabb;
Error err = _surface_set_data(p_arrays, format, offsets, vertex_element_size, attrib_element_size, skin_element_size, vertex_array, attrib_array, skin_array, array_len, index_array, index_array_len, aabb, bone_aabb);
Vector4 uv_scale = Vector4(0.0, 0.0, 0.0, 0.0);
Error err = _surface_set_data(p_arrays, format, offsets, vertex_element_size, normal_element_size, attrib_element_size, skin_element_size, vertex_array, attrib_array, skin_array, array_len, index_array, index_array_len, aabb, bone_aabb, uv_scale);
ERR_FAIL_COND_V_MSG(err != OK, ERR_INVALID_DATA, "Invalid array format for surface.");
Vector<uint8_t> blend_shape_data;
@ -987,7 +1226,8 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
Vector<uint8_t> noskin;
AABB laabb;
Error err2 = _surface_set_data(p_blend_shapes[i], bs_format, offsets, vertex_element_size, 0, 0, vertex_array_shape, noattrib, noskin, array_len, noindex, 0, laabb, bone_aabb);
Vector4 bone_uv_scale; // Not used.
Error err2 = _surface_set_data(p_blend_shapes[i], bs_format, offsets, vertex_element_size, normal_element_size, 0, 0, vertex_array_shape, noattrib, noskin, array_len, noindex, 0, laabb, bone_aabb, bone_uv_scale);
aabb.merge_with(laabb);
ERR_FAIL_COND_V_MSG(err2 != OK, ERR_INVALID_DATA, "Invalid blend shape array format for surface.");
@ -1048,6 +1288,7 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
surface_data.blend_shape_data = blend_shape_data;
surface_data.bone_aabbs = bone_aabb;
surface_data.lods = lods;
surface_data.uv_scale = uv_scale;
return OK;
}
@ -1061,13 +1302,14 @@ void RenderingServer::mesh_add_surface_from_arrays(RID p_mesh, PrimitiveType p_p
mesh_add_surface(p_mesh, sd);
}
Array RenderingServer::_get_array_from_surface(uint32_t p_format, Vector<uint8_t> p_vertex_data, Vector<uint8_t> p_attrib_data, Vector<uint8_t> p_skin_data, int p_vertex_len, Vector<uint8_t> p_index_data, int p_index_len) const {
Array RenderingServer::_get_array_from_surface(uint64_t p_format, Vector<uint8_t> p_vertex_data, Vector<uint8_t> p_attrib_data, Vector<uint8_t> p_skin_data, int p_vertex_len, Vector<uint8_t> p_index_data, int p_index_len, const AABB &p_aabb) const {
uint32_t offsets[RS::ARRAY_MAX];
uint32_t vertex_elem_size;
uint32_t normal_elem_size;
uint32_t attrib_elem_size;
uint32_t skin_elem_size;
mesh_surface_make_offsets_from_format(p_format, p_vertex_len, p_index_len, offsets, vertex_elem_size, attrib_elem_size, skin_elem_size);
mesh_surface_make_offsets_from_format(p_format, p_vertex_len, p_index_len, offsets, vertex_elem_size, normal_elem_size, attrib_elem_size, skin_elem_size);
Array ret;
ret.resize(RS::ARRAY_MAX);
@ -1077,7 +1319,7 @@ Array RenderingServer::_get_array_from_surface(uint32_t p_format, Vector<uint8_t
const uint8_t *sr = p_skin_data.ptr();
for (int i = 0; i < RS::ARRAY_MAX; i++) {
if (!(p_format & (1 << i))) {
if (!(p_format & (1ULL << i))) {
continue;
}
@ -1104,9 +1346,54 @@ Array RenderingServer::_get_array_from_surface(uint32_t p_format, Vector<uint8_t
{
Vector3 *w = arr_3d.ptrw();
for (int j = 0; j < p_vertex_len; j++) {
const float *v = reinterpret_cast<const float *>(&r[j * vertex_elem_size + offsets[i]]);
w[j] = Vector3(v[0], v[1], v[2]);
if (p_format & ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
bool using_normals_tangents = (p_format & RS::ARRAY_FORMAT_NORMAL) && (p_format & RS::ARRAY_FORMAT_TANGENT);
// We only have vertices to read, so just read them and skip everything else.
if (!using_normals_tangents) {
for (int j = 0; j < p_vertex_len; j++) {
const uint16_t *v = reinterpret_cast<const uint16_t *>(&r[j * vertex_elem_size + offsets[i]]);
Vector3 vec = Vector3(float(v[0]) / 65535.0, float(v[1]) / 65535.0, float(v[2]) / 65535.0);
w[j] = (vec * p_aabb.size) + p_aabb.position;
}
continue;
}
Vector<Vector3> normals;
normals.resize(p_vertex_len);
Vector3 *normalsw = normals.ptrw();
Vector<float> tangents;
tangents.resize(p_vertex_len * 4);
float *tangentsw = tangents.ptrw();
for (int j = 0; j < p_vertex_len; j++) {
const uint32_t n = *(const uint32_t *)&r[j * normal_elem_size + offsets[RS::ARRAY_NORMAL]];
Vector3 axis = Vector3::octahedron_decode(Vector2((n & 0xFFFF) / 65535.0, ((n >> 16) & 0xFFFF) / 65535.0));
const uint16_t *v = reinterpret_cast<const uint16_t *>(&r[j * vertex_elem_size + offsets[i]]);
Vector3 vec = Vector3(float(v[0]) / 65535.0, float(v[1]) / 65535.0, float(v[2]) / 65535.0);
float angle = float(v[3]) / 65535.0;
w[j] = (vec * p_aabb.size) + p_aabb.position;
Vector3 normal;
Vector4 tan;
_get_tbn_from_axis_angle(axis, angle, normal, tan);
normalsw[j] = normal;
tangentsw[j * 4 + 0] = tan.x;
tangentsw[j * 4 + 1] = tan.y;
tangentsw[j * 4 + 2] = tan.z;
tangentsw[j * 4 + 3] = tan.w;
}
ret[RS::ARRAY_NORMAL] = normals;
ret[RS::ARRAY_FORMAT_TANGENT] = tangents;
} else {
for (int j = 0; j < p_vertex_len; j++) {
const float *v = reinterpret_cast<const float *>(&r[j * vertex_elem_size + offsets[i]]);
w[j] = Vector3(v[0], v[1], v[2]);
}
}
}
@ -1115,39 +1402,41 @@ Array RenderingServer::_get_array_from_surface(uint32_t p_format, Vector<uint8_t
} break;
case RS::ARRAY_NORMAL: {
Vector<Vector3> arr;
arr.resize(p_vertex_len);
if (!(p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
Vector<Vector3> arr;
arr.resize(p_vertex_len);
Vector3 *w = arr.ptrw();
Vector3 *w = arr.ptrw();
for (int j = 0; j < p_vertex_len; j++) {
const uint32_t v = *(const uint32_t *)&r[j * vertex_elem_size + offsets[i]];
for (int j = 0; j < p_vertex_len; j++) {
const uint32_t v = *(const uint32_t *)&r[j * normal_elem_size + offsets[i]];
w[j] = Vector3::octahedron_decode(Vector2((v & 0xFFFF) / 65535.0, ((v >> 16) & 0xFFFF) / 65535.0));
w[j] = Vector3::octahedron_decode(Vector2((v & 0xFFFF) / 65535.0, ((v >> 16) & 0xFFFF) / 65535.0));
}
ret[i] = arr;
}
ret[i] = arr;
} break;
case RS::ARRAY_TANGENT: {
Vector<float> arr;
arr.resize(p_vertex_len * 4);
if (!(p_format & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES)) {
Vector<float> arr;
arr.resize(p_vertex_len * 4);
float *w = arr.ptrw();
float *w = arr.ptrw();
for (int j = 0; j < p_vertex_len; j++) {
const uint32_t v = *(const uint32_t *)&r[j * vertex_elem_size + offsets[i]];
float tangent_sign;
Vector3 res = Vector3::octahedron_tangent_decode(Vector2((v & 0xFFFF) / 65535.0, ((v >> 16) & 0xFFFF) / 65535.0), &tangent_sign);
w[j * 4 + 0] = res.x;
w[j * 4 + 1] = res.y;
w[j * 4 + 2] = res.z;
w[j * 4 + 3] = tangent_sign;
for (int j = 0; j < p_vertex_len; j++) {
const uint32_t v = *(const uint32_t *)&r[j * normal_elem_size + offsets[i]];
float tangent_sign;
Vector3 res = Vector3::octahedron_tangent_decode(Vector2((v & 0xFFFF) / 65535.0, ((v >> 16) & 0xFFFF) / 65535.0), &tangent_sign);
w[j * 4 + 0] = res.x;
w[j * 4 + 1] = res.y;
w[j * 4 + 2] = res.z;
w[j * 4 + 3] = tangent_sign;
}
ret[i] = arr;
}
ret[i] = arr;
} break;
case RS::ARRAY_COLOR: {
Vector<Color> arr;
@ -1168,12 +1457,17 @@ Array RenderingServer::_get_array_from_surface(uint32_t p_format, Vector<uint8_t
arr.resize(p_vertex_len);
Vector2 *w = arr.ptrw();
for (int j = 0; j < p_vertex_len; j++) {
const float *v = reinterpret_cast<const float *>(&ar[j * attrib_elem_size + offsets[i]]);
w[j] = Vector2(v[0], v[1]);
if (p_format & ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
for (int j = 0; j < p_vertex_len; j++) {
const uint16_t *v = reinterpret_cast<const uint16_t *>(&ar[j * attrib_elem_size + offsets[i]]);
w[j] = Vector2(float(v[0]) / 65535.0, float(v[1]) / 65535.0);
}
} else {
for (int j = 0; j < p_vertex_len; j++) {
const float *v = reinterpret_cast<const float *>(&ar[j * attrib_elem_size + offsets[i]]);
w[j] = Vector2(v[0], v[1]);
}
}
ret[i] = arr;
} break;
@ -1183,9 +1477,16 @@ Array RenderingServer::_get_array_from_surface(uint32_t p_format, Vector<uint8_t
Vector2 *w = arr.ptrw();
for (int j = 0; j < p_vertex_len; j++) {
const float *v = reinterpret_cast<const float *>(&ar[j * attrib_elem_size + offsets[i]]);
w[j] = Vector2(v[0], v[1]);
if (p_format & ARRAY_FLAG_COMPRESS_ATTRIBUTES) {
for (int j = 0; j < p_vertex_len; j++) {
const uint16_t *v = reinterpret_cast<const uint16_t *>(&ar[j * attrib_elem_size + offsets[i]]);
w[j] = Vector2(float(v[0]) / 65535.0, float(v[1]) / 65535.0);
}
} else {
for (int j = 0; j < p_vertex_len; j++) {
const float *v = reinterpret_cast<const float *>(&ar[j * attrib_elem_size + offsets[i]]);
w[j] = Vector2(v[0], v[1]);
}
}
ret[i] = arr;
@ -1358,12 +1659,13 @@ TypedArray<Array> RenderingServer::mesh_surface_get_blend_shape_arrays(RID p_mes
uint32_t bs_offsets[RS::ARRAY_MAX];
uint32_t bs_format = (sd.format & RS::ARRAY_FORMAT_BLEND_SHAPE_MASK);
uint32_t vertex_elem_size;
uint32_t normal_elem_size;
uint32_t attrib_elem_size;
uint32_t skin_elem_size;
//CLAY
mesh_surface_make_offsets_from_format(bs_format, sd.vertex_count, 0, bs_offsets, vertex_elem_size, normal_elem_size, attrib_elem_size, skin_elem_size);
mesh_surface_make_offsets_from_format(bs_format, sd.vertex_count, 0, bs_offsets, vertex_elem_size, attrib_elem_size, skin_elem_size);
int divisor = vertex_elem_size * sd.vertex_count;
int divisor = (vertex_elem_size + normal_elem_size) * sd.vertex_count;
ERR_FAIL_COND_V((blend_shape_data.size() % divisor) != 0, Array());
uint32_t blend_shape_count = blend_shape_data.size() / divisor;
@ -1375,7 +1677,7 @@ TypedArray<Array> RenderingServer::mesh_surface_get_blend_shape_arrays(RID p_mes
for (uint32_t i = 0; i < blend_shape_count; i++) {
Vector<uint8_t> bs_data = blend_shape_data.slice(i * divisor, (i + 1) * divisor);
Vector<uint8_t> unused;
blend_shape_array.set(i, _get_array_from_surface(bs_format, bs_data, unused, unused, sd.vertex_count, unused, 0));
blend_shape_array.set(i, _get_array_from_surface(bs_format, bs_data, unused, unused, sd.vertex_count, unused, 0, sd.aabb));
}
return blend_shape_array;
@ -1395,9 +1697,9 @@ Array RenderingServer::mesh_create_arrays_from_surface_data(const SurfaceData &p
Vector<uint8_t> index_data = p_data.index_data;
int index_len = p_data.index_count;
uint32_t format = p_data.format;
uint64_t format = p_data.format;
return _get_array_from_surface(format, vertex_data, attrib_data, skin_data, vertex_len, index_data, index_len);
return _get_array_from_surface(format, vertex_data, attrib_data, skin_data, vertex_len, index_data, index_len, p_data.aabb);
}
#if 0
Array RenderingServer::_mesh_surface_get_skeleton_aabb_bind(RID p_mesh, int p_surface) const {
@ -1531,7 +1833,9 @@ static RS::SurfaceData _dict_to_surf(const Dictionary &p_dictionary) {
RS::SurfaceData sd;
sd.primitive = RS::PrimitiveType(int(p_dictionary["primitive"]));
sd.format = p_dictionary["format"];
if (p_dictionary.has("uv_scale")) {
sd.format = p_dictionary["format"];
}
sd.vertex_data = p_dictionary["vertex_data"];
if (p_dictionary.has("attribute_data")) {
sd.attribute_data = p_dictionary["attribute_data"];
@ -1549,6 +1853,7 @@ static RS::SurfaceData _dict_to_surf(const Dictionary &p_dictionary) {
}
sd.aabb = p_dictionary["aabb"];
sd.uv_scale = p_dictionary["uv_scale"];
if (p_dictionary.has("lods")) {
Array lods = p_dictionary["lods"];
@ -1610,6 +1915,7 @@ Dictionary RenderingServer::_mesh_get_surface(RID p_mesh, int p_idx) {
d["index_count"] = sd.index_count;
}
d["aabb"] = sd.aabb;
d["uv_scale"] = sd.uv_scale;
if (sd.lods.size()) {
Array lods;
@ -1663,6 +1969,117 @@ void RenderingServer::_particles_set_trail_bind_poses(RID p_particles, const Typ
particles_set_trail_bind_poses(p_particles, tbposes);
}
Vector<uint8_t> _convert_surface_version_1_to_surface_version_2(uint64_t p_format, Vector<uint8_t> p_vertex_data, uint32_t p_vertex_count, uint32_t p_old_stride, uint32_t p_vertex_size, uint32_t p_normal_size, uint32_t p_position_stride, uint32_t p_normal_tangent_stride) {
Vector<uint8_t> new_vertex_data;
new_vertex_data.resize(p_vertex_data.size());
uint8_t *dst_vertex_ptr = new_vertex_data.ptrw();
const uint8_t *src_vertex_ptr = p_vertex_data.ptr();
uint32_t position_size = p_position_stride * p_vertex_count;
for (uint32_t j = 0; j < RS::ARRAY_COLOR; j++) {
if (!(p_format & (1ULL << j))) {
continue;
}
switch (j) {
case RS::ARRAY_VERTEX: {
if (p_format & RS::ARRAY_FLAG_USE_2D_VERTICES) {
for (uint32_t i = 0; i < p_vertex_count; i++) {
const float *src = (const float *)&src_vertex_ptr[i * p_old_stride];
float *dst = (float *)&dst_vertex_ptr[i * p_position_stride];
dst[0] = src[0];
dst[1] = src[1];
}
} else {
for (uint32_t i = 0; i < p_vertex_count; i++) {
const float *src = (const float *)&src_vertex_ptr[i * p_old_stride];
float *dst = (float *)&dst_vertex_ptr[i * p_position_stride];
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
}
}
} break;
case RS::ARRAY_NORMAL: {
for (uint32_t i = 0; i < p_vertex_count; i++) {
const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * p_old_stride + p_vertex_size];
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * p_normal_tangent_stride + position_size];
dst[0] = src[0];
dst[1] = src[1];
}
} break;
case RS::ARRAY_TANGENT: {
for (uint32_t i = 0; i < p_vertex_count; i++) {
const uint16_t *src = (const uint16_t *)&src_vertex_ptr[i * p_old_stride + p_vertex_size + p_normal_size];
uint16_t *dst = (uint16_t *)&dst_vertex_ptr[i * p_normal_tangent_stride + position_size + p_normal_size];
dst[0] = src[0];
dst[1] = src[1];
}
} break;
}
}
return new_vertex_data;
}
#ifndef DISABLE_DEPRECATED
void RenderingServer::_fix_surface_compatibility(SurfaceData &p_surface) {
uint64_t surface_version = p_surface.format & (ARRAY_FLAG_FORMAT_VERSION_MASK << ARRAY_FLAG_FORMAT_VERSION_SHIFT);
ERR_FAIL_COND_MSG(surface_version > ARRAY_FLAG_FORMAT_CURRENT_VERSION, "Cannot convert surface with version provided (" + itos((surface_version >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) & RS::ARRAY_FLAG_FORMAT_VERSION_MASK) + ") to current version (" + itos((RS::ARRAY_FLAG_FORMAT_CURRENT_VERSION >> RS::ARRAY_FLAG_FORMAT_VERSION_SHIFT) & RS::ARRAY_FLAG_FORMAT_VERSION_MASK) + ")");
if (surface_version == ARRAY_FLAG_FORMAT_VERSION_1) {
// The only difference for now is that Version 1 uses interleaved vertex positions while version 2 does not.
// I.e. PNTPNTPNT -> PPPNTNTNT.
WARN_PRINT_ED("Upgrading mesh from older surface format. Once saved again (or re-imported), this mesh will be incompatible with earlier versions of Godot.");
int vertex_size = 0;
int normal_size = 0;
int tangent_size = 0;
if (p_surface.format & ARRAY_FORMAT_VERTEX) {
if (p_surface.format & ARRAY_FLAG_USE_2D_VERTICES) {
vertex_size = sizeof(float) * 2;
} else {
vertex_size = sizeof(float) * 3;
}
}
if (p_surface.format & ARRAY_FORMAT_NORMAL) {
normal_size += sizeof(uint16_t) * 2;
}
if (p_surface.format & ARRAY_FORMAT_TANGENT) {
tangent_size = sizeof(uint16_t) * 2;
}
int stride = p_surface.vertex_data.size() / p_surface.vertex_count;
int position_stride = vertex_size;
int normal_tangent_stride = normal_size + tangent_size;
p_surface.vertex_data = _convert_surface_version_1_to_surface_version_2(p_surface.format, p_surface.vertex_data, p_surface.vertex_count, stride, vertex_size, normal_size, position_stride, normal_tangent_stride);
if (p_surface.blend_shape_data.size() > 0) {
// The size of one blend shape.
int divisor = (vertex_size + normal_size + tangent_size) * p_surface.vertex_count;
ERR_FAIL_COND((p_surface.blend_shape_data.size() % divisor) != 0);
uint32_t blend_shape_count = p_surface.blend_shape_data.size() / divisor;
Vector<uint8_t> new_blend_shape_data;
for (uint32_t i = 0; i < blend_shape_count; i++) {
Vector<uint8_t> bs_data = p_surface.blend_shape_data.slice(i * divisor, (i + 1) * divisor);
Vector<uint8_t> blend_shape = _convert_surface_version_1_to_surface_version_2(p_surface.format, bs_data, p_surface.vertex_count, stride, vertex_size, normal_size, position_stride, normal_tangent_stride);
new_blend_shape_data.append_array(blend_shape);
}
ERR_FAIL_COND(p_surface.blend_shape_data.size() != new_blend_shape_data.size());
p_surface.blend_shape_data = new_blend_shape_data;
}
}
p_surface.format &= ~(ARRAY_FLAG_FORMAT_VERSION_MASK << ARRAY_FLAG_FORMAT_VERSION_SHIFT);
p_surface.format |= ARRAY_FLAG_FORMAT_CURRENT_VERSION & (ARRAY_FLAG_FORMAT_VERSION_MASK << ARRAY_FLAG_FORMAT_VERSION_SHIFT);
}
#endif
void RenderingServer::_bind_methods() {
BIND_CONSTANT(NO_INDEX_ARRAY);
BIND_CONSTANT(ARRAY_WEIGHTS_SIZE);
@ -1753,6 +2170,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("mesh_create"), &RenderingServer::mesh_create);
ClassDB::bind_method(D_METHOD("mesh_surface_get_format_offset", "format", "vertex_count", "array_index"), &RenderingServer::mesh_surface_get_format_offset);
ClassDB::bind_method(D_METHOD("mesh_surface_get_format_vertex_stride", "format", "vertex_count"), &RenderingServer::mesh_surface_get_format_vertex_stride);
ClassDB::bind_method(D_METHOD("mesh_surface_get_format_normal_tangent_stride", "format", "vertex_count"), &RenderingServer::mesh_surface_get_format_normal_tangent_stride);
ClassDB::bind_method(D_METHOD("mesh_surface_get_format_attribute_stride", "format", "vertex_count"), &RenderingServer::mesh_surface_get_format_attribute_stride);
ClassDB::bind_method(D_METHOD("mesh_surface_get_format_skin_stride", "format", "vertex_count"), &RenderingServer::mesh_surface_get_format_skin_stride);
ClassDB::bind_method(D_METHOD("mesh_add_surface", "mesh", "surface"), &RenderingServer::_mesh_add_surface);
@ -1835,6 +2253,15 @@ void RenderingServer::_bind_methods() {
BIND_BITFIELD_FLAG(ARRAY_FLAG_USE_8_BONE_WEIGHTS);
BIND_BITFIELD_FLAG(ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY);
BIND_BITFIELD_FLAG(ARRAY_FLAG_COMPRESS_ATTRIBUTES);
BIND_BITFIELD_FLAG(ARRAY_FLAG_FORMAT_VERSION_BASE);
BIND_BITFIELD_FLAG(ARRAY_FLAG_FORMAT_VERSION_SHIFT);
BIND_BITFIELD_FLAG(ARRAY_FLAG_FORMAT_VERSION_1);
BIND_BITFIELD_FLAG(ARRAY_FLAG_FORMAT_VERSION_2);
BIND_BITFIELD_FLAG(ARRAY_FLAG_FORMAT_CURRENT_VERSION);
BIND_BITFIELD_FLAG(ARRAY_FLAG_FORMAT_VERSION_MASK);
BIND_ENUM_CONSTANT(PRIMITIVE_POINTS);
BIND_ENUM_CONSTANT(PRIMITIVE_LINES);
BIND_ENUM_CONSTANT(PRIMITIVE_LINE_STRIP);

View file

@ -52,7 +52,7 @@ class RenderingServer : public Object {
int mm_policy = 0;
bool render_loop_enabled = true;
Array _get_array_from_surface(uint32_t p_format, Vector<uint8_t> p_vertex_data, Vector<uint8_t> p_attrib_data, Vector<uint8_t> p_skin_data, int p_vertex_len, Vector<uint8_t> p_index_data, int p_index_len) const;
Array _get_array_from_surface(uint64_t p_format, Vector<uint8_t> p_vertex_data, Vector<uint8_t> p_attrib_data, Vector<uint8_t> p_skin_data, int p_vertex_len, Vector<uint8_t> p_index_data, int p_index_len, const AABB &p_aabb) const;
const Vector2 SMALL_VEC2 = Vector2(CMP_EPSILON, CMP_EPSILON);
const Vector3 SMALL_VEC3 = Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON);
@ -66,7 +66,7 @@ protected:
RID white_texture;
RID test_material;
Error _surface_set_data(Array p_arrays, uint32_t p_format, uint32_t *p_offsets, uint32_t p_vertex_stride, uint32_t p_attrib_stride, uint32_t p_skin_stride, Vector<uint8_t> &r_vertex_array, Vector<uint8_t> &r_attrib_array, Vector<uint8_t> &r_skin_array, int p_vertex_array_len, Vector<uint8_t> &r_index_array, int p_index_array_len, AABB &r_aabb, Vector<AABB> &r_bone_aabb);
Error _surface_set_data(Array p_arrays, uint64_t p_format, uint32_t *p_offsets, uint32_t p_vertex_stride, uint32_t p_normal_stride, uint32_t p_attrib_stride, uint32_t p_skin_stride, Vector<uint8_t> &r_vertex_array, Vector<uint8_t> &r_attrib_array, Vector<uint8_t> &r_skin_array, int p_vertex_array_len, Vector<uint8_t> &r_index_array, int p_index_array_len, AABB &r_aabb, Vector<AABB> &r_bone_aabb, Vector4 &r_uv_scale);
static RenderingServer *(*create_func)();
static void _bind_methods();
@ -220,12 +220,12 @@ public:
/* MESH API */
enum ArrayType {
ARRAY_VERTEX = 0, // RG32F or RGB32F (depending on 2D bit)
ARRAY_NORMAL = 1, // A2B10G10R10, A is ignored.
ARRAY_TANGENT = 2, // A2B10G10R10, A flips sign of binormal.
ARRAY_VERTEX = 0, // RG32F (2D), RGB32F, RGBA16 (compressed)
ARRAY_NORMAL = 1, // RG16
ARRAY_TANGENT = 2, // BA16 (with normal) or A16 (with vertex, when compressed)
ARRAY_COLOR = 3, // RGBA8
ARRAY_TEX_UV = 4, // RG32F
ARRAY_TEX_UV2 = 5, // RG32F
ARRAY_TEX_UV = 4, // RG32F or RG16
ARRAY_TEX_UV2 = 5, // RG32F or RG16
ARRAY_CUSTOM0 = 6, // Depends on ArrayCustomFormat.
ARRAY_CUSTOM1 = 7,
ARRAY_CUSTOM2 = 8,
@ -252,7 +252,7 @@ public:
ARRAY_CUSTOM_MAX
};
enum ArrayFormat {
enum ArrayFormat : uint64_t {
/* ARRAY FORMAT FLAGS */
ARRAY_FORMAT_VERTEX = 1 << ARRAY_VERTEX,
ARRAY_FORMAT_NORMAL = 1 << ARRAY_NORMAL,
@ -284,7 +284,18 @@ public:
ARRAY_FLAG_USE_DYNAMIC_UPDATE = 1 << (ARRAY_COMPRESS_FLAGS_BASE + 1),
ARRAY_FLAG_USE_8_BONE_WEIGHTS = 1 << (ARRAY_COMPRESS_FLAGS_BASE + 2),
ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY = 1 << (ARRAY_INDEX + 1 + 15),
ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY = 1 << (ARRAY_COMPRESS_FLAGS_BASE + 3),
ARRAY_FLAG_COMPRESS_ATTRIBUTES = 1 << (ARRAY_COMPRESS_FLAGS_BASE + 4),
// We leave enough room for up to 5 more compression flags.
ARRAY_FLAG_FORMAT_VERSION_BASE = ARRAY_COMPRESS_FLAGS_BASE + 10,
ARRAY_FLAG_FORMAT_VERSION_SHIFT = ARRAY_FLAG_FORMAT_VERSION_BASE,
// When changes are made to the mesh format, add a new version and use it for the CURRENT_VERSION.
ARRAY_FLAG_FORMAT_VERSION_1 = 0,
ARRAY_FLAG_FORMAT_VERSION_2 = 1ULL << ARRAY_FLAG_FORMAT_VERSION_SHIFT,
ARRAY_FLAG_FORMAT_CURRENT_VERSION = ARRAY_FLAG_FORMAT_VERSION_2,
ARRAY_FLAG_FORMAT_VERSION_MASK = 0xFF, // 8 bits version
};
enum PrimitiveType {
@ -299,7 +310,7 @@ public:
struct SurfaceData {
PrimitiveType primitive = PRIMITIVE_MAX;
uint32_t format = 0;
uint64_t format = ARRAY_FLAG_FORMAT_CURRENT_VERSION;
Vector<uint8_t> vertex_data; // Vertex, Normal, Tangent (change with skinning, blendshape).
Vector<uint8_t> attribute_data; // Color, UV, UV2, Custom0-3.
Vector<uint8_t> skin_data; // Bone index, Bone weight.
@ -317,6 +328,8 @@ public:
Vector<uint8_t> blend_shape_data;
Vector4 uv_scale;
RID material;
};
@ -327,12 +340,13 @@ public:
virtual uint32_t mesh_surface_get_format_offset(BitField<ArrayFormat> p_format, int p_vertex_len, int p_array_index) const;
virtual uint32_t mesh_surface_get_format_vertex_stride(BitField<ArrayFormat> p_format, int p_vertex_len) const;
virtual uint32_t mesh_surface_get_format_normal_tangent_stride(BitField<ArrayFormat> p_format, int p_vertex_len) const;
virtual uint32_t mesh_surface_get_format_attribute_stride(BitField<ArrayFormat> p_format, int p_vertex_len) const;
virtual uint32_t mesh_surface_get_format_skin_stride(BitField<ArrayFormat> p_format, int p_vertex_len) const;
/// Returns stride
virtual void mesh_surface_make_offsets_from_format(uint32_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets, uint32_t &r_vertex_element_size, uint32_t &r_attrib_element_size, uint32_t &r_skin_element_size) const;
virtual Error mesh_create_surface_data_from_arrays(SurfaceData *r_surface_data, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), uint32_t p_compress_format = 0);
virtual void mesh_surface_make_offsets_from_format(uint64_t p_format, int p_vertex_len, int p_index_len, uint32_t *r_offsets, uint32_t &r_vertex_element_size, uint32_t &r_normal_element_size, uint32_t &r_attrib_element_size, uint32_t &r_skin_element_size) const;
virtual Error mesh_create_surface_data_from_arrays(SurfaceData *r_surface_data, PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), uint64_t p_compress_format = 0);
Array mesh_create_arrays_from_surface_data(const SurfaceData &p_data) const;
Array mesh_surface_get_arrays(RID p_mesh, int p_surface) const;
TypedArray<Array> mesh_surface_get_blend_shape_arrays(RID p_mesh, int p_surface) const;
@ -1610,6 +1624,10 @@ public:
RenderingServer();
virtual ~RenderingServer();
#ifndef DISABLE_DEPRECATED
static void _fix_surface_compatibility(SurfaceData &p_surface);
#endif
private:
// Binder helpers
RID _texture_2d_layered_create(const TypedArray<Image> &p_layers, TextureLayeredType p_layered_type);