/* * 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 . * * Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / rasdaman GmbH. * * For more information please see * or contact Peter Baumann via . / /** * 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 #include #include #include #include #include #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 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; jvertices; 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<y *= (1<z *= (1<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<y *= (1<z *= (1<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; kx) && (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; kvertices; 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; ibBox., 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; ifirst_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; ix - 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; ivertices; 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; jvertices; 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; jbbox_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; isegs; 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; isegs; 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; isegs; 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; isegs-1; i++) { for (j=i+1; jsegs; 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; isegs; 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; isegs; 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; isegs; 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; isegs-1; i++) { for (j=i+1; jsegs; 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; nclipl) \ { \ 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 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; ilights.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= 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; iclipu) || (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; iclipu) || (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; iambient)) >= 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 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 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 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; nleft.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; iy - 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 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; inumDims; 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