/* * 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 . */ // 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 #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;ixresolution*rad_MaxSurface->yresolution; for (en=0;enelements[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;tsurface_type==ST_PORTAL) ignore=1; for (j=0;jxresolution*surf->yresolution;j++,ff_index++) { if (ignore) continue; rad_element *ep=&surf->elements[j]; if (ep->flags & EF_IGNORE) continue; for (k=0;knum_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;knum_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;ixresolution*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= min (rad_FormFactors[ff_index],1.0); else rff= (float) min(rad_FormFactors[ff_index] * rad_MaxSurface->area / dest_element->area,1.0); // 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;inormal; // 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;iyresolution*surf->xresolution;t++,ff_index++) { surfval[ff_index]=i; } } for (i=0;i=0 && id<=rad_NumElements); if (triangulate) { if (nv > 3) { g3Point *tripoints[3]; tripoints[0] = pointlist[0]; tripoints[2] = pointlist[1]; for (i=0;ip3_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 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=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++; } } } } }