This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/render/intern/source/convertblender.c
Matt Ebb 46aac7b4fc * Volume rendering - z transparency
This solves one of the last remaining hurdles for 
volume rendering. Previously it always used ray 
tracing to shade other objects inside or behind the 
volume. This meant that said objects would look 
aliased, unless you used Full OSA on the volume 
(which is slow!). As well as this, it meant that you didn't 
get a good alpha channel out of the volume to use for 
compositing, similar to ray refracting materials.

This commit enables z transparency for volume 
materials. Although it can be potentially less 
physically correct, in most situations there's no 
difference, and you get the benefit of nice sampling for 
other objects and an alpha channel for compositing too.
2009-08-23 02:54:30 +00:00

5598 lines
149 KiB
C

/**
* $Id$
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* Contributors: 2004/2005/2006 Blender Foundation, full recode
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include "MTC_matrixops.h"
#include "MEM_guardedalloc.h"
#include "BLI_arithb.h"
#include "BLI_blenlib.h"
#include "BLI_rand.h"
#include "BLI_memarena.h"
#include "BLI_ghash.h"
#include "DNA_armature_types.h"
#include "DNA_camera_types.h"
#include "DNA_material_types.h"
#include "DNA_curve_types.h"
#include "DNA_effect_types.h"
#include "DNA_group_types.h"
#include "DNA_lamp_types.h"
#include "DNA_image_types.h"
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_meta_types.h"
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_object_force.h"
#include "DNA_object_fluidsim.h"
#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
#include "DNA_texture_types.h"
#include "DNA_view3d_types.h"
#include "BKE_anim.h"
#include "BKE_armature.h"
#include "BKE_action.h"
#include "BKE_curve.h"
#include "BKE_customdata.h"
#include "BKE_colortools.h"
#include "BKE_constraint.h"
#include "BKE_displist.h"
#include "BKE_deform.h"
#include "BKE_DerivedMesh.h"
#include "BKE_effect.h"
#include "BKE_global.h"
#include "BKE_group.h"
#include "BKE_key.h"
#include "BKE_ipo.h"
#include "BKE_image.h"
#include "BKE_lattice.h"
#include "BKE_library.h"
#include "BKE_material.h"
#include "BKE_main.h"
#include "BKE_mball.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_particle.h"
#include "BKE_scene.h"
#include "BKE_subsurf.h"
#include "BKE_texture.h"
#include "BKE_utildefines.h"
#include "BKE_world.h"
#include "PIL_time.h"
#include "IMB_imbuf_types.h"
#include "envmap.h"
#include "occlusion.h"
#include "pointdensity.h"
#include "voxeldata.h"
#include "render_types.h"
#include "rendercore.h"
#include "renderdatabase.h"
#include "renderpipeline.h"
#include "shadbuf.h"
#include "shading.h"
#include "strand.h"
#include "texture.h"
#include "volume_precache.h"
#include "sss.h"
#include "strand.h"
#include "zbuf.h"
#include "sunsky.h"
/* 10 times larger than normal epsilon, test it on default nurbs sphere with ray_transp (for quad detection) */
/* or for checking vertex normal flips */
#define FLT_EPSILON10 1.19209290e-06F
/* ------------------------------------------------------------------------- */
/* Stuff for stars. This sits here because it uses gl-things. Part of
this code may move down to the converter. */
/* ------------------------------------------------------------------------- */
/* this is a bad beast, since it is misused by the 3d view drawing as well. */
static HaloRen *initstar(Render *re, ObjectRen *obr, float *vec, float hasize)
{
HaloRen *har;
float hoco[4];
projectverto(vec, re->winmat, hoco);
har= RE_findOrAddHalo(obr, obr->tothalo++);
/* projectvert is done in function zbufvlaggen again, because of parts */
VECCOPY(har->co, vec);
har->hasize= hasize;
har->zd= 0.0;
return har;
}
/* there must be a 'fixed' amount of stars generated between
* near and far
* all stars must by preference lie on the far and solely
* differ in clarity/color
*/
void RE_make_stars(Render *re, Scene *scenev3d, void (*initfunc)(void),
void (*vertexfunc)(float*), void (*termfunc)(void))
{
extern unsigned char hash[512];
ObjectRen *obr= NULL;
World *wrld= NULL;
HaloRen *har;
Scene *scene;
Camera *camera;
double dblrand, hlfrand;
float vec[4], fx, fy, fz;
float fac, starmindist, clipend;
float mat[4][4], stargrid, maxrand, maxjit, force, alpha;
int x, y, z, sx, sy, sz, ex, ey, ez, done = 0;
if(initfunc) {
scene= scenev3d;
wrld= scene->world;
}
else {
scene= re->scene;
wrld= &(re->wrld);
}
stargrid = wrld->stardist; /* distance between stars */
maxrand = 2.0; /* amount a star can be shifted (in grid units) */
maxjit = (wrld->starcolnoise); /* amount a color is being shifted */
/* size of stars */
force = ( wrld->starsize );
/* minimal free space (starting at camera) */
starmindist= wrld->starmindist;
if (stargrid <= 0.10) return;
if (re) re->flag |= R_HALO;
else stargrid *= 1.0; /* then it draws fewer */
if(re) MTC_Mat4Invert(mat, re->viewmat);
else MTC_Mat4One(mat);
/* BOUNDING BOX CALCULATION
* bbox goes from z = loc_near_var | loc_far_var,
* x = -z | +z,
* y = -z | +z
*/
if(scene->camera==NULL)
return;
camera = scene->camera->data;
clipend = camera->clipend;
/* convert to grid coordinates */
sx = ((mat[3][0] - clipend) / stargrid) - maxrand;
sy = ((mat[3][1] - clipend) / stargrid) - maxrand;
sz = ((mat[3][2] - clipend) / stargrid) - maxrand;
ex = ((mat[3][0] + clipend) / stargrid) + maxrand;
ey = ((mat[3][1] + clipend) / stargrid) + maxrand;
ez = ((mat[3][2] + clipend) / stargrid) + maxrand;
dblrand = maxrand * stargrid;
hlfrand = 2.0 * dblrand;
if (initfunc) {
initfunc();
}
if(re) /* add render object for stars */
obr= RE_addRenderObject(re, NULL, NULL, 0, 0, 0);
for (x = sx, fx = sx * stargrid; x <= ex; x++, fx += stargrid) {
for (y = sy, fy = sy * stargrid; y <= ey ; y++, fy += stargrid) {
for (z = sz, fz = sz * stargrid; z <= ez; z++, fz += stargrid) {
BLI_srand((hash[z & 0xff] << 24) + (hash[y & 0xff] << 16) + (hash[x & 0xff] << 8));
vec[0] = fx + (hlfrand * BLI_drand()) - dblrand;
vec[1] = fy + (hlfrand * BLI_drand()) - dblrand;
vec[2] = fz + (hlfrand * BLI_drand()) - dblrand;
vec[3] = 1.0;
if (vertexfunc) {
if(done & 1) vertexfunc(vec);
done++;
}
else {
MTC_Mat4MulVecfl(re->viewmat, vec);
/* in vec are global coordinates
* calculate distance to camera
* and using that, define the alpha
*/
{
float tx, ty, tz;
tx = vec[0];
ty = vec[1];
tz = vec[2];
alpha = sqrt(tx * tx + ty * ty + tz * tz);
if (alpha >= clipend) alpha = 0.0;
else if (alpha <= starmindist) alpha = 0.0;
else if (alpha <= 2.0 * starmindist) {
alpha = (alpha - starmindist) / starmindist;
} else {
alpha -= 2.0 * starmindist;
alpha /= (clipend - 2.0 * starmindist);
alpha = 1.0 - alpha;
}
}
if (alpha != 0.0) {
fac = force * BLI_drand();
har = initstar(re, obr, vec, fac);
if (har) {
har->alfa = sqrt(sqrt(alpha));
har->add= 255;
har->r = har->g = har->b = 1.0;
if (maxjit) {
har->r += ((maxjit * BLI_drand()) ) - maxjit;
har->g += ((maxjit * BLI_drand()) ) - maxjit;
har->b += ((maxjit * BLI_drand()) ) - maxjit;
}
har->hard = 32;
har->lay= -1;
har->type |= HA_ONLYSKY;
done++;
}
}
}
}
/* do not call blender_test_break() here, since it is used in UI as well, confusing the callback system */
/* main cause is G.afbreek of course, a global again... (ton) */
}
}
if (termfunc) termfunc();
if(obr)
re->tothalo += obr->tothalo;
}
/* ------------------------------------------------------------------------- */
/* tool functions/defines for ad hoc simplification and possible future
cleanup */
/* ------------------------------------------------------------------------- */
#define UVTOINDEX(u,v) (startvlak + (u) * sizev + (v))
/*
NOTE THAT U/V COORDINATES ARE SOMETIMES SWAPPED !!
^ ()----p4----p3----()
| | | | |
u | | F1 | F2 |
| | | |
()----p1----p2----()
v ->
*/
/* ------------------------------------------------------------------------- */
static void split_v_renderfaces(ObjectRen *obr, int startvlak, int startvert, int usize, int vsize, int uIndex, int cyclu, int cyclv)
{
int vLen = vsize-1+(!!cyclv);
int v;
for (v=0; v<vLen; v++) {
VlakRen *vlr = RE_findOrAddVlak(obr, startvlak + vLen*uIndex + v);
VertRen *vert = RE_vertren_copy(obr, vlr->v2);
if (cyclv) {
vlr->v2 = vert;
if (v==vLen-1) {
VlakRen *vlr = RE_findOrAddVlak(obr, startvlak + vLen*uIndex + 0);
vlr->v1 = vert;
} else {
VlakRen *vlr = RE_findOrAddVlak(obr, startvlak + vLen*uIndex + v+1);
vlr->v1 = vert;
}
} else {
vlr->v2 = vert;
if (v<vLen-1) {
VlakRen *vlr = RE_findOrAddVlak(obr, startvlak + vLen*uIndex + v+1);
vlr->v1 = vert;
}
if (v==0) {
vlr->v1 = RE_vertren_copy(obr, vlr->v1);
}
}
}
}
/* ------------------------------------------------------------------------- */
static int check_vnormal(float *n, float *veno)
{
float inp;
inp=n[0]*veno[0]+n[1]*veno[1]+n[2]*veno[2];
if(inp < -FLT_EPSILON10) return 1;
return 0;
}
/* ------------------------------------------------------------------------- */
/* Stress, tangents and normals */
/* ------------------------------------------------------------------------- */
static void calc_edge_stress_add(float *accum, VertRen *v1, VertRen *v2)
{
float len= VecLenf(v1->co, v2->co)/VecLenf(v1->orco, v2->orco);
float *acc;
acc= accum + 2*v1->index;
acc[0]+= len;
acc[1]+= 1.0f;
acc= accum + 2*v2->index;
acc[0]+= len;
acc[1]+= 1.0f;
}
static void calc_edge_stress(Render *re, ObjectRen *obr, Mesh *me)
{
float loc[3], size[3], *accum, *acc, *accumoffs, *stress;
int a;
if(obr->totvert==0) return;
mesh_get_texspace(me, loc, NULL, size);
accum= MEM_callocN(2*sizeof(float)*obr->totvert, "temp accum for stress");
/* de-normalize orco */
for(a=0; a<obr->totvert; a++) {
VertRen *ver= RE_findOrAddVert(obr, a);
if(ver->orco) {
ver->orco[0]= ver->orco[0]*size[0] +loc[0];
ver->orco[1]= ver->orco[1]*size[1] +loc[1];
ver->orco[2]= ver->orco[2]*size[2] +loc[2];
}
}
/* add stress values */
accumoffs= accum; /* so we can use vertex index */
for(a=0; a<obr->totvlak; a++) {
VlakRen *vlr= RE_findOrAddVlak(obr, a);
if(vlr->v1->orco && vlr->v4) {
calc_edge_stress_add(accumoffs, vlr->v1, vlr->v2);
calc_edge_stress_add(accumoffs, vlr->v2, vlr->v3);
calc_edge_stress_add(accumoffs, vlr->v3, vlr->v1);
if(vlr->v4) {
calc_edge_stress_add(accumoffs, vlr->v3, vlr->v4);
calc_edge_stress_add(accumoffs, vlr->v4, vlr->v1);
calc_edge_stress_add(accumoffs, vlr->v2, vlr->v4);
}
}
}
for(a=0; a<obr->totvert; a++) {
VertRen *ver= RE_findOrAddVert(obr, a);
if(ver->orco) {
/* find stress value */
acc= accumoffs + 2*ver->index;
if(acc[1]!=0.0f)
acc[0]/= acc[1];
stress= RE_vertren_get_stress(obr, ver, 1);
*stress= *acc;
/* restore orcos */
ver->orco[0] = (ver->orco[0]-loc[0])/size[0];
ver->orco[1] = (ver->orco[1]-loc[1])/size[1];
ver->orco[2] = (ver->orco[2]-loc[2])/size[2];
}
}
MEM_freeN(accum);
}
/* gets tangent from tface or orco */
static void calc_tangent_vector(ObjectRen *obr, VertexTangent **vtangents, MemArena *arena, VlakRen *vlr, int do_nmap_tangent, int do_tangent)
{
MTFace *tface= RE_vlakren_get_tface(obr, vlr, obr->actmtface, NULL, 0);
VertRen *v1=vlr->v1, *v2=vlr->v2, *v3=vlr->v3, *v4=vlr->v4;
float tang[3], *tav;
float *uv1, *uv2, *uv3, *uv4;
float uv[4][2];
if(tface) {
uv1= tface->uv[0];
uv2= tface->uv[1];
uv3= tface->uv[2];
uv4= tface->uv[3];
}
else if(v1->orco) {
uv1= uv[0]; uv2= uv[1]; uv3= uv[2]; uv4= uv[3];
spheremap(v1->orco[0], v1->orco[1], v1->orco[2], &uv[0][0], &uv[0][1]);
spheremap(v2->orco[0], v2->orco[1], v2->orco[2], &uv[1][0], &uv[1][1]);
spheremap(v3->orco[0], v3->orco[1], v3->orco[2], &uv[2][0], &uv[2][1]);
if(v4)
spheremap(v4->orco[0], v4->orco[1], v4->orco[2], &uv[3][0], &uv[3][1]);
}
else return;
tangent_from_uv(uv1, uv2, uv3, v1->co, v2->co, v3->co, vlr->n, tang);
if(do_tangent) {
tav= RE_vertren_get_tangent(obr, v1, 1);
VECADD(tav, tav, tang);
tav= RE_vertren_get_tangent(obr, v2, 1);
VECADD(tav, tav, tang);
tav= RE_vertren_get_tangent(obr, v3, 1);
VECADD(tav, tav, tang);
}
if(do_nmap_tangent) {
sum_or_add_vertex_tangent(arena, &vtangents[v1->index], tang, uv1);
sum_or_add_vertex_tangent(arena, &vtangents[v2->index], tang, uv2);
sum_or_add_vertex_tangent(arena, &vtangents[v3->index], tang, uv3);
}
if(v4) {
tangent_from_uv(uv1, uv3, uv4, v1->co, v3->co, v4->co, vlr->n, tang);
if(do_tangent) {
tav= RE_vertren_get_tangent(obr, v1, 1);
VECADD(tav, tav, tang);
tav= RE_vertren_get_tangent(obr, v3, 1);
VECADD(tav, tav, tang);
tav= RE_vertren_get_tangent(obr, v4, 1);
VECADD(tav, tav, tang);
}
if(do_nmap_tangent) {
sum_or_add_vertex_tangent(arena, &vtangents[v1->index], tang, uv1);
sum_or_add_vertex_tangent(arena, &vtangents[v3->index], tang, uv3);
sum_or_add_vertex_tangent(arena, &vtangents[v4->index], tang, uv4);
}
}
}
static void calc_vertexnormals(Render *re, ObjectRen *obr, int do_tangent, int do_nmap_tangent)
{
MemArena *arena= NULL;
VertexTangent **vtangents= NULL;
int a;
if(do_nmap_tangent) {
arena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
BLI_memarena_use_calloc(arena);
vtangents= MEM_callocN(sizeof(VertexTangent*)*obr->totvert, "VertexTangent");
}
/* clear all vertex normals */
for(a=0; a<obr->totvert; a++) {
VertRen *ver= RE_findOrAddVert(obr, a);
ver->n[0]=ver->n[1]=ver->n[2]= 0.0f;
}
/* calculate cos of angles and point-masses, use as weight factor to
add face normal to vertex */
for(a=0; a<obr->totvlak; a++) {
VlakRen *vlr= RE_findOrAddVlak(obr, a);
if(vlr->flag & ME_SMOOTH) {
VertRen *v1= vlr->v1;
VertRen *v2= vlr->v2;
VertRen *v3= vlr->v3;
VertRen *v4= vlr->v4;
float n1[3], n2[3], n3[3], n4[3];
float fac1, fac2, fac3, fac4=0.0f;
if(re->flag & R_GLOB_NOPUNOFLIP)
vlr->flag |= R_NOPUNOFLIP;
VecSubf(n1, v2->co, v1->co);
Normalize(n1);
VecSubf(n2, v3->co, v2->co);
Normalize(n2);
if(v4==NULL) {
VecSubf(n3, v1->co, v3->co);
Normalize(n3);
fac1= saacos(-n1[0]*n3[0]-n1[1]*n3[1]-n1[2]*n3[2]);
fac2= saacos(-n1[0]*n2[0]-n1[1]*n2[1]-n1[2]*n2[2]);
fac3= saacos(-n2[0]*n3[0]-n2[1]*n3[1]-n2[2]*n3[2]);
}
else {
VecSubf(n3, v4->co, v3->co);
Normalize(n3);
VecSubf(n4, v1->co, v4->co);
Normalize(n4);
fac1= saacos(-n4[0]*n1[0]-n4[1]*n1[1]-n4[2]*n1[2]);
fac2= saacos(-n1[0]*n2[0]-n1[1]*n2[1]-n1[2]*n2[2]);
fac3= saacos(-n2[0]*n3[0]-n2[1]*n3[1]-n2[2]*n3[2]);
fac4= saacos(-n3[0]*n4[0]-n3[1]*n4[1]-n3[2]*n4[2]);
if(!(vlr->flag & R_NOPUNOFLIP)) {
if( check_vnormal(vlr->n, v4->n) ) fac4= -fac4;
}
v4->n[0] +=fac4*vlr->n[0];
v4->n[1] +=fac4*vlr->n[1];
v4->n[2] +=fac4*vlr->n[2];
}
if(!(vlr->flag & R_NOPUNOFLIP)) {
if( check_vnormal(vlr->n, v1->n) ) fac1= -fac1;
if( check_vnormal(vlr->n, v2->n) ) fac2= -fac2;
if( check_vnormal(vlr->n, v3->n) ) fac3= -fac3;
}
v1->n[0] +=fac1*vlr->n[0];
v1->n[1] +=fac1*vlr->n[1];
v1->n[2] +=fac1*vlr->n[2];
v2->n[0] +=fac2*vlr->n[0];
v2->n[1] +=fac2*vlr->n[1];
v2->n[2] +=fac2*vlr->n[2];
v3->n[0] +=fac3*vlr->n[0];
v3->n[1] +=fac3*vlr->n[1];
v3->n[2] +=fac3*vlr->n[2];
}
if(do_nmap_tangent || do_tangent) {
/* tangents still need to be calculated for flat faces too */
/* weighting removed, they are not vertexnormals */
calc_tangent_vector(obr, vtangents, arena, vlr, do_nmap_tangent, do_tangent);
}
}
/* do solid faces */
for(a=0; a<obr->totvlak; a++) {
VlakRen *vlr= RE_findOrAddVlak(obr, a);
if((vlr->flag & ME_SMOOTH)==0) {
float *f1= vlr->v1->n;
if(f1[0]==0.0 && f1[1]==0.0 && f1[2]==0.0) VECCOPY(f1, vlr->n);
f1= vlr->v2->n;
if(f1[0]==0.0 && f1[1]==0.0 && f1[2]==0.0) VECCOPY(f1, vlr->n);
f1= vlr->v3->n;
if(f1[0]==0.0 && f1[1]==0.0 && f1[2]==0.0) VECCOPY(f1, vlr->n);
if(vlr->v4) {
f1= vlr->v4->n;
if(f1[0]==0.0 && f1[1]==0.0 && f1[2]==0.0) VECCOPY(f1, vlr->n);
}
}
if(do_nmap_tangent) {
VertRen *v1=vlr->v1, *v2=vlr->v2, *v3=vlr->v3, *v4=vlr->v4;
MTFace *tface= RE_vlakren_get_tface(obr, vlr, obr->actmtface, NULL, 0);
if(tface) {
float *vtang, *ftang= RE_vlakren_get_nmap_tangent(obr, vlr, 1);
vtang= find_vertex_tangent(vtangents[v1->index], tface->uv[0]);
VECCOPY(ftang, vtang);
Normalize(ftang);
vtang= find_vertex_tangent(vtangents[v2->index], tface->uv[1]);
VECCOPY(ftang+3, vtang);
Normalize(ftang+3);
vtang= find_vertex_tangent(vtangents[v3->index], tface->uv[2]);
VECCOPY(ftang+6, vtang);
Normalize(ftang+6);
if(v4) {
vtang= find_vertex_tangent(vtangents[v4->index], tface->uv[3]);
VECCOPY(ftang+9, vtang);
Normalize(ftang+9);
}
}
}
}
/* normalize vertex normals */
for(a=0; a<obr->totvert; a++) {
VertRen *ver= RE_findOrAddVert(obr, a);
Normalize(ver->n);
if(do_tangent) {
float *tav= RE_vertren_get_tangent(obr, ver, 0);
if (tav) {
/* orthonorm. */
float tdn = tav[0]*ver->n[0] + tav[1]*ver->n[1] + tav[2]*ver->n[2];
tav[0] -= ver->n[0]*tdn;
tav[1] -= ver->n[1]*tdn;
tav[2] -= ver->n[2]*tdn;
Normalize(tav);
}
}
}
if(arena)
BLI_memarena_free(arena);
if(vtangents)
MEM_freeN(vtangents);
}
/* ------------------------------------------------------------------------- */
/* Autosmoothing: */
/* ------------------------------------------------------------------------- */
typedef struct ASvert {
int totface;
ListBase faces;
} ASvert;
typedef struct ASface {
struct ASface *next, *prev;
VlakRen *vlr[4];
VertRen *nver[4];
} ASface;
static void as_addvert(ASvert *asv, VertRen *v1, VlakRen *vlr)
{
ASface *asf;
int a;
if(v1 == NULL) return;
if(asv->faces.first==NULL) {
asf= MEM_callocN(sizeof(ASface), "asface");
BLI_addtail(&asv->faces, asf);
}
asf= asv->faces.last;
for(a=0; a<4; a++) {
if(asf->vlr[a]==NULL) {
asf->vlr[a]= vlr;
asv->totface++;
break;
}
}
/* new face struct */
if(a==4) {
asf= MEM_callocN(sizeof(ASface), "asface");
BLI_addtail(&asv->faces, asf);
asf->vlr[0]= vlr;
asv->totface++;
}
}
static int as_testvertex(VlakRen *vlr, VertRen *ver, ASvert *asv, float thresh)
{
/* return 1: vertex needs a copy */
ASface *asf;
float inp;
int a;
if(vlr==0) return 0;
asf= asv->faces.first;
while(asf) {
for(a=0; a<4; a++) {
if(asf->vlr[a] && asf->vlr[a]!=vlr) {
inp= fabs( vlr->n[0]*asf->vlr[a]->n[0] + vlr->n[1]*asf->vlr[a]->n[1] + vlr->n[2]*asf->vlr[a]->n[2] );
if(inp < thresh) return 1;
}
}
asf= asf->next;
}
return 0;
}
static VertRen *as_findvertex(VlakRen *vlr, VertRen *ver, ASvert *asv, float thresh)
{
/* return when new vertex already was made */
ASface *asf;
float inp;
int a;
asf= asv->faces.first;
while(asf) {
for(a=0; a<4; a++) {
if(asf->vlr[a] && asf->vlr[a]!=vlr) {
/* this face already made a copy for this vertex! */
if(asf->nver[a]) {
inp= fabs( vlr->n[0]*asf->vlr[a]->n[0] + vlr->n[1]*asf->vlr[a]->n[1] + vlr->n[2]*asf->vlr[a]->n[2] );
if(inp >= thresh) {
return asf->nver[a];
}
}
}
}
asf= asf->next;
}
return NULL;
}
/* note; autosmooth happens in object space still, after applying autosmooth we rotate */
/* note2; actually, when original mesh and displist are equal sized, face normals are from original mesh */
static void autosmooth(Render *re, ObjectRen *obr, float mat[][4], int degr)
{
ASvert *asv, *asverts;
ASface *asf;
VertRen *ver, *v1;
VlakRen *vlr;
float thresh;
int a, b, totvert;
if(obr->totvert==0) return;
asverts= MEM_callocN(sizeof(ASvert)*obr->totvert, "all smooth verts");
thresh= cos( M_PI*(0.5f+(float)degr)/180.0 );
/* step zero: give faces normals of original mesh, if this is provided */
/* step one: construct listbase of all vertices and pointers to faces */
for(a=0; a<obr->totvlak; a++) {
vlr= RE_findOrAddVlak(obr, a);
/* skip wire faces */
if(vlr->v2 != vlr->v3) {
as_addvert(asverts+vlr->v1->index, vlr->v1, vlr);
as_addvert(asverts+vlr->v2->index, vlr->v2, vlr);
as_addvert(asverts+vlr->v3->index, vlr->v3, vlr);
if(vlr->v4)
as_addvert(asverts+vlr->v4->index, vlr->v4, vlr);
}
}
totvert= obr->totvert;
/* we now test all vertices, when faces have a normal too much different: they get a new vertex */
for(a=0, asv=asverts; a<totvert; a++, asv++) {
if(asv && asv->totface>1) {
ver= RE_findOrAddVert(obr, a);
asf= asv->faces.first;
while(asf) {
for(b=0; b<4; b++) {
/* is there a reason to make a new vertex? */
vlr= asf->vlr[b];
if( as_testvertex(vlr, ver, asv, thresh) ) {
/* already made a new vertex within threshold? */
v1= as_findvertex(vlr, ver, asv, thresh);
if(v1==NULL) {
/* make a new vertex */
v1= RE_vertren_copy(obr, ver);
}
asf->nver[b]= v1;
if(vlr->v1==ver) vlr->v1= v1;
if(vlr->v2==ver) vlr->v2= v1;
if(vlr->v3==ver) vlr->v3= v1;
if(vlr->v4==ver) vlr->v4= v1;
}
}
asf= asf->next;
}
}
}
/* free */
for(a=0; a<totvert; a++) {
BLI_freelistN(&asverts[a].faces);
}
MEM_freeN(asverts);
/* rotate vertices and calculate normal of faces */
for(a=0; a<obr->totvert; a++) {
ver= RE_findOrAddVert(obr, a);
MTC_Mat4MulVecfl(mat, ver->co);
}
for(a=0; a<obr->totvlak; a++) {
vlr= RE_findOrAddVlak(obr, a);
/* skip wire faces */
if(vlr->v2 != vlr->v3) {
if(vlr->v4)
CalcNormFloat4(vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co, vlr->n);
else
CalcNormFloat(vlr->v3->co, vlr->v2->co, vlr->v1->co, vlr->n);
}
}
}
/* ------------------------------------------------------------------------- */
/* Orco hash and Materials */
/* ------------------------------------------------------------------------- */
static float *get_object_orco(Render *re, Object *ob)
{
float *orco;
if (!re->orco_hash)
re->orco_hash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
orco = BLI_ghash_lookup(re->orco_hash, ob);
if (!orco) {
if (ELEM(ob->type, OB_CURVE, OB_FONT)) {
orco = make_orco_curve(re->scene, ob);
} else if (ob->type==OB_SURF) {
orco = make_orco_surf(ob);
} else if (ob->type==OB_MBALL) {
orco = make_orco_mball(ob);
}
if (orco)
BLI_ghash_insert(re->orco_hash, ob, orco);
}
return orco;
}
static void set_object_orco(Render *re, void *ob, float *orco)
{
if (!re->orco_hash)
re->orco_hash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
BLI_ghash_insert(re->orco_hash, ob, orco);
}
static void free_mesh_orco_hash(Render *re)
{
if (re->orco_hash) {
BLI_ghash_free(re->orco_hash, NULL, (GHashValFreeFP)MEM_freeN);
re->orco_hash = NULL;
}
}
static void flag_render_node_material(Render *re, bNodeTree *ntree)
{
bNode *node;
for(node=ntree->nodes.first; node; node= node->next) {
if(node->id) {
if(GS(node->id->name)==ID_MA) {
Material *ma= (Material *)node->id;
if((ma->mode & MA_TRANSP) && (ma->mode & MA_ZTRANSP))
re->flag |= R_ZTRA;
ma->flag |= MA_IS_USED;
}
else if(node->type==NODE_GROUP)
flag_render_node_material(re, (bNodeTree *)node->id);
}
}
}
static Material *give_render_material(Render *re, Object *ob, int nr)
{
extern Material defmaterial; /* material.c */
Material *ma;
ma= give_current_material(ob, nr);
if(ma==NULL)
ma= &defmaterial;
if(re->r.mode & R_SPEED) ma->texco |= NEED_UV;
if(ma->material_type == MA_TYPE_VOLUME) ma->mode |= MA_TRANSP;
if((ma->mode & MA_TRANSP) && (ma->mode & MA_ZTRANSP))
re->flag |= R_ZTRA;
/* for light groups */
ma->flag |= MA_IS_USED;
if(ma->nodetree && ma->use_nodes)
flag_render_node_material(re, ma->nodetree);
return ma;
}
/* ------------------------------------------------------------------------- */
/* Particles */
/* ------------------------------------------------------------------------- */
typedef struct ParticleStrandData
{
struct MCol *mcol;
float *orco, *uvco, *surfnor;
float time, adapt_angle, adapt_pix, size;
int totuv, totcol;
int first, line, adapt, override_uv;
}
ParticleStrandData;
/* future thread problem... */
static void static_particle_strand(Render *re, ObjectRen *obr, Material *ma, ParticleStrandData *sd, float *vec, float *vec1)
{
static VertRen *v1= NULL, *v2= NULL;
VlakRen *vlr= NULL;
float nor[3], cross[3], crosslen, w, dx, dy, width;
static float anor[3], avec[3];
int flag, i;
static int second=0;
VecSubf(nor, vec, vec1);
Normalize(nor); // nor needed as tangent
Crossf(cross, vec, nor);
/* turn cross in pixelsize */
w= vec[2]*re->winmat[2][3] + re->winmat[3][3];
dx= re->winx*cross[0]*re->winmat[0][0];
dy= re->winy*cross[1]*re->winmat[1][1];
w= sqrt(dx*dx + dy*dy)/w;
if(w!=0.0f) {
float fac;
if(ma->strand_ease!=0.0f) {
if(ma->strand_ease<0.0f)
fac= pow(sd->time, 1.0+ma->strand_ease);
else
fac= pow(sd->time, 1.0/(1.0f-ma->strand_ease));
}
else fac= sd->time;
width= ((1.0f-fac)*ma->strand_sta + (fac)*ma->strand_end);
/* use actual Blender units for strand width and fall back to minimum width */
if(ma->mode & MA_STR_B_UNITS){
crosslen= VecLength(cross);
w= 2.0f*crosslen*ma->strand_min/w;
if(width < w)
width= w;
/*cross is the radius of the strand so we want it to be half of full width */
VecMulf(cross,0.5/crosslen);
}
else
width/=w;
VecMulf(cross, width);
}
else width= 1.0f;
if(ma->mode & MA_TANGENT_STR)
flag= R_SMOOTH|R_NOPUNOFLIP|R_TANGENT;
else
flag= R_SMOOTH;
/* only 1 pixel wide strands filled in as quads now, otherwise zbuf errors */
if(ma->strand_sta==1.0f)
flag |= R_STRAND;
/* single face line */
if(sd->line) {
vlr= RE_findOrAddVlak(obr, obr->totvlak++);
vlr->flag= flag;
vlr->v1= RE_findOrAddVert(obr, obr->totvert++);
vlr->v2= RE_findOrAddVert(obr, obr->totvert++);
vlr->v3= RE_findOrAddVert(obr, obr->totvert++);
vlr->v4= RE_findOrAddVert(obr, obr->totvert++);
VECCOPY(vlr->v1->co, vec);
VecAddf(vlr->v1->co, vlr->v1->co, cross);
VECCOPY(vlr->v1->n, nor);
vlr->v1->orco= sd->orco;
vlr->v1->accum= -1.0f; // accum abuse for strand texco
VECCOPY(vlr->v2->co, vec);
VecSubf(vlr->v2->co, vlr->v2->co, cross);
VECCOPY(vlr->v2->n, nor);
vlr->v2->orco= sd->orco;
vlr->v2->accum= vlr->v1->accum;
VECCOPY(vlr->v4->co, vec1);
VecAddf(vlr->v4->co, vlr->v4->co, cross);
VECCOPY(vlr->v4->n, nor);
vlr->v4->orco= sd->orco;
vlr->v4->accum= 1.0f; // accum abuse for strand texco
VECCOPY(vlr->v3->co, vec1);
VecSubf(vlr->v3->co, vlr->v3->co, cross);
VECCOPY(vlr->v3->n, nor);
vlr->v3->orco= sd->orco;
vlr->v3->accum= vlr->v4->accum;
CalcNormFloat4(vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co, vlr->n);
vlr->mat= ma;
vlr->ec= ME_V2V3;
if(sd->surfnor) {
float *snor= RE_vlakren_get_surfnor(obr, vlr, 1);
VECCOPY(snor, sd->surfnor);
}
if(sd->uvco){
for(i=0; i<sd->totuv; i++){
MTFace *mtf;
mtf=RE_vlakren_get_tface(obr,vlr,i,NULL,1);
mtf->uv[0][0]=mtf->uv[1][0]=
mtf->uv[2][0]=mtf->uv[3][0]=(sd->uvco+2*i)[0];
mtf->uv[0][1]=mtf->uv[1][1]=
mtf->uv[2][1]=mtf->uv[3][1]=(sd->uvco+2*i)[1];
}
if(sd->override_uv>=0){
MTFace *mtf;
mtf=RE_vlakren_get_tface(obr,vlr,sd->override_uv,NULL,0);
mtf->uv[0][0]=mtf->uv[3][0]=0.0f;
mtf->uv[1][0]=mtf->uv[2][0]=1.0f;
mtf->uv[0][1]=mtf->uv[1][1]=0.0f;
mtf->uv[2][1]=mtf->uv[3][1]=1.0f;
}
}
if(sd->mcol){
for(i=0; i<sd->totcol; i++){
MCol *mc;
mc=RE_vlakren_get_mcol(obr,vlr,i,NULL,1);
mc[0]=mc[1]=mc[2]=mc[3]=sd->mcol[i];
mc[0]=mc[1]=mc[2]=mc[3]=sd->mcol[i];
}
}
}
/* first two vertices of a strand */
else if(sd->first) {
if(sd->adapt){
VECCOPY(anor, nor);
VECCOPY(avec, vec);
second=1;
}
v1= RE_findOrAddVert(obr, obr->totvert++);
v2= RE_findOrAddVert(obr, obr->totvert++);
VECCOPY(v1->co, vec);
VecAddf(v1->co, v1->co, cross);
VECCOPY(v1->n, nor);
v1->orco= sd->orco;
v1->accum= -1.0f; // accum abuse for strand texco
VECCOPY(v2->co, vec);
VecSubf(v2->co, v2->co, cross);
VECCOPY(v2->n, nor);
v2->orco= sd->orco;
v2->accum= v1->accum;
}
/* more vertices & faces to strand */
else {
if(sd->adapt==0 || second){
vlr= RE_findOrAddVlak(obr, obr->totvlak++);
vlr->flag= flag;
vlr->v1= v1;
vlr->v2= v2;
vlr->v3= RE_findOrAddVert(obr, obr->totvert++);
vlr->v4= RE_findOrAddVert(obr, obr->totvert++);
v1= vlr->v4; // cycle
v2= vlr->v3; // cycle
if(sd->adapt){
second=0;
VECCOPY(anor,nor);
VECCOPY(avec,vec);
}
}
else if(sd->adapt){
float dvec[3],pvec[3];
VecSubf(dvec,avec,vec);
Projf(pvec,dvec,vec);
VecSubf(dvec,dvec,pvec);
w= vec[2]*re->winmat[2][3] + re->winmat[3][3];
dx= re->winx*dvec[0]*re->winmat[0][0]/w;
dy= re->winy*dvec[1]*re->winmat[1][1]/w;
w= sqrt(dx*dx + dy*dy);
if(Inpf(anor,nor)<sd->adapt_angle && w>sd->adapt_pix){
vlr= RE_findOrAddVlak(obr, obr->totvlak++);
vlr->flag= flag;
vlr->v1= v1;
vlr->v2= v2;
vlr->v3= RE_findOrAddVert(obr, obr->totvert++);
vlr->v4= RE_findOrAddVert(obr, obr->totvert++);
v1= vlr->v4; // cycle
v2= vlr->v3; // cycle
VECCOPY(anor,nor);
VECCOPY(avec,vec);
}
else{
vlr= RE_findOrAddVlak(obr, obr->totvlak-1);
}
}
VECCOPY(vlr->v4->co, vec);
VecAddf(vlr->v4->co, vlr->v4->co, cross);
VECCOPY(vlr->v4->n, nor);
vlr->v4->orco= sd->orco;
vlr->v4->accum= -1.0f + 2.0f*sd->time; // accum abuse for strand texco
VECCOPY(vlr->v3->co, vec);
VecSubf(vlr->v3->co, vlr->v3->co, cross);
VECCOPY(vlr->v3->n, nor);
vlr->v3->orco= sd->orco;
vlr->v3->accum= vlr->v4->accum;
CalcNormFloat4(vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co, vlr->n);
vlr->mat= ma;
vlr->ec= ME_V2V3;
if(sd->surfnor) {
float *snor= RE_vlakren_get_surfnor(obr, vlr, 1);
VECCOPY(snor, sd->surfnor);
}
if(sd->uvco){
for(i=0; i<sd->totuv; i++){
MTFace *mtf;
mtf=RE_vlakren_get_tface(obr,vlr,i,NULL,1);
mtf->uv[0][0]=mtf->uv[1][0]=
mtf->uv[2][0]=mtf->uv[3][0]=(sd->uvco+2*i)[0];
mtf->uv[0][1]=mtf->uv[1][1]=
mtf->uv[2][1]=mtf->uv[3][1]=(sd->uvco+2*i)[1];
}
if(sd->override_uv>=0){
MTFace *mtf;
mtf=RE_vlakren_get_tface(obr,vlr,sd->override_uv,NULL,0);
mtf->uv[0][0]=mtf->uv[3][0]=0.0f;
mtf->uv[1][0]=mtf->uv[2][0]=1.0f;
mtf->uv[0][1]=mtf->uv[1][1]=(vlr->v1->accum+1.0f)/2.0f;
mtf->uv[2][1]=mtf->uv[3][1]=(vlr->v3->accum+1.0f)/2.0f;
}
}
if(sd->mcol){
for(i=0; i<sd->totcol; i++){
MCol *mc;
mc=RE_vlakren_get_mcol(obr,vlr,i,NULL,1);
mc[0]=mc[1]=mc[2]=mc[3]=sd->mcol[i];
mc[0]=mc[1]=mc[2]=mc[3]=sd->mcol[i];
}
}
}
}
static void static_particle_wire(ObjectRen *obr, Material *ma, float *vec, float *vec1, int first, int line)
{
VlakRen *vlr;
static VertRen *v1;
if(line) {
vlr= RE_findOrAddVlak(obr, obr->totvlak++);
vlr->v1= RE_findOrAddVert(obr, obr->totvert++);
vlr->v2= RE_findOrAddVert(obr, obr->totvert++);
vlr->v3= vlr->v2;
vlr->v4= NULL;
VECCOPY(vlr->v1->co, vec);
VECCOPY(vlr->v2->co, vec1);
VecSubf(vlr->n, vec, vec1);
Normalize(vlr->n);
VECCOPY(vlr->v1->n, vlr->n);
VECCOPY(vlr->v2->n, vlr->n);
vlr->mat= ma;
vlr->ec= ME_V1V2;
}
else if(first) {
v1= RE_findOrAddVert(obr, obr->totvert++);
VECCOPY(v1->co, vec);
}
else {
vlr= RE_findOrAddVlak(obr, obr->totvlak++);
vlr->v1= v1;
vlr->v2= RE_findOrAddVert(obr, obr->totvert++);
vlr->v3= vlr->v2;
vlr->v4= NULL;
v1= vlr->v2; // cycle
VECCOPY(v1->co, vec);
VecSubf(vlr->n, vec, vec1);
Normalize(vlr->n);
VECCOPY(v1->n, vlr->n);
vlr->mat= ma;
vlr->ec= ME_V1V2;
}
}
static void particle_curve(Render *re, ObjectRen *obr, DerivedMesh *dm, Material *ma, ParticleStrandData *sd, float *loc, float *loc1, int seed)
{
HaloRen *har=0;
if(ma->material_type == MA_TYPE_WIRE)
static_particle_wire(obr, ma, loc, loc1, sd->first, sd->line);
else if(ma->material_type == MA_TYPE_HALO) {
har= RE_inithalo_particle(re, obr, dm, ma, loc, loc1, sd->orco, sd->uvco, sd->size, 1.0, seed);
if(har) har->lay= obr->ob->lay;
}
else
static_particle_strand(re, obr, ma, sd, loc, loc1);
}
static void particle_billboard(Render *re, ObjectRen *obr, Material *ma, ParticleBillboardData *bb)
{
VlakRen *vlr;
MTFace *mtf;
float xvec[3], yvec[3], zvec[3], bb_center[3];
float uvx = 0.0f, uvy = 0.0f, uvdx = 1.0f, uvdy = 1.0f, time = 0.0f;
vlr= RE_findOrAddVlak(obr, obr->totvlak++);
vlr->v1= RE_findOrAddVert(obr, obr->totvert++);
vlr->v2= RE_findOrAddVert(obr, obr->totvert++);
vlr->v3= RE_findOrAddVert(obr, obr->totvert++);
vlr->v4= RE_findOrAddVert(obr, obr->totvert++);
psys_make_billboard(bb, xvec, yvec, zvec, bb_center);
VECADD(vlr->v1->co, bb_center, xvec);
VECADD(vlr->v1->co, vlr->v1->co, yvec);
MTC_Mat4MulVecfl(re->viewmat, vlr->v1->co);
VECSUB(vlr->v2->co, bb_center, xvec);
VECADD(vlr->v2->co, vlr->v2->co, yvec);
MTC_Mat4MulVecfl(re->viewmat, vlr->v2->co);
VECSUB(vlr->v3->co, bb_center, xvec);
VECSUB(vlr->v3->co, vlr->v3->co, yvec);
MTC_Mat4MulVecfl(re->viewmat, vlr->v3->co);
VECADD(vlr->v4->co, bb_center, xvec);
VECSUB(vlr->v4->co, vlr->v4->co, yvec);
MTC_Mat4MulVecfl(re->viewmat, vlr->v4->co);
CalcNormFloat4(vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co, vlr->n);
VECCOPY(vlr->v1->n,vlr->n);
VECCOPY(vlr->v2->n,vlr->n);
VECCOPY(vlr->v3->n,vlr->n);
VECCOPY(vlr->v4->n,vlr->n);
vlr->mat= ma;
vlr->ec= ME_V2V3;
if(bb->uv_split > 1){
uvdx = uvdy = 1.0f / (float)bb->uv_split;
if(bb->anim == PART_BB_ANIM_TIME) {
if(bb->split_offset == PART_BB_OFF_NONE)
time = bb->time;
else if(bb->split_offset == PART_BB_OFF_LINEAR)
time = (float)fmod(bb->time + (float)bb->num / (float)(bb->uv_split * bb->uv_split), 1.0f);
else /* split_offset==PART_BB_OFF_RANDOM */
time = (float)fmod(bb->time + bb->random, 1.0f);
}
else if(bb->anim == PART_BB_ANIM_ANGLE) {
if(bb->align == PART_BB_VIEW) {
time = (float)fmod((bb->tilt + 1.0f) / 2.0f, 1.0);
}
else{
float axis1[3] = {0.0f,0.0f,0.0f};
float axis2[3] = {0.0f,0.0f,0.0f};
axis1[(bb->align + 1) % 3] = 1.0f;
axis2[(bb->align + 2) % 3] = 1.0f;
if(bb->lock == 0) {
zvec[bb->align] = 0.0f;
Normalize(zvec);
}
time = saacos(Inpf(zvec, axis1)) / (float)M_PI;
if(Inpf(zvec, axis2) < 0.0f)
time = 1.0f - time / 2.0f;
else
time = time / 2.0f;
}
if(bb->split_offset == PART_BB_OFF_LINEAR)
time = (float)fmod(bb->time + (float)bb->num / (float)(bb->uv_split * bb->uv_split), 1.0f);
else if(bb->split_offset == PART_BB_OFF_RANDOM)
time = (float)fmod(bb->time + bb->random, 1.0f);
}
else{
if(bb->split_offset == PART_BB_OFF_NONE)
time = 0.0f;
else if(bb->split_offset == PART_BB_OFF_LINEAR)
time = (float)fmod((float)bb->num /(float)(bb->uv_split * bb->uv_split) , 1.0f);
else /* split_offset==PART_BB_OFF_RANDOM */
time = bb->random;
}
uvx = uvdx * floor((float)(bb->uv_split * bb->uv_split) * (float)fmod((double)time, (double)uvdx));
uvy = uvdy * floor((1.0f - time) * (float)bb->uv_split);
if(fmod(time, 1.0f / bb->uv_split) == 0.0f)
uvy -= uvdy;
}
/* normal UVs */
if(bb->uv[0] >= 0){
mtf = RE_vlakren_get_tface(obr, vlr, bb->uv[0], NULL, 1);
mtf->uv[0][0] = 1.0f;
mtf->uv[0][1] = 1.0f;
mtf->uv[1][0] = 0.0f;
mtf->uv[1][1] = 1.0f;
mtf->uv[2][0] = 0.0f;
mtf->uv[2][1] = 0.0f;
mtf->uv[3][0] = 1.0f;
mtf->uv[3][1] = 0.0f;
}
/* time-index UVs */
if(bb->uv[1] >= 0){
mtf = RE_vlakren_get_tface(obr, vlr, bb->uv[1], NULL, 1);
mtf->uv[0][0] = mtf->uv[1][0] = mtf->uv[2][0] = mtf->uv[3][0] = bb->time;
mtf->uv[0][1] = mtf->uv[1][1] = mtf->uv[2][1] = mtf->uv[3][1] = (float)bb->num/(float)bb->totnum;
}
/* split UVs */
if(bb->uv_split > 1 && bb->uv[2] >= 0){
mtf = RE_vlakren_get_tface(obr, vlr, bb->uv[2], NULL, 1);
mtf->uv[0][0] = uvx + uvdx;
mtf->uv[0][1] = uvy + uvdy;
mtf->uv[1][0] = uvx;
mtf->uv[1][1] = uvy + uvdy;
mtf->uv[2][0] = uvx;
mtf->uv[2][1] = uvy;
mtf->uv[3][0] = uvx + uvdx;
mtf->uv[3][1] = uvy;
}
}
static void particle_normal_ren(short ren_as, ParticleSettings *part, Render *re, ObjectRen *obr, DerivedMesh *dm, Material *ma, ParticleStrandData *sd, ParticleBillboardData *bb, ParticleKey *state, int seed, float hasize)
{
float loc[3], loc0[3], loc1[3], vel[3];
VECCOPY(loc, state->co);
if(ren_as != PART_DRAW_BB)
MTC_Mat4MulVecfl(re->viewmat, loc);
switch(ren_as) {
case PART_DRAW_LINE:
sd->line = 1;
sd->time = 0.0f;
sd->size = hasize;
VECCOPY(vel, state->vel);
MTC_Mat4Mul3Vecfl(re->viewmat, vel);
Normalize(vel);
if(part->draw & PART_DRAW_VEL_LENGTH)
VecMulf(vel, VecLength(state->vel));
VECADDFAC(loc0, loc, vel, -part->draw_line[0]);
VECADDFAC(loc1, loc, vel, part->draw_line[1]);
particle_curve(re, obr, dm, ma, sd, loc0, loc1, seed);
break;
case PART_DRAW_BB:
VECCOPY(bb->vec, loc);
VECCOPY(bb->vel, state->vel);
particle_billboard(re, obr, ma, bb);
break;
default:
{
HaloRen *har=0;
har = RE_inithalo_particle(re, obr, dm, ma, loc, NULL, sd->orco, sd->uvco, hasize, 0.0, seed);
if(har) har->lay= obr->ob->lay;
break;
}
}
}
static void get_particle_uvco_mcol(short from, DerivedMesh *dm, float *fuv, int num, ParticleStrandData *sd)
{
int i;
/* get uvco */
if(sd->uvco && ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)) {
for(i=0; i<sd->totuv; i++) {
if(num != DMCACHE_NOTFOUND) {
MFace *mface = dm->getFaceData(dm, num, CD_MFACE);
MTFace *mtface = (MTFace*)CustomData_get_layer_n(&dm->faceData, CD_MTFACE, i);
mtface += num;
psys_interpolate_uvs(mtface, mface->v4, fuv, sd->uvco + 2 * i);
}
else {
sd->uvco[2*i] = 0.0f;
sd->uvco[2*i + 1] = 0.0f;
}
}
}
/* get mcol */
if(sd->mcol && ELEM(from,PART_FROM_FACE,PART_FROM_VOLUME)) {
for(i=0; i<sd->totcol; i++) {
if(num != DMCACHE_NOTFOUND) {
MFace *mface = dm->getFaceData(dm, num, CD_MFACE);
MCol *mc = (MCol*)CustomData_get_layer_n(&dm->faceData, CD_MCOL, i);
mc += num * 4;
psys_interpolate_mcol(mc, mface->v4, fuv, sd->mcol + i);
}
else
memset(&sd->mcol[i], 0, sizeof(MCol));
}
}
}
static int render_new_particle_system(Render *re, ObjectRen *obr, ParticleSystem *psys, int timeoffset)
{
Object *ob= obr->ob;
Object *tob=0;
Material *ma=0;
ParticleSystemModifierData *psmd;
ParticleSystem *tpsys=0;
ParticleSettings *part, *tpart=0;
ParticleData *pars, *pa=0,*tpa=0;
ParticleKey *states=0;
ParticleKey state;
ParticleCacheKey *cache=0;
ParticleBillboardData bb;
ParticleStrandData sd;
StrandBuffer *strandbuf=0;
StrandVert *svert=0;
StrandBound *sbound= 0;
StrandRen *strand=0;
RNG *rng= 0;
float loc[3],loc1[3],loc0[3],mat[4][4],nmat[3][3],co[3],nor[3],time;
float strandlen=0.0f, curlen=0.0f;
float hasize, pa_size, r_tilt, r_length, cfra=bsystem_time(re->scene, ob, (float)re->scene->r.cfra, 0.0);
float pa_time, pa_birthtime, pa_dietime;
float random, simplify[2];
int i, a, k, max_k=0, totpart, dosimplify = 0, dosurfacecache = 0;
int totchild=0;
int seed, path_nbr=0, orco1=0, num;
int totface, *origindex = 0;
char **uv_name=0;
/* 1. check that everything is ok & updated */
if(psys==NULL)
return 0;
totchild=psys->totchild;
part=psys->part;
pars=psys->particles;
if(part==NULL || pars==NULL || !psys_check_enabled(ob, psys))
return 0;
if(part->ren_as==PART_DRAW_OB || part->ren_as==PART_DRAW_GR || part->ren_as==PART_DRAW_NOT)
return 1;
/* 2. start initialising things */
if(part->phystype==PART_PHYS_KEYED)
psys_count_keyed_targets(ob,psys);
/* last possibility to bail out! */
psmd= psys_get_modifier(ob,psys);
if(!(psmd->modifier.mode & eModifierMode_Render))
return 0;
if(G.rendering == 0) { /* preview render */
totchild = (int)((float)totchild * (float)part->disp / 100.0f);
}
psys->flag |= PSYS_DRAWING;
rng= rng_new(psys->seed);
totpart=psys->totpart;
memset(&sd, 0, sizeof(ParticleStrandData));
sd.override_uv = -1;
/* 2.1 setup material stff */
ma= give_render_material(re, ob, part->omat);
#if 0 // XXX old animation system
if(ma->ipo){
calc_ipo(ma->ipo, cfra);
execute_ipo((ID *)ma, ma->ipo);
}
#endif // XXX old animation system
hasize = ma->hasize;
seed = ma->seed1;
re->flag |= R_HALO;
RE_set_customdata_names(obr, &psmd->dm->faceData);
sd.totuv = CustomData_number_of_layers(&psmd->dm->faceData, CD_MTFACE);
sd.totcol = CustomData_number_of_layers(&psmd->dm->faceData, CD_MCOL);
if(ma->texco & TEXCO_UV && sd.totuv) {
sd.uvco = MEM_callocN(sd.totuv * 2 * sizeof(float), "particle_uvs");
if(ma->strand_uvname[0]) {
sd.override_uv = CustomData_get_named_layer_index(&psmd->dm->faceData, CD_MTFACE, ma->strand_uvname);
sd.override_uv -= CustomData_get_layer_index(&psmd->dm->faceData, CD_MTFACE);
}
}
else
sd.uvco = NULL;
if(sd.totcol)
sd.mcol = MEM_callocN(sd.totcol * sizeof(MCol), "particle_mcols");
/* 2.2 setup billboards */
if(part->ren_as == PART_DRAW_BB) {
int first_uv = CustomData_get_layer_index(&psmd->dm->faceData, CD_MTFACE);
bb.uv[0] = CustomData_get_named_layer_index(&psmd->dm->faceData, CD_MTFACE, psys->bb_uvname[0]);
if(bb.uv[0] < 0)
bb.uv[0] = CustomData_get_active_layer_index(&psmd->dm->faceData, CD_MTFACE);
bb.uv[1] = CustomData_get_named_layer_index(&psmd->dm->faceData, CD_MTFACE, psys->bb_uvname[1]);
bb.uv[2] = CustomData_get_named_layer_index(&psmd->dm->faceData, CD_MTFACE, psys->bb_uvname[2]);
if(first_uv >= 0) {
bb.uv[0] -= first_uv;
bb.uv[1] -= first_uv;
bb.uv[2] -= first_uv;
}
bb.align = part->bb_align;
bb.anim = part->bb_anim;
bb.lock = part->draw & PART_DRAW_BB_LOCK;
bb.ob = (part->bb_ob ? part->bb_ob : re->scene->camera);
bb.offset[0] = part->bb_offset[0];
bb.offset[1] = part->bb_offset[1];
bb.split_offset = part->bb_split_offset;
bb.totnum = totpart+totchild;
bb.uv_split = part->bb_uv_split;
}
#if 0 // XXX old animation system
/* 2.3 setup time */
if(part->flag&PART_ABS_TIME && part->ipo) {
calc_ipo(part->ipo, cfra);
execute_ipo((ID *)part, part->ipo);
}
if(part->flag & PART_GLOB_TIME)
#endif // XXX old animation system
cfra = bsystem_time(re->scene, 0, (float)re->scene->r.cfra, 0.0);
/* 2.4 setup reactors */
if(part->type == PART_REACTOR){
psys_get_reactor_target(ob, psys, &tob, &tpsys);
if(tpsys && (part->from==PART_FROM_PARTICLE || part->phystype==PART_PHYS_NO)){
psmd = psys_get_modifier(tob,tpsys);
tpart = tpsys->part;
}
}
/* 2.5 setup matrices */
MTC_Mat4MulMat4(mat, ob->obmat, re->viewmat);
MTC_Mat4Invert(ob->imat, mat); /* need to be that way, for imat texture */
Mat3CpyMat4(nmat, ob->imat);
Mat3Transp(nmat);
/* 2.6 setup strand rendering */
if(part->ren_as == PART_DRAW_PATH && psys->pathcache){
path_nbr=(int)pow(2.0,(double) part->ren_step);
if(path_nbr) {
if(!ELEM(ma->material_type, MA_TYPE_HALO, MA_TYPE_WIRE)) {
sd.orco = MEM_mallocN(3*sizeof(float)*(totpart+totchild), "particle orcos");
set_object_orco(re, psys, sd.orco);
}
}
if(part->draw & PART_DRAW_REN_ADAPT) {
sd.adapt = 1;
sd.adapt_pix = (float)part->adapt_pix;
sd.adapt_angle = cos((float)part->adapt_angle * (float)(M_PI / 180.0));
}
if(re->r.renderer==R_INTERN && part->draw&PART_DRAW_REN_STRAND) {
strandbuf= RE_addStrandBuffer(obr, (totpart+totchild)*(path_nbr+1));
strandbuf->ma= ma;
strandbuf->lay= ob->lay;
Mat4CpyMat4(strandbuf->winmat, re->winmat);
strandbuf->winx= re->winx;
strandbuf->winy= re->winy;
strandbuf->maxdepth= 2;
strandbuf->adaptcos= cos((float)part->adapt_angle*(float)(M_PI/180.0));
strandbuf->overrideuv= sd.override_uv;
strandbuf->minwidth= ma->strand_min;
if(ma->strand_widthfade == 0.0f)
strandbuf->widthfade= 0.0f;
else if(ma->strand_widthfade >= 1.0f)
strandbuf->widthfade= 2.0f - ma->strand_widthfade;
else
strandbuf->widthfade= 1.0f/MAX2(ma->strand_widthfade, 1e-5f);
if(part->flag & PART_HAIR_BSPLINE)
strandbuf->flag |= R_STRAND_BSPLINE;
if(ma->mode & MA_STR_B_UNITS)
strandbuf->flag |= R_STRAND_B_UNITS;
svert= strandbuf->vert;
if(re->r.mode & R_SPEED)
dosurfacecache= 1;
else if((re->wrld.mode & WO_AMB_OCC) && (re->wrld.ao_gather_method == WO_AOGATHER_APPROX))
if(ma->amb != 0.0f)
dosurfacecache= 1;
totface= psmd->dm->getNumFaces(psmd->dm);
origindex= psmd->dm->getFaceDataArray(psmd->dm, CD_ORIGINDEX);
if(origindex) {
for(a=0; a<totface; a++)
strandbuf->totbound= MAX2(strandbuf->totbound, origindex[a]);
strandbuf->totbound++;
}
strandbuf->totbound++;
strandbuf->bound= MEM_callocN(sizeof(StrandBound)*strandbuf->totbound, "StrandBound");
sbound= strandbuf->bound;
sbound->start= sbound->end= 0;
}
}
if(sd.orco == 0) {
sd.orco = MEM_mallocN(3 * sizeof(float), "particle orco");
orco1 = 1;
}
if(path_nbr == 0)
psys->lattice = psys_get_lattice(re->scene, ob, psys);
/* 3. start creating renderable things */
for(a=0,pa=pars; a<totpart+totchild; a++, pa++, seed++) {
random = rng_getFloat(rng);
/* setup per particle individual stuff */
if(a<totpart){
if(pa->flag & PARS_UNEXIST) continue;
pa_time=(cfra-pa->time)/pa->lifetime;
pa_birthtime = pa->time;
pa_dietime = pa->dietime;
#if 0 // XXX old animation system
if((part->flag&PART_ABS_TIME) == 0){
if(ma->ipo) {
/* correction for lifetime */
calc_ipo(ma->ipo, 100.0f * pa_time);
execute_ipo((ID *)ma, ma->ipo);
}
if(part->ipo){
/* correction for lifetime */
calc_ipo(part->ipo, 100.0f*pa_time);
execute_ipo((ID *)part, part->ipo);
}
}
#endif // XXX old animation system
hasize = ma->hasize;
/* get orco */
if(tpsys && (part->from==PART_FROM_PARTICLE || part->phystype==PART_PHYS_NO)){
tpa=tpsys->particles+pa->num;
psys_particle_on_emitter(psmd,tpart->from,tpa->num,pa->num_dmcache,tpa->fuv,tpa->foffset,co,nor,0,0,sd.orco,0);
}
else
psys_particle_on_emitter(psmd,part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co,nor,0,0,sd.orco,0);
/* get uvco & mcol */
num= pa->num_dmcache;
if(num == DMCACHE_NOTFOUND)
if(pa->num < psmd->dm->getNumFaces(psmd->dm))
num= pa->num;
get_particle_uvco_mcol(part->from, psmd->dm, pa->fuv, num, &sd);
pa_size = pa->size;
r_tilt = 1.0f + pa->r_ave[0];
r_length = 0.5f * (1.0f + pa->r_ave[1]);
if(path_nbr) {
cache = psys->pathcache[a];
max_k = (int)cache->steps;
}
if(totchild && (part->draw&PART_DRAW_PARENT)==0) continue;
}
else {
ChildParticle *cpa= psys->child+a-totpart;
if(path_nbr) {
cache = psys->childcache[a-totpart];
if(cache->steps < 0)
continue;
max_k = (int)cache->steps;
}
pa_time = psys_get_child_time(psys, cpa, cfra, &pa_birthtime, &pa_dietime);
#if 0 // XXX old animation system
if((part->flag & PART_ABS_TIME) == 0) {
if(ma->ipo){
/* correction for lifetime */
calc_ipo(ma->ipo, 100.0f * pa_time);
execute_ipo((ID *)ma, ma->ipo);
}
if(part->ipo) {
/* correction for lifetime */
calc_ipo(part->ipo, 100.0f * pa_time);
execute_ipo((ID *)part, part->ipo);
}
}
#endif // XXX old animation system
pa_size = psys_get_child_size(psys, cpa, cfra, &pa_time);
r_tilt = 2.0f * cpa->rand[2];
r_length = cpa->rand[1];
num = cpa->num;
/* get orco */
if(part->childtype == PART_CHILD_FACES) {
psys_particle_on_emitter(psmd,
PART_FROM_FACE, cpa->num,DMCACHE_ISCHILD,
cpa->fuv,cpa->foffset,co,nor,0,0,sd.orco,0);
}
else {
ParticleData *par = psys->particles + cpa->parent;
psys_particle_on_emitter(psmd, part->from,
par->num,DMCACHE_ISCHILD,par->fuv,
par->foffset,co,nor,0,0,sd.orco,0);
}
/* get uvco & mcol */
if(part->from!=PART_FROM_PARTICLE && part->childtype==PART_CHILD_FACES) {
get_particle_uvco_mcol(PART_FROM_FACE, psmd->dm, cpa->fuv, cpa->num, &sd);
}
else {
ParticleData *parent = psys->particles + cpa->parent;
num = parent->num_dmcache;
if(num == DMCACHE_NOTFOUND)
if(parent->num < psmd->dm->getNumFaces(psmd->dm))
num = parent->num;
get_particle_uvco_mcol(part->from, psmd->dm, parent->fuv, num, &sd);
}
dosimplify = psys_render_simplify_params(psys, cpa, simplify);
if(strandbuf) {
if(origindex[cpa->num]+1 > sbound - strandbuf->bound) {
sbound= strandbuf->bound + origindex[cpa->num]+1;
sbound->start= sbound->end= obr->totstrand;
}
}
}
/* surface normal shading setup */
if(ma->mode_l & MA_STR_SURFDIFF) {
Mat3MulVecfl(nmat, nor);
sd.surfnor= nor;
}
else
sd.surfnor= NULL;
/* strand render setup */
if(strandbuf) {
strand= RE_findOrAddStrand(obr, obr->totstrand++);
strand->buffer= strandbuf;
strand->vert= svert;
VECCOPY(strand->orco, sd.orco);
if(dosimplify) {
float *ssimplify= RE_strandren_get_simplify(obr, strand, 1);
ssimplify[0]= simplify[0];
ssimplify[1]= simplify[1];
}
if(sd.surfnor) {
float *snor= RE_strandren_get_surfnor(obr, strand, 1);
VECCOPY(snor, sd.surfnor);
}
if(dosurfacecache && num >= 0) {
int *facenum= RE_strandren_get_face(obr, strand, 1);
*facenum= num;
}
if(sd.uvco) {
for(i=0; i<sd.totuv; i++) {
if(i != sd.override_uv) {
float *uv= RE_strandren_get_uv(obr, strand, i, NULL, 1);
uv[0]= sd.uvco[2*i];
uv[1]= sd.uvco[2*i+1];
}
}
}
if(sd.mcol) {
for(i=0; i<sd.totcol; i++) {
MCol *mc= RE_strandren_get_mcol(obr, strand, i, NULL, 1);
*mc = sd.mcol[i];
}
}
sbound->end++;
}
/* strandco computation setup */
if(path_nbr) {
strandlen= 0.0f;
curlen= 0.0f;
for(k=1; k<=path_nbr; k++)
if(k<=max_k)
strandlen += VecLenf((cache+k-1)->co, (cache+k)->co);
}
if(path_nbr) {
/* render strands */
for(k=0; k<=path_nbr; k++){
if(k<=max_k){
VECCOPY(state.co,(cache+k)->co);
VECCOPY(state.vel,(cache+k)->vel);
}
else
continue;
if(k > 0)
curlen += VecLenf((cache+k-1)->co, (cache+k)->co);
time= curlen/strandlen;
VECCOPY(loc,state.co);
MTC_Mat4MulVecfl(re->viewmat,loc);
if(strandbuf) {
VECCOPY(svert->co, loc);
svert->strandco= -1.0f + 2.0f*time;
svert++;
strand->totvert++;
}
else{
sd.size = hasize;
if(k==1){
sd.first = 1;
sd.time = 0.0f;
VECSUB(loc0,loc1,loc);
VECADD(loc0,loc1,loc0);
particle_curve(re, obr, psmd->dm, ma, &sd, loc1, loc0, seed);
}
sd.first = 0;
sd.time = time;
if(k)
particle_curve(re, obr, psmd->dm, ma, &sd, loc, loc1, seed);
VECCOPY(loc1,loc);
}
}
}
else {
/* render normal particles */
if(part->trail_count > 1) {
float length = part->path_end * (1.0 - part->randlength * r_length);
int trail_count = part->trail_count * (1.0 - part->randlength * r_length);
float ct = (part->draw & PART_ABS_PATH_TIME) ? cfra : pa_time;
float dt = length / (trail_count ? (float)trail_count : 1.0f);
for(i=0; i < trail_count; i++, ct -= dt) {
if(part->draw & PART_ABS_PATH_TIME) {
if(ct < pa_birthtime || ct > pa_dietime)
continue;
}
else if(ct < 0.0f || ct > 1.0f)
continue;
state.time = (part->draw & PART_ABS_PATH_TIME) ? -ct : ct;
psys_get_particle_on_path(re->scene,ob,psys,a,&state,1);
if(psys->parent)
Mat4MulVecfl(psys->parent->obmat, state.co);
if(part->ren_as == PART_DRAW_BB) {
bb.random = random;
bb.size = pa_size;
bb.tilt = part->bb_tilt * (1.0f - part->bb_rand_tilt * r_tilt);
bb.time = ct;
bb.num = a;
}
particle_normal_ren(part->ren_as, part, re, obr, psmd->dm, ma, &sd, &bb, &state, seed, hasize);
}
}
else {
time=0.0f;
state.time=cfra;
if(psys_get_particle_state(re->scene,ob,psys,a,&state,0)==0)
continue;
if(psys->parent)
Mat4MulVecfl(psys->parent->obmat, state.co);
if(part->ren_as == PART_DRAW_BB) {
bb.random = random;
bb.size = pa_size;
bb.tilt = part->bb_tilt * (1.0f - part->bb_rand_tilt * r_tilt);
bb.time = pa_time;
bb.num = a;
}
particle_normal_ren(part->ren_as, part, re, obr, psmd->dm, ma, &sd, &bb, &state, seed, hasize);
}
}
if(orco1==0)
sd.orco+=3;
if(re->test_break(re->tbh))
break;
}
if(dosurfacecache)
strandbuf->surface= cache_strand_surface(re, obr, psmd->dm, mat, timeoffset);
/* 4. clean up */
#if 0 // XXX old animation system
if(ma) do_mat_ipo(re->scene, ma);
#endif // XXX old animation system
if(orco1)
MEM_freeN(sd.orco);
if(sd.uvco)
MEM_freeN(sd.uvco);
if(sd.mcol)
MEM_freeN(sd.mcol);
if(uv_name)
MEM_freeN(uv_name);
if(states)
MEM_freeN(states);
rng_free(rng);
psys->flag &= ~PSYS_DRAWING;
if(psys->lattice){
end_latt_deform(psys->lattice);
psys->lattice= NULL;
}
if(path_nbr && (ma->mode_l & MA_TANGENT_STR)==0)
calc_vertexnormals(re, obr, 0, 0);
return 1;
}
/* ------------------------------------------------------------------------- */
/* Halo's */
/* ------------------------------------------------------------------------- */
static void make_render_halos(Render *re, ObjectRen *obr, Mesh *me, int totvert, MVert *mvert, Material *ma, float *orco)
{
Object *ob= obr->ob;
HaloRen *har;
float xn, yn, zn, nor[3], view[3];
float vec[3], hasize, mat[4][4], imat[3][3];
int a, ok, seed= ma->seed1;
MTC_Mat4MulMat4(mat, ob->obmat, re->viewmat);
MTC_Mat3CpyMat4(imat, ob->imat);
re->flag |= R_HALO;
for(a=0; a<totvert; a++, mvert++) {
ok= 1;
if(ok) {
hasize= ma->hasize;
VECCOPY(vec, mvert->co);
MTC_Mat4MulVecfl(mat, vec);
if(ma->mode & MA_HALOPUNO) {
xn= mvert->no[0];
yn= mvert->no[1];
zn= mvert->no[2];
/* transpose ! */
nor[0]= imat[0][0]*xn+imat[0][1]*yn+imat[0][2]*zn;
nor[1]= imat[1][0]*xn+imat[1][1]*yn+imat[1][2]*zn;
nor[2]= imat[2][0]*xn+imat[2][1]*yn+imat[2][2]*zn;
Normalize(nor);
VECCOPY(view, vec);
Normalize(view);
zn= nor[0]*view[0]+nor[1]*view[1]+nor[2]*view[2];
if(zn>=0.0) hasize= 0.0;
else hasize*= zn*zn*zn*zn;
}
if(orco) har= RE_inithalo(re, obr, ma, vec, NULL, orco, hasize, 0.0, seed);
else har= RE_inithalo(re, obr, ma, vec, NULL, mvert->co, hasize, 0.0, seed);
if(har) har->lay= ob->lay;
}
if(orco) orco+= 3;
seed++;
}
}
static int verghalo(const void *a1, const void *a2)
{
const HaloRen *har1= *(const HaloRen**)a1;
const HaloRen *har2= *(const HaloRen**)a2;
if(har1->zs < har2->zs) return 1;
else if(har1->zs > har2->zs) return -1;
return 0;
}
static void sort_halos(Render *re, int totsort)
{
ObjectRen *obr;
HaloRen *har= NULL, **haso;
int a;
if(re->tothalo==0) return;
re->sortedhalos= MEM_callocN(sizeof(HaloRen*)*re->tothalo, "sorthalos");
haso= re->sortedhalos;
for(obr=re->objecttable.first; obr; obr=obr->next) {
for(a=0; a<obr->tothalo; a++) {
if((a & 255)==0) har= obr->bloha[a>>8];
else har++;
*(haso++)= har;
}
}
qsort(re->sortedhalos, totsort, sizeof(HaloRen*), verghalo);
}
/* ------------------------------------------------------------------------- */
/* Displacement Mapping */
/* ------------------------------------------------------------------------- */
static short test_for_displace(Render *re, Object *ob)
{
/* return 1 when this object uses displacement textures. */
Material *ma;
int i;
for (i=1; i<=ob->totcol; i++) {
ma=give_render_material(re, ob, i);
/* ma->mapto is ORed total of all mapto channels */
if(ma && (ma->mapto & MAP_DISPLACE)) return 1;
}
return 0;
}
static void displace_render_vert(Render *re, ObjectRen *obr, ShadeInput *shi, VertRen *vr, int vindex, float *scale, float mat[][4], float imat[][3])
{
MTFace *tface;
short texco= shi->mat->texco;
float sample=0, displace[3];
char *name;
int i;
/* shi->co is current render coord, just make sure at least some vector is here */
VECCOPY(shi->co, vr->co);
/* vertex normal is used for textures type 'col' and 'var' */
VECCOPY(shi->vn, vr->n);
if(mat)
Mat4MulVecfl(mat, shi->co);
if(imat) {
shi->vn[0]= imat[0][0]*vr->n[0]+imat[0][1]*vr->n[1]+imat[0][2]*vr->n[2];
shi->vn[1]= imat[1][0]*vr->n[0]+imat[1][1]*vr->n[1]+imat[1][2]*vr->n[2];
shi->vn[2]= imat[2][0]*vr->n[0]+imat[2][1]*vr->n[1]+imat[2][2]*vr->n[2];
}
if (texco & TEXCO_UV) {
shi->totuv= 0;
shi->actuv= obr->actmtface;
for (i=0; (tface=RE_vlakren_get_tface(obr, shi->vlr, i, &name, 0)); i++) {
ShadeInputUV *suv= &shi->uv[i];
/* shi.uv needs scale correction from tface uv */
suv->uv[0]= 2*tface->uv[vindex][0]-1.0f;
suv->uv[1]= 2*tface->uv[vindex][1]-1.0f;
suv->uv[2]= 0.0f;
suv->name= name;
shi->totuv++;
}
}
/* set all rendercoords, 'texco' is an ORed value for all textures needed */
if ((texco & TEXCO_ORCO) && (vr->orco)) {
VECCOPY(shi->lo, vr->orco);
}
if (texco & TEXCO_STICKY) {
float *sticky= RE_vertren_get_sticky(obr, vr, 0);
if(sticky) {
shi->sticky[0]= sticky[0];
shi->sticky[1]= sticky[1];
shi->sticky[2]= 0.0f;
}
}
if (texco & TEXCO_GLOB) {
VECCOPY(shi->gl, shi->co);
MTC_Mat4MulVecfl(re->viewinv, shi->gl);
}
if (texco & TEXCO_NORM) {
VECCOPY(shi->orn, shi->vn);
}
if(texco & TEXCO_REFL) {
/* not (yet?) */
}
shi->displace[0]= shi->displace[1]= shi->displace[2]= 0.0;
do_material_tex(shi);
//printf("no=%f, %f, %f\nbefore co=%f, %f, %f\n", vr->n[0], vr->n[1], vr->n[2],
//vr->co[0], vr->co[1], vr->co[2]);
displace[0]= shi->displace[0] * scale[0];
displace[1]= shi->displace[1] * scale[1];
displace[2]= shi->displace[2] * scale[2];
if(mat)
Mat3MulVecfl(imat, displace);
/* 0.5 could become button once? */
vr->co[0] += displace[0];
vr->co[1] += displace[1];
vr->co[2] += displace[2];
//printf("after co=%f, %f, %f\n", vr->co[0], vr->co[1], vr->co[2]);
/* we just don't do this vertex again, bad luck for other face using same vertex with
different material... */
vr->flag |= 1;
/* Pass sample back so displace_face can decide which way to split the quad */
sample = shi->displace[0]*shi->displace[0];
sample += shi->displace[1]*shi->displace[1];
sample += shi->displace[2]*shi->displace[2];
vr->accum=sample;
/* Should be sqrt(sample), but I'm only looking for "bigger". Save the cycles. */
return;
}
static void displace_render_face(Render *re, ObjectRen *obr, VlakRen *vlr, float *scale, float mat[][4], float imat[][3])
{
ShadeInput shi;
/* Warning, This is not that nice, and possibly a bit slow,
however some variables were not initialized properly in, unless using shade_input_initialize(...), we need to do a memset */
memset(&shi, 0, sizeof(ShadeInput));
/* end warning! - Campbell */
/* set up shadeinput struct for multitex() */
/* memset above means we dont need this */
/*shi.osatex= 0;*/ /* signal not to use dx[] and dy[] texture AA vectors */
shi.obr= obr;
shi.vlr= vlr; /* current render face */
shi.mat= vlr->mat; /* current input material */
shi.thread= 0;
/* Displace the verts, flag is set when done */
if (!vlr->v1->flag)
displace_render_vert(re, obr, &shi, vlr->v1,0, scale, mat, imat);
if (!vlr->v2->flag)
displace_render_vert(re, obr, &shi, vlr->v2, 1, scale, mat, imat);
if (!vlr->v3->flag)
displace_render_vert(re, obr, &shi, vlr->v3, 2, scale, mat, imat);
if (vlr->v4) {
if (!vlr->v4->flag)
displace_render_vert(re, obr, &shi, vlr->v4, 3, scale, mat, imat);
/* closest in displace value. This will help smooth edges. */
if ( fabs(vlr->v1->accum - vlr->v3->accum) > fabs(vlr->v2->accum - vlr->v4->accum))
vlr->flag |= R_DIVIDE_24;
else vlr->flag &= ~R_DIVIDE_24;
}
/* Recalculate the face normal - if flipped before, flip now */
if(vlr->v4) {
CalcNormFloat4(vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co, vlr->n);
}
else {
CalcNormFloat(vlr->v3->co, vlr->v2->co, vlr->v1->co, vlr->n);
}
}
static void do_displacement(Render *re, ObjectRen *obr, float mat[][4], float imat[][3])
{
VertRen *vr;
VlakRen *vlr;
// float min[3]={1e30, 1e30, 1e30}, max[3]={-1e30, -1e30, -1e30};
float scale[3]={1.0f, 1.0f, 1.0f}, temp[3];//, xn
int i; //, texflag=0;
Object *obt;
/* Object Size with parenting */
obt=obr->ob;
while(obt){
VecAddf(temp, obt->size, obt->dsize);
scale[0]*=temp[0]; scale[1]*=temp[1]; scale[2]*=temp[2];
obt=obt->parent;
}
/* Clear all flags */
for(i=0; i<obr->totvert; i++){
vr= RE_findOrAddVert(obr, i);
vr->flag= 0;
}
for(i=0; i<obr->totvlak; i++){
vlr=RE_findOrAddVlak(obr, i);
displace_render_face(re, obr, vlr, scale, mat, imat);
}
/* Recalc vertex normals */
calc_vertexnormals(re, obr, 0, 0);
}
/* ------------------------------------------------------------------------- */
/* Metaball */
/* ------------------------------------------------------------------------- */
static void init_render_mball(Render *re, ObjectRen *obr)
{
Object *ob= obr->ob;
DispList *dl;
VertRen *ver;
VlakRen *vlr, *vlr1;
Material *ma;
float *data, *nors, *orco, mat[4][4], imat[3][3], xn, yn, zn;
int a, need_orco, vlakindex, *index;
if (ob!=find_basis_mball(re->scene, ob))
return;
MTC_Mat4MulMat4(mat, ob->obmat, re->viewmat);
MTC_Mat4Invert(ob->imat, mat);
MTC_Mat3CpyMat4(imat, ob->imat);
ma= give_render_material(re, ob, 1);
need_orco= 0;
if(ma->texco & TEXCO_ORCO) {
need_orco= 1;
}
makeDispListMBall(re->scene, ob);
dl= ob->disp.first;
if(dl==0) return;
data= dl->verts;
nors= dl->nors;
orco= get_object_orco(re, ob);
for(a=0; a<dl->nr; a++, data+=3, nors+=3, orco+=3) {
ver= RE_findOrAddVert(obr, obr->totvert++);
VECCOPY(ver->co, data);
MTC_Mat4MulVecfl(mat, ver->co);
/* render normals are inverted */
xn= -nors[0];
yn= -nors[1];
zn= -nors[2];
/* transpose ! */
ver->n[0]= imat[0][0]*xn+imat[0][1]*yn+imat[0][2]*zn;
ver->n[1]= imat[1][0]*xn+imat[1][1]*yn+imat[1][2]*zn;
ver->n[2]= imat[2][0]*xn+imat[2][1]*yn+imat[2][2]*zn;
Normalize(ver->n);
//if(ob->transflag & OB_NEG_SCALE) VecMulf(ver->n. -1.0);
if(need_orco) ver->orco= orco;
}
index= dl->index;
for(a=0; a<dl->parts; a++, index+=4) {
vlr= RE_findOrAddVlak(obr, obr->totvlak++);
vlr->v1= RE_findOrAddVert(obr, index[0]);
vlr->v2= RE_findOrAddVert(obr, index[1]);
vlr->v3= RE_findOrAddVert(obr, index[2]);
vlr->v4= 0;
if(ob->transflag & OB_NEG_SCALE)
CalcNormFloat(vlr->v1->co, vlr->v2->co, vlr->v3->co, vlr->n);
else
CalcNormFloat(vlr->v3->co, vlr->v2->co, vlr->v1->co, vlr->n);
vlr->mat= ma;
vlr->flag= ME_SMOOTH+R_NOPUNOFLIP;
vlr->ec= 0;
/* mball -too bad- always has triangles, because quads can be non-planar */
if(index[3] && index[3]!=index[2]) {
vlr1= RE_findOrAddVlak(obr, obr->totvlak++);
vlakindex= vlr1->index;
*vlr1= *vlr;
vlr1->index= vlakindex;
vlr1->v2= vlr1->v3;
vlr1->v3= RE_findOrAddVert(obr, index[3]);
if(ob->transflag & OB_NEG_SCALE)
CalcNormFloat(vlr1->v1->co, vlr1->v2->co, vlr1->v3->co, vlr1->n);
else
CalcNormFloat(vlr1->v3->co, vlr1->v2->co, vlr1->v1->co, vlr1->n);
}
}
/* enforce display lists remade */
freedisplist(&ob->disp);
/* this enforces remake for real, orco displist is small (in scale) */
ob->recalc |= OB_RECALC_DATA;
}
/* ------------------------------------------------------------------------- */
/* Surfaces and Curves */
/* ------------------------------------------------------------------------- */
/* returns amount of vertices added for orco */
static int dl_surf_to_renderdata(ObjectRen *obr, DispList *dl, Material **matar, float *orco, float mat[4][4])
{
Object *ob= obr->ob;
VertRen *v1, *v2, *v3, *v4, *ver;
VlakRen *vlr, *vlr1, *vlr2, *vlr3;
Curve *cu= ob->data;
float *data, n1[3];
int u, v, orcoret= 0;
int p1, p2, p3, p4, a;
int sizeu, nsizeu, sizev, nsizev;
int startvert, startvlak;
startvert= obr->totvert;
nsizeu = sizeu = dl->parts; nsizev = sizev = dl->nr;
data= dl->verts;
for (u = 0; u < sizeu; u++) {
v1 = RE_findOrAddVert(obr, obr->totvert++); /* save this for possible V wrapping */
VECCOPY(v1->co, data); data += 3;
if(orco) {
v1->orco= orco; orco+= 3; orcoret++;
}
MTC_Mat4MulVecfl(mat, v1->co);
for (v = 1; v < sizev; v++) {
ver= RE_findOrAddVert(obr, obr->totvert++);
VECCOPY(ver->co, data); data += 3;
if(orco) {
ver->orco= orco; orco+= 3; orcoret++;
}
MTC_Mat4MulVecfl(mat, ver->co);
}
/* if V-cyclic, add extra vertices at end of the row */
if (dl->flag & DL_CYCL_U) {
ver= RE_findOrAddVert(obr, obr->totvert++);
VECCOPY(ver->co, v1->co);
if(orco) {
ver->orco= orco; orco+=3; orcoret++; //orcobase + 3*(u*sizev + 0);
}
}
}
/* Done before next loop to get corner vert */
if (dl->flag & DL_CYCL_U) nsizev++;
if (dl->flag & DL_CYCL_V) nsizeu++;
/* if U cyclic, add extra row at end of column */
if (dl->flag & DL_CYCL_V) {
for (v = 0; v < nsizev; v++) {
v1= RE_findOrAddVert(obr, startvert + v);
ver= RE_findOrAddVert(obr, obr->totvert++);
VECCOPY(ver->co, v1->co);
if(orco) {
ver->orco= orco; orco+=3; orcoret++; //ver->orco= orcobase + 3*(0*sizev + v);
}
}
}
sizeu = nsizeu;
sizev = nsizev;
startvlak= obr->totvlak;
for(u = 0; u < sizeu - 1; u++) {
p1 = startvert + u * sizev; /* walk through face list */
p2 = p1 + 1;
p3 = p2 + sizev;
p4 = p3 - 1;
for(v = 0; v < sizev - 1; v++) {
v1= RE_findOrAddVert(obr, p1);
v2= RE_findOrAddVert(obr, p2);
v3= RE_findOrAddVert(obr, p3);
v4= RE_findOrAddVert(obr, p4);
vlr= RE_findOrAddVlak(obr, obr->totvlak++);
vlr->v1= v1; vlr->v2= v2; vlr->v3= v3; vlr->v4= v4;
CalcNormFloat4(vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co, n1);
VECCOPY(vlr->n, n1);
vlr->mat= matar[ dl->col];
vlr->ec= ME_V1V2+ME_V2V3;
vlr->flag= dl->rt;
if( (cu->flag & CU_NOPUNOFLIP) ) {
vlr->flag |= R_NOPUNOFLIP;
}
VecAddf(v1->n, v1->n, n1);
VecAddf(v2->n, v2->n, n1);
VecAddf(v3->n, v3->n, n1);
VecAddf(v4->n, v4->n, n1);
p1++; p2++; p3++; p4++;
}
}
/* fix normals for U resp. V cyclic faces */
sizeu--; sizev--; /* dec size for face array */
if (dl->flag & DL_CYCL_V) {
for (v = 0; v < sizev; v++)
{
/* optimize! :*/
vlr= RE_findOrAddVlak(obr, UVTOINDEX(sizeu - 1, v));
vlr1= RE_findOrAddVlak(obr, UVTOINDEX(0, v));
VecAddf(vlr1->v1->n, vlr1->v1->n, vlr->n);
VecAddf(vlr1->v2->n, vlr1->v2->n, vlr->n);
VecAddf(vlr->v3->n, vlr->v3->n, vlr1->n);
VecAddf(vlr->v4->n, vlr->v4->n, vlr1->n);
}
}
if (dl->flag & DL_CYCL_U) {
for (u = 0; u < sizeu; u++)
{
/* optimize! :*/
vlr= RE_findOrAddVlak(obr, UVTOINDEX(u, 0));
vlr1= RE_findOrAddVlak(obr, UVTOINDEX(u, sizev-1));
VecAddf(vlr1->v2->n, vlr1->v2->n, vlr->n);
VecAddf(vlr1->v3->n, vlr1->v3->n, vlr->n);
VecAddf(vlr->v1->n, vlr->v1->n, vlr1->n);
VecAddf(vlr->v4->n, vlr->v4->n, vlr1->n);
}
}
/* last vertex is an extra case:
^ ()----()----()----()
| | | || |
u | |(0,n)||(0,0)|
| | || |
()====()====[]====()
| | || |
| |(m,n)||(m,0)|
| | || |
()----()----()----()
v ->
vertex [] is no longer shared, therefore distribute
normals of the surrounding faces to all of the duplicates of []
*/
if ((dl->flag & DL_CYCL_V) && (dl->flag & DL_CYCL_U))
{
vlr= RE_findOrAddVlak(obr, UVTOINDEX(sizeu - 1, sizev - 1)); /* (m,n) */
vlr1= RE_findOrAddVlak(obr, UVTOINDEX(0,0)); /* (0,0) */
VecAddf(n1, vlr->n, vlr1->n);
vlr2= RE_findOrAddVlak(obr, UVTOINDEX(0, sizev-1)); /* (0,n) */
VecAddf(n1, n1, vlr2->n);
vlr3= RE_findOrAddVlak(obr, UVTOINDEX(sizeu-1, 0)); /* (m,0) */
VecAddf(n1, n1, vlr3->n);
VECCOPY(vlr->v3->n, n1);
VECCOPY(vlr1->v1->n, n1);
VECCOPY(vlr2->v2->n, n1);
VECCOPY(vlr3->v4->n, n1);
}
for(a = startvert; a < obr->totvert; a++) {
ver= RE_findOrAddVert(obr, a);
Normalize(ver->n);
}
return orcoret;
}
static void init_render_surf(Render *re, ObjectRen *obr)
{
Object *ob= obr->ob;
Nurb *nu=0;
Curve *cu;
ListBase displist;
DispList *dl;
Material **matar;
float *orco=NULL, *orcobase=NULL, mat[4][4];
int a, totmat, need_orco=0;
cu= ob->data;
nu= cu->nurb.first;
if(nu==0) return;
MTC_Mat4MulMat4(mat, ob->obmat, re->viewmat);
MTC_Mat4Invert(ob->imat, mat);
/* material array */
totmat= ob->totcol+1;
matar= MEM_callocN(sizeof(Material*)*totmat, "init_render_surf matar");
for(a=0; a<totmat; a++) {
matar[a]= give_render_material(re, ob, a+1);
if(matar[a] && matar[a]->texco & TEXCO_ORCO)
need_orco= 1;
}
if(ob->parent && (ob->parent->type==OB_LATTICE)) need_orco= 1;
if(need_orco) orcobase= orco= get_object_orco(re, ob);
displist.first= displist.last= 0;
makeDispListSurf(re->scene, ob, &displist, 1, 0);
/* walk along displaylist and create rendervertices/-faces */
for(dl=displist.first; dl; dl=dl->next) {
/* watch out: u ^= y, v ^= x !! */
if(dl->type==DL_SURF)
orco+= 3*dl_surf_to_renderdata(obr, dl, matar, orco, mat);
}
freedisplist(&displist);
MEM_freeN(matar);
}
static void init_render_curve(Render *re, ObjectRen *obr, int timeoffset)
{
Object *ob= obr->ob;
Curve *cu;
VertRen *ver;
VlakRen *vlr;
DispList *dl;
ListBase olddl={NULL, NULL};
Material **matar;
float len, *data, *fp, *orco=NULL, *orcobase= NULL;
float n[3], mat[4][4];
int nr, startvert, startvlak, a, b;
int frontside, need_orco=0, totmat;
cu= ob->data;
if(ob->type==OB_FONT && cu->str==NULL) return;
else if(ob->type==OB_CURVE && cu->nurb.first==NULL) return;
/* no modifier call here, is in makedisp */
if(cu->resolu_ren)
SWAP(ListBase, olddl, cu->disp);
/* test displist */
if(cu->disp.first==NULL)
makeDispListCurveTypes(re->scene, ob, 0);
dl= cu->disp.first;
if(cu->disp.first==NULL) return;
MTC_Mat4MulMat4(mat, ob->obmat, re->viewmat);
MTC_Mat4Invert(ob->imat, mat);
/* material array */
totmat= ob->totcol+1;
matar= MEM_callocN(sizeof(Material*)*totmat, "init_render_surf matar");
for(a=0; a<totmat; a++) {
matar[a]= give_render_material(re, ob, a+1);
if(matar[a] && matar[a]->texco & TEXCO_ORCO)
need_orco= 1;
}
if(need_orco) orcobase=orco= get_object_orco(re, ob);
dl= cu->disp.first;
while(dl) {
if(dl->type==DL_INDEX3) {
int *index;
startvert= obr->totvert;
data= dl->verts;
n[0]= ob->imat[0][2];
n[1]= ob->imat[1][2];
n[2]= ob->imat[2][2];
Normalize(n);
for(a=0; a<dl->nr; a++, data+=3) {
ver= RE_findOrAddVert(obr, obr->totvert++);
VECCOPY(ver->co, data);
/* flip normal if face is backfacing, also used in face loop below */
if(ver->co[2] < 0.0) {
VECCOPY(ver->n, n);
ver->flag = 1;
}
else {
ver->n[0]= -n[0]; ver->n[1]= -n[1]; ver->n[2]= -n[2];
ver->flag = 0;
}
MTC_Mat4MulVecfl(mat, ver->co);
if (orco) {
ver->orco = orco;
orco += 3;
}
}
if(timeoffset==0) {
startvlak= obr->totvlak;
index= dl->index;
for(a=0; a<dl->parts; a++, index+=3) {
vlr= RE_findOrAddVlak(obr, obr->totvlak++);
vlr->v1= RE_findOrAddVert(obr, startvert+index[0]);
vlr->v2= RE_findOrAddVert(obr, startvert+index[1]);
vlr->v3= RE_findOrAddVert(obr, startvert+index[2]);
vlr->v4= NULL;
if(vlr->v1->flag) {
VECCOPY(vlr->n, n);
}
else {
vlr->n[0]= -n[0]; vlr->n[1]= -n[1]; vlr->n[2]= -n[2];
}
vlr->mat= matar[ dl->col ];
vlr->flag= 0;
if( (cu->flag & CU_NOPUNOFLIP) ) {
vlr->flag |= R_NOPUNOFLIP;
}
vlr->ec= 0;
}
}
}
else if (dl->type==DL_SURF) {
/* cyclic U means an extruded full circular curve, we skip bevel splitting then */
if (dl->flag & DL_CYCL_U) {
orco+= 3*dl_surf_to_renderdata(obr, dl, matar, orco, mat);
}
else {
int p1,p2,p3,p4;
fp= dl->verts;
startvert= obr->totvert;
nr= dl->nr*dl->parts;
while(nr--) {
ver= RE_findOrAddVert(obr, obr->totvert++);
VECCOPY(ver->co, fp);
MTC_Mat4MulVecfl(mat, ver->co);
fp+= 3;
if (orco) {
ver->orco = orco;
orco += 3;
}
}
if(dl->bevelSplitFlag || timeoffset==0) {
startvlak= obr->totvlak;
for(a=0; a<dl->parts; a++) {
frontside= (a >= dl->nr/2);
if (surfindex_displist(dl, a, &b, &p1, &p2, &p3, &p4)==0)
break;
p1+= startvert;
p2+= startvert;
p3+= startvert;
p4+= startvert;
for(; b<dl->nr; b++) {
vlr= RE_findOrAddVlak(obr, obr->totvlak++);
vlr->v1= RE_findOrAddVert(obr, p2);
vlr->v2= RE_findOrAddVert(obr, p1);
vlr->v3= RE_findOrAddVert(obr, p3);
vlr->v4= RE_findOrAddVert(obr, p4);
vlr->ec= ME_V2V3+ME_V3V4;
if(a==0) vlr->ec+= ME_V1V2;
vlr->flag= dl->rt;
/* this is not really scientific: the vertices
* 2, 3 en 4 seem to give better vertexnormals than 1 2 3:
* front and backside treated different!!
*/
if(frontside)
CalcNormFloat(vlr->v2->co, vlr->v3->co, vlr->v4->co, vlr->n);
else
CalcNormFloat(vlr->v1->co, vlr->v2->co, vlr->v3->co, vlr->n);
vlr->mat= matar[ dl->col ];
p4= p3;
p3++;
p2= p1;
p1++;
}
}
if (dl->bevelSplitFlag) {
for(a=0; a<dl->parts-1+!!(dl->flag&DL_CYCL_V); a++)
if(dl->bevelSplitFlag[a>>5]&(1<<(a&0x1F)))
split_v_renderfaces(obr, startvlak, startvert, dl->parts, dl->nr, a, dl->flag&DL_CYCL_V, dl->flag&DL_CYCL_U);
}
/* vertex normals */
for(a= startvlak; a<obr->totvlak; a++) {
vlr= RE_findOrAddVlak(obr, a);
VecAddf(vlr->v1->n, vlr->v1->n, vlr->n);
VecAddf(vlr->v3->n, vlr->v3->n, vlr->n);
VecAddf(vlr->v2->n, vlr->v2->n, vlr->n);
VecAddf(vlr->v4->n, vlr->v4->n, vlr->n);
}
for(a=startvert; a<obr->totvert; a++) {
ver= RE_findOrAddVert(obr, a);
len= Normalize(ver->n);
if(len==0.0) ver->flag= 1; /* flag abuse, its only used in zbuf now */
else ver->flag= 0;
}
for(a= startvlak; a<obr->totvlak; a++) {
vlr= RE_findOrAddVlak(obr, a);
if(vlr->v1->flag) VECCOPY(vlr->v1->n, vlr->n);
if(vlr->v2->flag) VECCOPY(vlr->v2->n, vlr->n);
if(vlr->v3->flag) VECCOPY(vlr->v3->n, vlr->n);
if(vlr->v4->flag) VECCOPY(vlr->v4->n, vlr->n);
}
}
}
}
dl= dl->next;
}
/* not very elegant... but we want original displist in UI */
if(cu->resolu_ren) {
freedisplist(&cu->disp);
SWAP(ListBase, olddl, cu->disp);
}
MEM_freeN(matar);
}
/* ------------------------------------------------------------------------- */
/* Mesh */
/* ------------------------------------------------------------------------- */
struct edgesort {
int v1, v2;
int f;
int i1, i2;
};
/* edges have to be added with lowest index first for sorting */
static void to_edgesort(struct edgesort *ed, int i1, int i2, int v1, int v2, int f)
{
if(v1>v2) {
SWAP(int, v1, v2);
SWAP(int, i1, i2);
}
ed->v1= v1;
ed->v2= v2;
ed->i1= i1;
ed->i2= i2;
ed->f = f;
}
static int vergedgesort(const void *v1, const void *v2)
{
const struct edgesort *x1=v1, *x2=v2;
if( x1->v1 > x2->v1) return 1;
else if( x1->v1 < x2->v1) return -1;
else if( x1->v2 > x2->v2) return 1;
else if( x1->v2 < x2->v2) return -1;
return 0;
}
static struct edgesort *make_mesh_edge_lookup(DerivedMesh *dm, int *totedgesort)
{
MFace *mf, *mface;
MTFace *tface=NULL;
struct edgesort *edsort, *ed;
unsigned int *mcol=NULL;
int a, totedge=0, totface;
mface= dm->getFaceArray(dm);
totface= dm->getNumFaces(dm);
tface= dm->getFaceDataArray(dm, CD_MTFACE);
mcol= dm->getFaceDataArray(dm, CD_MCOL);
if(mcol==NULL && tface==NULL) return NULL;
/* make sorted table with edges and face indices in it */
for(a= totface, mf= mface; a>0; a--, mf++) {
if(mf->v4) totedge+=4;
else if(mf->v3) totedge+=3;
}
if(totedge==0)
return NULL;
ed= edsort= MEM_callocN(totedge*sizeof(struct edgesort), "edgesort");
for(a=0, mf=mface; a<totface; a++, mf++) {
to_edgesort(ed++, 0, 1, mf->v1, mf->v2, a);
to_edgesort(ed++, 1, 2, mf->v2, mf->v3, a);
if(mf->v4) {
to_edgesort(ed++, 2, 3, mf->v3, mf->v4, a);
to_edgesort(ed++, 3, 0, mf->v4, mf->v1, a);
}
else if(mf->v3)
to_edgesort(ed++, 2, 3, mf->v3, mf->v1, a);
}
qsort(edsort, totedge, sizeof(struct edgesort), vergedgesort);
*totedgesort= totedge;
return edsort;
}
static void use_mesh_edge_lookup(ObjectRen *obr, DerivedMesh *dm, MEdge *medge, VlakRen *vlr, struct edgesort *edgetable, int totedge)
{
struct edgesort ed, *edp;
CustomDataLayer *layer;
MTFace *mtface, *mtf;
MCol *mcol, *mc;
int index, mtfn, mcn;
char *name;
if(medge->v1 < medge->v2) {
ed.v1= medge->v1;
ed.v2= medge->v2;
}
else {
ed.v1= medge->v2;
ed.v2= medge->v1;
}
edp= bsearch(&ed, edgetable, totedge, sizeof(struct edgesort), vergedgesort);
/* since edges have different index ordering, we have to duplicate mcol and tface */
if(edp) {
mtfn= mcn= 0;
for(index=0; index<dm->faceData.totlayer; index++) {
layer= &dm->faceData.layers[index];
name= layer->name;
if(layer->type == CD_MTFACE && mtfn < MAX_MTFACE) {
mtface= &((MTFace*)layer->data)[edp->f];
mtf= RE_vlakren_get_tface(obr, vlr, mtfn++, &name, 1);
*mtf= *mtface;
memcpy(mtf->uv[0], mtface->uv[edp->i1], sizeof(float)*2);
memcpy(mtf->uv[1], mtface->uv[edp->i2], sizeof(float)*2);
memcpy(mtf->uv[2], mtface->uv[1], sizeof(float)*2);
memcpy(mtf->uv[3], mtface->uv[1], sizeof(float)*2);
}
else if(layer->type == CD_MCOL && mcn < MAX_MCOL) {
mcol= &((MCol*)layer->data)[edp->f*4];
mc= RE_vlakren_get_mcol(obr, vlr, mcn++, &name, 1);
mc[0]= mcol[edp->i1];
mc[1]= mc[2]= mc[3]= mcol[edp->i2];
}
}
}
}
static void free_camera_inside_volumes(Render *re)
{
BLI_freelistN(&re->render_volumes_inside);
}
static void init_camera_inside_volumes(Render *re)
{
ObjectInstanceRen *obi;
VolumeOb *vo;
float co[3] = {0.f, 0.f, 0.f};
for(vo= re->volumes.first; vo; vo= vo->next) {
for(obi= re->instancetable.first; obi; obi= obi->next) {
if (obi->obr == vo->obr) {
if (point_inside_volume_objectinstance(obi, co)) {
MatInside *mi;
mi = MEM_mallocN(sizeof(MatInside), "camera inside material");
mi->ma = vo->ma;
BLI_addtail(&(re->render_volumes_inside), mi);
}
}
}
}
/* debug {
MatInside *m;
for (m=re->render_volumes_inside.first; m; m=m->next) {
printf("matinside: ma: %s \n", m->ma->id.name+2);
}
}*/
}
static void add_volume(Render *re, ObjectRen *obr, Material *ma)
{
struct VolumeOb *vo;
vo = MEM_mallocN(sizeof(VolumeOb), "volume object");
vo->ma = ma;
vo->obr = obr;
BLI_addtail(&re->volumes, vo);
}
static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset)
{
Object *ob= obr->ob;
Mesh *me;
MVert *mvert = NULL;
MFace *mface;
VlakRen *vlr; //, *vlr1;
VertRen *ver;
Material *ma;
MSticky *ms = NULL;
DerivedMesh *dm;
CustomDataMask mask;
float xn, yn, zn, imat[3][3], mat[4][4]; //nor[3],
float *orco=0;
int need_orco=0, need_stress=0, need_nmap_tangent=0, need_tangent=0;
int a, a1, ok, vertofs;
int end, do_autosmooth=0, totvert = 0;
int use_original_normals= 0;
me= ob->data;
MTC_Mat4MulMat4(mat, ob->obmat, re->viewmat);
MTC_Mat4Invert(ob->imat, mat);
MTC_Mat3CpyMat4(imat, ob->imat);
if(me->totvert==0)
return;
need_orco= 0;
for(a=1; a<=ob->totcol; a++) {
ma= give_render_material(re, ob, a);
if(ma) {
if(ma->texco & (TEXCO_ORCO|TEXCO_STRESS))
need_orco= 1;
if(ma->texco & TEXCO_STRESS)
need_stress= 1;
/* normalmaps, test if tangents needed, separated from shading */
if(ma->mode_l & MA_TANGENT_V) {
need_tangent= 1;
if(me->mtface==NULL)
need_orco= 1;
}
if(ma->mode_l & MA_NORMAP_TANG) {
if(me->mtface==NULL) {
need_orco= 1;
need_tangent= 1;
}
need_nmap_tangent= 1;
}
if (ma->material_type == MA_TYPE_VOLUME)
add_volume(re, obr, ma);
}
}
if(re->flag & R_NEED_TANGENT) {
/* exception for tangent space baking */
if(me->mtface==NULL) {
need_orco= 1;
need_tangent= 1;
}
need_nmap_tangent= 1;
}
/* check autosmooth and displacement, we then have to skip only-verts optimize */
do_autosmooth |= (me->flag & ME_AUTOSMOOTH);
if(do_autosmooth)
timeoffset= 0;
if(test_for_displace(re, ob ) )
timeoffset= 0;
mask= CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL;
if(!timeoffset)
if(need_orco)
mask |= CD_MASK_ORCO;
dm= mesh_create_derived_render(re->scene, ob, mask);
if(dm==NULL) return; /* in case duplicated object fails? */
if(mask & CD_MASK_ORCO) {
orco= dm->getVertDataArray(dm, CD_ORCO);
if(orco) {
orco= MEM_dupallocN(orco);
set_object_orco(re, ob, orco);
}
}
mvert= dm->getVertArray(dm);
totvert= dm->getNumVerts(dm);
/* attempt to autsmooth on original mesh, only without subsurf */
if(do_autosmooth && me->totvert==totvert && me->totface==dm->getNumFaces(dm))
use_original_normals= 1;
ms = (totvert==me->totvert)?me->msticky:NULL;
ma= give_render_material(re, ob, 1);
if(ma->material_type == MA_TYPE_HALO) {
make_render_halos(re, obr, me, totvert, mvert, ma, orco);
}
else {
for(a=0; a<totvert; a++, mvert++) {
ver= RE_findOrAddVert(obr, obr->totvert++);
VECCOPY(ver->co, mvert->co);
if(do_autosmooth==0) /* autosmooth on original unrotated data to prevent differences between frames */
MTC_Mat4MulVecfl(mat, ver->co);
if(orco) {
ver->orco= orco;
orco+=3;
}
if(ms) {
float *sticky= RE_vertren_get_sticky(obr, ver, 1);
sticky[0]= ms->co[0];
sticky[1]= ms->co[1];
ms++;
}
}
if(!timeoffset) {
/* store customdata names, because DerivedMesh is freed */
RE_set_customdata_names(obr, &dm->faceData);
/* still to do for keys: the correct local texture coordinate */
/* faces in order of color blocks */
vertofs= obr->totvert - totvert;
for(a1=0; (a1<ob->totcol || (a1==0 && ob->totcol==0)); a1++) {
ma= give_render_material(re, ob, a1+1);
/* test for 100% transparant */
ok= 1;
if(ma->alpha==0.0 && ma->spectra==0.0) {
ok= 0;
/* texture on transparency? */
for(a=0; a<MAX_MTEX; a++) {
if(ma->mtex[a] && ma->mtex[a]->tex) {
if(ma->mtex[a]->mapto & MAP_ALPHA) ok= 1;
}
}
}
/* if wire material, and we got edges, don't do the faces */
if(ma->material_type == MA_TYPE_WIRE) {
end= dm->getNumEdges(dm);
if(end) ok= 0;
}
if(ok) {
end= dm->getNumFaces(dm);
mface= dm->getFaceArray(dm);
for(a=0; a<end; a++, mface++) {
int v1, v2, v3, v4, flag;
if( mface->mat_nr==a1 ) {
float len;
v1= mface->v1;
v2= mface->v2;
v3= mface->v3;
v4= mface->v4;
flag= mface->flag & ME_SMOOTH;
vlr= RE_findOrAddVlak(obr, obr->totvlak++);
vlr->v1= RE_findOrAddVert(obr, vertofs+v1);
vlr->v2= RE_findOrAddVert(obr, vertofs+v2);
vlr->v3= RE_findOrAddVert(obr, vertofs+v3);
if(v4) vlr->v4= RE_findOrAddVert(obr, vertofs+v4);
else vlr->v4= 0;
/* render normals are inverted in render */
if(use_original_normals) {
MFace *mf= me->mface+a;
MVert *mv= me->mvert;
if(vlr->v4)
len= CalcNormFloat4( mv[mf->v4].co, mv[mf->v3].co, mv[mf->v2].co, mv[mf->v1].co, vlr->n);
else
len= CalcNormFloat(mv[mf->v3].co, mv[mf->v2].co, mv[mf->v1].co, vlr->n);
}
else {
if(vlr->v4)
len= CalcNormFloat4(vlr->v4->co, vlr->v3->co, vlr->v2->co, vlr->v1->co, vlr->n);
else
len= CalcNormFloat(vlr->v3->co, vlr->v2->co, vlr->v1->co, vlr->n);
}
vlr->mat= ma;
vlr->flag= flag;
if((me->flag & ME_NOPUNOFLIP) ) {
vlr->flag |= R_NOPUNOFLIP;
}
vlr->ec= 0; /* mesh edges rendered separately */
if(len==0) obr->totvlak--;
else {
CustomDataLayer *layer;
MTFace *mtface, *mtf;
MCol *mcol, *mc;
int index, mtfn= 0, mcn= 0;
char *name;
for(index=0; index<dm->faceData.totlayer; index++) {
layer= &dm->faceData.layers[index];
name= layer->name;
if(layer->type == CD_MTFACE && mtfn < MAX_MTFACE) {
mtf= RE_vlakren_get_tface(obr, vlr, mtfn++, &name, 1);
mtface= (MTFace*)layer->data;
*mtf= mtface[a];
}
else if(layer->type == CD_MCOL && mcn < MAX_MCOL) {
mc= RE_vlakren_get_mcol(obr, vlr, mcn++, &name, 1);
mcol= (MCol*)layer->data;
memcpy(mc, &mcol[a*4], sizeof(MCol)*4);
}
}
}
}
}
}
}
/* exception... we do edges for wire mode. potential conflict when faces exist... */
end= dm->getNumEdges(dm);
mvert= dm->getVertArray(dm);
ma= give_render_material(re, ob, 1);
if(end && (ma->material_type == MA_TYPE_WIRE)) {
MEdge *medge;
struct edgesort *edgetable;
int totedge= 0;
medge= dm->getEdgeArray(dm);
/* we want edges to have UV and vcol too... */
edgetable= make_mesh_edge_lookup(dm, &totedge);
for(a1=0; a1<end; a1++, medge++) {
if (medge->flag&ME_EDGERENDER) {
MVert *v0 = &mvert[medge->v1];
MVert *v1 = &mvert[medge->v2];
vlr= RE_findOrAddVlak(obr, obr->totvlak++);
vlr->v1= RE_findOrAddVert(obr, vertofs+medge->v1);
vlr->v2= RE_findOrAddVert(obr, vertofs+medge->v2);
vlr->v3= vlr->v2;
vlr->v4= NULL;
if(edgetable)
use_mesh_edge_lookup(obr, dm, medge, vlr, edgetable, totedge);
xn= -(v0->no[0]+v1->no[0]);
yn= -(v0->no[1]+v1->no[1]);
zn= -(v0->no[2]+v1->no[2]);
/* transpose ! */
vlr->n[0]= imat[0][0]*xn+imat[0][1]*yn+imat[0][2]*zn;
vlr->n[1]= imat[1][0]*xn+imat[1][1]*yn+imat[1][2]*zn;
vlr->n[2]= imat[2][0]*xn+imat[2][1]*yn+imat[2][2]*zn;
Normalize(vlr->n);
vlr->mat= ma;
vlr->flag= 0;
vlr->ec= ME_V1V2;
}
}
if(edgetable)
MEM_freeN(edgetable);
}
}
}
if(!timeoffset) {
if (test_for_displace(re, ob ) ) {
calc_vertexnormals(re, obr, 0, 0);
if(do_autosmooth)
do_displacement(re, obr, mat, imat);
else
do_displacement(re, obr, NULL, NULL);
}
if(do_autosmooth) {
autosmooth(re, obr, mat, me->smoothresh);
}
calc_vertexnormals(re, obr, need_tangent, need_nmap_tangent);
if(need_stress)
calc_edge_stress(re, obr, me);
}
dm->release(dm);
}
/* ------------------------------------------------------------------------- */
/* Lamps and Shadowbuffers */
/* ------------------------------------------------------------------------- */
static void initshadowbuf(Render *re, LampRen *lar, float mat[][4])
{
struct ShadBuf *shb;
float viewinv[4][4];
/* if(la->spsi<16) return; */
/* memory alloc */
shb= (struct ShadBuf *)MEM_callocN( sizeof(struct ShadBuf),"initshadbuf");
lar->shb= shb;
if(shb==NULL) return;
VECCOPY(shb->co, lar->co);
/* percentage render: keep track of min and max */
shb->size= (lar->bufsize*re->r.size)/100;
if(shb->size<512) shb->size= 512;
else if(shb->size > lar->bufsize) shb->size= lar->bufsize;
shb->size &= ~15; /* make sure its multiples of 16 */
shb->samp= lar->samp;
shb->soft= lar->soft;
shb->shadhalostep= lar->shadhalostep;
MTC_Mat4Ortho(mat);
MTC_Mat4Invert(shb->winmat, mat); /* winmat is temp */
/* matrix: combination of inverse view and lampmat */
/* calculate again: the ortho-render has no correct viewinv */
MTC_Mat4Invert(viewinv, re->viewmat);
MTC_Mat4MulMat4(shb->viewmat, viewinv, shb->winmat);
/* projection */
shb->d= lar->clipsta;
shb->clipend= lar->clipend;
/* bias is percentage, made 2x larger because of correction for angle of incidence */
/* when a ray is closer to parallel of a face, bias value is increased during render */
shb->bias= (0.02*lar->bias)*0x7FFFFFFF;
shb->bias= shb->bias*(100/re->r.size);
/* halfway method (average of first and 2nd z) reduces bias issues */
if(lar->buftype==LA_SHADBUF_HALFWAY)
shb->bias= 0.1f*shb->bias;
}
static void area_lamp_vectors(LampRen *lar)
{
float xsize= 0.5*lar->area_size, ysize= 0.5*lar->area_sizey, multifac;
/* make it smaller, so area light can be multisampled */
multifac= 1.0f/sqrt((float)lar->ray_totsamp);
xsize *= multifac;
ysize *= multifac;
/* corner vectors */
lar->area[0][0]= lar->co[0] - xsize*lar->mat[0][0] - ysize*lar->mat[1][0];
lar->area[0][1]= lar->co[1] - xsize*lar->mat[0][1] - ysize*lar->mat[1][1];
lar->area[0][2]= lar->co[2] - xsize*lar->mat[0][2] - ysize*lar->mat[1][2];
/* corner vectors */
lar->area[1][0]= lar->co[0] - xsize*lar->mat[0][0] + ysize*lar->mat[1][0];
lar->area[1][1]= lar->co[1] - xsize*lar->mat[0][1] + ysize*lar->mat[1][1];
lar->area[1][2]= lar->co[2] - xsize*lar->mat[0][2] + ysize*lar->mat[1][2];
/* corner vectors */
lar->area[2][0]= lar->co[0] + xsize*lar->mat[0][0] + ysize*lar->mat[1][0];
lar->area[2][1]= lar->co[1] + xsize*lar->mat[0][1] + ysize*lar->mat[1][1];
lar->area[2][2]= lar->co[2] + xsize*lar->mat[0][2] + ysize*lar->mat[1][2];
/* corner vectors */
lar->area[3][0]= lar->co[0] + xsize*lar->mat[0][0] - ysize*lar->mat[1][0];
lar->area[3][1]= lar->co[1] + xsize*lar->mat[0][1] - ysize*lar->mat[1][1];
lar->area[3][2]= lar->co[2] + xsize*lar->mat[0][2] - ysize*lar->mat[1][2];
/* only for correction button size, matrix size works on energy */
lar->areasize= lar->dist*lar->dist/(4.0*xsize*ysize);
}
/* If lar takes more lamp data, the decoupling will be better. */
static GroupObject *add_render_lamp(Render *re, Object *ob)
{
Lamp *la= ob->data;
LampRen *lar;
GroupObject *go;
float mat[4][4], angle, xn, yn;
float vec[3];
int c;
/* previewrender sets this to zero... prevent accidents */
if(la==NULL) return NULL;
/* prevent only shadow from rendering light */
if(la->mode & LA_ONLYSHADOW)
if((re->r.mode & R_SHADOW)==0)
return NULL;
re->totlamp++;
/* groups is used to unify support for lightgroups, this is the global lightgroup */
go= MEM_callocN(sizeof(GroupObject), "groupobject");
BLI_addtail(&re->lights, go);
go->ob= ob;
/* lamprens are in own list, for freeing */
lar= (LampRen *)MEM_callocN(sizeof(LampRen),"lampren");
BLI_addtail(&re->lampren, lar);
go->lampren= lar;
MTC_Mat4MulMat4(mat, ob->obmat, re->viewmat);
MTC_Mat4Invert(ob->imat, mat);
MTC_Mat3CpyMat4(lar->mat, mat);
MTC_Mat3CpyMat4(lar->imat, ob->imat);
lar->bufsize = la->bufsize;
lar->samp = la->samp;
lar->buffers= la->buffers;
if(lar->buffers==0) lar->buffers= 1;
lar->buftype= la->buftype;
lar->filtertype= la->filtertype;
lar->soft = la->soft;
lar->shadhalostep = la->shadhalostep;
lar->clipsta = la->clipsta;
lar->clipend = la->clipend;
lar->bias = la->bias;
lar->type= la->type;
lar->mode= la->mode;
lar->energy= la->energy;
if(la->mode & LA_NEG) lar->energy= -lar->energy;
lar->vec[0]= -mat[2][0];
lar->vec[1]= -mat[2][1];
lar->vec[2]= -mat[2][2];
Normalize(lar->vec);
lar->co[0]= mat[3][0];
lar->co[1]= mat[3][1];
lar->co[2]= mat[3][2];
lar->dist= la->dist;
lar->haint= la->haint;
lar->distkw= lar->dist*lar->dist;
lar->r= lar->energy*la->r;
lar->g= lar->energy*la->g;
lar->b= lar->energy*la->b;
lar->shdwr= la->shdwr;
lar->shdwg= la->shdwg;
lar->shdwb= la->shdwb;
lar->k= la->k;
// area
lar->ray_samp= la->ray_samp;
lar->ray_sampy= la->ray_sampy;
lar->ray_sampz= la->ray_sampz;
lar->area_size= la->area_size;
lar->area_sizey= la->area_sizey;
lar->area_sizez= la->area_sizez;
lar->area_shape= la->area_shape;
/* Annoying, lamp UI does this, but the UI might not have been used? - add here too.
* make sure this matches buttons_shading.c's logic */
if(ELEM4(la->type, LA_AREA, LA_SPOT, LA_SUN, LA_LOCAL) && (la->mode & LA_SHAD_RAY))
if (ELEM3(la->type, LA_SPOT, LA_SUN, LA_LOCAL))
if (la->ray_samp_method == LA_SAMP_CONSTANT) la->ray_samp_method = LA_SAMP_HALTON;
lar->ray_samp_method= la->ray_samp_method;
lar->ray_samp_type= la->ray_samp_type;
lar->adapt_thresh= la->adapt_thresh;
lar->sunsky = NULL;
if( ELEM(lar->type, LA_SPOT, LA_LOCAL)) {
lar->ray_totsamp= lar->ray_samp*lar->ray_samp;
lar->area_shape = LA_AREA_SQUARE;
lar->area_sizey= lar->area_size;
}
else if(lar->type==LA_AREA) {
switch(lar->area_shape) {
case LA_AREA_SQUARE:
lar->ray_totsamp= lar->ray_samp*lar->ray_samp;
lar->ray_sampy= lar->ray_samp;
lar->area_sizey= lar->area_size;
break;
case LA_AREA_RECT:
lar->ray_totsamp= lar->ray_samp*lar->ray_sampy;
break;
case LA_AREA_CUBE:
lar->ray_totsamp= lar->ray_samp*lar->ray_samp*lar->ray_samp;
lar->ray_sampy= lar->ray_samp;
lar->ray_sampz= lar->ray_samp;
lar->area_sizey= lar->area_size;
lar->area_sizez= lar->area_size;
break;
case LA_AREA_BOX:
lar->ray_totsamp= lar->ray_samp*lar->ray_sampy*lar->ray_sampz;
break;
}
area_lamp_vectors(lar);
init_jitter_plane(lar); // subsamples
}
else if(lar->type==LA_SUN){
lar->ray_totsamp= lar->ray_samp*lar->ray_samp;
lar->area_shape = LA_AREA_SQUARE;
lar->area_sizey= lar->area_size;
if((la->sun_effect_type & LA_SUN_EFFECT_SKY) ||
(la->sun_effect_type & LA_SUN_EFFECT_AP)){
lar->sunsky = (struct SunSky*)MEM_callocN(sizeof(struct SunSky), "sunskyren");
lar->sunsky->effect_type = la->sun_effect_type;
VECCOPY(vec,ob->obmat[2]);
Normalize(vec);
InitSunSky(lar->sunsky, la->atm_turbidity, vec, la->horizon_brightness,
la->spread, la->sun_brightness, la->sun_size, la->backscattered_light,
la->skyblendfac, la->skyblendtype, la->sky_exposure, la->sky_colorspace);
InitAtmosphere(lar->sunsky, la->sun_intensity, 1.0, 1.0, la->atm_inscattering_factor, la->atm_extinction_factor,
la->atm_distance_factor);
}
}
else lar->ray_totsamp= 0;
lar->spotsi= la->spotsize;
if(lar->mode & LA_HALO) {
if(lar->spotsi>170.0) lar->spotsi= 170.0;
}
lar->spotsi= cos( M_PI*lar->spotsi/360.0 );
lar->spotbl= (1.0-lar->spotsi)*la->spotblend;
memcpy(lar->mtex, la->mtex, MAX_MTEX*sizeof(void *));
lar->lay= ob->lay & 0xFFFFFF; // higher 8 bits are localview layers
lar->falloff_type = la->falloff_type;
lar->ld1= la->att1;
lar->ld2= la->att2;
lar->curfalloff = curvemapping_copy(la->curfalloff);
if(lar->type==LA_SPOT) {
Normalize(lar->imat[0]);
Normalize(lar->imat[1]);
Normalize(lar->imat[2]);
xn= saacos(lar->spotsi);
xn= sin(xn)/cos(xn);
lar->spottexfac= 1.0/(xn);
if(lar->mode & LA_ONLYSHADOW) {
if((lar->mode & (LA_SHAD_BUF|LA_SHAD_RAY))==0) lar->mode -= LA_ONLYSHADOW;
}
}
/* set flag for spothalo en initvars */
if(la->type==LA_SPOT && (la->mode & LA_HALO)) {
if(la->haint>0.0) {
re->flag |= R_LAMPHALO;
/* camera position (0,0,0) rotate around lamp */
lar->sh_invcampos[0]= -lar->co[0];
lar->sh_invcampos[1]= -lar->co[1];
lar->sh_invcampos[2]= -lar->co[2];
MTC_Mat3MulVecfl(lar->imat, lar->sh_invcampos);
/* z factor, for a normalized volume */
angle= saacos(lar->spotsi);
xn= lar->spotsi;
yn= sin(angle);
lar->sh_zfac= yn/xn;
/* pre-scale */
lar->sh_invcampos[2]*= lar->sh_zfac;
}
}
else if(la->type==LA_HEMI) {
lar->mode &= ~(LA_SHAD_RAY|LA_SHAD_BUF);
}
for(c=0; c<MAX_MTEX; c++) {
if(la->mtex[c] && la->mtex[c]->tex) {
if (la->mtex[c]->mapto & LAMAP_COL)
lar->mode |= LA_TEXTURE;
if (la->mtex[c]->mapto & LAMAP_SHAD)
lar->mode |= LA_SHAD_TEX;
if(G.rendering) {
if(re->osa) {
if(la->mtex[c]->tex->type==TEX_IMAGE) lar->mode |= LA_OSATEX;
}
}
}
}
/* yafray: shadow flag should not be cleared, only used with internal renderer */
if (re->r.renderer==R_INTERN) {
/* to make sure we can check ray shadow easily in the render code */
if(lar->mode & LA_SHAD_RAY) {
if( (re->r.mode & R_RAYTRACE)==0)
lar->mode &= ~LA_SHAD_RAY;
}
if(re->r.mode & R_SHADOW) {
if(la->type==LA_AREA && (lar->mode & LA_SHAD_RAY) && (lar->ray_samp_method == LA_SAMP_CONSTANT)) {
init_jitter_plane(lar);
}
else if (la->type==LA_SPOT && (lar->mode & LA_SHAD_BUF) ) {
/* Per lamp, one shadow buffer is made. */
lar->bufflag= la->bufflag;
Mat4CpyMat4(mat, ob->obmat);
initshadowbuf(re, lar, mat); // mat is altered
}
/* this is the way used all over to check for shadow */
if(lar->shb || (lar->mode & LA_SHAD_RAY)) {
LampShadowSample *ls;
LampShadowSubSample *lss;
int a, b;
memset(re->shadowsamplenr, 0, sizeof(re->shadowsamplenr));
lar->shadsamp= MEM_mallocN(re->r.threads*sizeof(LampShadowSample), "lamp shadow sample");
ls= lar->shadsamp;
/* shadfacs actually mean light, let's put them to 1 to prevent unitialized accidents */
for(a=0; a<re->r.threads; a++, ls++) {
lss= ls->s;
for(b=0; b<re->r.osa; b++, lss++) {
lss->samplenr= -1; /* used to detect whether we store or read */
lss->shadfac[0]= 1.0f;
lss->shadfac[1]= 1.0f;
lss->shadfac[2]= 1.0f;
lss->shadfac[3]= 1.0f;
}
}
}
}
}
return go;
}
/* layflag: allows material group to ignore layerflag */
static void add_lightgroup(Render *re, Group *group, int exclusive)
{
GroupObject *go, *gol;
group->id.flag &= ~LIB_DOIT;
/* it's a bit too many loops in loops... but will survive */
/* note that 'exclusive' will remove it from the global list */
for(go= group->gobject.first; go; go= go->next) {
go->lampren= NULL;
if(go->ob->lay & re->scene->lay) {
if(go->ob && go->ob->type==OB_LAMP) {
for(gol= re->lights.first; gol; gol= gol->next) {
if(gol->ob==go->ob) {
go->lampren= gol->lampren;
break;
}
}
if(go->lampren==NULL)
gol= add_render_lamp(re, go->ob);
if(gol && exclusive) {
BLI_remlink(&re->lights, gol);
MEM_freeN(gol);
}
}
}
}
}
static void set_material_lightgroups(Render *re)
{
Group *group;
Material *ma;
/* not for preview render */
if(re->scene->r.scemode & R_PREVIEWBUTS)
return;
for(group= G.main->group.first; group; group=group->id.next)
group->id.flag |= LIB_DOIT;
/* it's a bit too many loops in loops... but will survive */
/* hola! materials not in use...? */
for(ma= G.main->mat.first; ma; ma=ma->id.next) {
if(ma->group && (ma->group->id.flag & LIB_DOIT))
add_lightgroup(re, ma->group, ma->mode & MA_GROUP_NOLAY);
}
}
static void set_renderlayer_lightgroups(Render *re, Scene *sce)
{
SceneRenderLayer *srl;
for(srl= sce->r.layers.first; srl; srl= srl->next) {
if(srl->light_override)
add_lightgroup(re, srl->light_override, 0);
}
}
/* ------------------------------------------------------------------------- */
/* World */
/* ------------------------------------------------------------------------- */
void init_render_world(Render *re)
{
int a;
char *cp;
if(re->scene && re->scene->world) {
re->wrld= *(re->scene->world);
cp= (char *)&re->wrld.fastcol;
cp[0]= 255.0*re->wrld.horr;
cp[1]= 255.0*re->wrld.horg;
cp[2]= 255.0*re->wrld.horb;
cp[3]= 1;
VECCOPY(re->grvec, re->viewmat[2]);
Normalize(re->grvec);
Mat3CpyMat4(re->imat, re->viewinv);
for(a=0; a<MAX_MTEX; a++)
if(re->wrld.mtex[a] && re->wrld.mtex[a]->tex) re->wrld.skytype |= WO_SKYTEX;
/* AO samples should be OSA minimum */
if(re->osa)
while(re->wrld.aosamp*re->wrld.aosamp < re->osa)
re->wrld.aosamp++;
if(!(re->r.mode & R_RAYTRACE) && (re->wrld.ao_gather_method == WO_AOGATHER_RAYTRACE))
re->wrld.mode &= ~WO_AMB_OCC;
}
else {
memset(&re->wrld, 0, sizeof(World));
re->wrld.exp= 0.0f;
re->wrld.range= 1.0f;
/* for mist pass */
re->wrld.miststa= re->clipsta;
re->wrld.mistdist= re->clipend-re->clipsta;
re->wrld.misi= 1.0f;
}
re->wrld.linfac= 1.0 + pow((2.0*re->wrld.exp + 0.5), -10);
re->wrld.logfac= log( (re->wrld.linfac-1.0)/re->wrld.linfac )/re->wrld.range;
}
/* ------------------------------------------------------------------------- */
/* Object Finalization */
/* ------------------------------------------------------------------------- */
/* prevent phong interpolation for giving ray shadow errors (terminator problem) */
static void set_phong_threshold(ObjectRen *obr)
{
// VertRen *ver;
VlakRen *vlr;
float thresh= 0.0, dot;
int tot=0, i;
/* Added check for 'pointy' situations, only dotproducts of 0.9 and larger
are taken into account. This threshold is meant to work on smooth geometry, not
for extreme cases (ton) */
for(i=0; i<obr->totvlak; i++) {
vlr= RE_findOrAddVlak(obr, i);
if(vlr->flag & R_SMOOTH) {
dot= INPR(vlr->n, vlr->v1->n);
dot= ABS(dot);
if(dot>0.9) {
thresh+= dot; tot++;
}
dot= INPR(vlr->n, vlr->v2->n);
dot= ABS(dot);
if(dot>0.9) {
thresh+= dot; tot++;
}
dot= INPR(vlr->n, vlr->v3->n);
dot= ABS(dot);
if(dot>0.9) {
thresh+= dot; tot++;
}
if(vlr->v4) {
dot= INPR(vlr->n, vlr->v4->n);
dot= ABS(dot);
if(dot>0.9) {
thresh+= dot; tot++;
}
}
}
}
if(tot) {
thresh/= (float)tot;
obr->ob->smoothresh= cos(0.5*M_PI-saacos(thresh));
}
}
/* per face check if all samples should be taken.
if raytrace or multisample, do always for raytraced material, or when material full_osa set */
static void set_fullsample_flag(Render *re, ObjectRen *obr)
{
VlakRen *vlr;
int a, trace, mode;
if(re->osa==0)
return;
trace= re->r.mode & R_RAYTRACE;
for(a=obr->totvlak-1; a>=0; a--) {
vlr= RE_findOrAddVlak(obr, a);
mode= vlr->mat->mode;
if(mode & MA_FULL_OSA)
vlr->flag |= R_FULL_OSA;
else if(trace) {
if(mode & MA_SHLESS);
else if(vlr->mat->material_type == MA_TYPE_VOLUME);
else if((mode & MA_RAYMIRROR) || ((mode & MA_TRANSP) && (mode & MA_RAYTRANSP)))
/* for blurry reflect/refract, better to take more samples
* inside the raytrace than as OSA samples */
if ((vlr->mat->gloss_mir == 1.0) && (vlr->mat->gloss_tra == 1.0))
vlr->flag |= R_FULL_OSA;
}
}
}
/* split quads for pradictable baking
* dir 1 == (0,1,2) (0,2,3), 2 == (1,3,0) (1,2,3)
*/
static void split_quads(ObjectRen *obr, int dir)
{
VlakRen *vlr, *vlr1;
int a;
for(a=obr->totvlak-1; a>=0; a--) {
vlr= RE_findOrAddVlak(obr, a);
/* test if rendering as a quad or triangle, skip wire */
if(vlr->v4 && (vlr->flag & R_STRAND)==0 && (vlr->mat->material_type != MA_TYPE_WIRE)) {
if(vlr->v4) {
vlr1= RE_vlakren_copy(obr, vlr);
vlr1->flag |= R_FACE_SPLIT;
if( dir==2 ) vlr->flag |= R_DIVIDE_24;
else vlr->flag &= ~R_DIVIDE_24;
/* new vertex pointers */
if (vlr->flag & R_DIVIDE_24) {
vlr1->v1= vlr->v2;
vlr1->v2= vlr->v3;
vlr1->v3= vlr->v4;
vlr->v3 = vlr->v4;
vlr1->flag |= R_DIVIDE_24;
}
else {
vlr1->v1= vlr->v1;
vlr1->v2= vlr->v3;
vlr1->v3= vlr->v4;
vlr1->flag &= ~R_DIVIDE_24;
}
vlr->v4 = vlr1->v4 = NULL;
/* new normals */
CalcNormFloat(vlr->v3->co, vlr->v2->co, vlr->v1->co, vlr->n);
CalcNormFloat(vlr1->v3->co, vlr1->v2->co, vlr1->v1->co, vlr1->n);
}
/* clear the flag when not divided */
else vlr->flag &= ~R_DIVIDE_24;
}
}
}
static void check_non_flat_quads(ObjectRen *obr)
{
VlakRen *vlr, *vlr1;
VertRen *v1, *v2, *v3, *v4;
float nor[3], xn, flen;
int a;
for(a=obr->totvlak-1; a>=0; a--) {
vlr= RE_findOrAddVlak(obr, a);
/* test if rendering as a quad or triangle, skip wire */
if(vlr->v4 && (vlr->flag & R_STRAND)==0 && (vlr->mat->material_type != MA_TYPE_WIRE)) {
/* check if quad is actually triangle */
v1= vlr->v1;
v2= vlr->v2;
v3= vlr->v3;
v4= vlr->v4;
VECSUB(nor, v1->co, v2->co);
if( ABS(nor[0])<FLT_EPSILON10 && ABS(nor[1])<FLT_EPSILON10 && ABS(nor[2])<FLT_EPSILON10 ) {
vlr->v1= v2;
vlr->v2= v3;
vlr->v3= v4;
vlr->v4= NULL;
}
else {
VECSUB(nor, v2->co, v3->co);
if( ABS(nor[0])<FLT_EPSILON10 && ABS(nor[1])<FLT_EPSILON10 && ABS(nor[2])<FLT_EPSILON10 ) {
vlr->v2= v3;
vlr->v3= v4;
vlr->v4= NULL;
}
else {
VECSUB(nor, v3->co, v4->co);
if( ABS(nor[0])<FLT_EPSILON10 && ABS(nor[1])<FLT_EPSILON10 && ABS(nor[2])<FLT_EPSILON10 ) {
vlr->v4= NULL;
}
else {
VECSUB(nor, v4->co, v1->co);
if( ABS(nor[0])<FLT_EPSILON10 && ABS(nor[1])<FLT_EPSILON10 && ABS(nor[2])<FLT_EPSILON10 ) {
vlr->v4= NULL;
}
}
}
}
if(vlr->v4) {
/* Face is divided along edge with the least gradient */
/* Flagged with R_DIVIDE_24 if divide is from vert 2 to 4 */
/* 4---3 4---3 */
/* |\ 1| or |1 /| */
/* |0\ | |/ 0| */
/* 1---2 1---2 0 = orig face, 1 = new face */
/* render normals are inverted in render! we calculate normal of single tria here */
flen= CalcNormFloat(vlr->v4->co, vlr->v3->co, vlr->v1->co, nor);
if(flen==0.0) CalcNormFloat(vlr->v4->co, vlr->v2->co, vlr->v1->co, nor);
xn= nor[0]*vlr->n[0] + nor[1]*vlr->n[1] + nor[2]*vlr->n[2];
if(ABS(xn) < 0.999995 ) { // checked on noisy fractal grid
float d1, d2;
vlr1= RE_vlakren_copy(obr, vlr);
vlr1->flag |= R_FACE_SPLIT;
/* split direction based on vnorms */
CalcNormFloat(vlr->v1->co, vlr->v2->co, vlr->v3->co, nor);
d1= nor[0]*vlr->v1->n[0] + nor[1]*vlr->v1->n[1] + nor[2]*vlr->v1->n[2];
CalcNormFloat(vlr->v2->co, vlr->v3->co, vlr->v4->co, nor);
d2= nor[0]*vlr->v2->n[0] + nor[1]*vlr->v2->n[1] + nor[2]*vlr->v2->n[2];
if( fabs(d1) < fabs(d2) ) vlr->flag |= R_DIVIDE_24;
else vlr->flag &= ~R_DIVIDE_24;
/* new vertex pointers */
if (vlr->flag & R_DIVIDE_24) {
vlr1->v1= vlr->v2;
vlr1->v2= vlr->v3;
vlr1->v3= vlr->v4;
vlr->v3 = vlr->v4;
vlr1->flag |= R_DIVIDE_24;
}
else {
vlr1->v1= vlr->v1;
vlr1->v2= vlr->v3;
vlr1->v3= vlr->v4;
vlr1->flag &= ~R_DIVIDE_24;
}
vlr->v4 = vlr1->v4 = NULL;
/* new normals */
CalcNormFloat(vlr->v3->co, vlr->v2->co, vlr->v1->co, vlr->n);
CalcNormFloat(vlr1->v3->co, vlr1->v2->co, vlr1->v1->co, vlr1->n);
}
/* clear the flag when not divided */
else vlr->flag &= ~R_DIVIDE_24;
}
}
}
}
static void finalize_render_object(Render *re, ObjectRen *obr, int timeoffset)
{
Object *ob= obr->ob;
VertRen *ver= NULL;
StrandRen *strand= NULL;
StrandBound *sbound= NULL;
float min[3], max[3], smin[3], smax[3];
int a, b;
if(obr->totvert || obr->totvlak || obr->tothalo || obr->totstrand) {
/* the exception below is because displace code now is in init_render_mesh call,
I will look at means to have autosmooth enabled for all object types
and have it as general postprocess, like displace */
if(ob->type!=OB_MESH && test_for_displace(re, ob))
do_displacement(re, obr, NULL, NULL);
if(!timeoffset) {
/* phong normal interpolation can cause error in tracing
* (terminator problem) */
ob->smoothresh= 0.0;
if((re->r.mode & R_RAYTRACE) && (re->r.mode & R_SHADOW))
set_phong_threshold(obr);
if (re->flag & R_BAKING && re->r.bake_quad_split != 0) {
/* Baking lets us define a quad split order */
split_quads(obr, re->r.bake_quad_split);
} else {
check_non_flat_quads(obr);
}
set_fullsample_flag(re, obr);
/* compute bounding boxes for clipping */
INIT_MINMAX(min, max);
for(a=0; a<obr->totvert; a++) {
if((a & 255)==0) ver= obr->vertnodes[a>>8].vert;
else ver++;
DO_MINMAX(ver->co, min, max);
}
if(obr->strandbuf) {
sbound= obr->strandbuf->bound;
for(b=0; b<obr->strandbuf->totbound; b++, sbound++) {
INIT_MINMAX(smin, smax);
for(a=sbound->start; a<sbound->end; a++) {
strand= RE_findOrAddStrand(obr, a);
strand_minmax(strand, smin, smax);
}
VECCOPY(sbound->boundbox[0], smin);
VECCOPY(sbound->boundbox[1], smax);
DO_MINMAX(smin, min, max);
DO_MINMAX(smax, min, max);
}
}
VECCOPY(obr->boundbox[0], min);
VECCOPY(obr->boundbox[1], max);
}
}
}
/* ------------------------------------------------------------------------- */
/* Database */
/* ------------------------------------------------------------------------- */
static int render_object_type(int type)
{
return ELEM5(type, OB_FONT, OB_CURVE, OB_SURF, OB_MESH, OB_MBALL);
}
static void find_dupli_instances(Render *re, ObjectRen *obr)
{
ObjectInstanceRen *obi;
float imat[4][4], obmat[4][4], obimat[4][4], nmat[3][3];
int first = 1;
Mat4MulMat4(obmat, obr->obmat, re->viewmat);
Mat4Invert(imat, obmat);
/* for objects instanced by dupliverts/faces/particles, we go over the
* list of instances to find ones that instance obr, and setup their
* matrices and obr pointer */
for(obi=re->instancetable.last; obi; obi=obi->prev) {
if(!obi->obr && obi->ob == obr->ob && obi->psysindex == obr->psysindex) {
obi->obr= obr;
/* compute difference between object matrix and
* object matrix with dupli transform, in viewspace */
Mat4CpyMat4(obimat, obi->mat);
Mat4MulMat4(obi->mat, imat, obimat);
Mat3CpyMat4(nmat, obi->mat);
Mat3Inv(obi->nmat, nmat);
Mat3Transp(obi->nmat);
if(!first) {
re->totvert += obr->totvert;
re->totvlak += obr->totvlak;
re->tothalo += obr->tothalo;
re->totstrand += obr->totstrand;
}
else
first= 0;
}
}
}
static void assign_dupligroup_dupli(Render *re, ObjectInstanceRen *obi, ObjectRen *obr)
{
float imat[4][4], obmat[4][4], obimat[4][4], nmat[3][3];
Mat4MulMat4(obmat, obr->obmat, re->viewmat);
Mat4Invert(imat, obmat);
obi->obr= obr;
/* compute difference between object matrix and
* object matrix with dupli transform, in viewspace */
Mat4CpyMat4(obimat, obi->mat);
Mat4MulMat4(obi->mat, imat, obimat);
Mat3CpyMat4(nmat, obi->mat);
Mat3Inv(obi->nmat, nmat);
Mat3Transp(obi->nmat);
re->totvert += obr->totvert;
re->totvlak += obr->totvlak;
re->tothalo += obr->tothalo;
re->totstrand += obr->totstrand;
}
static ObjectRen *find_dupligroup_dupli(Render *re, Object *ob, int psysindex)
{
ObjectRen *obr;
/* if the object is itself instanced, we don't want to create an instance
* for it */
if(ob->transflag & OB_RENDER_DUPLI)
return NULL;
/* try to find an object that was already created so we can reuse it
* and save memory */
for(obr=re->objecttable.first; obr; obr=obr->next)
if(obr->ob == ob && obr->psysindex == psysindex && (obr->flag & R_INSTANCEABLE))
return obr;
return NULL;
}
static void set_dupli_tex_mat(Render *re, ObjectInstanceRen *obi, DupliObject *dob)
{
/* For duplis we need to have a matrix that transform the coordinate back
* to it's original position, without the dupli transforms. We also check
* the matrix is actually needed, to save memory on lots of dupliverts for
* example */
static Object *lastob= NULL;
static int needtexmat= 0;
/* init */
if(!re) {
lastob= NULL;
needtexmat= 0;
return;
}
/* check if we actually need it */
if(lastob != dob->ob) {
Material ***material;
short a, *totmaterial;
lastob= dob->ob;
needtexmat= 0;
totmaterial= give_totcolp(dob->ob);
material= give_matarar(dob->ob);
if(totmaterial && material)
for(a= 0; a<*totmaterial; a++)
if((*material)[a] && (*material)[a]->texco & TEXCO_OBJECT)
needtexmat= 1;
}
if(needtexmat) {
float imat[4][4];
obi->duplitexmat= BLI_memarena_alloc(re->memArena, sizeof(float)*4*4);
Mat4Invert(imat, dob->mat);
MTC_Mat4MulSerie(obi->duplitexmat, re->viewmat, dob->omat, imat, re->viewinv, 0, 0, 0, 0);
}
}
static void init_render_object_data(Render *re, ObjectRen *obr, int timeoffset)
{
Object *ob= obr->ob;
ParticleSystem *psys;
int i;
if(obr->psysindex) {
if((!obr->prev || obr->prev->ob != ob) && ob->type==OB_MESH) {
/* the emitter mesh wasn't rendered so the modifier stack wasn't
* evaluated with render settings */
DerivedMesh *dm;
dm = mesh_create_derived_render(re->scene, ob, CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL);
dm->release(dm);
}
for(psys=ob->particlesystem.first, i=0; i<obr->psysindex-1; i++)
psys= psys->next;
render_new_particle_system(re, obr, psys, timeoffset);
}
else {
if ELEM(ob->type, OB_FONT, OB_CURVE)
init_render_curve(re, obr, timeoffset);
else if(ob->type==OB_SURF)
init_render_surf(re, obr);
else if(ob->type==OB_MESH)
init_render_mesh(re, obr, timeoffset);
else if(ob->type==OB_MBALL)
init_render_mball(re, obr);
}
finalize_render_object(re, obr, timeoffset);
re->totvert += obr->totvert;
re->totvlak += obr->totvlak;
re->tothalo += obr->tothalo;
re->totstrand += obr->totstrand;
}
static void add_render_object(Render *re, Object *ob, Object *par, DupliObject *dob, int timeoffset, int vectorlay)
{
ObjectRen *obr;
ObjectInstanceRen *obi;
ParticleSystem *psys;
int show_emitter, allow_render= 1, index, psysindex;
index= (dob)? dob->index: 0;
/* the emitter has to be processed first (render levels of modifiers) */
/* so here we only check if the emitter should be rendered */
if(ob->particlesystem.first) {
show_emitter= 0;
for(psys=ob->particlesystem.first; psys; psys=psys->next) {
show_emitter += psys->part->draw & PART_DRAW_EMITTER;
psys_render_set(ob, psys, re->viewmat, re->winmat, re->winx, re->winy, timeoffset);
}
/* if no psys has "show emitter" selected don't render emitter */
if(show_emitter == 0)
allow_render= 0;
}
/* one render object for the data itself */
if(allow_render) {
obr= RE_addRenderObject(re, ob, par, index, 0, ob->lay);
if((dob && !dob->animated) || (ob->transflag & OB_RENDER_DUPLI)) {
obr->flag |= R_INSTANCEABLE;
Mat4CpyMat4(obr->obmat, ob->obmat);
}
if(obr->lay & vectorlay)
obr->flag |= R_NEED_VECTORS;
init_render_object_data(re, obr, timeoffset);
/* only add instance for objects that have not been used for dupli */
if(!(ob->transflag & OB_RENDER_DUPLI)) {
obi= RE_addRenderInstance(re, obr, ob, par, index, 0, NULL, ob->lay);
if(dob) set_dupli_tex_mat(re, obi, dob);
}
else
find_dupli_instances(re, obr);
}
/* and one render object per particle system */
if(ob->particlesystem.first) {
psysindex= 1;
for(psys=ob->particlesystem.first; psys; psys=psys->next, psysindex++) {
obr= RE_addRenderObject(re, ob, par, index, psysindex, ob->lay);
if((dob && !dob->animated) || (ob->transflag & OB_RENDER_DUPLI)) {
obr->flag |= R_INSTANCEABLE;
Mat4CpyMat4(obr->obmat, ob->obmat);
}
if(obr->lay & vectorlay)
obr->flag |= R_NEED_VECTORS;
init_render_object_data(re, obr, timeoffset);
psys_render_restore(ob, psys);
/* only add instance for objects that have not been used for dupli */
if(!(ob->transflag & OB_RENDER_DUPLI)) {
obi= RE_addRenderInstance(re, obr, ob, par, index, psysindex, NULL, ob->lay);
if(dob) set_dupli_tex_mat(re, obi, dob);
}
else
find_dupli_instances(re, obr);
}
}
}
/* par = pointer to duplicator parent, needed for object lookup table */
/* index = when duplicater copies same object (particle), the counter */
static void init_render_object(Render *re, Object *ob, Object *par, DupliObject *dob, int timeoffset, int vectorlay)
{
static double lasttime= 0.0;
double time;
float mat[4][4];
if(ob->type==OB_LAMP)
add_render_lamp(re, ob);
else if(render_object_type(ob->type))
add_render_object(re, ob, par, dob, timeoffset, vectorlay);
else {
MTC_Mat4MulMat4(mat, ob->obmat, re->viewmat);
MTC_Mat4Invert(ob->imat, mat);
}
time= PIL_check_seconds_timer();
if(time - lasttime > 1.0) {
lasttime= time;
/* clumsy copying still */
re->i.totvert= re->totvert;
re->i.totface= re->totvlak;
re->i.totstrand= re->totstrand;
re->i.tothalo= re->tothalo;
re->i.totlamp= re->totlamp;
re->stats_draw(re->sdh, &re->i);
}
ob->flag |= OB_DONE;
}
void RE_Database_Free(Render *re)
{
Object *ob = NULL;
LampRen *lar;
/* statistics for debugging render memory usage */
if((G.f & G_DEBUG) && (G.rendering)) {
if((re->r.scemode & R_PREVIEWBUTS)==0) {
BKE_image_print_memlist();
MEM_printmemlist_stats();
}
}
/* FREE */
for(lar= re->lampren.first; lar; lar= lar->next) {
freeshadowbuf(lar);
if(lar->jitter) MEM_freeN(lar->jitter);
if(lar->shadsamp) MEM_freeN(lar->shadsamp);
if(lar->sunsky) MEM_freeN(lar->sunsky);
curvemapping_free(lar->curfalloff);
}
free_volume_precache(re);
BLI_freelistN(&re->lampren);
BLI_freelistN(&re->lights);
free_renderdata_tables(re);
/* free orco. check all objects because of duplis and sets */
ob= G.main->object.first;
while(ob) {
if(ob->type==OB_MBALL) {
if(ob->disp.first && ob->disp.first!=ob->disp.last) {
DispList *dl= ob->disp.first;
BLI_remlink(&ob->disp, dl);
freedisplist(&ob->disp);
BLI_addtail(&ob->disp, dl);
}
}
ob= ob->id.next;
}
free_mesh_orco_hash(re);
#if 0 /* radio can be redone better */
end_radio_render();
#endif
end_render_materials();
end_render_textures();
free_pointdensities(re);
free_voxeldata(re);
free_camera_inside_volumes(re);
if(re->wrld.aosphere) {
MEM_freeN(re->wrld.aosphere);
re->wrld.aosphere= NULL;
re->scene->world->aosphere= NULL;
}
if(re->wrld.aotables) {
MEM_freeN(re->wrld.aotables);
re->wrld.aotables= NULL;
re->scene->world->aotables= NULL;
}
if(re->r.mode & R_RAYTRACE)
free_render_qmcsampler(re);
if(re->r.mode & R_RAYTRACE) freeraytree(re);
free_sss(re);
free_occ(re);
free_strand_surface(re);
re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0;
re->i.convertdone= 0;
if(re->scene)
if(re->scene->r.scemode & R_FREE_IMAGE)
if((re->r.scemode & R_PREVIEWBUTS)==0)
BKE_image_free_all_textures();
if(re->memArena) {
BLI_memarena_free(re->memArena);
re->memArena = NULL;
}
}
static int allow_render_object(Object *ob, int nolamps, int onlyselected, Object *actob)
{
/* override not showing object when duplis are used with particles */
if(ob->transflag & OB_DUPLIPARTS)
; /* let particle system(s) handle showing vs. not showing */
else if((ob->transflag & OB_DUPLI) && !(ob->transflag & OB_DUPLIFRAMES))
return 0;
if(nolamps && (ob->type==OB_LAMP))
return 0;
if(onlyselected && (ob!=actob && !(ob->flag & SELECT)))
return 0;
return 1;
}
static int allow_render_dupli_instance(Render *re, DupliObject *dob, Object *obd)
{
ParticleSystem *psys;
Material *ma;
short a, *totmaterial;
/* don't allow objects with halos. we need to have
* all halo's to sort them globally in advance */
totmaterial= give_totcolp(obd);
if(totmaterial) {
for(a= 0; a<*totmaterial; a++) {
ma= give_current_material(obd, a);
if(ma && (ma->material_type == MA_TYPE_HALO))
return 0;
}
}
for(psys=obd->particlesystem.first; psys; psys=psys->next)
if(!ELEM5(psys->part->ren_as, PART_DRAW_BB, PART_DRAW_LINE, PART_DRAW_PATH, PART_DRAW_OB, PART_DRAW_GR))
return 0;
/* don't allow lamp, animated duplis, or radio render */
return (render_object_type(obd->type) &&
(!(dob->type == OB_DUPLIGROUP) || !dob->animated) &&
!(re->r.mode & R_RADIO));
}
static void dupli_render_particle_set(Render *re, Object *ob, int timeoffset, int level, int enable)
{
/* ugly function, but we need to set particle systems to their render
* settings before calling object_duplilist, to get render level duplis */
Group *group;
GroupObject *go;
ParticleSystem *psys;
DerivedMesh *dm;
if(level >= MAX_DUPLI_RECUR)
return;
if(ob->transflag & OB_DUPLIPARTS) {
for(psys=ob->particlesystem.first; psys; psys=psys->next) {
if(ELEM(psys->part->ren_as, PART_DRAW_OB, PART_DRAW_GR)) {
if(enable)
psys_render_set(ob, psys, re->viewmat, re->winmat, re->winx, re->winy, timeoffset);
else
psys_render_restore(ob, psys);
}
}
if(level == 0 && enable) {
/* this is to make sure we get render level duplis in groups:
* the derivedmesh must be created before init_render_mesh,
* since object_duplilist does dupliparticles before that */
dm = mesh_create_derived_render(re->scene, ob, CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL);
dm->release(dm);
for(psys=ob->particlesystem.first; psys; psys=psys->next)
psys_get_modifier(ob, psys)->flag &= ~eParticleSystemFlag_psys_updated;
}
}
if(ob->dup_group==NULL) return;
group= ob->dup_group;
for(go= group->gobject.first; go; go= go->next)
dupli_render_particle_set(re, go->ob, timeoffset, level+1, enable);
}
static int get_vector_renderlayers(Scene *sce)
{
SceneRenderLayer *srl;
int lay= 0;
for(srl= sce->r.layers.first; srl; srl= srl->next)
if(srl->passflag & SCE_PASS_VECTOR)
lay |= srl->lay;
return lay;
}
static void add_group_render_dupli_obs(Render *re, Group *group, int nolamps, int onlyselected, Object *actob, int timeoffset, int vectorlay, int level)
{
GroupObject *go;
Object *ob;
/* simple preventing of too deep nested groups */
if(level>MAX_DUPLI_RECUR) return;
/* recursively go into dupligroups to find objects with OB_RENDER_DUPLI
* that were not created yet */
for(go= group->gobject.first; go; go= go->next) {
ob= go->ob;
if(ob->flag & OB_DONE) {
if(ob->transflag & OB_RENDER_DUPLI) {
if(allow_render_object(ob, nolamps, onlyselected, actob)) {
init_render_object(re, ob, NULL, 0, timeoffset, vectorlay);
ob->transflag &= ~OB_RENDER_DUPLI;
if(ob->dup_group)
add_group_render_dupli_obs(re, ob->dup_group, nolamps, onlyselected, actob, timeoffset, vectorlay, level+1);
}
}
}
}
}
static void database_init_objects(Render *re, unsigned int renderlay, int nolamps, int onlyselected, Object *actob, int timeoffset)
{
Base *base;
Object *ob;
Group *group;
ObjectInstanceRen *obi;
Scene *sce;
float mat[4][4];
int lay, vectorlay, redoimat= 0;
/* for duplis we need the Object texture mapping to work as if
* untransformed, set_dupli_tex_mat sets the matrix to allow that
* NULL is just for init */
set_dupli_tex_mat(NULL, NULL, NULL);
for(SETLOOPER(re->scene, base)) {
ob= base->object;
/* imat objects has to be done here, since displace can have texture using Object map-input */
MTC_Mat4MulMat4(mat, ob->obmat, re->viewmat);
MTC_Mat4Invert(ob->imat, mat);
/* each object should only be rendered once */
ob->flag &= ~OB_DONE;
ob->transflag &= ~OB_RENDER_DUPLI;
}
for(SETLOOPER(re->scene, base)) {
ob= base->object;
/* in the prev/next pass for making speed vectors, avoid creating
* objects that are not on a renderlayer with a vector pass, can
* save a lot of time in complex scenes */
vectorlay= get_vector_renderlayers(sce);
lay= (timeoffset)? renderlay & vectorlay: renderlay;
/* if the object has been restricted from rendering in the outliner, ignore it */
if(ob->restrictflag & OB_RESTRICT_RENDER) continue;
/* OB_DONE means the object itself got duplicated, so was already converted */
if(ob->flag & OB_DONE) {
/* OB_RENDER_DUPLI means instances for it were already created, now
* it still needs to create the ObjectRen containing the data */
if(ob->transflag & OB_RENDER_DUPLI) {
if(allow_render_object(ob, nolamps, onlyselected, actob)) {
init_render_object(re, ob, NULL, 0, timeoffset, vectorlay);
ob->transflag &= ~OB_RENDER_DUPLI;
}
}
}
else if((base->lay & lay) || (ob->type==OB_LAMP && (base->lay & re->scene->lay)) ) {
if((ob->transflag & OB_DUPLI) && (ob->type!=OB_MBALL)) {
DupliObject *dob;
ListBase *lb;
redoimat= 1;
/* create list of duplis generated by this object, particle
* system need to have render settings set for dupli particles */
dupli_render_particle_set(re, ob, timeoffset, 0, 1);
lb= object_duplilist(sce, ob);
dupli_render_particle_set(re, ob, timeoffset, 0, 0);
for(dob= lb->first; dob; dob= dob->next) {
Object *obd= dob->ob;
Mat4CpyMat4(obd->obmat, dob->mat);
/* group duplis need to set ob matrices correct, for deform. so no_draw is part handled */
if(!(obd->transflag & OB_RENDER_DUPLI) && dob->no_draw)
continue;
if(obd->restrictflag & OB_RESTRICT_RENDER)
continue;
if(obd->type==OB_MBALL)
continue;
if(!allow_render_object(obd, nolamps, onlyselected, actob))
continue;
if(allow_render_dupli_instance(re, dob, obd)) {
ParticleSystem *psys;
ObjectRen *obr = NULL;
int psysindex;
float mat[4][4];
/* instances instead of the actual object are added in two cases, either
* this is a duplivert/face/particle, or it is a non-animated object in
* a dupligroup that has already been created before */
if(dob->type != OB_DUPLIGROUP || (obr=find_dupligroup_dupli(re, obd, 0))) {
Mat4MulMat4(mat, dob->mat, re->viewmat);
obi= RE_addRenderInstance(re, NULL, obd, ob, dob->index, 0, mat, obd->lay);
/* fill in instance variables for texturing */
set_dupli_tex_mat(re, obi, dob);
if(dob->type != OB_DUPLIGROUP) {
VECCOPY(obi->dupliorco, dob->orco);
obi->dupliuv[0]= dob->uv[0];
obi->dupliuv[1]= dob->uv[1];
}
else {
/* for the second case, setup instance to point to the already
* created object, and possibly setup instances if this object
* itself was duplicated. for the first case find_dupli_instances
* will be called later. */
assign_dupligroup_dupli(re, obi, obr);
if(obd->transflag & OB_RENDER_DUPLI)
find_dupli_instances(re, obr);
}
}
else
/* can't instance, just create the object */
init_render_object(re, obd, ob, dob, timeoffset, vectorlay);
/* same logic for particles, each particle system has it's own object, so
* need to go over them separately */
psysindex= 1;
for(psys=obd->particlesystem.first; psys; psys=psys->next) {
if(dob->type != OB_DUPLIGROUP || (obr=find_dupligroup_dupli(re, ob, psysindex))) {
obi= RE_addRenderInstance(re, NULL, obd, ob, dob->index, psysindex++, mat, obd->lay);
set_dupli_tex_mat(re, obi, dob);
if(dob->type != OB_DUPLIGROUP) {
VECCOPY(obi->dupliorco, dob->orco);
obi->dupliuv[0]= dob->uv[0];
obi->dupliuv[1]= dob->uv[1];
}
else {
assign_dupligroup_dupli(re, obi, obr);
if(obd->transflag & OB_RENDER_DUPLI)
find_dupli_instances(re, obr);
}
}
}
if(dob->type != OB_DUPLIGROUP) {
obd->flag |= OB_DONE;
obd->transflag |= OB_RENDER_DUPLI;
}
}
else
init_render_object(re, obd, ob, dob, timeoffset, vectorlay);
if(re->test_break(re->tbh)) break;
}
free_object_duplilist(lb);
if(allow_render_object(ob, nolamps, onlyselected, actob))
init_render_object(re, ob, NULL, 0, timeoffset, vectorlay);
}
else if(allow_render_object(ob, nolamps, onlyselected, actob))
init_render_object(re, ob, NULL, 0, timeoffset, vectorlay);
}
if(re->test_break(re->tbh)) break;
}
/* objects in groups with OB_RENDER_DUPLI set still need to be created,
* since they may not be part of the scene */
for(group= G.main->group.first; group; group=group->id.next)
add_group_render_dupli_obs(re, group, nolamps, onlyselected, actob, timeoffset, renderlay, 0);
/* imat objects has to be done again, since groups can mess it up */
if(redoimat) {
for(SETLOOPER(re->scene, base)) {
ob= base->object;
MTC_Mat4MulMat4(mat, ob->obmat, re->viewmat);
MTC_Mat4Invert(ob->imat, mat);
}
}
if(!re->test_break(re->tbh))
RE_makeRenderInstances(re);
}
/* used to be 'rotate scene' */
void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view)
{
extern int slurph_opt; /* key.c */
Scene *sce;
float mat[4][4];
float amb[3];
unsigned int lay;
re->scene= scene;
/* per second, per object, stats print this */
re->i.infostr= "Preparing Scene data";
re->i.cfra= scene->r.cfra;
strncpy(re->i.scenename, scene->id.name+2, 20);
/* XXX add test if dbase was filled already? */
re->memArena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0;
re->lights.first= re->lights.last= NULL;
re->lampren.first= re->lampren.last= NULL;
slurph_opt= 0;
re->i.partsdone= 0; /* signal now in use for previewrender */
/* in localview, lamps are using normal layers, objects only local bits */
if(re->scene->lay & 0xFF000000) lay= re->scene->lay & 0xFF000000;
else lay= re->scene->lay;
/* applies changes fully */
if((re->r.scemode & R_PREVIEWBUTS)==0)
scene_update_for_newframe(re->scene, lay);
/* if no camera, viewmat should have been set! */
if(use_camera_view && re->scene->camera) {
Mat4Ortho(re->scene->camera->obmat);
Mat4Invert(mat, re->scene->camera->obmat);
RE_SetView(re, mat);
re->scene->camera->recalc= OB_RECALC_OB; /* force correct matrix for scaled cameras */
}
init_render_world(re); /* do first, because of ambient. also requires re->osa set correct */
if(re->r.mode & R_RAYTRACE) {
init_render_qmcsampler(re);
if(re->wrld.mode & WO_AMB_OCC)
if (re->wrld.ao_samp_method == WO_AOSAMP_CONSTANT)
init_ao_sphere(&re->wrld);
}
/* still bad... doing all */
init_render_textures(re);
if (re->r.color_mgt_flag & R_COLOR_MANAGEMENT) color_manage_linearize(amb, &re->wrld.ambr);
else VECCOPY(amb, &re->wrld.ambr);
init_render_materials(re->r.mode, amb);
set_node_shader_lamp_loop(shade_material_loop);
/* MAKE RENDER DATA */
database_init_objects(re, lay, 0, 0, 0, 0);
init_camera_inside_volumes(re);
if(!re->test_break(re->tbh)) {
int tothalo;
set_material_lightgroups(re);
for(sce= re->scene; sce; sce= sce->set)
set_renderlayer_lightgroups(re, sce);
slurph_opt= 1;
/* for now some clumsy copying still */
re->i.totvert= re->totvert;
re->i.totface= re->totvlak;
re->i.totstrand= re->totstrand;
re->i.tothalo= re->tothalo;
re->i.totlamp= re->totlamp;
re->stats_draw(re->sdh, &re->i);
/* don't sort stars */
tothalo= re->tothalo;
if(!re->test_break(re->tbh))
if(re->wrld.mode & WO_STARS)
RE_make_stars(re, NULL, NULL, NULL, NULL);
sort_halos(re, tothalo);
re->i.infostr= "Creating Shadowbuffers";
re->stats_draw(re->sdh, &re->i);
/* SHADOW BUFFER */
threaded_makeshadowbufs(re);
/* yafray: 'direct' radiosity, environment maps and raytree init not needed for yafray render */
/* although radio mode could be useful at some point, later */
if (re->r.renderer==R_INTERN) {
#if 0 /* RADIO was removed */
/* RADIO (uses no R anymore) */
if(!re->test_break(re->tbh))
if(re->r.mode & R_RADIO) do_radio_render(re);
#endif
/* raytree */
if(!re->test_break(re->tbh)) {
if(re->r.mode & R_RAYTRACE) {
makeraytree(re);
}
}
/* ENVIRONMENT MAPS */
if(!re->test_break(re->tbh))
make_envmaps(re);
/* point density texture */
if(!re->test_break(re->tbh))
make_pointdensities(re);
/* voxel data texture */
if(!re->test_break(re->tbh))
make_voxeldata(re);
}
if(!re->test_break(re->tbh))
project_renderdata(re, projectverto, re->r.mode & R_PANORAMA, 0, 1);
/* Occlusion */
if((re->wrld.mode & WO_AMB_OCC) && !re->test_break(re->tbh))
if(re->wrld.ao_gather_method == WO_AOGATHER_APPROX)
if(re->r.renderer==R_INTERN)
if(re->r.mode & R_SHADOW)
make_occ_tree(re);
/* SSS */
if((re->r.mode & R_SSS) && !re->test_break(re->tbh))
if(re->r.renderer==R_INTERN)
make_sss_tree(re);
if(!re->test_break(re->tbh))
if(re->r.mode & R_RAYTRACE)
volume_precache(re);
}
if(re->test_break(re->tbh))
RE_Database_Free(re);
else
re->i.convertdone= 1;
re->i.infostr= NULL;
re->stats_draw(re->sdh, &re->i);
}
/* exported call to recalculate hoco for vertices, when winmat changed */
void RE_DataBase_ApplyWindow(Render *re)
{
project_renderdata(re, projectverto, 0, 0, 0);
}
void RE_DataBase_GetView(Render *re, float mat[][4])
{
Mat4CpyMat4(mat, re->viewmat);
}
/* ------------------------------------------------------------------------- */
/* Speed Vectors */
/* ------------------------------------------------------------------------- */
static void database_fromscene_vectors(Render *re, Scene *scene, int timeoffset)
{
extern int slurph_opt; /* key.c */
float mat[4][4];
unsigned int lay;
re->scene= scene;
/* XXX add test if dbase was filled already? */
re->memArena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0;
re->i.totface=re->i.totvert=re->i.totstrand=re->i.totlamp=re->i.tothalo= 0;
re->lights.first= re->lights.last= NULL;
slurph_opt= 0;
/* in localview, lamps are using normal layers, objects only local bits */
if(re->scene->lay & 0xFF000000) lay= re->scene->lay & 0xFF000000;
else lay= re->scene->lay;
/* applies changes fully */
scene->r.cfra += timeoffset;
scene_update_for_newframe(re->scene, lay);
/* if no camera, viewmat should have been set! */
if(re->scene->camera) {
Mat4Ortho(re->scene->camera->obmat);
Mat4Invert(mat, re->scene->camera->obmat);
RE_SetView(re, mat);
}
/* MAKE RENDER DATA */
database_init_objects(re, lay, 0, 0, 0, timeoffset);
if(!re->test_break(re->tbh))
project_renderdata(re, projectverto, re->r.mode & R_PANORAMA, 0, 1);
/* do this in end, particles for example need cfra */
scene->r.cfra -= timeoffset;
}
/* choose to use static, to prevent giving too many args to this call */
static void speedvector_project(Render *re, float *zco, float *co, float *ho)
{
static float pixelphix=0.0f, pixelphiy=0.0f, zmulx=0.0f, zmuly=0.0f;
static int pano= 0;
float div;
/* initialize */
if(re) {
pano= re->r.mode & R_PANORAMA;
/* precalculate amount of radians 1 pixel rotates */
if(pano) {
/* size of 1 pixel mapped to viewplane coords */
float psize= (re->viewplane.xmax-re->viewplane.xmin)/(float)re->winx;
/* x angle of a pixel */
pixelphix= atan(psize/re->clipsta);
psize= (re->viewplane.ymax-re->viewplane.ymin)/(float)re->winy;
/* y angle of a pixel */
pixelphiy= atan(psize/re->clipsta);
}
zmulx= re->winx/2;
zmuly= re->winy/2;
return;
}
/* now map hocos to screenspace, uses very primitive clip still */
if(ho[3]<0.1f) div= 10.0f;
else div= 1.0f/ho[3];
/* use cylinder projection */
if(pano) {
float vec[3], ang;
/* angle between (0,0,-1) and (co) */
VECCOPY(vec, co);
ang= saacos(-vec[2]/sqrt(vec[0]*vec[0] + vec[2]*vec[2]));
if(vec[0]<0.0f) ang= -ang;
zco[0]= ang/pixelphix + zmulx;
ang= 0.5f*M_PI - saacos(vec[1]/sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]));
zco[1]= ang/pixelphiy + zmuly;
}
else {
zco[0]= zmulx*(1.0f+ho[0]*div);
zco[1]= zmuly*(1.0f+ho[1]*div);
}
}
static void calculate_speedvector(float *vectors, int step, float winsq, float winroot, float *co, float *ho, float *speed)
{
float zco[2], len;
speedvector_project(NULL, zco, co, ho);
zco[0]= vectors[0] - zco[0];
zco[1]= vectors[1] - zco[1];
/* enable nice masks for hardly moving stuff or float inaccuracy */
if(zco[0]<0.1f && zco[0]>-0.1f && zco[1]<0.1f && zco[1]>-0.1f ) {
zco[0]= 0.0f;
zco[1]= 0.0f;
}
/* maximize speed for image width, otherwise it never looks good */
len= zco[0]*zco[0] + zco[1]*zco[1];
if(len > winsq) {
len= winroot/sqrt(len);
zco[0]*= len;
zco[1]*= len;
}
/* note; in main vecblur loop speedvec is negated again */
if(step) {
speed[2]= -zco[0];
speed[3]= -zco[1];
}
else {
speed[0]= zco[0];
speed[1]= zco[1];
}
}
static float *calculate_strandsurface_speedvectors(Render *re, ObjectInstanceRen *obi, StrandSurface *mesh)
{
float winsq= re->winx*re->winy, winroot= sqrt(winsq), (*winspeed)[4];
float ho[4], prevho[4], nextho[4], winmat[4][4], vec[2];
int a;
if(mesh->co && mesh->prevco && mesh->nextco) {
if(obi->flag & R_TRANSFORMED)
Mat4MulMat4(winmat, obi->mat, re->winmat);
else
Mat4CpyMat4(winmat, re->winmat);
winspeed= MEM_callocN(sizeof(float)*4*mesh->totvert, "StrandSurfWin");
for(a=0; a<mesh->totvert; a++) {
projectvert(mesh->co[a], winmat, ho);
projectvert(mesh->prevco[a], winmat, prevho);
speedvector_project(NULL, vec, mesh->prevco[a], prevho);
calculate_speedvector(vec, 0, winsq, winroot, mesh->co[a], ho, winspeed[a]);
projectvert(mesh->nextco[a], winmat, nextho);
speedvector_project(NULL, vec, mesh->nextco[a], nextho);
calculate_speedvector(vec, 1, winsq, winroot, mesh->co[a], ho, winspeed[a]);
}
return (float*)winspeed;
}
return NULL;
}
static void calculate_speedvectors(Render *re, ObjectInstanceRen *obi, float *vectors, int step)
{
ObjectRen *obr= obi->obr;
VertRen *ver= NULL;
StrandRen *strand= NULL;
StrandBuffer *strandbuf;
StrandSurface *mesh= NULL;
float *speed, (*winspeed)[4]=NULL, ho[4], winmat[4][4];
float *co1, *co2, *co3, *co4, w[4];
float winsq= re->winx*re->winy, winroot= sqrt(winsq);
int a, *face, *index;
if(obi->flag & R_TRANSFORMED)
Mat4MulMat4(winmat, obi->mat, re->winmat);
else
Mat4CpyMat4(winmat, re->winmat);
if(obr->vertnodes) {
for(a=0; a<obr->totvert; a++, vectors+=2) {
if((a & 255)==0) ver= obr->vertnodes[a>>8].vert;
else ver++;
speed= RE_vertren_get_winspeed(obi, ver, 1);
projectvert(ver->co, winmat, ho);
calculate_speedvector(vectors, step, winsq, winroot, ver->co, ho, speed);
}
}
if(obr->strandnodes) {
strandbuf= obr->strandbuf;
mesh= (strandbuf)? strandbuf->surface: NULL;
/* compute speed vectors at surface vertices */
if(mesh)
winspeed= (float(*)[4])calculate_strandsurface_speedvectors(re, obi, mesh);
if(winspeed) {
for(a=0; a<obr->totstrand; a++, vectors+=2) {
if((a & 255)==0) strand= obr->strandnodes[a>>8].strand;
else strand++;
index= RE_strandren_get_face(obr, strand, 0);
if(index && *index < mesh->totface) {
speed= RE_strandren_get_winspeed(obi, strand, 1);
/* interpolate speed vectors from strand surface */
face= mesh->face[*index];
co1= mesh->co[face[0]];
co2= mesh->co[face[1]];
co3= mesh->co[face[2]];
co4= (face[3])? mesh->co[face[3]]: NULL;
InterpWeightsQ3Dfl(co1, co2, co3, co4, strand->vert->co, w);
speed[0]= speed[1]= speed[2]= speed[3]= 0.0f;
QUATADDFAC(speed, speed, winspeed[face[0]], w[0]);
QUATADDFAC(speed, speed, winspeed[face[1]], w[1]);
QUATADDFAC(speed, speed, winspeed[face[2]], w[2]);
if(face[3])
QUATADDFAC(speed, speed, winspeed[face[3]], w[3]);
}
}
MEM_freeN(winspeed);
}
}
}
static int load_fluidsimspeedvectors(Render *re, ObjectInstanceRen *obi, float *vectors, int step)
{
ObjectRen *obr= obi->obr;
Object *fsob= obr->ob;
VertRen *ver= NULL;
float *speed, div, zco[2], avgvel[4] = {0.0, 0.0, 0.0, 0.0};
float zmulx= re->winx/2, zmuly= re->winy/2, len;
float winsq= re->winx*re->winy, winroot= sqrt(winsq);
int a, j;
float hoco[4], ho[4], fsvec[4], camco[4];
float mat[4][4], winmat[4][4];
float imat[4][4];
FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(fsob, eModifierType_Fluidsim);
FluidsimSettings *fss = fluidmd->fss;
float *velarray = NULL;
/* only one step needed */
if(step) return 1;
if(fluidmd)
fss = fluidmd->fss;
else
return 0;
Mat4CpyMat4(mat, re->viewmat);
MTC_Mat4Invert(imat, mat);
/* set first vertex OK */
if(!fss->meshSurfNormals) return 0;
if( obr->totvert != GET_INT_FROM_POINTER(fss->meshSurface) ) {
//fprintf(stderr, "load_fluidsimspeedvectors - modified fluidsim mesh, not using speed vectors (%d,%d)...\n", obr->totvert, fsob->fluidsimSettings->meshSurface->totvert); // DEBUG
return 0;
}
velarray = (float *)fss->meshSurfNormals;
if(obi->flag & R_TRANSFORMED)
Mat4MulMat4(winmat, obi->mat, re->winmat);
else
Mat4CpyMat4(winmat, re->winmat);
/* (bad) HACK calculate average velocity */
/* better solution would be fixing getVelocityAt() in intern/elbeem/intern/solver_util.cpp
so that also small drops/little water volumes return a velocity != 0.
But I had no luck in fixing that function - DG */
for(a=0; a<obr->totvert; a++) {
for(j=0;j<3;j++) avgvel[j] += velarray[3*a + j];
}
for(j=0;j<3;j++) avgvel[j] /= (float)(obr->totvert);
for(a=0; a<obr->totvert; a++, vectors+=2) {
if((a & 255)==0)
ver= obr->vertnodes[a>>8].vert;
else
ver++;
// get fluid velocity
fsvec[3] = 0.;
//fsvec[0] = fsvec[1] = fsvec[2] = fsvec[3] = 0.; fsvec[2] = 2.; // NT fixed test
for(j=0;j<3;j++) fsvec[j] = velarray[3*a + j];
/* (bad) HACK insert average velocity if none is there (see previous comment) */
if((fsvec[0] == 0.0) && (fsvec[1] == 0.0) && (fsvec[2] == 0.0))
{
fsvec[0] = avgvel[0];
fsvec[1] = avgvel[1];
fsvec[2] = avgvel[2];
}
// transform (=rotate) to cam space
camco[0]= imat[0][0]*fsvec[0] + imat[0][1]*fsvec[1] + imat[0][2]*fsvec[2];
camco[1]= imat[1][0]*fsvec[0] + imat[1][1]*fsvec[1] + imat[1][2]*fsvec[2];
camco[2]= imat[2][0]*fsvec[0] + imat[2][1]*fsvec[1] + imat[2][2]*fsvec[2];
// get homogenous coordinates
projectvert(camco, winmat, hoco);
projectvert(ver->co, winmat, ho);
/* now map hocos to screenspace, uses very primitive clip still */
// use ho[3] of original vertex, xy component of vel. direction
if(ho[3]<0.1f) div= 10.0f;
else div= 1.0f/ho[3];
zco[0]= zmulx*hoco[0]*div;
zco[1]= zmuly*hoco[1]*div;
// maximize speed as usual
len= zco[0]*zco[0] + zco[1]*zco[1];
if(len > winsq) {
len= winroot/sqrt(len);
zco[0]*= len; zco[1]*= len;
}
speed= RE_vertren_get_winspeed(obi, ver, 1);
// set both to the same value
speed[0]= speed[2]= zco[0];
speed[1]= speed[3]= zco[1];
//if(a<20) fprintf(stderr,"speed %d %f,%f | camco %f,%f,%f | hoco %f,%f,%f,%f \n", a, speed[0], speed[1], camco[0],camco[1], camco[2], hoco[0],hoco[1], hoco[2],hoco[3]); // NT DEBUG
}
return 1;
}
/* makes copy per object of all vectors */
/* result should be that we can free entire database */
static void copy_dbase_object_vectors(Render *re, ListBase *lb)
{
ObjectInstanceRen *obi, *obilb;
ObjectRen *obr;
VertRen *ver= NULL;
float *vec, ho[4], winmat[4][4];
int a, totvector;
for(obi= re->instancetable.first; obi; obi= obi->next) {
obr= obi->obr;
obilb= MEM_mallocN(sizeof(ObjectInstanceRen), "ObInstanceVector");
memcpy(obilb, obi, sizeof(ObjectInstanceRen));
BLI_addtail(lb, obilb);
obilb->totvector= totvector= obr->totvert;
if(totvector > 0) {
vec= obilb->vectors= MEM_mallocN(2*sizeof(float)*totvector, "vector array");
if(obi->flag & R_TRANSFORMED)
Mat4MulMat4(winmat, obi->mat, re->winmat);
else
Mat4CpyMat4(winmat, re->winmat);
for(a=0; a<obr->totvert; a++, vec+=2) {
if((a & 255)==0) ver= obr->vertnodes[a>>8].vert;
else ver++;
projectvert(ver->co, winmat, ho);
speedvector_project(NULL, vec, ver->co, ho);
}
}
}
}
static void free_dbase_object_vectors(ListBase *lb)
{
ObjectInstanceRen *obi;
for(obi= lb->first; obi; obi= obi->next)
if(obi->vectors)
MEM_freeN(obi->vectors);
BLI_freelistN(lb);
}
void RE_Database_FromScene_Vectors(Render *re, Scene *sce)
{
ObjectInstanceRen *obi, *oldobi;
StrandSurface *mesh;
ListBase *table;
ListBase oldtable= {NULL, NULL}, newtable= {NULL, NULL};
ListBase strandsurface;
int step;
re->i.infostr= "Calculating previous vectors";
re->r.mode |= R_SPEED;
speedvector_project(re, NULL, NULL, NULL); /* initializes projection code */
/* creates entire dbase */
database_fromscene_vectors(re, sce, -1);
/* copy away vertex info */
copy_dbase_object_vectors(re, &oldtable);
/* free dbase and make the future one */
strandsurface= re->strandsurface;
memset(&re->strandsurface, 0, sizeof(ListBase));
RE_Database_Free(re);
re->strandsurface= strandsurface;
if(!re->test_break(re->tbh)) {
/* creates entire dbase */
re->i.infostr= "Calculating next frame vectors";
database_fromscene_vectors(re, sce, +1);
}
/* copy away vertex info */
copy_dbase_object_vectors(re, &newtable);
/* free dbase and make the real one */
strandsurface= re->strandsurface;
memset(&re->strandsurface, 0, sizeof(ListBase));
RE_Database_Free(re);
re->strandsurface= strandsurface;
if(!re->test_break(re->tbh))
RE_Database_FromScene(re, sce, 1);
if(!re->test_break(re->tbh)) {
for(step= 0; step<2; step++) {
if(step)
table= &newtable;
else
table= &oldtable;
oldobi= table->first;
for(obi= re->instancetable.first; obi && oldobi; obi= obi->next) {
int ok= 1;
FluidsimModifierData *fluidmd;
if(!(obi->obr->flag & R_NEED_VECTORS))
continue;
obi->totvector= obi->obr->totvert;
/* find matching object in old table */
if(oldobi->ob!=obi->ob || oldobi->par!=obi->par || oldobi->index!=obi->index || oldobi->psysindex!=obi->psysindex) {
ok= 0;
for(oldobi= table->first; oldobi; oldobi= oldobi->next)
if(oldobi->ob==obi->ob && oldobi->par==obi->par && oldobi->index==obi->index && oldobi->psysindex==obi->psysindex)
break;
if(oldobi==NULL)
oldobi= table->first;
else
ok= 1;
}
if(ok==0) {
printf("speed table: missing object %s\n", obi->ob->id.name+2);
continue;
}
// NT check for fluidsim special treatment
fluidmd = (FluidsimModifierData *)modifiers_findByType(obi->ob, eModifierType_Fluidsim);
if(fluidmd && fluidmd->fss && (fluidmd->fss->type & OB_FLUIDSIM_DOMAIN)) {
// use preloaded per vertex simulation data , only does calculation for step=1
// NOTE/FIXME - velocities and meshes loaded unnecessarily often during the database_fromscene_vectors calls...
load_fluidsimspeedvectors(re, obi, oldobi->vectors, step);
}
else {
/* check if both have same amounts of vertices */
if(obi->totvector==oldobi->totvector)
calculate_speedvectors(re, obi, oldobi->vectors, step);
else
printf("Warning: object %s has different amount of vertices or strands on other frame\n", obi->ob->id.name+2);
} // not fluidsim
oldobi= oldobi->next;
}
}
}
free_dbase_object_vectors(&oldtable);
free_dbase_object_vectors(&newtable);
for(mesh=re->strandsurface.first; mesh; mesh=mesh->next) {
if(mesh->prevco) {
MEM_freeN(mesh->prevco);
mesh->prevco= NULL;
}
if(mesh->nextco) {
MEM_freeN(mesh->nextco);
mesh->nextco= NULL;
}
}
re->i.infostr= NULL;
re->stats_draw(re->sdh, &re->i);
}
/* ------------------------------------------------------------------------- */
/* Baking */
/* ------------------------------------------------------------------------- */
/* setup for shaded view or bake, so only lamps and materials are initialized */
/* type:
RE_BAKE_LIGHT: for shaded view, only add lamps
RE_BAKE_ALL: for baking, all lamps and objects
RE_BAKE_NORMALS:for baking, no lamps and only selected objects
RE_BAKE_AO: for baking, no lamps, but all objects
RE_BAKE_TEXTURE:for baking, no lamps, only selected objects
RE_BAKE_DISPLACEMENT:for baking, no lamps, only selected objects
RE_BAKE_SHADOW: for baking, only shadows, but all objects
*/
void RE_Database_Baking(Render *re, Scene *scene, int type, Object *actob)
{
float mat[4][4];
float amb[3];
unsigned int lay;
int onlyselected, nolamps;
re->scene= scene;
/* renderdata setup and exceptions */
re->r= scene->r;
RE_init_threadcount(re);
re->flag |= R_GLOB_NOPUNOFLIP;
re->flag |= R_BAKING;
re->excludeob= actob;
if(actob)
re->flag |= R_BAKE_TRACE;
if(type==RE_BAKE_NORMALS && re->r.bake_normal_space==R_BAKE_SPACE_TANGENT)
re->flag |= R_NEED_TANGENT;
if(!actob && ELEM4(type, RE_BAKE_LIGHT, RE_BAKE_NORMALS, RE_BAKE_TEXTURE, RE_BAKE_DISPLACEMENT)) {
re->r.mode &= ~R_SHADOW;
re->r.mode &= ~R_RAYTRACE;
}
if(!actob && (type==RE_BAKE_SHADOW)) {
re->r.mode |= R_SHADOW;
}
/* setup render stuff */
re->memArena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
re->totvlak=re->totvert=re->totstrand=re->totlamp=re->tothalo= 0;
re->lights.first= re->lights.last= NULL;
re->lampren.first= re->lampren.last= NULL;
/* in localview, lamps are using normal layers, objects only local bits */
if(re->scene->lay & 0xFF000000) lay= re->scene->lay & 0xFF000000;
else lay= re->scene->lay;
/* if no camera, set unit */
if(re->scene->camera) {
Mat4Ortho(re->scene->camera->obmat);
Mat4Invert(mat, re->scene->camera->obmat);
RE_SetView(re, mat);
}
else {
Mat4One(mat);
RE_SetView(re, mat);
}
init_render_world(re); /* do first, because of ambient. also requires re->osa set correct */
if(re->r.mode & R_RAYTRACE) {
init_render_qmcsampler(re);
if(re->wrld.mode & WO_AMB_OCC)
if (re->wrld.ao_samp_method == WO_AOSAMP_CONSTANT)
init_ao_sphere(&re->wrld);
}
/* still bad... doing all */
init_render_textures(re);
if (re->r.color_mgt_flag & R_COLOR_MANAGEMENT) color_manage_linearize(amb, &re->wrld.ambr);
else VECCOPY(amb, &re->wrld.ambr);
init_render_materials(re->r.mode, amb);
set_node_shader_lamp_loop(shade_material_loop);
/* MAKE RENDER DATA */
nolamps= !ELEM3(type, RE_BAKE_LIGHT, RE_BAKE_ALL, RE_BAKE_SHADOW);
onlyselected= ELEM3(type, RE_BAKE_NORMALS, RE_BAKE_TEXTURE, RE_BAKE_DISPLACEMENT);
database_init_objects(re, lay, nolamps, onlyselected, actob, 0);
set_material_lightgroups(re);
/* SHADOW BUFFER */
if(type!=RE_BAKE_LIGHT)
if(re->r.mode & R_SHADOW)
threaded_makeshadowbufs(re);
/* raytree */
if(!re->test_break(re->tbh))
if(re->r.mode & R_RAYTRACE)
makeraytree(re);
/* occlusion */
if((re->wrld.mode & WO_AMB_OCC) && !re->test_break(re->tbh))
if(re->wrld.ao_gather_method == WO_AOGATHER_APPROX)
if(re->r.mode & R_SHADOW)
make_occ_tree(re);
}
/* ------------------------------------------------------------------------- */
/* Sticky texture coords */
/* ------------------------------------------------------------------------- */
void RE_make_sticky(Scene *scene, View3D *v3d)
{
Object *ob;
Base *base;
MVert *mvert;
Mesh *me;
MSticky *ms;
Render *re;
float ho[4], mat[4][4];
int a;
if(v3d==NULL) {
printf("Need a 3d view to make sticky\n");
return;
}
if(scene->camera==NULL) {
printf("Need camera to make sticky\n");
return;
}
if(scene->obedit) {
printf("Unable to make sticky in Edit Mode\n");
return;
}
re= RE_NewRender("_make sticky_");
RE_InitState(re, NULL, &scene->r, scene->r.xsch, scene->r.ysch, NULL);
/* use renderdata and camera to set viewplane */
RE_SetCamera(re, scene->camera);
/* and set view matrix */
Mat4Ortho(scene->camera->obmat);
Mat4Invert(mat, scene->camera->obmat);
RE_SetView(re, mat);
for(base= FIRSTBASE; base; base= base->next) {
if TESTBASELIB(v3d, base) {
if(base->object->type==OB_MESH) {
ob= base->object;
me= ob->data;
mvert= me->mvert;
if(me->msticky)
CustomData_free_layer_active(&me->vdata, CD_MSTICKY, me->totvert);
me->msticky= CustomData_add_layer(&me->vdata, CD_MSTICKY,
CD_CALLOC, NULL, me->totvert);
where_is_object(scene, ob);
Mat4MulMat4(mat, ob->obmat, re->viewmat);
ms= me->msticky;
for(a=0; a<me->totvert; a++, ms++, mvert++) {
VECCOPY(ho, mvert->co);
Mat4MulVecfl(mat, ho);
projectverto(ho, re->winmat, ho);
ms->co[0]= ho[0]/ho[3];
ms->co[1]= ho[1]/ho[3];
}
}
}
}
}