Remove requirement to have vertex positions when creating a mesh. Meshes can now be constructed from an index buffer alone

This commit is contained in:
clayjohn 2022-06-24 11:22:26 -07:00
parent b35ff86c17
commit bbbcdd725a
10 changed files with 60 additions and 37 deletions

View File

@ -867,7 +867,6 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP);
GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, cm.matrix[2][0], cm.matrix[0][0], cm.matrix[2][1], cm.matrix[1][1], shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, cm.matrix[2][0], cm.matrix[0][0], cm.matrix[2][1], cm.matrix[1][1], shader_data->version, SkyShaderGLES3::MODE_CUBEMAP);
// Bind a vertex array or else OpenGL complains. We won't actually use it
glBindVertexArray(sky_globals.screen_triangle_array); glBindVertexArray(sky_globals.screen_triangle_array);
glViewport(0, 0, sky->radiance_size, sky->radiance_size); glViewport(0, 0, sky->radiance_size, sky->radiance_size);
@ -2150,7 +2149,9 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
index_array_gl = mesh_storage->mesh_surface_get_index_buffer(mesh_surface, surf->lod_index); index_array_gl = mesh_storage->mesh_surface_get_index_buffer(mesh_surface, surf->lod_index);
if (prev_vertex_array_gl != vertex_array_gl) { if (prev_vertex_array_gl != vertex_array_gl) {
if (vertex_array_gl != 0) {
glBindVertexArray(vertex_array_gl); glBindVertexArray(vertex_array_gl);
}
prev_vertex_array_gl = vertex_array_gl; prev_vertex_array_gl = vertex_array_gl;
} }

View File

