mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 19:55:23 +00:00
902 lines
23 KiB
C++
902 lines
23 KiB
C++
/*
|
|
* Descent 3
|
|
* Copyright (C) 2024 Parallax Software
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
// rad_scan
|
|
|
|
#include "vecmat.h"
|
|
#include "3d.h"
|
|
#include "radiosity.h"
|
|
#include "hemicube.h"
|
|
#include "gr.h"
|
|
#include "d3edit.h"
|
|
#include "ddio.h"
|
|
#include "mem.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <algorithm>
|
|
|
|
#define TOP_FACE 0
|
|
#define LEFT_FACE 1
|
|
#define RIGHT_FACE 2
|
|
#define FRONT_FACE 3
|
|
#define BACK_FACE 4
|
|
|
|
float Hemicube_view_zoom = 1.0;
|
|
|
|
int rad_Drawing = 0;
|
|
|
|
g3Point Element_points[100];
|
|
rad_element *rad_MaxElement;
|
|
|
|
rad_hemicube rad_Hemicube;
|
|
|
|
extern void ShowRadView();
|
|
int Show_rad_progress = 0;
|
|
int Cracks_this_frame, Cracks_this_side;
|
|
|
|
float Highest_top_delta, Highest_side_delta;
|
|
|
|
#define PI 3.141592654
|
|
|
|
// Calculates delta form factors
|
|
void CalculateDeltaFormFactors() {
|
|
int i, j; // Loop indices
|
|
float da; // Cell area
|
|
float dx, dy, dz; // Cell dimensions
|
|
float r, x, y, z; // Cell co-ordinates
|
|
float val;
|
|
|
|
// Initialize cell dimensions and area
|
|
Highest_top_delta = -1.0f;
|
|
Highest_side_delta = -1.0f;
|
|
dx = dy = dz = 2.0 / (float)rad_Hemicube.ff_res;
|
|
da = 4.0 / ((float)rad_Hemicube.ff_res * (float)rad_Hemicube.ff_res);
|
|
|
|
// Calculate top face delta form factors
|
|
x = dx / 2.0;
|
|
for (i = 0; i < rad_Hemicube.grid_dim; i++) {
|
|
y = dy / 2.0;
|
|
for (j = 0; j < rad_Hemicube.grid_dim; j++) {
|
|
r = x * x + y * y + 1.0;
|
|
val = (float)(da / (PI * r * r));
|
|
rad_Hemicube.top_array[j * rad_Hemicube.grid_dim + i] = val;
|
|
|
|
if (val > Highest_top_delta)
|
|
Highest_top_delta = val;
|
|
|
|
y += dy;
|
|
}
|
|
x += dx;
|
|
}
|
|
|
|
// Calculate side face delta form factors
|
|
x = dx / 2.0;
|
|
for (i = 0; i < rad_Hemicube.grid_dim; i++) {
|
|
z = dz / 2.0;
|
|
for (j = 0; j < rad_Hemicube.grid_dim; j++) {
|
|
r = x * x + z * z + 1.0;
|
|
val = (float)(z * da / (PI * r * r));
|
|
rad_Hemicube.side_array[j * rad_Hemicube.grid_dim + i] = val;
|
|
|
|
if (val > Highest_side_delta)
|
|
Highest_side_delta = val;
|
|
|
|
z += dz;
|
|
}
|
|
x += dx;
|
|
}
|
|
}
|
|
|
|
// Calculates the form factors for a hemicube
|
|
void CalculateFormFactorsHemiCube() {
|
|
g3Point *pointlist[100];
|
|
int i, t, k, j, en_limit, en;
|
|
int ff_index = 0;
|
|
int self;
|
|
rad_element *dest_element;
|
|
|
|
for (i = 0; i < rad_NumElements; i++)
|
|
rad_FormFactors[i] = 0.0f;
|
|
|
|
// Shoot from patch (faster) or element?
|
|
if (Shoot_from_patch)
|
|
en_limit = 1;
|
|
else
|
|
en_limit = rad_MaxSurface->xresolution * rad_MaxSurface->yresolution;
|
|
for (en = 0; en < en_limit; en++) {
|
|
if (Shoot_from_patch) {
|
|
SetSurfaceView(rad_MaxSurface);
|
|
} else {
|
|
rad_MaxElement = &rad_MaxSurface->elements[en];
|
|
if (rad_MaxElement->flags & EF_IGNORE)
|
|
continue;
|
|
|
|
SetElementView(rad_MaxElement);
|
|
}
|
|
|
|
Cracks_this_frame = 0;
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
ClearHemicubeGrid();
|
|
UpdateView(i);
|
|
StartHemicubeDrawing();
|
|
|
|
ff_index = 0;
|
|
|
|
for (t = 0; t < rad_NumSurfaces; t++) {
|
|
int ignore = 0;
|
|
rad_surface *surf = &rad_Surfaces[t];
|
|
|
|
if (surf == rad_MaxSurface)
|
|
ignore = 1;
|
|
if (surf->surface_type == ST_PORTAL)
|
|
ignore = 1;
|
|
|
|
for (j = 0; j < surf->xresolution * surf->yresolution; j++, ff_index++) {
|
|
if (ignore)
|
|
continue;
|
|
|
|
rad_element *ep = &surf->elements[j];
|
|
|
|
if (ep->flags & EF_IGNORE)
|
|
continue;
|
|
|
|
for (k = 0; k < ep->num_verts; k++) {
|
|
vector vec = ep->verts[k];
|
|
g3_RotatePoint(&Element_points[k], &vec);
|
|
Element_points[k].p3_flags = 0;
|
|
}
|
|
|
|
if (g3_CheckNormalFacing(&ep->verts[0], &surf->normal)) {
|
|
for (k = 0; k < ep->num_verts; k++) {
|
|
g3Point *p = &Element_points[k];
|
|
pointlist[k] = p;
|
|
}
|
|
|
|
DrawRadiosityPoly(ep->num_verts, pointlist, ff_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
SumDeltas(rad_FormFactors, i);
|
|
EndHemicubeDrawing(i);
|
|
}
|
|
}
|
|
|
|
// Now extract the results
|
|
for (ff_index = 0, i = 0; i < rad_NumSurfaces; i++) {
|
|
rad_surface *surf = &rad_Surfaces[i];
|
|
|
|
// Check for self surface
|
|
if (rad_MaxSurface == surf)
|
|
self = 1;
|
|
else
|
|
self = 0;
|
|
|
|
for (t = 0; t < surf->xresolution * surf->yresolution; t++, ff_index++) {
|
|
if (self)
|
|
continue;
|
|
|
|
dest_element = &surf->elements[t];
|
|
|
|
if (rad_FormFactors[ff_index] > 0.0) {
|
|
spectra delta;
|
|
float reflect_factor = surf->reflectivity;
|
|
|
|
// Compute reciprocal form factor
|
|
float rff;
|
|
|
|
if (rad_MaxSurface->surface_type == ST_SATELLITE)
|
|
rff = std::min(rad_FormFactors[ff_index], 1.0f);
|
|
else
|
|
rff = (float)std::min(rad_FormFactors[ff_index] * rad_MaxSurface->area / dest_element->area, 1.0f);
|
|
|
|
// Get shooting patch unsent exitance
|
|
spectra shoot = rad_MaxSurface->exitance;
|
|
|
|
// Calculate delta exitance
|
|
delta.r = shoot.r * reflect_factor * rff;
|
|
delta.g = shoot.g * reflect_factor * rff;
|
|
delta.b = shoot.b * reflect_factor * rff;
|
|
|
|
// Update element exitance
|
|
dest_element->exitance.r += delta.r;
|
|
dest_element->exitance.g += delta.g;
|
|
dest_element->exitance.b += delta.b;
|
|
|
|
// Update patch unsent exitance
|
|
|
|
surf->exitance.r += ((dest_element->area / surf->area) * delta.r);
|
|
surf->exitance.g += ((dest_element->area / surf->area) * delta.g);
|
|
surf->exitance.b += ((dest_element->area / surf->area) * delta.b);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void InitHemicube(int resolution) {
|
|
// Make sure resolution is even
|
|
ASSERT(resolution % 2 == 0);
|
|
|
|
rad_Drawing = 1;
|
|
|
|
rad_Hemicube.ff_res = resolution;
|
|
rad_Hemicube.grid_dim = resolution / 2;
|
|
|
|
rad_Hemicube.id_grid = (int *)mem_malloc(rad_Hemicube.ff_res * rad_Hemicube.ff_res * sizeof(int));
|
|
ASSERT(rad_Hemicube.id_grid != NULL);
|
|
rad_Hemicube.depth_grid = (float *)mem_malloc(rad_Hemicube.ff_res * rad_Hemicube.ff_res * sizeof(float));
|
|
ASSERT(rad_Hemicube.depth_grid != NULL);
|
|
|
|
rad_Hemicube.top_array = (float *)mem_malloc(rad_Hemicube.grid_dim * rad_Hemicube.grid_dim * sizeof(float));
|
|
ASSERT(rad_Hemicube.top_array != NULL);
|
|
rad_Hemicube.side_array = (float *)mem_malloc(rad_Hemicube.grid_dim * rad_Hemicube.grid_dim * sizeof(float));
|
|
ASSERT(rad_Hemicube.side_array != NULL);
|
|
|
|
CalculateDeltaFormFactors();
|
|
|
|
// Get a surface to draw to
|
|
rad_Hemicube.drawing_surface.create(rad_Hemicube.ff_res, rad_Hemicube.ff_res, BPP_16);
|
|
rad_Hemicube.vport = new grViewport(&rad_Hemicube.drawing_surface);
|
|
}
|
|
|
|
void CloseHemicube() {
|
|
delete rad_Hemicube.vport;
|
|
rad_Hemicube.drawing_surface.free();
|
|
|
|
mem_free(rad_Hemicube.depth_grid);
|
|
mem_free(rad_Hemicube.id_grid);
|
|
mem_free(rad_Hemicube.side_array);
|
|
mem_free(rad_Hemicube.top_array);
|
|
|
|
rad_Drawing = 0;
|
|
}
|
|
|
|
void ClearHemicubeGrid() {
|
|
int i, t;
|
|
|
|
for (i = 0; i < rad_Hemicube.ff_res; i++) {
|
|
for (t = 0; t < rad_Hemicube.ff_res; t++) {
|
|
rad_Hemicube.depth_grid[i * rad_Hemicube.ff_res + t] = 999999.0f;
|
|
rad_Hemicube.id_grid[i * rad_Hemicube.ff_res + t] = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetElementView(rad_element *ep) {
|
|
vector rv; // Random vector
|
|
vector u, v, n;
|
|
|
|
// Select random vector for hemicube orientation
|
|
rv.x = ((rand() / RAND_MAX) * 2.0 - 1.0);
|
|
rv.y = ((rand() / RAND_MAX) * 2.0 - 1.0);
|
|
rv.z = ((rand() / RAND_MAX) * 2.0 - 1.0);
|
|
|
|
n = rad_MaxSurface->normal; // Get patch normal
|
|
|
|
do // Get valid u-axis vector
|
|
{
|
|
vm_CrossProduct(&u, &n, &rv);
|
|
} while (vm_GetMagnitude(&u) < .0001);
|
|
|
|
vm_NormalizeVector(&u);
|
|
vm_CrossProduct(&v, &u, &n); // Determine v-axis
|
|
|
|
rad_Hemicube.head_matrix.rvec = u;
|
|
rad_Hemicube.head_matrix.uvec = v;
|
|
rad_Hemicube.head_matrix.fvec = n;
|
|
|
|
vm_VectorToMatrix(&rad_Hemicube.head_matrix, &n, NULL, NULL);
|
|
|
|
rad_Hemicube.shooting_element = ep;
|
|
}
|
|
|
|
void SetSurfaceView(rad_surface *surf) {
|
|
vector rv; // Random vector
|
|
vector u, v, n;
|
|
|
|
// Select random vector for hemicube orientation
|
|
rv.x = ((rand() / RAND_MAX) * 2.0 - 1.0);
|
|
rv.y = ((rand() / RAND_MAX) * 2.0 - 1.0);
|
|
rv.z = ((rand() / RAND_MAX) * 2.0 - 1.0);
|
|
|
|
n = rad_MaxSurface->normal; // Get patch normal
|
|
|
|
do // Get valid u-axis vector
|
|
{
|
|
vm_CrossProduct(&u, &n, &rv);
|
|
} while (vm_GetMagnitude(&u) < .0001);
|
|
|
|
vm_NormalizeVector(&u);
|
|
vm_CrossProduct(&v, &u, &n); // Determine v-axis
|
|
|
|
rad_Hemicube.head_matrix.rvec = u;
|
|
rad_Hemicube.head_matrix.uvec = v;
|
|
rad_Hemicube.head_matrix.fvec = n;
|
|
|
|
vm_VectorToMatrix(&rad_Hemicube.head_matrix, &n, NULL, NULL);
|
|
|
|
rad_Hemicube.shooting_surface = surf;
|
|
}
|
|
|
|
// Build transformation matrix for our hemicube
|
|
void BuildTransform(vector *nu, vector *nv, vector *nn) {
|
|
matrix *vm = &rad_Hemicube.view_matrix; // view matrix
|
|
|
|
if (Shoot_from_patch)
|
|
GetCenterOfSurface(rad_Hemicube.shooting_surface, &rad_Hemicube.view_position);
|
|
else
|
|
GetCenterOfElement(rad_Hemicube.shooting_element, &rad_Hemicube.view_position);
|
|
|
|
rad_Hemicube.view_position += (rad_MaxSurface->normal / 16.0);
|
|
|
|
vm->fvec = *nn;
|
|
vm->uvec = *nv;
|
|
vm->rvec = *nu;
|
|
}
|
|
|
|
void StartHemicubeDrawing() {
|
|
StartEditorFrame(rad_Hemicube.vport, &rad_Hemicube.view_position, &rad_Hemicube.view_matrix, Hemicube_view_zoom);
|
|
}
|
|
|
|
int surface_colors[9000];
|
|
void EndHemicubeDrawing(int face) {
|
|
static int first = 1;
|
|
|
|
EndEditorFrame();
|
|
|
|
mprintf_at(2, 4, 0, "CTF=%d ", Cracks_this_frame);
|
|
mprintf_at(2, 5, 0, "CTS=%d ", Cracks_this_side);
|
|
|
|
if (Show_rad_progress) {
|
|
int i, t;
|
|
int key;
|
|
|
|
if (first) {
|
|
for (i = 0; i < 9000; i++) {
|
|
int r = (rand() % 127) + 128;
|
|
int g = (rand() % 127) + 128;
|
|
int b = (rand() % 127) + 128;
|
|
|
|
surface_colors[i] = GR_RGB(r, g, b);
|
|
}
|
|
|
|
first = 0;
|
|
}
|
|
|
|
while ((key = ddio_KeyInKey()) != 0)
|
|
;
|
|
|
|
uint16_t surfval[90000];
|
|
int ff_index = 0;
|
|
|
|
for (i = 0; i < rad_NumSurfaces; i++) {
|
|
rad_surface *surf = &rad_Surfaces[i];
|
|
|
|
for (t = 0; t < surf->yresolution * surf->xresolution; t++, ff_index++) {
|
|
surfval[ff_index] = i;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < rad_Hemicube.ff_res; i++) {
|
|
for (t = 0; t < rad_Hemicube.ff_res; t++) {
|
|
int element_num = rad_Hemicube.id_grid[i * rad_Hemicube.ff_res + t];
|
|
float factor;
|
|
|
|
if (face == TOP_FACE)
|
|
factor = GetTopFactor(i, t);
|
|
else
|
|
factor = GetSideFactor(i, t);
|
|
|
|
if (element_num == -1)
|
|
rad_Hemicube.id_grid[i * rad_Hemicube.ff_res + t] = -1;
|
|
else {
|
|
int surfnum = surfval[element_num];
|
|
int color = surface_colors[surfnum];
|
|
|
|
float norm;
|
|
|
|
if (face == TOP_FACE)
|
|
norm = factor / Highest_top_delta;
|
|
else
|
|
norm = factor / Highest_side_delta;
|
|
|
|
int r = GR_COLOR_RED(color) * norm;
|
|
int g = GR_COLOR_GREEN(color) * norm;
|
|
int b = GR_COLOR_BLUE(color) * norm;
|
|
|
|
rad_Hemicube.id_grid[i * rad_Hemicube.ff_res + t] = GR_RGB(r, g, b);
|
|
}
|
|
}
|
|
}
|
|
|
|
ShowRadView();
|
|
|
|
while ((key = ddio_KeyInKey()) == 0) {
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update hemicube view transformation matrix
|
|
void UpdateView(int face_id) {
|
|
vector nu, nv, nn; // View space co-ordinates
|
|
|
|
switch (face_id) // Exchange co-ordinates
|
|
{
|
|
case TOP_FACE:
|
|
nu = rad_Hemicube.head_matrix.rvec;
|
|
nv = rad_Hemicube.head_matrix.uvec;
|
|
nn = rad_Hemicube.head_matrix.fvec;
|
|
break;
|
|
case FRONT_FACE:
|
|
nu = rad_Hemicube.head_matrix.rvec;
|
|
nv = rad_Hemicube.head_matrix.fvec;
|
|
nn = rad_Hemicube.head_matrix.uvec * -1;
|
|
break;
|
|
case RIGHT_FACE:
|
|
nu = rad_Hemicube.head_matrix.uvec;
|
|
nv = rad_Hemicube.head_matrix.fvec;
|
|
nn = rad_Hemicube.head_matrix.rvec;
|
|
break;
|
|
case BACK_FACE:
|
|
nu = rad_Hemicube.head_matrix.rvec * -1;
|
|
nv = rad_Hemicube.head_matrix.fvec;
|
|
nn = (rad_Hemicube.head_matrix.uvec);
|
|
break;
|
|
case LEFT_FACE:
|
|
nu = (rad_Hemicube.head_matrix.uvec * -1);
|
|
nv = rad_Hemicube.head_matrix.fvec;
|
|
nn = (rad_Hemicube.head_matrix.rvec * -1);
|
|
break;
|
|
default:
|
|
Int3();
|
|
break;
|
|
}
|
|
|
|
// Build new view transformation matrix
|
|
BuildTransform(&nu, &nv, &nn);
|
|
}
|
|
|
|
void DrawRadiosityPoly(int nv, g3Point **pointlist, int id) {
|
|
int i;
|
|
g3Codes cc;
|
|
bool was_clipped = 0;
|
|
int triangulate = 1;
|
|
|
|
ASSERT(id >= 0 && id <= rad_NumElements);
|
|
|
|
if (triangulate) {
|
|
if (nv > 3) {
|
|
g3Point *tripoints[3];
|
|
|
|
tripoints[0] = pointlist[0];
|
|
tripoints[2] = pointlist[1];
|
|
|
|
for (i = 0; i < nv - 2; i++) {
|
|
tripoints[1] = tripoints[2];
|
|
tripoints[2] = pointlist[2 + i];
|
|
DrawRadiosityPoly(3, tripoints, id);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Initialize
|
|
cc.cc_or = 0;
|
|
cc.cc_and = 0xff;
|
|
|
|
// Get codes for this polygon, and copy uvls into points
|
|
for (i = 0; i < nv; i++) {
|
|
uint8_t c;
|
|
|
|
c = pointlist[i]->p3_codes;
|
|
|
|
cc.cc_and &= c;
|
|
cc.cc_or |= c;
|
|
}
|
|
|
|
// All points off grid?
|
|
if (cc.cc_and)
|
|
return;
|
|
|
|
// One or more point off screen, so clip
|
|
if (cc.cc_or) {
|
|
|
|
// Clip the polygon, getting pointer to new buffer
|
|
pointlist = g3_ClipPolygon(pointlist, &nv, &cc);
|
|
|
|
// Flag as clipped so temp points will be freed
|
|
was_clipped = 1;
|
|
|
|
// Check for polygon clipped away, or clip otherwise failed
|
|
if ((nv == 0) || (cc.cc_or & CC_BEHIND) || cc.cc_and)
|
|
goto free_points;
|
|
}
|
|
|
|
// Make list of 2d coords
|
|
for (i = 0; i < nv; i++) {
|
|
g3Point *p = pointlist[i];
|
|
g3_ProjectPoint(p);
|
|
}
|
|
|
|
// Draw!
|
|
ScanRadiosityPoly(pointlist, nv, id);
|
|
|
|
free_points:;
|
|
|
|
// If was clipped, free temp points
|
|
if (was_clipped)
|
|
g3_FreeTempPoints(pointlist, nv);
|
|
}
|
|
|
|
/*void GetVertexOrdering (hemicube_point *t, int nv, int *vlt, int *vlb, int *vrt, int *vrb,int *bottom_y_ind)
|
|
{
|
|
int i;
|
|
float min_y,max_y;
|
|
int min_y_ind;
|
|
int original_vrt;
|
|
float min_x;
|
|
|
|
// Scan all vertices, set min_y_ind to vertex with smallest y coordinate.
|
|
min_y = t[0].sy;
|
|
max_y = min_y;
|
|
min_y_ind = 0;
|
|
min_x = t[0].sx;
|
|
*bottom_y_ind = 0;
|
|
|
|
for (i=1; i<nv; i++)
|
|
{
|
|
if (t[i].sy < min_y)
|
|
{
|
|
min_y = t[i].sy;
|
|
min_y_ind = i;
|
|
min_x = t[i].sx;
|
|
}
|
|
else if (t[i].sy == min_y)
|
|
{
|
|
if (t[i].sx < min_x)
|
|
{
|
|
min_y_ind = i;
|
|
min_x = t[i].sx;
|
|
}
|
|
}
|
|
if (t[i].sy > max_y)
|
|
{
|
|
max_y = t[i].sy;
|
|
*bottom_y_ind = i;
|
|
}
|
|
}
|
|
|
|
|
|
// Set "vertex left top", etc. based on vertex with topmost y coordinate
|
|
*vlt = min_y_ind;
|
|
*vrt = *vlt;
|
|
*vlb = PrevIndex(*vlt,nv);
|
|
*vrb = NextIndex(*vrt,nv);
|
|
|
|
// If right edge is horizontal, then advance along polygon bound until it no longer is or until all
|
|
// vertices have been examined.
|
|
// (Left edge cannot be horizontal, because *vlt is set to leftmost point with highest y coordinate.)
|
|
|
|
original_vrt = *vrt;
|
|
|
|
while (t[*vrt].sy == t[*vrb].sy)
|
|
{
|
|
if (NextIndex(*vrt,nv) == original_vrt)
|
|
break;
|
|
|
|
*vrt = NextIndex(*vrt,nv);
|
|
*vrb = NextIndex(*vrt,nv);
|
|
}
|
|
}*/
|
|
|
|
void GetVertexOrdering(hemicube_point *t, int nv, int *vlt, int *vlb, int *vrt, int *vrb, float *top_y, float *bottom_y,
|
|
int *left_edge_dir) {
|
|
int i;
|
|
float min_y, max_y;
|
|
int min_index_l, min_index_r, max_index;
|
|
|
|
// Scan all vertices, set min_y_ind to vertex with smallest y coordinate.
|
|
|
|
*bottom_y = 0;
|
|
*top_y = 0;
|
|
|
|
min_index_l = max_index = 0;
|
|
max_y = min_y = t[0].sy;
|
|
for (i = 1; i < nv; i++) {
|
|
if (t[i].sy < min_y)
|
|
min_y = t[min_index_l = i].sy;
|
|
else if (t[i].sy > max_y)
|
|
max_y = t[max_index = i].sy;
|
|
}
|
|
if (min_y == max_y)
|
|
return;
|
|
|
|
// Scan in ascending order to find the last top-edge point */
|
|
min_index_r = min_index_l;
|
|
while (t[min_index_r].sy == min_y)
|
|
min_index_r = NextIndex(min_index_r, nv);
|
|
min_index_r = PrevIndex(min_index_r, nv);
|
|
|
|
// Now scan in descending order to find the first top-edge point
|
|
while (t[min_index_l].sy == min_y)
|
|
min_index_l = PrevIndex(min_index_l, nv);
|
|
min_index_l = NextIndex(min_index_l, nv);
|
|
|
|
*left_edge_dir = -1;
|
|
if (t[min_index_l].sx != t[min_index_r].sx) {
|
|
// If the top is flat, just see which of the ends is leftmost
|
|
if (t[min_index_l].sx > t[min_index_r].sx) {
|
|
*left_edge_dir = 1;
|
|
|
|
int temp = min_index_l;
|
|
min_index_l = min_index_r;
|
|
min_index_r = temp;
|
|
}
|
|
} else {
|
|
// Point to the downward end of the first line of each of the
|
|
// two edges down from the top
|
|
int next_index = min_index_r;
|
|
next_index = NextIndex(next_index, nv);
|
|
int prev_index = min_index_r;
|
|
prev_index = PrevIndex(prev_index, nv);
|
|
|
|
/* Calculate X and Y lengths from the top vertex to the end of
|
|
the first line down each edge; use those to compare slopes
|
|
and see which line is leftmost */
|
|
float deltaXN = t[next_index].sx - t[min_index_l].sx;
|
|
float deltaYN = t[next_index].sy - t[min_index_l].sy;
|
|
float deltaXP = t[prev_index].sx - t[min_index_l].sx;
|
|
float deltaYP = t[prev_index].sy - t[min_index_l].sy;
|
|
if (((deltaXN * deltaYP) - (deltaYN * deltaXP)) < 0) {
|
|
*left_edge_dir = 1;
|
|
|
|
int temp = min_index_l;
|
|
min_index_l = min_index_r;
|
|
min_index_r = temp;
|
|
}
|
|
}
|
|
|
|
*bottom_y = max_y;
|
|
*top_y = min_y;
|
|
|
|
*vlt = min_index_l;
|
|
*vrt = min_index_r;
|
|
|
|
if (*left_edge_dir == -1) {
|
|
*vlb = PrevIndex(min_index_l, nv);
|
|
*vrb = NextIndex(min_index_r, nv);
|
|
} else {
|
|
*vrb = PrevIndex(min_index_r, nv);
|
|
*vlb = NextIndex(min_index_l, nv);
|
|
}
|
|
}
|
|
|
|
// Returns number preceding val modulo modulus.
|
|
// prevmod(3,4) = 2
|
|
// prevmod(0,4) = 3
|
|
int PrevIndex(int val, int modulus) {
|
|
if (val > 0)
|
|
return val - 1;
|
|
else
|
|
return modulus - 1;
|
|
}
|
|
|
|
// Returns number succeeding val modulo modulus.
|
|
// succmod(3,4) = 0
|
|
// succmod(0,4) = 1
|
|
int NextIndex(int val, int modulus) {
|
|
if (val < modulus - 1)
|
|
return val + 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void ScanRadiosityPoly(g3Point **pl, int nv, int element_id) {
|
|
int i, left_edge_dir;
|
|
|
|
float Delta_right_x, Right_x, Delta_left_x, Left_x;
|
|
float Left_z, Right_z, Delta_left_z, Delta_right_z;
|
|
|
|
float height, left_height, right_height;
|
|
int vlt, vlb, vrt, vrb, desty;
|
|
float top_y, bottom_y, next_break_left, next_break_right, y;
|
|
|
|
hemicube_point cp[100];
|
|
|
|
int destptr;
|
|
|
|
ASSERT(element_id >= 0 && element_id <= rad_NumElements);
|
|
|
|
for (i = 0; i < nv; i++) {
|
|
g3Point p;
|
|
p = *pl[i];
|
|
|
|
cp[i].sx = p.p3_sx;
|
|
cp[i].sy = p.p3_sy;
|
|
cp[i].z = 1.0 / (float)p.p3_vec.z;
|
|
}
|
|
|
|
// Determine top and bottom y coords.
|
|
GetVertexOrdering(cp, nv, &vlt, &vlb, &vrt, &vrb, &top_y, &bottom_y, &left_edge_dir);
|
|
|
|
height = bottom_y - top_y;
|
|
if (height < 1.0)
|
|
return;
|
|
|
|
#include "radscan_leftedge.h"
|
|
#include "radscan_rightedge.h"
|
|
|
|
// Set the destptr to equal the first row to draw to
|
|
destptr = ((int)top_y * rad_Hemicube.ff_res);
|
|
desty = (int)top_y;
|
|
|
|
for (y = top_y; y < bottom_y; y++) {
|
|
if (y >= next_break_left) {
|
|
do {
|
|
vlt = vlb;
|
|
|
|
if (left_edge_dir == -1)
|
|
vlb = PrevIndex(vlb, nv);
|
|
else
|
|
vlb = NextIndex(vlb, nv);
|
|
} while (y == cp[vlb].sy);
|
|
|
|
#include "radscan_leftedge.h"
|
|
}
|
|
|
|
if (y >= next_break_right) {
|
|
do {
|
|
vrt = vrb;
|
|
if (left_edge_dir == -1)
|
|
vrb = NextIndex(vrb, nv);
|
|
else
|
|
vrb = PrevIndex(vrb, nv);
|
|
|
|
} while (y == cp[vrb].sy);
|
|
#include "radscan_rightedge.h"
|
|
}
|
|
|
|
// Draw a scanline
|
|
|
|
float sl, sr;
|
|
float lz, rz;
|
|
|
|
sl = Left_x;
|
|
sr = Right_x;
|
|
|
|
lz = Left_z;
|
|
rz = Right_z;
|
|
|
|
int x1 = sl;
|
|
int width = (sr - x1) + 1;
|
|
|
|
if (desty < 0 || desty >= rad_Hemicube.ff_res)
|
|
goto UpdateExtents;
|
|
|
|
if (x1 < 0 || x1 >= rad_Hemicube.ff_res)
|
|
goto UpdateExtents;
|
|
if ((x1 + width) > rad_Hemicube.ff_res) {
|
|
width = (rad_Hemicube.ff_res - x1);
|
|
}
|
|
|
|
if (width > 0) {
|
|
float z = lz;
|
|
float dz = (rz - z) / width;
|
|
|
|
// Enter scan line
|
|
for (int x = x1; x < x1 + width; x++) {
|
|
float realz = 1.0 / z;
|
|
// Check element visibility
|
|
if (realz <= rad_Hemicube.depth_grid[destptr + x]) {
|
|
// Update Z-buffer
|
|
rad_Hemicube.depth_grid[destptr + x] = realz;
|
|
|
|
// Set polygon identifier
|
|
rad_Hemicube.id_grid[destptr + x] = element_id;
|
|
}
|
|
// Update element pseudodepth
|
|
z += dz;
|
|
}
|
|
}
|
|
|
|
UpdateExtents:
|
|
|
|
destptr += rad_Hemicube.ff_res;
|
|
desty++;
|
|
|
|
Left_x += Delta_left_x;
|
|
Right_x += Delta_right_x;
|
|
Right_z += Delta_right_z;
|
|
Left_z += Delta_left_z;
|
|
}
|
|
}
|
|
|
|
float GetTopFactor(int row, int col) {
|
|
if (row >= rad_Hemicube.grid_dim)
|
|
row -= rad_Hemicube.grid_dim;
|
|
else
|
|
row = rad_Hemicube.grid_dim - row - 1;
|
|
|
|
if (col >= rad_Hemicube.grid_dim)
|
|
col -= rad_Hemicube.grid_dim;
|
|
else
|
|
col = rad_Hemicube.grid_dim - col - 1;
|
|
|
|
return rad_Hemicube.top_array[row * rad_Hemicube.grid_dim + col];
|
|
}
|
|
|
|
// Get side face cell form factor
|
|
float GetSideFactor(int row, int col) {
|
|
if (col >= rad_Hemicube.grid_dim)
|
|
col -= rad_Hemicube.grid_dim;
|
|
else
|
|
col = rad_Hemicube.grid_dim - col - 1;
|
|
|
|
if (row >= rad_Hemicube.grid_dim)
|
|
return 0.0f;
|
|
else
|
|
row = rad_Hemicube.grid_dim - row - 1;
|
|
|
|
return rad_Hemicube.side_array[(row * rad_Hemicube.grid_dim) + col];
|
|
}
|
|
|
|
// Sums the delta form factors
|
|
void SumDeltas(float *ff_array, int face_id) {
|
|
int poly_id; // Polygon identifier
|
|
int row, col; // Face cell indices
|
|
|
|
Cracks_this_side = 0;
|
|
|
|
if (face_id == TOP_FACE) {
|
|
// Scan entire face buffer
|
|
for (row = 0; row < rad_Hemicube.ff_res; row++) {
|
|
for (col = 0; col < rad_Hemicube.ff_res; col++) {
|
|
poly_id = rad_Hemicube.id_grid[row * rad_Hemicube.ff_res + col];
|
|
if (poly_id != -1) {
|
|
if (Shoot_from_patch)
|
|
ff_array[poly_id] += (GetTopFactor(row, col));
|
|
else
|
|
ff_array[poly_id] += (GetTopFactor(row, col) * (rad_MaxElement->area / rad_MaxSurface->area));
|
|
} else {
|
|
Cracks_this_frame++;
|
|
Cracks_this_side++;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Scan upper half of face buffer only
|
|
for (row = 0; row < rad_Hemicube.grid_dim; row++) {
|
|
for (col = 0; col < rad_Hemicube.ff_res; col++) {
|
|
poly_id = rad_Hemicube.id_grid[row * rad_Hemicube.ff_res + col];
|
|
if (poly_id != -1) {
|
|
if (Shoot_from_patch)
|
|
ff_array[poly_id] += GetSideFactor(row, col);
|
|
else
|
|
ff_array[poly_id] += (GetSideFactor(row, col) * (rad_MaxElement->area / rad_MaxSurface->area));
|
|
} else {
|
|
Cracks_this_frame++;
|
|
Cracks_this_side++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|