diff options
author | Constantin Jucovschi <cj@ubuntu.localdomain> | 2009-04-24 07:20:22 -0400 |
---|---|---|
committer | Constantin Jucovschi <cj@ubuntu.localdomain> | 2009-04-24 07:20:22 -0400 |
commit | 8f27e65bddd7d4b8515ce620fb485fdd78fcdf89 (patch) | |
tree | bd328a4dd4f92d32202241b5e3a7f36177792c5f /applications/rview/cube_render.c | |
download | rasdaman-upstream-8.0.tar.gz rasdaman-upstream-8.0.tar.xz rasdaman-upstream-8.0.zip |
Initial commitv8.0
Diffstat (limited to 'applications/rview/cube_render.c')
-rw-r--r-- | applications/rview/cube_render.c | 3106 |
1 files changed, 3106 insertions, 0 deletions
diff --git a/applications/rview/cube_render.c b/applications/rview/cube_render.c new file mode 100644 index 0000000..250afce --- /dev/null +++ b/applications/rview/cube_render.c @@ -0,0 +1,3106 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** + * SOURCE: cube_render.c + * + * MODULE: applications/rview + * + * PURPOSE: + * Renderers for RasDaMan MDD of various base types. Renderers provided + * are: + * 3D: surface ( RenderCubeSurf() ), voxel ( RenderCubeVoxel() ) + * 2D: height-fields ( RenderHeightField() ). + * Misc primitives: lines ( RenderLineSegment() ), shaded polyings using + * a Z-Buffer ( RenderShadedPolygon() ). + * + * This file includes cube_render_line.c, cube_render_core.c, + * cube_render_voxline.c and cube_render_mesh.c to build renderers + * for different base types. + * + * The renderer module is standalone and can be used independently + * from rView. In the rView project it's used by rviewImage. + * + * COMMENTS: + * No comments + * + * BUGS: + */ + + +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> + +#include <limits.h> +#include <float.h> + + +#include "cube_render.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +/* Internally visible structs and declarations */ + +/* 0 for LSB, 1 for MSB */ +#ifdef LITTLE_ENDIAN +#define MACHINE_BYTE_ORDER 0 +#else +#define MACHINE_BYTE_ORDER 1 +#endif + +#define FIXPOINT_PREC 16 + + +/* Flags used in faces */ +#define CUBE_RENDER_FACE_HIDDEN 1 + + +/* Set debug level */ +#ifndef CUBE_RENDER_DEBUG +#define CUBE_RENDER_DEBUG 0 +#endif + +/* 8 bit values */ +typedef char int8; +typedef unsigned char uint8; + +/* 16 bit values */ +typedef short int16; +typedef unsigned short uint16; + +/* 32bit values */ +typedef long int32; +typedef unsigned long uint32; + +/* RGB data */ +typedef struct rgb_pixel { + uint8 r, g, b; +} rgb_pixel; + + + +#define MAXIMUM_SEGMENTS 7 +/* + * For voxel rendering: holds global and texture coordinates for one segment of a plane. + */ +typedef struct project_plane_intersect { + vertex_fp left_g; + vertex_fp right_g; + vertex_fp left_t; + vertex_fp right_t; + vertex_fp deltaLR_g; + vertex_fp deltaLR_t; + long left_p; + long right_p; +} project_plane_intersect; + +/* + * For approximating the normal when voxel rendering + */ +typedef struct norm_kernel_desc { + int region; + real_t *kernel; +} norm_kernel_desc; + +/* + * For voxel rendering: holds all global and texture coordinates for all segments of a + * plane, the number of planes, the scanline extent and additional rendering parameters. + */ +typedef struct project_plane_desc { + project_plane_intersect ppi[MAXIMUM_SEGMENTS]; + long left_p, right_p; + int segs; + unsigned long pixelThresholdLow; /* Minimum brightness threshold. Default 4. */ + unsigned long pixelThresholdHigh; /* Same for upper threshold. Default: type's maximum value */ + unsigned long weightThreshold; /* Terminate depth scan when this weight is reached. Default 64 */ + double pixelThresholdLowF; /* The same for FP types */ + double pixelThresholdHighF; + double weightThresholdF; + int weightQuantisation; /* log2 of weight quantisation steps */ + real_t zpro; + vertex_fp lights; + real_t lightsAmbient; + real_t lightsCos; + real_t lightsScale; + real_t lightsScintCos; + real_t lightsScintScale; + norm_kernel_desc *kDesc; + void *voxColour; /* For shading isosurfaces: each voxel gets same colour */ +} project_plane_desc; + + + +static void RenderCubeDumpFaces(const face *faces, int first, int last); +static void RenderCubeGetScanline(int ys, int faceNo, render_desc *renderDesc, int scaleTexture); + +/* Surface-oriented rendering cores */ +static void RenderCubeCore1(int faceNo, render_desc *renderDesc); +static void RenderCubeCore2(int faceNo, render_desc *renderDesc); +static void RenderCubeCore3(int faceNo, render_desc *renderDesc); +static void RenderCubeCore4(int faceNo, render_desc *renderDesc); +static void RenderCubeCore8(int faceNo, render_desc *renderDesc); + +/* Voxel-oriented rendering cores */ +static void RenderCubeVoxLine1(int line, project_plane_desc *ppd, const render_desc *renderDesc); +static void RenderCubeVoxLine2(int line, project_plane_desc *ppd, const render_desc *renderDesc); +static void RenderCubeVoxLine3(int line, project_plane_desc *ppd, const render_desc *renderDesc); +static void RenderCubeVoxLine3B(int line, project_plane_desc *ppd, const render_desc *renderDesc); +static void RenderCubeVoxLine4(int line, project_plane_desc *ppd, const render_desc *renderDesc); +static void RenderCubeVoxLine4F(int line, project_plane_desc *ppd, const render_desc *renderDesc); +static void RenderCubeVoxLine8F(int line, project_plane_desc *ppd, const render_desc *renderDesc); + + + + +#define sqr(x) (x)*(x) +#define RENDER_SWAP(x,y,h) h=x; x=y; y=h; + + +/* Number of vertices reserved per cube face (including x/z-clipping and appended 1st). + Each clipping pass can introduce a new vertex ==> 6, append first ==> 7. */ +#define VERTICES_PER_FACE 7 +/* The same for the clipped face. */ +#define VERTICES_CLIP_FACE 8 + +#define VERTICES_TOTAL (6*VERTICES_PER_FACE + VERTICES_CLIP_FACE) + + + + +#if (MACHINE_BYTE_ORDER == 0) +#define CUBE_RENDER_BSHIFT 0 +#define CUBE_RENDER_BSTEP 8 +#define CUBE_RENDER_SSHIFT 0 +#define CUBE_RENDER_SSTEP 16 +#else +#define CUBE_RENDER_BSHIFT 24 +#define CUBE_RENDER_BSTEP -8 +#define CUBE_RENDER_SSHIFT 16 +#define CUBE_RENDER_SSTEP -16 +#endif + + + + + +/* For easier writing down the cube initialisation. First coordinate is the constant. + Have to use vectors for initialising! */ +#define INIT_CUBE(x,y,z,u,v,w) \ + currentv[0].x = u.x; currentv[0].y = u.y; currentv[0].z = u.z; \ + currentv[1].x = u.x + w.x; currentv[1].y = u.y + w.y; currentv[1].z = u.z + w.z; \ + currentv[2].x = u.x + v.x + w.x; currentv[2].y = u.y + v.y + w.y; currentv[2].z = u.z + v.z + w.z; \ + currentv[3].x = u.x + v.x; currentv[3].y = u.y + v.y; currentv[3].z = u.z + v.z; \ + currentv += 7; + +/* Initialise the texture base vectors. They have to be normalised in such a way that + when multiplied with the cube's base-vectors the result is the cube texture's + base vector (i.e. the texture dimension). Therefore divide by L_2^2 instead of L_2. */ +#define INIT_TEXBASE(i,c) \ + l = sqr((real_t)(geomData[i].x)) + sqr((real_t)(geomData[i].y)) + sqr((real_t)(geomData[i].z)); \ + if (l > 0) { \ + h = ((real_t)(texDesc->c) / l); \ + t[i-1].x = h*geomData[i].x; t[i-1].y = h*geomData[i].y; t[i-1].z = h*geomData[i].z; \ + } \ + else { \ + return -1; \ + } + +/* Init a bounding box structure */ +#define INIT_BBOX(root) root minx = INT_MAX; root maxx = INT_MIN; \ + root miny = INT_MAX; root maxy = INT_MIN; + +/* Update the bounding box */ +#define UPDATE_BBOX(root,x,y) if (root minx > x) {root minx = x;} \ + if (root maxx < x) {root maxx = x;} \ + if (root miny > y) {root miny = y;} \ + if (root maxy < y) {root maxy = y;} + + + + + + + +/* Global vars */ +static norm_kernel_desc NormalizeKernelHomo = {-1, NULL}; +static norm_kernel_desc NormalizeKernelLinear = {-1, NULL}; +static norm_kernel_desc NormalizeKernelGauss = {-1, NULL}; +static norm_kernel_desc NormalizeKernelDummy = {0, NULL}; + + + +/* + * For debugging purposes: dumps all faces to stdout. + */ +#if (CUBE_RENDER_DEBUG > 0) +static void RenderCubeDumpFaces(const face *faces, int first, int last) +{ + int i, j; + + for (i=first; i<=last; i++) + { + printf("Face #%d\n", i); + for (j=0; j<faces[i].vertices; j++) + { + printf("\t(%f, %f, %f) : (%d, %d)\n", + faces[i].first[j].x, faces[i].first[j].y, faces[i].first[j].z, + faces[i].first_p[j].x, faces[i].first_p[j].y); + } + if (faces[i].vertices > 0) + { + printf("\t[%f, %f, %f] : [%d, %d]\n", + faces[i].first[j].x, faces[i].first[j].y, faces[i].first[j].z, + faces[i].first_p[j].x, faces[i].first_p[j].y); + } + printf("\n"); + } + fflush(stdout); +} +#endif + + + +/* + * Calculate the intersection of the current scanplane with the face number faceNo. + * If found the translation to texture coordinates is also performed here. + */ + +static void RenderCubeGetScanline(int ys, int faceNo, render_desc *renderDesc, int scaleTexture) +{ + int j; + face *cface; + real_t h; + real_t x, y, z, zpro, scanLine; + long xp; + vertex_fp *vx, *t, *sg, *st; + vertex_p *vxp; + + scanLine = (real_t)ys; + renderDesc->found = 0; zpro = (real_t)(renderDesc->graphEnv->zpro); + cface = renderDesc->faces + faceNo; + renderDesc->left_p = INT_MAX; renderDesc->right_p = INT_MIN; + + for (j=0, vx=cface->first, vxp=cface->first_p; j<cface->vertices; j++, vx++, vxp++) + { + /* Does this line segment intersect with the scanplane? */ + if (((vxp[1].y <= ys) && (vxp[0].y >= ys)) || + ((vxp[1].y >= ys) && (vxp[0].y <= ys))) + { + h = (vx[1].y - vx[0].y) * zpro - (vx[1].z - vx[0].z) * scanLine; + if (h != 0) + { + h = - (vx[0].y * zpro - vx[0].z * scanLine) / h; + } + /* Make sure it's in range (rounding errors could result in problems) */ + if (h < 0.0) h = 0.0; + if (h > 1.0) h = 1.0; + x = vx[0].x + h * (vx[1].x - vx[0].x); + y = vx[0].y + h * (vx[1].y - vx[0].y); + z = vx[0].z + h * (vx[1].z - vx[0].z); + xp = (long)((zpro * x) / z + 0.5); + + if (xp < renderDesc->left_p) + { + renderDesc->left_p = xp; + sg = &(renderDesc->left_g); sg->x = x; sg->y = y; sg->z = z; + renderDesc->found++; + } + if (xp > renderDesc->right_p) + { + renderDesc->right_p = xp; + sg = &(renderDesc->right_g); sg->x = x; sg->y = y; sg->z = z; + renderDesc->found++; + } + } + } + if (renderDesc->found == 0) return; + + /* Calculate the texel positions only when the texture descriptor is defined */ + if (renderDesc->texDesc != NULL) + { + vx = &(renderDesc->org); t = renderDesc->texbase; + /* Calculate the corresponding texel positions by projecting the global data to the + texture base vectors */ + sg = &(renderDesc->left_g); st = &(renderDesc->left_t); + st->x = (sg->x - vx->x)*t[0].x + (sg->y - vx->y)*t[0].y + (sg->z - vx->z)*t[0].z; + st->y = (sg->x - vx->x)*t[1].x + (sg->y - vx->y)*t[1].y + (sg->z - vx->z)*t[1].z; + st->z = (sg->x - vx->x)*t[2].x + (sg->y - vx->y)*t[2].y + (sg->z - vx->z)*t[2].z; + if (scaleTexture != 0) + { + st->x *= (1<<FIXPOINT_PREC); st->y *= (1<<FIXPOINT_PREC); st->z *= (1<<FIXPOINT_PREC); + } + if (st->x < 0.0) st->x = 0.0; if (st->x > renderDesc->tmax.x) st->x = renderDesc->tmax.x; + if (st->y < 0.0) st->y = 0.0; if (st->y > renderDesc->tmax.y) st->y = renderDesc->tmax.y; + if (st->z < 0.0) st->z = 0.0; if (st->z > renderDesc->tmax.z) st->z = renderDesc->tmax.z; +#if (CUBE_RENDER_DEBUG > 1) + printf("Left (%d, %d):\t(%f, %f, %f) : (%f, %f, %f)\n", + ys, faceNo, sg->x, sg->y, sg->z, st->x, st->y, st->z); +#endif + sg = &(renderDesc->right_g); st = &(renderDesc->right_t); + st->x = (sg->x - vx->x)*t[0].x + (sg->y - vx->y)*t[0].y + (sg->z - vx->z)*t[0].z; + st->y = (sg->x - vx->x)*t[1].x + (sg->y - vx->y)*t[1].y + (sg->z - vx->z)*t[1].z; + st->z = (sg->x - vx->x)*t[2].x + (sg->y - vx->y)*t[2].y + (sg->z - vx->z)*t[2].z; + if (scaleTexture != 0) + { + st->x *= (1<<FIXPOINT_PREC); st->y *= (1<<FIXPOINT_PREC); st->z *= (1<<FIXPOINT_PREC); + } + if (st->x < 0.0) st->x = 0.0; if (st->x > renderDesc->tmax.x) st->x = renderDesc->tmax.x; + if (st->y < 0.0) st->y = 0.0; if (st->y > renderDesc->tmax.y) st->y = renderDesc->tmax.y; + if (st->z < 0.0) st->z = 0.0; if (st->z > renderDesc->tmax.z) st->z = renderDesc->tmax.z; +#if (CUBE_RENDER_DEBUG > 1) + printf("Right (%d, %d):\t(%f, %f, %f) : (%f, %f, %f)\n", + ys, faceNo, sg->x, sg->y, sg->z, st->x, st->y, st->z); +#endif + } +} + + +/* + * The core functionality of the rendering engine: each function renders all the + * visible lines. The code generated is dependent on the following makros: + * + * TEXEL_BSIZE The size in bytes of one cell. + * TEXEL_POINTER The name of the texture pointer (basetype dependent!). + * TEXEL_MULTIPLIER Used when calculating the texel position. If the base type is an atomic + * type set it to 1 and TEXEL_POINTER to the atomic type. Otherwise set + * TEXEL_POINTER to texture.c (byte) and TEXEL_MULTIPLIER to TEXEL_BSIZE. + * TEXEL_ASSIGN Used to write one texel to the next pixel and incrementing the pixel ptr. + * TEXEL_ACCU_0..3 Used to read texels into a 32 bit var for cacheing. + */ + +/* Byte-sized base type */ +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_POINTER +#undef TEXEL_MULTIPLIER +#undef TEXEL_ASSIGN +#undef TEXEL_ACCU_0 +#undef TEXEL_ACCU_1 +#undef TEXEL_ACCU_2 +#undef TEXEL_ACCU_3 +#define RENDER_CORE_NAME RenderCubeCore1 +#define TEXEL_BSIZE 1 +#define TEXEL_POINTER texture.c +#define TEXEL_MULTIPLIER 1 +#define TEXEL_ASSIGN \ + *dest.c++ = (TEXEL_FETCH); TEXEL_STEP; +#define TEXEL_ACCU_0(a) \ + a = ((TEXEL_FETCH) << CUBE_RENDER_BSHIFT); TEXEL_STEP; +#define TEXEL_ACCU_1(a) \ + a |= ((TEXEL_FETCH) << (CUBE_RENDER_BSHIFT + CUBE_RENDER_BSTEP)); TEXEL_STEP; +#define TEXEL_ACCU_2(a) \ + a |= ((TEXEL_FETCH) << (CUBE_RENDER_BSHIFT + 2*CUBE_RENDER_BSTEP)); TEXEL_STEP; +#define TEXEL_ACCU_3(a) \ + a |= ((TEXEL_FETCH) << (CUBE_RENDER_BSHIFT + 3*CUBE_RENDER_BSTEP)); TEXEL_STEP; *dest.l++ = a; +#include "cube_render_core.c" + +/* Short-sized base type */ +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_POINTER +#undef TEXEL_MULTIPLIER +#undef TEXEL_ASSIGN +#undef TEXEL_ACCU_0 +#undef TEXEL_ACCU_1 +#undef TEXEL_ACCU_2 +#undef TEXEL_ACCU_3 +#define RENDER_CORE_NAME RenderCubeCore2 +#define TEXEL_BSIZE 2 +#define TEXEL_POINTER texture.s +#define TEXEL_MULTIPLIER 1 +#define TEXEL_ASSIGN \ + *dest.s++ = (TEXEL_FETCH); TEXEL_STEP; +#define TEXEL_ACCU_0(a) \ + a = ((TEXEL_FETCH) << CUBE_RENDER_SSHIFT); TEXEL_STEP; +#define TEXEL_ACCU_1(a) \ + a |= ((TEXEL_FETCH) << (CUBE_RENDER_SSHIFT + CUBE_RENDER_SSTEP)); TEXEL_STEP; *dest.l++ = a; +#define TEXEL_ACCU_2(a) \ + a = ((TEXEL_FETCH) << CUBE_RENDER_SSHIFT); TEXEL_STEP; +#define TEXEL_ACCU_3(a) \ + a |= ((TEXEL_FETCH) << (CUBE_RENDER_SSHIFT + CUBE_RENDER_SSTEP)); TEXEL_STEP; *dest.l++ = a; +#include "cube_render_core.c" + +/* Shared by RGB, Long and double: */ +#undef TEXEL_ACCU_0 +#undef TEXEL_ACCU_1 +#undef TEXEL_ACCU_2 +#undef TEXEL_ACCU_3 +#define TEXEL_ACCU_0(a) TEXEL_ASSIGN +#define TEXEL_ACCU_1(a) TEXEL_ASSIGN +#define TEXEL_ACCU_2(a) TEXEL_ASSIGN +#define TEXEL_ACCU_3(a) TEXEL_ASSIGN + +/* RGB-sized base type */ +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_POINTER +#undef TEXEL_MULTIPLIER +#undef TEXEL_ASSIGN +#define RENDER_CORE_NAME RenderCubeCore3 +#define TEXEL_BSIZE 3 +#define TEXEL_POINTER texture.c +#define TEXEL_MULTIPLIER 3 +#define TEXEL_ASSIGN \ + auxPtr = &TEXEL_FETCH; *dest.c++ = auxPtr[0]; *dest.c++ = auxPtr[1]; *dest.c++ = auxPtr[2]; TEXEL_STEP; +#include "cube_render_core.c" + +/* Long-sized base type */ +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_POINTER +#undef TEXEL_MULTIPLIER +#undef TEXEL_ASSIGN +#define RENDER_CORE_NAME RenderCubeCore4 +#define TEXEL_BSIZE 4 +#define TEXEL_POINTER texture.l +#define TEXEL_MULTIPLIER 1 +#define TEXEL_ASSIGN \ + *dest.l++ = (TEXEL_FETCH); TEXEL_STEP; +#include "cube_render_core.c" + + +/* Double-sized base type */ +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_POINTER +#undef TEXEL_MULTIPLIER +#undef TEXEL_ASSIGN +#define RENDER_CORE_NAME RenderCubeCore8 +#define TEXEL_BSIZE 8 +#define TEXEL_POINTER texture.l +#define TEXEL_MULTIPLIER 2 +#define TEXEL_ASSIGN \ + auxPtr = &TEXEL_FETCH; *dest.l++ = auxPtr[0]; *dest.l++ = auxPtr[1]; TEXEL_STEP; +#include "cube_render_core.c" + + + + +/* + * Function builds a clipped cube into the render_desc structure. Requires faces + * and graphEnv to be set up correctly. + */ + +void RenderCubeClipCube(const vertex_fp geomData[4], render_desc *renderDesc, int removeHidden) +{ + face *faces, *cface; + vertex_fp *currentv, *nextv, *clippedv, *pool; + vertex_p *currentv_p, *nextv_p, *clippedv_p, *pool_p; + vertex_fp norm; + int i, j, k, number; + real_t l, h; + real_t z0, z1, clip; + real_t zpro; + long longvar; + graph_env *graphEnv; + + faces = renderDesc->faces; + pool = renderDesc->faces[0].first; pool_p = renderDesc->faces[0].first_p; + graphEnv = renderDesc->graphEnv; zpro = (real_t)(graphEnv->zpro); + + /* Now expand descriptor to full cube description */ + /* Keep first two vertices of each face free (-> clipping) */ + currentv = pool + 2; + /* Init 3 vertices in pool which aren't needed yet with the coordinates of + geomData[0] + geomData[i] */ + for (i=0; i<3; i++) + { + pool[VERTICES_PER_FACE*i].x = geomData[0].x + geomData[i+1].x; + pool[VERTICES_PER_FACE*i].y = geomData[0].y + geomData[i+1].y; + pool[VERTICES_PER_FACE*i].z = geomData[0].z + geomData[i+1].z; + } + INIT_CUBE(z,x,y,geomData[0],geomData[1],geomData[2]); + INIT_CUBE(z,x,y,pool[2*VERTICES_PER_FACE],geomData[2],geomData[1]); + INIT_CUBE(x,y,z,geomData[0],geomData[2],geomData[3]); + INIT_CUBE(x,y,z,pool[0],geomData[3],geomData[2]); + INIT_CUBE(y,x,z,geomData[0],geomData[3],geomData[1]); + INIT_CUBE(y,x,z,pool[VERTICES_PER_FACE],geomData[1],geomData[3]); + + for (i=0; i<7; i++) faces[i].flags = 0; + + /* Init seventh (clipz-) face. */ + faces[6].vertices = 0; + faces[6].first = pool + 6*VERTICES_PER_FACE + 1; faces[6].first_p = pool_p + 6*VERTICES_PER_FACE + 1; + + /* First pass: clipz, remove hidden surfaces. */ + for (i=0; i<6; i++) + { + cface = faces + i; + cface->vertices = 4; + cface->first = pool + i*VERTICES_PER_FACE + 2; cface->first_p = pool_p + i*VERTICES_PER_FACE + 1; + /* Mustn't remove hidden surfaces before z-clipping! Otherwise z-clipping doesn't + work right. */ + + /* Init min / max values: get bounding box of projected cube. */ + INIT_BBOX(cface->bBox.); + + /* Append first vertex */ + cface->first[4].x = cface->first[0].x; + cface->first[4].y = cface->first[0].y; + cface->first[4].z = cface->first[0].z; + + /* z-clipping (vertices is still a constant 4 here) */ + clip = (real_t)(graphEnv->clipz); nextv = cface->first; z0 = nextv->z; + currentv = nextv-1; cface->first = currentv; currentv_p = cface->first_p; + for (j=0, number=0; j<4; j++) + { + z1 = nextv[1].z; + if ((z0 >= clip) || (z1 >= clip)) + { + clippedv = NULL; + if (z0 < clip) /* clip first: just store clipped vertex */ + { + h = (real_t)(z1 - 2*clip + z0) / (z1 - z0); + /* Have to trap this case or we might get identical consecutive vertices */ + currentv->x = (real_t)(0.5*(nextv[1].x + nextv[0].x - h*(nextv[1].x - nextv[0].x))); + currentv->y = (real_t)(0.5*(nextv[1].y + nextv[0].y - h*(nextv[1].y - nextv[0].y))); + currentv->z = clip; + clippedv = currentv; clippedv_p = currentv_p; + } + else if (z1 < clip) /* clip second: store first vertex (in range) AND clipped vertex */ + { + currentv->x = nextv->x; currentv->y = nextv->y; currentv->z = nextv->z; + /* Also do the central for the first vertex projection here */ + h = (zpro / z0); + currentv_p->x = (long)(h*(currentv->x)+0.5); currentv_p->y = (long)(h*(currentv->y)+0.5); + UPDATE_BBOX(cface->bBox., currentv_p->x, currentv_p->y); + currentv++; currentv_p++; + h = (real_t)(z1 - 2*clip + z0) / (z1 - z0); + currentv->x = (real_t)(0.5*(nextv[1].x + nextv[0].x - h*(nextv[1].x - nextv[0].x))); + currentv->y = (real_t)(0.5*(nextv[1].y + nextv[0].y - h*(nextv[1].y - nextv[0].y))); + currentv->z = clip; + clippedv = currentv; clippedv_p = currentv_p; + number++; + } + else + { + currentv->x = nextv->x; currentv->y = nextv->y; currentv->z = nextv->z; + } + + /* Do central projection (have to use currentv here rather than z0!) */ + h = (zpro / currentv->z); + currentv_p->x = (long)(h*(currentv->x)+0.5); currentv_p->y = (long)(h*(currentv->y)+0.5); + UPDATE_BBOX(cface->bBox., currentv_p->x, currentv_p->y); + currentv++; currentv_p++; number++; + + /* Was a vertex clipped? Yes ==> build seventh face */ + if (clippedv != NULL) + { +#if (CUBE_RENDER_DEBUG > 1) + printf("Clipped (face %d): (%f, %f, %f)\n", i, clippedv->x, clippedv->y, clippedv->z); +#endif + /* Compare projected vertex with ones already stored. This is used because projected + vertices are integers and thus the test for equality actually means something ;-) */ + for (k=0; k<faces[6].vertices; k++) + { + if ((faces[6].first_p[k].x == clippedv_p->x) && (faces[6].first_p[k].y == clippedv_p->y)) + break; + } + /* Didn't break loop, i.e. didn't find duplicate, therefore add it */ + if (k == faces[6].vertices) + { + faces[6].first[k].x = clippedv->x; + faces[6].first[k].y = clippedv->y; + faces[6].first[k].z = clippedv->z; + faces[6].first_p[k].x = clippedv_p->x; + faces[6].first_p[k].y = clippedv_p->y; + faces[6].vertices++; + } + } + } + z0 = z1; nextv++; + } + /* In case this face isn't visible after all set its vertex count to 0 */ + if ((number < 3) || (cface->bBox.minx > graphEnv->clipr) || (cface->bBox.maxx < graphEnv->clipl) + || (cface->bBox.miny > graphEnv->clipu) || (cface->bBox.maxy < graphEnv->clipd)) + { + cface->vertices = 0; +#if (CUBE_RENDER_DEBUG > 0) + printf("Clipz (%d): %d, %d, %d, %d : %d\n", i, cface->bBox.minx, cface->bBox.miny, cface->bBox.maxx, cface->bBox.maxy, number); +#endif + } + else + { + cface->vertices = number; + /* Remove identical vertices */ + number = 1; + currentv = cface->first; nextv = currentv+1; + currentv_p = cface->first_p; nextv_p = currentv_p + 1; + for (k=1; k<cface->vertices; k++) + { + l = sqr(nextv->x - currentv->x) + + sqr(nextv->y - currentv->y) + + sqr(nextv->z - currentv->z); + if (l > 1e-3) + { + currentv++; currentv_p++; number++; + currentv->x = nextv->x; currentv->y = nextv->y; currentv->z = nextv->z; + currentv_p->x = nextv_p->x; currentv_p->y = nextv_p->y; + } + nextv++; nextv_p++; + } + + currentv = cface->first + 1; k = 2; + /* Calculate face normal. Take into account that consecutive + vertices might be identical + but sets of 3 may be linearily correlated. So loop until the + normal vector isn't of zero length. */ + do + { + norm.x = (cface->first[1].y - cface->first[0].y) * (currentv[1].z - currentv[0].z) + - (currentv[1].y - currentv[0].y) * (cface->first[1].z - cface->first[0].z); + norm.y = (cface->first[1].z - cface->first[0].z) * (currentv[1].x - currentv[0].x) + - (currentv[1].z - currentv[0].z) * (cface->first[1].x - cface->first[0].x); + norm.z = (cface->first[1].x - cface->first[0].x) * (currentv[1].y - currentv[0].y) + - (currentv[1].x - currentv[0].x) * (cface->first[1].y - cface->first[0].y); + l = sqr(norm.x) + sqr(norm.y) + sqr(norm.z); k++; currentv++; + } + while ((l == 0) && (k < number)); + + currentv = cface->first; + + /* project normal to a straight line connecting the global origin and a point on + the face's surface (use first vertex for convenience). The sign of this scalar + product determines whether the face is visible or not. */ + l = norm.x * currentv->x + norm.y * currentv->y + norm.z * currentv->z; + + if (l >= 0) + { + cface->flags |= CUBE_RENDER_FACE_HIDDEN; + } + + if (removeHidden == 0) l = -1; + + if (l < 0) + { + cface->vertices = number; + /* Append first vertex again */ + currentv = cface->first + number; currentv_p = cface->first_p + number; + currentv->x = cface->first->x; currentv->y = cface->first->y; currentv->z = cface->first->z; + currentv_p->x = cface->first_p->x; currentv_p->y = cface->first_p->y; + } + else + { + cface->vertices = 0; +#if (CUBE_RENDER_DEBUG > 0) + printf("Face invisible (%d)\n", i); +#endif + } + } + } + + /* Some code to test the ordering of the clipped face's vertices: + cface = faces + 6; cface->first = pool+42; cface->first_p = pool_p+42; + cface->vertices = 6; + for (i=0; i<6; i++) + { + cface->first[i].x = 5*i*i; cface->first[i].y = 200*i*(0.5 - (i & 1)); cface->first[i].z = 0; + } + */ + + /* If a new face had to be created due to z-clipping, vertices have to be ordered so + there are no crossing lines. The orientation is irrelevant, however, because + the clipped face is always visible and the hidden face removal has already been + done at this point. Careful, however, because the face might be completely off + screen. */ + if ((number = faces[6].vertices) >= 3) + { + cface = faces + 6; + + /* Create a bounding box for the clipped face too */ + INIT_BBOX(cface->bBox.); + for (i=0; i<number; i++) + { + UPDATE_BBOX(cface->bBox., cface->first_p[i].x, cface->first_p[i].y); + } + /* Is bbox off screen? */ + if ((cface->bBox.minx > graphEnv->clipr) || (cface->bBox.maxx < graphEnv->clipl) + || (cface->bBox.miny > graphEnv->clipu) || (cface->bBox.maxy < graphEnv->clipd)) + {number = 0; cface->vertices = 0;} + + /* Basically the clipped face can contain up to 6 vertices. If there are + only 3 nothing has to be done. */ + if (number > 3) + { + real_t tangi[VERTICES_CLIP_FACE]; /* Will contain the tangens of each face */ + + /* Calculate the tangens of each vertex relative to the first one. Since tan is + strictly monotonous in (-pi/2, pi/2) ordering by tan(x) equals ordering by x. + For 2D-coordinates just use the _projected_ coordinates rather than do an + expensive transformation into face coordinates. */ + + /* First up the anchor point has to be the leftmost or rightmost one (since tan + can't tell the difference between y/x and -y/-x). Use left. */ + longvar = INT_MAX; j = 0; + for (i=0; i<number; i++) + { + if (cface->first_p[i].x < longvar) {longvar = cface->first_p[i].x; j = i;} + } + if (j != 0) + { + RENDER_SWAP(cface->first[0].x, cface->first[j].x, z0); + RENDER_SWAP(cface->first[0].y, cface->first[j].y, z0); + RENDER_SWAP(cface->first[0].z, cface->first[j].z, z0); + RENDER_SWAP(cface->first_p[0].x, cface->first_p[j].x, longvar); + RENDER_SWAP(cface->first_p[0].y, cface->first_p[j].y, longvar); + } + /* Now calculate the tangi */ + currentv_p = cface->first_p; nextv_p = currentv_p + 1; + for (i=1; i<number; i++, nextv_p++) + { + z0 = (real_t)(nextv_p->x - currentv_p->x); + z1 = (real_t)(nextv_p->y - currentv_p->y); + if (z0 < 0.0) {z0 = -z0; z1 = -z1;} + if (z0 < 1e-6) z0 = (real_t)1e-6; /* Trap division by very small values */ + tangi[i] = (z1/z0); + } + /* Now sort (Quicksort is not a good idea with <= 6 vertices) */ + for (i=1; i < number-1; i++) + { + for (j=i+1; j < number; j++) + { + if (tangi[i] > tangi[j]) + { + RENDER_SWAP(tangi[i], tangi[j], z0); + RENDER_SWAP(cface->first[i].x, cface->first[j].x, z0); + RENDER_SWAP(cface->first[i].y, cface->first[j].y, z0); + RENDER_SWAP(cface->first[i].z, cface->first[j].z, z0); + RENDER_SWAP(cface->first_p[i].x, cface->first_p[j].x, longvar); + RENDER_SWAP(cface->first_p[i].y, cface->first_p[j].y, longvar); + } + } + } + + /*printf("Tangi (%d): ", cface->vertices); + for (i=1; i<cface->vertices; i++) + { + printf("%f ", tangi[i]); + } + printf("\n"); fflush(stdout);*/ + } + + /* Append first vertex to new face too */ + currentv = cface->first + number; currentv_p = cface->first_p + number; + currentv->x = cface->first->x; currentv->y = cface->first->y; currentv->z = cface->first->z; + currentv_p->x = cface->first_p->x; currentv_p->y = cface->first_p->y; + } + else + { + faces[6].vertices = 0; + } + +#if (CUBE_RENDER_DEBUG > 0) + printf("Pass1 -- clipz + hidden face removal:\n"); + RenderCubeDumpFaces(faces, 0, 6); +#endif + + /* Clip at the left border by calculating intersections with the clipping plane. + Since each face's bounding box has been determined above this can't eliminate + any more faces, it's purely for efficiency. */ + for (i=0; i<7; i++) + { + cface = faces + i; + if ((cface->vertices > 0) && (cface->bBox.minx < graphEnv->clipl)) + { + /* Have to build the bbox anew in that case (updated y) */ + INIT_BBOX(cface->bBox.); + + nextv = cface->first; currentv = nextv-1; cface->first = currentv; + nextv_p = cface->first_p; currentv_p = nextv_p-1; cface->first_p = currentv_p; + z0 = (real_t)(nextv_p->x); + clip = (real_t)(graphEnv->clipl) - 0.5f; /* -0.5 is vital here or you get frayed edges. */ + for (j=0, number=0; j<cface->vertices; j++) + { + z1 = (real_t)(nextv_p[1].x); + if ((z0 >= clip) || (z1 >= clip)) + { + /* In case both >= clip or the 2nd vertex lies outside the range: copy 1st */ + if (z0 >= clip) + { + currentv->x = nextv->x; currentv->y = nextv->y; currentv->z = nextv->z; + currentv_p->x = nextv_p->x; currentv_p->y = nextv_p->y; + UPDATE_BBOX(cface->bBox., currentv_p->x, currentv_p->y); + currentv++; currentv_p++; number++; + } + /* Next the actual clipping */ + if ((z0 < clip) || (z1 < clip)) + { + l = ((nextv[1].x - nextv[0].x) * zpro - (nextv[1].z - nextv[0].z) * clip); + if (l == 0.0) {h = 0.0;} + else + { + h = (nextv->x * zpro - nextv->z * clip) / l; + } + currentv->x = nextv[0].x - h*(nextv[1].x - nextv[0].x); + currentv->y = nextv[0].y - h*(nextv[1].y - nextv[0].y); + currentv->z = nextv[0].z - h*(nextv[1].z - nextv[0].z); + currentv_p->x = graphEnv->clipl; currentv_p->y = (long)((zpro * currentv->y) / currentv->z + 0.5); + UPDATE_BBOX(cface->bBox., currentv_p->x, currentv_p->y); + currentv++; currentv_p++; number++; + } + } + z0 = z1; nextv++; nextv_p++; + } + cface->vertices = number; + /* Append first vertex */ + currentv->x = cface->first->x; currentv->y = cface->first->y; currentv->z = cface->first->z; + currentv_p->x = cface->first_p->x; currentv_p->y = cface->first_p->y; + } + + if (cface->vertices > 0) + { + /* Restrict bBox y values (do that _after_ x-clipping) */ + if (cface->bBox.miny < graphEnv->clipd) {cface->bBox.miny = graphEnv->clipd;} + if (cface->bBox.maxy > graphEnv->clipu) {cface->bBox.maxy = graphEnv->clipu;} + +#if (CUBE_RENDER_DEBUG > 0) + printf("Bounding Box: %d, %d, %d, %d\n", + cface->bBox.minx, cface->bBox.miny, cface->bBox.maxx, cface->bBox.maxy); +#endif + } + } + +#if (CUBE_RENDER_DEBUG > 0) + printf("Pass2 -- clipx\n"); + RenderCubeDumpFaces(faces, 0, 6); +#endif +} + + + + +/* + * Initialise texture descriptor and transform matrix + */ +static int CubeRenderInitTextures(const vertex_fp *geomData, const tex_desc *texDesc, render_desc *renderDesc, int scaleTexture) +{ + real_t l, h; + vertex_fp *t; + + t = renderDesc->texbase; + + /* Calculate the texture base vectors */ + INIT_TEXBASE(1,widthx); + INIT_TEXBASE(2,widthy); + INIT_TEXBASE(3,widthz); + + /* Highest legal value of texture coordinates scaled by fixpoint precision. + It's _terribly_ important where you subtract a fraction! You can get + _incredible_ distortions by subtracting before shifting. + Also the amount to subtract has to be set relative to the texture + dimensions because if the subtracted value is too small relative to + the number's size you get cases where (x - y) == x (e.g. dim = 300 + and real_t = float). */ + if (scaleTexture == 0) + { + renderDesc->tmax.x = ((real_t)(texDesc->widthx)) - 0.5f; + renderDesc->tmax.y = ((real_t)(texDesc->widthy)) - 0.5f; + renderDesc->tmax.z = ((real_t)(texDesc->widthz)) - 0.5f; + } + else + { + renderDesc->tmax.x = (real_t)((texDesc->widthx << FIXPOINT_PREC) - texDesc->widthx / 64.0); + renderDesc->tmax.y = (real_t)((texDesc->widthy << FIXPOINT_PREC) - texDesc->widthy / 64.0); + renderDesc->tmax.z = (real_t)((texDesc->widthz << FIXPOINT_PREC) - texDesc->widthz / 64.0); + } + + /*printf("%f,%f,%f\n", renderDesc->tmax.x, renderDesc->tmax.y, renderDesc->tmax.z);*/ + + renderDesc->texDesc = (tex_desc *)texDesc; + + return 0; +} + + + + +/* + * Render the bounding box. + */ +static void RenderCubeBBox(const render_desc *renderDesc, int hidden) +{ + int i, j; + unsigned int flagval; + face *faces; + graph_env *graphEnv; + + if (hidden == 0) flagval = 0; else flagval = CUBE_RENDER_FACE_HIDDEN; + faces = renderDesc->faces; + graphEnv = renderDesc->graphEnv; + + for (i=0; i<7; i++) + { + if ((faces[i].vertices != 0) && ((faces[i].flags & CUBE_RENDER_FACE_HIDDEN) == flagval)) + { + for (j=0; j<faces[i].vertices; j++) + { + RenderLineSegment(faces[i].first_p + j, faces[i].first_p + (j+1), (render_desc*)renderDesc, graphEnv->bbox_colour); + } + } + } +} + + + +/* + * Main function to render a cube in 3D. + * geomData contains four vertices: the origin (0) and the three base vectors. + * graphEnv describes where the plot goes to and what clippling should be applied. + * texDesc is the descriptor of the texture data (the cube's contents). + */ + +int RenderCubeSurf(const vertex_fp geomData[4], const graph_env *graphEnv, const tex_desc *texDesc) +{ + vertex_fp t[3]; + face faces[7]; /* For each face */ + vertex_fp pool[VERTICES_TOTAL]; + vertex_p pool_p[VERTICES_TOTAL]; +#if (CUBE_RENDER_DEBUG > 0) + vertex_fp *currentv, *nextv; + vertex_p *currentv_p, *nextv_p; + int j; + long longvar; +#endif + /*real_t l, h;*/ + int i; + render_desc renderDesc; + + renderDesc.texbase = t; + + CubeRenderInitTextures(geomData, texDesc, &renderDesc, 1); + +#if (CUBE_RENDER_DEBUG > 0) + /* Projections of the base vectors on the texture base vectors: */ + for (i=0; i<3; i++) + { + longvar = (long)(t[i].x * (real_t)(geomData[i+1].x) + t[i].y * (real_t)(geomData[i+1].y) + t[i].z * (real_t)(geomData[i+1].z)); + printf("texbase %d: (%f, %f, %f), max tex: %ld\n", i, t[i].x, t[i].y, t[i].z, longvar); + } + /* Scalar products of the texture base vectors (how orthogonal are they?) */ + for (i=0; i<2; i++) + { + for (j=i+1; j<3; j++) + { + printf("t_%d*t_%d: %g ", i, j, t[i].x*t[j].x + t[i].y*t[j].y + t[i].z*t[j].z); + } + } + printf("\n"); fflush(stdout); + /* Projection of cube diagonal on texture base vectors (= translation to texture coordinates) */ + pool->x = geomData[0].x + geomData[1].x + geomData[2].x + geomData[3].x; + pool->y = geomData[0].y + geomData[1].y + geomData[2].y + geomData[3].y; + pool->z = geomData[0].z + geomData[1].z + geomData[2].z + geomData[3].z; + printf("pool: %f, %f, %f\n", pool->x, pool->y, pool->z); + for (i=0; i<3; i++) + { + printf("tex_%d = %f ", i, ((pool->x - geomData[0].x)*t[i].x + (pool->y - geomData[0].y)*t[i].y + (pool->z - geomData[0].z)*t[i].z)); + } + printf("\n"); fflush(stdout); +#endif + + /* Set up rendering descriptor */ + renderDesc.faces = faces; + renderDesc.faces[0].first = pool; renderDesc.faces[0].first_p = pool_p; + renderDesc.graphEnv = (graph_env *)graphEnv; + + /* Set up descriptor structure for scanline analyser */ + renderDesc.org.x = (real_t)(geomData[0].x); + renderDesc.org.y = (real_t)(geomData[0].y); + renderDesc.org.z = (real_t)(geomData[0].z); + + RenderCubeClipCube(geomData, &renderDesc, 1); + + /*for (i=0; i<7; i++) + { + j = faces[i].first + faces[i].vertices - pool; + if ((j >= VERTICES_TOTAL) || (j < 0)) {printf("ARGHH1 (%d: %d)!!!", i, j); fflush(stdout); exit(0);} + j = faces[i].first_p + faces[i].vertices - pool_p; + if ((j >= VERTICES_TOTAL) || (j < 0)) {printf("ARGHH2 (%d: %d)!!!", i, j); fflush(stdout); exit(0);} + }*/ + + /* Now render all faces */ + for (i=0; i<7; i++) + { + if (faces[i].vertices > 0) + { +#if (CUBE_RENDER_DEBUG > 0) + currentv = faces[i].first; nextv = currentv + faces[i].vertices; + currentv_p = faces[i].first_p; nextv_p = currentv_p + faces[i].vertices; + if ((currentv->x != nextv->x) || (currentv->y != nextv->y) || (currentv->z != nextv->z) || (currentv_p->x != nextv_p->x) || (currentv_p->y != nextv_p->y)) + { + printf("First vertex not appended correctly (%f, %f, %f : %d, %d)!\n", nextv->x, nextv->y, nextv->z, nextv_p->x, nextv_p->y); fflush(stdout); + } +#endif + switch (texDesc->baseSize) + { + case 1: RenderCubeCore1(i, &renderDesc); break; + case 2: RenderCubeCore2(i, &renderDesc); break; + case 3: RenderCubeCore3(i, &renderDesc); break; + case 4: RenderCubeCore4(i, &renderDesc); break; + case 8: RenderCubeCore8(i, &renderDesc); break; + default: fprintf(stderr, "Bad base type size (%d)\n", texDesc->baseSize); exit(-1); + } + } + } + + if ((graphEnv->bbox_colour) != 0xffffffff) + { + RenderCubeBBox(&renderDesc, 0); + } + +#if (CUBE_RENDER_DEBUG > 0) + printf("CubeRender successful.\n"); fflush(stdout); +#endif + + return(0); +} + + + + +/* + * Render one line in voxel mode for various depths + */ + +#define RENDER_CAST_TYPE uint32 + +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_FETCH +#define RENDER_CORE_NAME RenderCubeVoxLine1 +#define TEXEL_BSIZE 1 +#define TEXEL_FETCH (texture.c[(((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz + (tz >> FIXPOINT_PREC))]) +#define RENDER_TABLE_TYPE texture.c +#include "cube_render_voxline.c" + +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_FETCH +#undef RENDER_TABLE_TYPE +#define RENDER_CORE_NAME RenderCubeVoxLine2 +#define TEXEL_BSIZE 2 +#define TEXEL_FETCH (texture.s[(((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz + (tz >> FIXPOINT_PREC))]) +#define RENDER_TABLE_TYPE texture.s +#include "cube_render_voxline.c" + +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_FETCH +#undef RENDER_TABLE_TYPE +#define RENDER_CORE_NAME RenderCubeVoxLine3 +#define TEXEL_BSIZE 3 +#define TEXEL_FETCH srcPix=(texture.c + (((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz + (tz >> FIXPOINT_PREC)) * 3); +#define RENDER_TABLE_TYPE texture.c +#include "cube_render_voxline.c" + +#undef RENDER_CORE_NAME +#define RENDER_CORE_NAME RenderCubeVoxLine3B +#define TEXEL_RGB_BRIGHTNESS +#include "cube_render_voxline.c" +#undef TEXEL_RGB_BRIGHTNESS + +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_FETCH +#undef RENDER_TABLE_TYPE +#define RENDER_CORE_NAME RenderCubeVoxLine4 +#define TEXEL_BSIZE 4 +#define TEXEL_FETCH (texture.l[(((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz + (tz >> FIXPOINT_PREC))]) +#define RENDER_TABLE_TYPE texture.l +#include "cube_render_voxline.c" + +#undef RENDER_CAST_TYPE + +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_FETCH +#undef RENDER_TABLE_TYPE +#define RENDER_CAST_TYPE float +#define RENDER_FLOAT_TYPE float +#define RENDER_CORE_NAME RenderCubeVoxLine4F +#define TEXEL_BSIZE 4 +#define TEXEL_FETCH (texture.f[(((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz + (tz >> FIXPOINT_PREC))]) +#define RENDER_TABLE_TYPE texture.f +#include "cube_render_voxline.c" + +#undef RENDER_CAST_TYPE +#undef RENDER_FLOAT_TYPE +#undef RENDER_CORE_NAME +#undef TEXEL_BSIZE +#undef TEXEL_FETCH +#undef RENDER_TABLE_TYPE +#define RENDER_CAST_TYPE double +#define RENDER_FLOAT_TYPE double +#define RENDER_CORE_NAME RenderCubeVoxLine8F +#define TEXEL_BSIZE 8 +#define TEXEL_FETCH (texture.d[(((tx >> FIXPOINT_PREC) * dimy + (ty >> FIXPOINT_PREC)) * dimz + (tz >> FIXPOINT_PREC))]) +#define RENDER_TABLE_TYPE texture.d +#include "cube_render_voxline.c" + +#undef RENDER_FLOAT_TYPE + + + + +#define RENDER_SWAP_PLANE_DESC(i,j) \ + memcpy(&aux, ppd->ppi + i, sizeof(project_plane_intersect)); \ + memcpy(ppd->ppi + i, ppd->ppi + j, sizeof(project_plane_intersect)); \ + memcpy(ppd->ppi + j, &aux, sizeof(project_plane_intersect)); + +/* sort project_plane_desc structure into front/back segments and init some members */ +static void CubeRenderNormalizePlane(project_plane_desc *ppd) +{ + int i; + long min, max; + /*int j; + unsigned long isBack; + real_t nom, den, pos, h, actual_z, zpro; + project_plane_intersect aux;*/ + + /* Eliminate segments that are parallel to the scan-beam */ + i=0; + while (i < ppd->segs) + { + if (ppd->ppi[i].left_p == ppd->ppi[i].right_p) + { + (ppd->segs)--; + if (i < ppd->segs) + { + memmove(ppd->ppi + i, ppd->ppi + (i+1), (ppd->segs - i) * sizeof(project_plane_intersect)); + } + } + else i++; + } + +#if 0 + /* Sorting doesn't work well at all... */ + for (i=0; i<ppd->segs; i++) + { + /* Use deltaLR for coordinates of middle of segment */ + ppd->ppi[i].deltaLR_g.x = (ppd->ppi[i].left_g.x + ppd->ppi[i].right_g.x) / 2; + ppd->ppi[i].deltaLR_g.y = (ppd->ppi[i].left_g.y + ppd->ppi[i].right_g.y) / 2; + ppd->ppi[i].deltaLR_g.z = (ppd->ppi[i].left_g.z + ppd->ppi[i].right_g.z) / 2; + } + + /* Leftmost segment (described by middle) to position 0 */ + for (i=1; i<ppd->segs; i++) + { + if (ppd->ppi[i].deltaLR_g.x < ppd->ppi[0].deltaLR_g.x) + { + RENDER_SWAP_PLANE_DESC(i,0); + } + } + /* Determine tan, using deltaLR_t.x */ + for (i=1; i<ppd->segs; i++) + { + h = (ppd->ppi[i].deltaLR_g.x - ppd->ppi[0].deltaLR_g.x); + if (h == 0.0) h = 1e-4; + ppd->ppi[i].deltaLR_t.x = (ppd->ppi[i].deltaLR_g.z - ppd->ppi[0].deltaLR_g.z) / h; + } + /* Sort by tan in ascending order (i.e. counter-clockwise) */ + for (i=1; i<ppd->segs-1; i++) + { + for (j=i+1; j<ppd->segs; j++) + { + if (ppd->ppi[j].deltaLR_t.x < ppd->ppi[i].deltaLR_t.x) + { + RENDER_SWAP_PLANE_DESC(i,j); + } + } + } + /* Make sure there are no gaps between segments */ + min = ppd->ppi[0].left_p; max = min; + for (i=0; i<ppd->segs; i++) + { + j = i+1; if (j >= ppd->segs) j = 0; + if ((ppd->ppi[i].left_p != ppd->ppi[j].left_p) && (ppd->ppi[i].right_p != ppd->ppi[j].right_p) + && (ppd->ppi[i].left_p != ppd->ppi[j].right_p) && (ppd->ppi[i].right_p != ppd->ppi[j].left_p)) + { + int dll, drr, dlr, drl; + + dll = ppd->ppi[i].left_p - ppd->ppi[j].left_p; if (dll < 0) dll = -dll; + drr = ppd->ppi[i].right_p - ppd->ppi[j].right_p; if (drr < 0) drr = -drr; + dlr = ppd->ppi[i].left_p - ppd->ppi[j].right_p; if (dlr < 0) dlr = -dlr; + drl = ppd->ppi[i].right_p - ppd->ppi[j].left_p; if (drl < 0) drl = -drl; + if ((dll < drr) && (dll < dlr) && (dll < drl)) + { + ppd->ppi[i].left_p = ppd->ppi[j].left_p; + } + else if ((drr < dll) && (drr < dlr) && (drr < drl)) + { + ppd->ppi[i].right_p = ppd->ppi[j].right_p; + } + else if ((dlr < dll) && (dlr < drr) && (dlr < drl)) + { + ppd->ppi[i].left_p = ppd->ppi[j].right_p; + } + else + { + ppd->ppi[i].right_p = ppd->ppi[j].left_p; + } + } + if (ppd->ppi[i].left_p < min) min = ppd->ppi[i].left_p; + if (ppd->ppi[i].right_p > max) max = ppd->ppi[i].right_p; + } + ppd->left_p = min; ppd->right_p = max; + + /* + * If the first segment is a back segment is a back segment then its left_p is identical + * to the next segment's. Since the polygon is convex there can only be two cases of + * orderings of the leftmost front / back segments. + */ + if (ppd->ppi[0].left_p == ppd->ppi[1].left_p) + { + /* First segment is back segment ==> move to end */ + memcpy(&aux, ppd->ppi + 0, sizeof(project_plane_intersect)); + memmove(ppd->ppi + 0, ppd->ppi + 1, (ppd->segs - 1) * sizeof(project_plane_intersect)); + memcpy(ppd->ppi + (ppd->segs - 1), &aux, sizeof(project_plane_intersect)); + } + + /*for (i=0; i<ppd->segs; i++) + { + printf("%f,%f,%f\n", ppd->ppi[i].deltaLR_g.x, ppd->ppi[i].deltaLR_g.y, ppd->ppi[i].deltaLR_g.z); + } + printf("\n");*/ +#endif + + min = ppd->ppi[0].left_p; max = min; + for (i=0; i<ppd->segs; i++) + { + /*printf("[%d: %d,%d] ", i, ppd->ppi[i].left_p, ppd->ppi[i].right_p);*/ + ppd->ppi[i].deltaLR_g.x = ppd->ppi[i].right_g.x - ppd->ppi[i].left_g.x; + ppd->ppi[i].deltaLR_g.y = ppd->ppi[i].right_g.y - ppd->ppi[i].left_g.y; + ppd->ppi[i].deltaLR_g.z = ppd->ppi[i].right_g.z - ppd->ppi[i].left_g.z; + ppd->ppi[i].deltaLR_t.x = ppd->ppi[i].right_t.x - ppd->ppi[i].left_t.x; + ppd->ppi[i].deltaLR_t.y = ppd->ppi[i].right_t.y - ppd->ppi[i].left_t.y; + ppd->ppi[i].deltaLR_t.z = ppd->ppi[i].right_t.z - ppd->ppi[i].left_t.z; + if (ppd->ppi[i].left_p < min) min = ppd->ppi[i].left_p; + if (ppd->ppi[i].right_p > max) max = ppd->ppi[i].right_p; + } + ppd->left_p = min; ppd->right_p = max; + /*printf("\n");*/ + + /*for (i=0; i<ppd->segs-1; i++) + { + for (j=i+1; j<ppd->segs; j++) + { + if (ppd->ppi[j].left_p < ppd->ppi[i].left_p) + { + RENDER_SWAP_PLANE_DESC(i,j); + } + } + }*/ +} + + + + +int RenderCube(const vertex_fp geomData[4], const graph_env *graphEnv, const tex_desc *texDesc) +{ + voxel_desc voxDesc; + tex_desc newTexDesc; + + voxDesc.pixelThresholdLow = (double)4; + voxDesc.pixelThresholdHigh = (double)0xffffffff; + voxDesc.weightThreshold = (double)64; + voxDesc.weightQuantisation = 4; + voxDesc.useRgbBrightness = 0; + /*voxDesc.lights.x = 0.70710678; voxDesc.lights.y = 0.70710678; voxDesc.lights.z = 0;*/ + voxDesc.light.lights.x = 512; voxDesc.light.lights.y = 512; voxDesc.light.lights.z = (real_t)(graphEnv->zpro); + voxDesc.light.ambient = 0.5; voxDesc.light.gain = 1.0; + voxDesc.light.cosine = 0.0; voxDesc.light.scintCos = 0.0; + voxDesc.kernSize = 2; voxDesc.kernType = RENDER_NORM_KERNEL_GAUSS; + voxDesc.voxColour = NULL; + memcpy(&newTexDesc, texDesc, sizeof(tex_desc)); + newTexDesc.floatType = 0; + newTexDesc.minVal = 0.0; newTexDesc.maxVal = 1e6; + return RenderCubeVoxel(geomData, graphEnv, &newTexDesc, &voxDesc); +} + + +static norm_kernel_desc *RenderCubeEnsureNormKernel(voxel_desc *voxDesc) +{ + if (voxDesc->light.ambient >= 0) + { + norm_kernel_desc *kDesc; + int ksize; + + switch (voxDesc->kernType) + { + case RENDER_NORM_KERNEL_HOMO: kDesc = &NormalizeKernelHomo; break; + case RENDER_NORM_KERNEL_LINEAR: kDesc = &NormalizeKernelLinear; break; + case RENDER_NORM_KERNEL_GAUSS: kDesc = &NormalizeKernelGauss; break; + default: kDesc = &NormalizeKernelDummy; return kDesc; + } + if (kDesc->region != voxDesc->kernSize) + { + int i, j, k; + real_t *kernel; + + if (kDesc->kernel != NULL) + { + free(kDesc->kernel); kDesc->kernel = NULL; kDesc->region = -1; + } + if ((kDesc->region = voxDesc->kernSize) == 0) return kDesc; + ksize = 2 * voxDesc->kernSize + 1; + ksize = ksize * ksize * ksize; + if ((kDesc->kernel = (real_t*)malloc(ksize * sizeof(real_t))) == NULL) + return NULL; + kernel = kDesc->kernel; + /* This doesn't have to be efficient */ + for (i=-voxDesc->kernSize; i<=voxDesc->kernSize; i++) + { + for (j=-voxDesc->kernSize; j<=voxDesc->kernSize; j++) + { + for (k=-voxDesc->kernSize; k<=voxDesc->kernSize; k++) + { + switch (voxDesc->kernType) + { + case RENDER_NORM_KERNEL_HOMO: + *kernel = 1.0; + break; + case RENDER_NORM_KERNEL_LINEAR: + *kernel = (real_t)(1.0 - sqrt(sqr(i) + sqr(j) + sqr(k)) / (sqrt(3) * voxDesc->kernSize)); + break; + case RENDER_NORM_KERNEL_GAUSS: + *kernel = (real_t)(exp(-(sqr(i) + sqr(j) + sqr(k)) / 2)); + break; + default: + break; + } + kernel++; + } + } + } + /*kernel = kDesc->kernel; + for (i=-voxDesc->kernSize; i<=voxDesc->kernSize; i++) + { + for (j=-voxDesc->kernSize; j<=voxDesc->kernSize; j++) + { + printf("%d,%d: ", i, j); + for (k=-voxDesc->kernSize; k<=voxDesc->kernSize; k++) + { + printf("%f ", *kernel); kernel++; + } + printf("\n"); + } + }*/ + } + return kDesc; + } + return NULL; +} + + + +/* + * Voxel renderer. + */ + +int RenderCubeVoxel(const vertex_fp geomData[4], const graph_env *graphEnv, const tex_desc *texDesc, voxel_desc *voxDesc) +{ + vertex_fp t[3]; + face faces[7]; + vertex_fp pool[VERTICES_TOTAL]; + vertex_p pool_p[VERTICES_TOTAL]; + project_plane_desc ppd; + int segsPerLine; + render_desc renderDesc; + long miny_p, maxy_p; + int i, j; + double h; + + /* standard renderer preamble (see RenderCube()) */ + renderDesc.texbase = t; + + CubeRenderInitTextures(geomData, texDesc, &renderDesc, 0); + + renderDesc.faces = faces; + renderDesc.faces[0].first = pool; renderDesc.faces[0].first_p = pool_p; + renderDesc.graphEnv = (graph_env *)graphEnv; + + renderDesc.org.x = (real_t)(geomData[0].x); + renderDesc.org.y = (real_t)(geomData[0].y); + renderDesc.org.z = (real_t)(geomData[0].z); + + RenderCubeClipCube(geomData, &renderDesc, 0); + + /* Determine bounding box of entire cube */ + miny_p = graphEnv->clipu + 1; + maxy_p = graphEnv->clipd - 1; + for (i=0; i<7; i++) + { + if (faces[i].vertices != 0) + { + if (faces[i].bBox.miny < miny_p) miny_p = faces[i].bBox.miny; + if (faces[i].bBox.maxy > maxy_p) maxy_p = faces[i].bBox.maxy; + } + } + /* vertical clipping */ + if ((miny_p > graphEnv->clipu) || (maxy_p < graphEnv->clipd)) return 0; + if (miny_p < graphEnv->clipd) miny_p = graphEnv->clipd; + if (maxy_p > graphEnv->clipu) maxy_p = graphEnv->clipu; + + /* Set rendering thresholds */ + if ((texDesc->floatType != 0) || (texDesc->baseSize == 8)) + { + ppd.pixelThresholdLowF = voxDesc->pixelThresholdLow; + ppd.pixelThresholdHighF = voxDesc->pixelThresholdHigh; + ppd.weightThresholdF = voxDesc->weightThreshold; + } + else + { + ppd.pixelThresholdLow = (voxDesc->pixelThresholdLow > (double)ULONG_MAX) ? ULONG_MAX : (unsigned long)(voxDesc->pixelThresholdLow); + ppd.pixelThresholdHigh = (voxDesc->pixelThresholdHigh > (double)ULONG_MAX) ? ULONG_MAX : (unsigned long)(voxDesc->pixelThresholdHigh); + ppd.weightThreshold = (voxDesc->weightThreshold > (double)ULONG_MAX) ? ULONG_MAX : (unsigned long)(voxDesc->weightThreshold); + /*printf("%ul:%f, %ul:%f, %ul:%f\n", ppd.pixelThresholdLow, voxDesc->pixelThresholdLow, ppd.pixelThresholdHigh, voxDesc->pixelThresholdHigh, ppd.weightThreshold, voxDesc->weightThreshold);*/ + } + ppd.weightQuantisation = voxDesc->weightQuantisation; + ppd.zpro = (real_t)graphEnv->zpro; + + /* + * Transform the lights into texture coordinates. This is done by applying the + * same rotations to the lightsource that are necessary to rotate the cube into + * a standard orthonormal 3D base. + */ + if ((ppd.lightsAmbient = (real_t)(voxDesc->light.ambient)) >= 0) + { + vertex_fp lights; + real_t gain; + + if ((gain = (real_t)(voxDesc->light.gain)) < 0) gain = 0.0; + if ((ppd.lightsCos = (real_t)(voxDesc->light.cosine)) > 0.999) ppd.lightsCos = 0.999f; + ppd.lightsScale = (real_t)(1 / (1.0 - ppd.lightsCos)); + if ((ppd.lightsScintCos = (real_t)(voxDesc->light.scintCos)) > 0.999) ppd.lightsScintCos = 0.999f; + ppd.lightsScintScale = (real_t)(gain / (1.0 - ppd.lightsScintCos)); + + /* Translate light coordinates into texture coordinates */ + lights.x = voxDesc->light.lights.x - geomData[0].x; + lights.y = voxDesc->light.lights.y - geomData[0].y; + lights.z = voxDesc->light.lights.z - geomData[0].z; + +#if 0 + rotation_desc rd[3]; + + RenderCubeDetermineRotation(geomData+1, rd); + + /* Rotate light into texture coordinates */ + h = rd[2].cos * lights.x + rd[2].sin * lights.y; + lights.y = -rd[2].sin * lights.x + rd[2].cos * lights.y; + lights.x = h; + h = rd[1].cos * lights.x + rd[1].sin * lights.z; + lights.z = -rd[1].sin * lights.x + rd[1].cos * lights.z; + lights.x = h; + h = rd[0].cos * lights.y + rd[0].sin * lights.z; + lights.z = -rd[0].sin * lights.y + rd[0].cos * lights.z; + lights.y = h; + + ppd.lights.x = lights.x; + ppd.lights.y = lights.y; + ppd.lights.z = lights.z; +#else + +#define PROJECT_LIGHT_SOURCE(n,c) \ + h = sqr(geomData[n].x) + sqr(geomData[n].y) + sqr(geomData[n].z); \ + if (h != 0) \ + { \ + h = 1/sqrt(h); ppd.lights.c = (real_t)(h * (geomData[n].x * (lights.x-geomData[0].x) + geomData[n].y * (lights.y-geomData[0].y) + geomData[n].z * (lights.z)-geomData[0].z)); \ + } \ + + PROJECT_LIGHT_SOURCE(1,x); + PROJECT_LIGHT_SOURCE(2,y); + PROJECT_LIGHT_SOURCE(3,z); +#endif + + ppd.kDesc = RenderCubeEnsureNormKernel(voxDesc); + } + ppd.voxColour = voxDesc->voxColour; + + /* Render hidden surfaces first */ + if (graphEnv->bbox_colour != 0xffffffff) + { + RenderCubeBBox(&renderDesc, 1); + } + + /* Now render the voxels */ + for (i=maxy_p; i>=miny_p; i--) + { + /*printf("line %d\n", i);*/ + segsPerLine = 0; + for (j=0; j<7; j++) + { + if (faces[j].vertices != 0) + { + RenderCubeGetScanline(i, j, &renderDesc, 0); + if (renderDesc.found != 0) + { + ppd.ppi[segsPerLine].left_g = renderDesc.left_g; + ppd.ppi[segsPerLine].right_g = renderDesc.right_g; + ppd.ppi[segsPerLine].left_t = renderDesc.left_t; + ppd.ppi[segsPerLine].right_t = renderDesc.right_t; + ppd.ppi[segsPerLine].left_p = renderDesc.left_p; + ppd.ppi[segsPerLine].right_p = renderDesc.right_p; + ppd.segs = ++segsPerLine; + } + } + } + /* need at least two segments for further computation */ + if (segsPerLine < 2) continue; + + CubeRenderNormalizePlane(&ppd); + + switch (texDesc->baseSize) + { + case 1: RenderCubeVoxLine1(i, &ppd, &renderDesc); break; + case 2: RenderCubeVoxLine2(i, &ppd, &renderDesc); break; + case 3: + if (voxDesc->useRgbBrightness == 0) + RenderCubeVoxLine3(i, &ppd, &renderDesc); + else + RenderCubeVoxLine3B(i, &ppd, &renderDesc); + break; + case 4: + if (texDesc->floatType == 0) + RenderCubeVoxLine4(i, &ppd, &renderDesc); + else + RenderCubeVoxLine4F(i, &ppd, &renderDesc); + break; + case 8: RenderCubeVoxLine8F(i, &ppd, &renderDesc); break; + default: fprintf(stderr, "Bad base type size (%d)\n", texDesc->baseSize); exit(-1); break; + } + } + + if (graphEnv->bbox_colour != 0xffffffff) + { + RenderCubeBBox(&renderDesc, 0); + } + +#if 0 + if (voxDesc->lightsAmbient >= 0) + { + vertex_p from, to; + + printf("Lights at %f, %f, %f\n", ppd.lights.x, ppd.lights.y, ppd.lights.z); + from.x = (geomData[0].x * graphEnv->zpro) / geomData[0].z; + from.y = (geomData[0].y * graphEnv->zpro) / geomData[0].z; + to.x = ((geomData[0].x + ppd.lights.x) * graphEnv->zpro) / (geomData[0].z + ppd.lights.z); + to.y = ((geomData[0].y + ppd.lights.y) * graphEnv->zpro) / (geomData[0].z + ppd.lights.z); + RenderLineSegment(&from, &to, &renderDesc, graphEnv->bbox_colour); + } +#endif + + return 0; +} + + + +/* + * Functions for external use of the clipped cube. + */ + +render_desc *RenderCubeBuild(const vertex_fp geomData[4], const graph_env *graphEnv) +{ + render_desc *renderDesc; + + if ((renderDesc = (render_desc *)malloc(sizeof(render_desc))) == NULL) + return NULL; + if ((renderDesc->faces = (face *)malloc(7*sizeof(face))) == NULL) + { + free(renderDesc); return NULL; + } + if ((renderDesc->faces[0].first = (vertex_fp *)malloc(VERTICES_TOTAL * sizeof(vertex_fp))) == NULL) + { + free(renderDesc->faces); free(renderDesc); return NULL; + } + if ((renderDesc->faces[0].first_p = (vertex_p *)malloc(VERTICES_TOTAL * sizeof(vertex_p))) == NULL) + { + free(renderDesc->faces[0].first); free(renderDesc->faces); free(renderDesc); return NULL; + } + renderDesc->graphEnv = (graph_env *)graphEnv; + /* Setting the texture descriptor to NULL disables the calculation of left_t, right_t in + RenderCubeGetScanline. */ + renderDesc->texDesc = NULL; + + RenderCubeClipCube(geomData, renderDesc, 1); + + return renderDesc; +} + + + +void RenderCubeFreeDesc(render_desc *renderDesc) +{ + free(renderDesc->faces[0].first_p); + free(renderDesc->faces[0].first); + free(renderDesc->faces); + free(renderDesc); +} + + + +/* + * This function rotates a cube into the standard orthonormal 3D-base and + * logs the necessary rotations. Rotation is done in z,y,x order, so in + * order to rotate a standard base such that its axes coincide with the + * input cube's the rotations have to be applied in x,y,z order with + * negative sin values. + */ +void RenderCubeDetermineRotation(const vertex_fp *base, rotation_desc *rd) +{ + vertex_fp e[3]; + double len; + double c, s, h; + int i; + + /* Normalize the base */ + for (i=0; i<3; i++) + { + len = sqr(base[i].x) + sqr(base[i].y) + sqr(base[i].z); + h = 1/sqrt(len); + e[i].x = (real_t)(h*base[i].x); e[i].y = (real_t)(h*base[i].y); e[i].z = (real_t)(h*base[i].z); + } + + /* First rotate x-vector around z into xz plane */ + len = sqr(e[0].x) + sqr(e[0].y); + if (fabs(len) <= 1e-12) + { + rd[2].sin = 0.0; rd[2].cos = 1; + } + else + { + h = 1/sqrt(len); c = h * e[0].x; s = h * e[0].y; + rd[2].sin = s; rd[2].cos = c; + for (i=0; i<3; i++) + { + h = c*e[i].x + s*e[i].y; + e[i].y = (real_t)(-s*e[i].x + c*e[i].y); + e[i].x = (real_t)h; + /*printf("RotZ: %f, %f, %f\n", e[i].x, e[i].y, e[i].z);*/ + } + } + /* Then rotate x-vector around y to x axis */ + len = sqr(e[0].x) + sqr(e[0].z); + h = 1/sqrt(len); c = h * e[0].x; s = h * e[0].z; + rd[1].sin = s; rd[1].cos = c; + for (i=0; i<3; i++) + { + h = c*e[i].x + s*e[i].z; + e[i].z = (real_t)(-s*e[i].x + c*e[i].z); + e[i].x = (real_t)h; + /*printf("RotY: %f, %f, %f\n", e[i].x, e[i].y, e[i].z);*/ + } + /* Finally rotate y-vector around x to y-axis */ + len = sqr(e[1].y) + sqr(e[1].z); + h = 1/sqrt(len); c = h * e[1].y; s = h * e[1].z; + rd[0].sin = s; rd[0].cos = c; + for (i=0; i<3; i++) + { + h = c*e[i].y + s*e[i].z; + e[i].z = (real_t)(-s*e[i].y + c*e[i].z); + e[i].y = (real_t)h; + /*printf("RotX: %f, %f, %f\n", e[i].x, e[i].y, e[i].z);*/ + } +} + + + +/* + * This function can be called to get the 3D coordinates of the intersection of a + * line from (0,0) through (x_p, y_p) and the cube. It requires renderDesc to be + * set up by the function RenderCubeBuild. + */ + +int RenderCubeGetPosition(int x_p, int y_p, vertex_fp *pos, render_desc *renderDesc) +{ + int i, status; + face *cface; + real_t h, zpro, x_r; + + status = 0; zpro = (real_t)(renderDesc->graphEnv->zpro); x_r = (real_t)x_p; + for (i=0; (i<7) && (status == 0); i++) + { + cface = renderDesc->faces + i; + if (renderDesc->faces[i].vertices > 0) + { + if ((y_p >= cface->bBox.miny) && (y_p <= cface->bBox.maxy)) + { + RenderCubeGetScanline(y_p, i, renderDesc, 1); + if (renderDesc->found != 0) + { + if ((x_p >= renderDesc->left_p) && (x_p <= renderDesc->right_p)) + { + pos->x = renderDesc->right_g.x - renderDesc->left_g.x; + pos->y = renderDesc->right_g.y - renderDesc->left_g.y; + pos->z = renderDesc->right_g.z - renderDesc->left_g.z; + h = pos->x * zpro - pos->z * x_r; + if (h != 0) + { + h = - (renderDesc->left_g.x * zpro - renderDesc->left_g.z * x_r) / h; + } + if (h < 0.0) h = 0.0; + if (h > 1.0) h = 1.0; + pos->x = renderDesc->left_g.x + h * pos->x; + pos->y = renderDesc->left_g.y + h * pos->y; + pos->z = renderDesc->left_g.z + h * pos->z; + status = 1; + } + } + } + } + } + return status; +} + + + +/* Used for plotting the lines in various depths */ +#define RENDER_LINE_CORE_H(d,col) \ + while (x0 <= x1) \ + { \ + *d = col; \ + if (h < 0) h += delta1; else {h += delta2; destPtr.c += step;} \ + x0++; d++; \ + } + +#define RENDER_LINE_CORE_V(d,col) \ + while (y0 != y1) \ + { \ + *d = col; \ + if (h < 0) h += delta1; else {h += delta2; d++;} \ + y0 += stepy; destPtr.c += step; \ + } \ + *d = col; + +/* + * Render a line (for drawing the object's bounding box). Write a line-plotter explicitly so it + * can be easily combined with the rest of the renderer. + */ + +void RenderLineSegment(const vertex_p *from, const vertex_p *to, const render_desc *renderDesc, long colour) +{ + int x0, y0, x1, y1, h, step, delta1, delta2, baseSize; + graph_env *graphEnv; + union {uint8 *c; uint16 *s; rgb_pixel *r; uint32 *l; float *f; double *d;} destPtr; + rgb_pixel rgb_colour; + + graphEnv = renderDesc->graphEnv; + baseSize = renderDesc->texDesc->baseSize; + + /* Order vertices from left to right */ + if (from->x < to->x) + { + x0 = from->x; y0 = from->y; x1 = to->x; y1 = to->y; + } + else + { + x0 = to->x; y0 = to->y; x1 = from->x; y1 = from->y; + } + /* Completely off-screen? */ + if ((x0 > graphEnv->clipr) || (x1 < graphEnv->clipl)) return; + if ((y0 > graphEnv->clipu) && (y1 > graphEnv->clipu)) return; + if ((y0 < graphEnv->clipd) && (y1 < graphEnv->clipd)) return; + + /*printf("%4d,%4d,%4d,%4d\n", x0, y0, x1, y1); fflush(stdout);*/ + + /* Horizontal clipping */ + if (x0 < graphEnv->clipl) + { + y0 = (y0 + y1 - ((y1 - y0) * (x0 - 2*(graphEnv->clipl) + x1)) / (x1 - x0)) >> 1; + x0 = graphEnv->clipl; + } + if (x1 > graphEnv->clipr) + { + y1 = (y0 + y1 - ((y1 - y0) * (x0 - 2*(graphEnv->clipr) + x1)) / (x1 - x0)) >> 1; + x1 = graphEnv->clipr; + } + + /* After horizontal clipping both y-values can be outside the clipping rectangle! */ + if ((y0 > graphEnv->clipu) && (y1 > graphEnv->clipu)) return; + if ((y0 < graphEnv->clipd) && (y1 < graphEnv->clipd)) return; + + /* Vertical clipping */ + if ((y0 > graphEnv->clipu) || (y1 > graphEnv->clipu)) + { + h = (x0 + x1 - ((x1 - x0) * (y0 - 2*(graphEnv->clipu) + y1)) / (y1 - y0)) >> 1; + if (y0 > graphEnv->clipu) + { + y0 = graphEnv->clipu; x0 = h; + } + else + { + y1 = graphEnv->clipu; x1 = h; + } + } + if ((y0 < graphEnv->clipd) || (y1 < graphEnv->clipd)) + { + h = (x0 + x1 - ((x1 - x0) * (y0 - 2*(graphEnv->clipd) + y1)) / (y1 - y0)) >> 1; + if (y0 < graphEnv->clipd) + { + y0 = graphEnv->clipd; x0 = h; + } + else + { + y1 = graphEnv->clipd; x1 = h; + } + } + + /*printf("%4d,%4d,%4d,%4d [%4d,%4d,%4d,%4d]\n", x0, y0, x1, y1, graphEnv->clipl, graphEnv->clipr, graphEnv->clipd, graphEnv->clipu); fflush(stdout);*/ + + destPtr.c = (uint8*)(graphEnv->dest) + (graphEnv->midy - y0) * (graphEnv->lineadd) + + (graphEnv->midx + x0) * baseSize; + + /* vertical stepping in negative logical coordinates steps in positive physical coordinates */ + step = (y1 < y0) ? graphEnv->lineadd : -graphEnv->lineadd; + + if (baseSize == 3) + { + rgb_colour.r = colour & 0xff; rgb_colour.g = (colour >> 8) & 0xff; rgb_colour.b = (colour >> 16) & 0xff; + } + + /* dx is always positive. dy isn't */ + h = (y1 - y0); if (h < 0) h = -h; + if ((x1 - x0) >= h) + { + delta1 = 2*(y1 - y0); if (delta1 < 0) delta1 = -delta1; + delta2 = delta1 - 2*(x1 - x0); + h = delta1 - (x1 - x0); + + switch (baseSize) + { + case 1: + { + RENDER_LINE_CORE_H(destPtr.c, (uint8)colour); + } + break; + case 2: + { + RENDER_LINE_CORE_H(destPtr.s, (uint16)colour); + } + break; + case 3: + { + RENDER_LINE_CORE_H(destPtr.r, rgb_colour); + } + break; + case 4: + if ((renderDesc->texDesc == NULL) || (renderDesc->texDesc->floatType == 0)) + { + RENDER_LINE_CORE_H(destPtr.l, (uint32)colour); + } + else + { + float fpcol = (float)(renderDesc->texDesc->maxVal); + RENDER_LINE_CORE_H(destPtr.f, fpcol); + } + break; + case 8: + { + double fpcol = (double)(renderDesc->texDesc->maxVal); + RENDER_LINE_CORE_H(destPtr.d, fpcol); + } + break; + default: break; + } + } + else + { + int stepy; + + stepy = (y1 < y0) ? -1 : 1; + delta1 = 2*(x1 - x0); + h = (y1 - y0); if (h < 0) h = -h; + delta2 = delta1 - 2*h; + h = delta1 - h; + + switch (baseSize) + { + case 1: + { + RENDER_LINE_CORE_V(destPtr.c, (uint8)colour); + } + break; + case 2: + { + RENDER_LINE_CORE_V(destPtr.s, (uint16)colour); + } + break; + case 3: + { + RENDER_LINE_CORE_V(destPtr.r, rgb_colour); + } + break; + case 4: + if ((renderDesc->texDesc == NULL) || (renderDesc->texDesc->floatType == 0)) + { + RENDER_LINE_CORE_V(destPtr.l, (uint32)colour); + } + else + { + float fpcol = (float)(renderDesc->texDesc->maxVal); + RENDER_LINE_CORE_V(destPtr.f, fpcol); + } + break; + case 8: + { + double fpcol = (double)(renderDesc->texDesc->maxVal); + RENDER_LINE_CORE_V(destPtr.d, fpcol); + } + break; + default: break; + } + } +} + + + +void Render3DLine(const vertex_fp *from, const vertex_fp *to, const render_desc *renderDesc, long colour) +{ + const graph_env *ge = renderDesc->graphEnv; + + if ((from->z >= ge->clipz) || (to->z >= ge->clipz)) + { + vertex_fp v1, v2; + vertex_p p1, p2; + double h; + + memcpy(&v1, from, sizeof(vertex_fp)); + memcpy(&v2, to, sizeof(vertex_fp)); + + /* do z-clipping if necessary */ + if ((from->z < ge->clipz) || (to->z < ge->clipz)) + { + vertex_fp *cv; + + h = (-v1.z + 2*ge->clipz - v2.z) / (v2.z - v1.z); + cv = (from->z < ge->clipz) ? &v1 : &v2; + cv->x = 0.5*(v1.x + v2.x + h * (v2.x - v1.x)); + cv->y = 0.5*(v1.y + v2.y + h * (v2.y - v1.y)); + cv->z = ge->clipz; + } + + /* project to 2D coordinates */ + h = (ge->zpro) / v1.z; + p1.x = (long)(h * v1.x); + p1.y = (long)(h * v1.y); + h = (ge->zpro) / v2.z; + p2.x = (long)(h * v2.x); + p2.y = (long)(h * v2.y); + + /* and finally render it */ + RenderLineSegment(&p1, &p2, renderDesc, colour); + } +} + + + + + +/* + * Shaded polygons + */ + +#define POLYSHADE_BUILD_GREY_FULL \ + { \ + real_t lcos, scos; \ + uint32 pixel; \ + lcos = cospos - lcosine; if (lcos < 0.0) lcos = 0.0; else lcos *= lweight; \ + scos = cospos - scosine; if (scos < 0.0) scos = 0.0; else scos *= sweight; \ + pixel = (uint32)(colour * (ambient + (1-ambient)*lcos + gain*scos)); \ + *destPtr = (pixel > 0xff) ? 0xff : (uint8)pixel; \ + } + +#define POLYSHADE_BUILD_GREY_FAST \ + *destPtr = (uint8)((colour * (cospos + 1)) / 2); + +#define POLYSHADE_BUILD_RGB_FULL \ + { \ + real_t lcos, scos; \ + uint32 pixel; \ + lcos = cospos - lcosine; if (lcos < 0.0) lcos = 0.0; else lcos *= lweight; \ + scos = cospos - scosine; if (scos < 0.0) scos = 0.0; else scos *= sweight; \ + lcos = (ambient + (1-ambient)*lcos); scos = gain*scos*colgrey; \ + pixel = (uint32)( red * lcos + scos); destPtr[0] = (pixel > 0xff) ? 0xff : (uint8)pixel; \ + pixel = (uint32)(green * lcos + scos); destPtr[1] = (pixel > 0xff) ? 0xff : (uint8)pixel; \ + pixel = (uint32)( blue * lcos + scos); destPtr[2] = (pixel > 0xff) ? 0xff : (uint8)pixel; \ + } + +#define POLYSHADE_BUILD_RGB_FAST \ + { \ + real_t lw = (cospos+1)/2; \ + uint32 cc; \ + cc = (uint32)(red * lw); destPtr[0] = (uint8)cc; \ + cc = (uint32)(green * lw); destPtr[1] = (uint8)cc; \ + cc = (uint32)(blue * lw); destPtr[2] = (uint8)cc; \ + } + +#ifdef POLYSHADE_RENDER_EXPERIMENTAL + +#define INIT_POLYSHADE_SIDE(side) \ + side##_ctr = proj[side##_idx].y - proj[side##_idx+1].y; \ + h = (real_t)((side##_ctr == 0) ? 1.0 : (1.0 / side##_ctr)); \ + side##_dp = h * (proj[side##_idx+1].x - proj[side##_idx].x); \ + side##_dc = h * (cosine[side##_idx+1] - cosine[side##_idx]); \ + if (side##_ctr < 0) \ + { \ + side##_p = (real_t)(proj[side##_idx+1].x) + 0.5; side##_c = cosine[side##_idx+1]; \ + side##_ctr = -side##_ctr; \ + } \ + else \ + { \ + side##_p = (real_t)(proj[side##_idx].x) + 0.5; side##_c = cosine[side##_idx]; \ + } + +#define UPDATE_POLYSHADE_SIDE(side) \ + if (side##_ctr-- <= 0) \ + { \ + side##_idx += side##_step; \ + if (side##_idx < 0) side##_idx = number-1; \ + if (side##_idx >= number) side##_idx = 0; \ + INIT_POLYSHADE_SIDE(side) \ + } \ + else \ + { \ + side##_p += side##_dp; side##_c += side##_dc; \ + } + +#define POLYSHADE_LINE_LOOP(PIXSIZE, BUILD_PIXEL) \ + destLine = (uint8*)(graphEnv->dest) + (graphEnv->midy - maxy) * (graphEnv->lineadd) + (graphEnv->midx) * PIXSIZE; \ + for (i=maxy; i>=miny; i--) \ + { \ + int xlp, xrp; \ + xlp = (int)left_p; \ + if (xlp <= graphEnv->clipr) \ + { \ + xrp = (int)right_p; \ + if (xrp >= graphEnv->clipl) \ + { \ + real_t cospos, cosstep; \ + if (xlp > xrp) {printf("l %d, x %d %d, i %d %d\n", i, xlp, xrp, left_idx, right_idx); for (n=0; n<number; n++) printf("%d: %f,%f,%f: %d %d\n", n, vert[n].x, vert[n].y, vert[n].z, proj[n].x, proj[n].y); xrp = (int)left_p; xlp = (int)right_p;} \ + h = (real_t)((xrp == xlp) ? 1 : xrp - xlp); \ + cospos = left_c; cosstep = (right_c - left_c) / h; \ + if (xlp < graphEnv->clipl) \ + { \ + cospos += cosstep * (graphEnv->clipl - xlp); xlp = graphEnv->clipl; \ + } \ + if (xrp > graphEnv->clipr) xrp = graphEnv->clipr; \ + destPtr = destLine + xlp * PIXSIZE; \ + for (; xlp <= xrp; xlp++, destPtr += PIXSIZE) \ + { \ + BUILD_PIXEL \ + cospos += cosstep; \ + } \ + } \ + } \ + destLine += graphEnv->lineadd; \ + UPDATE_POLYSHADE_SIDE(left) \ + UPDATE_POLYSHADE_SIDE(right) \ + } + +#else + +#define POLYSHADE_LINE_LOOP(PIXSIZE, BUILD_PIXEL) \ + destLine = (uint8*)(graphEnv->dest) + (graphEnv->midy - maxy) * (graphEnv->lineadd) + (graphEnv->midx) * PIXSIZE; \ + for (i=(maxy-miny); i>=0; i--, destLine += graphEnv->lineadd, zbLine += zbWidth) \ + { \ + int xlp, xrp; \ + real_t cospos, cosstep; \ + int zpos, zstep; \ + xlp = PolyShadeLines[i].left.xp >> FIXPOINT_PREC; xrp = PolyShadeLines[i].right.xp >> FIXPOINT_PREC; \ + if ((xlp <= graphEnv->clipr) && (xrp >= graphEnv->clipl)) \ + { \ + cospos = PolyShadeLines[i].left.c; zpos = PolyShadeLines[i].left.z; \ + h = (real_t)((xlp == xrp) ? 1.0 : 1.0 / (xrp - xlp)); \ + cosstep = h * (PolyShadeLines[i].right.c - cospos); \ + zstep = (int)(h * (PolyShadeLines[i].right.z - zpos)); \ + if (xlp < graphEnv->clipl) {cospos += (graphEnv->clipl - xlp) * cosstep; zpos += (graphEnv->clipl - xlp) * zstep; xlp = graphEnv->clipl;} \ + if (xrp > graphEnv->clipr) xrp = graphEnv->clipr; \ + destPtr = destLine + xlp * PIXSIZE; zbPtr = zbLine + xlp; \ + for (; xlp <= xrp; xlp++, destPtr += PIXSIZE, zbPtr++, cospos += cosstep, zpos += zstep) \ + { \ + if ((zbuffer_t)(zpos >> FIXPOINT_PREC) < *zbPtr) \ + { \ + *zbPtr = (zbuffer_t)(zpos >> FIXPOINT_PREC); \ + BUILD_PIXEL \ + } \ + } \ + } \ + } + + +typedef struct polyshade_side_t { + int xp; + int z; + real_t c; +} polyshade_side_t; + +typedef struct polyshade_line_t { + polyshade_side_t left; + polyshade_side_t right; +} polyshade_line_t; + +static int PolyShadeLineSize = 0; +static polyshade_line_t *PolyShadeLines = NULL; + + +static int RenderInitZBuffer(const graph_env *graphEnv, mesh_desc *meshDesc) +{ + int width; + unsigned int total; + + width = graphEnv->clipr - graphEnv->clipl + 1; + total = width * (graphEnv->clipu - graphEnv->clipd + 1); + if (meshDesc->zbuffSize != total) + { + if (meshDesc->zbuffer != NULL) free(meshDesc->zbuffer); + meshDesc->zbuffSize = total; + meshDesc->zbuffer = (zbuffer_t*)malloc(total * sizeof(zbuffer_t)); + } + memset(meshDesc->zbuffer, 0xff, total * sizeof(zbuffer_t)); + + return width; +} + +#endif + +/* Render a (convex!) polygon using shading */ +int RenderShadedPolygon(int numVert, const vertex_fp *vertices, const vertex_fp *normals, unsigned int colour, const graph_env *graphEnv, const light_desc *light, const vertex_fp *real_norm, zbuffer_t *zbuffer) +{ + vertex_fp *vert, *vr, *vw; + vertex_p *proj, *pr, *pw; + real_t *cosine, *cr, *cw; + real_t clipz, zpro; + real_t h, orient; + int miny, maxy, minx, maxx, lastY; + int number; + int i, n; +#ifdef POLYSHADE_RENDER_EXPERIMENTAL + int left_idx, right_idx, left_step, right_step; + real_t left_p, left_dp, right_p, right_dp; + real_t left_c, left_dc, right_c, right_dc; + int left_ctr, right_ctr; + vertex_fp dir1, dir2, snorm; + const vertex_fp *snormp; +#endif + uint8 *destLine, *destPtr; + uint8 red, green, blue; + real_t lcosine, scosine, lweight, sweight, ambient, gain; + /* zBuffer */ + zbuffer_t *zbLine, *zbPtr; + int zbWidth; + + clipz = (real_t)(graphEnv->clipz); zpro = (real_t)(graphEnv->zpro); + + /* Early termination of off-screen polygons */ + minx = INT_MAX; maxx = INT_MIN; miny = INT_MAX; maxy = INT_MIN; + for (i=0; i<numVert; i++) + { + int xp, yp; + + /* The effects of this can't be predicted without explicit z-clipping */ + if (vertices[i].z <= 0) break; + h = zpro / vertices[i].z; + xp = (int)(h * vertices[i].x); yp = (int)(h * vertices[i].y); + if (xp < minx) minx = xp; if (xp > maxx) maxx = xp; + if (yp < miny) miny = yp; if (yp > maxy) maxy = yp; + } + if (i >= numVert) + { + if ((maxx < graphEnv->clipl) || (minx > graphEnv->clipr) || (maxy < graphEnv->clipd) || (miny > graphEnv->clipu)) return 0; + } + + /* +1 vertex for closing the outline +3 vertices for clipping effects (2 y, 1 z) */ + vert = (vertex_fp*)malloc((numVert + 4) * sizeof(vertex_fp)); + proj = (vertex_p*)malloc((numVert + 4) * sizeof(vertex_p)); + cosine = (real_t*)malloc((numVert + 4) * sizeof(real_t)); + + vr = vert+3; memcpy(vr, vertices, numVert*sizeof(vertex_fp)); + vr[numVert] = vertices[0]; + cw = cosine+3; + + /* Surface orientation just doesn't work well */ +#if 0 + if (real_norm == NULL) + { + /* Determine surface orientation */ + dir2.x = vertices[2].x - vertices[1].x; dir1.x = vertices[1].x - vertices[0].x; + dir2.y = vertices[2].y - vertices[1].y; dir1.y = vertices[1].y - vertices[0].y; + dir2.z = vertices[2].z - vertices[1].z; dir1.z = vertices[1].z - vertices[0].z; + snorm.x = dir1.y * dir2.z - dir1.z * dir2.y; + snorm.y = dir1.z * dir2.x - dir1.x * dir2.z; + snorm.z = dir1.x * dir2.y - dir1.y * dir2.x; /* signed surface normal */ + snormp = &snorm; + } + else + { + snormp = real_norm; + } + /* If the side facing us is the backside, mirror the normal vectors */ + h = snormp->x * vertices[0].x + snormp->y * vertices[0].y + snormp->z * vertices[0].z; + if (h < 0) orient = -1; else orient = 1; +#else + orient = 1; +#endif + /* Calculate the scalar products of the vertices with the light vector */ + for (i=0; i<numVert; i++) + { + vertex_fp l; + + l.x = (light->lights.x - vertices[i].x); + l.y = (light->lights.y - vertices[i].y); + l.z = (light->lights.z - vertices[i].z); + h = l.x * l.x + l.y * l.y + l.z * l.z; + if (h < 1e-6) h = 1e-6f; + h = (real_t)(orient/sqrt(h)); + cw[i] = h * ((normals[i].x * l.x) + (normals[i].y * l.y) + (normals[i].z * l.z)); + } + cw[i] = cw[0]; + + number = 0; cr = cosine+3; vw = vert+2; cw = cosine+2; pw = proj+2; + + /* z clipping */ + for (i=0; i<numVert; i++) + { + if ((vr[0].z >= clipz) || (vr[1].z >= clipz)) + { + if (vr[0].z >= clipz) + { + *vw++ = *vr; *cw++ = *cr; + pw->x = (long)((zpro * vr->x) / vr->z); pw->y = (long)((zpro * vr->y) / vr->z); pw++; + number++; if (number >= numVert+1) break; + } + if ((vr[0].z < clipz) || (vr[1].z < clipz)) + { + h = (vr[0].z - 2 * clipz + vr[1].z) / (vr[1].z - vr[0].z); + vw->x = (real_t)(0.5*(vr[1].x + vr[0].x - h * (vr[1].x - vr[0].x))); + vw->y = (real_t)(0.5*(vr[1].y + vr[0].y - h * (vr[1].y - vr[0].y))); + vw->z = clipz; + *cw = (real_t)(0.5*(cr[1] + cr[0] - h * (cr[1] - cr[0]))); + vw++; cw++; + pw->x = (long)((zpro * vr->x) / vr->z); pw->y = (long)((zpro * vr->y) / vr->z); pw++; + number++; if (number >= numVert+1) break; + } + } + vr++; cr++; + } + if (number == 0) + { +#if (CUBE_RENDER_DEBUG > 0) + printf("PolyShade: void (z)\n"); +#endif + free(vert); free(cosine); free(proj); + return 0; + } + vr = vert+2; cr = cosine+2; pr = proj+2; *vw = *vr; *cw = *cr; *pw = *pr; + +#if (CUBE_RENDER_DEBUG > 0) + for (i=0; i<number; i++) printf("%d: %f %f %f\n", i, vr[i].x, vr[i].y, vr[i].z); +#endif + + miny = INT_MAX; maxy = INT_MIN; minx = INT_MAX; maxx = INT_MIN; + /* y clipping */ + vw = vert; cw = cosine; pw = proj; n = 0; +#if 1 + lastY = (int)((zpro * vr[0].y) / vr[0].z); + for (i=0; i<number; i++) + { + vertex_fp v0, v1, *uv; + real_t c0, c1, *uc; + int modTwo; + int yp; + + yp = (int)((zpro * vr[1].y) / vr[1].z); + v0 = vr[0]; v1 = vr[1]; c0 = cr[0]; c1 = cr[1]; modTwo = 0; + /* Clip upper bound */ + if ((lastY <= graphEnv->clipu) || (yp <= graphEnv->clipu)) + { + if ((lastY > graphEnv->clipu) || (yp > graphEnv->clipu)) + { + h = (real_t)(zpro * (v1.y - v0.y) - (graphEnv->clipu + 0.5) * (v1.z - v0.z)); + if (h != 0.0) + { + h = (real_t)( - (zpro * v0.y - (graphEnv->clipu + 0.5) * v0.z) / h); + if (lastY > graphEnv->clipu) + { + uv = &v0; uc = &c0; + } + else + { + uv = &v1; uc = &c1; modTwo = 1; + } + uv->x = v0.x + h * (v1.x - v0.x); + uv->y = v0.y + h * (v1.y - v0.y); + uv->z = v0.z + h * (v1.z - v0.z); + *uc = c0 + h * (c1 - c0); + } + } + } + if ((lastY >= graphEnv->clipd) || (yp >= graphEnv->clipd)) + { + if ((lastY < graphEnv->clipd) || (yp < graphEnv->clipd)) + { + h = (real_t)(zpro * (v1.y - v0.y) - (graphEnv->clipd - 0.5) * (v1.z - v0.z)); + if (h != 0.0) + { + h = (real_t)( - (zpro * v0.y - (graphEnv->clipd - 0.5) * v0.z) / h); + if (lastY < graphEnv->clipd) + { + uv = &v0; uc = &c0; + } + else + { + uv = &v1; uc = &c1; modTwo = 1; + } + uv->x = v0.x + h * (v1.x - v0.x); + uv->y = v0.y + h * (v1.y - v0.y); + uv->z = v0.z + h * (v1.z - v0.z); + *uc = c0 + h * (c1 - c0); + } + } + } + if (((lastY <= graphEnv->clipu) || (yp <= graphEnv->clipu)) && ((lastY >= graphEnv->clipd) || (yp >= graphEnv->clipd))) + { + *vw++ = v0; *cw++ = c0; + pw->x = (long)((zpro * v0.x) / v0.z); pw->y = (long)((zpro * v0.y) / v0.z); + if (pw->y < miny) miny = pw->y; if (pw->y > maxy) maxy = pw->y; + if (pw->x < minx) minx = pw->x; if (pw->x > maxx) maxx = pw->x; + pw++; n++; if (n >= numVert+3) break; + if (modTwo != 0) + { + *vw++ = v1; *cw++ = c1; + pw->x = (long)((zpro * v1.x) / v1.z); pw->y = (long)((zpro * v1.y) / v1.z); + if (pw->y < miny) miny = pw->y; if (pw->y > maxy) maxy = pw->y; + if (pw->x < minx) minx = pw->x; if (pw->x > maxx) maxx = pw->x; + pw++; n++; if (n >= numVert+3) break; + } + } + vr++; cr++; lastY = yp; + } +#else + lastY = pr[0].y; + for (i=0; i<number; i++) + { + vertex_p p0, p1, *up; + real_t c0, c1, *uc; + int modTwo; + int yp; + + yp = pr[1].y; + p0 = pr[0]; p1 = pr[1]; c0 = cr[0]; c1 = cr[1]; modTwo = 0; + if ((lastY <= graphEnv->clipu) || (yp <= graphEnv->clipu)) + { + if ((lastY > graphEnv->clipu) || (yp > graphEnv->clipu)) + { + h = (yp - 2 * graphEnv->clipu - 1 + lastY) / (yp - lastY); + if (lastY > graphEnv->clipu) + { + up = &p0; uc = &c0; + } + else + { + up = &p1; uc = &c1; modTwo = 1; + } + up->x = (int)(0.5*(p1.x + p0.x - h * (p1.x - p0.x))); + up->y = graphEnv->clipu; + *uc = 0.5*(c1 + c0 - h * (c1 - c0)); + } + } + if ((lastY >= graphEnv->clipd) || (yp >= graphEnv->clipd)) + { + if ((lastY < graphEnv->clipd) || (yp < graphEnv->clipd)) + { + h = (yp - 2 * graphEnv->clipd + 1 + lastY) / (yp - lastY); + if (lastY < graphEnv->clipd) + { + up = &p0; uc = &c0; + } + else + { + up = &p1; uc = &c1; modTwo = 1; + } + up->x = (int)(0.5*(p1.x + p0.x - h * (p1.x - p0.x))); + up->y = graphEnv->clipd; + *uc = 0.5*(c1 + c0 - h * (c1 - c0)); + } + } + if (((lastY <= graphEnv->clipu) || (yp <= graphEnv->clipu)) && ((lastY >= graphEnv->clipd) || (yp >= graphEnv->clipd))) + { + *pw++ = p0; *cw++ = c0; n++; + if (p0.y < miny) miny = p0.y; if (p0.y > maxy) maxy = p0.y; + if (p0.x < minx) minx = p0.x; if (p0.x > maxx) maxx = p0.x; + if (n >= numVert+3) break; + if (modTwo != 0) + { + *pw++ = p1; *cw++ = c1; n++; + if (p1.y < miny) miny = p1.y; if (p1.y > maxy) maxy = p1.y; + if (p1/x < minx) minx = p1.x; if (p1.x > maxx) maxx = p1.x; + if (n >= numVert+3) break; + } + } + pr++; vr++; + } +#endif + if (n == 0) + { +#if (CUBE_RENDER_DEBUG > 0) + printf("PolyShade: void (y)\n"); +#endif + free(vert); free(cosine); free(proj); + return 0; + } + if ((minx > graphEnv->clipr) || (maxx < graphEnv->clipl)) + { +#if (CUBE_RENDER_DEBUG > 0) + printf("PolyShade: clipped all x\n"); +#endif + free(vert); free(cosine); free(proj); + return 0; + } + *vw = *vert; *cw = *cosine; *pw = *proj; + number = n; + +#if (CUBE_RENDER_DEBUG > 0) + printf("miny %d, maxy %d\n", miny, maxy); + for (i=0; i<number; i++) printf("%d: %f %f %f : %d %d\n", i, vert[i].x, vert[i].y, vert[i].z, proj[i].x, proj[i].y); +#endif + + /* Init lighting parameters */ + if ((ambient = (real_t)(light->ambient)) >= 0) + { + lcosine = (real_t)(light->cosine); scosine = (real_t)(light->scintCos); + lweight = (lcosine == 1) ? 1 : 1 / (1 - lcosine); + sweight = (scosine == 0) ? 1 : 1 / (1 - scosine); + gain = (real_t)(light->gain); + } + +#ifdef POLYSHADE_RENDER_EXPERIMENTAL + /* Find the two topmost, non-horizontal bounding lines */ + left_idx = -1; right_idx = -1; + for (n=0; n<number; n++) + { + if (((proj[n].y == maxy) || (proj[n+1].y == maxy)) && (proj[n].y != proj[n+1].y)) + { + if (left_idx == -1) left_idx = n; else right_idx = n; + } + } + /* is ``polygon'' completely flat? Then just find leftmost / rightmost line */ + if (right_idx < 0) + { + left_idx = 0; right_idx = 0; + for (i=1; i<number; i++) + { + if (proj[i].x < proj[left_idx].x) left_idx = i; + if (proj[i].x > proj[right_idx].x) right_idx = i; + } + } +#if 0 /* Can't happen any more */ + /* Did something serious go wrong? */ + if (right_idx < 0) + { +#if (CUBE_RENDER_DEBUG > 0) + fprintf(stderr, "Bad polygon:\n"); + for (i=0; i<number; i++) + { + fprintf(stderr, "\t%d: v(%f,%f,%f), p(%4d,%4d), c%f\n", i, vert[i].x, vert[i].y, vert[i].z, proj[i].x, proj[i].y, cosine[i]); + } +#endif + free(vert); free(cosine); free(proj); + return -1; + } +#endif + /* Determine surface orientation */ +#if 1 + h = snorm.x * vert[0].x + snorm.y * vert[0].y + snorm.z * vert[0].z; +#else + dir1.x = vert[1].x / vert[1].z; dir1.y = vert[1].y / vert[1].z; + dir2.x = vert[2].x / vert[2].z - dir1.x; dir2.y = vert[2].y / vert[2].z - dir1.y; + dir1.x -= vert[0].x / vert[0].z; dir1.y -= vert[0].y / vert[0].z; + h = dir1.x * dir2.y - dir1.y * dir2.x; +#endif + if (h <= 0) /* clockwise */ + { + /* left side must have growing yp component */ + if (proj[left_idx].y > proj[left_idx+1].y) + { + left_step = left_idx; left_idx = right_idx; right_idx = left_step; + } + left_step = -1; right_step = 1; + } + else /* counter-clockwise */ + { + /* left side must have decreasing yp component */ + if (proj[left_idx].y < proj[left_idx+1].y) + { + left_step = left_idx; left_idx = right_idx; right_idx = left_step; + } + left_step = 1; right_step = -1; + } + + if (miny < graphEnv->clipd) miny = graphEnv->clipd; + if (maxy > graphEnv->clipu) maxy = graphEnv->clipu; + + /*for (i=0; i<number; i++) {int xlp, ylp; xlp = (zpro*vert[i].x)/vert[i].z; ylp = (zpro*vert[i].y)/vert[i].z; if ((xlp != proj[i].x) || (ylp != proj[i].y)) printf("No match %d:%d,%d -- %d,%d\n", i, xlp, ylp, proj[i].x, proj[i].y);}*/ + + INIT_POLYSHADE_SIDE(left) + INIT_POLYSHADE_SIDE(right) + +#if (CUBE_RENDER_DEBUG > 0) + printf("left %d, right %d; ls %d, rs %d; lc %d, rc %d\n", left_idx, right_idx, left_step, right_step, left_ctr, right_ctr); +#endif + + /* Top bits of colour determine rgb / grey mode */ + if ((colour & 0xff000000) != 0) + { + red = colour & 0xff; green = (colour >> 8) & 0xff; blue = (colour >> 16) & 0xff; + POLYSHADE_LINE_LOOP(3, POLYSHADE_BUILD_RGB_FAST) + } + else + { + POLYSHADE_LINE_LOOP(1, POLYSHADE_BUILD_GREY_FAST) + } + +#else + + if (PolyShadeLineSize < (maxy-miny+1)) + { + PolyShadeLineSize = (maxy-miny+1); + if (PolyShadeLines != NULL) free(PolyShadeLines); + PolyShadeLines = (polyshade_line_t*)malloc(PolyShadeLineSize * sizeof(polyshade_line_t)); + } + + for (i=(maxy-miny); i>=0; i--) + { + PolyShadeLines[i].left.xp = INT_MAX; PolyShadeLines[i].right.xp = INT_MIN; + } + + zbWidth = (graphEnv->clipr - graphEnv->clipl + 1); + zbLine = zbuffer + ((graphEnv->midy - maxy) * zbWidth + graphEnv->midx); + + /* Plot the outline into this structure */ + for (n=0; n<number; n++) + { + polyshade_line_t *pl; + int xp, xpstep; + real_t c, cstep; + int z, zstep; + int yp, count, step; + + xp = proj[n].x << FIXPOINT_PREC; yp = proj[n].y; c = cosine[n]; z = (int)((1<<FIXPOINT_PREC)*vert[n].z); + if ((count = proj[n+1].y - yp) < 0) {count = -count; step = -1;} else {step = 1;} + h = (real_t)((count == 0) ? 1.0 : 1.0 / ((real_t)count)); + xpstep = (int)(h * ((proj[n+1].x << FIXPOINT_PREC) - xp)); + cstep = h * (cosine[n+1] - c); zstep = (int)(h * (1<<FIXPOINT_PREC) * (vert[n+1].z - vert[n].z)); + pl = PolyShadeLines + (yp - miny); + if (xp < pl->left.xp) + { + pl->left.xp = xp; pl->left.c = c; pl->left.z = z; + } + if (xp > pl->right.xp) + { + pl->right.xp = xp; pl->right.c = c; pl->right.z = z; + } + while (count-- > 0) + { + pl += step; xp += xpstep; c += cstep; z += zstep; + if (xp < pl->left.xp) + { + pl->left.xp = xp; pl->left.c = c; pl->left.z = z; + } + if (xp > pl->right.xp) + { + pl->right.xp = xp; pl->right.c = c; pl->right.z = z; + } + } + } + /* Top bits of colour determine rgb / grey mode */ + if ((colour & 0xff000000) != 0) + { + uint32 colgrey; + + red = colour & 0xff; green = (colour >> 8) & 0xff; blue = (colour >> 16) & 0xff; + colgrey = (red + green + blue) / 3; + if (ambient < 0) + { + POLYSHADE_LINE_LOOP(3, POLYSHADE_BUILD_RGB_FAST) + } + else + { + POLYSHADE_LINE_LOOP(3, POLYSHADE_BUILD_RGB_FULL) + } + } + else + { + if (ambient < 0) + { + POLYSHADE_LINE_LOOP(1, POLYSHADE_BUILD_GREY_FAST) + } + else + { + POLYSHADE_LINE_LOOP(1, POLYSHADE_BUILD_GREY_FULL) + } + } +#endif + + free(vert); free(proj); free(cosine); + + return 0; +} + + + +#define RENDER_MESH_NAME RenderHeightMesh1 +#define RENDER_MESH_TYPE c +#include "cube_render_mesh.c" +#undef RENDER_MESH_NAME +#undef RENDER_MESH_TYPE + +#define RENDER_MESH_NAME RenderHeightMesh2 +#define RENDER_MESH_TYPE s +#include "cube_render_mesh.c" +#undef RENDER_MESH_NAME +#undef RENDER_MESH_TYPE + +#define RENDER_MESH_NAME RenderHeightMesh4 +#define RENDER_MESH_TYPE l +#include "cube_render_mesh.c" +#undef RENDER_MESH_NAME +#undef RENDER_MESH_TYPE + +#define RENDER_MESH_NAME RenderHeightMesh4F +#define RENDER_MESH_TYPE f +#include "cube_render_mesh.c" +#undef RENDER_MESH_NAME +#undef RENDER_MESH_TYPE + +#define RENDER_MESH_NAME RenderHeightMesh8F +#define RENDER_MESH_TYPE d +#include "cube_render_mesh.c" +#undef RENDER_MESH_NAME +#undef RENDER_MESH_TYPE + +int RenderHeightField(mesh_desc *meshDesc, const vertex_fp *rotTrans, const graph_env *graphEnv, const mdd_desc *mddDesc, const light_desc *lightDesc) +{ + int i, j, dimx, dimz; + vertex_fp *vert, *vp, *vbase; + vertex_fp *norm, *np, *nbase; + int number; + int srcIncX, srcIncZ; + uint8 *src; + real_t h, hh; + real_t x0, x1, x2, x3; + real_t z0, z1, z2, z3; + int backoff, backstep, sideoff, sidestep, iter0, iter1; + zbuffer_t *zbuffer; +#ifdef POLYSHADE_HEIGHT_EXPERIMENTAL + int indices[4][8]; +#endif + + if (RenderHeightGetDomain(mddDesc, &dimx, &dimz, &srcIncX, &srcIncZ) != 0) + return -1; + + number = (dimx * dimz); + vert = (vertex_fp*)malloc(number * sizeof(vertex_fp)); + norm = (vertex_fp*)malloc(number * sizeof(vertex_fp)); + + /* Only create a new mesh if it has changed */ + if ((meshDesc->srcData != mddDesc->data) || (meshDesc->width != dimx) || (meshDesc->height != dimz) || (meshDesc->oldGrid != meshDesc->scaleGrid) || (meshDesc->oldHeight != meshDesc->scaleHeight)) + { + if (meshDesc->vert != NULL) free(meshDesc->vert); + if (meshDesc->norm != NULL) free(meshDesc->norm); + + meshDesc->vert = (vertex_fp*)malloc(number * sizeof(vertex_fp)); + meshDesc->norm = (vertex_fp*)malloc(number * sizeof(vertex_fp)); + + /* Create a vertex mesh (type-dependent) */ + src = (uint8*)(mddDesc->data); + + vp = meshDesc->vert; + switch (mddDesc->baseSize) + { + case 1: RenderHeightMesh1(vp, src, srcIncX, srcIncZ, dimx, dimz, meshDesc->scaleGrid, meshDesc->scaleHeight); + break; + case 2: RenderHeightMesh2(vp, src, srcIncX, srcIncZ, dimx, dimz, meshDesc->scaleGrid, meshDesc->scaleHeight); + break; + case 4: + if (mddDesc->floatType == 0) + RenderHeightMesh4(vp, src, srcIncX, srcIncZ, dimx, dimz, meshDesc->scaleGrid, meshDesc->scaleHeight); + else + RenderHeightMesh4F(vp, src, srcIncX, srcIncZ, dimx, dimz, meshDesc->scaleGrid, meshDesc->scaleHeight); + break; + case 8: RenderHeightMesh8F(vp, src, srcIncX, srcIncZ, dimx, dimz, meshDesc->scaleGrid, meshDesc->scaleHeight); + break; + default: + fprintf(stderr, "Unable to render a height field for this base type.\n"); + free(vert); free(norm); return -1; + } + + /* Construct normal vector approximations */ + vp = meshDesc->vert; np = meshDesc->norm; z0 = vp[0].y; z1 = z0; + for (i = 0; i < dimx; i++) + { + for (j = 0; j < dimz; j++) + { + vertex_fp v; + /*real_t vy;*/ + + /* Determine min/max */ + if (vp->y < z0) z0 = vp->y; + if (vp->y > z1) z1 = vp->y; + /* Calculate normal */ + if (i == 0) v.x = -(real_t)(vp[dimz].y - vp[0].y); + else if (i == dimx-1) v.x = -(real_t)(vp[0].y - vp[-dimz].y); + else v.x = (real_t)( - (vp[dimz].y - vp[-dimz].y) * 0.5); + v.y = 2 * meshDesc->scaleGrid; + if (j == 0) v.z = -(real_t)(vp[1].y - vp[0].y); + else if (j == dimz-1) v.z = -(real_t)(vp[0].y - vp[-1].y); + else v.z = (real_t)( - (vp[1].y - vp[-1].y) * 0.5); + h = (real_t)(1 / sqrt((v.x) * (v.x) + (v.y) * (v.y) + (v.z) * (v.z))); /* v.y != 0 ! */ + np->x = h * v.x; np->y = h * v.y; np->z = h * v.z; + vp++; np++; + } + } + meshDesc->miny = z0; meshDesc->maxy = z1; + meshDesc->srcData = mddDesc->data; + meshDesc->width = dimx; meshDesc->height = dimz; + meshDesc->oldGrid = meshDesc->scaleGrid; meshDesc->oldHeight = meshDesc->scaleHeight; + } + + memcpy(vert, meshDesc->vert, number * sizeof(vertex_fp)); + memcpy(norm, meshDesc->norm, number * sizeof(vertex_fp)); + + z0 = (meshDesc->miny + meshDesc->maxy) / 2; + z1 = (real_t)(rotTrans[0].y - 0.5*(rotTrans[2].x * dimx * meshDesc->scaleGrid + rotTrans[2].z * dimz * meshDesc->scaleGrid)); + + /* Transform vertices and normals */ + vp = meshDesc->vert; np = meshDesc->norm; + for (i=0; i<dimx*dimz; i++, vp++, np++) + { + z2 = vp->y - z0; + vert[i].x = rotTrans[1].x * vp->x + rotTrans[1].y * z2 + rotTrans[1].z * vp->z + rotTrans[0].x; + vert[i].y = rotTrans[2].x * vp->x + rotTrans[2].y * z2 + rotTrans[2].z * vp->z + z1; + vert[i].z = rotTrans[3].x * vp->x + rotTrans[3].y * z2 + rotTrans[3].z * vp->z + rotTrans[0].z; + norm[i].x = rotTrans[1].x * np->x + rotTrans[1].y * np->y + rotTrans[1].z * np->z; + norm[i].y = rotTrans[2].x * np->x + rotTrans[2].y * np->y + rotTrans[2].z * np->z; + norm[i].z = rotTrans[3].x * np->x + rotTrans[3].y * np->y + rotTrans[3].z * np->z; + } + + + /* + * Find order in which to plot: 1) find hindmost side of the grid using min((dz/dx)^2), + * 2) test left/right z of this side for running order in inner loop. + */ + x0 = vert[0].x; x1 = vert[dimz * (dimx-1)].x; x2 = vert[dimz * dimx - 1].x; x3 = vert[dimz-1].x; + z0 = vert[0].z; z1 = vert[dimz * (dimx-1)].z; z2 = vert[dimz * dimx - 1].z; z3 = vert[dimz-1].z; + /* Hindmost corner 0 */ + if ((z0 >= z1) && (z0 >= z2) && (z0 >= z3)) + { + /* (dz0/dx0)^2 < (dz1/dx1)^2 <==> (dz0dx1)^2 < (dz1dx0)^2 */ + h = (z1 - z0) * (x3 - x0); hh = (z3 - z0) * (x1 - x0); + if (h*h < hh*hh) /* back is 01 */ + { + backoff = 0; backstep = 1; iter0 = dimz; + sideoff = 0; sidestep = dimz; + } + else /* back is 30 */ + { + backoff = 0; backstep = dimz; iter0 = dimx; + sideoff = 0; sidestep = 1; + } + } + /* Hindmost corner 1 */ + else if ((z1 >= z0) && (z1 >= z2) && (z1 >= z3)) + { + h = (z2 - z1) * (x0 - x1); hh = (x2 - x1) * (z0 - z1); + if (h*h < hh*hh) /* back is 12 */ + { + backoff = (dimx-2)*dimz; backstep = -dimz; iter0 = dimx; + sideoff = 0; sidestep = 1; + } + else /* back is 01 */ + { + backoff = 0; backstep = 1; iter0 = dimz; + sideoff = (dimx-2)*dimz; sidestep = -dimz; + } + } + /* Hindmost corner 2 */ + else if ((z2 >= z0) && (z2 >= z1) && (z2 >= z3)) + { + h = (z3 - z2) * (x1 - x2); hh = (x3 - x2) * (z1 - z2); + if (h*h < hh*hh) /* back is 23 */ + { + backoff = dimz-2; backstep = -1; iter0 = dimz; + sideoff = (dimx-2)*dimz; sidestep = -dimz; + } + else /* back is 12 */ + { + backoff = (dimx-2)*dimz; backstep = -dimz; iter0 = dimx; + sideoff = dimz-2; sidestep = -1; + } + } + /* Hindmost corner 3 */ + else + { + h = (z0 - z3) * (x2 - x3); hh = (x0 - x3) * (z2 - z3); + if (h*h < hh*hh) /* back is 30 */ + { + backoff = 0; backstep = dimz; iter0 = dimx; + sideoff = dimz-2; sidestep = -1; + } + else /* back is 23 */ + { + backoff = dimz-2; backstep = -1; iter0 = dimz; + sideoff = 0; sidestep = dimz; + } + } +#ifdef POLYSHADE_HEIGHT_EXPERIMENTAL + /* Init indices for triangle plotting order */ + /* 0: 012:023, 1: 023:012, 2: 130:123, 3: 123:130 */ + indices[0][0] = 0; indices[0][1] = dimz; indices[0][2] = dimz+1; + indices[0][3] = 0; indices[0][4] = dimz+1; indices[0][5] = 1; + indices[1][0] = 0; indices[1][1] = dimz+1; indices[1][2] = 1; + indices[1][3] = 0; indices[1][4] = dimz; indices[1][5] = dimz+1; + indices[2][0] = dimz; indices[2][1] = 1; indices[2][2] = 0; + indices[2][3] = dimz; indices[2][4] = dimz+1; indices[2][5] = 1; + indices[3][0] = dimz; indices[3][1] = dimz+1; indices[3][2] = 1; + indices[3][3] = dimz; indices[3][4] = 1; indices[3][5] = 0; + /* normals associated with these */ + indices[0][6] = 0; indices[0][7] = 1; indices[1][6] = 1; indices[1][7] = 0; + indices[2][6] = 0; indices[2][7] = 1; indices[3][6] = 1; indices[3][7] = 0; + + vbase = vert + backoff + sideoff; nbase = norm + backoff + sideoff; +#else + /* When using the ZBuffer we try to render front to back for much more efficiency */ + i = (dimx-2) * dimz + dimz - 2 - backoff - sideoff; vbase = vert + i; nbase = norm + i; + backstep = -backstep; sidestep = -sidestep; + + RenderInitZBuffer(graphEnv, meshDesc); + zbuffer = meshDesc->zbuffer; +#endif + + iter1 = dimx + dimz - iter0; + for (i=0; i<iter0-1; i++) + { + vp = vbase; np = nbase; + for (j=0; j<iter1-1; j++) + { +#if 0 + vertex_fp polygon[4], normals[4]; + + polygon[0] = vp[0]; polygon[1] = vp[dimz]; polygon[2] = vp[dimz+1]; polygon[3] = vp[1]; + normals[0] = np[0]; normals[1] = np[dimz]; normals[2] = np[dimz+1]; normals[3] = np[1]; + /*printf("%d,%d : %f,%f,%f : %f,%f,%f : %f,%f,%f : %f,%f,%f\n", i, j, polygon[0].x, polygon[0].y, polygon[0].z, polygon[1].x, polygon[1].y, polygon[1].z, polygon[2].x, polygon[2].y, polygon[2].z, polygon[3].x, polygon[3].y, polygon[3].z);*/ + /*colour = (((i+j)&1) == 0) ? 128 : 255;*/ + RenderShadedPolygon(4, polygon, normals, colour, graphEnv, lightDesc, NULL, zbuffer); +#else + vertex_fp normals[3]; + vertex_fp diag1, diag2; +#ifdef POLYSHADE_HEIGHT_EXPERIMENTAL + vertex_fp polygon[4], tnorms[2]; + int *ind; +#else + vertex_fp polygon[3]; +#endif + + /*colour = (((i+j)&1) == 0) ? 128 : 255;*/ + + /* + * Determine the best splitting line (BCD, BDA vs. ABC, ACD) by minimizing the scalar product + * of the normalized diagonals with the normals at their endpoints + */ + diag1.x = vp[1].x - vp[dimz].x; diag1.y = vp[1].y - vp[dimz].y; diag1.z = vp[1].z - vp[dimz].z; + h = diag1.x * diag1.x + diag1.y * diag1.y + diag1.z * diag1.z; + if (h > 1e-6) + { + h = (real_t)(1/sqrt(h)); + diag1.x *= h; diag1.y *= h; diag1.z *= h; + } + diag2.x = vp[dimz+1].x - vp[0].x; diag2.y = vp[dimz+1].y - vp[0].y; diag2.z = vp[dimz+1].z - vp[0].z; + h = diag2.x * diag2.x + diag2.y * diag2.y + diag2.z * diag2.z; + if (h > 1e-6) + { + h = (real_t)(1/sqrt(h)); + diag2.x *= h; diag2.y *= h; diag2.z *= h; + } + h = diag1.x * np[dimz].x + diag1.y * np[dimz].y + diag1.z * np[dimz].z; + hh = diag1.x * np[1].x + diag1.y * np[1].y + diag1.z * np[1].z; + x0 = h*h + hh*hh; + h = diag2.x * np[dimz+1].x + diag2.y * np[dimz+1].y + diag2.z * np[dimz+1].z; + hh = diag2.x * np[0].x + diag2.y * np[0].y + diag2.z * np[0].z; + x1 = h*h + hh*hh; +#ifdef POLYSHADE_HEIGHT_EXPERIMENTAL + polygon[0].x = vp[dimz].x - vp[0].x; polygon[1].x = vp[dimz+1].x - vp[dimz].x; + polygon[0].y = vp[dimz].y - vp[0].y; polygon[1].y = vp[dimz+1].y - vp[dimz].y; + polygon[0].z = vp[dimz].z - vp[0].z; polygon[1].z = vp[dimz+1].z - vp[dimz].z; + polygon[2].x = vp[1].x - vp[dimz+1].x; polygon[3].x = vp[0].x - vp[1].x; + polygon[2].y = vp[1].y - vp[dimz+1].y; polygon[3].y = vp[0].y - vp[1].y; + polygon[2].z = vp[1].z - vp[dimz+1].z; polygon[3].z = vp[0].z - vp[1].z; + + if (x1 < x0) + { + /* Diagonal is 02 = diag2 */ + tnorms[0].x = polygon[0].y * polygon[1].z - polygon[0].z * polygon[1].y; + tnorms[0].y = polygon[0].z * polygon[1].x - polygon[0].x * polygon[1].z; + tnorms[0].z = polygon[0].x * polygon[1].y - polygon[0].y * polygon[1].x; + tnorms[1].x = polygon[2].y * polygon[3].z - polygon[2].z * polygon[3].y; + tnorms[1].y = polygon[2].z * polygon[3].x - polygon[2].x * polygon[3].z; + tnorms[1].z = polygon[2].x * polygon[3].y - polygon[2].y * polygon[3].x; +#if 0 + /* Method #1: scalar product of (real) surface normals with position of normals */ + h = tnorms[0].x * vp[dimz].x + tnorms[0].y * vp[dimz].y + tnorms[0].z * vp[dimz].z; + hh = tnorms[1].x * vp[1].x + tnorms[1].y * vp[1].y + tnorms[1].z * vp[1].z; + if (tnorms[0].x * diag1.x + tnorms[0].y * diag1.y + tnorms[0].z * diag1.z > 0) + { + h = -h; hh = -hh; + } + if (h < hh) ind = indices[1]; else ind = indices[0]; +#else + /* Method #2: Compare z values */ + h = tnorms[1].x * vp[dimz].x + tnorms[1].y * vp[dimz].y + tnorms[1].z * vp[dimz].z; + if (h != 0.0) + { + h = (tnorms[1].x * vp[1].x + tnorms[1].y * vp[1].y + tnorms[1].z * vp[1].z) / h; + } + if (h > 1.0) ind = indices[1]; else ind = indices[0]; +#endif + } + else + { + /* Diagonal is 13 = diag1 */ + tnorms[0].x = polygon[3].y * polygon[0].z - polygon[3].z * polygon[0].y; + tnorms[0].y = polygon[3].z * polygon[0].x - polygon[3].x * polygon[0].z; + tnorms[0].z = polygon[3].x * polygon[0].y - polygon[3].y * polygon[0].x; + tnorms[1].x = polygon[1].y * polygon[2].z - polygon[1].z * polygon[2].y; + tnorms[1].y = polygon[1].z * polygon[2].x - polygon[1].x * polygon[2].z; + tnorms[1].z = polygon[1].x * polygon[2].y - polygon[1].y * polygon[2].x; +#if 0 + h = tnorms[0].x * vp[0].x + tnorms[0].y * vp[0].y + tnorms[0].z * vp[0].z; + hh = tnorms[1].x * vp[dimz+1].x + tnorms[1].y * vp[dimz+1].y + tnorms[1].z * vp[dimz+1].z; + if (tnorms[0].x * diag2.x + tnorms[0].y * diag2.y + tnorms[0].z * diag2.z > 0) + { + h = -h; hh = -hh; + } + if (h < hh) ind = indices[3]; else ind = indices[2]; +#else + h = tnorms[1].x * vp[0].x + tnorms[1].y * vp[0].y + tnorms[1].z * vp[0].z; + if (h != 0.0) + { + h = (tnorms[1].x * vp[dimz+1].x + tnorms[1].y * vp[dimz+1].y + tnorms[1].z * vp[dimz+1].z) / h; + } + if (h > 1.0) ind = indices[3]; else ind = indices[2]; +#endif + } + polygon[0] = vp[ind[0]]; polygon[1] = vp[ind[1]]; polygon[2] = vp[ind[2]]; + normals[0] = np[ind[0]]; normals[1] = np[ind[1]]; normals[2] = np[ind[2]]; + RenderShadedPolygon(3, polygon, normals, meshDesc->colour, graphEnv, lightDesc, tnorms + ind[6], zbuffer); + polygon[1] = vp[ind[4]]; polygon[2] = vp[ind[5]]; + normals[1] = np[ind[4]]; normals[2] = np[ind[5]]; + RenderShadedPolygon(3, polygon, normals, meshDesc->colour, graphEnv, lightDesc, tnorms + ind[7], zbuffer); +#else + if (x1 < x0) /* splitting diagonal is 02 */ + { + polygon[0] = vp[0]; polygon[1] = vp[dimz]; polygon[2] = vp[dimz+1]; + normals[0] = np[0]; normals[1] = np[dimz]; normals[2] = np[dimz+1]; + RenderShadedPolygon(3, polygon, normals, meshDesc->colour, graphEnv, lightDesc, NULL, zbuffer); + polygon[1] = vp[dimz+1]; polygon[2] = vp[1]; + normals[1] = np[dimz+1]; normals[2] = np[1]; + RenderShadedPolygon(3, polygon, normals, meshDesc->colour, graphEnv, lightDesc, NULL, zbuffer); + } + else + { + polygon[0] = vp[dimz]; polygon[1] = vp[dimz+1]; polygon[2] = vp[1]; + normals[0] = np[dimz]; normals[1] = np[dimz+1]; normals[2] = np[1]; + RenderShadedPolygon(3, polygon, normals, meshDesc->colour, graphEnv, lightDesc, NULL, zbuffer); + polygon[1] = vp[1]; polygon[2] = vp[0]; + normals[1] = np[1]; normals[2] = np[0]; + RenderShadedPolygon(3, polygon, normals, meshDesc->colour, graphEnv, lightDesc, NULL, zbuffer); + } +#endif /* POLYSHADE_HEIGHT_EXPERIMENTAL */ + +#endif + vp += sidestep; np += sidestep; + } + vbase += backstep; nbase += backstep; + } + + free(vert); free(norm); + + return 0; +} + + +void RenderHeightFreeMesh(mesh_desc *meshDesc) +{ + if (meshDesc->vert != NULL) free(meshDesc->vert); + if (meshDesc->norm != NULL) free(meshDesc->norm); + if (meshDesc->zbuffer != NULL) free(meshDesc->zbuffer); + meshDesc->vert = NULL; meshDesc->norm = NULL; + meshDesc->zbuffer = NULL; meshDesc->zbuffSize = 0; + meshDesc->srcData = NULL; meshDesc->width = 0; meshDesc->height = 0; +} + + +int RenderHeightGetDomain(const mdd_desc *mddDesc, int *dimx, int *dimz, int *stepx, int *stepz) +{ + int i, idx1=-1, idx2=-1; + + for (i=0; i<mddDesc->numDims; i++) + { + if (mddDesc->widths[i] != 1) + { + if (idx1 < 0) idx1 = i; + else if (idx2 < 0) idx2 = i; + else break; + } + } + if ((idx1 < 0) || (idx2 < 0) || (i < mddDesc->numDims)) + { + fprintf(stderr, "Height field projection must be 2D!\n"); + return -1; + } + if (dimx != NULL) *dimx = mddDesc->dims[idx1]; + if (dimz != NULL) *dimz = mddDesc->dims[idx2]; + if ((stepx != NULL) || (stepz != NULL)) + { + int s; + + s = mddDesc->baseSize; + for (i=mddDesc->numDims-1; i>idx2; i--) s *= mddDesc->dims[i]; + if (stepz != NULL) *stepz = s; + if (stepx != NULL) + { + for ( ; i>idx1; i--) s *= mddDesc->dims[i]; + *stepx = s; + } + } + return 0; +} + +#ifdef __cplusplus +} +#endif |