Merge branch 'master' into blender2.8

This commit is contained in:
2018-06-08 08:10:35 +02:00
191 changed files with 36025 additions and 2057 deletions

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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__ */

View 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__ */

View 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

View 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__ */

View 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

View 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__ */

View 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__ */

View 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);

View 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

View 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__*/

View 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__ */

View 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__ */

View 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

View 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;
}
}

View 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__ */

View 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);
}
}

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -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

View 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;
}

View 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__ */

View 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

View 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);
}

View 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;
}
};

View 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__ */

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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;
}

File diff suppressed because it is too large Load Diff

View 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];
}
/* ---------------------------------------------------------------------------- */

View 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 */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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 */

View 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;
}

View 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;
}