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