pandemonium_engine_minimal/drivers/gles2/rasterizer_scene_gles2.cpp
2023-12-15 22:37:50 +01:00

1193 lines
43 KiB
C++

/*************************************************************************/
/* rasterizer_scene_gles2.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#include "rasterizer_scene_gles2.h"
#include "core/config/project_settings.h"
#include "core/containers/vmap.h"
#include "core/math/math_funcs.h"
#include "core/math/transform.h"
#include "core/os/os.h"
#include "rasterizer_canvas_gles2.h"
#include "servers/rendering/rendering_server_raster.h"
#ifndef GLES_OVER_GL
#define glClearDepth glClearDepthf
#endif
#ifndef GLES_OVER_GL
#ifdef IPHONE_ENABLED
#include <OpenGLES/ES2/glext.h>
//void *glResolveMultisampleFramebufferAPPLE;
#define GL_READ_FRAMEBUFFER 0x8CA8
#define GL_DRAW_FRAMEBUFFER 0x8CA9
#endif
#endif
////////////////////////////
void RasterizerSceneGLES2::_add_geometry(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, int p_material, bool p_depth_pass) {
RasterizerStorageGLES2::Material *material = nullptr;
RID material_src;
if (p_instance->material_override.is_valid()) {
material_src = p_instance->material_override;
} else if (p_material >= 0) {
material_src = p_instance->materials[p_material];
} else {
material_src = p_geometry->material;
}
if (material_src.is_valid()) {
material = storage->material_owner.getornull(material_src);
if (!material->shader || !material->shader->valid) {
material = nullptr;
}
}
if (!material) {
material = storage->material_owner.getptr(default_material);
}
ERR_FAIL_COND(!material);
_add_geometry_with_material(p_geometry, p_instance, p_owner, material, p_depth_pass);
while (material->next_pass.is_valid()) {
material = storage->material_owner.getornull(material->next_pass);
if (!material || !material->shader || !material->shader->valid) {
break;
}
_add_geometry_with_material(p_geometry, p_instance, p_owner, material, p_depth_pass);
}
// Repeat the "nested chain" logic also for the overlay
if (p_instance->material_overlay.is_valid()) {
material = storage->material_owner.getornull(p_instance->material_overlay);
if (!material || !material->shader || !material->shader->valid) {
return;
}
_add_geometry_with_material(p_geometry, p_instance, p_owner, material, p_depth_pass);
while (material->next_pass.is_valid()) {
material = storage->material_owner.getornull(material->next_pass);
if (!material || !material->shader || !material->shader->valid) {
break;
}
_add_geometry_with_material(p_geometry, p_instance, p_owner, material, p_depth_pass);
}
}
}
void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, RasterizerStorageGLES2::Material *p_material, bool p_depth_pass) {
bool has_base_alpha = (p_material->shader->spatial.uses_alpha && !p_material->shader->spatial.uses_alpha_scissor) || p_material->shader->spatial.uses_screen_texture || p_material->shader->spatial.uses_depth_texture;
bool has_blend_alpha = p_material->shader->spatial.blend_mode != RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_MIX;
bool has_alpha = has_base_alpha || has_blend_alpha;
bool mirror = p_instance->mirror;
if (p_material->shader->spatial.cull_mode == RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_DISABLED) {
mirror = false;
} else if (p_material->shader->spatial.cull_mode == RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_FRONT) {
mirror = !mirror;
}
//if (p_material->shader->spatial.uses_sss) {
// state.used_sss = true;
//}
if (p_material->shader->spatial.uses_screen_texture) {
state.used_screen_texture = true;
}
if (p_depth_pass) {
if (has_blend_alpha || p_material->shader->spatial.uses_depth_texture || (has_base_alpha && p_material->shader->spatial.depth_draw_mode != RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS)) {
return; //bye
}
if (!p_material->shader->spatial.uses_alpha_scissor && !p_material->shader->spatial.writes_modelview_or_projection && !p_material->shader->spatial.uses_vertex && !p_material->shader->spatial.uses_discard && p_material->shader->spatial.depth_draw_mode != RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS) {
//shader does not use discard and does not write a vertex position, use generic material
p_material = storage->material_owner.getptr(p_material->shader->spatial.uses_world_coordinates ? default_worldcoord_material : default_material);
}
has_alpha = false;
}
RenderList::Element *e = (has_alpha || p_material->shader->spatial.no_depth_test) ? render_list.add_alpha_element() : render_list.add_element();
if (!e) {
return;
}
e->geometry = p_geometry;
e->material = p_material;
e->instance = p_instance;
e->owner = p_owner;
e->sort_key = 0;
e->depth_key = 0;
e->use_accum = false;
e->light_index = RenderList::MAX_LIGHTS;
e->use_accum_ptr = &e->use_accum;
e->instancing = (e->instance->base_type == RS::INSTANCE_MULTIMESH) ? 1 : 0;
e->front_facing = false;
if (e->geometry->last_pass != render_pass) {
e->geometry->last_pass = render_pass;
e->geometry->index = current_geometry_index++;
}
e->geometry_index = e->geometry->index;
if (e->material->last_pass != render_pass) {
e->material->last_pass = render_pass;
e->material->index = current_material_index++;
if (e->material->shader->last_pass != render_pass) {
e->material->shader->index = current_shader_index++;
}
}
e->material_index = e->material->index;
if (mirror) {
e->front_facing = true;
}
if (!p_depth_pass) {
e->depth_layer = e->instance->depth_layer;
e->priority = p_material->render_priority;
if (has_alpha && p_material->shader->spatial.depth_draw_mode == RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS) {
//add element to opaque
RenderList::Element *eo = render_list.add_element();
*eo = *e;
eo->use_accum_ptr = &eo->use_accum;
}
//add directional lights
e->light_mode = LIGHTMODE_UNSHADED;
}
// do not add anything here, as lights are duplicated elements..
if (p_material->shader->spatial.uses_time) {
RenderingServerRaster::redraw_request(false);
}
}
void RasterizerSceneGLES2::_copy_texture_to_buffer(GLuint p_texture, GLuint p_buffer) {
//copy to front buffer
glBindFramebuffer(GL_FRAMEBUFFER, p_buffer);
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_BLEND);
glDepthFunc(GL_LEQUAL);
glColorMask(1, 1, 1, 1);
WRAPPED_GL_ACTIVE_TEXTURE(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, p_texture);
glViewport(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height);
storage->shaders.copy.bind();
storage->bind_quad_array();
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void RasterizerSceneGLES2::_fill_render_list(InstanceBase **p_cull_result, int p_cull_count, bool p_depth_pass) {
render_pass++;
current_material_index = 0;
current_geometry_index = 0;
current_shader_index = 0;
for (int i = 0; i < p_cull_count; i++) {
InstanceBase *instance = p_cull_result[i];
switch (instance->base_type) {
case RS::INSTANCE_MESH: {
RasterizerStorageGLES2::Mesh *mesh = storage->mesh_owner.getornull(instance->base);
ERR_CONTINUE(!mesh);
int num_surfaces = mesh->surfaces.size();
for (int j = 0; j < num_surfaces; j++) {
int material_index = instance->materials[j].is_valid() ? j : -1;
RasterizerStorageGLES2::Surface *surface = mesh->surfaces[j];
_add_geometry(surface, instance, nullptr, material_index, p_depth_pass);
}
} break;
case RS::INSTANCE_MULTIMESH: {
RasterizerStorageGLES2::MultiMesh *multi_mesh = storage->multimesh_owner.getptr(instance->base);
ERR_CONTINUE(!multi_mesh);
if (multi_mesh->size == 0 || multi_mesh->visible_instances == 0) {
continue;
}
RasterizerStorageGLES2::Mesh *mesh = storage->mesh_owner.getptr(multi_mesh->mesh);
if (!mesh) {
continue;
}
int ssize = mesh->surfaces.size();
for (int j = 0; j < ssize; j++) {
RasterizerStorageGLES2::Surface *s = mesh->surfaces[j];
_add_geometry(s, instance, multi_mesh, -1, p_depth_pass);
}
} break;
default: {
}
}
}
}
const GLenum RasterizerSceneGLES2::gl_primitive[] = {
GL_POINTS,
GL_LINES,
GL_LINE_STRIP,
GL_LINE_LOOP,
GL_TRIANGLES,
GL_TRIANGLE_STRIP,
GL_TRIANGLE_FAN
};
void RasterizerSceneGLES2::_set_cull(bool p_front, bool p_disabled, bool p_reverse_cull) {
bool front = p_front;
if (p_reverse_cull) {
front = !front;
}
if (p_disabled != state.cull_disabled) {
if (p_disabled) {
glDisable(GL_CULL_FACE);
} else {
glEnable(GL_CULL_FACE);
}
state.cull_disabled = p_disabled;
}
if (front != state.cull_front) {
glCullFace(front ? GL_FRONT : GL_BACK);
state.cull_front = front;
}
}
bool RasterizerSceneGLES2::_setup_material(RasterizerStorageGLES2::Material *p_material, bool p_alpha_pass, Size2i p_skeleton_tex_size) {
// material parameters
state.scene_shader.set_custom_shader(p_material->shader->custom_code_id);
if (p_material->shader->spatial.uses_screen_texture && storage->frame.current_rt) {
WRAPPED_GL_ACTIVE_TEXTURE(GL_TEXTURE0 + storage->config.max_texture_image_units - 4);
glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->copy_screen_effect.color);
}
if (p_material->shader->spatial.uses_depth_texture && storage->frame.current_rt) {
WRAPPED_GL_ACTIVE_TEXTURE(GL_TEXTURE0 + storage->config.max_texture_image_units - 4);
glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->depth);
}
bool shader_rebind = state.scene_shader.bind();
if (p_material->shader->spatial.no_depth_test || p_material->shader->spatial.uses_depth_texture) {
glDisable(GL_DEPTH_TEST);
} else {
glEnable(GL_DEPTH_TEST);
}
switch (p_material->shader->spatial.depth_draw_mode) {
case RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS:
case RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_OPAQUE: {
glDepthMask(!p_alpha_pass && !p_material->shader->spatial.uses_depth_texture);
} break;
case RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALWAYS: {
glDepthMask(GL_TRUE && !p_material->shader->spatial.uses_depth_texture);
} break;
case RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_NEVER: {
glDepthMask(GL_FALSE);
} break;
}
int tc = p_material->textures.size();
const Pair<StringName, RID> *textures = p_material->textures.ptr();
const ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = p_material->shader->texture_hints.ptr();
state.scene_shader.set_uniform(SceneShaderGLES2::SKELETON_TEXTURE_SIZE, p_skeleton_tex_size);
state.current_main_tex = 0;
for (int i = 0; i < tc; i++) {
WRAPPED_GL_ACTIVE_TEXTURE(GL_TEXTURE0 + i);
RasterizerStorageGLES2::Texture *t = storage->texture_owner.getornull(textures[i].second);
if (!t) {
switch (texture_hints[i]) {
case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO:
case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: {
glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex);
} break;
case ShaderLanguage::ShaderNode::Uniform::HINT_TRANSPARENT: {
glBindTexture(GL_TEXTURE_2D, storage->resources.transparent_tex);
} break;
case ShaderLanguage::ShaderNode::Uniform::HINT_ANISO: {
glBindTexture(GL_TEXTURE_2D, storage->resources.aniso_tex);
} break;
case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: {
glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex);
} break;
default: {
glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex);
} break;
}
continue;
}
if (t->redraw_if_visible) { //must check before proxy because this is often used with proxies
RenderingServerRaster::redraw_request(false);
}
t = t->get_ptr();
#ifdef TOOLS_ENABLED
if (t->detect_3d) {
t->detect_3d(t->detect_3d_ud);
}
#endif
#ifdef TOOLS_ENABLED
if (t->detect_normal && texture_hints[i] == ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL) {
t->detect_normal(t->detect_normal_ud);
}
#endif
if (t->render_target) {
t->render_target->used_in_frame = true;
}
glBindTexture(t->target, t->tex_id);
if (i == 0) {
state.current_main_tex = t->tex_id;
}
}
state.scene_shader.use_material((void *)p_material);
return shader_rebind;
}
void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element) {
switch (p_element->instance->base_type) {
case RS::INSTANCE_MESH: {
RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
if (s->index_array_len > 0) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_id);
}
for (int i = 0; i < RS::ARRAY_MAX - 1; i++) {
if (s->attribs[i].enabled) {
glEnableVertexAttribArray(i);
if (!s->blend_shape_data.empty() && s->blend_shape_buffer_size > 0) {
glBindBuffer(GL_ARRAY_BUFFER, s->blend_shape_buffer_id);
// When using octahedral compression (2 component normal/tangent)
// decompression changes the component count to 3/4
int size;
switch (i) {
case RS::ARRAY_NORMAL: {
size = 3;
} break;
case RS::ARRAY_TANGENT: {
size = 4;
} break;
default:
size = s->attribs[i].size;
}
glVertexAttribPointer(s->attribs[i].index, size, GL_FLOAT, GL_FALSE, 8 * 4 * sizeof(float), CAST_INT_TO_UCHAR_PTR(i * 4 * sizeof(float)));
} else {
glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id);
glVertexAttribPointer(s->attribs[i].index, s->attribs[i].size, s->attribs[i].type, s->attribs[i].normalized, s->attribs[i].stride, CAST_INT_TO_UCHAR_PTR(s->attribs[i].offset));
}
} else {
glDisableVertexAttribArray(i);
switch (i) {
case RS::ARRAY_NORMAL: {
glVertexAttrib4f(RS::ARRAY_NORMAL, 0.0, 0.0, 1, 1);
} break;
case RS::ARRAY_COLOR: {
glVertexAttrib4f(RS::ARRAY_COLOR, 1, 1, 1, 1);
} break;
default: {
}
}
}
}
} break;
case RS::INSTANCE_MULTIMESH: {
RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
glBindBuffer(GL_ARRAY_BUFFER, s->vertex_id);
if (s->index_array_len > 0) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_id);
}
for (int i = 0; i < RS::ARRAY_MAX - 1; i++) {
if (s->attribs[i].enabled) {
glEnableVertexAttribArray(i);
glVertexAttribPointer(s->attribs[i].index, s->attribs[i].size, s->attribs[i].type, s->attribs[i].normalized, s->attribs[i].stride, CAST_INT_TO_UCHAR_PTR(s->attribs[i].offset));
} else {
glDisableVertexAttribArray(i);
switch (i) {
case RS::ARRAY_NORMAL: {
glVertexAttrib4f(RS::ARRAY_NORMAL, 0.0, 0.0, 1, 1);
} break;
case RS::ARRAY_COLOR: {
glVertexAttrib4f(RS::ARRAY_COLOR, 1, 1, 1, 1);
} break;
default: {
}
}
}
}
// prepare multimesh (disable)
glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 0);
glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 1);
glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 2);
glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 3);
glDisableVertexAttribArray(INSTANCE_ATTRIB_BASE + 4);
glDisableVertexAttribArray(INSTANCE_BONE_BASE + 0);
glDisableVertexAttribArray(INSTANCE_BONE_BASE + 1);
glDisableVertexAttribArray(INSTANCE_BONE_BASE + 2);
} break;
default: {
}
}
}
void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) {
switch (p_element->instance->base_type) {
case RS::INSTANCE_MESH: {
RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
// drawing
if (s->index_array_len > 0) {
glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, nullptr);
storage->info.render.vertices_count += s->index_array_len;
} else {
glDrawArrays(gl_primitive[s->primitive], 0, s->array_len);
storage->info.render.vertices_count += s->array_len;
}
} break;
case RS::INSTANCE_MULTIMESH: {
RasterizerStorageGLES2::MultiMesh *multi_mesh = static_cast<RasterizerStorageGLES2::MultiMesh *>(p_element->owner);
RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
int amount = MIN(multi_mesh->size, multi_mesh->visible_instances);
if (amount == -1) {
amount = multi_mesh->size;
}
if (!amount) {
return;
}
int stride = multi_mesh->color_floats + multi_mesh->custom_data_floats + multi_mesh->xform_floats;
int color_ofs = multi_mesh->xform_floats;
int custom_data_ofs = color_ofs + multi_mesh->color_floats;
// drawing
const float *base_buffer = multi_mesh->data.ptr();
for (int i = 0; i < amount; i++) {
const float *buffer = base_buffer + i * stride;
{
glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 0, &buffer[0]);
glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 1, &buffer[4]);
glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 2, &buffer[8]);
}
if (multi_mesh->color_floats) {
if (multi_mesh->color_format == RS::MULTIMESH_COLOR_8BIT) {
uint8_t *color_data = (uint8_t *)(buffer + color_ofs);
glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 3, color_data[0] / 255.0, color_data[1] / 255.0, color_data[2] / 255.0, color_data[3] / 255.0);
} else {
glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 3, buffer + color_ofs);
}
} else {
glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 3, 1.0, 1.0, 1.0, 1.0);
}
if (multi_mesh->custom_data_floats) {
if (multi_mesh->custom_data_format == RS::MULTIMESH_CUSTOM_DATA_8BIT) {
uint8_t *custom_data = (uint8_t *)(buffer + custom_data_ofs);
glVertexAttrib4f(INSTANCE_ATTRIB_BASE + 4, custom_data[0] / 255.0, custom_data[1] / 255.0, custom_data[2] / 255.0, custom_data[3] / 255.0);
} else {
glVertexAttrib4fv(INSTANCE_ATTRIB_BASE + 4, buffer + custom_data_ofs);
}
}
if (s->index_array_len > 0) {
glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, nullptr);
storage->info.render.vertices_count += s->index_array_len;
} else {
glDrawArrays(gl_primitive[s->primitive], 0, s->array_len);
storage->info.render.vertices_count += s->array_len;
}
}
} break;
default: {
}
}
}
void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, int p_element_count, const Transform &p_view_transform, const Projection &p_projection, const int p_eye, bool p_reverse_cull, bool p_alpha_pass) {
Vector2 viewport_size = state.viewport_size;
Vector2 screen_pixel_size = state.screen_pixel_size;
bool use_radiance_map = false;
bool prev_unshaded = false;
bool prev_instancing = false;
bool prev_depth_prepass = false;
state.scene_shader.set_conditional(SceneShaderGLES2::SHADELESS, false);
RasterizerStorageGLES2::Material *prev_material = nullptr;
RasterizerStorageGLES2::Geometry *prev_geometry = nullptr;
RasterizerStorageGLES2::GeometryOwner *prev_owner = nullptr;
Transform view_transform_inverse = p_view_transform.inverse();
Projection projection_inverse = p_projection.inverse();
bool prev_base_pass = false;
bool prev_vertex_lit = false;
int prev_blend_mode = -2; //will always catch the first go
state.cull_front = false;
state.cull_disabled = false;
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
if (p_alpha_pass) {
glEnable(GL_BLEND);
} else {
glDisable(GL_BLEND);
}
bool using_fog = false;
storage->info.render.draw_call_count += p_element_count;
for (int i = 0; i < p_element_count; i++) {
RenderList::Element *e = p_elements[i];
RasterizerStorageGLES2::Material *material = e->material;
bool rebind = false;
bool accum_pass = *e->use_accum_ptr;
*e->use_accum_ptr = true; //set to accum for next time this is found
if (material->shader) {
bool unshaded = material->shader->spatial.unshaded;
if (unshaded != prev_unshaded) {
rebind = true;
if (unshaded) {
state.scene_shader.set_conditional(SceneShaderGLES2::SHADELESS, true);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTING, false);
} else {
state.scene_shader.set_conditional(SceneShaderGLES2::SHADELESS, false);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, use_radiance_map);
}
prev_unshaded = unshaded;
}
bool base_pass = !accum_pass && !unshaded; //conditions for a base pass
if (base_pass != prev_base_pass) {
state.scene_shader.set_conditional(SceneShaderGLES2::BASE_PASS, base_pass);
rebind = true;
prev_base_pass = base_pass;
}
int blend_mode = p_alpha_pass ? material->shader->spatial.blend_mode : -1; // -1 no blend, no mix
if (prev_blend_mode != blend_mode) {
if (prev_blend_mode == -1 && blend_mode != -1) {
//does blend
glEnable(GL_BLEND);
} else if (blend_mode == -1 && prev_blend_mode != -1) {
//do not blend
glDisable(GL_BLEND);
}
switch (blend_mode) {
//-1 not handled because not blend is enabled anyway
case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_MIX: {
glBlendEquation(GL_FUNC_ADD);
if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
} else {
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
}
} break;
case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_ADD: {
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(p_alpha_pass ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
} break;
case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_SUB: {
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
} break;
case RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_MUL: {
glBlendEquation(GL_FUNC_ADD);
if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_DST_ALPHA, GL_ZERO);
} else {
glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE);
}
} break;
}
prev_blend_mode = blend_mode;
}
//condition to enable vertex lighting on this object
bool vertex_lit = (material->shader->spatial.uses_vertex_lighting || storage->config.force_vertex_shading) && ((!unshaded) || using_fog); //fog forces vertex lighting because it still applies even if unshaded or no fog
if (vertex_lit != prev_vertex_lit) {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_VERTEX_LIGHTING, vertex_lit);
prev_vertex_lit = vertex_lit;
rebind = true;
}
}
bool depth_prepass = false;
if (!p_alpha_pass && material->shader->spatial.depth_draw_mode == RasterizerStorageGLES2::Shader::Spatial::DEPTH_DRAW_ALPHA_PREPASS) {
depth_prepass = true;
}
if (depth_prepass != prev_depth_prepass) {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_DEPTH_PREPASS, depth_prepass);
prev_depth_prepass = depth_prepass;
rebind = true;
}
bool instancing = e->instance->base_type == RS::INSTANCE_MULTIMESH;
if (instancing != prev_instancing) {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, instancing);
rebind = true;
}
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, false);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, false);
if (e->owner != prev_owner || e->geometry != prev_geometry) {
_setup_geometry(e);
storage->info.render.surface_switch_count++;
}
state.scene_shader.set_conditional(SceneShaderGLES2::USE_PHYSICAL_LIGHT_ATTENUATION, storage->config.use_physical_light_attenuation);
bool shader_rebind = false;
if (rebind || material != prev_material) {
storage->info.render.material_switch_count++;
shader_rebind = _setup_material(material, p_alpha_pass, Size2i(0, 0));
if (shader_rebind) {
storage->info.render.shader_rebind_count++;
}
}
_set_cull(e->front_facing, material->shader->spatial.cull_mode == RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_DISABLED, p_reverse_cull);
if (i == 0 || shader_rebind) { //first time must rebind
if (use_radiance_map) {
// would be a bit weird if we don't have this...
state.scene_shader.set_uniform(SceneShaderGLES2::RADIANCE_INVERSE_XFORM, p_view_transform);
}
state.scene_shader.set_uniform(SceneShaderGLES2::BG_ENERGY, 1.0);
state.scene_shader.set_uniform(SceneShaderGLES2::BG_COLOR, state.default_bg);
state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_SKY_CONTRIBUTION, 1.0);
state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_COLOR, state.default_ambient);
state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_ENERGY, 1.0);
state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform);
state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, view_transform_inverse);
state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection);
state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, projection_inverse);
state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]);
state.scene_shader.set_uniform(SceneShaderGLES2::VIEW_INDEX, p_eye == 2 ? 1 : 0);
state.scene_shader.set_uniform(SceneShaderGLES2::VIEWPORT_SIZE, viewport_size);
state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size);
}
state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform);
_render_geometry(e);
prev_geometry = e->geometry;
prev_owner = e->owner;
prev_material = material;
prev_instancing = instancing;
}
state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_OCTAHEDRAL_COMPRESSION, false);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, false);
state.scene_shader.set_conditional(SceneShaderGLES2::SHADELESS, false);
state.scene_shader.set_conditional(SceneShaderGLES2::BASE_PASS, false);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, false);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, false);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM3, false);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, false);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, false);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_VERTEX_LIGHTING, false);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE1, false);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_REFLECTION_PROBE2, false);
state.scene_shader.set_conditional(SceneShaderGLES2::FOG_DEPTH_ENABLED, false);
state.scene_shader.set_conditional(SceneShaderGLES2::FOG_HEIGHT_ENABLED, false);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_DEPTH_PREPASS, false);
}
void RasterizerSceneGLES2::_post_process(const Projection &p_cam_projection) {
//copy to front buffer
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_BLEND);
glDepthFunc(GL_LEQUAL);
glColorMask(1, 1, 1, 1);
//no post process on small or render targets without an env
bool use_post_process = false;
// If using multisample buffer, resolve to post_process_effect buffer or to front buffer
if (storage->frame.current_rt && storage->frame.current_rt->multisample_active) {
GLuint next_buffer;
if (use_post_process) {
next_buffer = storage->frame.current_rt->mip_maps[0].sizes[0].fbo;
} else if (storage->frame.current_rt->external.fbo != 0) {
next_buffer = storage->frame.current_rt->external.fbo;
} else {
// set next_buffer to front buffer so multisample blit can happen if needed
next_buffer = storage->frame.current_rt->fbo;
}
#ifdef GLES_OVER_GL
glBindFramebuffer(GL_READ_FRAMEBUFFER, storage->frame.current_rt->multisample_fbo);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, next_buffer);
glBlitFramebuffer(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height, 0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
#elif IPHONE_ENABLED
glBindFramebuffer(GL_READ_FRAMEBUFFER, storage->frame.current_rt->multisample_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, next_buffer);
glResolveMultisampleFramebufferAPPLE();
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
#elif ANDROID_ENABLED
// In GLES2 Android Blit is not available, so just copy color texture manually
_copy_texture_to_buffer(storage->frame.current_rt->multisample_color, next_buffer);
#else
// TODO: any other platform not supported? this will fail.. maybe we should just call _copy_texture_to_buffer here as well?
(void)next_buffer; // Silence warning as it's unused.
#endif
} else if (use_post_process) {
if (storage->frame.current_rt->external.fbo != 0) {
_copy_texture_to_buffer(storage->frame.current_rt->external.color, storage->frame.current_rt->mip_maps[0].sizes[0].fbo);
} else {
_copy_texture_to_buffer(storage->frame.current_rt->color, storage->frame.current_rt->mip_maps[0].sizes[0].fbo);
}
}
if (!use_post_process) {
return;
}
// Order of operation
//1) DOF Blur (first blur, then copy to buffer applying the blur) //only on desktop
//2) FXAA
//3) Bloom (Glow) //only on desktop
//4) Adjustments
//Adjustments
state.tonemap_shader.set_conditional(TonemapShaderGLES2::DISABLE_ALPHA, !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]);
state.tonemap_shader.bind();
if (storage->frame.current_rt->use_fxaa) {
state.tonemap_shader.set_uniform(TonemapShaderGLES2::PIXEL_SIZE, Vector2(1.0 / storage->frame.current_rt->width, 1.0 / storage->frame.current_rt->height));
}
storage->_copy_screen();
//turn off everything used
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_FXAA, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_GLOW_LEVEL1, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_GLOW_LEVEL2, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_GLOW_LEVEL3, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_GLOW_LEVEL4, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_GLOW_LEVEL5, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_GLOW_LEVEL6, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_GLOW_LEVEL7, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_GLOW_REPLACE, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_GLOW_SCREEN, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_GLOW_SOFTLIGHT, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_GLOW_FILTER_BICUBIC, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_MULTI_TEXTURE_GLOW, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_BCS, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::USE_COLOR_CORRECTION, false);
state.tonemap_shader.set_conditional(TonemapShaderGLES2::DISABLE_ALPHA, false);
}
void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const Projection &p_cam_projection, const int p_eye, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count) {
Transform cam_transform = p_cam_transform;
storage->info.render.object_count += p_cull_count;
GLuint current_fb = 0;
int viewport_width, viewport_height;
int viewport_x = 0;
int viewport_y = 0;
bool probe_interior = false;
bool reverse_cull = false;
if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP]) {
cam_transform.basis.set_axis(1, -cam_transform.basis.get_axis(1));
reverse_cull = true;
}
state.render_no_shadows = false;
if (storage->frame.current_rt->multisample_active) {
current_fb = storage->frame.current_rt->multisample_fbo;
} else if (storage->frame.current_rt->external.fbo != 0) {
current_fb = storage->frame.current_rt->external.fbo;
} else {
current_fb = storage->frame.current_rt->fbo;
}
viewport_width = storage->frame.current_rt->width;
viewport_height = storage->frame.current_rt->height;
viewport_x = storage->frame.current_rt->x;
if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_DIRECT_TO_SCREEN]) {
viewport_y = OS::get_singleton()->get_window_size().height - viewport_height - storage->frame.current_rt->y;
} else {
viewport_y = storage->frame.current_rt->y;
}
state.used_screen_texture = false;
state.viewport_size.x = viewport_width;
state.viewport_size.y = viewport_height;
state.screen_pixel_size.x = 1.0 / viewport_width;
state.screen_pixel_size.y = 1.0 / viewport_height;
// render list stuff
render_list.clear();
_fill_render_list(p_cull_result, p_cull_count, false);
// other stuff
glBindFramebuffer(GL_FRAMEBUFFER, current_fb);
glViewport(viewport_x, viewport_y, viewport_width, viewport_height);
if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_DIRECT_TO_SCREEN]) {
glScissor(viewport_x, viewport_y, viewport_width, viewport_height);
glEnable(GL_SCISSOR_TEST);
}
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT);
// clear color
Color clear_color(0, 0, 0, 1);
if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
clear_color = Color(0, 0, 0, 0);
storage->frame.clear_request = false;
} else {
storage->frame.clear_request = false;
}
state.default_ambient = Color(clear_color.r, clear_color.g, clear_color.b, 1.0);
state.default_bg = Color(clear_color.r, clear_color.g, clear_color.b, 1.0);
if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_DIRECT_TO_SCREEN]) {
glDisable(GL_SCISSOR_TEST);
}
glVertexAttrib4f(RS::ARRAY_COLOR, 1, 1, 1, 1);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// render sky
if (probe_interior) {
state.default_ambient = Color(0, 0, 0, 1); //black as default ambient for interior
state.default_bg = Color(0, 0, 0, 1); //black as default background for interior
}
// make sure we set our output mode correctly
if (storage->frame.current_rt) {
state.scene_shader.set_conditional(SceneShaderGLES2::OUTPUT_LINEAR, storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_KEEP_3D_LINEAR]);
} else {
state.scene_shader.set_conditional(SceneShaderGLES2::OUTPUT_LINEAR, false);
}
// render opaque things first
render_list.sort_by_key(false);
_render_render_list(render_list.elements, render_list.element_count, cam_transform, p_cam_projection, p_eye, reverse_cull, false);
if (storage->frame.current_rt && state.used_screen_texture) {
//copy screen texture
if (storage->frame.current_rt->multisample_active) {
// Resolve framebuffer to front buffer before copying
#ifdef GLES_OVER_GL
glBindFramebuffer(GL_READ_FRAMEBUFFER, storage->frame.current_rt->multisample_fbo);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, storage->frame.current_rt->fbo);
glBlitFramebuffer(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height, 0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
#elif IPHONE_ENABLED
glBindFramebuffer(GL_READ_FRAMEBUFFER, storage->frame.current_rt->multisample_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, storage->frame.current_rt->fbo);
glResolveMultisampleFramebufferAPPLE();
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
#elif ANDROID_ENABLED
// In GLES2 AndroidBlit is not available, so just copy color texture manually
_copy_texture_to_buffer(storage->frame.current_rt->multisample_color, storage->frame.current_rt->fbo);
#endif
}
storage->canvas->_copy_screen(Rect2());
if (storage->frame.current_rt && storage->frame.current_rt->multisample_active) {
// Rebind the current framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, current_fb);
glViewport(0, 0, viewport_width, viewport_height);
}
}
// alpha pass
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
render_list.sort_by_reverse_depth_and_priority(true);
_render_render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, cam_transform, p_cam_projection, p_eye, reverse_cull, true);
//post process
_post_process(p_cam_projection);
// return to default
state.scene_shader.set_conditional(SceneShaderGLES2::OUTPUT_LINEAR, false);
}
void RasterizerSceneGLES2::set_scene_pass(uint64_t p_pass) {
scene_pass = p_pass;
}
bool RasterizerSceneGLES2::free(RID p_rid) {
return false;
}
void RasterizerSceneGLES2::set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) {
}
void RasterizerSceneGLES2::initialize() {
state.scene_shader.init();
state.scene_shader.set_conditional(SceneShaderGLES2::USE_RGBA_SHADOWS, storage->config.use_rgba_3d_shadows);
state.cube_to_dp_shader.init();
state.effect_blur_shader.init();
state.tonemap_shader.init();
render_list.init();
render_pass = 1;
{
//default material and shader
default_shader = RID_PRIME(storage->shader_create());
storage->shader_set_code(default_shader, "shader_type spatial;\n");
default_material = RID_PRIME(storage->material_create());
storage->material_set_shader(default_material, default_shader);
default_shader_twosided = RID_PRIME(storage->shader_create());
default_material_twosided = RID_PRIME(storage->material_create());
storage->shader_set_code(default_shader_twosided, "shader_type spatial; render_mode cull_disabled;\n");
storage->material_set_shader(default_material_twosided, default_shader_twosided);
}
{
default_worldcoord_shader = RID_PRIME(storage->shader_create());
storage->shader_set_code(default_worldcoord_shader, "shader_type spatial; render_mode world_vertex_coords;\n");
default_worldcoord_material = RID_PRIME(storage->material_create());
storage->material_set_shader(default_worldcoord_material, default_worldcoord_shader);
default_worldcoord_shader_twosided = RID_PRIME(storage->shader_create());
default_worldcoord_material_twosided = RID_PRIME(storage->material_create());
storage->shader_set_code(default_worldcoord_shader_twosided, "shader_type spatial; render_mode cull_disabled,world_vertex_coords;\n");
storage->material_set_shader(default_worldcoord_material_twosided, default_worldcoord_shader_twosided);
}
{
//default material and shader
default_overdraw_shader = RID_PRIME(storage->shader_create());
// Use relatively low opacity so that more "layers" of overlapping objects can be distinguished.
storage->shader_set_code(default_overdraw_shader, "shader_type spatial;\nrender_mode blend_add,unshaded;\n void fragment() { ALBEDO=vec3(0.4,0.8,0.8); ALPHA=0.1; }");
default_overdraw_material = RID_PRIME(storage->material_create());
storage->material_set_shader(default_overdraw_material, default_overdraw_shader);
}
{
glGenBuffers(1, &state.sky_verts);
glBindBuffer(GL_ARRAY_BUFFER, state.sky_verts);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vector3) * 8, nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
{
uint32_t immediate_buffer_size = GLOBAL_DEF("rendering/limits/buffers/immediate_buffer_size_kb", 2048);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/immediate_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/immediate_buffer_size_kb", PROPERTY_HINT_RANGE, "0,8192,1,or_greater"));
glGenBuffers(1, &state.immediate_buffer);
glBindBuffer(GL_ARRAY_BUFFER, state.immediate_buffer);
glBufferData(GL_ARRAY_BUFFER, immediate_buffer_size * 1024, nullptr, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glFrontFace(GL_CW);
}
void RasterizerSceneGLES2::iteration() {
}
void RasterizerSceneGLES2::finalize() {
}
RasterizerSceneGLES2::RasterizerSceneGLES2() {
}
RasterizerSceneGLES2::~RasterizerSceneGLES2() {
storage->free(default_material);
default_material = RID();
storage->free(default_material_twosided);
default_material_twosided = RID();
storage->free(default_shader);
default_shader = RID();
storage->free(default_shader_twosided);
default_shader_twosided = RID();
storage->free(default_worldcoord_material);
default_worldcoord_material = RID();
storage->free(default_worldcoord_material_twosided);
default_worldcoord_material_twosided = RID();
storage->free(default_worldcoord_shader);
default_worldcoord_shader = RID();
storage->free(default_worldcoord_shader_twosided);
default_worldcoord_shader_twosided = RID();
storage->free(default_overdraw_material);
default_overdraw_material = RID();
storage->free(default_overdraw_shader);
default_overdraw_shader = RID();
}