diff options
Diffstat (limited to 'source/blender/render/intern')
34 files changed, 5093 insertions, 2173 deletions
diff --git a/source/blender/render/intern/Makefile b/source/blender/render/intern/Makefile index 859a813bd2f..4fce37df175 100644 --- a/source/blender/render/intern/Makefile +++ b/source/blender/render/intern/Makefile @@ -29,6 +29,6 @@ # Bounces make to subdirectories. SOURCEDIR = source/blender/render/intern -DIRS = source +DIRS = source raytrace include nan_subdirs.mk diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index e50e498228d..48bf34d0696 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -53,6 +53,8 @@ struct VlakTableNode; struct GHash; struct RenderBuckets; struct ObjectInstanceRen; +struct RayObject; +struct RayFace; #define TABLEINITSIZE 1024 #define LAMPINITSIZE 256 @@ -121,6 +123,10 @@ struct Render RenderResult *pushedresult; /* a list of RenderResults, for fullsample */ ListBase fullresult; + /* read/write mutex, all internal code that writes to re->result must use a + write lock, all external code must use a read lock. internal code is assumed + to not conflict with writes, so no lock used for that */ + ThreadRWMutex resultmutex; /* window size, display rect, viewplane */ int winx, winy; @@ -168,7 +174,10 @@ struct Render ListBase parts; /* octree tables and variables for raytrace */ - void *raytree; + struct RayObject *raytree; + struct RayFace *rayfaces; + struct VlakPrimitive *rayprimitives; + float maxdist; /* needed for keeping an incorrect behaviour of SUN and HEMI lights (avoid breaking old scenes) */ /* occlusion tree */ void *occlusiontree; @@ -236,10 +245,17 @@ struct Render struct ISBData; +typedef struct DeepSample { + int z; + float v; +} DeepSample; + typedef struct ShadSampleBuf { struct ShadSampleBuf *next, *prev; intptr_t *zbuf; char *cbuf; + DeepSample **deepbuf; + int *totbuf; } ShadSampleBuf; typedef struct ShadBuf { @@ -249,7 +265,7 @@ typedef struct ShadBuf { float viewmat[4][4]; float winmat[4][4]; float *jit, *weight; - float d, clipend, pixsize, soft; + float d, clipend, pixsize, soft, compressthresh; int co[3]; int size, bias; ListBase buffers; @@ -281,6 +297,13 @@ typedef struct ObjectRen { int actmtface, actmcol, bakemtface; float obmat[4][4]; /* only used in convertblender.c, for instancing */ + + /* used on makeraytree */ + struct RayObject *raytree; + struct RayFace *rayfaces; + struct VlakPrimitive *rayprimitives; + struct ObjectInstanceRen *rayobi; + } ObjectRen; typedef struct ObjectInstanceRen { @@ -300,6 +323,11 @@ typedef struct ObjectInstanceRen { float *vectors; int totvector; + + /* used on makeraytree */ + struct RayObject *raytree; + int transform_primitives; + } ObjectInstanceRen; /* ------------------------------------------------------------------------- */ @@ -425,7 +453,7 @@ typedef struct MatInside { typedef struct VolPrecachePart { struct VolPrecachePart *next, *prev; - struct RayTree *tree; + struct RayObject *tree; struct ShadeInput *shi; struct ObjectInstanceRen *obi; int num; @@ -506,6 +534,8 @@ typedef struct LampRen { float clipend; /** A small depth offset to prevent self-shadowing. */ float bias; + /* Compression threshold for deep shadow maps */ + float compressthresh; short ray_samp, ray_sampy, ray_sampz, ray_samp_method, ray_samp_type, area_shape, ray_totsamp; short xold[BLENDER_MAX_THREADS], yold[BLENDER_MAX_THREADS]; /* last jitter table for area lights */ @@ -536,8 +566,7 @@ typedef struct LampRen { short YF_glowtype; /* ray optim */ - VlakRen *vlr_last[BLENDER_MAX_THREADS]; - ObjectInstanceRen *obi_last[BLENDER_MAX_THREADS]; + struct RayObject *last_hit[BLENDER_MAX_THREADS]; struct MTex *mtex[MAX_MTEX]; diff --git a/source/blender/render/intern/include/rendercore.h b/source/blender/render/intern/include/rendercore.h index 4b28529a147..250fbc000cb 100644 --- a/source/blender/render/intern/include/rendercore.h +++ b/source/blender/render/intern/include/rendercore.h @@ -95,6 +95,7 @@ int get_sample_layers(struct RenderPart *pa, struct RenderLayer *rl, struct Rend extern void freeraytree(Render *re); extern void makeraytree(Render *re); +RayObject* makeraytree_object(Render *re, ObjectInstanceRen *obi); extern void ray_shadow(ShadeInput *, LampRen *, float *); extern void ray_trace(ShadeInput *, ShadeResult *); diff --git a/source/blender/render/intern/include/volume_precache.h b/source/blender/render/intern/include/volume_precache.h index 9d87a219c82..5c3a7bdfbf0 100644 --- a/source/blender/render/intern/include/volume_precache.h +++ b/source/blender/render/intern/include/volume_precache.h @@ -28,6 +28,6 @@ void volume_precache(Render *re); void free_volume_precache(Render *re); -int point_inside_volume_objectinstance(ObjectInstanceRen *obi, float *co); +int point_inside_volume_objectinstance(Render *re, ObjectInstanceRen *obi, float *co); -#define VOL_MS_TIMESTEP 0.1f
\ No newline at end of file +#define VOL_MS_TIMESTEP 0.1f diff --git a/source/blender/render/intern/include/volumetric.h b/source/blender/render/intern/include/volumetric.h index 026b4840ea3..97e7e022fa0 100644 --- a/source/blender/render/intern/include/volumetric.h +++ b/source/blender/render/intern/include/volumetric.h @@ -26,9 +26,8 @@ * ***** END GPL LICENSE BLOCK ***** */ -float vol_get_stepsize(struct ShadeInput *shi, int context); float vol_get_density(struct ShadeInput *shi, float *co); -void vol_get_scattering(ShadeInput *shi, float *scatter_col, float *co, float stepsize, float density); +void vol_get_scattering(ShadeInput *shi, float *scatter_col, float *co_); void shade_volume_outside(ShadeInput *shi, ShadeResult *shr); void shade_volume_inside(ShadeInput *shi, ShadeResult *shr); diff --git a/source/blender/render/intern/include/zbuf.h b/source/blender/render/intern/include/zbuf.h index b6d0c656f63..a0665daf916 100644 --- a/source/blender/render/intern/include/zbuf.h +++ b/source/blender/render/intern/include/zbuf.h @@ -37,6 +37,7 @@ struct VlakRen; struct ListBase; struct ZSpan; struct APixstrand; +struct APixstr; struct StrandShadeCache; void fillrect(int *rect, int x, int y, int val); @@ -50,11 +51,12 @@ void projectverto(float *v1, float winmat[][4], float *adr); int testclip(float *v); void zbuffer_shadow(struct Render *re, float winmat[][4], struct LampRen *lar, int *rectz, int size, float jitx, float jity); +void zbuffer_abuf_shadow(struct Render *re, struct LampRen *lar, float winmat[][4], struct APixstr *APixbuf, struct APixstrand *apixbuf, struct ListBase *apsmbase, int size, int samples, float (*jit)[2]); void zbuffer_solid(struct RenderPart *pa, struct RenderLayer *rl, void (*fillfunc)(struct RenderPart*, struct ZSpan*, int, void*), void *data); unsigned short *zbuffer_transp_shade(struct RenderPart *pa, struct RenderLayer *rl, float *pass, struct ListBase *psmlist); void zbuffer_sss(RenderPart *pa, unsigned int lay, void *handle, void (*func)(void*, int, int, int, int, int)); -int zbuffer_strands_abuf(struct Render *re, struct RenderPart *pa, struct RenderLayer *rl, struct APixstrand *apixbuf, struct ListBase *apsmbase, struct StrandShadeCache *cache); +int zbuffer_strands_abuf(struct Render *re, struct RenderPart *pa, struct APixstrand *apixbuf, struct ListBase *apsmbase, unsigned int lay, int negzmask, float winmat[][4], int winx, int winy, int sample, float (*jit)[2], float clipcrop, int shadow, struct StrandShadeCache *cache); typedef struct APixstr { unsigned short mask[4]; /* jitter mask */ @@ -118,6 +120,7 @@ typedef struct ZSpan { /* exported to shadbuf.c */ void zbufclip4(struct ZSpan *zspan, int obi, int zvlnr, float *f1, float *f2, float *f3, float *f4, int c1, int c2, int c3, int c4); void zbuf_free_span(struct ZSpan *zspan); +void freepsA(struct ListBase *lb); /* to rendercore.c */ void zspan_scanconvert(struct ZSpan *zpan, void *handle, float *v1, float *v2, float *v3, void (*func)(void *, int, int, float, float) ); @@ -128,7 +131,7 @@ void zbuf_alloc_span(struct ZSpan *zspan, int rectx, int recty, float clipcrop); void zbufclipwire(struct ZSpan *zspan, int obi, int zvlnr, int ec, float *ho1, float *ho2, float *ho3, float *ho4, int c1, int c2, int c3, int c4); /* exported to shadeinput.c */ -void zbuf_make_winmat(Render *re, float duplimat[][4], float winmat[][4]); +void zbuf_make_winmat(Render *re, float winmat[][4]); void zbuf_render_project(float winmat[][4], float *co, float *ho); #endif diff --git a/source/blender/render/intern/raytrace/Makefile b/source/blender/render/intern/raytrace/Makefile new file mode 100644 index 00000000000..6e40c544c6f --- /dev/null +++ b/source/blender/render/intern/raytrace/Makefile @@ -0,0 +1,65 @@ +# +# $Id$ +# +# ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ***** +# +# + +LIBNAME = render_raytrace +DIR = $(OCGDIR)/blender/render + +include nan_compile.mk + +CFLAGS += $(LEVEL_1_C_WARNINGS) + +# first /include is my own includes, second is the external interface. +# The external modules follow after. There should be a nicer way to say this. +CPPFLAGS += -I../include +CPPFLAGS += -I../../extern/include +CPPFLAGS += -I../../../blenlib +CPPFLAGS += -I../../../imbuf +CPPFLAGS += -I../../../makesdna +CPPFLAGS += -I../../../makesrna +CPPFLAGS += -I../../../blenkernel +CPPFLAGS += -I../../../quicktime +CPPFLAGS += -I../../../../kernel/gen_messaging +CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include +# not very neat: the rest of blender.. +CPPFLAGS += -I../../../editors/include +CPPFLAGS += $(NAN_SDLCFLAGS) +CPPFLAGS += -I../../../../../intern/smoke/extern + +ifeq ($(WITH_QUICKTIME), true) + CPPFLAGS += -DWITH_QUICKTIME +endif + +ifeq ($(WITH_FFMPEG),true) + CPPFLAGS += -DWITH_FFMPEG +endif + +ifeq ($(WITH_OPENEXR),true) + CPPFLAGS += -DWITH_OPENEXR +endif diff --git a/source/blender/render/intern/raytrace/bvh.h b/source/blender/render/intern/raytrace/bvh.h new file mode 100644 index 00000000000..ad7f44acb8d --- /dev/null +++ b/source/blender/render/intern/raytrace/bvh.h @@ -0,0 +1,400 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ***** + */ +#include "rayobject.h" +#include "raycounter.h" +#include "MEM_guardedalloc.h" +#include "rayobject_rtbuild.h" +#include "rayobject_hint.h" + +#include <assert.h> + +#ifdef __SSE__ +#include <xmmintrin.h> +#endif + +#ifndef RE_RAYTRACE_BVH_H +#define RE_RAYTRACE_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_load1_ps(&isec->labda); + + const __m128 tmin1 = _mm_max_ps(tmin0, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[0]], _mm_load1_ps(&isec->start[0]) ), _mm_load1_ps(&isec->idot_axis[0])) ); + const __m128 tmax1 = _mm_min_ps(tmax0, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[1]], _mm_load1_ps(&isec->start[0]) ), _mm_load1_ps(&isec->idot_axis[0])) ); + const __m128 tmin2 = _mm_max_ps(tmin1, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[2]], _mm_load1_ps(&isec->start[1]) ), _mm_load1_ps(&isec->idot_axis[1])) ); + const __m128 tmax2 = _mm_min_ps(tmax1, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[3]], _mm_load1_ps(&isec->start[1]) ), _mm_load1_ps(&isec->idot_axis[1])) ); + const __m128 tmin3 = _mm_max_ps(tmin2, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[4]], _mm_load1_ps(&isec->start[2]) ), _mm_load1_ps(&isec->idot_axis[2])) ); + const __m128 tmax3 = _mm_min_ps(tmax2, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[5]], _mm_load1_ps(&isec->start[2]) ), _mm_load1_ps(&isec->idot_axis[2])) ); + + return _mm_movemask_ps(_mm_cmpge_ps(tmax3, tmin3)); +} +#endif + + +/* bvh tree generics */ +template<class Tree> static int bvh_intersect(Tree *obj, Isect *isec); + +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) +{ + bvh_node_merge_bb(obj->root, min, max); +} + + +template<class Tree> +static float bvh_cost(Tree *obj) +{ + assert(obj->cost >= 0.0); + return obj->cost; +} + + + +/* bvh tree nodes generics */ +template<class Node> static inline int bvh_node_hit_test(Node *node, Isect *isec) +{ + return RE_rayobject_bb_intersect_test(isec, (const float*)node->bb); +} + + +template<class Node> +static inline void bvh_node_merge_bb(Node *node, float *min, float *max) +{ + if(is_leaf(node)) + { + RE_rayobject_merge_bb( (RayObject*)node, min, max); + } + else + { + DO_MIN(node->bb , min); + DO_MAX(node->bb+3, max); + } +} + + + +/* + * recursivly 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> +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(hit && isec->mode == RE_RAY_SHADOW) 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) ); +/* + 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; + } +*/ + 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 + */ +/* +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; +} +*/ + +template<class Node,class HintObject> +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 diff --git a/source/blender/render/intern/raytrace/rayobject.cpp b/source/blender/render/intern/raytrace/rayobject.cpp new file mode 100644 index 00000000000..8aff7a38317 --- /dev/null +++ b/source/blender/render/intern/raytrace/rayobject.cpp @@ -0,0 +1,580 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ***** + */ +#include <assert.h> + +#include "BKE_utildefines.h" +#include "BLI_arithb.h" +#include "DNA_material_types.h" + +#include "RE_raytrace.h" +#include "render_types.h" +#include "rayobject.h" +#include "raycounter.h" + +/* + * 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] + */ +int RE_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.0 || t2y < 0.0 || t2z < 0.0) return 0; + if(t1x > isec->labda || t1y > isec->labda || t1z > isec->labda) return 0; + RE_RC_COUNT(isec->raycounter->bb.hit); + + return 1; +} + + +/* only for self-intersecting test with current render face (where ray left) */ +static int intersection2(VlakRen *face, float r0, float r1, float r2, float rx1, float ry1, float rz1) +{ + float co1[3], co2[3], co3[3], co4[3]; + float x0,x1,x2,t00,t01,t02,t10,t11,t12,t20,t21,t22; + float m0, m1, m2, divdet, det, det1; + float u1, v, u2; + + VECCOPY(co1, face->v1->co); + VECCOPY(co2, face->v2->co); + if(face->v4) + { + VECCOPY(co3, face->v4->co); + VECCOPY(co4, face->v3->co); + } + else + { + VECCOPY(co3, face->v3->co); + } + + t00= co3[0]-co1[0]; + t01= co3[1]-co1[1]; + t02= co3[2]-co1[2]; + t10= co3[0]-co2[0]; + t11= co3[1]-co2[1]; + t12= co3[2]-co2[2]; + + x0= t11*r2-t12*r1; + x1= t12*r0-t10*r2; + x2= t10*r1-t11*r0; + + divdet= t00*x0+t01*x1+t02*x2; + + m0= rx1-co3[0]; + m1= ry1-co3[1]; + m2= rz1-co3[2]; + det1= m0*x0+m1*x1+m2*x2; + + if(divdet!=0.0f) { + u1= det1/divdet; + + if(u1<ISECT_EPSILON) { + det= t00*(m1*r2-m2*r1); + det+= t01*(m2*r0-m0*r2); + det+= t02*(m0*r1-m1*r0); + v= det/divdet; + + if(v<ISECT_EPSILON && (u1 + v) > -(1.0f+ISECT_EPSILON)) { + return 1; + } + } + } + + if(face->v4) { + + t20= co3[0]-co4[0]; + t21= co3[1]-co4[1]; + t22= co3[2]-co4[2]; + + divdet= t20*x0+t21*x1+t22*x2; + if(divdet!=0.0f) { + u2= det1/divdet; + + if(u2<ISECT_EPSILON) { + det= t20*(m1*r2-m2*r1); + det+= t21*(m2*r0-m0*r2); + det+= t22*(m0*r1-m1*r0); + v= det/divdet; + + if(v<ISECT_EPSILON && (u2 + v) >= -(1.0f+ISECT_EPSILON)) { + return 2; + } + } + } + } + return 0; +} + +static 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->mat->mode & MA_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 (is->lay & obi->lay); +} + +static inline int vlr_check_intersect_solid(Isect *is, ObjectInstanceRen* obi, VlakRen *vlr) +{ + /* solid material types only */ + if (vlr->mat->material_type == MA_TYPE_SURFACE) + return 1; + else + return 0; +} + +static inline int rayface_check_cullface(RayFace *face, Isect *is) +{ + float nor[3]; + + /* don't intersect if the ray faces along the face normal */ + if(face->quad) CalcNormFloat4(face->v1, face->v2, face->v3, face->v4, nor); + else CalcNormFloat(face->v1, face->v2, face->v3, nor); + + return (INPR(nor, is->vec) < 0); +} + +/* ray - triangle or quad intersection */ +/* this function shall only modify Isect if it detects an hit */ +static int intersect_rayface(RayObject *hit_obj, RayFace *face, Isect *is) +{ + float co1[3],co2[3],co3[3],co4[3]; + float x0,x1,x2,t00,t01,t02,t10,t11,t12,t20,t21,t22,r0,r1,r2; + float m0, m1, m2, divdet, det1; + float labda, u, v; + short ok=0; + + if(is->orig.ob == face->ob && is->orig.face == face->face) + return 0; + + + if(is->skip & RE_SKIP_VLR_RENDER_CHECK) + { + if(vlr_check_intersect(is, (ObjectInstanceRen*)face->ob, (VlakRen*)face->face ) == 0) + return 0; + } + if(is->skip & RE_SKIP_VLR_NON_SOLID_MATERIAL) + { + if(vlr_check_intersect_solid(is, (ObjectInstanceRen*)face->ob, (VlakRen*)face->face) == 0) + return 0; + } + if(is->skip & RE_SKIP_CULLFACE) + { + if(rayface_check_cullface(face, is) == 0) + return 0; + } + + RE_RC_COUNT(is->raycounter->faces.test); + + //Load coords + VECCOPY(co1, face->v1); + VECCOPY(co2, face->v2); + if(RE_rayface_isQuad(face)) + { + VECCOPY(co3, face->v4); + VECCOPY(co4, face->v3); + } + else + { + VECCOPY(co3, face->v3); + } + + t00= co3[0]-co1[0]; + t01= co3[1]-co1[1]; + t02= co3[2]-co1[2]; + t10= co3[0]-co2[0]; + t11= co3[1]-co2[1]; + t12= co3[2]-co2[2]; + + r0= is->vec[0]; + r1= is->vec[1]; + r2= is->vec[2]; + + x0= t12*r1-t11*r2; + x1= t10*r2-t12*r0; + x2= t11*r0-t10*r1; + + divdet= t00*x0+t01*x1+t02*x2; + + m0= is->start[0]-co3[0]; + m1= is->start[1]-co3[1]; + m2= is->start[2]-co3[2]; + det1= m0*x0+m1*x1+m2*x2; + + if(divdet!=0.0f) { + + divdet= 1.0f/divdet; + u= det1*divdet; + if(u<ISECT_EPSILON && u>-(1.0f+ISECT_EPSILON)) { + float cros0, cros1, cros2; + + cros0= m1*t02-m2*t01; + cros1= m2*t00-m0*t02; + cros2= m0*t01-m1*t00; + v= divdet*(cros0*r0 + cros1*r1 + cros2*r2); + + if(v<ISECT_EPSILON && (u + v) > -(1.0f+ISECT_EPSILON)) { + labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12); + + if(labda>-ISECT_EPSILON && labda<is->labda) { + ok= 1; + } + } + } + } + + if(ok==0 && RE_rayface_isQuad(face)) { + + t20= co3[0]-co4[0]; + t21= co3[1]-co4[1]; + t22= co3[2]-co4[2]; + + divdet= t20*x0+t21*x1+t22*x2; + if(divdet!=0.0f) { + divdet= 1.0f/divdet; + u = det1*divdet; + + if(u<ISECT_EPSILON && u>-(1.0f+ISECT_EPSILON)) { + float cros0, cros1, cros2; + cros0= m1*t22-m2*t21; + cros1= m2*t20-m0*t22; + cros2= m0*t21-m1*t20; + v= divdet*(cros0*r0 + cros1*r1 + cros2*r2); + + if(v<ISECT_EPSILON && (u + v) >-(1.0f+ISECT_EPSILON)) { + labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12); + + if(labda>-ISECT_EPSILON && labda<is->labda) { + ok= 2; + } + } + } + } + } + + 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 neighbour face */ + if(is->skip & RE_SKIP_VLR_NEIGHBOUR) + { + if(labda < 0.1f && is->orig.ob == face->ob) + { + VlakRen * a = (VlakRen*)is->orig.face; + VlakRen * b = (VlakRen*)face->face; + + /* so there's a shared edge or vertex, let's intersect ray with face + itself, if that's true we can safely return 1, otherwise we assume + the intersection is invalid, 0 */ + if(a->v1==b->v1 || a->v2==b->v1 || a->v3==b->v1 || a->v4==b->v1 + || a->v1==b->v2 || a->v2==b->v2 || a->v3==b->v2 || a->v4==b->v2 + || a->v1==b->v3 || a->v2==b->v3 || a->v3==b->v3 || a->v4==b->v3 + || (b->v4 && (a->v1==b->v4 || a->v2==b->v4 || a->v3==b->v4 || a->v4==b->v4))) + if(!intersection2((VlakRen*)a, -r0, -r1, -r2, is->start[0], is->start[1], is->start[2])) + { + return 0; + } + } + } + + RE_RC_COUNT(is->raycounter->faces.hit); + + is->isect= ok; // wich half of the quad + is->labda= labda; + is->u= u; is->v= v; + + 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; +} + +RayObject* RE_rayface_from_vlak(RayFace *rayface, ObjectInstanceRen *obi, VlakRen *vlr) +{ + return RE_rayface_from_coords(rayface, obi, vlr, vlr->v1->co, vlr->v2->co, vlr->v3->co, vlr->v4 ? vlr->v4->co : 0 ); +} + +RayObject* RE_rayface_from_coords(RayFace *rayface, void *ob, void *face, float *v1, float *v2, float *v3, float *v4) +{ + rayface->ob = ob; + rayface->face = face; + + VECCOPY(rayface->v1, v1); + VECCOPY(rayface->v2, v2); + VECCOPY(rayface->v3, v3); + if(v4) + { + VECCOPY(rayface->v4, v4); + rayface->quad = 1; + } + else + { + rayface->quad = 0; + } + + return RE_rayobject_unalignRayFace(rayface); +} + +RayObject* RE_vlakprimitive_from_vlak(VlakPrimitive *face, struct ObjectInstanceRen *obi, struct VlakRen *vlr) +{ + face->ob = obi; + face->face = vlr; + return RE_rayobject_unalignVlakPrimitive(face); +} + + +int RE_rayobject_raycast(RayObject *r, Isect *isec) +{ + int i; + RE_RC_COUNT(isec->raycounter->raycast.test); + + /* Setup vars used on raycast */ + isec->dist = VecLength(isec->vec); + + for(i=0; i<3; i++) + { + isec->idot_axis[i] = 1.0f / isec->vec[i]; + + isec->bv_index[2*i] = isec->idot_axis[i] < 0.0 ? 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; + RE_rayface_from_vlak(&nface, face->ob, face->face); + + if(face->ob->transform_primitives) + { + Mat4MulVecfl(face->ob->mat, nface.v1); + Mat4MulVecfl(face->ob->mat, nface.v2); + Mat4MulVecfl(face->ob->mat, nface.v3); + if(RE_rayface_isQuad(&nface)) + Mat4MulVecfl(face->ob->mat, nface.v4); + } + + 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); +} + +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 ); +} + +void RE_rayobject_merge_bb(RayObject *r, float *min, float *max) +{ + 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); + VlakRen *vlr = face->face; + + DO_MINMAX( vlr->v1->co, min, max ); + DO_MINMAX( vlr->v2->co, min, max ); + DO_MINMAX( vlr->v3->co, min, max ); + if(vlr->v4) DO_MINMAX( vlr->v4->co, min, max ); + } + else if(RE_rayobject_isRayAPI(r)) + { + r = RE_rayobject_align( r ); + r->api->bb( r, min, max ); + } + else assert(0); +} + +float RE_rayobject_cost(RayObject *r) +{ + if(RE_rayobject_isRayFace(r) || RE_rayobject_isVlakPrimitive(r)) + { + return 1.0; + } + else if(RE_rayobject_isRayAPI(r)) + { + r = RE_rayobject_align( r ); + return r->api->cost( r ); + } + else assert(0); +} + +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); +} + +int RE_rayobjectcontrol_test_break(RayObjectControl *control) +{ + if(control->test_break) + return control->test_break( control->data ); + + return 0; +} + + +/* + * Empty raytree + */ +static int RE_rayobject_empty_intersect(RayObject *o, Isect *is) +{ + return 0; +} + +static void RE_rayobject_empty_free(RayObject *o) +{ +} + +static void RE_rayobject_empty_bb(RayObject *o, float *min, float *max) +{ + return; +} + +static float RE_rayobject_empty_cost(RayObject *o) +{ + return 0.0; +} + +static void RE_rayobject_empty_hint_bb(RayObject *o, RayHint *hint, float *min, float *max) +{} + +static RayObjectAPI empty_api = +{ + RE_rayobject_empty_intersect, + NULL, //static void RE_rayobject_instance_add(RayObject *o, RayObject *ob); + NULL, //static void RE_rayobject_instance_done(RayObject *o); + RE_rayobject_empty_free, + RE_rayobject_empty_bb, + RE_rayobject_empty_cost, + RE_rayobject_empty_hint_bb +}; + +static RayObject empty_raytree = { &empty_api, {0, 0} }; + +RayObject *RE_rayobject_empty_create() +{ + return RE_rayobject_unalignRayAPI( &empty_raytree ); +} diff --git a/source/blender/render/intern/raytrace/rayobject_hint.h b/source/blender/render/intern/raytrace/rayobject_hint.h new file mode 100644 index 00000000000..d85465aec66 --- /dev/null +++ b/source/blender/render/intern/raytrace/rayobject_hint.h @@ -0,0 +1,70 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ***** + */ +#ifndef RE_RAYTRACE_RAYOBJECT_HINT_H +#define RE_RAYTRACE_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; +} +/* +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 diff --git a/source/blender/render/intern/raytrace/rayobject_qbvh.cpp b/source/blender/render/intern/raytrace/rayobject_qbvh.cpp new file mode 100644 index 00000000000..2719675e3b8 --- /dev/null +++ b/source/blender/render/intern/raytrace/rayobject_qbvh.cpp @@ -0,0 +1,149 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ***** + */ +#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); + BLI_memarena_use_malloc(arena1); + + MemArena *arena2 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); + 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; + } + + pushup_simd<VBVHNode,4>(root); + obj->root = Reorganize_SVBVH<VBVHNode>(arena2).transform(root); + + //Cleanup + BLI_memarena_free(arena1); + + rtbuild_free( obj->builder ); + obj->builder = NULL; + + obj->node_arena = arena2; + obj->cost = 1.0; +} + + +template<int StackSize> +int intersect(QBVHTree *obj, Isect* isec) +{ + //TODO renable hint support + if(RE_rayobject_isAligned(obj->root)) + return bvh_node_stack_raycast<SVBVHNode,StackSize,false>( obj->root, isec); + else + return RE_rayobject_intersect( (RayObject*) obj->root, isec ); +} + +template<class Tree> +void bvh_hint_bb(Tree *tree, LCTSHint *hint, float *min, float *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> +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_qbvh_create(int size) +{ + return bvh_create_tree<QBVHTree,DFS_STACK_SIZE>(size); +} + + +#else + +RayObject *RE_rayobject_qbvh_create(int size) +{ + puts("WARNING: SSE disabled at compile time\n"); + return NULL; +} + +#endif diff --git a/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp b/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp new file mode 100644 index 00000000000..a16499d4f91 --- /dev/null +++ b/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp @@ -0,0 +1,496 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ***** + */ +#include <assert.h> +#include <math.h> +#include <stdlib.h> +#include <algorithm> + +#include "rayobject_rtbuild.h" +#include "MEM_guardedalloc.h" +#include "BLI_arithb.h" +#include "BKE_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 = 0; + b->primitives.end = 0; + b->primitives.maxsize = 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] = 0; + + 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) +{ + assert( b->primitives.begin + b->primitives.maxsize != b->primitives.end ); + + b->primitives.end->obj = o; + b->primitives.end->cost = RE_rayobject_cost(o); + + INIT_MINMAX(b->primitives.end->bb, b->primitives.end->bb+3); + RE_rayobject_merge_bb(o, b->primitives.end->bb, b->primitives.end->bb+3); + + 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 ); + + 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] = 0; + tmp->sorted_end [i] = 0; + } + + return tmp; +} + +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, float *max) +{ + rtbuild_calc_bb(b); + DO_MIN(b->bb, min); + DO_MAX(b->bb+3, max); +} + +/* +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); +} +*/ + +/* + * "separators" is an array of dim NCHILDS-1 + * and indicates where to cut the childs + */ +/* +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); +} +*/ + +//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) + { + 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) + { + VECCOPY(sweep[i].bb, obj[i]->bb); + VECCOPY(sweep[i].bb+3, obj[i]->bb+3); + sweep[i].cost = obj[i]->cost; + } + else + { + sweep[i].bb[0] = MIN2(obj[i]->bb[0], sweep[i+1].bb[0]); + sweep[i].bb[1] = MIN2(obj[i]->bb[1], sweep[i+1].bb[1]); + sweep[i].bb[2] = MIN2(obj[i]->bb[2], sweep[i+1].bb[2]); + sweep[i].bb[3] = MAX2(obj[i]->bb[3], sweep[i+1].bb[3]); + sweep[i].bb[4] = MAX2(obj[i]->bb[4], sweep[i+1].bb[4]); + sweep[i].bb[5] = MAX2(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; + + 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)); + 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); + if( hcost < bcost + || (hcost == bcost && axis < baxis)) //this makes sure the tree built is the same whatever is the order of the sorting axis + { + 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); + } + + + 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 + * basicly this a std::nth_element (like on C++ STL algorithm) + */ +/* +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>); + } +} +*/ + +/* + * Bounding Box utils + */ +float bb_volume(float *min, float *max) +{ + return (max[0]-min[0])*(max[1]-min[1])*(max[2]-min[2]); +} + +float bb_area(float *min, float *max) +{ + 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; + assert(a >= 0.0); + return a; +} + +int bb_largest_axis(float *min, float *max) +{ + 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; + } +} + +int bb_fits_inside(float *outer_min, float *outer_max, float *inner_min, float *inner_max) +{ + 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; +} diff --git a/source/blender/render/intern/raytrace/rayobject_rtbuild.h b/source/blender/render/intern/raytrace/rayobject_rtbuild.h new file mode 100644 index 00000000000..71665681586 --- /dev/null +++ b/source/blender/render/intern/raytrace/rayobject_rtbuild.h @@ -0,0 +1,121 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ***** + */ +#ifndef RE_RAYOBJECT_RTBUILD_H +#define RE_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 organiza/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 + + +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]; + +} 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, float *max); +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(float *min, float *max); +float bb_volume(float *min, float *max); +int bb_largest_axis(float *min, float *max); +int bb_fits_inside(float *outer_min, float *outer_max, float *inner_min, float *inner_max); /* only returns 0 if merging inner and outerbox would create a box larger than outer box */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/blender/render/intern/raytrace/rayobject_svbvh.cpp b/source/blender/render/intern/raytrace/rayobject_svbvh.cpp new file mode 100644 index 00000000000..a877b91e2ff --- /dev/null +++ b/source/blender/render/intern/raytrace/rayobject_svbvh.cpp @@ -0,0 +1,181 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ***** + */ +#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); + BLI_memarena_use_malloc(arena1); + + MemArena *arena2 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); + 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; + } + + VBVH_optimalPackSIMD<OVBVHNode,PackCost>(PackCost()).transform(root); + obj->root = Reorganize_SVBVH<OVBVHNode>(arena2).transform(root); + } + + + //Free data + BLI_memarena_free(arena1); + + obj->node_arena = arena2; + obj->cost = 1.0; + + + rtbuild_free( obj->builder ); + obj->builder = NULL; +} + +template<int StackSize> +int intersect(SVBVHTree *obj, Isect* isec) +{ + //TODO renable hint support + if(RE_rayobject_isAligned(obj->root)) + return bvh_node_stack_raycast<SVBVHNode,StackSize,false>( obj->root, isec); + else + return RE_rayobject_intersect( (RayObject*) obj->root, isec ); +} + +template<class Tree> +void bvh_hint_bb(Tree *tree, LCTSHint *hint, float *min, float *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> +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_svbvh_create(int size) +{ + return bvh_create_tree<SVBVHTree,DFS_STACK_SIZE>(size); +} +#else + +RayObject *RE_rayobject_svbvh_create(int size) +{ + puts("WARNING: SSE disabled at compile time\n"); + return NULL; +} + +#endif diff --git a/source/blender/render/intern/raytrace/rayobject_vbvh.cpp b/source/blender/render/intern/raytrace/rayobject_vbvh.cpp new file mode 100644 index 00000000000..11f04c04141 --- /dev/null +++ b/source/blender/render/intern/raytrace/rayobject_vbvh.cpp @@ -0,0 +1,191 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ***** + */ +int tot_pushup = 0; +int tot_pushdown = 0; +int tot_hints = 0; + + +#include <assert.h> +#include "rayobject.h" +#include "rayobject_rtbuild.h" +#include "RE_raytrace.h" +#include "BLI_memarena.h" +#include "MEM_guardedalloc.h" +#include "BKE_utildefines.h" +#include "BLI_arithb.h" + +#include "reorganize.h" +#include "bvh.h" +#include "vbvh.h" +#include "svbvh.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); + 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; + } + + reorganize(root); + remove_useless(root, &root); + bvh_refit(root); + + pushup(root); + pushdown(root); + obj->root = root; + } + else + { +/* + TODO + MemArena *arena2 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); + 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); + */ + } + + //Cleanup + rtbuild_free( obj->builder ); + obj->builder = NULL; + + obj->node_arena = arena1; + obj->cost = 1.0; +} + +template<int StackSize> +int intersect(VBVHTree *obj, Isect* isec) +{ + //TODO renable hint support + if(RE_rayobject_isAligned(obj->root)) + return bvh_node_stack_raycast<VBVHNode,StackSize,false>( obj->root, isec); + else + return RE_rayobject_intersect( (RayObject*) obj->root, isec ); +} + +template<class Tree> +void bvh_hint_bb(Tree *tree, LCTSHint *hint, float *min, float *max) +{ + //TODO renable hint support + { + hint->size = 0; + hint->stack[hint->size++] = (RayObject*)tree->root; + } +} + +void bfree(VBVHTree *tree) +{ + if(tot_pushup + tot_pushdown + tot_hints + tot_moves) + { + 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); +} + +/* 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> +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); +} diff --git a/source/blender/render/intern/raytrace/reorganize.h b/source/blender/render/intern/raytrace/reorganize.h new file mode 100644 index 00000000000..f0335b769d5 --- /dev/null +++ b/source/blender/render/intern/raytrace/reorganize.h @@ -0,0 +1,516 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ***** + */ +#include <stdio.h> +#include <math.h> +#include <algorithm> +#include <vector> +#include <queue> + +extern int tot_pushup; +extern int tot_pushdown; + +template<class Node> +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> +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); + } + } +} + +static int tot_moves = 0; +template<class Node> +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; + + tot_moves++; + } + + + } + } + if(node != root) + { + } + } +} + +/* + * Prunes useless nodes from trees: + * erases nodes with total ammount of primitives = 0 + * prunes nodes with only one child (except if that child is a primitive) + */ +template<class Node> +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 == 0) + *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 == 0) + *new_node = 0; +} + +/* + * Minimizes expected number of BBtest by colapsing nodes + * it uses surface area heuristic for determining whether a node should be colapsed + */ +template<class Node> +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; ) + { + float c_area = bb_area(child->bb, child->bb+3) ; + int nchilds = count_childs(child); + float original_cost = (c_area / p_area)*nchilds + 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> +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> +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 + * reajust nodes BB (useful if nodes childs where modified) + */ +template<class Node> +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 acording to a given test cost function + * with the purpose to reduce the expected cost (eg.: number of BB tests). + */ +#include <vector> +#include <cmath> +#define MAX_CUT_SIZE 16 +#define MAX_OPTIMIZE_CHILDS MAX_CUT_SIZE + +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) + { + 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) + { + 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 + { + OVBVHNode **cut = &(this->child); + set_cut( 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] = bb_area(child->bb, child->bb+3) / parent_area; + 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 archieve 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 += ( bb_area(child->bb, child->bb+3) / parent_area ) * 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; + } + } + } + assert(node->cut_cost[0] != INFINITY); + } + else + { + node->cut_cost[0] = 1.0f; + for(int i=1; i<MAX_CUT_SIZE; i++) + node->cut_cost[i] = INFINITY; + } + } + + Node *transform(Node *node) + { + if(RE_rayobject_isAligned(node->child)) + { + static int num = 0; + bool first = false; + if(num == 0) { num++; first = true; } + + calc_costs(node); + if(first) printf("expected cost = %f (%d)\n", node->cut_cost[0], node->best_cutsize ); + node->optimize(); + } + return node; + } +}; diff --git a/source/blender/render/intern/raytrace/svbvh.h b/source/blender/render/intern/raytrace/svbvh.h new file mode 100644 index 00000000000..80e6e2ea190 --- /dev/null +++ b/source/blender/render/intern/raytrace/svbvh.h @@ -0,0 +1,249 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ***** + */ +#ifdef __SSE__ + +#ifndef RE_RAYTRACE_SVBVH_H +#define RE_RAYTRACE_SVBVH_H + +#include "bvh.h" +#include "BLI_memarena.h" +#include <stdio.h> +#include <algorithm> + +struct SVBVHNode +{ + int nchilds; + + //Array of bb, array of childs + float *child_bb; + SVBVHNode **child; +}; + +template<> +inline int bvh_node_hit_test<SVBVHNode>(SVBVHNode *node, Isect *isec) +{ + return 1; +} + +template<> +inline void bvh_node_push_childs<SVBVHNode>(SVBVHNode *node, Isect *isec, SVBVHNode **stack, int &stack_pos) +{ + int i=0; + while(i+4 <= node->nchilds) + { + int res = test_bb_group4( (__m128*) (node->child_bb+6*i), isec ); + RE_RC_COUNT(isec->raycounter->simd_bb.test); + + if(res & 1) { stack[stack_pos++] = node->child[i+0]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); } + if(res & 2) { stack[stack_pos++] = node->child[i+1]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); } + if(res & 4) { stack[stack_pos++] = node->child[i+2]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); } + if(res & 8) { stack[stack_pos++] = node->child[i+3]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); } + + i += 4; + } + while(i < node->nchilds) + { + if(RE_rayobject_bb_intersect_test(isec, (const float*)node->child_bb+6*i)) + stack[stack_pos++] = node->child[i]; + i++; + } +} + +template<> +inline void bvh_node_merge_bb<SVBVHNode>(SVBVHNode *node, float *min, float *max) +{ + if(is_leaf(node)) + { + RE_rayobject_merge_bb( (RayObject*)node, min, max); + } + else + { + int i=0; + while(i+4 <= node->nchilds) + { + float *res = node->child_bb + 6*i; + for(int j=0; j<3; j++) + { + min[j] = MIN2(min[j], res[4*j+0]); + min[j] = MIN2(min[j], res[4*j+1]); + min[j] = MIN2(min[j], res[4*j+2]); + min[j] = MIN2(min[j], res[4*j+3]); + } + for(int j=0; j<3; j++) + { + max[j] = MAX2(max[j], res[4*(j+3)+0]); + max[j] = MAX2(max[j], res[4*(j+3)+1]); + max[j] = MAX2(max[j], res[4*(j+3)+2]); + max[j] = MAX2(max[j], res[4*(j+3)+3]); + } + + i += 4; + } + + 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() + { + 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)); + } + + SVBVHNode *create_node(int nchilds) + { + SVBVHNode *node = (SVBVHNode*)BLI_memarena_alloc(arena, sizeof(SVBVHNode)); + node->nchilds = nchilds; + node->child_bb = (float*)BLI_memarena_alloc(arena, sizeof(float)*6*nchilds); + node->child= (SVBVHNode**)BLI_memarena_alloc(arena, sizeof(SVBVHNode*)*nchilds); + + return node; + } + + void copy_bb(float *bb, const float *old_bb) + { + 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_MIN, FLT_MIN, FLT_MIN }; + alloc_childs--; + node->child[alloc_childs] = 0; + 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 + +#endif //__SSE__ diff --git a/source/blender/render/intern/raytrace/vbvh.h b/source/blender/render/intern/raytrace/vbvh.h new file mode 100644 index 00000000000..7d3dbffd125 --- /dev/null +++ b/source/blender/render/intern/raytrace/vbvh.h @@ -0,0 +1,237 @@ +/** + * $Id$ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ***** + */ +#include <assert.h> + +#include <algorithm> +#include "rayobject_rtbuild.h" +#include "BLI_memarena.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 *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(is_leaf(child->child)) + stack[stack_pos++] = child->child; + else +*/ + stack[stack_pos++] = child; + + child = child->sibling; + } + } +} + + +template<class Node> +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> +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 == 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(); + + INIT_MINMAX(node->bb, node->bb+3); + rtbuild_merge_bb(builder, node->bb, node->bb+3); + + Node **child = &node->child; + + int nc = rtbuild_split(builder); + assert(nc == 2); + for(int i=0; i<nc; i++) + { + RTBuilder tmp; + rtbuild_get_child(builder, i, &tmp); + + *child = _transform(&tmp); + child = &((*child)->sibling); + } + + *child = 0; + return node; + } + } +}; + +/* +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; + } +}; +*/ diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index a35e13e1df7..f54d7dd20ab 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -889,6 +889,28 @@ static void free_mesh_orco_hash(Render *re) } } +static void check_material_mapto(Material *ma) +{ + int a; + ma->mapto_textured = 0; + + /* cache which inputs are actually textured. + * this can avoid a bit of time spent iterating through all the texture slots, map inputs and map tos + * every time a property which may or may not be textured is accessed */ + + for(a=0; a<MAX_MTEX; a++) { + if(ma->mtex[a] && ma->mtex[a]->tex) { + /* currently used only in volume render, so we'll check for those flags */ + if(ma->mtex[a]->mapto & MAP_DENSITY) ma->mapto_textured |= MAP_DENSITY; + if(ma->mtex[a]->mapto & MAP_EMISSION) ma->mapto_textured |= MAP_EMISSION; + if(ma->mtex[a]->mapto & MAP_EMISSION_COL) ma->mapto_textured |= MAP_EMISSION_COL; + if(ma->mtex[a]->mapto & MAP_SCATTERING) ma->mapto_textured |= MAP_SCATTERING; + if(ma->mtex[a]->mapto & MAP_TRANSMISSION_COL) ma->mapto_textured |= MAP_TRANSMISSION_COL; + if(ma->mtex[a]->mapto & MAP_REFLECTION) ma->mapto_textured |= MAP_REFLECTION; + if(ma->mtex[a]->mapto & MAP_REFLECTION_COL) ma->mapto_textured |= MAP_REFLECTION_COL; + } + } +} static void flag_render_node_material(Render *re, bNodeTree *ntree) { bNode *node; @@ -920,7 +942,10 @@ static Material *give_render_material(Render *re, Object *ob, int nr) if(re->r.mode & R_SPEED) ma->texco |= NEED_UV; - if(ma->material_type == MA_TYPE_VOLUME) ma->mode |= MA_TRANSP; + if(ma->material_type == MA_TYPE_VOLUME) { + ma->mode |= MA_TRANSP; + ma->mode &= ~MA_SHADBUF; + } if((ma->mode & MA_TRANSP) && (ma->mode & MA_ZTRANSP)) re->flag |= R_ZTRA; @@ -930,6 +955,8 @@ static Material *give_render_material(Render *re, Object *ob, int nr) if(ma->nodetree && ma->use_nodes) flag_render_node_material(re, ma->nodetree); + check_material_mapto(ma); + return ma; } @@ -1474,7 +1501,7 @@ static void get_particle_uvco_mcol(short from, DerivedMesh *dm, float *fuv, int static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem *psys, int timeoffset) { Object *ob= obr->ob; - Object *tob=0; +// Object *tob=0; Material *ma=0; ParticleSystemModifierData *psmd; ParticleSystem *tpsys=0; @@ -1484,6 +1511,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem ParticleKey state; ParticleCacheKey *cache=0; ParticleBillboardData bb; + ParticleSimulationData sim = {re->scene, ob, psys, NULL}; ParticleStrandData sd; StrandBuffer *strandbuf=0; StrandVert *svert=0; @@ -1517,14 +1545,16 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem return 1; /* 2. start initialising things */ - if(part->phystype==PART_PHYS_KEYED) - psys_count_keyed_targets(ob,psys); /* last possibility to bail out! */ - psmd= psys_get_modifier(ob,psys); + sim.psmd = psmd = psys_get_modifier(ob,psys); if(!(psmd->modifier.mode & eModifierMode_Render)) return 0; + if(part->phystype==PART_PHYS_KEYED) + psys_count_keyed_targets(&sim); + + if(G.rendering == 0) { /* preview render */ totchild = (int)((float)totchild * (float)part->disp / 100.0f); } @@ -1611,14 +1641,14 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem #endif // XXX old animation system cfra = bsystem_time(re->scene, 0, (float)re->scene->r.cfra, 0.0); -/* 2.4 setup reactors */ - if(part->type == PART_REACTOR){ - psys_get_reactor_target(ob, psys, &tob, &tpsys); - if(tpsys && (part->from==PART_FROM_PARTICLE || part->phystype==PART_PHYS_NO)){ - psmd = psys_get_modifier(tob,tpsys); - tpart = tpsys->part; - } - } +///* 2.4 setup reactors */ +// if(part->type == PART_REACTOR){ +// psys_get_reactor_target(ob, psys, &tob, &tpsys); +// if(tpsys && (part->from==PART_FROM_PARTICLE || part->phystype==PART_PHYS_NO)){ +// psmd = psys_get_modifier(tob,tpsys); +// tpart = tpsys->part; +// } +// } /* 2.5 setup matrices */ Mat4MulMat4(mat, ob->obmat, re->viewmat); @@ -1695,7 +1725,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem } if(path_nbr == 0) - psys->lattice = psys_get_lattice(re->scene, ob, psys); + psys->lattice = psys_get_lattice(&sim); /* 3. start creating renderable things */ for(a=0,pa=pars; a<totpart+totchild; a++, pa++, seed++) { @@ -1786,8 +1816,8 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem pa_size = psys_get_child_size(psys, cpa, cfra, &pa_time); - r_tilt = 2.0f * cpa->rand[2]; - r_length = cpa->rand[1]; + r_tilt = 2.0f*(PSYS_FRAND(a + 21) - 0.5f); + r_length = PSYS_FRAND(a + 22); num = cpa->num; @@ -1952,7 +1982,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem continue; state.time = (part->draw & PART_ABS_PATH_TIME) ? -ct : ct; - psys_get_particle_on_path(re->scene,ob,psys,a,&state,1); + psys_get_particle_on_path(&sim,a,&state,1); if(psys->parent) Mat4MulVecfl(psys->parent->obmat, state.co); @@ -1971,7 +2001,7 @@ static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem else { time=0.0f; state.time=cfra; - if(psys_get_particle_state(re->scene,ob,psys,a,&state,0)==0) + if(psys_get_particle_state(&sim,a,&state,0)==0) continue; if(psys->parent) @@ -2996,7 +3026,7 @@ static void init_camera_inside_volumes(Render *re) for(vo= re->volumes.first; vo; vo= vo->next) { for(obi= re->instancetable.first; obi; obi= obi->next) { if (obi->obr == vo->obr) { - if (point_inside_volume_objectinstance(obi, co)) { + if (point_inside_volume_objectinstance(re, obi, co)) { MatInside *mi; mi = MEM_mallocN(sizeof(MatInside), "camera inside material"); @@ -3372,9 +3402,10 @@ static void initshadowbuf(Render *re, LampRen *lar, float mat[][4]) shb->bias= shb->bias*(100/re->r.size); /* halfway method (average of first and 2nd z) reduces bias issues */ - if(lar->buftype==LA_SHADBUF_HALFWAY) + if(ELEM(lar->buftype, LA_SHADBUF_HALFWAY, LA_SHADBUF_DEEP)) shb->bias= 0.1f*shb->bias; + shb->compressthresh= lar->compressthresh; } static void area_lamp_vectors(LampRen *lar) @@ -3456,6 +3487,7 @@ static GroupObject *add_render_lamp(Render *re, Object *ob) lar->clipend = la->clipend; lar->bias = la->bias; + lar->compressthresh = la->compressthresh; lar->type= la->type; lar->mode= la->mode; @@ -4820,8 +4852,6 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view) /* MAKE RENDER DATA */ database_init_objects(re, lay, 0, 0, 0, 0); - init_camera_inside_volumes(re); - if(!re->test_break(re->tbh)) { int tothalo; @@ -4846,6 +4876,8 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view) RE_make_stars(re, NULL, NULL, NULL, NULL); sort_halos(re, tothalo); + init_camera_inside_volumes(re); + re->i.infostr= "Creating Shadowbuffers"; re->stats_draw(re->sdh, &re->i); diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 8ef52991ca2..edf3cd83404 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -213,11 +213,15 @@ static void free_render_result(ListBase *lb, RenderResult *rr) /* all layers except the active one get temporally pushed away */ static void push_render_result(Render *re) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + /* officially pushed result should be NULL... error can happen with do_seq */ RE_FreeRenderResult(re->pushedresult); re->pushedresult= re->result; re->result= NULL; + + BLI_rw_mutex_unlock(&re->resultmutex); } /* if scemode is R_SINGLE_LAYER, at end of rendering, merge the both render results */ @@ -229,6 +233,8 @@ static void pop_render_result(Render *re) return; } if(re->pushedresult) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + if(re->pushedresult->rectx==re->result->rectx && re->pushedresult->recty==re->result->recty) { /* find which layer in pushedresult should be replaced */ SceneRenderLayer *srl; @@ -255,6 +261,8 @@ static void pop_render_result(Render *re) RE_FreeRenderResult(re->pushedresult); re->pushedresult= NULL; + + BLI_rw_mutex_unlock(&re->resultmutex); } } @@ -350,6 +358,13 @@ static char *get_pass_name(int passtype, int channel) if(channel==-1) return "Mist"; return "Mist.Z"; } + if(passtype == SCE_PASS_RAYHITS) + { + if(channel==-1) return "Rayhits"; + if(channel==0) return "Rayhits.R"; + if(channel==1) return "Rayhits.G"; + return "Rayhits.B"; + } return "Unknown"; } @@ -401,12 +416,14 @@ static int passtype_from_name(char *str) if(strcmp(str, "Mist")==0) return SCE_PASS_MIST; + if(strcmp(str, "RAYHITS")==0) + return SCE_PASS_RAYHITS; return 0; } static void render_unique_exr_name(Render *re, char *str, int sample) { - char di[FILE_MAX], name[FILE_MAXFILE], fi[FILE_MAXFILE]; + char di[FILE_MAX], name[FILE_MAXFILE+MAX_ID_NAME+100], fi[FILE_MAXFILE]; BLI_strncpy(di, G.sce, FILE_MAX); BLI_splitdirstring(di, fi); @@ -528,7 +545,7 @@ static RenderResult *new_render_result(Render *re, rcti *partrct, int crop, int rl->lay= srl->lay; rl->lay_zmask= srl->lay_zmask; rl->layflag= srl->layflag; - rl->passflag= srl->passflag; + rl->passflag= srl->passflag | SCE_PASS_RAYHITS; rl->pass_xor= srl->pass_xor; rl->light_override= srl->light_override; rl->mat_override= srl->mat_override; @@ -572,6 +589,8 @@ static RenderResult *new_render_result(Render *re, rcti *partrct, int crop, int render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXOB); if(srl->passflag & SCE_PASS_MIST) render_layer_add_pass(rr, rl, 1, SCE_PASS_MIST); + if(rl->passflag & SCE_PASS_RAYHITS) + render_layer_add_pass(rr, rl, 4, SCE_PASS_RAYHITS); } /* sss, previewrender and envmap don't do layers, so we make a default one */ @@ -920,6 +939,8 @@ static void read_render_result(Render *re, int sample) { char str[FILE_MAX]; + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + RE_FreeRenderResult(re->result); re->result= new_render_result(re, &re->disprect, 0, RR_USEMEM); @@ -928,6 +949,8 @@ static void read_render_result(Render *re, int sample) if(!read_render_result_from_file(str, re->result)) printf("cannot read: %s\n", str); + + BLI_rw_mutex_unlock(&re->resultmutex); } /* *************************************************** */ @@ -946,13 +969,32 @@ Render *RE_GetRender(const char *name) } /* if you want to know exactly what has been done */ -RenderResult *RE_GetResult(Render *re) +RenderResult *RE_AcquireResultRead(Render *re) { - if(re) + if(re) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ); + return re->result; + } + + return NULL; +} + +RenderResult *RE_AcquireResultWrite(Render *re) +{ + if(re) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); return re->result; + } + return NULL; } +void RE_ReleaseResult(Render *re) +{ + if(re) + BLI_rw_mutex_unlock(&re->resultmutex); +} + /* displist.c util.... */ Scene *RE_GetScene(Render *re) { @@ -973,38 +1015,49 @@ RenderLayer *render_get_active_layer(Render *re, RenderResult *rr) /* fill provided result struct with what's currently active or done */ -void RE_GetResultImage(Render *re, RenderResult *rr) +void RE_AcquireResultImage(Render *re, RenderResult *rr) { memset(rr, 0, sizeof(RenderResult)); - if(re && re->result) { - RenderLayer *rl; - - rr->rectx= re->result->rectx; - rr->recty= re->result->recty; - - rr->rectf= re->result->rectf; - rr->rectz= re->result->rectz; - rr->rect32= re->result->rect32; - - /* active layer */ - rl= render_get_active_layer(re, re->result); + if(re) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ); - if(rl) { - if(rr->rectf==NULL) - rr->rectf= rl->rectf; - if(rr->rectz==NULL) - rr->rectz= RE_RenderLayerGetPass(rl, SCE_PASS_Z); + if(re->result) { + RenderLayer *rl; + + rr->rectx= re->result->rectx; + rr->recty= re->result->recty; + + rr->rectf= re->result->rectf; + rr->rectz= re->result->rectz; + rr->rect32= re->result->rect32; + + /* active layer */ + rl= render_get_active_layer(re, re->result); + + if(rl) { + if(rr->rectf==NULL) + rr->rectf= rl->rectf; + if(rr->rectz==NULL) + rr->rectz= RE_RenderLayerGetPass(rl, SCE_PASS_Z); + } } } } +void RE_ReleaseResultImage(Render *re) +{ + if(re) + BLI_rw_mutex_unlock(&re->resultmutex); +} + /* caller is responsible for allocating rect in correct size! */ void RE_ResultGet32(Render *re, unsigned int *rect) { RenderResult rres; - RE_GetResultImage(re, &rres); + RE_AcquireResultImage(re, &rres); + if(rres.rect32) memcpy(rect, rres.rect32, sizeof(int)*rres.rectx*rres.recty); else if(rres.rectf) { @@ -1022,6 +1075,8 @@ void RE_ResultGet32(Render *re, unsigned int *rect) else /* else fill with black */ memset(rect, 0, sizeof(int)*re->rectx*re->recty); + + RE_ReleaseResultImage(re); } @@ -1042,12 +1097,15 @@ Render *RE_NewRender(const char *name) re= MEM_callocN(sizeof(Render), "new render"); BLI_addtail(&RenderList, re); strncpy(re->name, name, RE_MAXNAME); + BLI_rw_mutex_init(&re->resultmutex); } /* prevent UI to draw old results */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); RE_FreeRenderResult(re->result); re->result= NULL; re->result_ok= 0; + BLI_rw_mutex_unlock(&re->resultmutex); /* set default empty callbacks */ re->display_init= result_nothing; @@ -1072,6 +1130,7 @@ Render *RE_NewRender(const char *name) /* only call this while you know it will remove the link too */ void RE_FreeRender(Render *re) { + BLI_rw_mutex_end(&re->resultmutex); free_renderdata_tables(re); free_sample_tables(re); @@ -1153,6 +1212,8 @@ void RE_InitState(Render *re, Render *source, RenderData *rd, int winx, int winy make_sample_tables(re); /* if preview render, we try to keep old result */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + if(re->r.scemode & R_PREVIEWBUTS) { if(re->result && re->result->rectx==re->rectx && re->result->recty==re->recty); else { @@ -1168,6 +1229,8 @@ void RE_InitState(Render *re, Render *source, RenderData *rd, int winx, int winy re->result->rectx= re->rectx; re->result->recty= re->recty; } + + BLI_rw_mutex_unlock(&re->resultmutex); /* we clip faces with a minimum of 2 pixel boundary outside of image border. see zbuf.c */ re->clipcrop= 1.0f + 2.0f/(float)(re->winx>re->winy?re->winy:re->winx); @@ -1184,8 +1247,12 @@ void RE_SetDispRect (struct Render *re, rcti *disprect) re->recty= disprect->ymax-disprect->ymin; /* initialize render result */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + RE_FreeRenderResult(re->result); re->result= new_render_result(re, &re->disprect, 0, RR_USEMEM); + + BLI_rw_mutex_unlock(&re->resultmutex); } void RE_SetWindow(Render *re, rctf *viewplane, float clipsta, float clipend) @@ -1345,11 +1412,15 @@ static void render_tile_processor(Render *re, int firsttile) if(re->test_break(re->tbh)) return; + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + /* hrmf... exception, this is used for preview render, re-entrant, so render result has to be re-used */ if(re->result==NULL || re->result->layers.first==NULL) { if(re->result) RE_FreeRenderResult(re->result); re->result= new_render_result(re, &re->disprect, 0, RR_USEMEM); } + + BLI_rw_mutex_unlock(&re->resultmutex); re->stats_draw(re->sdh, &re->i); @@ -1357,7 +1428,7 @@ static void render_tile_processor(Render *re, int firsttile) return; initparts(re); - + /* assuming no new data gets added to dbase... */ R= *re; @@ -1384,7 +1455,7 @@ static void render_tile_processor(Render *re, int firsttile) break; } } - + freeparts(re); } @@ -1522,6 +1593,8 @@ static void threaded_tile_processor(Render *re) rctf viewplane= re->viewplane; int rendering=1, counter= 1, drawtimer=0, hasdrawn, minx=0; + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + /* first step; free the entire render result, make new, and/or prepare exr buffer saving */ if(re->result==NULL || !(re->r.scemode & R_PREVIEWBUTS)) { RE_FreeRenderResult(re->result); @@ -1533,6 +1606,8 @@ static void threaded_tile_processor(Render *re) else re->result= new_render_result(re, &re->disprect, 0, re->r.scemode & (R_EXR_TILE_FILE|R_FULL_SAMPLE)); } + + BLI_rw_mutex_unlock(&re->resultmutex); if(re->result==NULL) return; @@ -1540,7 +1615,7 @@ static void threaded_tile_processor(Render *re) /* warning; no return here without closing exr file */ initparts(re); - + if(re->result->exrhandle) { RenderResult *rr; char str[FILE_MAX]; @@ -1629,6 +1704,8 @@ static void threaded_tile_processor(Render *re) } + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + if(re->result->exrhandle) { RenderResult *rr; @@ -1644,6 +1721,8 @@ static void threaded_tile_processor(Render *re) read_render_result(re, 0); } + + BLI_rw_mutex_unlock(&re->resultmutex); /* unset threadsafety */ g_break= 0; @@ -1823,8 +1902,10 @@ static void do_render_blur_3d(Render *re) } /* swap results */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); RE_FreeRenderResult(re->result); re->result= rres; + BLI_rw_mutex_unlock(&re->resultmutex); set_mblur_offs(0.0f); re->i.curblur= 0; /* stats */ @@ -1894,8 +1975,11 @@ static void do_render_fields_3d(Render *re) do_render_blur_3d(re); else do_render_3d(re); + + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); rr1= re->result; re->result= NULL; + BLI_rw_mutex_unlock(&re->resultmutex); /* second field */ if(!re->test_break(re->tbh)) { @@ -1921,8 +2005,11 @@ static void do_render_fields_3d(Render *re) re->recty *= 2; re->disprect.ymin *= 2; re->disprect.ymax *= 2; + + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); re->result= new_render_result(re, &re->disprect, 0, RR_USEMEM); - + RE_FreeRenderResult(rr1); + if(rr2) { if(re->r.mode & R_ODDFIELD) merge_renderresult_fields(re->result, rr2, rr1); @@ -1931,12 +2018,14 @@ static void do_render_fields_3d(Render *re) RE_FreeRenderResult(rr2); } - RE_FreeRenderResult(rr1); re->i.curfield= 0; /* stats */ /* weak... the display callback wants an active renderlayer pointer... */ re->result->renlay= render_get_active_layer(re, re->result); + + BLI_rw_mutex_unlock(&re->resultmutex); + re->display_draw(re->ddh, re->result, NULL); } @@ -2000,6 +2089,8 @@ static void do_render_fields_blur_3d(Render *re) if((re->r.mode & R_CROP)==0) { RenderResult *rres; + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + /* sub-rect for merge call later on */ re->result->tilerect= re->disprect; @@ -2020,9 +2111,16 @@ static void do_render_fields_blur_3d(Render *re) /* weak... the display callback wants an active renderlayer pointer... */ re->result->renlay= render_get_active_layer(re, re->result); + BLI_rw_mutex_unlock(&re->resultmutex); + re->display_init(re->dih, re->result); re->display_draw(re->ddh, re->result, NULL); } + else { + /* set offset (again) for use in compositor, disprect was manipulated. */ + re->result->xof= 0; + re->result->yof= 0; + } } } } @@ -2176,7 +2274,7 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree) } /* ensure we get either composited result or the active layer */ - RE_GetResultImage(re, &rres); + RE_AcquireResultImage(re, &rres); /* accumulate with filter, and clip */ mask= (1<<sample); @@ -2195,6 +2293,8 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree) } } + RE_ReleaseResultImage(re); + /* show stuff */ if(sample!=re->osa-1) { /* weak... the display callback wants an active renderlayer pointer... */ @@ -2206,9 +2306,11 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree) break; } + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); if(re->result->rectf) MEM_freeN(re->result->rectf); re->result->rectf= rectf; + BLI_rw_mutex_unlock(&re->resultmutex); } void RE_MergeFullSample(Render *re, Scene *sce, bNodeTree *ntree) @@ -2309,9 +2411,12 @@ static void do_render_composite_fields_blur_3d(Render *re) static void renderresult_stampinfo(Scene *scene) { RenderResult rres; + Render *re= RE_GetRender(scene->id.name); + /* this is the basic trick to get the displayed float or char rect from render result */ - RE_GetResultImage(RE_GetRender(scene->id.name), &rres); + RE_AcquireResultImage(re, &rres); BKE_stamp_buf(scene, (unsigned char *)rres.rect32, rres.rectf, rres.rectx, rres.recty, 4); + RE_ReleaseResultImage(re); } static void do_render_seq(Render * re) @@ -2327,6 +2432,8 @@ static void do_render_seq(Render * re) recurs_depth--; + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + if(ibuf) { if(ibuf->rect_float) { if (!rr->rectf) @@ -2369,6 +2476,8 @@ static void do_render_seq(Render * re) else rr->rect32= MEM_callocN(sizeof(int)*rr->rectx*rr->recty, "render_seq rect"); } + + BLI_rw_mutex_unlock(&re->resultmutex); } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -2388,14 +2497,15 @@ static void do_render_all_options(Render *re) re->stats_draw(re->sdh, &re->i); re->display_draw(re->ddh, re->result, NULL); - } else { do_render_composite_fields_blur_3d(re); } /* for UI only */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); renderresult_add_names(re->result); + BLI_rw_mutex_unlock(&re->resultmutex); re->i.lastframetime= PIL_check_seconds_timer()- re->i.starttime; @@ -2622,7 +2732,7 @@ static void do_write_image_or_movie(Render *re, Scene *scene, bMovieHandle *mh) char name[FILE_MAX]; RenderResult rres; - RE_GetResultImage(re, &rres); + RE_AcquireResultImage(re, &rres); /* write movie or image */ if(BKE_imtype_is_movie(scene->r.imtype)) { @@ -2686,6 +2796,8 @@ static void do_write_image_or_movie(Render *re, Scene *scene, bMovieHandle *mh) } } + RE_ReleaseResultImage(re); + BLI_timestr(re->i.lastframetime, name); printf(" Time: %s\n", name); fflush(stdout); /* needed for renderd !! (not anymore... (ton)) */ @@ -2723,7 +2835,8 @@ void RE_BlenderAnim(Render *re, Scene *scene, int sfra, int efra, int tfra) do_write_image_or_movie(re, scene, mh); } } else { - re->test_break(re->tbh); + if(re->test_break(re->tbh)) + G.afbreek= 1; } } } else { @@ -2769,9 +2882,10 @@ void RE_BlenderAnim(Render *re, Scene *scene, int sfra, int efra, int tfra) do_render_all_options(re); - if(re->test_break(re->tbh) == 0) { + if(re->test_break(re->tbh) == 0) do_write_image_or_movie(re, scene, mh); - } + else + G.afbreek= 1; if(G.afbreek==1) { /* remove touched file */ @@ -3006,6 +3120,7 @@ static void external_render_3d(Render *re, RenderEngineType *type) { RenderEngine engine; + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); if(re->result==NULL || !(re->r.scemode & R_PREVIEWBUTS)) { RE_FreeRenderResult(re->result); @@ -3014,6 +3129,7 @@ static void external_render_3d(Render *re, RenderEngineType *type) else re->result= new_render_result(re, &re->disprect, 0, 0); // XXX re->r.scemode & (R_EXR_TILE_FILE|R_FULL_SAMPLE)); } + BLI_rw_mutex_unlock(&re->resultmutex); if(re->result==NULL) return; @@ -3027,6 +3143,7 @@ static void external_render_3d(Render *re, RenderEngineType *type) free_render_result(&engine.fullresult, engine.fullresult.first); + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); if(re->result->exrhandle) { RenderResult *rr; @@ -3042,5 +3159,6 @@ static void external_render_3d(Render *re, RenderEngineType *type) read_render_result(re, 0); } + BLI_rw_mutex_unlock(&re->resultmutex); } diff --git a/source/blender/render/intern/source/pointdensity.c b/source/blender/render/intern/source/pointdensity.c index 5f8cf5504fa..b7832e74cd1 100644 --- a/source/blender/render/intern/source/pointdensity.c +++ b/source/blender/render/intern/source/pointdensity.c @@ -92,6 +92,7 @@ static void pointdensity_cache_psys(Render *re, PointDensity *pd, Object *ob, Pa { DerivedMesh* dm; ParticleKey state; + ParticleSimulationData sim = {re->scene, ob, psys, NULL}; ParticleData *pa=NULL; float cfra = bsystem_time(re->scene, ob, (float)re->scene->r.cfra, 0.0); int i, childexists; @@ -120,7 +121,7 @@ static void pointdensity_cache_psys(Render *re, PointDensity *pd, Object *ob, Pa Mat4Invert(ob->imat, ob->obmat); total_particles = psys->totpart+psys->totchild; - psys->lattice=psys_get_lattice(re->scene,ob,psys); + psys->lattice=psys_get_lattice(&sim); pd->point_tree = BLI_bvhtree_new(total_particles, 0.0, 4, 6); alloc_point_data(pd, total_particles, data_used); @@ -133,7 +134,7 @@ static void pointdensity_cache_psys(Render *re, PointDensity *pd, Object *ob, Pa for (i=0, pa=psys->particles; i < total_particles; i++, pa++) { state.time = cfra; - if(psys_get_particle_state(re->scene, ob, psys, i, &state, 0)) { + if(psys_get_particle_state(&sim, i, &state, 0)) { VECCOPY(partco, state.co); diff --git a/source/blender/render/intern/source/rayshade.c b/source/blender/render/intern/source/rayshade.c index d2599f6050c..4ad4d6370f8 100644 --- a/source/blender/render/intern/source/rayshade.c +++ b/source/blender/render/intern/source/rayshade.c @@ -25,10 +25,12 @@ * ***** END GPL LICENSE BLOCK ***** */ +#include <stdio.h> #include <math.h> #include <string.h> #include <stdlib.h> #include <float.h> +#include <assert.h> #include "MEM_guardedalloc.h" @@ -57,6 +59,9 @@ #include "volumetric.h" #include "RE_raytrace.h" +#include "rayobject.h" +#include "raycounter.h" + #define RAY_TRA 1 #define RAY_TRAFLIP 2 @@ -68,159 +73,396 @@ /* only to be used here in this file, it's for speed */ extern struct Render R; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -static void vlr_face_coords(RayFace *face, float **v1, float **v2, float **v3, float **v4) +static int test_break(void *data) { - VlakRen *vlr= (VlakRen*)face; - - *v1 = (vlr->v1)? vlr->v1->co: NULL; - *v2 = (vlr->v2)? vlr->v2->co: NULL; - *v3 = (vlr->v3)? vlr->v3->co: NULL; - *v4 = (vlr->v4)? vlr->v4->co: NULL; + Render *re = (Render*)data; + return re->test_break(re->tbh); } -static int vlr_check_intersect(Isect *is, int ob, RayFace *face) +static void RE_rayobject_config_control(RayObject *r, Render *re) { - ObjectInstanceRen *obi= RAY_OBJECT_GET((Render*)is->userdata, ob); - VlakRen *vlr = (VlakRen*)face; - - /* for baking selected to active non-traceable materials might still - * be in the raytree */ - if(!(vlr->mat->mode & MA_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 (is->lay & obi->lay); + if(RE_rayobject_isRayAPI(r)) + { + r = RE_rayobject_align( r ); + r->control.data = re; + r->control.test_break = test_break; + } } -static int vlr_check_intersect_solid(Isect *is, int ob, RayFace *face) +RayObject* RE_rayobject_create(Render *re, int type, int size) { - VlakRen *vlr = (VlakRen*)face; + RayObject * res = NULL; - /* solid material types only */ - if (vlr->mat->material_type == MA_TYPE_SURFACE) - return 1; + if(type == R_RAYSTRUCTURE_AUTO) + { + //TODO + //if(detect_simd()) +#ifdef __SSE__ + type = R_RAYSTRUCTURE_SIMD_SVBVH; +#else + type = R_RAYSTRUCTURE_VBVH; +#endif + } + +#ifndef __SSE__ + if(type == R_RAYSTRUCTURE_SIMD_SVBVH || type == R_RAYSTRUCTURE_SIMD_QBVH) + { + puts("Warning: Using VBVH (SSE was disabled at compile time)"); + type = R_RAYSTRUCTURE_VBVH; + } +#endif + + + if(type == R_RAYSTRUCTURE_OCTREE) //TODO dynamic ocres + res = RE_rayobject_octree_create(re->r.ocres, size); + else if(type == R_RAYSTRUCTURE_BLIBVH) + res = RE_rayobject_blibvh_create(size); + else if(type == R_RAYSTRUCTURE_VBVH) + res = RE_rayobject_vbvh_create(size); + else if(type == R_RAYSTRUCTURE_SIMD_SVBVH) + res = RE_rayobject_svbvh_create(size); + else if(type == R_RAYSTRUCTURE_SIMD_QBVH) + res = RE_rayobject_qbvh_create(size); else - return 0; + res = RE_rayobject_vbvh_create(size); //Fallback + + + if(res) + RE_rayobject_config_control( res, re ); + + return res; } -static float *vlr_get_transform(void *userdata, int i) -{ - ObjectInstanceRen *obi= RAY_OBJECT_GET((Render*)userdata, i); +#ifdef RE_RAYCOUNTER +RayCounter re_rc_counter[BLENDER_MAX_THREADS]; +#endif - return (obi->flag & R_TRANSFORMED)? (float*)obi->mat: NULL; -} void freeraytree(Render *re) { - if(re->raytree) { - RE_ray_tree_free(re->raytree); - re->raytree= NULL; + ObjectInstanceRen *obi; + + if(re->raytree) + { + RE_rayobject_free(re->raytree); + re->raytree = NULL; + } + if(re->rayfaces) + { + MEM_freeN(re->rayfaces); + re->rayfaces = NULL; + } + if(re->rayprimitives) + { + MEM_freeN(re->rayprimitives); + re->rayprimitives = NULL; + } + + for(obi=re->instancetable.first; obi; obi=obi->next) + { + ObjectRen *obr = obi->obr; + if(obr->raytree) + { + RE_rayobject_free(obr->raytree); + obr->raytree = NULL; + } + if(obr->rayfaces) + { + MEM_freeN(obr->rayfaces); + obr->rayfaces = NULL; + } + if(obi->raytree) + { + RE_rayobject_free(obi->raytree); + obi->raytree = NULL; + } + } + +#ifdef RE_RAYCOUNTER + { + RayCounter sum; + memset( &sum, 0, sizeof(sum) ); + int i; + for(i=0; i<BLENDER_MAX_THREADS; i++) + RE_RC_MERGE(&sum, re_rc_counter+i); + RE_RC_INFO(&sum); } +#endif } -void makeraytree(Render *re) +static int is_raytraceable_vlr(Render *re, VlakRen *vlr) { - ObjectInstanceRen *obi; - ObjectRen *obr; - VlakRen *vlr= NULL; - float min[3], max[3], co1[3], co2[3], co3[3], co4[3]; - double lasttime= PIL_check_seconds_timer(); - int v, totv = 0, totface = 0; + if((re->flag & R_BAKE_TRACE) || (vlr->mat->mode & MA_TRACEBLE)) + if(vlr->mat->material_type != MA_TYPE_WIRE) + return 1; + return 0; +} - INIT_MINMAX(min, max); +static int is_raytraceable(Render *re, ObjectInstanceRen *obi) +{ + int v; + ObjectRen *obr = obi->obr; - /* first min max raytree space */ - for(obi=re->instancetable.first; obi; obi=obi->next) { - obr= obi->obr; - - if(re->excludeob && obr->ob == re->excludeob) - continue; - - for(v=0;v<obr->totvlak;v++) { - if((v & 255)==0) vlr= obr->vlaknodes[v>>8].vlak; - else vlr++; - /* baking selected to active needs non-traceable too */ - if((re->flag & R_BAKE_TRACE) || (vlr->mat->mode & MA_TRACEBLE)) { - if(vlr->mat->material_type != MA_TYPE_WIRE) { - VECCOPY(co1, vlr->v1->co); - VECCOPY(co2, vlr->v2->co); - VECCOPY(co3, vlr->v3->co); - - if(obi->flag & R_TRANSFORMED) { - Mat4MulVecfl(obi->mat, co1); - Mat4MulVecfl(obi->mat, co2); - Mat4MulVecfl(obi->mat, co3); - } + if(re->excludeob && obr->ob == re->excludeob) + return 0; + + for(v=0;v<obr->totvlak;v++) + { + VlakRen *vlr = obr->vlaknodes[v>>8].vlak + (v&255); + if(is_raytraceable_vlr(re, vlr)) + return 1; + } + return 0; +} - DO_MINMAX(co1, min, max); - DO_MINMAX(co2, min, max); - DO_MINMAX(co3, min, max); - if(vlr->v4) { - VECCOPY(co4, vlr->v4->co); - if(obi->flag & R_TRANSFORMED) - Mat4MulVecfl(obi->mat, co4); - DO_MINMAX(co4, min, max); - } +RayObject* makeraytree_object(Render *re, ObjectInstanceRen *obi) +{ + //TODO + // out-of-memory safeproof + // break render + // update render stats + ObjectRen *obr = obi->obr; + + if(obr->raytree == NULL) + { + RayObject *raytree; + RayFace *face = NULL; + VlakPrimitive *vlakprimitive = NULL; + int v; + + //Count faces + int faces = 0; + for(v=0;v<obr->totvlak;v++) + { + VlakRen *vlr = obr->vlaknodes[v>>8].vlak + (v&255); + if(is_raytraceable_vlr(re, vlr)) + faces++; + } + assert( faces > 0 ); - totface++; + //Create Ray cast accelaration structure + raytree = obr->raytree = RE_rayobject_create( re, re->r.raytrace_structure, faces ); + if( (re->r.raytrace_options & R_RAYTRACE_USE_LOCAL_COORDS) ) + vlakprimitive = obr->rayprimitives = (VlakPrimitive*)MEM_callocN(faces*sizeof(VlakPrimitive), "ObjectRen primitives"); + else + face = obr->rayfaces = (RayFace*)MEM_callocN(faces*sizeof(RayFace), "ObjectRen faces"); + + obr->rayobi = obi; + + for(v=0;v<obr->totvlak;v++) + { + VlakRen *vlr = obr->vlaknodes[v>>8].vlak + (v&255); + if(is_raytraceable_vlr(re, vlr)) + { + if( (re->r.raytrace_options & R_RAYTRACE_USE_LOCAL_COORDS) ) + { + RE_rayobject_add( raytree, RE_vlakprimitive_from_vlak( vlakprimitive, obi, vlr ) ); + vlakprimitive++; + } + else + { + RE_rayface_from_vlak( face, obi, vlr ); + RE_rayobject_add( raytree, RE_rayobject_unalignRayFace(face) ); + face++; } } } + RE_rayobject_done( raytree ); } - re->raytree= RE_ray_tree_create(re->r.ocres, totface, min, max, - vlr_face_coords, vlr_check_intersect, vlr_get_transform, re); - if(min[0] > max[0]) { /* empty raytree */ - RE_ray_tree_done(re->raytree); - return; + if((obi->flag & R_TRANSFORMED) && obi->raytree == NULL) + { + obi->transform_primitives = 0; + obi->raytree = RE_rayobject_instance_create( obr->raytree, obi->mat, obi, obi->obr->rayobi ); } + + if(obi->raytree) return obi->raytree; + return obi->obr->raytree; +} + +static int has_special_rayobject(Render *re, ObjectInstanceRen *obi) +{ + if( (obi->flag & R_TRANSFORMED) && (re->r.raytrace_options & R_RAYTRACE_USE_INSTANCES) ) + { + ObjectRen *obr = obi->obr; + int v, faces = 0; + + for(v=0;v<obr->totvlak;v++) + { + VlakRen *vlr = obr->vlaknodes[v>>8].vlak + (v&255); + if(is_raytraceable_vlr(re, vlr)) + { + faces++; + if(faces > 4) + return 1; + } + } + } + return 0; +} +/* + * create a single raytrace structure with all faces + */ +static void makeraytree_single(Render *re) +{ + ObjectInstanceRen *obi; + RayObject *raytree; + RayFace *face = NULL; + VlakPrimitive *vlakprimitive = NULL; + int faces = 0, obs = 0, special = 0; - for(obi=re->instancetable.first; obi; obi=obi->next) { - obr= obi->obr; + for(obi=re->instancetable.first; obi; obi=obi->next) + if(is_raytraceable(re, obi)) + { + int v; + ObjectRen *obr = obi->obr; + obs++; + + if(has_special_rayobject(re, obi)) + { + special++; + } + else + { + for(v=0;v<obr->totvlak;v++) + { + VlakRen *vlr = obr->vlaknodes[v>>8].vlak + (v&255); + if(is_raytraceable_vlr(re, vlr)) + faces++; + } + } + } + + if(faces + special == 0) + { + re->raytree = RE_rayobject_empty_create(); + return; + } + + //Create raytree + raytree = re->raytree = RE_rayobject_create( re, re->r.raytrace_structure, faces+special ); - if(re->excludeob && obr->ob == re->excludeob) - continue; + if( (re->r.raytrace_options & R_RAYTRACE_USE_LOCAL_COORDS) ) + { + vlakprimitive = re->rayprimitives = (VlakPrimitive*)MEM_callocN(faces*sizeof(VlakPrimitive), "Raytrace vlak-primitives"); + } + else + { + face = re->rayfaces = (RayFace*)MEM_callocN(faces*sizeof(RayFace), "Render ray faces"); + } + + for(obi=re->instancetable.first; obi; obi=obi->next) + if(is_raytraceable(re, obi)) + { + if(test_break(re)) + break; - for(v=0; v<obr->totvlak; v++, totv++) { - if((v & 255)==0) { - double time= PIL_check_seconds_timer(); + if(has_special_rayobject(re, obi)) + { + RayObject *obj = makeraytree_object(re, obi); + RE_rayobject_add( re->raytree, obj ); + } + else + { + int v; + ObjectRen *obr = obi->obr; + + if(obi->flag & R_TRANSFORMED) + { + obi->transform_primitives = 1; + } - vlr= obr->vlaknodes[v>>8].vlak; - if(re->test_break(re->tbh)) - break; - if(time-lasttime>1.0f) { - char str[32]; - sprintf(str, "Filling Octree: %d", totv); - re->i.infostr= str; - re->stats_draw(re->sdh, &re->i); - re->i.infostr= NULL; - lasttime= time; + for(v=0;v<obr->totvlak;v++) + { + VlakRen *vlr = obr->vlaknodes[v>>8].vlak + (v&255); + if(is_raytraceable_vlr(re, vlr)) + { + if( (re->r.raytrace_options & R_RAYTRACE_USE_LOCAL_COORDS) ) + { + RayObject *obj = RE_vlakprimitive_from_vlak( vlakprimitive, obi, vlr ); + RE_rayobject_add( raytree, obj ); + vlakprimitive++; + } + else + { + RE_rayface_from_vlak(face, obi, vlr); + if((obi->flag & R_TRANSFORMED)) + { + Mat4MulVecfl(obi->mat, face->v1); + Mat4MulVecfl(obi->mat, face->v2); + Mat4MulVecfl(obi->mat, face->v3); + if(RE_rayface_isQuad(face)) + Mat4MulVecfl(obi->mat, face->v4); + } + + RE_rayobject_add( raytree, RE_rayobject_unalignRayFace(face) ); + face++; + } } } - else vlr++; - - if((re->flag & R_BAKE_TRACE) || (vlr->mat->mode & MA_TRACEBLE)) - if(vlr->mat->material_type != MA_TYPE_WIRE) - RE_ray_tree_add_face(re->raytree, RAY_OBJECT_SET(re, obi), vlr); } } + + if(!test_break(re)) + { + re->i.infostr= "Raytree.. building"; + re->stats_draw(re->sdh, &re->i); - RE_ray_tree_done(re->raytree); + RE_rayobject_done( raytree ); + } +} + +void makeraytree(Render *re) +{ + float min[3], max[3], sub[3]; + int i; - re->i.infostr= NULL; + re->i.infostr= "Raytree.. preparing"; re->stats_draw(re->sdh, &re->i); + + /* disable options not yet suported by octree, + they might actually never be supported (unless people really need it) */ + if(re->r.raytrace_structure == R_RAYSTRUCTURE_OCTREE) + re->r.raytrace_options &= ~( R_RAYTRACE_USE_INSTANCES | R_RAYTRACE_USE_LOCAL_COORDS); + + BENCH(makeraytree_single(re), tree_build); + if(test_break(re)) + { + freeraytree(re); + + re->i.infostr= "Raytree building canceled"; + re->stats_draw(re->sdh, &re->i); + } + else + { + //Calculate raytree max_size + //This is ONLY needed to kept a bogus behaviour of SUN and HEMI lights + RE_rayobject_merge_bb( re->raytree, min, max ); + for(i=0; i<3; i++) + { + min[i] += 0.01f; + max[i] += 0.01f; + sub[i] = max[i]-min[i]; + } + re->maxdist = sqrt( sub[0]*sub[0] + sub[1]*sub[1] + sub[2]*sub[2] ); + + re->i.infostr= "Raytree finished"; + re->stats_draw(re->sdh, &re->i); + } + +#ifdef RE_RAYCOUNTER + memset( re_rc_counter, 0, sizeof(re_rc_counter) ); +#endif } + + void shade_ray(Isect *is, ShadeInput *shi, ShadeResult *shr) { - VlakRen *vlr= (VlakRen*)is->face; - ObjectInstanceRen *obi= RAY_OBJECT_GET(&R, is->ob); + ObjectInstanceRen *obi= (ObjectInstanceRen*)is->hit.ob; + VlakRen *vlr= (VlakRen*)is->hit.face; int osatex= 0; /* set up view vector */ @@ -450,7 +692,7 @@ static void traceray(ShadeInput *origshi, ShadeResult *origshr, short depth, flo ShadeResult shr; Isect isec; float f, f1, fr, fg, fb; - float ref[3], maxsize=RE_ray_tree_max_size(R.raytree); + float ref[3]; float dist_mir = origshi->mat->dist_mir; /* Warning, This is not that nice, and possibly a bit slow for every ray, @@ -459,20 +701,17 @@ static void traceray(ShadeInput *origshi, ShadeResult *origshr, short depth, flo /* end warning! - Campbell */ VECCOPY(isec.start, start); - if (dist_mir > 0.0) { - isec.end[0]= start[0]+dist_mir*vec[0]; - isec.end[1]= start[1]+dist_mir*vec[1]; - isec.end[2]= start[2]+dist_mir*vec[2]; - } else { - isec.end[0]= start[0]+maxsize*vec[0]; - isec.end[1]= start[1]+maxsize*vec[1]; - isec.end[2]= start[2]+maxsize*vec[2]; - } + VECCOPY(isec.vec, vec ); + isec.labda = dist_mir > 0 ? dist_mir : RE_RAYTRACE_MAXDIST; isec.mode= RE_RAY_MIRROR; - isec.faceorig= (RayFace*)vlr; - isec.oborig= RAY_OBJECT_SET(&R, obi); + isec.skip = RE_SKIP_VLR_NEIGHBOUR | RE_SKIP_VLR_RENDER_CHECK; + isec.hint = 0; + + isec.orig.ob = obi; + isec.orig.face = vlr; + RE_RC_INIT(isec, shi); - if(RE_ray_tree_intersect(R.raytree, &isec)) { + if(RE_rayobject_raycast(R.raytree, &isec)) { float d= 1.0f; shi.mask= origshi->mask; @@ -596,6 +835,7 @@ static void traceray(ShadeInput *origshi, ShadeResult *origshr, short depth, flo else { ray_fadeout_endcolor(col, origshi, &shi, origshr, &isec, vec); } + RE_RC_MERGE(&origshi->raycounter, &shi.raycounter); } /* **************** jitter blocks ********** */ @@ -1214,7 +1454,6 @@ void ray_trace(ShadeInput *shi, ShadeResult *shr) do_tra= ((shi->mat->mode & MA_TRANSP) && (shi->mat->mode & MA_RAYTRANSP) && shr->alpha!=1.0f); do_mir= ((shi->mat->mode & MA_RAYMIRROR) && shi->ray_mirror!=0.0f); - /* raytrace mirror amd refract like to separate the spec color */ if(shi->combinedflag & SCE_PASS_SPEC) @@ -1314,8 +1553,9 @@ static void ray_trace_shadow_tra(Isect *is, ShadeInput *origshi, int depth, int if it has col[3]>0.0f continue. so exit when alpha is full */ ShadeInput shi; ShadeResult shr; - - if(RE_ray_tree_intersect(R.raytree, is)) { + float initial_labda = is->labda; + + if(RE_rayobject_raycast(R.raytree, is)) { float d= 1.0f; /* we got a face */ @@ -1343,18 +1583,22 @@ static void ray_trace_shadow_tra(Isect *is, ShadeInput *origshi, int depth, int /* mix colors based on shadfac (rgb + amount of light factor) */ addAlphaLight(is->col, shr.diff, shr.alpha, d*shi.mat->filter); } else if (shi.mat->material_type == MA_TYPE_VOLUME) { - addAlphaLight(is->col, shr.combined, shr.alpha, 1.0f); + QUATCOPY(is->col, shr.combined); + is->col[3] = 1.f; } if(depth>0 && is->col[3]>0.0f) { /* adapt isect struct */ VECCOPY(is->start, shi.co); - is->oborig= RAY_OBJECT_SET(&R, shi.obi); - is->faceorig= (RayFace*)shi.vlr; + is->labda = initial_labda-is->labda; + is->orig.ob = shi.obi; + is->orig.face = shi.vlr; ray_trace_shadow_tra(is, origshi, depth-1, traflag | RAY_TRA); } + + RE_RC_MERGE(&origshi->raycounter, &shi.raycounter); } } @@ -1367,9 +1611,11 @@ int ray_trace_shadow_rad(ShadeInput *ship, ShadeResult *shr) Isect isec; ShadeInput shi; ShadeResult shr_t; - float vec[3], accum[3], div= 0.0f, maxsize= RE_ray_tree_max_size(R.raytree); + float vec[3], accum[3], div= 0.0f; int a; + assert(0); + if(only_one) { return 0; } @@ -1377,8 +1623,13 @@ int ray_trace_shadow_rad(ShadeInput *ship, ShadeResult *shr) accum[0]= accum[1]= accum[2]= 0.0f; isec.mode= RE_RAY_MIRROR; - isec.faceorig= (RayFace*)ship->vlr; - isec.oborig= RAY_OBJECT_SET(&R, ship->obi); + isec.orig.ob = ship->obi; + isec.orig.face = ship->vlr; + isec.hint = 0; + + VECCOPY(isec.start, ship->co); + + RE_RC_INIT(isec, shi); for(a=0; a<8*8; a++) { @@ -1390,12 +1641,11 @@ int ray_trace_shadow_rad(ShadeInput *ship, ShadeResult *shr) vec[1]-= vec[1]; vec[2]-= vec[2]; } - VECCOPY(isec.start, ship->co); - isec.end[0]= isec.start[0] + maxsize*vec[0]; - isec.end[1]= isec.start[1] + maxsize*vec[1]; - isec.end[2]= isec.start[2] + maxsize*vec[2]; - - if(RE_ray_tree_intersect(R.raytree, &isec)) { + + VECCOPY(isec.vec, vec ); + isec.labda = RE_RAYTRACE_MAXDIST; + + if(RE_rayobject_raycast(R.raytree, &isec)) { float fac; /* Warning, This is not that nice, and possibly a bit slow for every ray, @@ -1568,6 +1818,7 @@ static float *sphere_sampler(int type, int resol, int thread, int xs, int ys) static void ray_ao_qmc(ShadeInput *shi, float *shadfac) { Isect isec; + RayHint point_hint; QMCSampler *qsa=NULL; float samp3d[3]; float up[3], side[3], dir[3], nrm[3]; @@ -1583,13 +1834,25 @@ static void ray_ao_qmc(ShadeInput *shi, float *shadfac) float dxyview[3], skyadded=0, div; int aocolor; - isec.faceorig= (RayFace*)shi->vlr; - isec.oborig= RAY_OBJECT_SET(&R, shi->obi); - isec.face_last= NULL; - isec.ob_last= 0; + RE_RC_INIT(isec, *shi); + isec.orig.ob = shi->obi; + isec.orig.face = shi->vlr; + isec.skip = RE_SKIP_VLR_NEIGHBOUR | RE_SKIP_VLR_RENDER_CHECK | RE_SKIP_VLR_NON_SOLID_MATERIAL; + isec.hint = 0; + + isec.hit.ob = 0; + isec.hit.face = 0; + + isec.last_hit = NULL; + isec.mode= (R.wrld.aomode & WO_AODIST)?RE_RAY_SHADOW_TRA:RE_RAY_SHADOW; isec.lay= -1; + VECCOPY(isec.start, shi->co); + RE_rayobject_hint_bb( R.raytree, &point_hint, isec.start, isec.start ); + isec.hint = &point_hint; + + shadfac[0]= shadfac[1]= shadfac[2]= 0.0f; /* prevent sky colors to be added for only shadow (shadow becomes alpha) */ @@ -1627,6 +1890,7 @@ static void ray_ao_qmc(ShadeInput *shi, float *shadfac) QMC_initPixel(qsa, shi->thread); + while (samples < max_samples) { /* sampling, returns quasi-random vector in unit hemisphere */ @@ -1638,14 +1902,14 @@ static void ray_ao_qmc(ShadeInput *shi, float *shadfac) Normalize(dir); - VECCOPY(isec.start, shi->co); - isec.end[0] = shi->co[0] - maxdist*dir[0]; - isec.end[1] = shi->co[1] - maxdist*dir[1]; - isec.end[2] = shi->co[2] - maxdist*dir[2]; + isec.vec[0] = -dir[0]; + isec.vec[1] = -dir[1]; + isec.vec[2] = -dir[2]; + isec.labda = maxdist; prev = fac; - if(RE_ray_tree_intersect_check(R.raytree, &isec, vlr_check_intersect_solid)) { + if(RE_rayobject_raycast(R.raytree, &isec)) { if (R.wrld.aomode & WO_AODIST) fac+= exp(-isec.labda*R.wrld.aodistfac); else fac+= 1.0f; } @@ -1705,18 +1969,29 @@ static void ray_ao_qmc(ShadeInput *shi, float *shadfac) static void ray_ao_spheresamp(ShadeInput *shi, float *shadfac) { Isect isec; + RayHint point_hint; float *vec, *nrm, div, bias, sh=0.0f; float maxdist = R.wrld.aodist; float dxyview[3]; int j= -1, tot, actual=0, skyadded=0, aocolor, resol= R.wrld.aosamp; - isec.faceorig= (RayFace*)shi->vlr; - isec.oborig= RAY_OBJECT_SET(&R, shi->obi); - isec.face_last= NULL; - isec.ob_last= 0; + RE_RC_INIT(isec, *shi); + isec.orig.ob = shi->obi; + isec.orig.face = shi->vlr; + isec.skip = RE_SKIP_VLR_NEIGHBOUR | RE_SKIP_VLR_RENDER_CHECK; + isec.hint = 0; + + isec.hit.ob = 0; + isec.hit.face = 0; + + isec.last_hit = NULL; + isec.mode= (R.wrld.aomode & WO_AODIST)?RE_RAY_SHADOW_TRA:RE_RAY_SHADOW; isec.lay= -1; + VECCOPY(isec.start, shi->co); + RE_rayobject_hint_bb( R.raytree, &point_hint, isec.start, isec.start ); + isec.hint = &point_hint; shadfac[0]= shadfac[1]= shadfac[2]= 0.0f; @@ -1763,14 +2038,14 @@ static void ray_ao_spheresamp(ShadeInput *shi, float *shadfac) actual++; - /* always set start/end, RE_ray_tree_intersect clips it */ - VECCOPY(isec.start, shi->co); - isec.end[0] = shi->co[0] - maxdist*vec[0]; - isec.end[1] = shi->co[1] - maxdist*vec[1]; - isec.end[2] = shi->co[2] - maxdist*vec[2]; + /* always set start/vec/labda */ + isec.vec[0] = -vec[0]; + isec.vec[1] = -vec[1]; + isec.vec[2] = -vec[2]; + isec.labda = maxdist; /* do the trace */ - if(RE_ray_tree_intersect_check(R.raytree, &isec, vlr_check_intersect_solid)) { + if(RE_rayobject_raycast(R.raytree, &isec)) { if (R.wrld.aomode & WO_AODIST) sh+= exp(-isec.labda*R.wrld.aodistfac); else sh+= 1.0f; } @@ -1876,12 +2151,15 @@ static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float * int samples=0; float samp3d[3]; - float fac=0.0f, vec[3]; + float fac=0.0f, vec[3], end[3]; float colsq[4]; float adapt_thresh = lar->adapt_thresh; int min_adapt_samples=4, max_samples = lar->ray_totsamp; float *co; - int do_soft=1, full_osa=0; + int do_soft=1, full_osa=0, i; + + float min[3], max[3]; + RayHint bb_hint; float jitco[RE_MAX_OSA][3]; int totjitco; @@ -1900,7 +2178,8 @@ static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float * else max_samples = 1; } else { if (do_soft) max_samples = lar->ray_totsamp; - else max_samples = (R.osa > 4)?R.osa:5; + else if (shi->depth == 0) max_samples = (R.osa > 4)?R.osa:5; + else max_samples = 1; } ray_shadow_jittered_coords(shi, max_samples, jitco, &totjitco); @@ -1912,13 +2191,22 @@ static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float * qsa = get_thread_qmcsampler(&R, shi->thread, SAMP_TYPE_HAMMERSLEY, max_samples); QMC_initPixel(qsa, shi->thread); + + INIT_MINMAX(min, max); + for(i=0; i<totjitco; i++) + { + DO_MINMAX(jitco[i], min, max); + } + RE_rayobject_hint_bb( R.raytree, &bb_hint, min, max); + isec->hint = &bb_hint; + isec->skip = RE_SKIP_VLR_NEIGHBOUR | RE_SKIP_VLR_RENDER_CHECK; VECCOPY(vec, lampco); - while (samples < max_samples) { - isec->faceorig= (RayFace*)shi->vlr; - isec->oborig= RAY_OBJECT_SET(&R, shi->obi); + + isec->orig.ob = shi->obi; + isec->orig.face = shi->vlr; /* manually jitter the start shading co-ord per sample * based on the pre-generated OSA texture sampling offsets, @@ -1954,11 +2242,11 @@ static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float * /* align samples to lamp vector */ Mat3MulVecfl(lar->mat, samp3d); } - isec->end[0]= vec[0]+samp3d[0]; - isec->end[1]= vec[1]+samp3d[1]; - isec->end[2]= vec[2]+samp3d[2]; + end[0] = vec[0]+samp3d[0]; + end[1] = vec[1]+samp3d[1]; + end[2] = vec[2]+samp3d[2]; } else { - VECCOPY(isec->end, vec); + VECCOPY(end, vec); } if(shi->strand) { @@ -1966,7 +2254,7 @@ static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float * float jitbias= 0.5f*(VecLength(shi->dxco) + VecLength(shi->dyco)); float v[3]; - VECSUB(v, co, isec->end); + VECSUB(v, co, end); Normalize(v); co[0] -= jitbias*v[0]; @@ -1975,6 +2263,10 @@ static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float * } VECCOPY(isec->start, co); + isec->vec[0] = end[0]-isec->start[0]; + isec->vec[1] = end[1]-isec->start[1]; + isec->vec[2] = end[2]-isec->start[2]; + isec->labda = 1.0f; // * Normalize(isec->vec); /* trace the ray */ if(isec->mode==RE_RAY_SHADOW_TRA) { @@ -1993,7 +2285,7 @@ static void ray_shadow_qmc(ShadeInput *shi, LampRen *lar, float *lampco, float * colsq[2] += isec->col[2]*isec->col[2]; } else { - if( RE_ray_tree_intersect(R.raytree, isec) ) fac+= 1.0f; + if( RE_rayobject_raycast(R.raytree, isec) ) fac+= 1.0f; } samples++; @@ -2033,6 +2325,7 @@ static void ray_shadow_jitter(ShadeInput *shi, LampRen *lar, float *lampco, floa float *jitlamp; float fac=0.0f, div=0.0f, vec[3]; int a, j= -1, mask; + RayHint point_hint; if(isec->mode==RE_RAY_SHADOW_TRA) { shadfac[0]= shadfac[1]= shadfac[2]= shadfac[3]= 0.0f; @@ -2049,6 +2342,12 @@ static void ray_shadow_jitter(ShadeInput *shi, LampRen *lar, float *lampco, floa if(a==4) mask |= (mask>>4)|(mask>>8); else if(a==9) mask |= (mask>>9); + VECCOPY(isec->start, shi->co); + isec->orig.ob = shi->obi; + isec->orig.face = shi->vlr; + RE_rayobject_hint_bb( R.raytree, &point_hint, isec->start, isec->start ); + isec->hint = &point_hint; + while(a--) { if(R.r.mode & R_OSA) { @@ -2060,19 +2359,17 @@ static void ray_shadow_jitter(ShadeInput *shi, LampRen *lar, float *lampco, floa } } - isec->faceorig= (RayFace*)shi->vlr; - isec->oborig= RAY_OBJECT_SET(&R, shi->obi); - vec[0]= jitlamp[0]; vec[1]= jitlamp[1]; vec[2]= 0.0f; Mat3MulVecfl(lar->mat, vec); - /* set start and end, RE_ray_tree_intersect clips it */ - VECCOPY(isec->start, shi->co); - isec->end[0]= lampco[0]+vec[0]; - isec->end[1]= lampco[1]+vec[1]; - isec->end[2]= lampco[2]+vec[2]; + /* set start and vec */ + isec->vec[0] = vec[0]+lampco[0]-isec->start[0]; + isec->vec[1] = vec[1]+lampco[1]-isec->start[1]; + isec->vec[2] = vec[2]+lampco[2]-isec->start[2]; + isec->labda = 1.0f; + isec->skip = RE_SKIP_VLR_NEIGHBOUR | RE_SKIP_VLR_RENDER_CHECK; if(isec->mode==RE_RAY_SHADOW_TRA) { /* isec.col is like shadfac, so defines amount of light (0.0 is full shadow) */ @@ -2085,7 +2382,7 @@ static void ray_shadow_jitter(ShadeInput *shi, LampRen *lar, float *lampco, floa shadfac[2] += isec->col[2]; shadfac[3] += isec->col[3]; } - else if( RE_ray_tree_intersect(R.raytree, isec) ) fac+= 1.0f; + else if( RE_rayobject_raycast(R.raytree, isec) ) fac+= 1.0f; div+= 1.0f; jitlamp+= 2; @@ -2109,11 +2406,13 @@ static void ray_shadow_jitter(ShadeInput *shi, LampRen *lar, float *lampco, floa void ray_shadow(ShadeInput *shi, LampRen *lar, float *shadfac) { Isect isec; - float lampco[3], maxsize; + float lampco[3]; /* setup isec */ + RE_RC_INIT(isec, *shi); if(shi->mat->mode & MA_SHADOW_TRA) isec.mode= RE_RAY_SHADOW_TRA; else isec.mode= RE_RAY_SHADOW; + isec.hint = 0; if(lar->mode & (LA_LAYER|LA_LAYER_SHADOW)) isec.lay= lar->lay; @@ -2122,19 +2421,29 @@ void ray_shadow(ShadeInput *shi, LampRen *lar, float *shadfac) /* only when not mir tracing, first hit optimm */ if(shi->depth==0) { - isec.face_last= (RayFace*)lar->vlr_last[shi->thread]; - isec.ob_last= RAY_OBJECT_SET(&R, lar->obi_last[shi->thread]); + isec.last_hit = lar->last_hit[shi->thread]; } else { - isec.face_last= NULL; - isec.ob_last= 0; + isec.last_hit = NULL; } if(lar->type==LA_SUN || lar->type==LA_HEMI) { - maxsize= RE_ray_tree_max_size(R.raytree); - lampco[0]= shi->co[0] - maxsize*lar->vec[0]; - lampco[1]= shi->co[1] - maxsize*lar->vec[1]; - lampco[2]= shi->co[2] - maxsize*lar->vec[2]; + /* jitter and QMC sampling add a displace vector to the lamp position + * that's incorrect because a SUN lamp does not has an exact position + * and the displace should be done at the ray vector instead of the + * lamp position. + * This is easily verified by noticing that shadows of SUN lights change + * with the scene BB. + * + * This was detected during SoC 2009 - Raytrace Optimization, but to keep + * consistency with older render code it wasn't removed. + * + * If the render code goes through some recode/serious bug-fix then this + * is something to consider! + */ + lampco[0]= shi->co[0] - R.maxdist*lar->vec[0]; + lampco[1]= shi->co[1] - R.maxdist*lar->vec[1]; + lampco[2]= shi->co[2] - R.maxdist*lar->vec[2]; } else { VECCOPY(lampco, lar->co); @@ -2147,13 +2456,15 @@ void ray_shadow(ShadeInput *shi, LampRen *lar, float *shadfac) } else { if(lar->ray_totsamp<2) { - isec.faceorig= (RayFace*)shi->vlr; - isec.oborig= RAY_OBJECT_SET(&R, shi->obi); + isec.orig.ob = shi->obi; + isec.orig.face = shi->vlr; + shadfac[3]= 1.0f; // 1.0=full light /* set up isec vec */ VECCOPY(isec.start, shi->co); - VECCOPY(isec.end, lampco); + VECSUB(isec.vec, lampco, isec.start); + isec.labda = 1.0f; if(isec.mode==RE_RAY_SHADOW_TRA) { /* isec.col is like shadfac, so defines amount of light (0.0 is full shadow) */ @@ -2163,7 +2474,8 @@ void ray_shadow(ShadeInput *shi, LampRen *lar, float *shadfac) ray_trace_shadow_tra(&isec, shi, DEPTH_SHADOW_TRA, 0); QUATCOPY(shadfac, isec.col); } - else if(RE_ray_tree_intersect(R.raytree, &isec)) shadfac[3]= 0.0f; + else if(RE_rayobject_raycast(R.raytree, &isec)) + shadfac[3]= 0.0f; } else { ray_shadow_jitter(shi, lar, lampco, shadfac, &isec); @@ -2172,8 +2484,7 @@ void ray_shadow(ShadeInput *shi, LampRen *lar, float *shadfac) /* for first hit optim, set last interesected shadow face */ if(shi->depth==0) { - lar->vlr_last[shi->thread]= (VlakRen*)isec.face_last; - lar->obi_last[shi->thread]= RAY_OBJECT_GET(&R, isec.ob_last); + lar->last_hit[shi->thread] = isec.last_hit; } } @@ -2183,31 +2494,34 @@ void ray_shadow(ShadeInput *shi, LampRen *lar, float *shadfac) static void ray_translucent(ShadeInput *shi, LampRen *lar, float *distfac, float *co) { Isect isec; - float lampco[3], maxsize; + float lampco[3]; + + assert(0); /* setup isec */ + RE_RC_INIT(isec, *shi); isec.mode= RE_RAY_SHADOW_TRA; + isec.hint = 0; if(lar->mode & LA_LAYER) isec.lay= lar->lay; else isec.lay= -1; if(lar->type==LA_SUN || lar->type==LA_HEMI) { - maxsize= RE_ray_tree_max_size(R.raytree); - lampco[0]= shi->co[0] - maxsize*lar->vec[0]; - lampco[1]= shi->co[1] - maxsize*lar->vec[1]; - lampco[2]= shi->co[2] - maxsize*lar->vec[2]; + lampco[0]= shi->co[0] - RE_RAYTRACE_MAXDIST*lar->vec[0]; + lampco[1]= shi->co[1] - RE_RAYTRACE_MAXDIST*lar->vec[1]; + lampco[2]= shi->co[2] - RE_RAYTRACE_MAXDIST*lar->vec[2]; } else { VECCOPY(lampco, lar->co); } - isec.faceorig= (RayFace*)shi->vlr; - isec.oborig= RAY_OBJECT_SET(&R, shi->obi); + isec.orig.ob = shi->obi; + isec.orig.face = shi->vlr; /* set up isec vec */ VECCOPY(isec.start, shi->co); VECCOPY(isec.end, lampco); - if(RE_ray_tree_intersect(R.raytree, &isec)) { + if(RE_rayobject_raycast(R.raytree, &isec)) { /* we got a face */ /* render co */ diff --git a/source/blender/render/intern/source/raytrace.c b/source/blender/render/intern/source/raytrace.c deleted file mode 100644 index b34fe6a7039..00000000000 --- a/source/blender/render/intern/source/raytrace.c +++ /dev/null @@ -1,1442 +0,0 @@ -/** - * $Id$ - * - * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * The Original Code is Copyright (C) 1990-1998 NeoGeo BV. - * All rights reserved. - * - * Contributors: 2004/2005 Blender Foundation, full recode - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/* IMPORTANT NOTE: this code must be independent of any other render code - to use it outside the renderer! */ - -#include <math.h> -#include <string.h> -#include <stdlib.h> -#include <float.h> - -#include "MEM_guardedalloc.h" - -#include "DNA_material_types.h" - -#include "BKE_utildefines.h" - -#include "BLI_arithb.h" - -#include "RE_raytrace.h" - -/* ********** structs *************** */ - -#define BRANCH_ARRAY 1024 -#define NODE_ARRAY 4096 - -typedef struct Branch -{ - struct Branch *b[8]; -} Branch; - -typedef struct OcVal -{ - short ocx, ocy, ocz; -} OcVal; - -typedef struct Node -{ - struct RayFace *v[8]; - int ob[8]; - struct OcVal ov[8]; - struct Node *next; -} Node; - -typedef struct Octree { - struct Branch **adrbranch; - struct Node **adrnode; - float ocsize; /* ocsize: mult factor, max size octree */ - float ocfacx,ocfacy,ocfacz; - float min[3], max[3]; - int ocres; - int branchcount, nodecount; - char *ocface; /* during building only */ - RayCoordsFunc coordsfunc; - RayCheckFunc checkfunc; - RayObjectTransformFunc transformfunc; - void *userdata; -} Octree; - -/* ******** globals ***************** */ - -/* just for statistics */ -static int raycount; -static int accepted, rejected, coherent_ray; - - -/* **************** ocval method ******************* */ -/* within one octree node, a set of 3x15 bits defines a 'boundbox' to OR with */ - -#define OCVALRES 15 -#define BROW16(min, max) (((max)>=OCVALRES? 0xFFFF: (1<<(max+1))-1) - ((min>0)? ((1<<(min))-1):0) ) - -static void calc_ocval_face(float *v1, float *v2, float *v3, float *v4, short x, short y, short z, OcVal *ov) -{ - float min[3], max[3]; - int ocmin, ocmax; - - VECCOPY(min, v1); - VECCOPY(max, v1); - DO_MINMAX(v2, min, max); - DO_MINMAX(v3, min, max); - if(v4) { - DO_MINMAX(v4, min, max); - } - - ocmin= OCVALRES*(min[0]-x); - ocmax= OCVALRES*(max[0]-x); - ov->ocx= BROW16(ocmin, ocmax); - - ocmin= OCVALRES*(min[1]-y); - ocmax= OCVALRES*(max[1]-y); - ov->ocy= BROW16(ocmin, ocmax); - - ocmin= OCVALRES*(min[2]-z); - ocmax= OCVALRES*(max[2]-z); - ov->ocz= BROW16(ocmin, ocmax); - -} - -static void calc_ocval_ray(OcVal *ov, float xo, float yo, float zo, float *vec1, float *vec2) -{ - int ocmin, ocmax; - - if(vec1[0]<vec2[0]) { - ocmin= OCVALRES*(vec1[0] - xo); - ocmax= OCVALRES*(vec2[0] - xo); - } else { - ocmin= OCVALRES*(vec2[0] - xo); - ocmax= OCVALRES*(vec1[0] - xo); - } - ov->ocx= BROW16(ocmin, ocmax); - - if(vec1[1]<vec2[1]) { - ocmin= OCVALRES*(vec1[1] - yo); - ocmax= OCVALRES*(vec2[1] - yo); - } else { - ocmin= OCVALRES*(vec2[1] - yo); - ocmax= OCVALRES*(vec1[1] - yo); - } - ov->ocy= BROW16(ocmin, ocmax); - - if(vec1[2]<vec2[2]) { - ocmin= OCVALRES*(vec1[2] - zo); - ocmax= OCVALRES*(vec2[2] - zo); - } else { - ocmin= OCVALRES*(vec2[2] - zo); - ocmax= OCVALRES*(vec1[2] - zo); - } - ov->ocz= BROW16(ocmin, ocmax); -} - -/* ************* octree ************** */ - -static Branch *addbranch(Octree *oc, Branch *br, short ocb) -{ - int index; - - if(br->b[ocb]) return br->b[ocb]; - - oc->branchcount++; - index= oc->branchcount>>12; - - if(oc->adrbranch[index]==NULL) - oc->adrbranch[index]= MEM_callocN(4096*sizeof(Branch), "new oc branch"); - - if(oc->branchcount>= BRANCH_ARRAY*4096) { - printf("error; octree branches full\n"); - oc->branchcount=0; - } - - return br->b[ocb]= oc->adrbranch[index]+(oc->branchcount & 4095); -} - -static Node *addnode(Octree *oc) -{ - int index; - - oc->nodecount++; - index= oc->nodecount>>12; - - if(oc->adrnode[index]==NULL) - oc->adrnode[index]= MEM_callocN(4096*sizeof(Node),"addnode"); - - if(oc->nodecount> NODE_ARRAY*NODE_ARRAY) { - printf("error; octree nodes full\n"); - oc->nodecount=0; - } - - return oc->adrnode[index]+(oc->nodecount & 4095); -} - -static int face_in_node(RayFace *face, short x, short y, short z, float rtf[][3]) -{ - static float nor[3], d; - float fx, fy, fz; - - // init static vars - if(face) { - CalcNormFloat(rtf[0], rtf[1], rtf[2], nor); - d= -nor[0]*rtf[0][0] - nor[1]*rtf[0][1] - nor[2]*rtf[0][2]; - return 0; - } - - fx= x; - fy= y; - fz= z; - - if((fx)*nor[0] + (fy)*nor[1] + (fz)*nor[2] + d > 0.0f) { - if((fx+1)*nor[0] + (fy )*nor[1] + (fz )*nor[2] + d < 0.0f) return 1; - if((fx )*nor[0] + (fy+1)*nor[1] + (fz )*nor[2] + d < 0.0f) return 1; - if((fx+1)*nor[0] + (fy+1)*nor[1] + (fz )*nor[2] + d < 0.0f) return 1; - - if((fx )*nor[0] + (fy )*nor[1] + (fz+1)*nor[2] + d < 0.0f) return 1; - if((fx+1)*nor[0] + (fy )*nor[1] + (fz+1)*nor[2] + d < 0.0f) return 1; - if((fx )*nor[0] + (fy+1)*nor[1] + (fz+1)*nor[2] + d < 0.0f) return 1; - if((fx+1)*nor[0] + (fy+1)*nor[1] + (fz+1)*nor[2] + d < 0.0f) return 1; - } - else { - if((fx+1)*nor[0] + (fy )*nor[1] + (fz )*nor[2] + d > 0.0f) return 1; - if((fx )*nor[0] + (fy+1)*nor[1] + (fz )*nor[2] + d > 0.0f) return 1; - if((fx+1)*nor[0] + (fy+1)*nor[1] + (fz )*nor[2] + d > 0.0f) return 1; - - if((fx )*nor[0] + (fy )*nor[1] + (fz+1)*nor[2] + d > 0.0f) return 1; - if((fx+1)*nor[0] + (fy )*nor[1] + (fz+1)*nor[2] + d > 0.0f) return 1; - if((fx )*nor[0] + (fy+1)*nor[1] + (fz+1)*nor[2] + d > 0.0f) return 1; - if((fx+1)*nor[0] + (fy+1)*nor[1] + (fz+1)*nor[2] + d > 0.0f) return 1; - } - - return 0; -} - -static void ocwrite(Octree *oc, int ob, RayFace *face, int quad, short x, short y, short z, float rtf[][3]) -{ - Branch *br; - Node *no; - short a, oc0, oc1, oc2, oc3, oc4, oc5; - - x<<=2; - y<<=1; - - br= oc->adrbranch[0]; - - if(oc->ocres==512) { - oc0= ((x & 1024)+(y & 512)+(z & 256))>>8; - br= addbranch(oc, br, oc0); - } - if(oc->ocres>=256) { - oc0= ((x & 512)+(y & 256)+(z & 128))>>7; - br= addbranch(oc, br, oc0); - } - if(oc->ocres>=128) { - oc0= ((x & 256)+(y & 128)+(z & 64))>>6; - br= addbranch(oc, br, oc0); - } - - oc0= ((x & 128)+(y & 64)+(z & 32))>>5; - oc1= ((x & 64)+(y & 32)+(z & 16))>>4; - oc2= ((x & 32)+(y & 16)+(z & 8))>>3; - oc3= ((x & 16)+(y & 8)+(z & 4))>>2; - oc4= ((x & 8)+(y & 4)+(z & 2))>>1; - oc5= ((x & 4)+(y & 2)+(z & 1)); - - br= addbranch(oc, br,oc0); - br= addbranch(oc, br,oc1); - br= addbranch(oc, br,oc2); - br= addbranch(oc, br,oc3); - br= addbranch(oc, br,oc4); - no= (Node *)br->b[oc5]; - if(no==NULL) br->b[oc5]= (Branch *)(no= addnode(oc)); - - while(no->next) no= no->next; - - a= 0; - if(no->v[7]) { /* node full */ - no->next= addnode(oc); - no= no->next; - } - else { - while(no->v[a]!=NULL) a++; - } - - no->v[a]= face; - no->ob[a]= ob; - - if(quad) - calc_ocval_face(rtf[0], rtf[1], rtf[2], rtf[3], x>>2, y>>1, z, &no->ov[a]); - else - calc_ocval_face(rtf[0], rtf[1], rtf[2], NULL, x>>2, y>>1, z, &no->ov[a]); -} - -static void d2dda(Octree *oc, short b1, short b2, short c1, short c2, char *ocface, short rts[][3], float rtf[][3]) -{ - int ocx1,ocx2,ocy1,ocy2; - int x,y,dx=0,dy=0; - float ox1,ox2,oy1,oy2; - float labda,labdao,labdax,labday,ldx,ldy; - - ocx1= rts[b1][c1]; - ocy1= rts[b1][c2]; - ocx2= rts[b2][c1]; - ocy2= rts[b2][c2]; - - if(ocx1==ocx2 && ocy1==ocy2) { - ocface[oc->ocres*ocx1+ocy1]= 1; - return; - } - - ox1= rtf[b1][c1]; - oy1= rtf[b1][c2]; - ox2= rtf[b2][c1]; - oy2= rtf[b2][c2]; - - if(ox1!=ox2) { - if(ox2-ox1>0.0f) { - labdax= (ox1-ocx1-1.0f)/(ox1-ox2); - ldx= -1.0f/(ox1-ox2); - dx= 1; - } else { - labdax= (ox1-ocx1)/(ox1-ox2); - ldx= 1.0f/(ox1-ox2); - dx= -1; - } - } else { - labdax=1.0f; - ldx=0; - } - - if(oy1!=oy2) { - if(oy2-oy1>0.0f) { - labday= (oy1-ocy1-1.0f)/(oy1-oy2); - ldy= -1.0f/(oy1-oy2); - dy= 1; - } else { - labday= (oy1-ocy1)/(oy1-oy2); - ldy= 1.0f/(oy1-oy2); - dy= -1; - } - } else { - labday=1.0f; - ldy=0; - } - - x=ocx1; y=ocy1; - labda= MIN2(labdax, labday); - - while(TRUE) { - - if(x<0 || y<0 || x>=oc->ocres || y>=oc->ocres); - else ocface[oc->ocres*x+y]= 1; - - labdao=labda; - if(labdax==labday) { - labdax+=ldx; - x+=dx; - labday+=ldy; - y+=dy; - } else { - if(labdax<labday) { - labdax+=ldx; - x+=dx; - } else { - labday+=ldy; - y+=dy; - } - } - labda=MIN2(labdax,labday); - if(labda==labdao) break; - if(labda>=1.0f) break; - } - ocface[oc->ocres*ocx2+ocy2]=1; -} - -static void filltriangle(Octree *oc, short c1, short c2, char *ocface, short *ocmin) -{ - short *ocmax; - int a, x, y, y1, y2; - - ocmax=ocmin+3; - - for(x=ocmin[c1];x<=ocmax[c1];x++) { - a= oc->ocres*x; - for(y=ocmin[c2];y<=ocmax[c2];y++) { - if(ocface[a+y]) { - y++; - while(ocface[a+y] && y!=ocmax[c2]) y++; - for(y1=ocmax[c2];y1>y;y1--) { - if(ocface[a+y1]) { - for(y2=y;y2<=y1;y2++) ocface[a+y2]=1; - y1=0; - } - } - y=ocmax[c2]; - } - } - } -} - -void RE_ray_tree_free(RayTree *tree) -{ - Octree *oc= (Octree*)tree; - -#if 0 - printf("branches %d nodes %d\n", oc->branchcount, oc->nodecount); - printf("raycount %d \n", raycount); - printf("ray coherent %d \n", coherent_ray); - printf("accepted %d rejected %d\n", accepted, rejected); -#endif - if(oc->ocface) - MEM_freeN(oc->ocface); - - if(oc->adrbranch) { - int a= 0; - while(oc->adrbranch[a]) { - MEM_freeN(oc->adrbranch[a]); - oc->adrbranch[a]= NULL; - a++; - } - MEM_freeN(oc->adrbranch); - oc->adrbranch= NULL; - } - oc->branchcount= 0; - - if(oc->adrnode) { - int a= 0; - while(oc->adrnode[a]) { - MEM_freeN(oc->adrnode[a]); - oc->adrnode[a]= NULL; - a++; - } - MEM_freeN(oc->adrnode); - oc->adrnode= NULL; - } - oc->nodecount= 0; - - MEM_freeN(oc); -} - -RayTree *RE_ray_tree_create(int ocres, int totface, float *min, float *max, RayCoordsFunc coordsfunc, RayCheckFunc checkfunc, RayObjectTransformFunc transformfunc, void *userdata) -{ - Octree *oc; - float t00, t01, t02; - int c, ocres2; - - oc= MEM_callocN(sizeof(Octree), "Octree"); - oc->adrbranch= MEM_callocN(sizeof(void *)*BRANCH_ARRAY, "octree branches"); - oc->adrnode= MEM_callocN(sizeof(void *)*NODE_ARRAY, "octree nodes"); - - oc->coordsfunc= coordsfunc; - oc->checkfunc= checkfunc; - oc->transformfunc= transformfunc; - oc->userdata= userdata; - - /* only for debug info */ - raycount=0; - accepted= 0; - rejected= 0; - coherent_ray= 0; - - /* fill main octree struct */ - oc->ocres= ocres; - ocres2= oc->ocres*oc->ocres; - - VECCOPY(oc->min, min); - VECCOPY(oc->max, max); - - oc->adrbranch[0]=(Branch *)MEM_callocN(4096*sizeof(Branch), "makeoctree"); - - /* the lookup table, per face, for which nodes to fill in */ - oc->ocface= MEM_callocN( 3*ocres2 + 8, "ocface"); - memset(oc->ocface, 0, 3*ocres2); - - for(c=0;c<3;c++) { /* octree enlarge, still needed? */ - oc->min[c]-= 0.01f; - oc->max[c]+= 0.01f; - } - - t00= oc->max[0]-oc->min[0]; - t01= oc->max[1]-oc->min[1]; - t02= oc->max[2]-oc->min[2]; - - /* this minus 0.1 is old safety... seems to be needed? */ - oc->ocfacx= (oc->ocres-0.1)/t00; - oc->ocfacy= (oc->ocres-0.1)/t01; - oc->ocfacz= (oc->ocres-0.1)/t02; - - oc->ocsize= sqrt(t00*t00+t01*t01+t02*t02); /* global, max size octree */ - - return (RayTree*)oc; -} - -void RE_ray_tree_add_face(RayTree *tree, int ob, RayFace *face) -{ - Octree *oc = (Octree*)tree; - float *v1, *v2, *v3, *v4, ocfac[3], rtf[4][3]; - float co1[3], co2[3], co3[3], co4[3]; - short rts[4][3], ocmin[6], *ocmax; - char *ocface= oc->ocface; // front, top, size view of face, to fill in - int a, b, c, oc1, oc2, oc3, oc4, x, y, z, ocres2; - - ocfac[0]= oc->ocfacx; - ocfac[1]= oc->ocfacy; - ocfac[2]= oc->ocfacz; - - ocres2= oc->ocres*oc->ocres; - - ocmax= ocmin+3; - - oc->coordsfunc(face, &v1, &v2, &v3, &v4); - - VECCOPY(co1, v1); - VECCOPY(co2, v2); - VECCOPY(co3, v3); - if(v4) - VECCOPY(co4, v4); - - if(ob >= RE_RAY_TRANSFORM_OFFS) { - float (*mat)[4]= (float(*)[4])oc->transformfunc(oc->userdata, ob); - - if(mat) { - Mat4MulVecfl(mat, co1); - Mat4MulVecfl(mat, co2); - Mat4MulVecfl(mat, co3); - if(v4) - Mat4MulVecfl(mat, co4); - } - } - - for(c=0;c<3;c++) { - rtf[0][c]= (co1[c]-oc->min[c])*ocfac[c] ; - rts[0][c]= (short)rtf[0][c]; - rtf[1][c]= (co2[c]-oc->min[c])*ocfac[c] ; - rts[1][c]= (short)rtf[1][c]; - rtf[2][c]= (co3[c]-oc->min[c])*ocfac[c] ; - rts[2][c]= (short)rtf[2][c]; - if(v4) { - rtf[3][c]= (co4[c]-oc->min[c])*ocfac[c] ; - rts[3][c]= (short)rtf[3][c]; - } - } - - for(c=0;c<3;c++) { - oc1= rts[0][c]; - oc2= rts[1][c]; - oc3= rts[2][c]; - if(v4==NULL) { - ocmin[c]= MIN3(oc1,oc2,oc3); - ocmax[c]= MAX3(oc1,oc2,oc3); - } - else { - oc4= rts[3][c]; - ocmin[c]= MIN4(oc1,oc2,oc3,oc4); - ocmax[c]= MAX4(oc1,oc2,oc3,oc4); - } - if(ocmax[c]>oc->ocres-1) ocmax[c]=oc->ocres-1; - if(ocmin[c]<0) ocmin[c]=0; - } - - if(ocmin[0]==ocmax[0] && ocmin[1]==ocmax[1] && ocmin[2]==ocmax[2]) { - ocwrite(oc, ob, face, (v4 != NULL), ocmin[0], ocmin[1], ocmin[2], rtf); - } - else { - - d2dda(oc, 0,1,0,1,ocface+ocres2,rts,rtf); - d2dda(oc, 0,1,0,2,ocface,rts,rtf); - d2dda(oc, 0,1,1,2,ocface+2*ocres2,rts,rtf); - d2dda(oc, 1,2,0,1,ocface+ocres2,rts,rtf); - d2dda(oc, 1,2,0,2,ocface,rts,rtf); - d2dda(oc, 1,2,1,2,ocface+2*ocres2,rts,rtf); - if(v4==NULL) { - d2dda(oc, 2,0,0,1,ocface+ocres2,rts,rtf); - d2dda(oc, 2,0,0,2,ocface,rts,rtf); - d2dda(oc, 2,0,1,2,ocface+2*ocres2,rts,rtf); - } - else { - d2dda(oc, 2,3,0,1,ocface+ocres2,rts,rtf); - d2dda(oc, 2,3,0,2,ocface,rts,rtf); - d2dda(oc, 2,3,1,2,ocface+2*ocres2,rts,rtf); - d2dda(oc, 3,0,0,1,ocface+ocres2,rts,rtf); - d2dda(oc, 3,0,0,2,ocface,rts,rtf); - d2dda(oc, 3,0,1,2,ocface+2*ocres2,rts,rtf); - } - /* nothing todo with triangle..., just fills :) */ - filltriangle(oc, 0,1,ocface+ocres2,ocmin); - filltriangle(oc, 0,2,ocface,ocmin); - filltriangle(oc, 1,2,ocface+2*ocres2,ocmin); - - /* init static vars here */ - face_in_node(face, 0,0,0, rtf); - - for(x=ocmin[0];x<=ocmax[0];x++) { - a= oc->ocres*x; - for(y=ocmin[1];y<=ocmax[1];y++) { - if(ocface[a+y+ocres2]) { - b= oc->ocres*y+2*ocres2; - for(z=ocmin[2];z<=ocmax[2];z++) { - if(ocface[b+z] && ocface[a+z]) { - if(face_in_node(NULL, x, y, z, rtf)) - ocwrite(oc, ob, face, (v4 != NULL), x,y,z, rtf); - } - } - } - } - } - - /* same loops to clear octree, doubt it can be done smarter */ - for(x=ocmin[0];x<=ocmax[0];x++) { - a= oc->ocres*x; - for(y=ocmin[1];y<=ocmax[1];y++) { - /* x-y */ - ocface[a+y+ocres2]= 0; - - b= oc->ocres*y + 2*ocres2; - for(z=ocmin[2];z<=ocmax[2];z++) { - /* y-z */ - ocface[b+z]= 0; - /* x-z */ - ocface[a+z]= 0; - } - } - } - } -} - -void RE_ray_tree_done(RayTree *tree) -{ - Octree *oc= (Octree*)tree; - - MEM_freeN(oc->ocface); - oc->ocface= NULL; -} - -/* ************ raytracer **************** */ - -#define ISECT_EPSILON ((float)FLT_EPSILON) - -/* only for self-intersecting test with current render face (where ray left) */ -static int intersection2(RayFace *face, int ob, RayObjectTransformFunc transformfunc, RayCoordsFunc coordsfunc, void *userdata, float r0, float r1, float r2, float rx1, float ry1, float rz1) -{ - float *v1, *v2, *v3, *v4, co1[3], co2[3], co3[3], co4[3]; - float x0,x1,x2,t00,t01,t02,t10,t11,t12,t20,t21,t22; - float m0, m1, m2, divdet, det, det1; - float u1, v, u2; - - coordsfunc(face, &v1, &v2, &v3, &v4); - - /* happens for baking with non existing face */ - if(v1==NULL) - return 1; - - if(v4) { - SWAP(float*, v3, v4); - } - - VECCOPY(co1, v1); - VECCOPY(co2, v2); - VECCOPY(co3, v3); - if(v4) - VECCOPY(co4, v4); - - if(ob >= RE_RAY_TRANSFORM_OFFS) { - float (*mat)[4]= (float(*)[4])transformfunc(userdata, ob); - - if(mat) { - Mat4MulVecfl(mat, co1); - Mat4MulVecfl(mat, co2); - Mat4MulVecfl(mat, co3); - if(v4) - Mat4MulVecfl(mat, co4); - } - } - - t00= co3[0]-co1[0]; - t01= co3[1]-co1[1]; - t02= co3[2]-co1[2]; - t10= co3[0]-co2[0]; - t11= co3[1]-co2[1]; - t12= co3[2]-co2[2]; - - x0= t11*r2-t12*r1; - x1= t12*r0-t10*r2; - x2= t10*r1-t11*r0; - - divdet= t00*x0+t01*x1+t02*x2; - - m0= rx1-co3[0]; - m1= ry1-co3[1]; - m2= rz1-co3[2]; - det1= m0*x0+m1*x1+m2*x2; - - if(divdet!=0.0f) { - u1= det1/divdet; - - if(u1<ISECT_EPSILON) { - det= t00*(m1*r2-m2*r1); - det+= t01*(m2*r0-m0*r2); - det+= t02*(m0*r1-m1*r0); - v= det/divdet; - - if(v<ISECT_EPSILON && (u1 + v) > -(1.0f+ISECT_EPSILON)) { - return 1; - } - } - } - - if(v4) { - - t20= co3[0]-co4[0]; - t21= co3[1]-co4[1]; - t22= co3[2]-co4[2]; - - divdet= t20*x0+t21*x1+t22*x2; - if(divdet!=0.0f) { - u2= det1/divdet; - - if(u2<ISECT_EPSILON) { - det= t20*(m1*r2-m2*r1); - det+= t21*(m2*r0-m0*r2); - det+= t22*(m0*r1-m1*r0); - v= det/divdet; - - if(v<ISECT_EPSILON && (u2 + v) >= -(1.0f+ISECT_EPSILON)) { - return 2; - } - } - } - } - return 0; -} - -#if 0 -/* ray - line intersection */ -/* disabled until i got real & fast cylinder checking, this code doesnt work proper -for faster strands */ - -static int intersection_strand(Isect *is) -{ - float v1[3], v2[3]; /* length of strand */ - float axis[3], rc[3], nor[3], radline, dist, len; - - /* radius strand */ - radline= 0.5f*VecLenf(is->vlr->v1->co, is->vlr->v2->co); - - VecMidf(v1, is->vlr->v1->co, is->vlr->v2->co); - VecMidf(v2, is->vlr->v3->co, is->vlr->v4->co); - - VECSUB(rc, v1, is->start); /* vector from base ray to base cylinder */ - VECSUB(axis, v2, v1); /* cylinder axis */ - - CROSS(nor, is->vec, axis); - len= VecLength(nor); - - if(len<FLT_EPSILON) - return 0; - - dist= INPR(rc, nor)/len; /* distance between ray and axis cylinder */ - - if(dist<radline && dist>-radline) { - float dot1, dot2, dot3, rlen, alen, div; - float labda; - - /* calculating the intersection point of shortest distance */ - dot1 = INPR(rc, is->vec); - dot2 = INPR(is->vec, axis); - dot3 = INPR(rc, axis); - rlen = INPR(is->vec, is->vec); - alen = INPR(axis, axis); - - div = alen * rlen - dot2 * dot2; - if (ABS(div) < FLT_EPSILON) - return 0; - - labda = (dot1*dot2 - dot3*rlen)/div; - - radline/= sqrt(alen); - - /* labda: where on axis do we have closest intersection? */ - if(labda >= -radline && labda <= 1.0f+radline) { - VlakRen *vlr= is->faceorig; - VertRen *v1= is->vlr->v1, *v2= is->vlr->v2, *v3= is->vlr->v3, *v4= is->vlr->v4; - /* but we dont do shadows from faces sharing edge */ - - if(v1==vlr->v1 || v2==vlr->v1 || v3==vlr->v1 || v4==vlr->v1) return 0; - if(v1==vlr->v2 || v2==vlr->v2 || v3==vlr->v2 || v4==vlr->v2) return 0; - if(v1==vlr->v3 || v2==vlr->v3 || v3==vlr->v3 || v4==vlr->v3) return 0; - if(vlr->v4) { - if(v1==vlr->v4 || v2==vlr->v4 || v3==vlr->v4 || v4==vlr->v4) return 0; - } - return 1; - } - } - return 0; -} -#endif - -/* ray - triangle or quad intersection */ -int RE_ray_face_intersection(Isect *is, RayObjectTransformFunc transformfunc, RayCoordsFunc coordsfunc) -{ - RayFace *face= is->face; - int ob= is->ob; - float *v1,*v2,*v3,*v4,co1[3],co2[3],co3[3],co4[3]; - float x0,x1,x2,t00,t01,t02,t10,t11,t12,t20,t21,t22,r0,r1,r2; - float m0, m1, m2, divdet, det1; - short ok=0; - - /* disabled until i got real & fast cylinder checking, this code doesnt work proper - for faster strands */ -// if(is->mode==RE_RAY_SHADOW && is->vlr->flag & R_STRAND) -// return intersection_strand(is); - - coordsfunc(face, &v1, &v2, &v3, &v4); - - if(v4) { - SWAP(float*, v3, v4); - } - - VECCOPY(co1, v1); - VECCOPY(co2, v2); - VECCOPY(co3, v3); - if(v4) - VECCOPY(co4, v4); - - if(ob) { - float (*mat)[4]= (float(*)[4])transformfunc(is->userdata, ob); - - if(mat) { - Mat4MulVecfl(mat, co1); - Mat4MulVecfl(mat, co2); - Mat4MulVecfl(mat, co3); - if(v4) - Mat4MulVecfl(mat, co4); - } - } - - t00= co3[0]-co1[0]; - t01= co3[1]-co1[1]; - t02= co3[2]-co1[2]; - t10= co3[0]-co2[0]; - t11= co3[1]-co2[1]; - t12= co3[2]-co2[2]; - - r0= is->vec[0]; - r1= is->vec[1]; - r2= is->vec[2]; - - x0= t12*r1-t11*r2; - x1= t10*r2-t12*r0; - x2= t11*r0-t10*r1; - - divdet= t00*x0+t01*x1+t02*x2; - - m0= is->start[0]-co3[0]; - m1= is->start[1]-co3[1]; - m2= is->start[2]-co3[2]; - det1= m0*x0+m1*x1+m2*x2; - - if(divdet!=0.0f) { - float u; - - divdet= 1.0f/divdet; - u= det1*divdet; - if(u<ISECT_EPSILON && u>-(1.0f+ISECT_EPSILON)) { - float v, cros0, cros1, cros2; - - cros0= m1*t02-m2*t01; - cros1= m2*t00-m0*t02; - cros2= m0*t01-m1*t00; - v= divdet*(cros0*r0 + cros1*r1 + cros2*r2); - - if(v<ISECT_EPSILON && (u + v) > -(1.0f+ISECT_EPSILON)) { - float labda; - labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12); - - if(labda>-ISECT_EPSILON && labda<1.0f+ISECT_EPSILON) { - is->labda= labda; - is->u= u; is->v= v; - ok= 1; - } - } - } - } - - if(ok==0 && v4) { - - t20= co3[0]-co4[0]; - t21= co3[1]-co4[1]; - t22= co3[2]-co4[2]; - - divdet= t20*x0+t21*x1+t22*x2; - if(divdet!=0.0f) { - float u; - divdet= 1.0f/divdet; - u = det1*divdet; - - if(u<ISECT_EPSILON && u>-(1.0f+ISECT_EPSILON)) { - float v, cros0, cros1, cros2; - cros0= m1*t22-m2*t21; - cros1= m2*t20-m0*t22; - cros2= m0*t21-m1*t20; - v= divdet*(cros0*r0 + cros1*r1 + cros2*r2); - - if(v<ISECT_EPSILON && (u + v) >-(1.0f+ISECT_EPSILON)) { - float labda; - labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12); - - if(labda>-ISECT_EPSILON && labda<1.0f+ISECT_EPSILON) { - ok= 2; - is->labda= labda; - is->u= u; is->v= v; - } - } - } - } - } - - if(ok) { - is->isect= ok; // wich half of the quad - - if(is->mode!=RE_RAY_SHADOW) { - /* for mirror & tra-shadow: large faces can be filled in too often, this prevents - a face being detected too soon... */ - if(is->labda > is->ddalabda) { - return 0; - } - } - - /* when a shadow ray leaves a face, it can be little outside the edges of it, causing - intersection to be detected in its neighbour face */ - - if(is->facecontr && is->faceisect); // optimizing, the tests below are not needed - else if(is->labda< .1 && is->faceorig) { - RayFace *face= is->faceorig; - float *origv1, *origv2, *origv3, *origv4; - short de= 0; - - coordsfunc(face, &origv1, &origv2, &origv3, &origv4); - - if(ob == is->oborig) { - if(v1==origv1 || v2==origv1 || v3==origv1 || v4==origv1) de++; - if(v1==origv2 || v2==origv2 || v3==origv2 || v4==origv2) de++; - if(v1==origv3 || v2==origv3 || v3==origv3 || v4==origv3) de++; - if(origv4) { - if(v1==origv4 || v2==origv4 || v3==origv4 || v4==origv4) de++; - } - } - if(de) { - /* so there's a shared edge or vertex, let's intersect ray with face - itself, if that's true we can safely return 1, otherwise we assume - the intersection is invalid, 0 */ - - if(is->facecontr==NULL) { - is->obcontr= is->oborig; - is->facecontr= face; - is->faceisect= intersection2(face, is->oborig, transformfunc, coordsfunc, is->userdata, -r0, -r1, -r2, is->start[0], is->start[1], is->start[2]); - } - - if(is->faceisect) return 1; - return 0; - } - } - - return 1; - } - - return 0; -} - -/* check all faces in this node */ -static int testnode(Octree *oc, Isect *is, Node *no, OcVal ocval, RayCheckFunc checkfunc) -{ - RayFace *face; - int ob; - short nr=0; - OcVal *ov; - - /* return on any first hit */ - if(is->mode==RE_RAY_SHADOW) { - - face= no->v[0]; - ob= no->ob[0]; - while(face) { - - if(!(is->faceorig == face && is->oborig == ob)) { - - if(checkfunc(is, ob, face)) { - - ov= no->ov+nr; - if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) { - //accepted++; - is->ob= ob; - is->face= face; - - if(RE_ray_face_intersection(is, oc->transformfunc, oc->coordsfunc)) { - is->ob_last= ob; - is->face_last= face; - return 1; - } - } - //else rejected++; - } - } - - nr++; - if(nr==8) { - no= no->next; - if(no==0) return 0; - nr=0; - } - face= no->v[nr]; - ob= no->ob[nr]; - } - } - else { /* else mirror or glass or shadowtra, return closest face */ - Isect isect; - int found= 0; - - is->labda= 1.0f; /* needed? */ - isect= *is; /* copy for sorting */ - - face= no->v[0]; - ob= no->ob[0]; - while(face) { - - if(!(is->faceorig == face && is->oborig == ob)) { - if(checkfunc(is, ob, face)) { - ov= no->ov+nr; - if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) { - //accepted++; - - isect.ob= ob; - isect.face= face; - if(RE_ray_face_intersection(&isect, oc->transformfunc, oc->coordsfunc)) { - if(isect.labda<is->labda) { - *is= isect; - found= 1; - } - - } - } - //else rejected++; - } - } - - nr++; - if(nr==8) { - no= no->next; - if(no==NULL) break; - nr=0; - } - face= no->v[nr]; - ob= no->ob[nr]; - } - - return found; - } - - return 0; -} - -/* find the Node for the octree coord x y z */ -static Node *ocread(Octree *oc, int x, int y, int z) -{ - Branch *br; - int oc1; - - x<<=2; - y<<=1; - - br= oc->adrbranch[0]; - - if(oc->ocres==512) { - oc1= ((x & 1024)+(y & 512)+(z & 256))>>8; - br= br->b[oc1]; - if(br==NULL) { - return NULL; - } - } - if(oc->ocres>=256) { - oc1= ((x & 512)+(y & 256)+(z & 128))>>7; - br= br->b[oc1]; - if(br==NULL) { - return NULL; - } - } - if(oc->ocres>=128) { - oc1= ((x & 256)+(y & 128)+(z & 64))>>6; - br= br->b[oc1]; - if(br==NULL) { - return NULL; - } - } - - oc1= ((x & 128)+(y & 64)+(z & 32))>>5; - br= br->b[oc1]; - if(br) { - oc1= ((x & 64)+(y & 32)+(z & 16))>>4; - br= br->b[oc1]; - if(br) { - oc1= ((x & 32)+(y & 16)+(z & 8))>>3; - br= br->b[oc1]; - if(br) { - oc1= ((x & 16)+(y & 8)+(z & 4))>>2; - br= br->b[oc1]; - if(br) { - oc1= ((x & 8)+(y & 4)+(z & 2))>>1; - br= br->b[oc1]; - if(br) { - oc1= ((x & 4)+(y & 2)+(z & 1)); - return (Node *)br->b[oc1]; - } - } - } - } - } - - return NULL; -} - -static int cliptest(float p, float q, float *u1, float *u2) -{ - float r; - - if(p<0.0f) { - if(q<p) return 0; - else if(q<0.0f) { - r= q/p; - if(r>*u2) return 0; - else if(r>*u1) *u1=r; - } - } - else { - if(p>0.0f) { - if(q<0.0f) return 0; - else if(q<p) { - r= q/p; - if(r<*u1) return 0; - else if(r<*u2) *u2=r; - } - } - else if(q<0.0f) return 0; - } - return 1; -} - -/* extensive coherence checks/storage cancels out the benefit of it, and gives errors... we - need better methods, sample code commented out below (ton) */ - -/* - -in top: static int coh_nodes[16*16*16][6]; -in makeoctree: memset(coh_nodes, 0, sizeof(coh_nodes)); - -static void add_coherence_test(int ocx1, int ocx2, int ocy1, int ocy2, int ocz1, int ocz2) -{ - short *sp; - - sp= coh_nodes[ (ocx2 & 15) + 16*(ocy2 & 15) + 256*(ocz2 & 15) ]; - sp[0]= ocx1; sp[1]= ocy1; sp[2]= ocz1; - sp[3]= ocx2; sp[4]= ocy2; sp[5]= ocz2; - -} - -static int do_coherence_test(int ocx1, int ocx2, int ocy1, int ocy2, int ocz1, int ocz2) -{ - short *sp; - - sp= coh_nodes[ (ocx2 & 15) + 16*(ocy2 & 15) + 256*(ocz2 & 15) ]; - if(sp[0]==ocx1 && sp[1]==ocy1 && sp[2]==ocz1 && - sp[3]==ocx2 && sp[4]==ocy2 && sp[5]==ocz2) return 1; - return 0; -} - -*/ - -int RE_ray_tree_intersect(RayTree *tree, Isect *is) -{ - Octree *oc= (Octree*)tree; - - return RE_ray_tree_intersect_check(tree, is, oc->checkfunc); -} - -/* return 1: found valid intersection */ -/* starts with is->faceorig */ -int RE_ray_tree_intersect_check(RayTree *tree, Isect *is, RayCheckFunc checkfunc) -{ - Octree *oc= (Octree*)tree; - Node *no; - OcVal ocval; - float vec1[3], vec2[3]; - float u1,u2,ox1,ox2,oy1,oy2,oz1,oz2; - float labdao,labdax,ldx,labday,ldy,labdaz,ldz, ddalabda; - int dx,dy,dz; - int xo,yo,zo,c1=0; - int ocx1,ocx2,ocy1, ocy2,ocz1,ocz2; - - /* clip with octree */ - if(oc->branchcount==0) return 0; - - /* do this before intersect calls */ - is->facecontr= NULL; /* to check shared edge */ - is->obcontr= 0; - is->faceisect= is->isect= 0; /* shared edge, quad half flag */ - is->userdata= oc->userdata; - - /* only for shadow! */ - if(is->mode==RE_RAY_SHADOW) { - - /* check with last intersected shadow face */ - if(is->face_last!=NULL && !(is->face_last==is->faceorig && is->ob_last==is->oborig)) { - if(checkfunc(is, is->ob_last, is->face_last)) { - is->ob= is->ob_last; - is->face= is->face_last; - VECSUB(is->vec, is->end, is->start); - if(RE_ray_face_intersection(is, oc->transformfunc, oc->coordsfunc)) return 1; - } - } - } - - ldx= is->end[0] - is->start[0]; - u1= 0.0f; - u2= 1.0f; - - /* clip with octree cube */ - if(cliptest(-ldx, is->start[0]-oc->min[0], &u1,&u2)) { - if(cliptest(ldx, oc->max[0]-is->start[0], &u1,&u2)) { - ldy= is->end[1] - is->start[1]; - if(cliptest(-ldy, is->start[1]-oc->min[1], &u1,&u2)) { - if(cliptest(ldy, oc->max[1]-is->start[1], &u1,&u2)) { - ldz= is->end[2] - is->start[2]; - if(cliptest(-ldz, is->start[2]-oc->min[2], &u1,&u2)) { - if(cliptest(ldz, oc->max[2]-is->start[2], &u1,&u2)) { - c1=1; - if(u2<1.0f) { - is->end[0]= is->start[0]+u2*ldx; - is->end[1]= is->start[1]+u2*ldy; - is->end[2]= is->start[2]+u2*ldz; - } - if(u1>0.0f) { - is->start[0]+=u1*ldx; - is->start[1]+=u1*ldy; - is->start[2]+=u1*ldz; - } - } - } - } - } - } - } - - if(c1==0) return 0; - - /* reset static variables in ocread */ - //ocread(oc, oc->ocres, 0, 0); - - /* setup 3dda to traverse octree */ - ox1= (is->start[0]-oc->min[0])*oc->ocfacx; - oy1= (is->start[1]-oc->min[1])*oc->ocfacy; - oz1= (is->start[2]-oc->min[2])*oc->ocfacz; - ox2= (is->end[0]-oc->min[0])*oc->ocfacx; - oy2= (is->end[1]-oc->min[1])*oc->ocfacy; - oz2= (is->end[2]-oc->min[2])*oc->ocfacz; - - ocx1= (int)ox1; - ocy1= (int)oy1; - ocz1= (int)oz1; - ocx2= (int)ox2; - ocy2= (int)oy2; - ocz2= (int)oz2; - - /* for intersection */ - VECSUB(is->vec, is->end, is->start); - - if(ocx1==ocx2 && ocy1==ocy2 && ocz1==ocz2) { - no= ocread(oc, ocx1, ocy1, ocz1); - if(no) { - /* exact intersection with node */ - vec1[0]= ox1; vec1[1]= oy1; vec1[2]= oz1; - vec2[0]= ox2; vec2[1]= oy2; vec2[2]= oz2; - calc_ocval_ray(&ocval, (float)ocx1, (float)ocy1, (float)ocz1, vec1, vec2); - is->ddalabda= 1.0f; - if( testnode(oc, is, no, ocval, checkfunc) ) return 1; - } - } - else { - //static int coh_ocx1,coh_ocx2,coh_ocy1, coh_ocy2,coh_ocz1,coh_ocz2; - float dox, doy, doz; - int eqval; - - /* calc labda en ld */ - dox= ox1-ox2; - doy= oy1-oy2; - doz= oz1-oz2; - - if(dox<-FLT_EPSILON) { - ldx= -1.0f/dox; - labdax= (ocx1-ox1+1.0f)*ldx; - dx= 1; - } else if(dox>FLT_EPSILON) { - ldx= 1.0f/dox; - labdax= (ox1-ocx1)*ldx; - dx= -1; - } else { - labdax=1.0f; - ldx=0; - dx= 0; - } - - if(doy<-FLT_EPSILON) { - ldy= -1.0f/doy; - labday= (ocy1-oy1+1.0f)*ldy; - dy= 1; - } else if(doy>FLT_EPSILON) { - ldy= 1.0f/doy; - labday= (oy1-ocy1)*ldy; - dy= -1; - } else { - labday=1.0f; - ldy=0; - dy= 0; - } - - if(doz<-FLT_EPSILON) { - ldz= -1.0f/doz; - labdaz= (ocz1-oz1+1.0f)*ldz; - dz= 1; - } else if(doz>FLT_EPSILON) { - ldz= 1.0f/doz; - labdaz= (oz1-ocz1)*ldz; - dz= -1; - } else { - labdaz=1.0f; - ldz=0; - dz= 0; - } - - xo=ocx1; yo=ocy1; zo=ocz1; - labdao= ddalabda= MIN3(labdax,labday,labdaz); - - vec2[0]= ox1; - vec2[1]= oy1; - vec2[2]= oz1; - - /* this loop has been constructed to make sure the first and last node of ray - are always included, even when ddalabda==1.0f or larger */ - - while(TRUE) { - - no= ocread(oc, xo, yo, zo); - if(no) { - - /* calculate ray intersection with octree node */ - VECCOPY(vec1, vec2); - // dox,y,z is negative - vec2[0]= ox1-ddalabda*dox; - vec2[1]= oy1-ddalabda*doy; - vec2[2]= oz1-ddalabda*doz; - calc_ocval_ray(&ocval, (float)xo, (float)yo, (float)zo, vec1, vec2); - - is->ddalabda= ddalabda; - if( testnode(oc, is, no, ocval, checkfunc) ) return 1; - } - - labdao= ddalabda; - - /* traversing ocree nodes need careful detection of smallest values, with proper - exceptions for equal labdas */ - eqval= (labdax==labday); - if(labday==labdaz) eqval += 2; - if(labdax==labdaz) eqval += 4; - - if(eqval) { // only 4 cases exist! - if(eqval==7) { // x=y=z - xo+=dx; labdax+=ldx; - yo+=dy; labday+=ldy; - zo+=dz; labdaz+=ldz; - } - else if(eqval==1) { // x=y - if(labday < labdaz) { - xo+=dx; labdax+=ldx; - yo+=dy; labday+=ldy; - } - else { - zo+=dz; labdaz+=ldz; - } - } - else if(eqval==2) { // y=z - if(labdax < labday) { - xo+=dx; labdax+=ldx; - } - else { - yo+=dy; labday+=ldy; - zo+=dz; labdaz+=ldz; - } - } - else { // x=z - if(labday < labdax) { - yo+=dy; labday+=ldy; - } - else { - xo+=dx; labdax+=ldx; - zo+=dz; labdaz+=ldz; - } - } - } - else { // all three different, just three cases exist - eqval= (labdax<labday); - if(labday<labdaz) eqval += 2; - if(labdax<labdaz) eqval += 4; - - if(eqval==7 || eqval==5) { // x smallest - xo+=dx; labdax+=ldx; - } - else if(eqval==2 || eqval==6) { // y smallest - yo+=dy; labday+=ldy; - } - else { // z smallest - zo+=dz; labdaz+=ldz; - } - - } - - ddalabda=MIN3(labdax,labday,labdaz); - if(ddalabda==labdao) break; - /* to make sure the last node is always checked */ - if(labdao>=1.0f) break; - } - } - - /* reached end, no intersections found */ - is->ob_last= 0; - is->face_last= NULL; - return 0; -} - -float RE_ray_tree_max_size(RayTree *tree) -{ - return ((Octree*)tree)->ocsize; -} - diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c index 5db81288c1e..6c18592b8d2 100644 --- a/source/blender/render/intern/source/rendercore.c +++ b/source/blender/render/intern/source/rendercore.c @@ -31,6 +31,7 @@ #include <math.h> #include <float.h> #include <string.h> +#include <assert.h> /* External modules: */ #include "MEM_guardedalloc.h" @@ -520,6 +521,12 @@ static void add_filt_passes(RenderLayer *rl, int curmask, int rectx, int offset, } } break; + + case SCE_PASS_RAYHITS: + /* */ + col= shr->rayhits; + pixsize= 4; + break; } if(col) { fp= rpass->rect + pixsize*offset; @@ -596,6 +603,10 @@ static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult fp= rpass->rect + offset; *fp= shr->mist; break; + case SCE_PASS_RAYHITS: + col= shr->rayhits; + pixsize= 4; + break; } if(col) { fp= rpass->rect + pixsize*offset; @@ -2227,6 +2238,7 @@ static void bake_displacement(void *handle, ShadeInput *shi, float dist, int x, } } +#if 0 static int bake_check_intersect(Isect *is, int ob, RayFace *face) { BakeShade *bs = (BakeShade*)is->userdata; @@ -2236,9 +2248,13 @@ static int bake_check_intersect(Isect *is, int ob, RayFace *face) return (R.objectinstance[ob & ~RE_RAY_TRANSFORM_OFFS].obr->ob != bs->actob); } +#endif -static int bake_intersect_tree(RayTree* raytree, Isect* isect, float *start, float *dir, float sign, float *hitco, float *dist) +static int bake_intersect_tree(RayObject* raytree, Isect* isect, float *start, float *dir, float sign, float *hitco, float *dist) { + //TODO + assert( 0 ); +#if 0 float maxdist; int hit; @@ -2246,15 +2262,17 @@ static int bake_intersect_tree(RayTree* raytree, Isect* isect, float *start, flo if(R.r.bake_maxdist > 0.0f) maxdist= R.r.bake_maxdist; else - maxdist= RE_ray_tree_max_size(R.raytree) + R.r.bake_biasdist; + maxdist= FLT_MAX + R.r.bake_biasdist; + //TODO normalized direction? VECADDFAC(isect->start, start, dir, -R.r.bake_biasdist); + isect->dir[0] = dir[0]*sign; + isect->dir[1] = dir[1]*sign; + isect->dir[2] = dir[2]*sign; + isect->labda = maxdist; - isect->end[0] = isect->start[0] + dir[0]*maxdist*sign; - isect->end[1] = isect->start[1] + dir[1]*maxdist*sign; - isect->end[2] = isect->start[2] + dir[2]*maxdist*sign; - - hit = RE_ray_tree_intersect_check(R.raytree, isect, bake_check_intersect); + hit = RE_rayobject_raycast(raytree, isect); + //TODO bake_check_intersect if(hit) { hitco[0] = isect->start[0] + isect->labda*isect->vec[0]; hitco[1] = isect->start[1] + isect->labda*isect->vec[1]; @@ -2264,6 +2282,8 @@ static int bake_intersect_tree(RayTree* raytree, Isect* isect, float *start, flo } return hit; +#endif + return 0; } static void bake_set_vlr_dxyco(BakeShade *bs, float *uv1, float *uv2, float *uv3) @@ -2380,8 +2400,9 @@ static void do_bake_shade(void *handle, int x, int y, float u, float v) for(sign=-1; sign<=1; sign+=2) { memset(&isec, 0, sizeof(isec)); isec.mode= RE_RAY_MIRROR; - isec.faceorig= (RayFace*)vlr; - isec.oborig= RAY_OBJECT_SET(&R, obi); + + isec.orig.ob = obi; + isec.orig.face = vlr; isec.userdata= bs; if(bake_intersect_tree(R.raytree, &isec, shi->co, shi->vn, sign, co, &dist)) { @@ -2405,8 +2426,8 @@ static void do_bake_shade(void *handle, int x, int y, float u, float v) /* if hit, we shade from the new point, otherwise from point one starting face */ if(hit) { - vlr= (VlakRen*)minisec.face; - obi= RAY_OBJECT_GET(&R, minisec.ob); + obi= (ObjectInstanceRen*)minisec.hit.ob; + vlr= (VlakRen*)minisec.hit.face; quad= (minisec.isect == 2); VECCOPY(shi->co, minco); diff --git a/source/blender/render/intern/source/renderdatabase.c b/source/blender/render/intern/source/renderdatabase.c index 621831fb341..54863ef3295 100644 --- a/source/blender/render/intern/source/renderdatabase.c +++ b/source/blender/render/intern/source/renderdatabase.c @@ -75,6 +75,7 @@ #include "BKE_DerivedMesh.h" #include "RE_render_ext.h" /* externtex */ +#include "RE_raytrace.h" #include "renderpipeline.h" #include "render_types.h" @@ -872,13 +873,34 @@ void free_renderdata_tables(Render *re) MEM_freeN(obr->mtface); if(obr->mcol) MEM_freeN(obr->mcol); + + if(obr->rayfaces) + { + MEM_freeN(obr->rayfaces); + obr->rayfaces = NULL; + } + if(obr->rayprimitives) + { + MEM_freeN(obr->rayprimitives); + obr->rayprimitives = NULL; + } + if(obr->raytree) + { + RE_rayobject_free(obr->raytree); + obr->raytree = NULL; + } } if(re->objectinstance) { for(obi=re->instancetable.first; obi; obi=obi->next) + { if(obi->vectors) MEM_freeN(obi->vectors); + if(obi->raytree) + RE_rayobject_free(obi->raytree); + } + MEM_freeN(re->objectinstance); re->objectinstance= NULL; re->totinstance= 0; @@ -1024,7 +1046,7 @@ HaloRen *RE_inithalo(Render *re, ObjectRen *obr, Material *ma, float *vec, f externtex(mtex, texvec, &tin, &tr, &tg, &tb, &ta); yn= tin*mtex->colfac; - zn= tin*mtex->varfac; + zn= tin*mtex->alphafac; if(mtex->mapto & MAP_COL) { zn= 1.0-yn; @@ -1156,7 +1178,7 @@ HaloRen *RE_inithalo_particle(Render *re, ObjectRen *obr, DerivedMesh *dm, Mater externtex(mtex, texvec, &tin, &tr, &tg, &tb, &ta); //yn= tin*mtex->colfac; - //zn= tin*mtex->varfac; + //zn= tin*mtex->alphafac; if(mtex->mapto & MAP_COL) { tex[0]=tr; tex[1]=tg; @@ -1175,11 +1197,11 @@ HaloRen *RE_inithalo_particle(Render *re, ObjectRen *obr, DerivedMesh *dm, Mater har->b= in[2]; } if(mtex->mapto & MAP_ALPHA) - har->alfa = texture_value_blend(mtex->def_var,har->alfa,tin,mtex->varfac,mtex->blendtype,mtex->maptoneg & MAP_ALPHA); + har->alfa = texture_value_blend(mtex->def_var,har->alfa,tin,mtex->alphafac,mtex->blendtype); if(mtex->mapto & MAP_HAR) - har->hard = 1.0+126.0*texture_value_blend(mtex->def_var,((float)har->hard)/127.0,tin,mtex->varfac,mtex->blendtype,mtex->maptoneg & MAP_HAR); + har->hard = 1.0+126.0*texture_value_blend(mtex->def_var,((float)har->hard)/127.0,tin,mtex->hardfac,mtex->blendtype); if(mtex->mapto & MAP_RAYMIRR) - har->hasize = 100.0*texture_value_blend(mtex->def_var,har->hasize/100.0,tin,mtex->varfac,mtex->blendtype,mtex->maptoneg & MAP_RAYMIRR); + har->hasize = 100.0*texture_value_blend(mtex->def_var,har->hasize/100.0,tin,mtex->raymirrfac,mtex->blendtype); /* now what on earth is this good for?? */ //if(mtex->texco & 16) { // har->alfa= tin; diff --git a/source/blender/render/intern/source/shadbuf.c b/source/blender/render/intern/source/shadbuf.c index 48305d31e10..50e0321a6eb 100644 --- a/source/blender/render/intern/source/shadbuf.c +++ b/source/blender/render/intern/source/shadbuf.c @@ -49,8 +49,8 @@ #include "render_types.h" #include "renderdatabase.h" #include "rendercore.h" - #include "shadbuf.h" +#include "shading.h" #include "zbuf.h" /* XXX, could be better implemented... this is for endian issues @@ -166,6 +166,326 @@ static void make_jitter_weight_tab(Render *re, ShadBuf *shb, short filtertype) } } +static int verg_deepsample(const void *poin1, const void *poin2) +{ + const DeepSample *ds1= (const DeepSample*)poin1; + const DeepSample *ds2= (const DeepSample*)poin2; + + if(ds1->z < ds2->z) return -1; + else if(ds1->z == ds2->z) return 0; + else return 1; +} + +static int compress_deepsamples(DeepSample *dsample, int tot, float epsilon) +{ + /* uses doubles to avoid overflows and other numerical issues, + could be improved */ + DeepSample *ds, *newds; + float v; + double slope, slopemin, slopemax, min, max, div, newmin, newmax; + int a, first, z, newtot= 0; + + /*if(print) { + for(a=0, ds=dsample; a<tot; a++, ds++) + printf("%lf,%f ", ds->z/(double)0x7FFFFFFF, ds->v); + printf("\n"); + }*/ + + /* read from and write into same array */ + ds= dsample; + newds= dsample; + a= 0; + + /* as long as we are not at the end of the array */ + for(a++, ds++; a<tot; a++, ds++) { + slopemin= 0.0f; + slopemax= 0.0f; + first= 1; + + for(; a<tot; a++, ds++) { + //dz= ds->z - newds->z; + if(ds->z == newds->z) { + /* still in same z position, simply check + visibility difference against epsilon */ + if(!(fabs(newds->v - ds->v) <= epsilon)) { + break; + } + } + else { + /* compute slopes */ + div= (double)0x7FFFFFFF/((double)ds->z - (double)newds->z); + min= ((ds->v - epsilon) - newds->v)*div; + max= ((ds->v + epsilon) - newds->v)*div; + + /* adapt existing slopes */ + if(first) { + newmin= min; + newmax= max; + first= 0; + } + else { + newmin= MAX2(slopemin, min); + newmax= MIN2(slopemax, max); + + /* verify if there is still space between the slopes */ + if(newmin > newmax) { + ds--; + a--; + break; + } + } + + slopemin= newmin; + slopemax= newmax; + } + } + + if(a == tot) { + ds--; + a--; + } + + /* always previous z */ + z= ds->z; + + if(first || a==tot-1) { + /* if slopes were not initialized, use last visibility */ + v= ds->v; + } + else { + /* compute visibility at center between slopes at z */ + slope= (slopemin+slopemax)*0.5; + v= newds->v + slope*((z - newds->z)/(double)0x7FFFFFFF); + } + + newds++; + newtot++; + + newds->z= z; + newds->v= v; + } + + if(newtot == 0 || (newds->v != (newds-1)->v)) + newtot++; + + /*if(print) { + for(a=0, ds=dsample; a<newtot; a++, ds++) + printf("%lf,%f ", ds->z/(double)0x7FFFFFFF, ds->v); + printf("\n"); + }*/ + + return newtot; +} + +static float deep_alpha(Render *re, int obinr, int facenr, int strand) +{ + ObjectInstanceRen *obi= &re->objectinstance[obinr]; + Material *ma; + + if(strand) { + StrandRen *strand= RE_findOrAddStrand(obi->obr, facenr-1); + ma= strand->buffer->ma; + } + else { + VlakRen *vlr= RE_findOrAddVlak(obi->obr, (facenr-1) & RE_QUAD_MASK); + ma= vlr->mat; + } + + return ma->shad_alpha; +} + +static void compress_deepshadowbuf(Render *re, ShadBuf *shb, APixstr *apixbuf, APixstrand *apixbufstrand) +{ + ShadSampleBuf *shsample; + DeepSample *ds[RE_MAX_OSA], *sampleds[RE_MAX_OSA], *dsb, *newbuf; + APixstr *ap, *apn; + APixstrand *aps, *apns; + float visibility, totbuf= shb->totbuf; + int a, b, c, tot, minz, found, size= shb->size, prevtot, newtot; + int sampletot[RE_MAX_OSA], totsample = 0, totsamplec = 0; + + shsample= MEM_callocN( sizeof(ShadSampleBuf), "shad sample buf"); + BLI_addtail(&shb->buffers, shsample); + + shsample->totbuf= MEM_callocN(sizeof(int)*size*size, "deeptotbuf"); + shsample->deepbuf= MEM_callocN(sizeof(DeepSample*)*size*size, "deepbuf"); + + ap= apixbuf; + aps= apixbufstrand; + for(a=0; a<size*size; a++, ap++, aps++) { + /* count number of samples */ + for(c=0; c<totbuf; c++) + sampletot[c]= 0; + + tot= 0; + for(apn=ap; apn; apn=apn->next) + for(b=0; b<4; b++) + if(apn->p[b]) + for(c=0; c<totbuf; c++) + if(apn->mask[b] & (1<<c)) + sampletot[c]++; + + if(apixbufstrand) { + for(apns=aps; apns; apns=apns->next) + for(b=0; b<4; b++) + if(apns->p[b]) + for(c=0; c<totbuf; c++) + if(apns->mask[b] & (1<<c)) + sampletot[c]++; + } + + for(c=0; c<totbuf; c++) + tot += sampletot[c]; + + if(tot == 0) { + shsample->deepbuf[a]= NULL; + shsample->totbuf[a]= 0; + continue; + } + + /* fill samples */ + ds[0]= sampleds[0]= MEM_callocN(sizeof(DeepSample)*tot*2, "deepsample"); + for(c=1; c<totbuf; c++) + ds[c]= sampleds[c]= sampleds[c-1] + sampletot[c-1]*2; + + for(apn=ap; apn; apn=apn->next) { + for(b=0; b<4; b++) { + if(apn->p[b]) { + for(c=0; c<totbuf; c++) { + if(apn->mask[b] & (1<<c)) { + /* two entries to create step profile */ + ds[c]->z= apn->z[b]; + ds[c]->v= 1.0f; /* not used */ + ds[c]++; + ds[c]->z= apn->z[b]; + ds[c]->v= deep_alpha(re, apn->obi[b], apn->p[b], 0); + ds[c]++; + } + } + } + } + } + + if(apixbufstrand) { + for(apns=aps; apns; apns=apns->next) { + for(b=0; b<4; b++) { + if(apns->p[b]) { + for(c=0; c<totbuf; c++) { + if(apns->mask[b] & (1<<c)) { + /* two entries to create step profile */ + ds[c]->z= apns->z[b]; + ds[c]->v= 1.0f; /* not used */ + ds[c]++; + ds[c]->z= apns->z[b]; + ds[c]->v= deep_alpha(re, apns->obi[b], apns->p[b], 1); + ds[c]++; + } + } + } + } + } + } + + for(c=0; c<totbuf; c++) { + /* sort by increasing z */ + qsort(sampleds[c], sampletot[c], sizeof(DeepSample)*2, verg_deepsample); + + /* sum visibility, replacing alpha values */ + visibility= 1.0f; + ds[c]= sampleds[c]; + + for(b=0; b<sampletot[c]; b++) { + /* two entries creating step profile */ + ds[c]->v= visibility; + ds[c]++; + + visibility *= 1.0f-ds[c]->v; + ds[c]->v= visibility; + ds[c]++; + } + + /* halfway trick, probably won't work well for volumes? */ + ds[c]= sampleds[c]; + for(b=0; b<sampletot[c]; b++) { + if(b+1 < sampletot[c]) { + ds[c]->z= (ds[c]->z>>1) + ((ds[c]+2)->z>>1); + ds[c]++; + ds[c]->z= (ds[c]->z>>1) + ((ds[c]+2)->z>>1); + ds[c]++; + } + else { + ds[c]->z= (ds[c]->z>>1) + (0x7FFFFFFF>>1); + ds[c]++; + ds[c]->z= (ds[c]->z>>1) + (0x7FFFFFFF>>1); + ds[c]++; + } + } + + /* init for merge loop */ + ds[c]= sampleds[c]; + sampletot[c] *= 2; + } + + shsample->deepbuf[a]= MEM_callocN(sizeof(DeepSample)*tot*2, "deepsample"); + shsample->totbuf[a]= 0; + + /* merge buffers */ + dsb= shsample->deepbuf[a]; + while(1) { + minz= 0; + found= 0; + + for(c=0; c<totbuf; c++) { + if(sampletot[c] && (!found || ds[c]->z < minz)) { + minz= ds[c]->z; + found= 1; + } + } + + if(!found) + break; + + dsb->z= minz; + dsb->v= 0.0f; + + visibility= 0.0f; + for(c=0; c<totbuf; c++) { + if(sampletot[c] && ds[c]->z == minz) { + ds[c]++; + sampletot[c]--; + } + + if(sampleds[c] == ds[c]) + visibility += 1.0f/totbuf; + else + visibility += (ds[c]-1)->v/totbuf; + } + + dsb->v= visibility; + dsb++; + shsample->totbuf[a]++; + } + + prevtot= shsample->totbuf[a]; + totsample += prevtot; + + newtot= compress_deepsamples(shsample->deepbuf[a], prevtot, shb->compressthresh); + shsample->totbuf[a]= newtot; + totsamplec += newtot; + + if(newtot < prevtot) { + newbuf= MEM_mallocN(sizeof(DeepSample)*newtot, "cdeepsample"); + memcpy(newbuf, shsample->deepbuf[a], sizeof(DeepSample)*newtot); + MEM_freeN(shsample->deepbuf[a]); + shsample->deepbuf[a]= newbuf; + } + + MEM_freeN(sampleds[0]); + } + + //printf("%d -> %d, ratio %f\n", totsample, totsamplec, (float)totsamplec/(float)totsample); +} + /* create Z tiles (for compression): this system is 24 bits!!! */ static void compress_shadowbuf(ShadBuf *shb, int *rectz, int square) { @@ -176,7 +496,7 @@ static void compress_shadowbuf(ShadBuf *shb, int *rectz, int square) int a, x, y, minx, miny, byt1, byt2; char *rc, *rcline, *ctile, *zt; - shsample= MEM_mallocN( sizeof(ShadSampleBuf), "shad sample buf"); + shsample= MEM_callocN( sizeof(ShadSampleBuf), "shad sample buf"); BLI_addtail(&shb->buffers, shsample); shsample->zbuf= MEM_mallocN( sizeof(uintptr_t)*(size*size)/256, "initshadbuf2"); @@ -277,7 +597,6 @@ static void compress_shadowbuf(ShadBuf *shb, int *rectz, int square) } MEM_freeN(rcline); - } /* sets start/end clipping. lar->shb should be initialized */ @@ -381,11 +700,54 @@ static void shadowbuf_autoclip(Render *re, LampRen *lar) } } +static void makeflatshadowbuf(Render *re, LampRen *lar, float *jitbuf) +{ + ShadBuf *shb= lar->shb; + int *rectz, samples; + + /* zbuffering */ + rectz= MEM_mapallocN(sizeof(int)*shb->size*shb->size, "makeshadbuf"); + + for(samples=0; samples<shb->totbuf; samples++) { + zbuffer_shadow(re, shb->persmat, lar, rectz, shb->size, jitbuf[2*samples], jitbuf[2*samples+1]); + /* create Z tiles (for compression): this system is 24 bits!!! */ + compress_shadowbuf(shb, rectz, lar->mode & LA_SQUARE); + + if(re->test_break(re->tbh)) + break; + } + + MEM_freeN(rectz); +} + +static void makedeepshadowbuf(Render *re, LampRen *lar, float *jitbuf) +{ + ShadBuf *shb= lar->shb; + APixstr *apixbuf; + APixstrand *apixbufstrand= NULL; + ListBase apsmbase= {NULL, NULL}; + + /* zbuffering */ + apixbuf= MEM_callocN(sizeof(APixstr)*shb->size*shb->size, "APixbuf"); + if(re->totstrand) + apixbufstrand= MEM_callocN(sizeof(APixstrand)*shb->size*shb->size, "APixbufstrand"); + + zbuffer_abuf_shadow(re, lar, shb->persmat, apixbuf, apixbufstrand, &apsmbase, shb->size, + shb->totbuf, (float(*)[2])jitbuf); + + /* create Z tiles (for compression): this system is 24 bits!!! */ + compress_deepshadowbuf(re, shb, apixbuf, apixbufstrand); + + MEM_freeN(apixbuf); + if(apixbufstrand) + MEM_freeN(apixbufstrand); + freepsA(&apsmbase); +} + void makeshadowbuf(Render *re, LampRen *lar) { ShadBuf *shb= lar->shb; float wsize, *jitbuf, twozero[2]= {0.0f, 0.0f}, angle, temp; - int *rectz, samples; if(lar->bufflag & (LA_SHADBUF_AUTO_START|LA_SHADBUF_AUTO_END)) shadowbuf_autoclip(re, lar); @@ -405,31 +767,26 @@ void makeshadowbuf(Render *re, LampRen *lar) i_window(-wsize, wsize, -wsize, wsize, shb->d, shb->clipend, shb->winmat); Mat4MulMat4(shb->persmat, shb->viewmat, shb->winmat); - if(ELEM(lar->buftype, LA_SHADBUF_REGULAR, LA_SHADBUF_HALFWAY)) { + if(ELEM3(lar->buftype, LA_SHADBUF_REGULAR, LA_SHADBUF_HALFWAY, LA_SHADBUF_DEEP)) { + shb->totbuf= lar->buffers; + /* jitter, weights - not threadsafe! */ BLI_lock_thread(LOCK_CUSTOM1); shb->jit= give_jitter_tab(get_render_shadow_samples(&re->r, shb->samp)); make_jitter_weight_tab(re, shb, lar->filtertype); BLI_unlock_thread(LOCK_CUSTOM1); - shb->totbuf= lar->buffers; if(shb->totbuf==4) jitbuf= give_jitter_tab(2); else if(shb->totbuf==9) jitbuf= give_jitter_tab(3); else jitbuf= twozero; /* zbuffering */ - rectz= MEM_mapallocN(sizeof(int)*shb->size*shb->size, "makeshadbuf"); - - for(samples=0; samples<shb->totbuf; samples++) { - zbuffer_shadow(re, shb->persmat, lar, rectz, shb->size, jitbuf[2*samples], jitbuf[2*samples+1]); - /* create Z tiles (for compression): this system is 24 bits!!! */ - compress_shadowbuf(shb, rectz, lar->mode & LA_SQUARE); - - if(re->test_break(re->tbh)) - break; + if(lar->buftype == LA_SHADBUF_DEEP) { + makedeepshadowbuf(re, lar, jitbuf); + shb->totbuf= 1; } - - MEM_freeN(rectz); + else + makeflatshadowbuf(re, lar, jitbuf); /* printf("lampbuf %d\n", sizeoflampbuf(shb)); */ } @@ -539,17 +896,27 @@ void freeshadowbuf(LampRen *lar) ShadSampleBuf *shsample; int b, v; - v= (shb->size*shb->size)/256; - for(shsample= shb->buffers.first; shsample; shsample= shsample->next) { - intptr_t *ztile= shsample->zbuf; - char *ctile= shsample->cbuf; - - for(b=0; b<v; b++, ztile++, ctile++) - if(*ctile) MEM_freeN((void *) *ztile); - - MEM_freeN(shsample->zbuf); - MEM_freeN(shsample->cbuf); + if(shsample->deepbuf) { + v= shb->size*shb->size; + for(b=0; b<v; b++) + if(shsample->deepbuf[b]) + MEM_freeN(shsample->deepbuf[b]); + + MEM_freeN(shsample->deepbuf); + MEM_freeN(shsample->totbuf); + } + else { + intptr_t *ztile= shsample->zbuf; + char *ctile= shsample->cbuf; + + v= (shb->size*shb->size)/256; + for(b=0; b<v; b++, ztile++, ctile++) + if(*ctile) MEM_freeN((void *) *ztile); + + MEM_freeN(shsample->zbuf); + MEM_freeN(shsample->cbuf); + } } BLI_freelistN(&shb->buffers); @@ -567,6 +934,9 @@ static int firstreadshadbuf(ShadBuf *shb, ShadSampleBuf *shsample, int **rz, int int ofs; char *ct; + if(shsample->deepbuf) + return 0; + /* always test borders of shadowbuffer */ if(xs<0) xs= 0; else if(xs>=shb->size) xs= shb->size-1; if(ys<0) ys= 0; else if(ys>=shb->size) ys= shb->size-1; @@ -587,6 +957,67 @@ static int firstreadshadbuf(ShadBuf *shb, ShadSampleBuf *shsample, int **rz, int return 0; } +static float readdeepvisibility(DeepSample *dsample, int tot, int z, int bias, float *biast) +{ + DeepSample *ds, *prevds; + float t; + int a; + + /* tricky stuff here; we use ints which can overflow easily with bias values */ + + ds= dsample; + for(a=0; a<tot && (z-bias > ds->z); a++, ds++) + ; + + if(a == tot) { + if(biast) + *biast= 0.0f; + return (ds-1)->v; /* completely behind all samples */ + } + + /* check if this read needs bias blending */ + if(biast) { + if(z > ds->z) + *biast= (float)(z - ds->z)/(float)bias; + else + *biast= 0.0f; + } + + if(a == 0) + return 1.0f; /* completely in front of all samples */ + + prevds= ds-1; + t= (float)(z-bias - prevds->z)/(float)(ds->z - prevds->z); + return t*ds->v + (1.0f-t)*prevds->v; +} + +static float readdeepshadowbuf(ShadBuf *shb, ShadSampleBuf *shsample, int bias, int xs, int ys, int zs) +{ + float v, biasv, biast; + int ofs, tot; + + if(zs < - 0x7FFFFE00 + bias) + return 1.0; /* extreme close to clipstart */ + + /* calc z */ + ofs= ys*shb->size + xs; + tot= shsample->totbuf[ofs]; + if(tot == 0) + return 1.0f; + + v= readdeepvisibility(shsample->deepbuf[ofs], tot, zs, bias, &biast); + + if(biast != 0.0f) { + /* in soft bias area */ + biasv= readdeepvisibility(shsample->deepbuf[ofs], tot, zs, 0, 0); + + biast= biast*biast; + return (1.0f-biast)*v + biast*biasv; + } + + return v; +} + /* return 1.0 : fully in light */ static float readshadowbuf(ShadBuf *shb, ShadSampleBuf *shsample, int bias, int xs, int ys, int zs) { @@ -603,6 +1034,9 @@ static float readshadowbuf(ShadBuf *shb, ShadSampleBuf *shsample, int bias, int if(xs<0) xs= 0; else if(xs>=shb->size) xs= shb->size-1; if(ys<0) ys= 0; else if(ys>=shb->size) ys= shb->size-1; + if(shsample->deepbuf) + return readdeepshadowbuf(shb, shsample, bias, xs, ys, zs); + /* calc z */ ofs= (ys>>4)*(shb->size>>4) + (xs>>4); ct= shsample->cbuf+ofs; diff --git a/source/blender/render/intern/source/shadeinput.c b/source/blender/render/intern/source/shadeinput.c index 7541ce53073..79ee6c89460 100644 --- a/source/blender/render/intern/source/shadeinput.c +++ b/source/blender/render/intern/source/shadeinput.c @@ -44,6 +44,7 @@ #include "BKE_node.h" /* local include */ +#include "raycounter.h" #include "renderpipeline.h" #include "render_types.h" #include "renderdatabase.h" @@ -181,6 +182,9 @@ void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr) float alpha; /* ------ main shading loop -------- */ +#ifdef RE_RAYCOUNTER + memset(&shi->raycounter, 0, sizeof(shi->raycounter)); +#endif if(shi->mat->nodetree && shi->mat->use_nodes) { ntreeShaderExecTree(shi->mat->nodetree, shi, shr); @@ -230,6 +234,18 @@ void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr) /* add z */ shr->z= -shi->co[2]; + + /* RAYHITS */ +/* + if(1 || shi->passflag & SCE_PASS_RAYHITS) + { + shr->rayhits[0] = (float)shi->raycounter.faces.test; + shr->rayhits[1] = (float)shi->raycounter.bb.hit; + shr->rayhits[2] = 0.0; + shr->rayhits[3] = 1.0; + } + */ + RE_RC_MERGE(&re_rc_counter[shi->thread], &shi->raycounter); } /* **************************************************************************** */ @@ -1216,7 +1232,7 @@ void shade_input_set_shade_texco(ShadeInput *shi) s3= RE_vertren_get_sticky(obr, v3, 0); if(s1 && s2 && s3) { - float winmat[4][4], ho1[4], ho2[4], ho3[4]; + float obwinmat[4][4], winmat[4][4], ho1[4], ho2[4], ho3[4]; float Zmulx, Zmuly; float hox, hoy, l, dl, u, v; float s00, s01, s10, s11, detsh; @@ -1224,14 +1240,15 @@ void shade_input_set_shade_texco(ShadeInput *shi) /* old globals, localized now */ Zmulx= ((float)R.winx)/2.0f; Zmuly= ((float)R.winy)/2.0f; + zbuf_make_winmat(&R, winmat); if(shi->obi->flag & R_TRANSFORMED) - zbuf_make_winmat(&R, shi->obi->mat, winmat); + Mat4MulMat4(obwinmat, obi->mat, winmat); else - zbuf_make_winmat(&R, NULL, winmat); + Mat4CpyMat4(obwinmat, winmat); - zbuf_render_project(winmat, v1->co, ho1); - zbuf_render_project(winmat, v2->co, ho2); - zbuf_render_project(winmat, v3->co, ho3); + zbuf_render_project(obwinmat, v1->co, ho1); + zbuf_render_project(obwinmat, v2->co, ho2); + zbuf_render_project(obwinmat, v3->co, ho3); s00= ho3[0]/ho3[3] - ho1[0]/ho1[3]; s01= ho3[1]/ho3[3] - ho1[1]/ho1[3]; @@ -1290,7 +1307,7 @@ void shade_input_initialize(ShadeInput *shi, RenderPart *pa, RenderLayer *rl, in shi->sample= sample; shi->thread= pa->thread; - shi->do_preview= R.r.scemode & R_NODE_PREVIEW; + shi->do_preview= (R.r.scemode & R_MATNODE_PREVIEW) != 0; shi->lay= rl->lay; shi->layflag= rl->layflag; shi->passflag= rl->passflag; diff --git a/source/blender/render/intern/source/sss.c b/source/blender/render/intern/source/sss.c index 29f4d7729fe..a416c2d2764 100644 --- a/source/blender/render/intern/source/sss.c +++ b/source/blender/render/intern/source/sss.c @@ -862,6 +862,7 @@ static void sss_create_tree_mat(Render *re, Material *mat) setting them back, maybe we need to create our own Render? */ /* do SSS preprocessing render */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); rr= re->result; osa= re->osa; osaflag= re->r.mode & R_OSA; @@ -875,13 +876,16 @@ static void sss_create_tree_mat(Render *re, Material *mat) if(!(re->r.scemode & R_PREVIEWBUTS)) re->result= NULL; + BLI_rw_mutex_unlock(&re->resultmutex); RE_TileProcessor(re, 0, 1); + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); if(!(re->r.scemode & R_PREVIEWBUTS)) { RE_FreeRenderResult(re->result); re->result= rr; } + BLI_rw_mutex_unlock(&re->resultmutex); re->i.partsdone= partsdone; re->sss_mat= NULL; diff --git a/source/blender/render/intern/source/strand.c b/source/blender/render/intern/source/strand.c index aeaca559918..28b45183771 100644 --- a/source/blender/render/intern/source/strand.c +++ b/source/blender/render/intern/source/strand.c @@ -417,6 +417,8 @@ typedef struct StrandPart { intptr_t *rectdaps; int rectx, recty; int sample; + int shadow; + float (*jit)[2]; StrandSegment *segment; float t[3], s[3]; @@ -525,7 +527,7 @@ static void do_strand_fillac(void *handle, int x, int y, float u, float v, float } } else { - bufferz= spart->rectz[offset]; + bufferz= (spart->rectz)? spart->rectz[offset]: 0x7FFFFFFF; if(spart->rectmask) maskz= spart->rectmask[offset]; } @@ -560,8 +562,10 @@ static void do_strand_fillac(void *handle, int x, int y, float u, float v, float CHECK_ASSIGN(0); } - strand_shade_refcount(cache, sseg->v[1]); - strand_shade_refcount(cache, sseg->v[2]); + if(cache) { + strand_shade_refcount(cache, sseg->v[1]); + strand_shade_refcount(cache, sseg->v[2]); + } spart->totapixbuf[offset]++; } } @@ -596,23 +600,16 @@ static void do_scanconvert_strand(Render *re, StrandPart *spart, ZSpan *zspan, f VECCOPY(jco3, co3); VECCOPY(jco4, co4); - if(re->osa) { - jx= -re->jit[sample][0]; - jy= -re->jit[sample][1]; + if(spart->jit) { + jx= -spart->jit[sample][0]; + jy= -spart->jit[sample][1]; jco1[0] += jx; jco1[1] += jy; jco2[0] += jx; jco2[1] += jy; jco3[0] += jx; jco3[1] += jy; jco4[0] += jx; jco4[1] += jy; - } - else if(re->i.curblur) { - jx= -re->jit[re->i.curblur-1][0]; - jy= -re->jit[re->i.curblur-1][1]; - jco1[0] += jx; jco1[1] += jy; - jco2[0] += jx; jco2[1] += jy; - jco3[0] += jx; jco3[1] += jy; - jco4[0] += jx; jco4[1] += jy; + /* XXX mblur? */ } spart->sample= sample; @@ -756,7 +753,7 @@ void render_strand_segment(Render *re, float winmat[][4], StrandPart *spart, ZSp } /* render call to fill in strands */ -int zbuffer_strands_abuf(Render *re, RenderPart *pa, RenderLayer *rl, APixstrand *apixbuf, ListBase *apsmbase, StrandShadeCache *cache) +int zbuffer_strands_abuf(Render *re, RenderPart *pa, APixstrand *apixbuf, ListBase *apsmbase, unsigned int lay, int negzmask, float winmat[][4], int winx, int winy, int sample, float (*jit)[2], float clipcrop, int shadow, StrandShadeCache *cache) { ObjectRen *obr; ObjectInstanceRen *obi; @@ -768,7 +765,7 @@ int zbuffer_strands_abuf(Render *re, RenderPart *pa, RenderLayer *rl, APixstrand StrandSegment sseg; StrandSortSegment *sortsegments = NULL, *sortseg, *firstseg; MemArena *memarena; - float z[4], bounds[4], winmat[4][4]; + float z[4], bounds[4], obwinmat[4][4]; int a, b, c, i, totsegment, clip[4]; if(re->test_break(re->tbh)) @@ -788,27 +785,31 @@ int zbuffer_strands_abuf(Render *re, RenderPart *pa, RenderLayer *rl, APixstrand spart.rectz= pa->rectz; spart.rectmask= pa->rectmask; spart.cache= cache; + spart.shadow= shadow; + spart.jit= jit; - zbuf_alloc_span(&zspan, pa->rectx, pa->recty, re->clipcrop); + zbuf_alloc_span(&zspan, pa->rectx, pa->recty, clipcrop); /* needed for transform from hoco to zbuffer co */ - zspan.zmulx= ((float)re->winx)/2.0; - zspan.zmuly= ((float)re->winy)/2.0; + zspan.zmulx= ((float)winx)/2.0; + zspan.zmuly= ((float)winy)/2.0; zspan.zofsx= -pa->disprect.xmin; zspan.zofsy= -pa->disprect.ymin; /* to center the sample position */ - zspan.zofsx -= 0.5f; - zspan.zofsy -= 0.5f; + if(!shadow) { + zspan.zofsx -= 0.5f; + zspan.zofsy -= 0.5f; + } zspan.apsmbase= apsmbase; /* clipping setup */ - bounds[0]= (2*pa->disprect.xmin - re->winx-1)/(float)re->winx; - bounds[1]= (2*pa->disprect.xmax - re->winx+1)/(float)re->winx; - bounds[2]= (2*pa->disprect.ymin - re->winy-1)/(float)re->winy; - bounds[3]= (2*pa->disprect.ymax - re->winy+1)/(float)re->winy; + bounds[0]= (2*pa->disprect.xmin - winx-1)/(float)winx; + bounds[1]= (2*pa->disprect.xmax - winx+1)/(float)winx; + bounds[2]= (2*pa->disprect.ymin - winy-1)/(float)winy; + bounds[3]= (2*pa->disprect.ymax - winy+1)/(float)winy; memarena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE); firstseg= NULL; @@ -819,14 +820,14 @@ int zbuffer_strands_abuf(Render *re, RenderPart *pa, RenderLayer *rl, APixstrand for(obi=re->instancetable.first, i=0; obi; obi=obi->next, i++) { obr= obi->obr; - if(!obr->strandbuf || !(obr->strandbuf->lay & rl->lay)) + if(!obr->strandbuf || !(obr->strandbuf->lay & lay)) continue; /* compute matrix and try clipping whole object */ if(obi->flag & R_TRANSFORMED) - zbuf_make_winmat(re, obi->mat, winmat); + Mat4MulMat4(obwinmat, obi->mat, winmat); else - zbuf_make_winmat(re, NULL, winmat); + Mat4CpyMat4(obwinmat, winmat); if(clip_render_object(obi->obr->boundbox, bounds, winmat)) continue; @@ -843,14 +844,14 @@ int zbuffer_strands_abuf(Render *re, RenderPart *pa, RenderLayer *rl, APixstrand svert= strand->vert; /* keep clipping and z depth for 4 control points */ - clip[1]= strand_test_clip(winmat, &zspan, bounds, svert->co, &z[1]); - clip[2]= strand_test_clip(winmat, &zspan, bounds, (svert+1)->co, &z[2]); + clip[1]= strand_test_clip(obwinmat, &zspan, bounds, svert->co, &z[1]); + clip[2]= strand_test_clip(obwinmat, &zspan, bounds, (svert+1)->co, &z[2]); clip[0]= clip[1]; z[0]= z[1]; for(b=0; b<strand->totvert-1; b++, svert++) { /* compute 4th point clipping and z depth */ if(b < strand->totvert-2) { - clip[3]= strand_test_clip(winmat, &zspan, bounds, (svert+2)->co, &z[3]); + clip[3]= strand_test_clip(obwinmat, &zspan, bounds, (svert+2)->co, &z[3]); } else { clip[3]= clip[2]; z[3]= z[2]; @@ -900,7 +901,11 @@ int zbuffer_strands_abuf(Render *re, RenderPart *pa, RenderLayer *rl, APixstrand obi= &re->objectinstance[sortseg->obi]; obr= obi->obr; - zbuf_make_winmat(re, NULL, winmat); + + if(obi->flag & R_TRANSFORMED) + Mat4MulMat4(obwinmat, obi->mat, winmat); + else + Mat4CpyMat4(obwinmat, winmat); sseg.obi= obi; sseg.strand= RE_findOrAddStrand(obr, sortseg->strand); @@ -917,7 +922,7 @@ int zbuffer_strands_abuf(Render *re, RenderPart *pa, RenderLayer *rl, APixstrand spart.segment= &sseg; - render_strand_segment(re, winmat, &spart, &zspan, 1, &sseg); + render_strand_segment(re, obwinmat, &spart, &zspan, 1, &sseg); } } diff --git a/source/blender/render/intern/source/texture.c b/source/blender/render/intern/source/texture.c index 2d2c01e0bf1..9510dc15e5a 100644 --- a/source/blender/render/intern/source/texture.c +++ b/source/blender/render/intern/source/texture.c @@ -721,7 +721,7 @@ static int evalnodes(Tex *tex, float *texvec, float *dxt, float *dyt, TexResult short rv = TEX_INT; bNodeTree *nodes = tex->nodetree; - ntreeTexExecTree(nodes, texres, texvec, dxt, dyt, thread, tex, which_output, R.r.cfra); + ntreeTexExecTree(nodes, texres, texvec, dxt, dyt, thread, tex, which_output, R.r.cfra, (R.r.scemode & R_TEXNODE_PREVIEW) != 0); if(texres->nor) rv |= TEX_NOR; rv |= TEX_RGB; @@ -1182,7 +1182,8 @@ static int multitex(Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, texres->talpha= 0; /* is set when image texture returns alpha (considered premul) */ if(tex->use_nodes && tex->nodetree) { - retval = evalnodes(tex, texvec, dxt, dyt, texres, thread, which_output); + if(osatex) retval = evalnodes(tex, texvec, dxt, dyt, texres, thread, which_output); + else retval = evalnodes(tex, texvec, NULL, NULL, texres, thread, which_output); } else switch(tex->type) { @@ -1468,9 +1469,12 @@ void texture_rgb_blend(float *in, float *tex, float *out, float fact, float facg } } -float texture_value_blend(float tex, float out, float fact, float facg, int blendtype, int flip) +float texture_value_blend(float tex, float out, float fact, float facg, int blendtype) { float in=0.0, facm, col, scf; + int flip= (facg < 0.0f); + + facg= fabsf(facg); fact*= facg; facm= 1.0-fact; @@ -1631,7 +1635,7 @@ void do_material_tex(ShadeInput *shi) float fact, facm, factt, facmm, stencilTin=1.0; float texvec[3], dxt[3], dyt[3], tempvec[3], norvec[3], warpvec[3]={0.0f, 0.0f, 0.0f}, Tnor=1.0; int tex_nr, rgbnor= 0, warpdone=0; - float nu[3], nv[3], nn[3] = {0,0,0}, dudnu = 1.f, dudnv = 0.f, dvdnu = 0.f, dvdnv = 1.f; // bump mapping + float nu[3] = {0,0,0}, nv[3] = {0,0,0}, nn[3] = {0,0,0}, dudnu = 1.f, dudnv = 0.f, dvdnu = 0.f, dvdnv = 1.f; // bump mapping int nunvdone= 0; if (R.r.scemode & R_NO_TEX) return; @@ -1732,6 +1736,16 @@ void do_material_tex(ShadeInput *shi) vlr_set_uv_indices(shi->vlr, &j1, &j2, &j3); + // compute ortho basis around normal + if(!nunvdone) { + // render normal is negated + nn[0] = -shi->vn[0]; + nn[1] = -shi->vn[1]; + nn[2] = -shi->vn[2]; + VecOrthoBasisf(nn, nu, nv); + nunvdone= 1; + } + if (tf) { float *uv1 = tf->uv[j1], *uv2 = tf->uv[j2], *uv3 = tf->uv[j3]; const float an[3] = {fabsf(nn[0]), fabsf(nn[1]), fabsf(nn[2])}; @@ -1813,7 +1827,7 @@ void do_material_tex(ShadeInput *shi) TexResult ttexr = {0, 0, 0, 0, 0, texres.talpha, NULL}; // temp TexResult float tco[3], texv[3], cd, ud, vd, du, dv, idu, idv; const int fromrgb = ((tex->type == TEX_IMAGE) || ((tex->flag & TEX_COLORBAND)!=0)); - const float bf = 0.04f*Tnor*((mtex->maptoneg & MAP_NORM) ? -mtex->norfac : mtex->norfac); + const float bf = 0.04f*Tnor*mtex->norfac; // disable internal bump eval float* nvec = texres.nor; texres.nor = NULL; @@ -2020,10 +2034,9 @@ void do_material_tex(ShadeInput *shi) /* mapping */ if(mtex->mapto & (MAP_COL+MAP_COLSPEC+MAP_COLMIR)) { - float tcol[3], colfac; + float tcol[3]; /* stencil maps on the texture control slider, not texture intensity value */ - colfac= mtex->colfac*stencilTin; tcol[0]=texres.tr; tcol[1]=texres.tg; tcol[2]=texres.tb; @@ -2043,15 +2056,19 @@ void do_material_tex(ShadeInput *shi) } if(mtex->mapto & MAP_COL) { + float colfac= mtex->colfac*stencilTin; texture_rgb_blend(&shi->r, tcol, &shi->r, texres.tin, colfac, mtex->blendtype); } if(mtex->mapto & MAP_COLSPEC) { - texture_rgb_blend(&shi->specr, tcol, &shi->specr, texres.tin, colfac, mtex->blendtype); + float colspecfac= mtex->colspecfac*stencilTin; + texture_rgb_blend(&shi->specr, tcol, &shi->specr, texres.tin, colspecfac, mtex->blendtype); } if(mtex->mapto & MAP_COLMIR) { + float mirrfac= mtex->mirrfac*stencilTin; + // exception for envmap only if(tex->type==TEX_ENVMAP && mtex->blendtype==MTEX_BLEND) { - fact= texres.tin*colfac; + fact= texres.tin*mirrfac; facm= 1.0- fact; shi->refcol[0]= fact + facm*shi->refcol[0]; shi->refcol[1]= fact*tcol[0] + facm*shi->refcol[1]; @@ -2059,14 +2076,13 @@ void do_material_tex(ShadeInput *shi) shi->refcol[3]= fact*tcol[2] + facm*shi->refcol[3]; } else { - texture_rgb_blend(&shi->mirr, tcol, &shi->mirr, texres.tin, colfac, mtex->blendtype); + texture_rgb_blend(&shi->mirr, tcol, &shi->mirr, texres.tin, mirrfac, mtex->blendtype); } } } if( (mtex->mapto & MAP_NORM) ) { if(texres.nor) { - if(mtex->maptoneg & MAP_NORM) tex->norfac= -mtex->norfac; - else tex->norfac= mtex->norfac; + tex->norfac= mtex->norfac; /* we need to code blending modes for normals too once.. now 1 exception hardcoded */ @@ -2074,7 +2090,7 @@ void do_material_tex(ShadeInput *shi) /* qdn: for normalmaps, to invert the normalmap vector, it is better to negate x & y instead of subtracting the vector as was done before */ tex->norfac = mtex->norfac; - if (mtex->maptoneg & MAP_NORM) { + if (tex->norfac < 0.0f) { texres.nor[0] = -texres.nor[0]; texres.nor[1] = -texres.nor[1]; } @@ -2159,8 +2175,7 @@ void do_material_tex(ShadeInput *shi) /* Now that most textures offer both Nor and Intensity, allow */ /* both to work, and let user select with slider. */ if(texres.nor) { - if(mtex->maptoneg & MAP_DISPLACE) tex->norfac= -mtex->norfac; - else tex->norfac= mtex->norfac; + tex->norfac= mtex->norfac; shi->displace[0]+= 0.2f*Tnor*tex->norfac*texres.nor[0]; shi->displace[1]+= 0.2f*Tnor*tex->norfac*texres.nor[1]; @@ -2172,12 +2187,7 @@ void do_material_tex(ShadeInput *shi) else texres.tin= (0.35f*texres.tr+0.45f*texres.tg+0.2f*texres.tb); } - if(mtex->maptoneg & MAP_DISPLACE) { - factt= (texres.tin-0.5f)*mtex->dispfac*stencilTin; facmm= 1.0f-factt; - } - else { - factt= (0.5f-texres.tin)*mtex->dispfac*stencilTin; facmm= 1.0f-factt; - } + factt= (0.5f-texres.tin)*mtex->dispfac*stencilTin; facmm= 1.0f-factt; if(mtex->blendtype==MTEX_BLEND) { shi->displace[0]= factt*shi->vn[0] + facmm*shi->displace[0]; @@ -2200,7 +2210,6 @@ void do_material_tex(ShadeInput *shi) if(mtex->mapto & MAP_VARS) { /* stencil maps on the texture control slider, not texture intensity value */ - float varfac= mtex->varfac*stencilTin; if(rgbnor & TEX_RGB) { if(texres.talpha) texres.tin= texres.ta; @@ -2208,66 +2217,59 @@ void do_material_tex(ShadeInput *shi) } if(mtex->mapto & MAP_REF) { - int flip= mtex->maptoneg & MAP_REF; + float difffac= mtex->difffac*stencilTin; - shi->refl= texture_value_blend(mtex->def_var, shi->refl, texres.tin, varfac, mtex->blendtype, flip); + shi->refl= texture_value_blend(mtex->def_var, shi->refl, texres.tin, difffac, mtex->blendtype); if(shi->refl<0.0) shi->refl= 0.0; } if(mtex->mapto & MAP_SPEC) { - int flip= mtex->maptoneg & MAP_SPEC; + float specfac= mtex->specfac*stencilTin; - shi->spec= texture_value_blend(mtex->def_var, shi->spec, texres.tin, varfac, mtex->blendtype, flip); + shi->spec= texture_value_blend(mtex->def_var, shi->spec, texres.tin, specfac, mtex->blendtype); if(shi->spec<0.0) shi->spec= 0.0; } if(mtex->mapto & MAP_EMIT) { - int flip= mtex->maptoneg & MAP_EMIT; + float emitfac= mtex->emitfac*stencilTin; - shi->emit= texture_value_blend(mtex->def_var, shi->emit, texres.tin, varfac, mtex->blendtype, flip); + shi->emit= texture_value_blend(mtex->def_var, shi->emit, texres.tin, emitfac, mtex->blendtype); if(shi->emit<0.0) shi->emit= 0.0; } if(mtex->mapto & MAP_ALPHA) { - int flip= mtex->maptoneg & MAP_ALPHA; + float alphafac= mtex->alphafac*stencilTin; - shi->alpha= texture_value_blend(mtex->def_var, shi->alpha, texres.tin, varfac, mtex->blendtype, flip); + shi->alpha= texture_value_blend(mtex->def_var, shi->alpha, texres.tin, alphafac, mtex->blendtype); if(shi->alpha<0.0) shi->alpha= 0.0; else if(shi->alpha>1.0) shi->alpha= 1.0; } if(mtex->mapto & MAP_HAR) { - int flip= mtex->maptoneg & MAP_HAR; float har; // have to map to 0-1 + float hardfac= mtex->hardfac*stencilTin; har= ((float)shi->har)/128.0; - har= 128.0*texture_value_blend(mtex->def_var, har, texres.tin, varfac, mtex->blendtype, flip); + har= 128.0*texture_value_blend(mtex->def_var, har, texres.tin, hardfac, mtex->blendtype); if(har<1.0) shi->har= 1; else if(har>511.0) shi->har= 511; else shi->har= (int)har; } if(mtex->mapto & MAP_RAYMIRR) { - int flip= mtex->maptoneg & MAP_RAYMIRR; + float raymirrfac= mtex->raymirrfac*stencilTin; - shi->ray_mirror= texture_value_blend(mtex->def_var, shi->ray_mirror, texres.tin, varfac, mtex->blendtype, flip); + shi->ray_mirror= texture_value_blend(mtex->def_var, shi->ray_mirror, texres.tin, raymirrfac, mtex->blendtype); if(shi->ray_mirror<0.0) shi->ray_mirror= 0.0; else if(shi->ray_mirror>1.0) shi->ray_mirror= 1.0; } if(mtex->mapto & MAP_TRANSLU) { - int flip= mtex->maptoneg & MAP_TRANSLU; + float translfac= mtex->translfac*stencilTin; - shi->translucency= texture_value_blend(mtex->def_var, shi->translucency, texres.tin, varfac, mtex->blendtype, flip); + shi->translucency= texture_value_blend(mtex->def_var, shi->translucency, texres.tin, translfac, mtex->blendtype); if(shi->translucency<0.0) shi->translucency= 0.0; else if(shi->translucency>1.0) shi->translucency= 1.0; } - if(mtex->mapto & MAP_LAYER) { - int flip= mtex->maptoneg & MAP_LAYER; - - shi->layerfac= texture_value_blend(mtex->def_var, shi->layerfac, texres.tin, varfac, mtex->blendtype, flip); - if(shi->layerfac<0.0) shi->layerfac= 0.0; - else if(shi->layerfac>1.0) shi->layerfac= 1.0; - } if(mtex->mapto & MAP_AMB) { - int flip= mtex->maptoneg & MAP_AMB; + float ambfac= mtex->ambfac*stencilTin; - shi->amb= texture_value_blend(mtex->def_var, shi->amb, texres.tin, varfac, mtex->blendtype, flip); + shi->amb= texture_value_blend(mtex->def_var, shi->amb, texres.tin, ambfac, mtex->blendtype); if(shi->amb<0.0) shi->amb= 0.0; else if(shi->amb>1.0) shi->amb= 1.0; @@ -2385,11 +2387,10 @@ void do_volume_tex(ShadeInput *shi, float *xyz, int mapto_flag, float *col, floa } - if((mapto_flag & (MAP_EMISSION_COL+MAP_ABSORPTION_COL)) && (mtex->mapto & (MAP_EMISSION_COL+MAP_ABSORPTION_COL))) { - float tcol[3], colfac; + if((mapto_flag & (MAP_EMISSION_COL+MAP_TRANSMISSION_COL+MAP_REFLECTION_COL)) && (mtex->mapto & (MAP_EMISSION_COL+MAP_TRANSMISSION_COL+MAP_REFLECTION_COL))) { + float tcol[3]; /* stencil maps on the texture control slider, not texture intensity value */ - colfac= mtex->colfac*stencilTin; if((rgbnor & TEX_RGB)==0) { tcol[0]= mtex->r; @@ -2410,18 +2411,23 @@ void do_volume_tex(ShadeInput *shi, float *xyz, int mapto_flag, float *col, floa /* used for emit */ if((mapto_flag & MAP_EMISSION_COL) && (mtex->mapto & MAP_EMISSION_COL)) { - texture_rgb_blend(col, tcol, col, texres.tin, colfac, mtex->blendtype); + float colemitfac= mtex->colemitfac*stencilTin; + texture_rgb_blend(col, tcol, col, texres.tin, colemitfac, mtex->blendtype); } - /* MAP_COLMIR is abused for absorption colour at the moment */ - if((mapto_flag & MAP_ABSORPTION_COL) && (mtex->mapto & MAP_ABSORPTION_COL)) { - texture_rgb_blend(col, tcol, col, texres.tin, colfac, mtex->blendtype); + if((mapto_flag & MAP_REFLECTION_COL) && (mtex->mapto & MAP_REFLECTION_COL)) { + float colreflfac= mtex->colreflfac*stencilTin; + texture_rgb_blend(col, tcol, col, texres.tin, colreflfac, mtex->blendtype); + } + + if((mapto_flag & MAP_TRANSMISSION_COL) && (mtex->mapto & MAP_TRANSMISSION_COL)) { + float coltransfac= mtex->coltransfac*stencilTin; + texture_rgb_blend(col, tcol, col, texres.tin, coltransfac, mtex->blendtype); } } if((mapto_flag & MAP_VARS) && (mtex->mapto & MAP_VARS)) { /* stencil maps on the texture control slider, not texture intensity value */ - float varfac= mtex->varfac*stencilTin; /* convert RGB to intensity if intensity info isn't provided */ if (!(rgbnor & TEX_INT)) { @@ -2432,27 +2438,27 @@ void do_volume_tex(ShadeInput *shi, float *xyz, int mapto_flag, float *col, floa } if((mapto_flag & MAP_EMISSION) && (mtex->mapto & MAP_EMISSION)) { - int flip= mtex->maptoneg & MAP_EMISSION; + float emitfac= mtex->emitfac*stencilTin; - *val = texture_value_blend(mtex->def_var, *val, texres.tin, varfac, mtex->blendtype, flip); + *val = texture_value_blend(mtex->def_var, *val, texres.tin, emitfac, mtex->blendtype); if(*val<0.0) *val= 0.0; } if((mapto_flag & MAP_DENSITY) && (mtex->mapto & MAP_DENSITY)) { - int flip= mtex->maptoneg & MAP_DENSITY; + float densfac= mtex->densfac*stencilTin; - *val = texture_value_blend(mtex->def_var, *val, texres.tin, varfac, mtex->blendtype, flip); + *val = texture_value_blend(mtex->def_var, *val, texres.tin, densfac, mtex->blendtype); CLAMP(*val, 0.0, 1.0); } - if((mapto_flag & MAP_ABSORPTION) && (mtex->mapto & MAP_ABSORPTION)) { - int flip= mtex->maptoneg & MAP_ABSORPTION; + if((mapto_flag & MAP_SCATTERING) && (mtex->mapto & MAP_SCATTERING)) { + float scatterfac= mtex->scatterfac*stencilTin; - *val = texture_value_blend(mtex->def_var, *val, texres.tin, varfac, mtex->blendtype, flip); + *val = texture_value_blend(mtex->def_var, *val, texres.tin, scatterfac, mtex->blendtype); CLAMP(*val, 0.0, 1.0); } - if((mapto_flag & MAP_SCATTERING) && (mtex->mapto & MAP_SCATTERING)) { - int flip= mtex->maptoneg & MAP_SCATTERING; + if((mapto_flag & MAP_REFLECTION) && (mtex->mapto & MAP_REFLECTION)) { + float reflfac= mtex->reflfac*stencilTin; - *val = texture_value_blend(mtex->def_var, *val, texres.tin, varfac, mtex->blendtype, flip); + *val = texture_value_blend(mtex->def_var, *val, texres.tin, reflfac, mtex->blendtype); CLAMP(*val, 0.0, 1.0); } } @@ -2767,7 +2773,7 @@ void do_sky_tex(float *rco, float *lo, float *dxyview, float *hor, float *zen, f if(mtex->mapto & WOMAP_BLEND) { if(rgb) texres.tin= (0.35*texres.tr+0.45*texres.tg+0.2*texres.tb); - *blend= texture_value_blend(mtex->def_var, *blend, texres.tin, mtex->varfac, mtex->blendtype, 0); + *blend= texture_value_blend(mtex->def_var, *blend, texres.tin, mtex->blendfac, mtex->blendtype); } } } diff --git a/source/blender/render/intern/source/volume_precache.c b/source/blender/render/intern/source/volume_precache.c index 15d8643fea4..62d7343036a 100644 --- a/source/blender/render/intern/source/volume_precache.c +++ b/source/blender/render/intern/source/volume_precache.c @@ -46,6 +46,7 @@ #include "DNA_material_types.h" #include "render_types.h" +#include "rendercore.h" #include "renderdatabase.h" #include "volumetric.h" #include "volume_precache.h" @@ -66,18 +67,21 @@ extern struct Render R; /* 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 */ -int intersect_outside_volume(RayTree *tree, Isect *isect, float *offset, int limit, int depth) +int intersect_outside_volume(RayObject *tree, Isect *isect, float *offset, int limit, int depth) { if (limit == 0) return depth; - if (RE_ray_tree_intersect(tree, isect)) { - float hitco[3]; + if (RE_rayobject_raycast(tree, isect)) { + + isect->start[0] = isect->start[0] + isect->labda*isect->vec[0]; + isect->start[1] = isect->start[1] + isect->labda*isect->vec[1]; + isect->start[2] = isect->start[2] + isect->labda*isect->vec[2]; + + isect->labda = FLT_MAX; + isect->skip = RE_SKIP_VLR_NEIGHBOUR; + isect->orig.face= isect->hit.face; + isect->orig.ob= isect->hit.ob; - hitco[0] = isect->start[0] + isect->labda*isect->vec[0]; - hitco[1] = isect->start[1] + isect->labda*isect->vec[1]; - hitco[2] = isect->start[2] + isect->labda*isect->vec[2]; - VecAddf(isect->start, hitco, offset); - return intersect_outside_volume(tree, isect, offset, limit-1, depth+1); } else { return depth; @@ -85,9 +89,8 @@ int intersect_outside_volume(RayTree *tree, Isect *isect, float *offset, int lim } /* Uses ray tracing to check if a point is inside or outside an ObjectInstanceRen */ -int point_inside_obi(RayTree *tree, ObjectInstanceRen *obi, float *co) +int point_inside_obi(RayObject *tree, ObjectInstanceRen *obi, float *co) { - float maxsize = RE_ray_tree_max_size(tree); Isect isect; float vec[3] = {0.0f,0.0f,1.0f}; int final_depth=0, depth=0, limit=20; @@ -95,18 +98,15 @@ int point_inside_obi(RayTree *tree, ObjectInstanceRen *obi, float *co) /* set up the isect */ memset(&isect, 0, sizeof(isect)); VECCOPY(isect.start, co); - isect.end[0] = co[0] + vec[0] * maxsize; - isect.end[1] = co[1] + vec[1] * maxsize; - isect.end[2] = co[2] + vec[2] * maxsize; - - /* and give it a little offset to prevent self-intersections */ - VecMulf(vec, 1e-5); - VecAddf(isect.start, isect.start, vec); - + VECCOPY(isect.vec, vec); isect.mode= RE_RAY_MIRROR; - isect.face_last= NULL; + isect.last_hit= NULL; isect.lay= -1; + isect.labda = FLT_MAX; + isect.orig.face= NULL; + isect.orig.ob = NULL; + final_depth = intersect_outside_volume(tree, &isect, vec, limit, depth); /* even number of intersections: point is outside @@ -115,44 +115,6 @@ int point_inside_obi(RayTree *tree, ObjectInstanceRen *obi, float *co) else return 1; } -static int inside_check_func(Isect *is, int ob, RayFace *face) -{ - return 1; -} -static void vlr_face_coords(RayFace *face, float **v1, float **v2, float **v3, float **v4) -{ - VlakRen *vlr= (VlakRen*)face; - - *v1 = (vlr->v1)? vlr->v1->co: NULL; - *v2 = (vlr->v2)? vlr->v2->co: NULL; - *v3 = (vlr->v3)? vlr->v3->co: NULL; - *v4 = (vlr->v4)? vlr->v4->co: NULL; -} - -RayTree *create_raytree_obi(ObjectInstanceRen *obi, float *bbmin, float *bbmax) -{ - int v; - VlakRen *vlr= NULL; - - /* create empty raytree */ - RayTree *tree = RE_ray_tree_create(64, obi->obr->totvlak, bbmin, bbmax, - vlr_face_coords, inside_check_func, NULL, NULL); - - /* fill it with faces */ - for(v=0; v<obi->obr->totvlak; v++) { - if((v & 255)==0) - vlr= obi->obr->vlaknodes[v>>8].vlak; - else - vlr++; - - RE_ray_tree_add_face(tree, 0, vlr); - } - - RE_ray_tree_done(tree); - - return tree; -} - /* *** light cache filtering *** */ static float get_avg_surrounds(float *cache, int *res, int xx, int yy, int zz) @@ -185,9 +147,9 @@ static float get_avg_surrounds(float *cache, int *res, int xx, int yy, int zz) } } - tot /= added; + if (added > 0) tot /= added; - return ((added>0)?tot:0.0f); + return tot; } /* function to filter the edges of the light cache, where there was no volume originally. @@ -202,17 +164,54 @@ static void lightcache_filter(VolumePrecache *vp) for (y=0; y < vp->res[1]; y++) { for (x=0; x < vp->res[0]; x++) { /* trigger for outside mesh */ - if (vp->data_r[ V_I(x, y, z, vp->res) ] < -0.5f) + if (vp->data_r[ V_I(x, y, z, vp->res) ] < -0.f) vp->data_r[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_r, vp->res, x, y, z); - if (vp->data_g[ V_I(x, y, z, vp->res) ] < -0.5f) + if (vp->data_g[ V_I(x, y, z, vp->res) ] < -0.f) vp->data_g[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_g, vp->res, x, y, z); - if (vp->data_b[ V_I(x, y, z, vp->res) ] < -0.5f) + if (vp->data_b[ V_I(x, y, z, vp->res) ] < -0.f) vp->data_b[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_b, vp->res, x, y, z); } } } } +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 */ + if (vp->data_r[ V_I(x, y, z, vp->res) ] < -0.f) + new_r[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_r, vp->res, x, y, z); + if (vp->data_g[ V_I(x, y, z, vp->res) ] < -0.f) + new_g[ V_I(x, y, z, vp->res) ] = get_avg_surrounds(vp->data_g, vp->res, x, y, z); + if (vp->data_b[ V_I(x, y, z, vp->res) ] < -0.f) + new_b[ V_I(x, y, z, vp->res) ] = 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; } +} + static inline int ms_I(int x, int y, int z, int *n) //has a pad of 1 voxel surrounding the core for boundary simulation { return z*(n[1]+2)*(n[0]+2) + y*(n[0]+2) + x; @@ -364,7 +363,7 @@ void multiple_scattering_diffusion(Render *re, VolumePrecache *vp, Material *ma) fac *= (energy_ss / energy_ms); /* blend multiple scattering back in the light cache */ - if (shade_type == MA_VOL_SHADE_SINGLEPLUSMULTIPLE) { + if (shade_type == MA_VOL_SHADE_SHADEDPLUSMULTIPLE) { /* conserve energy - half single, half multiple */ origf = 0.5f; fac *= 0.5f; @@ -421,13 +420,12 @@ static void *vol_precache_part(void *data) { VolPrecachePart *pa = (VolPrecachePart *)data; ObjectInstanceRen *obi = pa->obi; - RayTree *tree = pa->tree; + RayObject *tree = pa->tree; ShadeInput *shi = pa->shi; - float density, scatter_col[3] = {0.f, 0.f, 0.f}; + float scatter_col[3] = {0.f, 0.f, 0.f}; float co[3]; int x, y, z; const int res[3]= {pa->res[0], pa->res[1], pa->res[2]}; - const float stepsize = vol_get_stepsize(shi, STEPSIZE_VIEW); for (z= pa->minz; z < pa->maxz; z++) { co[2] = pa->bbmin[2] + (pa->voxel[2] * (z + 0.5f)); @@ -448,8 +446,7 @@ static void *vol_precache_part(void *data) VecCopyf(shi->view, co); Normalize(shi->view); - density = vol_get_density(shi, co); - vol_get_scattering(shi, scatter_col, co, stepsize, density); + vol_get_scattering(shi, scatter_col, co); obi->volume_precache->data_r[ V_I(x, y, z, res) ] = scatter_col[0]; obi->volume_precache->data_g[ V_I(x, y, z, res) ] = scatter_col[1]; @@ -478,7 +475,7 @@ static void precache_setup_shadeinput(Render *re, ObjectInstanceRen *obi, Materi shi->lay = re->scene->lay; } -static void precache_init_parts(Render *re, RayTree *tree, ShadeInput *shi, ObjectInstanceRen *obi, int totthread, int *parts) +static void precache_init_parts(Render *re, RayObject *tree, ShadeInput *shi, ObjectInstanceRen *obi, int totthread, int *parts) { VolumePrecache *vp = obi->volume_precache; int i=0, x, y, z; @@ -593,7 +590,7 @@ void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Mat { VolumePrecache *vp; VolPrecachePart *nextpa, *pa; - RayTree *tree; + RayObject *tree; ShadeInput shi; ListBase threads; float *bbmin=obi->obr->boundbox[0], *bbmax=obi->obr->boundbox[1]; @@ -608,8 +605,11 @@ void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Mat /* create a raytree with just the faces of the instanced ObjectRen, * used for checking if the cached point is inside or outside. */ - tree = create_raytree_obi(obi, bbmin, bbmax); + //tree = create_raytree_obi(obi, bbmin, bbmax); + tree = makeraytree_object(&R, obi); if (!tree) return; + INIT_MINMAX(bbmin, bbmax); + RE_rayobject_merge_bb( tree, bbmin, bbmax); vp = MEM_callocN(sizeof(VolumePrecache), "volume light cache"); @@ -672,13 +672,14 @@ void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Mat BLI_freelistN(&re->volume_precache_parts); if(tree) { - RE_ray_tree_free(tree); - tree= NULL; + //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; } lightcache_filter(obi->volume_precache); - if (ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SINGLEPLUSMULTIPLE)) + if (ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SHADEDPLUSMULTIPLE)) { multiple_scattering_diffusion(re, vp, ma); } @@ -686,8 +687,8 @@ void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Mat static int using_lightcache(Material *ma) { - return (((ma->vol.shadeflag & MA_VOL_PRECACHESHADING) && (ma->vol.shade_type == MA_VOL_SHADE_SINGLE)) - || (ELEM(ma->vol.shade_type, MA_VOL_SHADE_MULTIPLE, MA_VOL_SHADE_SINGLEPLUSMULTIPLE))); + 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) @@ -728,18 +729,19 @@ void free_volume_precache(Render *re) BLI_freelistN(&re->volumes); } -int point_inside_volume_objectinstance(ObjectInstanceRen *obi, float *co) +int point_inside_volume_objectinstance(Render *re, ObjectInstanceRen *obi, float *co) { - RayTree *tree; + RayObject *tree; int inside=0; - tree = create_raytree_obi(obi, obi->obr->boundbox[0], obi->obr->boundbox[1]); + tree = makeraytree_object(re, obi); //create_raytree_obi(obi, obi->obr->boundbox[0], obi->obr->boundbox[1]); if (!tree) return 0; inside = point_inside_obi(tree, obi, co); - RE_ray_tree_free(tree); - tree= NULL; + //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; } diff --git a/source/blender/render/intern/source/volumetric.c b/source/blender/render/intern/source/volumetric.c index bc425c8a1a3..7cb165ccaea 100644 --- a/source/blender/render/intern/source/volumetric.c +++ b/source/blender/render/intern/source/volumetric.c @@ -51,6 +51,7 @@ #include "render_types.h" #include "pixelshading.h" #include "shading.h" +#include "shadbuf.h" #include "texture.h" #include "volumetric.h" #include "volume_precache.h" @@ -72,11 +73,53 @@ inline float luminance(float* col) } /* tracing */ +static float vol_get_shadow(ShadeInput *shi, LampRen *lar, float *co) +{ + float visibility = 1.f; + + if(lar->shb) { + float dot=1.f; + 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; + + VecCopyf(is.start, co); + if(lar->type==LA_SUN || lar->type==LA_HEMI) { + is.vec[0] = -lar->vec[0]; + is.vec[1] = -lar->vec[1]; + is.vec[2] = -lar->vec[2]; + is.labda = R.maxdist; + } else { + VECSUB( is.vec, lar->co, is.start ); + is.labda = VecLength( is.vec ); + } + + is.mode = RE_RAY_MIRROR; + is.skip = RE_SKIP_VLR_RENDER_CHECK | RE_SKIP_VLR_NON_SOLID_MATERIAL; + + 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]; + + if(RE_rayobject_raycast(R.raytree,&is)) { + visibility = 0.f; + } + + lar->last_hit[shi->thread]= is.last_hit; + } + return visibility; +} static int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco, Isect *isect, int intersect_type) { - float maxsize = RE_ray_tree_max_size(R.raytree); - /* XXX TODO - get raytrace max distance from object instance's bounding box */ /* need to account for scaling only, but keep coords in camera space... * below code is WIP and doesn't work! @@ -86,20 +129,22 @@ static int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco, */ VECCOPY(isect->start, co); - isect->end[0] = co[0] + vec[0] * maxsize; - isect->end[1] = co[1] + vec[1] * maxsize; - isect->end[2] = co[2] + vec[2] * maxsize; - + VECCOPY(isect->vec, vec ); + isect->labda = FLT_MAX; isect->mode= RE_RAY_MIRROR; - isect->oborig= RAY_OBJECT_SET(&R, shi->obi); - isect->face_last= NULL; - isect->ob_last= 0; + isect->last_hit = NULL; isect->lay= -1; - if (intersect_type == VOL_BOUNDS_DEPTH) isect->faceorig= (RayFace*)shi->vlr; - else if (intersect_type == VOL_BOUNDS_SS) isect->faceorig= NULL; + 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->orig.face= NULL; + isect->orig.ob = NULL; + } - if(RE_ray_tree_intersect(R.raytree, isect)) + if(RE_rayobject_raycast(R.raytree, isect)) { hitco[0] = isect->start[0] + isect->labda*isect->vec[0]; hitco[1] = isect->start[1] + isect->labda*isect->vec[1]; @@ -146,23 +191,20 @@ static void shade_intersection(ShadeInput *shi, float *col, Isect *is) static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, float *co, float *col) { Isect isect; - float maxsize = RE_ray_tree_max_size(R.raytree); VECCOPY(isect.start, co); - isect.end[0] = isect.start[0] + shi->view[0] * maxsize; - isect.end[1] = isect.start[1] + shi->view[1] * maxsize; - isect.end[2] = isect.start[2] + shi->view[2] * maxsize; - - isect.faceorig= (RayFace *)vlr; + VECCOPY(isect.vec, shi->view); + isect.labda = FLT_MAX; isect.mode= RE_RAY_MIRROR; - isect.oborig= RAY_OBJECT_SET(&R, shi->obi); - isect.face_last= NULL; - isect.ob_last= 0; + isect.skip = RE_SKIP_VLR_NEIGHBOUR | RE_SKIP_VLR_RENDER_CHECK; + 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 */ - if(RE_ray_tree_intersect(R.raytree, &isect)) { + if(RE_rayobject_raycast(R.raytree, &isect)) { shade_intersection(shi, col, &isect); } else { shadeSkyView(col, co, shi->view, NULL, shi->thread); @@ -170,29 +212,6 @@ static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, float *co, float *co } } -/* input shader data */ - -float vol_get_stepsize(struct ShadeInput *shi, int context) -{ - if (shi->mat->vol.stepsize_type == MA_VOL_STEP_RANDOMIZED) { - /* range between 0.75 and 1.25 */ - const float rnd = 0.5f * BLI_thread_frand(shi->thread) + 0.75f; - - if (context == STEPSIZE_VIEW) - return shi->mat->vol.stepsize * rnd; - else if (context == STEPSIZE_SHADE) - return shi->mat->vol.shade_stepsize * rnd; - } - else { // MA_VOL_STEP_CONSTANT - - if (context == STEPSIZE_VIEW) - return shi->mat->vol.stepsize; - else if (context == STEPSIZE_SHADE) - return shi->mat->vol.shade_stepsize; - } - - return shi->mat->vol.stepsize; -} /* trilinear interpolation */ static void vol_get_precached_scattering(ShadeInput *shi, float *scatter_col, float *co) @@ -212,9 +231,9 @@ static void vol_get_precached_scattering(ShadeInput *shi, float *scatter_col, fl sample_co[1] = ((co[1] - bbmin[1]) / dim[1]); sample_co[2] = ((co[2] - bbmin[2]) / dim[2]); - scatter_col[0] = voxel_sample_trilinear(vp->data_r, vp->res, sample_co); - scatter_col[1] = voxel_sample_trilinear(vp->data_g, vp->res, sample_co); - scatter_col[2] = voxel_sample_trilinear(vp->data_b, vp->res, sample_co); + scatter_col[0] = voxel_sample_triquadratic(vp->data_r, vp->res, sample_co); + scatter_col[1] = voxel_sample_triquadratic(vp->data_g, vp->res, sample_co); + scatter_col[2] = voxel_sample_triquadratic(vp->data_b, vp->res, sample_co); } /* Meta object density, brute force for now @@ -270,7 +289,8 @@ float vol_get_density(struct ShadeInput *shi, float *co) float density = shi->mat->vol.density; float density_scale = shi->mat->vol.density_scale; - do_volume_tex(shi, co, MAP_DENSITY, NULL, &density); + if (shi->mat->mapto_textured & MAP_DENSITY) + do_volume_tex(shi, co, MAP_DENSITY, NULL, &density); // if meta-object, modulate by metadensity without increasing it if (shi->obi->obr->ob->type == OB_MBALL) { @@ -281,79 +301,110 @@ float vol_get_density(struct ShadeInput *shi, float *co) return density * density_scale; } -/* scattering multiplier, values above 1.0 are non-physical, - * but can be useful to tweak lighting */ -float vol_get_scattering_fac(ShadeInput *shi, float *co) + +/* 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 */ +void vol_get_reflection_color(ShadeInput *shi, float *ref_col, float *co) { float scatter = shi->mat->vol.scattering; - float col[3] = {0.0, 0.0, 0.0}; + float reflection= shi->mat->vol.reflection; + VECCOPY(ref_col, shi->mat->vol.reflection_col); - do_volume_tex(shi, co, MAP_SCATTERING, col, &scatter); + if (shi->mat->mapto_textured & (MAP_SCATTERING+MAP_REFLECTION_COL)) + do_volume_tex(shi, co, MAP_SCATTERING+MAP_REFLECTION_COL, ref_col, &scatter); - return scatter; + /* 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); + + 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' */ -void vol_get_emission(ShadeInput *shi, float *emission_col, float *co, float density) +void vol_get_emission(ShadeInput *shi, float *emission_col, float *co) { float emission = shi->mat->vol.emission; VECCOPY(emission_col, shi->mat->vol.emission_col); - do_volume_tex(shi, co, MAP_EMISSION+MAP_EMISSION_COL, emission_col, &emission); + if (shi->mat->mapto_textured & (MAP_EMISSION+MAP_EMISSION_COL)) + do_volume_tex(shi, co, MAP_EMISSION+MAP_EMISSION_COL, emission_col, &emission); - emission_col[0] = emission_col[0] * emission * density; - emission_col[1] = emission_col[1] * emission * density; - emission_col[2] = emission_col[2] * emission * density; + emission_col[0] = emission_col[0] * emission; + emission_col[1] = emission_col[1] * emission; + emission_col[2] = emission_col[2] * emission; } -void vol_get_absorption(ShadeInput *shi, float *absorb_col, float *co) + +/* A combination of scattering and absorption -> known as sigma T. + * This can possibly use a specific scattering colour, + * 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. */ +void vol_get_sigma_t(ShadeInput *shi, float *sigma_t, float *co) { - float absorption = shi->mat->vol.absorption; - VECCOPY(absorb_col, shi->mat->vol.absorption_col); + /* 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; - do_volume_tex(shi, co, MAP_ABSORPTION+MAP_ABSORPTION_COL, absorb_col, &absorption); + if (shi->mat->mapto_textured & (MAP_SCATTERING+MAP_TRANSMISSION_COL)) + do_volume_tex(shi, co, MAP_SCATTERING+MAP_TRANSMISSION_COL, transmission_col, &scattering); - absorb_col[0] = (1.0f - absorb_col[0]) * absorption; - absorb_col[1] = (1.0f - absorb_col[1]) * absorption; - absorb_col[2] = (1.0f - absorb_col[2]) * absorption; + 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 */ -float vol_get_phasefunc(ShadeInput *shi, short phasefunc_type, float g, float *w, float *wp) +float vol_get_phasefunc(ShadeInput *shi, float g, float *w, float *wp) { - const float costheta = Inpf(w, wp); - const float scale = M_PI; - - /* - * Scale constant is required, since Blender's shading system doesn't normalise for - * energy conservation - eg. scaling by 1/pi for a lambert shader. - * This makes volumes darker than other solid objects, for the same lighting intensity. - * To correct this, scale up the phase function values + 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 normalise 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 - .55f * g * g * g; + const float kcostheta = k * Inpf(w, wp); + return normalize * (1.f - k*k) / ((1.f - kcostheta) * (1.f - kcostheta)); + } + + /* + * not used, but here for reference: switch (phasefunc_type) { case MA_VOL_PH_MIEHAZY: - return scale * (0.5f + 4.5f * powf(0.5 * (1.f + costheta), 8.f)) / (4.f*M_PI); + return normalize * (0.5f + 4.5f * powf(0.5 * (1.f + costheta), 8.f)); case MA_VOL_PH_MIEMURKY: - return scale * (0.5f + 16.5f * powf(0.5 * (1.f + costheta), 32.f)) / (4.f*M_PI); + return normalize * (0.5f + 16.5f * powf(0.5 * (1.f + costheta), 32.f)); case MA_VOL_PH_RAYLEIGH: - return scale * 3.f/(16.f*M_PI) * (1 + costheta * costheta); + return normalize * 3.f/4.f * (1 + costheta * costheta); case MA_VOL_PH_HG: - return scale * (1.f / (4.f * M_PI) * (1.f - g*g) / powf(1.f + g*g - 2.f * g * costheta, 1.5f)); + 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 - .55f * g * g * g; const float kcostheta = k * costheta; - return scale * (1.f / (4.f * M_PI) * (1.f - k*k) / ((1.f - kcostheta) * (1.f - kcostheta))); + return normalize * (1.f - k*k) / ((1.f - kcostheta) * (1.f - kcostheta)); } case MA_VOL_PH_ISOTROPIC: default: - return scale * (1.f / (4.f * M_PI)); + return normalize * 1.f; } + */ } /* Compute transmittance = e^(-attenuation) */ @@ -361,15 +412,15 @@ void vol_get_transmittance_seg(ShadeInput *shi, float *tr, float stepsize, float { /* input density = density at co */ float tau[3] = {0.f, 0.f, 0.f}; - float absorb[3]; - const float scatter_dens = vol_get_scattering_fac(shi, co) * density * stepsize; - - vol_get_absorption(shi, absorb, co); + const float stepd = density * stepsize; + float sigma_t[3]; + + vol_get_sigma_t(shi, sigma_t, co); /* homogenous volume within the sampled distance */ - tau[0] += scatter_dens * absorb[0]; - tau[1] += scatter_dens * absorb[1]; - tau[2] += scatter_dens * absorb[2]; + tau[0] += stepd * sigma_t[0]; + tau[1] += stepd * sigma_t[1]; + tau[2] += stepd * sigma_t[2]; tr[0] *= exp(-tau[0]); tr[1] *= exp(-tau[1]); @@ -381,31 +432,29 @@ static void vol_get_transmittance(ShadeInput *shi, float *tr, float *co, float * { 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 ambtau = -logf(shi->mat->vol.depth_cutoff); // never zero float tau[3] = {0.f, 0.f, 0.f}; float t0 = 0.f; float t1 = Normalize(step_vec); float pt0 = t0; - t0 += shi->mat->vol.shade_stepsize * ((shi->mat->vol.stepsize_type == MA_VOL_STEP_CONSTANT) ? 0.5f : BLI_thread_frand(shi->thread)); + 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]; - VecMulf(step_vec, shi->mat->vol.shade_stepsize); + VecMulf(step_vec, shi->mat->vol.stepsize); - for (; t0 < t1; pt0 = t0, t0 += shi->mat->vol.shade_stepsize) { - float absorb[3]; + for (; t0 < t1; pt0 = t0, t0 += shi->mat->vol.stepsize) { const float d = vol_get_density(shi, p); const float stepd = (t0 - pt0) * d; - const float scatter_dens = vol_get_scattering_fac(shi, p) * stepd; - vol_get_absorption(shi, absorb, p); + float sigma_t[3]; - tau[0] += scatter_dens * absorb[0]; - tau[1] += scatter_dens * absorb[1]; - tau[2] += scatter_dens * absorb[2]; + vol_get_sigma_t(shi, sigma_t, co); + + tau[0] += stepd * sigma_t[0]; + tau[1] += stepd * sigma_t[1]; + tau[2] += stepd * sigma_t[2]; - //if (luminance(tau) >= ambtau) break; VecAddf(p, p, step_vec); } @@ -420,9 +469,7 @@ void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float * float visifac, lv[3], lampdist; float tr[3]={1.0,1.0,1.0}; float hitco[3], *atten_co; - float p; - float scatter_fac; - float shade_stepsize = vol_get_stepsize(shi, STEPSIZE_SHADE); + 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; @@ -443,13 +490,22 @@ void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float * VECCOPY(lv, lar->vec); VecMulf(lv, -1.0f); - if (shi->mat->vol.shade_type != MA_VOL_SHADE_NONE) { + if (shi->mat->vol.shade_type == MA_VOL_SHADE_SHADOWED) { + VecMulf(lacol, vol_get_shadow(shi, lar, co)); + } + else if (shi->mat->vol.shade_type == MA_VOL_SHADE_SHADED) + { Isect is; + if (shi->mat->vol.shadeflag & MA_VOL_RECV_EXT_SHADOW) { + VecMulf(lacol, vol_get_shadow(shi, lar, co)); + if (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 = VecLenf(co, hitco); - VlakRen *vlr = (VlakRen *)is.face; + VlakRen *vlr = (VlakRen *)is.hit.face; /* simple internal shadowing */ if (vlr->mat->material_type == MA_TYPE_SURFACE) { @@ -476,15 +532,20 @@ void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float * } } - p = vol_get_phasefunc(shi, shi->mat->vol.phasefunc_type, shi->mat->vol.phasefunc_g, shi->view, lv); - VecMulf(lacol, p); + if (luminance(lacol) < 0.001f) return; + + p = vol_get_phasefunc(shi, shi->mat->vol.asymmetry, shi->view, lv); - scatter_fac = vol_get_scattering_fac(shi, co); - VecMulf(lacol, scatter_fac); + /* 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, float *co, float stepsize, float density) +void vol_get_scattering(ShadeInput *shi, float *scatter_col, float *co) { ListBase *lights; GroupObject *go; @@ -516,71 +577,69 @@ outgoing radiance from behind surface * beam transmittance/attenuation --> 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 colouring the outscattered light results + * in the inverse colour being transmitted through the rest of the volume. + */ static void volumeintegrate(struct ShadeInput *shi, float *col, float *co, float *endco) { - float tr[3] = {1.0f, 1.0f, 1.0f}; - float radiance[3] = {0.f, 0.f, 0.f}, d_radiance[3] = {0.f, 0.f, 0.f}; - float stepsize = vol_get_stepsize(shi, STEPSIZE_VIEW); - int nsteps, s; - float emit_col[3], scatter_col[3] = {0.0, 0.0, 0.0}; - float stepvec[3], step_sta[3], step_end[3], step_mid[3]; - float density; - const float depth_cutoff = shi->mat->vol.depth_cutoff; - - /* ray marching */ - nsteps = (int)((VecLenf(co, endco) / stepsize) + 0.5); - - VecSubf(stepvec, endco, co); - VecMulf(stepvec, 1.0f / nsteps); - VecCopyf(step_sta, co); - VecAddf(step_end, step_sta, stepvec); - - /* get radiance from all points along the ray due to participating media */ - for (s = 0; s < nsteps; s++) { - - density = vol_get_density(shi, step_sta); + 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(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]; + VecMulf(step_vec, stepsize); + + for (; t0 < t1; pt0 = t0, t0 += stepsize) { + const float density = vol_get_density(shi, p); - /* there's only any use in shading here if there's actually some density to shade! */ if (density > 0.01f) { - + float scatter_col[3], emit_col[3]; + const float stepd = (t0 - pt0) * density; + /* transmittance component (alpha) */ vol_get_transmittance_seg(shi, tr, stepsize, co, density); - - step_mid[0] = step_sta[0] + (stepvec[0] * 0.5); - step_mid[1] = step_sta[1] + (stepvec[1] * 0.5); - step_mid[2] = step_sta[2] + (stepvec[2] * 0.5); - - /* incoming light via emission or scattering (additive) */ - vol_get_emission(shi, emit_col, step_mid, density); - if (shi->obi->volume_precache) - vol_get_precached_scattering(shi, scatter_col, step_mid); - else - vol_get_scattering(shi, scatter_col, step_mid, stepsize, density); + if (luminance(tr) < shi->mat->vol.depth_cutoff) break; - VecMulf(scatter_col, density); - VecAddf(d_radiance, emit_col, scatter_col); + vol_get_emission(shi, emit_col, p); - /* Lv += Tr * (Lve() + Ld) */ - VecMulVecf(d_radiance, tr, d_radiance); - VecMulf(d_radiance, stepsize); + if (shi->obi->volume_precache) { + float p2[3]; + + p2[0] = p[0] + (step_vec[0] * 0.5); + p2[1] = p[1] + (step_vec[1] * 0.5); + p2[2] = p[2] + (step_vec[2] * 0.5); + + vol_get_precached_scattering(shi, scatter_col, p2); + } else + vol_get_scattering(shi, scatter_col, p); - VecAddf(radiance, radiance, d_radiance); + 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]); } - - VecCopyf(step_sta, step_end); - VecAddf(step_end, step_end, stepvec); - - /* luminance rec. 709 */ - if ((0.2126*tr[0] + 0.7152*tr[1] + 0.0722*tr[2]) < depth_cutoff) break; + VecAddf(p, p, step_vec); } - /* multiply original color (behind volume) with beam transmittance over entire distance */ - VecMulVecf(col, tr, col); + /* multiply original color (from behind volume) with transmittance over entire distance */ + VecMulVecf(col, tr, col); VecAddf(col, col, radiance); /* alpha <-- transmission luminance */ - col[3] = 1.0f -(0.2126*tr[0] + 0.7152*tr[1] + 0.0722*tr[2]); + col[3] = 1.0f - luminance(tr); } /* the main entry point for volume shading */ @@ -607,7 +666,7 @@ static void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr, int in /* don't render the backfaces of ztransp volume materials. * volume shading renders the internal volume from between the - * near view intersection of the solid volume to the + * ' view intersection of the solid volume to the * intersection on the other side, as part of the shading of * the front face. @@ -646,7 +705,7 @@ static void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr, int in /* (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.face; + VlakRen *vlr = (VlakRen *)is.hit.face; startco = shi->co; endco = hitco; @@ -655,7 +714,7 @@ static void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr, int in /* 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.face, endco, col); + vol_trace_behind(shi, (VlakRen *)is.hit.face, endco, col); } else { shade_intersection(shi, col, &is); } @@ -683,7 +742,6 @@ void shade_volume_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct float hitco[3]; float tr[3] = {1.0,1.0,1.0}; Isect is; - float shade_stepsize = vol_get_stepsize(shi, STEPSIZE_SHADE); float *startco, *endco; float density=0.f; @@ -711,8 +769,7 @@ void shade_volume_shadow(struct ShadeInput *shi, struct ShadeResult *shr, struct vol_get_transmittance(shi, tr, startco, endco); VecCopyf(shr->combined, tr); - shr->combined[3] = 1.0f -(0.2126*tr[0] + 0.7152*tr[1] + 0.0722*tr[2]); - shr->alpha = shr->combined[3]; + shr->combined[3] = 1.0f - luminance(tr); } @@ -749,4 +806,4 @@ void shade_volume_inside(ShadeInput *shi, ShadeResult *shr) shi->mat = mat_backup; shi->obi = obi_backup; shi->obr = obi_backup->obr; -}
\ No newline at end of file +} diff --git a/source/blender/render/intern/source/voxeldata.c b/source/blender/render/intern/source/voxeldata.c index 9318d37620c..479f33c9ff2 100644 --- a/source/blender/render/intern/source/voxeldata.c +++ b/source/blender/render/intern/source/voxeldata.c @@ -226,9 +226,6 @@ void make_voxeldata(struct Render *re) { Tex *tex; - if(re->scene->r.scemode & R_PREVIEWBUTS) - return; - re->i.infostr= "Loading voxel datasets"; re->stats_draw(re->sdh, &re->i); @@ -259,9 +256,6 @@ void free_voxeldata(Render *re) { Tex *tex; - if(re->scene->r.scemode & R_PREVIEWBUTS) - return; - for (tex= G.main->tex.first; tex; tex= tex->id.next) { if(tex->id.us && tex->type==TEX_VOXELDATA) { free_voxeldata_one(re, tex); diff --git a/source/blender/render/intern/source/zbuf.c b/source/blender/render/intern/source/zbuf.c index 3b3a8568933..6e305a2b82b 100644 --- a/source/blender/render/intern/source/zbuf.c +++ b/source/blender/render/intern/source/zbuf.c @@ -271,7 +271,7 @@ static APixstr *addpsmainA(ListBase *lb) return psm->ps; } -static void freepsA(ListBase *lb) +void freepsA(ListBase *lb) { APixstrMain *psm, *psmnext; @@ -1760,12 +1760,12 @@ static int zbuf_shadow_project(ZbufProjectCache *cache, int index, float winmat[ } } -static void zbuffer_part_bounds(Render *re, RenderPart *pa, float *bounds) +static void zbuffer_part_bounds(int winx, int winy, RenderPart *pa, float *bounds) { - bounds[0]= (2*pa->disprect.xmin - re->winx-1)/(float)re->winx; - bounds[1]= (2*pa->disprect.xmax - re->winx+1)/(float)re->winx; - bounds[2]= (2*pa->disprect.ymin - re->winy-1)/(float)re->winy; - bounds[3]= (2*pa->disprect.ymax - re->winy+1)/(float)re->winy; + bounds[0]= (2*pa->disprect.xmin - winx-1)/(float)winx; + bounds[1]= (2*pa->disprect.xmax - winx+1)/(float)winx; + bounds[2]= (2*pa->disprect.ymin - winy-1)/(float)winy; + bounds[3]= (2*pa->disprect.ymax - winy+1)/(float)winy; } static int zbuf_part_project(ZbufProjectCache *cache, int index, float winmat[][4], float *bounds, float *co, float *ho) @@ -1803,7 +1803,7 @@ void zbuf_render_project(float winmat[][4], float *co, float *ho) projectvert(vec, winmat, ho); } -void zbuf_make_winmat(Render *re, float duplimat[][4], float winmat[][4]) +void zbuf_make_winmat(Render *re, float winmat[][4]) { float panomat[4][4]; @@ -1814,13 +1814,8 @@ void zbuf_make_winmat(Render *re, float duplimat[][4], float winmat[][4]) panomat[2][0]= -re->panosi; panomat[2][2]= re->panoco; - if(duplimat) - Mat4MulSerie(winmat, re->winmat, panomat, duplimat, 0, 0, 0, 0, 0); - else - Mat4MulMat4(winmat, panomat, re->winmat); + Mat4MulMat4(winmat, panomat, re->winmat); } - else if(duplimat) - Mat4MulMat4(winmat, duplimat, re->winmat); else Mat4CpyMat4(winmat, re->winmat); } @@ -2047,12 +2042,15 @@ void zbuffer_solid(RenderPart *pa, RenderLayer *rl, void(*fillfunc)(RenderPart*, Material *ma=0; ObjectInstanceRen *obi; ObjectRen *obr; - float winmat[4][4], bounds[4], ho1[4], ho2[4], ho3[4], ho4[4]={0}; + float obwinmat[4][4], winmat[4][4], bounds[4]; + float ho1[4], ho2[4], ho3[4], ho4[4]={0}; unsigned int lay= rl->lay, lay_zmask= rl->lay_zmask; int i, v, zvlnr, zsample, samples, c1, c2, c3, c4=0; short nofill=0, env=0, wire=0, zmaskpass=0; short all_z= (rl->layflag & SCE_LAY_ALL_Z) && !(rl->layflag & SCE_LAY_ZMASK); short neg_zmask= (rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK); + + zbuf_make_winmat(&R, winmat); samples= (R.osa? R.osa: 1); samples= MIN2(4, samples-pa->sample); @@ -2060,7 +2058,7 @@ void zbuffer_solid(RenderPart *pa, RenderLayer *rl, void(*fillfunc)(RenderPart*, for(zsample=0; zsample<samples; zsample++) { zspan= &zspans[zsample]; - zbuffer_part_bounds(&R, pa, bounds); + zbuffer_part_bounds(R.winx, R.winy, pa, bounds); zbuf_alloc_span(zspan, pa->rectx, pa->recty, R.clipcrop); /* needed for transform from hoco to zbuffer co */ @@ -2135,9 +2133,9 @@ void zbuffer_solid(RenderPart *pa, RenderLayer *rl, void(*fillfunc)(RenderPart*, continue; if(obi->flag & R_TRANSFORMED) - zbuf_make_winmat(&R, obi->mat, winmat); + Mat4MulMat4(obwinmat, obi->mat, winmat); else - zbuf_make_winmat(&R, NULL, winmat); + Mat4CpyMat4(obwinmat, winmat); if(clip_render_object(obi->obr->boundbox, bounds, winmat)) continue; @@ -2182,14 +2180,14 @@ void zbuffer_solid(RenderPart *pa, RenderLayer *rl, void(*fillfunc)(RenderPart*, v3= vlr->v3; v4= vlr->v4; - c1= zbuf_part_project(cache, v1->index, winmat, bounds, v1->co, ho1); - c2= zbuf_part_project(cache, v2->index, winmat, bounds, v2->co, ho2); - c3= zbuf_part_project(cache, v3->index, winmat, bounds, v3->co, ho3); + c1= zbuf_part_project(cache, v1->index, obwinmat, bounds, v1->co, ho1); + c2= zbuf_part_project(cache, v2->index, obwinmat, bounds, v2->co, ho2); + c3= zbuf_part_project(cache, v3->index, obwinmat, bounds, v3->co, ho3); /* partclipping doesn't need viewplane clipping */ partclip= c1 & c2 & c3; if(v4) { - c4= zbuf_part_project(cache, v4->index, winmat, bounds, v4->co, ho4); + c4= zbuf_part_project(cache, v4->index, obwinmat, bounds, v4->co, ho4); partclip &= c4; } @@ -2511,11 +2509,13 @@ void zbuffer_sss(RenderPart *pa, unsigned int lay, void *handle, void (*func)(vo VlakRen *vlr= NULL; VertRen *v1, *v2, *v3, *v4; Material *ma=0, *sss_ma= R.sss_mat; - float winmat[4][4], bounds[4], ho1[4], ho2[4], ho3[4], ho4[4]={0}; + float obwinmat[4][4], winmat[4][4], bounds[4]; + float ho1[4], ho2[4], ho3[4], ho4[4]={0}; int i, v, zvlnr, c1, c2, c3, c4=0; short nofill=0, env=0, wire=0; - zbuffer_part_bounds(&R, pa, bounds); + zbuf_make_winmat(&R, winmat); + zbuffer_part_bounds(R.winx, R.winy, pa, bounds); zbuf_alloc_span(&zspan, pa->rectx, pa->recty, R.clipcrop); zspan.sss_handle= handle; @@ -2551,9 +2551,9 @@ void zbuffer_sss(RenderPart *pa, unsigned int lay, void *handle, void (*func)(vo continue; if(obi->flag & R_TRANSFORMED) - zbuf_make_winmat(&R, obi->mat, winmat); + Mat4MulMat4(obwinmat, obi->mat, winmat); else - zbuf_make_winmat(&R, NULL, winmat); + Mat4CpyMat4(obwinmat, winmat); if(clip_render_object(obi->obr->boundbox, bounds, winmat)) continue; @@ -3182,9 +3182,18 @@ static void copyto_abufz(RenderPart *pa, int *arectz, int *rectmask, int sample) intptr_t *rd; if(R.osa==0) { - memcpy(arectz, pa->rectz, sizeof(int)*pa->rectx*pa->recty); + if(!pa->rectz) + fillrect(arectz, pa->rectx, pa->recty, 0x7FFFFFFE); + else + memcpy(arectz, pa->rectz, sizeof(int)*pa->rectx*pa->recty); + if(rectmask && pa->rectmask) memcpy(rectmask, pa->rectmask, sizeof(int)*pa->rectx*pa->recty); + + return; + } + else if(!pa->rectdaps) { + fillrect(arectz, pa->rectx, pa->recty, 0x7FFFFFFE); return; } @@ -3222,7 +3231,7 @@ static void copyto_abufz(RenderPart *pa, int *arectz, int *rectmask, int sample) * Do accumulation z buffering. */ -static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, RenderLayer *rl, unsigned int lay) +static int zbuffer_abuf(Render *re, RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, unsigned int lay, int negzmask, float winmat[][4], int winx, int winy, int samples, float (*jit)[2], float clipcrop, int shadow) { ZbufProjectCache cache[ZBUF_PROJECT_CACHE_SIZE]; ZSpan zspans[16], *zspan; /* MAX_OSA */ @@ -3232,28 +3241,27 @@ static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, Re VlakRen *vlr=NULL; VertRen *v1, *v2, *v3, *v4; float vec[3], hoco[4], mul, zval, fval; - float winmat[4][4], bounds[4], ho1[4], ho2[4], ho3[4], ho4[4]={0}; + float obwinmat[4][4], bounds[4], ho1[4], ho2[4], ho3[4], ho4[4]={0}; int i, v, zvlnr, c1, c2, c3, c4=0, dofill= 0; - int zsample, samples, polygon_offset; + int zsample, polygon_offset; - zbuffer_part_bounds(&R, pa, bounds); - samples= (R.osa? R.osa: 1); + zbuffer_part_bounds(winx, winy, pa, bounds); for(zsample=0; zsample<samples; zsample++) { zspan= &zspans[zsample]; - zbuf_alloc_span(zspan, pa->rectx, pa->recty, R.clipcrop); + zbuf_alloc_span(zspan, pa->rectx, pa->recty, re->clipcrop); /* needed for transform from hoco to zbuffer co */ - zspan->zmulx= ((float)R.winx)/2.0; - zspan->zmuly= ((float)R.winy)/2.0; + zspan->zmulx= ((float)winx)/2.0; + zspan->zmuly= ((float)winy)/2.0; /* the buffers */ zspan->arectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "Arectz"); zspan->apixbuf= APixbuf; zspan->apsmbase= apsmbase; - if((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)) + if(negzmask) zspan->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "Arectmask"); /* filling methods */ @@ -3263,36 +3271,35 @@ static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, Re copyto_abufz(pa, zspan->arectz, zspan->rectmask, zsample); /* init zbuffer */ zspan->mask= 1<<zsample; - if(R.osa) { - zspan->zofsx= -pa->disprect.xmin - R.jit[zsample][0]; - zspan->zofsy= -pa->disprect.ymin - R.jit[zsample][1]; - } - else if(R.i.curblur) { - zspan->zofsx= -pa->disprect.xmin - R.jit[R.i.curblur-1][0]; - zspan->zofsy= -pa->disprect.ymin - R.jit[R.i.curblur-1][1]; + if(jit) { + zspan->zofsx= -pa->disprect.xmin + jit[zsample][0]; + zspan->zofsy= -pa->disprect.ymin + jit[zsample][1]; } else { zspan->zofsx= -pa->disprect.xmin; zspan->zofsy= -pa->disprect.ymin; } - /* to center the sample position */ - zspan->zofsx -= 0.5f; - zspan->zofsy -= 0.5f; + + if(!shadow) { + /* to center the sample position */ + zspan->zofsx -= 0.5f; + zspan->zofsy -= 0.5f; + } } /* we use this to test if nothing was filled in */ zvlnr= 0; - for(i=0, obi=R.instancetable.first; obi; i++, obi=obi->next) { + for(i=0, obi=re->instancetable.first; obi; i++, obi=obi->next) { obr= obi->obr; if(!(obi->lay & lay)) continue; if(obi->flag & R_TRANSFORMED) - zbuf_make_winmat(&R, obi->mat, winmat); + Mat4MulMat4(obwinmat, obi->mat, winmat); else - zbuf_make_winmat(&R, NULL, winmat); + Mat4CpyMat4(obwinmat, winmat); if(clip_render_object(obi->obr->boundbox, bounds, winmat)) continue; @@ -3306,7 +3313,7 @@ static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, Re if(vlr->mat!=ma) { ma= vlr->mat; - dofill= ((ma->mode & MA_TRANSP) && (ma->mode & MA_ZTRANSP)) && !(ma->mode & MA_ONLYCAST); + dofill= shadow || (((ma->mode & MA_TRANSP) && (ma->mode & MA_ZTRANSP)) && !(ma->mode & MA_ONLYCAST)); } if(dofill) { @@ -3318,27 +3325,27 @@ static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, Re v3= vlr->v3; v4= vlr->v4; - c1= zbuf_part_project(cache, v1->index, winmat, bounds, v1->co, ho1); - c2= zbuf_part_project(cache, v2->index, winmat, bounds, v2->co, ho2); - c3= zbuf_part_project(cache, v3->index, winmat, bounds, v3->co, ho3); + c1= zbuf_part_project(cache, v1->index, obwinmat, bounds, v1->co, ho1); + c2= zbuf_part_project(cache, v2->index, obwinmat, bounds, v2->co, ho2); + c3= zbuf_part_project(cache, v3->index, obwinmat, bounds, v3->co, ho3); /* partclipping doesn't need viewplane clipping */ partclip= c1 & c2 & c3; if(v4) { - c4= zbuf_part_project(cache, v4->index, winmat, bounds, v4->co, ho4); + c4= zbuf_part_project(cache, v4->index, obwinmat, bounds, v4->co, ho4); partclip &= c4; } if(partclip==0) { /* a little advantage for transp rendering (a z offset) */ - if( ma->zoffs != 0.0) { + if(!shadow && ma->zoffs != 0.0) { mul= 0x7FFFFFFF; zval= mul*(1.0+ho1[2]/ho1[3]); VECCOPY(vec, v1->co); /* z is negative, otherwise its being clipped */ vec[2]-= ma->zoffs; - projectverto(vec, R.winmat, hoco); + projectverto(vec, obwinmat, hoco); fval= mul*(1.0+hoco[2]/hoco[3]); polygon_offset= (int) fabs(zval - fval ); @@ -3376,13 +3383,13 @@ static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, Re } } if((v & 255)==255) - if(R.test_break(R.tbh)) + if(re->test_break(re->tbh)) break; } } } - if(R.test_break(R.tbh)) break; + if(re->test_break(re->tbh)) break; } for(zsample=0; zsample<samples; zsample++) { @@ -3396,6 +3403,51 @@ static int zbuffer_abuf(RenderPart *pa, APixstr *APixbuf, ListBase *apsmbase, Re return zvlnr; } +static int zbuffer_abuf_render(RenderPart *pa, APixstr *APixbuf, APixstrand *APixbufstrand, ListBase *apsmbase, RenderLayer *rl, StrandShadeCache *sscache) +{ + float winmat[4][4], (*jit)[2]; + int samples, negzmask, doztra= 0; + + samples= (R.osa)? R.osa: 1; + negzmask= ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)); + + if(R.osa) + jit= R.jit; + else if(R.i.curblur) + jit= &R.jit[R.i.curblur-1]; + else + jit= NULL; + + zbuf_make_winmat(&R, winmat); + + if(rl->layflag & SCE_LAY_ZTRA) + doztra+= zbuffer_abuf(&R, pa, APixbuf, apsmbase, rl->lay, negzmask, winmat, R.winx, R.winy, samples, jit, R.clipcrop, 0); + if((rl->layflag & SCE_LAY_STRAND) && APixbufstrand) + doztra+= zbuffer_strands_abuf(&R, pa, APixbufstrand, apsmbase, rl->lay, negzmask, winmat, R.winx, R.winy, samples, jit, R.clipcrop, 0, sscache); + + return doztra; +} + +void zbuffer_abuf_shadow(Render *re, LampRen *lar, float winmat[][4], APixstr *APixbuf, APixstrand *APixbufstrand, ListBase *apsmbase, int size, int samples, float (*jit)[2]) +{ + RenderPart pa; + int lay= -1; + + if(lar->mode & LA_LAYER) lay= lar->lay; + + memset(&pa, 0, sizeof(RenderPart)); + pa.rectx= size; + pa.recty= size; + pa.disprect.xmin= 0; + pa.disprect.ymin= 0; + pa.disprect.xmax= size; + pa.disprect.ymax= size; + + zbuffer_abuf(re, &pa, APixbuf, apsmbase, lay, 0, winmat, size, size, samples, jit, 1.0f, 1); + if(APixbufstrand) + zbuffer_strands_abuf(re, &pa, APixbufstrand, apsmbase, lay, 0, winmat, size, size, samples, jit, 1.0f, 1, NULL); +} + /* different rules for speed in transparent pass... */ /* speed pointer NULL = sky, we clear */ /* else if either alpha is full or no solid was filled in: copy speed */ @@ -3902,11 +3954,7 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas sampalpha= 1.0f; /* fill the Apixbuf */ - doztra= 0; - if(rl->layflag & SCE_LAY_ZTRA) - doztra+= zbuffer_abuf(pa, APixbuf, &apsmbase, rl, rl->lay); - if((rl->layflag & SCE_LAY_STRAND) && APixbufstrand) - doztra+= zbuffer_strands_abuf(&R, pa, rl, APixbufstrand, &apsmbase, sscache); + doztra= zbuffer_abuf_render(pa, APixbuf, APixbufstrand, &apsmbase, rl, sscache); if(doztra == 0) { /* nothing filled in */ |