diff -r -u -N -x linux2-config.py -x CVS blender-cvs/intern/guardedalloc/intern/mallocn.c blender/intern/guardedalloc/intern/mallocn.c --- blender-cvs/intern/guardedalloc/intern/mallocn.c 2006-06-16 15:27:03.000000000 -0400 +++ blender/intern/guardedalloc/intern/mallocn.c 2006-06-16 13:34:32.000000000 -0400 @@ -39,6 +39,7 @@ #include #include /* memcpy */ #include +#include /* mmap exception */ #if defined(AMIGA) || defined(__BeOS) || defined(WIN32) @@ -234,6 +235,69 @@ return NULL; } +void *MEM_reallocN(void *ptr, unsigned int len) +{ + MemHead *memh; + MemTail *memt; + MemTail memt_tmp; + static const char *name; + + /* emulate real realloc */ + if (ptr == NULL) + return (void*) MEM_mallocN(len, "realloc NULL ptr"); + + /* Recover structure pointers */ + memh = ((MemHead*) ptr) - 1; + memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + memh->len); + + /* Sanity checks */ + if(sizeof(long)==8) { + if (((long) memh) & 0x7) { + MemorY_ErroR("realloc","attempt to realloc illegal pointer"); + return NULL; + } + } else { + if (((long) memh) & 0x3) { + MemorY_ErroR("realloc","attempt to realloc illegal pointer"); + return NULL; + } + } + + if ((memh->tag1 == MEMTAG1) && (memh->tag2 == MEMTAG2) && ((memh->len & 0x3) == 0)) { + if (memt->tag3 != MEMTAG3){ + MemorY_ErroR(memh->name, "end corrupt"); + name = check_memlist(memh); + if (name != 0 && name != memh->name) + MemorY_ErroR(name,"is also corrupt"); + } + } else { + name = check_memlist(memh); + if (name == 0) MemorY_ErroR("realloc","pointer not in memlist"); + else MemorY_ErroR(name,"realloc error in header"); + } + + /* Save old memt because it could get dropped in the realloc */ + memt_tmp.tag3 = memt->tag3; + memt_tmp.pad = memt->pad; + + len = (len + 3 ) & ~3; /* allocate in units of 4 */ + + memh = (MemHead *)realloc(memh, len+sizeof(MemHead)+sizeof(MemTail)); + memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len); + + /* Perform surgery on existing memt / memh */ + if(memh) { + memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len); + memt->tag3 = memt_tmp.tag3; + memt->pad = memt_tmp.pad; + mem_in_use += len - memh->len; + memh->len = len; + return ptr; + } + print_error("Realloc returns nill: len=%d in %s, total %u\n",len, memh->name, mem_in_use); + return NULL; +} + void *MEM_callocN(unsigned int len, const char *str) { MemHead *memh; diff -r -u -N -x linux2-config.py -x CVS blender-cvs/intern/guardedalloc/MEM_guardedalloc.h blender/intern/guardedalloc/MEM_guardedalloc.h --- blender-cvs/intern/guardedalloc/MEM_guardedalloc.h 2006-06-16 15:27:03.000000000 -0400 +++ blender/intern/guardedalloc/MEM_guardedalloc.h 2006-06-16 02:48:24.000000000 -0400 @@ -99,6 +99,11 @@ * */ void *MEM_mapallocN(unsigned int len, const char * str); + /** Resize previously allocated memory at address ptr to size len. + * Can be free'd with MEM_freeN as usual. + * */ + void *MEM_reallocN(void *ptr, unsigned int len); + /** Print a list of the names and sizes of all allocated memory * blocks. */ void MEM_printmemlist(void); diff -r -u -N -x linux2-config.py -x CVS blender-cvs/source/blender/render/intern/include/define.h blender/source/blender/render/intern/include/define.h --- blender-cvs/source/blender/render/intern/include/define.h 1969-12-31 19:00:00.000000000 -0500 +++ blender/source/blender/render/intern/include/define.h 2006-06-14 03:39:06.000000000 -0400 @@ -0,0 +1,247 @@ +/* D E F I N E . H + * + * @file define.h + * + * BRL-CAD + * + * Copyright (c) 2002-2006 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + * + * D E F I N E. H + * + * Comments - + * Triangle Intersection Engine Defines + * + * Author - + * Justin L. Shumaker + * + * Source - + * The U. S. Army Research Laboratory + * Aberdeen Proving Ground, Maryland 21005-5068 USA + * + * $Id: define.h,v 1.23 2006/01/18 06:46:11 brlcad Exp $ + */ +/** @addtogroup libtie */ /** @{ */ + +#ifndef _TIE_DEFINE_H +#define _TIE_DEFINE_H + +#include + +#define TIE_SINGLE_PREC 1 /* Use Single Precision Math */ +#define TIE_TAB1 "\1\0\0\2\2\1" /* Triangle Index Table */ +#define TIE_KDTREE_NODE_MAX 4 /* Maximum number of triangles that can reside in a given node until it should be split */ +#define TIE_KDTREE_DEPTH_K1 1.6 /* K1 Depth Constant Coefficient */ +#define TIE_KDTREE_DEPTH_K2 1 /* K2 Contant */ + +#define MAX_SLICES 100 +#define MIN_SLICES 35 +#define MIN_DENSITY 0.01 +#define MIN_SPAN 0.15 +#define SCALE_COEF 1.80 + + +/* Type to use for floating precision */ +#if TIE_SINGLE_PREC +#define tfloat float +#else +#define tfloat double +#endif + + +/** + * + */ +#define math_min3(_a, _b, _c, _d) { \ + _a = _b < _c ? _b < _d ? _b : _d : _c < _d ? _c : _d; } + +/** + * + */ +#define math_max3(_a, _b, _c, _d) { \ + _a = _b > _c ? _b > _d ? _b : _d : _c > _d ? _c : _d; } + +/** + * + */ +#define math_vec_set(_a, _b, _c, _d) { \ + _a.v[0] = _b; \ + _a.v[1] = _c; \ + _a.v[2] = _d; } + +/** + * + */ +#define math_vec_min(_a, _b) { \ + _a.v[0] = _a.v[0] < _b.v[0] ? _a.v[0] : _b.v[0]; \ + _a.v[1] = _a.v[1] < _b.v[1] ? _a.v[1] : _b.v[1]; \ + _a.v[2] = _a.v[2] < _b.v[2] ? _a.v[2] : _b.v[2]; } + +/** + * + */ +#define math_vec_max(_a, _b) { \ + _a.v[0] = _a.v[0] > _b.v[0] ? _a.v[0] : _b.v[0]; \ + _a.v[1] = _a.v[1] > _b.v[1] ? _a.v[1] : _b.v[1]; \ + _a.v[2] = _a.v[2] > _b.v[2] ? _a.v[2] : _b.v[2]; } + +/** + * + */ +#define math_vec_cross(_a, _b, _c) {\ + _a.v[0] = _b.v[1]*_c.v[2] - _b.v[2]*_c.v[1]; \ + _a.v[1] = _b.v[2]*_c.v[0] - _b.v[0]*_c.v[2]; \ + _a.v[2] = _b.v[0]*_c.v[1] - _b.v[1]*_c.v[0]; } + +/** + * + */ +#define math_vec_unitize(_a) { \ + tfloat _b = 1/sqrt(_a.v[0]*_a.v[0] + _a.v[1]*_a.v[1] + _a.v[2]*_a.v[2]); \ + _a.v[0] *= _b; _a.v[1] *= _b; _a.v[2] *= _b; } + +/** + * + */ +#define math_vec_sq(_a, _b) { \ + _a.v[0] = _b.v[0] * _b.v[0]; \ + _a.v[1] = _b.v[1] * _b.v[1]; \ + _a.v[2] = _b.v[2] * _b.v[2]; } + +/** + * + */ +#define math_vec_dot(_a, _b, _c) { \ + _a = _b.v[0]*_c.v[0] + _b.v[1]*_c.v[1] + _b.v[2]*_c.v[2]; } + +/** + * + */ +#define math_vec_add(_a, _b, _c) { \ + _a.v[0] = _b.v[0] + _c.v[0]; \ + _a.v[1] = _b.v[1] + _c.v[1]; \ + _a.v[2] = _b.v[2] + _c.v[2]; } + +/** + * + */ +#define math_vec_sub(_a, _b, _c) { \ + _a.v[0] = _b.v[0] - _c.v[0]; \ + _a.v[1] = _b.v[1] - _c.v[1]; \ + _a.v[2] = _b.v[2] - _c.v[2]; } + +/** + * + */ +#define math_vec_mul(_a, _b, _c) { \ + _a.v[0] = _b.v[0] * _c.v[0]; \ + _a.v[1] = _b.v[1] * _c.v[1]; \ + _a.v[2] = _b.v[2] * _c.v[2]; } + +/** + * + */ +#define math_vec_mul_scalar(_a, _b, _c) { \ + _a.v[0] = _b.v[0] * _c; \ + _a.v[1] = _b.v[1] * _c; \ + _a.v[2] = _b.v[2] * _c; } + +/** + * + */ +#define math_vec_div(_a, _b, _c) { \ + _a.v[0] = _b.v[0] / _c.v[0]; \ + _a.v[1] = _b.v[1] / _c.v[1]; \ + _a.v[2] = _b.v[2] / _c.v[2]; } + +/** + * + */ +#define math_bbox(_a, _b, _c, _d, _e) { \ + math_min3(_a.v[0], _c.v[0], _d.v[0], _e.v[0]); \ + math_min3(_a.v[1], _c.v[1], _d.v[1], _e.v[1]); \ + math_min3(_a.v[2], _c.v[2], _d.v[2], _e.v[2]); \ + math_max3(_b.v[0], _c.v[0], _d.v[0], _e.v[0]); \ + math_max3(_b.v[1], _c.v[1], _d.v[1], _e.v[1]); \ + math_max3(_b.v[2], _c.v[2], _d.v[2], _e.v[2]); } + +/* ======================== X-tests ======================== */ +/** + * + */ +#define AXISTEST_X01(a, b, fa, fb) \ + p.v[0] = a*v0.v[1] - b*v0.v[2]; \ + p.v[2] = a*v2.v[1] - b*v2.v[2]; \ + if (p.v[0] < p.v[2]) { min = p.v[0]; max = p.v[2]; } else { min = p.v[2]; max = p.v[0]; } \ + rad = fa * half_size -> v[1] + fb * half_size -> v[2]; \ + if (min > rad || max < -rad) return 0; \ + +/** + * + */ +#define AXISTEST_X2(a, b, fa, fb) \ + p.v[0] = a*v0.v[1] - b*v0.v[2]; \ + p.v[1] = a*v1.v[1] - b*v1.v[2]; \ + if (p.v[0] < p.v[1]) { min = p.v[0]; max = p.v[1]; } else { min = p.v[1]; max = p.v[0]; } \ + rad = fa * half_size -> v[1] + fb * half_size -> v[2]; \ + if (min > rad || max < -rad) return 0; + +/* ======================== Y-tests ======================== */ +/** + * + */ +#define AXISTEST_Y02(a, b, fa, fb) \ + p.v[0] = -a*v0.v[0] + b*v0.v[2]; \ + p.v[2] = -a*v2.v[0] + b*v2.v[2]; \ + if (p.v[0] < p.v[2]) { min = p.v[0]; max = p.v[2]; } else { min = p.v[2]; max = p.v[0]; } \ + rad = fa * half_size -> v[0] + fb * half_size -> v[2]; \ + if (min > rad || max < -rad) return 0; + +/** + * + */ +#define AXISTEST_Y1(a, b, fa, fb) \ + p.v[0] = -a*v0.v[0] + b*v0.v[2]; \ + p.v[1] = -a*v1.v[0] + b*v1.v[2]; \ + if (p.v[0] < p.v[1]) { min = p.v[0]; max = p.v[1]; } else { min = p.v[1]; max = p.v[0]; } \ + rad = fa * half_size -> v[0] + fb * half_size -> v[2]; \ + if (min > rad || max < -rad) return 0; + +/* ======================== Z-tests ======================== */ +/** + * + */ +#define AXISTEST_Z12(a, b, fa, fb) \ + p.v[1] = a*v1.v[0] - b*v1.v[1]; \ + p.v[2] = a*v2.v[0] - b*v2.v[1]; \ + if (p.v[2] < p.v[1]) { min = p.v[2]; max = p.v[1]; } else { min = p.v[1]; max = p.v[2]; } \ + rad = fa * half_size -> v[0] + fb * half_size -> v[1]; \ + if (min > rad || max < -rad) return 0; + +/** + * + */ +#define AXISTEST_Z0(a, b, fa, fb) \ + p.v[0] = a*v0.v[0] - b*v0.v[1]; \ + p.v[1] = a*v1.v[0] - b*v1.v[1]; \ + if (p.v[0] < p.v[1]) { min = p.v[0]; max = p.v[1]; } else { min = p.v[1]; max = p.v[0]; } \ + rad = fa * half_size -> v[0] + fb * half_size -> v[1]; \ + if (min > rad || max < -rad) return 0; + +#endif + +/** @} */ diff -r -u -N -x linux2-config.py -x CVS blender-cvs/source/blender/render/intern/include/kdtree.h blender/source/blender/render/intern/include/kdtree.h --- blender-cvs/source/blender/render/intern/include/kdtree.h 1969-12-31 19:00:00.000000000 -0500 +++ blender/source/blender/render/intern/include/kdtree.h 2006-06-14 03:35:34.000000000 -0400 @@ -0,0 +1,62 @@ +/* T I E . H + * + * @file kdtree.h + * + * BRL-CAD + * + * Copyright (c) 2002-2006 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + * + * Comments - + * KD-Tree Routines + * + * Author - + * Justin L. Shumaker + * + * Source - + * The U. S. Army Research Laboratory + * Aberdeen Proving Ground, Maryland 21005-5068 USA + * + * $Id: kdtree.h,v 1.3 2006/01/18 06:46:11 brlcad Exp $ + */ +/** addtogroup libtie */ /** @{ */ + +#ifndef _KDTREE_H +#define _KDTREE_H + +#include "define.h" +#include "struct.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +void tie_kdtree_free(tie_t *tie); +void tie_kdtree_cache_free(tie_t *tie, void **cache); +void tie_kdtree_cache_load(tie_t *tie, void *cache); +void tie_kdtree_prep(tie_t *tie); +extern tfloat TIE_PREC; + + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ diff -r -u -N -x linux2-config.py -x CVS blender-cvs/source/blender/render/intern/include/render_types.h blender/source/blender/render/intern/include/render_types.h --- blender-cvs/source/blender/render/intern/include/render_types.h 2006-06-18 13:14:47.000000000 -0400 +++ blender/source/blender/render/intern/include/render_types.h 2006-06-18 13:14:28.000000000 -0400 @@ -40,6 +40,8 @@ #include "RE_pipeline.h" #include "RE_shader_ext.h" /* TexResult, ShadeResult, ShadeInput */ +#include "tie.h" /* triangle intersection engine */ + struct Object; struct MemArena; struct VertTableNode; @@ -144,6 +146,7 @@ /* octree tables and variables for raytrace */ Octree oc; + tie_t tie; /* use this instead of R.r.cfra */ float cfra; @@ -246,7 +249,7 @@ typedef struct VlakRen { - struct VertRen *v1, *v2, *v3, *v4; + struct VertRen *v1, *v2, *v3, *v4; /* 1 2 3 first tri, 1 3 4 second */ unsigned int lay; float n[3]; struct Material *mat; diff -r -u -N -x linux2-config.py -x CVS blender-cvs/source/blender/render/intern/include/.#render_types.h.1.13 blender/source/blender/render/intern/include/.#render_types.h.1.13 --- blender-cvs/source/blender/render/intern/include/.#render_types.h.1.13 1969-12-31 19:00:00.000000000 -0500 +++ blender/source/blender/render/intern/include/.#render_types.h.1.13 2006-06-14 13:01:47.000000000 -0400 @@ -0,0 +1,374 @@ +/** + * $Id: render_types.h,v 1.13 2006/06/10 16:30:44 ton Exp $ + * + * ***** 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. + * + * Contributor(s): (c) 2006 Blender Foundation, full refactor + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef RENDER_TYPES_H +#define RENDER_TYPES_H + +/* ------------------------------------------------------------------------- */ +/* exposed internal in render module only! */ +/* ------------------------------------------------------------------------- */ + +#include "DNA_scene_types.h" +#include "DNA_world_types.h" +#include "DNA_object_types.h" +#include "DNA_vec_types.h" + +#include "RE_pipeline.h" +#include "RE_shader_ext.h" /* TexResult, ShadeResult, ShadeInput */ + +#include "tie.h" /* triangle intersection engine */ + +struct Object; +struct MemArena; +struct VertTableNode; +struct Octree; +struct GHash; + +#define TABLEINITSIZE 1024 +#define LAMPINITSIZE 256 + +typedef struct SampleTables +{ + float centLut[16]; + float *fmask1[9], *fmask2[9]; + char cmask[256], *centmask; + +} SampleTables; + +/* this is handed over to threaded hiding/passes/shading engine */ +typedef struct RenderPart +{ + struct RenderPart *next, *prev; + + /* result of part rendering */ + RenderResult *result; + + unsigned int *rectp; /* polygon index table */ + int *rectz; /* zbuffer */ + long *rectdaps; /* delta acum buffer for pixel structs */ + + rcti disprect; /* part coordinates within total picture */ + int rectx, recty; /* the size */ + short crop, ready; /* crop is amount of pixels we crop, for filter */ + short sample, nr; /* sample can be used by zbuffers, nr is partnr */ + short thread; /* thread id */ + +} RenderPart; + +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; +} Octree; + +/* controls state of render, everything that's read-only during render stage */ +struct Render +{ + struct Render *next, *prev; + char name[RE_MAXNAME]; + + /* state settings */ + short flag, osa, ok, do_gamma; + + /* result of rendering */ + RenderResult *result; + /* if render with single-layer option, other rendered layers are stored here */ + RenderResult *pushedresult; + + /* window size, display rect, viewplane */ + int winx, winy; + rcti disprect; /* part within winx winy */ + rctf viewplane; /* mapped on winx winy */ + float viewdx, viewdy; /* size of 1 pixel */ + + /* final picture width and height (within disprect) */ + int rectx, recty; + + /* real maximum amount of xparts/yparts after correction for minimum */ + int xparts, yparts; + /* real maximum size of parts after correction for minimum + partx*xparts can be larger than rectx, in that case last part is smaller */ + int partx, party; + + /* values for viewing */ + float lens, ycor, viewfac; + float bluroffsx, bluroffsy; + float panophi, panosi, panoco, panodxp, panodxv; + + /* Matrices */ + float grvec[3]; /* for world */ + float imat[3][3]; /* copy of viewinv */ + float viewmat[4][4], viewinv[4][4]; + float winmat[4][4]; + + /* clippping */ + float clipsta; + float clipend; + + /* samples */ + SampleTables *samples; + float jit[32][2]; + + /* scene, and its full copy of renderdata and world */ + Scene *scene; + RenderData r; + World wrld; + + ListBase parts; + + /* octree tables and variables for raytrace */ + Octree oc; + tie_t tie; + + /* use this instead of R.r.cfra */ + float cfra; + + /* render database */ + int totvlak, totvert, tothalo, totlamp; + ListBase lights; + + int vertnodeslen; + struct VertTableNode *vertnodes; + int blohalen; + struct HaloRen **bloha; + int blovllen; + struct VlakRen **blovl; + ListBase objecttable; + + struct Image *backbuf; + + struct GHash *orco_hash; + + /* arena for allocating data for use during render, for + * example dynamic TFaces to go in the VlakRen structure. + */ + struct MemArena *memArena; + + /* callbacks */ + void (*display_init)(RenderResult *rr); + void (*display_clear)(RenderResult *rr); + void (*display_draw)(RenderResult *rr, volatile rcti *rect); + + void (*stats_draw)(RenderStats *ri); + void (*timecursor)(int i); + + int (*test_break)(void); + int (*test_return)(void); + void (*error)(char *str); + + RenderStats i; +}; + +/* ------------------------------------------------------------------------- */ + +typedef struct ShadSampleBuf { + struct ShadSampleBuf *next, *prev; + long *zbuf; + char *cbuf; +} ShadSampleBuf; + +typedef struct ShadBuf { + short samp, shadhalostep, totbuf; + float persmat[4][4]; + float viewmat[4][4]; + float winmat[4][4]; + float *jit, *weight; + float d, clipend, pixsize, soft; + int co[3]; + int size, bias; + ListBase buffers; +} ShadBuf; + +/* ------------------------------------------------------------------------- */ +/* lookup of objects in database */ +typedef struct ObjectRen { + struct ObjectRen *next, *prev; + struct Object *ob, *par; + int index, startvert, endvert, startface, endface; + float *vectors; +} ObjectRen; + +/* ------------------------------------------------------------------------- */ + +typedef struct VertRen +{ + float co[3]; + float n[3]; + float ho[4]; + float *orco; + short clip; + unsigned short flag; /* in use for clipping zbuffer parts, temp setting stuff in convertblender.c */ + float accum; /* accum for radio weighting, and for strand texco static particles */ + int index; /* index allows extending vertren with any property */ +} VertRen; + +/* ------------------------------------------------------------------------- */ + +struct halosort { + struct HaloRen *har; + int z; +}; + +/* ------------------------------------------------------------------------- */ +struct Material; +struct TFace; + +typedef struct RadFace { + float unshot[3], totrad[3]; + float norm[3], cent[3], area; + int flag; +} RadFace; + +typedef struct VlakRen +{ + struct VertRen *v1, *v2, *v3, *v4; /* 1 2 3 first tri, 1 3 4 second */ + unsigned int lay; + float n[3]; + struct Material *mat; + struct TFace *tface; + unsigned int *vcol; + char snproj, puno; + char flag, ec; + RadFace *radface; + Object *ob; +} VlakRen; + +typedef struct HaloRen +{ + short miny, maxy; + float alfa, xs, ys, rad, radsq, sin, cos, co[3], no[3]; + float hard, b, g, r; + int zs, zd; + int zBufDist; /* depth in the z-buffer coordinate system */ + char starpoints, type, add, tex; + char linec, ringc, seed; + short flarec; /* used to be a char. why ?*/ + float hasize; + int pixels; + unsigned int lay; + struct Material *mat; +} HaloRen; + +struct LampRen; +struct MTex; + +/** + * For each lamp in a scene, a LampRen is created. It determines the + * properties of a lightsource. + */ +typedef struct LampRen +{ + float xs, ys, dist; + float co[3]; + short type, mode; + float r, g, b, k; + float energy, haint; + int lay; + float spotsi,spotbl; + float vec[3]; + float xsp, ysp, distkw, inpr; + float halokw, halo; + float ld1,ld2; + + /* copied from Lamp, to decouple more rendering stuff */ + /** Size of the shadowbuffer */ + short bufsize; + /** Number of samples for the shadows */ + short samp; + /** Softness factor for shadow */ + float soft; + /** amount of subsample buffers */ + short buffers, filtertype; + /** shadow plus halo: detail level */ + short shadhalostep; + /** Near clip of the lamp */ + float clipsta; + /** Far clip of the lamp */ + float clipend; + /** A small depth offset to prevent self-shadowing. */ + float bias; + + short ray_samp, ray_sampy, ray_sampz, ray_samp_type, area_shape, ray_totsamp; + short xold1, yold1, xold2, yold2; /* last jitter table for area lights */ + float area_size, area_sizey, area_sizez; + + struct ShadBuf *shb; + float *jitter; + + float imat[3][3]; + float spottexfac; + float sh_invcampos[3], sh_zfac; /* sh_= spothalo */ + + float mat[3][3]; /* 3x3 part from lampmat x viewmat */ + float area[8][3], areasize; + + /* yafray: photonlight params */ + int YF_numphotons, YF_numsearch; + short YF_phdepth, YF_useqmc, YF_bufsize; + float YF_causticblur, YF_ltradius; + float YF_glowint, YF_glowofs; + short YF_glowtype; + + /* ray optim */ + VlakRen *vlr_last; + + struct MTex *mtex[MAX_MTEX]; +} LampRen; + +/* **************** defines ********************* */ + +/* R.r.mode flag is same as for renderdata */ + +/* R.flag */ +#define R_ZTRA 1 +#define R_HALO 2 +#define R_SEC_FIELD 4 +#define R_LAMPHALO 8 + +/* vlakren->flag (vlak = face in dutch) char!!! */ +#define R_SMOOTH 1 +#define R_VISIBLE 2 +/* strand flag, means special handling */ +#define R_STRAND 4 +#define R_NOPUNOFLIP 8 +#define R_FULL_OSA 16 +#define R_FACE_SPLIT 32 +/* Tells render to divide face other way. */ +#define R_DIVIDE_24 64 +/* vertex normals are tangent or view-corrected vector, for hair strands */ +#define R_TANGENT 128 + + + + + +#endif /* RENDER_TYPES_H */ + diff -r -u -N -x linux2-config.py -x CVS blender-cvs/source/blender/render/intern/include/struct.h blender/source/blender/render/intern/include/struct.h --- blender-cvs/source/blender/render/intern/include/struct.h 1969-12-31 19:00:00.000000000 -0500 +++ blender/source/blender/render/intern/include/struct.h 2006-06-14 03:35:34.000000000 -0400 @@ -0,0 +1,136 @@ +/* S T R U C T. H + * BRL-CAD + * + * Copyright (c) 2002-2006 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + */ +/** @file struct.h + * S T R U C T . H + * + * Triangle Intersection Engine Structures + * + * Author - + * Justin L. Shumaker + * + * Source - + * The U. S. Army Research Laboratory + * Aberdeen Proving Ground, Maryland 21005-5068 USA + * + * $Id: struct.h,v 1.9 2006/01/18 06:46:11 brlcad Exp $ + */ +/** @addtogroup libtie */ /** @{ */ + +#ifndef _TIE_STRUCT_H +#define _TIE_STRUCT_H + +#include "define.h" +#include +#if TIE_RAY_ACCOUNTING +# include +#endif + +/** + * @struct TIE_3_s struct.h + * This is a 3-tuple + */ +typedef struct TIE_3_s { + tfloat v[3]; +} TIE_3; + +/** + * @struct tie_ray_s struct.h + * All the information about a ray. + * + * includes position, direction and depth + */ +typedef struct tie_ray_s { + TIE_3 pos; /* Position */ + TIE_3 dir; /* Direction */ + short depth; /* Depth */ + short kdtree_depth; +} tie_ray_t; + +/** + * @struct tie_id_s struct.h + * + * Ray intersection data (id) + * + * point, normal, distance, and barycentric coordinates + */ +typedef struct tie_id_s { + TIE_3 pos; /* Point */ + TIE_3 norm; /* Normal */ + tfloat dist; /* Distance */ + tfloat alpha; /* Barycentric Coordinate Alpha */ + tfloat beta; /* Barycentric Coordinate Beta */ +} tie_id_t; + +/** + * @struct tie_tri_s struct.h + * + * Everything you need to know about a triangle + */ +typedef struct tie_tri_s { + TIE_3 data[3]; /* 36-bytes, Data[0] = Point, Data[1] = Normal, Data[2] = DotProduct, VectorU, VectorV */ + tfloat *v; /* 8-bytes */ + void *ptr; /* 4-bytes */ +} tie_tri_t; + +/** + * @struct tie_kdtree_s struct.h + * + * The binary space partitioning tree + */ +typedef struct tie_kdtree_s { + tfloat axis; + void *data; +} tie_kdtree_t; + +/** + * + */ +typedef struct tie_geom_s { + tie_tri_t **tri_list; + int tri_num; +} tie_geom_t; + +/** + * + */ +typedef struct tie_stack_s { + tie_kdtree_t *node; + tfloat near; + tfloat far; +} tie_stack_t; + +/** + * + */ +typedef struct tie_s { + uint64_t rays_fired; + tie_kdtree_t *kdtree; + int max_depth; /* Maximum depth allowed for given geometry */ + TIE_3 min, max; + unsigned int tri_num; + unsigned int tri_num_alloc; + tie_tri_t *tri_list; + int stat; /* used for testing various statistics */ +} tie_t; + +#endif + +/** @} */ diff -r -u -N -x linux2-config.py -x CVS blender-cvs/source/blender/render/intern/include/tie.h blender/source/blender/render/intern/include/tie.h --- blender-cvs/source/blender/render/intern/include/tie.h 1969-12-31 19:00:00.000000000 -0500 +++ blender/source/blender/render/intern/include/tie.h 2006-06-14 03:35:34.000000000 -0400 @@ -0,0 +1,65 @@ +/* T I E . H + * + * @file tie.h + * + * BRL-CAD + * + * Copyright (c) 2002-2006 United States Government as represented by + * the U.S. Army Research Laboratory. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this file; see the file named COPYING for more + * information. + * + * Comments - + * Triangle Intersection Engine Header + * + * Author - + * Justin L. Shumaker + * + * Source - + * The U. S. Army Research Laboratory + * Aberdeen Proving Ground, Maryland 21005-5068 USA + * + * $Id: tie.h,v 1.6 2006/01/18 06:46:11 brlcad Exp $ + */ +/** addtogroup libtie */ /** @{ */ + +#ifndef _TIE_H +#define _TIE_H + +#include "define.h" +#include "struct.h" +#include "kdtree.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +void tie_init(tie_t *tie, unsigned int tri_num); +void tie_free(tie_t *tie); +void tie_prep(tie_t *tie); +void* tie_work(tie_t *tie, tie_ray_t *ray, tie_id_t *id, + void *(*hitfunc)(tie_ray_t*, tie_id_t*, tie_tri_t*, void *ptr), + void *ptr); +void tie_push(tie_t *tie, TIE_3 *tlist, int tnum, void *plist, int pstride); + + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ diff -r -u -N -x linux2-config.py -x CVS blender-cvs/source/blender/render/intern/source/convertblender.c blender/source/blender/render/intern/source/convertblender.c --- blender-cvs/source/blender/render/intern/source/convertblender.c 2006-06-16 15:27:12.000000000 -0400 +++ blender/source/blender/render/intern/source/convertblender.c 2006-06-16 03:47:39.000000000 -0400 @@ -2933,7 +2933,8 @@ re->scene->world->aosphere= NULL; } - if(re->r.mode & R_RAYTRACE) freeoctree(re); + //if(re->r.mode & R_RAYTRACE) freeoctree(re); + if(re->r.mode & R_RAYTRACE) freekdtree(re); re->totvlak=re->totvert=re->totlamp=re->tothalo= 0; re->i.convertdone= 0; @@ -3352,7 +3353,8 @@ /* octree */ if(!re->test_break()) { if(re->r.mode & R_RAYTRACE) { - makeoctree(re); + /*makeoctree(re); */ + makekdtree(re); } } /* ENVIRONMENT MAPS */ diff -r -u -N -x linux2-config.py -x CVS blender-cvs/source/blender/render/intern/source/kdtree.c blender/source/blender/render/intern/source/kdtree.c --- blender-cvs/source/blender/render/intern/source/kdtree.c 1969-12-31 19:00:00.000000000 -0500 +++ blender/source/blender/render/intern/source/kdtree.c 2006-06-16 02:51:44.000000000 -0400 @@ -0,0 +1,929 @@ +/* K D T R E E . C +* +* @file tie.c +* +* BRL-CAD +* +* Copyright (c) 2002-2006 United States Government as represented by +* the U.S. Army Research Laboratory. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public License +* as published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This library 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 +* Library General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this file; see the file named COPYING for more +* information. +* +* @brief support routines for shooting at triangles +* Comments - +* KD-Tree Routines +* +* Author - +* Justin L. Shumaker +* +* Source - +* The U. S. Army Research Laboratory +* Aberdeen Proving Ground, Maryland 21005-5068 USA +* +* $Id: kdtree.c,v 1.11 2006/01/18 06:46:11 brlcad Exp $ +*/ +/** @addtogroup libtie + * @{ + */ + +#include "kdtree.h" +#include +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +tfloat TIE_PREC; + +/************************************************************* + **************** PRIVATE FUNCTIONS ************************** + *************************************************************/ + + +static void tie_kdtree_free_node(tie_kdtree_t *node) +{ + tie_kdtree_t *node_aligned = (tie_kdtree_t *)((intptr_t)node & ~0x7L); + + if (((intptr_t)(node_aligned->data)) & 0x4) { + /* Node Data is KDTREE Children, Recurse */ + tie_kdtree_free_node(&((tie_kdtree_t *)(((intptr_t)(node_aligned->data)) & ~0x7L))[0]); + tie_kdtree_free_node(&((tie_kdtree_t *)(((intptr_t)(node_aligned->data)) & ~0x7L))[1]); + MEM_freeN((void*)((intptr_t)(node_aligned->data) & ~0x7L)); + } else { + /* This node points to a geometry node, free it */ + MEM_freeN(((tie_geom_t *)((intptr_t)(node_aligned->data) & ~0x7L))->tri_list); + MEM_freeN((void *)((intptr_t)(node_aligned->data) & ~0x7L)); + } +} + + +static void tie_kdtree_cache_free_node(tie_t *tie, tie_kdtree_t *node, void **cache) +{ + tie_kdtree_t *node_aligned = (tie_kdtree_t *)((intptr_t)node & ~0x7L); + unsigned int size, mem, tri_num, i, tri_ind; + unsigned char type, split; + + memcpy(&size, *cache, sizeof(unsigned int)); + memcpy(&mem, &((char *)*cache)[sizeof(unsigned int)], sizeof(unsigned int)); + /* + * If the available size for this cache is under 1MB, then grow it 4MB larger. + * This reduces the number of realloc's required. Makes things much much faster + * on systems like FreeBSD that do a full copy for each realloc. + */ + if (mem - size < 1 << 20) { + mem += 1 << 23; + memcpy(&((char *)*cache)[sizeof(unsigned int)], &mem, sizeof(unsigned int)); + *cache = MEM_reallocN(*cache, mem); + } + + if (((intptr_t)(node_aligned->data)) & 0x4) { + /* Create a KD-Tree Node in the cache */ + type = 0; + memcpy(&((char *)*cache)[size], &type, 1); + size += 1; + memcpy(&((char *)*cache)[size], &(node_aligned->axis), sizeof(tfloat)); + size += sizeof(tfloat); + split = ((intptr_t)(node_aligned->data)) & 0x3; + memcpy(&((char *)*cache)[size], &split, 1); + size += 1; + + /* Update size of cache */ + memcpy(*cache, &size, sizeof(unsigned int)); + + /* Node Data is KDTREE Children, Recurse */ + tie_kdtree_cache_free_node(tie, &((tie_kdtree_t *)(((intptr_t)(node_aligned->data)) & ~0x7L))[0], cache); + tie_kdtree_cache_free_node(tie, &((tie_kdtree_t *)(((intptr_t)(node_aligned->data)) & ~0x7L))[1], cache); + MEM_freeN((void *)((intptr_t)(node_aligned->data) & ~0x7L)); + } else { + tri_num = ((tie_geom_t *)((intptr_t)(node_aligned->data) & ~0x7L))->tri_num; + type = 1; + memcpy(&((char *)*cache)[size], &type, 1); + size += 1; + memcpy(&((char *)*cache)[size], &tri_num, sizeof(unsigned int)); + + size += sizeof(unsigned int); + + for (i = 0; i < tri_num; i++) { + /* + * Pointer subtraction gives us the index of the triangle since the block of memory + * that the triangle exists in is contiguous memory. + */ + tri_ind = ((tie_geom_t *)((intptr_t)(node_aligned->data) & ~0x7L))->tri_list[i] - &tie->tri_list[0]; + memcpy(&((char *)*cache)[size], &tri_ind, sizeof(unsigned int)); + size += sizeof(unsigned int); + } + + /* Update size of cache */ + memcpy(*cache, &size, sizeof(unsigned int)); + + /* This node points to a geometry node, free it */ + MEM_freeN(((tie_geom_t *)((intptr_t)(node_aligned->data) & ~0x7L))->tri_list); + MEM_freeN((void *)((intptr_t)(node_aligned->data) & ~0x7L)); + } +} + + +static void tie_kdtree_prep_head(tie_t *tie, tie_tri_t *tri_list, int tri_num) +{ + tie_geom_t *g; + TIE_3 min, max; + int i; + + + if (!tri_num) + return ; + + /* Insert all triangles into the Head Node */ + if (!tie->kdtree) { + tie->kdtree = (tie_kdtree_t *)MEM_mallocN(sizeof(tie_kdtree_t), "kdtree"); + tie->kdtree->data = (void *)MEM_mallocN(sizeof(tie_geom_t), "kdtree data"); + g = ((tie_geom_t *)(tie->kdtree->data)); + g->tri_num = 0; + + math_bbox(tie->min, tie->max, tri_list[0].data[0], tri_list[0].data[1], tri_list[0].data[2]); + + g->tri_list = (tie_tri_t **)MEM_mallocN(sizeof(tie_tri_t *) * tri_num, "kdtree tri_list"); + + /* form bounding box of scene */ + for (i = 0; i < tri_num; i++) { + g->tri_list[i] = &tri_list[i]; + + /* Get Bounding Box of Triangle */ + math_bbox(min, max, tri_list[i].data[0], tri_list[i].data[1], tri_list[i].data[2]); + + /* Check to see if defines a new Max or Min point */ + math_vec_min(tie->min, min); + math_vec_max(tie->max, max); + /* printf("Box: [%.3f, %.3f, %.3f] [%.3f, %.3f, %.3f]\n", tie->min.v[0], tie->min.v[1], tie->min.v[2], tie->max.v[0], tie->max.v[1], tie->max.v[2]); */ + } + + ((tie_geom_t *)(tie->kdtree->data))->tri_num = tri_num; + } +} + + +static int tie_kdtree_tri_box_overlap(TIE_3 *center, TIE_3 *half_size, TIE_3 triverts[3]) +{ + /* + * use separating axis theorem to test overlap between triangle and box + * need to test for overlap in these directions: + * 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle + * we do not even need to test these) + * 2) normal of the triangle + * 3) crossproduct(edge from tri, {x,y,z}-directin) + * this gives 3x3=9 more tests + */ + TIE_3 v0, v1, v2, normal, e0, e1, e2, fe, p; + tfloat min, max, d, t, rad; + + /* move everything so that the boxcenter is in (0,0,0) */ + math_vec_sub(v0, triverts[0], (*center)); + math_vec_sub(v1, triverts[1], (*center)); + math_vec_sub(v2, triverts[2], (*center)); + + /* + * First test overlap in the {x,y,z}-directions + * find min, max of the triangle each direction, and test for overlap in + * that direction -- this is equivalent to testing a minimal AABB around + * the triangle against the AABB + */ + + /* test in X-direction */ + math_min3(min, v0.v[0], v1.v[0], v2.v[0]); + math_max3(max, v0.v[0], v1.v[0], v2.v[0]); + if (min > half_size->v[0] || max < -half_size->v[0]) + return 0; + + /* test in Y-direction */ + math_min3(min, v0.v[1], v1.v[1], v2.v[1]); + math_max3(max, v0.v[1], v1.v[1], v2.v[1]); + if (min > half_size->v[1] || max < -half_size->v[1]) + return 0; + + /* test in Z-direction */ + math_min3(min, v0.v[2], v1.v[2], v2.v[2]); + math_max3(max, v0.v[2], v1.v[2], v2.v[2]); + if (min > half_size->v[2] || max < -half_size->v[2]) + return 0; + + /* compute triangle edges */ + math_vec_sub(e0, v1, v0); /* tri edge 0 */ + math_vec_sub(e1, v2, v1); /* tri edge 1 */ + math_vec_sub(e2, v0, v2); /* tri edge 2 */ + + /* Perform the 9 tests */ + fe.v[0] = fabs(e0.v[0]); + fe.v[1] = fabs(e0.v[1]); + fe.v[2] = fabs(e0.v[2]); + + AXISTEST_X01(e0.v[2], e0.v[1], fe.v[2], fe.v[1]); + AXISTEST_Y02(e0.v[2], e0.v[0], fe.v[2], fe.v[0]); + AXISTEST_Z12(e0.v[1], e0.v[0], fe.v[1], fe.v[0]); + + fe.v[0] = fabs(e1.v[0]); + fe.v[1] = fabs(e1.v[1]); + fe.v[2] = fabs(e1.v[2]); + + AXISTEST_X01(e1.v[2], e1.v[1], fe.v[2], fe.v[1]); + AXISTEST_Y02(e1.v[2], e1.v[0], fe.v[2], fe.v[0]); + AXISTEST_Z0(e1.v[1], e1.v[0], fe.v[1], fe.v[0]); + + fe.v[0] = fabs(e2.v[0]); + fe.v[1] = fabs(e2.v[1]); + fe.v[2] = fabs(e2.v[2]); + + AXISTEST_X2(e2.v[2], e2.v[1], fe.v[2], fe.v[1]); + AXISTEST_Y1(e2.v[2], e2.v[0], fe.v[2], fe.v[0]); + AXISTEST_Z12(e2.v[1], e2.v[0], fe.v[1], fe.v[0]); + + /* + * Test if the box intersects the plane of the triangle + * compute plane equation of triangle: normal*x+d=0 + */ + math_vec_cross(normal, e0, e1); + math_vec_dot(d, normal, v0); /* plane eq: normal . x + d = 0 */ + + p.v[0] = normal.v[0] > 0 ? half_size->v[0] : -half_size->v[0]; + p.v[1] = normal.v[1] > 0 ? half_size->v[1] : -half_size->v[1]; + p.v[2] = normal.v[2] > 0 ? half_size->v[2] : -half_size->v[2]; + + math_vec_dot(t, normal, p); + return t >= d ? 1 : 0; +} + + +static void tie_kdtree_build(tie_t *tie, tie_kdtree_t *node, int depth, TIE_3 min, TIE_3 max, int node_a, int node_b) +{ + tie_geom_t *child[2], *node_gd = (tie_geom_t *)(node->data); + TIE_3 cmin[2], cmax[2], center[2], half_size[2]; + int i, j, n, split, cnt[2]; + +#if 0 + /* if(depth >= 26) */ + printf("%f %f %f %f %f %f\n", min.v[0], min.v[1], min.v[2], max.v[0], max.v[1], max.v[2]); +#endif + + /* Terminating criteria for KDTREE subdivision */ + if (node_gd->tri_num <= TIE_KDTREE_NODE_MAX || depth > tie->max_depth) { + /* tie->stat++; */ + tie->stat += node_gd->tri_num; +#if 0 + + if (node_gd->tri_num > tie->stat) + tie->stat = node_gd->tri_num; + if (node_gd->tri_num > tie->stat) { + tie->stat = node_gd->tri_num; + printf("depth: %d, tris: %d\n", depth, node_gd->tri_num); + printf("%f %f %f %f %f %f\n", min.v[0], min.v[1], min.v[2], max.v[0], max.v[1], max.v[2]); + } + exit(0); +#endif + + return ; + } + +#if 0 + { + /********************** + * MID-SPLIT ALGORITHM * + ***********************/ + TIE_3 vec; + + /* Left Child */ + cmin[0] = min; + cmax[0] = max; + + /* Right Child */ + cmin[1] = min; + cmax[1] = max; + + math_vec_add(center[0], max, min); + math_vec_mul_scalar(center[0], center[0], 0.5); + + /* Split along largest Axis to keep node sizes relatively cube-like (Naive) */ + math_vec_sub(vec, max, min); + + /* Determine the largest Axis */ + if (vec.v[0] >= vec.v[1] && vec.v[0] >= vec.v[2]) + { + cmax[0].v[0] = center[0].v[0]; + cmin[1].v[0] = center[0].v[0]; + node->axis = center[0].v[0]; + split = 0; + } else if (vec.v[1] >= vec.v[0] && vec.v[1] >= vec.v[2]) + { + cmax[0].v[1] = center[0].v[1]; + cmin[1].v[1] = center[0].v[1]; + node->axis = center[0].v[1]; + split = 1; + } else + { + cmax[0].v[2] = center[0].v[2]; + cmin[1].v[2] = center[0].v[2]; + node->axis = center[0].v[2]; + split = 2; + } + } +#else + { + /**************************************** + * Justin's Aggressive KD-Tree Algorithm * + *****************************************/ + int slice[3][MAX_SLICES + MIN_SLICES], gap[3][2], active, split_slice; + int side[3][MAX_SLICES + MIN_SLICES][2], d, s, k, smax[3], smin, slice_num; + tfloat coef[3][MAX_SLICES + MIN_SLICES], split_coef, beg, end, d_min, d_max; + tie_tri_t *tri; + + /* + * Calculate number of slices to use as a function of triangle density. + * Slices as a function of relative node size does not work so well. + */ + slice_num = MIN_SLICES + MAX_SLICES * ((tfloat)node_gd->tri_num / (tfloat)tie->tri_num); + + for (d = 0; d < 3; d++) + { + /* + * Optimization: Walk each triangle and find the min and max for the given dimension + * of the complete triangle list. This will tell us what slices we needn't bother + * doing any computations for. + */ + for (i = 0; i < node_gd->tri_num; i++) { + tri = node_gd->tri_list[i]; + /* Set min anx max */ + math_min3(tri->v[0], tri->data[0].v[d], tri->data[1].v[d], tri->data[2].v[d]); + math_max3(tri->v[1], tri->data[0].v[d], tri->data[1].v[d], tri->data[2].v[d]); + + /* Clamp to node AABB */ + if (tri->v[0] < min.v[d]) + tri->v[0] = min.v[d]; + if (tri->v[1] > max.v[d]) + tri->v[1] = max.v[d]; + + if (i == 0 || tri->v[0] < d_min) + d_min = tri->v[0]; + + if (i == 0 || tri->v[1] > d_max) + d_max = tri->v[1]; + } + + for (k = 0; k < slice_num; k++) { + slice[d][k] = 0; + side[d][k][0] = 0; + side[d][k][1] = 0; + + /* Left Child */ + cmin[0] = min; + cmax[0] = max; + + /* Right Child */ + cmin[1] = min; + cmax[1] = max; + + /* construct slices so as not to use the boundaries as slices */ + coef[d][k] = ((tfloat)k / (tfloat)(slice_num - 1)) * (tfloat)(slice_num - 2) / (tfloat)slice_num + (tfloat)1 / (tfloat)slice_num; + cmax[0].v[d] = min.v[d] * (1.0 - coef[d][k]) + max.v[d] * coef[d][k]; + cmin[1].v[d] = cmax[0].v[d]; + + if (cmax[0].v[d] < d_min || cmax[0].v[d] > d_max) + continue; + + for (n = 0; n < 2; n++) { + math_vec_add(center[n], cmax[n], cmin[n]); + math_vec_mul_scalar(center[n], center[n], 0.5); + math_vec_sub(half_size[n], cmax[n], cmin[n]); + math_vec_mul_scalar(half_size[n], half_size[n], 0.5); + } + + for (i = 0; i < node_gd->tri_num; i++) { + /* + * Optimization: If the points for the triangle of the dimension being tested + * do not span the cutting plane, then do not bother with the next test. + */ + if ((node_gd->tri_list[i]->data[0].v[d] > cmax[0].v[d] && + node_gd->tri_list[i]->data[1].v[d] > cmax[0].v[d] && + node_gd->tri_list[i]->data[2].v[d] > cmax[0].v[d]) || + (node_gd->tri_list[i]->data[0].v[d] < cmax[0].v[d] && + node_gd->tri_list[i]->data[1].v[d] < cmax[0].v[d] && + node_gd->tri_list[i]->data[2].v[d] < cmax[0].v[d])) + continue; + + /* Check that the triangle is in both node A and B for it to span. */ + s = 0; + for (n = 0; n < 2; n++) { + /* + * Check to see if any triangle points are inside of the node before + * spending alot of cycles on the full blown triangle box overlap + */ + for (j = 0; j < 3; j++) + if (node_gd->tri_list[i]->data[j].v[0] > cmin[n].v[0] && + node_gd->tri_list[i]->data[j].v[0] < cmax[n].v[0] && + node_gd->tri_list[i]->data[j].v[1] > cmin[n].v[1] && + node_gd->tri_list[i]->data[j].v[1] < cmax[n].v[1] && + node_gd->tri_list[i]->data[j].v[2] > cmin[n].v[2] && + node_gd->tri_list[i]->data[j].v[2] < cmax[n].v[2]) { + j = 4; + } + + if (j == 5) { + s++; + side[d][k][n]++; + } else { + if (tie_kdtree_tri_box_overlap(¢er[n], &half_size[n], node_gd->tri_list[i]->data)) { + s++; + side[d][k][n]++; + } + } + } + + if (s == 2) + slice[d][k]++; + } + } + } + + /* Store the max value from each of the 3 Slice arrays */ + for (d = 0; d < 3; d++) + { + smax[d] = 0; + for (k = 0; k < slice_num; k++) { + if (slice[d][k] > smax[d]) + smax[d] = slice[d][k]; + } + } + + /* + * To eliminate "empty" areas, build a list of spans where geometric complexity is + * less than MIN_SPAN of the overal nodes size and then selecting the splitting plane + * the corresponds to the span slice domain nearest the center to bias towards a balanced tree + */ + + for (d = 0; d < 3; d++) + { + gap[d][0] = 0; + gap[d][1] = 0; + beg = 0; + end = 0; + active = 0; + + for (k = 0; k < slice_num; k++) { + /* printf("slice[%d][%d]: %d < %d\n", d, k, slice[d][k], (int)(MIN_DENSITY * (tfloat)smax[d])); */ + if (slice[d][k] < (int)(MIN_DENSITY * (tfloat)smax[d])) { + if (!active) { + active = 1; + beg = k; + end = k; + } else { + end = k; + } + } else { + if (active) { + if (end - beg > gap[d][1] - gap[d][0]) { + gap[d][0] = beg; + gap[d][1] = end; + } + } + active = 0; + } + } + + if (active) + if (end - beg > gap[d][1] - gap[d][0]) { + gap[d][0] = beg; + gap[d][1] = end; + } + } + +#if 0 + printf("gap_x: %d->%d = %d\n", gap[0][0], gap[0][1], gap[0][1] - gap[0][0]); + printf("gap_y: %d->%d = %d\n", gap[1][0], gap[1][1], gap[1][1] - gap[1][0]); + printf("gap_z: %d->%d = %d\n", gap[2][0], gap[2][1], gap[2][1] - gap[2][0]); +#endif + + /* + * If there is a gap atleast MIN_SPAN in side wrt the nodes dimension size + * then use the nearest edge of the gap to 0.5 as the splitting plane, + * Use the the gap with the largest span. + * If no gaps are found meeting the criteria then weight the span values to + * bias towards a balanced kd-tree and choose the minima of that weighted curve. + */ + + /* Largest gap */ + d = 0; + if (gap[1][1] - gap[1][0] > gap[d][1] - gap[d][0]) + d = 1; + if (gap[2][1] - gap[2][0] > gap[d][1] - gap[d][0]) + d = 2; + + /* + * Largest gap found must meet MIN_SPAN requirements + * There must be atleast 500 triangles or we don't bother. + * Lower triangle numbers means there is a higher probability that + * triangles lack any sort of coherent structure. + */ + if ((tfloat)(gap[d][1] - gap[d][0]) / (tfloat)slice_num > MIN_SPAN && node_gd->tri_num > 500) + { + /* printf("choosing slice[%d]: %d->%d :: %d tris\n", d, gap[d][0], gap[d][1], node_gd->tri_num); */ + split = d; + if (abs(gap[d][0] - slice_num / 2) < abs(gap[d][1] - slice_num / 2)) { + /* choose gap[d][0] as splitting plane */ + split_coef = ((tfloat)gap[d][0] / (tfloat)(slice_num - 1)) * (tfloat)(slice_num - 2) / (tfloat)slice_num + (tfloat)1 / (tfloat)slice_num; + split_slice = gap[d][0]; + } else { + /* choose gap[d][1] as splitting plane */ + split_coef = ((tfloat)gap[d][1] / (tfloat)(slice_num - 1)) * (tfloat)(slice_num - 2) / (tfloat)slice_num + (tfloat)1 / (tfloat)slice_num; + split_slice = gap[d][1]; + } + } else + { + /* + * Weight the slices based on a heuristic driven linear scaling function to bias values + * towards the center as more desirable. This solves the case of a partially linear graph + * to prevent marching to determine a desirable splitting point. If this section of code + * is being executed it's typically because most 'empty space' has now been eliminated + * and/or the resulting geometry is now losing structure as the smaller cells are being + * created, i.e dividing a fraction of a wing-nut instead of an engine-block. + */ + for (d = 0; d < 3; d++) { + for (k = 0; k < slice_num; k++) { + slice[d][k] += fabs(coef[d][k] - 0.5) * SCALE_COEF * smax[d]; + /* printf("%.3f %d\n", coef[d][k], slice[d][k]); */ + } + } + + /* Choose the slice with the graphs minima as the splitting plane. */ + split = 0; + smin = tie->tri_num; + split_coef = 0.5; + for (d = 0; d < 3; d++) { + for (k = 0; k < slice_num; k++) { + if (slice[d][k] < smin) { + split_coef = coef[d][k]; + split = d; + split_slice = k; + smin = slice[d][k]; + } + } + } + + /* + * If the dimension chosen to split along has a value of 0 for the maximum value + * then the geometry was aligned such that it fell undetectable between the slices + * and therefore was not picked up by the marching slices. In the event that this + * happens, choose to naively split along the middle as this last ditch decision + * will give better results than the algorithm naively picking the first of the + * the slices forming these irregular, short followed by a long box, splits. + */ + if (smax[split] == 0) { + split_coef = coef[split][slice_num / 2]; + split_slice = slice_num / 2; + } + } + + /* + * Lastly, after we have supposedly chosen the most ideal splitting point, + * check to see that the subdivision that is about to take place is worth + * doing. In other words, if both children have the same number of triangles + * as the parent does then stop. + */ + if (side[split][split_slice][0] == node_gd->tri_num && side[split][split_slice][1] == node_gd->tri_num) + { + tie->stat += node_gd->tri_num; + return ; + } + +#if 0 + if (side[split][split_slice][0] == node_a && side[split][split_slice][1] == node_b) + { + if (node_gd->tri_num < 10) + return ; + /* printf("%f %f %f %f %f %f\n", min.v[0], min.v[1], min.v[2], max.v[0], max.v[1], max.v[2]); */ + /* printf("moo: %d - %d\n", depth, node_gd->tri_num); */ + } +#endif + + +#if 0 + printf("winner: depth: %d, dim = %d, smin = %d, coef: %.3f\n", depth, split, smin, split_coef); + printf("winner: min: %.3f %.3f %.3f, max: %.3f %.3f %.3f, tris: %d\n", min.v[0], min.v[1], min.v[2], max.v[0], max.v[1], max.v[2], node_gd->tri_num); +#endif + + /* Based on the winner, construct the two child nodes */ + /* Left Child */ + cmin[0] = min; + cmax[0] = max; + + /* Right Child */ + cmin[1] = min; + cmax[1] = max; + + cmax[0].v[split] = min.v[split] * (1.0 - split_coef) + max.v[split] * split_coef; + cmin[1].v[split] = cmax[0].v[split]; + node->axis = cmax[0].v[split]; + } +#endif + + + /* Allocate 2 children nodes for the parent node */ + node->data = (void *)MEM_mallocN(2 * sizeof(tie_kdtree_t), "kdtree build"); + + ((tie_kdtree_t *)(node->data))[0].data = MEM_mallocN(sizeof(tie_geom_t), "kdtree node data"); + ((tie_kdtree_t *)(node->data))[1].data = MEM_mallocN(sizeof(tie_geom_t), "kdtree node data"); + + /* Initialize Triangle List */ + child[0] = ((tie_geom_t *)(((tie_kdtree_t *)(node->data))[0].data)); + child[1] = ((tie_geom_t *)(((tie_kdtree_t *)(node->data))[1].data)); + + child[0]->tri_list = (tie_tri_t **)MEM_mallocN(sizeof(tie_tri_t *) * node_gd->tri_num, "kdtree tri_list"); + child[0]->tri_num = 0; + + child[1]->tri_list = (tie_tri_t **)MEM_mallocN(sizeof(tie_tri_t *) * node_gd->tri_num, "kdtree tri_list"); + child[1]->tri_num = 0; + + + /* + * Determine if the triangles touch either of the two children nodes, + * if it does insert it into them respectively. + */ + for (n = 0; n < 2; n++) { + cnt[n] = 0; + + math_vec_add(center[n], cmax[n], cmin[n]); + math_vec_mul_scalar(center[n], center[n], 0.5); + math_vec_sub(half_size[n], cmax[n], cmin[n]); + math_vec_mul_scalar(half_size[n], half_size[n], 0.5); + + for (i = 0; i < node_gd->tri_num; i++) { + /* + * Check to see if any triangle points are inside of the node before + * spending alot of cycles on the full blown triangle box overlap + */ + for (j = 0; j < 3; j++) + if (node_gd->tri_list[i]->data[j].v[0] > cmin[n].v[0] && + node_gd->tri_list[i]->data[j].v[0] < cmax[n].v[0] && + node_gd->tri_list[i]->data[j].v[1] > cmin[n].v[1] && + node_gd->tri_list[i]->data[j].v[1] < cmax[n].v[1] && + node_gd->tri_list[i]->data[j].v[2] > cmin[n].v[2] && + node_gd->tri_list[i]->data[j].v[2] < cmax[n].v[2]) { + j = 4; + } + + if (j == 5) { + child[n]->tri_list[child[n]->tri_num++] = node_gd->tri_list[i]; + cnt[n]++; + } else { + if (tie_kdtree_tri_box_overlap(¢er[n], &half_size[n], node_gd->tri_list[i]->data)) { + child[n]->tri_list[child[n]->tri_num++] = node_gd->tri_list[i]; + cnt[n]++; + } + } + } + + /* Resize Tri List to actual ammount of memory used */ + child[n]->tri_list = (tie_tri_t **)MEM_reallocN(child[n]->tri_list, sizeof(tie_tri_t *) * child[n]->tri_num); + } + + /* + * Now that the triangles have been propogated to the appropriate child nodes, + * free the triangle list on this node. + */ + node_gd->tri_num = 0; + MEM_freeN(node_gd->tri_list); + MEM_freeN(node_gd); + + /* Push each child through the same process. */ + tie_kdtree_build(tie, &((tie_kdtree_t *)(node->data))[0], depth + 1, cmin[0], cmax[0], cnt[0], cnt[1]); + tie_kdtree_build(tie, &((tie_kdtree_t *)(node->data))[1], depth + 1, cmin[1], cmax[1], cnt[0], cnt[1]); + + /* Assign the splitting dimension to the node */ + /* If we've come this far then YES, this node DOES have child nodes, MARK it as so. */ + node->data = (void *)((intptr_t)(node->data) + split + 4); +} + + +/************************************************************* + **************** EXPORTED FUNCTIONS ************************* + *************************************************************/ + +/** + * Free up all the stuff associated with the kdtree + * + * All of the KDTREE nodes and triangles that we have allocated need to + * be freed in a controlled manner. This routine does that. + * + * @param tie pointer to a struct tie_t + * @return void + */ +void tie_kdtree_free(tie_t *tie) +{ + /* Free KDTREE Nodes */ + /* prevent tie from crashing when a tie_free() is called right after a tie_init() */ + if (tie->kdtree) + tie_kdtree_free_node(tie->kdtree); + MEM_freeN(tie->kdtree); +} + + +/** + * Free up all the stuff associated with the kdtree and build a + * cache as the data is freed. Building the cache while the data + * is freed allows the peak memory not to go any higher than it + * already is. If there were seprarate cache and free functions + * then the cache would exist in memory while the triangles and + * kd-tree were in memory thus severly limiting optimal memory + * usage. + * + * All of the KDTREE nodes and triangles that we have allocated need to + * be freed in a controlled manner. This routine does that. + * + * @param tie pointer to a struct tie_t and void **cache to store data + * @return void + */ +void tie_kdtree_cache_free(tie_t *tie, void **cache) +{ + unsigned int size; + + /* + * Free KDTREE Node + * Prevent tie from crashing when a tie_free() is called right after a tie_init() + */ + if (tie->kdtree) { + *cache = MEM_mallocN(2 * sizeof(unsigned int), "kdtree cache"); + size = 2 * sizeof(unsigned int); + memcpy(*cache, &size, sizeof(unsigned int)); + memcpy(&((char *)*cache)[sizeof(unsigned int)], &size, sizeof(unsigned int)); + + /* Build the cache */ + tie_kdtree_cache_free_node(tie, tie->kdtree, cache); + + /* Resize the array back to it's real value */ + memcpy(&size, *cache, sizeof(unsigned int)); + *cache = MEM_reallocN(*cache, size); + } + MEM_freeN(tie->kdtree); +} + + +void tie_kdtree_cache_load(tie_t *tie, void *cache) +{ + tie_kdtree_t *node, *temp_node, *stack[64]; + tie_geom_t *geom; + TIE_3 min, max; + unsigned int i, size, index, tri_ind, stack_ind; + char type, split; + + + if (!cache) + return ; + + memcpy(&size, cache, sizeof(unsigned int)); + /* Advance past the first (2) unsigned ints to the actualy data */ + index = 2 * sizeof(unsigned int); + stack_ind = 0; + + while (index < size) { + memcpy(&type, &((char *)cache)[index], 1); + index += 1; + + if (type) { + /* Geometry Node - Allocate a tie_geom_t and assign to node->data. */ + node->data = MEM_mallocN(sizeof(tie_geom_t), "kdtree node data"); + geom = (tie_geom_t *)node->data; + + memcpy(&(geom->tri_num), &((char *)cache)[index], sizeof(unsigned int)); + index += sizeof(unsigned int); + + geom->tri_list = (tie_tri_t **)MEM_mallocN(geom->tri_num * sizeof(tie_tri_t *), "kdtree tri_list"); + for (i = 0; i < geom->tri_num; i++) { + memcpy(&tri_ind, &((char *)cache)[index], sizeof(unsigned int)); + index += sizeof(unsigned int); + + /* Translate the numerical index to a pointer index into tie->tri_list. */ + geom->tri_list[i] = &tie->tri_list[0] + tri_ind; + } + + if (stack_ind) { + stack_ind--; + node = stack[stack_ind]; + } + } else { + /* KD-Tree Node */ + if (!tie->kdtree) { + tie->kdtree = (tie_kdtree_t *)MEM_mallocN(sizeof(tie_kdtree_t), "kdtree cache load"); + node = tie->kdtree; + } + + /* Assign splitting axis value */ + memcpy(&node->axis, &((char *)cache)[index], sizeof(tfloat)); + index += sizeof(tfloat); + + /* Get splitting plane */ + memcpy(&split, &((char *)cache)[index], 1); + index += 1; + + /* Allocate memory for 2 child nodes */ + node->data = MEM_mallocN(2 * sizeof(tie_kdtree_t), "kdtree cache load"); + + /* Push B on the stack and Process A */ + stack[stack_ind] = &((tie_kdtree_t *)node->data)[1]; + stack_ind++; + + /* Set the new current node */ + temp_node = node; + node = &((tie_kdtree_t *)node->data)[0]; + + /* + * Mask the splitting plane and mark it as a kdtree node + * using the lower bits of the ptr. + */ + temp_node->data = (void *)((intptr_t)(temp_node->data) + split + 4); + } + } + + /* form bounding box of scene */ + math_bbox(tie->min, tie->max, tie->tri_list[0].data[0], tie->tri_list[0].data[1], tie->tri_list[0].data[2]); + for (i = 0; i < tie->tri_num; i++) { + /* Get Bounding Box of Triangle */ + math_bbox(min, max, tie->tri_list[i].data[0], tie->tri_list[i].data[1], tie->tri_list[i].data[2]); + + /* Check to see if defines a new Max or Min point */ + math_vec_min(tie->min, min); + math_vec_max(tie->max, max); + } +} + + +/** + * Get ready to shoot rays at triangles + * + * Build the KDTREE tree for the triangles we have + * + * @param tie pointer to a struct tie_t which now has all the triangles in it + * @return void + */ +void tie_kdtree_prep(tie_t *tie) +{ + TIE_3 delta; + int already_built; + + + already_built = tie->kdtree ? 1 : 0; + + /* Set bounding volume and make head node a geometry node */ + if (!already_built) + tie_kdtree_prep_head(tie, tie->tri_list, tie->tri_num); + + if (!tie->kdtree) + return ; + + /* Trim KDTREE to number of actual triangles if it's not that size already. */ + if (!already_built) + ((tie_geom_t *)(tie->kdtree->data))->tri_list = (tie_tri_t **)MEM_reallocN(((tie_geom_t *)(tie->kdtree->data))->tri_list, sizeof(tie_tri_t *) * ((tie_geom_t *)(tie->kdtree->data))->tri_num); + + /* + * Compute Floating Fuzz Precision Value + * For now, take largest dimension as basis for TIE_PREC + */ + math_vec_sub(delta, tie->max, tie->min); + math_max3(TIE_PREC, delta.v[0], delta.v[1], delta.v[2]); +#if TIE_SINGLE_PREC + + TIE_PREC *= 0.000001; +#else + + TIE_PREC *= 0.000000000001; +#endif + + /* Grow the head node to avoid floating point fuzz in the building process with edges */ + math_vec_mul_scalar(delta, delta, 1.0); /* XXX */ + math_vec_sub(tie->min, tie->min, delta); + math_vec_add(tie->max, tie->max, delta); + + /* Compute Max Depth to allow the KD-Tree to grow to */ + tie->max_depth = (int)(TIE_KDTREE_DEPTH_K1 * (log(tie->tri_num) / log(2)) + TIE_KDTREE_DEPTH_K2); + printf("max_depth: %d\n", tie->max_depth); + + /* Build the KDTREE */ + if (!already_built) + tie_kdtree_build(tie, tie->kdtree, 0, tie->min, tie->max, 0, 0); + + printf("stat: %d\n", tie->stat); + tie->stat = 0; + + /* exit(0); */ /* uncomment to profile prep phase only */ +} + +/** @} */ diff -r -u -N -x linux2-config.py -x CVS blender-cvs/source/blender/render/intern/source/pipeline.c blender/source/blender/render/intern/source/pipeline.c --- blender-cvs/source/blender/render/intern/source/pipeline.c 2006-06-18 13:14:47.000000000 -0400 +++ blender/source/blender/render/intern/source/pipeline.c 2006-06-18 13:14:29.000000000 -0400 @@ -724,6 +724,7 @@ /* init some variables */ re->ycor= 1.0f; + re->tie.kdtree = NULL; return re; } diff -r -u -N -x linux2-config.py -x CVS blender-cvs/source/blender/render/intern/source/.#pipeline.c.1.69 blender/source/blender/render/intern/source/.#pipeline.c.1.69 --- blender-cvs/source/blender/render/intern/source/.#pipeline.c.1.69 1969-12-31 19:00:00.000000000 -0500 +++ blender/source/blender/render/intern/source/.#pipeline.c.1.69 2006-06-16 17:43:20.000000000 -0400 @@ -0,0 +1,1974 @@ +/** + * + * ***** 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) 2006 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include "DNA_group_types.h" +#include "DNA_image_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_userdef_types.h" + +#include "BKE_global.h" +#include "BKE_image.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_scene.h" +#include "BKE_writeavi.h" /* <------ should be replaced once with generic movie module */ + +#include "MEM_guardedalloc.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_threads.h" + +#include "PIL_time.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "intern/openexr/openexr_multi.h" + +#include "RE_pipeline.h" +#include "radio.h" + +#include "BSE_sequence.h" /* <----------------- bad!!! */ + +/* yafray: include for yafray export/render */ +#include "YafRay_Api.h" + +/* internal */ +#include "render_types.h" +#include "renderpipeline.h" +#include "renderdatabase.h" +#include "rendercore.h" +#include "envmap.h" +#include "initrender.h" +#include "shadbuf.h" +#include "zbuf.h" + + +/* render flow + +1) Initialize state +- state data, tables +- movie/image file init +- everything that doesn't change during animation + +2) Initialize data +- camera, world, matrices +- make render verts, faces, halos, strands +- everything can change per frame/field + +3) Render Processor +- multiple layers +- tiles, rect, baking +- layers/tiles optionally to disk or directly in Render Result + +4) Composit Render Result +- also read external files etc + +5) Image Files +- save file or append in movie + +*/ + + +/* ********* globals ******** */ + +/* here we store all renders */ +static struct ListBase RenderList= {NULL, NULL}; + +/* hardcopy of current render, used while rendering for speed */ +Render R; + +/* ********* alloc and free ******** */ + + +static volatile int g_break= 0; +static int thread_break(void) +{ + return g_break; +} + +/* default callbacks, set in each new render */ +static void result_nothing(RenderResult *rr) {} +static void result_rcti_nothing(RenderResult *rr, volatile struct rcti *rect) {} +static void stats_nothing(RenderStats *rs) {} +static void int_nothing(int val) {} +static int void_nothing(void) {return 0;} +static void print_error(char *str) {printf("ERROR: %s\n", str);} + +static void stats_background(RenderStats *rs) +{ + extern unsigned long mem_in_use; + float megs_used_memory= mem_in_use/(1024.0*1024.0); + char str[400], *spos= str; + + if(rs->convertdone) { + + spos+= sprintf(spos, "Fra:%d Mem:%.2fM ", G.scene->r.cfra, megs_used_memory); + + if(rs->infostr) { + spos+= sprintf(spos, " | %s", rs->infostr); + } + else { + if(rs->tothalo) + spos+= sprintf(spos, "Sce: %s Ve:%d Fa:%d Ha:%d La:%d", G.scene->id.name+2, rs->totvert, rs->totface, rs->tothalo, rs->totlamp); + else + spos+= sprintf(spos, "Sce: %s Ve:%d Fa:%d La:%d", G.scene->id.name+2, rs->totvert, rs->totface, rs->totlamp); + } + printf(str); printf("\n"); + } +} + +static void free_render_result(RenderResult *res) +{ + if(res==NULL) return; + + while(res->layers.first) { + RenderLayer *rl= res->layers.first; + + if(rl->rectf) MEM_freeT(rl->rectf); + while(rl->passes.first) { + RenderPass *rpass= rl->passes.first; + if(rpass->rect) MEM_freeT(rpass->rect); + BLI_remlink(&rl->passes, rpass); + MEM_freeT(rpass); + } + BLI_remlink(&res->layers, rl); + MEM_freeT(rl); + } + + if(res->rect32) + MEM_freeT(res->rect32); + if(res->rectz) + MEM_freeT(res->rectz); + if(res->rectf) + MEM_freeT(res->rectf); + + MEM_freeT(res); +} + +/* all layers except the active one get temporally pushed away */ +static void push_render_result(Render *re) +{ + re->pushedresult= re->result; + re->result= NULL; +} + +/* if scemode is R_SINGLE_LAYER, at end of rendering, merge the both render results */ +static void pop_render_result(Render *re) +{ + + if(re->result==NULL) { + printf("pop render result error; no current result!\n"); + return; + } + if(re->pushedresult) { + if(re->pushedresult->rectx==re->result->rectx && re->pushedresult->recty==re->result->recty) { + /* find which layer in pushedresult should be replaced */ + SceneRenderLayer *srl; + RenderLayer *rlpush; + RenderLayer *rl= re->result->layers.first; + int nr; + + /* render result should be empty after this */ + BLI_remlink(&re->result->layers, rl); + + /* reconstruct render result layers */ + for(nr=0, srl= re->scene->r.layers.first; srl; srl= srl->next, nr++) { + if(nr==re->r.actlay) + BLI_addtail(&re->result->layers, rl); + else { + rlpush= RE_GetRenderLayer(re->pushedresult, srl->name); + if(rlpush) { + BLI_remlink(&re->pushedresult->layers, rlpush); + BLI_addtail(&re->result->layers, rlpush); + } + } + } + } + + free_render_result(re->pushedresult); + re->pushedresult= NULL; + } +} + + +static char *get_pass_name(int passtype, int channel) +{ + + if(passtype == SCE_PASS_COMBINED) { + if(channel==0) return "Combined.R"; + else if(channel==1) return "Combined.G"; + else if(channel==2) return "Combined.B"; + else return "Combined.A"; + } + if(passtype == SCE_PASS_Z) + return "Z"; + if(passtype == SCE_PASS_VECTOR) { + if(channel==0) return "Vector.X"; + else if(channel==1) return "Vector.Y"; + else if(channel==2) return "Vector.Z"; + else return "Vector.W"; + } + if(passtype == SCE_PASS_NORMAL) { + if(channel==0) return "Normal.X"; + else if(channel==1) return "Normal.Y"; + else return "Normal.Z"; + } + if(passtype == SCE_PASS_RGBA) { + if(channel==0) return "Color.R"; + else if(channel==1) return "Color.G"; + else if(channel==2) return "Color.B"; + else return "Color.A"; + } + if(passtype == SCE_PASS_DIFFUSE) { + if(channel==0) return "Diffuse.R"; + else if(channel==1) return "Diffuse.G"; + else return "Diffuse.B"; + } + if(passtype == SCE_PASS_SPEC) { + if(channel==0) return "Spec.R"; + else if(channel==1) return "Spec.G"; + else return "Spec.B"; + } + if(passtype == SCE_PASS_SHADOW) { + if(channel==0) return "Shadow.R"; + else if(channel==1) return "Shadow.G"; + else return "Shadow.B"; + } + if(passtype == SCE_PASS_AO) { + if(channel==0) return "AO.R"; + else if(channel==1) return "AO.G"; + else return "AO.B"; + } + if(passtype == SCE_PASS_RAY) { + if(channel==0) return "Ray.R"; + else if(channel==1) return "Ray.G"; + else return "Ray.B"; + } + return "Unknown"; +} + +static void render_unique_exr_name(Render *re, char *str) +{ + char di[FILE_MAXDIR+FILE_MAXFILE], name[FILE_MAXFILE], fi[FILE_MAXFILE]; + + BLI_strncpy(di, G.sce, FILE_MAXDIR+FILE_MAXFILE); + BLI_splitdirstring(di, fi); + sprintf(name, "%s_%s.exr", fi, re->scene->id.name+2); + if(G.background) + BLI_make_file_string("/", str, "/tmp/", name); + else + BLI_make_file_string("/", str, U.tempdir, name); + +} + +static void render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype) +{ + char *typestr= get_pass_name(passtype, 0); + RenderPass *rpass= MEM_callocT(sizeof(RenderPass), typestr); + int rectsize= rr->rectx*rr->recty*channels; + + BLI_addtail(&rl->passes, rpass); + rpass->passtype= passtype; + rpass->channels= channels; + + if(rr->exrhandle) { + int a; + for(a=0; aexrhandle, rl->name, get_pass_name(passtype, a)); + } + else { + if(passtype==SCE_PASS_VECTOR) { + float *rect; + int x; + + /* initialize to max speed */ + rect= rpass->rect= MEM_mapallocT(sizeof(float)*rectsize, typestr); + for(x= rectsize-1; x>=0; x--) + rect[x]= PASS_VECTOR_MAX; + } + else + rpass->rect= MEM_mapallocT(sizeof(float)*rectsize, typestr); + } +} + +float *RE_RenderLayerGetPass(RenderLayer *rl, int passtype) +{ + RenderPass *rpass; + + for(rpass=rl->passes.first; rpass; rpass= rpass->next) + if(rpass->passtype== passtype) + return rpass->rect; + return NULL; +} + +RenderLayer *RE_GetRenderLayer(RenderResult *rr, const char *name) +{ + RenderLayer *rl; + + if(rr==NULL) return NULL; + + for(rl= rr->layers.first; rl; rl= rl->next) + if(strncmp(rl->name, name, RE_MAXNAME)==0) + return rl; + return NULL; +} + +#define RR_USEMEM 0 +/* called by main render as well for parts */ +/* will read info from Render *re to define layers */ +/* called in threads */ +/* re->winx,winy is coordinate space of entire image, partrct the part within */ +static RenderResult *new_render_result(Render *re, rcti *partrct, int crop, int savebuffers) +{ + RenderResult *rr; + RenderLayer *rl; + SceneRenderLayer *srl; + int rectx, recty, nr; + + rectx= partrct->xmax - partrct->xmin; + recty= partrct->ymax - partrct->ymin; + + if(rectx<=0 || recty<=0) + return NULL; + + rr= MEM_callocT(sizeof(RenderResult), "new render result"); + rr->rectx= rectx; + rr->recty= recty; + rr->renrect.xmin= 0; rr->renrect.xmax= rectx-2*crop; + /* crop is one or two extra pixels rendered for filtering, is used for merging and display too */ + rr->crop= crop; + + /* tilerect is relative coordinates within render disprect. do not subtract crop yet */ + rr->tilerect.xmin= partrct->xmin - re->disprect.xmin; + rr->tilerect.xmax= partrct->xmax - re->disprect.xmax; + rr->tilerect.ymin= partrct->ymin - re->disprect.ymin; + rr->tilerect.ymax= partrct->ymax - re->disprect.ymax; + + if(savebuffers) { + rr->exrhandle= IMB_exr_get_handle(); + } + + /* check renderdata for amount of layers */ + for(nr=0, srl= re->r.layers.first; srl; srl= srl->next, nr++) { + + if((re->r.scemode & R_SINGLE_LAYER) && nr!=re->r.actlay) + continue; + + rl= MEM_callocT(sizeof(RenderLayer), "new render layer"); + BLI_addtail(&rr->layers, rl); + + strcpy(rl->name, srl->name); + rl->lay= srl->lay; + rl->layflag= srl->layflag; + rl->passflag= srl->passflag; + + if(rr->exrhandle) { + IMB_exr_add_channel(rr->exrhandle, rl->name, "Combined.R"); + IMB_exr_add_channel(rr->exrhandle, rl->name, "Combined.G"); + IMB_exr_add_channel(rr->exrhandle, rl->name, "Combined.B"); + IMB_exr_add_channel(rr->exrhandle, rl->name, "Combined.A"); + } + else + rl->rectf= MEM_mapallocT(rectx*recty*sizeof(float)*4, "Combined rgba"); + + if(srl->passflag & SCE_PASS_Z) + render_layer_add_pass(rr, rl, 1, SCE_PASS_Z); + if(srl->passflag & SCE_PASS_VECTOR) + render_layer_add_pass(rr, rl, 4, SCE_PASS_VECTOR); + if(srl->passflag & SCE_PASS_NORMAL) + render_layer_add_pass(rr, rl, 3, SCE_PASS_NORMAL); + if(srl->passflag & SCE_PASS_RGBA) + render_layer_add_pass(rr, rl, 4, SCE_PASS_RGBA); + if(srl->passflag & SCE_PASS_DIFFUSE) + render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE); + if(srl->passflag & SCE_PASS_SPEC) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SPEC); + if(srl->passflag & SCE_PASS_SHADOW) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SHADOW); + if(srl->passflag & SCE_PASS_AO) + render_layer_add_pass(rr, rl, 3, SCE_PASS_AO); + if(srl->passflag & SCE_PASS_RAY) + render_layer_add_pass(rr, rl, 3, SCE_PASS_RAY); + + } + /* previewrender and envmap don't do layers, so we make a default one */ + if(rr->layers.first==NULL) { + rl= MEM_callocT(sizeof(RenderLayer), "new render layer"); + BLI_addtail(&rr->layers, rl); + + rl->rectf= MEM_mapallocT(rectx*recty*sizeof(float)*4, "prev/env float rgba"); + + /* note, this has to be in sync with scene.c */ + rl->lay= (1<<20) -1; + rl->layflag= 0x7FFF; /* solid ztra halo strand */ + rl->passflag= SCE_PASS_COMBINED; + + re->r.actlay= 0; + } + + /* border render; calculate offset for use in compositor. compo is centralized coords */ + rr->xof= re->disprect.xmin + (re->disprect.xmax - re->disprect.xmin)/2 - re->winx/2; + rr->yof= re->disprect.ymin + (re->disprect.ymax - re->disprect.ymin)/2 - re->winy/2; + + return rr; +} + +static int render_scene_needs_vector(Render *re) +{ + if(re->r.scemode & R_DOCOMP) { + SceneRenderLayer *srl; + + for(srl= re->scene->r.layers.first; srl; srl= srl->next) + if(srl->passflag & SCE_PASS_VECTOR) + return 1; + } + return 0; +} + +static void do_merge_tile(RenderResult *rr, RenderResult *rrpart, float *target, float *tile, int pixsize) +{ + int y, ofs, copylen, tilex, tiley; + + copylen= tilex= rrpart->rectx; + tiley= rrpart->recty; + + if(rrpart->crop) { /* filters add pixel extra */ + tile+= pixsize*(rrpart->crop + rrpart->crop*tilex); + + copylen= tilex - 2*rrpart->crop; + tiley -= 2*rrpart->crop; + + ofs= (rrpart->tilerect.ymin + rrpart->crop)*rr->rectx + (rrpart->tilerect.xmin+rrpart->crop); + target+= pixsize*ofs; + } + else { + ofs= (rrpart->tilerect.ymin*rr->rectx + rrpart->tilerect.xmin); + target+= pixsize*ofs; + } + + copylen *= sizeof(float)*pixsize; + tilex *= pixsize; + ofs= pixsize*rr->rectx; + + for(y=0; ylayers.first, rlp= rrpart->layers.first; rl && rlp; rl= rl->next, rlp= rlp->next) { + + /* combined */ + if(rl->rectf && rlp->rectf) + do_merge_tile(rr, rrpart, rl->rectf, rlp->rectf, 4); + + /* passes are allocated in sync */ + for(rpass= rl->passes.first, rpassp= rlp->passes.first; rpass && rpassp; rpass= rpass->next, rpassp= rpassp->next) { + do_merge_tile(rr, rrpart, rpass->rect, rpassp->rect, rpass->channels); + } + } +} + + +static void save_render_result_tile(Render *re, RenderPart *pa) +{ + RenderResult *rrpart= pa->result; + RenderLayer *rlp; + RenderPass *rpassp; + int offs, partx, party; + + BLI_lock_thread(LOCK_CUSTOM1); + + for(rlp= rrpart->layers.first; rlp; rlp= rlp->next) { + + if(rrpart->crop) { /* filters add pixel extra */ + offs= (rrpart->crop + rrpart->crop*rrpart->rectx); + } + else { + offs= 0; + } + + /* combined */ + if(rlp->rectf) { + int a, xstride= 4; + for(a=0; aresult->exrhandle, rlp->name, get_pass_name(SCE_PASS_COMBINED, a), + xstride, xstride*pa->rectx, rlp->rectf+a + xstride*offs); + } + + /* passes are allocated in sync */ + for(rpassp= rlp->passes.first; rpassp; rpassp= rpassp->next) { + int a, xstride= rpassp->channels; + for(a=0; aresult->exrhandle, rlp->name, get_pass_name(rpassp->passtype, a), + xstride, xstride*pa->rectx, rpassp->rect+a + xstride*offs); + } + + } + + party= rrpart->tilerect.ymin + rrpart->crop; + partx= rrpart->tilerect.xmin + rrpart->crop; + IMB_exrtile_write_channels(re->result->exrhandle, partx, party); + + BLI_unlock_thread(LOCK_CUSTOM1); + +} + +static void read_render_result(Render *re) +{ + RenderLayer *rl; + RenderPass *rpass; + void *exrhandle= IMB_exr_get_handle(); + int rectx, recty; + char str[FILE_MAXDIR+FILE_MAXFILE]; + + free_render_result(re->result); + re->result= new_render_result(re, &re->disprect, 0, RR_USEMEM); + + render_unique_exr_name(re, str); + if(IMB_exr_begin_read(exrhandle, str, &rectx, &recty)==0) { + printf("cannot read render result\n"); + return; + } + + if(rectx!=re->result->rectx || recty!=re->result->recty) { + printf("error in reading render result\n"); + } + else { + for(rl= re->result->layers.first; rl; rl= rl->next) { + + /* combined */ + if(rl->rectf) { + int a, xstride= 4; + for(a=0; aname, get_pass_name(SCE_PASS_COMBINED, a), + xstride, xstride*rectx, rl->rectf+a); + } + + /* passes are allocated in sync */ + for(rpass= rl->passes.first; rpass; rpass= rpass->next) { + int a, xstride= rpass->channels; + for(a=0; aname, get_pass_name(rpass->passtype, a), + xstride, xstride*rectx, rpass->rect+a); + } + + } + IMB_exr_read_channels(exrhandle); + } + + IMB_exr_close(exrhandle); +} + +/* *************************************************** */ + +Render *RE_GetRender(const char *name) +{ + Render *re; + + /* search for existing renders */ + for(re= RenderList.first; re; re= re->next) { + if(strncmp(re->name, name, RE_MAXNAME)==0) { + break; + } + } + return re; +} + +/* if you want to know exactly what has been done */ +RenderResult *RE_GetResult(Render *re) +{ + if(re) + return re->result; + return NULL; +} + +RenderLayer *render_get_active_layer(Render *re, RenderResult *rr) +{ + RenderLayer *rl= BLI_findlink(&rr->layers, re->r.actlay); + + if(rl) + return rl; + else + return rr->layers.first; +} + + +/* fill provided result struct with what's currently active or done */ +void RE_GetResultImage(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(rl) { + if(rr->rectf==NULL) + rr->rectf= rl->rectf; + if(rr->rectz==NULL) + rr->rectz= RE_RenderLayerGetPass(rl, SCE_PASS_Z); + } + } +} + +#define FTOCHAR(val) val<=0.0f?0: (val>=1.0f?255: (char)(255.0f*val)) +/* caller is responsible for allocating rect in correct size! */ +void RE_ResultGet32(Render *re, unsigned int *rect) +{ + RenderResult rres; + + RE_GetResultImage(re, &rres); + if(rres.rect32) + memcpy(rect, rres.rect32, sizeof(int)*rres.rectx*rres.recty); + else if(rres.rectf) { + float *fp= rres.rectf; + int tot= rres.rectx*rres.recty; + char *cp= (char *)rect; + + for(;tot>0; tot--, cp+=4, fp+=4) { + cp[0] = FTOCHAR(fp[0]); + cp[1] = FTOCHAR(fp[1]); + cp[2] = FTOCHAR(fp[2]); + cp[3] = FTOCHAR(fp[3]); + } + } + else + /* else fill with black */ + memset(rect, 0, sizeof(int)*re->rectx*re->recty); +} + + +RenderStats *RE_GetStats(Render *re) +{ + return &re->i; +} + +Render *RE_NewRender(const char *name) +{ + Render *re; + + /* only one render per name exists */ + re= RE_GetRender(name); + if(re==NULL) { + + /* new render data struct */ + re= MEM_callocT(sizeof(Render), "new render"); + BLI_addtail(&RenderList, re); + strncpy(re->name, name, RE_MAXNAME); + } + + /* set default empty callbacks */ + re->display_init= result_nothing; + re->display_clear= result_nothing; + re->display_draw= result_rcti_nothing; + re->timecursor= int_nothing; + re->test_break= void_nothing; + re->test_return= void_nothing; + re->error= print_error; + if(G.background) + re->stats_draw= stats_background; + else + re->stats_draw= stats_nothing; + + /* init some variables */ + re->ycor= 1.0f; + re->tie.kdtree = NULL; + + return re; +} + +/* only call this while you know it will remove the link too */ +void RE_FreeRender(Render *re) +{ + + free_renderdata_tables(re); + free_sample_tables(re); + + free_render_result(re->result); + free_render_result(re->pushedresult); + + BLI_remlink(&RenderList, re); + MEM_freeT(re); +} + +/* exit blender */ +void RE_FreeAllRender(void) +{ + while(RenderList.first) { + RE_FreeRender(RenderList.first); + } +} + +/* ********* initialize state ******** */ + + +/* what doesn't change during entire render sequence */ +/* disprect is optional, if NULL it assumes full window render */ +void RE_InitState(Render *re, RenderData *rd, int winx, int winy, rcti *disprect) +{ + re->ok= TRUE; /* maybe flag */ + + re->i.starttime= PIL_check_seconds_timer(); + re->r= *rd; /* hardcopy */ + + re->winx= winx; + re->winy= winy; + if(disprect) { + re->disprect= *disprect; + re->rectx= disprect->xmax-disprect->xmin; + re->recty= disprect->ymax-disprect->ymin; + } + else { + re->disprect.xmin= re->disprect.ymin= 0; + re->disprect.xmax= winx; + re->disprect.ymax= winy; + re->rectx= winx; + re->recty= winy; + } + + if(re->rectx < 2 || re->recty < 2) { + re->error("Image too small"); + re->ok= 0; + } + else { + /* check state variables, osa? */ + if(re->r.mode & (R_OSA|R_MBLUR)) { + re->osa= re->r.osa; + if(re->osa>16) re->osa= 16; + } + else re->osa= 0; + + /* always call, checks for gamma, gamma tables and jitter too */ + make_sample_tables(re); + + /* make empty render result, so display callbacks can initialize */ + free_render_result(re->result); + re->result= MEM_callocN(sizeof(RenderResult), "new render result"); + re->result->rectx= re->rectx; + re->result->recty= re->recty; + } +} + +void RE_SetDispRect (struct Render *re, rcti *disprect) +{ + re->disprect= *disprect; + re->rectx= disprect->xmax-disprect->xmin; + re->recty= disprect->ymax-disprect->ymin; + + /* initialize render result */ + free_render_result(re->result); + re->result= new_render_result(re, &re->disprect, 0, RR_USEMEM); +} + +void RE_SetWindow(Render *re, rctf *viewplane, float clipsta, float clipend) +{ + /* re->ok flag? */ + + re->viewplane= *viewplane; + re->clipsta= clipsta; + re->clipend= clipend; + + i_window(re->viewplane.xmin, re->viewplane.xmax, re->viewplane.ymin, re->viewplane.ymax, re->clipsta, re->clipend, re->winmat); +} + +void RE_SetOrtho(Render *re, rctf *viewplane, float clipsta, float clipend) +{ + /* re->ok flag? */ + + re->viewplane= *viewplane; + re->clipsta= clipsta; + re->clipend= clipend; + re->r.mode |= R_ORTHO; + + i_ortho(re->viewplane.xmin, re->viewplane.xmax, re->viewplane.ymin, re->viewplane.ymax, re->clipsta, re->clipend, re->winmat); +} + +void RE_SetView(Render *re, float mat[][4]) +{ + /* re->ok flag? */ + Mat4CpyMat4(re->viewmat, mat); + Mat4Invert(re->viewinv, re->viewmat); +} + +/* image and movie output has to move to either imbuf or kernel */ +void RE_display_init_cb(Render *re, void (*f)(RenderResult *rr)) +{ + re->display_init= f; +} +void RE_display_clear_cb(Render *re, void (*f)(RenderResult *rr)) +{ + re->display_clear= f; +} +void RE_display_draw_cb(Render *re, void (*f)(RenderResult *rr, volatile rcti *rect)) +{ + re->display_draw= f; +} + +void RE_stats_draw_cb(Render *re, void (*f)(RenderStats *rs)) +{ + re->stats_draw= f; +} +void RE_timecursor_cb(Render *re, void (*f)(int)) +{ + re->timecursor= f; +} + +void RE_test_break_cb(Render *re, int (*f)(void)) +{ + re->test_break= f; +} +void RE_test_return_cb(Render *re, int (*f)(void)) +{ + re->test_return= f; +} +void RE_error_cb(Render *re, void (*f)(char *str)) +{ + re->error= f; +} + + +/* ********* add object data (later) ******** */ + +/* object is considered fully prepared on correct time etc */ +/* includes lights */ +void RE_AddObject(Render *re, Object *ob) +{ + +} + +/* *************************************** */ + +static void *do_part_thread(void *pa_v) +{ + RenderPart *pa= pa_v; + + /* need to return nicely all parts on esc */ + if(R.test_break()==0) { + + pa->result= new_render_result(&R, &pa->disprect, pa->crop, RR_USEMEM); + + if(R.osa) + zbufshadeDA_tile(pa); + else + zbufshade_tile(pa); + + /* merge too on break! */ + if(R.result->exrhandle) + save_render_result_tile(&R, pa); + else + merge_render_result(R.result, pa->result); + } + + pa->ready= 1; + + return NULL; +} + +/* returns with render result filled, not threaded, used for preview now only */ +static void render_tile_processor(Render *re, int firsttile) +{ + RenderPart *pa; + + if(re->test_break()) + return; + + /* 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) free_render_result(re->result); + re->result= new_render_result(re, &re->disprect, 0, RR_USEMEM); + } + + re->i.lastframetime= PIL_check_seconds_timer()- re->i.starttime; + re->stats_draw(&re->i); + re->i.starttime= PIL_check_seconds_timer(); + + if(re->result==NULL) + return; + + initparts(re); + + /* assuming no new data gets added to dbase... */ + R= *re; + + for(pa= re->parts.first; pa; pa= pa->next) { + if(firsttile) { + re->i.partsdone++; /* was reset in initparts */ + firsttile--; + } + else { + do_part_thread(pa); + + if(pa->result) { + if(!re->test_break()) { + re->display_draw(pa->result, NULL); + + re->i.partsdone++; + } + free_render_result(pa->result); + pa->result= NULL; + } + if(re->test_break()) + break; + } + } + + re->i.lastframetime= PIL_check_seconds_timer()- re->i.starttime; + re->stats_draw(&re->i); + freeparts(re); +} + +/* calculus for how much 1 pixel rendered should rotate the 3d geometry */ +/* is not that simple, needs to be corrected for errors of larger viewplane sizes */ +/* called in initrender.c, initparts() and convertblender.c, for speedvectors */ +float panorama_pixel_rot(Render *re) +{ + float psize, phi, xfac; + + /* size of 1 pixel mapped to viewplane coords */ + psize= (re->viewplane.xmax-re->viewplane.xmin)/(float)re->winx; + /* angle of a pixel */ + phi= atan(psize/re->clipsta); + + /* correction factor for viewplane shifting, first calculate how much the viewplane angle is */ + xfac= ((re->viewplane.xmax-re->viewplane.xmin))/(float)re->xparts; + xfac= atan(0.5f*xfac/re->clipsta); + /* and how much the same viewplane angle is wrapped */ + psize= 0.5f*phi*((float)re->partx); + + /* the ratio applied to final per-pixel angle */ + phi*= xfac/psize; + + return phi; +} + +/* call when all parts stopped rendering, to find the next Y slice */ +/* if slice found, it rotates the dbase */ +static RenderPart *find_next_pano_slice(Render *re, int *minx, rctf *viewplane) +{ + RenderPart *pa, *best= NULL; + + *minx= re->winx; + + /* most left part of the non-rendering parts */ + for(pa= re->parts.first; pa; pa= pa->next) { + if(pa->ready==0 && pa->nr==0) { + if(pa->disprect.xmin < *minx) { + best= pa; + *minx= pa->disprect.xmin; + } + } + } + + if(best) { + float phi= panorama_pixel_rot(re); + + R.panodxp= (re->winx - (best->disprect.xmin + best->disprect.xmax) )/2; + R.panodxv= ((viewplane->xmax-viewplane->xmin)*R.panodxp)/(float)R.winx; + + /* shift viewplane */ + R.viewplane.xmin = viewplane->xmin + R.panodxv; + R.viewplane.xmax = viewplane->xmax + R.panodxv; + RE_SetWindow(re, &R.viewplane, R.clipsta, R.clipend); + Mat4CpyMat4(R.winmat, re->winmat); + + /* rotate database according to part coordinates */ + project_renderdata(re, projectverto, 1, -R.panodxp*phi); + R.panosi= sin(R.panodxp*phi); + R.panoco= cos(R.panodxp*phi); + } + return best; +} + +static RenderPart *find_next_part(Render *re, int minx) +{ + RenderPart *pa, *best= NULL; + int centx=re->winx/2, centy=re->winy/2, tot=1; + int mindist, distx, disty; + + /* find center of rendered parts, image center counts for 1 too */ + for(pa= re->parts.first; pa; pa= pa->next) { + if(pa->ready) { + centx+= (pa->disprect.xmin+pa->disprect.xmax)/2; + centy+= (pa->disprect.ymin+pa->disprect.ymax)/2; + tot++; + } + } + centx/=tot; + centy/=tot; + + /* closest of the non-rendering parts */ + mindist= re->winx*re->winy; + for(pa= re->parts.first; pa; pa= pa->next) { + if(pa->ready==0 && pa->nr==0) { + distx= centx - (pa->disprect.xmin+pa->disprect.xmax)/2; + disty= centy - (pa->disprect.ymin+pa->disprect.ymax)/2; + distx= (int)sqrt(distx*distx + disty*disty); + if(distxr.mode & R_PANORAMA) { + if(pa->disprect.xmin==minx) { + best= pa; + mindist= distx; + } + } + else { + best= pa; + mindist= distx; + } + } + } + } + return best; +} + +static void print_part_stats(Render *re, RenderPart *pa) +{ + char str[64]; + + sprintf(str, "Part %d-%d", pa->nr, re->i.totpart); + re->i.infostr= str; + re->stats_draw(&re->i); + re->i.infostr= NULL; +} + +static void threaded_tile_processor(Render *re) +{ + ListBase threads; + RenderPart *pa, *nextpa; + RenderResult *rr; + rctf viewplane= re->viewplane; + int maxthreads, rendering=1, counter= 1, drawtimer=0, hasdrawn, minx=0; + + /* first step; the entire render result, or prepare exr buffer saving */ + free_render_result(re->result); + rr= re->result= new_render_result(re, &re->disprect, 0, re->r.scemode & R_EXR_TILE_FILE); + + if(rr==NULL) + return; + if(re->test_break()) + return; + + initparts(re); + + if(rr->exrhandle) { + char str[FILE_MAXDIR+FILE_MAXFILE]; + render_unique_exr_name(re, str); + printf("write exr tmp file, %dx%d, %s\n", rr->rectx, rr->recty, str); + + IMB_exrtile_begin_write(rr->exrhandle, str, rr->rectx, rr->recty, rr->rectx/re->xparts, rr->recty/re->yparts); + } + + if(re->r.mode & R_THREADS) maxthreads= 2; + else maxthreads= 1; + + BLI_init_threads(&threads, do_part_thread, maxthreads); + + /* assuming no new data gets added to dbase... */ + R= *re; + + /* set threadsafe break */ + R.test_break= thread_break; + + /* timer loop demands to sleep when no parts are left, so we enter loop with a part */ + if(re->r.mode & R_PANORAMA) + nextpa= find_next_pano_slice(re, &minx, &viewplane); + else + nextpa= find_next_part(re, 0); + + while(rendering) { + + if(re->test_break()) + PIL_sleep_ms(50); + else if(nextpa && BLI_available_threads(&threads)) { + drawtimer= 0; + nextpa->nr= counter++; /* for nicest part, and for stats */ + nextpa->thread= BLI_available_thread_index(&threads); /* sample index */ + BLI_insert_thread(&threads, nextpa); + + nextpa= find_next_part(re, minx); + } + else if(re->r.mode & R_PANORAMA) { + if(nextpa==NULL && BLI_available_threads(&threads)==maxthreads) + nextpa= find_next_pano_slice(re, &minx, &viewplane); + else { + PIL_sleep_ms(50); + drawtimer++; + } + } + else { + PIL_sleep_ms(50); + drawtimer++; + } + + /* check for ready ones to display, and if we need to continue */ + rendering= 0; + hasdrawn= 0; + for(pa= re->parts.first; pa; pa= pa->next) { + if(pa->ready) { + if(pa->result) { + BLI_remove_thread(&threads, pa); + + re->display_draw(pa->result, NULL); + print_part_stats(re, pa); + + free_render_result(pa->result); + pa->result= NULL; + re->i.partsdone++; + hasdrawn= 1; + } + } + else { + rendering= 1; + if(pa->nr && pa->result && drawtimer>20) { + re->display_draw(pa->result, &pa->result->renrect); + hasdrawn= 1; + } + } + } + if(hasdrawn) + drawtimer= 0; + + /* on break, wait for all slots to get freed */ + if( (g_break=re->test_break()) && BLI_available_threads(&threads)==maxthreads) + rendering= 0; + + } + + if(rr->exrhandle) { + IMB_exr_close(rr->exrhandle); + rr->exrhandle= NULL; + if(!re->test_break()) + read_render_result(re); + } + + /* unset threadsafety */ + g_break= 0; + + BLI_end_threads(&threads); + freeparts(re); +} + +/* currently only called by preview renders and envmap */ +void RE_TileProcessor(Render *re, int firsttile) +{ + /* the partsdone variable has to be reset to firsttile, to survive esc before it was set to zero */ + + re->i.partsdone= firsttile; + + //if(re->r.mode & R_THREADS) + // threaded_tile_processor(re); + //else + render_tile_processor(re, firsttile); + +} + + +/* ************ This part uses API, for rendering Blender scenes ********** */ + +static void do_render_3d(Render *re) +{ + +// re->cfra= cfra; /* <- unused! */ + + /* make render verts/faces/halos/lamps */ + if(render_scene_needs_vector(re)) + RE_Database_FromScene_Vectors(re, re->scene); + else + RE_Database_FromScene(re, re->scene, 1); + + threaded_tile_processor(re); + + /* do left-over 3d post effects (flares) */ + if(re->flag & R_HALO) + if(!re->test_break()) + add_halo_flare(re); + + + /* free all render verts etc */ + RE_Database_Free(re); +} + +/* called by blur loop, accumulate renderlayers */ +static void addblur_rect(RenderResult *rr, float *rectf, float *rectf1, float blurfac, int channels) +{ + float mfac= 1.0f - blurfac; + int a, b, stride= channels*rr->rectx; + int len= stride*sizeof(float); + + for(a=0; arecty; a++) { + if(blurfac==1.0f) { + memcpy(rectf, rectf1, len); + } + else { + float *rf= rectf, *rf1= rectf1; + + for( b= rr->rectx*channels; b>0; b--, rf++, rf1++) { + rf[0]= mfac*rf[0] + blurfac*rf1[0]; + } + } + rectf+= stride; + rectf1+= stride; + } +} + +/* called by blur loop, accumulate renderlayers */ +static void merge_renderresult_blur(RenderResult *rr, RenderResult *brr, float blurfac) +{ + RenderLayer *rl, *rl1; + RenderPass *rpass, *rpass1; + + rl1= brr->layers.first; + for(rl= rr->layers.first; rl && rl1; rl= rl->next, rl1= rl1->next) { + + /* combined */ + if(rl->rectf && rl1->rectf) + addblur_rect(rr, rl->rectf, rl1->rectf, blurfac, 4); + + /* passes are allocated in sync */ + rpass1= rl1->passes.first; + for(rpass= rl->passes.first; rpass && rpass1; rpass= rpass->next, rpass1= rpass1->next) { + addblur_rect(rr, rpass->rect, rpass1->rect, blurfac, rpass->channels); + } + } +} + +/* main blur loop, can be called by fields too */ +static void do_render_blur_3d(Render *re) +{ + RenderResult *rres; + float blurfac; + int blur= re->r.osa; + + /* create accumulation render result */ + rres= new_render_result(re, &re->disprect, 0, RR_USEMEM); + + /* do the blur steps */ + while(blur--) { + set_mblur_offs( re->r.blurfac*((float)(re->r.osa-blur))/(float)re->r.osa ); + + do_render_3d(re); + + blurfac= 1.0f/(float)(re->r.osa-blur); + + merge_renderresult_blur(rres, re->result, blurfac); + if(re->test_break()) break; + } + + /* swap results */ + free_render_result(re->result); + re->result= rres; + + set_mblur_offs(0.0f); + + /* weak... the display callback wants an active renderlayer pointer... */ + re->result->renlay= render_get_active_layer(re, re->result); + re->display_draw(re->result, NULL); +} + + +/* function assumes rectf1 and rectf2 to be half size of rectf */ +static void interleave_rect(RenderResult *rr, float *rectf, float *rectf1, float *rectf2, int channels) +{ + int a, stride= channels*rr->rectx; + int len= stride*sizeof(float); + + for(a=0; arecty; a+=2) { + memcpy(rectf, rectf1, len); + rectf+= stride; + rectf1+= stride; + memcpy(rectf, rectf2, len); + rectf+= stride; + rectf2+= stride; + } +} + +/* merge render results of 2 fields */ +static void merge_renderresult_fields(RenderResult *rr, RenderResult *rr1, RenderResult *rr2) +{ + RenderLayer *rl, *rl1, *rl2; + RenderPass *rpass, *rpass1, *rpass2; + + rl1= rr1->layers.first; + rl2= rr2->layers.first; + for(rl= rr->layers.first; rl && rl1 && rl2; rl= rl->next, rl1= rl1->next, rl2= rl2->next) { + + /* combined */ + if(rl->rectf && rl1->rectf && rl2->rectf) + interleave_rect(rr, rl->rectf, rl1->rectf, rl2->rectf, 4); + + /* passes are allocated in sync */ + rpass1= rl1->passes.first; + rpass2= rl2->passes.first; + for(rpass= rl->passes.first; rpass && rpass1 && rpass2; rpass= rpass->next, rpass1= rpass1->next, rpass2= rpass2->next) { + interleave_rect(rr, rpass->rect, rpass1->rect, rpass2->rect, rpass->channels); + } + } +} + + +/* interleaves 2 frames */ +static void do_render_fields_3d(Render *re) +{ + RenderResult *rr1, *rr2= NULL; + + /* no render result was created, we can safely halve render y */ + re->winy /= 2; + re->recty /= 2; + re->disprect.ymin /= 2; + re->disprect.ymax /= 2; + + /* first field, we have to call camera routine for correct aspect and subpixel offset */ + RE_SetCamera(re, re->scene->camera); + if(re->r.mode & R_MBLUR) + do_render_blur_3d(re); + else + do_render_3d(re); + rr1= re->result; + re->result= NULL; + + /* second field */ + if(!re->test_break()) { + re->flag |= R_SEC_FIELD; + if((re->r.mode & R_FIELDSTILL)==0) + set_field_offs(0.5f); + RE_SetCamera(re, re->scene->camera); + if(re->r.mode & R_MBLUR) + do_render_blur_3d(re); + else + do_render_3d(re); + re->flag &= ~R_SEC_FIELD; + set_field_offs(0.0f); + + rr2= re->result; + } + + /* allocate original height new buffers */ + re->winy *= 2; + re->recty *= 2; + re->disprect.ymin *= 2; + re->disprect.ymax *= 2; + re->result= new_render_result(re, &re->disprect, 0, RR_USEMEM); + + if(rr2) { + if(re->r.mode & R_ODDFIELD) + merge_renderresult_fields(re->result, rr2, rr1); + else + merge_renderresult_fields(re->result, rr1, rr2); + + free_render_result(rr2); + } + free_render_result(rr1); + + /* weak... the display callback wants an active renderlayer pointer... */ + re->result->renlay= render_get_active_layer(re, re->result); + re->display_draw(re->result, NULL); +} + +static void load_backbuffer(Render *re) +{ + if(re->r.alphamode == R_ADDSKY) { + Image *bima; + char name[256]; + + strcpy(name, re->r.backbuf); + BLI_convertstringcode(name, G.sce, re->r.cfra); + + if(re->backbuf) { + re->backbuf->id.us--; + bima= re->backbuf; + } + else bima= NULL; + + re->backbuf= add_image(name); + + if(bima && bima->id.us<1) { + free_image_buffers(bima); + } + + if(re->backbuf && re->backbuf->ibuf==NULL) { + re->backbuf->ibuf= IMB_loadiffname(re->backbuf->name, IB_rect); + if(re->backbuf->ibuf==NULL) re->backbuf->ok= 0; + else re->backbuf->ok= 1; + } + if(re->backbuf==NULL || re->backbuf->ok==0) { + // error() doesnt work with render window open + //error("No backbuf there!"); + printf("Error: No backbuf %s\n", name); + } + } +} + +/* main render routine, no compositing */ +static void do_render_fields_blur_3d(Render *re) +{ + /* only check for camera here */ + if(re->scene->camera==NULL) { + re->error("No camera"); + return; + } + + /* backbuffer initialize */ + if(re->r.bufflag & 1) + load_backbuffer(re); + + /* now use renderdata and camera to set viewplane */ + RE_SetCamera(re, re->scene->camera); + + if(re->r.mode & R_FIELDS) + do_render_fields_3d(re); + else if(re->r.mode & R_MBLUR) + do_render_blur_3d(re); + else + do_render_3d(re); + + /* when border render, check if we have to insert it in black */ + if(re->result) { + if(re->r.mode & R_BORDER) { + if((re->r.mode & R_CROP)==0) { + RenderResult *rres; + + /* sub-rect for merge call later on */ + re->result->tilerect= re->disprect; + + /* this copying sequence could become function? */ + re->disprect.xmin= re->disprect.ymin= 0; + re->disprect.xmax= re->winx; + re->disprect.ymax= re->winy; + re->rectx= re->winx; + re->recty= re->winy; + + rres= new_render_result(re, &re->disprect, 0, RR_USEMEM); + + merge_render_result(rres, re->result); + free_render_result(re->result); + re->result= rres; + + re->display_init(re->result); + re->display_draw(re->result, NULL); + } + } + } +} + + +/* within context of current Render *re, render another scene. + it uses current render image size and disprect, but doesn't execute composite +*/ +static void render_scene(Render *re, Scene *sce, int cfra) +{ + Render *resc= RE_NewRender(sce->id.name); + + sce->r.cfra= cfra; + + /* initial setup */ + RE_InitState(resc, &sce->r, re->winx, re->winy, &re->disprect); + + /* this to enable this scene to create speed vectors */ + resc->r.scemode |= R_DOCOMP; + + /* still unsure entity this... */ + resc->scene= sce; + + /* ensure scene has depsgraph, base flags etc OK. Warning... also sets G.scene */ + set_scene_bg(sce); + + /* copy callbacks */ + resc->display_draw= re->display_draw; + resc->test_break= re->test_break; + resc->stats_draw= re->stats_draw; + + do_render_fields_blur_3d(resc); +} + +static void ntree_render_scenes(Render *re) +{ + bNode *node; + int cfra= re->scene->r.cfra; + + if(re->scene->nodetree==NULL) return; + + /* check for render-result nodes using other scenes, we tag them LIB_DOIT */ + for(node= re->scene->nodetree->nodes.first; node; node= node->next) { + if(node->type==CMP_NODE_R_RESULT) { + if(node->id) { + if(node->id != (ID *)re->scene) + node->id->flag |= LIB_DOIT; + else + node->id->flag &= ~LIB_DOIT; + } + } + } + + /* now foreach render-result node tagged we do a full render */ + /* results are stored in a way compisitor will find it */ + for(node= re->scene->nodetree->nodes.first; node; node= node->next) { + if(node->type==CMP_NODE_R_RESULT) { + if(node->id && node->id != (ID *)re->scene) { + if(node->id->flag & LIB_DOIT) { + render_scene(re, (Scene *)node->id, cfra); + node->id->flag &= ~LIB_DOIT; + } + } + } + } + + /* still the global... */ + if(G.scene!=re->scene) + set_scene_bg(re->scene); + +} + +/* helper call to detect if theres a composite with render-result node */ +static int composite_needs_render(Scene *sce) +{ + bNodeTree *ntree= sce->nodetree; + bNode *node; + + if(ntree==NULL) return 1; + if(sce->use_nodes==0) return 1; + if((sce->r.scemode & R_DOCOMP)==0) return 1; + + for(node= ntree->nodes.first; node; node= node->next) { + if(node->type==CMP_NODE_R_RESULT) + if(node->id==NULL || node->id!=&sce->id) + return 1; + } + return 0; +} + +/* bad call... need to think over proper method still */ +static void render_composit_stats(char *str) +{ + R.i.infostr= str; + R.stats_draw(&R.i); + R.i.infostr= NULL; +} + +/* returns fully composited render-result on given time step (in RenderData) */ +static void do_render_composite_fields_blur_3d(Render *re) +{ + bNodeTree *ntree= re->scene->nodetree; + + /* we set start time here, for main Blender loops */ + re->i.starttime= PIL_check_seconds_timer(); + + if(composite_needs_render(re->scene)) { + /* save memory... free all cached images */ + ntreeFreeCache(ntree); + + do_render_fields_blur_3d(re); + } + + /* swap render result */ + if(re->r.scemode & R_SINGLE_LAYER) + pop_render_result(re); + + if(!re->test_break() && ntree) { + ntreeCompositTagRender(ntree); + ntreeCompositTagAnimated(ntree); + + if(re->r.scemode & R_DOCOMP) { + /* checks if there are render-result nodes that need scene */ + if((re->r.scemode & R_SINGLE_LAYER)==0) + ntree_render_scenes(re); + + if(!re->test_break()) { + ntree->stats_draw= render_composit_stats; + ntree->test_break= re->test_break; + /* in case it was never initialized */ + R.stats_draw= re->stats_draw; + + ntreeCompositExecTree(ntree, &re->r, G.background==0); + ntree->stats_draw= NULL; + ntree->test_break= NULL; + } + } + } + + re->i.lastframetime= PIL_check_seconds_timer()- re->i.starttime; + re->stats_draw(&re->i); + + re->display_draw(re->result, NULL); +} + + +/* yafray: main yafray render/export call */ +static void yafrayRender(Render *re) +{ + free_render_result(re->result); + re->result= new_render_result(re, &re->disprect, 0, RR_USEMEM); + + // need this too, for aspect/ortho/etc info + RE_SetCamera(re, re->scene->camera); + + // switch must be done before prepareScene() + if (!re->r.YFexportxml) + YAF_switchFile(); + else + YAF_switchPlugin(); + + printf("Starting scene conversion.\n"); + RE_Database_FromScene(re, re->scene, 1); + printf("Scene conversion done.\n"); + + re->i.starttime = PIL_check_seconds_timer(); + + YAF_exportScene(re); + + re->i.lastframetime = PIL_check_seconds_timer()- re->i.starttime; + re->stats_draw(&re->i); + + RE_Database_Free(re); +} + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + +/* main loop: doing sequence + fields + blur + 3d render + compositing */ +static void do_render_all_options(Render *re) +{ + if(re->r.scemode & R_DOSEQ) { + if(!re->test_break()) + do_render_seq(re->result, re->r.cfra); + } + else { + if(re->r.renderer==R_YAFRAY) + yafrayRender(re); + else + do_render_composite_fields_blur_3d(re); + } +} + +static int is_rendering_allowed(Render *re) +{ + + /* forbidden combinations */ + if(re->r.mode & R_PANORAMA) { + if(re->r.mode & R_BORDER) { + re->error("No border supported for Panorama"); + return 0; + } + if(re->r.mode & R_ORTHO) { + re->error("No Ortho render possible for Panorama"); + return 0; + } + } + + if(re->r.mode & R_BORDER) { + if(re->r.border.xmax <= re->r.border.xmin || + re->r.border.ymax <= re->r.border.ymin) { + re->error("No border area selected."); + return 0; + } + if(re->r.scemode & R_EXR_TILE_FILE) { + re->error("Border render and Buffer-save not supported yet"); + return 0; + } + } + + if(re->r.scemode & R_DOCOMP) { + if(re->scene->use_nodes) { + bNodeTree *ntree= re->scene->nodetree; + bNode *node; + + if(ntree==NULL) { + re->error("No Nodetree in Scene"); + return 0; + } + + for(node= ntree->nodes.first; node; node= node->next) + if(node->type==CMP_NODE_COMPOSITE) + break; + + if(node==NULL) { + re->error("No Render Output Node in Scene"); + return 0; + } + } + } + + /* check valid camera, without camera render is OK (compo, seq) */ + if(re->scene->camera==NULL) + re->scene->camera= scene_find_camera(re->scene); + + return 1; +} + +/* evaluating scene options for general Blender render */ +static int render_initialize_from_scene(Render *re, Scene *scene) +{ + int winx, winy; + rcti disprect; + + /* r.xsch and r.ysch has the actual view window size + r.border is the clipping rect */ + + /* calculate actual render result and display size */ + winx= (scene->r.size*scene->r.xsch)/100; + winy= (scene->r.size*scene->r.ysch)/100; + + /* we always render smaller part, inserting it in larger image is compositor bizz, it uses disprect for it */ + if(scene->r.mode & R_BORDER) { + disprect.xmin= scene->r.border.xmin*winx; + disprect.xmax= scene->r.border.xmax*winx; + + disprect.ymin= scene->r.border.ymin*winy; + disprect.ymax= scene->r.border.ymax*winy; + } + else { + disprect.xmin= disprect.ymin= 0; + disprect.xmax= winx; + disprect.ymax= winy; + } + + if(scene->r.scemode & R_EXR_TILE_FILE) { + int partx= winx/scene->r.xparts, party= winy/scene->r.yparts; + + /* stupid exr tiles dont like different sizes */ + if(winx != partx*scene->r.xparts || winy != party*scene->r.yparts) { + re->error("Sorry... exr tile saving only allowed with equally sized parts"); + return 0; + } + if((scene->r.mode & R_FIELDS) && (party & 1)) { + re->error("Sorry... exr tile saving only allowed with equally sized parts"); + return 0; + } + } + + if(scene->r.scemode & R_SINGLE_LAYER) + push_render_result(re); + + RE_InitState(re, &scene->r, winx, winy, &disprect); + + re->scene= scene; + if(!is_rendering_allowed(re)) + return 0; + + re->display_init(re->result); + re->display_clear(re->result); + + return 1; +} + +/* general Blender frame render call */ +void RE_BlenderFrame(Render *re, Scene *scene, int frame) +{ + /* ugly global still... is to prevent renderwin events and signal subsurfs etc to make full resol */ + /* is also set by caller renderwin.c */ + G.rendering= 1; + + scene->r.cfra= frame; + + if(render_initialize_from_scene(re, scene)) { + do_render_all_options(re); + } + + /* UGLY WARNING */ + G.rendering= 0; +} + +static void do_write_image_or_movie(Render *re, Scene *scene, bMovieHandle *mh) +{ + char name[FILE_MAXDIR+FILE_MAXFILE]; + RenderResult rres; + + RE_GetResultImage(re, &rres); + + /* write movie or image */ + if(BKE_imtype_is_movie(scene->r.imtype)) { + int dofree = 0; + /* note; the way it gets 32 bits rects is weak... */ + if(rres.rect32==NULL) { + rres.rect32= MEM_mapallocT(sizeof(int)*rres.rectx*rres.recty, "temp 32 bits rect"); + dofree = 1; + } + RE_ResultGet32(re, rres.rect32); + mh->append_movie(scene->r.cfra, rres.rect32, rres.rectx, rres.recty); + if(dofree) { + MEM_freeT(rres.rect32); + } + printf("Append frame %d", scene->r.cfra); + } else { + ImBuf *ibuf= IMB_allocImBuf(rres.rectx, rres.recty, scene->r.planes, 0, 0); + int ok; + + BKE_makepicstring(name, (scene->r.cfra)); + + /* if not exists, BKE_write_ibuf makes one */ + ibuf->rect= rres.rect32; + ibuf->rect_float= rres.rectf; + ibuf->zbuf_float= rres.rectz; + + /* float factor for random dither, imbuf takes care of it */ + ibuf->dither= scene->r.dither_intensity; + + ok= BKE_write_ibuf(ibuf, name, scene->r.imtype, scene->r.subimtype, scene->r.quality); + + if(ok==0) { + printf("Render error: cannot save %s\n", name); + G.afbreek=1; + return; + } + else printf("Saved: %s", name); + + /* optional preview images for exr */ + if(ok && scene->r.imtype==R_OPENEXR && (scene->r.subimtype & R_PREVIEW_JPG)) { + if(BLI_testextensie(name, ".exr")) + name[strlen(name)-4]= 0; + BKE_add_image_extension(name, R_JPEG90); + ibuf->depth= 24; + BKE_write_ibuf(ibuf, name, R_JPEG90, scene->r.subimtype, scene->r.quality); + printf("\nSaved: %s", name); + } + + /* imbuf knows which rects are not part of ibuf */ + IMB_freeImBuf(ibuf); + } + + BLI_timestr(re->i.lastframetime, name); + printf(" Time: %s\n", name); + fflush(stdout); /* needed for renderd !! (not anymore... (ton)) */ +} + +/* saves images to disk */ +void RE_BlenderAnim(Render *re, Scene *scene, int sfra, int efra) +{ + bMovieHandle *mh= BKE_get_movie_handle(scene->r.imtype); + int cfrao= scene->r.cfra; + + /* on each frame initialize, this for py scripts that define renderdata settings */ + if(!render_initialize_from_scene(re, scene)) + return; + + /* ugly global still... is to prevent renderwin events and signal subsurfs etc to make full resol */ + /* is also set by caller renderwin.c */ + G.rendering= 1; + + if(BKE_imtype_is_movie(scene->r.imtype)) + mh->start_movie(&re->r, re->rectx, re->recty); + + if (mh->get_next_frame) { + while (!(G.afbreek == 1)) { + int nf = mh->get_next_frame(); + if (nf >= 0 && nf >= scene->r.sfra && nf <= scene->r.efra) { + scene->r.cfra = nf; + + do_render_all_options(re); + + if(re->test_break() == 0) { + do_write_image_or_movie(re, scene, mh); + } + } + } + } else { + for(scene->r.cfra= sfra; + scene->r.cfra<=efra; scene->r.cfra++) { + re->r.cfra= scene->r.cfra; /* weak.... */ + + do_render_all_options(re); + + if(re->test_break() == 0) { + do_write_image_or_movie(re, scene, mh); + } + + if(G.afbreek==1) break; + } + } + + /* end movie */ + if(BKE_imtype_is_movie(scene->r.imtype)) + mh->end_movie(); + + scene->r.cfra= cfrao; + + /* UGLY WARNING */ + G.rendering= 0; +} + +/* note; repeated win/disprect calc... solve that nicer, also in compo */ + +void RE_ReadRenderResult(Scene *scene, Scene *scenode) +{ + Render *re; + int winx, winy; + rcti disprect; + + /* calculate actual render result and display size */ + winx= (scene->r.size*scene->r.xsch)/100; + winy= (scene->r.size*scene->r.ysch)/100; + + /* only in movie case we render smaller part */ + if(scene->r.mode & R_BORDER) { + disprect.xmin= scene->r.border.xmin*winx; + disprect.xmax= scene->r.border.xmax*winx; + + disprect.ymin= scene->r.border.ymin*winy; + disprect.ymax= scene->r.border.ymax*winy; + } + else { + disprect.xmin= disprect.ymin= 0; + disprect.xmax= winx; + disprect.ymax= winy; + } + + if(scenode) + scene= scenode; + + re= RE_NewRender(scene->id.name); + RE_InitState(re, &scene->r, winx, winy, &disprect); + re->scene= scene; + + read_render_result(re); +} diff -r -u -N -x linux2-config.py -x CVS blender-cvs/source/blender/render/intern/source/ray.c blender/source/blender/render/intern/source/ray.c --- blender-cvs/source/blender/render/intern/source/ray.c 2006-06-16 15:27:12.000000000 -0400 +++ blender/source/blender/render/intern/source/ray.c 2006-06-16 17:53:35.000000000 -0400 @@ -653,6 +653,107 @@ re->stats_draw(&re->i); } +void freekdtree(Render *re) +{ + + printf("Freeing kdtree\n"); + if (re->tie.kdtree) { + printf(" actually freeing\n"); + tie_free(&re->tie); + re->tie.kdtree = NULL; /* HACK HACK BUG SCREAM */ + } + fflush(stdout); +} + +/* The new kdtree method; first implementation is Twingy's code from BRL-CAD. + * Later I'll try my own tree which stores hyperrectangles rather than + * splitting planes and is a bit more space efficient. */ +void makekdtree(Render *re) +{ + tie_t *tie = &re->tie; + VlakRen *vlr = NULL; + //double lasttime= PIL_check_seconds_timer(); + int triangle_count = 0; + int v; + + printf("Making kdtree\n"); + if (re->tie.kdtree) { + printf(" but it already exists. bailing for now rather than reconstructing\n"); + fflush(stdout); + return; + } + fflush(stdout); + + /* Update the status bar */ + re->i.infostr = "Counting triangles"; + re->stats_draw(&re->i); + + /* Only for debug info */ + raycount = 0; + accepted = 0; + rejected = 0; + coherent_ray = 0; + + /* Count how many triangles we have */ + triangle_count = 0; + for(v=0;vtotvlak;v++) { + if((v & 255)==0) + vlr = re->blovl[v>>8]; + else + vlr++; + if((vlr->mat->mode & MA_TRACEBLE) && + ((vlr->mat->mode & MA_WIRE)==0)) { + triangle_count++; + if(vlr->v4) { + triangle_count++; + } + } + } + + re->i.infostr = "Initializing the kdtree"; + re->stats_draw(&re->i); + + /* Allocate enough space in the kdtree */ + tie_init(tie, triangle_count); + + /* Push each triangle into the triangle intersection engine */ + for(v=0;vtotvlak;v++) { + if((v & 255)==0) + vlr= re->blovl[v>>8]; + else + vlr++; + if((vlr->mat->mode & MA_TRACEBLE) && + ((vlr->mat->mode & MA_WIRE)==0)) { + float tlist[9]; + tlist[0] = vlr->v1->co[0]; + tlist[1] = vlr->v1->co[1]; + tlist[2] = vlr->v1->co[2]; + tlist[3] = vlr->v2->co[0]; + tlist[4] = vlr->v2->co[1]; + tlist[5] = vlr->v2->co[2]; + tlist[6] = vlr->v3->co[0]; + tlist[7] = vlr->v3->co[1]; + tlist[8] = vlr->v3->co[2]; + tie_push(tie, (TIE_3*)tlist, 1, vlr, 4); + if(vlr->v4) { + tlist[6] = vlr->v4->co[0]; + tlist[7] = vlr->v4->co[1]; + tlist[8] = vlr->v4->co[2]; + tie_push(tie, (TIE_3*)tlist, 1, vlr, 4); + } + } + } + + re->i.infostr = "Building the kdtree"; + re->stats_draw(&re->i); + + /* Now do the hard work an build the kdtree */ + tie_prep(tie); + + re->i.infostr = NULL; + re->stats_draw(&re->i); +} + /* ************ raytracer **************** */ /* only for self-intersecting test with current render face (where ray left) */ @@ -949,7 +1050,7 @@ VlakRen *vlr; short nr=0; OcVal *ov; - + if(is->mode==DDA_SHADOW) { vlr= no->v[0]; @@ -1142,7 +1243,8 @@ /* return 1: found valid intersection */ /* starts with is->vlrorig */ -static int d3dda(Isect *is) +/* replaced by d3dda kdtree traversal below */ +static int d3dda_old(Isect *is) { Node *no; OcVal ocval; @@ -1387,6 +1489,72 @@ return 0; } +/* callback for triangle intersection; invoked by tie_work when an intersection + * is found. */ +void *tie_hitfunc(tie_ray_t* ray, tie_id_t* id, tie_tri_t* tri, void *ptr) +{ + Isect *is = ptr; + + is->start[0] = ray->pos.v[0]; + is->start[1] = ray->pos.v[1]; + is->start[2] = ray->pos.v[2]; + + /* FIXME is->vlrisect? */ + /* FIXME is->* i.e. other members? */ + + /* we don't want to continue intersecting... other code will do that */ + /* clearly we should be smarter about this but for now this is just a + * proof of concept */ + return (void*) 1; +} + +/* return 1: found valid intersection */ +/* starts with is->vlrorig */ +/* now using the kdtree */ +/* mode DDA_MIRROR: get closest intersection */ +/* mode DDA_SHADOW: get any intersection */ +/* mode DDA_SHADOW_TRA: get any intersection */ +static int d3dda(Isect *is) +{ + /* init tie_ray struct */ + tie_ray_t ray; + tie_id_t id; + ray.pos.v[0] = is->start[0]; + ray.pos.v[1] = is->start[1]; + ray.pos.v[2] = is->start[2]; + ray.dir.v[0] = is->vec[0]; + ray.dir.v[1] = is->vec[1]; + ray.dir.v[2] = is->vec[2]; + ray.depth = 0; + ray.kdtree_depth = 0; + + /* do this before intersect calls */ + is->vlrcontr= NULL; /* to check shared edge */ + + /* only for shadow! we don't care how far or close; any intersection + * will do (WHY?) */ + if(is->mode==DDA_SHADOW) { + + /* check with last intersected shadow face */ + if(is->vlr_last!=NULL && is->vlr_last!=is->vlrorig) { + if(is->lay & is->vlr_last->lay) { + is->vlr= is->vlr_last; + VECSUB(is->vec, is->end, is->start); + if(intersection(is)) return 1; + } + } + } + + VlakRen* hit; + hit = (VlakRen*) tie_work(&R.tie, &ray, &id, tie_hitfunc, is); + + if (!hit) + /* no intersections found */ + is->vlr_last= NULL; + + return 0; +} + static void shade_ray(Isect *is, ShadeInput *shi, ShadeResult *shr) { diff -r -u -N -x linux2-config.py -x CVS blender-cvs/source/blender/render/intern/source/tie.c blender/source/blender/render/intern/source/tie.c --- blender-cvs/source/blender/render/intern/source/tie.c 1969-12-31 19:00:00.000000000 -0500 +++ blender/source/blender/render/intern/source/tie.c 2006-06-16 03:38:28.000000000 -0400 @@ -0,0 +1,459 @@ +/* T I E . C +* +* @file tie.c +* +* BRL-CAD +* +* Copyright (c) 2002-2006 United States Government as represented by +* the U.S. Army Research Laboratory. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public License +* as published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This library 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 +* Library General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this file; see the file named COPYING for more +* information. +* +* @brief support routines for shooting at triangles +* Comments - +* Triangle Intersection Engine +* +* The calling sequence is as follows: +* - tie_init() initialize the data structure +* - tie_push() add triangles to the universe to be raytraced +* - tie_prep() build the KDTREE for the triangles +* - tie_work() shoot some ray +* - tie_work() shoot some ray +* - tie_work() shoot some ray +* - tie_work() shoot some ray +* - tie_free() Free up all the memory +* +* Author - +* Justin L. Shumaker +* +* Source - +* The U. S. Army Research Laboratory +* Aberdeen Proving Ground, Maryland 21005-5068 USA +* +* $Id: tie.c,v 1.42 2006/01/18 06:46:11 brlcad Exp $ +*/ +/** @addtogroup libtie + * + * The calling sequence is as follows: + * @li tie_init() initialize the data structure + * @li tie_push() add triangles to the universe to be raytraced + * @li tie_prep() build the KDTREE for the triangles + * @li tie_work() shoot some ray + * @li tie_work() shoot some ray + * @li tie_work() shoot some ray + * @li tie_work() shoot some ray + * @li tie_work() shoot some ray + * @li tie_free() Free up all the memory + * @{ + */ + +#ifdef HAVE_CONFIG_H +# include "brlcad_config.h" +#endif + +#include "tie.h" +#include +#include +#include +#include +#include +#include +#include "struct.h" + +#include "MEM_guardedalloc.h" + +#ifdef TIE_SSE +# include +#endif + +/************************************************************* + **************** PRIVATE FUNCTIONS ************************** + *************************************************************/ + +static void tie_tri_prep(tie_t *tie) +{ + TIE_3 v1, v2, u, v; + int i, i1, i2; + tie_tri_t *tri; + + for (i = 0; i < tie->tri_num; i++) { + tri = &tie->tri_list[i]; + + v1 = tri->data[1]; + v2 = tri->data[2]; + + /* Compute Normal */ + math_vec_sub(u, tri->data[1], tri->data[0]); + math_vec_sub(v, tri->data[2], tri->data[0]); + math_vec_cross(tri->data[1], u, v); + math_vec_unitize(tri->data[1]); + + /* Compute i1 and i2 */ + u.v[0] = fabs(tri->data[1].v[0]); + u.v[1] = fabs(tri->data[1].v[1]); + u.v[2] = fabs(tri->data[1].v[2]); + + if (u.v[2] > u.v[1] && u.v[2] > u.v[0]) { + i1 = 0; + i2 = 1; + } else if (u.v[1] > u.v[2] && u.v[1] > u.v[0]) { + i1 = 0; + i2 = 2; + } else { + i1 = 1; + i2 = 2; + } + + /* compute u1, v2, u2, v2 */ + tri->data[2].v[1] = v1.v[i1] - tri->data[0].v[i1]; + tri->data[2].v[2] = v2.v[i1] - tri->data[0].v[i1]; + tri->v[0] = v1.v[i2] - tri->data[0].v[i2]; + tri->v[1] = v2.v[i2] - tri->data[0].v[i2]; + + if (i1 == 0 && i2 == 1) { + tri->v = (tfloat *)((intptr_t)(tri->v) + 2); + } else if (i1 == 0) { + tri->v = (tfloat *)((intptr_t)(tri->v) + 1); + } + + /* Compute DotVN */ + math_vec_mul_scalar(v1, tri->data[0], -1.0); + math_vec_dot(tri->data[2].v[0], v1, tri->data[1]); + } +} + + +/************************************************************* + **************** EXPORTED FUNCTIONS ************************* + *************************************************************/ + +/** + * Initialize a tie_t data structure + * + * This needs to be called before any other libtie data structures are called. + * + * @param tie pointer to a struct tie_t + * @return void + */ +void tie_init(tie_t *tie, unsigned int tri_num) +{ + tie->kdtree = NULL; + tie->tri_num = 0; + tie->tri_num_alloc = tri_num; + tie->tri_list = (tie_tri_t *)MEM_mallocN(sizeof(tie_tri_t) * tri_num, "tri list"); + tie->stat = 0; + tie->rays_fired = 0; +} + +/** + * Free up all the stuff associated with libtie + * + * All of the KDTREE nodes and triangles that we have allocated need to + * be freed in a controlled manner. This routine does that. + * + * @param tie pointer to a struct tie_t + * @return void + */ +void tie_free(tie_t *tie) +{ + int i; + + /* Free Triangle Data */ + for (i = 0; i < tie->tri_num; i++) + MEM_freeN( (void *)((intptr_t)(tie->tri_list[i].v) & ~0x7L)); + MEM_freeN(tie->tri_list); + + /* Free KDTREE Nodes */ + tie_kdtree_free(tie); +} + + +/** + * Get ready to shoot rays at triangles + * + * Build the KDTREE tree for the triangles we have + * + * @param tie pointer to a struct tie_t which now has all the triangles in it + * @return void + */ +void tie_prep(tie_t *tie) +{ + /* Build the kd-tree */ + tie_kdtree_prep(tie); + + /* Prep all the triangles */ + tie_tri_prep(tie); +} + + +/** + * Shoot a ray at some triangles + * + * The user-provided hitfunc is called at each ray/triangle intersection. + * Calls are guaranteed to be made in the ray-intersection order. + * The last argument (void *ptr) is passed to the hitfunc as-is, to allow + * application specific data to be passed to the hitfunc. + * + * @param tie a struct tie_t universe + * @param ray the ray to be intersected with the geometry + * @param id the intersection data for each intersection + * @param hitfunc the application routine to be called upon ray/triangle intersection. + * This function should return 0 if the ray is to continue propagating through the geometry, + * or non-zero if ray intersection should cease. + * @param ptr a pointer to be passed to the hitfunc when it is called. + * + * @return the return value from the user hitfunc() is used. + * In the event that the ray does not hit anything, or the ray exits the geometry space, a null value will be returned. + * @retval 0 ray did not hit anything, or ray was propagated through the geometry completely. + * @retval !0 the value returned from the last invokation of hitfunc() + */ +void* tie_work(tie_t *tie, tie_ray_t *ray, tie_id_t *id, void *(*hitfunc)(tie_ray_t*, tie_id_t*, tie_tri_t*, void *ptr), void *ptr) +{ + tie_stack_t stack[40]; + tie_id_t t, id_list[256]; + tie_tri_t *hit_list[256], *tri; + tie_geom_t *data; + tie_kdtree_t *node_aligned, *temp[2]; + tfloat near, far, dirinv[3], dist; + int i, n, ab[3], split, stack_ind, hit_count; + void *result; + + + if (!tie->kdtree) + return (NULL); + + ray->kdtree_depth = 0; + + /* + * Precompute direction inverse since it's used in a bunch of divides, + * this allows those divides to become fast multiplies. + */ + for (i = 0; i < 3; i++) { + if (ray->dir.v[i] == 0) + ray->dir.v[i] = TIE_PREC; + dirinv[i] = 1.0 / ray->dir.v[i]; + ab[i] = dirinv[i] < 0 ? 1 : 0; + } + + /* Extracting value of splitting plane from tie->kdtree pointer */ + split = ((intptr_t)(((tie_kdtree_t *)((intptr_t)tie->kdtree & ~0x7L))->data)) & 0x3; + + /* Initialize ray segment */ + if (ray->dir.v[split] < 0) { + far = (tie->min.v[split] - ray->pos.v[split]) * dirinv[split]; + } else { + far = (tie->max.v[split] - ray->pos.v[split]) * dirinv[split]; + } + + stack_ind = 0; + stack[0].node = tie->kdtree; + stack[0].near = 0; + stack[0].far = far; + + /* Process items on the stack */ + while (stack_ind >= 0) { + near = stack[stack_ind].near; + far = stack[stack_ind].far; + + /* + * Take the pointer from stack[stack_ind] and remove lower pts bits used to store data to + * give a valid ptr address. + */ + node_aligned = (tie_kdtree_t *)((intptr_t)stack[stack_ind].node & ~0x7L); + stack_ind--; + + /* + * KDTREE TRAVERSAL + * + * 3 conditions can happen here: + * - Ray only intersects the nearest node + * - Ray only intersects the furthest node + * - Ray intersects both nodes, pushing the furthest onto the stack + * + * Gordon Stoll's Mantra - Rays are Measured in Millions :-) + */ + while (((intptr_t)(node_aligned->data)) & 0x4) { + ray->kdtree_depth++; + + /* Retreive the splitting plane */ + split = ((intptr_t)(node_aligned->data)) & 0x3; + + /* Calculate the projected 1d distance to splitting axis */ + dist = (node_aligned->axis - ray->pos.v[split]) * dirinv[split]; + + temp[0] = &((tie_kdtree_t *)(node_aligned->data))[ab[split]]; + temp[1] = &((tie_kdtree_t *)(node_aligned->data))[1 - ab[split]]; + + i = near >= dist; /* Node B Only? */ + node_aligned = (tie_kdtree_t *)((intptr_t)(temp[i]) & ~0x7L); + + if (far < dist || i) + continue; + + /* Nearest Node and Push Furthest */ + stack_ind++; + stack[stack_ind].node = temp[1]; + stack[stack_ind].near = dist; + stack[stack_ind].far = far; + far = dist; + } + + + /* + * RAY/TRIANGLE INTERSECTION - Only gets executed on geometry nodes. + * This part of the function is being executed because the KDTREE Traversal is Complete. + */ + data = (tie_geom_t *)(node_aligned->data); + if (data->tri_num == 0) + continue; + + hit_count = 0; + for (i = 0; i < data->tri_num; i++) { + /* + * Triangle Intersection Code + */ + tfloat u0, v0, *v; + int i1, i2; + + tri = data->tri_list[i]; + math_vec_dot(u0, tri->data[1], ray->pos); + math_vec_dot(v0, tri->data[1], ray->dir); + t.dist = -(tri->data[2].v[0] + u0) / v0; + + /* + * Intersection point on triangle must lie within the kdtree node or it is rejected + * Apply TIE_PREC to near and far such that triangles that lie on orthogonal planes + * aren't in a precision fuzz boundary, thus missing something they should actualy + * have hit. + */ + if (t.dist < near - TIE_PREC || t.dist > far + TIE_PREC) + continue; + + /* Compute Intersection Point (P = O + Dt) */ + math_vec_mul_scalar(t.pos, ray->dir, t.dist); + math_vec_add(t.pos, ray->pos, t.pos); + + /* Extract i1 and i2 indices from lower bits of the v pointer */ + v = (tfloat *)((intptr_t)(tri->v) & ~0x7L); + + i1 = TIE_TAB1[((intptr_t)(tri->v) & 0x7)]; + i2 = TIE_TAB1[3 + ((intptr_t)(tri->v) & 0x7)]; + + /* Compute U and V */ + u0 = t.pos.v[i1] - tri->data[0].v[i1]; + v0 = t.pos.v[i2] - tri->data[0].v[i2]; + + /* + * Compute the barycentric coordinates, and make sure the coordinates + * fall within the boundaries of the triangle plane. + */ + if (fabs(tri->data[2].v[1]) <= TIE_PREC) { + t.beta = u0 / tri->data[2].v[2]; + if (t.beta < 0 || t.beta > 1) + continue; + t.alpha = (v0 - t.beta * v[1]) / v[0]; + } else { + t.beta = (v0 * tri->data[2].v[1] - u0 * v[0]) / (v[1] * tri->data[2].v[1] - tri->data[2].v[2] * v[0]); + if (t.beta < 0 || t.beta > 1) + continue; + t.alpha = (u0 - t.beta * tri->data[2].v[2]) / tri->data[2].v[1]; + } + + if (t.alpha < 0 || (t.alpha + t.beta) > 1.0) + continue; + + /* Triangle Intersected, append it in the list */ + if (hit_count < 0xff) { + hit_list[hit_count] = tri; + id_list[hit_count] = t; + hit_count++; + } + + + } + + + /* If we hit something, then sort the hit triangles on demand */ + for (i = 0; i < hit_count; i++) { + /* Sort the list so that HitList and IDList [n] is in order wrt [i] */ + for (n = i; n < hit_count; n++) { + if (id_list[n].dist < id_list[i].dist) { + /* Swap */ + tri = hit_list[i]; + t = id_list[i]; + hit_list[i] = hit_list[n]; + id_list[i] = id_list[n]; + hit_list[n] = tri; + id_list[n] = t; + } + } + + id_list[i].norm = hit_list[i]->data[1]; + result = hitfunc(ray, &id_list[i], hit_list[i], ptr); + + if (result) { + *id = id_list[i]; + return (result); + } + } + } + + return (NULL); +} + + +/** + * Add a triangle + * + * Add a new triangle to the universe to be raytraced. + * + * @param tie the universe + * @param tlist is an array of TIE_3 vertice triplets (v0, v1, v2) that form each triangle. + * @param tnum is the number of triangles (tlist = 3 * tnum of TIE_3's). + * @param plist is a list of pointer data that gets assigned to the ptr of each triangle. + * This will typically be 4-byte (32-bit architecture) spaced array of pointers that + * associate the triangle pointer with your arbitrary structure, i.e a mesh. + * @param pstride is the number of bytes to increment the pointer list as it assigns + * a pointer to each mesh, typically a value of 4 (for 32-bit machines). If you have + * a single pointer that groups all triangles to a common structure then you can use + * a value of 0 for pstride. This will give the pointer of all triangles the pointer + * address of plist. + * @return void + */ +void tie_push(tie_t *tie, TIE_3 *tlist, int tnum, void *plist, int pstride) +{ + int i; + + if (tnum + tie->tri_num > tie->tri_num_alloc) { + tie->tri_list = (tie_tri_t *)MEM_reallocN(tie->tri_list, sizeof(tie_tri_t) * (tie->tri_num + tnum)); + tie->tri_num_alloc += tnum; + } + + for (i = 0; i < tnum; i++) { + tie->tri_list[tie->tri_num].data[0] = tlist[i * 3 + 0]; + tie->tri_list[tie->tri_num].data[1] = tlist[i * 3 + 1]; + tie->tri_list[tie->tri_num].data[2] = tlist[i * 3 + 2]; + if (plist) { + tie->tri_list[tie->tri_num].ptr = plist; + plist = (void *)((intptr_t)plist + pstride); + } else { + tie->tri_list[tie->tri_num].ptr = NULL; + } + tie->tri_list[tie->tri_num].v = (tfloat *)MEM_mallocN(2 * sizeof(tfloat), "kdtree tri_list push"); + tie->tri_num++; + } +} + +/** @} */