mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 19:55:23 +00:00
1112 lines
32 KiB
C++
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);
|
|
}
|
|
}
|