@ -1450,8 +1450,8 @@ MaterialStorage::MaterialStorage() {
actions.renames["UV2"] = "uv2_interp"; actions.renames["UV2"] = "uv2_interp";
actions.renames["COLOR"] = "color_interp"; actions.renames["COLOR"] = "color_interp";
actions.renames["POINT_SIZE"] = "gl_PointSize"; actions.renames["POINT_SIZE"] = "gl_PointSize";
actions.renames["INSTANCE_ID"] = "gl_InstanceIndex"; actions.renames["INSTANCE_ID"] = "gl_InstanceID";
actions.renames["VERTEX_ID"] = "gl_VertexIndex"; actions.renames["VERTEX_ID"] = "gl_VertexID";
actions.renames["ALPHA_SCISSOR_THRESHOLD"] = "alpha_scissor_threshold"; actions.renames["ALPHA_SCISSOR_THRESHOLD"] = "alpha_scissor_threshold";
actions.renames["ALPHA_HASH_SCALE"] = "alpha_hash_scale"; actions.renames["ALPHA_HASH_SCALE"] = "alpha_hash_scale";

View File

@ -186,11 +186,13 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
s->format = p_surface.format; s->format = p_surface.format;
s->primitive = p_surface.primitive; s->primitive = p_surface.primitive;
if (p_surface.vertex_data.size()) {
glGenBuffers(1, &s->vertex_buffer); glGenBuffers(1, &s->vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer); glBindBuffer(GL_ARRAY_BUFFER, s->vertex_buffer);
glBufferData(GL_ARRAY_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); glBufferData(GL_ARRAY_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);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
s->vertex_buffer_size = p_surface.vertex_data.size(); s->vertex_buffer_size = p_surface.vertex_data.size();
}
if (p_surface.attribute_data.size()) { if (p_surface.attribute_data.size()) {
glGenBuffers(1, &s->attribute_buffer); glGenBuffers(1, &s->attribute_buffer);
@ -214,7 +216,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
} }
if (p_surface.index_count) { if (p_surface.index_count) {
bool is_index_16 = p_surface.vertex_count <= 65536; bool is_index_16 = p_surface.vertex_count <= 65536 && p_surface.vertex_count > 0;
glGenBuffers(1, &s->index_buffer); glGenBuffers(1, &s->index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_surface.index_data.size(), p_surface.index_data.ptr(), GL_STATIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, p_surface.index_data.size(), p_surface.index_data.ptr(), GL_STATIC_DRAW);
@ -238,6 +240,8 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
} }
} }
ERR_FAIL_COND_MSG(!p_surface.index_count && !p_surface.vertex_count, "Meshes must contain a vertex array, an index array, or both");
s->aabb = p_surface.aabb; s->aabb = p_surface.aabb;
s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them. s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them.
@ -340,7 +344,9 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const {
RS::SurfaceData sd; RS::SurfaceData sd;
sd.format = s.format; sd.format = s.format;
if (s.vertex_buffer != 0) {
sd.vertex_data = Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.vertex_buffer, s.vertex_buffer_size); sd.vertex_data = Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.vertex_buffer, s.vertex_buffer_size);
}
if (s.attribute_buffer != 0) { if (s.attribute_buffer != 0) {
sd.attribute_data = Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.attribute_buffer, s.attribute_buffer_size); sd.attribute_data = Utilities::buffer_get_data(GL_ARRAY_BUFFER, s.attribute_buffer, s.attribute_buffer_size);

View File

@ -75,7 +75,7 @@ struct Mesh {
// Cache vertex arrays so they can be created // Cache vertex arrays so they can be created
struct Version { struct Version {
uint32_t input_mask = 0; uint32_t input_mask = 0;
GLuint vertex_array; GLuint vertex_array = 0;
Attrib attribs[RS::ARRAY_MAX]; Attrib attribs[RS::ARRAY_MAX];
}; };
@ -92,7 +92,7 @@ struct Mesh {
float edge_length = 0.0; float edge_length = 0.0;
uint32_t index_count = 0; uint32_t index_count = 0;
uint32_t index_buffer_size = 0; uint32_t index_buffer_size = 0;
GLuint index_buffer; GLuint index_buffer = 0;
}; };
LOD *lods = nullptr; LOD *lods = nullptr;
@ -175,7 +175,7 @@ struct MultiMesh {
bool *data_cache_dirty_regions = nullptr; bool *data_cache_dirty_regions = nullptr;
uint32_t data_cache_used_dirty_regions = 0; uint32_t data_cache_used_dirty_regions = 0;
GLuint buffer; GLuint buffer = 0;
bool dirty = false; bool dirty = false;
MultiMesh *dirty_list = nullptr; MultiMesh *dirty_list = nullptr;
@ -362,7 +362,7 @@ public:
_FORCE_INLINE_ GLenum mesh_surface_get_index_type(void *p_surface) const { _FORCE_INLINE_ GLenum mesh_surface_get_index_type(void *p_surface) const {
Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface);
return s->vertex_count <= 65536 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; return (s->vertex_count <= 65536 && s->vertex_count > 0) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
} }
// Use this to cache Vertex Array Objects so they are only generated once // Use this to cache Vertex Array Objects so they are only generated once

View File

@ -7805,12 +7805,6 @@ void RenderingDeviceVulkan::draw_list_draw(DrawListID p_list, bool p_use_indices
ERR_FAIL_COND_MSG(!dl->validation.index_array_size, ERR_FAIL_COND_MSG(!dl->validation.index_array_size,
"Draw command requested indices, but no index buffer was set."); "Draw command requested indices, but no index buffer was set.");
if (dl->validation.pipeline_vertex_format != INVALID_ID) {
// Uses vertices, do some vertex validations.
ERR_FAIL_COND_MSG(dl->validation.vertex_array_size < dl->validation.index_array_max_index,
"Index array references (max index: " + itos(dl->validation.index_array_max_index) + ") indices beyond the vertex array size (" + itos(dl->validation.vertex_array_size) + ").");
}
ERR_FAIL_COND_MSG(dl->validation.pipeline_uses_restart_indices != dl->validation.index_buffer_uses_restart_indices, ERR_FAIL_COND_MSG(dl->validation.pipeline_uses_restart_indices != dl->validation.index_buffer_uses_restart_indices,
"The usage of restart indices in index buffer does not match the render primitive in the pipeline."); "The usage of restart indices in index buffer does not match the render primitive in the pipeline.");
#endif #endif

View File

@ -201,7 +201,9 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
continue; continue;
} }
int len = (surface_get_format(i) & ARRAY_FORMAT_INDEX) ? surface_get_array_index_len(i) : surface_get_array_len(i); int len = (surface_get_format(i) & ARRAY_FORMAT_INDEX) ? surface_get_array_index_len(i) : surface_get_array_len(i);
if ((primitive == PRIMITIVE_TRIANGLES && (len == 0 || (len % 3) != 0)) || (primitive == PRIMITIVE_TRIANGLE_STRIP && len < 3)) { if ((primitive == PRIMITIVE_TRIANGLES && (len == 0 || (len % 3) != 0)) ||
(primitive == PRIMITIVE_TRIANGLE_STRIP && len < 3) ||
(surface_get_format(i) & ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY)) {
// Error was already shown, just skip (including zero). // Error was already shown, just skip (including zero).
continue; continue;
} }
@ -211,6 +213,7 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
int vc = surface_get_array_len(i); int vc = surface_get_array_len(i);
Vector<Vector3> vertices = a[ARRAY_VERTEX]; Vector<Vector3> vertices = a[ARRAY_VERTEX];
ERR_FAIL_COND_V(vertices.is_empty(), Ref<TriangleMesh>());
const Vector3 *vr = vertices.ptr(); const Vector3 *vr = vertices.ptr();
int32_t from_index = widx / 3; int32_t from_index = widx / 3;

