Descent3/editor/rad_cast.cpp
2024-06-15 20:12:48 +02:00

1112 lines
32 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/>.
--- HISTORICAL COMMENTS FOLLOW ---
* $Logfile: /DescentIII/Main/editor/rad_cast.cpp $
* $Revision: 1.1.1.1 $
* $Date: 2003-08-26 03:57:38 $
* $Author: kevinb $
*
* Ray casting for radiosity, I think.
*
* $Log: not supported by cvs2svn $
*
* 87 5/20/99 11:50a Jason
* made super detail not do volume lights super detail
*
* 86 4/18/99 3:37p 3dsmax
* tweaks
*
* 85 4/18/99 5:42a Chris
* Added the FQ_IGNORE_RENDER_THROUGH_PORTALS flag
*
* 84 4/16/99 5:50p Jason
* tweaked smooth specular stuff some more
*
* 83 4/15/99 2:56p Jason
* fixed stupid lighting bug
*
* 82 4/05/99 10:36a 3dsmax
* took out mprintfs
*
* 81 4/02/99 5:34p Jason
* added verbose mprintfs for super detail
*
* 80 3/23/99 4:09p Jason
* upped smooth specular sensitivity
*
* 79 3/11/99 1:07p Jason
* more fixes for smooth specular lighting
*
* 78 3/10/99 7:12p Jason
* added smooth specular shading for curved surfaces
*
* 77 3/10/99 2:09p Jason
* made better specular lighting
*
* 76 2/24/99 5:14p 3dsmax
*
*
* 75 2/09/99 9:59a Chris
* Massive BOA update :) Terrain happy now. Vis happy now. Sound happy
* now.
*
* 74 1/27/99 12:19p Dan
* another clipping fix
*
* 73 1/27/99 12:16p Dan
* finally fixed dumb clipping problem
*
* 72 1/27/99 11:06a Jason
* fixed satellite clipping problem
*
* 71 1/24/99 1:17p Jason
* fixed satellite casting problem
*
* 70 12/21/98 11:52a Jason
* changes for specular lighting
*
* 69 11/19/98 12:22p Jason
* optimizations
*
* 68 10/28/98 12:18p Jason
* sped up volume lighting a bit
*
* 67 10/05/98 6:53p Jason
* took out some unused variables
*
* 66 10/02/98 11:55a Jason
* more fixes for satellite clipping
*
* 65 9/24/98 11:27a Jason
* fixed satellite clipping
*
* 64 8/21/98 12:29p Jason
* upped specular lighting threshold
*
* 63 8/10/98 11:25a Jason
* added better specular lighting
*
* 62 6/30/98 3:12p Jason
* sped up super detail by not doing surper-detail from surfaces that
* aren't lightsources
*
* 61 6/03/98 12:22p Jason
* specular lighting optimizations
*
* 60 6/02/98 6:03p Jason
* added specular lightmaps
*
* 59 6/02/98 11:01a Jason
* Post E3 Checkin
*
* 58 5/26/98 10:11p Sean
* don't do specular lighting for single rooms
*
* 57 5/25/98 3:46p Jason
* added better light glows
*
* 56 5/22/98 3:28p Jason
* added specular lighting
*
* 55 5/21/98 11:51a Jason
* made volume lights work with super detail
*
* 54 5/20/98 1:37p Jason
* upped facelimit on volume raycasting
*
* 53 5/19/98 2:36p Jason
* improved volume light casting
*
* 52 5/15/98 5:41p Jason
* implemented volume lighting system
*
* 51 5/01/98 4:32p Jason
* temp fix for lighting
*
* 50 4/19/98 5:23p Jason
* made terrain satellites only cast light on rooms that touch terrain
*
* 49 4/08/98 3:26p Jason
* made terrain satellites automatically cast light into rooms
*
* 48 3/17/98 5:15p Jason
* changed normal casting length
*
* 47 3/16/98 5:47p Jason
* added FQ_NO_RELINK
*
* 46 3/11/98 6:14p Jason
* better lighting, again
*
* 45 3/11/98 5:13p Jason
* always draw an object if any point is behind you
*
* 44 3/06/98 3:54p Jason
* fixed bug with satellite lighting indoors
*
* 43 3/06/98 3:23p Jason
* added lighting from satellites to indoor rooms
*
* 42 2/27/98 5:58p Jason
* fixed terrain lighting problem
*
* 41 2/25/98 1:02p Jason
* offset lights and dests by 1/8 of a normal
*
* 40 2/23/98 6:57p Jason
* fixed bug caused by my last rev
*
* 39 2/23/98 6:50p Jason
* changes to help facilitate fast lighting with shadow volumes
*
*
* 38 2/22/98 3:05p Jason
* sped up raycasting by switching the directions rays get shot. This
* allows us to ignore backfaces
*
* 37 2/19/98 5:44p Jason
* made super detail work correctly
*
* 36 2/09/98 3:45p Jason
* changed normal distance offset to be friendly with BSP tree stuff
*
* 35 2/06/98 12:35p Jason
* fixed problem with external rooms
*
* 34 2/05/98 4:20p Matt
* Fixed terrain lighting broken in roomnum/cellnum change
*
* 33 2/04/98 6:23p Matt
* Changed object room number to indicate a terrain cell via a flag. Got
* rid of the object flag which used to indicate terrain.
*
*/
#include <stdlib.h>
#include <memory.h>
#include "radiosity.h"
#include "rad_cast.h"
#include "terrain.h"
#include "room.h"
#include "editor_lighting.h"
#include "findintersection.h"
#include "special_face.h"
#include "bsp.h"
#include "boa.h"
// A ray must contribute at least this amount to be accepted (currently .5%)
#define DEFAULT_IGNORE_LIMIT .005f
float Ignore_limit = DEFAULT_IGNORE_LIMIT;
extern rad_element *rad_MaxElement;
float Current_max_specular_strength;
spectra Current_max_specular_color;
// Returns 1 if a src vector can hit dest vector for a volume point
int ShootRayToVolumePoint(vector *src, vector *dest, int start_room) {
fvi_info hit_info;
fvi_query fq;
if (UseBSP) {
int fate = BSPRayOccluded(src, dest, MineBSP.root);
if (!fate)
return 1;
return 0;
}
// shoot a ray from the light position to the current vertex
fq.p0 = src;
fq.p1 = dest;
fq.startroom = start_room;
fq.rad = 0.0f;
fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_NON_LIGHTMAP_OBJECTS | FQ_NO_RELINK | FQ_IGNORE_RENDER_THROUGH_PORTALS;
fq.thisobjnum = -1;
fq.ignore_obj_list = NULL;
int fate = fvi_FindIntersection(&fq, &hit_info);
float dist = vm_VectorDistance(&hit_info.hit_pnt, dest);
if (dist > .1)
return 0;
return 1;
}
void ClipSatelliteToTerrain(vector *answer, vector *src_vec, vector *dest_vec) {
vector cur_vec = *src_vec;
float terrain_limit = 256 * TERRAIN_SIZE;
float mag, diff;
vector ray;
// Check ceiling
if (cur_vec.y > (MAX_TERRAIN_HEIGHT * 30)) {
ray = cur_vec - *dest_vec;
mag = vm_GetMagnitude(&ray);
ray /= mag;
diff = ((MAX_TERRAIN_HEIGHT * 30) - dest_vec->y) / ray.y;
cur_vec = (*dest_vec + (ray * diff)) - (ray / 4);
}
// Check right edge
if (cur_vec.x > terrain_limit) {
ray = cur_vec - *dest_vec;
mag = vm_GetMagnitude(&ray);
ray /= mag;
diff = (terrain_limit - dest_vec->x) / ray.x;
cur_vec = (*dest_vec + (ray * diff)) - (ray / 4);
}
// Check left edge
if (cur_vec.x < 0) {
ray = cur_vec - *dest_vec;
mag = vm_GetMagnitude(&ray);
ray /= mag;
diff = (-dest_vec->x) / ray.x;
cur_vec = (*dest_vec + (ray * diff)) - (ray / 4);
}
// Check top edge
if (cur_vec.z > terrain_limit) {
ray = cur_vec - *dest_vec;
mag = vm_GetMagnitude(&ray);
ray /= mag;
diff = (terrain_limit - dest_vec->z) / ray.z;
cur_vec = (*dest_vec + (ray * diff)) - (ray / 4);
}
// Check bottom edge
if (cur_vec.z < 0) {
ray = cur_vec - *dest_vec;
mag = vm_GetMagnitude(&ray);
ray /= mag;
diff = (-dest_vec->z) / ray.z;
cur_vec = (*dest_vec + (ray * diff)) - (ray / 4);
}
if (cur_vec.x > 4095)
cur_vec.x = 4095;
if (cur_vec.z > 4095)
cur_vec.z = 4095;
*answer = cur_vec;
}
// Returns 1 if a src vector can hit dest vector unobstructed, else 0
int ShootRayFromPoint(vector *src, vector *dest, rad_surface *src_surf, rad_surface *dest_surf) {
float dist;
bool do_backface = 0;
fvi_info hit_info;
fvi_query fq;
vector temp_src = *src;
vector temp_dest = *dest;
int from_satellite = 0;
// Trivially reject all rooms
if (dest_surf->surface_type == ST_ROOM || dest_surf->surface_type == ST_ROOM_OBJECT) {
if (src_surf->surface_type == ST_ROOM || src_surf->surface_type == ST_ROOM_OBJECT) {
if (!BOA_IsVisible(dest_surf->roomnum, src_surf->roomnum))
return 0;
}
}
if (UseBSP) {
if (dest_surf->surface_type == ST_ROOM || dest_surf->surface_type == ST_ROOM_OBJECT) {
if (src_surf->surface_type == ST_ROOM || src_surf->surface_type == ST_ROOM_OBJECT) {
int fate = BSPRayOccluded(src, dest, MineBSP.root);
if (!fate)
return 1;
return 0;
}
}
}
// If this ray is too high, clip it to the ceiling
if (src_surf->surface_type == ST_SATELLITE) {
if (1 || dest->y >= MAX_TERRAIN_HEIGHT) {
from_satellite = 1;
// swap the src/dest the variables because we now want to shoot from the ground to the satellite
rad_surface *temp_surf;
vector *temp_vec;
temp_surf = src_surf;
src_surf = dest_surf;
dest_surf = temp_surf;
temp_vec = src;
src = dest;
dest = temp_vec;
temp_dest = *dest;
temp_src = *src;
ClipSatelliteToTerrain(&temp_src, src, &temp_dest);
} else {
ClipSatelliteToTerrain(&temp_src, src, &temp_dest);
int src_cell = GetTerrainCellFromPos(&temp_src);
if (src_cell < 0) {
src_cell = 0;
Int3(); // Get Jason, satellite clipped off terrain?
}
src_surf->roomnum = MAKE_ROOMNUM(src_cell);
}
}
// shoot a ray from the light position to the current vertex
fq.p0 = &temp_src;
fq.p1 = &temp_dest;
if (src_surf->surface_type == ST_EXTERNAL_ROOM)
fq.startroom = GetTerrainRoomFromPos(src);
else
fq.startroom = src_surf->roomnum;
fq.rad = 0.0f;
fq.flags = FQ_CHECK_OBJS | FQ_IGNORE_NON_LIGHTMAP_OBJECTS | FQ_NO_RELINK | FQ_IGNORE_RENDER_THROUGH_PORTALS;
fq.thisobjnum = -1;
fq.ignore_obj_list = NULL;
int fate = fvi_FindIntersection(&fq, &hit_info);
if (from_satellite) {
if (fate == HIT_NONE || fate == HIT_OUT_OF_TERRAIN_BOUNDS)
return 1;
else
return 0;
}
dist = vm_VectorDistance(&hit_info.hit_pnt, &temp_dest);
if (dist > .1) {
// mprintf(0,"Didn't hit!\n");
return 0;
}
return 1;
}
int Rays_ignored = 0;
float GetMaxColor(spectra *sp);
float GetFormFactorForElementAndSatellite(rad_surface *dest_surf, rad_element *dest_element, vector *src_center) {
vector dest_center;
vector light_center = *src_center;
float form_factor = 0.0;
if (Ignore_satellites)
return 0;
// Bash src center to first element vertex (if sun or moon)
light_center = rad_MaxSurface->elements[0].verts[0];
for (int j = 0; j < dest_element->num_verts; j++) {
dest_center = dest_element->verts[j];
dest_center += (dest_surf->normal / 32.0);
float temp_factor = 0;
int hit = 0;
vector ray = light_center - dest_center;
if (dest_surf->surface_type != ST_TERRAIN) {
if ((vm_DotProduct(&ray, &dest_surf->normal)) < 0)
continue;
}
// If this surface is a terrain surface, use the terrain speedup table
if (dest_surf->surface_type == ST_TERRAIN) {
ASSERT(ROOMNUM_OUTSIDE(dest_surf->roomnum));
int cellnum = CELLNUM(dest_surf->roomnum);
if (dest_surf->facenum == 0) {
if (j == 1)
cellnum += TERRAIN_WIDTH;
else if (j == 2)
cellnum += (TERRAIN_WIDTH + 1);
} else {
if (j == 1)
cellnum += (TERRAIN_WIDTH + 1);
else if (j == 2)
cellnum++;
}
hit = TerrainLightSpeedup[rad_MaxSurface->roomnum][cellnum];
} else
hit = ShootRayFromPoint(&light_center, &dest_center, rad_MaxSurface, dest_surf);
if (hit) {
float ray_length = vm_GetMagnitude(&ray);
vector dest_norm_ray = ray / ray_length;
vector dest_normal = dest_surf->normal;
float ff;
ff = vm_DotProduct(&dest_normal, &dest_norm_ray);
if (ff > 0)
temp_factor += ff;
}
form_factor += temp_factor;
}
form_factor /= dest_element->num_verts;
if (form_factor > 1)
mprintf(0, "form factor >1!\n");
return form_factor;
}
// Calculates the percentage of specular light that is coming from the
// currently shooting patch
void CheckToUpdateSpecularFace(rad_surface *dest_surf, spectra *color, vector *src_center) {
vector light_center;
float total_strength = (color->r * .3) + (color->g * .33) + (color->b * .33);
float threshold = .7f;
if (dest_surf->surface_type != ST_ROOM)
return;
if (Rooms[dest_surf->roomnum].faces[dest_surf->facenum].special_handle == BAD_SPECIAL_FACE_INDEX)
return;
if (src_center == NULL)
GetCenterOfSurface(rad_MaxSurface, &light_center);
else
light_center = *src_center;
room *rp = &Rooms[dest_surf->roomnum];
face *fp = &rp->faces[dest_surf->facenum];
if (GameTextures[fp->tmap].flags & TF_SMOOTH_SPECULAR)
threshold = .01f;
else {
if (!(rad_MaxSurface->flags & SF_LIGHTSOURCE))
return;
}
int i, t;
for (i = 0; i < 4; i++) {
if (total_strength > threshold &&
total_strength > Room_strongest_value[dest_surf->roomnum][i][dest_surf->facenum]) {
float scalar = (total_strength / 50.0) + .5;
if (scalar > 1)
scalar = 1.0;
int special_index = Rooms[dest_surf->roomnum].faces[dest_surf->facenum].special_handle;
// Move the others down
for (t = 2; t >= i; t--) {
Room_strongest_value[dest_surf->roomnum][t + 1][dest_surf->facenum] =
Room_strongest_value[dest_surf->roomnum][t][dest_surf->facenum];
SpecialFaces[special_index].spec_instance[t + 1].bright_color =
SpecialFaces[special_index].spec_instance[t].bright_color;
SpecialFaces[special_index].spec_instance[t + 1].bright_center =
SpecialFaces[special_index].spec_instance[t].bright_center;
}
// Update our arrays with strongest light and src patch center
Room_strongest_value[dest_surf->roomnum][i][dest_surf->facenum] = total_strength;
float rmax = GetMaxColor(color);
float r = color->r;
float g = color->g;
float b = color->b;
// Normalize colors
if (rmax > 1) {
r /= rmax;
g /= rmax;
b /= rmax;
}
r *= (255.0 * scalar);
g *= (255.0 * scalar);
b *= (255.0 * scalar);
SpecialFaces[special_index].spec_instance[i].bright_color = GR_RGB16((int)r, (int)g, (int)b);
SpecialFaces[special_index].spec_instance[i].bright_center = light_center;
return;
}
}
}
float GetFormFactorForElement(rad_surface *dest_surf, rad_element *dest_element, vector *src_center) {
int touched = 0;
vector dest_center;
int i, limit;
float ray_area;
int ignored = 0;
vector light_center = *src_center;
float form_factor = 0.0;
int multiple_shoots = 0;
ray_area = rad_MaxSurface->area;
float rmax = GetMaxColor(&rad_MaxSurface->exitance);
multiple_shoots = 0;
limit = 1;
for (i = 0; i < limit; i++) {
float temp_factor = 0.0;
if (multiple_shoots && (rad_MaxSurface->elements[i].flags & EF_IGNORE))
continue;
if (multiple_shoots) {
GetCenterOfElement(&rad_MaxSurface->elements[i], &light_center);
light_center += (rad_MaxSurface->normal / 16.0);
ray_area = rad_MaxSurface->elements[i].area;
} else
ray_area = rad_MaxSurface->area;
GetCenterOfElement(dest_element, &dest_center);
dest_center += (dest_surf->normal / 16.0);
int hit = 0;
vector ray = light_center - dest_center;
vector revray = dest_center - light_center;
if ((vm_DotProduct(&ray, &dest_surf->normal)) < 0)
continue;
if ((vm_DotProduct(&revray, &rad_MaxSurface->normal)) < 0)
continue;
// Check to see if this ray even matters
float ray_length = vm_GetMagnitudeFast(&ray);
vector dest_norm_ray = ray / ray_length;
vector src_norm_ray = -dest_norm_ray;
vector dest_normal = dest_surf->normal;
float ff;
vector src_normal = rad_MaxSurface->normal;
ff = (vm_DotProduct(&src_norm_ray, &src_normal) * vm_DotProduct(&dest_normal, &dest_norm_ray)) /
((3.14 * ray_length * ray_length) + ray_area);
ff *= ray_area;
ff *= rmax;
// If shooting from elements, scale the test up
if (multiple_shoots)
ff *= rad_MaxSurface->area / rad_MaxSurface->elements[i].area;
if (ff < Ignore_limit) {
ignored++;
continue;
}
hit = ShootRayFromPoint(&light_center, &dest_center, rad_MaxSurface, dest_surf);
if (hit) {
// We got a hit...figure out how much light contribution this light has
ray_length = vm_GetMagnitude(&ray);
dest_norm_ray = ray / ray_length;
src_norm_ray = -dest_norm_ray;
dest_normal = dest_surf->normal;
src_normal = rad_MaxSurface->normal;
ff = (vm_DotProduct(&src_norm_ray, &src_normal) * vm_DotProduct(&dest_normal, &dest_norm_ray)) /
((3.14 * ray_length * ray_length) + ray_area);
if (ff > 1)
mprintf(0, "ff >1!\n");
if (ff > 0)
temp_factor += ff;
// Update specularity if needed
if (rad_MaxSurface->surface_type == ST_ROOM && Calculate_specular_lighting) {
spectra color;
float scalar = ff;
scalar *= ray_area;
scalar *= 5;
color.r = rad_MaxSurface->exitance.r * scalar;
color.g = rad_MaxSurface->exitance.g * scalar;
color.b = rad_MaxSurface->exitance.b * scalar;
float total_strength = (color.r * .33) + (color.g * .33) + (color.b * .33);
if (total_strength > Current_max_specular_strength) {
Current_max_specular_strength = total_strength;
Current_max_specular_color = color;
}
}
}
temp_factor *= ray_area;
// temp_factor/=dest_element->num_verts;
// if (multiple_shoots)
// temp_factor*=(rad_MaxSurface->elements[i].area/rad_MaxSurface->area);
form_factor += temp_factor;
if (form_factor > 1)
mprintf(0, "form factor >1! val=%f\n", form_factor);
}
if (ignored > 0)
Rays_ignored++;
return form_factor;
}
float GetFormFactorForElementSuperDetail(rad_surface *dest_surf, rad_element *dest_element) {
int touched = 0;
vector dest_center;
float ray_area;
int ignored = 0;
vector light_center;
vector patch_center;
float form_factor = 0.0;
int multiple_shoots = 0;
ray_area = rad_MaxElement->area;
if (rad_MaxElement->flags & EF_IGNORE)
return 0;
float rmax = GetMaxColor(&rad_MaxElement->exitance);
float temp_factor = 0.0;
GetCenterOfSurface(rad_MaxSurface, &patch_center);
GetCenterOfElement(rad_MaxElement, &light_center);
light_center += (rad_MaxSurface->normal / 16.0);
GetCenterOfElement(dest_element, &dest_center);
dest_center += (dest_surf->normal / 16.0);
int hit = 0;
vector ray = light_center - dest_center;
vector revray = dest_center - light_center;
if ((vm_DotProduct(&ray, &dest_surf->normal)) < 0)
return 0;
if ((vm_DotProduct(&revray, &rad_MaxSurface->normal)) < 0)
return 0;
// Check to see if this ray even matters
float ray_length = vm_GetMagnitudeFast(&ray);
vector dest_norm_ray = ray / ray_length;
vector src_norm_ray = -dest_norm_ray;
vector dest_normal = dest_surf->normal;
float ff;
vector src_normal = rad_MaxSurface->normal;
ff = (vm_DotProduct(&src_norm_ray, &src_normal) * vm_DotProduct(&dest_normal, &dest_norm_ray)) /
((3.14 * ray_length * ray_length) + ray_area);
ff *= ray_area;
ff *= rmax;
// If shooting from elements, scale the test up
ff *= rad_MaxSurface->area / rad_MaxElement->area;
if (ff < Ignore_limit) {
Rays_ignored++;
return 0;
}
hit = ShootRayFromPoint(&light_center, &dest_center, rad_MaxSurface, dest_surf);
if (hit) {
// We got a hit...figure out how much light contribution this light has
ray_length = vm_GetMagnitude(&ray);
dest_norm_ray = ray / ray_length;
src_norm_ray = -dest_norm_ray;
dest_normal = dest_surf->normal;
src_normal = rad_MaxSurface->normal;
ff = (vm_DotProduct(&src_norm_ray, &src_normal) * vm_DotProduct(&dest_normal, &dest_norm_ray)) /
((3.14 * ray_length * ray_length) + ray_area);
if (ff > 1)
mprintf(0, "ff >1!\n");
if (ff > 0)
temp_factor += ff;
// Update specularity if needed
if (rad_MaxSurface->surface_type == ST_ROOM && Calculate_specular_lighting) {
spectra color;
float scalar = ff;
scalar *= ray_area;
scalar *= (rad_MaxSurface->area / ray_area);
scalar *= 5;
color.r = rad_MaxSurface->exitance.r * scalar;
color.g = rad_MaxSurface->exitance.g * scalar;
color.b = rad_MaxSurface->exitance.b * scalar;
float total_strength = (color.r * .33) + (color.g * .33) + (color.b * .33);
if (total_strength > Current_max_specular_strength) {
Current_max_specular_strength = total_strength;
Current_max_specular_color = color;
}
}
}
temp_factor *= ray_area;
form_factor += temp_factor;
if (form_factor > 1)
mprintf(0, "form factor >1! val=%f\n", form_factor);
return form_factor;
}
// Calculates the volume lighting for the currently shooting patch
void CalculateVolumeLightsForRay(float total_sphere_dist, vector *src_center) {
float sphere_dist = total_sphere_dist;
int i, t;
// Do volume lighting
if ((rad_MaxSurface->surface_type == ST_ROOM || rad_MaxSurface->surface_type == ST_ROOM_OBJECT)) {
if (sphere_dist > .1) {
fvi_face_room_list facelist[5000];
uint8_t check_room[MAX_VOLUME_ELEMENTS];
memset(check_room, 0, MAX_VOLUME_ELEMENTS);
// Build a list of rooms to check
int num_faces = fvi_QuickDistFaceList(rad_MaxSurface->roomnum, src_center, sphere_dist, facelist, 4000);
check_room[rad_MaxSurface->roomnum] = 1;
for (i = 0; i < num_faces; i++) {
if (Rooms[facelist[i].room_index].flags & RF_EXTERNAL)
continue;
check_room[facelist[i].room_index] = 1;
}
for (int roomnum = 0; roomnum < MAX_VOLUME_ELEMENTS; roomnum++) {
if (Volume_elements[roomnum] == NULL)
continue;
if (check_room[roomnum] == 0)
continue;
if (!BOA_IsVisible(roomnum, rad_MaxSurface->roomnum)) {
continue;
}
int w = Rooms[roomnum].volume_width;
int h = Rooms[roomnum].volume_height;
int d = Rooms[roomnum].volume_depth;
if (0 && !Shoot_from_patch && (rad_MaxSurface->flags & SF_LIGHTSOURCE)) // super detail
{
int src_num_elements = rad_MaxSurface->xresolution * rad_MaxSurface->yresolution;
for (int k = 0; k < src_num_elements; k++) {
vector light_center;
rad_MaxElement = &rad_MaxSurface->elements[k];
if (rad_MaxElement->flags & EF_IGNORE)
continue;
GetCenterOfElement(rad_MaxElement, &light_center);
light_center += (rad_MaxSurface->normal / 4);
sphere_dist = total_sphere_dist;
// sphere_dist*=(rad_MaxElement->area/rad_MaxSurface->area);
for (i = 0; i < d; i++) {
for (t = 0; t < h; t++) {
for (int j = 0; j < w; j++) {
volume_element *ve = &Volume_elements[roomnum][(i * w * h) + (t * w) + j];
if (ve->color.r < 0)
continue;
vector subvec = ve->pos - light_center;
float mag = vm_GetMagnitudeFast(&subvec);
if (mag > sphere_dist)
continue;
subvec /= mag;
int hit;
float scalar = subvec * rad_MaxSurface->normal;
if (scalar < 0)
continue;
scalar /= ((3.14 * mag * mag) + rad_MaxElement->area);
scalar *= rad_MaxElement->area;
// Trivially reject all rooms
if (!BOA_IsVisible(roomnum, rad_MaxSurface->roomnum)) {
hit = 0;
} else {
if (ve->flags & VEF_REVERSE_SHOOT)
hit = ShootRayToVolumePoint(&ve->pos, &light_center, roomnum);
else
hit = ShootRayToVolumePoint(&light_center, &ve->pos, rad_MaxSurface->roomnum);
}
if (hit) {
ve->color.r += rad_MaxElement->exitance.r * scalar;
ve->color.g += rad_MaxElement->exitance.g * scalar;
ve->color.b += rad_MaxElement->exitance.b * scalar;
}
}
}
}
}
} else {
for (i = 0; i < d; i++) {
for (t = 0; t < h; t++) {
for (int j = 0; j < w; j++) {
volume_element *ve = &Volume_elements[roomnum][(i * w * h) + (t * w) + j];
if (ve->color.r < 0)
continue;
vector subvec = ve->pos - *src_center;
float mag = vm_GetMagnitudeFast(&subvec);
if (mag > sphere_dist)
continue;
subvec /= mag;
int hit;
float scalar = subvec * rad_MaxSurface->normal;
if (scalar < 0)
continue;
scalar /= ((3.14 * mag * mag) + rad_MaxSurface->area);
scalar *= rad_MaxSurface->area;
if (!BOA_IsVisible(roomnum, rad_MaxSurface->roomnum)) {
hit = 0;
} else {
if (ve->flags & VEF_REVERSE_SHOOT)
hit = ShootRayToVolumePoint(&ve->pos, src_center, roomnum);
else
hit = ShootRayToVolumePoint(src_center, &ve->pos, rad_MaxSurface->roomnum);
}
if (hit) {
ve->color.r += rad_MaxSurface->exitance.r * scalar;
ve->color.g += rad_MaxSurface->exitance.g * scalar;
ve->color.b += rad_MaxSurface->exitance.b * scalar;
}
}
}
}
}
}
}
}
}
// Calculates the form factors using a raycasting method
void CalculateFormFactorsRaycast() {
int i, t, k;
int ignore;
vector src_center;
float form_factor;
int raycount = 0;
Rays_ignored = 0;
if (rad_MaxSurface->surface_type == ST_SATELLITE) {
src_center = rad_MaxSurface->verts[0];
} else {
GetCenterOfSurface(rad_MaxSurface, &src_center);
src_center += (rad_MaxSurface->normal / 16.0);
}
float sphere_dist = 0;
// Get the max area of influence for this light
if (rad_MaxSurface->surface_type == ST_ROOM || rad_MaxSurface->surface_type == ST_ROOM_OBJECT) {
float rmax = GetMaxColor(&rad_MaxSurface->exitance);
float power = rmax * rad_MaxSurface->area;
float temp_ignore = Ignore_limit / power;
float express = 1.0 / temp_ignore;
express /= 3.14f;
express -= rad_MaxSurface->area;
sphere_dist = sqrt(express);
if (sphere_dist > 0)
fvi_QuickDistFaceList(rad_MaxSurface->roomnum, &src_center, sphere_dist, NULL, rad_NumSurfaces);
}
// Do volume lighting
if (Do_volume_lighting)
CalculateVolumeLightsForRay(sphere_dist, &src_center);
// Shoot this patches light to each element within range
for (i = 0; i < rad_NumSurfaces; i++) {
rad_surface *dest_surf = &rad_Surfaces[i];
Current_max_specular_strength = 0;
Current_max_specular_color.r = 0;
Current_max_specular_color.g = 0;
Current_max_specular_color.b = 0;
// Check for self surface
if (rad_MaxSurface == dest_surf)
ignore = 1;
else
ignore = 0;
if (dest_surf->surface_type == ST_PORTAL)
ignore = 1;
if (dest_surf->surface_type == ST_SATELLITE)
ignore = 1;
if (rad_MaxSurface->surface_type == ST_ROOM || rad_MaxSurface->surface_type == ST_ROOM_OBJECT) {
if (dest_surf->surface_type == ST_ROOM) {
if (!(Rooms[dest_surf->roomnum].faces[dest_surf->facenum].flags & FF_TOUCHED))
ignore = 1;
Rooms[dest_surf->roomnum].faces[dest_surf->facenum].flags &= ~FF_TOUCHED;
}
}
if (dest_surf->surface_type == ST_ROOM)
Rooms[dest_surf->roomnum].faces[dest_surf->facenum].flags &= ~FF_TOUCHED;
int dest_num_elements = dest_surf->xresolution * dest_surf->yresolution;
// Ignore this surface if we're shooting from a satellite and we cant possibly see it
if (rad_MaxSurface->surface_type == ST_SATELLITE) {
if (dest_surf->surface_type == ST_ROOM || dest_surf->surface_type == ST_ROOM_OBJECT) {
if (!dest_surf->flags & SF_TOUCHES_TERRAIN)
ignore = 1;
}
}
if (ignore) {
raycount += dest_num_elements;
continue;
}
for (t = 0; t < dest_num_elements; t++) {
rad_element *dest_element = &dest_surf->elements[t];
if ((raycount % 1000) == 0) {
mprintf_at(2, 4, 0, "Ray=%d ", raycount);
mprintf_at(2, 5, 0, "Ignore=%d ", Rays_ignored);
}
raycount++;
if (ignore)
continue;
if (dest_element->flags & EF_IGNORE)
continue;
if (Shoot_from_patch || rad_MaxSurface->surface_type == ST_SATELLITE ||
!(rad_MaxSurface->flags & SF_LIGHTSOURCE)) {
if (rad_MaxSurface->surface_type == ST_SATELLITE)
form_factor = GetFormFactorForElementAndSatellite(dest_surf, dest_element, &src_center);
else
form_factor = GetFormFactorForElement(dest_surf, dest_element, &src_center);
// Now extract the form factor info out to exitances
if (form_factor > 0) {
spectra shoot = rad_MaxSurface->exitance;
spectra delta;
float reflect_factor = dest_surf->reflectivity;
delta.r = shoot.r * reflect_factor * form_factor;
delta.g = shoot.g * reflect_factor * form_factor;
delta.b = shoot.b * reflect_factor * form_factor;
// update element exitance
dest_element->exitance.r += delta.r;
dest_element->exitance.g += delta.g;
dest_element->exitance.b += delta.b;
dest_surf->exitance.r += ((dest_element->area / dest_surf->area) * delta.r);
dest_surf->exitance.g += ((dest_element->area / dest_surf->area) * delta.g);
dest_surf->exitance.b += ((dest_element->area / dest_surf->area) * delta.b);
}
} else // Do super detail
{
int src_num_elements = rad_MaxSurface->xresolution * rad_MaxSurface->yresolution;
for (k = 0; k < src_num_elements; k++) {
rad_MaxElement = &rad_MaxSurface->elements[k];
if (rad_MaxElement->flags & EF_IGNORE)
continue;
form_factor = GetFormFactorForElementSuperDetail(dest_surf, dest_element);
// Now extract the form factor info out to exitances
if (form_factor > 0) {
spectra shoot = rad_MaxElement->exitance;
spectra delta;
float reflect_factor = dest_surf->reflectivity;
delta.r = shoot.r * reflect_factor * form_factor;
delta.g = shoot.g * reflect_factor * form_factor;
delta.b = shoot.b * reflect_factor * form_factor;
// update element exitance
dest_element->exitance.r += delta.r;
dest_element->exitance.g += delta.g;
dest_element->exitance.b += delta.b;
dest_surf->exitance.r += ((dest_element->area / dest_surf->area) * delta.r);
dest_surf->exitance.g += ((dest_element->area / dest_surf->area) * delta.g);
dest_surf->exitance.b += ((dest_element->area / dest_surf->area) * delta.b);
}
}
}
}
if (rad_MaxSurface->surface_type == ST_ROOM && Calculate_specular_lighting)
CheckToUpdateSpecularFace(dest_surf, &Current_max_specular_color, NULL);
// mprintf_at(2,4,0,"Ray=%d ",raycount);
// mprintf_at(2,5,0,"Ignore=%d ",Rays_ignored);
}
}