2462 lines
84 KiB
C++
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() {
|
|
}
|