View File

@ -144,6 +144,7 @@ public:
ARRAY_FLAG_USE_DYNAMIC_UPDATE = RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE, ARRAY_FLAG_USE_DYNAMIC_UPDATE = RS::ARRAY_FLAG_USE_DYNAMIC_UPDATE,
ARRAY_FLAG_USE_8_BONE_WEIGHTS = RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS, 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,
}; };
virtual int get_surface_count() const; virtual int get_surface_count() const;

View File

@ -327,8 +327,10 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
bool use_as_storage = (p_surface.skin_data.size() || mesh->blend_shape_count > 0); bool use_as_storage = (p_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 = 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(); s->vertex_buffer_size = p_surface.vertex_data.size();
}
if (p_surface.attribute_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); s->attribute_buffer = RD::get_singleton()->vertex_buffer_create(p_surface.attribute_data.size(), p_surface.attribute_data);
@ -345,7 +347,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
} }
if (p_surface.index_count) { if (p_surface.index_count) {
bool is_index_16 = p_surface.vertex_count <= 65536; bool is_index_16 = p_surface.vertex_count <= 65536 && p_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_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_count = p_surface.index_count;
@ -364,6 +366,8 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
} }
} }
ERR_FAIL_COND_MSG(!p_surface.index_count && !p_surface.vertex_count, "Meshes must contain a vertex array, an index array, or both");
s->aabb = p_surface.aabb; s->aabb = p_surface.aabb;
s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them. s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them.
@ -377,7 +381,11 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface)
RD::Uniform u; RD::Uniform u;
u.binding = 0; u.binding = 0;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
if (s->vertex_buffer.is_valid()) {
u.append_id(s->vertex_buffer); u.append_id(s->vertex_buffer);
} else {
u.append_id(default_rd_storage_buffer);
}
uniforms.push_back(u); uniforms.push_back(u);
} }
{ {
@ -470,6 +478,7 @@ void MeshStorage::mesh_surface_update_vertex_region(RID p_mesh, int p_surface, i
ERR_FAIL_COND(!mesh); ERR_FAIL_COND(!mesh);
ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count);
ERR_FAIL_COND(p_data.size() == 0); ERR_FAIL_COND(p_data.size() == 0);
ERR_FAIL_COND(mesh->surfaces[p_surface]->vertex_buffer.is_null());
uint64_t data_size = p_data.size(); uint64_t data_size = p_data.size();
const uint8_t *r = p_data.ptr(); const uint8_t *r = p_data.ptr();
@ -527,7 +536,9 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const {
RS::SurfaceData sd; RS::SurfaceData sd;
sd.format = s.format; sd.format = s.format;
if (s.vertex_buffer.is_valid()) {
sd.vertex_data = RD::get_singleton()->buffer_get_data(s.vertex_buffer); sd.vertex_data = RD::get_singleton()->buffer_get_data(s.vertex_buffer);
}
if (s.attribute_buffer.is_valid()) { if (s.attribute_buffer.is_valid()) {
sd.attribute_data = RD::get_singleton()->buffer_get_data(s.attribute_buffer); sd.attribute_data = RD::get_singleton()->buffer_get_data(s.attribute_buffer);
} }
@ -705,7 +716,9 @@ void MeshStorage::mesh_clear(RID p_mesh) {
ERR_FAIL_COND(!mesh); ERR_FAIL_COND(!mesh);
for (uint32_t i = 0; i < mesh->surface_count; i++) { for (uint32_t i = 0; i < mesh->surface_count; i++) {
Mesh::Surface &s = *mesh->surfaces[i]; Mesh::Surface &s = *mesh->surfaces[i];
if (s.vertex_buffer.is_valid()) {
RD::get_singleton()->free(s.vertex_buffer); //clears arrays as dependency automatically, including all versions RD::get_singleton()->free(s.vertex_buffer); //clears arrays as dependency automatically, including all versions
}
if (s.attribute_buffer.is_valid()) { if (s.attribute_buffer.is_valid()) {
RD::get_singleton()->free(s.attribute_buffer); RD::get_singleton()->free(s.attribute_buffer);
} }
@ -851,7 +864,7 @@ void MeshStorage::_mesh_instance_add_surface(MeshInstance *mi, Mesh *mesh, uint3
} }
MeshInstance::Surface s; MeshInstance::Surface s;
if (mesh->blend_shape_count > 0 || (mesh->surfaces[p_surface]->format & RS::ARRAY_FORMAT_BONES)) { if ((mesh->blend_shape_count > 0 || (mesh->surfaces[p_surface]->format & RS::ARRAY_FORMAT_BONES)) && mesh->surfaces[p_surface]->vertex_buffer_size > 0) {
//surface warrants transform //surface warrants transform
s.vertex_buffer = RD::get_singleton()->vertex_buffer_create(mesh->surfaces[p_surface]->vertex_buffer_size, Vector<uint8_t>(), true); s.vertex_buffer = RD::get_singleton()->vertex_buffer_create(mesh->surfaces[p_surface]->vertex_buffer_size, Vector<uint8_t>(), true);

View File

@ -627,7 +627,7 @@ Error RenderingServer::_surface_set_data(Array p_arrays, uint32_t p_format, uint
const int *src = indices.ptr(); const int *src = indices.ptr();
for (int i = 0; i < p_index_array_len; i++) { for (int i = 0; i < p_index_array_len; i++) {
if (p_vertex_array_len < (1 << 16)) { if (p_vertex_array_len < (1 << 16) && p_vertex_array_len > 0) {
uint16_t v = src[i]; uint16_t v = src[i];
memcpy(&iw[i * 2], &v, 2); memcpy(&iw[i * 2], &v, 2);
@ -836,9 +836,8 @@ void RenderingServer::mesh_surface_make_offsets_from_format(uint32_t p_format, i
break; break;
} }
/* determine whether using 16 or 32 bits indices */ /* determine whether using 16 or 32 bits indices */
if (p_vertex_len >= (1 << 16)) { if (p_vertex_len >= (1 << 16) || p_vertex_len == 0) {
elem_size = 4; elem_size = 4;
} else { } else {
elem_size = 2; elem_size = 2;
} }
@ -909,8 +908,6 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
} }
} }
ERR_FAIL_COND_V((format & RS::ARRAY_FORMAT_VERTEX) == 0, ERR_INVALID_PARAMETER); // Mandatory
if (p_blend_shapes.size()) { if (p_blend_shapes.size()) {
// Validate format for morphs. // Validate format for morphs.
for (int i = 0; i < p_blend_shapes.size(); i++) { for (int i = 0; i < p_blend_shapes.size(); i++) {
@ -944,6 +941,12 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
uint32_t mask = (1 << ARRAY_MAX) - 1; uint32_t mask = (1 << ARRAY_MAX) - 1;
format |= (~mask) & p_compress_format; // Make the full format. format |= (~mask) & p_compress_format; // Make the full format.
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; int vertex_array_size = vertex_element_size * array_len;
int attrib_array_size = attrib_element_size * array_len; int attrib_array_size = attrib_element_size * array_len;
int skin_array_size = skin_element_size * array_len; int skin_array_size = skin_element_size * array_len;
@ -1378,7 +1381,7 @@ Array RenderingServer::mesh_create_arrays_from_surface_data(const SurfaceData &p
Vector<uint8_t> attrib_data = p_data.attribute_data; Vector<uint8_t> attrib_data = p_data.attribute_data;
Vector<uint8_t> skin_data = p_data.skin_data; Vector<uint8_t> skin_data = p_data.skin_data;
ERR_FAIL_COND_V(vertex_data.size() == 0, Array()); ERR_FAIL_COND_V(vertex_data.size() == 0 && (p_data.format & RS::ARRAY_FORMAT_VERTEX), Array());
int vertex_len = p_data.vertex_count; int vertex_len = p_data.vertex_count;
Vector<uint8_t> index_data = p_data.index_data; Vector<uint8_t> index_data = p_data.index_data;

View File

@ -244,7 +244,7 @@ public:
enum ArrayFormat { enum ArrayFormat {
/* ARRAY FORMAT FLAGS */ /* ARRAY FORMAT FLAGS */
ARRAY_FORMAT_VERTEX = 1 << ARRAY_VERTEX, // Mandatory ARRAY_FORMAT_VERTEX = 1 << ARRAY_VERTEX,
ARRAY_FORMAT_NORMAL = 1 << ARRAY_NORMAL, ARRAY_FORMAT_NORMAL = 1 << ARRAY_NORMAL,
ARRAY_FORMAT_TANGENT = 1 << ARRAY_TANGENT, ARRAY_FORMAT_TANGENT = 1 << ARRAY_TANGENT,
ARRAY_FORMAT_COLOR = 1 << ARRAY_COLOR, ARRAY_FORMAT_COLOR = 1 << ARRAY_COLOR,
@ -262,17 +262,19 @@ public:
ARRAY_FORMAT_CUSTOM_BASE = (ARRAY_INDEX + 1), ARRAY_FORMAT_CUSTOM_BASE = (ARRAY_INDEX + 1),
ARRAY_FORMAT_CUSTOM_BITS = 3, ARRAY_FORMAT_CUSTOM_BITS = 3,
ARRAY_FORMAT_CUSTOM_MASK = 0x7,
ARRAY_FORMAT_CUSTOM0_SHIFT = (ARRAY_FORMAT_CUSTOM_BASE + 0), ARRAY_FORMAT_CUSTOM0_SHIFT = (ARRAY_FORMAT_CUSTOM_BASE + 0),
ARRAY_FORMAT_CUSTOM1_SHIFT = (ARRAY_FORMAT_CUSTOM_BASE + ARRAY_FORMAT_CUSTOM_BITS), ARRAY_FORMAT_CUSTOM1_SHIFT = (ARRAY_FORMAT_CUSTOM_BASE + ARRAY_FORMAT_CUSTOM_BITS),
ARRAY_FORMAT_CUSTOM2_SHIFT = (ARRAY_FORMAT_CUSTOM_BASE + ARRAY_FORMAT_CUSTOM_BITS * 2), ARRAY_FORMAT_CUSTOM2_SHIFT = (ARRAY_FORMAT_CUSTOM_BASE + ARRAY_FORMAT_CUSTOM_BITS * 2),
ARRAY_FORMAT_CUSTOM3_SHIFT = (ARRAY_FORMAT_CUSTOM_BASE + ARRAY_FORMAT_CUSTOM_BITS * 3), ARRAY_FORMAT_CUSTOM3_SHIFT = (ARRAY_FORMAT_CUSTOM_BASE + ARRAY_FORMAT_CUSTOM_BITS * 3),
ARRAY_FORMAT_CUSTOM_MASK = 0x7,
ARRAY_COMPRESS_FLAGS_BASE = (ARRAY_INDEX + 1 + 12), ARRAY_COMPRESS_FLAGS_BASE = (ARRAY_INDEX + 1 + 12),
ARRAY_FLAG_USE_2D_VERTICES = 1 << (ARRAY_COMPRESS_FLAGS_BASE + 0), ARRAY_FLAG_USE_2D_VERTICES = 1 << (ARRAY_COMPRESS_FLAGS_BASE + 0),
ARRAY_FLAG_USE_DYNAMIC_UPDATE = 1 << (ARRAY_COMPRESS_FLAGS_BASE + 1), 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_USE_8_BONE_WEIGHTS = 1 << (ARRAY_COMPRESS_FLAGS_BASE + 2),
ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY = 1 << (ARRAY_INDEX + 1 + 15),
}; };
enum PrimitiveType { enum PrimitiveType {