This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/gpu/intern/gpu_lamp.c
Dalai Felinto 2325d15d02 Lamps should not have their own gpu material
This was leading to multiple crashes when freeing the lamps or
materials when opening old files.

Follow up on b50839038d.
2017-07-11 11:02:56 +02:00

484 lines
12 KiB
C

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2006 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Brecht Van Lommel, Clément Foucault.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/gpu/intern/gpu_lamp.c
* \ingroup gpu
*
* Manages Opengl lights.
*/
#include "MEM_guardedalloc.h"
#include "DNA_lamp_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BKE_group.h"
#include "GPU_framebuffer.h"
#include "GPU_glew.h"
#include "GPU_lamp.h"
#include "GPU_material.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
#include "gpu_lamp_private.h"
bool GPU_lamp_override_visible(GPULamp *lamp, SceneRenderLayer *srl, Material *ma)
{
if (srl && srl->light_override)
return BKE_group_object_exists(srl->light_override, lamp->ob);
else if (ma && ma->group)
return BKE_group_object_exists(ma->group, lamp->ob);
else
return true;
}
static void gpu_lamp_calc_winmat(GPULamp *lamp)
{
float temp, angle, pixsize, wsize;
if (lamp->type == LA_SUN) {
wsize = lamp->la->shadow_frustum_size;
orthographic_m4(lamp->winmat, -wsize, wsize, -wsize, wsize, lamp->d, lamp->clipend);
}
else if (lamp->type == LA_SPOT) {
angle = saacos(lamp->spotsi);
temp = 0.5f * lamp->size * cosf(angle) / sinf(angle);
pixsize = lamp->d / temp;
wsize = pixsize * 0.5f * lamp->size;
/* compute shadows according to X and Y scaling factors */
perspective_m4(
lamp->winmat,
-wsize * lamp->spotvec[0], wsize * lamp->spotvec[0],
-wsize * lamp->spotvec[1], wsize * lamp->spotvec[1],
lamp->d, lamp->clipend);
}
}
void GPU_lamp_update(GPULamp *lamp, int lay, int hide, float obmat[4][4])
{
float mat[4][4];
float obmat_scale[3];
lamp->lay = lay;
lamp->hide = hide;
normalize_m4_m4_ex(mat, obmat, obmat_scale);
copy_v3_v3(lamp->vec, mat[2]);
copy_v3_v3(lamp->co, mat[3]);
copy_m4_m4(lamp->obmat, mat);
invert_m4_m4(lamp->imat, mat);
if (lamp->type == LA_SPOT) {
/* update spotlamp scale on X and Y axis */
lamp->spotvec[0] = obmat_scale[0] / obmat_scale[2];
lamp->spotvec[1] = obmat_scale[1] / obmat_scale[2];
}
if (GPU_lamp_has_shadow_buffer(lamp)) {
/* makeshadowbuf */
gpu_lamp_calc_winmat(lamp);
}
}
void GPU_lamp_update_colors(GPULamp *lamp, float r, float g, float b, float energy)
{
lamp->energy = energy;
if (lamp->mode & LA_NEG) lamp->energy = -lamp->energy;
lamp->col[0] = r;
lamp->col[1] = g;
lamp->col[2] = b;
}
void GPU_lamp_update_distance(GPULamp *lamp, float distance, float att1, float att2,
float coeff_const, float coeff_lin, float coeff_quad)
{
lamp->dist = distance;
lamp->att1 = att1;
lamp->att2 = att2;
lamp->coeff_const = coeff_const;
lamp->coeff_lin = coeff_lin;
lamp->coeff_quad = coeff_quad;
}
void GPU_lamp_update_spot(GPULamp *lamp, float spotsize, float spotblend)
{
lamp->spotsi = cosf(spotsize * 0.5f);
lamp->spotbl = (1.0f - lamp->spotsi) * spotblend;
}
static void gpu_lamp_from_blender(Scene *scene, Object *ob, Object *par, Lamp *la, GPULamp *lamp)
{
lamp->scene = scene;
lamp->ob = ob;
lamp->par = par;
lamp->la = la;
/* add_render_lamp */
lamp->mode = la->mode;
lamp->type = la->type;
lamp->energy = la->energy;
if (lamp->mode & LA_NEG) lamp->energy = -lamp->energy;
lamp->col[0] = la->r;
lamp->col[1] = la->g;
lamp->col[2] = la->b;
GPU_lamp_update(lamp, ob->lay, (ob->restrictflag & OB_RESTRICT_RENDER), ob->obmat);
lamp->spotsi = la->spotsize;
if (lamp->mode & LA_HALO)
if (lamp->spotsi > DEG2RADF(170.0f))
lamp->spotsi = DEG2RADF(170.0f);
lamp->spotsi = cosf(lamp->spotsi * 0.5f);
lamp->spotbl = (1.0f - lamp->spotsi) * la->spotblend;
lamp->k = la->k;
lamp->dist = la->dist;
lamp->falloff_type = la->falloff_type;
lamp->att1 = la->att1;
lamp->att2 = la->att2;
lamp->coeff_const = la->coeff_const;
lamp->coeff_lin = la->coeff_lin;
lamp->coeff_quad = la->coeff_quad;
lamp->curfalloff = la->curfalloff;
/* initshadowbuf */
lamp->bias = 0.02f * la->bias;
lamp->size = la->bufsize;
lamp->d = la->clipsta;
lamp->clipend = la->clipend;
/* arbitrary correction for the fact we do no soft transition */
lamp->bias *= 0.25f;
}
static void gpu_lamp_shadow_free(GPULamp *lamp)
{
if (lamp->tex) {
GPU_texture_free(lamp->tex);
lamp->tex = NULL;
}
if (lamp->depthtex) {
GPU_texture_free(lamp->depthtex);
lamp->depthtex = NULL;
}
if (lamp->fb) {
GPU_framebuffer_free(lamp->fb);
lamp->fb = NULL;
}
if (lamp->blurtex) {
GPU_texture_free(lamp->blurtex);
lamp->blurtex = NULL;
}
if (lamp->blurfb) {
GPU_framebuffer_free(lamp->blurfb);
lamp->blurfb = NULL;
}
}
static GPUTexture *gpu_lamp_create_vsm_shadow_map(int size)
{
return GPU_texture_create_2D_custom(size, size, 2, GPU_RG32F, NULL, NULL);
}
LampEngineData *GPU_lamp_engine_data_get(Scene *scene, Object *ob, Object *par, struct RenderEngineType *re)
{
GPULamp *lamp;
LinkData *link;
for (link = ob->gpulamp.first; link; link = link->next) {
lamp = (GPULamp *)link->data;
if ((lamp->par == par) && (lamp->scene == scene) && (lamp->re == re))
return &lamp->data;
}
lamp = MEM_callocN(sizeof(GPULamp), "GPULamp");
link = MEM_callocN(sizeof(LinkData), "GPULampLink");
link->data = lamp;
BLI_addtail(&ob->gpulamp, link);
lamp->scene = scene;
lamp->ob = ob;
lamp->par = par;
lamp->la = ob->data;
lamp->re = re;
return &lamp->data;
}
GPULamp *GPU_lamp_from_blender(Scene *scene, Object *ob, Object *par)
{
Lamp *la;
GPULamp *lamp;
LinkData *link;
for (link = ob->gpulamp.first; link; link = link->next) {
lamp = (GPULamp *)link->data;
if (lamp->par == par && lamp->scene == scene)
return link->data;
}
lamp = MEM_callocN(sizeof(GPULamp), "GPULamp");
link = MEM_callocN(sizeof(LinkData), "GPULampLink");
link->data = lamp;
BLI_addtail(&ob->gpulamp, link);
la = ob->data;
gpu_lamp_from_blender(scene, ob, par, la, lamp);
if ((la->type == LA_SPOT && (la->mode & (LA_SHAD_BUF | LA_SHAD_RAY))) ||
(la->type == LA_SUN && (la->mode & LA_SHAD_RAY)))
{
/* opengl */
lamp->fb = GPU_framebuffer_create();
if (!lamp->fb) {
gpu_lamp_shadow_free(lamp);
return lamp;
}
if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) {
/* Shadow depth map */
lamp->depthtex = GPU_texture_create_depth(lamp->size, lamp->size, NULL);
if (!lamp->depthtex) {
gpu_lamp_shadow_free(lamp);
return lamp;
}
GPU_texture_bind(lamp->depthtex, 0);
GPU_texture_compare_mode(lamp->depthtex, true);
GPU_texture_unbind(lamp->depthtex);
if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->depthtex, 0, 0)) {
gpu_lamp_shadow_free(lamp);
return lamp;
}
/* Shadow color map */
lamp->tex = gpu_lamp_create_vsm_shadow_map(lamp->size);
if (!lamp->tex) {
gpu_lamp_shadow_free(lamp);
return lamp;
}
if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, 0)) {
gpu_lamp_shadow_free(lamp);
return lamp;
}
if (!GPU_framebuffer_check_valid(lamp->fb, NULL)) {
gpu_lamp_shadow_free(lamp);
return lamp;
}
/* FBO and texture for blurring */
lamp->blurfb = GPU_framebuffer_create();
if (!lamp->blurfb) {
gpu_lamp_shadow_free(lamp);
return lamp;
}
lamp->blurtex = gpu_lamp_create_vsm_shadow_map(lamp->size * 0.5);
if (!lamp->blurtex) {
gpu_lamp_shadow_free(lamp);
return lamp;
}
if (!GPU_framebuffer_texture_attach(lamp->blurfb, lamp->blurtex, 0, 0)) {
gpu_lamp_shadow_free(lamp);
return lamp;
}
/* we need to properly bind to test for completeness */
GPU_texture_bind_as_framebuffer(lamp->blurtex);
if (!GPU_framebuffer_check_valid(lamp->blurfb, NULL)) {
gpu_lamp_shadow_free(lamp);
return lamp;
}
GPU_framebuffer_texture_unbind(lamp->blurfb, lamp->blurtex);
}
else {
lamp->tex = GPU_texture_create_depth(lamp->size, lamp->size, NULL);
if (!lamp->tex) {
gpu_lamp_shadow_free(lamp);
return lamp;
}
GPU_texture_bind(lamp->tex, 0);
GPU_texture_compare_mode(lamp->tex, true);
GPU_texture_unbind(lamp->tex);
if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, 0)) {
gpu_lamp_shadow_free(lamp);
return lamp;
}
if (!GPU_framebuffer_check_valid(lamp->fb, NULL)) {
gpu_lamp_shadow_free(lamp);
return lamp;
}
}
GPU_framebuffer_restore();
lamp->shadow_color[0] = la->shdwr;
lamp->shadow_color[1] = la->shdwg;
lamp->shadow_color[2] = la->shdwb;
}
else {
lamp->shadow_color[0] = 1.0;
lamp->shadow_color[1] = 1.0;
lamp->shadow_color[2] = 1.0;
}
return lamp;
}
void GPU_lamp_engine_data_free(LampEngineData *led)
{
for (int i = 0; i < MAX_LAMP_DATA; ++i) {
if (led->storage[i]) {
MEM_freeN(led->storage[i]);
led->storage[i] = NULL;
}
}
}
void GPU_lamp_free(Object *ob)
{
GPULamp *lamp;
LinkData *link;
for (link = ob->gpulamp.first; link; link = link->next) {
lamp = link->data;
gpu_lamp_shadow_free(lamp);
GPU_lamp_engine_data_free(&lamp->data);
MEM_freeN(lamp);
}
BLI_freelistN(&ob->gpulamp);
}
bool GPU_lamp_has_shadow_buffer(GPULamp *lamp)
{
return (!(lamp->scene->gm.flag & GAME_GLSL_NO_SHADOWS) &&
!(lamp->scene->gm.flag & GAME_GLSL_NO_LIGHTS) &&
lamp->tex && lamp->fb);
}
void GPU_lamp_update_buffer_mats(GPULamp *lamp)
{
float rangemat[4][4], persmat[4][4];
/* initshadowbuf */
invert_m4_m4(lamp->viewmat, lamp->obmat);
normalize_v3(lamp->viewmat[0]);
normalize_v3(lamp->viewmat[1]);
normalize_v3(lamp->viewmat[2]);
/* makeshadowbuf */
mul_m4_m4m4(persmat, lamp->winmat, lamp->viewmat);
/* opengl depth buffer is range 0.0..1.0 instead of -1.0..1.0 in blender */
unit_m4(rangemat);
rangemat[0][0] = 0.5f;
rangemat[1][1] = 0.5f;
rangemat[2][2] = 0.5f;
rangemat[3][0] = 0.5f;
rangemat[3][1] = 0.5f;
rangemat[3][2] = 0.5f;
mul_m4_m4m4(lamp->persmat, rangemat, persmat);
}
void GPU_lamp_shadow_buffer_bind(GPULamp *lamp, float viewmat[4][4], int *winsize, float winmat[4][4])
{
GPU_lamp_update_buffer_mats(lamp);
/* opengl */
glDisable(GL_SCISSOR_TEST);
GPU_texture_bind_as_framebuffer(lamp->tex);
if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE)
GPU_shader_bind(GPU_shader_get_builtin_shader(GPU_SHADER_VSM_STORE));
/* set matrices */
copy_m4_m4(viewmat, lamp->viewmat);
copy_m4_m4(winmat, lamp->winmat);
*winsize = lamp->size;
}
void GPU_lamp_shadow_buffer_unbind(GPULamp *lamp)
{
if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) {
GPU_shader_unbind();
GPU_framebuffer_blur(lamp->fb, lamp->tex, lamp->blurfb, lamp->blurtex);
}
GPU_framebuffer_texture_unbind(lamp->fb, lamp->tex);
GPU_framebuffer_restore();
glEnable(GL_SCISSOR_TEST);
}
int GPU_lamp_shadow_buffer_type(GPULamp *lamp)
{
return lamp->la->shadowmap_type;
}
int GPU_lamp_shadow_bind_code(GPULamp *lamp)
{
return lamp->tex ? GPU_texture_opengl_bindcode(lamp->tex) : -1;
}
float *GPU_lamp_dynpersmat(GPULamp *lamp)
{
return &lamp->dynpersmat[0][0];
}
int GPU_lamp_shadow_layer(GPULamp *lamp)
{
if (lamp->fb && lamp->tex && (lamp->mode & (LA_LAYER | LA_LAYER_SHADOW)))
return lamp->lay;
else
return -1;
}