godot/drivers/gles2/rasterizer_scene_gles2.cpp

2462 lines
84 KiB
C++

/*************************************************************************/
/* rasterizer_scene_gles2.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2018 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 "math/transform.h"
#include "math_funcs.h"
#include "os/os.h"
#include "project_settings.h"
#include "rasterizer_canvas_gles2.h"
#include "servers/visual/visual_server_raster.h"
#include "vmap.h"
#ifndef GLES_OVER_GL
#define glClearDepth glClearDepthf
#endif
static const GLenum _cube_side_enum[6] = {
GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
GL_TEXTURE_CUBE_MAP_POSITIVE_X,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
};
/* SHADOW ATLAS API */
RID RasterizerSceneGLES2::shadow_atlas_create() {
ShadowAtlas *shadow_atlas = memnew(ShadowAtlas);
shadow_atlas->fbo = 0;
shadow_atlas->depth = 0;
shadow_atlas->size = 0;
shadow_atlas->smallest_subdiv = 0;
for (int i = 0; i < 4; i++) {
shadow_atlas->size_order[i] = i;
}
return shadow_atlas_owner.make_rid(shadow_atlas);
}
void RasterizerSceneGLES2::shadow_atlas_set_size(RID p_atlas, int p_size) {
ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas);
ERR_FAIL_COND(!shadow_atlas);
ERR_FAIL_COND(p_size < 0);
p_size = next_power_of_2(p_size);
if (p_size == shadow_atlas->size)
return;
// erase the old atlast
if (shadow_atlas->fbo) {
glDeleteTextures(1, &shadow_atlas->depth);
glDeleteFramebuffers(1, &shadow_atlas->fbo);
shadow_atlas->fbo = 0;
shadow_atlas->depth = 0;
}
// erase shadow atlast references from lights
for (Map<RID, uint32_t>::Element *E = shadow_atlas->shadow_owners.front(); E; E = E->next()) {
LightInstance *li = light_instance_owner.getornull(E->key());
ERR_CONTINUE(!li);
li->shadow_atlases.erase(p_atlas);
}
shadow_atlas->shadow_owners.clear();
shadow_atlas->size = p_size;
if (shadow_atlas->size) {
glGenFramebuffers(1, &shadow_atlas->fbo);
glBindFramebuffer(GL_FRAMEBUFFER, shadow_atlas->fbo);
// create a depth texture
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &shadow_atlas->depth);
glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, shadow_atlas->size, shadow_atlas->size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadow_atlas->depth, 0);
glViewport(0, 0, shadow_atlas->size, shadow_atlas->size);
glDepthMask(GL_TRUE);
glClearDepth(0.0f);
glClear(GL_DEPTH_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}
void RasterizerSceneGLES2::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) {
ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas);
ERR_FAIL_COND(!shadow_atlas);
ERR_FAIL_INDEX(p_quadrant, 4);
ERR_FAIL_INDEX(p_subdivision, 16384);
uint32_t subdiv = next_power_of_2(p_subdivision);
if (subdiv & 0xaaaaaaaa) { // sqrt(subdiv) must be integer
subdiv <<= 1;
}
subdiv = int(Math::sqrt((float)subdiv));
if (shadow_atlas->quadrants[p_quadrant].shadows.size() == subdiv)
return;
// erase all data from the quadrant
for (int i = 0; i < shadow_atlas->quadrants[p_quadrant].shadows.size(); i++) {
if (shadow_atlas->quadrants[p_quadrant].shadows[i].owner.is_valid()) {
shadow_atlas->shadow_owners.erase(shadow_atlas->quadrants[p_quadrant].shadows[i].owner);
LightInstance *li = light_instance_owner.getornull(shadow_atlas->quadrants[p_quadrant].shadows[i].owner);
ERR_CONTINUE(!li);
li->shadow_atlases.erase(p_atlas);
}
}
shadow_atlas->quadrants[p_quadrant].shadows.resize(0);
shadow_atlas->quadrants[p_quadrant].shadows.resize(subdiv);
shadow_atlas->quadrants[p_quadrant].subdivision = subdiv;
// cache the smallest subdivision for faster allocations
shadow_atlas->smallest_subdiv = 1 << 30;
for (int i = 0; i < 4; i++) {
if (shadow_atlas->quadrants[i].subdivision) {
shadow_atlas->smallest_subdiv = MIN(shadow_atlas->smallest_subdiv, shadow_atlas->quadrants[i].subdivision);
}
}
if (shadow_atlas->smallest_subdiv == 1 << 30) {
shadow_atlas->smallest_subdiv = 0;
}
// re-sort the quadrants
int swaps = 0;
do {
swaps = 0;
for (int i = 0; i < 3; i++) {
if (shadow_atlas->quadrants[shadow_atlas->size_order[i]].subdivision < shadow_atlas->quadrants[shadow_atlas->size_order[i + 1]].subdivision) {
SWAP(shadow_atlas->size_order[i], shadow_atlas->size_order[i + 1]);
swaps++;
}
}
} while (swaps > 0);
}
bool RasterizerSceneGLES2::_shadow_atlas_find_shadow(ShadowAtlas *shadow_atlas, int *p_in_quadrants, int p_quadrant_count, int p_current_subdiv, uint64_t p_tick, int &r_quadrant, int &r_shadow) {
for (int i = p_quadrant_count - 1; i >= 0; i--) {
int qidx = p_in_quadrants[i];
if (shadow_atlas->quadrants[qidx].subdivision == (uint32_t)p_current_subdiv) {
return false;
}
// look for an empty space
int sc = shadow_atlas->quadrants[qidx].shadows.size();
ShadowAtlas::Quadrant::Shadow *sarr = shadow_atlas->quadrants[qidx].shadows.ptrw();
int found_free_idx = -1; // found a free one
int found_used_idx = -1; // found an existing one, must steal it
uint64_t min_pass = 0; // pass of the existing one, try to use the least recently
for (int j = 0; j < sc; j++) {
if (!sarr[j].owner.is_valid()) {
found_free_idx = j;
break;
}
LightInstance *sli = light_instance_owner.getornull(sarr[j].owner);
ERR_CONTINUE(!sli);
if (sli->last_scene_pass != scene_pass) {
// was just allocated, don't kill it so soon, wait a bit...
if (p_tick - sarr[j].alloc_tick < shadow_atlas_realloc_tolerance_msec) {
continue;
}
if (found_used_idx == -1 || sli->last_scene_pass < min_pass) {
found_used_idx = j;
min_pass = sli->last_scene_pass;
}
}
}
if (found_free_idx == -1 && found_used_idx == -1) {
continue; // nothing found
}
if (found_free_idx == -1 && found_used_idx != -1) {
found_free_idx = found_used_idx;
}
r_quadrant = qidx;
r_shadow = found_free_idx;
return true;
}
return false;
}
bool RasterizerSceneGLES2::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) {
ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_atlas);
ERR_FAIL_COND_V(!shadow_atlas, false);
LightInstance *li = light_instance_owner.getornull(p_light_intance);
ERR_FAIL_COND_V(!li, false);
if (shadow_atlas->size == 0 || shadow_atlas->smallest_subdiv == 0) {
return false;
}
uint32_t quad_size = shadow_atlas->size >> 1;
int desired_fit = MIN(quad_size / shadow_atlas->smallest_subdiv, next_power_of_2(quad_size * p_coverage));
int valid_quadrants[4];
int valid_quadrant_count = 0;
int best_size = -1;
int best_subdiv = -1;
for (int i = 0; i < 4; i++) {
int q = shadow_atlas->size_order[i];
int sd = shadow_atlas->quadrants[q].subdivision;
if (sd == 0) {
continue;
}
int max_fit = quad_size / sd;
if (best_size != -1 && max_fit > best_size) {
break; // what we asked for is bigger than this.
}
valid_quadrants[valid_quadrant_count] = q;
valid_quadrant_count++;
best_subdiv = sd;
if (max_fit >= desired_fit) {
best_size = max_fit;
}
}
ERR_FAIL_COND_V(valid_quadrant_count == 0, false); // no suitable block available
uint64_t tick = OS::get_singleton()->get_ticks_msec();
if (shadow_atlas->shadow_owners.has(p_light_intance)) {
// light was already known!
uint32_t key = shadow_atlas->shadow_owners[p_light_intance];
uint32_t q = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3;
uint32_t s = key & ShadowAtlas::SHADOW_INDEX_MASK;
bool should_realloc = shadow_atlas->quadrants[q].subdivision != (uint32_t)best_subdiv && (shadow_atlas->quadrants[q].shadows[s].alloc_tick - tick > shadow_atlas_realloc_tolerance_msec);
bool should_redraw = shadow_atlas->quadrants[q].shadows[s].version != p_light_version;
if (!should_realloc) {
shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version;
return should_redraw;
}
int new_quadrant;
int new_shadow;
// find a better place
if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, shadow_atlas->quadrants[q].subdivision, tick, new_quadrant, new_shadow)) {
// found a better place
ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow];
if (sh->owner.is_valid()) {
// it is take but invalid, so we can take it
shadow_atlas->shadow_owners.erase(sh->owner);
LightInstance *sli = light_instance_owner.get(sh->owner);
sli->shadow_atlases.erase(p_atlas);
}
// erase previous
shadow_atlas->quadrants[q].shadows.write[s].version = 0;
shadow_atlas->quadrants[q].shadows.write[s].owner = RID();
sh->owner = p_light_intance;
sh->alloc_tick = tick;
sh->version = p_light_version;
li->shadow_atlases.insert(p_atlas);
// make a new key
key = new_quadrant << ShadowAtlas::QUADRANT_SHIFT;
key |= new_shadow;
// update it in the map
shadow_atlas->shadow_owners[p_light_intance] = key;
// make it dirty, so we redraw
return true;
}
// no better place found, so we keep the current place
shadow_atlas->quadrants[q].shadows.write[s].version = p_light_version;
return should_redraw;
}
int new_quadrant;
int new_shadow;
if (_shadow_atlas_find_shadow(shadow_atlas, valid_quadrants, valid_quadrant_count, -1, tick, new_quadrant, new_shadow)) {
// found a better place
ShadowAtlas::Quadrant::Shadow *sh = &shadow_atlas->quadrants[new_quadrant].shadows.write[new_shadow];
if (sh->owner.is_valid()) {
// it is take but invalid, so we can take it
shadow_atlas->shadow_owners.erase(sh->owner);
LightInstance *sli = light_instance_owner.get(sh->owner);
sli->shadow_atlases.erase(p_atlas);
}
sh->owner = p_light_intance;
sh->alloc_tick = tick;
sh->version = p_light_version;
li->shadow_atlases.insert(p_atlas);
// make a new key
uint32_t key = new_quadrant << ShadowAtlas::QUADRANT_SHIFT;
key |= new_shadow;
// update it in the map
shadow_atlas->shadow_owners[p_light_intance] = key;
// make it dirty, so we redraw
return true;
}
return false;
}
void RasterizerSceneGLES2::set_directional_shadow_count(int p_count) {
directional_shadow.light_count = p_count;
directional_shadow.current_light = 0;
}
int RasterizerSceneGLES2::get_directional_light_shadow_size(RID p_light_intance) {
ERR_FAIL_COND_V(directional_shadow.light_count == 0, 0);
int shadow_size;
if (directional_shadow.light_count == 1) {
shadow_size = directional_shadow.size;
} else {
shadow_size = directional_shadow.size / 2; //more than 4 not supported anyway
}
LightInstance *light_instance = light_instance_owner.getornull(p_light_intance);
ERR_FAIL_COND_V(!light_instance, 0);
switch (light_instance->light_ptr->directional_shadow_mode) {
case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL:
break; //none
case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS:
case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS:
shadow_size /= 2;
break;
}
return shadow_size;
}
//////////////////////////////////////////////////////
RID RasterizerSceneGLES2::reflection_atlas_create() {
return RID();
}
void RasterizerSceneGLES2::reflection_atlas_set_size(RID p_ref_atlas, int p_size) {
}
void RasterizerSceneGLES2::reflection_atlas_set_subdivision(RID p_ref_atlas, int p_subdiv) {
}
////////////////////////////////////////////////////
RID RasterizerSceneGLES2::reflection_probe_instance_create(RID p_probe) {
return RID();
}
void RasterizerSceneGLES2::reflection_probe_instance_set_transform(RID p_instance, const Transform &p_transform) {
}
void RasterizerSceneGLES2::reflection_probe_release_atlas_index(RID p_instance) {
}
bool RasterizerSceneGLES2::reflection_probe_instance_needs_redraw(RID p_instance) {
return false;
}
bool RasterizerSceneGLES2::reflection_probe_instance_has_reflection(RID p_instance) {
return false;
}
bool RasterizerSceneGLES2::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) {
return false;
}
bool RasterizerSceneGLES2::reflection_probe_instance_postprocess_step(RID p_instance) {
return false;
}
/* ENVIRONMENT API */
RID RasterizerSceneGLES2::environment_create() {
Environment *env = memnew(Environment);
return environment_owner.make_rid(env);
}
void RasterizerSceneGLES2::environment_set_background(RID p_env, VS::EnvironmentBG p_bg) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->bg_mode = p_bg;
}
void RasterizerSceneGLES2::environment_set_sky(RID p_env, RID p_sky) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->sky = p_sky;
}
void RasterizerSceneGLES2::environment_set_sky_custom_fov(RID p_env, float p_scale) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->sky_custom_fov = p_scale;
}
void RasterizerSceneGLES2::environment_set_bg_color(RID p_env, const Color &p_color) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->bg_color = p_color;
}
void RasterizerSceneGLES2::environment_set_bg_energy(RID p_env, float p_energy) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->bg_energy = p_energy;
}
void RasterizerSceneGLES2::environment_set_canvas_max_layer(RID p_env, int p_max_layer) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->canvas_max_layer = p_max_layer;
}
void RasterizerSceneGLES2::environment_set_ambient_light(RID p_env, const Color &p_color, float p_energy, float p_sky_contribution) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
env->ambient_color = p_color;
env->ambient_energy = p_energy;
env->ambient_sky_contribution = p_sky_contribution;
}
void RasterizerSceneGLES2::environment_set_dof_blur_far(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
}
void RasterizerSceneGLES2::environment_set_dof_blur_near(RID p_env, bool p_enable, float p_distance, float p_transition, float p_amount, VS::EnvironmentDOFBlurQuality p_quality) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
}
void RasterizerSceneGLES2::environment_set_glow(RID p_env, bool p_enable, int p_level_flags, float p_intensity, float p_strength, float p_bloom_threshold, VS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, bool p_bicubic_upscale) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
}
void RasterizerSceneGLES2::environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
}
void RasterizerSceneGLES2::environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance, bool p_roughness) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
}
void RasterizerSceneGLES2::environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VisualServer::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
}
void RasterizerSceneGLES2::environment_set_tonemap(RID p_env, VS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
}
void RasterizerSceneGLES2::environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
}
void RasterizerSceneGLES2::environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
}
void RasterizerSceneGLES2::environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
}
void RasterizerSceneGLES2::environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) {
Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND(!env);
}
bool RasterizerSceneGLES2::is_environment(RID p_env) {
return environment_owner.owns(p_env);
}
VS::EnvironmentBG RasterizerSceneGLES2::environment_get_background(RID p_env) {
const Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, VS::ENV_BG_MAX);
return env->bg_mode;
}
int RasterizerSceneGLES2::environment_get_canvas_max_layer(RID p_env) {
const Environment *env = environment_owner.getornull(p_env);
ERR_FAIL_COND_V(!env, -1);
return env->canvas_max_layer;
}
RID RasterizerSceneGLES2::light_instance_create(RID p_light) {
LightInstance *light_instance = memnew(LightInstance);
light_instance->last_scene_pass = 0;
light_instance->light = p_light;
light_instance->light_ptr = storage->light_owner.getornull(p_light);
ERR_FAIL_COND_V(!light_instance->light_ptr, RID());
light_instance->self = light_instance_owner.make_rid(light_instance);
return light_instance->self;
}
void RasterizerSceneGLES2::light_instance_set_transform(RID p_light_instance, const Transform &p_transform) {
LightInstance *light_instance = light_instance_owner.getornull(p_light_instance);
ERR_FAIL_COND(!light_instance);
light_instance->transform = p_transform;
}
void RasterizerSceneGLES2::light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_bias_scale) {
LightInstance *light_instance = light_instance_owner.getornull(p_light_instance);
ERR_FAIL_COND(!light_instance);
if (light_instance->light_ptr->type != VS::LIGHT_DIRECTIONAL) {
p_pass = 0;
}
ERR_FAIL_INDEX(p_pass, 4);
light_instance->shadow_transform[p_pass].camera = p_projection;
light_instance->shadow_transform[p_pass].transform = p_transform;
light_instance->shadow_transform[p_pass].farplane = p_far;
light_instance->shadow_transform[p_pass].split = p_split;
light_instance->shadow_transform[p_pass].bias_scale = p_bias_scale;
}
void RasterizerSceneGLES2::light_instance_mark_visible(RID p_light_instance) {
LightInstance *light_instance = light_instance_owner.getornull(p_light_instance);
ERR_FAIL_COND(!light_instance);
light_instance->last_scene_pass = scene_pass;
}
//////////////////////
RID RasterizerSceneGLES2::gi_probe_instance_create() {
return RID();
}
void RasterizerSceneGLES2::gi_probe_instance_set_light_data(RID p_probe, RID p_base, RID p_data) {
}
void RasterizerSceneGLES2::gi_probe_instance_set_transform_to_data(RID p_probe, const Transform &p_xform) {
}
void RasterizerSceneGLES2::gi_probe_instance_set_bounds(RID p_probe, const Vector3 &p_bounds) {
}
////////////////////////////
////////////////////////////
////////////////////////////
void RasterizerSceneGLES2::_add_geometry(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, int p_material, bool p_depth_pass, bool p_shadow_pass) {
RasterizerStorageGLES2::Material *material = NULL;
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 = NULL;
}
}
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, p_shadow_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, p_shadow_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 p_shadow_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;
// TODO add this stuff
// bool mirror = p_instance->mirror;
// bool no_cull = false;
RenderList::Element *e = has_alpha ? 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;
// TODO check render pass of geometry
// TODO check directional light flag
if (p_depth_pass) {
// if we are in the depth pass we can sort out a few things to improve performance
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;
}
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 doesn't use discard or writes a custom vertex position,
// so we can use a stripped down shader instead
// TODO twosided and worldcoord stuff
p_material = storage->material_owner.getptr(default_material_twosided);
}
has_alpha = false;
}
e->sort_key |= uint64_t(e->geometry->index) << RenderList::SORT_KEY_GEOMETRY_INDEX_SHIFT;
e->sort_key |= uint64_t(e->instance->base_type) << RenderList::SORT_KEY_GEOMETRY_TYPE_SHIFT;
if (p_material->shader->spatial.unshaded) {
e->sort_key |= SORT_KEY_UNSHADED_FLAG;
}
if (!p_depth_pass) {
e->sort_key |= uint64_t(e->material->index) << RenderList::SORT_KEY_MATERIAL_INDEX_SHIFT;
e->sort_key |= uint64_t(p_material->render_priority + 128) << RenderList::SORT_KEY_PRIORITY_SHIFT;
} else {
// TODO
}
if (p_material->shader->spatial.uses_time) {
VisualServerRaster::redraw_request();
}
}
void RasterizerSceneGLES2::_fill_render_list(InstanceBase **p_cull_result, int p_cull_count, bool p_depth_pass, bool p_shadow_pass) {
for (int i = 0; i < p_cull_count; i++) {
InstanceBase *instance = p_cull_result[i];
switch (instance->base_type) {
case VS::INSTANCE_MESH: {
RasterizerStorageGLES2::Mesh *mesh = storage->mesh_owner.getornull(instance->base);
ERR_CONTINUE(!mesh);
int num_surfaces = mesh->surfaces.size();
for (int i = 0; i < num_surfaces; i++) {
int material_index = instance->materials[i].is_valid() ? i : -1;
RasterizerStorageGLES2::Surface *surface = mesh->surfaces[i];
_add_geometry(surface, instance, NULL, material_index, p_depth_pass, p_shadow_pass);
}
} break;
case VS::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 i = 0; i < ssize; i++) {
RasterizerStorageGLES2::Surface *s = mesh->surfaces[i];
_add_geometry(s, instance, multi_mesh, -1, p_depth_pass, p_shadow_pass);
}
} break;
case VS::INSTANCE_IMMEDIATE: {
RasterizerStorageGLES2::Immediate *im = storage->immediate_owner.getptr(instance->base);
ERR_CONTINUE(!im);
_add_geometry(im, instance, NULL, -1, p_depth_pass, p_shadow_pass);
} break;
default: {
} break;
}
}
}
static const GLenum gl_primitive[] = {
GL_POINTS,
GL_LINES,
GL_LINE_STRIP,
GL_LINE_LOOP,
GL_TRIANGLES,
GL_TRIANGLE_STRIP,
GL_TRIANGLE_FAN
};
void RasterizerSceneGLES2::_setup_material(RasterizerStorageGLES2::Material *p_material, bool p_reverse_cull, Size2i p_skeleton_tex_size) {
// material parameters
state.scene_shader.set_custom_shader(p_material->shader->custom_code_id);
state.scene_shader.bind();
if (p_material->shader->spatial.no_depth_test) {
glDisable(GL_DEPTH_TEST);
} else {
glEnable(GL_DEPTH_TEST);
}
// TODO whyyyyy????
p_reverse_cull = true;
switch (p_material->shader->spatial.cull_mode) {
case RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_DISABLED: {
glDisable(GL_CULL_FACE);
} break;
case RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_BACK: {
glEnable(GL_CULL_FACE);
glCullFace(p_reverse_cull ? GL_FRONT : GL_BACK);
} break;
case RasterizerStorageGLES2::Shader::Spatial::CULL_MODE_FRONT: {
glEnable(GL_CULL_FACE);
glCullFace(p_reverse_cull ? GL_BACK : GL_FRONT);
} break;
}
int tc = p_material->textures.size();
Pair<StringName, RID> *textures = p_material->textures.ptrw();
ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = p_material->shader->texture_hints.ptrw();
state.scene_shader.set_uniform(SceneShaderGLES2::SKELETON_TEXTURE_SIZE, p_skeleton_tex_size);
for (int i = 0; i < tc; i++) {
glActiveTexture(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_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;
}
t = t->get_ptr();
glBindTexture(t->target, t->tex_id);
}
state.scene_shader.use_material((void *)p_material);
}
void RasterizerSceneGLES2::_setup_geometry(RenderList::Element *p_element, RasterizerStorageGLES2::Skeleton *p_skeleton) {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON, p_skeleton != NULL);
// state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, !storage->config.float_texture_supported);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_SKELETON_SOFTWARE, true);
switch (p_element->instance->base_type) {
case VS::INSTANCE_MESH: {
RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, false);
state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_COLOR_INTERP, s->attribs[VS::ARRAY_COLOR].enabled);
state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV_INTERP, s->attribs[VS::ARRAY_TEX_UV].enabled);
state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV2_INTERP, s->attribs[VS::ARRAY_TEX_UV2].enabled);
} break;
case VS::INSTANCE_MULTIMESH: {
RasterizerStorageGLES2::MultiMesh *multi_mesh = static_cast<RasterizerStorageGLES2::MultiMesh *>(p_element->owner);
RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_COLOR_INTERP, true);
state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, true);
state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV_INTERP, s->attribs[VS::ARRAY_TEX_UV].enabled);
state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV2_INTERP, s->attribs[VS::ARRAY_TEX_UV2].enabled);
} break;
case VS::INSTANCE_IMMEDIATE: {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_INSTANCING, false);
state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_COLOR_INTERP, true);
state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV_INTERP, true);
state.scene_shader.set_conditional(SceneShaderGLES2::ENABLE_UV2_INTERP, true);
} break;
default: {
} break;
}
if (false && storage->config.float_texture_supported) {
if (p_skeleton) {
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, p_skeleton->tex_id);
}
return;
}
if (p_skeleton) {
ERR_FAIL_COND(p_skeleton->use_2d);
PoolVector<float> &transform_buffer = storage->resources.skeleton_transform_cpu_buffer;
switch (p_element->instance->base_type) {
case VS::INSTANCE_MESH: {
RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
if (!s->attribs[VS::ARRAY_BONES].enabled || !s->attribs[VS::ARRAY_WEIGHTS].enabled) {
break; // the whole instance has a skeleton, but this surface is not affected by it.
}
// 3 * vec4 per vertex
if (transform_buffer.size() < s->array_len * 12) {
transform_buffer.resize(s->array_len * 12);
}
const size_t bones_offset = s->attribs[VS::ARRAY_BONES].offset;
const size_t bones_stride = s->attribs[VS::ARRAY_BONES].stride;
const size_t bone_weight_offset = s->attribs[VS::ARRAY_WEIGHTS].offset;
const size_t bone_weight_stride = s->attribs[VS::ARRAY_WEIGHTS].stride;
{
PoolVector<float>::Write write = transform_buffer.write();
float *buffer = write.ptr();
PoolVector<uint8_t>::Read vertex_array_read = s->data.read();
const uint8_t *vertex_data = vertex_array_read.ptr();
for (int i = 0; i < s->array_len; i++) {
// do magic
size_t bones[4];
float bone_weight[4];
if (s->attribs[VS::ARRAY_BONES].type == GL_UNSIGNED_BYTE) {
// read as byte
const uint8_t *bones_ptr = vertex_data + bones_offset + (i * bones_stride);
bones[0] = bones_ptr[0];
bones[1] = bones_ptr[1];
bones[2] = bones_ptr[2];
bones[3] = bones_ptr[3];
} else {
// read as short
const uint16_t *bones_ptr = (const uint16_t *)(vertex_data + bones_offset + (i * bones_stride));
bones[0] = bones_ptr[0];
bones[1] = bones_ptr[1];
bones[2] = bones_ptr[2];
bones[3] = bones_ptr[3];
}
if (s->attribs[VS::ARRAY_WEIGHTS].type == GL_FLOAT) {
// read as float
const float *weight_ptr = (const float *)(vertex_data + bone_weight_offset + (i * bone_weight_stride));
bone_weight[0] = weight_ptr[0];
bone_weight[1] = weight_ptr[1];
bone_weight[2] = weight_ptr[2];
bone_weight[3] = weight_ptr[3];
} else {
// read as half
const uint16_t *weight_ptr = (const uint16_t *)(vertex_data + bone_weight_offset + (i * bone_weight_stride));
bone_weight[0] = (weight_ptr[0] / (float)0xFFFF);
bone_weight[1] = (weight_ptr[1] / (float)0xFFFF);
bone_weight[2] = (weight_ptr[2] / (float)0xFFFF);
bone_weight[3] = (weight_ptr[3] / (float)0xFFFF);
}
size_t offset = i * 12;
Transform transform;
Transform bone_transforms[4] = {
storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[0]),
storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[1]),
storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[2]),
storage->skeleton_bone_get_transform(p_element->instance->skeleton, bones[3]),
};
transform.origin =
bone_weight[0] * bone_transforms[0].origin +
bone_weight[1] * bone_transforms[1].origin +
bone_weight[2] * bone_transforms[2].origin +
bone_weight[3] * bone_transforms[3].origin;
transform.basis =
bone_transforms[0].basis * bone_weight[0] +
bone_transforms[1].basis * bone_weight[1] +
bone_transforms[2].basis * bone_weight[2] +
bone_transforms[3].basis * bone_weight[3];
float row[3][4] = {
{ transform.basis[0][0], transform.basis[0][1], transform.basis[0][2], transform.origin[0] },
{ transform.basis[1][0], transform.basis[1][1], transform.basis[1][2], transform.origin[1] },
{ transform.basis[2][0], transform.basis[2][1], transform.basis[2][2], transform.origin[2] },
};
size_t transform_buffer_offset = i * 12;
copymem(&buffer[transform_buffer_offset], row, sizeof(row));
}
}
storage->_update_skeleton_transform_buffer(transform_buffer, s->array_len * 12);
} break;
default: {
} break;
}
}
}
void RasterizerSceneGLES2::_render_geometry(RenderList::Element *p_element) {
switch (p_element->instance->base_type) {
case VS::INSTANCE_MESH: {
RasterizerStorageGLES2::Surface *s = static_cast<RasterizerStorageGLES2::Surface *>(p_element->geometry);
// set up
if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) {
glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer);
glEnableVertexAttribArray(VS::ARRAY_MAX + 0);
glEnableVertexAttribArray(VS::ARRAY_MAX + 1);
glEnableVertexAttribArray(VS::ARRAY_MAX + 2);
glVertexAttribPointer(VS::ARRAY_MAX + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0));
glVertexAttribPointer(VS::ARRAY_MAX + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1));
glVertexAttribPointer(VS::ARRAY_MAX + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2));
} else {
// just to make sure
glDisableVertexAttribArray(VS::ARRAY_MAX + 0);
glDisableVertexAttribArray(VS::ARRAY_MAX + 1);
glDisableVertexAttribArray(VS::ARRAY_MAX + 2);
glVertexAttrib4f(VS::ARRAY_MAX + 0, 1, 0, 0, 0);
glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0);
glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0);
}
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 < VS::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, (uint8_t *)0 + s->attribs[i].offset);
} else {
glDisableVertexAttribArray(i);
}
}
// 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, 0);
} else {
glDrawArrays(gl_primitive[s->primitive], 0, s->array_len);
}
// tear down
for (int i = 0; i < VS::ARRAY_MAX - 1; i++) {
glDisableVertexAttribArray(i);
}
if (s->index_array_len > 0) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) {
glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer);
glDisableVertexAttribArray(VS::ARRAY_MAX + 0);
glDisableVertexAttribArray(VS::ARRAY_MAX + 1);
glDisableVertexAttribArray(VS::ARRAY_MAX + 2);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
} break;
case VS::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 (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) {
glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer);
glEnableVertexAttribArray(VS::ARRAY_MAX + 0);
glEnableVertexAttribArray(VS::ARRAY_MAX + 1);
glEnableVertexAttribArray(VS::ARRAY_MAX + 2);
glVertexAttribPointer(VS::ARRAY_MAX + 0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 0));
glVertexAttribPointer(VS::ARRAY_MAX + 1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 1));
glVertexAttribPointer(VS::ARRAY_MAX + 2, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 12, (const void *)(sizeof(float) * 4 * 2));
} else {
// just to make sure
glDisableVertexAttribArray(VS::ARRAY_MAX + 0);
glDisableVertexAttribArray(VS::ARRAY_MAX + 1);
glDisableVertexAttribArray(VS::ARRAY_MAX + 2);
glVertexAttrib4f(VS::ARRAY_MAX + 0, 1, 0, 0, 0);
glVertexAttrib4f(VS::ARRAY_MAX + 1, 0, 1, 0, 0);
glVertexAttrib4f(VS::ARRAY_MAX + 2, 0, 0, 1, 0);
}
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 < VS::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, (uint8_t *)0 + s->attribs[i].offset);
} else {
glDisableVertexAttribArray(i);
}
}
glDisableVertexAttribArray(12); // transform 0
glDisableVertexAttribArray(13); // transform 1
glDisableVertexAttribArray(14); // transform 2
glDisableVertexAttribArray(15); // color
glDisableVertexAttribArray(8); // custom data
glVertexAttrib4f(15, 1, 1, 1, 1);
glVertexAttrib4f(8, 0, 0, 0, 0);
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
for (int i = 0; i < amount; i++) {
float *buffer = &multi_mesh->data.write[i * stride];
{
// inline of multimesh_get_transform since it's such a pain
// to get a RID from here...
Transform transform;
transform.basis.elements[0][0] = buffer[0];
transform.basis.elements[0][1] = buffer[1];
transform.basis.elements[0][2] = buffer[2];
transform.origin.x = buffer[3];
transform.basis.elements[1][0] = buffer[4];
transform.basis.elements[1][1] = buffer[5];
transform.basis.elements[1][2] = buffer[6];
transform.origin.y = buffer[7];
transform.basis.elements[2][0] = buffer[8];
transform.basis.elements[2][1] = buffer[9];
transform.basis.elements[2][2] = buffer[10];
transform.origin.z = buffer[11];
float row[3][4] = {
{ transform.basis[0][0], transform.basis[0][1], transform.basis[0][2], transform.origin[0] },
{ transform.basis[1][0], transform.basis[1][1], transform.basis[1][2], transform.origin[1] },
{ transform.basis[2][0], transform.basis[2][1], transform.basis[2][2], transform.origin[2] },
};
glVertexAttrib4fv(12, row[0]);
glVertexAttrib4fv(13, row[1]);
glVertexAttrib4fv(14, row[2]);
}
if (multi_mesh->color_floats) {
glVertexAttrib4fv(15, buffer + color_ofs);
}
if (multi_mesh->custom_data_floats) {
glVertexAttrib4fv(8, 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, 0);
} else {
glDrawArrays(gl_primitive[s->primitive], 0, s->array_len);
}
}
// tear down
for (int i = 0; i < VS::ARRAY_MAX - 1; i++) {
glDisableVertexAttribArray(i);
}
if (s->index_array_len > 0) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
if (p_element->instance->skeleton.is_valid() && s->attribs[VS::ARRAY_BONES].enabled && s->attribs[VS::ARRAY_WEIGHTS].enabled) {
glBindBuffer(GL_ARRAY_BUFFER, storage->resources.skeleton_transform_buffer);
glDisableVertexAttribArray(VS::ARRAY_MAX + 0);
glDisableVertexAttribArray(VS::ARRAY_MAX + 1);
glDisableVertexAttribArray(VS::ARRAY_MAX + 2);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
} break;
case VS::INSTANCE_IMMEDIATE: {
const RasterizerStorageGLES2::Immediate *im = static_cast<const RasterizerStorageGLES2::Immediate *>(p_element->geometry);
if (im->building) {
return;
}
bool restore_tex = false;
glBindBuffer(GL_ARRAY_BUFFER, state.immediate_buffer);
for (const List<RasterizerStorageGLES2::Immediate::Chunk>::Element *E = im->chunks.front(); E; E = E->next()) {
const RasterizerStorageGLES2::Immediate::Chunk &c = E->get();
if (c.vertices.empty()) {
continue;
}
int vertices = c.vertices.size();
uint32_t buf_ofs = 0;
storage->info.render.vertices_count += vertices;
if (c.texture.is_valid() && storage->texture_owner.owns(c.texture)) {
RasterizerStorageGLES2::Texture *t = storage->texture_owner.get(c.texture);
t = t->get_ptr();
if (t->redraw_if_visible) {
VisualServerRaster::redraw_request();
}
#ifdef TOOLS_ENABLED
if (t->detect_3d) {
t->detect_3d(t->detect_3d_ud);
}
#endif
if (t->render_target) {
t->render_target->used_in_frame = true;
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(t->target, t->tex_id);
restore_tex = true;
} else if (restore_tex) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, state.current_main_tex);
restore_tex = false;
}
if (!c.normals.empty()) {
glEnableVertexAttribArray(VS::ARRAY_NORMAL);
glBufferSubData(GL_ARRAY_BUFFER, buf_ofs, sizeof(Vector3) * vertices, c.normals.ptr());
glVertexAttribPointer(VS::ARRAY_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3), ((uint8_t *)NULL) + buf_ofs);
buf_ofs += sizeof(Vector3) * vertices;
} else {
glDisableVertexAttribArray(VS::ARRAY_NORMAL);
}
if (!c.tangents.empty()) {
glEnableVertexAttribArray(VS::ARRAY_TANGENT);
glBufferSubData(GL_ARRAY_BUFFER, buf_ofs, sizeof(Plane) * vertices, c.tangents.ptr());
glVertexAttribPointer(VS::ARRAY_TANGENT, 4, GL_FLOAT, GL_FALSE, sizeof(Plane), ((uint8_t *)NULL) + buf_ofs);
buf_ofs += sizeof(Plane) * vertices;
} else {
glDisableVertexAttribArray(VS::ARRAY_TANGENT);
}
if (!c.colors.empty()) {
glEnableVertexAttribArray(VS::ARRAY_COLOR);
glBufferSubData(GL_ARRAY_BUFFER, buf_ofs, sizeof(Color) * vertices, c.colors.ptr());
glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), ((uint8_t *)NULL) + buf_ofs);
buf_ofs += sizeof(Color) * vertices;
} else {
glDisableVertexAttribArray(VS::ARRAY_COLOR);
}
if (!c.uvs.empty()) {
glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
glBufferSubData(GL_ARRAY_BUFFER, buf_ofs, sizeof(Vector2) * vertices, c.uvs.ptr());
glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), ((uint8_t *)NULL) + buf_ofs);
buf_ofs += sizeof(Vector2) * vertices;
} else {
glDisableVertexAttribArray(VS::ARRAY_TEX_UV);
}
if (!c.uv2s.empty()) {
glEnableVertexAttribArray(VS::ARRAY_TEX_UV2);
glBufferSubData(GL_ARRAY_BUFFER, buf_ofs, sizeof(Vector2) * vertices, c.uv2s.ptr());
glVertexAttribPointer(VS::ARRAY_TEX_UV2, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), ((uint8_t *)NULL) + buf_ofs);
buf_ofs += sizeof(Vector2) * vertices;
} else {
glDisableVertexAttribArray(VS::ARRAY_TEX_UV2);
}
glEnableVertexAttribArray(VS::ARRAY_VERTEX);
glBufferSubData(GL_ARRAY_BUFFER, buf_ofs, sizeof(Vector3) * vertices, c.vertices.ptr());
glVertexAttribPointer(VS::ARRAY_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3), ((uint8_t *)NULL) + buf_ofs);
glDrawArrays(gl_primitive[c.primitive], 0, c.vertices.size());
}
if (restore_tex) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, state.current_main_tex);
restore_tex = false;
}
} break;
}
}
void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, int p_element_count, const RID *p_directional_lights, int p_directional_light_count, const Transform &p_view_transform, const CameraMatrix &p_projection, RID p_shadow_atlas, Environment *p_env, GLuint p_base_env, float p_shadow_bias, float p_shadow_normal_bias, bool p_reverse_cull, bool p_alpha_pass, bool p_shadow, bool p_directional_add) {
ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas);
Vector2 screen_pixel_size;
screen_pixel_size.x = 1.0 / storage->frame.current_rt->width;
screen_pixel_size.y = 1.0 / storage->frame.current_rt->height;
bool use_radiance_map = false;
VMap<RID, Vector<RenderList::Element *> > lit_objects;
for (int i = 0; i < p_element_count; i++) {
RenderList::Element *e = p_elements[i];
RasterizerStorageGLES2::Material *material = e->material;
RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton);
if (p_base_env) {
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 2);
glBindTexture(GL_TEXTURE_CUBE_MAP, p_base_env);
use_radiance_map = true;
}
state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, use_radiance_map);
if (material->shader->spatial.unshaded) {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false);
} else {
state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, use_radiance_map);
}
// opaque pass
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, false);
_setup_geometry(e, skeleton);
_setup_material(material, p_reverse_cull, Size2i(skeleton ? skeleton->size * 3 : 0, 0));
if (use_radiance_map) {
state.scene_shader.set_uniform(SceneShaderGLES2::RADIANCE_INVERSE_XFORM, p_view_transform);
}
if (p_shadow) {
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_BIAS, p_shadow_bias);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_NORMAL_BIAS, p_shadow_normal_bias);
}
if (p_env) {
state.scene_shader.set_uniform(SceneShaderGLES2::BG_ENERGY, p_env->bg_energy);
state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_SKY_CONTRIBUTION, p_env->ambient_sky_contribution);
state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_COLOR, p_env->ambient_color);
state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_ENERGY, p_env->ambient_energy);
} else {
state.scene_shader.set_uniform(SceneShaderGLES2::BG_ENERGY, 1.0);
state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_SKY_CONTRIBUTION, 1.0);
state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_COLOR, Color(1.0, 1.0, 1.0, 1.0));
state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_ENERGY, 1.0);
}
glEnable(GL_BLEND);
if (p_alpha_pass || p_directional_add) {
int desired_blend_mode;
if (p_directional_add) {
desired_blend_mode = RasterizerStorageGLES2::Shader::Spatial::BLEND_MODE_ADD;
} else {
desired_blend_mode = material->shader->spatial.blend_mode;
}
switch (desired_blend_mode) {
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 {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
} 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;
}
} else {
// no blend mode given - assume 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 {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
}
state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform.inverse());
state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, p_view_transform);
state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection);
state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, p_projection.inverse());
state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]);
state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size);
state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror?
state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform);
_render_geometry(e);
if (material->shader->spatial.unshaded)
continue;
if (p_shadow)
continue;
for (int light = 0; light < e->instance->light_instances.size(); light++) {
RID light_instance = e->instance->light_instances[light];
lit_objects[light_instance].push_back(e);
}
}
if (p_shadow) {
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_PSSM2, false);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, false);
return;
}
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, true);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
for (int lo = 0; lo < lit_objects.size(); lo++) {
RID key = lit_objects.getk(lo);
LightInstance *light = light_instance_owner.getornull(key);
RasterizerStorageGLES2::Light *light_ptr = light->light_ptr;
const Vector<RenderList::Element *> &list = lit_objects.getv(lo);
for (int i = 0; i < list.size(); i++) {
RenderList::Element *e = list[i];
RasterizerStorageGLES2::Material *material = e->material;
RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton);
{
_setup_geometry(e, skeleton);
_setup_material(material, p_reverse_cull, Size2i(skeleton ? skeleton->size * 3 : 0, 0));
if (shadow_atlas != NULL) {
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4);
glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth);
}
state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform.inverse());
state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, p_view_transform);
state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection);
state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, p_projection.inverse());
state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]);
state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size);
state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror?
state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform);
}
switch (light_ptr->type) {
case VS::LIGHT_OMNI: {
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_TYPE, (int)1);
Vector3 position = p_view_transform.inverse().xform(light->transform.origin);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_POSITION, position);
float range = light_ptr->param[VS::LIGHT_PARAM_RANGE];
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_RANGE, range);
Color attenuation = Color(0.0, 0.0, 0.0, 0.0);
attenuation.a = light_ptr->param[VS::LIGHT_PARAM_ATTENUATION];
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ATTENUATION, attenuation);
if (light_ptr->shadow && shadow_atlas->shadow_owners.has(light->self)) {
uint32_t key = shadow_atlas->shadow_owners[light->self];
uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03;
uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK;
ERR_CONTINUE(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size());
uint32_t atlas_size = shadow_atlas->size;
uint32_t quadrant_size = atlas_size >> 1;
uint32_t x = (quadrant & 1) * quadrant_size;
uint32_t y = (quadrant >> 1) * quadrant_size;
uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision);
x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size;
y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size;
uint32_t width = shadow_size;
uint32_t height = shadow_size;
if (light->light_ptr->omni_shadow_detail == VS::LIGHT_OMNI_SHADOW_DETAIL_HORIZONTAL) {
height /= 2;
} else {
width /= 2;
}
Transform proj = (p_view_transform.inverse() * light->transform).inverse();
Color light_clamp;
light_clamp[0] = float(x) / atlas_size;
light_clamp[1] = float(y) / atlas_size;
light_clamp[2] = float(width) / atlas_size;
light_clamp[3] = float(height) / atlas_size;
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX, proj);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 1.0);
} else {
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0);
}
} break;
case VS::LIGHT_SPOT: {
Vector3 position = p_view_transform.inverse().xform(light->transform.origin);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_TYPE, (int)2);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_POSITION, position);
Vector3 direction = p_view_transform.inverse().basis.xform(light->transform.basis.xform(Vector3(0, 0, -1))).normalized();
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_DIRECTION, direction);
Color attenuation = Color(0.0, 0.0, 0.0, 0.0);
attenuation.a = light_ptr->param[VS::LIGHT_PARAM_ATTENUATION];
float range = light_ptr->param[VS::LIGHT_PARAM_RANGE];
float spot_attenuation = light_ptr->param[VS::LIGHT_PARAM_SPOT_ATTENUATION];
float angle = light_ptr->param[VS::LIGHT_PARAM_SPOT_ANGLE];
angle = Math::cos(Math::deg2rad(angle));
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ATTENUATION, attenuation);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_ATTENUATION, spot_attenuation);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_RANGE, spot_attenuation);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPOT_ANGLE, angle);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_RANGE, range);
if (light->light_ptr->shadow && shadow_atlas && shadow_atlas->shadow_owners.has(light->self)) {
uint32_t key = shadow_atlas->shadow_owners[light->self];
uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03;
uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK;
ERR_CONTINUE(shadow >= (uint32_t)shadow_atlas->quadrants[quadrant].shadows.size());
uint32_t atlas_size = shadow_atlas->size;
uint32_t quadrant_size = atlas_size >> 1;
uint32_t x = (quadrant & 1) * quadrant_size;
uint32_t y = (quadrant >> 1) * quadrant_size;
uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision);
x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size;
y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size;
uint32_t width = shadow_size;
uint32_t height = shadow_size;
Rect2 rect(float(x) / atlas_size, float(y) / atlas_size, float(width) / atlas_size, float(height) / atlas_size);
Color light_clamp;
light_clamp[0] = rect.position.x;
light_clamp[1] = rect.position.y;
light_clamp[2] = rect.size.x;
light_clamp[3] = rect.size.y;
Transform modelview = (p_view_transform.inverse() * light->transform).inverse();
CameraMatrix bias;
bias.set_light_bias();
CameraMatrix rectm;
rectm.set_light_atlas_rect(rect);
CameraMatrix shadow_matrix = rectm * bias * light->shadow_transform[0].camera * modelview;
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 1.0);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX, shadow_matrix);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp);
} else {
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0);
}
} break;
default: break;
}
float energy = light->light_ptr->param[VS::LIGHT_PARAM_ENERGY];
float specular = light->light_ptr->param[VS::LIGHT_PARAM_SPECULAR];
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ENERGY, energy);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_COLOR, light->light_ptr->color.to_linear());
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPECULAR, specular);
_render_geometry(e);
}
}
for (int dl = 0; dl < p_directional_light_count; dl++) {
RID light_rid = p_directional_lights[dl];
LightInstance *light = light_instance_owner.getornull(light_rid);
RasterizerStorageGLES2::Light *light_ptr = light->light_ptr;
switch (light_ptr->directional_shadow_mode) {
case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: {
} break;
case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: {
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM2, true);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, light_ptr->directional_blend_splits);
} break;
case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: {
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM4, true);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, light_ptr->directional_blend_splits);
} break;
default:
break;
}
for (int i = 0; i < p_element_count; i++) {
RenderList::Element *e = p_elements[i];
RasterizerStorageGLES2::Material *material = e->material;
RasterizerStorageGLES2::Skeleton *skeleton = storage->skeleton_owner.getornull(e->instance->skeleton);
{
_setup_material(material, p_reverse_cull, Size2i(skeleton ? skeleton->size * 3 : 0, 0));
if (directional_shadow.depth) {
glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 4); // TODO move into base pass
glBindTexture(GL_TEXTURE_2D, directional_shadow.depth);
}
state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_MATRIX, p_view_transform.inverse());
state.scene_shader.set_uniform(SceneShaderGLES2::CAMERA_INVERSE_MATRIX, p_view_transform);
state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_MATRIX, p_projection);
state.scene_shader.set_uniform(SceneShaderGLES2::PROJECTION_INVERSE_MATRIX, p_projection.inverse());
state.scene_shader.set_uniform(SceneShaderGLES2::TIME, storage->frame.time[0]);
state.scene_shader.set_uniform(SceneShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size);
state.scene_shader.set_uniform(SceneShaderGLES2::NORMAL_MULT, 1.0); // TODO mirror?
state.scene_shader.set_uniform(SceneShaderGLES2::WORLD_TRANSFORM, e->instance->transform);
}
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_TYPE, (int)0);
Vector3 direction = p_view_transform.inverse().basis.xform(light->transform.basis.xform(Vector3(0, 0, -1))).normalized();
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_DIRECTION, direction);
float energy = light_ptr->param[VS::LIGHT_PARAM_ENERGY];
float specular = light_ptr->param[VS::LIGHT_PARAM_SPECULAR];
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_ENERGY, energy);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPECULAR, specular);
float sign = light_ptr->negative ? -1 : 1;
Color linear_col = light_ptr->color.to_linear();
Color color;
for (int c = 0; c < 3; c++)
color[c] = linear_col[c] * sign * energy * Math_PI;
color[3] = 0;
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_COLOR, color);
CameraMatrix matrices[4];
if (light_ptr->shadow && directional_shadow.depth) {
int shadow_count = 0;
Color split_offsets;
switch (light_ptr->directional_shadow_mode) {
case VS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL: {
shadow_count = 1;
} break;
case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS: {
shadow_count = 2;
} break;
case VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS: {
shadow_count = 4;
} break;
}
for (int k = 0; k < shadow_count; k++) {
uint32_t x = light->directional_rect.position.x;
uint32_t y = light->directional_rect.position.y;
uint32_t width = light->directional_rect.size.x;
uint32_t height = light->directional_rect.size.y;
if (light_ptr->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) {
width /= 2;
height /= 2;
if (k == 0) {
} else if (k == 1) {
x += width;
} else if (k == 2) {
y += height;
} else if (k == 3) {
x += width;
y += height;
}
} else if (light_ptr->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) {
height /= 2;
if (k == 0) {
} else {
y += height;
}
}
split_offsets[k] = light->shadow_transform[k].split;
Transform modelview = (p_view_transform * light->shadow_transform[k].transform).inverse();
CameraMatrix bias;
bias.set_light_bias();
CameraMatrix rectm;
Rect2 atlas_rect = Rect2(float(x) / directional_shadow.size, float(y) / directional_shadow.size, float(width) / directional_shadow.size, float(height) / directional_shadow.size);
rectm.set_light_atlas_rect(atlas_rect);
CameraMatrix shadow_mtx = rectm * bias * light->shadow_transform[k].camera * modelview;
matrices[k] = shadow_mtx.inverse();
Color light_clamp;
light_clamp[0] = atlas_rect.position.x;
light_clamp[1] = atlas_rect.position.y;
light_clamp[2] = atlas_rect.size.x;
light_clamp[3] = atlas_rect.size.y;
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 1.0);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_CLAMP, light_clamp);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SPLIT_OFFSETS, split_offsets);
}
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX1, matrices[0]);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX2, matrices[1]);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX3, matrices[2]);
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_SHADOW_MATRIX4, matrices[3]);
} else {
state.scene_shader.set_uniform(SceneShaderGLES2::LIGHT_HAS_SHADOW, 0.0);
}
_render_geometry(e);
}
}
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_PASS, 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_PSSM2, false);
state.scene_shader.set_conditional(SceneShaderGLES2::LIGHT_USE_PSSM_BLEND, false);
}
void RasterizerSceneGLES2::_draw_sky(RasterizerStorageGLES2::Sky *p_sky, const CameraMatrix &p_projection, const Transform &p_transform, bool p_vflip, float p_custom_fov, float p_energy) {
ERR_FAIL_COND(!p_sky);
RasterizerStorageGLES2::Texture *tex = storage->texture_owner.getornull(p_sky->panorama);
ERR_FAIL_COND(!tex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(tex->target, tex->tex_id);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_BLEND);
glDepthFunc(GL_LEQUAL);
glColorMask(1, 1, 1, 1);
// Camera
CameraMatrix camera;
if (p_custom_fov) {
float near_plane = p_projection.get_z_near();
float far_plane = p_projection.get_z_far();
float aspect = p_projection.get_aspect();
camera.set_perspective(p_custom_fov, aspect, near_plane, far_plane);
} else {
camera = p_projection;
}
float flip_sign = p_vflip ? -1 : 1;
// If matrix[2][0] or matrix[2][1] we're dealing with an asymmetrical projection matrix. This is the case for stereoscopic rendering (i.e. VR).
// To ensure the image rendered is perspective correct we need to move some logic into the shader. For this the USE_ASYM_PANO option is introduced.
// It also means the uv coordinates are ignored in this mode and we don't need our loop.
bool asymmetrical = ((camera.matrix[2][0] != 0.0) || (camera.matrix[2][1] != 0.0));
Vector3 vertices[8] = {
Vector3(-1, -1 * flip_sign, 1),
Vector3(0, 1, 0),
Vector3(1, -1 * flip_sign, 1),
Vector3(1, 1, 0),
Vector3(1, 1 * flip_sign, 1),
Vector3(1, 0, 0),
Vector3(-1, 1 * flip_sign, 1),
Vector3(0, 0, 0),
};
if (!asymmetrical) {
float vw, vh, zn;
camera.get_viewport_size(vw, vh);
zn = p_projection.get_z_near();
for (int i = 0; i < 4; i++) {
Vector3 uv = vertices[i * 2 + 1];
uv.x = (uv.x * 2.0 - 1.0) * vw;
uv.y = -(uv.y * 2.0 - 1.0) * vh;
uv.z = -zn;
vertices[i * 2 + 1] = p_transform.basis.xform(uv).normalized();
vertices[i * 2 + 1].z = -vertices[i * 2 + 1].z;
}
}
glBindBuffer(GL_ARRAY_BUFFER, state.sky_verts);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vector3) * 8, vertices);
// bind sky vertex array....
glVertexAttribPointer(VS::ARRAY_VERTEX, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3) * 2, 0);
glVertexAttribPointer(VS::ARRAY_TEX_UV, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3) * 2, ((uint8_t *)NULL) + sizeof(Vector3));
glEnableVertexAttribArray(VS::ARRAY_VERTEX);
glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, true);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, true);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_COPY_SECTION, false);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUSTOM_ALPHA, false);
storage->shaders.copy.bind();
storage->shaders.copy.set_uniform(CopyShaderGLES2::MULTIPLIER, p_energy);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableVertexAttribArray(VS::ARRAY_VERTEX);
glDisableVertexAttribArray(VS::ARRAY_TEX_UV);
glBindBuffer(GL_ARRAY_BUFFER, 0);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, false);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false);
}
void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID p_environment, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {
glEnable(GL_BLEND);
GLuint current_fb = storage->frame.current_rt->fbo;
Environment *env = environment_owner.getornull(p_environment);
// render list stuff
render_list.clear();
_fill_render_list(p_cull_result, p_cull_count, false, false);
// other stuff
glBindFramebuffer(GL_FRAMEBUFFER, current_fb);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
storage->frame.clear_request = false;
glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// render sky
RasterizerStorageGLES2::Sky *sky = NULL;
GLuint env_radiance_tex = 0;
if (env) {
switch (env->bg_mode) {
case VS::ENV_BG_COLOR_SKY:
case VS::ENV_BG_SKY: {
sky = storage->sky_owner.getornull(env->sky);
if (sky) {
env_radiance_tex = sky->radiance;
}
} break;
default: {
print_line("uhm");
} break;
}
}
if (env && env->bg_mode == VS::ENV_BG_SKY && (!storage->frame.current_rt || !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT])) {
if (sky && sky->panorama.is_valid()) {
_draw_sky(sky, p_cam_projection, p_cam_transform, false, env->sky_custom_fov, env->bg_energy);
}
}
Vector<RID> directional_lights;
for (int i = 0; i < p_light_cull_count; i++) {
RID light_rid = p_light_cull_result[i];
LightInstance *light = light_instance_owner.getornull(light_rid);
if (light->light_ptr->type == VS::LIGHT_DIRECTIONAL) {
directional_lights.push_back(light_rid);
}
}
// render opaque things first
render_list.sort_by_key(false);
_render_render_list(render_list.elements, render_list.element_count, directional_lights.ptr(), directional_lights.size(), p_cam_transform, p_cam_projection, p_shadow_atlas, env, env_radiance_tex, 0.0, 0.0, false, false, false, false);
// alpha pass
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
render_list.sort_by_key(true);
_render_render_list(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, directional_lights.ptr(), directional_lights.size(), p_cam_transform, p_cam_projection, p_shadow_atlas, env, env_radiance_tex, 0.0, 0.0, false, true, false, false);
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
// #define GLES2_SHADOW_ATLAS_DEBUG_VIEW
#ifdef GLES2_SHADOW_ATLAS_DEBUG_VIEW
ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas);
if (shadow_atlas) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, shadow_atlas->depth);
glViewport(0, 0, storage->frame.current_rt->width / 4, storage->frame.current_rt->height / 4);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUBEMAP, false);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_COPY_SECTION, false);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_CUSTOM_ALPHA, false);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_MULTIPLIER, false);
storage->shaders.copy.set_conditional(CopyShaderGLES2::USE_PANORAMA, false);
storage->shaders.copy.bind();
storage->_copy_screen();
}
#endif
}
void RasterizerSceneGLES2::render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) {
LightInstance *light_instance = light_instance_owner.getornull(p_light);
ERR_FAIL_COND(!light_instance);
RasterizerStorageGLES2::Light *light = light_instance->light_ptr;
ERR_FAIL_COND(!light);
uint32_t x;
uint32_t y;
uint32_t width;
uint32_t height;
uint32_t vp_height;
float zfar = 0;
bool flip_facing = false;
int custom_vp_size = 0;
GLuint fbo = 0;
int current_cubemap = -1;
float bias = 0;
float normal_bias = 0;
CameraMatrix light_projection;
Transform light_transform;
// TODO directional light
if (light->type == VS::LIGHT_DIRECTIONAL) {
// set pssm stuff
// TODO set this only when changed
light_instance->light_directional_index = directional_shadow.current_light;
light_instance->last_scene_shadow_pass = scene_pass;
directional_shadow.current_light++;
if (directional_shadow.light_count == 1) {
light_instance->directional_rect = Rect2(0, 0, directional_shadow.size, directional_shadow.size);
} else if (directional_shadow.light_count == 2) {
light_instance->directional_rect = Rect2(0, 0, directional_shadow.size, directional_shadow.size / 2);
if (light_instance->light_directional_index == 1) {
light_instance->directional_rect.position.x += light_instance->directional_rect.size.x;
}
} else { //3 and 4
light_instance->directional_rect = Rect2(0, 0, directional_shadow.size / 2, directional_shadow.size / 2);
if (light_instance->light_directional_index & 1) {
light_instance->directional_rect.position.x += light_instance->directional_rect.size.x;
}
if (light_instance->light_directional_index / 2) {
light_instance->directional_rect.position.y += light_instance->directional_rect.size.y;
}
}
light_projection = light_instance->shadow_transform[p_pass].camera;
light_transform = light_instance->shadow_transform[p_pass].transform;
x = light_instance->directional_rect.position.x;
y = light_instance->directional_rect.position.y;
width = light_instance->directional_rect.size.width;
height = light_instance->directional_rect.size.height;
if (light->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) {
width /= 2;
height /= 2;
if (p_pass == 0) {
} else if (p_pass == 1) {
x += width;
} else if (p_pass == 2) {
y += height;
} else if (p_pass == 3) {
x += width;
y += height;
}
} else if (light->directional_shadow_mode == VS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) {
height /= 2;
if (p_pass == 0) {
} else {
y += height;
}
}
float bias_mult = Math::lerp(1.0f, light_instance->shadow_transform[p_pass].bias_scale, light->param[VS::LIGHT_PARAM_SHADOW_BIAS_SPLIT_SCALE]);
zfar = light->param[VS::LIGHT_PARAM_RANGE];
bias = light->param[VS::LIGHT_PARAM_SHADOW_BIAS] * bias_mult;
normal_bias = light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] * bias_mult;
fbo = directional_shadow.fbo;
vp_height = directional_shadow.size;
} else {
ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas);
ERR_FAIL_COND(!shadow_atlas);
ERR_FAIL_COND(!shadow_atlas->shadow_owners.has(p_light));
fbo = shadow_atlas->fbo;
vp_height = shadow_atlas->size;
uint32_t key = shadow_atlas->shadow_owners[p_light];
uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x03;
uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK;
ERR_FAIL_INDEX((int)shadow, shadow_atlas->quadrants[quadrant].shadows.size());
uint32_t quadrant_size = shadow_atlas->size >> 1;
x = (quadrant & 1) * quadrant_size;
y = (quadrant >> 1) * quadrant_size;
uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision);
x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size;
y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size;
width = shadow_size;
height = shadow_size;
if (light->type == VS::LIGHT_OMNI) {
// cubemap only
if (light->omni_shadow_mode == VS::LIGHT_OMNI_SHADOW_CUBE) {
int cubemap_index = shadow_cubemaps.size() - 1;
// find an appropriate cubemap to render to
for (int i = shadow_cubemaps.size() - 1; i >= 0; i--) {
if (shadow_cubemaps[i].size > shadow_size * 2) {
break;
}
cubemap_index = i;
}
fbo = shadow_cubemaps[cubemap_index].fbo[p_pass];
light_projection = light_instance->shadow_transform[0].camera;
light_transform = light_instance->shadow_transform[0].transform;
custom_vp_size = shadow_cubemaps[cubemap_index].size;
zfar = light->param[VS::LIGHT_PARAM_RANGE];
current_cubemap = cubemap_index;
}
} else {
light_projection = light_instance->shadow_transform[0].camera;
light_transform = light_instance->shadow_transform[0].transform;
flip_facing = false;
zfar = light->param[VS::LIGHT_PARAM_RANGE];
bias = light->param[VS::LIGHT_PARAM_SHADOW_BIAS];
normal_bias = light->param[VS::LIGHT_PARAM_SHADOW_NORMAL_BIAS];
}
}
render_list.clear();
_fill_render_list(p_cull_result, p_cull_count, true, true);
render_list.sort_by_depth(false);
glDisable(GL_BLEND);
glDisable(GL_DITHER);
glEnable(GL_DEPTH_TEST);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glDepthMask(GL_TRUE);
glColorMask(0, 0, 0, 0);
if (custom_vp_size) {
glViewport(0, 0, custom_vp_size, custom_vp_size);
glScissor(0, 0, custom_vp_size, custom_vp_size);
} else {
glViewport(x, y, width, height);
glScissor(x, y, width, height);
}
glEnable(GL_SCISSOR_TEST);
glClearDepth(1.0f);
glClear(GL_DEPTH_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
state.scene_shader.set_conditional(SceneShaderGLES2::RENDER_DEPTH, true);
_render_render_list(render_list.elements, render_list.element_count, NULL, 0, light_transform, light_projection, RID(), NULL, 0, bias, normal_bias, false, false, true, false);
state.scene_shader.set_conditional(SceneShaderGLES2::RENDER_DEPTH, false);
// convert cubemap to dual paraboloid if needed
if (light->type == VS::LIGHT_OMNI && light->omni_shadow_mode == VS::LIGHT_OMNI_SHADOW_CUBE && p_pass == 5) {
ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas);
glBindFramebuffer(GL_FRAMEBUFFER, shadow_atlas->fbo);
state.cube_to_dp_shader.bind();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, shadow_cubemaps[current_cubemap].cubemap);
glDisable(GL_CULL_FACE);
for (int i = 0; i < 2; i++) {
state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::Z_FLIP, i == 1);
state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::Z_NEAR, light_projection.get_z_near());
state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::Z_FAR, light_projection.get_z_far());
state.cube_to_dp_shader.set_uniform(CubeToDpShaderGLES2::BIAS, light->param[VS::LIGHT_PARAM_SHADOW_BIAS]);
uint32_t local_width = width;
uint32_t local_height = height;
uint32_t local_x = x;
uint32_t local_y = y;
if (light->omni_shadow_detail == VS::LIGHT_OMNI_SHADOW_DETAIL_HORIZONTAL) {
local_height /= 2;
local_y += i * local_height;
} else {
local_width /= 2;
local_x += i * local_width;
}
glViewport(local_x, local_y, local_width, local_height);
glScissor(local_x, local_y, local_width, local_height);
glEnable(GL_SCISSOR_TEST);
glClearDepth(1.0f);
glClear(GL_DEPTH_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
storage->_copy_screen();
}
}
glViewport(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height);
}
void RasterizerSceneGLES2::set_scene_pass(uint64_t p_pass) {
scene_pass = p_pass;
}
bool RasterizerSceneGLES2::free(RID p_rid) {
return true;
}
void RasterizerSceneGLES2::set_debug_draw_mode(VS::ViewportDebugDraw p_debug_draw) {
}
void RasterizerSceneGLES2::initialize() {
state.scene_shader.init();
state.cube_to_dp_shader.init();
render_list.init();
shadow_atlas_realloc_tolerance_msec = 500;
{
//default material and shader
default_shader = storage->shader_create();
storage->shader_set_code(default_shader, "shader_type spatial;\n");
default_material = storage->material_create();
storage->material_set_shader(default_material, default_shader);
default_shader_twosided = storage->shader_create();
default_material_twosided = 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);
}
{
glGenBuffers(1, &state.sky_verts);
glBindBuffer(GL_ARRAY_BUFFER, state.sky_verts);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vector3) * 8, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
{
uint32_t immediate_buffer_size = GLOBAL_DEF("rendering/limits/buffers/immediate_buffer_size_kb", 2048);
glGenBuffers(1, &state.immediate_buffer);
glBindBuffer(GL_ARRAY_BUFFER, state.immediate_buffer);
glBufferData(GL_ARRAY_BUFFER, immediate_buffer_size * 1024, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// cubemaps for shadows
{
int max_shadow_cubemap_sampler_size = 512;
int cube_size = max_shadow_cubemap_sampler_size;
glActiveTexture(GL_TEXTURE0);
while (cube_size >= 32) {
ShadowCubeMap cube;
cube.size = cube_size;
glGenTextures(1, &cube.cubemap);
glBindTexture(GL_TEXTURE_CUBE_MAP, cube.cubemap);
for (int i = 0; i < 6; i++) {
glTexImage2D(_cube_side_enum[i], 0, GL_DEPTH_COMPONENT16, cube_size, cube_size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glGenFramebuffers(6, cube.fbo);
for (int i = 0; i < 6; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, cube.fbo[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, _cube_side_enum[i], cube.cubemap, 0);
}
shadow_cubemaps.push_back(cube);
cube_size >>= 1;
}
}
{
// directional shadows
directional_shadow.light_count = 0;
directional_shadow.size = next_power_of_2(GLOBAL_GET("rendering/quality/directional_shadow/size"));
glGenFramebuffers(1, &directional_shadow.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, directional_shadow.fbo);
glGenTextures(1, &directional_shadow.depth);
glBindTexture(GL_TEXTURE_2D, directional_shadow.depth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, directional_shadow.size, directional_shadow.size, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, directional_shadow.depth, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
ERR_PRINT("Directional shadow framebuffer status invalid");
}
}
}
void RasterizerSceneGLES2::iteration() {
}
void RasterizerSceneGLES2::finalize() {
}
RasterizerSceneGLES2::RasterizerSceneGLES2() {
}