Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/render/intern/raytrace/rayobject_octree.cpp')
-rw-r--r--source/blender/render/intern/raytrace/rayobject_octree.cpp1101
1 files changed, 1101 insertions, 0 deletions
diff --git a/source/blender/render/intern/raytrace/rayobject_octree.cpp b/source/blender/render/intern/raytrace/rayobject_octree.cpp
new file mode 100644
index 00000000000..4b73e64ca45
--- /dev/null
+++ b/source/blender/render/intern/raytrace/rayobject_octree.cpp
@@ -0,0 +1,1101 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 1990-1998 NeoGeo BV.
+ * All rights reserved.
+ *
+ * Contributors: 2004/2005 Blender Foundation, full recode
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/render/intern/raytrace/rayobject_octree.cpp
+ * \ingroup render
+ */
+
+
+/* IMPORTANT NOTE: this code must be independent of any other render code
+ * to use it outside the renderer! */
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <float.h>
+#include <assert.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_material_types.h"
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "rayintersection.h"
+#include "rayobject.h"
+
+/* ********** structs *************** */
+#define BRANCH_ARRAY 1024
+#define NODE_ARRAY 4096
+
+typedef struct Branch {
+ struct Branch *b[8];
+} Branch;
+
+typedef struct OcVal {
+ short ocx, ocy, ocz;
+} OcVal;
+
+typedef struct Node {
+ struct RayFace *v[8];
+ struct OcVal ov[8];
+ struct Node *next;
+} Node;
+
+typedef struct Octree {
+ RayObject rayobj;
+
+ struct Branch **adrbranch;
+ struct Node **adrnode;
+ float ocsize; /* ocsize: mult factor, max size octree */
+ float ocfacx, ocfacy, ocfacz;
+ float min[3], max[3];
+ int ocres;
+ int branchcount, nodecount;
+
+ /* during building only */
+ char *ocface;
+
+ RayFace **ro_nodes;
+ int ro_nodes_size, ro_nodes_used;
+
+} Octree;
+
+static int RE_rayobject_octree_intersect(RayObject *o, Isect *isec);
+static void RE_rayobject_octree_add(RayObject *o, RayObject *ob);
+static void RE_rayobject_octree_done(RayObject *o);
+static void RE_rayobject_octree_free(RayObject *o);
+static void RE_rayobject_octree_bb(RayObject *o, float *min, float *max);
+
+/*
+ * This function is not expected to be called by current code state.
+ */
+static float RE_rayobject_octree_cost(RayObject *UNUSED(o))
+{
+ return 1.0;
+}
+
+static void RE_rayobject_octree_hint_bb(RayObject *UNUSED(o), RayHint *UNUSED(hint),
+ float *UNUSED(min), float *UNUSED(max))
+{
+ return;
+}
+
+static RayObjectAPI octree_api =
+{
+ RE_rayobject_octree_intersect,
+ RE_rayobject_octree_add,
+ RE_rayobject_octree_done,
+ RE_rayobject_octree_free,
+ RE_rayobject_octree_bb,
+ RE_rayobject_octree_cost,
+ RE_rayobject_octree_hint_bb
+};
+
+/* **************** ocval method ******************* */
+/* within one octree node, a set of 3x15 bits defines a 'boundbox' to OR with */
+
+#define OCVALRES 15
+#define BROW16(min, max) \
+ (((max) >= OCVALRES ? 0xFFFF : (1 << ((max) + 1)) - 1) - (((min) > 0) ? ((1 << (min)) - 1) : 0))
+
+static void calc_ocval_face(float *v1, float *v2, float *v3, float *v4, short x, short y, short z, OcVal *ov)
+{
+ float min[3], max[3];
+ int ocmin, ocmax;
+
+ copy_v3_v3(min, v1);
+ copy_v3_v3(max, v1);
+ DO_MINMAX(v2, min, max);
+ DO_MINMAX(v3, min, max);
+ if (v4) {
+ DO_MINMAX(v4, min, max);
+ }
+
+ ocmin = OCVALRES * (min[0] - x);
+ ocmax = OCVALRES * (max[0] - x);
+ ov->ocx = BROW16(ocmin, ocmax);
+
+ ocmin = OCVALRES * (min[1] - y);
+ ocmax = OCVALRES * (max[1] - y);
+ ov->ocy = BROW16(ocmin, ocmax);
+
+ ocmin = OCVALRES * (min[2] - z);
+ ocmax = OCVALRES * (max[2] - z);
+ ov->ocz = BROW16(ocmin, ocmax);
+
+}
+
+static void calc_ocval_ray(OcVal *ov, float xo, float yo, float zo, float *vec1, float *vec2)
+{
+ int ocmin, ocmax;
+
+ if (vec1[0] < vec2[0]) {
+ ocmin = OCVALRES * (vec1[0] - xo);
+ ocmax = OCVALRES * (vec2[0] - xo);
+ }
+ else {
+ ocmin = OCVALRES * (vec2[0] - xo);
+ ocmax = OCVALRES * (vec1[0] - xo);
+ }
+ ov->ocx = BROW16(ocmin, ocmax);
+
+ if (vec1[1] < vec2[1]) {
+ ocmin = OCVALRES * (vec1[1] - yo);
+ ocmax = OCVALRES * (vec2[1] - yo);
+ }
+ else {
+ ocmin = OCVALRES * (vec2[1] - yo);
+ ocmax = OCVALRES * (vec1[1] - yo);
+ }
+ ov->ocy = BROW16(ocmin, ocmax);
+
+ if (vec1[2] < vec2[2]) {
+ ocmin = OCVALRES * (vec1[2] - zo);
+ ocmax = OCVALRES * (vec2[2] - zo);
+ }
+ else {
+ ocmin = OCVALRES * (vec2[2] - zo);
+ ocmax = OCVALRES * (vec1[2] - zo);
+ }
+ ov->ocz = BROW16(ocmin, ocmax);
+}
+
+/* ************* octree ************** */
+
+static Branch *addbranch(Octree *oc, Branch *br, short ocb)
+{
+ int index;
+
+ if (br->b[ocb]) return br->b[ocb];
+
+ oc->branchcount++;
+ index = oc->branchcount >> 12;
+
+ if (oc->adrbranch[index] == NULL)
+ oc->adrbranch[index] = (Branch *)MEM_callocN(4096 * sizeof(Branch), "new oc branch");
+
+ if (oc->branchcount >= BRANCH_ARRAY * 4096) {
+ printf("error; octree branches full\n");
+ oc->branchcount = 0;
+ }
+
+ return br->b[ocb] = oc->adrbranch[index] + (oc->branchcount & 4095);
+}
+
+static Node *addnode(Octree *oc)
+{
+ int index;
+
+ oc->nodecount++;
+ index = oc->nodecount >> 12;
+
+ if (oc->adrnode[index] == NULL)
+ oc->adrnode[index] = (Node *)MEM_callocN(4096 * sizeof(Node), "addnode");
+
+ if (oc->nodecount > NODE_ARRAY * NODE_ARRAY) {
+ printf("error; octree nodes full\n");
+ oc->nodecount = 0;
+ }
+
+ return oc->adrnode[index] + (oc->nodecount & 4095);
+}
+
+static bool face_in_node(RayFace *face, short x, short y, short z, float rtf[4][3])
+{
+ static float nor[3], d;
+ float fx, fy, fz;
+
+ // init static vars
+ if (face) {
+ normal_tri_v3(nor, rtf[0], rtf[1], rtf[2]);
+ d = -nor[0] * rtf[0][0] - nor[1] * rtf[0][1] - nor[2] * rtf[0][2];
+ return 0;
+ }
+
+ fx = x;
+ fy = y;
+ fz = z;
+
+ if ((fx) * nor[0] + (fy) * nor[1] + (fz) * nor[2] + d > 0.0f) {
+ if ((fx + 1) * nor[0] + (fy ) * nor[1] + (fz ) * nor[2] + d < 0.0f) return 1;
+ if ((fx ) * nor[0] + (fy + 1) * nor[1] + (fz ) * nor[2] + d < 0.0f) return 1;
+ if ((fx + 1) * nor[0] + (fy + 1) * nor[1] + (fz ) * nor[2] + d < 0.0f) return 1;
+
+ if ((fx ) * nor[0] + (fy ) * nor[1] + (fz + 1) * nor[2] + d < 0.0f) return 1;
+ if ((fx + 1) * nor[0] + (fy ) * nor[1] + (fz + 1) * nor[2] + d < 0.0f) return 1;
+ if ((fx ) * nor[0] + (fy + 1) * nor[1] + (fz + 1) * nor[2] + d < 0.0f) return 1;
+ if ((fx + 1) * nor[0] + (fy + 1) * nor[1] + (fz + 1) * nor[2] + d < 0.0f) return 1;
+ }
+ else {
+ if ((fx + 1) * nor[0] + (fy ) * nor[1] + (fz ) * nor[2] + d > 0.0f) return 1;
+ if ((fx ) * nor[0] + (fy + 1) * nor[1] + (fz ) * nor[2] + d > 0.0f) return 1;
+ if ((fx + 1) * nor[0] + (fy + 1) * nor[1] + (fz ) * nor[2] + d > 0.0f) return 1;
+
+ if ((fx ) * nor[0] + (fy ) * nor[1] + (fz + 1) * nor[2] + d > 0.0f) return 1;
+ if ((fx + 1) * nor[0] + (fy ) * nor[1] + (fz + 1) * nor[2] + d > 0.0f) return 1;
+ if ((fx ) * nor[0] + (fy + 1) * nor[1] + (fz + 1) * nor[2] + d > 0.0f) return 1;
+ if ((fx + 1) * nor[0] + (fy + 1) * nor[1] + (fz + 1) * nor[2] + d > 0.0f) return 1;
+ }
+
+ return 0;
+}
+
+static void ocwrite(Octree *oc, RayFace *face, int quad, short x, short y, short z, float rtf[4][3])
+{
+ Branch *br;
+ Node *no;
+ short a, oc0, oc1, oc2, oc3, oc4, oc5;
+
+ x <<= 2;
+ y <<= 1;
+
+ br = oc->adrbranch[0];
+
+ if (oc->ocres == 512) {
+ oc0 = ((x & 1024) + (y & 512) + (z & 256)) >> 8;
+ br = addbranch(oc, br, oc0);
+ }
+ if (oc->ocres >= 256) {
+ oc0 = ((x & 512) + (y & 256) + (z & 128)) >> 7;
+ br = addbranch(oc, br, oc0);
+ }
+ if (oc->ocres >= 128) {
+ oc0 = ((x & 256) + (y & 128) + (z & 64)) >> 6;
+ br = addbranch(oc, br, oc0);
+ }
+
+ oc0 = ((x & 128) + (y & 64) + (z & 32)) >> 5;
+ oc1 = ((x & 64) + (y & 32) + (z & 16)) >> 4;
+ oc2 = ((x & 32) + (y & 16) + (z & 8)) >> 3;
+ oc3 = ((x & 16) + (y & 8) + (z & 4)) >> 2;
+ oc4 = ((x & 8) + (y & 4) + (z & 2)) >> 1;
+ oc5 = ((x & 4) + (y & 2) + (z & 1));
+
+ br = addbranch(oc, br, oc0);
+ br = addbranch(oc, br, oc1);
+ br = addbranch(oc, br, oc2);
+ br = addbranch(oc, br, oc3);
+ br = addbranch(oc, br, oc4);
+ no = (Node *)br->b[oc5];
+ if (no == NULL) br->b[oc5] = (Branch *)(no = addnode(oc));
+
+ while (no->next) no = no->next;
+
+ a = 0;
+ if (no->v[7]) { /* node full */
+ no->next = addnode(oc);
+ no = no->next;
+ }
+ else {
+ while (no->v[a] != NULL) a++;
+ }
+
+ no->v[a] = (RayFace *) RE_rayobject_align(face);
+
+ if (quad)
+ calc_ocval_face(rtf[0], rtf[1], rtf[2], rtf[3], x >> 2, y >> 1, z, &no->ov[a]);
+ else
+ calc_ocval_face(rtf[0], rtf[1], rtf[2], NULL, x >> 2, y >> 1, z, &no->ov[a]);
+}
+
+static void d2dda(Octree *oc, short b1, short b2, short c1, short c2, char *ocface, short rts[4][3], float rtf[4][3])
+{
+ int ocx1, ocx2, ocy1, ocy2;
+ int x, y, dx = 0, dy = 0;
+ float ox1, ox2, oy1, oy2;
+ float lambda, lambda_o, lambda_x, lambda_y, ldx, ldy;
+
+ ocx1 = rts[b1][c1];
+ ocy1 = rts[b1][c2];
+ ocx2 = rts[b2][c1];
+ ocy2 = rts[b2][c2];
+
+ if (ocx1 == ocx2 && ocy1 == ocy2) {
+ ocface[oc->ocres * ocx1 + ocy1] = 1;
+ return;
+ }
+
+ ox1 = rtf[b1][c1];
+ oy1 = rtf[b1][c2];
+ ox2 = rtf[b2][c1];
+ oy2 = rtf[b2][c2];
+
+ if (ox1 != ox2) {
+ if (ox2 - ox1 > 0.0f) {
+ lambda_x = (ox1 - ocx1 - 1.0f) / (ox1 - ox2);
+ ldx = -1.0f / (ox1 - ox2);
+ dx = 1;
+ }
+ else {
+ lambda_x = (ox1 - ocx1) / (ox1 - ox2);
+ ldx = 1.0f / (ox1 - ox2);
+ dx = -1;
+ }
+ }
+ else {
+ lambda_x = 1.0f;
+ ldx = 0;
+ }
+
+ if (oy1 != oy2) {
+ if (oy2 - oy1 > 0.0f) {
+ lambda_y = (oy1 - ocy1 - 1.0f) / (oy1 - oy2);
+ ldy = -1.0f / (oy1 - oy2);
+ dy = 1;
+ }
+ else {
+ lambda_y = (oy1 - ocy1) / (oy1 - oy2);
+ ldy = 1.0f / (oy1 - oy2);
+ dy = -1;
+ }
+ }
+ else {
+ lambda_y = 1.0f;
+ ldy = 0;
+ }
+
+ x = ocx1; y = ocy1;
+ lambda = MIN2(lambda_x, lambda_y);
+
+ while (true) {
+
+ if (x < 0 || y < 0 || x >= oc->ocres || y >= oc->ocres) {
+ /* pass*/
+ }
+ else {
+ ocface[oc->ocres * x + y] = 1;
+ }
+
+ lambda_o = lambda;
+ if (lambda_x == lambda_y) {
+ lambda_x += ldx;
+ x += dx;
+ lambda_y += ldy;
+ y += dy;
+ }
+ else {
+ if (lambda_x < lambda_y) {
+ lambda_x += ldx;
+ x += dx;
+ }
+ else {
+ lambda_y += ldy;
+ y += dy;
+ }
+ }
+ lambda = MIN2(lambda_x, lambda_y);
+ if (lambda == lambda_o) break;
+ if (lambda >= 1.0f) break;
+ }
+ ocface[oc->ocres * ocx2 + ocy2] = 1;
+}
+
+static void filltriangle(Octree *oc, short c1, short c2, char *ocface, short *ocmin, short *ocmax)
+{
+ int a, x, y, y1, y2;
+
+ for (x = ocmin[c1]; x <= ocmax[c1]; x++) {
+ a = oc->ocres * x;
+ for (y = ocmin[c2]; y <= ocmax[c2]; y++) {
+ if (ocface[a + y]) {
+ y++;
+ while (ocface[a + y] && y != ocmax[c2]) y++;
+ for (y1 = ocmax[c2]; y1 > y; y1--) {
+ if (ocface[a + y1]) {
+ for (y2 = y; y2 <= y1; y2++) ocface[a + y2] = 1;
+ y1 = 0;
+ }
+ }
+ y = ocmax[c2];
+ }
+ }
+ }
+}
+
+static void RE_rayobject_octree_free(RayObject *tree)
+{
+ Octree *oc = (Octree *)tree;
+
+#if 0
+ printf("branches %d nodes %d\n", oc->branchcount, oc->nodecount);
+ printf("raycount %d\n", raycount);
+ printf("ray coherent %d\n", coherent_ray);
+ printf("accepted %d rejected %d\n", accepted, rejected);
+#endif
+ if (oc->ocface)
+ MEM_freeN(oc->ocface);
+
+ if (oc->adrbranch) {
+ int a = 0;
+ while (oc->adrbranch[a]) {
+ MEM_freeN(oc->adrbranch[a]);
+ oc->adrbranch[a] = NULL;
+ a++;
+ }
+ MEM_freeN(oc->adrbranch);
+ oc->adrbranch = NULL;
+ }
+ oc->branchcount = 0;
+
+ if (oc->adrnode) {
+ int a = 0;
+ while (oc->adrnode[a]) {
+ MEM_freeN(oc->adrnode[a]);
+ oc->adrnode[a] = NULL;
+ a++;
+ }
+ MEM_freeN(oc->adrnode);
+ oc->adrnode = NULL;
+ }
+ oc->nodecount = 0;
+
+ MEM_freeN(oc);
+}
+
+
+RayObject *RE_rayobject_octree_create(int ocres, int size)
+{
+ Octree *oc = (Octree *)MEM_callocN(sizeof(Octree), "Octree");
+ assert(RE_rayobject_isAligned(oc) ); /* RayObject API assumes real data to be 4-byte aligned */
+
+ oc->rayobj.api = &octree_api;
+
+ oc->ocres = ocres;
+
+ oc->ro_nodes = (RayFace **)MEM_callocN(sizeof(RayFace *) * size, "octree rayobject nodes");
+ oc->ro_nodes_size = size;
+ oc->ro_nodes_used = 0;
+
+
+ return RE_rayobject_unalignRayAPI((RayObject *) oc);
+}
+
+
+static void RE_rayobject_octree_add(RayObject *tree, RayObject *node)
+{
+ Octree *oc = (Octree *)tree;
+
+ assert(RE_rayobject_isRayFace(node) );
+ assert(oc->ro_nodes_used < oc->ro_nodes_size);
+ oc->ro_nodes[oc->ro_nodes_used++] = (RayFace *)RE_rayobject_align(node);
+}
+
+static void octree_fill_rayface(Octree *oc, RayFace *face)
+{
+ float ocfac[3], rtf[4][3];
+ float co1[3], co2[3], co3[3], co4[3];
+ short rts[4][3];
+ short ocmin[3], ocmax[3];
+ char *ocface = oc->ocface; // front, top, size view of face, to fill in
+ int a, b, c, oc1, oc2, oc3, oc4, x, y, z, ocres2;
+
+ ocfac[0] = oc->ocfacx;
+ ocfac[1] = oc->ocfacy;
+ ocfac[2] = oc->ocfacz;
+
+ ocres2 = oc->ocres * oc->ocres;
+
+ copy_v3_v3(co1, face->v1);
+ copy_v3_v3(co2, face->v2);
+ copy_v3_v3(co3, face->v3);
+ if (RE_rayface_isQuad(face))
+ copy_v3_v3(co4, face->v4);
+
+ for (c = 0; c < 3; c++) {
+ rtf[0][c] = (co1[c] - oc->min[c]) * ocfac[c];
+ rts[0][c] = (short)rtf[0][c];
+ rtf[1][c] = (co2[c] - oc->min[c]) * ocfac[c];
+ rts[1][c] = (short)rtf[1][c];
+ rtf[2][c] = (co3[c] - oc->min[c]) * ocfac[c];
+ rts[2][c] = (short)rtf[2][c];
+ if (RE_rayface_isQuad(face)) {
+ rtf[3][c] = (co4[c] - oc->min[c]) * ocfac[c];
+ rts[3][c] = (short)rtf[3][c];
+ }
+ }
+
+ for (c = 0; c < 3; c++) {
+ oc1 = rts[0][c];
+ oc2 = rts[1][c];
+ oc3 = rts[2][c];
+ if (!RE_rayface_isQuad(face)) {
+ ocmin[c] = min_iii(oc1, oc2, oc3);
+ ocmax[c] = max_iii(oc1, oc2, oc3);
+ }
+ else {
+ oc4 = rts[3][c];
+ ocmin[c] = min_iiii(oc1, oc2, oc3, oc4);
+ ocmax[c] = max_iiii(oc1, oc2, oc3, oc4);
+ }
+ if (ocmax[c] > oc->ocres - 1) ocmax[c] = oc->ocres - 1;
+ if (ocmin[c] < 0) ocmin[c] = 0;
+ }
+
+ if (ocmin[0] == ocmax[0] && ocmin[1] == ocmax[1] && ocmin[2] == ocmax[2]) {
+ ocwrite(oc, face, RE_rayface_isQuad(face), ocmin[0], ocmin[1], ocmin[2], rtf);
+ }
+ else {
+
+ d2dda(oc, 0, 1, 0, 1, ocface + ocres2, rts, rtf);
+ d2dda(oc, 0, 1, 0, 2, ocface, rts, rtf);
+ d2dda(oc, 0, 1, 1, 2, ocface + 2 * ocres2, rts, rtf);
+ d2dda(oc, 1, 2, 0, 1, ocface + ocres2, rts, rtf);
+ d2dda(oc, 1, 2, 0, 2, ocface, rts, rtf);
+ d2dda(oc, 1, 2, 1, 2, ocface + 2 * ocres2, rts, rtf);
+ if (!RE_rayface_isQuad(face)) {
+ d2dda(oc, 2, 0, 0, 1, ocface + ocres2, rts, rtf);
+ d2dda(oc, 2, 0, 0, 2, ocface, rts, rtf);
+ d2dda(oc, 2, 0, 1, 2, ocface + 2 * ocres2, rts, rtf);
+ }
+ else {
+ d2dda(oc, 2, 3, 0, 1, ocface + ocres2, rts, rtf);
+ d2dda(oc, 2, 3, 0, 2, ocface, rts, rtf);
+ d2dda(oc, 2, 3, 1, 2, ocface + 2 * ocres2, rts, rtf);
+ d2dda(oc, 3, 0, 0, 1, ocface + ocres2, rts, rtf);
+ d2dda(oc, 3, 0, 0, 2, ocface, rts, rtf);
+ d2dda(oc, 3, 0, 1, 2, ocface + 2 * ocres2, rts, rtf);
+ }
+ /* nothing todo with triangle..., just fills :) */
+ filltriangle(oc, 0, 1, ocface + ocres2, ocmin, ocmax);
+ filltriangle(oc, 0, 2, ocface, ocmin, ocmax);
+ filltriangle(oc, 1, 2, ocface + 2 * ocres2, ocmin, ocmax);
+
+ /* init static vars here */
+ face_in_node(face, 0, 0, 0, rtf);
+
+ for (x = ocmin[0]; x <= ocmax[0]; x++) {
+ a = oc->ocres * x;
+ for (y = ocmin[1]; y <= ocmax[1]; y++) {
+ if (ocface[a + y + ocres2]) {
+ b = oc->ocres * y + 2 * ocres2;
+ for (z = ocmin[2]; z <= ocmax[2]; z++) {
+ if (ocface[b + z] && ocface[a + z]) {
+ if (face_in_node(NULL, x, y, z, rtf))
+ ocwrite(oc, face, RE_rayface_isQuad(face), x, y, z, rtf);
+ }
+ }
+ }
+ }
+ }
+
+ /* same loops to clear octree, doubt it can be done smarter */
+ for (x = ocmin[0]; x <= ocmax[0]; x++) {
+ a = oc->ocres * x;
+ for (y = ocmin[1]; y <= ocmax[1]; y++) {
+ /* x-y */
+ ocface[a + y + ocres2] = 0;
+
+ b = oc->ocres * y + 2 * ocres2;
+ for (z = ocmin[2]; z <= ocmax[2]; z++) {
+ /* y-z */
+ ocface[b + z] = 0;
+ /* x-z */
+ ocface[a + z] = 0;
+ }
+ }
+ }
+ }
+}
+
+static void RE_rayobject_octree_done(RayObject *tree)
+{
+ Octree *oc = (Octree *)tree;
+ int c;
+ float t00, t01, t02;
+ int ocres2 = oc->ocres * oc->ocres;
+
+ INIT_MINMAX(oc->min, oc->max);
+
+ /* Calculate Bounding Box */
+ for (c = 0; c < oc->ro_nodes_used; c++)
+ RE_rayobject_merge_bb(RE_rayobject_unalignRayFace(oc->ro_nodes[c]), oc->min, oc->max);
+
+ /* Alloc memory */
+ oc->adrbranch = (Branch **)MEM_callocN(sizeof(void *) * BRANCH_ARRAY, "octree branches");
+ oc->adrnode = (Node **)MEM_callocN(sizeof(void *) * NODE_ARRAY, "octree nodes");
+
+ oc->adrbranch[0] = (Branch *)MEM_callocN(4096 * sizeof(Branch), "makeoctree");
+
+ /* the lookup table, per face, for which nodes to fill in */
+ oc->ocface = (char *)MEM_callocN(3 * ocres2 + 8, "ocface");
+ memset(oc->ocface, 0, 3 * ocres2);
+
+ for (c = 0; c < 3; c++) { /* octree enlarge, still needed? */
+ oc->min[c] -= 0.01f;
+ oc->max[c] += 0.01f;
+ }
+
+ t00 = oc->max[0] - oc->min[0];
+ t01 = oc->max[1] - oc->min[1];
+ t02 = oc->max[2] - oc->min[2];
+
+ /* this minus 0.1 is old safety... seems to be needed? */
+ oc->ocfacx = (oc->ocres - 0.1f) / t00;
+ oc->ocfacy = (oc->ocres - 0.1f) / t01;
+ oc->ocfacz = (oc->ocres - 0.1f) / t02;
+
+ oc->ocsize = sqrtf(t00 * t00 + t01 * t01 + t02 * t02); /* global, max size octree */
+
+ for (c = 0; c < oc->ro_nodes_used; c++) {
+ octree_fill_rayface(oc, oc->ro_nodes[c]);
+ }
+
+ MEM_freeN(oc->ocface);
+ oc->ocface = NULL;
+ MEM_freeN(oc->ro_nodes);
+ oc->ro_nodes = NULL;
+
+#if 0
+ printf("%f %f - %f\n", oc->min[0], oc->max[0], oc->ocfacx);
+ printf("%f %f - %f\n", oc->min[1], oc->max[1], oc->ocfacy);
+ printf("%f %f - %f\n", oc->min[2], oc->max[2], oc->ocfacz);
+#endif
+}
+
+static void RE_rayobject_octree_bb(RayObject *tree, float *min, float *max)
+{
+ Octree *oc = (Octree *)tree;
+ DO_MINMAX(oc->min, min, max);
+ DO_MINMAX(oc->max, min, max);
+}
+
+/* check all faces in this node */
+static int testnode(Octree *UNUSED(oc), Isect *is, Node *no, OcVal ocval)
+{
+ short nr = 0;
+
+ /* return on any first hit */
+ if (is->mode == RE_RAY_SHADOW) {
+
+ for (; no; no = no->next) {
+ for (nr = 0; nr < 8; nr++) {
+ RayFace *face = no->v[nr];
+ OcVal *ov = no->ov + nr;
+
+ if (!face) break;
+
+ if ( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) {
+ if (RE_rayobject_intersect(RE_rayobject_unalignRayFace(face), is) )
+ return 1;
+ }
+ }
+ }
+ }
+ else {
+ /* else mirror or glass or shadowtra, return closest face */
+ int found = 0;
+
+ for (; no; no = no->next) {
+ for (nr = 0; nr < 8; nr++) {
+ RayFace *face = no->v[nr];
+ OcVal *ov = no->ov + nr;
+
+ if (!face) break;
+
+ if ( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) {
+ if (RE_rayobject_intersect(RE_rayobject_unalignRayFace(face), is) ) {
+ found = 1;
+ }
+ }
+ }
+ }
+
+ return found;
+ }
+
+ return 0;
+}
+
+/* find the Node for the octree coord x y z */
+static Node *ocread(Octree *oc, int x, int y, int z)
+{
+ Branch *br;
+ int oc1;
+
+ x <<= 2;
+ y <<= 1;
+
+ br = oc->adrbranch[0];
+
+ if (oc->ocres == 512) {
+ oc1 = ((x & 1024) + (y & 512) + (z & 256)) >> 8;
+ br = br->b[oc1];
+ if (br == NULL) {
+ return NULL;
+ }
+ }
+ if (oc->ocres >= 256) {
+ oc1 = ((x & 512) + (y & 256) + (z & 128)) >> 7;
+ br = br->b[oc1];
+ if (br == NULL) {
+ return NULL;
+ }
+ }
+ if (oc->ocres >= 128) {
+ oc1 = ((x & 256) + (y & 128) + (z & 64)) >> 6;
+ br = br->b[oc1];
+ if (br == NULL) {
+ return NULL;
+ }
+ }
+
+ oc1 = ((x & 128) + (y & 64) + (z & 32)) >> 5;
+ br = br->b[oc1];
+ if (br) {
+ oc1 = ((x & 64) + (y & 32) + (z & 16)) >> 4;
+ br = br->b[oc1];
+ if (br) {
+ oc1 = ((x & 32) + (y & 16) + (z & 8)) >> 3;
+ br = br->b[oc1];
+ if (br) {
+ oc1 = ((x & 16) + (y & 8) + (z & 4)) >> 2;
+ br = br->b[oc1];
+ if (br) {
+ oc1 = ((x & 8) + (y & 4) + (z & 2)) >> 1;
+ br = br->b[oc1];
+ if (br) {
+ oc1 = ((x & 4) + (y & 2) + (z & 1));
+ return (Node *)br->b[oc1];
+ }
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static int cliptest(float p, float q, float *u1, float *u2)
+{
+ float r;
+
+ if (p < 0.0f) {
+ if (q < p) return 0;
+ else if (q < 0.0f) {
+ r = q / p;
+ if (r > *u2) return 0;
+ else if (r > *u1) *u1 = r;
+ }
+ }
+ else {
+ if (p > 0.0f) {
+ if (q < 0.0f) return 0;
+ else if (q < p) {
+ r = q / p;
+ if (r < *u1) return 0;
+ else if (r < *u2) *u2 = r;
+ }
+ }
+ else if (q < 0.0f) return 0;
+ }
+ return 1;
+}
+
+/* extensive coherence checks/storage cancels out the benefit of it, and gives errors... we
+ * need better methods, sample code commented out below (ton) */
+
+#if 0
+
+in top : static int coh_nodes[16 * 16 * 16][6];
+in makeoctree : memset(coh_nodes, 0, sizeof(coh_nodes));
+
+static void add_coherence_test(int ocx1, int ocx2, int ocy1, int ocy2, int ocz1, int ocz2)
+{
+ short *sp;
+
+ sp = coh_nodes[(ocx2 & 15) + 16 * (ocy2 & 15) + 256 * (ocz2 & 15)];
+ sp[0] = ocx1; sp[1] = ocy1; sp[2] = ocz1;
+ sp[3] = ocx2; sp[4] = ocy2; sp[5] = ocz2;
+
+}
+
+static int do_coherence_test(int ocx1, int ocx2, int ocy1, int ocy2, int ocz1, int ocz2)
+{
+ short *sp;
+
+ sp = coh_nodes[(ocx2 & 15) + 16 * (ocy2 & 15) + 256 * (ocz2 & 15)];
+ if (sp[0] == ocx1 && sp[1] == ocy1 && sp[2] == ocz1 &&
+ sp[3] == ocx2 && sp[4] == ocy2 && sp[5] == ocz2) return 1;
+ return 0;
+}
+
+#endif
+
+/* return 1: found valid intersection */
+/* starts with is->orig.face */
+static int RE_rayobject_octree_intersect(RayObject *tree, Isect *is)
+{
+ Octree *oc = (Octree *)tree;
+ Node *no;
+ OcVal ocval;
+ float vec1[3], vec2[3], start[3], end[3];
+ float u1, u2, ox1, ox2, oy1, oy2, oz1, oz2;
+ float lambda_o, lambda_x, ldx, lambda_y, ldy, lambda_z, ldz, dda_lambda;
+ float o_lambda = 0;
+ int dx, dy, dz;
+ int xo, yo, zo, c1 = 0;
+ int ocx1, ocx2, ocy1, ocy2, ocz1, ocz2;
+
+ /* clip with octree */
+ if (oc->branchcount == 0) return 0;
+
+ /* do this before intersect calls */
+#if 0
+ is->facecontr = NULL; /* to check shared edge */
+ is->obcontr = 0;
+ is->faceisect = is->isect = 0; /* shared edge, quad half flag */
+ is->userdata = oc->userdata;
+#endif
+
+ copy_v3_v3(start, is->start);
+ madd_v3_v3v3fl(end, is->start, is->dir, is->dist);
+ ldx = is->dir[0] * is->dist;
+ o_lambda = is->dist;
+ u1 = 0.0f;
+ u2 = 1.0f;
+
+ /* clip with octree cube */
+ if (cliptest(-ldx, start[0] - oc->min[0], &u1, &u2)) {
+ if (cliptest(ldx, oc->max[0] - start[0], &u1, &u2)) {
+ ldy = is->dir[1] * is->dist;
+ if (cliptest(-ldy, start[1] - oc->min[1], &u1, &u2)) {
+ if (cliptest(ldy, oc->max[1] - start[1], &u1, &u2)) {
+ ldz = is->dir[2] * is->dist;
+ if (cliptest(-ldz, start[2] - oc->min[2], &u1, &u2)) {
+ if (cliptest(ldz, oc->max[2] - start[2], &u1, &u2)) {
+ c1 = 1;
+ if (u2 < 1.0f) {
+ end[0] = start[0] + u2 * ldx;
+ end[1] = start[1] + u2 * ldy;
+ end[2] = start[2] + u2 * ldz;
+ }
+
+ if (u1 > 0.0f) {
+ start[0] += u1 * ldx;
+ start[1] += u1 * ldy;
+ start[2] += u1 * ldz;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (c1 == 0) return 0;
+
+ /* reset static variables in ocread */
+ //ocread(oc, oc->ocres, 0, 0);
+
+ /* setup 3dda to traverse octree */
+ ox1 = (start[0] - oc->min[0]) * oc->ocfacx;
+ oy1 = (start[1] - oc->min[1]) * oc->ocfacy;
+ oz1 = (start[2] - oc->min[2]) * oc->ocfacz;
+ ox2 = (end[0] - oc->min[0]) * oc->ocfacx;
+ oy2 = (end[1] - oc->min[1]) * oc->ocfacy;
+ oz2 = (end[2] - oc->min[2]) * oc->ocfacz;
+
+ ocx1 = (int)ox1;
+ ocy1 = (int)oy1;
+ ocz1 = (int)oz1;
+ ocx2 = (int)ox2;
+ ocy2 = (int)oy2;
+ ocz2 = (int)oz2;
+
+ if (ocx1 == ocx2 && ocy1 == ocy2 && ocz1 == ocz2) {
+ no = ocread(oc, ocx1, ocy1, ocz1);
+ if (no) {
+ /* exact intersection with node */
+ vec1[0] = ox1; vec1[1] = oy1; vec1[2] = oz1;
+ vec2[0] = ox2; vec2[1] = oy2; vec2[2] = oz2;
+ calc_ocval_ray(&ocval, (float)ocx1, (float)ocy1, (float)ocz1, vec1, vec2);
+ if (testnode(oc, is, no, ocval) ) return 1;
+ }
+ }
+ else {
+ int found = 0;
+ //static int coh_ocx1, coh_ocx2, coh_ocy1, coh_ocy2, coh_ocz1, coh_ocz2;
+ float dox, doy, doz;
+ int eqval;
+
+ /* calc lambda en ld */
+ dox = ox1 - ox2;
+ doy = oy1 - oy2;
+ doz = oz1 - oz2;
+
+ if (dox < -FLT_EPSILON) {
+ ldx = -1.0f / dox;
+ lambda_x = (ocx1 - ox1 + 1.0f) * ldx;
+ dx = 1;
+ }
+ else if (dox > FLT_EPSILON) {
+ ldx = 1.0f / dox;
+ lambda_x = (ox1 - ocx1) * ldx;
+ dx = -1;
+ }
+ else {
+ lambda_x = 1.0f;
+ ldx = 0;
+ dx = 0;
+ }
+
+ if (doy < -FLT_EPSILON) {
+ ldy = -1.0f / doy;
+ lambda_y = (ocy1 - oy1 + 1.0f) * ldy;
+ dy = 1;
+ }
+ else if (doy > FLT_EPSILON) {
+ ldy = 1.0f / doy;
+ lambda_y = (oy1 - ocy1) * ldy;
+ dy = -1;
+ }
+ else {
+ lambda_y = 1.0f;
+ ldy = 0;
+ dy = 0;
+ }
+
+ if (doz < -FLT_EPSILON) {
+ ldz = -1.0f / doz;
+ lambda_z = (ocz1 - oz1 + 1.0f) * ldz;
+ dz = 1;
+ }
+ else if (doz > FLT_EPSILON) {
+ ldz = 1.0f / doz;
+ lambda_z = (oz1 - ocz1) * ldz;
+ dz = -1;
+ }
+ else {
+ lambda_z = 1.0f;
+ ldz = 0;
+ dz = 0;
+ }
+
+ xo = ocx1; yo = ocy1; zo = ocz1;
+ dda_lambda = min_fff(lambda_x, lambda_y, lambda_z);
+
+ vec2[0] = ox1;
+ vec2[1] = oy1;
+ vec2[2] = oz1;
+
+ /* this loop has been constructed to make sure the first and last node of ray
+ * are always included, even when dda_lambda==1.0f or larger */
+
+ while (true) {
+
+ no = ocread(oc, xo, yo, zo);
+ if (no) {
+
+ /* calculate ray intersection with octree node */
+ copy_v3_v3(vec1, vec2);
+ // dox, y, z is negative
+ vec2[0] = ox1 - dda_lambda * dox;
+ vec2[1] = oy1 - dda_lambda * doy;
+ vec2[2] = oz1 - dda_lambda * doz;
+ calc_ocval_ray(&ocval, (float)xo, (float)yo, (float)zo, vec1, vec2);
+
+ //is->dist = (u1 + dda_lambda * (u2 - u1)) * o_lambda;
+ if (testnode(oc, is, no, ocval) )
+ found = 1;
+
+ if (is->dist < (u1 + dda_lambda * (u2 - u1)) * o_lambda)
+ return found;
+ }
+
+
+ lambda_o = dda_lambda;
+
+ /* traversing octree nodes need careful detection of smallest values, with proper
+ * exceptions for equal lambdas */
+ eqval = (lambda_x == lambda_y);
+ if (lambda_y == lambda_z) eqval += 2;
+ if (lambda_x == lambda_z) eqval += 4;
+
+ if (eqval) { // only 4 cases exist!
+ if (eqval == 7) { // x=y=z
+ xo += dx; lambda_x += ldx;
+ yo += dy; lambda_y += ldy;
+ zo += dz; lambda_z += ldz;
+ }
+ else if (eqval == 1) { // x=y
+ if (lambda_y < lambda_z) {
+ xo += dx; lambda_x += ldx;
+ yo += dy; lambda_y += ldy;
+ }
+ else {
+ zo += dz; lambda_z += ldz;
+ }
+ }
+ else if (eqval == 2) { // y=z
+ if (lambda_x < lambda_y) {
+ xo += dx; lambda_x += ldx;
+ }
+ else {
+ yo += dy; lambda_y += ldy;
+ zo += dz; lambda_z += ldz;
+ }
+ }
+ else { // x=z
+ if (lambda_y < lambda_x) {
+ yo += dy; lambda_y += ldy;
+ }
+ else {
+ xo += dx; lambda_x += ldx;
+ zo += dz; lambda_z += ldz;
+ }
+ }
+ }
+ else { // all three different, just three cases exist
+ eqval = (lambda_x < lambda_y);
+ if (lambda_y < lambda_z) eqval += 2;
+ if (lambda_x < lambda_z) eqval += 4;
+
+ if (eqval == 7 || eqval == 5) { // x smallest
+ xo += dx; lambda_x += ldx;
+ }
+ else if (eqval == 2 || eqval == 6) { // y smallest
+ yo += dy; lambda_y += ldy;
+ }
+ else { // z smallest
+ zo += dz; lambda_z += ldz;
+ }
+
+ }
+
+ dda_lambda = min_fff(lambda_x, lambda_y, lambda_z);
+ if (dda_lambda == lambda_o) break;
+ /* to make sure the last node is always checked */
+ if (lambda_o >= 1.0f) break;
+ }
+ }
+
+ /* reached end, no intersections found */
+ return 0;
+}
+
+
+