Merge branch 'master' into blender2.8
This commit is contained in:
163
source/blender/nodes/shader/nodes/node_shader_geom.c
Normal file
163
source/blender/nodes/shader/nodes/node_shader_geom.c
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* ***** 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) 2005 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): none yet.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/nodes/shader/nodes/node_shader_geom.c
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
|
||||
#include "node_shader_util.h"
|
||||
|
||||
#include "DNA_customdata_types.h"
|
||||
|
||||
/* **************** GEOMETRY ******************** */
|
||||
|
||||
/* output socket type definition */
|
||||
static bNodeSocketTemplate sh_node_geom_out[] = {
|
||||
{ SOCK_VECTOR, 0, N_("Global")},
|
||||
{ SOCK_VECTOR, 0, N_("Local")},
|
||||
{ SOCK_VECTOR, 0, N_("View")},
|
||||
{ SOCK_VECTOR, 0, N_("Orco")},
|
||||
{ SOCK_VECTOR, 0, N_("UV")},
|
||||
{ SOCK_VECTOR, 0, N_("Normal")},
|
||||
{ SOCK_RGBA, 0, N_("Vertex Color")},
|
||||
{ SOCK_FLOAT, 0, N_("Vertex Alpha")},
|
||||
{ SOCK_FLOAT, 0, N_("Front/Back")},
|
||||
{ -1, 0, "" }
|
||||
};
|
||||
|
||||
/* node execute callback */
|
||||
static void node_shader_exec_geom(void *data, int UNUSED(thread), bNode *node, bNodeExecData *UNUSED(execdata), bNodeStack **UNUSED(in), bNodeStack **out)
|
||||
{
|
||||
if (data) {
|
||||
ShadeInput *shi = ((ShaderCallData *)data)->shi;
|
||||
NodeGeometry *ngeo = (NodeGeometry *)node->storage;
|
||||
ShadeInputUV *suv = &shi->uv[shi->actuv];
|
||||
static float defaultvcol[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
int i;
|
||||
|
||||
if (ngeo->uvname[0]) {
|
||||
/* find uv map by name */
|
||||
for (i = 0; i < shi->totuv; i++) {
|
||||
if (STREQ(shi->uv[i].name, ngeo->uvname)) {
|
||||
suv = &shi->uv[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* out: global, local, view, orco, uv, normal, vertex color */
|
||||
copy_v3_v3(out[GEOM_OUT_GLOB]->vec, shi->gl);
|
||||
copy_v3_v3(out[GEOM_OUT_LOCAL]->vec, shi->co);
|
||||
copy_v3_v3(out[GEOM_OUT_VIEW]->vec, shi->view);
|
||||
copy_v3_v3(out[GEOM_OUT_ORCO]->vec, shi->lo);
|
||||
copy_v3_v3(out[GEOM_OUT_UV]->vec, suv->uv);
|
||||
copy_v3_v3(out[GEOM_OUT_NORMAL]->vec, shi->vno);
|
||||
|
||||
if (shi->use_world_space_shading) {
|
||||
negate_v3(out[GEOM_OUT_NORMAL]->vec);
|
||||
mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEWINV_MATRIX), out[GEOM_OUT_NORMAL]->vec);
|
||||
}
|
||||
if (shi->totcol) {
|
||||
/* find vertex color layer by name */
|
||||
ShadeInputCol *scol = &shi->col[0];
|
||||
|
||||
if (ngeo->colname[0]) {
|
||||
for (i = 0; i < shi->totcol; i++) {
|
||||
if (STREQ(shi->col[i].name, ngeo->colname)) {
|
||||
scol = &shi->col[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srgb_to_linearrgb_v3_v3(out[GEOM_OUT_VCOL]->vec, scol->col);
|
||||
out[GEOM_OUT_VCOL]->vec[3] = scol->col[3];
|
||||
out[GEOM_OUT_VCOL_ALPHA]->vec[0] = scol->col[3];
|
||||
}
|
||||
else {
|
||||
memcpy(out[GEOM_OUT_VCOL]->vec, defaultvcol, sizeof(defaultvcol));
|
||||
out[GEOM_OUT_VCOL_ALPHA]->vec[0] = 1.0f;
|
||||
}
|
||||
|
||||
if (shi->osatex) {
|
||||
out[GEOM_OUT_GLOB]->data = shi->dxgl;
|
||||
out[GEOM_OUT_GLOB]->datatype = NS_OSA_VECTORS;
|
||||
out[GEOM_OUT_LOCAL]->data = shi->dxco;
|
||||
out[GEOM_OUT_LOCAL]->datatype = NS_OSA_VECTORS;
|
||||
out[GEOM_OUT_VIEW]->data = &shi->dxview;
|
||||
out[GEOM_OUT_VIEW]->datatype = NS_OSA_VALUES;
|
||||
out[GEOM_OUT_ORCO]->data = shi->dxlo;
|
||||
out[GEOM_OUT_ORCO]->datatype = NS_OSA_VECTORS;
|
||||
out[GEOM_OUT_UV]->data = suv->dxuv;
|
||||
out[GEOM_OUT_UV]->datatype = NS_OSA_VECTORS;
|
||||
out[GEOM_OUT_NORMAL]->data = shi->dxno;
|
||||
out[GEOM_OUT_NORMAL]->datatype = NS_OSA_VECTORS;
|
||||
}
|
||||
|
||||
/* front/back, normal flipping was stored */
|
||||
out[GEOM_OUT_FRONTBACK]->vec[0] = (shi->flippednor) ? 0.0f : 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static void node_shader_init_geometry(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
node->storage = MEM_callocN(sizeof(NodeGeometry), "NodeGeometry");
|
||||
}
|
||||
|
||||
static int gpu_shader_geom(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
|
||||
{
|
||||
NodeGeometry *ngeo = (NodeGeometry *)node->storage;
|
||||
GPUNodeLink *orco = GPU_attribute(CD_ORCO, "");
|
||||
GPUNodeLink *mtface = GPU_attribute(CD_MTFACE, ngeo->uvname);
|
||||
GPUNodeLink *mcol = GPU_attribute(CD_MCOL, ngeo->colname);
|
||||
|
||||
bool ret = GPU_stack_link(mat, "geom", in, out,
|
||||
GPU_builtin(GPU_VIEW_POSITION), GPU_builtin(GPU_VIEW_NORMAL),
|
||||
GPU_builtin(GPU_INVERSE_VIEW_MATRIX), orco, mtface, mcol);
|
||||
if (GPU_material_use_world_space_shading(mat)) {
|
||||
GPU_link(mat, "vec_math_negate", out[5].link, &out[5].link);
|
||||
ret &= GPU_link(mat, "direction_transform_m4v3", out[5].link, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &out[5].link);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* node type definition */
|
||||
void register_node_type_sh_geom(void)
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, SH_NODE_GEOMETRY, "Geometry", NODE_CLASS_INPUT, 0);
|
||||
node_type_compatibility(&ntype, NODE_OLD_SHADING);
|
||||
node_type_socket_templates(&ntype, NULL, sh_node_geom_out);
|
||||
node_type_init(&ntype, node_shader_init_geometry);
|
||||
node_type_storage(&ntype, "NodeGeometry", node_free_standard_storage, node_copy_standard_storage);
|
||||
node_type_exec(&ntype, NULL, NULL, node_shader_exec_geom);
|
||||
node_type_gpu(&ntype, gpu_shader_geom);
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
374
source/blender/nodes/shader/nodes/node_shader_material.c
Normal file
374
source/blender/nodes/shader/nodes/node_shader_material.c
Normal file
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* ***** 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) 2005 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): none yet.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/nodes/shader/nodes/node_shader_material.c
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "node_shader_util.h"
|
||||
|
||||
/* **************** MATERIAL ******************** */
|
||||
|
||||
static bNodeSocketTemplate sh_node_material_in[] = {
|
||||
{ SOCK_RGBA, 1, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f},
|
||||
{ SOCK_RGBA, 1, N_("Spec"), 0.0f, 0.0f, 0.0f, 1.0f},
|
||||
{ SOCK_FLOAT, 1, N_("DiffuseIntensity"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
|
||||
{ SOCK_VECTOR, 1, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_DIRECTION},
|
||||
{ -1, 0, "" }
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate sh_node_material_out[] = {
|
||||
{ SOCK_RGBA, 0, N_("Color")},
|
||||
{ SOCK_FLOAT, 0, N_("Alpha")},
|
||||
{ SOCK_VECTOR, 0, N_("Normal")},
|
||||
{ -1, 0, "" }
|
||||
};
|
||||
|
||||
/* **************** EXTENDED MATERIAL ******************** */
|
||||
|
||||
static bNodeSocketTemplate sh_node_material_ext_in[] = {
|
||||
{ SOCK_RGBA, 1, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f},
|
||||
{ SOCK_RGBA, 1, N_("Spec"), 0.0f, 0.0f, 0.0f, 1.0f},
|
||||
{ SOCK_FLOAT, 1, N_("DiffuseIntensity"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
|
||||
{ SOCK_VECTOR, 1, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_DIRECTION},
|
||||
{ SOCK_RGBA, 1, N_("Mirror"), 0.0f, 0.0f, 0.0f, 1.0f},
|
||||
{ SOCK_FLOAT, 1, N_("Ambient"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
|
||||
{ SOCK_FLOAT, 1, N_("Emit"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED},
|
||||
{ SOCK_FLOAT, 1, N_("SpecTra"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
|
||||
{ SOCK_FLOAT, 1, N_("Reflectivity"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE},
|
||||
{ SOCK_FLOAT, 1, N_("Alpha"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED},
|
||||
{ SOCK_FLOAT, 1, N_("Translucency"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
|
||||
{ -1, 0, "" }
|
||||
};
|
||||
|
||||
static bNodeSocketTemplate sh_node_material_ext_out[] = {
|
||||
{ SOCK_RGBA, 0, N_("Color")},
|
||||
{ SOCK_FLOAT, 0, N_("Alpha")},
|
||||
{ SOCK_VECTOR, 0, N_("Normal")},
|
||||
{ SOCK_RGBA, 0, N_("Diffuse")},
|
||||
{ SOCK_RGBA, 0, N_("Spec")},
|
||||
{ SOCK_RGBA, 0, N_("AO")},
|
||||
{ -1, 0, "" }
|
||||
};
|
||||
|
||||
static void node_shader_exec_material(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
|
||||
{
|
||||
if (data && node->id) {
|
||||
ShadeResult shrnode;
|
||||
ShadeInput *shi;
|
||||
ShaderCallData *shcd = data;
|
||||
float col[4];
|
||||
bNodeSocket *sock;
|
||||
char hasinput[NUM_MAT_IN] = {'\0'};
|
||||
int i, mode;
|
||||
|
||||
/* note: cannot use the in[]->hasinput flags directly, as these are not necessarily
|
||||
* the constant input stack values (e.g. in case material node is inside a group).
|
||||
* we just want to know if a node input uses external data or the material setting.
|
||||
* this is an ugly hack, but so is this node as a whole.
|
||||
*/
|
||||
for (sock = node->inputs.first, i = 0; sock; sock = sock->next, ++i)
|
||||
hasinput[i] = (sock->link != NULL);
|
||||
|
||||
shi = shcd->shi;
|
||||
shi->mat = (Material *)node->id;
|
||||
|
||||
/* copy all relevant material vars, note, keep this synced with render_types.h */
|
||||
memcpy(&shi->r, &shi->mat->r, 23 * sizeof(float));
|
||||
shi->har = shi->mat->har;
|
||||
|
||||
/* write values */
|
||||
if (hasinput[MAT_IN_COLOR])
|
||||
nodestack_get_vec(&shi->r, SOCK_VECTOR, in[MAT_IN_COLOR]);
|
||||
|
||||
if (hasinput[MAT_IN_SPEC])
|
||||
nodestack_get_vec(&shi->specr, SOCK_VECTOR, in[MAT_IN_SPEC]);
|
||||
|
||||
if (hasinput[MAT_IN_REFL])
|
||||
nodestack_get_vec(&shi->refl, SOCK_FLOAT, in[MAT_IN_REFL]);
|
||||
|
||||
/* retrieve normal */
|
||||
if (hasinput[MAT_IN_NORMAL]) {
|
||||
nodestack_get_vec(shi->vn, SOCK_VECTOR, in[MAT_IN_NORMAL]);
|
||||
if (shi->use_world_space_shading) {
|
||||
negate_v3(shi->vn);
|
||||
mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEW_MATRIX), shi->vn);
|
||||
}
|
||||
normalize_v3(shi->vn);
|
||||
}
|
||||
else
|
||||
copy_v3_v3(shi->vn, shi->vno);
|
||||
|
||||
/* custom option to flip normal */
|
||||
if (node->custom1 & SH_NODE_MAT_NEG) {
|
||||
negate_v3(shi->vn);
|
||||
}
|
||||
|
||||
if (node->type == SH_NODE_MATERIAL_EXT) {
|
||||
if (hasinput[MAT_IN_MIR])
|
||||
nodestack_get_vec(&shi->mirr, SOCK_VECTOR, in[MAT_IN_MIR]);
|
||||
if (hasinput[MAT_IN_AMB])
|
||||
nodestack_get_vec(&shi->amb, SOCK_FLOAT, in[MAT_IN_AMB]);
|
||||
if (hasinput[MAT_IN_EMIT])
|
||||
nodestack_get_vec(&shi->emit, SOCK_FLOAT, in[MAT_IN_EMIT]);
|
||||
if (hasinput[MAT_IN_SPECTRA])
|
||||
nodestack_get_vec(&shi->spectra, SOCK_FLOAT, in[MAT_IN_SPECTRA]);
|
||||
if (hasinput[MAT_IN_RAY_MIRROR])
|
||||
nodestack_get_vec(&shi->ray_mirror, SOCK_FLOAT, in[MAT_IN_RAY_MIRROR]);
|
||||
if (hasinput[MAT_IN_ALPHA])
|
||||
nodestack_get_vec(&shi->alpha, SOCK_FLOAT, in[MAT_IN_ALPHA]);
|
||||
if (hasinput[MAT_IN_TRANSLUCENCY])
|
||||
nodestack_get_vec(&shi->translucency, SOCK_FLOAT, in[MAT_IN_TRANSLUCENCY]);
|
||||
}
|
||||
|
||||
/* make alpha output give results even if transparency is only enabled on
|
||||
* the material linked in this not and not on the parent material */
|
||||
mode = shi->mode;
|
||||
if (shi->mat->mode & MA_TRANSP)
|
||||
shi->mode |= MA_TRANSP;
|
||||
|
||||
shi->nodes = 1; /* temp hack to prevent trashadow recursion */
|
||||
node_shader_lamp_loop(shi, &shrnode); /* clears shrnode */
|
||||
shi->nodes = 0;
|
||||
|
||||
shi->mode = mode;
|
||||
|
||||
/* write to outputs */
|
||||
if (node->custom1 & SH_NODE_MAT_DIFF) {
|
||||
copy_v3_v3(col, shrnode.combined);
|
||||
if (!(node->custom1 & SH_NODE_MAT_SPEC)) {
|
||||
sub_v3_v3(col, shrnode.spec);
|
||||
}
|
||||
}
|
||||
else if (node->custom1 & SH_NODE_MAT_SPEC) {
|
||||
copy_v3_v3(col, shrnode.spec);
|
||||
}
|
||||
else
|
||||
col[0] = col[1] = col[2] = 0.0f;
|
||||
|
||||
col[3] = shrnode.alpha;
|
||||
|
||||
if (shi->do_preview)
|
||||
BKE_node_preview_set_pixel(execdata->preview, col, shi->xs, shi->ys, shi->do_manage);
|
||||
|
||||
copy_v3_v3(out[MAT_OUT_COLOR]->vec, col);
|
||||
out[MAT_OUT_ALPHA]->vec[0] = shrnode.alpha;
|
||||
|
||||
if (node->custom1 & SH_NODE_MAT_NEG) {
|
||||
shi->vn[0] = -shi->vn[0];
|
||||
shi->vn[1] = -shi->vn[1];
|
||||
shi->vn[2] = -shi->vn[2];
|
||||
}
|
||||
|
||||
copy_v3_v3(out[MAT_OUT_NORMAL]->vec, shi->vn);
|
||||
|
||||
if (shi->use_world_space_shading) {
|
||||
negate_v3(out[MAT_OUT_NORMAL]->vec);
|
||||
mul_mat3_m4_v3((float (*)[4])RE_render_current_get_matrix(RE_VIEWINV_MATRIX), out[MAT_OUT_NORMAL]->vec);
|
||||
}
|
||||
/* Extended material options */
|
||||
if (node->type == SH_NODE_MATERIAL_EXT) {
|
||||
/* Shadow, Reflect, Refract, Radiosity, Speed seem to cause problems inside
|
||||
* a node tree :( */
|
||||
copy_v3_v3(out[MAT_OUT_DIFFUSE]->vec, shrnode.diffshad);
|
||||
copy_v3_v3(out[MAT_OUT_SPEC]->vec, shrnode.spec);
|
||||
copy_v3_v3(out[MAT_OUT_AO]->vec, shrnode.ao);
|
||||
}
|
||||
|
||||
/* copy passes, now just active node */
|
||||
if (node->flag & NODE_ACTIVE_ID) {
|
||||
float combined[4], alpha;
|
||||
|
||||
copy_v4_v4(combined, shcd->shr->combined);
|
||||
alpha = shcd->shr->alpha;
|
||||
|
||||
*(shcd->shr) = shrnode;
|
||||
|
||||
copy_v4_v4(shcd->shr->combined, combined);
|
||||
shcd->shr->alpha = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void node_shader_init_material(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
node->custom1 = SH_NODE_MAT_DIFF | SH_NODE_MAT_SPEC;
|
||||
}
|
||||
|
||||
/* XXX this is also done as a local static function in gpu_codegen.c,
|
||||
* but we need this to hack around the crappy material node.
|
||||
*/
|
||||
static GPUNodeLink *gpu_get_input_link(GPUMaterial *mat, GPUNodeStack *in)
|
||||
{
|
||||
if (in->link) {
|
||||
return in->link;
|
||||
}
|
||||
else {
|
||||
GPUNodeLink *result = NULL;
|
||||
|
||||
/* note GPU_uniform() is only intended to be used as a parameter to
|
||||
* GPU_link(), returning it directly results in leaks or double frees */
|
||||
if (in->type == GPU_FLOAT)
|
||||
GPU_link(mat, "set_value", GPU_uniform(in->vec), &result);
|
||||
else if (in->type == GPU_VEC3)
|
||||
GPU_link(mat, "set_rgb", GPU_uniform(in->vec), &result);
|
||||
else if (in->type == GPU_VEC4)
|
||||
GPU_link(mat, "set_rgba", GPU_uniform(in->vec), &result);
|
||||
else
|
||||
BLI_assert(0);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static int gpu_shader_material(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
|
||||
{
|
||||
if (node->id) {
|
||||
GPUShadeInput shi;
|
||||
GPUShadeResult shr;
|
||||
bNodeSocket *sock;
|
||||
char hasinput[NUM_MAT_IN] = {'\0'};
|
||||
int i;
|
||||
|
||||
/* note: cannot use the in[]->hasinput flags directly, as these are not necessarily
|
||||
* the constant input stack values (e.g. in case material node is inside a group).
|
||||
* we just want to know if a node input uses external data or the material setting.
|
||||
*/
|
||||
for (sock = node->inputs.first, i = 0; sock; sock = sock->next, ++i)
|
||||
hasinput[i] = (sock->link != NULL);
|
||||
|
||||
GPU_shadeinput_set(mat, (Material *)node->id, &shi);
|
||||
|
||||
/* write values */
|
||||
if (hasinput[MAT_IN_COLOR])
|
||||
shi.rgb = gpu_get_input_link(mat, &in[MAT_IN_COLOR]);
|
||||
|
||||
if (hasinput[MAT_IN_SPEC])
|
||||
shi.specrgb = gpu_get_input_link(mat, &in[MAT_IN_SPEC]);
|
||||
|
||||
if (hasinput[MAT_IN_REFL])
|
||||
shi.refl = gpu_get_input_link(mat, &in[MAT_IN_REFL]);
|
||||
|
||||
/* retrieve normal */
|
||||
if (hasinput[MAT_IN_NORMAL]) {
|
||||
GPUNodeLink *tmp;
|
||||
shi.vn = gpu_get_input_link(mat, &in[MAT_IN_NORMAL]);
|
||||
if (GPU_material_use_world_space_shading(mat)) {
|
||||
GPU_link(mat, "vec_math_negate", shi.vn, &shi.vn);
|
||||
GPU_link(mat, "direction_transform_m4v3", shi.vn, GPU_builtin(GPU_VIEW_MATRIX), &shi.vn);
|
||||
}
|
||||
GPU_link(mat, "vec_math_normalize", shi.vn, &shi.vn, &tmp);
|
||||
}
|
||||
|
||||
/* custom option to flip normal */
|
||||
if (node->custom1 & SH_NODE_MAT_NEG)
|
||||
GPU_link(mat, "vec_math_negate", shi.vn, &shi.vn);
|
||||
|
||||
if (node->type == SH_NODE_MATERIAL_EXT) {
|
||||
if (hasinput[MAT_IN_MIR])
|
||||
shi.mir = gpu_get_input_link(mat, &in[MAT_IN_MIR]);
|
||||
if (hasinput[MAT_IN_AMB])
|
||||
shi.amb = gpu_get_input_link(mat, &in[MAT_IN_AMB]);
|
||||
if (hasinput[MAT_IN_EMIT])
|
||||
shi.emit = gpu_get_input_link(mat, &in[MAT_IN_EMIT]);
|
||||
if (hasinput[MAT_IN_SPECTRA])
|
||||
shi.spectra = gpu_get_input_link(mat, &in[MAT_IN_SPECTRA]);
|
||||
if (hasinput[MAT_IN_ALPHA])
|
||||
shi.alpha = gpu_get_input_link(mat, &in[MAT_IN_ALPHA]);
|
||||
}
|
||||
|
||||
GPU_shaderesult_set(&shi, &shr); /* clears shr */
|
||||
|
||||
/* write to outputs */
|
||||
if (node->custom1 & SH_NODE_MAT_DIFF) {
|
||||
out[MAT_OUT_COLOR].link = shr.combined;
|
||||
|
||||
if (!(node->custom1 & SH_NODE_MAT_SPEC)) {
|
||||
GPUNodeLink *link;
|
||||
GPU_link(mat, "vec_math_sub", shr.combined, shr.spec, &out[MAT_OUT_COLOR].link, &link);
|
||||
}
|
||||
}
|
||||
else if (node->custom1 & SH_NODE_MAT_SPEC) {
|
||||
out[MAT_OUT_COLOR].link = shr.spec;
|
||||
}
|
||||
else
|
||||
GPU_link(mat, "set_rgb_zero", &out[MAT_OUT_COLOR].link);
|
||||
|
||||
GPU_link(mat, "mtex_alpha_to_col", out[MAT_OUT_COLOR].link, shr.alpha, &out[MAT_OUT_COLOR].link);
|
||||
|
||||
out[MAT_OUT_ALPHA].link = shr.alpha; //
|
||||
|
||||
if (node->custom1 & SH_NODE_MAT_NEG)
|
||||
GPU_link(mat, "vec_math_negate", shi.vn, &shi.vn);
|
||||
out[MAT_OUT_NORMAL].link = shi.vn;
|
||||
if (GPU_material_use_world_space_shading(mat)) {
|
||||
GPU_link(mat, "vec_math_negate", out[MAT_OUT_NORMAL].link, &out[MAT_OUT_NORMAL].link);
|
||||
GPU_link(mat, "direction_transform_m4v3", out[MAT_OUT_NORMAL].link, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &out[MAT_OUT_NORMAL].link);
|
||||
}
|
||||
|
||||
if (node->type == SH_NODE_MATERIAL_EXT) {
|
||||
out[MAT_OUT_DIFFUSE].link = shr.diff;
|
||||
out[MAT_OUT_SPEC].link = shr.spec;
|
||||
GPU_link(mat, "set_rgb_one", &out[MAT_OUT_AO].link);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_node_type_sh_material(void)
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, SH_NODE_MATERIAL, "Material", NODE_CLASS_INPUT, NODE_PREVIEW);
|
||||
node_type_compatibility(&ntype, NODE_OLD_SHADING);
|
||||
node_type_socket_templates(&ntype, sh_node_material_in, sh_node_material_out);
|
||||
node_type_init(&ntype, node_shader_init_material);
|
||||
node_type_exec(&ntype, NULL, NULL, node_shader_exec_material);
|
||||
node_type_gpu(&ntype, gpu_shader_material);
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
||||
|
||||
void register_node_type_sh_material_ext(void)
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, SH_NODE_MATERIAL_EXT, "Extended Material", NODE_CLASS_INPUT, NODE_PREVIEW);
|
||||
node_type_compatibility(&ntype, NODE_OLD_SHADING);
|
||||
node_type_socket_templates(&ntype, sh_node_material_ext_in, sh_node_material_ext_out);
|
||||
node_type_init(&ntype, node_shader_init_material);
|
||||
node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
|
||||
node_type_exec(&ntype, NULL, NULL, node_shader_exec_material);
|
||||
node_type_gpu(&ntype, gpu_shader_material);
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
97
source/blender/nodes/shader/nodes/node_shader_output.c
Normal file
97
source/blender/nodes/shader/nodes/node_shader_output.c
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* ***** 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) 2005 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): none yet.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/nodes/shader/nodes/node_shader_output.c
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
|
||||
#include "node_shader_util.h"
|
||||
|
||||
/* **************** OUTPUT ******************** */
|
||||
static bNodeSocketTemplate sh_node_output_in[] = {
|
||||
{ SOCK_RGBA, 1, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f},
|
||||
{ SOCK_FLOAT, 1, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE},
|
||||
{ -1, 0, "" }
|
||||
};
|
||||
|
||||
static void node_shader_exec_output(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **UNUSED(out))
|
||||
{
|
||||
if (data) {
|
||||
ShadeInput *shi = ((ShaderCallData *)data)->shi;
|
||||
float col[4];
|
||||
|
||||
/* stack order input sockets: col, alpha, normal */
|
||||
nodestack_get_vec(col, SOCK_VECTOR, in[0]);
|
||||
nodestack_get_vec(col + 3, SOCK_FLOAT, in[1]);
|
||||
|
||||
if (shi->do_preview) {
|
||||
BKE_node_preview_set_pixel(execdata->preview, col, shi->xs, shi->ys, shi->do_manage);
|
||||
node->lasty = shi->ys;
|
||||
}
|
||||
|
||||
if (node->flag & NODE_DO_OUTPUT) {
|
||||
ShadeResult *shr = ((ShaderCallData *)data)->shr;
|
||||
|
||||
copy_v4_v4(shr->combined, col);
|
||||
shr->alpha = col[3];
|
||||
|
||||
// copy_v3_v3(shr->nor, in[3]->vec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int gpu_shader_output(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
|
||||
{
|
||||
GPUNodeLink *outlink;
|
||||
|
||||
#if 0
|
||||
if (in[1].hasinput)
|
||||
GPU_material_enable_alpha(mat);
|
||||
#endif
|
||||
|
||||
GPU_stack_link(mat, "output_node", in, out, &outlink);
|
||||
GPU_material_output_link(mat, outlink);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void register_node_type_sh_output(void)
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, SH_NODE_OUTPUT, "Output", NODE_CLASS_OUTPUT, NODE_PREVIEW);
|
||||
node_type_compatibility(&ntype, NODE_OLD_SHADING);
|
||||
node_type_socket_templates(&ntype, sh_node_output_in, NULL);
|
||||
node_type_exec(&ntype, NULL, NULL, node_shader_exec_output);
|
||||
node_type_gpu(&ntype, gpu_shader_output);
|
||||
|
||||
/* Do not allow muting output node. */
|
||||
node_type_internal_links(&ntype, NULL);
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
166
source/blender/nodes/shader/nodes/node_shader_texture.c
Normal file
166
source/blender/nodes/shader/nodes/node_shader_texture.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* ***** 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) 2005 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): none yet.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/nodes/shader/nodes/node_shader_texture.c
|
||||
* \ingroup shdnodes
|
||||
*/
|
||||
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
#include "node_shader_util.h"
|
||||
|
||||
#include "GPU_material.h"
|
||||
|
||||
/* **************** TEXTURE ******************** */
|
||||
static bNodeSocketTemplate sh_node_texture_in[] = {
|
||||
{ SOCK_VECTOR, 1, "Vector", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, /* no limit */
|
||||
{ -1, 0, "" }
|
||||
};
|
||||
static bNodeSocketTemplate sh_node_texture_out[] = {
|
||||
{ SOCK_FLOAT, 0, N_("Value"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_NO_INTERNAL_LINK},
|
||||
{ SOCK_RGBA, 0, N_("Color"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_NO_INTERNAL_LINK},
|
||||
{ SOCK_VECTOR, 0, N_("Normal"), 0, 0, 0, 0, 0, 0, PROP_NONE, SOCK_NO_INTERNAL_LINK},
|
||||
{ -1, 0, "" }
|
||||
};
|
||||
|
||||
static void node_shader_exec_texture(void *data, int UNUSED(thread), bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
|
||||
{
|
||||
if (data && node->id) {
|
||||
ShadeInput *shi = ((ShaderCallData *)data)->shi;
|
||||
TexResult texres;
|
||||
bNodeSocket *sock_vector = node->inputs.first;
|
||||
float vec[3], nor[3] = {0.0f, 0.0f, 0.0f};
|
||||
int retval;
|
||||
short which_output = node->custom1;
|
||||
|
||||
short thread = shi->thread;
|
||||
|
||||
/* out: value, color, normal */
|
||||
|
||||
/* we should find out if a normal as output is needed, for now we do all */
|
||||
texres.nor = nor;
|
||||
texres.tr = texres.tg = texres.tb = 0.0f;
|
||||
|
||||
/* don't use in[0]->hasinput, see material node for explanation */
|
||||
if (sock_vector->link) {
|
||||
nodestack_get_vec(vec, SOCK_VECTOR, in[0]);
|
||||
|
||||
if (in[0]->datatype == NS_OSA_VECTORS) {
|
||||
float *fp = in[0]->data;
|
||||
retval = multitex_nodes((Tex *)node->id, vec, fp, fp + 3, shi->osatex, &texres, thread, which_output, NULL, NULL, NULL);
|
||||
}
|
||||
else if (in[0]->datatype == NS_OSA_VALUES) {
|
||||
const float *fp = in[0]->data;
|
||||
float dxt[3], dyt[3];
|
||||
|
||||
dxt[0] = fp[0]; dxt[1] = dxt[2] = 0.0f;
|
||||
dyt[0] = fp[1]; dyt[1] = dyt[2] = 0.0f;
|
||||
retval = multitex_nodes((Tex *)node->id, vec, dxt, dyt, shi->osatex, &texres, thread, which_output, NULL, NULL, NULL);
|
||||
}
|
||||
else
|
||||
retval = multitex_nodes((Tex *)node->id, vec, NULL, NULL, 0, &texres, thread, which_output, NULL, NULL, NULL);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(vec, shi->lo);
|
||||
retval = multitex_nodes((Tex *)node->id, vec, NULL, NULL, 0, &texres, thread, which_output, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/* stupid exception */
|
||||
if ( ((Tex *)node->id)->type == TEX_STUCCI) {
|
||||
texres.tin = 0.5f + 0.7f * texres.nor[0];
|
||||
CLAMP(texres.tin, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
/* intensity and color need some handling */
|
||||
if (texres.talpha)
|
||||
out[0]->vec[0] = texres.ta;
|
||||
else
|
||||
out[0]->vec[0] = texres.tin;
|
||||
|
||||
if ((retval & TEX_RGB) == 0) {
|
||||
copy_v3_fl(out[1]->vec, out[0]->vec[0]);
|
||||
out[1]->vec[3] = 1.0f;
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(out[1]->vec, &texres.tr);
|
||||
out[1]->vec[3] = 1.0f;
|
||||
}
|
||||
|
||||
copy_v3_v3(out[2]->vec, nor);
|
||||
|
||||
if (shi->do_preview) {
|
||||
BKE_node_preview_set_pixel(execdata->preview, out[1]->vec, shi->xs, shi->ys, shi->do_manage);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int gpu_shader_texture(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
|
||||
{
|
||||
Tex *tex = (Tex *)node->id;
|
||||
|
||||
if (tex && tex->ima && (tex->type == TEX_IMAGE || tex->type == TEX_ENVMAP)) {
|
||||
if (tex->type == TEX_IMAGE) {
|
||||
GPUNodeLink *texlink = GPU_image(tex->ima, &tex->iuser, false);
|
||||
GPU_stack_link(mat, "texture_image", in, out, texlink);
|
||||
}
|
||||
else { /* TEX_ENVMAP */
|
||||
if (!in[0].link)
|
||||
in[0].link = GPU_uniform(in[0].vec);
|
||||
if (!GPU_material_use_world_space_shading(mat))
|
||||
GPU_link(mat, "direction_transform_m4v3", in[0].link, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &in[0].link);
|
||||
GPU_link(mat, "mtex_cube_map_refl_from_refldir",
|
||||
GPU_cube_map(tex->ima, &tex->iuser, false), in[0].link, &out[0].link, &out[1].link);
|
||||
GPU_link(mat, "color_to_normal", out[1].link, &out[2].link);
|
||||
}
|
||||
|
||||
ImBuf *ibuf = BKE_image_acquire_ibuf(tex->ima, &tex->iuser, NULL);
|
||||
if (ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 &&
|
||||
GPU_material_do_color_management(mat))
|
||||
{
|
||||
GPU_link(mat, "srgb_to_linearrgb", out[1].link, &out[1].link);
|
||||
}
|
||||
BKE_image_release_ibuf(tex->ima, ibuf, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void register_node_type_sh_texture(void)
|
||||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, SH_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT, NODE_PREVIEW);
|
||||
node_type_compatibility(&ntype, NODE_OLD_SHADING);
|
||||
node_type_socket_templates(&ntype, sh_node_texture_in, sh_node_texture_out);
|
||||
node_type_exec(&ntype, NULL, NULL, node_shader_exec_texture);
|
||||
node_type_gpu(&ntype, gpu_shader_texture);
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
54
source/blender/render/intern/include/envmap.h
Normal file
54
source/blender/render/intern/include/envmap.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* envmap_ext.h
|
||||
*
|
||||
*
|
||||
* ***** 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) 2001-2002 by NaN Holding BV.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): none yet.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/include/envmap.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __ENVMAP_H__
|
||||
#define __ENVMAP_H__
|
||||
|
||||
/**
|
||||
* Make environment maps for all objects in the scene that have an
|
||||
* environment map as texture.
|
||||
* (initrender.c)
|
||||
*/
|
||||
|
||||
struct Render;
|
||||
struct TexResult;
|
||||
struct ImagePool;
|
||||
|
||||
void make_envmaps(struct Render *re);
|
||||
int envmaptex(struct Tex *tex, const float texvec[3], float dxt[3], float dyt[3], int osatex, struct TexResult *texres, struct ImagePool *pool, const bool skip_image_load);
|
||||
void env_rotate_scene(struct Render *re, float mat[4][4], int do_rotate);
|
||||
|
||||
#endif /* __ENVMAP_H__ */
|
||||
|
65
source/blender/render/intern/include/pixelblending.h
Normal file
65
source/blender/render/intern/include/pixelblending.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* ***** 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) 2001-2002 by NaN Holding BV.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributor(s): 2004-2006 Blender Foundation, full recode
|
||||
*
|
||||
* ***** END GPL/BL DUAL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/include/pixelblending.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __PIXELBLENDING_H__
|
||||
#define __PIXELBLENDING_H__
|
||||
|
||||
|
||||
/**
|
||||
* add 1 pixel to into filtered three lines
|
||||
* (float vecs to float vec)
|
||||
*/
|
||||
void add_filt_fmask(unsigned int mask, const float col[4], float *rowbuf, int row_w);
|
||||
void add_filt_fmask_pixsize(unsigned int mask, float *in, float *rowbuf, int row_w, int pixsize);
|
||||
void add_filt_fmask_coord(float filt[3][3], const float col[4], float *rowbuf, int row_stride, int x, int y, rcti *mask);
|
||||
void mask_array(unsigned int mask, float filt[3][3]);
|
||||
|
||||
/**
|
||||
* Alpha-over blending for floats.
|
||||
*/
|
||||
void addAlphaOverFloat(float dest[4], const float source[4]);
|
||||
|
||||
/**
|
||||
* Alpha-under blending for floats.
|
||||
*/
|
||||
void addAlphaUnderFloat(float dest[4], const float source[4]);
|
||||
|
||||
|
||||
/**
|
||||
* Same for floats
|
||||
*/
|
||||
void addalphaAddfacFloat(float dest[4], const float source[4], char addfac);
|
||||
|
||||
/**
|
||||
* dest = dest + source
|
||||
*/
|
||||
void addalphaAddFloat(float dest[4], const float source[4]);
|
||||
|
||||
#endif /* __PIXELBLENDING_H__ */
|
62
source/blender/render/intern/include/pixelshading.h
Normal file
62
source/blender/render/intern/include/pixelshading.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* ***** 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) 2001-2002 by NaN Holding BV.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributor(s): 2004-2006, Blender Foundation, full recode
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/include/pixelshading.h
|
||||
* \ingroup render
|
||||
*
|
||||
* These functions determine what actual color a pixel will have.
|
||||
*/
|
||||
|
||||
#ifndef __PIXELSHADING_H__
|
||||
#define __PIXELSHADING_H__
|
||||
|
||||
|
||||
/**
|
||||
* Render the pixel at (x,y) for object ap. Apply the jitter mask.
|
||||
* Output is given in float collector[4]. The type vector:
|
||||
* t[0] - min. distance
|
||||
* t[1] - face/halo index
|
||||
* t[2] - jitter mask
|
||||
* t[3] - type ZB_POLY or ZB_HALO
|
||||
* t[4] - max. distance
|
||||
* mask is pixel coverage in bits
|
||||
* \return pointer to the object
|
||||
*/
|
||||
int shadeHaloFloat(HaloRen *har,
|
||||
float *col, int zz,
|
||||
float dist, float xn,
|
||||
float yn, short flarec);
|
||||
|
||||
/**
|
||||
* Render the sky at pixel (x, y).
|
||||
*/
|
||||
void shadeSkyPixel(float collector[4], float fx, float fy, short thread);
|
||||
void shadeSkyView(float col_r[3], const float rco[3], const float view[3], const float dxyview[2], short thread);
|
||||
void shadeAtmPixel(struct SunSky *sunsky, float *collector, float fx, float fy, float distance);
|
||||
void shadeSunView(float col_r[3], const float view[3]);
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
#endif
|
||||
|
51
source/blender/render/intern/include/pointdensity.h
Normal file
51
source/blender/render/intern/include/pointdensity.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* ***** 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) 2001-2002 by NaN Holding BV.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Matt Ebb
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/include/pointdensity.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __POINTDENSITY_H__
|
||||
#define __POINTDENSITY_H__
|
||||
|
||||
/**
|
||||
* Make point density kd-trees for all point density textures in the scene
|
||||
*/
|
||||
|
||||
struct PointDensity;
|
||||
struct Render;
|
||||
struct TexResult;
|
||||
|
||||
void free_pointdensity(struct PointDensity *pd);
|
||||
void cache_pointdensity(struct Render *re, struct PointDensity *pd);
|
||||
void make_pointdensities(struct Render *re);
|
||||
void free_pointdensities(struct Render *re);
|
||||
int pointdensitytex(struct Tex *tex, const float texvec[3], struct TexResult *texres);
|
||||
|
||||
#endif /* __POINTDENSITY_H__ */
|
||||
|
74
source/blender/render/intern/include/raycounter.h
Normal file
74
source/blender/render/intern/include/raycounter.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/include/raycounter.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __RAYCOUNTER_H__
|
||||
#define __RAYCOUNTER_H__
|
||||
|
||||
//#define RE_RAYCOUNTER /* enable counters per ray, useful for measuring raytrace structures performance */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef RE_RAYCOUNTER
|
||||
|
||||
/* ray counter functions */
|
||||
|
||||
typedef struct RayCounter {
|
||||
struct {
|
||||
unsigned long long test, hit;
|
||||
} faces, bb, simd_bb, raycast, raytrace_hint, rayshadow_last_hit;
|
||||
} RayCounter;
|
||||
|
||||
#define RE_RC_INIT(isec, shi) (isec).raycounter = &((shi).shading.raycounter)
|
||||
void RE_RC_INFO(RayCounter *rc);
|
||||
void RE_RC_MERGE(RayCounter *rc, RayCounter *tmp);
|
||||
#define RE_RC_COUNT(var) (var)++
|
||||
|
||||
extern RayCounter re_rc_counter[];
|
||||
|
||||
#else
|
||||
|
||||
/* ray counter stubs */
|
||||
|
||||
#define RE_RC_INIT(isec,shi)
|
||||
#define RE_RC_INFO(rc)
|
||||
#define RE_RC_MERGE(dest,src)
|
||||
#define RE_RC_COUNT(var)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
136
source/blender/render/intern/include/rayintersection.h
Normal file
136
source/blender/render/intern/include/rayintersection.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* ***** 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) 2007 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
* RE_raytrace.h: ray tracing api, can be used independently from the renderer.
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/include/rayintersection.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __RAYINTERSECTION_H__
|
||||
#define __RAYINTERSECTION_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "BLI_math_geom.h"
|
||||
|
||||
struct RayObject;
|
||||
|
||||
/* Ray Hints */
|
||||
|
||||
#define RE_RAY_LCTS_MAX_SIZE 256
|
||||
#define RT_USE_LAST_HIT /* last shadow hit is reused before raycasting on whole tree */
|
||||
//#define RT_USE_HINT /* last hit object is reused before raycasting on whole tree */
|
||||
|
||||
typedef struct LCTSHint {
|
||||
int size;
|
||||
struct RayObject *stack[RE_RAY_LCTS_MAX_SIZE];
|
||||
} LCTSHint;
|
||||
|
||||
typedef struct RayHint {
|
||||
union { LCTSHint lcts; } data;
|
||||
} RayHint;
|
||||
|
||||
/* Ray Intersection */
|
||||
|
||||
typedef struct Isect {
|
||||
/* ray start, direction (normalized vector), and max distance. on hit,
|
||||
* the distance is modified to be the distance to the hit point. */
|
||||
float start[3];
|
||||
float dir[3];
|
||||
float dist;
|
||||
|
||||
/* for envmap and incremental view update renders */
|
||||
float origstart[3];
|
||||
float origdir[3];
|
||||
|
||||
/* precomputed values to accelerate bounding box intersection */
|
||||
int bv_index[6];
|
||||
float idot_axis[3];
|
||||
|
||||
/* intersection options */
|
||||
int mode; /* RE_RAY_SHADOW, RE_RAY_MIRROR, RE_RAY_SHADOW_TRA */
|
||||
int lay; /* -1 default, set for layer lamps */
|
||||
int skip; /* skip flags */
|
||||
int check; /* check flags */
|
||||
void *userdata; /* used by bake check */
|
||||
|
||||
/* hit information */
|
||||
float u, v;
|
||||
int isect; /* which half of quad */
|
||||
|
||||
struct {
|
||||
void *ob;
|
||||
void *face;
|
||||
} hit, orig;
|
||||
|
||||
/* last hit optimization */
|
||||
struct RayObject *last_hit;
|
||||
|
||||
/* hints */
|
||||
#ifdef RT_USE_HINT
|
||||
RayTraceHint *hint, *hit_hint;
|
||||
#endif
|
||||
RayHint *hint;
|
||||
|
||||
/* ray counter */
|
||||
#ifdef RE_RAYCOUNTER
|
||||
RayCounter *raycounter;
|
||||
#endif
|
||||
|
||||
/* Precalculated coefficients for watertight intersection check. */
|
||||
struct IsectRayPrecalc isect_precalc;
|
||||
} Isect;
|
||||
|
||||
/* ray types */
|
||||
#define RE_RAY_SHADOW 0
|
||||
#define RE_RAY_MIRROR 1
|
||||
#define RE_RAY_SHADOW_TRA 2
|
||||
|
||||
/* skip options */
|
||||
#define RE_SKIP_CULLFACE (1 << 0)
|
||||
/* if using this flag then *face should be a pointer to a VlakRen */
|
||||
#define RE_SKIP_VLR_NEIGHBOUR (1 << 1)
|
||||
|
||||
/* check options */
|
||||
#define RE_CHECK_VLR_NONE 0
|
||||
#define RE_CHECK_VLR_RENDER 1
|
||||
#define RE_CHECK_VLR_NON_SOLID_MATERIAL 2
|
||||
#define RE_CHECK_VLR_BAKE 3
|
||||
|
||||
/* arbitrary, but can't use e.g. FLT_MAX because of precision issues */
|
||||
#define RE_RAYTRACE_MAXDIST 1e15f
|
||||
#define RE_RAYTRACE_EPSILON 0.0f
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __RAYINTERSECTION_H__ */
|
||||
|
105
source/blender/render/intern/include/rendercore.h
Normal file
105
source/blender/render/intern/include/rendercore.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* ***** 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) 2001-2002 by NaN Holding BV.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): none yet.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __RENDERCORE_H__
|
||||
#define __RENDERCORE_H__
|
||||
|
||||
/** \file blender/render/intern/include/rendercore.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#include "render_types.h"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "NOD_composite.h"
|
||||
|
||||
struct ShadeInput;
|
||||
struct ShadeResult;
|
||||
struct World;
|
||||
struct RenderPart;
|
||||
struct RenderLayer;
|
||||
struct RayObject;
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
typedef struct PixStr {
|
||||
struct PixStr *next;
|
||||
int obi, facenr, z, maskz;
|
||||
unsigned short mask;
|
||||
short shadfac;
|
||||
} PixStr;
|
||||
|
||||
typedef struct PixStrMain {
|
||||
struct PixStrMain *next, *prev;
|
||||
struct PixStr *ps;
|
||||
int counter;
|
||||
} PixStrMain;
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
void calc_view_vector(float view[3], float x, float y);
|
||||
float mistfactor(float zcor, const float co[3]); /* dist and height, return alpha */
|
||||
|
||||
void renderspothalo(struct ShadeInput *shi, float col[4], float alpha);
|
||||
void add_halo_flare(Render *re);
|
||||
|
||||
void calc_renderco_zbuf(float co[3], const float view[3], int z);
|
||||
void calc_renderco_ortho(float co[3], float x, float y, int z);
|
||||
|
||||
int count_mask(unsigned short mask);
|
||||
|
||||
void zbufshade_tile(struct RenderPart *pa);
|
||||
void zbufshadeDA_tile(struct RenderPart *pa);
|
||||
|
||||
void zbufshade_sss_tile(struct RenderPart *pa);
|
||||
|
||||
int get_sample_layers(struct RenderPart *pa, struct RenderLayer *rl, struct RenderLayer **rlpp);
|
||||
|
||||
void render_internal_update_passes(struct RenderEngine *engine, struct Scene *scene, struct SceneRenderLayer *srl);
|
||||
|
||||
|
||||
/* -------- ray.c ------- */
|
||||
|
||||
struct RayObject *RE_rayobject_create(int type, int size, int octree_resolution);
|
||||
|
||||
extern void freeraytree(Render *re);
|
||||
extern void makeraytree(Render *re);
|
||||
struct RayObject* makeraytree_object(Render *re, ObjectInstanceRen *obi);
|
||||
|
||||
extern void ray_shadow(ShadeInput *shi, LampRen *lar, float shadfac[4]);
|
||||
extern void ray_trace(ShadeInput *shi, ShadeResult *);
|
||||
extern void ray_ao(ShadeInput *shi, float ao[3], float env[3]);
|
||||
extern void init_jitter_plane(LampRen *lar);
|
||||
extern void init_ao_sphere(Render *re, struct World *wrld);
|
||||
extern void init_render_qmcsampler(Render *re);
|
||||
extern void free_render_qmcsampler(Render *re);
|
||||
|
||||
#endif /* __RENDERCORE_H__ */
|
105
source/blender/render/intern/include/shading.h
Normal file
105
source/blender/render/intern/include/shading.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* ***** 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.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/include/shading.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
struct ShadeInput;
|
||||
struct ShadeResult;
|
||||
struct RenderPart;
|
||||
struct RenderLayer;
|
||||
struct PixStr;
|
||||
struct LampRen;
|
||||
struct VlakRen;
|
||||
struct StrandPoint;
|
||||
struct ObjectInstanceRen;
|
||||
struct Isect;
|
||||
|
||||
/* shadeinput.c */
|
||||
|
||||
#define RE_MAX_OSA 16
|
||||
|
||||
/* needed to calculate shadow and AO for an entire pixel */
|
||||
typedef struct ShadeSample {
|
||||
int tot; /* amount of shi in use, can be 1 for not FULL_OSA */
|
||||
|
||||
RenderLayer *rlpp[RE_MAX_OSA]; /* fast lookup from sample to renderlayer (fullsample buf) */
|
||||
|
||||
/* could be malloced once */
|
||||
ShadeInput shi[RE_MAX_OSA];
|
||||
ShadeResult shr[RE_MAX_OSA];
|
||||
} ShadeSample;
|
||||
|
||||
|
||||
/* also the node shader callback */
|
||||
void shade_material_loop(struct ShadeInput *shi, struct ShadeResult *shr);
|
||||
|
||||
void shade_input_set_triangle_i(struct ShadeInput *shi, struct ObjectInstanceRen *obi, struct VlakRen *vlr, short i1, short i2, short i3);
|
||||
void shade_input_set_triangle(struct ShadeInput *shi, int obi, int facenr, int normal_flip);
|
||||
void shade_input_copy_triangle(struct ShadeInput *shi, struct ShadeInput *from);
|
||||
void shade_input_calc_viewco(struct ShadeInput *shi, float x, float y, float z, float view[3], float dxyview[2], float co[3], float dxco[3], float dyco[3]);
|
||||
void shade_input_set_viewco(struct ShadeInput *shi, float x, float y, float sx, float sy, float z);
|
||||
void shade_input_set_uv(struct ShadeInput *shi);
|
||||
void shade_input_set_normals(struct ShadeInput *shi);
|
||||
void shade_input_set_vertex_normals(struct ShadeInput *shi);
|
||||
void shade_input_flip_normals(struct ShadeInput *shi);
|
||||
void shade_input_set_shade_texco(struct ShadeInput *shi);
|
||||
void shade_input_set_strand(struct ShadeInput *shi, struct StrandRen *strand, struct StrandPoint *spoint);
|
||||
void shade_input_set_strand_texco(struct ShadeInput *shi, struct StrandRen *strand, struct StrandVert *svert, struct StrandPoint *spoint);
|
||||
void shade_input_do_shade(struct ShadeInput *shi, struct ShadeResult *shr);
|
||||
|
||||
void shade_input_init_material(struct ShadeInput *shi);
|
||||
void shade_input_initialize(struct ShadeInput *shi, struct RenderPart *pa, struct RenderLayer *rl, int sample);
|
||||
|
||||
void shade_sample_initialize(struct ShadeSample *ssamp, struct RenderPart *pa, struct RenderLayer *rl);
|
||||
void shade_samples_do_AO(struct ShadeSample *ssamp);
|
||||
void shade_samples_fill_with_ps(struct ShadeSample *ssamp, struct PixStr *ps, int x, int y);
|
||||
int shade_samples(struct ShadeSample *ssamp, struct PixStr *ps, int x, int y);
|
||||
|
||||
void vlr_set_uv_indices(struct VlakRen *vlr, int *i1, int *i2, int *i3);
|
||||
|
||||
void calc_R_ref(struct ShadeInput *shi);
|
||||
|
||||
void barycentric_differentials_from_position(
|
||||
const float co[3], const float v1[3], const float v2[3], const float v3[3],
|
||||
const float dxco[3], const float dyco[3], const float facenor[3], const bool differentials,
|
||||
float *u, float *v, float *dx_u, float *dx_v, float *dy_u, float *dy_v);
|
||||
|
||||
/* shadeoutput. */
|
||||
void shade_lamp_loop(struct ShadeInput *shi, struct ShadeResult *shr);
|
||||
|
||||
void shade_color(struct ShadeInput *shi, ShadeResult *shr);
|
||||
|
||||
void ambient_occlusion(struct ShadeInput *shi);
|
||||
void environment_lighting_apply(struct ShadeInput *shi, struct ShadeResult *shr);
|
||||
|
||||
ListBase *get_lights(struct ShadeInput *shi);
|
||||
float lamp_get_visibility(struct LampRen *lar, const float co[3], float lv[3], float *dist);
|
||||
void lamp_get_shadow(struct LampRen *lar, ShadeInput *shi, float inp, float shadfac[4], int do_real);
|
||||
|
||||
float fresnel_fac(const float view[3], const float vn[3], float fresnel, float fac);
|
||||
|
||||
/* rayshade.c */
|
||||
extern void shade_ray(struct Isect *is, struct ShadeInput *shi, struct ShadeResult *shr);
|
99
source/blender/render/intern/include/strand.h
Normal file
99
source/blender/render/intern/include/strand.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* ***** 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.
|
||||
*
|
||||
* Contributor(s): Brecht Van Lommel.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/include/strand.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __STRAND_H__
|
||||
#define __STRAND_H__
|
||||
|
||||
struct StrandVert;
|
||||
struct StrandRen;
|
||||
struct StrandBuffer;
|
||||
struct ShadeSample;
|
||||
struct StrandPart;
|
||||
struct Render;
|
||||
struct ZSpan;
|
||||
struct ObjectInstanceRen;
|
||||
struct StrandSurface;
|
||||
struct DerivedMesh;
|
||||
struct ObjectRen;
|
||||
|
||||
typedef struct StrandPoint {
|
||||
/* position within segment */
|
||||
float t;
|
||||
|
||||
/* camera space */
|
||||
float co[3];
|
||||
float nor[3];
|
||||
float tan[3];
|
||||
float strandco;
|
||||
float width;
|
||||
|
||||
/* derivatives */
|
||||
float dtco[3], dsco[3];
|
||||
float dtstrandco;
|
||||
|
||||
/* outer points */
|
||||
float co1[3], co2[3];
|
||||
float hoco1[4], hoco2[4];
|
||||
float zco1[3], zco2[3];
|
||||
int clip1, clip2;
|
||||
|
||||
/* screen space */
|
||||
float hoco[4];
|
||||
float x, y;
|
||||
|
||||
/* simplification */
|
||||
float alpha;
|
||||
} StrandPoint;
|
||||
|
||||
typedef struct StrandSegment {
|
||||
struct StrandVert *v[4];
|
||||
struct StrandRen *strand;
|
||||
struct StrandBuffer *buffer;
|
||||
struct ObjectInstanceRen *obi;
|
||||
float sqadaptcos;
|
||||
|
||||
StrandPoint point1, point2;
|
||||
int shaded;
|
||||
} StrandSegment;
|
||||
|
||||
struct StrandShadeCache;
|
||||
typedef struct StrandShadeCache StrandShadeCache;
|
||||
|
||||
void strand_eval_point(StrandSegment *sseg, StrandPoint *spoint);
|
||||
void render_strand_segment(struct Render *re, float winmat[4][4], struct StrandPart *spart, struct ZSpan *zspan, int totzspan, StrandSegment *sseg);
|
||||
void strand_minmax(struct StrandRen *strand, float min[3], float max[3], const float width);
|
||||
|
||||
struct StrandSurface *cache_strand_surface(struct Render *re, struct ObjectRen *obr, struct DerivedMesh *dm, float mat[4][4], int timeoffset);
|
||||
void free_strand_surface(struct Render *re);
|
||||
|
||||
struct StrandShadeCache *strand_shade_cache_create(void);
|
||||
void strand_shade_cache_free(struct StrandShadeCache *cache);
|
||||
void strand_shade_segment(struct Render *re, struct StrandShadeCache *cache, struct StrandSegment *sseg, struct ShadeSample *ssamp, float t, float s, int addpassflag);
|
||||
void strand_shade_unref(struct StrandShadeCache *cache, struct ObjectInstanceRen *obi, struct StrandVert *svert);
|
||||
|
||||
#endif
|
||||
|
81
source/blender/render/intern/include/sunsky.h
Normal file
81
source/blender/render/intern/include/sunsky.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* ***** 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.
|
||||
*
|
||||
* Contributor(s): zaghaghi
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/include/sunsky.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#ifndef __SUNSKY_H__
|
||||
#define __SUNSKY_H__
|
||||
|
||||
// #define SPECTRUM_MAX_COMPONENTS 100
|
||||
|
||||
typedef struct SunSky {
|
||||
short effect_type, skyblendtype, sky_colorspace;
|
||||
float turbidity;
|
||||
float theta, phi;
|
||||
|
||||
float toSun[3];
|
||||
|
||||
/*float sunSpectralRaddata[SPECTRUM_MAX_COMPONENTS];*/
|
||||
float sunSolidAngle;
|
||||
|
||||
float zenith_Y, zenith_x, zenith_y;
|
||||
|
||||
float perez_Y[5], perez_x[5], perez_y[5];
|
||||
|
||||
/* suggested by glome in patch [#8063] */
|
||||
float horizon_brightness;
|
||||
float spread;
|
||||
float sun_brightness;
|
||||
float sun_size;
|
||||
float backscattered_light;
|
||||
float skyblendfac;
|
||||
float sky_exposure;
|
||||
|
||||
float atm_HGg;
|
||||
|
||||
float atm_SunIntensity;
|
||||
float atm_InscatteringMultiplier;
|
||||
float atm_ExtinctionMultiplier;
|
||||
float atm_BetaRayMultiplier;
|
||||
float atm_BetaMieMultiplier;
|
||||
float atm_DistanceMultiplier;
|
||||
|
||||
float atm_BetaRay[3];
|
||||
float atm_BetaDashRay[3];
|
||||
float atm_BetaMie[3];
|
||||
float atm_BetaDashMie[3];
|
||||
float atm_BetaRM[3];
|
||||
} SunSky;
|
||||
|
||||
void InitSunSky(struct SunSky *sunsky, float turb, const float toSun[3], float horizon_brightness,
|
||||
float spread, float sun_brightness, float sun_size, float back_scatter,
|
||||
float skyblendfac, short skyblendtype, float sky_exposure, float sky_colorspace);
|
||||
|
||||
void GetSkyXYZRadiance(struct SunSky *sunsky, float theta, float phi, float color_out[3]);
|
||||
void GetSkyXYZRadiancef(struct SunSky *sunsky, const float varg[3], float color_out[3]);
|
||||
void InitAtmosphere(struct SunSky *sunSky, float sun_intens, float mief, float rayf, float inscattf, float extincf, float disf);
|
||||
void AtmospherePixleShader(struct SunSky *sunSky, float view[3], float s, float rgb[3]);
|
||||
void ClipColor(float c[3]);
|
||||
|
||||
#endif /*__SUNSKY_H__*/
|
35
source/blender/render/intern/include/texture_ocean.h
Normal file
35
source/blender/render/intern/include/texture_ocean.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* ***** 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) 2001-2002 by NaN Holding BV.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributors: Matt Ebb
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __TEXTURE_OCEAN_H__
|
||||
#define __TEXTURE_OCEAN_H__
|
||||
|
||||
/** \file blender/render/intern/include/texture_ocean.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
int ocean_texture(struct Tex *tex, const float texvec[2], struct TexResult *texres);
|
||||
|
||||
#endif /* __TEXTURE_OCEAN_H__ */
|
47
source/blender/render/intern/include/voxeldata.h
Normal file
47
source/blender/render/intern/include/voxeldata.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* ***** 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) 2001-2002 by NaN Holding BV.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Raul Fernandez Hernandez (Farsthary), Matt Ebb.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/include/voxeldata.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#ifndef __VOXELDATA_H__
|
||||
#define __VOXELDATA_H__
|
||||
|
||||
struct Render;
|
||||
struct TexResult;
|
||||
|
||||
typedef struct VoxelDataHeader {
|
||||
int resolX, resolY, resolZ;
|
||||
int frames;
|
||||
} VoxelDataHeader;
|
||||
|
||||
void cache_voxeldata(Tex *tex, int scene_frame);
|
||||
void make_voxeldata(struct Render *re);
|
||||
int voxeldatatex(struct Tex *tex, const float texvec[3], struct TexResult *texres);
|
||||
|
||||
#endif /* __VOXELDATA_H__ */
|
407
source/blender/render/intern/raytrace/bvh.h
Normal file
407
source/blender/render/intern/raytrace/bvh.h
Normal file
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/raytrace/bvh.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "raycounter.h"
|
||||
#include "rayintersection.h"
|
||||
#include "rayobject.h"
|
||||
#include "rayobject_hint.h"
|
||||
#include "rayobject_rtbuild.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef __SSE__
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#ifndef __BVH_H__
|
||||
#define __BVH_H__
|
||||
|
||||
#ifdef __SSE__
|
||||
inline int test_bb_group4(__m128 *bb_group, const Isect *isec)
|
||||
{
|
||||
const __m128 tmin0 = _mm_setzero_ps();
|
||||
const __m128 tmax0 = _mm_set_ps1(isec->dist);
|
||||
|
||||
float start[3], idot_axis[3];
|
||||
copy_v3_v3(start, isec->start);
|
||||
copy_v3_v3(idot_axis, isec->idot_axis);
|
||||
|
||||
const __m128 tmin1 = _mm_max_ps(tmin0, _mm_mul_ps(_mm_sub_ps(bb_group[isec->bv_index[0]], _mm_set_ps1(start[0]) ), _mm_set_ps1(idot_axis[0])) );
|
||||
const __m128 tmax1 = _mm_min_ps(tmax0, _mm_mul_ps(_mm_sub_ps(bb_group[isec->bv_index[1]], _mm_set_ps1(start[0]) ), _mm_set_ps1(idot_axis[0])) );
|
||||
const __m128 tmin2 = _mm_max_ps(tmin1, _mm_mul_ps(_mm_sub_ps(bb_group[isec->bv_index[2]], _mm_set_ps1(start[1]) ), _mm_set_ps1(idot_axis[1])) );
|
||||
const __m128 tmax2 = _mm_min_ps(tmax1, _mm_mul_ps(_mm_sub_ps(bb_group[isec->bv_index[3]], _mm_set_ps1(start[1]) ), _mm_set_ps1(idot_axis[1])) );
|
||||
const __m128 tmin3 = _mm_max_ps(tmin2, _mm_mul_ps(_mm_sub_ps(bb_group[isec->bv_index[4]], _mm_set_ps1(start[2]) ), _mm_set_ps1(idot_axis[2])) );
|
||||
const __m128 tmax3 = _mm_min_ps(tmax2, _mm_mul_ps(_mm_sub_ps(bb_group[isec->bv_index[5]], _mm_set_ps1(start[2]) ), _mm_set_ps1(idot_axis[2])) );
|
||||
|
||||
return _mm_movemask_ps(_mm_cmpge_ps(tmax3, tmin3));
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Determines the distance that the ray must travel to hit the bounding volume of the given node
|
||||
* Based on Tactical Optimization of Ray/Box Intersection, by Graham Fyffe
|
||||
* [http://tog.acm.org/resources/RTNews/html/rtnv21n1.html#art9]
|
||||
*/
|
||||
static inline int rayobject_bb_intersect_test(const Isect *isec, const float *_bb)
|
||||
{
|
||||
const float *bb = _bb;
|
||||
|
||||
float t1x = (bb[isec->bv_index[0]] - isec->start[0]) * isec->idot_axis[0];
|
||||
float t2x = (bb[isec->bv_index[1]] - isec->start[0]) * isec->idot_axis[0];
|
||||
float t1y = (bb[isec->bv_index[2]] - isec->start[1]) * isec->idot_axis[1];
|
||||
float t2y = (bb[isec->bv_index[3]] - isec->start[1]) * isec->idot_axis[1];
|
||||
float t1z = (bb[isec->bv_index[4]] - isec->start[2]) * isec->idot_axis[2];
|
||||
float t2z = (bb[isec->bv_index[5]] - isec->start[2]) * isec->idot_axis[2];
|
||||
|
||||
RE_RC_COUNT(isec->raycounter->bb.test);
|
||||
|
||||
if (t1x > t2y || t2x < t1y || t1x > t2z || t2x < t1z || t1y > t2z || t2y < t1z) return 0;
|
||||
if (t2x < 0.0f || t2y < 0.0f || t2z < 0.0f) return 0;
|
||||
if (t1x > isec->dist || t1y > isec->dist || t1z > isec->dist) return 0;
|
||||
RE_RC_COUNT(isec->raycounter->bb.hit);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* bvh tree generics */
|
||||
template<class Tree> static void bvh_add(Tree *obj, RayObject *ob)
|
||||
{
|
||||
rtbuild_add(obj->builder, ob);
|
||||
}
|
||||
|
||||
template<class Node>
|
||||
inline bool is_leaf(Node *node)
|
||||
{
|
||||
return !RE_rayobject_isAligned(node);
|
||||
}
|
||||
|
||||
template<class Tree> static void bvh_done(Tree *obj);
|
||||
|
||||
template<class Tree>
|
||||
static void bvh_free(Tree *obj)
|
||||
{
|
||||
if (obj->builder)
|
||||
rtbuild_free(obj->builder);
|
||||
|
||||
if (obj->node_arena)
|
||||
BLI_memarena_free(obj->node_arena);
|
||||
|
||||
MEM_freeN(obj);
|
||||
}
|
||||
|
||||
template<class Tree>
|
||||
static void bvh_bb(Tree *obj, float *min, float *max)
|
||||
{
|
||||
if (obj->root)
|
||||
bvh_node_merge_bb(obj->root, min, max);
|
||||
}
|
||||
|
||||
|
||||
template<class Tree>
|
||||
static float bvh_cost(Tree *obj)
|
||||
{
|
||||
assert(obj->cost >= 0.0f);
|
||||
return obj->cost;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* bvh tree nodes generics */
|
||||
template<class Node> static inline int bvh_node_hit_test(Node *node, Isect *isec)
|
||||
{
|
||||
return rayobject_bb_intersect_test(isec, (const float *)node->bb);
|
||||
}
|
||||
|
||||
|
||||
template<class Node>
|
||||
static inline void bvh_node_merge_bb(Node *node, float min[3], float max[3])
|
||||
{
|
||||
if (is_leaf(node)) {
|
||||
RE_rayobject_merge_bb((RayObject *)node, min, max);
|
||||
}
|
||||
else {
|
||||
DO_MIN(node->bb, min);
|
||||
DO_MAX(node->bb + 3, max);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* recursively transverse a BVH looking for a rayhit using a local stack
|
||||
*/
|
||||
template<class Node> static inline void bvh_node_push_childs(Node *node, Isect *isec, Node **stack, int &stack_pos);
|
||||
|
||||
template<class Node, int MAX_STACK_SIZE, bool TEST_ROOT, bool SHADOW>
|
||||
static int bvh_node_stack_raycast(Node *root, Isect *isec)
|
||||
{
|
||||
Node *stack[MAX_STACK_SIZE];
|
||||
int hit = 0, stack_pos = 0;
|
||||
|
||||
if (!TEST_ROOT && !is_leaf(root))
|
||||
bvh_node_push_childs(root, isec, stack, stack_pos);
|
||||
else
|
||||
stack[stack_pos++] = root;
|
||||
|
||||
while (stack_pos) {
|
||||
Node *node = stack[--stack_pos];
|
||||
if (!is_leaf(node)) {
|
||||
if (bvh_node_hit_test(node, isec)) {
|
||||
bvh_node_push_childs(node, isec, stack, stack_pos);
|
||||
assert(stack_pos <= MAX_STACK_SIZE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
hit |= RE_rayobject_intersect( (RayObject *)node, isec);
|
||||
if (SHADOW && hit) return hit;
|
||||
}
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __SSE__
|
||||
/*
|
||||
* Generic SIMD bvh recursion
|
||||
* this was created to be able to use any simd (with the cost of some memmoves)
|
||||
* it can take advantage of any SIMD width and doens't needs any special tree care
|
||||
*/
|
||||
template<class Node, int MAX_STACK_SIZE, bool TEST_ROOT>
|
||||
static int bvh_node_stack_raycast_simd(Node *root, Isect *isec)
|
||||
{
|
||||
Node *stack[MAX_STACK_SIZE];
|
||||
|
||||
int hit = 0, stack_pos = 0;
|
||||
|
||||
if (!TEST_ROOT) {
|
||||
if (!is_leaf(root)) {
|
||||
if (!is_leaf(root->child))
|
||||
bvh_node_push_childs(root, isec, stack, stack_pos);
|
||||
else
|
||||
return RE_rayobject_intersect( (RayObject *)root->child, isec);
|
||||
}
|
||||
else
|
||||
return RE_rayobject_intersect( (RayObject *)root, isec);
|
||||
}
|
||||
else {
|
||||
if (!is_leaf(root))
|
||||
stack[stack_pos++] = root;
|
||||
else
|
||||
return RE_rayobject_intersect( (RayObject *)root, isec);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
//Use SIMD 4
|
||||
if (stack_pos >= 4) {
|
||||
__m128 t_bb[6];
|
||||
Node *t_node[4];
|
||||
|
||||
stack_pos -= 4;
|
||||
|
||||
/* prepare the 4BB for SIMD */
|
||||
t_node[0] = stack[stack_pos + 0]->child;
|
||||
t_node[1] = stack[stack_pos + 1]->child;
|
||||
t_node[2] = stack[stack_pos + 2]->child;
|
||||
t_node[3] = stack[stack_pos + 3]->child;
|
||||
|
||||
const float *bb0 = stack[stack_pos + 0]->bb;
|
||||
const float *bb1 = stack[stack_pos + 1]->bb;
|
||||
const float *bb2 = stack[stack_pos + 2]->bb;
|
||||
const float *bb3 = stack[stack_pos + 3]->bb;
|
||||
|
||||
const __m128 x0y0x1y1 = _mm_shuffle_ps(_mm_load_ps(bb0), _mm_load_ps(bb1), _MM_SHUFFLE(1, 0, 1, 0) );
|
||||
const __m128 x2y2x3y3 = _mm_shuffle_ps(_mm_load_ps(bb2), _mm_load_ps(bb3), _MM_SHUFFLE(1, 0, 1, 0) );
|
||||
t_bb[0] = _mm_shuffle_ps(x0y0x1y1, x2y2x3y3, _MM_SHUFFLE(2, 0, 2, 0) );
|
||||
t_bb[1] = _mm_shuffle_ps(x0y0x1y1, x2y2x3y3, _MM_SHUFFLE(3, 1, 3, 1) );
|
||||
|
||||
const __m128 z0X0z1X1 = _mm_shuffle_ps(_mm_load_ps(bb0), _mm_load_ps(bb1), _MM_SHUFFLE(3, 2, 3, 2) );
|
||||
const __m128 z2X2z3X3 = _mm_shuffle_ps(_mm_load_ps(bb2), _mm_load_ps(bb3), _MM_SHUFFLE(3, 2, 3, 2) );
|
||||
t_bb[2] = _mm_shuffle_ps(z0X0z1X1, z2X2z3X3, _MM_SHUFFLE(2, 0, 2, 0) );
|
||||
t_bb[3] = _mm_shuffle_ps(z0X0z1X1, z2X2z3X3, _MM_SHUFFLE(3, 1, 3, 1) );
|
||||
|
||||
const __m128 Y0Z0Y1Z1 = _mm_shuffle_ps(_mm_load_ps(bb0 + 4), _mm_load_ps(bb1 + 4), _MM_SHUFFLE(1, 0, 1, 0) );
|
||||
const __m128 Y2Z2Y3Z3 = _mm_shuffle_ps(_mm_load_ps(bb2 + 4), _mm_load_ps(bb3 + 4), _MM_SHUFFLE(1, 0, 1, 0) );
|
||||
t_bb[4] = _mm_shuffle_ps(Y0Z0Y1Z1, Y2Z2Y3Z3, _MM_SHUFFLE(2, 0, 2, 0) );
|
||||
t_bb[5] = _mm_shuffle_ps(Y0Z0Y1Z1, Y2Z2Y3Z3, _MM_SHUFFLE(3, 1, 3, 1) );
|
||||
#if 0
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
Node *t = stack[stack_pos + i];
|
||||
assert(!is_leaf(t));
|
||||
|
||||
float *bb = ((float *)t_bb) + i;
|
||||
bb[4 * 0] = t->bb[0];
|
||||
bb[4 * 1] = t->bb[1];
|
||||
bb[4 * 2] = t->bb[2];
|
||||
bb[4 * 3] = t->bb[3];
|
||||
bb[4 * 4] = t->bb[4];
|
||||
bb[4 * 5] = t->bb[5];
|
||||
t_node[i] = t->child;
|
||||
}
|
||||
#endif
|
||||
RE_RC_COUNT(isec->raycounter->simd_bb.test);
|
||||
int res = test_bb_group4(t_bb, isec);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (res & (1 << i)) {
|
||||
RE_RC_COUNT(isec->raycounter->simd_bb.hit);
|
||||
if (!is_leaf(t_node[i])) {
|
||||
for (Node *t = t_node[i]; t; t = t->sibling) {
|
||||
assert(stack_pos < MAX_STACK_SIZE);
|
||||
stack[stack_pos++] = t;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hit |= RE_rayobject_intersect( (RayObject *)t_node[i], isec);
|
||||
if (hit && isec->mode == RE_RAY_SHADOW) return hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (stack_pos > 0) {
|
||||
Node *node = stack[--stack_pos];
|
||||
assert(!is_leaf(node));
|
||||
|
||||
if (bvh_node_hit_test(node, isec)) {
|
||||
if (!is_leaf(node->child)) {
|
||||
bvh_node_push_childs(node, isec, stack, stack_pos);
|
||||
assert(stack_pos <= MAX_STACK_SIZE);
|
||||
}
|
||||
else {
|
||||
hit |= RE_rayobject_intersect( (RayObject *)node->child, isec);
|
||||
if (hit && isec->mode == RE_RAY_SHADOW) return hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
else break;
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* recursively transverse a BVH looking for a rayhit using system stack
|
||||
*/
|
||||
#if 0
|
||||
template<class Node>
|
||||
static int bvh_node_raycast(Node *node, Isect *isec)
|
||||
{
|
||||
int hit = 0;
|
||||
if (bvh_test_node(node, isec))
|
||||
{
|
||||
if (isec->idot_axis[node->split_axis] > 0.0f)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < BVH_NCHILDS; i++)
|
||||
if (!is_leaf(node->child[i]))
|
||||
{
|
||||
if (node->child[i] == 0) break;
|
||||
|
||||
hit |= bvh_node_raycast(node->child[i], isec);
|
||||
if (hit && isec->mode == RE_RAY_SHADOW) return hit;
|
||||
}
|
||||
else {
|
||||
hit |= RE_rayobject_intersect( (RayObject *)node->child[i], isec);
|
||||
if (hit && isec->mode == RE_RAY_SHADOW) return hit;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
for (i = BVH_NCHILDS - 1; i >= 0; i--)
|
||||
if (!is_leaf(node->child[i]))
|
||||
{
|
||||
if (node->child[i])
|
||||
{
|
||||
hit |= dfs_raycast(node->child[i], isec);
|
||||
if (hit && isec->mode == RE_RAY_SHADOW) return hit;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hit |= RE_rayobject_intersect( (RayObject *)node->child[i], isec);
|
||||
if (hit && isec->mode == RE_RAY_SHADOW) return hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class Node, class HintObject>
|
||||
static void bvh_dfs_make_hint(Node *node, LCTSHint *hint, int reserve_space, HintObject *hintObject)
|
||||
{
|
||||
assert(hint->size + reserve_space + 1 <= RE_RAY_LCTS_MAX_SIZE);
|
||||
|
||||
if (is_leaf(node)) {
|
||||
hint->stack[hint->size++] = (RayObject *)node;
|
||||
}
|
||||
else {
|
||||
int childs = count_childs(node);
|
||||
if (hint->size + reserve_space + childs <= RE_RAY_LCTS_MAX_SIZE) {
|
||||
int result = hint_test_bb(hintObject, node->bb, node->bb + 3);
|
||||
if (result == HINT_RECURSE) {
|
||||
/* We are 100% sure the ray will be pass inside this node */
|
||||
bvh_dfs_make_hint_push_siblings(node->child, hint, reserve_space, hintObject);
|
||||
}
|
||||
else if (result == HINT_ACCEPT) {
|
||||
hint->stack[hint->size++] = (RayObject *)node;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hint->stack[hint->size++] = (RayObject *)node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class Tree>
|
||||
static RayObjectAPI *bvh_get_api(int maxstacksize);
|
||||
|
||||
|
||||
template<class Tree, int DFS_STACK_SIZE>
|
||||
static inline RayObject *bvh_create_tree(int size)
|
||||
{
|
||||
Tree *obj = (Tree *)MEM_callocN(sizeof(Tree), "BVHTree");
|
||||
assert(RE_rayobject_isAligned(obj)); /* RayObject API assumes real data to be 4-byte aligned */
|
||||
|
||||
obj->rayobj.api = bvh_get_api<Tree>(DFS_STACK_SIZE);
|
||||
obj->root = NULL;
|
||||
|
||||
obj->node_arena = NULL;
|
||||
obj->builder = rtbuild_create(size);
|
||||
|
||||
return RE_rayobject_unalignRayAPI((RayObject *) obj);
|
||||
}
|
||||
|
||||
#endif
|
534
source/blender/render/intern/raytrace/rayobject.cpp
Normal file
534
source/blender/render/intern/raytrace/rayobject.cpp
Normal file
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/raytrace/rayobject.cpp
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
|
||||
#include "rayintersection.h"
|
||||
#include "rayobject.h"
|
||||
#include "raycounter.h"
|
||||
#include "render_types.h"
|
||||
#include "renderdatabase.h"
|
||||
|
||||
/* RayFace
|
||||
*
|
||||
* note we force always inline here, because compiler refuses to otherwise
|
||||
* because function is too long. Since this is code that is called billions
|
||||
* of times we really do want to inline. */
|
||||
|
||||
MALWAYS_INLINE RayObject *rayface_from_coords(RayFace *rayface, void *ob, void *face,
|
||||
float *v1, float *v2, float *v3, float *v4)
|
||||
{
|
||||
rayface->ob = ob;
|
||||
rayface->face = face;
|
||||
|
||||
copy_v3_v3(rayface->v1, v1);
|
||||
copy_v3_v3(rayface->v2, v2);
|
||||
copy_v3_v3(rayface->v3, v3);
|
||||
|
||||
if (v4) {
|
||||
copy_v3_v3(rayface->v4, v4);
|
||||
rayface->quad = 1;
|
||||
}
|
||||
else {
|
||||
rayface->quad = 0;
|
||||
}
|
||||
|
||||
return RE_rayobject_unalignRayFace(rayface);
|
||||
}
|
||||
|
||||
MALWAYS_INLINE void rayface_from_vlak(RayFace *rayface, ObjectInstanceRen *obi, VlakRen *vlr)
|
||||
{
|
||||
rayface_from_coords(rayface, obi, vlr, vlr->v1->co, vlr->v2->co, vlr->v3->co, vlr->v4 ? vlr->v4->co : NULL);
|
||||
|
||||
if (obi->transform_primitives) {
|
||||
mul_m4_v3(obi->mat, rayface->v1);
|
||||
mul_m4_v3(obi->mat, rayface->v2);
|
||||
mul_m4_v3(obi->mat, rayface->v3);
|
||||
|
||||
if (RE_rayface_isQuad(rayface))
|
||||
mul_m4_v3(obi->mat, rayface->v4);
|
||||
}
|
||||
}
|
||||
|
||||
RayObject *RE_rayface_from_vlak(RayFace *rayface, ObjectInstanceRen *obi, VlakRen *vlr)
|
||||
{
|
||||
return rayface_from_coords(rayface, obi, vlr, vlr->v1->co, vlr->v2->co, vlr->v3->co, vlr->v4 ? vlr->v4->co : NULL);
|
||||
}
|
||||
|
||||
RayObject *RE_rayface_from_coords(RayFace *rayface, void *ob, void *face, float *v1, float *v2, float *v3, float *v4)
|
||||
{
|
||||
return rayface_from_coords(rayface, ob, face, v1, v2, v3, v4);
|
||||
}
|
||||
|
||||
/* VlakPrimitive */
|
||||
|
||||
RayObject *RE_vlakprimitive_from_vlak(VlakPrimitive *face, struct ObjectInstanceRen *obi, struct VlakRen *vlr)
|
||||
{
|
||||
face->ob = obi;
|
||||
face->face = vlr;
|
||||
|
||||
return RE_rayobject_unalignVlakPrimitive(face);
|
||||
}
|
||||
|
||||
/* Checks for ignoring faces or materials */
|
||||
|
||||
MALWAYS_INLINE int vlr_check_intersect(Isect *is, ObjectInstanceRen *obi, VlakRen *vlr)
|
||||
{
|
||||
/* for baking selected to active non-traceable materials might still
|
||||
* be in the raytree */
|
||||
if (!(vlr->flag & R_TRACEBLE))
|
||||
return 0;
|
||||
|
||||
/* I know... cpu cycle waste, might do smarter once */
|
||||
if (is->mode == RE_RAY_MIRROR)
|
||||
return !(vlr->mat->mode & MA_ONLYCAST);
|
||||
else
|
||||
return (vlr->mat->mode2 & MA_CASTSHADOW) && (is->lay & obi->lay);
|
||||
}
|
||||
|
||||
MALWAYS_INLINE int vlr_check_intersect_solid(Isect *UNUSED(is), ObjectInstanceRen *UNUSED(obi), VlakRen *vlr)
|
||||
{
|
||||
/* solid material types only */
|
||||
if (vlr->mat->material_type == MA_TYPE_SURFACE)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
MALWAYS_INLINE int vlr_check_bake(Isect *is, ObjectInstanceRen *obi, VlakRen *UNUSED(vlr))
|
||||
{
|
||||
return (obi->obr->ob != is->userdata) && (obi->obr->ob->flag & SELECT);
|
||||
}
|
||||
|
||||
/* Ray Triangle/Quad Intersection */
|
||||
|
||||
static bool isect_ray_tri_watertight_no_sign_check_v3(
|
||||
const float ray_origin[3], const struct IsectRayPrecalc *isect_precalc,
|
||||
const float v0[3], const float v1[3], const float v2[3],
|
||||
float *r_lambda, float r_uv[2])
|
||||
{
|
||||
const int kx = isect_precalc->kx;
|
||||
const int ky = isect_precalc->ky;
|
||||
const int kz = isect_precalc->kz;
|
||||
const float sx = isect_precalc->sx;
|
||||
const float sy = isect_precalc->sy;
|
||||
const float sz = isect_precalc->sz;
|
||||
|
||||
/* Calculate vertices relative to ray origin. */
|
||||
const float a[3] = {v0[0] - ray_origin[0], v0[1] - ray_origin[1], v0[2] - ray_origin[2]};
|
||||
const float b[3] = {v1[0] - ray_origin[0], v1[1] - ray_origin[1], v1[2] - ray_origin[2]};
|
||||
const float c[3] = {v2[0] - ray_origin[0], v2[1] - ray_origin[1], v2[2] - ray_origin[2]};
|
||||
|
||||
const float a_kx = a[kx], a_ky = a[ky], a_kz = a[kz];
|
||||
const float b_kx = b[kx], b_ky = b[ky], b_kz = b[kz];
|
||||
const float c_kx = c[kx], c_ky = c[ky], c_kz = c[kz];
|
||||
|
||||
/* Perform shear and scale of vertices. */
|
||||
const float ax = a_kx - sx * a_kz;
|
||||
const float ay = a_ky - sy * a_kz;
|
||||
const float bx = b_kx - sx * b_kz;
|
||||
const float by = b_ky - sy * b_kz;
|
||||
const float cx = c_kx - sx * c_kz;
|
||||
const float cy = c_ky - sy * c_kz;
|
||||
|
||||
/* Calculate scaled barycentric coordinates. */
|
||||
const float u = cx * by - cy * bx;
|
||||
const float v = ax * cy - ay * cx;
|
||||
const float w = bx * ay - by * ax;
|
||||
float det;
|
||||
|
||||
if ((u < 0.0f || v < 0.0f || w < 0.0f) &&
|
||||
(u > 0.0f || v > 0.0f || w > 0.0f))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Calculate determinant. */
|
||||
det = u + v + w;
|
||||
if (UNLIKELY(det == 0.0f)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
/* Calculate scaled z-coordinates of vertices and use them to calculate
|
||||
* the hit distance.
|
||||
*/
|
||||
const float t = (u * a_kz + v * b_kz + w * c_kz) * sz;
|
||||
/* Normalize u, v and t. */
|
||||
const float inv_det = 1.0f / det;
|
||||
if (r_uv) {
|
||||
r_uv[0] = u * inv_det;
|
||||
r_uv[1] = v * inv_det;
|
||||
}
|
||||
*r_lambda = t * inv_det;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
MALWAYS_INLINE int isec_tri_quad(const float start[3],
|
||||
const struct IsectRayPrecalc *isect_precalc,
|
||||
const RayFace *face,
|
||||
float r_uv[2], float *r_lambda)
|
||||
{
|
||||
float uv[2], l;
|
||||
|
||||
if (isect_ray_tri_watertight_v3(start, isect_precalc, face->v1, face->v2, face->v3, &l, uv)) {
|
||||
/* check if intersection is within ray length */
|
||||
if (l > -RE_RAYTRACE_EPSILON && l < *r_lambda) {
|
||||
r_uv[0] = -uv[0];
|
||||
r_uv[1] = -uv[1];
|
||||
*r_lambda = l;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* intersect second triangle in quad */
|
||||
if (RE_rayface_isQuad(face)) {
|
||||
if (isect_ray_tri_watertight_v3(start, isect_precalc, face->v1, face->v3, face->v4, &l, uv)) {
|
||||
/* check if intersection is within ray length */
|
||||
if (l > -RE_RAYTRACE_EPSILON && l < *r_lambda) {
|
||||
r_uv[0] = -uv[0];
|
||||
r_uv[1] = -uv[1];
|
||||
*r_lambda = l;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Simpler yes/no Ray Triangle/Quad Intersection */
|
||||
|
||||
MALWAYS_INLINE int isec_tri_quad_neighbour(const float start[3],
|
||||
const float dir[3],
|
||||
const RayFace *face)
|
||||
{
|
||||
float r[3];
|
||||
struct IsectRayPrecalc isect_precalc;
|
||||
float uv[2], l;
|
||||
|
||||
negate_v3_v3(r, dir); /* note, different than above function */
|
||||
|
||||
isect_ray_tri_watertight_v3_precalc(&isect_precalc, r);
|
||||
|
||||
if (isect_ray_tri_watertight_no_sign_check_v3(start, &isect_precalc, face->v1, face->v2, face->v3, &l, uv)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* intersect second triangle in quad */
|
||||
if (RE_rayface_isQuad(face)) {
|
||||
if (isect_ray_tri_watertight_no_sign_check_v3(start, &isect_precalc, face->v1, face->v3, face->v4, &l, uv)) {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* RayFace intersection with checks and neighbor verifaction included,
|
||||
* Isect is modified if the face is hit. */
|
||||
|
||||
MALWAYS_INLINE int intersect_rayface(RayObject *hit_obj, RayFace *face, Isect *is)
|
||||
{
|
||||
float dist, uv[2];
|
||||
int ok = 0;
|
||||
|
||||
/* avoid self-intersection */
|
||||
if (is->orig.ob == face->ob && is->orig.face == face->face)
|
||||
return 0;
|
||||
|
||||
/* check if we should intersect this face */
|
||||
if (is->check == RE_CHECK_VLR_RENDER) {
|
||||
if (vlr_check_intersect(is, (ObjectInstanceRen *)face->ob, (VlakRen *)face->face) == 0)
|
||||
return 0;
|
||||
}
|
||||
else if (is->check == RE_CHECK_VLR_NON_SOLID_MATERIAL) {
|
||||
if (vlr_check_intersect(is, (ObjectInstanceRen *)face->ob, (VlakRen *)face->face) == 0)
|
||||
return 0;
|
||||
if (vlr_check_intersect_solid(is, (ObjectInstanceRen *)face->ob, (VlakRen *)face->face) == 0)
|
||||
return 0;
|
||||
}
|
||||
else if (is->check == RE_CHECK_VLR_BAKE) {
|
||||
if (vlr_check_bake(is, (ObjectInstanceRen *)face->ob, (VlakRen *)face->face) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ray counter */
|
||||
RE_RC_COUNT(is->raycounter->faces.test);
|
||||
|
||||
dist = is->dist;
|
||||
ok = isec_tri_quad(is->start, &is->isect_precalc, face, uv, &dist);
|
||||
|
||||
if (ok) {
|
||||
|
||||
/* when a shadow ray leaves a face, it can be little outside the edges
|
||||
* of it, causing intersection to be detected in its neighbor face */
|
||||
if (is->skip & RE_SKIP_VLR_NEIGHBOUR) {
|
||||
if (dist < 0.1f && is->orig.ob == face->ob) {
|
||||
VlakRen *a = (VlakRen *)is->orig.face;
|
||||
VlakRen *b = (VlakRen *)face->face;
|
||||
ObjectRen *obr = ((ObjectInstanceRen *)face->ob)->obr;
|
||||
|
||||
VertRen **va, **vb;
|
||||
int *org_idx_a, *org_idx_b;
|
||||
int i, j;
|
||||
bool is_neighbor = false;
|
||||
|
||||
/* "same" vertex means either the actual same VertRen, or the same 'final org index', if available
|
||||
* (autosmooth only, currently). */
|
||||
for (i = 0, va = &a->v1; !is_neighbor && i < 4 && *va; ++i, ++va) {
|
||||
org_idx_a = RE_vertren_get_origindex(obr, *va, false);
|
||||
for (j = 0, vb = &b->v1; !is_neighbor && j < 4 && *vb; ++j, ++vb) {
|
||||
if (*va == *vb) {
|
||||
is_neighbor = true;
|
||||
}
|
||||
else if (org_idx_a) {
|
||||
org_idx_b = RE_vertren_get_origindex(obr, *vb, 0);
|
||||
if (org_idx_b && *org_idx_a == *org_idx_b) {
|
||||
is_neighbor = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* So there's a shared edge or vertex, let's intersect ray with self, if that's true
|
||||
* we can safely return 1, otherwise we assume the intersection is invalid, 0 */
|
||||
if (is_neighbor) {
|
||||
/* create RayFace from original face, transformed if necessary */
|
||||
RayFace origface;
|
||||
ObjectInstanceRen *ob = (ObjectInstanceRen *)is->orig.ob;
|
||||
rayface_from_vlak(&origface, ob, (VlakRen *)is->orig.face);
|
||||
|
||||
if (!isec_tri_quad_neighbour(is->start, is->dir, &origface)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RE_RC_COUNT(is->raycounter->faces.hit);
|
||||
|
||||
is->isect = ok; // which half of the quad
|
||||
is->dist = dist;
|
||||
is->u = uv[0]; is->v = uv[1];
|
||||
|
||||
is->hit.ob = face->ob;
|
||||
is->hit.face = face->face;
|
||||
#ifdef RT_USE_LAST_HIT
|
||||
is->last_hit = hit_obj;
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Intersection */
|
||||
|
||||
int RE_rayobject_raycast(RayObject *r, Isect *isec)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Pre-calculate orientation for watertight intersection checks. */
|
||||
isect_ray_tri_watertight_v3_precalc(&isec->isect_precalc, isec->dir);
|
||||
|
||||
RE_RC_COUNT(isec->raycounter->raycast.test);
|
||||
|
||||
/* setup vars used on raycast */
|
||||
for (i = 0; i < 3; i++) {
|
||||
isec->idot_axis[i] = 1.0f / isec->dir[i];
|
||||
|
||||
isec->bv_index[2 * i] = isec->idot_axis[i] < 0.0f ? 1 : 0;
|
||||
isec->bv_index[2 * i + 1] = 1 - isec->bv_index[2 * i];
|
||||
|
||||
isec->bv_index[2 * i] = i + 3 * isec->bv_index[2 * i];
|
||||
isec->bv_index[2 * i + 1] = i + 3 * isec->bv_index[2 * i + 1];
|
||||
}
|
||||
|
||||
#ifdef RT_USE_LAST_HIT
|
||||
/* last hit heuristic */
|
||||
if (isec->mode == RE_RAY_SHADOW && isec->last_hit) {
|
||||
RE_RC_COUNT(isec->raycounter->rayshadow_last_hit.test);
|
||||
|
||||
if (RE_rayobject_intersect(isec->last_hit, isec)) {
|
||||
RE_RC_COUNT(isec->raycounter->raycast.hit);
|
||||
RE_RC_COUNT(isec->raycounter->rayshadow_last_hit.hit);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef RT_USE_HINT
|
||||
isec->hit_hint = 0;
|
||||
#endif
|
||||
|
||||
if (RE_rayobject_intersect(r, isec)) {
|
||||
RE_RC_COUNT(isec->raycounter->raycast.hit);
|
||||
|
||||
#ifdef RT_USE_HINT
|
||||
isec->hint = isec->hit_hint;
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RE_rayobject_intersect(RayObject *r, Isect *i)
|
||||
{
|
||||
if (RE_rayobject_isRayFace(r)) {
|
||||
return intersect_rayface(r, (RayFace *) RE_rayobject_align(r), i);
|
||||
}
|
||||
else if (RE_rayobject_isVlakPrimitive(r)) {
|
||||
//TODO optimize (useless copy to RayFace to avoid duplicate code)
|
||||
VlakPrimitive *face = (VlakPrimitive *) RE_rayobject_align(r);
|
||||
RayFace nface;
|
||||
rayface_from_vlak(&nface, face->ob, face->face);
|
||||
|
||||
return intersect_rayface(r, &nface, i);
|
||||
}
|
||||
else if (RE_rayobject_isRayAPI(r)) {
|
||||
r = RE_rayobject_align(r);
|
||||
return r->api->raycast(r, i);
|
||||
}
|
||||
else {
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Building */
|
||||
|
||||
void RE_rayobject_add(RayObject *r, RayObject *o)
|
||||
{
|
||||
r = RE_rayobject_align(r);
|
||||
return r->api->add(r, o);
|
||||
}
|
||||
|
||||
void RE_rayobject_done(RayObject *r)
|
||||
{
|
||||
r = RE_rayobject_align(r);
|
||||
r->api->done(r);
|
||||
}
|
||||
|
||||
void RE_rayobject_free(RayObject *r)
|
||||
{
|
||||
r = RE_rayobject_align(r);
|
||||
r->api->free(r);
|
||||
}
|
||||
|
||||
float RE_rayobject_cost(RayObject *r)
|
||||
{
|
||||
if (RE_rayobject_isRayFace(r) || RE_rayobject_isVlakPrimitive(r)) {
|
||||
return 1.0f;
|
||||
}
|
||||
else if (RE_rayobject_isRayAPI(r)) {
|
||||
r = RE_rayobject_align(r);
|
||||
return r->api->cost(r);
|
||||
}
|
||||
else {
|
||||
assert(0);
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Bounding Boxes */
|
||||
|
||||
void RE_rayobject_merge_bb(RayObject *r, float min[3], float max[3])
|
||||
{
|
||||
if (RE_rayobject_isRayFace(r)) {
|
||||
RayFace *face = (RayFace *) RE_rayobject_align(r);
|
||||
|
||||
DO_MINMAX(face->v1, min, max);
|
||||
DO_MINMAX(face->v2, min, max);
|
||||
DO_MINMAX(face->v3, min, max);
|
||||
if (RE_rayface_isQuad(face)) DO_MINMAX(face->v4, min, max);
|
||||
}
|
||||
else if (RE_rayobject_isVlakPrimitive(r)) {
|
||||
VlakPrimitive *face = (VlakPrimitive *) RE_rayobject_align(r);
|
||||
RayFace nface;
|
||||
rayface_from_vlak(&nface, face->ob, face->face);
|
||||
|
||||
DO_MINMAX(nface.v1, min, max);
|
||||
DO_MINMAX(nface.v2, min, max);
|
||||
DO_MINMAX(nface.v3, min, max);
|
||||
if (RE_rayface_isQuad(&nface)) DO_MINMAX(nface.v4, min, max);
|
||||
}
|
||||
else if (RE_rayobject_isRayAPI(r)) {
|
||||
r = RE_rayobject_align(r);
|
||||
r->api->bb(r, min, max);
|
||||
}
|
||||
else
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* Hints */
|
||||
|
||||
void RE_rayobject_hint_bb(RayObject *r, RayHint *hint, float *min, float *max)
|
||||
{
|
||||
if (RE_rayobject_isRayFace(r) || RE_rayobject_isVlakPrimitive(r)) {
|
||||
return;
|
||||
}
|
||||
else if (RE_rayobject_isRayAPI(r)) {
|
||||
r = RE_rayobject_align(r);
|
||||
return r->api->hint_bb(r, hint, min, max);
|
||||
}
|
||||
else
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* RayObjectControl */
|
||||
|
||||
int RE_rayobjectcontrol_test_break(RayObjectControl *control)
|
||||
{
|
||||
if (control->test_break)
|
||||
return control->test_break(control->data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RE_rayobject_set_control(RayObject *r, void *data, RE_rayobjectcontrol_test_break_callback test_break)
|
||||
{
|
||||
if (RE_rayobject_isRayAPI(r)) {
|
||||
r = RE_rayobject_align(r);
|
||||
r->control.data = data;
|
||||
r->control.test_break = test_break;
|
||||
}
|
||||
}
|
||||
|
72
source/blender/render/intern/raytrace/rayobject_hint.h
Normal file
72
source/blender/render/intern/raytrace/rayobject_hint.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/raytrace/rayobject_hint.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __RAYOBJECT_HINT_H__
|
||||
#define __RAYOBJECT_HINT_H__
|
||||
|
||||
#define HINT_RECURSE 1
|
||||
#define HINT_ACCEPT 0
|
||||
#define HINT_DISCARD -1
|
||||
|
||||
struct HintBB {
|
||||
float bb[6];
|
||||
};
|
||||
|
||||
inline int hint_test_bb(HintBB *obj, float *Nmin, float *Nmax)
|
||||
{
|
||||
if (bb_fits_inside(Nmin, Nmax, obj->bb, obj->bb + 3) )
|
||||
return HINT_RECURSE;
|
||||
else
|
||||
return HINT_ACCEPT;
|
||||
}
|
||||
#if 0
|
||||
struct HintFrustum {
|
||||
float co[3];
|
||||
float no[4][3];
|
||||
};
|
||||
|
||||
inline int hint_test_bb(HintFrustum &obj, float *Nmin, float *Nmax)
|
||||
{
|
||||
//if frustum inside BB
|
||||
{
|
||||
return HINT_RECURSE;
|
||||
}
|
||||
//if BB outside frustum
|
||||
{
|
||||
return HINT_DISCARD;
|
||||
}
|
||||
|
||||
return HINT_ACCEPT;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __RAYOBJECT_HINT_H__ */
|
211
source/blender/render/intern/raytrace/rayobject_instance.cpp
Normal file
211
source/blender/render/intern/raytrace/rayobject_instance.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/raytrace/rayobject_instance.cpp
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "rayintersection.h"
|
||||
#include "rayobject.h"
|
||||
|
||||
#define RE_COST_INSTANCE (1.0f)
|
||||
|
||||
static int RE_rayobject_instance_intersect(RayObject *o, Isect *isec);
|
||||
static void RE_rayobject_instance_free(RayObject *o);
|
||||
static void RE_rayobject_instance_bb(RayObject *o, float *min, float *max);
|
||||
static float RE_rayobject_instance_cost(RayObject *o);
|
||||
|
||||
static void RE_rayobject_instance_hint_bb(RayObject *UNUSED(o), RayHint *UNUSED(hint),
|
||||
float *UNUSED(min), float *UNUSED(max))
|
||||
{}
|
||||
|
||||
static RayObjectAPI instance_api =
|
||||
{
|
||||
RE_rayobject_instance_intersect,
|
||||
NULL, //static void RE_rayobject_instance_add(RayObject *o, RayObject *ob);
|
||||
NULL, //static void RE_rayobject_instance_done(RayObject *o);
|
||||
RE_rayobject_instance_free,
|
||||
RE_rayobject_instance_bb,
|
||||
RE_rayobject_instance_cost,
|
||||
RE_rayobject_instance_hint_bb
|
||||
};
|
||||
|
||||
typedef struct InstanceRayObject {
|
||||
RayObject rayobj;
|
||||
RayObject *target;
|
||||
|
||||
void *ob; //Object represented by this instance
|
||||
void *target_ob; //Object represented by the inner RayObject, needed to handle self-intersection
|
||||
|
||||
float global2target[4][4];
|
||||
float target2global[4][4];
|
||||
|
||||
} InstanceRayObject;
|
||||
|
||||
|
||||
RayObject *RE_rayobject_instance_create(RayObject *target, float transform[4][4], void *ob, void *target_ob)
|
||||
{
|
||||
InstanceRayObject *obj = (InstanceRayObject *)MEM_callocN(sizeof(InstanceRayObject), "InstanceRayObject");
|
||||
assert(RE_rayobject_isAligned(obj) ); /* RayObject API assumes real data to be 4-byte aligned */
|
||||
|
||||
obj->rayobj.api = &instance_api;
|
||||
obj->target = target;
|
||||
obj->ob = ob;
|
||||
obj->target_ob = target_ob;
|
||||
|
||||
copy_m4_m4(obj->target2global, transform);
|
||||
invert_m4_m4(obj->global2target, obj->target2global);
|
||||
|
||||
return RE_rayobject_unalignRayAPI((RayObject *) obj);
|
||||
}
|
||||
|
||||
static int RE_rayobject_instance_intersect(RayObject *o, Isect *isec)
|
||||
{
|
||||
InstanceRayObject *obj = (InstanceRayObject *)o;
|
||||
float start[3], dir[3], idot_axis[3], dist;
|
||||
int changed = 0, i, res;
|
||||
|
||||
// TODO - this is disabling self intersection on instances
|
||||
if (isec->orig.ob == obj->ob && obj->ob) {
|
||||
changed = 1;
|
||||
isec->orig.ob = obj->target_ob;
|
||||
}
|
||||
|
||||
// backup old values
|
||||
copy_v3_v3(start, isec->start);
|
||||
copy_v3_v3(dir, isec->dir);
|
||||
copy_v3_v3(idot_axis, isec->idot_axis);
|
||||
dist = isec->dist;
|
||||
|
||||
// transform to target coordinates system
|
||||
mul_m4_v3(obj->global2target, isec->start);
|
||||
mul_mat3_m4_v3(obj->global2target, isec->dir);
|
||||
isec->dist *= normalize_v3(isec->dir);
|
||||
|
||||
// update idot_axis and bv_index
|
||||
for (i = 0; i < 3; i++) {
|
||||
isec->idot_axis[i] = 1.0f / isec->dir[i];
|
||||
|
||||
isec->bv_index[2 * i] = isec->idot_axis[i] < 0.0f ? 1 : 0;
|
||||
isec->bv_index[2 * i + 1] = 1 - isec->bv_index[2 * i];
|
||||
|
||||
isec->bv_index[2 * i] = i + 3 * isec->bv_index[2 * i];
|
||||
isec->bv_index[2 * i + 1] = i + 3 * isec->bv_index[2 * i + 1];
|
||||
}
|
||||
|
||||
// Pre-calculate orientation for watertight intersection checks.
|
||||
isect_ray_tri_watertight_v3_precalc(&isec->isect_precalc, isec->dir);
|
||||
|
||||
// raycast
|
||||
res = RE_rayobject_intersect(obj->target, isec);
|
||||
|
||||
// map dist into original coordinate space
|
||||
if (res == 0) {
|
||||
isec->dist = dist;
|
||||
}
|
||||
else {
|
||||
// note we don't just multiply dist, because of possible
|
||||
// non-uniform scaling in the transform matrix
|
||||
float vec[3];
|
||||
|
||||
mul_v3_v3fl(vec, isec->dir, isec->dist);
|
||||
mul_mat3_m4_v3(obj->target2global, vec);
|
||||
|
||||
isec->dist = len_v3(vec);
|
||||
isec->hit.ob = obj->ob;
|
||||
|
||||
#ifdef RT_USE_LAST_HIT
|
||||
// TODO support for last hit optimization in instances that can jump
|
||||
// directly to the last hit face.
|
||||
// For now it jumps directly to the last-hit instance root node.
|
||||
isec->last_hit = RE_rayobject_unalignRayAPI((RayObject *) obj);
|
||||
#endif
|
||||
}
|
||||
|
||||
// restore values
|
||||
copy_v3_v3(isec->start, start);
|
||||
copy_v3_v3(isec->dir, dir);
|
||||
copy_v3_v3(isec->idot_axis, idot_axis);
|
||||
|
||||
if (changed)
|
||||
isec->orig.ob = obj->ob;
|
||||
|
||||
// restore bv_index
|
||||
for (i = 0; i < 3; i++) {
|
||||
isec->bv_index[2 * i] = isec->idot_axis[i] < 0.0f ? 1 : 0;
|
||||
isec->bv_index[2 * i + 1] = 1 - isec->bv_index[2 * i];
|
||||
|
||||
isec->bv_index[2 * i] = i + 3 * isec->bv_index[2 * i];
|
||||
isec->bv_index[2 * i + 1] = i + 3 * isec->bv_index[2 * i + 1];
|
||||
}
|
||||
|
||||
// Pre-calculate orientation for watertight intersection checks.
|
||||
isect_ray_tri_watertight_v3_precalc(&isec->isect_precalc, isec->dir);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void RE_rayobject_instance_free(RayObject *o)
|
||||
{
|
||||
InstanceRayObject *obj = (InstanceRayObject *)o;
|
||||
MEM_freeN(obj);
|
||||
}
|
||||
|
||||
static float RE_rayobject_instance_cost(RayObject *o)
|
||||
{
|
||||
InstanceRayObject *obj = (InstanceRayObject *)o;
|
||||
return RE_rayobject_cost(obj->target) + RE_COST_INSTANCE;
|
||||
}
|
||||
|
||||
static void RE_rayobject_instance_bb(RayObject *o, float *min, float *max)
|
||||
{
|
||||
//TODO:
|
||||
// *better bb.. calculated without rotations of bb
|
||||
// *maybe cache that better-fitted-BB at the InstanceRayObject
|
||||
InstanceRayObject *obj = (InstanceRayObject *)o;
|
||||
|
||||
float m[3], M[3], t[3];
|
||||
int i, j;
|
||||
INIT_MINMAX(m, M);
|
||||
RE_rayobject_merge_bb(obj->target, m, M);
|
||||
|
||||
//There must be a faster way than rotating all the 8 vertexs of the BB
|
||||
for (i = 0; i < 8; i++) {
|
||||
for (j = 0; j < 3; j++) t[j] = (i & (1 << j)) ? M[j] : m[j];
|
||||
mul_m4_v3(obj->target2global, t);
|
||||
DO_MINMAX(t, min, max);
|
||||
}
|
||||
}
|
||||
|
1101
source/blender/render/intern/raytrace/rayobject_octree.cpp
Normal file
1101
source/blender/render/intern/raytrace/rayobject_octree.cpp
Normal file
File diff suppressed because it is too large
Load Diff
160
source/blender/render/intern/raytrace/rayobject_qbvh.cpp
Normal file
160
source/blender/render/intern/raytrace/rayobject_qbvh.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/raytrace/rayobject_qbvh.cpp
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "vbvh.h"
|
||||
#include "svbvh.h"
|
||||
#include "reorganize.h"
|
||||
|
||||
#ifdef __SSE__
|
||||
|
||||
#define DFS_STACK_SIZE 256
|
||||
|
||||
struct QBVHTree {
|
||||
RayObject rayobj;
|
||||
|
||||
SVBVHNode *root;
|
||||
MemArena *node_arena;
|
||||
|
||||
float cost;
|
||||
RTBuilder *builder;
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
void bvh_done<QBVHTree>(QBVHTree *obj)
|
||||
{
|
||||
rtbuild_done(obj->builder, &obj->rayobj.control);
|
||||
|
||||
//TODO find a away to exactly calculate the needed memory
|
||||
MemArena *arena1 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "qbvh arena");
|
||||
BLI_memarena_use_malloc(arena1);
|
||||
|
||||
MemArena *arena2 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "qbvh arena 2");
|
||||
BLI_memarena_use_malloc(arena2);
|
||||
BLI_memarena_use_align(arena2, 16);
|
||||
|
||||
//Build and optimize the tree
|
||||
//TODO do this in 1 pass (half memory usage during building)
|
||||
VBVHNode *root = BuildBinaryVBVH<VBVHNode>(arena1, &obj->rayobj.control).transform(obj->builder);
|
||||
|
||||
if (RE_rayobjectcontrol_test_break(&obj->rayobj.control)) {
|
||||
BLI_memarena_free(arena1);
|
||||
BLI_memarena_free(arena2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root) {
|
||||
pushup_simd<VBVHNode, 4>(root);
|
||||
obj->root = Reorganize_SVBVH<VBVHNode>(arena2).transform(root);
|
||||
}
|
||||
else
|
||||
obj->root = NULL;
|
||||
|
||||
//Free data
|
||||
BLI_memarena_free(arena1);
|
||||
|
||||
obj->node_arena = arena2;
|
||||
obj->cost = 1.0;
|
||||
|
||||
rtbuild_free(obj->builder);
|
||||
obj->builder = NULL;
|
||||
}
|
||||
|
||||
template<int StackSize>
|
||||
static int intersect(QBVHTree *obj, Isect *isec)
|
||||
{
|
||||
//TODO renable hint support
|
||||
if (RE_rayobject_isAligned(obj->root)) {
|
||||
if (isec->mode == RE_RAY_SHADOW)
|
||||
return svbvh_node_stack_raycast<StackSize, true>(obj->root, isec);
|
||||
else
|
||||
return svbvh_node_stack_raycast<StackSize, false>(obj->root, isec);
|
||||
}
|
||||
else
|
||||
return RE_rayobject_intersect((RayObject *)obj->root, isec);
|
||||
}
|
||||
|
||||
template<class Tree>
|
||||
static void bvh_hint_bb(Tree *tree, LCTSHint *hint, float *UNUSED(min), float *UNUSED(max))
|
||||
{
|
||||
//TODO renable hint support
|
||||
{
|
||||
hint->size = 0;
|
||||
hint->stack[hint->size++] = (RayObject *)tree->root;
|
||||
}
|
||||
}
|
||||
/* the cast to pointer function is needed to workarround gcc bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11407 */
|
||||
template<class Tree, int STACK_SIZE>
|
||||
static RayObjectAPI make_api()
|
||||
{
|
||||
static RayObjectAPI api =
|
||||
{
|
||||
(RE_rayobject_raycast_callback) ((int (*)(Tree *, Isect *)) & intersect<STACK_SIZE>),
|
||||
(RE_rayobject_add_callback) ((void (*)(Tree *, RayObject *)) & bvh_add<Tree>),
|
||||
(RE_rayobject_done_callback) ((void (*)(Tree *)) & bvh_done<Tree>),
|
||||
(RE_rayobject_free_callback) ((void (*)(Tree *)) & bvh_free<Tree>),
|
||||
(RE_rayobject_merge_bb_callback)((void (*)(Tree *, float *, float *)) & bvh_bb<Tree>),
|
||||
(RE_rayobject_cost_callback) ((float (*)(Tree *)) & bvh_cost<Tree>),
|
||||
(RE_rayobject_hint_bb_callback) ((void (*)(Tree *, LCTSHint *, float *, float *)) & bvh_hint_bb<Tree>)
|
||||
};
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
template<class Tree>
|
||||
RayObjectAPI *bvh_get_api(int maxstacksize)
|
||||
{
|
||||
static RayObjectAPI bvh_api256 = make_api<Tree, 1024>();
|
||||
|
||||
if (maxstacksize <= 1024) return &bvh_api256;
|
||||
assert(maxstacksize <= 256);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RayObject *RE_rayobject_qbvh_create(int size)
|
||||
{
|
||||
return bvh_create_tree<QBVHTree, DFS_STACK_SIZE>(size);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
RayObject *RE_rayobject_qbvh_create(int UNUSED(size))
|
||||
{
|
||||
puts("WARNING: SSE disabled at compile time\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/raytrace/rayobject_raycounter.cpp
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#include "rayobject.h"
|
||||
#include "raycounter.h"
|
||||
|
||||
#ifdef RE_RAYCOUNTER
|
||||
|
||||
void RE_RC_INFO(RayCounter *info)
|
||||
{
|
||||
printf("----------- Raycast counter --------\n");
|
||||
printf("Rays total: %llu\n", info->raycast.test );
|
||||
printf("Rays hit: %llu\n", info->raycast.hit );
|
||||
printf("\n");
|
||||
printf("BB tests: %llu\n", info->bb.test );
|
||||
printf("BB hits: %llu\n", info->bb.hit );
|
||||
printf("\n");
|
||||
printf("SIMD BB tests: %llu\n", info->simd_bb.test );
|
||||
printf("SIMD BB hits: %llu\n", info->simd_bb.hit );
|
||||
printf("\n");
|
||||
printf("Primitives tests: %llu\n", info->faces.test );
|
||||
printf("Primitives hits: %llu\n", info->faces.hit );
|
||||
printf("------------------------------------\n");
|
||||
printf("Shadow last-hit tests per ray: %f\n", info->rayshadow_last_hit.test / ((float)info->raycast.test) );
|
||||
printf("Shadow last-hit hits per ray: %f\n", info->rayshadow_last_hit.hit / ((float)info->raycast.test) );
|
||||
printf("\n");
|
||||
printf("Hint tests per ray: %f\n", info->raytrace_hint.test / ((float)info->raycast.test) );
|
||||
printf("Hint hits per ray: %f\n", info->raytrace_hint.hit / ((float)info->raycast.test) );
|
||||
printf("\n");
|
||||
printf("BB tests per ray: %f\n", info->bb.test / ((float)info->raycast.test) );
|
||||
printf("BB hits per ray: %f\n", info->bb.hit / ((float)info->raycast.test) );
|
||||
printf("\n");
|
||||
printf("SIMD tests per ray: %f\n", info->simd_bb.test / ((float)info->raycast.test) );
|
||||
printf("SIMD hits per ray: %f\n", info->simd_bb.hit / ((float)info->raycast.test) );
|
||||
printf("\n");
|
||||
printf("Primitives tests per ray: %f\n", info->faces.test / ((float)info->raycast.test) );
|
||||
printf("Primitives hits per ray: %f\n", info->faces.hit / ((float)info->raycast.test) );
|
||||
printf("------------------------------------\n");
|
||||
}
|
||||
|
||||
void RE_RC_MERGE(RayCounter *dest, RayCounter *tmp)
|
||||
{
|
||||
dest->faces.test += tmp->faces.test;
|
||||
dest->faces.hit += tmp->faces.hit;
|
||||
|
||||
dest->bb.test += tmp->bb.test;
|
||||
dest->bb.hit += tmp->bb.hit;
|
||||
|
||||
dest->simd_bb.test += tmp->simd_bb.test;
|
||||
dest->simd_bb.hit += tmp->simd_bb.hit;
|
||||
|
||||
dest->raycast.test += tmp->raycast.test;
|
||||
dest->raycast.hit += tmp->raycast.hit;
|
||||
|
||||
dest->rayshadow_last_hit.test += tmp->rayshadow_last_hit.test;
|
||||
dest->rayshadow_last_hit.hit += tmp->rayshadow_last_hit.hit;
|
||||
|
||||
dest->raytrace_hint.test += tmp->raytrace_hint.test;
|
||||
dest->raytrace_hint.hit += tmp->raytrace_hint.hit;
|
||||
}
|
||||
|
||||
#endif
|
531
source/blender/render/intern/raytrace/rayobject_rtbuild.cpp
Normal file
531
source/blender/render/intern/raytrace/rayobject_rtbuild.cpp
Normal file
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/raytrace/rayobject_rtbuild.cpp
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
#include <cmath>
|
||||
using std::isfinite;
|
||||
#else
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
#include "rayobject_rtbuild.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
static bool selected_node(RTBuilder::Object *node)
|
||||
{
|
||||
return node->selected;
|
||||
}
|
||||
|
||||
static void rtbuild_init(RTBuilder *b)
|
||||
{
|
||||
b->split_axis = -1;
|
||||
b->primitives.begin = NULL;
|
||||
b->primitives.end = NULL;
|
||||
b->primitives.maxsize = 0;
|
||||
b->depth = 0;
|
||||
|
||||
for (int i = 0; i < RTBUILD_MAX_CHILDS; i++)
|
||||
b->child_offset[i] = 0;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
b->sorted_begin[i] = b->sorted_end[i] = NULL;
|
||||
|
||||
INIT_MINMAX(b->bb, b->bb + 3);
|
||||
}
|
||||
|
||||
RTBuilder *rtbuild_create(int size)
|
||||
{
|
||||
RTBuilder *builder = (RTBuilder *) MEM_mallocN(sizeof(RTBuilder), "RTBuilder");
|
||||
RTBuilder::Object *memblock = (RTBuilder::Object *)MEM_mallocN(sizeof(RTBuilder::Object) * size, "RTBuilder.objects");
|
||||
|
||||
|
||||
rtbuild_init(builder);
|
||||
|
||||
builder->primitives.begin = builder->primitives.end = memblock;
|
||||
builder->primitives.maxsize = size;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
builder->sorted_begin[i] = (RTBuilder::Object **)MEM_mallocN(sizeof(RTBuilder::Object *) * size, "RTBuilder.sorted_objects");
|
||||
builder->sorted_end[i] = builder->sorted_begin[i];
|
||||
}
|
||||
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
void rtbuild_free(RTBuilder *b)
|
||||
{
|
||||
if (b->primitives.begin) MEM_freeN(b->primitives.begin);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
if (b->sorted_begin[i])
|
||||
MEM_freeN(b->sorted_begin[i]);
|
||||
|
||||
MEM_freeN(b);
|
||||
}
|
||||
|
||||
void rtbuild_add(RTBuilder *b, RayObject *o)
|
||||
{
|
||||
float bb[6];
|
||||
|
||||
assert(b->primitives.begin + b->primitives.maxsize != b->primitives.end);
|
||||
|
||||
INIT_MINMAX(bb, bb + 3);
|
||||
RE_rayobject_merge_bb(o, bb, bb + 3);
|
||||
|
||||
/* skip objects with invalid bounding boxes, nan causes DO_MINMAX
|
||||
* to do nothing, so we get these invalid values. this shouldn't
|
||||
* happen usually, but bugs earlier in the pipeline can cause it. */
|
||||
if (bb[0] > bb[3] || bb[1] > bb[4] || bb[2] > bb[5])
|
||||
return;
|
||||
/* skip objects with inf bounding boxes */
|
||||
if (!isfinite(bb[0]) || !isfinite(bb[1]) || !isfinite(bb[2]))
|
||||
return;
|
||||
if (!isfinite(bb[3]) || !isfinite(bb[4]) || !isfinite(bb[5]))
|
||||
return;
|
||||
/* skip objects with zero bounding box, they are of no use, and
|
||||
* will give problems in rtbuild_heuristic_object_split later */
|
||||
if (bb[0] == bb[3] && bb[1] == bb[4] && bb[2] == bb[5])
|
||||
return;
|
||||
|
||||
copy_v3_v3(b->primitives.end->bb, bb);
|
||||
copy_v3_v3(b->primitives.end->bb + 3, bb + 3);
|
||||
b->primitives.end->obj = o;
|
||||
b->primitives.end->cost = RE_rayobject_cost(o);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
*(b->sorted_end[i]) = b->primitives.end;
|
||||
b->sorted_end[i]++;
|
||||
}
|
||||
b->primitives.end++;
|
||||
}
|
||||
|
||||
int rtbuild_size(RTBuilder *b)
|
||||
{
|
||||
return b->sorted_end[0] - b->sorted_begin[0];
|
||||
}
|
||||
|
||||
|
||||
template<class Obj, int Axis>
|
||||
static bool obj_bb_compare(const Obj &a, const Obj &b)
|
||||
{
|
||||
if (a->bb[Axis] != b->bb[Axis])
|
||||
return a->bb[Axis] < b->bb[Axis];
|
||||
return a->obj < b->obj;
|
||||
}
|
||||
|
||||
template<class Item>
|
||||
static void object_sort(Item *begin, Item *end, int axis)
|
||||
{
|
||||
if (axis == 0) return std::sort(begin, end, obj_bb_compare<Item, 0> );
|
||||
if (axis == 1) return std::sort(begin, end, obj_bb_compare<Item, 1> );
|
||||
if (axis == 2) return std::sort(begin, end, obj_bb_compare<Item, 2> );
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void rtbuild_done(RTBuilder *b, RayObjectControl *ctrl)
|
||||
{
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (b->sorted_begin[i]) {
|
||||
if (RE_rayobjectcontrol_test_break(ctrl)) break;
|
||||
object_sort(b->sorted_begin[i], b->sorted_end[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RayObject *rtbuild_get_primitive(RTBuilder *b, int index)
|
||||
{
|
||||
return b->sorted_begin[0][index]->obj;
|
||||
}
|
||||
|
||||
RTBuilder *rtbuild_get_child(RTBuilder *b, int child, RTBuilder *tmp)
|
||||
{
|
||||
rtbuild_init(tmp);
|
||||
|
||||
tmp->depth = b->depth + 1;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
if (b->sorted_begin[i]) {
|
||||
tmp->sorted_begin[i] = b->sorted_begin[i] + b->child_offset[child];
|
||||
tmp->sorted_end[i] = b->sorted_begin[i] + b->child_offset[child + 1];
|
||||
}
|
||||
else {
|
||||
tmp->sorted_begin[i] = NULL;
|
||||
tmp->sorted_end[i] = NULL;
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void rtbuild_calc_bb(RTBuilder *b)
|
||||
{
|
||||
if (b->bb[0] == 1.0e30f) {
|
||||
for (RTBuilder::Object **index = b->sorted_begin[0]; index != b->sorted_end[0]; index++)
|
||||
RE_rayobject_merge_bb( (*index)->obj, b->bb, b->bb + 3);
|
||||
}
|
||||
}
|
||||
|
||||
void rtbuild_merge_bb(RTBuilder *b, float min[3], float max[3])
|
||||
{
|
||||
rtbuild_calc_bb(b);
|
||||
DO_MIN(b->bb, min);
|
||||
DO_MAX(b->bb + 3, max);
|
||||
}
|
||||
|
||||
#if 0
|
||||
int rtbuild_get_largest_axis(RTBuilder *b)
|
||||
{
|
||||
rtbuild_calc_bb(b);
|
||||
return bb_largest_axis(b->bb, b->bb + 3);
|
||||
}
|
||||
|
||||
//Left balanced tree
|
||||
int rtbuild_mean_split(RTBuilder *b, int nchilds, int axis)
|
||||
{
|
||||
int i;
|
||||
int mleafs_per_child, Mleafs_per_child;
|
||||
int tot_leafs = rtbuild_size(b);
|
||||
int missing_leafs;
|
||||
|
||||
long long s;
|
||||
|
||||
assert(nchilds <= RTBUILD_MAX_CHILDS);
|
||||
|
||||
//TODO optimize calc of leafs_per_child
|
||||
for (s = nchilds; s < tot_leafs; s *= nchilds) ;
|
||||
Mleafs_per_child = s / nchilds;
|
||||
mleafs_per_child = Mleafs_per_child / nchilds;
|
||||
|
||||
//split min leafs per child
|
||||
b->child_offset[0] = 0;
|
||||
for (i = 1; i <= nchilds; i++)
|
||||
b->child_offset[i] = mleafs_per_child;
|
||||
|
||||
//split remaining leafs
|
||||
missing_leafs = tot_leafs - mleafs_per_child * nchilds;
|
||||
for (i = 1; i <= nchilds; i++)
|
||||
{
|
||||
if (missing_leafs > Mleafs_per_child - mleafs_per_child)
|
||||
{
|
||||
b->child_offset[i] += Mleafs_per_child - mleafs_per_child;
|
||||
missing_leafs -= Mleafs_per_child - mleafs_per_child;
|
||||
}
|
||||
else {
|
||||
b->child_offset[i] += missing_leafs;
|
||||
missing_leafs = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//adjust for accumulative offsets
|
||||
for (i = 1; i <= nchilds; i++)
|
||||
b->child_offset[i] += b->child_offset[i - 1];
|
||||
|
||||
//Count created childs
|
||||
for (i = nchilds; b->child_offset[i] == b->child_offset[i - 1]; i--) ;
|
||||
split_leafs(b, b->child_offset, i, axis);
|
||||
|
||||
assert(b->child_offset[0] == 0 && b->child_offset[i] == tot_leafs);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
int rtbuild_mean_split_largest_axis(RTBuilder *b, int nchilds)
|
||||
{
|
||||
int axis = rtbuild_get_largest_axis(b);
|
||||
return rtbuild_mean_split(b, nchilds, axis);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* "separators" is an array of dim NCHILDS-1
|
||||
* and indicates where to cut the childs
|
||||
*/
|
||||
#if 0
|
||||
int rtbuild_median_split(RTBuilder *b, float *separators, int nchilds, int axis)
|
||||
{
|
||||
int size = rtbuild_size(b);
|
||||
|
||||
assert(nchilds <= RTBUILD_MAX_CHILDS);
|
||||
if (size <= nchilds)
|
||||
{
|
||||
return rtbuild_mean_split(b, nchilds, axis);
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
|
||||
b->split_axis = axis;
|
||||
|
||||
//Calculate child offsets
|
||||
b->child_offset[0] = 0;
|
||||
for (i = 0; i < nchilds - 1; i++)
|
||||
b->child_offset[i + 1] = split_leafs_by_plane(b, b->child_offset[i], size, separators[i]);
|
||||
b->child_offset[nchilds] = size;
|
||||
|
||||
for (i = 0; i < nchilds; i++)
|
||||
if (b->child_offset[i + 1] - b->child_offset[i] == size)
|
||||
return rtbuild_mean_split(b, nchilds, axis);
|
||||
|
||||
return nchilds;
|
||||
}
|
||||
}
|
||||
|
||||
int rtbuild_median_split_largest_axis(RTBuilder *b, int nchilds)
|
||||
{
|
||||
int la, i;
|
||||
float separators[RTBUILD_MAX_CHILDS];
|
||||
|
||||
rtbuild_calc_bb(b);
|
||||
|
||||
la = bb_largest_axis(b->bb, b->bb + 3);
|
||||
for (i = 1; i < nchilds; i++)
|
||||
separators[i - 1] = (b->bb[la + 3] - b->bb[la]) * i / nchilds;
|
||||
|
||||
return rtbuild_median_split(b, separators, nchilds, la);
|
||||
}
|
||||
#endif
|
||||
|
||||
//Heuristics Object Splitter
|
||||
|
||||
|
||||
struct SweepCost {
|
||||
float bb[6];
|
||||
float cost;
|
||||
};
|
||||
|
||||
/* Object Surface Area Heuristic splitter */
|
||||
int rtbuild_heuristic_object_split(RTBuilder *b, int nchilds)
|
||||
{
|
||||
int size = rtbuild_size(b);
|
||||
assert(nchilds == 2);
|
||||
assert(size > 1);
|
||||
int baxis = -1, boffset = 0;
|
||||
|
||||
if (size > nchilds) {
|
||||
if (b->depth > RTBUILD_MAX_SAH_DEPTH) {
|
||||
// for degenerate cases we avoid running out of stack space
|
||||
// by simply splitting the children in the middle
|
||||
b->child_offset[0] = 0;
|
||||
b->child_offset[1] = (size+1)/2;
|
||||
b->child_offset[2] = size;
|
||||
return 2;
|
||||
}
|
||||
|
||||
float bcost = FLT_MAX;
|
||||
baxis = -1;
|
||||
boffset = size / 2;
|
||||
|
||||
SweepCost *sweep = (SweepCost *)MEM_mallocN(sizeof(SweepCost) * size, "RTBuilder.HeuristicSweep");
|
||||
|
||||
for (int axis = 0; axis < 3; axis++) {
|
||||
SweepCost sweep_left;
|
||||
|
||||
RTBuilder::Object **obj = b->sorted_begin[axis];
|
||||
|
||||
// float right_cost = 0;
|
||||
for (int i = size - 1; i >= 0; i--) {
|
||||
if (i == size - 1) {
|
||||
copy_v3_v3(sweep[i].bb, obj[i]->bb);
|
||||
copy_v3_v3(sweep[i].bb + 3, obj[i]->bb + 3);
|
||||
sweep[i].cost = obj[i]->cost;
|
||||
}
|
||||
else {
|
||||
sweep[i].bb[0] = min_ff(obj[i]->bb[0], sweep[i + 1].bb[0]);
|
||||
sweep[i].bb[1] = min_ff(obj[i]->bb[1], sweep[i + 1].bb[1]);
|
||||
sweep[i].bb[2] = min_ff(obj[i]->bb[2], sweep[i + 1].bb[2]);
|
||||
sweep[i].bb[3] = max_ff(obj[i]->bb[3], sweep[i + 1].bb[3]);
|
||||
sweep[i].bb[4] = max_ff(obj[i]->bb[4], sweep[i + 1].bb[4]);
|
||||
sweep[i].bb[5] = max_ff(obj[i]->bb[5], sweep[i + 1].bb[5]);
|
||||
sweep[i].cost = obj[i]->cost + sweep[i + 1].cost;
|
||||
}
|
||||
// right_cost += obj[i]->cost;
|
||||
}
|
||||
|
||||
sweep_left.bb[0] = obj[0]->bb[0];
|
||||
sweep_left.bb[1] = obj[0]->bb[1];
|
||||
sweep_left.bb[2] = obj[0]->bb[2];
|
||||
sweep_left.bb[3] = obj[0]->bb[3];
|
||||
sweep_left.bb[4] = obj[0]->bb[4];
|
||||
sweep_left.bb[5] = obj[0]->bb[5];
|
||||
sweep_left.cost = obj[0]->cost;
|
||||
|
||||
// right_cost -= obj[0]->cost; if (right_cost < 0) right_cost = 0;
|
||||
|
||||
for (int i = 1; i < size; i++) {
|
||||
//Worst case heuristic (cost of each child is linear)
|
||||
float hcost, left_side, right_side;
|
||||
|
||||
// not using log seems to have no impact on raytracing perf, but
|
||||
// makes tree construction quicker, left out for now to test (brecht)
|
||||
// left_side = bb_area(sweep_left.bb, sweep_left.bb + 3) * (sweep_left.cost + logf((float)i));
|
||||
// right_side = bb_area(sweep[i].bb, sweep[i].bb + 3) * (sweep[i].cost + logf((float)size - i));
|
||||
left_side = bb_area(sweep_left.bb, sweep_left.bb + 3) * (sweep_left.cost);
|
||||
right_side = bb_area(sweep[i].bb, sweep[i].bb + 3) * (sweep[i].cost);
|
||||
hcost = left_side + right_side;
|
||||
|
||||
assert(left_side >= 0);
|
||||
assert(right_side >= 0);
|
||||
|
||||
if (left_side > bcost) break; //No way we can find a better heuristic in this axis
|
||||
|
||||
assert(hcost >= 0);
|
||||
// this makes sure the tree built is the same whatever is the order of the sorting axis
|
||||
if (hcost < bcost || (hcost == bcost && axis < baxis)) {
|
||||
bcost = hcost;
|
||||
baxis = axis;
|
||||
boffset = i;
|
||||
}
|
||||
DO_MIN(obj[i]->bb, sweep_left.bb);
|
||||
DO_MAX(obj[i]->bb + 3, sweep_left.bb + 3);
|
||||
|
||||
sweep_left.cost += obj[i]->cost;
|
||||
// right_cost -= obj[i]->cost; if (right_cost < 0) right_cost = 0;
|
||||
}
|
||||
|
||||
//assert(baxis >= 0 && baxis < 3);
|
||||
if (!(baxis >= 0 && baxis < 3))
|
||||
baxis = 0;
|
||||
}
|
||||
|
||||
|
||||
MEM_freeN(sweep);
|
||||
}
|
||||
else if (size == 2) {
|
||||
baxis = 0;
|
||||
boffset = 1;
|
||||
}
|
||||
else if (size == 1) {
|
||||
b->child_offset[0] = 0;
|
||||
b->child_offset[1] = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
b->child_offset[0] = 0;
|
||||
b->child_offset[1] = boffset;
|
||||
b->child_offset[2] = size;
|
||||
|
||||
|
||||
/* Adjust sorted arrays for childs */
|
||||
for (int i = 0; i < boffset; i++) b->sorted_begin[baxis][i]->selected = true;
|
||||
for (int i = boffset; i < size; i++) b->sorted_begin[baxis][i]->selected = false;
|
||||
for (int i = 0; i < 3; i++)
|
||||
std::stable_partition(b->sorted_begin[i], b->sorted_end[i], selected_node);
|
||||
|
||||
return nchilds;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper code
|
||||
* PARTITION code / used on mean-split
|
||||
* basically this a std::nth_element (like on C++ STL algorithm)
|
||||
*/
|
||||
#if 0
|
||||
static void split_leafs(RTBuilder *b, int *nth, int partitions, int split_axis)
|
||||
{
|
||||
int i;
|
||||
b->split_axis = split_axis;
|
||||
|
||||
for (i = 0; i < partitions - 1; i++)
|
||||
{
|
||||
assert(nth[i] < nth[i + 1] && nth[i + 1] < nth[partitions]);
|
||||
|
||||
if (split_axis == 0) std::nth_element(b, nth[i], nth[i + 1], nth[partitions], obj_bb_compare<RTBuilder::Object, 0>);
|
||||
if (split_axis == 1) std::nth_element(b, nth[i], nth[i + 1], nth[partitions], obj_bb_compare<RTBuilder::Object, 1>);
|
||||
if (split_axis == 2) std::nth_element(b, nth[i], nth[i + 1], nth[partitions], obj_bb_compare<RTBuilder::Object, 2>);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Bounding Box utils
|
||||
*/
|
||||
float bb_volume(const float min[3], const float max[3])
|
||||
{
|
||||
return (max[0] - min[0]) * (max[1] - min[1]) * (max[2] - min[2]);
|
||||
}
|
||||
|
||||
float bb_area(const float min[3], const float max[3])
|
||||
{
|
||||
float sub[3], a;
|
||||
sub[0] = max[0] - min[0];
|
||||
sub[1] = max[1] - min[1];
|
||||
sub[2] = max[2] - min[2];
|
||||
|
||||
a = (sub[0] * sub[1] + sub[0] * sub[2] + sub[1] * sub[2]) * 2.0f;
|
||||
/* used to have an assert() here on negative results
|
||||
* however, in this case its likely some overflow or ffast math error.
|
||||
* so just return 0.0f instead. */
|
||||
return a < 0.0f ? 0.0f : a;
|
||||
}
|
||||
|
||||
int bb_largest_axis(const float min[3], const float max[3])
|
||||
{
|
||||
float sub[3];
|
||||
|
||||
sub[0] = max[0] - min[0];
|
||||
sub[1] = max[1] - min[1];
|
||||
sub[2] = max[2] - min[2];
|
||||
if (sub[0] > sub[1]) {
|
||||
if (sub[0] > sub[2])
|
||||
return 0;
|
||||
else
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
if (sub[1] > sub[2])
|
||||
return 1;
|
||||
else
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* only returns 0 if merging inner and outerbox would create a box larger than outer box */
|
||||
int bb_fits_inside(const float outer_min[3], const float outer_max[3],
|
||||
const float inner_min[3], const float inner_max[3])
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 3; i++)
|
||||
if (outer_min[i] > inner_min[i]) return 0;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
if (outer_max[i] < inner_max[i]) return 0;
|
||||
|
||||
return 1;
|
||||
}
|
125
source/blender/render/intern/raytrace/rayobject_rtbuild.h
Normal file
125
source/blender/render/intern/raytrace/rayobject_rtbuild.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/raytrace/rayobject_rtbuild.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#ifndef __RAYOBJECT_RTBUILD_H__
|
||||
#define __RAYOBJECT_RTBUILD_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "rayobject.h"
|
||||
|
||||
|
||||
/*
|
||||
* Ray Tree Builder
|
||||
* this structs helps building any type of tree
|
||||
* it contains several methods to organize/split nodes
|
||||
* allowing to create a given tree on the fly.
|
||||
*
|
||||
* Idea is that other trees BVH, BIH can use this code to
|
||||
* generate with simple calls, and then convert to the theirs
|
||||
* specific structure on the fly.
|
||||
*/
|
||||
#define RTBUILD_MAX_CHILDS 32
|
||||
#define RTBUILD_MAX_SAH_DEPTH 256
|
||||
|
||||
|
||||
typedef struct RTBuilder {
|
||||
struct Object {
|
||||
RayObject *obj;
|
||||
float cost;
|
||||
float bb[6];
|
||||
int selected;
|
||||
};
|
||||
|
||||
/* list to all primitives added in this tree */
|
||||
struct {
|
||||
Object *begin, *end;
|
||||
int maxsize;
|
||||
} primitives;
|
||||
|
||||
/* sorted list of rayobjects */
|
||||
struct Object **sorted_begin[3], **sorted_end[3];
|
||||
|
||||
/* axis used (if any) on the split method */
|
||||
int split_axis;
|
||||
|
||||
/* child partitions calculated during splitting */
|
||||
int child_offset[RTBUILD_MAX_CHILDS + 1];
|
||||
|
||||
// int child_sorted_axis; /* -1 if not sorted */
|
||||
|
||||
float bb[6];
|
||||
|
||||
/* current depth */
|
||||
int depth;
|
||||
} RTBuilder;
|
||||
|
||||
/* used during creation */
|
||||
RTBuilder *rtbuild_create(int size);
|
||||
void rtbuild_free(RTBuilder *b);
|
||||
void rtbuild_add(RTBuilder *b, RayObject *o);
|
||||
void rtbuild_done(RTBuilder *b, RayObjectControl *c);
|
||||
void rtbuild_merge_bb(RTBuilder *b, float min[3], float max[3]);
|
||||
int rtbuild_size(RTBuilder *b);
|
||||
|
||||
RayObject *rtbuild_get_primitive(RTBuilder *b, int offset);
|
||||
|
||||
/* used during tree reorganization */
|
||||
RTBuilder *rtbuild_get_child(RTBuilder *b, int child, RTBuilder *tmp);
|
||||
|
||||
/* Calculates child partitions and returns number of efectively needed partitions */
|
||||
int rtbuild_get_largest_axis(RTBuilder *b);
|
||||
|
||||
//Object partition
|
||||
int rtbuild_mean_split(RTBuilder *b, int nchilds, int axis);
|
||||
int rtbuild_mean_split_largest_axis(RTBuilder *b, int nchilds);
|
||||
|
||||
int rtbuild_heuristic_object_split(RTBuilder *b, int nchilds);
|
||||
|
||||
//Space partition
|
||||
int rtbuild_median_split(RTBuilder *b, float *separators, int nchilds, int axis);
|
||||
int rtbuild_median_split_largest_axis(RTBuilder *b, int nchilds);
|
||||
|
||||
|
||||
/* bb utils */
|
||||
float bb_area(const float min[3], const float max[3]);
|
||||
float bb_volume(const float min[3], const float max[3]);
|
||||
int bb_largest_axis(const float min[3], const float max[3]);
|
||||
int bb_fits_inside(const float outer_min[3], const float outer_max[3],
|
||||
const float inner_min[3], const float inner_max[3]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __RAYOBJECT_RTBUILD_H__ */
|
192
source/blender/render/intern/raytrace/rayobject_svbvh.cpp
Normal file
192
source/blender/render/intern/raytrace/rayobject_svbvh.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/raytrace/rayobject_svbvh.cpp
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "vbvh.h"
|
||||
#include "svbvh.h"
|
||||
#include "reorganize.h"
|
||||
|
||||
#ifdef __SSE__
|
||||
|
||||
#define DFS_STACK_SIZE 256
|
||||
|
||||
struct SVBVHTree {
|
||||
RayObject rayobj;
|
||||
|
||||
SVBVHNode *root;
|
||||
MemArena *node_arena;
|
||||
|
||||
float cost;
|
||||
RTBuilder *builder;
|
||||
};
|
||||
|
||||
/*
|
||||
* Cost to test N childs
|
||||
*/
|
||||
struct PackCost {
|
||||
float operator()(int n)
|
||||
{
|
||||
return (n / 4) + ((n % 4) > 2 ? 1 : n % 4);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
void bvh_done<SVBVHTree>(SVBVHTree *obj)
|
||||
{
|
||||
rtbuild_done(obj->builder, &obj->rayobj.control);
|
||||
|
||||
//TODO find a away to exactly calculate the needed memory
|
||||
MemArena *arena1 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "svbvh arena");
|
||||
BLI_memarena_use_malloc(arena1);
|
||||
|
||||
MemArena *arena2 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "svbvh arena2");
|
||||
BLI_memarena_use_malloc(arena2);
|
||||
BLI_memarena_use_align(arena2, 16);
|
||||
|
||||
//Build and optimize the tree
|
||||
if (0) {
|
||||
VBVHNode *root = BuildBinaryVBVH<VBVHNode>(arena1, &obj->rayobj.control).transform(obj->builder);
|
||||
|
||||
if (RE_rayobjectcontrol_test_break(&obj->rayobj.control)) {
|
||||
BLI_memarena_free(arena1);
|
||||
BLI_memarena_free(arena2);
|
||||
return;
|
||||
}
|
||||
|
||||
reorganize(root);
|
||||
remove_useless(root, &root);
|
||||
bvh_refit(root);
|
||||
|
||||
pushup(root);
|
||||
pushdown(root);
|
||||
pushup_simd<VBVHNode, 4>(root);
|
||||
|
||||
obj->root = Reorganize_SVBVH<VBVHNode>(arena2).transform(root);
|
||||
}
|
||||
else {
|
||||
//Finds the optimal packing of this tree using a given cost model
|
||||
//TODO this uses quite a lot of memory, find ways to reduce memory usage during building
|
||||
OVBVHNode *root = BuildBinaryVBVH<OVBVHNode>(arena1, &obj->rayobj.control).transform(obj->builder);
|
||||
|
||||
if (RE_rayobjectcontrol_test_break(&obj->rayobj.control)) {
|
||||
BLI_memarena_free(arena1);
|
||||
BLI_memarena_free(arena2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root) {
|
||||
VBVH_optimalPackSIMD<OVBVHNode, PackCost>(PackCost()).transform(root);
|
||||
obj->root = Reorganize_SVBVH<OVBVHNode>(arena2).transform(root);
|
||||
}
|
||||
else
|
||||
obj->root = NULL;
|
||||
}
|
||||
|
||||
//Free data
|
||||
BLI_memarena_free(arena1);
|
||||
|
||||
obj->node_arena = arena2;
|
||||
obj->cost = 1.0;
|
||||
|
||||
rtbuild_free(obj->builder);
|
||||
obj->builder = NULL;
|
||||
}
|
||||
|
||||
template<int StackSize>
|
||||
static int intersect(SVBVHTree *obj, Isect *isec)
|
||||
{
|
||||
//TODO renable hint support
|
||||
if (RE_rayobject_isAligned(obj->root)) {
|
||||
if (isec->mode == RE_RAY_SHADOW)
|
||||
return svbvh_node_stack_raycast<StackSize, true>(obj->root, isec);
|
||||
else
|
||||
return svbvh_node_stack_raycast<StackSize, false>(obj->root, isec);
|
||||
}
|
||||
else
|
||||
return RE_rayobject_intersect( (RayObject *) obj->root, isec);
|
||||
}
|
||||
|
||||
template<class Tree>
|
||||
static void bvh_hint_bb(Tree *tree, LCTSHint *hint, float *UNUSED(min), float *UNUSED(max))
|
||||
{
|
||||
//TODO renable hint support
|
||||
{
|
||||
hint->size = 0;
|
||||
hint->stack[hint->size++] = (RayObject *)tree->root;
|
||||
}
|
||||
}
|
||||
/* the cast to pointer function is needed to workarround gcc bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11407 */
|
||||
template<class Tree, int STACK_SIZE>
|
||||
static RayObjectAPI make_api()
|
||||
{
|
||||
static RayObjectAPI api =
|
||||
{
|
||||
(RE_rayobject_raycast_callback) ((int (*)(Tree *, Isect *)) & intersect<STACK_SIZE>),
|
||||
(RE_rayobject_add_callback) ((void (*)(Tree *, RayObject *)) & bvh_add<Tree>),
|
||||
(RE_rayobject_done_callback) ((void (*)(Tree *)) & bvh_done<Tree>),
|
||||
(RE_rayobject_free_callback) ((void (*)(Tree *)) & bvh_free<Tree>),
|
||||
(RE_rayobject_merge_bb_callback)((void (*)(Tree *, float *, float *)) & bvh_bb<Tree>),
|
||||
(RE_rayobject_cost_callback) ((float (*)(Tree *)) & bvh_cost<Tree>),
|
||||
(RE_rayobject_hint_bb_callback) ((void (*)(Tree *, LCTSHint *, float *, float *)) & bvh_hint_bb<Tree>)
|
||||
};
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
template<class Tree>
|
||||
static RayObjectAPI *bvh_get_api(int maxstacksize)
|
||||
{
|
||||
static RayObjectAPI bvh_api256 = make_api<Tree, 1024>();
|
||||
|
||||
if (maxstacksize <= 1024) return &bvh_api256;
|
||||
assert(maxstacksize <= 256);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RayObject *RE_rayobject_svbvh_create(int size)
|
||||
{
|
||||
return bvh_create_tree<SVBVHTree, DFS_STACK_SIZE>(size);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
RayObject *RE_rayobject_svbvh_create(int UNUSED(size))
|
||||
{
|
||||
puts("WARNING: SSE disabled at compile time\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
206
source/blender/render/intern/raytrace/rayobject_vbvh.cpp
Normal file
206
source/blender/render/intern/raytrace/rayobject_vbvh.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/raytrace/rayobject_vbvh.cpp
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
int tot_pushup = 0;
|
||||
int tot_pushdown = 0;
|
||||
int tot_hints = 0;
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_memarena.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_global.h"
|
||||
|
||||
#include "rayintersection.h"
|
||||
#include "rayobject.h"
|
||||
#include "rayobject_rtbuild.h"
|
||||
|
||||
#include "reorganize.h"
|
||||
#include "bvh.h"
|
||||
#include "vbvh.h"
|
||||
|
||||
#include <queue>
|
||||
#include <algorithm>
|
||||
|
||||
#define DFS_STACK_SIZE 256
|
||||
|
||||
struct VBVHTree {
|
||||
RayObject rayobj;
|
||||
VBVHNode *root;
|
||||
MemArena *node_arena;
|
||||
float cost;
|
||||
RTBuilder *builder;
|
||||
};
|
||||
|
||||
/*
|
||||
* Cost to test N childs
|
||||
*/
|
||||
struct PackCost {
|
||||
float operator()(int n)
|
||||
{
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
void bvh_done<VBVHTree>(VBVHTree *obj)
|
||||
{
|
||||
rtbuild_done(obj->builder, &obj->rayobj.control);
|
||||
|
||||
//TODO find a away to exactly calculate the needed memory
|
||||
MemArena *arena1 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "vbvh arena");
|
||||
BLI_memarena_use_malloc(arena1);
|
||||
|
||||
//Build and optimize the tree
|
||||
if (1) {
|
||||
VBVHNode *root = BuildBinaryVBVH<VBVHNode>(arena1, &obj->rayobj.control).transform(obj->builder);
|
||||
if (RE_rayobjectcontrol_test_break(&obj->rayobj.control)) {
|
||||
BLI_memarena_free(arena1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root) {
|
||||
reorganize(root);
|
||||
remove_useless(root, &root);
|
||||
bvh_refit(root);
|
||||
|
||||
pushup(root);
|
||||
pushdown(root);
|
||||
obj->root = root;
|
||||
}
|
||||
else
|
||||
obj->root = NULL;
|
||||
}
|
||||
else {
|
||||
/* TODO */
|
||||
#if 0
|
||||
MemArena *arena2 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "vbvh arena2");
|
||||
BLI_memarena_use_malloc(arena2);
|
||||
|
||||
//Finds the optimal packing of this tree using a given cost model
|
||||
//TODO this uses quite a lot of memory, find ways to reduce memory usage during building
|
||||
OVBVHNode *root = BuildBinaryVBVH<OVBVHNode>(arena2).transform(obj->builder);
|
||||
VBVH_optimalPackSIMD<OVBVHNode, PackCost>(PackCost()).transform(root);
|
||||
obj->root = Reorganize_VBVH<OVBVHNode>(arena1).transform(root);
|
||||
|
||||
BLI_memarena_free(arena2);
|
||||
#endif
|
||||
}
|
||||
|
||||
//Cleanup
|
||||
rtbuild_free(obj->builder);
|
||||
obj->builder = NULL;
|
||||
|
||||
obj->node_arena = arena1;
|
||||
obj->cost = 1.0;
|
||||
}
|
||||
|
||||
template<int StackSize>
|
||||
static int intersect(VBVHTree *obj, Isect *isec)
|
||||
{
|
||||
//TODO renable hint support
|
||||
if (RE_rayobject_isAligned(obj->root)) {
|
||||
if (isec->mode == RE_RAY_SHADOW)
|
||||
return bvh_node_stack_raycast<VBVHNode, StackSize, false, true>(obj->root, isec);
|
||||
else
|
||||
return bvh_node_stack_raycast<VBVHNode, StackSize, false, false>(obj->root, isec);
|
||||
}
|
||||
else
|
||||
return RE_rayobject_intersect( (RayObject *) obj->root, isec);
|
||||
}
|
||||
|
||||
template<class Tree>
|
||||
static void bvh_hint_bb(Tree *tree, LCTSHint *hint, float *UNUSED(min), float *UNUSED(max))
|
||||
{
|
||||
//TODO renable hint support
|
||||
{
|
||||
hint->size = 0;
|
||||
hint->stack[hint->size++] = (RayObject *)tree->root;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 /* UNUSED */
|
||||
static void bfree(VBVHTree *tree)
|
||||
{
|
||||
if (tot_pushup + tot_pushdown + tot_hints + tot_moves) {
|
||||
if (G.debug & G_DEBUG) {
|
||||
printf("tot pushups: %d\n", tot_pushup);
|
||||
printf("tot pushdowns: %d\n", tot_pushdown);
|
||||
printf("tot moves: %d\n", tot_moves);
|
||||
printf("tot hints created: %d\n", tot_hints);
|
||||
}
|
||||
|
||||
tot_pushup = 0;
|
||||
tot_pushdown = 0;
|
||||
tot_hints = 0;
|
||||
tot_moves = 0;
|
||||
}
|
||||
bvh_free(tree);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* the cast to pointer function is needed to workarround gcc bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11407 */
|
||||
template<class Tree, int STACK_SIZE>
|
||||
static RayObjectAPI make_api()
|
||||
{
|
||||
static RayObjectAPI api =
|
||||
{
|
||||
(RE_rayobject_raycast_callback) ((int (*)(Tree *, Isect *)) & intersect<STACK_SIZE>),
|
||||
(RE_rayobject_add_callback) ((void (*)(Tree *, RayObject *)) & bvh_add<Tree>),
|
||||
(RE_rayobject_done_callback) ((void (*)(Tree *)) & bvh_done<Tree>),
|
||||
(RE_rayobject_free_callback) ((void (*)(Tree *)) & bvh_free<Tree>),
|
||||
(RE_rayobject_merge_bb_callback)((void (*)(Tree *, float *, float *)) & bvh_bb<Tree>),
|
||||
(RE_rayobject_cost_callback) ((float (*)(Tree *)) & bvh_cost<Tree>),
|
||||
(RE_rayobject_hint_bb_callback) ((void (*)(Tree *, LCTSHint *, float *, float *)) & bvh_hint_bb<Tree>)
|
||||
};
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
template<class Tree>
|
||||
RayObjectAPI *bvh_get_api(int maxstacksize)
|
||||
{
|
||||
static RayObjectAPI bvh_api256 = make_api<Tree, 1024>();
|
||||
|
||||
if (maxstacksize <= 1024) return &bvh_api256;
|
||||
assert(maxstacksize <= 256);
|
||||
return 0;
|
||||
}
|
||||
|
||||
RayObject *RE_rayobject_vbvh_create(int size)
|
||||
{
|
||||
return bvh_create_tree<VBVHTree, DFS_STACK_SIZE>(size);
|
||||
}
|
513
source/blender/render/intern/raytrace/reorganize.h
Normal file
513
source/blender/render/intern/raytrace/reorganize.h
Normal file
@@ -0,0 +1,513 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/raytrace/reorganize.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include "BKE_global.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef INFINITY
|
||||
# undef INFINITY
|
||||
# endif
|
||||
# define INFINITY FLT_MAX // in mingw math.h: (1.0F/0.0F). This generates compile error, though.
|
||||
#endif
|
||||
|
||||
extern int tot_pushup;
|
||||
extern int tot_pushdown;
|
||||
|
||||
#if !defined(INFINITY) && defined(HUGE_VAL)
|
||||
#define INFINITY HUGE_VAL
|
||||
#endif
|
||||
|
||||
template<class Node>
|
||||
static bool node_fits_inside(Node *a, Node *b)
|
||||
{
|
||||
return bb_fits_inside(b->bb, b->bb + 3, a->bb, a->bb + 3);
|
||||
}
|
||||
|
||||
template<class Node>
|
||||
static void reorganize_find_fittest_parent(Node *tree, Node *node, std::pair<float, Node *> &cost)
|
||||
{
|
||||
std::queue<Node *> q;
|
||||
q.push(tree);
|
||||
|
||||
while (!q.empty()) {
|
||||
Node *parent = q.front();
|
||||
q.pop();
|
||||
|
||||
if (parent == node) continue;
|
||||
if (node_fits_inside(node, parent) && RE_rayobject_isAligned(parent->child) ) {
|
||||
float pcost = bb_area(parent->bb, parent->bb + 3);
|
||||
cost = std::min(cost, std::make_pair(pcost, parent) );
|
||||
for (Node *child = parent->child; child; child = child->sibling)
|
||||
q.push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Node>
|
||||
static void reorganize(Node *root)
|
||||
{
|
||||
std::queue<Node *> q;
|
||||
|
||||
q.push(root);
|
||||
while (!q.empty()) {
|
||||
Node *node = q.front();
|
||||
q.pop();
|
||||
|
||||
if (RE_rayobject_isAligned(node->child)) {
|
||||
for (Node **prev = &node->child; *prev; ) {
|
||||
assert(RE_rayobject_isAligned(*prev));
|
||||
q.push(*prev);
|
||||
|
||||
std::pair<float, Node *> best(FLT_MAX, root);
|
||||
reorganize_find_fittest_parent(root, *prev, best);
|
||||
|
||||
if (best.second == node) {
|
||||
//Already inside the fitnest BB
|
||||
prev = &(*prev)->sibling;
|
||||
}
|
||||
else {
|
||||
Node *tmp = *prev;
|
||||
*prev = (*prev)->sibling;
|
||||
|
||||
tmp->sibling = best.second->child;
|
||||
best.second->child = tmp;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
if (node != root) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Prunes useless nodes from trees:
|
||||
* erases nodes with total amount of primitives = 0
|
||||
* prunes nodes with only one child (except if that child is a primitive)
|
||||
*/
|
||||
template<class Node>
|
||||
static void remove_useless(Node *node, Node **new_node)
|
||||
{
|
||||
if (RE_rayobject_isAligned(node->child) ) {
|
||||
|
||||
for (Node **prev = &node->child; *prev; ) {
|
||||
Node *next = (*prev)->sibling;
|
||||
remove_useless(*prev, prev);
|
||||
if (*prev == NULL)
|
||||
*prev = next;
|
||||
else {
|
||||
(*prev)->sibling = next;
|
||||
prev = &((*prev)->sibling);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node->child) {
|
||||
if (RE_rayobject_isAligned(node->child) && node->child->sibling == 0)
|
||||
*new_node = node->child;
|
||||
}
|
||||
else if (node->child == NULL) {
|
||||
*new_node = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Minimizes expected number of BBtest by colapsing nodes
|
||||
* it uses surface area heuristic for determining whether a node should be colapsed
|
||||
*/
|
||||
template<class Node>
|
||||
static void pushup(Node *parent)
|
||||
{
|
||||
if (is_leaf(parent)) return;
|
||||
|
||||
float p_area = bb_area(parent->bb, parent->bb + 3);
|
||||
Node **prev = &parent->child;
|
||||
for (Node *child = parent->child; RE_rayobject_isAligned(child) && child; ) {
|
||||
const float c_area = bb_area(child->bb, child->bb + 3);
|
||||
const int nchilds = count_childs(child);
|
||||
float original_cost = ((p_area != 0.0f) ? (c_area / p_area) * nchilds : 1.0f) + 1;
|
||||
float flatten_cost = nchilds;
|
||||
if (flatten_cost < original_cost && nchilds >= 2) {
|
||||
append_sibling(child, child->child);
|
||||
child = child->sibling;
|
||||
*prev = child;
|
||||
|
||||
// *prev = child->child;
|
||||
// append_sibling( *prev, child->sibling );
|
||||
// child = *prev;
|
||||
tot_pushup++;
|
||||
}
|
||||
else {
|
||||
*prev = child;
|
||||
prev = &(*prev)->sibling;
|
||||
child = *prev;
|
||||
}
|
||||
}
|
||||
|
||||
for (Node *child = parent->child; RE_rayobject_isAligned(child) && child; child = child->sibling)
|
||||
pushup(child);
|
||||
}
|
||||
|
||||
/*
|
||||
* try to optimize number of childs to be a multiple of SSize
|
||||
*/
|
||||
template<class Node, int SSize>
|
||||
static void pushup_simd(Node *parent)
|
||||
{
|
||||
if (is_leaf(parent)) return;
|
||||
|
||||
int n = count_childs(parent);
|
||||
|
||||
Node **prev = &parent->child;
|
||||
for (Node *child = parent->child; RE_rayobject_isAligned(child) && child; ) {
|
||||
int cn = count_childs(child);
|
||||
if (cn - 1 <= (SSize - (n % SSize) ) % SSize && RE_rayobject_isAligned(child->child) ) {
|
||||
n += (cn - 1);
|
||||
append_sibling(child, child->child);
|
||||
child = child->sibling;
|
||||
*prev = child;
|
||||
}
|
||||
else {
|
||||
*prev = child;
|
||||
prev = &(*prev)->sibling;
|
||||
child = *prev;
|
||||
}
|
||||
}
|
||||
|
||||
for (Node *child = parent->child; RE_rayobject_isAligned(child) && child; child = child->sibling)
|
||||
pushup_simd<Node, SSize>(child);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pushdown
|
||||
* makes sure no child fits inside any of its sibling
|
||||
*/
|
||||
template<class Node>
|
||||
static void pushdown(Node *parent)
|
||||
{
|
||||
Node **s_child = &parent->child;
|
||||
Node *child = parent->child;
|
||||
|
||||
while (child && RE_rayobject_isAligned(child)) {
|
||||
Node *next = child->sibling;
|
||||
Node **next_s_child = &child->sibling;
|
||||
|
||||
//assert(bb_fits_inside(parent->bb, parent->bb+3, child->bb, child->bb+3));
|
||||
|
||||
for (Node *i = parent->child; RE_rayobject_isAligned(i) && i; i = i->sibling)
|
||||
if (child != i && bb_fits_inside(i->bb, i->bb + 3, child->bb, child->bb + 3) && RE_rayobject_isAligned(i->child)) {
|
||||
// todo optimize (should the one with the smallest area?)
|
||||
// float ia = bb_area(i->bb, i->bb+3)
|
||||
// if (child->i)
|
||||
*s_child = child->sibling;
|
||||
child->sibling = i->child;
|
||||
i->child = child;
|
||||
next_s_child = s_child;
|
||||
|
||||
tot_pushdown++;
|
||||
break;
|
||||
}
|
||||
child = next;
|
||||
s_child = next_s_child;
|
||||
}
|
||||
|
||||
for (Node *i = parent->child; RE_rayobject_isAligned(i) && i; i = i->sibling) {
|
||||
pushdown(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* BVH refit
|
||||
* readjust nodes BB (useful if nodes childs where modified)
|
||||
*/
|
||||
template<class Node>
|
||||
static float bvh_refit(Node *node)
|
||||
{
|
||||
if (is_leaf(node)) return 0;
|
||||
if (is_leaf(node->child)) return 0;
|
||||
|
||||
float total = 0;
|
||||
|
||||
for (Node *child = node->child; child; child = child->sibling)
|
||||
total += bvh_refit(child);
|
||||
|
||||
float old_area = bb_area(node->bb, node->bb + 3);
|
||||
INIT_MINMAX(node->bb, node->bb + 3);
|
||||
for (Node *child = node->child; child; child = child->sibling) {
|
||||
DO_MIN(child->bb, node->bb);
|
||||
DO_MAX(child->bb + 3, node->bb + 3);
|
||||
}
|
||||
total += old_area - bb_area(node->bb, node->bb + 3);
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* this finds the best way to packing a tree according to a given test cost function
|
||||
* with the purpose to reduce the expected cost (eg.: number of BB tests).
|
||||
*/
|
||||
#include <vector>
|
||||
#define MAX_CUT_SIZE 4 /* svbvh assumes max 4 children! */
|
||||
#define MAX_OPTIMIZE_CHILDS MAX_CUT_SIZE
|
||||
|
||||
#define CUT_SIZE_IS_VALID(cut_size) ((cut_size) < MAX_CUT_SIZE && (cut_size) >= 0)
|
||||
#define CUT_SIZE_INVALID -1
|
||||
|
||||
|
||||
struct OVBVHNode {
|
||||
float bb[6];
|
||||
|
||||
OVBVHNode *child;
|
||||
OVBVHNode *sibling;
|
||||
|
||||
/*
|
||||
* Returns min cost to represent the subtree starting at the given node,
|
||||
* allowing it to have a given cutsize
|
||||
*/
|
||||
float cut_cost[MAX_CUT_SIZE];
|
||||
float get_cost(int cutsize)
|
||||
{
|
||||
assert(CUT_SIZE_IS_VALID(cutsize - 1));
|
||||
return cut_cost[cutsize - 1];
|
||||
}
|
||||
|
||||
/*
|
||||
* This saves the cut size of this child, when parent is reaching
|
||||
* its minimum cut with the given cut size
|
||||
*/
|
||||
int cut_size[MAX_CUT_SIZE];
|
||||
int get_cut_size(int parent_cut_size)
|
||||
{
|
||||
assert(CUT_SIZE_IS_VALID(parent_cut_size - 1));
|
||||
return cut_size[parent_cut_size - 1];
|
||||
}
|
||||
|
||||
/*
|
||||
* Reorganize the node based on calculated cut costs
|
||||
*/
|
||||
int best_cutsize;
|
||||
void set_cut(int cutsize, OVBVHNode ***cut)
|
||||
{
|
||||
if (cutsize == 1) {
|
||||
**cut = this;
|
||||
*cut = &(**cut)->sibling;
|
||||
}
|
||||
else {
|
||||
if (cutsize > MAX_CUT_SIZE) {
|
||||
for (OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling) {
|
||||
child->set_cut(1, cut);
|
||||
cutsize--;
|
||||
}
|
||||
assert(cutsize == 0);
|
||||
}
|
||||
else {
|
||||
for (OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling) {
|
||||
child->set_cut(child->get_cut_size(cutsize), cut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void optimize()
|
||||
{
|
||||
if (RE_rayobject_isAligned(this->child)) {
|
||||
//Calc new childs
|
||||
if (this->best_cutsize != CUT_SIZE_INVALID) {
|
||||
OVBVHNode **cut = &(this->child);
|
||||
set_cut(this->best_cutsize, &cut);
|
||||
*cut = NULL;
|
||||
}
|
||||
|
||||
//Optimize new childs
|
||||
for (OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling)
|
||||
child->optimize();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Calculates an optimal SIMD packing
|
||||
*
|
||||
*/
|
||||
template<class Node, class TestCost>
|
||||
struct VBVH_optimalPackSIMD {
|
||||
TestCost testcost;
|
||||
|
||||
VBVH_optimalPackSIMD(TestCost testcost)
|
||||
{
|
||||
this->testcost = testcost;
|
||||
}
|
||||
|
||||
/*
|
||||
* calc best cut on a node
|
||||
*/
|
||||
struct calc_best {
|
||||
Node *child[MAX_OPTIMIZE_CHILDS];
|
||||
float child_hit_prob[MAX_OPTIMIZE_CHILDS];
|
||||
|
||||
calc_best(Node *node)
|
||||
{
|
||||
int nchilds = 0;
|
||||
//Fetch childs and needed data
|
||||
{
|
||||
float parent_area = bb_area(node->bb, node->bb + 3);
|
||||
for (Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling) {
|
||||
this->child[nchilds] = child;
|
||||
this->child_hit_prob[nchilds] = (parent_area != 0.0f) ? bb_area(child->bb, child->bb + 3) / parent_area : 1.0f;
|
||||
nchilds++;
|
||||
}
|
||||
|
||||
assert(nchilds >= 2 && nchilds <= MAX_OPTIMIZE_CHILDS);
|
||||
}
|
||||
|
||||
|
||||
//Build DP table to find minimum cost to represent this node with a given cutsize
|
||||
int bt[MAX_OPTIMIZE_CHILDS + 1][MAX_CUT_SIZE + 1]; //backtrace table
|
||||
float cost[MAX_OPTIMIZE_CHILDS + 1][MAX_CUT_SIZE + 1]; //cost table (can be reduced to float[2][MAX_CUT_COST])
|
||||
|
||||
for (int i = 0; i <= nchilds; i++) {
|
||||
for (int j = 0; j <= MAX_CUT_SIZE; j++) {
|
||||
cost[i][j] = INFINITY;
|
||||
}
|
||||
}
|
||||
|
||||
cost[0][0] = 0;
|
||||
|
||||
for (int i = 1; i <= nchilds; i++) {
|
||||
for (int size = i - 1; size /*+(nchilds-i)*/ <= MAX_CUT_SIZE; size++) {
|
||||
for (int cut = 1; cut + size /*+(nchilds-i)*/ <= MAX_CUT_SIZE; cut++) {
|
||||
float new_cost = cost[i - 1][size] + child_hit_prob[i - 1] * child[i - 1]->get_cost(cut);
|
||||
if (new_cost < cost[i][size + cut]) {
|
||||
cost[i][size + cut] = new_cost;
|
||||
bt[i][size + cut] = cut;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the ways to archive the minimum cost with a given cutsize */
|
||||
for (int i = nchilds; i <= MAX_CUT_SIZE; i++) {
|
||||
node->cut_cost[i - 1] = cost[nchilds][i];
|
||||
if (cost[nchilds][i] < INFINITY) {
|
||||
int current_size = i;
|
||||
for (int j = nchilds; j > 0; j--) {
|
||||
child[j - 1]->cut_size[i - 1] = bt[j][current_size];
|
||||
current_size -= bt[j][current_size];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void calc_costs(Node *node)
|
||||
{
|
||||
|
||||
if (RE_rayobject_isAligned(node->child) ) {
|
||||
int nchilds = 0;
|
||||
for (Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling) {
|
||||
calc_costs(child);
|
||||
nchilds++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_CUT_SIZE; i++)
|
||||
node->cut_cost[i] = INFINITY;
|
||||
|
||||
//We are not allowed to look on nodes with with so many childs
|
||||
if (nchilds > MAX_CUT_SIZE) {
|
||||
float cost = 0;
|
||||
|
||||
float parent_area = bb_area(node->bb, node->bb + 3);
|
||||
for (Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling) {
|
||||
cost += ((parent_area != 0.0f) ? (bb_area(child->bb, child->bb + 3) / parent_area) : 1.0f) * child->get_cost(1);
|
||||
}
|
||||
|
||||
cost += testcost(nchilds);
|
||||
node->cut_cost[0] = cost;
|
||||
node->best_cutsize = nchilds;
|
||||
}
|
||||
else {
|
||||
calc_best calc(node);
|
||||
|
||||
//calc expected cost if we optimaly pack this node
|
||||
for (int cutsize = nchilds; cutsize <= MAX_CUT_SIZE; cutsize++) {
|
||||
float m = node->get_cost(cutsize) + testcost(cutsize);
|
||||
if (m < node->cut_cost[0]) {
|
||||
node->cut_cost[0] = m;
|
||||
node->best_cutsize = cutsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node->cut_cost[0] == INFINITY) {
|
||||
node->best_cutsize = CUT_SIZE_INVALID;
|
||||
}
|
||||
}
|
||||
else {
|
||||
node->cut_cost[0] = 1.0f;
|
||||
for (int i = 1; i < MAX_CUT_SIZE; i++)
|
||||
node->cut_cost[i] = INFINITY;
|
||||
|
||||
/* node->best_cutsize can remain unset here */
|
||||
}
|
||||
}
|
||||
|
||||
Node *transform(Node *node)
|
||||
{
|
||||
if (RE_rayobject_isAligned(node->child)) {
|
||||
#ifdef DEBUG
|
||||
static int num = 0;
|
||||
bool first = false;
|
||||
if (num == 0) { num++; first = true; }
|
||||
#endif
|
||||
|
||||
calc_costs(node);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (first && G.debug) {
|
||||
printf("expected cost = %f (%d)\n", node->cut_cost[0], node->best_cutsize);
|
||||
}
|
||||
#endif
|
||||
node->optimize();
|
||||
}
|
||||
return node;
|
||||
}
|
||||
};
|
317
source/blender/render/intern/raytrace/svbvh.h
Normal file
317
source/blender/render/intern/raytrace/svbvh.h
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/raytrace/svbvh.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#ifndef __SVBVH_H__
|
||||
#define __SVBVH_H__
|
||||
|
||||
#ifdef __SSE__
|
||||
|
||||
#include "bvh.h"
|
||||
#include "BLI_memarena.h"
|
||||
#include <algorithm>
|
||||
|
||||
struct SVBVHNode {
|
||||
float child_bb[24];
|
||||
SVBVHNode *child[4];
|
||||
int nchilds;
|
||||
};
|
||||
|
||||
static int svbvh_bb_intersect_test_simd4(const Isect *isec, const __m128 *bb_group)
|
||||
{
|
||||
const __m128 tmin0 = _mm_setzero_ps();
|
||||
const __m128 tmax0 = _mm_set_ps1(isec->dist);
|
||||
|
||||
const __m128 start0 = _mm_set_ps1(isec->start[0]);
|
||||
const __m128 start1 = _mm_set_ps1(isec->start[1]);
|
||||
const __m128 start2 = _mm_set_ps1(isec->start[2]);
|
||||
const __m128 sub0 = _mm_sub_ps(bb_group[isec->bv_index[0]], start0);
|
||||
const __m128 sub1 = _mm_sub_ps(bb_group[isec->bv_index[1]], start0);
|
||||
const __m128 sub2 = _mm_sub_ps(bb_group[isec->bv_index[2]], start1);
|
||||
const __m128 sub3 = _mm_sub_ps(bb_group[isec->bv_index[3]], start1);
|
||||
const __m128 sub4 = _mm_sub_ps(bb_group[isec->bv_index[4]], start2);
|
||||
const __m128 sub5 = _mm_sub_ps(bb_group[isec->bv_index[5]], start2);
|
||||
const __m128 idot_axis0 = _mm_set_ps1(isec->idot_axis[0]);
|
||||
const __m128 idot_axis1 = _mm_set_ps1(isec->idot_axis[1]);
|
||||
const __m128 idot_axis2 = _mm_set_ps1(isec->idot_axis[2]);
|
||||
const __m128 mul0 = _mm_mul_ps(sub0, idot_axis0);
|
||||
const __m128 mul1 = _mm_mul_ps(sub1, idot_axis0);
|
||||
const __m128 mul2 = _mm_mul_ps(sub2, idot_axis1);
|
||||
const __m128 mul3 = _mm_mul_ps(sub3, idot_axis1);
|
||||
const __m128 mul4 = _mm_mul_ps(sub4, idot_axis2);
|
||||
const __m128 mul5 = _mm_mul_ps(sub5, idot_axis2);
|
||||
const __m128 tmin1 = _mm_max_ps(tmin0, mul0);
|
||||
const __m128 tmax1 = _mm_min_ps(tmax0, mul1);
|
||||
const __m128 tmin2 = _mm_max_ps(tmin1, mul2);
|
||||
const __m128 tmax2 = _mm_min_ps(tmax1, mul3);
|
||||
const __m128 tmin3 = _mm_max_ps(tmin2, mul4);
|
||||
const __m128 tmax3 = _mm_min_ps(tmax2, mul5);
|
||||
|
||||
return _mm_movemask_ps(_mm_cmpge_ps(tmax3, tmin3));
|
||||
}
|
||||
|
||||
static int svbvh_bb_intersect_test(const Isect *isec, const float *_bb)
|
||||
{
|
||||
const float *bb = _bb;
|
||||
|
||||
float t1x = (bb[isec->bv_index[0]] - isec->start[0]) * isec->idot_axis[0];
|
||||
float t2x = (bb[isec->bv_index[1]] - isec->start[0]) * isec->idot_axis[0];
|
||||
float t1y = (bb[isec->bv_index[2]] - isec->start[1]) * isec->idot_axis[1];
|
||||
float t2y = (bb[isec->bv_index[3]] - isec->start[1]) * isec->idot_axis[1];
|
||||
float t1z = (bb[isec->bv_index[4]] - isec->start[2]) * isec->idot_axis[2];
|
||||
float t2z = (bb[isec->bv_index[5]] - isec->start[2]) * isec->idot_axis[2];
|
||||
|
||||
RE_RC_COUNT(isec->raycounter->bb.test);
|
||||
|
||||
if (t1x > t2y || t2x < t1y || t1x > t2z || t2x < t1z || t1y > t2z || t2y < t1z) return 0;
|
||||
if (t2x < 0.0f || t2y < 0.0f || t2z < 0.0f) return 0;
|
||||
if (t1x > isec->dist || t1y > isec->dist || t1z > isec->dist) return 0;
|
||||
|
||||
RE_RC_COUNT(isec->raycounter->bb.hit);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool svbvh_node_is_leaf(const SVBVHNode *node)
|
||||
{
|
||||
return !RE_rayobject_isAligned(node);
|
||||
}
|
||||
|
||||
template<int MAX_STACK_SIZE, bool SHADOW>
|
||||
static int svbvh_node_stack_raycast(SVBVHNode *root, Isect *isec)
|
||||
{
|
||||
SVBVHNode *stack[MAX_STACK_SIZE], *node;
|
||||
int hit = 0, stack_pos = 0;
|
||||
|
||||
stack[stack_pos++] = root;
|
||||
|
||||
while (stack_pos) {
|
||||
node = stack[--stack_pos];
|
||||
|
||||
if (!svbvh_node_is_leaf(node)) {
|
||||
int nchilds = node->nchilds;
|
||||
|
||||
if (nchilds == 4) {
|
||||
float *child_bb = node->child_bb;
|
||||
int res = svbvh_bb_intersect_test_simd4(isec, ((__m128 *) (child_bb)));
|
||||
SVBVHNode **child = node->child;
|
||||
|
||||
RE_RC_COUNT(isec->raycounter->simd_bb.test);
|
||||
|
||||
if (res & 1) { stack[stack_pos++] = child[0]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); }
|
||||
if (res & 2) { stack[stack_pos++] = child[1]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); }
|
||||
if (res & 4) { stack[stack_pos++] = child[2]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); }
|
||||
if (res & 8) { stack[stack_pos++] = child[3]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); }
|
||||
}
|
||||
else {
|
||||
float *child_bb = node->child_bb;
|
||||
SVBVHNode **child = node->child;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nchilds; i++) {
|
||||
if (svbvh_bb_intersect_test(isec, (float *)child_bb + 6 * i)) {
|
||||
stack[stack_pos++] = child[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
hit |= RE_rayobject_intersect((RayObject *)node, isec);
|
||||
if (SHADOW && hit) break;
|
||||
}
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
inline void bvh_node_merge_bb<SVBVHNode>(SVBVHNode *node, float min[3], float max[3])
|
||||
{
|
||||
if (is_leaf(node)) {
|
||||
RE_rayobject_merge_bb((RayObject *)node, min, max);
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
for (i = 0; i + 4 <= node->nchilds; i += 4) {
|
||||
float *res = node->child_bb + 6 * i;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
min[j] = min_ff(min[j],
|
||||
min_ffff(res[4 * j + 0],
|
||||
res[4 * j + 1],
|
||||
res[4 * j + 2],
|
||||
res[4 * j + 3]));
|
||||
}
|
||||
for (int j = 0; j < 3; j++) {
|
||||
max[j] = max_ff(max[j],
|
||||
max_ffff(res[4 * (j + 3) + 0],
|
||||
res[4 * (j + 3) + 1],
|
||||
res[4 * (j + 3) + 2],
|
||||
res[4 * (j + 3) + 3]));
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < node->nchilds; i++) {
|
||||
DO_MIN(node->child_bb + 6 * i, min);
|
||||
DO_MAX(node->child_bb + 3 + 6 * i, max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Builds a SVBVH tree form a VBVHTree
|
||||
*/
|
||||
template<class OldNode>
|
||||
struct Reorganize_SVBVH {
|
||||
MemArena *arena;
|
||||
|
||||
float childs_per_node;
|
||||
int nodes_with_childs[16];
|
||||
int useless_bb;
|
||||
int nodes;
|
||||
|
||||
Reorganize_SVBVH(MemArena *a)
|
||||
{
|
||||
arena = a;
|
||||
nodes = 0;
|
||||
childs_per_node = 0;
|
||||
useless_bb = 0;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
nodes_with_childs[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
~Reorganize_SVBVH()
|
||||
{
|
||||
#if 0
|
||||
{
|
||||
printf("%f childs per node\n", childs_per_node / nodes);
|
||||
printf("%d childs BB are useless\n", useless_bb);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
printf("%i childs per node: %d/%d = %f\n", i, nodes_with_childs[i], nodes, nodes_with_childs[i] / float(nodes));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SVBVHNode *create_node(int nchilds)
|
||||
{
|
||||
SVBVHNode *node = (SVBVHNode *)BLI_memarena_alloc(arena, sizeof(SVBVHNode));
|
||||
node->nchilds = nchilds;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void copy_bb(float bb[6], const float old_bb[6])
|
||||
{
|
||||
std::copy(old_bb, old_bb + 6, bb);
|
||||
}
|
||||
|
||||
void prepare_for_simd(SVBVHNode *node)
|
||||
{
|
||||
int i = 0;
|
||||
while (i + 4 <= node->nchilds) {
|
||||
float vec_tmp[4 * 6];
|
||||
float *res = node->child_bb + 6 * i;
|
||||
std::copy(res, res + 6 * 4, vec_tmp);
|
||||
|
||||
for (int j = 0; j < 6; j++) {
|
||||
res[4 * j + 0] = vec_tmp[6 * 0 + j];
|
||||
res[4 * j + 1] = vec_tmp[6 * 1 + j];
|
||||
res[4 * j + 2] = vec_tmp[6 * 2 + j];
|
||||
res[4 * j + 3] = vec_tmp[6 * 3 + j];
|
||||
}
|
||||
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* amt must be power of two */
|
||||
inline int padup(int num, int amt)
|
||||
{
|
||||
return ((num + (amt - 1)) & ~(amt - 1));
|
||||
}
|
||||
|
||||
SVBVHNode *transform(OldNode *old)
|
||||
{
|
||||
if (is_leaf(old))
|
||||
return (SVBVHNode *)old;
|
||||
if (is_leaf(old->child))
|
||||
return (SVBVHNode *)old->child;
|
||||
|
||||
int nchilds = count_childs(old);
|
||||
int alloc_childs = nchilds;
|
||||
if (nchilds % 4 > 2)
|
||||
alloc_childs = padup(nchilds, 4);
|
||||
|
||||
SVBVHNode *node = create_node(alloc_childs);
|
||||
|
||||
childs_per_node += nchilds;
|
||||
nodes++;
|
||||
if (nchilds < 16)
|
||||
nodes_with_childs[nchilds]++;
|
||||
|
||||
useless_bb += alloc_childs - nchilds;
|
||||
while (alloc_childs > nchilds) {
|
||||
const static float def_bb[6] = {FLT_MAX, FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX};
|
||||
alloc_childs--;
|
||||
node->child[alloc_childs] = NULL;
|
||||
copy_bb(node->child_bb + alloc_childs * 6, def_bb);
|
||||
}
|
||||
|
||||
int i = nchilds;
|
||||
for (OldNode *o_child = old->child; o_child; o_child = o_child->sibling) {
|
||||
i--;
|
||||
node->child[i] = transform(o_child);
|
||||
if (is_leaf(o_child)) {
|
||||
float bb[6];
|
||||
INIT_MINMAX(bb, bb + 3);
|
||||
RE_rayobject_merge_bb((RayObject *)o_child, bb, bb + 3);
|
||||
copy_bb(node->child_bb + i * 6, bb);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
copy_bb(node->child_bb + i * 6, o_child->bb);
|
||||
}
|
||||
}
|
||||
assert(i == 0);
|
||||
|
||||
prepare_for_simd(node);
|
||||
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* __SSE__ */
|
||||
|
||||
#endif /* __SVBVH_H__ */
|
238
source/blender/render/intern/raytrace/vbvh.h
Normal file
238
source/blender/render/intern/raytrace/vbvh.h
Normal file
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* ***** 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) 2009 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): André Pinto.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/raytrace/vbvh.h
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "BLI_memarena.h"
|
||||
|
||||
#include "rayobject_rtbuild.h"
|
||||
|
||||
/*
|
||||
* VBVHNode represents a BVHNode with support for a variable number of childrens
|
||||
*/
|
||||
struct VBVHNode {
|
||||
float bb[6];
|
||||
|
||||
VBVHNode *child;
|
||||
VBVHNode *sibling;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Push nodes (used on dfs)
|
||||
*/
|
||||
template<class Node>
|
||||
inline static void bvh_node_push_childs(Node *node, Isect *UNUSED(isec), Node **stack, int &stack_pos)
|
||||
{
|
||||
Node *child = node->child;
|
||||
|
||||
if (is_leaf(child)) {
|
||||
stack[stack_pos++] = child;
|
||||
}
|
||||
else {
|
||||
while (child) {
|
||||
/* Skips BB tests on primitives */
|
||||
#if 0
|
||||
if (is_leaf(child->child)) {
|
||||
stack[stack_pos++] = child->child;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
stack[stack_pos++] = child;
|
||||
}
|
||||
|
||||
child = child->sibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class Node>
|
||||
static int count_childs(Node *parent)
|
||||
{
|
||||
int n = 0;
|
||||
for (Node *i = parent->child; i; i = i->sibling) {
|
||||
n++;
|
||||
if (is_leaf(i))
|
||||
break;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
template<class Node>
|
||||
static void append_sibling(Node *node, Node *sibling)
|
||||
{
|
||||
while (node->sibling)
|
||||
node = node->sibling;
|
||||
|
||||
node->sibling = sibling;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Builds a binary VBVH from a rtbuild
|
||||
*/
|
||||
template<class Node>
|
||||
struct BuildBinaryVBVH {
|
||||
MemArena *arena;
|
||||
RayObjectControl *control;
|
||||
|
||||
void test_break()
|
||||
{
|
||||
if (RE_rayobjectcontrol_test_break(control))
|
||||
throw "Stop";
|
||||
}
|
||||
|
||||
BuildBinaryVBVH(MemArena *a, RayObjectControl *c)
|
||||
{
|
||||
arena = a;
|
||||
control = c;
|
||||
}
|
||||
|
||||
Node *create_node()
|
||||
{
|
||||
Node *node = (Node *)BLI_memarena_alloc(arena, sizeof(Node) );
|
||||
assert(RE_rayobject_isAligned(node));
|
||||
|
||||
node->sibling = NULL;
|
||||
node->child = NULL;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
int rtbuild_split(RTBuilder *builder)
|
||||
{
|
||||
return ::rtbuild_heuristic_object_split(builder, 2);
|
||||
}
|
||||
|
||||
Node *transform(RTBuilder *builder)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _transform(builder);
|
||||
|
||||
} catch (...)
|
||||
{
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Node *_transform(RTBuilder *builder)
|
||||
{
|
||||
int size = rtbuild_size(builder);
|
||||
|
||||
if (size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
else if (size == 1) {
|
||||
Node *node = create_node();
|
||||
INIT_MINMAX(node->bb, node->bb + 3);
|
||||
rtbuild_merge_bb(builder, node->bb, node->bb + 3);
|
||||
node->child = (Node *) rtbuild_get_primitive(builder, 0);
|
||||
return node;
|
||||
}
|
||||
else {
|
||||
test_break();
|
||||
|
||||
Node *node = create_node();
|
||||
|
||||
Node **child = &node->child;
|
||||
|
||||
int nc = rtbuild_split(builder);
|
||||
INIT_MINMAX(node->bb, node->bb + 3);
|
||||
|
||||
assert(nc == 2);
|
||||
for (int i = 0; i < nc; i++) {
|
||||
RTBuilder tmp;
|
||||
rtbuild_get_child(builder, i, &tmp);
|
||||
|
||||
*child = _transform(&tmp);
|
||||
DO_MIN((*child)->bb, node->bb);
|
||||
DO_MAX((*child)->bb + 3, node->bb + 3);
|
||||
child = &((*child)->sibling);
|
||||
}
|
||||
|
||||
*child = NULL;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#if 0
|
||||
template<class Tree, class OldNode>
|
||||
struct Reorganize_VBVH {
|
||||
Tree *tree;
|
||||
|
||||
Reorganize_VBVH(Tree *t)
|
||||
{
|
||||
tree = t;
|
||||
}
|
||||
|
||||
VBVHNode *create_node()
|
||||
{
|
||||
VBVHNode *node = (VBVHNode *)BLI_memarena_alloc(tree->node_arena, sizeof(VBVHNode));
|
||||
return node;
|
||||
}
|
||||
|
||||
void copy_bb(VBVHNode *node, OldNode *old)
|
||||
{
|
||||
std::copy(old->bb, old->bb + 6, node->bb);
|
||||
}
|
||||
|
||||
VBVHNode *transform(OldNode *old)
|
||||
{
|
||||
if (is_leaf(old))
|
||||
return (VBVHNode *)old;
|
||||
|
||||
VBVHNode *node = create_node();
|
||||
VBVHNode **child_ptr = &node->child;
|
||||
node->sibling = 0;
|
||||
|
||||
copy_bb(node, old);
|
||||
|
||||
for (OldNode *o_child = old->child; o_child; o_child = o_child->sibling)
|
||||
{
|
||||
VBVHNode *n_child = transform(o_child);
|
||||
*child_ptr = n_child;
|
||||
if (is_leaf(n_child)) return node;
|
||||
child_ptr = &n_child->sibling;
|
||||
}
|
||||
*child_ptr = 0;
|
||||
|
||||
return node;
|
||||
}
|
||||
};
|
||||
#endif
|
1342
source/blender/render/intern/source/bake.c
Normal file
1342
source/blender/render/intern/source/bake.c
Normal file
File diff suppressed because it is too large
Load Diff
6014
source/blender/render/intern/source/convertblender.c
Normal file
6014
source/blender/render/intern/source/convertblender.c
Normal file
File diff suppressed because it is too large
Load Diff
822
source/blender/render/intern/source/envmap.c
Normal file
822
source/blender/render/intern/source/envmap.c
Normal file
@@ -0,0 +1,822 @@
|
||||
/*
|
||||
* ***** 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) 2001-2002 by NaN Holding BV.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributors: 2004/2005/2006 Blender Foundation, full recode
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/source/envmap.c
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
/* external modules: */
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "IMB_imbuf_types.h"
|
||||
#include "IMB_imbuf.h" /* for rectcpy */
|
||||
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_image_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_image.h" /* BKE_imbuf_write */
|
||||
#include "BKE_texture.h"
|
||||
#include "BKE_scene.h"
|
||||
|
||||
/* this module */
|
||||
#include "render_types.h"
|
||||
#include "envmap.h"
|
||||
#include "renderdatabase.h"
|
||||
#include "renderpipeline.h"
|
||||
#include "texture.h"
|
||||
#include "zbuf.h"
|
||||
#include "render_result.h"
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void envmap_split_ima(EnvMap *env, ImBuf *ibuf)
|
||||
{
|
||||
int dx, part;
|
||||
|
||||
/* after lock we test cube[1], if set the other thread has done it fine */
|
||||
BLI_thread_lock(LOCK_IMAGE);
|
||||
if (env->cube[1] == NULL) {
|
||||
|
||||
BKE_texture_envmap_free_data(env);
|
||||
|
||||
dx = ibuf->y;
|
||||
dx /= 2;
|
||||
if (3 * dx == ibuf->x) {
|
||||
env->type = ENV_CUBE;
|
||||
env->ok = ENV_OSA;
|
||||
}
|
||||
else if (ibuf->x == ibuf->y) {
|
||||
env->type = ENV_PLANE;
|
||||
env->ok = ENV_OSA;
|
||||
}
|
||||
else {
|
||||
printf("Incorrect envmap size\n");
|
||||
env->ok = 0;
|
||||
env->ima->ok = 0;
|
||||
}
|
||||
|
||||
if (env->ok) {
|
||||
if (env->type == ENV_CUBE) {
|
||||
for (part = 0; part < 6; part++) {
|
||||
env->cube[part] = IMB_allocImBuf(dx, dx, 24, IB_rect | IB_rectfloat);
|
||||
}
|
||||
IMB_float_from_rect(ibuf);
|
||||
|
||||
IMB_rectcpy(env->cube[0], ibuf,
|
||||
0, 0, 0, 0, dx, dx);
|
||||
IMB_rectcpy(env->cube[1], ibuf,
|
||||
0, 0, dx, 0, dx, dx);
|
||||
IMB_rectcpy(env->cube[2], ibuf,
|
||||
0, 0, 2 * dx, 0, dx, dx);
|
||||
IMB_rectcpy(env->cube[3], ibuf,
|
||||
0, 0, 0, dx, dx, dx);
|
||||
IMB_rectcpy(env->cube[4], ibuf,
|
||||
0, 0, dx, dx, dx, dx);
|
||||
IMB_rectcpy(env->cube[5], ibuf,
|
||||
0, 0, 2 * dx, dx, dx, dx);
|
||||
|
||||
}
|
||||
else { /* ENV_PLANE */
|
||||
env->cube[1] = IMB_dupImBuf(ibuf);
|
||||
IMB_float_from_rect(env->cube[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
BLI_thread_unlock(LOCK_IMAGE);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* ****************** RENDER ********************** */
|
||||
|
||||
/* copy current render */
|
||||
static Render *envmap_render_copy(Render *re, EnvMap *env)
|
||||
{
|
||||
Render *envre;
|
||||
float viewscale;
|
||||
int cuberes;
|
||||
|
||||
envre = RE_NewRender("Envmap");
|
||||
|
||||
env->lastsize = re->r.size;
|
||||
cuberes = (env->cuberes * re->r.size) / 100;
|
||||
cuberes &= 0xFFFC;
|
||||
|
||||
/* this flag has R_ZTRA in it for example */
|
||||
envre->flag = re->flag;
|
||||
|
||||
/* set up renderdata */
|
||||
render_copy_renderdata(&envre->r, &re->r);
|
||||
envre->r.mode &= ~(R_BORDER | R_PANORAMA | R_ORTHO | R_MBLUR);
|
||||
BLI_freelistN(&envre->r.layers);
|
||||
BLI_freelistN(&envre->r.views);
|
||||
envre->r.filtertype = 0;
|
||||
envre->r.tilex = envre->r.xsch / 2;
|
||||
envre->r.tiley = envre->r.ysch / 2;
|
||||
envre->r.size = 100;
|
||||
envre->r.yasp = envre->r.xasp = 1;
|
||||
|
||||
RE_InitState(envre, NULL, &envre->r, NULL, cuberes, cuberes, NULL);
|
||||
envre->main = re->main;
|
||||
envre->scene = re->scene; /* unsure about this... */
|
||||
envre->scene_color_manage = re->scene_color_manage;
|
||||
envre->lay = re->lay;
|
||||
|
||||
/* view stuff in env render */
|
||||
viewscale = (env->type == ENV_PLANE) ? env->viewscale : 1.0f;
|
||||
RE_SetEnvmapCamera(envre, env->object, viewscale, env->clipsta, env->clipend);
|
||||
copy_m4_m4(envre->viewmat_orig, re->viewmat_orig);
|
||||
|
||||
/* callbacks */
|
||||
envre->display_update = re->display_update;
|
||||
envre->duh = re->duh;
|
||||
envre->test_break = re->test_break;
|
||||
envre->tbh = re->tbh;
|
||||
envre->current_scene_update = re->current_scene_update;
|
||||
envre->suh = re->suh;
|
||||
|
||||
/* and for the evil stuff; copy the database... */
|
||||
envre->totvlak = re->totvlak;
|
||||
envre->totvert = re->totvert;
|
||||
envre->tothalo = re->tothalo;
|
||||
envre->totstrand = re->totstrand;
|
||||
envre->totlamp = re->totlamp;
|
||||
envre->sortedhalos = re->sortedhalos;
|
||||
envre->lights = re->lights;
|
||||
envre->objecttable = re->objecttable;
|
||||
envre->customdata_names = re->customdata_names;
|
||||
envre->raytree = re->raytree;
|
||||
envre->totinstance = re->totinstance;
|
||||
envre->instancetable = re->instancetable;
|
||||
envre->objectinstance = re->objectinstance;
|
||||
envre->qmcsamplers = re->qmcsamplers;
|
||||
|
||||
return envre;
|
||||
}
|
||||
|
||||
static void envmap_free_render_copy(Render *envre)
|
||||
{
|
||||
|
||||
envre->totvlak = 0;
|
||||
envre->totvert = 0;
|
||||
envre->tothalo = 0;
|
||||
envre->totstrand = 0;
|
||||
envre->totlamp = 0;
|
||||
envre->totinstance = 0;
|
||||
envre->sortedhalos = NULL;
|
||||
BLI_listbase_clear(&envre->lights);
|
||||
BLI_listbase_clear(&envre->objecttable);
|
||||
BLI_listbase_clear(&envre->customdata_names);
|
||||
envre->raytree = NULL;
|
||||
BLI_listbase_clear(&envre->instancetable);
|
||||
envre->objectinstance = NULL;
|
||||
envre->qmcsamplers = NULL;
|
||||
|
||||
RE_FreeRender(envre);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void envmap_transmatrix(float mat[4][4], int part)
|
||||
{
|
||||
float tmat[4][4], eul[3], rotmat[4][4];
|
||||
|
||||
eul[0] = eul[1] = eul[2] = 0.0;
|
||||
|
||||
if (part == 0) { /* neg z */
|
||||
/* pass */
|
||||
}
|
||||
else if (part == 1) { /* pos z */
|
||||
eul[0] = M_PI;
|
||||
}
|
||||
else if (part == 2) { /* pos y */
|
||||
eul[0] = M_PI / 2.0;
|
||||
}
|
||||
else if (part == 3) { /* neg x */
|
||||
eul[0] = M_PI / 2.0;
|
||||
eul[2] = M_PI / 2.0;
|
||||
}
|
||||
else if (part == 4) { /* neg y */
|
||||
eul[0] = M_PI / 2.0;
|
||||
eul[2] = M_PI;
|
||||
}
|
||||
else { /* pos x */
|
||||
eul[0] = M_PI / 2.0;
|
||||
eul[2] = -M_PI / 2.0;
|
||||
}
|
||||
|
||||
copy_m4_m4(tmat, mat);
|
||||
eul_to_mat4(rotmat, eul);
|
||||
mul_m4_m4m4(mat, tmat, rotmat);
|
||||
}
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void env_set_imats(Render *re)
|
||||
{
|
||||
Base *base;
|
||||
float mat[4][4];
|
||||
|
||||
base = re->scene->base.first;
|
||||
while (base) {
|
||||
mul_m4_m4m4(mat, re->viewmat, base->object->obmat);
|
||||
invert_m4_m4(base->object->imat, mat);
|
||||
|
||||
base = base->next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
void env_rotate_scene(Render *re, float mat[4][4], int do_rotate)
|
||||
{
|
||||
ObjectRen *obr;
|
||||
ObjectInstanceRen *obi;
|
||||
LampRen *lar = NULL;
|
||||
HaloRen *har = NULL;
|
||||
float imat[3][3], mat_inverse[4][4], smat[4][4], tmat[4][4], cmat[3][3], tmpmat[4][4];
|
||||
int a;
|
||||
|
||||
if (do_rotate == 0) {
|
||||
invert_m4_m4(tmat, mat);
|
||||
copy_m3_m4(imat, tmat);
|
||||
|
||||
copy_m4_m4(mat_inverse, mat);
|
||||
}
|
||||
else {
|
||||
copy_m4_m4(tmat, mat);
|
||||
copy_m3_m4(imat, mat);
|
||||
|
||||
invert_m4_m4(mat_inverse, tmat);
|
||||
}
|
||||
|
||||
for (obi = re->instancetable.first; obi; obi = obi->next) {
|
||||
/* append or set matrix depending on dupli */
|
||||
if (obi->flag & R_DUPLI_TRANSFORMED) {
|
||||
copy_m4_m4(tmpmat, obi->mat);
|
||||
mul_m4_m4m4(obi->mat, tmat, tmpmat);
|
||||
}
|
||||
else if (do_rotate == 1)
|
||||
copy_m4_m4(obi->mat, tmat);
|
||||
else
|
||||
unit_m4(obi->mat);
|
||||
|
||||
copy_m3_m4(cmat, obi->mat);
|
||||
invert_m3_m3(obi->nmat, cmat);
|
||||
transpose_m3(obi->nmat);
|
||||
|
||||
/* indicate the renderer has to use transform matrices */
|
||||
if (do_rotate == 0)
|
||||
obi->flag &= ~R_ENV_TRANSFORMED;
|
||||
else {
|
||||
obi->flag |= R_ENV_TRANSFORMED;
|
||||
copy_m4_m4(obi->imat, mat_inverse);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (obr = re->objecttable.first; obr; obr = obr->next) {
|
||||
for (a = 0; a < obr->tothalo; a++) {
|
||||
if ((a & 255) == 0) har = obr->bloha[a >> 8];
|
||||
else har++;
|
||||
|
||||
mul_m4_v3(tmat, har->co);
|
||||
}
|
||||
|
||||
/* imat_ren is needed for correct texture coordinates */
|
||||
mul_m4_m4m4(obr->ob->imat_ren, re->viewmat, obr->ob->obmat);
|
||||
invert_m4(obr->ob->imat_ren);
|
||||
}
|
||||
|
||||
for (lar = re->lampren.first; lar; lar = lar->next) {
|
||||
float lamp_imat[4][4];
|
||||
|
||||
/* copy from add_render_lamp */
|
||||
if (do_rotate == 1)
|
||||
mul_m4_m4m4(tmpmat, re->viewmat, lar->lampmat);
|
||||
else
|
||||
mul_m4_m4m4(tmpmat, re->viewmat_orig, lar->lampmat);
|
||||
|
||||
invert_m4_m4(lamp_imat, tmpmat);
|
||||
copy_m3_m4(lar->mat, tmpmat);
|
||||
copy_m3_m4(lar->imat, lamp_imat);
|
||||
|
||||
lar->vec[0]= -tmpmat[2][0];
|
||||
lar->vec[1]= -tmpmat[2][1];
|
||||
lar->vec[2]= -tmpmat[2][2];
|
||||
normalize_v3(lar->vec);
|
||||
lar->co[0]= tmpmat[3][0];
|
||||
lar->co[1]= tmpmat[3][1];
|
||||
lar->co[2]= tmpmat[3][2];
|
||||
|
||||
if (lar->type == LA_AREA) {
|
||||
area_lamp_vectors(lar);
|
||||
}
|
||||
else if (lar->type == LA_SPOT) {
|
||||
normalize_v3(lar->imat[0]);
|
||||
normalize_v3(lar->imat[1]);
|
||||
normalize_v3(lar->imat[2]);
|
||||
|
||||
lar->sh_invcampos[0] = -lar->co[0];
|
||||
lar->sh_invcampos[1] = -lar->co[1];
|
||||
lar->sh_invcampos[2] = -lar->co[2];
|
||||
mul_m3_v3(lar->imat, lar->sh_invcampos);
|
||||
lar->sh_invcampos[2] *= lar->sh_zfac;
|
||||
|
||||
if (lar->shb) {
|
||||
if (do_rotate == 1) {
|
||||
mul_m4_m4m4(smat, lar->shb->viewmat, mat_inverse);
|
||||
mul_m4_m4m4(lar->shb->persmat, lar->shb->winmat, smat);
|
||||
}
|
||||
else mul_m4_m4m4(lar->shb->persmat, lar->shb->winmat, lar->shb->viewmat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (do_rotate) {
|
||||
init_render_world(re);
|
||||
env_set_imats(re);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void env_layerflags(Render *re, unsigned int notlay)
|
||||
{
|
||||
ObjectRen *obr;
|
||||
VlakRen *vlr = NULL;
|
||||
int a;
|
||||
|
||||
/* invert notlay, so if face is in multiple layers it will still be visible,
|
||||
* unless all 'notlay' bits match the face bits.
|
||||
* face: 0110
|
||||
* not: 0100
|
||||
* ~not: 1011
|
||||
* now (face & ~not) is true
|
||||
*/
|
||||
|
||||
notlay = ~notlay;
|
||||
|
||||
for (obr = re->objecttable.first; obr; obr = obr->next) {
|
||||
if ((obr->lay & notlay) == 0) {
|
||||
for (a = 0; a < obr->totvlak; a++) {
|
||||
if ((a & 255) == 0) vlr = obr->vlaknodes[a >> 8].vlak;
|
||||
else vlr++;
|
||||
|
||||
vlr->flag |= R_HIDDEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void env_hideobject(Render *re, Object *ob)
|
||||
{
|
||||
ObjectRen *obr;
|
||||
VlakRen *vlr = NULL;
|
||||
int a;
|
||||
|
||||
for (obr = re->objecttable.first; obr; obr = obr->next) {
|
||||
for (a = 0; a < obr->totvlak; a++) {
|
||||
if ((a & 255) == 0) vlr = obr->vlaknodes[a >> 8].vlak;
|
||||
else vlr++;
|
||||
|
||||
if (obr->ob == ob)
|
||||
vlr->flag |= R_HIDDEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void env_showobjects(Render *re)
|
||||
{
|
||||
ObjectRen *obr;
|
||||
VlakRen *vlr = NULL;
|
||||
int a;
|
||||
|
||||
for (obr = re->objecttable.first; obr; obr = obr->next) {
|
||||
for (a = 0; a < obr->totvlak; a++) {
|
||||
if ((a & 255) == 0) vlr = obr->vlaknodes[a >> 8].vlak;
|
||||
else vlr++;
|
||||
|
||||
vlr->flag &= ~R_HIDDEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void render_envmap(Render *re, EnvMap *env)
|
||||
{
|
||||
/* only the cubemap and planar map is implemented */
|
||||
Render *envre;
|
||||
ImBuf *ibuf;
|
||||
float orthmat[4][4];
|
||||
float oldviewinv[4][4], mat[4][4], tmat[4][4];
|
||||
short part;
|
||||
|
||||
/* need a recalc: ortho-render has no correct viewinv */
|
||||
invert_m4_m4(oldviewinv, re->viewmat);
|
||||
|
||||
envre = envmap_render_copy(re, env);
|
||||
|
||||
/* precalc orthmat for object */
|
||||
copy_m4_m4(orthmat, env->object->obmat);
|
||||
normalize_m4(orthmat);
|
||||
|
||||
/* need imat later for texture imat */
|
||||
mul_m4_m4m4(mat, re->viewmat, orthmat);
|
||||
invert_m4_m4(tmat, mat);
|
||||
copy_m3_m4(env->obimat, tmat);
|
||||
|
||||
for (part = 0; part < 6; part++) {
|
||||
if (env->type == ENV_PLANE && part != 1)
|
||||
continue;
|
||||
|
||||
re->display_clear(re->dch, envre->result);
|
||||
|
||||
copy_m4_m4(tmat, orthmat);
|
||||
envmap_transmatrix(tmat, part);
|
||||
invert_m4_m4(mat, tmat);
|
||||
/* mat now is the camera 'viewmat' */
|
||||
|
||||
copy_m4_m4(envre->viewmat, mat);
|
||||
copy_m4_m4(envre->viewinv, tmat);
|
||||
|
||||
/* we have to correct for the already rotated vertexcoords */
|
||||
mul_m4_m4m4(tmat, envre->viewmat, oldviewinv);
|
||||
invert_m4_m4(env->imat, tmat);
|
||||
|
||||
env_rotate_scene(envre, tmat, 1);
|
||||
project_renderdata(envre, projectverto, 0, 0, 1);
|
||||
env_layerflags(envre, env->notlay);
|
||||
env_hideobject(envre, env->object);
|
||||
|
||||
if (re->test_break(re->tbh) == 0) {
|
||||
RE_TileProcessor(envre);
|
||||
}
|
||||
|
||||
/* rotate back */
|
||||
env_showobjects(envre);
|
||||
env_rotate_scene(envre, tmat, 0);
|
||||
|
||||
if (re->test_break(re->tbh) == 0) {
|
||||
int y;
|
||||
float *alpha;
|
||||
float *rect;
|
||||
|
||||
if (envre->result->do_exr_tile) {
|
||||
BLI_rw_mutex_lock(&envre->resultmutex, THREAD_LOCK_WRITE);
|
||||
render_result_exr_file_end(envre);
|
||||
BLI_rw_mutex_unlock(&envre->resultmutex);
|
||||
}
|
||||
|
||||
RenderLayer *rl = envre->result->layers.first;
|
||||
|
||||
/* envmap is rendered independently of multiview */
|
||||
rect = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, "");
|
||||
ibuf = IMB_allocImBuf(envre->rectx, envre->recty, 24, IB_rect | IB_rectfloat);
|
||||
memcpy(ibuf->rect_float, rect, ibuf->channels * ibuf->x * ibuf->y * sizeof(float));
|
||||
|
||||
/* envmap renders without alpha */
|
||||
alpha = ibuf->rect_float + 3;
|
||||
for (y = ibuf->x * ibuf->y - 1; y >= 0; y--, alpha += 4)
|
||||
*alpha = 1.0;
|
||||
|
||||
env->cube[part] = ibuf;
|
||||
}
|
||||
|
||||
if (re->test_break(re->tbh)) break;
|
||||
|
||||
}
|
||||
|
||||
if (re->test_break(re->tbh)) BKE_texture_envmap_free_data(env);
|
||||
else {
|
||||
if (envre->r.mode & R_OSA) env->ok = ENV_OSA;
|
||||
else env->ok = ENV_NORMAL;
|
||||
env->lastframe = re->scene->r.cfra;
|
||||
}
|
||||
|
||||
/* restore */
|
||||
envmap_free_render_copy(envre);
|
||||
env_set_imats(re);
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
void make_envmaps(Render *re)
|
||||
{
|
||||
Tex *tex;
|
||||
bool do_init = false;
|
||||
int depth = 0, trace;
|
||||
|
||||
if (!(re->r.mode & R_ENVMAP)) return;
|
||||
|
||||
/* we don't raytrace, disabling the flag will cause ray_transp render solid */
|
||||
trace = (re->r.mode & R_RAYTRACE);
|
||||
re->r.mode &= ~R_RAYTRACE;
|
||||
|
||||
re->i.infostr = IFACE_("Creating Environment maps");
|
||||
re->stats_draw(re->sdh, &re->i);
|
||||
|
||||
/* 5 = hardcoded max recursion level */
|
||||
while (depth < 5) {
|
||||
tex = re->main->tex.first;
|
||||
while (tex) {
|
||||
if (tex->id.us && tex->type == TEX_ENVMAP) {
|
||||
if (tex->env && tex->env->object) {
|
||||
EnvMap *env = tex->env;
|
||||
|
||||
if (env->object->lay & re->lay) {
|
||||
if (env->stype == ENV_LOAD) {
|
||||
float orthmat[4][4], mat[4][4], tmat[4][4];
|
||||
|
||||
/* precalc orthmat for object */
|
||||
copy_m4_m4(orthmat, env->object->obmat);
|
||||
normalize_m4(orthmat);
|
||||
|
||||
/* need imat later for texture imat */
|
||||
mul_m4_m4m4(mat, re->viewmat, orthmat);
|
||||
invert_m4_m4(tmat, mat);
|
||||
copy_m3_m4(env->obimat, tmat);
|
||||
}
|
||||
else {
|
||||
|
||||
/* decide if to render an envmap (again) */
|
||||
if (env->depth >= depth) {
|
||||
|
||||
/* set 'recalc' to make sure it does an entire loop of recalcs */
|
||||
|
||||
if (env->ok) {
|
||||
/* free when OSA, and old one isn't OSA */
|
||||
if ((re->r.mode & R_OSA) && env->ok == ENV_NORMAL)
|
||||
BKE_texture_envmap_free_data(env);
|
||||
/* free when size larger */
|
||||
else if (env->lastsize < re->r.size)
|
||||
BKE_texture_envmap_free_data(env);
|
||||
/* free when env is in recalcmode */
|
||||
else if (env->recalc)
|
||||
BKE_texture_envmap_free_data(env);
|
||||
}
|
||||
|
||||
if (env->ok == 0 && depth == 0) env->recalc = 1;
|
||||
|
||||
if (env->ok == 0) {
|
||||
do_init = true;
|
||||
render_envmap(re, env);
|
||||
|
||||
if (depth == env->depth) env->recalc = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tex = tex->id.next;
|
||||
}
|
||||
depth++;
|
||||
}
|
||||
|
||||
if (do_init) {
|
||||
re->display_init(re->dih, re->result);
|
||||
re->display_clear(re->dch, re->result);
|
||||
// re->flag |= R_REDRAW_PRV;
|
||||
}
|
||||
/* restore */
|
||||
re->r.mode |= trace;
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static int envcube_isect(EnvMap *env, const float vec[3], float answ[2])
|
||||
{
|
||||
float lambda;
|
||||
int face;
|
||||
|
||||
if (env->type == ENV_PLANE) {
|
||||
face = 1;
|
||||
|
||||
lambda = 1.0f / vec[2];
|
||||
answ[0] = env->viewscale * lambda * vec[0];
|
||||
answ[1] = -env->viewscale * lambda * vec[1];
|
||||
}
|
||||
else {
|
||||
/* which face */
|
||||
if (vec[2] <= -fabsf(vec[0]) && vec[2] <= -fabsf(vec[1]) ) {
|
||||
face = 0;
|
||||
lambda = -1.0f / vec[2];
|
||||
answ[0] = lambda * vec[0];
|
||||
answ[1] = lambda * vec[1];
|
||||
}
|
||||
else if (vec[2] >= fabsf(vec[0]) && vec[2] >= fabsf(vec[1])) {
|
||||
face = 1;
|
||||
lambda = 1.0f / vec[2];
|
||||
answ[0] = lambda * vec[0];
|
||||
answ[1] = -lambda * vec[1];
|
||||
}
|
||||
else if (vec[1] >= fabsf(vec[0])) {
|
||||
face = 2;
|
||||
lambda = 1.0f / vec[1];
|
||||
answ[0] = lambda * vec[0];
|
||||
answ[1] = lambda * vec[2];
|
||||
}
|
||||
else if (vec[0] <= -fabsf(vec[1])) {
|
||||
face = 3;
|
||||
lambda = -1.0f / vec[0];
|
||||
answ[0] = lambda * vec[1];
|
||||
answ[1] = lambda * vec[2];
|
||||
}
|
||||
else if (vec[1] <= -fabsf(vec[0])) {
|
||||
face = 4;
|
||||
lambda = -1.0f / vec[1];
|
||||
answ[0] = -lambda * vec[0];
|
||||
answ[1] = lambda * vec[2];
|
||||
}
|
||||
else {
|
||||
face = 5;
|
||||
lambda = 1.0f / vec[0];
|
||||
answ[0] = -lambda * vec[1];
|
||||
answ[1] = lambda * vec[2];
|
||||
}
|
||||
}
|
||||
|
||||
answ[0] = 0.5f + 0.5f * answ[0];
|
||||
answ[1] = 0.5f + 0.5f * answ[1];
|
||||
return face;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void set_dxtdyt(float r_dxt[3], float r_dyt[3], const float dxt[3], const float dyt[3], int face)
|
||||
{
|
||||
if (face == 2 || face == 4) {
|
||||
r_dxt[0] = dxt[0];
|
||||
r_dyt[0] = dyt[0];
|
||||
r_dxt[1] = dxt[2];
|
||||
r_dyt[1] = dyt[2];
|
||||
}
|
||||
else if (face == 3 || face == 5) {
|
||||
r_dxt[0] = dxt[1];
|
||||
r_dxt[1] = dxt[2];
|
||||
r_dyt[0] = dyt[1];
|
||||
r_dyt[1] = dyt[2];
|
||||
}
|
||||
else {
|
||||
r_dxt[0] = dxt[0];
|
||||
r_dyt[0] = dyt[0];
|
||||
r_dxt[1] = dxt[1];
|
||||
r_dyt[1] = dyt[1];
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
int envmaptex(Tex *tex, const float texvec[3], float dxt[3], float dyt[3], int osatex, TexResult *texres, struct ImagePool *pool, const bool skip_load_image)
|
||||
{
|
||||
extern Render R; /* only in this call */
|
||||
/* texvec should be the already reflected normal */
|
||||
EnvMap *env;
|
||||
ImBuf *ibuf;
|
||||
float fac, vec[3], sco[3], dxts[3], dyts[3];
|
||||
int face, face1;
|
||||
|
||||
env = tex->env;
|
||||
if (env == NULL || (env->stype != ENV_LOAD && env->object == NULL)) {
|
||||
texres->tin = 0.0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (env->stype == ENV_LOAD) {
|
||||
env->ima = tex->ima;
|
||||
if (env->ima && env->ima->ok) {
|
||||
if (env->cube[1] == NULL) {
|
||||
ImBuf *ibuf_ima = BKE_image_pool_acquire_ibuf(env->ima, NULL, pool);
|
||||
if (ibuf_ima)
|
||||
envmap_split_ima(env, ibuf_ima);
|
||||
else
|
||||
env->ok = 0;
|
||||
|
||||
if (env->type == ENV_PLANE)
|
||||
tex->extend = TEX_EXTEND;
|
||||
|
||||
BKE_image_pool_release_ibuf(env->ima, ibuf_ima, pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (env->ok == 0) {
|
||||
texres->tin = 0.0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rotate to envmap space, if object is set */
|
||||
copy_v3_v3(vec, texvec);
|
||||
if (env->object) {
|
||||
mul_m3_v3(env->obimat, vec);
|
||||
if (osatex) {
|
||||
mul_m3_v3(env->obimat, dxt);
|
||||
mul_m3_v3(env->obimat, dyt);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!BKE_scene_use_world_space_shading(R.scene)) {
|
||||
// texvec is in view space
|
||||
mul_mat3_m4_v3(R.viewinv, vec);
|
||||
if (osatex) {
|
||||
mul_mat3_m4_v3(R.viewinv, dxt);
|
||||
mul_mat3_m4_v3(R.viewinv, dyt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
face = envcube_isect(env, vec, sco);
|
||||
ibuf = env->cube[face];
|
||||
|
||||
if (osatex) {
|
||||
set_dxtdyt(dxts, dyts, dxt, dyt, face);
|
||||
imagewraposa(tex, NULL, ibuf, sco, dxts, dyts, texres, pool, skip_load_image);
|
||||
|
||||
/* edges? */
|
||||
|
||||
if (texres->ta < 1.0f) {
|
||||
TexResult texr1, texr2;
|
||||
|
||||
texr1.nor = texr2.nor = NULL;
|
||||
texr1.talpha = texr2.talpha = texres->talpha; /* boxclip expects this initialized */
|
||||
|
||||
add_v3_v3(vec, dxt);
|
||||
face1 = envcube_isect(env, vec, sco);
|
||||
sub_v3_v3(vec, dxt);
|
||||
|
||||
if (face != face1) {
|
||||
ibuf = env->cube[face1];
|
||||
set_dxtdyt(dxts, dyts, dxt, dyt, face1);
|
||||
imagewraposa(tex, NULL, ibuf, sco, dxts, dyts, &texr1, pool, skip_load_image);
|
||||
}
|
||||
else texr1.tr = texr1.tg = texr1.tb = texr1.ta = 0.0;
|
||||
|
||||
/* here was the nasty bug! results were not zero-ed. FPE! */
|
||||
|
||||
add_v3_v3(vec, dyt);
|
||||
face1 = envcube_isect(env, vec, sco);
|
||||
sub_v3_v3(vec, dyt);
|
||||
|
||||
if (face != face1) {
|
||||
ibuf = env->cube[face1];
|
||||
set_dxtdyt(dxts, dyts, dxt, dyt, face1);
|
||||
imagewraposa(tex, NULL, ibuf, sco, dxts, dyts, &texr2, pool, skip_load_image);
|
||||
}
|
||||
else texr2.tr = texr2.tg = texr2.tb = texr2.ta = 0.0;
|
||||
|
||||
fac = (texres->ta + texr1.ta + texr2.ta);
|
||||
if (fac != 0.0f) {
|
||||
fac = 1.0f / fac;
|
||||
|
||||
texres->tr = fac * (texres->ta * texres->tr + texr1.ta * texr1.tr + texr2.ta * texr2.tr);
|
||||
texres->tg = fac * (texres->ta * texres->tg + texr1.ta * texr1.tg + texr2.ta * texr2.tg);
|
||||
texres->tb = fac * (texres->ta * texres->tb + texr1.ta * texr1.tb + texr2.ta * texr2.tb);
|
||||
}
|
||||
texres->ta = 1.0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
imagewrap(tex, NULL, ibuf, sco, texres, pool, skip_load_image);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
1533
source/blender/render/intern/source/occlusion.c
Normal file
1533
source/blender/render/intern/source/occlusion.c
Normal file
File diff suppressed because it is too large
Load Diff
400
source/blender/render/intern/source/pixelblending.c
Normal file
400
source/blender/render/intern/source/pixelblending.c
Normal file
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
* ***** 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) 2001-2002 by NaN Holding BV.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributor(s): Full recode, 2004-2006 Blender Foundation
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/source/pixelblending.c
|
||||
* \ingroup render
|
||||
*
|
||||
* Functions to blend pixels with or without alpha, in various formats
|
||||
* nzc - June 2000
|
||||
*/
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
/* global includes */
|
||||
|
||||
/* own includes */
|
||||
#include "render_types.h"
|
||||
#include "pixelblending.h"
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
|
||||
/* only to be used here in this file, it's for speed */
|
||||
extern struct Render R;
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Debug/behavior defines */
|
||||
/* if defined: alpha blending with floats clips color, as with shorts */
|
||||
/* #define RE_FLOAT_COLOR_CLIPPING */
|
||||
/* if defined: alpha values are clipped */
|
||||
/* For now, we just keep alpha clipping. We run into thresholding and */
|
||||
/* blending difficulties otherwise. Be careful here. */
|
||||
#define RE_ALPHA_CLIPPING
|
||||
|
||||
|
||||
|
||||
/* Threshold for a 'full' pixel: pixels with alpha above this level are */
|
||||
/* considered opaque This is the decimal value for 0xFFF0 / 0xFFFF */
|
||||
#define RE_FULL_COLOR_FLOAT 0.9998f
|
||||
/* Threshold for an 'empty' pixel: pixels with alpha above this level are */
|
||||
/* considered completely transparent. This is the decimal value */
|
||||
/* for 0x000F / 0xFFFF */
|
||||
#define RE_EMPTY_COLOR_FLOAT 0.0002f
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
void addAlphaOverFloat(float dest[4], const float source[4])
|
||||
{
|
||||
/* d = s + (1-alpha_s)d*/
|
||||
float mul;
|
||||
|
||||
mul = 1.0f - source[3];
|
||||
|
||||
dest[0] = (mul * dest[0]) + source[0];
|
||||
dest[1] = (mul * dest[1]) + source[1];
|
||||
dest[2] = (mul * dest[2]) + source[2];
|
||||
dest[3] = (mul * dest[3]) + source[3];
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
void addAlphaUnderFloat(float dest[4], const float source[4])
|
||||
{
|
||||
float mul;
|
||||
|
||||
mul = 1.0f - dest[3];
|
||||
|
||||
dest[0] += (mul * source[0]);
|
||||
dest[1] += (mul * source[1]);
|
||||
dest[2] += (mul * source[2]);
|
||||
dest[3] += (mul * source[3]);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
void addalphaAddfacFloat(float dest[4], const float source[4], char addfac)
|
||||
{
|
||||
float m; /* weiging factor of destination */
|
||||
float c; /* intermediate color */
|
||||
|
||||
/* Addfac is a number between 0 and 1: rescale */
|
||||
/* final target is to diminish the influence of dest when addfac rises */
|
||||
m = 1.0f - (source[3] * ((255 - addfac) / 255.0f));
|
||||
|
||||
/* blend colors*/
|
||||
c = (m * dest[0]) + source[0];
|
||||
#ifdef RE_FLOAT_COLOR_CLIPPING
|
||||
if (c >= RE_FULL_COLOR_FLOAT) dest[0] = RE_FULL_COLOR_FLOAT;
|
||||
else
|
||||
#endif
|
||||
dest[0] = c;
|
||||
|
||||
c = (m * dest[1]) + source[1];
|
||||
#ifdef RE_FLOAT_COLOR_CLIPPING
|
||||
if (c >= RE_FULL_COLOR_FLOAT) dest[1] = RE_FULL_COLOR_FLOAT;
|
||||
else
|
||||
#endif
|
||||
dest[1] = c;
|
||||
|
||||
c = (m * dest[2]) + source[2];
|
||||
#ifdef RE_FLOAT_COLOR_CLIPPING
|
||||
if (c >= RE_FULL_COLOR_FLOAT) dest[2] = RE_FULL_COLOR_FLOAT;
|
||||
else
|
||||
#endif
|
||||
dest[2] = c;
|
||||
|
||||
c = (m * dest[3]) + source[3];
|
||||
#ifdef RE_ALPHA_CLIPPING
|
||||
if (c >= RE_FULL_COLOR_FLOAT) dest[3] = RE_FULL_COLOR_FLOAT;
|
||||
else
|
||||
#endif
|
||||
dest[3] = c;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* filtered adding to scanlines */
|
||||
void add_filt_fmask(unsigned int mask, const float col[4], float *rowbuf, int row_w)
|
||||
{
|
||||
/* calc the value of mask */
|
||||
float **fmask1 = R.samples->fmask1, **fmask2 = R.samples->fmask2;
|
||||
float *rb1, *rb2, *rb3;
|
||||
float val, r, g, b, al;
|
||||
unsigned int a, maskand, maskshift;
|
||||
int j;
|
||||
|
||||
r = col[0];
|
||||
g = col[1];
|
||||
b = col[2];
|
||||
al = col[3];
|
||||
|
||||
rb2 = rowbuf - 4;
|
||||
rb3 = rb2 - 4 * row_w;
|
||||
rb1 = rb2 + 4 * row_w;
|
||||
|
||||
maskand = (mask & 255);
|
||||
maskshift = (mask >> 8);
|
||||
|
||||
for (j = 2; j >= 0; j--) {
|
||||
|
||||
a = j;
|
||||
|
||||
val = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
|
||||
if (val != 0.0f) {
|
||||
rb1[0] += val * r;
|
||||
rb1[1] += val * g;
|
||||
rb1[2] += val * b;
|
||||
rb1[3] += val * al;
|
||||
}
|
||||
a += 3;
|
||||
|
||||
val = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
|
||||
if (val != 0.0f) {
|
||||
rb2[0] += val * r;
|
||||
rb2[1] += val * g;
|
||||
rb2[2] += val * b;
|
||||
rb2[3] += val * al;
|
||||
}
|
||||
a += 3;
|
||||
|
||||
val = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
|
||||
if (val != 0.0f) {
|
||||
rb3[0] += val * r;
|
||||
rb3[1] += val * g;
|
||||
rb3[2] += val * b;
|
||||
rb3[3] += val * al;
|
||||
}
|
||||
|
||||
rb1 += 4;
|
||||
rb2 += 4;
|
||||
rb3 += 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mask_array(unsigned int mask, float filt[3][3])
|
||||
{
|
||||
float **fmask1 = R.samples->fmask1, **fmask2 = R.samples->fmask2;
|
||||
unsigned int maskand = (mask & 255);
|
||||
unsigned int maskshift = (mask >> 8);
|
||||
int a, j;
|
||||
|
||||
for (j = 2; j >= 0; j--) {
|
||||
|
||||
a = j;
|
||||
|
||||
filt[2][2 - j] = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
|
||||
|
||||
a += 3;
|
||||
|
||||
filt[1][2 - j] = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
|
||||
|
||||
a += 3;
|
||||
|
||||
filt[0][2 - j] = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index ordering, scanline based:
|
||||
*
|
||||
* <pre>
|
||||
* --- --- ---
|
||||
* | 2,0 | 2,1 | 2,2 |
|
||||
* --- --- ---
|
||||
* | 1,0 | 1,1 | 1,2 |
|
||||
* --- --- ---
|
||||
* | 0,0 | 0,1 | 0,2 |
|
||||
* --- --- ---
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
void add_filt_fmask_coord(float filt[3][3], const float col[4], float *rowbuf, int row_stride, int x, int y, rcti *mask)
|
||||
{
|
||||
float *fpoin[3][3];
|
||||
float val, r, g, b, al, lfilt[3][3];
|
||||
|
||||
r = col[0];
|
||||
g = col[1];
|
||||
b = col[2];
|
||||
al = col[3];
|
||||
|
||||
memcpy(lfilt, filt, sizeof(lfilt));
|
||||
|
||||
fpoin[0][1] = rowbuf - 4 * row_stride;
|
||||
fpoin[1][1] = rowbuf;
|
||||
fpoin[2][1] = rowbuf + 4 * row_stride;
|
||||
|
||||
fpoin[0][0] = fpoin[0][1] - 4;
|
||||
fpoin[1][0] = fpoin[1][1] - 4;
|
||||
fpoin[2][0] = fpoin[2][1] - 4;
|
||||
|
||||
fpoin[0][2] = fpoin[0][1] + 4;
|
||||
fpoin[1][2] = fpoin[1][1] + 4;
|
||||
fpoin[2][2] = fpoin[2][1] + 4;
|
||||
|
||||
/* limit filtering to withing a mask for border rendering, so pixels don't
|
||||
* leak outside of the border */
|
||||
if (y <= mask->ymin) {
|
||||
fpoin[0][0] = fpoin[1][0];
|
||||
fpoin[0][1] = fpoin[1][1];
|
||||
fpoin[0][2] = fpoin[1][2];
|
||||
/* filter needs the opposite value yes! */
|
||||
lfilt[0][0] = filt[2][0];
|
||||
lfilt[0][1] = filt[2][1];
|
||||
lfilt[0][2] = filt[2][2];
|
||||
}
|
||||
else if (y >= mask->ymax - 1) {
|
||||
fpoin[2][0] = fpoin[1][0];
|
||||
fpoin[2][1] = fpoin[1][1];
|
||||
fpoin[2][2] = fpoin[1][2];
|
||||
|
||||
lfilt[2][0] = filt[0][0];
|
||||
lfilt[2][1] = filt[0][1];
|
||||
lfilt[2][2] = filt[0][2];
|
||||
}
|
||||
|
||||
if (x <= mask->xmin) {
|
||||
fpoin[2][0] = fpoin[2][1];
|
||||
fpoin[1][0] = fpoin[1][1];
|
||||
fpoin[0][0] = fpoin[0][1];
|
||||
|
||||
lfilt[2][0] = filt[2][2];
|
||||
lfilt[1][0] = filt[1][2];
|
||||
lfilt[0][0] = filt[0][2];
|
||||
}
|
||||
else if (x >= mask->xmax - 1) {
|
||||
fpoin[2][2] = fpoin[2][1];
|
||||
fpoin[1][2] = fpoin[1][1];
|
||||
fpoin[0][2] = fpoin[0][1];
|
||||
|
||||
lfilt[2][2] = filt[2][0];
|
||||
lfilt[1][2] = filt[1][0];
|
||||
lfilt[0][2] = filt[0][0];
|
||||
}
|
||||
|
||||
|
||||
/* loop unroll */
|
||||
#define MASKFILT(i, j) \
|
||||
val = lfilt[i][j]; \
|
||||
if (val != 0.0f) { \
|
||||
float *fp = fpoin[i][j]; \
|
||||
fp[0] += val * r; \
|
||||
fp[1] += val * g; \
|
||||
fp[2] += val * b; \
|
||||
fp[3] += val * al; \
|
||||
} (void)0
|
||||
|
||||
MASKFILT(0, 0);
|
||||
MASKFILT(0, 1);
|
||||
MASKFILT(0, 2);
|
||||
MASKFILT(1, 0);
|
||||
MASKFILT(1, 1);
|
||||
MASKFILT(1, 2);
|
||||
MASKFILT(2, 0);
|
||||
MASKFILT(2, 1);
|
||||
MASKFILT(2, 2);
|
||||
|
||||
#undef MASKFILT
|
||||
}
|
||||
|
||||
void add_filt_fmask_pixsize(unsigned int mask, float *in, float *rowbuf, int row_w, int pixsize)
|
||||
{
|
||||
/* calc the value of mask */
|
||||
float **fmask1 = R.samples->fmask1, **fmask2 = R.samples->fmask2;
|
||||
float *rb1, *rb2, *rb3;
|
||||
float val;
|
||||
unsigned int a, maskand, maskshift;
|
||||
int i, j;
|
||||
|
||||
rb2 = rowbuf - pixsize;
|
||||
rb3 = rb2 - pixsize * row_w;
|
||||
rb1 = rb2 + pixsize * row_w;
|
||||
|
||||
maskand = (mask & 255);
|
||||
maskshift = (mask >> 8);
|
||||
|
||||
for (j = 2; j >= 0; j--) {
|
||||
|
||||
a = j;
|
||||
|
||||
val = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
|
||||
if (val != 0.0f) {
|
||||
for (i = 0; i < pixsize; i++)
|
||||
rb1[i] += val * in[i];
|
||||
}
|
||||
a += 3;
|
||||
|
||||
val = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
|
||||
if (val != 0.0f) {
|
||||
for (i = 0; i < pixsize; i++)
|
||||
rb2[i] += val * in[i];
|
||||
}
|
||||
a += 3;
|
||||
|
||||
val = *(fmask1[a] + maskand) + *(fmask2[a] + maskshift);
|
||||
if (val != 0.0f) {
|
||||
for (i = 0; i < pixsize; i++)
|
||||
rb3[i] += val * in[i];
|
||||
}
|
||||
|
||||
rb1 += pixsize;
|
||||
rb2 += pixsize;
|
||||
rb3 += pixsize;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
void addalphaAddFloat(float dest[4], const float source[4])
|
||||
{
|
||||
|
||||
/* Makes me wonder whether this is required... */
|
||||
if (dest[3] < RE_EMPTY_COLOR_FLOAT) {
|
||||
dest[0] = source[0];
|
||||
dest[1] = source[1];
|
||||
dest[2] = source[2];
|
||||
dest[3] = source[3];
|
||||
return;
|
||||
}
|
||||
|
||||
/* no clipping! */
|
||||
dest[0] = dest[0] + source[0];
|
||||
dest[1] = dest[1] + source[1];
|
||||
dest[2] = dest[2] + source[2];
|
||||
dest[3] = dest[3] + source[3];
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------------- */
|
650
source/blender/render/intern/source/pixelshading.c
Normal file
650
source/blender/render/intern/source/pixelshading.c
Normal file
@@ -0,0 +1,650 @@
|
||||
/*
|
||||
* ***** 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) 2001-2002 by NaN Holding BV.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributor(s): 2004-2006, Blender Foundation, full recode
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/source/pixelshading.c
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
/* External modules: */
|
||||
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_image_types.h"
|
||||
#include "DNA_texture_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
|
||||
#include "BKE_material.h"
|
||||
|
||||
|
||||
/* own module */
|
||||
#include "render_types.h"
|
||||
#include "renderdatabase.h"
|
||||
#include "texture.h"
|
||||
#include "rendercore.h"
|
||||
#include "shadbuf.h"
|
||||
#include "pixelshading.h"
|
||||
#include "sunsky.h"
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
|
||||
/* only to be used here in this file, it's for speed */
|
||||
extern struct Render R;
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
|
||||
extern const float hashvectf[];
|
||||
|
||||
static void render_lighting_halo(HaloRen *har, float col_r[3])
|
||||
{
|
||||
GroupObject *go;
|
||||
LampRen *lar;
|
||||
float i, inp, inpr, rco[3], dco[3], lv[3], lampdist, ld, t, *vn;
|
||||
float ir, ig, ib, shadfac, soft, lacol[3];
|
||||
|
||||
ir= ig= ib= 0.0;
|
||||
|
||||
copy_v3_v3(rco, har->co);
|
||||
dco[0]=dco[1]=dco[2]= 1.0f/har->rad;
|
||||
|
||||
vn= har->no;
|
||||
|
||||
for (go=R.lights.first; go; go= go->next) {
|
||||
lar= go->lampren;
|
||||
|
||||
/* test for lamplayer */
|
||||
if (lar->mode & LA_LAYER) if ((lar->lay & har->lay)==0) continue;
|
||||
|
||||
/* lampdist cacluation */
|
||||
if (lar->type==LA_SUN || lar->type==LA_HEMI) {
|
||||
copy_v3_v3(lv, lar->vec);
|
||||
lampdist= 1.0;
|
||||
}
|
||||
else {
|
||||
lv[0]= rco[0]-lar->co[0];
|
||||
lv[1]= rco[1]-lar->co[1];
|
||||
lv[2]= rco[2]-lar->co[2];
|
||||
ld = len_v3(lv);
|
||||
lv[0]/= ld;
|
||||
lv[1]/= ld;
|
||||
lv[2]/= ld;
|
||||
|
||||
/* ld is re-used further on (texco's) */
|
||||
|
||||
if (lar->mode & LA_QUAD) {
|
||||
t= 1.0;
|
||||
if (lar->ld1>0.0f)
|
||||
t= lar->dist/(lar->dist+lar->ld1*ld);
|
||||
if (lar->ld2>0.0f)
|
||||
t*= lar->distkw/(lar->distkw+lar->ld2*ld*ld);
|
||||
|
||||
lampdist= t;
|
||||
}
|
||||
else {
|
||||
lampdist= (lar->dist/(lar->dist+ld));
|
||||
}
|
||||
|
||||
if (lar->mode & LA_SPHERE) {
|
||||
t= lar->dist - ld;
|
||||
if (t<0.0f) continue;
|
||||
|
||||
t/= lar->dist;
|
||||
lampdist*= (t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lacol[0]= lar->r;
|
||||
lacol[1]= lar->g;
|
||||
lacol[2]= lar->b;
|
||||
|
||||
if (lar->mode & LA_TEXTURE) {
|
||||
ShadeInput shi;
|
||||
|
||||
/* Warning, This is not that nice, and possibly a bit slow,
|
||||
* however some variables were not initialized properly in, unless using shade_input_initialize(...),
|
||||
* we need to do a memset */
|
||||
memset(&shi, 0, sizeof(ShadeInput));
|
||||
/* end warning! - Campbell */
|
||||
|
||||
copy_v3_v3(shi.co, rco);
|
||||
shi.osatex= 0;
|
||||
do_lamp_tex(lar, lv, &shi, lacol, LA_TEXTURE);
|
||||
}
|
||||
|
||||
if (lar->type==LA_SPOT) {
|
||||
|
||||
if (lar->mode & LA_SQUARE) {
|
||||
if (lv[0]*lar->vec[0]+lv[1]*lar->vec[1]+lv[2]*lar->vec[2]>0.0f) {
|
||||
float x, lvrot[3];
|
||||
|
||||
/* rotate view to lampspace */
|
||||
copy_v3_v3(lvrot, lv);
|
||||
mul_m3_v3(lar->imat, lvrot);
|
||||
|
||||
x = max_ff(fabsf(lvrot[0]/lvrot[2]), fabsf(lvrot[1]/lvrot[2]));
|
||||
/* 1.0/(sqrt(1+x*x)) is equivalent to cos(atan(x)) */
|
||||
|
||||
inpr = 1.0f / (sqrtf(1.0f + x * x));
|
||||
}
|
||||
else inpr= 0.0;
|
||||
}
|
||||
else {
|
||||
inpr= lv[0]*lar->vec[0]+lv[1]*lar->vec[1]+lv[2]*lar->vec[2];
|
||||
}
|
||||
|
||||
t= lar->spotsi;
|
||||
if (inpr<t) continue;
|
||||
else {
|
||||
t= inpr-t;
|
||||
soft= 1.0;
|
||||
if (t<lar->spotbl && lar->spotbl!=0.0f) {
|
||||
/* soft area */
|
||||
i= t/lar->spotbl;
|
||||
t= i*i;
|
||||
soft= (3.0f*t-2.0f*t*i);
|
||||
inpr*= soft;
|
||||
}
|
||||
if (lar->mode & LA_ONLYSHADOW) {
|
||||
/* if (ma->mode & MA_SHADOW) { */
|
||||
/* dot product positive: front side face! */
|
||||
inp= vn[0]*lv[0] + vn[1]*lv[1] + vn[2]*lv[2];
|
||||
if (inp>0.0f) {
|
||||
/* testshadowbuf==0.0 : 100% shadow */
|
||||
shadfac = testshadowbuf(&R, lar->shb, rco, dco, dco, inp, 0.0f);
|
||||
if ( shadfac>0.0f ) {
|
||||
shadfac*= inp*soft*lar->energy;
|
||||
ir -= shadfac;
|
||||
ig -= shadfac;
|
||||
ib -= shadfac;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* } */
|
||||
}
|
||||
lampdist*=inpr;
|
||||
}
|
||||
if (lar->mode & LA_ONLYSHADOW) continue;
|
||||
|
||||
}
|
||||
|
||||
/* dot product and reflectivity*/
|
||||
|
||||
inp = 1.0f - fabsf(dot_v3v3(vn, lv));
|
||||
|
||||
/* inp= cos(0.5*M_PI-acos(inp)); */
|
||||
|
||||
i= inp;
|
||||
|
||||
if (lar->type==LA_HEMI) {
|
||||
i= 0.5f*i+0.5f;
|
||||
}
|
||||
if (i>0.0f) {
|
||||
i*= lampdist;
|
||||
}
|
||||
|
||||
/* shadow */
|
||||
if (i> -0.41f) { /* heuristic valua! */
|
||||
if (lar->shb) {
|
||||
shadfac = testshadowbuf(&R, lar->shb, rco, dco, dco, inp, 0.0f);
|
||||
if (shadfac==0.0f) continue;
|
||||
i*= shadfac;
|
||||
}
|
||||
}
|
||||
|
||||
if (i>0.0f) {
|
||||
ir+= i*lacol[0];
|
||||
ig+= i*lacol[1];
|
||||
ib+= i*lacol[2];
|
||||
}
|
||||
}
|
||||
|
||||
if (ir<0.0f) ir= 0.0f;
|
||||
if (ig<0.0f) ig= 0.0f;
|
||||
if (ib<0.0f) ib= 0.0f;
|
||||
|
||||
col_r[0]*= ir;
|
||||
col_r[1]*= ig;
|
||||
col_r[2]*= ib;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a halo z-buffer value to distance from the camera's near plane
|
||||
* \param z The z-buffer value to convert
|
||||
* \return a distance from the camera's near plane in blender units
|
||||
*/
|
||||
static float haloZtoDist(int z)
|
||||
{
|
||||
float zco = 0;
|
||||
|
||||
if (z >= 0x7FFFFF)
|
||||
return 10e10;
|
||||
else {
|
||||
zco = (float)z/(float)0x7FFFFF;
|
||||
if (R.r.mode & R_ORTHO)
|
||||
return (R.winmat[3][2] - zco*R.winmat[3][3])/(R.winmat[2][2]);
|
||||
else
|
||||
return (R.winmat[3][2])/(R.winmat[2][2] - R.winmat[2][3]*zco);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \param col (float[4]) Store the rgb color here (with alpha)
|
||||
* The alpha is used to blend the color to the background
|
||||
* color_new = (1-alpha)*color_background + color
|
||||
* \param zz The current zbuffer value at the place of this pixel
|
||||
* \param dist Distance of the pixel from the center of the halo squared. Given in pixels
|
||||
* \param xn The x coordinate of the pixel relaticve to the center of the halo. given in pixels
|
||||
* \param yn The y coordinate of the pixel relaticve to the center of the halo. given in pixels
|
||||
*/
|
||||
int shadeHaloFloat(HaloRen *har, float col[4], int zz,
|
||||
float dist, float xn, float yn, short flarec)
|
||||
{
|
||||
/* fill in col */
|
||||
float t, zn, radist, ringf=0.0f, linef=0.0f, alpha, si, co;
|
||||
int a;
|
||||
|
||||
if (R.wrld.mode & WO_MIST) {
|
||||
if (har->type & HA_ONLYSKY) {
|
||||
alpha= har->alfa;
|
||||
}
|
||||
else {
|
||||
/* a bit patchy... */
|
||||
alpha= mistfactor(-har->co[2], har->co)*har->alfa;
|
||||
}
|
||||
}
|
||||
else alpha= har->alfa;
|
||||
|
||||
if (alpha==0.0f)
|
||||
return 0;
|
||||
|
||||
/* soften the halo if it intersects geometry */
|
||||
if (har->mat && har->mat->mode & MA_HALO_SOFT) {
|
||||
float segment_length, halo_depth, distance_from_z /* , visible_depth */ /* UNUSED */, soften;
|
||||
|
||||
/* calculate halo depth */
|
||||
segment_length= har->hasize*sasqrt(1.0f - dist/(har->rad*har->rad));
|
||||
halo_depth= 2.0f*segment_length;
|
||||
|
||||
if (halo_depth < FLT_EPSILON)
|
||||
return 0;
|
||||
|
||||
/* calculate how much of this depth is visible */
|
||||
distance_from_z = haloZtoDist(zz) - haloZtoDist(har->zs);
|
||||
/* visible_depth = halo_depth; */ /* UNUSED */
|
||||
if (distance_from_z < segment_length) {
|
||||
soften= (segment_length + distance_from_z)/halo_depth;
|
||||
|
||||
/* apply softening to alpha */
|
||||
if (soften < 1.0f)
|
||||
alpha *= soften;
|
||||
if (alpha <= 0.0f)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* not a soft halo. use the old softening code */
|
||||
/* halo being intersected? */
|
||||
if (har->zs> zz-har->zd) {
|
||||
t= ((float)(zz-har->zs))/(float)har->zd;
|
||||
alpha*= sqrtf(sqrtf(t));
|
||||
}
|
||||
}
|
||||
|
||||
radist = sqrtf(dist);
|
||||
|
||||
/* watch it: not used nicely: flarec is set at zero in pixstruct */
|
||||
if (flarec) har->pixels+= (int)(har->rad-radist);
|
||||
|
||||
if (har->ringc) {
|
||||
const float *rc;
|
||||
float fac;
|
||||
int ofs;
|
||||
|
||||
/* per ring an antialised circle */
|
||||
ofs= har->seed;
|
||||
|
||||
for (a= har->ringc; a>0; a--, ofs+=2) {
|
||||
|
||||
rc= hashvectf + (ofs % 768);
|
||||
|
||||
fac = fabsf(rc[1] * (har->rad * fabsf(rc[0]) - radist));
|
||||
|
||||
if (fac< 1.0f) {
|
||||
ringf+= (1.0f-fac);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (har->type & HA_VECT) {
|
||||
dist= fabsf(har->cos * (yn) - har->sin * (xn)) / har->rad;
|
||||
if (dist>1.0f) dist= 1.0f;
|
||||
if (har->tex) {
|
||||
zn= har->sin*xn - har->cos*yn;
|
||||
yn= har->cos*xn + har->sin*yn;
|
||||
xn= zn;
|
||||
}
|
||||
}
|
||||
else dist= dist/har->radsq;
|
||||
|
||||
if (har->type & HA_FLARECIRC) {
|
||||
dist = 0.5f + fabsf(dist - 0.5f);
|
||||
}
|
||||
|
||||
if (har->hard>=30) {
|
||||
dist = sqrtf(dist);
|
||||
if (har->hard>=40) {
|
||||
dist = sinf(dist*(float)M_PI_2);
|
||||
if (har->hard>=50) {
|
||||
dist = sqrtf(dist);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (har->hard<20) dist*=dist;
|
||||
|
||||
if (dist < 1.0f)
|
||||
dist= (1.0f-dist);
|
||||
else
|
||||
dist= 0.0f;
|
||||
|
||||
if (har->linec) {
|
||||
const float *rc;
|
||||
float fac;
|
||||
int ofs;
|
||||
|
||||
/* per starpoint an antialiased line */
|
||||
ofs= har->seed;
|
||||
|
||||
for (a= har->linec; a>0; a--, ofs+=3) {
|
||||
|
||||
rc= hashvectf + (ofs % 768);
|
||||
|
||||
fac = fabsf((xn) * rc[0] + (yn) * rc[1]);
|
||||
|
||||
if (fac< 1.0f )
|
||||
linef+= (1.0f-fac);
|
||||
}
|
||||
|
||||
linef*= dist;
|
||||
}
|
||||
|
||||
if (har->starpoints) {
|
||||
float ster, angle;
|
||||
/* rotation */
|
||||
angle = atan2f(yn, xn);
|
||||
angle *= (1.0f+0.25f*har->starpoints);
|
||||
|
||||
co= cosf(angle);
|
||||
si= sinf(angle);
|
||||
|
||||
angle= (co*xn+si*yn)*(co*yn-si*xn);
|
||||
|
||||
ster = fabsf(angle);
|
||||
if (ster>1.0f) {
|
||||
ster= (har->rad)/(ster);
|
||||
|
||||
if (ster<1.0f) dist*= sqrtf(ster);
|
||||
}
|
||||
}
|
||||
|
||||
/* disputable optimize... (ton) */
|
||||
if (dist<=0.00001f)
|
||||
return 0;
|
||||
|
||||
dist*= alpha;
|
||||
ringf*= dist;
|
||||
linef*= alpha;
|
||||
|
||||
/* The color is either the rgb spec-ed by the user, or extracted from */
|
||||
/* the texture */
|
||||
if (har->tex) {
|
||||
col[0]= har->r;
|
||||
col[1]= har->g;
|
||||
col[2]= har->b;
|
||||
col[3]= dist;
|
||||
|
||||
do_halo_tex(har, xn, yn, col);
|
||||
|
||||
col[0]*= col[3];
|
||||
col[1]*= col[3];
|
||||
col[2]*= col[3];
|
||||
|
||||
}
|
||||
else {
|
||||
col[0]= dist*har->r;
|
||||
col[1]= dist*har->g;
|
||||
col[2]= dist*har->b;
|
||||
if (har->type & HA_XALPHA) col[3]= dist*dist;
|
||||
else col[3]= dist;
|
||||
}
|
||||
|
||||
if (har->mat) {
|
||||
if (har->mat->mode & MA_HALO_SHADE) {
|
||||
/* we test for lights because of preview... */
|
||||
if (R.lights.first) render_lighting_halo(har, col);
|
||||
}
|
||||
|
||||
/* Next, we do the line and ring factor modifications. */
|
||||
if (linef!=0.0f) {
|
||||
Material *ma= har->mat;
|
||||
|
||||
col[0]+= linef * ma->specr;
|
||||
col[1]+= linef * ma->specg;
|
||||
col[2]+= linef * ma->specb;
|
||||
|
||||
if (har->type & HA_XALPHA) col[3]+= linef*linef;
|
||||
else col[3]+= linef;
|
||||
}
|
||||
if (ringf!=0.0f) {
|
||||
Material *ma= har->mat;
|
||||
|
||||
col[0]+= ringf * ma->mirr;
|
||||
col[1]+= ringf * ma->mirg;
|
||||
col[2]+= ringf * ma->mirb;
|
||||
|
||||
if (har->type & HA_XALPHA) col[3]+= ringf*ringf;
|
||||
else col[3]+= ringf;
|
||||
}
|
||||
}
|
||||
|
||||
/* alpha requires clip, gives black dots */
|
||||
if (col[3] > 1.0f)
|
||||
col[3]= 1.0f;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Only view vector is important here. Result goes to col_r[3] */
|
||||
void shadeSkyView(float col_r[3], const float rco[3], const float view[3], const float dxyview[2], short thread)
|
||||
{
|
||||
float zen[3], hor[3], blend, blendm;
|
||||
int skyflag;
|
||||
|
||||
/* flag indicating if we render the top hemisphere */
|
||||
skyflag = WO_ZENUP;
|
||||
|
||||
/* Some view vector stuff. */
|
||||
if (R.wrld.skytype & WO_SKYREAL) {
|
||||
|
||||
blend = dot_v3v3(view, R.grvec);
|
||||
|
||||
if (blend<0.0f) skyflag= 0;
|
||||
|
||||
blend = fabsf(blend);
|
||||
}
|
||||
else if (R.wrld.skytype & WO_SKYPAPER) {
|
||||
blend= 0.5f + 0.5f * view[1];
|
||||
}
|
||||
else {
|
||||
/* the fraction of how far we are above the bottom of the screen */
|
||||
blend = fabsf(0.5f + view[1]);
|
||||
}
|
||||
|
||||
copy_v3_v3(hor, &R.wrld.horr);
|
||||
copy_v3_v3(zen, &R.wrld.zenr);
|
||||
|
||||
/* Careful: SKYTEX and SKYBLEND are NOT mutually exclusive! If */
|
||||
/* SKYBLEND is active, the texture and color blend are added. */
|
||||
if (R.wrld.skytype & WO_SKYTEX) {
|
||||
float lo[3];
|
||||
copy_v3_v3(lo, view);
|
||||
if (R.wrld.skytype & WO_SKYREAL) {
|
||||
|
||||
mul_m3_v3(R.imat, lo);
|
||||
|
||||
SWAP(float, lo[1], lo[2]);
|
||||
|
||||
}
|
||||
do_sky_tex(rco, view, lo, dxyview, hor, zen, &blend, skyflag, thread);
|
||||
}
|
||||
|
||||
if (blend>1.0f) blend= 1.0f;
|
||||
blendm= 1.0f-blend;
|
||||
|
||||
/* No clipping, no conversion! */
|
||||
if (R.wrld.skytype & WO_SKYBLEND) {
|
||||
col_r[0] = (blendm*hor[0] + blend*zen[0]);
|
||||
col_r[1] = (blendm*hor[1] + blend*zen[1]);
|
||||
col_r[2] = (blendm*hor[2] + blend*zen[2]);
|
||||
}
|
||||
else {
|
||||
/* Done when a texture was grabbed. */
|
||||
col_r[0]= hor[0];
|
||||
col_r[1]= hor[1];
|
||||
col_r[2]= hor[2];
|
||||
}
|
||||
}
|
||||
|
||||
/* shade sky according to sun lamps, all parameters are like shadeSkyView except sunsky*/
|
||||
void shadeSunView(float col_r[3], const float view[3])
|
||||
{
|
||||
GroupObject *go;
|
||||
LampRen *lar;
|
||||
float sview[3];
|
||||
bool do_init = true;
|
||||
|
||||
for (go=R.lights.first; go; go= go->next) {
|
||||
lar= go->lampren;
|
||||
if (lar->type==LA_SUN && lar->sunsky && (lar->sunsky->effect_type & LA_SUN_EFFECT_SKY)) {
|
||||
float sun_collector[3];
|
||||
float colorxyz[3];
|
||||
|
||||
if (do_init) {
|
||||
|
||||
normalize_v3_v3(sview, view);
|
||||
mul_m3_v3(R.imat, sview);
|
||||
if (sview[2] < 0.0f)
|
||||
sview[2] = 0.0f;
|
||||
normalize_v3(sview);
|
||||
do_init = false;
|
||||
}
|
||||
|
||||
GetSkyXYZRadiancef(lar->sunsky, sview, colorxyz);
|
||||
xyz_to_rgb(colorxyz[0], colorxyz[1], colorxyz[2], &sun_collector[0], &sun_collector[1], &sun_collector[2],
|
||||
lar->sunsky->sky_colorspace);
|
||||
|
||||
ramp_blend(lar->sunsky->skyblendtype, col_r, lar->sunsky->skyblendfac, sun_collector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Stuff the sky color into the collector.
|
||||
*/
|
||||
void shadeSkyPixel(float collector[4], float fx, float fy, short thread)
|
||||
{
|
||||
float view[3], dxyview[2];
|
||||
|
||||
/*
|
||||
* The rules for sky:
|
||||
* 1. Draw an image, if a background image was provided. Stop
|
||||
* 2. get texture and color blend, and combine these.
|
||||
*/
|
||||
|
||||
float fac;
|
||||
|
||||
if ((R.wrld.skytype & (WO_SKYBLEND+WO_SKYTEX))==0) {
|
||||
/* 1. solid color */
|
||||
copy_v3_v3(collector, &R.wrld.horr);
|
||||
|
||||
collector[3] = 0.0f;
|
||||
}
|
||||
else {
|
||||
/* 2. */
|
||||
|
||||
/* This one true because of the context of this routine */
|
||||
if (R.wrld.skytype & WO_SKYPAPER) {
|
||||
view[0]= -1.0f + 2.0f*(fx/(float)R.winx);
|
||||
view[1]= -1.0f + 2.0f*(fy/(float)R.winy);
|
||||
view[2]= 0.0;
|
||||
|
||||
dxyview[0]= 1.0f/(float)R.winx;
|
||||
dxyview[1]= 1.0f/(float)R.winy;
|
||||
}
|
||||
else {
|
||||
calc_view_vector(view, fx, fy);
|
||||
fac= normalize_v3(view);
|
||||
|
||||
if (R.wrld.skytype & WO_SKYTEX) {
|
||||
dxyview[0]= -R.viewdx/fac;
|
||||
dxyview[1]= -R.viewdy/fac;
|
||||
}
|
||||
}
|
||||
|
||||
/* get sky color in the collector */
|
||||
shadeSkyView(collector, NULL, view, dxyview, thread);
|
||||
collector[3] = 0.0f;
|
||||
}
|
||||
|
||||
calc_view_vector(view, fx, fy);
|
||||
shadeSunView(collector, view);
|
||||
}
|
||||
|
||||
/* aerial perspective */
|
||||
void shadeAtmPixel(struct SunSky *sunsky, float collector[3], float fx, float fy, float distance)
|
||||
{
|
||||
float view[3];
|
||||
|
||||
calc_view_vector(view, fx, fy);
|
||||
normalize_v3(view);
|
||||
/*mul_m3_v3(R.imat, view);*/
|
||||
AtmospherePixleShader(sunsky, view, distance, collector);
|
||||
}
|
||||
|
||||
/* eof */
|
2503
source/blender/render/intern/source/rayshade.c
Normal file
2503
source/blender/render/intern/source/rayshade.c
Normal file
File diff suppressed because it is too large
Load Diff
2030
source/blender/render/intern/source/rendercore.c
Normal file
2030
source/blender/render/intern/source/rendercore.c
Normal file
File diff suppressed because it is too large
Load Diff
1603
source/blender/render/intern/source/renderdatabase.c
Normal file
1603
source/blender/render/intern/source/renderdatabase.c
Normal file
File diff suppressed because it is too large
Load Diff
2647
source/blender/render/intern/source/shadbuf.c
Normal file
2647
source/blender/render/intern/source/shadbuf.c
Normal file
File diff suppressed because it is too large
Load Diff
1490
source/blender/render/intern/source/shadeinput.c
Normal file
1490
source/blender/render/intern/source/shadeinput.c
Normal file
File diff suppressed because it is too large
Load Diff
2182
source/blender/render/intern/source/shadeoutput.c
Normal file
2182
source/blender/render/intern/source/shadeoutput.c
Normal file
File diff suppressed because it is too large
Load Diff
1074
source/blender/render/intern/source/sss.c
Normal file
1074
source/blender/render/intern/source/sss.c
Normal file
File diff suppressed because it is too large
Load Diff
1069
source/blender/render/intern/source/strand.c
Normal file
1069
source/blender/render/intern/source/strand.c
Normal file
File diff suppressed because it is too large
Load Diff
506
source/blender/render/intern/source/sunsky.c
Normal file
506
source/blender/render/intern/source/sunsky.c
Normal file
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
* ***** 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.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/source/sunsky.c
|
||||
* \ingroup render
|
||||
*
|
||||
* This feature comes from Preetham paper on "A Practical Analytic Model for Daylight"
|
||||
* and example code from Brian Smits, another author of that paper in
|
||||
* http://www.cs.utah.edu/vissim/papers/sunsky/code/
|
||||
*/
|
||||
|
||||
#include "sunsky.h"
|
||||
#include "BLI_math.h"
|
||||
|
||||
/**
|
||||
* These macros are defined for vector operations
|
||||
* */
|
||||
|
||||
/**
|
||||
* compute v1 = v2 op v3
|
||||
* v1, v2 and v3 are vectors contains 3 float
|
||||
* */
|
||||
#define VEC3OPV(v1, v2, op, v3) \
|
||||
{ \
|
||||
v1[0] = (v2[0] op v3[0]); \
|
||||
v1[1] = (v2[1] op v3[1]); \
|
||||
v1[2] = (v2[2] op v3[2]); \
|
||||
} (void)0
|
||||
|
||||
/**
|
||||
* compute v1 = v2 op f1
|
||||
* v1, v2 are vectors contains 3 float
|
||||
* and f1 is a float
|
||||
* */
|
||||
#define VEC3OPF(v1, v2, op, f1) \
|
||||
{ \
|
||||
v1[0] = (v2[0] op(f1)); \
|
||||
v1[1] = (v2[1] op(f1)); \
|
||||
v1[2] = (v2[2] op(f1)); \
|
||||
} (void)0
|
||||
|
||||
/**
|
||||
* compute v1 = f1 op v2
|
||||
* v1, v2 are vectors contains 3 float
|
||||
* and f1 is a float
|
||||
* */
|
||||
#define FOPVEC3(v1, f1, op, v2) \
|
||||
{ \
|
||||
v1[0] = ((f1) op v2[0]); \
|
||||
v1[1] = ((f1) op v2[1]); \
|
||||
v1[2] = ((f1) op v2[2]); \
|
||||
} (void)0
|
||||
|
||||
/**
|
||||
* ClipColor:
|
||||
* clip a color to range [0, 1];
|
||||
* */
|
||||
void ClipColor(float c[3])
|
||||
{
|
||||
if (c[0] > 1.0f) c[0] = 1.0f;
|
||||
if (c[0] < 0.0f) c[0] = 0.0f;
|
||||
if (c[1] > 1.0f) c[1] = 1.0f;
|
||||
if (c[1] < 0.0f) c[1] = 0.0f;
|
||||
if (c[2] > 1.0f) c[2] = 1.0f;
|
||||
if (c[2] < 0.0f) c[2] = 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* AngleBetween:
|
||||
* compute angle between to direction
|
||||
* all angles are in radians
|
||||
* */
|
||||
static float AngleBetween(float thetav, float phiv, float theta, float phi)
|
||||
{
|
||||
float cospsi = sinf(thetav) * sinf(theta) * cosf(phi - phiv) + cosf(thetav) * cosf(theta);
|
||||
|
||||
if (cospsi > 1.0f)
|
||||
return 0;
|
||||
if (cospsi < -1.0f)
|
||||
return M_PI;
|
||||
|
||||
return acosf(cospsi);
|
||||
}
|
||||
|
||||
/**
|
||||
* DirectionToThetaPhi:
|
||||
* this function convert a direction to it's theta and phi value
|
||||
* parameters:
|
||||
* toSun: contains direction information
|
||||
* theta, phi, are return values from this conversion
|
||||
* */
|
||||
static void DirectionToThetaPhi(float *toSun, float *theta, float *phi)
|
||||
{
|
||||
*theta = acosf(toSun[2]);
|
||||
if (fabsf(*theta) < 1e-5f)
|
||||
*phi = 0;
|
||||
else
|
||||
*phi = atan2f(toSun[1], toSun[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* PerezFunction:
|
||||
* compute perez function value based on input parameters
|
||||
*/
|
||||
static float PerezFunction(struct SunSky *sunsky, const float *lam, float theta, float gamma, float lvz)
|
||||
{
|
||||
float den, num;
|
||||
|
||||
den = ((1 + lam[0] * expf(lam[1])) *
|
||||
(1 + lam[2] * expf(lam[3] * sunsky->theta) + lam[4] * cosf(sunsky->theta) * cosf(sunsky->theta)));
|
||||
|
||||
num = ((1 + lam[0] * expf(lam[1] / cosf(theta))) *
|
||||
(1 + lam[2] * expf(lam[3] * gamma) + lam[4] * cosf(gamma) * cosf(gamma)));
|
||||
|
||||
return(lvz * num / den);
|
||||
}
|
||||
|
||||
/**
|
||||
* InitSunSky:
|
||||
* this function compute some sun,sky parameters according to input parameters and also initiate some other sun, sky parameters
|
||||
* parameters:
|
||||
* sunSky, is a structure that contains information about sun, sky and atmosphere, in this function, most of its values initiated
|
||||
* turb, is atmosphere turbidity
|
||||
* toSun, contains sun direction
|
||||
* horizon_brighness, controls the brightness of the horizon colors
|
||||
* spread, controls colors spreed at horizon
|
||||
* sun_brightness, controls sun's brightness
|
||||
* sun_size, controls sun's size
|
||||
* back_scatter, controls back scatter light
|
||||
* */
|
||||
void InitSunSky(struct SunSky *sunsky, float turb, const float toSun[3], float horizon_brightness,
|
||||
float spread, float sun_brightness, float sun_size, float back_scatter,
|
||||
float skyblendfac, short skyblendtype, float sky_exposure, float sky_colorspace)
|
||||
{
|
||||
float theta2;
|
||||
float theta3;
|
||||
float T;
|
||||
float T2;
|
||||
float chi;
|
||||
|
||||
sunsky->turbidity = turb;
|
||||
|
||||
sunsky->horizon_brightness = horizon_brightness;
|
||||
sunsky->spread = spread;
|
||||
sunsky->sun_brightness = sun_brightness;
|
||||
sunsky->sun_size = sun_size;
|
||||
sunsky->backscattered_light = back_scatter;
|
||||
sunsky->skyblendfac = skyblendfac;
|
||||
sunsky->skyblendtype = skyblendtype;
|
||||
sunsky->sky_exposure = -sky_exposure;
|
||||
sunsky->sky_colorspace = sky_colorspace;
|
||||
|
||||
sunsky->toSun[0] = toSun[0];
|
||||
sunsky->toSun[1] = toSun[1];
|
||||
sunsky->toSun[2] = toSun[2];
|
||||
|
||||
DirectionToThetaPhi(sunsky->toSun, &sunsky->theta, &sunsky->phi);
|
||||
|
||||
sunsky->sunSolidAngle = 0.25 * M_PI * 1.39 * 1.39 / (150 * 150); /* = 6.7443e-05 */
|
||||
|
||||
theta2 = sunsky->theta * sunsky->theta;
|
||||
theta3 = theta2 * sunsky->theta;
|
||||
T = turb;
|
||||
T2 = turb * turb;
|
||||
|
||||
chi = (4.0f / 9.0f - T / 120.0f) * ((float)M_PI - 2.0f * sunsky->theta);
|
||||
sunsky->zenith_Y = (4.0453f * T - 4.9710f) * tanf(chi) - 0.2155f * T + 2.4192f;
|
||||
sunsky->zenith_Y *= 1000; /* conversion from kcd/m^2 to cd/m^2 */
|
||||
|
||||
if (sunsky->zenith_Y <= 0)
|
||||
sunsky->zenith_Y = 1e-6;
|
||||
|
||||
sunsky->zenith_x =
|
||||
(+0.00165f * theta3 - 0.00374f * theta2 + 0.00208f * sunsky->theta + 0.0f) * T2 +
|
||||
(-0.02902f * theta3 + 0.06377f * theta2 - 0.03202f * sunsky->theta + 0.00394f) * T +
|
||||
(+0.11693f * theta3 - 0.21196f * theta2 + 0.06052f * sunsky->theta + 0.25885f);
|
||||
|
||||
sunsky->zenith_y =
|
||||
(+0.00275f * theta3 - 0.00610f * theta2 + 0.00316f * sunsky->theta + 0.0f) * T2 +
|
||||
(-0.04214f * theta3 + 0.08970f * theta2 - 0.04153f * sunsky->theta + 0.00515f) * T +
|
||||
(+0.15346f * theta3 - 0.26756f * theta2 + 0.06669f * sunsky->theta + 0.26688f);
|
||||
|
||||
|
||||
sunsky->perez_Y[0] = 0.17872f * T - 1.46303f;
|
||||
sunsky->perez_Y[1] = -0.35540f * T + 0.42749f;
|
||||
sunsky->perez_Y[2] = -0.02266f * T + 5.32505f;
|
||||
sunsky->perez_Y[3] = 0.12064f * T - 2.57705f;
|
||||
sunsky->perez_Y[4] = -0.06696f * T + 0.37027f;
|
||||
|
||||
sunsky->perez_x[0] = -0.01925f * T - 0.25922f;
|
||||
sunsky->perez_x[1] = -0.06651f * T + 0.00081f;
|
||||
sunsky->perez_x[2] = -0.00041f * T + 0.21247f;
|
||||
sunsky->perez_x[3] = -0.06409f * T - 0.89887f;
|
||||
sunsky->perez_x[4] = -0.00325f * T + 0.04517f;
|
||||
|
||||
sunsky->perez_y[0] = -0.01669f * T - 0.26078f;
|
||||
sunsky->perez_y[1] = -0.09495f * T + 0.00921f;
|
||||
sunsky->perez_y[2] = -0.00792f * T + 0.21023f;
|
||||
sunsky->perez_y[3] = -0.04405f * T - 1.65369f;
|
||||
sunsky->perez_y[4] = -0.01092f * T + 0.05291f;
|
||||
|
||||
/* suggested by glome in patch [#8063] */
|
||||
sunsky->perez_Y[0] *= sunsky->horizon_brightness;
|
||||
sunsky->perez_x[0] *= sunsky->horizon_brightness;
|
||||
sunsky->perez_y[0] *= sunsky->horizon_brightness;
|
||||
|
||||
sunsky->perez_Y[1] *= sunsky->spread;
|
||||
sunsky->perez_x[1] *= sunsky->spread;
|
||||
sunsky->perez_y[1] *= sunsky->spread;
|
||||
|
||||
sunsky->perez_Y[2] *= sunsky->sun_brightness;
|
||||
sunsky->perez_x[2] *= sunsky->sun_brightness;
|
||||
sunsky->perez_y[2] *= sunsky->sun_brightness;
|
||||
|
||||
sunsky->perez_Y[3] *= sunsky->sun_size;
|
||||
sunsky->perez_x[3] *= sunsky->sun_size;
|
||||
sunsky->perez_y[3] *= sunsky->sun_size;
|
||||
|
||||
sunsky->perez_Y[4] *= sunsky->backscattered_light;
|
||||
sunsky->perez_x[4] *= sunsky->backscattered_light;
|
||||
sunsky->perez_y[4] *= sunsky->backscattered_light;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetSkyXYZRadiance:
|
||||
* this function compute sky radiance according to a view parameters `theta' and `phi'and sunSky values
|
||||
* parameters:
|
||||
* sunSky, sontains sun and sky parameters
|
||||
* theta, is sun's theta
|
||||
* phi, is sun's phi
|
||||
* color_out, is computed color that shows sky radiance in XYZ color format
|
||||
* */
|
||||
void GetSkyXYZRadiance(struct SunSky *sunsky, float theta, float phi, float color_out[3])
|
||||
{
|
||||
float gamma;
|
||||
float x, y, Y, X, Z;
|
||||
float hfade = 1, nfade = 1;
|
||||
|
||||
|
||||
if (theta > (float)M_PI_2) {
|
||||
hfade = 1.0f - (theta * (float)M_1_PI - 0.5f) * 2.0f;
|
||||
hfade = hfade * hfade * (3.0f - 2.0f * hfade);
|
||||
theta = M_PI_2;
|
||||
}
|
||||
|
||||
if (sunsky->theta > (float)M_PI_2) {
|
||||
if (theta <= (float)M_PI_2) {
|
||||
nfade = 1.0f - (0.5f - theta * (float)M_1_PI) * 2.0f;
|
||||
nfade *= 1.0f - (sunsky->theta * (float)M_1_PI - 0.5f) * 2.0f;
|
||||
nfade = nfade * nfade * (3.0f - 2.0f * nfade);
|
||||
}
|
||||
}
|
||||
|
||||
gamma = AngleBetween(theta, phi, sunsky->theta, sunsky->phi);
|
||||
|
||||
/* Compute xyY values */
|
||||
x = PerezFunction(sunsky, sunsky->perez_x, theta, gamma, sunsky->zenith_x);
|
||||
y = PerezFunction(sunsky, sunsky->perez_y, theta, gamma, sunsky->zenith_y);
|
||||
Y = 6.666666667e-5f * nfade * hfade * PerezFunction(sunsky, sunsky->perez_Y, theta, gamma, sunsky->zenith_Y);
|
||||
|
||||
if (sunsky->sky_exposure != 0.0f)
|
||||
Y = 1.0 - exp(Y * sunsky->sky_exposure);
|
||||
|
||||
X = (x / y) * Y;
|
||||
Z = ((1 - x - y) / y) * Y;
|
||||
|
||||
color_out[0] = X;
|
||||
color_out[1] = Y;
|
||||
color_out[2] = Z;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetSkyXYZRadiancef:
|
||||
* this function compute sky radiance according to a view direction `varg' and sunSky values
|
||||
* parameters:
|
||||
* sunSky, sontains sun and sky parameters
|
||||
* varg, shows direction
|
||||
* color_out, is computed color that shows sky radiance in XYZ color format
|
||||
* */
|
||||
void GetSkyXYZRadiancef(struct SunSky *sunsky, const float varg[3], float color_out[3])
|
||||
{
|
||||
float theta, phi;
|
||||
float v[3];
|
||||
|
||||
normalize_v3_v3(v, varg);
|
||||
|
||||
if (v[2] < 0.001f) {
|
||||
v[2] = 0.001f;
|
||||
normalize_v3(v);
|
||||
}
|
||||
|
||||
DirectionToThetaPhi(v, &theta, &phi);
|
||||
GetSkyXYZRadiance(sunsky, theta, phi, color_out);
|
||||
}
|
||||
|
||||
/**
|
||||
* ComputeAttenuatedSunlight:
|
||||
* this function compute attenuated sun light based on sun's theta and atmosphere turbidity
|
||||
* parameters:
|
||||
* theta, is sun's theta
|
||||
* turbidity: is atmosphere turbidity
|
||||
* fTau: contains computed attenuated sun light
|
||||
* */
|
||||
static void ComputeAttenuatedSunlight(float theta, int turbidity, float fTau[3])
|
||||
{
|
||||
float fBeta;
|
||||
float fTauR, fTauA;
|
||||
float m;
|
||||
float fAlpha;
|
||||
|
||||
int i;
|
||||
float fLambda[3];
|
||||
fLambda[0] = 0.65f;
|
||||
fLambda[1] = 0.57f;
|
||||
fLambda[2] = 0.475f;
|
||||
|
||||
fAlpha = 1.3f;
|
||||
fBeta = 0.04608365822050f * turbidity - 0.04586025928522f;
|
||||
|
||||
m = 1.0f / (cosf(theta) + 0.15f * powf(93.885f - theta / (float)M_PI * 180.0f, -1.253f));
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
/* Rayleigh Scattering */
|
||||
fTauR = expf(-m * 0.008735f * powf(fLambda[i], (float)(-4.08f)));
|
||||
|
||||
/* Aerosal (water + dust) attenuation */
|
||||
fTauA = exp(-m * fBeta * powf(fLambda[i], -fAlpha));
|
||||
|
||||
fTau[i] = fTauR * fTauA;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* InitAtmosphere:
|
||||
* this function initiate sunSky structure with user input parameters.
|
||||
* parameters:
|
||||
* sunSky, contains information about sun, and in this function some atmosphere parameters will initiated
|
||||
* sun_intens, shows sun intensity value
|
||||
* mief, Mie scattering factor this factor currently call with 1.0
|
||||
* rayf, Rayleigh scattering factor, this factor currently call with 1.0
|
||||
* inscattf, inscatter light factor that range from 0.0 to 1.0, 0.0 means no inscatter light and 1.0 means full inscatter light
|
||||
* extincf, extinction light factor that range from 0.0 to 1.0, 0.0 means no extinction and 1.0 means full extinction
|
||||
* disf, is distance factor, multiplied to pixle's z value to compute each pixle's distance to camera,
|
||||
* */
|
||||
void InitAtmosphere(struct SunSky *sunSky, float sun_intens, float mief, float rayf,
|
||||
float inscattf, float extincf, float disf)
|
||||
{
|
||||
const float pi = M_PI;
|
||||
const float n = 1.003f; /* refractive index */
|
||||
const float N = 2.545e25;
|
||||
const float pn = 0.035f;
|
||||
const float T = 2.0f;
|
||||
float fTemp, fTemp2, fTemp3, fBeta, fBetaDash;
|
||||
float c = (6.544f * T - 6.51f) * 1e-17f;
|
||||
float K[3] = {0.685f, 0.679f, 0.670f};
|
||||
float vBetaMieTemp[3];
|
||||
|
||||
float fLambda[3], fLambda2[3], fLambda4[3];
|
||||
float vLambda2[3];
|
||||
float vLambda4[3];
|
||||
|
||||
int i;
|
||||
|
||||
sunSky->atm_SunIntensity = sun_intens;
|
||||
sunSky->atm_BetaMieMultiplier = mief;
|
||||
sunSky->atm_BetaRayMultiplier = rayf;
|
||||
sunSky->atm_InscatteringMultiplier = inscattf;
|
||||
sunSky->atm_ExtinctionMultiplier = extincf;
|
||||
sunSky->atm_DistanceMultiplier = disf;
|
||||
|
||||
sunSky->atm_HGg = 0.8;
|
||||
|
||||
fLambda[0] = 1 / 650e-9f;
|
||||
fLambda[1] = 1 / 570e-9f;
|
||||
fLambda[2] = 1 / 475e-9f;
|
||||
for (i = 0; i < 3; i++) {
|
||||
fLambda2[i] = fLambda[i] * fLambda[i];
|
||||
fLambda4[i] = fLambda2[i] * fLambda2[i];
|
||||
}
|
||||
|
||||
vLambda2[0] = fLambda2[0];
|
||||
vLambda2[1] = fLambda2[1];
|
||||
vLambda2[2] = fLambda2[2];
|
||||
|
||||
vLambda4[0] = fLambda4[0];
|
||||
vLambda4[1] = fLambda4[1];
|
||||
vLambda4[2] = fLambda4[2];
|
||||
|
||||
/* Rayleigh scattering constants. */
|
||||
fTemp = pi * pi * (n * n - 1) * (n * n - 1) * (6 + 3 * pn) / (6 - 7 * pn) / N;
|
||||
fBeta = 8 * fTemp * pi / 3;
|
||||
|
||||
VEC3OPF(sunSky->atm_BetaRay, vLambda4, *, fBeta);
|
||||
fBetaDash = fTemp / 2;
|
||||
VEC3OPF(sunSky->atm_BetaDashRay, vLambda4, *, fBetaDash);
|
||||
|
||||
|
||||
/* Mie scattering constants. */
|
||||
fTemp2 = 0.434f * c * (2 * pi) * (2 * pi) * 0.5f;
|
||||
VEC3OPF(sunSky->atm_BetaDashMie, vLambda2, *, fTemp2);
|
||||
|
||||
fTemp3 = 0.434f * c * pi * (2 * pi) * (2 * pi);
|
||||
|
||||
VEC3OPV(vBetaMieTemp, K, *, fLambda);
|
||||
VEC3OPF(sunSky->atm_BetaMie, vBetaMieTemp, *, fTemp3);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* AtmospherePixleShader:
|
||||
* this function apply atmosphere effect on a pixle color `rgb' at distance `s'
|
||||
* parameters:
|
||||
* sunSky, contains information about sun parameters and user values
|
||||
* view, is camera view vector
|
||||
* s, is distance
|
||||
* rgb, contains rendered color value for a pixle
|
||||
* */
|
||||
void AtmospherePixleShader(struct SunSky *sunSky, float view[3], float s, float rgb[3])
|
||||
{
|
||||
float costheta;
|
||||
float Phase_1;
|
||||
float Phase_2;
|
||||
float sunColor[3];
|
||||
|
||||
float E[3];
|
||||
float E1[3];
|
||||
|
||||
|
||||
float I[3];
|
||||
float fTemp;
|
||||
float vTemp1[3], vTemp2[3];
|
||||
|
||||
float sunDirection[3];
|
||||
|
||||
s *= sunSky->atm_DistanceMultiplier;
|
||||
|
||||
sunDirection[0] = sunSky->toSun[0];
|
||||
sunDirection[1] = sunSky->toSun[1];
|
||||
sunDirection[2] = sunSky->toSun[2];
|
||||
|
||||
costheta = dot_v3v3(view, sunDirection); /* cos(theta) */
|
||||
Phase_1 = 1 + (costheta * costheta); /* Phase_1 */
|
||||
|
||||
VEC3OPF(sunSky->atm_BetaRay, sunSky->atm_BetaRay, *, sunSky->atm_BetaRayMultiplier);
|
||||
VEC3OPF(sunSky->atm_BetaMie, sunSky->atm_BetaMie, *, sunSky->atm_BetaMieMultiplier);
|
||||
VEC3OPV(sunSky->atm_BetaRM, sunSky->atm_BetaRay, +, sunSky->atm_BetaMie);
|
||||
|
||||
/* e^(-(beta_1 + beta_2) * s) = E1 */
|
||||
VEC3OPF(E1, sunSky->atm_BetaRM, *, -s / (float)M_LN2);
|
||||
E1[0] = exp(E1[0]);
|
||||
E1[1] = exp(E1[1]);
|
||||
E1[2] = exp(E1[2]);
|
||||
|
||||
copy_v3_v3(E, E1);
|
||||
|
||||
/* Phase2(theta) = (1-g^2)/(1+g-2g*cos(theta))^(3/2) */
|
||||
fTemp = 1 + sunSky->atm_HGg - 2 * sunSky->atm_HGg * costheta;
|
||||
fTemp = fTemp * sqrtf(fTemp);
|
||||
Phase_2 = (1 - sunSky->atm_HGg * sunSky->atm_HGg) / fTemp;
|
||||
|
||||
VEC3OPF(vTemp1, sunSky->atm_BetaDashRay, *, Phase_1);
|
||||
VEC3OPF(vTemp2, sunSky->atm_BetaDashMie, *, Phase_2);
|
||||
|
||||
VEC3OPV(vTemp1, vTemp1, +, vTemp2);
|
||||
FOPVEC3(vTemp2, 1.0f, -, E1);
|
||||
VEC3OPV(vTemp1, vTemp1, *, vTemp2);
|
||||
|
||||
FOPVEC3(vTemp2, 1.0f, /, sunSky->atm_BetaRM);
|
||||
|
||||
VEC3OPV(I, vTemp1, *, vTemp2);
|
||||
|
||||
VEC3OPF(I, I, *, sunSky->atm_InscatteringMultiplier);
|
||||
VEC3OPF(E, E, *, sunSky->atm_ExtinctionMultiplier);
|
||||
|
||||
/* scale to color sun */
|
||||
ComputeAttenuatedSunlight(sunSky->theta, sunSky->turbidity, sunColor);
|
||||
VEC3OPV(E, E, *, sunColor);
|
||||
|
||||
VEC3OPF(I, I, *, sunSky->atm_SunIntensity);
|
||||
|
||||
VEC3OPV(rgb, rgb, *, E);
|
||||
VEC3OPV(rgb, rgb, +, I);
|
||||
}
|
||||
|
||||
#undef VEC3OPV
|
||||
#undef VEC3OPF
|
||||
#undef FOPVEC3
|
||||
|
||||
/* EOF */
|
855
source/blender/render/intern/source/volume_precache.c
Normal file
855
source/blender/render/intern/source/volume_precache.c
Normal file
@@ -0,0 +1,855 @@
|
||||
/*
|
||||
* ***** 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) 2001-2002 by NaN Holding BV.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Matt Ebb, Ra˙l Fern·ndez Hern·ndez (Farsthary).
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/source/volume_precache.c
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <float.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_voxel.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "PIL_time.h"
|
||||
|
||||
#include "RE_shader_ext.h"
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
|
||||
#include "rayintersection.h"
|
||||
#include "rayobject.h"
|
||||
#include "render_types.h"
|
||||
#include "rendercore.h"
|
||||
#include "renderdatabase.h"
|
||||
#include "volumetric.h"
|
||||
#include "volume_precache.h"
|
||||
|
||||
#include "atomic_ops.h"
|
||||
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
|
||||
/* only to be used here in this file, it's for speed */
|
||||
extern struct Render R;
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
/* *** utility code to set up an individual raytree for objectinstance, for checking inside/outside *** */
|
||||
|
||||
/* Recursive test for intersections, from a point inside the mesh, to outside
|
||||
* Number of intersections (depth) determine if a point is inside or outside the mesh */
|
||||
static int intersect_outside_volume(RayObject *tree, Isect *isect, float *offset, int limit, int depth)
|
||||
{
|
||||
if (limit == 0) return depth;
|
||||
|
||||
if (RE_rayobject_raycast(tree, isect)) {
|
||||
|
||||
isect->start[0] = isect->start[0] + isect->dist*isect->dir[0];
|
||||
isect->start[1] = isect->start[1] + isect->dist*isect->dir[1];
|
||||
isect->start[2] = isect->start[2] + isect->dist*isect->dir[2];
|
||||
|
||||
isect->dist = FLT_MAX;
|
||||
isect->skip = RE_SKIP_VLR_NEIGHBOUR;
|
||||
isect->orig.face= isect->hit.face;
|
||||
isect->orig.ob= isect->hit.ob;
|
||||
|
||||
return intersect_outside_volume(tree, isect, offset, limit-1, depth+1);
|
||||
}
|
||||
else {
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
|
||||
/* Uses ray tracing to check if a point is inside or outside an ObjectInstanceRen */
|
||||
static int point_inside_obi(RayObject *tree, ObjectInstanceRen *obi, const float co[3])
|
||||
{
|
||||
Isect isect= {{0}};
|
||||
float dir[3] = {0.0f, 0.0f, 1.0f};
|
||||
int final_depth=0, depth=0, limit=20;
|
||||
|
||||
/* set up the isect */
|
||||
copy_v3_v3(isect.start, co);
|
||||
copy_v3_v3(isect.dir, dir);
|
||||
isect.mode= RE_RAY_MIRROR;
|
||||
isect.last_hit= NULL;
|
||||
isect.lay= -1;
|
||||
|
||||
isect.dist = FLT_MAX;
|
||||
isect.orig.face= NULL;
|
||||
isect.orig.ob = NULL;
|
||||
|
||||
RE_instance_rotate_ray(obi, &isect);
|
||||
final_depth = intersect_outside_volume(tree, &isect, dir, limit, depth);
|
||||
RE_instance_rotate_ray_restore(obi, &isect);
|
||||
|
||||
/* even number of intersections: point is outside
|
||||
* odd number: point is inside */
|
||||
if (final_depth % 2 == 0) return 0;
|
||||
else return 1;
|
||||
}
|
||||
|
||||
/* find the bounding box of an objectinstance in global space */
|
||||
void global_bounds_obi(Render *re, ObjectInstanceRen *obi, float bbmin[3], float bbmax[3])
|
||||
{
|
||||
ObjectRen *obr = obi->obr;
|
||||
VolumePrecache *vp = obi->volume_precache;
|
||||
VertRen *ver= NULL;
|
||||
float co[3];
|
||||
int a;
|
||||
|
||||
if (vp->bbmin != NULL && vp->bbmax != NULL) {
|
||||
copy_v3_v3(bbmin, vp->bbmin);
|
||||
copy_v3_v3(bbmax, vp->bbmax);
|
||||
return;
|
||||
}
|
||||
|
||||
vp->bbmin = MEM_callocN(sizeof(float)*3, "volume precache min boundbox corner");
|
||||
vp->bbmax = MEM_callocN(sizeof(float)*3, "volume precache max boundbox corner");
|
||||
|
||||
INIT_MINMAX(bbmin, bbmax);
|
||||
|
||||
for (a=0; a<obr->totvert; a++) {
|
||||
if ((a & 255)==0) ver= obr->vertnodes[a>>8].vert;
|
||||
else ver++;
|
||||
|
||||
copy_v3_v3(co, ver->co);
|
||||
|
||||
/* transformed object instance in camera space */
|
||||
if (obi->flag & R_TRANSFORMED)
|
||||
mul_m4_v3(obi->mat, co);
|
||||
|
||||
/* convert to global space */
|
||||
mul_m4_v3(re->viewinv, co);
|
||||
|
||||
minmax_v3v3_v3(vp->bbmin, vp->bbmax, co);
|
||||
}
|
||||
|
||||
copy_v3_v3(bbmin, vp->bbmin);
|
||||
copy_v3_v3(bbmax, vp->bbmax);
|
||||
|
||||
}
|
||||
|
||||
/* *** light cache filtering *** */
|
||||
|
||||
static float get_avg_surrounds(float *cache, int *res, int xx, int yy, int zz)
|
||||
{
|
||||
int x, y, z, x_, y_, z_;
|
||||
int added=0;
|
||||
float tot=0.0f;
|
||||
|
||||
for (z=-1; z <= 1; z++) {
|
||||
z_ = zz+z;
|
||||
if (z_ >= 0 && z_ <= res[2]-1) {
|
||||
|
||||
for (y=-1; y <= 1; y++) {
|
||||
y_ = yy+y;
|
||||
if (y_ >= 0 && y_ <= res[1]-1) {
|
||||
|
||||
for (x=-1; x <= 1; x++) {
|
||||
x_ = xx+x;
|
||||
if (x_ >= 0 && x_ <= res[0]-1) {
|
||||
const int64_t i = BLI_VOXEL_INDEX(x_, y_, z_, res);
|
||||
|
||||
if (cache[i] > 0.0f) {
|
||||
tot += cache[i];
|
||||
added++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (added > 0) tot /= added;
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
/* function to filter the edges of the light cache, where there was no volume originally.
|
||||
* For each voxel which was originally external to the mesh, it finds the average values of
|
||||
* the surrounding internal voxels and sets the original external voxel to that average amount.
|
||||
* Works almost a bit like a 'dilate' filter */
|
||||
static void lightcache_filter(VolumePrecache *vp)
|
||||
{
|
||||
int x, y, z;
|
||||
|
||||
for (z=0; z < vp->res[2]; z++) {
|
||||
for (y=0; y < vp->res[1]; y++) {
|
||||
for (x=0; x < vp->res[0]; x++) {
|
||||
/* trigger for outside mesh */
|
||||
const int64_t i = BLI_VOXEL_INDEX(x, y, z, vp->res);
|
||||
|
||||
if (vp->data_r[i] < -0.f)
|
||||
vp->data_r[i] = get_avg_surrounds(vp->data_r, vp->res, x, y, z);
|
||||
if (vp->data_g[i] < -0.f)
|
||||
vp->data_g[i] = get_avg_surrounds(vp->data_g, vp->res, x, y, z);
|
||||
if (vp->data_b[i] < -0.f)
|
||||
vp->data_b[i] = get_avg_surrounds(vp->data_b, vp->res, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void lightcache_filter2(VolumePrecache *vp)
|
||||
{
|
||||
int x, y, z;
|
||||
float *new_r, *new_g, *new_b;
|
||||
int field_size = vp->res[0]*vp->res[1]*vp->res[2]*sizeof(float);
|
||||
|
||||
new_r = MEM_mallocN(field_size, "temp buffer for light cache filter r channel");
|
||||
new_g = MEM_mallocN(field_size, "temp buffer for light cache filter g channel");
|
||||
new_b = MEM_mallocN(field_size, "temp buffer for light cache filter b channel");
|
||||
|
||||
memcpy(new_r, vp->data_r, field_size);
|
||||
memcpy(new_g, vp->data_g, field_size);
|
||||
memcpy(new_b, vp->data_b, field_size);
|
||||
|
||||
for (z=0; z < vp->res[2]; z++) {
|
||||
for (y=0; y < vp->res[1]; y++) {
|
||||
for (x=0; x < vp->res[0]; x++) {
|
||||
/* trigger for outside mesh */
|
||||
const int64_t i = BLI_VOXEL_INDEX(x, y, z, vp->res);
|
||||
if (vp->data_r[i] < -0.f)
|
||||
new_r[i] = get_avg_surrounds(vp->data_r, vp->res, x, y, z);
|
||||
if (vp->data_g[i] < -0.f)
|
||||
new_g[i] = get_avg_surrounds(vp->data_g, vp->res, x, y, z);
|
||||
if (vp->data_b[i] < -0.f)
|
||||
new_b[i] = get_avg_surrounds(vp->data_b, vp->res, x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SWAP(float *, vp->data_r, new_r);
|
||||
SWAP(float *, vp->data_g, new_g);
|
||||
SWAP(float *, vp->data_b, new_b);
|
||||
|
||||
if (new_r) { MEM_freeN(new_r); new_r=NULL; }
|
||||
if (new_g) { MEM_freeN(new_g); new_g=NULL; }
|
||||
if (new_b) { MEM_freeN(new_b); new_b=NULL; }
|
||||
}
|
||||
#endif
|
||||
|
||||
/* has a pad of 1 voxel surrounding the core for boundary simulation */
|
||||
BLI_INLINE int64_t ms_I(int x, int y, int z, const int *n)
|
||||
{
|
||||
/* different ordering to light cache */
|
||||
return ((int64_t)x * (int64_t)(n[1] + 2) * (int64_t)(n[2] + 2) +
|
||||
(int64_t)y * (int64_t)(n[2] + 2) +
|
||||
(int64_t)z);
|
||||
}
|
||||
|
||||
/* has a pad of 1 voxel surrounding the core for boundary simulation */
|
||||
BLI_INLINE int64_t v_I_pad(int x, int y, int z, const int *n)
|
||||
{
|
||||
/* same ordering to light cache, with padding */
|
||||
return ((int64_t)z * (int64_t)(n[1] + 2) * (int64_t)(n[0] + 2) +
|
||||
(int64_t)y * (int64_t)(n[0] + 2) +
|
||||
(int64_t)x);
|
||||
}
|
||||
|
||||
BLI_INLINE int64_t lc_to_ms_I(int x, int y, int z, const int *n)
|
||||
{
|
||||
/* converting light cache index to multiple scattering index */
|
||||
return ((int64_t)(x - 1) * ((int64_t)n[1] * (int64_t)n[2]) +
|
||||
(int64_t)(y - 1) * ((int64_t)n[2]) +
|
||||
(int64_t)(z - 1));
|
||||
}
|
||||
|
||||
/* *** multiple scattering approximation *** */
|
||||
|
||||
/* get the total amount of light energy in the light cache. used to normalize after multiple scattering */
|
||||
static float total_ss_energy(Render *re, int do_test_break, VolumePrecache *vp)
|
||||
{
|
||||
int x, y, z;
|
||||
const int *res = vp->res;
|
||||
float energy=0.f;
|
||||
|
||||
for (z=0; z < res[2]; z++) {
|
||||
for (y=0; y < res[1]; y++) {
|
||||
for (x=0; x < res[0]; x++) {
|
||||
const int64_t i = BLI_VOXEL_INDEX(x, y, z, res);
|
||||
|
||||
if (vp->data_r[i] > 0.f) energy += vp->data_r[i];
|
||||
if (vp->data_g[i] > 0.f) energy += vp->data_g[i];
|
||||
if (vp->data_b[i] > 0.f) energy += vp->data_b[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (do_test_break && re->test_break(re->tbh)) break;
|
||||
}
|
||||
|
||||
return energy;
|
||||
}
|
||||
|
||||
static float total_ms_energy(Render *re, int do_test_break, float *sr, float *sg, float *sb, const int res[3])
|
||||
{
|
||||
int x, y, z;
|
||||
float energy=0.f;
|
||||
|
||||
for (z=1;z<=res[2];z++) {
|
||||
for (y=1;y<=res[1];y++) {
|
||||
for (x=1;x<=res[0];x++) {
|
||||
const int64_t i = ms_I(x, y, z, res);
|
||||
|
||||
if (sr[i] > 0.f) energy += sr[i];
|
||||
if (sg[i] > 0.f) energy += sg[i];
|
||||
if (sb[i] > 0.f) energy += sb[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (do_test_break && re->test_break(re->tbh)) break;
|
||||
}
|
||||
|
||||
return energy;
|
||||
}
|
||||
|
||||
/**
|
||||
* \param n: the unpadded resolution
|
||||
*/
|
||||
static void ms_diffuse(Render *re, int do_test_break, const float *x0, float *x, float diff, const int n[3])
|
||||
{
|
||||
int i, j, k, l;
|
||||
const float dt = VOL_MS_TIMESTEP;
|
||||
int64_t size = (int64_t)n[0] * (int64_t)n[1] * (int64_t)n[2];
|
||||
const float a = dt * diff * size;
|
||||
|
||||
for (l=0; l<20; l++) {
|
||||
for (k=1; k<=n[2]; k++) {
|
||||
for (j=1; j<=n[1]; j++) {
|
||||
for (i=1; i<=n[0]; i++) {
|
||||
x[v_I_pad(i, j, k, n)] =
|
||||
((x0[v_I_pad(i, j, k, n)]) + (
|
||||
(x0[v_I_pad(i - 1, j, k, n)] +
|
||||
x0[v_I_pad(i + 1, j, k, n)] +
|
||||
x0[v_I_pad(i, j - 1, k, n)] +
|
||||
x0[v_I_pad(i, j + 1, k, n)] +
|
||||
x0[v_I_pad(i, j, k - 1, n)] +
|
||||
x0[v_I_pad(i, j, k + 1, n)]) * a) / (1 + 6 * a));
|
||||
}
|
||||
}
|
||||
|
||||
if (do_test_break && re->test_break(re->tbh)) break;
|
||||
}
|
||||
|
||||
if (re->test_break(re->tbh)) break;
|
||||
}
|
||||
}
|
||||
|
||||
static void multiple_scattering_diffusion(Render *re, VolumePrecache *vp, Material *ma)
|
||||
{
|
||||
const float diff = ma->vol.ms_diff * 0.001f; /* compensate for scaling for a nicer UI range */
|
||||
const int simframes = (int)(ma->vol.ms_spread * (float)max_iii(vp->res[0], vp->res[1], vp->res[2]));
|
||||
const int shade_type = ma->vol.shade_type;
|
||||
float fac = ma->vol.ms_intensity;
|
||||
|
||||
int x, y, z, m;
|
||||
const int *n = vp->res;
|
||||
const int size = (n[0]+2)*(n[1]+2)*(n[2]+2);
|
||||
const int do_test_break = (size > 100000);
|
||||
double time, lasttime= PIL_check_seconds_timer();
|
||||
float total;
|
||||
float c=1.0f;
|
||||
float origf; /* factor for blending in original light cache */
|
||||
float energy_ss, energy_ms;
|
||||
|
||||
float *sr0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
|
||||
float *sr=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
|
||||
float *sg0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
|
||||
float *sg=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
|
||||
float *sb0=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
|
||||
float *sb=(float *)MEM_callocN(size*sizeof(float), "temporary multiple scattering buffer");
|
||||
|
||||
total = (float)(n[0]*n[1]*n[2]*simframes);
|
||||
|
||||
energy_ss = total_ss_energy(re, do_test_break, vp);
|
||||
|
||||
/* Scattering as diffusion pass */
|
||||
for (m=0; m<simframes; m++) {
|
||||
/* add sources */
|
||||
for (z=1; z<=n[2]; z++) {
|
||||
for (y=1; y<=n[1]; y++) {
|
||||
for (x=1; x<=n[0]; x++) {
|
||||
const int64_t i = lc_to_ms_I(x, y, z, n); //lc index
|
||||
const int64_t j = ms_I(x, y, z, n); //ms index
|
||||
|
||||
time= PIL_check_seconds_timer();
|
||||
c++;
|
||||
if (vp->data_r[i] > 0.0f)
|
||||
sr[j] += vp->data_r[i];
|
||||
if (vp->data_g[i] > 0.0f)
|
||||
sg[j] += vp->data_g[i];
|
||||
if (vp->data_b[i] > 0.0f)
|
||||
sb[j] += vp->data_b[i];
|
||||
|
||||
/* Displays progress every second */
|
||||
if (time-lasttime>1.0) {
|
||||
char str[64];
|
||||
BLI_snprintf(str, sizeof(str), IFACE_("Simulating multiple scattering: %d%%"),
|
||||
(int)(100.0f * (c / total)));
|
||||
re->i.infostr = str;
|
||||
re->stats_draw(re->sdh, &re->i);
|
||||
re->i.infostr = NULL;
|
||||
lasttime= time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (do_test_break && re->test_break(re->tbh)) break;
|
||||
}
|
||||
|
||||
if (re->test_break(re->tbh)) break;
|
||||
|
||||
SWAP(float *, sr, sr0);
|
||||
SWAP(float *, sg, sg0);
|
||||
SWAP(float *, sb, sb0);
|
||||
|
||||
/* main diffusion simulation */
|
||||
ms_diffuse(re, do_test_break, sr0, sr, diff, n);
|
||||
ms_diffuse(re, do_test_break, sg0, sg, diff, n);
|
||||
ms_diffuse(re, do_test_break, sb0, sb, diff, n);
|
||||
|
||||
if (re->test_break(re->tbh)) break;
|
||||
}
|
||||
|
||||
/* normalization factor to conserve energy */
|
||||
energy_ms = total_ms_energy(re, do_test_break, sr, sg, sb, n);
|
||||
fac *= (energy_ss / energy_ms);
|
||||
|
||||
/* blend multiple scattering back in the light cache */
|
||||
if (shade_type == MA_VOL_SHADE_SHADEDPLUSMULTIPLE) {
|
||||
/* conserve energy - half single, half multiple */
|
||||
origf = 0.5f;
|
||||
fac *= 0.5f;
|
||||
}
|
||||
else {
|
||||
origf = 0.0f;
|
||||
}
|
||||
|
||||
for (z=1;z<=n[2];z++) {
|
||||
for (y=1;y<=n[1];y++) {
|
||||
for (x=1;x<=n[0];x++) {
|
||||
const int64_t i = lc_to_ms_I(x, y, z, n); //lc index
|
||||
const int64_t j = ms_I(x, y, z, n); //ms index
|
||||
|
||||
vp->data_r[i] = origf * vp->data_r[i] + fac * sr[j];
|
||||
vp->data_g[i] = origf * vp->data_g[i] + fac * sg[j];
|
||||
vp->data_b[i] = origf * vp->data_b[i] + fac * sb[j];
|
||||
}
|
||||
}
|
||||
|
||||
if (do_test_break && re->test_break(re->tbh)) break;
|
||||
}
|
||||
|
||||
MEM_freeN(sr0);
|
||||
MEM_freeN(sr);
|
||||
MEM_freeN(sg0);
|
||||
MEM_freeN(sg);
|
||||
MEM_freeN(sb0);
|
||||
MEM_freeN(sb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if 0 /* debug stuff */
|
||||
static void *vol_precache_part_test(void *data)
|
||||
{
|
||||
VolPrecachePart *pa = data;
|
||||
|
||||
printf("part number: %d\n", pa->num);
|
||||
printf("done: %d\n", pa->done);
|
||||
printf("x min: %d x max: %d\n", pa->minx, pa->maxx);
|
||||
printf("y min: %d y max: %d\n", pa->miny, pa->maxy);
|
||||
printf("z min: %d z max: %d\n", pa->minz, pa->maxz);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Iterate over the 3d voxel grid, and fill the voxels with scattering information
|
||||
*
|
||||
* It's stored in memory as 3 big float grids next to each other, one for each RGB channel.
|
||||
* I'm guessing the memory alignment may work out better this way for the purposes
|
||||
* of doing linear interpolation, but I haven't actually tested this theory! :)
|
||||
*/
|
||||
typedef struct VolPrecacheState {
|
||||
double lasttime;
|
||||
unsigned int doneparts;
|
||||
unsigned int totparts;
|
||||
} VolPrecacheState;
|
||||
|
||||
static void vol_precache_part(TaskPool * __restrict pool, void *taskdata, int UNUSED(threadid))
|
||||
{
|
||||
VolPrecacheState *state = (VolPrecacheState *)BLI_task_pool_userdata(pool);
|
||||
VolPrecachePart *pa = (VolPrecachePart *)taskdata;
|
||||
Render *re = pa->re;
|
||||
|
||||
ObjectInstanceRen *obi = pa->obi;
|
||||
RayObject *tree = pa->tree;
|
||||
ShadeInput *shi = pa->shi;
|
||||
float scatter_col[3] = {0.f, 0.f, 0.f};
|
||||
float co[3], cco[3], view[3];
|
||||
int x, y, z;
|
||||
int res[3];
|
||||
double time;
|
||||
|
||||
if (re->test_break && re->test_break(re->tbh))
|
||||
return;
|
||||
|
||||
//printf("thread id %d\n", threadid);
|
||||
|
||||
res[0]= pa->res[0];
|
||||
res[1]= pa->res[1];
|
||||
res[2]= pa->res[2];
|
||||
|
||||
for (z= pa->minz; z < pa->maxz; z++) {
|
||||
co[2] = pa->bbmin[2] + (pa->voxel[2] * (z + 0.5f));
|
||||
|
||||
for (y= pa->miny; y < pa->maxy; y++) {
|
||||
co[1] = pa->bbmin[1] + (pa->voxel[1] * (y + 0.5f));
|
||||
|
||||
for (x=pa->minx; x < pa->maxx; x++) {
|
||||
int64_t i;
|
||||
co[0] = pa->bbmin[0] + (pa->voxel[0] * (x + 0.5f));
|
||||
|
||||
if (re->test_break && re->test_break(re->tbh))
|
||||
break;
|
||||
|
||||
/* convert from world->camera space for shading */
|
||||
mul_v3_m4v3(cco, pa->viewmat, co);
|
||||
|
||||
i = BLI_VOXEL_INDEX(x, y, z, res);
|
||||
|
||||
/* don't bother if the point is not inside the volume mesh */
|
||||
if (!point_inside_obi(tree, obi, cco)) {
|
||||
obi->volume_precache->data_r[i] = -1.0f;
|
||||
obi->volume_precache->data_g[i] = -1.0f;
|
||||
obi->volume_precache->data_b[i] = -1.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
copy_v3_v3(view, cco);
|
||||
normalize_v3(view);
|
||||
vol_get_scattering(shi, scatter_col, cco, view);
|
||||
|
||||
obi->volume_precache->data_r[i] = scatter_col[0];
|
||||
obi->volume_precache->data_g[i] = scatter_col[1];
|
||||
obi->volume_precache->data_b[i] = scatter_col[2];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int doneparts = atomic_add_and_fetch_u(&state->doneparts, 1);
|
||||
|
||||
time = PIL_check_seconds_timer();
|
||||
if (time - state->lasttime > 1.0) {
|
||||
ThreadMutex *mutex = BLI_task_pool_user_mutex(pool);
|
||||
|
||||
if (BLI_mutex_trylock(mutex)) {
|
||||
char str[64];
|
||||
float ratio = (float)doneparts/(float)state->totparts;
|
||||
BLI_snprintf(str, sizeof(str), IFACE_("Precaching volume: %d%%"), (int)(100.0f * ratio));
|
||||
re->i.infostr = str;
|
||||
re->stats_draw(re->sdh, &re->i);
|
||||
re->i.infostr = NULL;
|
||||
state->lasttime = time;
|
||||
|
||||
BLI_mutex_unlock(mutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void precache_setup_shadeinput(Render *re, ObjectInstanceRen *obi, Material *ma, ShadeInput *shi)
|
||||
{
|
||||
memset(shi, 0, sizeof(ShadeInput));
|
||||
shi->depth= 1;
|
||||
shi->mask= 1;
|
||||
shi->mat = ma;
|
||||
shi->vlr = NULL;
|
||||
memcpy(&shi->r, &shi->mat->r, 23*sizeof(float)); /* note, keep this synced with render_types.h */
|
||||
shi->har= shi->mat->har;
|
||||
shi->obi= obi;
|
||||
shi->obr= obi->obr;
|
||||
shi->lay = re->lay;
|
||||
}
|
||||
|
||||
static void precache_launch_parts(Render *re, RayObject *tree, ShadeInput *shi, ObjectInstanceRen *obi)
|
||||
{
|
||||
TaskScheduler *task_scheduler;
|
||||
TaskPool *task_pool;
|
||||
VolumePrecache *vp = obi->volume_precache;
|
||||
VolPrecacheState state;
|
||||
int i=0, x, y, z;
|
||||
float voxel[3];
|
||||
int sizex, sizey, sizez;
|
||||
float bbmin[3], bbmax[3];
|
||||
const int *res;
|
||||
int minx, maxx;
|
||||
int miny, maxy;
|
||||
int minz, maxz;
|
||||
int totthread = re->r.threads;
|
||||
int parts[3];
|
||||
|
||||
if (!vp) return;
|
||||
|
||||
/* currently we just subdivide the box, number of threads per side */
|
||||
parts[0] = parts[1] = parts[2] = totthread;
|
||||
res = vp->res;
|
||||
|
||||
/* setup task scheduler */
|
||||
memset(&state, 0, sizeof(state));
|
||||
state.doneparts = 0;
|
||||
state.totparts = parts[0]*parts[1]*parts[2];
|
||||
state.lasttime = PIL_check_seconds_timer();
|
||||
|
||||
task_scheduler = BLI_task_scheduler_create(totthread);
|
||||
task_pool = BLI_task_pool_create(task_scheduler, &state);
|
||||
|
||||
/* using boundbox in worldspace */
|
||||
global_bounds_obi(re, obi, bbmin, bbmax);
|
||||
sub_v3_v3v3(voxel, bbmax, bbmin);
|
||||
|
||||
voxel[0] /= (float)res[0];
|
||||
voxel[1] /= (float)res[1];
|
||||
voxel[2] /= (float)res[2];
|
||||
|
||||
for (x=0; x < parts[0]; x++) {
|
||||
sizex = ceil(res[0] / (float)parts[0]);
|
||||
minx = x * sizex;
|
||||
maxx = minx + sizex;
|
||||
maxx = (maxx>res[0])?res[0]:maxx;
|
||||
|
||||
for (y=0; y < parts[1]; y++) {
|
||||
sizey = ceil(res[1] / (float)parts[1]);
|
||||
miny = y * sizey;
|
||||
maxy = miny + sizey;
|
||||
maxy = (maxy>res[1])?res[1]:maxy;
|
||||
|
||||
for (z=0; z < parts[2]; z++) {
|
||||
VolPrecachePart *pa= MEM_callocN(sizeof(VolPrecachePart), "new precache part");
|
||||
|
||||
sizez = ceil(res[2] / (float)parts[2]);
|
||||
minz = z * sizez;
|
||||
maxz = minz + sizez;
|
||||
maxz = (maxz>res[2])?res[2]:maxz;
|
||||
|
||||
pa->re = re;
|
||||
pa->num = i;
|
||||
pa->tree = tree;
|
||||
pa->shi = shi;
|
||||
pa->obi = obi;
|
||||
copy_m4_m4(pa->viewmat, re->viewmat);
|
||||
|
||||
copy_v3_v3(pa->bbmin, bbmin);
|
||||
copy_v3_v3(pa->voxel, voxel);
|
||||
copy_v3_v3_int(pa->res, res);
|
||||
|
||||
pa->minx = minx; pa->maxx = maxx;
|
||||
pa->miny = miny; pa->maxy = maxy;
|
||||
pa->minz = minz; pa->maxz = maxz;
|
||||
|
||||
BLI_task_pool_push(task_pool, vol_precache_part, pa, true, TASK_PRIORITY_HIGH);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* work and wait until tasks are done */
|
||||
BLI_task_pool_work_and_wait(task_pool);
|
||||
|
||||
/* free */
|
||||
BLI_task_pool_free(task_pool);
|
||||
BLI_task_scheduler_free(task_scheduler);
|
||||
}
|
||||
|
||||
/* calculate resolution from bounding box in world space */
|
||||
static int precache_resolution(Render *re, VolumePrecache *vp, ObjectInstanceRen *obi, int res)
|
||||
{
|
||||
float dim[3], div;
|
||||
float bbmin[3], bbmax[3];
|
||||
|
||||
/* bound box in global space */
|
||||
global_bounds_obi(re, obi, bbmin, bbmax);
|
||||
sub_v3_v3v3(dim, bbmax, bbmin);
|
||||
|
||||
div = max_fff(dim[0], dim[1], dim[2]);
|
||||
dim[0] /= div;
|
||||
dim[1] /= div;
|
||||
dim[2] /= div;
|
||||
|
||||
vp->res[0] = ceil(dim[0] * res);
|
||||
vp->res[1] = ceil(dim[1] * res);
|
||||
vp->res[2] = ceil(dim[2] * res);
|
||||
|
||||
if ((vp->res[0] < 1) || (vp->res[1] < 1) || (vp->res[2] < 1))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Precache a volume into a 3D voxel grid.
|
||||
* The voxel grid is stored in the ObjectInstanceRen,
|
||||
* in camera space, aligned with the ObjectRen's bounding box.
|
||||
* Resolution is defined by the user.
|
||||
*/
|
||||
static void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Material *ma)
|
||||
{
|
||||
VolumePrecache *vp;
|
||||
RayObject *tree;
|
||||
ShadeInput shi;
|
||||
|
||||
R = *re;
|
||||
|
||||
/* create a raytree with just the faces of the instanced ObjectRen,
|
||||
* used for checking if the cached point is inside or outside. */
|
||||
tree = makeraytree_object(&R, obi);
|
||||
if (!tree) return;
|
||||
|
||||
vp = MEM_callocN(sizeof(VolumePrecache), "volume light cache");
|
||||
obi->volume_precache = vp;
|
||||
|
||||
if (!precache_resolution(re, vp, obi, ma->vol.precache_resolution)) {
|
||||
MEM_freeN(vp);
|
||||
vp = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
vp->data_r = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data red channel");
|
||||
vp->data_g = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data green channel");
|
||||
vp->data_b = MEM_callocN(sizeof(float)*vp->res[0]*vp->res[1]*vp->res[2], "volume light cache data blue channel");
|
||||
if (vp->data_r==NULL || vp->data_g==NULL || vp->data_b==NULL) {
|
||||
MEM_freeN(vp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Need a shadeinput to calculate scattering */
|
||||
precache_setup_shadeinput(re, obi, ma, &shi);
|
||||
|
||||
precache_launch_parts(re, tree, &shi, obi);
|
||||
|
||||
if (tree) {
|
||||
/* TODO: makeraytree_object creates a tree and saves it on OBI,
|
||||
* if we free this tree we should also clear other pointers to it */
|
||||
//RE_rayobject_free(tree);
|
||||
//tree= NULL;
|
||||
}
|
||||
|
||||
if (ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE)) {
|
||||
/* this should be before the filtering */
|
||||
multiple_scattering_diffusion(re, obi->volume_precache, ma);
|
||||
}
|
||||
|
||||
lightcache_filter(obi->volume_precache);
|
||||
}
|
||||
|
||||
static int using_lightcache(Material *ma)
|
||||
{
|
||||
return (((ma->vol.shadeflag & MA_VOL_PRECACHESHADING) && (ma->vol.shade_type == MA_VOL_SHADE_SHADED)) ||
|
||||
(ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE)));
|
||||
}
|
||||
|
||||
/* loop through all objects (and their associated materials)
|
||||
* marked for pre-caching in convertblender.c, and pre-cache them */
|
||||
void volume_precache(Render *re)
|
||||
{
|
||||
ObjectInstanceRen *obi;
|
||||
VolumeOb *vo;
|
||||
|
||||
re->i.infostr = IFACE_("Volume preprocessing");
|
||||
re->stats_draw(re->sdh, &re->i);
|
||||
|
||||
for (vo= re->volumes.first; vo; vo= vo->next) {
|
||||
if (using_lightcache(vo->ma)) {
|
||||
for (obi= re->instancetable.first; obi; obi= obi->next) {
|
||||
if (obi->obr == vo->obr) {
|
||||
vol_precache_objectinstance_threads(re, obi, vo->ma);
|
||||
|
||||
if (re->test_break && re->test_break(re->tbh))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (re->test_break && re->test_break(re->tbh))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
re->i.infostr = NULL;
|
||||
re->stats_draw(re->sdh, &re->i);
|
||||
}
|
||||
|
||||
void free_volume_precache(Render *re)
|
||||
{
|
||||
ObjectInstanceRen *obi;
|
||||
|
||||
for (obi= re->instancetable.first; obi; obi= obi->next) {
|
||||
if (obi->volume_precache != NULL) {
|
||||
MEM_freeN(obi->volume_precache->data_r);
|
||||
MEM_freeN(obi->volume_precache->data_g);
|
||||
MEM_freeN(obi->volume_precache->data_b);
|
||||
MEM_freeN(obi->volume_precache->bbmin);
|
||||
MEM_freeN(obi->volume_precache->bbmax);
|
||||
MEM_freeN(obi->volume_precache);
|
||||
obi->volume_precache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_freelistN(&re->volumes);
|
||||
}
|
||||
|
||||
int point_inside_volume_objectinstance(Render *re, ObjectInstanceRen *obi, const float co[3])
|
||||
{
|
||||
RayObject *tree;
|
||||
int inside=0;
|
||||
|
||||
tree = makeraytree_object(re, obi);
|
||||
if (!tree) return 0;
|
||||
|
||||
inside = point_inside_obi(tree, obi, co);
|
||||
|
||||
//TODO: makeraytree_object creates a tree and saves it on OBI, if we free this tree we should also clear other pointers to it
|
||||
//RE_rayobject_free(tree);
|
||||
//tree= NULL;
|
||||
|
||||
return inside;
|
||||
}
|
||||
|
836
source/blender/render/intern/source/volumetric.c
Normal file
836
source/blender/render/intern/source/volumetric.c
Normal file
@@ -0,0 +1,836 @@
|
||||
/*
|
||||
* ***** 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) 2001-2002 by NaN Holding BV.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): Matt Ebb, Raul Fernandez Hernandez (Farsthary)
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/render/intern/source/volumetric.c
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <float.h>
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_rand.h"
|
||||
#include "BLI_voxel.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "RE_shader_ext.h"
|
||||
|
||||
#include "IMB_colormanagement.h"
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_group_types.h"
|
||||
#include "DNA_lamp_types.h"
|
||||
#include "DNA_meta_types.h"
|
||||
|
||||
|
||||
#include "render_types.h"
|
||||
#include "pixelshading.h"
|
||||
#include "rayintersection.h"
|
||||
#include "rayobject.h"
|
||||
#include "renderdatabase.h"
|
||||
#include "shading.h"
|
||||
#include "shadbuf.h"
|
||||
#include "texture.h"
|
||||
#include "volumetric.h"
|
||||
#include "volume_precache.h"
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
|
||||
/* only to be used here in this file, it's for speed */
|
||||
extern struct Render R;
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
/* tracing */
|
||||
static float vol_get_shadow(ShadeInput *shi, LampRen *lar, const float co[3])
|
||||
{
|
||||
float visibility = 1.f;
|
||||
|
||||
if (lar->shb) {
|
||||
float dxco[3] = {0.f, 0.f, 0.f}, dyco[3] = {0.f, 0.f, 0.f};
|
||||
|
||||
visibility = testshadowbuf(&R, lar->shb, co, dxco, dyco, 1.0, 0.0);
|
||||
}
|
||||
else if (lar->mode & LA_SHAD_RAY) {
|
||||
/* trace shadow manually, no good lamp api atm */
|
||||
Isect is;
|
||||
|
||||
copy_v3_v3(is.start, co);
|
||||
if (lar->type == LA_SUN || lar->type == LA_HEMI) {
|
||||
is.dir[0] = -lar->vec[0];
|
||||
is.dir[1] = -lar->vec[1];
|
||||
is.dir[2] = -lar->vec[2];
|
||||
is.dist = R.maxdist;
|
||||
}
|
||||
else {
|
||||
sub_v3_v3v3(is.dir, lar->co, is.start);
|
||||
is.dist = normalize_v3(is.dir);
|
||||
}
|
||||
|
||||
is.mode = RE_RAY_MIRROR;
|
||||
is.check = RE_CHECK_VLR_NON_SOLID_MATERIAL;
|
||||
is.skip = 0;
|
||||
|
||||
if (lar->mode & (LA_LAYER | LA_LAYER_SHADOW))
|
||||
is.lay = lar->lay;
|
||||
else
|
||||
is.lay = -1;
|
||||
|
||||
is.orig.ob = NULL;
|
||||
is.orig.face = NULL;
|
||||
is.last_hit = lar->last_hit[shi->thread];
|
||||
|
||||
RE_instance_rotate_ray(shi->obi, &is);
|
||||
|
||||
if (RE_rayobject_raycast(R.raytree, &is)) {
|
||||
RE_instance_rotate_ray_restore(shi->obi, &is);
|
||||
|
||||
visibility = 0.f;
|
||||
}
|
||||
|
||||
lar->last_hit[shi->thread] = is.last_hit;
|
||||
}
|
||||
return visibility;
|
||||
}
|
||||
|
||||
static int vol_get_bounds(ShadeInput *shi, const float co[3], const float vec[3], float hitco[3], Isect *isect, int intersect_type)
|
||||
{
|
||||
|
||||
copy_v3_v3(isect->start, co);
|
||||
copy_v3_v3(isect->dir, vec);
|
||||
isect->dist = FLT_MAX;
|
||||
isect->mode = RE_RAY_MIRROR;
|
||||
isect->last_hit = NULL;
|
||||
isect->lay = -1;
|
||||
isect->check = RE_CHECK_VLR_NONE;
|
||||
|
||||
if (intersect_type == VOL_BOUNDS_DEPTH) {
|
||||
isect->skip = RE_SKIP_VLR_NEIGHBOUR;
|
||||
isect->orig.face = (void *)shi->vlr;
|
||||
isect->orig.ob = (void *)shi->obi;
|
||||
}
|
||||
else { // if (intersect_type == VOL_BOUNDS_SS) {
|
||||
isect->skip = 0;
|
||||
isect->orig.face = NULL;
|
||||
isect->orig.ob = NULL;
|
||||
}
|
||||
|
||||
RE_instance_rotate_ray(shi->obi, isect);
|
||||
|
||||
if (RE_rayobject_raycast(R.raytree, isect)) {
|
||||
RE_instance_rotate_ray_restore(shi->obi, isect);
|
||||
|
||||
hitco[0] = isect->start[0] + isect->dist * isect->dir[0];
|
||||
hitco[1] = isect->start[1] + isect->dist * isect->dir[1];
|
||||
hitco[2] = isect->start[2] + isect->dist * isect->dir[2];
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void shade_intersection(ShadeInput *shi, float col_r[4], Isect *is)
|
||||
{
|
||||
ShadeInput shi_new;
|
||||
ShadeResult shr_new;
|
||||
|
||||
memset(&shi_new, 0, sizeof(ShadeInput));
|
||||
|
||||
shi_new.mask = shi->mask;
|
||||
shi_new.osatex = shi->osatex;
|
||||
shi_new.thread = shi->thread;
|
||||
shi_new.depth = shi->depth + 1;
|
||||
shi_new.volume_depth = shi->volume_depth + 1;
|
||||
shi_new.xs = shi->xs;
|
||||
shi_new.ys = shi->ys;
|
||||
shi_new.lay = shi->lay;
|
||||
shi_new.passflag = SCE_PASS_COMBINED; /* result of tracing needs no pass info */
|
||||
shi_new.combinedflag = 0xFFFFFF; /* ray trace does all options */
|
||||
shi_new.light_override = shi->light_override;
|
||||
shi_new.mat_override = shi->mat_override;
|
||||
|
||||
copy_v3_v3(shi_new.camera_co, is->start);
|
||||
|
||||
memset(&shr_new, 0, sizeof(ShadeResult));
|
||||
|
||||
/* hardcoded limit of 100 for now - prevents problems in weird geometry */
|
||||
if (shi->volume_depth < 100) {
|
||||
shade_ray(is, &shi_new, &shr_new);
|
||||
}
|
||||
|
||||
copy_v3_v3(col_r, shr_new.combined);
|
||||
col_r[3] = shr_new.alpha;
|
||||
}
|
||||
|
||||
static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, const float co[3], float col_r[4])
|
||||
{
|
||||
Isect isect;
|
||||
|
||||
copy_v3_v3(isect.start, co);
|
||||
copy_v3_v3(isect.dir, shi->view);
|
||||
isect.dist = FLT_MAX;
|
||||
|
||||
isect.mode = RE_RAY_MIRROR;
|
||||
isect.check = RE_CHECK_VLR_NONE;
|
||||
isect.skip = RE_SKIP_VLR_NEIGHBOUR;
|
||||
isect.orig.ob = (void *) shi->obi;
|
||||
isect.orig.face = (void *)vlr;
|
||||
isect.last_hit = NULL;
|
||||
isect.lay = -1;
|
||||
|
||||
/* check to see if there's anything behind the volume, otherwise shade the sky */
|
||||
RE_instance_rotate_ray(shi->obi, &isect);
|
||||
|
||||
if (RE_rayobject_raycast(R.raytree, &isect)) {
|
||||
RE_instance_rotate_ray_restore(shi->obi, &isect);
|
||||
|
||||
shade_intersection(shi, col_r, &isect);
|
||||
}
|
||||
else {
|
||||
shadeSkyView(col_r, co, shi->view, NULL, shi->thread);
|
||||
shadeSunView(col_r, shi->view);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* trilinear interpolation */
|
||||
static void vol_get_precached_scattering(Render *re, ShadeInput *shi, float scatter_col[3], const float co[3])
|
||||
{
|
||||
VolumePrecache *vp = shi->obi->volume_precache;
|
||||
float bbmin[3], bbmax[3], dim[3];
|
||||
float world_co[3], sample_co[3];
|
||||
|
||||
if (!vp) return;
|
||||
|
||||
/* find sample point in global space bounding box 0.0-1.0 */
|
||||
global_bounds_obi(re, shi->obi, bbmin, bbmax);
|
||||
sub_v3_v3v3(dim, bbmax, bbmin);
|
||||
mul_v3_m4v3(world_co, re->viewinv, co);
|
||||
|
||||
/* sample_co in 0.0-1.0 */
|
||||
sample_co[0] = (world_co[0] - bbmin[0]) / dim[0];
|
||||
sample_co[1] = (world_co[1] - bbmin[1]) / dim[1];
|
||||
sample_co[2] = (world_co[2] - bbmin[2]) / dim[2];
|
||||
|
||||
scatter_col[0] = BLI_voxel_sample_triquadratic(vp->data_r, vp->res, sample_co);
|
||||
scatter_col[1] = BLI_voxel_sample_triquadratic(vp->data_g, vp->res, sample_co);
|
||||
scatter_col[2] = BLI_voxel_sample_triquadratic(vp->data_b, vp->res, sample_co);
|
||||
}
|
||||
|
||||
/* Meta object density, brute force for now
|
||||
* (might be good enough anyway, don't need huge number of metaobs to model volumetric objects */
|
||||
static float metadensity(Object *ob, const float co[3])
|
||||
{
|
||||
float mat[4][4], imat[4][4], dens = 0.f;
|
||||
MetaBall *mb = (MetaBall *)ob->data;
|
||||
MetaElem *ml;
|
||||
|
||||
/* transform co to meta-element */
|
||||
float tco[3] = {co[0], co[1], co[2]};
|
||||
mul_m4_m4m4(mat, R.viewmat, ob->obmat);
|
||||
invert_m4_m4(imat, mat);
|
||||
mul_m4_v3(imat, tco);
|
||||
|
||||
for (ml = mb->elems.first; ml; ml = ml->next) {
|
||||
float bmat[3][3], dist2;
|
||||
|
||||
/* element rotation transform */
|
||||
float tp[3] = {ml->x - tco[0], ml->y - tco[1], ml->z - tco[2]};
|
||||
quat_to_mat3(bmat, ml->quat);
|
||||
transpose_m3(bmat); /* rot.only, so inverse == transpose */
|
||||
mul_m3_v3(bmat, tp);
|
||||
|
||||
/* MB_BALL default */
|
||||
switch (ml->type) {
|
||||
case MB_ELIPSOID:
|
||||
tp[0] /= ml->expx;
|
||||
tp[1] /= ml->expy;
|
||||
tp[2] /= ml->expz;
|
||||
break;
|
||||
case MB_CUBE:
|
||||
tp[2] = (tp[2] > ml->expz) ? (tp[2] - ml->expz) : ((tp[2] < -ml->expz) ? (tp[2] + ml->expz) : 0.f);
|
||||
/* no break, xy as plane */
|
||||
ATTR_FALLTHROUGH;
|
||||
case MB_PLANE:
|
||||
tp[1] = (tp[1] > ml->expy) ? (tp[1] - ml->expy) : ((tp[1] < -ml->expy) ? (tp[1] + ml->expy) : 0.f);
|
||||
/* no break, x as tube */
|
||||
ATTR_FALLTHROUGH;
|
||||
case MB_TUBE:
|
||||
tp[0] = (tp[0] > ml->expx) ? (tp[0] - ml->expx) : ((tp[0] < -ml->expx) ? (tp[0] + ml->expx) : 0.f);
|
||||
}
|
||||
|
||||
/* ml->rad2 is not set */
|
||||
dist2 = 1.0f - (dot_v3v3(tp, tp) / (ml->rad * ml->rad));
|
||||
if (dist2 > 0.f)
|
||||
dens += (ml->flag & MB_NEGATIVE) ? -ml->s * dist2 * dist2 * dist2 : ml->s * dist2 * dist2 * dist2;
|
||||
}
|
||||
|
||||
dens -= mb->thresh;
|
||||
return (dens < 0.f) ? 0.f : dens;
|
||||
}
|
||||
|
||||
float vol_get_density(struct ShadeInput *shi, const float co[3])
|
||||
{
|
||||
float density = shi->mat->vol.density;
|
||||
float density_scale = shi->mat->vol.density_scale;
|
||||
|
||||
if (shi->mat->mapto_textured & MAP_DENSITY)
|
||||
do_volume_tex(shi, co, MAP_DENSITY, NULL, &density, &R);
|
||||
|
||||
/* if meta-object, modulate by metadensity without increasing it */
|
||||
if (shi->obi->obr->ob->type == OB_MBALL) {
|
||||
const float md = metadensity(shi->obi->obr->ob, co);
|
||||
if (md < 1.f) density *= md;
|
||||
}
|
||||
|
||||
return density * density_scale;
|
||||
}
|
||||
|
||||
|
||||
/* Color of light that gets scattered out by the volume */
|
||||
/* Uses same physically based scattering parameter as in transmission calculations,
|
||||
* along with artificial reflection scale/reflection color tint */
|
||||
static void vol_get_reflection_color(ShadeInput *shi, float ref_col[3], const float co[3])
|
||||
{
|
||||
float scatter = shi->mat->vol.scattering;
|
||||
float reflection = shi->mat->vol.reflection;
|
||||
copy_v3_v3(ref_col, shi->mat->vol.reflection_col);
|
||||
|
||||
if (shi->mat->mapto_textured & (MAP_SCATTERING + MAP_REFLECTION_COL))
|
||||
do_volume_tex(shi, co, MAP_SCATTERING + MAP_REFLECTION_COL, ref_col, &scatter, &R);
|
||||
|
||||
/* only one single float parameter at a time... :s */
|
||||
if (shi->mat->mapto_textured & (MAP_REFLECTION))
|
||||
do_volume_tex(shi, co, MAP_REFLECTION, NULL, &reflection, &R);
|
||||
|
||||
ref_col[0] = reflection * ref_col[0] * scatter;
|
||||
ref_col[1] = reflection * ref_col[1] * scatter;
|
||||
ref_col[2] = reflection * ref_col[2] * scatter;
|
||||
}
|
||||
|
||||
/* compute emission component, amount of radiance to add per segment
|
||||
* can be textured with 'emit' */
|
||||
static void vol_get_emission(ShadeInput *shi, float emission_col[3], const float co[3])
|
||||
{
|
||||
float emission = shi->mat->vol.emission;
|
||||
copy_v3_v3(emission_col, shi->mat->vol.emission_col);
|
||||
|
||||
if (shi->mat->mapto_textured & (MAP_EMISSION + MAP_EMISSION_COL))
|
||||
do_volume_tex(shi, co, MAP_EMISSION + MAP_EMISSION_COL, emission_col, &emission, &R);
|
||||
|
||||
emission_col[0] = emission_col[0] * emission;
|
||||
emission_col[1] = emission_col[1] * emission;
|
||||
emission_col[2] = emission_col[2] * emission;
|
||||
}
|
||||
|
||||
|
||||
/* A combination of scattering and absorption -> known as sigma T.
|
||||
* This can possibly use a specific scattering color,
|
||||
* and absorption multiplier factor too, but these parameters are left out for simplicity.
|
||||
* It's easy enough to get a good wide range of results with just these two parameters. */
|
||||
static void vol_get_sigma_t(ShadeInput *shi, float sigma_t[3], const float co[3])
|
||||
{
|
||||
/* technically absorption, but named transmission color
|
||||
* since it describes the effect of the coloring *after* absorption */
|
||||
float transmission_col[3] = {shi->mat->vol.transmission_col[0], shi->mat->vol.transmission_col[1], shi->mat->vol.transmission_col[2]};
|
||||
float scattering = shi->mat->vol.scattering;
|
||||
|
||||
if (shi->mat->mapto_textured & (MAP_SCATTERING + MAP_TRANSMISSION_COL))
|
||||
do_volume_tex(shi, co, MAP_SCATTERING + MAP_TRANSMISSION_COL, transmission_col, &scattering, &R);
|
||||
|
||||
sigma_t[0] = (1.0f - transmission_col[0]) + scattering;
|
||||
sigma_t[1] = (1.0f - transmission_col[1]) + scattering;
|
||||
sigma_t[2] = (1.0f - transmission_col[2]) + scattering;
|
||||
}
|
||||
|
||||
/* phase function - determines in which directions the light
|
||||
* is scattered in the volume relative to incoming direction
|
||||
* and view direction */
|
||||
static float vol_get_phasefunc(ShadeInput *UNUSED(shi), float g, const float w[3], const float wp[3])
|
||||
{
|
||||
const float normalize = 0.25f; // = 1.f/4.f = M_PI/(4.f*M_PI)
|
||||
|
||||
/* normalization constant is 1/4 rather than 1/4pi, since
|
||||
* Blender's shading system doesn't normalize for
|
||||
* energy conservation - eg. multiplying by pdf ( 1/pi for a lambert brdf ).
|
||||
* This means that lambert surfaces in Blender are pi times brighter than they 'should be'
|
||||
* and therefore, with correct energy conservation, volumes will darker than other solid objects,
|
||||
* for the same lighting intensity.
|
||||
* To correct this, scale up the phase function values by pi
|
||||
* until Blender's shading system supports this better. --matt
|
||||
*/
|
||||
|
||||
if (g == 0.f) { /* isotropic */
|
||||
return normalize * 1.f;
|
||||
}
|
||||
else { /* schlick */
|
||||
const float k = 1.55f * g - 0.55f * g * g * g;
|
||||
const float kcostheta = k * dot_v3v3(w, wp);
|
||||
return normalize * (1.f - k * k) / ((1.f - kcostheta) * (1.f - kcostheta));
|
||||
}
|
||||
|
||||
/* not used, but here for reference: */
|
||||
#if 0
|
||||
switch (phasefunc_type) {
|
||||
case MA_VOL_PH_MIEHAZY:
|
||||
return normalize * (0.5f + 4.5f * powf(0.5 * (1.f + costheta), 8.f));
|
||||
case MA_VOL_PH_MIEMURKY:
|
||||
return normalize * (0.5f + 16.5f * powf(0.5 * (1.f + costheta), 32.f));
|
||||
case MA_VOL_PH_RAYLEIGH:
|
||||
return normalize * 3.f / 4.f * (1 + costheta * costheta);
|
||||
case MA_VOL_PH_HG:
|
||||
return normalize * (1.f - g * g) / powf(1.f + g * g - 2.f * g * costheta, 1.5f);
|
||||
case MA_VOL_PH_SCHLICK:
|
||||
{
|
||||
const float k = 1.55f * g - 0.55f * g * g * g;
|
||||
const float kcostheta = k * costheta;
|
||||
return normalize * (1.f - k * k) / ((1.f - kcostheta) * (1.f - kcostheta));
|
||||
}
|
||||
case MA_VOL_PH_ISOTROPIC:
|
||||
default:
|
||||
return normalize * 1.f;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Compute transmittance = e^(-attenuation) */
|
||||
static void vol_get_transmittance_seg(ShadeInput *shi, float tr[3], float stepsize, const float co[3], float density)
|
||||
{
|
||||
/* input density = density at co */
|
||||
float tau[3] = {0.f, 0.f, 0.f};
|
||||
const float stepd = density * stepsize;
|
||||
float sigma_t[3];
|
||||
|
||||
vol_get_sigma_t(shi, sigma_t, co);
|
||||
|
||||
/* homogeneous volume within the sampled distance */
|
||||
tau[0] += stepd * sigma_t[0];
|
||||
tau[1] += stepd * sigma_t[1];
|
||||
tau[2] += stepd * sigma_t[2];
|
||||
|
||||
tr[0] *= expf(-tau[0]);
|
||||
tr[1] *= expf(-tau[1]);
|
||||
tr[2] *= expf(-tau[2]);
|
||||
}
|
||||
|
||||
/* Compute transmittance = e^(-attenuation) */
|
||||
static void vol_get_transmittance(ShadeInput *shi, float tr[3], const float co[3], const float endco[3])
|
||||
{
|
||||
float p[3] = {co[0], co[1], co[2]};
|
||||
float step_vec[3] = {endco[0] - co[0], endco[1] - co[1], endco[2] - co[2]};
|
||||
float tau[3] = {0.f, 0.f, 0.f};
|
||||
|
||||
float t0 = 0.f;
|
||||
float t1 = normalize_v3(step_vec);
|
||||
float pt0 = t0;
|
||||
|
||||
t0 += shi->mat->vol.stepsize * ((shi->mat->vol.stepsize_type == MA_VOL_STEP_CONSTANT) ? 0.5f : BLI_thread_frand(shi->thread));
|
||||
p[0] += t0 * step_vec[0];
|
||||
p[1] += t0 * step_vec[1];
|
||||
p[2] += t0 * step_vec[2];
|
||||
mul_v3_fl(step_vec, shi->mat->vol.stepsize);
|
||||
|
||||
for (; t0 < t1; pt0 = t0, t0 += shi->mat->vol.stepsize) {
|
||||
const float d = vol_get_density(shi, p);
|
||||
const float stepd = (t0 - pt0) * d;
|
||||
float sigma_t[3];
|
||||
|
||||
vol_get_sigma_t(shi, sigma_t, p);
|
||||
|
||||
tau[0] += stepd * sigma_t[0];
|
||||
tau[1] += stepd * sigma_t[1];
|
||||
tau[2] += stepd * sigma_t[2];
|
||||
|
||||
add_v3_v3(p, step_vec);
|
||||
}
|
||||
|
||||
/* return transmittance */
|
||||
tr[0] = expf(-tau[0]);
|
||||
tr[1] = expf(-tau[1]);
|
||||
tr[2] = expf(-tau[2]);
|
||||
}
|
||||
|
||||
static void vol_shade_one_lamp(struct ShadeInput *shi, const float co[3], const float view[3], LampRen *lar, float lacol[3])
|
||||
{
|
||||
float visifac, lv[3], lampdist;
|
||||
float tr[3] = {1.0, 1.0, 1.0};
|
||||
float hitco[3], *atten_co;
|
||||
float p, ref_col[3];
|
||||
|
||||
if (lar->mode & LA_LAYER) if ((lar->lay & shi->obi->lay) == 0) return;
|
||||
if ((lar->lay & shi->lay) == 0) return;
|
||||
if (lar->energy == 0.0f) return;
|
||||
|
||||
if ((visifac = lamp_get_visibility(lar, co, lv, &lampdist)) == 0.f) return;
|
||||
|
||||
copy_v3_v3(lacol, &lar->r);
|
||||
|
||||
if (lar->mode & LA_TEXTURE) {
|
||||
shi->osatex = 0;
|
||||
do_lamp_tex(lar, lv, shi, lacol, LA_TEXTURE);
|
||||
}
|
||||
|
||||
mul_v3_fl(lacol, visifac);
|
||||
|
||||
if (ELEM(lar->type, LA_SUN, LA_HEMI))
|
||||
copy_v3_v3(lv, lar->vec);
|
||||
negate_v3(lv);
|
||||
|
||||
if (shi->mat->vol.shade_type == MA_VOL_SHADE_SHADOWED) {
|
||||
mul_v3_fl(lacol, vol_get_shadow(shi, lar, co));
|
||||
}
|
||||
else if (ELEM(shi->mat->vol.shade_type, MA_VOL_SHADE_SHADED, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE)) {
|
||||
Isect is;
|
||||
|
||||
if (shi->mat->vol.shadeflag & MA_VOL_RECV_EXT_SHADOW) {
|
||||
mul_v3_fl(lacol, vol_get_shadow(shi, lar, co));
|
||||
if (IMB_colormanagement_get_luminance(lacol) < 0.001f) return;
|
||||
}
|
||||
|
||||
/* find minimum of volume bounds, or lamp coord */
|
||||
if (vol_get_bounds(shi, co, lv, hitco, &is, VOL_BOUNDS_SS)) {
|
||||
float dist = len_v3v3(co, hitco);
|
||||
VlakRen *vlr = (VlakRen *)is.hit.face;
|
||||
|
||||
/* simple internal shadowing */
|
||||
if (vlr->mat->material_type == MA_TYPE_SURFACE) {
|
||||
lacol[0] = lacol[1] = lacol[2] = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ELEM(lar->type, LA_SUN, LA_HEMI))
|
||||
/* infinite lights, can never be inside volume */
|
||||
atten_co = hitco;
|
||||
else if (lampdist < dist) {
|
||||
atten_co = lar->co;
|
||||
}
|
||||
else
|
||||
atten_co = hitco;
|
||||
|
||||
vol_get_transmittance(shi, tr, co, atten_co);
|
||||
|
||||
mul_v3_v3v3(lacol, lacol, tr);
|
||||
}
|
||||
else {
|
||||
/* Point is on the outside edge of the volume,
|
||||
* therefore no attenuation, full transmission.
|
||||
* Radiance from lamp remains unchanged */
|
||||
}
|
||||
}
|
||||
|
||||
if (IMB_colormanagement_get_luminance(lacol) < 0.001f) return;
|
||||
|
||||
normalize_v3(lv);
|
||||
p = vol_get_phasefunc(shi, shi->mat->vol.asymmetry, view, lv);
|
||||
|
||||
/* physically based scattering with non-physically based RGB gain */
|
||||
vol_get_reflection_color(shi, ref_col, co);
|
||||
|
||||
lacol[0] *= p * ref_col[0];
|
||||
lacol[1] *= p * ref_col[1];
|
||||
lacol[2] *= p * ref_col[2];
|
||||
}
|
||||
|
||||
/* single scattering only for now */
|
||||
void vol_get_scattering(ShadeInput *shi, float scatter_col[3], const float co[3], const float view[3])
|
||||
{
|
||||
ListBase *lights;
|
||||
GroupObject *go;
|
||||
LampRen *lar;
|
||||
|
||||
zero_v3(scatter_col);
|
||||
|
||||
lights = get_lights(shi);
|
||||
for (go = lights->first; go; go = go->next) {
|
||||
float lacol[3] = {0.f, 0.f, 0.f};
|
||||
lar = go->lampren;
|
||||
|
||||
if (lar) {
|
||||
vol_shade_one_lamp(shi, co, view, lar, lacol);
|
||||
add_v3_v3(scatter_col, lacol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The main volumetric integrator, using an emission/absorption/scattering model.
|
||||
*
|
||||
* Incoming radiance =
|
||||
*
|
||||
* outgoing radiance from behind surface * beam transmittance/attenuation
|
||||
* + added radiance from all points along the ray due to participating media
|
||||
* --> radiance for each segment =
|
||||
* (radiance added by scattering + radiance added by emission) * beam transmittance/attenuation
|
||||
*/
|
||||
|
||||
/* For ease of use, I've also introduced a 'reflection' and 'reflection color' parameter, which isn't
|
||||
* physically correct. This works as an RGB tint/gain on out-scattered light, but doesn't affect the light
|
||||
* that is transmitted through the volume. While having wavelength dependent absorption/scattering is more correct,
|
||||
* it also makes it harder to control the overall look of the volume since coloring the outscattered light results
|
||||
* in the inverse color being transmitted through the rest of the volume.
|
||||
*/
|
||||
static void volumeintegrate(struct ShadeInput *shi, float col[4], const float co[3], const float endco[3])
|
||||
{
|
||||
float radiance[3] = {0.f, 0.f, 0.f};
|
||||
float tr[3] = {1.f, 1.f, 1.f};
|
||||
float p[3] = {co[0], co[1], co[2]};
|
||||
float step_vec[3] = {endco[0] - co[0], endco[1] - co[1], endco[2] - co[2]};
|
||||
const float stepsize = shi->mat->vol.stepsize;
|
||||
|
||||
float t0 = 0.f;
|
||||
float pt0 = t0;
|
||||
float t1 = normalize_v3(step_vec); /* returns vector length */
|
||||
|
||||
t0 += stepsize * ((shi->mat->vol.stepsize_type == MA_VOL_STEP_CONSTANT) ? 0.5f : BLI_thread_frand(shi->thread));
|
||||
p[0] += t0 * step_vec[0];
|
||||
p[1] += t0 * step_vec[1];
|
||||
p[2] += t0 * step_vec[2];
|
||||
mul_v3_fl(step_vec, stepsize);
|
||||
|
||||
for (; t0 < t1; pt0 = t0, t0 += stepsize) {
|
||||
const float density = vol_get_density(shi, p);
|
||||
|
||||
if (density > 0.00001f) {
|
||||
float scatter_col[3] = {0.f, 0.f, 0.f}, emit_col[3];
|
||||
const float stepd = (t0 - pt0) * density;
|
||||
|
||||
/* transmittance component (alpha) */
|
||||
vol_get_transmittance_seg(shi, tr, stepsize, co, density);
|
||||
|
||||
if (t0 > t1 * 0.25f) {
|
||||
/* only use depth cutoff after we've traced a little way into the volume */
|
||||
if (IMB_colormanagement_get_luminance(tr) < shi->mat->vol.depth_cutoff) break;
|
||||
}
|
||||
|
||||
vol_get_emission(shi, emit_col, p);
|
||||
|
||||
if (shi->obi->volume_precache) {
|
||||
float p2[3];
|
||||
|
||||
p2[0] = p[0] + (step_vec[0] * 0.5f);
|
||||
p2[1] = p[1] + (step_vec[1] * 0.5f);
|
||||
p2[2] = p[2] + (step_vec[2] * 0.5f);
|
||||
|
||||
vol_get_precached_scattering(&R, shi, scatter_col, p2);
|
||||
}
|
||||
else
|
||||
vol_get_scattering(shi, scatter_col, p, shi->view);
|
||||
|
||||
radiance[0] += stepd * tr[0] * (emit_col[0] + scatter_col[0]);
|
||||
radiance[1] += stepd * tr[1] * (emit_col[1] + scatter_col[1]);
|
||||
radiance[2] += stepd * tr[2] * (emit_col[2] + scatter_col[2]);
|
||||
}
|
||||
add_v3_v3(p, step_vec);
|
||||
}
|
||||
|
||||
/* multiply original color (from behind volume) with transmittance over entire distance */
|
||||
mul_v3_v3v3(col, tr, col);
|
||||
add_v3_v3(col, radiance);
|
||||
|
||||
/* alpha <-- transmission luminance */
|
||||
col[3] = 1.0f - IMB_colormanagement_get_luminance(tr);
|
||||
}
|
||||
|
||||
/* the main entry point for volume shading */
|
||||
static void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr, int inside_volume)
|
||||
{
|
||||
float hitco[3], col[4] = {0.f, 0.f, 0.f, 0.f};
|
||||
const float *startco, *endco;
|
||||
int trace_behind = 1;
|
||||
const int ztransp = ((shi->depth == 0) && (shi->mat->mode & MA_TRANSP) && (shi->mat->mode & MA_ZTRANSP));
|
||||
Isect is;
|
||||
|
||||
/* check for shading an internal face a volume object directly */
|
||||
if (inside_volume == VOL_SHADE_INSIDE)
|
||||
trace_behind = 0;
|
||||
else if (inside_volume == VOL_SHADE_OUTSIDE) {
|
||||
if (shi->flippednor)
|
||||
inside_volume = VOL_SHADE_INSIDE;
|
||||
}
|
||||
|
||||
if (ztransp && inside_volume == VOL_SHADE_INSIDE) {
|
||||
MatInside *mi;
|
||||
int render_this = 0;
|
||||
|
||||
/* don't render the backfaces of ztransp volume materials.
|
||||
*
|
||||
* volume shading renders the internal volume from between the
|
||||
* ' view intersection of the solid volume to the
|
||||
* intersection on the other side, as part of the shading of
|
||||
* the front face.
|
||||
*
|
||||
* Because ztransp renders both front and back faces independently
|
||||
* this will double up, so here we prevent rendering the backface as well,
|
||||
* which would otherwise render the volume in between the camera and the backface
|
||||
* --matt */
|
||||
|
||||
for (mi = R.render_volumes_inside.first; mi; mi = mi->next) {
|
||||
/* weak... */
|
||||
if (mi->ma == shi->mat) render_this = 1;
|
||||
}
|
||||
if (!render_this) return;
|
||||
}
|
||||
|
||||
|
||||
if (inside_volume == VOL_SHADE_INSIDE) {
|
||||
startco = shi->camera_co;
|
||||
endco = shi->co;
|
||||
|
||||
if (trace_behind) {
|
||||
if (!ztransp)
|
||||
/* trace behind the volume object */
|
||||
vol_trace_behind(shi, shi->vlr, endco, col);
|
||||
}
|
||||
else {
|
||||
/* we're tracing through the volume between the camera
|
||||
* and a solid surface, so use that pre-shaded radiance */
|
||||
copy_v4_v4(col, shr->combined);
|
||||
}
|
||||
|
||||
/* shade volume from 'camera' to 1st hit point */
|
||||
volumeintegrate(shi, col, startco, endco);
|
||||
}
|
||||
/* trace to find a backface, the other side bounds of the volume */
|
||||
/* (ray intersect ignores front faces here) */
|
||||
else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) {
|
||||
VlakRen *vlr = (VlakRen *)is.hit.face;
|
||||
|
||||
startco = shi->co;
|
||||
endco = hitco;
|
||||
|
||||
if (!ztransp) {
|
||||
/* if it's another face in the same material */
|
||||
if (vlr->mat == shi->mat) {
|
||||
/* trace behind the 2nd (raytrace) hit point */
|
||||
vol_trace_behind(shi, (VlakRen *)is.hit.face, endco, col);
|
||||
}
|
||||
else {
|
||||
shade_intersection(shi, col, &is);
|
||||
}
|
||||
}
|
||||
|
||||
/* shade volume from 1st hit point to 2nd hit point */
|
||||
volumeintegrate(shi, col, startco, endco);
|
||||
}
|
||||
|
||||
if (ztransp)
|
||||
col[3] = col[3] > 1.f ? 1.f : col[3];
|
||||
else
|
||||
col[3] = 1.f;
|
||||
|
||||
copy_v3_v3(shr->combined, col);
|
||||
shr->alpha = col[3];
|
||||
|
||||
copy_v3_v3(shr->diff, shr->combined);
|
||||
copy_v3_v3(shr->diffshad, shr->diff);
|
||||
}
|
||||
|
||||
/* Traces a shadow through the object,
|
||||
* pretty much gets the transmission over a ray path */
|
||||
void shade_volume_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct Isect *last_is)
|
||||
{
|
||||
float hitco[3];
|
||||
float tr[3] = {1.0, 1.0, 1.0};
|
||||
Isect is = {{0}};
|
||||
const float *startco, *endco;
|
||||
|
||||
memset(shr, 0, sizeof(ShadeResult));
|
||||
|
||||
/* if 1st hit normal is facing away from the camera,
|
||||
* then we're inside the volume already. */
|
||||
if (shi->flippednor) {
|
||||
startco = last_is->start;
|
||||
endco = shi->co;
|
||||
}
|
||||
|
||||
/* trace to find a backface, the other side bounds of the volume */
|
||||
/* (ray intersect ignores front faces here) */
|
||||
else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH)) {
|
||||
startco = shi->co;
|
||||
endco = hitco;
|
||||
}
|
||||
else {
|
||||
shr->combined[0] = shr->combined[1] = shr->combined[2] = 0.f;
|
||||
shr->alpha = shr->combined[3] = 1.f;
|
||||
return;
|
||||
}
|
||||
|
||||
vol_get_transmittance(shi, tr, startco, endco);
|
||||
|
||||
|
||||
/* if we hit another face in the same volume bounds */
|
||||
/* shift raytrace coordinates to the hit point, to avoid shading volume twice */
|
||||
/* due to idiosyncracy in ray_trace_shadow_tra() */
|
||||
if (is.hit.ob == shi->obi) {
|
||||
copy_v3_v3(shi->co, hitco);
|
||||
last_is->dist += is.dist;
|
||||
shi->vlr = (VlakRen *)is.hit.face;
|
||||
}
|
||||
|
||||
|
||||
copy_v3_v3(shr->combined, tr);
|
||||
shr->combined[3] = 1.0f - IMB_colormanagement_get_luminance(tr);
|
||||
shr->alpha = shr->combined[3];
|
||||
}
|
||||
|
||||
|
||||
/* delivers a fully filled in ShadeResult, for all passes */
|
||||
void shade_volume_outside(ShadeInput *shi, ShadeResult *shr)
|
||||
{
|
||||
memset(shr, 0, sizeof(ShadeResult));
|
||||
volume_trace(shi, shr, VOL_SHADE_OUTSIDE);
|
||||
}
|
||||
|
||||
|
||||
void shade_volume_inside(ShadeInput *shi, ShadeResult *shr)
|
||||
{
|
||||
MatInside *m;
|
||||
Material *mat_backup;
|
||||
ObjectInstanceRen *obi_backup;
|
||||
float prev_alpha = shr->alpha;
|
||||
|
||||
/* XXX: extend to multiple volumes perhaps later */
|
||||
mat_backup = shi->mat;
|
||||
obi_backup = shi->obi;
|
||||
|
||||
m = R.render_volumes_inside.first;
|
||||
shi->mat = m->ma;
|
||||
shi->obi = m->obi;
|
||||
shi->obr = m->obi->obr;
|
||||
|
||||
volume_trace(shi, shr, VOL_SHADE_INSIDE);
|
||||
|
||||
shr->alpha = shr->alpha + prev_alpha;
|
||||
CLAMP(shr->alpha, 0.0f, 1.0f);
|
||||
|
||||
shi->mat = mat_backup;
|
||||
shi->obi = obi_backup;
|
||||
shi->obr = obi_backup->obr;
|
||||
}
|
Reference in New Issue
Block a user