/* GenSurf plugin for GtkRadiant Copyright (C) 2001 David Hyde, Loki software and qeradiant.com This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define SINGLE #ifdef SINGLE #define REAL float #else /* not SINGLE */ #define REAL double #endif /* not SINGLE */ #include #include #include #include "gensurf.h" #include "triangle.h" typedef struct { float error; int node; } TRITABLE; double dh, dv; int NVP1; #define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) void MakeDecimatedMap(int *NumNodes, int *NumTris, NODE **pNode, TRI **pTri) { int compare(TRITABLE *, TRITABLE *); int Bisect(NODE *, int, int, int); void CalcAngles(NODE *, int *, float *); void EdgeOnSide(int *, int *, int *); int tricall(int, NODE *, int *, TRI **, TRI **, char *); int CheckBorders(int *,int,NODE *,int *,TRI **); float biggesterror; int i, j, N; int j0, j1, j2; int NumNodesToSave; int NumNodesUsed; NODE *Node; TRI *Tri; TRITABLE *TriTable; if(Decimate <= 0) return; /* ghCursorCurrent = LoadCursor(NULL,IDC_WAIT); SetCursor(ghCursorCurrent); */ dh = (Hur-Hll)/NH; dv = (Vur-Vll)/NV; NVP1 = NV+1; NumNodes[0] = (NH+1)*(NVP1); *pNode = (NODE *) malloc(NumNodes[0] * sizeof(NODE)); Node = *pNode; memset(Node,0,NumNodes[0]*sizeof(NODE)); // Copy [NH][NV] vertex array to our working node array for(i=0,N=0; i<=NH; i++) { for(j=0; j<=NV; j++, N++) { Node[N].p[0] = (float)xyz[i][j].p[0]; Node[N].p[1] = (float)xyz[i][j].p[1]; Node[N].p[2] = (float)xyz[i][j].p[2]; Node[N].fixed = xyz[i][j].fixed; } } // Start things off with the corner values Node[ 0].used = 1; Node[NV].used = 1; Node[NH*NVP1].used = 1; Node[NH*NVP1+NV].used = 1; NumNodesUsed = 4; tricall(NumNodes[0], Node, NumTris, NULL, pTri, "cnzBNPY"); Tri = *pTri; // Which coordinates are we triangulating on? switch(Plane) { case PLANE_XZ0: case PLANE_XZ1: j0 = 1; j1 = 0; j2 = 2; break; case PLANE_YZ0: case PLANE_YZ1: j0 = 0; j1 = 1; j2 = 2; break; default: j0 = 2; j1 = 0; j2 = 1; } // TriTable stores the largest error in a triangle and the node where that // error occurs TriTable = (TRITABLE *) malloc(NH*NV*2 * sizeof(TRITABLE)); NumNodesToSave = min(NumNodes[0], (int)(0.01*(100-Decimate)*(NumNodes[0]-NumNodesUsed)+NumNodesUsed)); while(NumNodesUsed < NumNodesToSave) { for(i=0; i TriTable[Node[i].tri].error) { TriTable[Node[i].tri].error = (float)(Absolute(Node[i].error)); TriTable[Node[i].tri].node = i; } } qsort( (void *)TriTable, (size_t)(NumTris[0]), sizeof(TRITABLE), (int (*)(const void *, const void *))compare ); for(i=0; i 0.5*biggesterror; i++) { if(Node[TriTable[i].node].used) continue; // shouldn't happen NumNodesUsed++; Node[TriTable[i].node].used++; } free(Tri); tricall(NumNodes[0], Node, NumTris, NULL, pTri, "cnzBNPY"); Tri = *pTri; // Sliver-check along borders. Since borders are often linear, the errors // along borders will often be zero, so no new points will be added. This // tends to produce long, thin brushes. For all border triangles, check // that minimum angle isn't less than SLIVER_ANGLE. If it is, add another // vertex. while(CheckBorders(&NumNodesUsed,NumNodes[0],Node,NumTris,pTri) > 0) { } Tri = *pTri; } } free(TriTable); // One last time (because we're pessimistic), check border triangles // CheckBorders(&NumNodesUsed,NumNodes[0],Node,NumTris,pTri); // Tri = *pTri; // Check that all fixed points are exact. If not, add them to the mix. // First check to see if we have any fixed points that aren't already used. for(i=0, N=0; i 0.5) { NumNodesUsed++; Node[i].used++; free(Tri); tricall(NumNodes[0], Node, NumTris, NULL, pTri, "cnzBNPY"); Tri = *pTri; } } } // Swap node orders for surfaces facing down, north or west so that // they are counterclockwise when facing the surface if((Plane == PLANE_XY1) || (Plane == PLANE_XZ0) || (Plane == PLANE_YZ1) ) { for(i=0; i= R) && (v[1] >= R) ) { edge[0] = 0; border[0] = 1; } if( (v[1] >= R) && (v[2] >= R) ) { edge[0] = 1; border[0] = 1; } if( (v[2] >= R) && (v[0] >= R) ) { edge[0] = 2; border[0] = 1; } if(border[0] >= 0) { k0 = edge[0]; k1 = (k0+1) % 3; N = Absolute(v[k0] - v[k1]); Ndv = (float)(N*dv); } if( ((v[0] % NVP1) == 0) && ((v[1] % NVP1) == 0) ) { if(border[0] >= 0) if( Ndv > (Absolute(v[0] - v[1])*dh)) return; edge[0] = 0; border[0] = 2; return; } if( ((v[1] % NVP1) == 0) && ((v[2] % NVP1) == 0) ) { if(border[0] >= 0) if( Ndv > (Absolute(v[1] - v[2])*dh)) return; edge[0] = 1; border[0] = 2; return; } if( ((v[2] % NVP1) == 0) && ((v[0] % NVP1) == 0) ) { if(border[0] >= 0) if( Ndv > (Absolute(v[2] - v[0])*dh)) return; edge[0] = 2; border[0] = 2; return; } if( ((v[0] % NVP1) == NV) && ((v[1] % NVP1) == NV) ) { if(border[0] >= 0) if( Ndv > (Absolute(v[0] - v[1])*dh)) return; edge[0] = 0; border[0] = 3; return; } if( ((v[1] % NVP1) == NV) && ((v[2] % NVP1) == NV) ) { if(border[0] >= 0) if( Ndv > (Absolute(v[1] - v[2])*dh)) return; edge[0] = 1; border[0] = 3; return; } if( ((v[2] % NVP1) == NV) && ((v[0] % NVP1) == NV) ) { if(border[0] >= 0) if( Ndv > (Absolute(v[2] - v[0])*dh)) return; edge[0] = 2; border[0] = 3; return; } return; } void CalcAngles(NODE *node, int *v, float *angle) { int i, j, k; vec l; vec x0, x1, x2, y0, y1, y2; vec2 vv[3]; vec dot; switch(Plane) { case PLANE_XZ0: case PLANE_XZ1: i = 0; j = 2; break; case PLANE_YZ0: case PLANE_YZ1: i = 1; j = 2; break; default: i = 0; j = 1; } x0 = node[v[0]].p[i]; x1 = node[v[1]].p[i]; x2 = node[v[2]].p[i]; y0 = node[v[0]].p[j]; y1 = node[v[1]].p[j]; y2 = node[v[2]].p[j]; vv[0][0] = x1-x0; vv[0][1] = y1-y0; vv[1][0] = x2-x1; vv[1][1] = y2-y1; vv[2][0] = x0-x2; vv[2][1] = y0-y2; for(k=0; k<3; k++) { l = (vec)(sqrt( vv[k][0]*vv[k][0] + vv[k][1]*vv[k][1] )); if(l > 0.) { vv[k][0] /= l; vv[k][1] /= l; } } dot = -(vv[0][0]*vv[2][0] + vv[0][1]*vv[2][1]); angle[0] = (float)(acos(dot)); dot = -(vv[1][0]*vv[0][0] + vv[1][1]*vv[0][1]); angle[1] = (float)(acos(dot)); dot = -(vv[2][0]*vv[1][0] + vv[2][1]*vv[1][1]); angle[2] = (float)(acos(dot)); } //================================================================= int Bisect(NODE *node, int border, int j0, int j1) { int k; switch(border) { case 0: k = (j0+j1)/2; break; case 1: k = (j0+j1)/2; break; case 2: k = (int)((j0+j1)/(2*NVP1)) * NVP1; break; case 3: k = (int)((j0+j1+2)/(2*NVP1)) * NVP1 - 1; break; } return( ((k != j0) && (k != j1)) ? k : 0 ); } //================================================================= int compare(TRITABLE *t1, TRITABLE *t2) { if(t1->error > t2->error) return -1; if(t1->error < t2->error) return 1; return 0; } void MakeBrushes(int NumTris, NODE *Node, TRI *Tri,bool surf, int offset,char *texture0, char *texture1, char *texture2) { extern double backface; BRUSH brush; int contents; int i, j; float Steep; vec3_t PlaneNormal,SurfNormal; bool CheckAngle; vec3_t t[2]; // if texture2 is identical to texture0, there's no need to // check surface angle if(!g_strcasecmp(texture0,texture2) || !strlen(texture2)) CheckAngle = FALSE; else { CheckAngle = TRUE; Steep = (float)cos((double)SlantAngle/57.2957795); switch(Plane) { case PLANE_XY0: PlaneNormal[0]= 0.;PlaneNormal[1]= 0.;PlaneNormal[2]= 1.;break; case PLANE_XY1: PlaneNormal[0]= 0.;PlaneNormal[1]= 0.;PlaneNormal[2]=-1.;break; case PLANE_XZ0: PlaneNormal[0]= 0.;PlaneNormal[1]= 1.;PlaneNormal[2]= 1.;break; case PLANE_XZ1: PlaneNormal[0]= 0.;PlaneNormal[1]=-1.;PlaneNormal[2]= 1.;break; case PLANE_YZ0: PlaneNormal[0]= 1.;PlaneNormal[1]= 0.;PlaneNormal[2]= 1.;break; case PLANE_YZ1: PlaneNormal[0]=-1.;PlaneNormal[1]= 0.;PlaneNormal[2]= 1.;break; } } contents = 0; if(surf) { if(UseDetail) contents += CONTENTS_DETAIL; if(UseLadder) contents += CONTENTS_LADDER; } OpenFuncGroup(); for(i=0; i= max(Node[q[0]].p[j1],Node[q[2]].p[j1])) continue; if(Tri[k].min[j2] >= max(Node[q[0]].p[j2],Node[q[2]].p[j2])) continue; if(Tri[k].max[j1] <= min(Node[q[0]].p[j1],Node[q[2]].p[j1])) continue; if(Tri[k].max[j2] <= min(Node[q[0]].p[j2],Node[q[2]].p[j2])) continue; for(h0=0; h0<4 && OK; h0++) { h1 = (h0+1)%4; for(t=0; t<3 && OK; t++) { s[t] = side(Node[q[h0]].p[j1],Node[q[h0]].p[j2], Node[q[h1]].p[j1],Node[q[h1]].p[j2], Node[Tri[k].v[t]].p[j1],Node[Tri[k].v[t]].p[j2]); } if((s[1] > 0 || s[2] > 0) && s[0] < 0) OK=0; if((s[2] > 0 || s[0] > 0) && s[1] < 0) OK=0; if((s[0] > 0 || s[1] > 0) && s[2] < 0) OK=0; } } if(!OK) continue; switch(Plane) { case PLANE_XZ0: case PLANE_XZ1: // front brush.face[0].v[0][0] = Node[q[2]].p[0]; brush.face[0].v[0][1] = (float)front; brush.face[0].v[0][2] = Node[q[2]].p[2]; brush.face[0].v[1][0] = Node[q[1]].p[0]; brush.face[0].v[1][1] = (float)front; brush.face[0].v[1][2] = Node[q[1]].p[2]; brush.face[0].v[2][0] = Node[q[0]].p[0]; brush.face[0].v[2][1] = (float)front; brush.face[0].v[2][2] = Node[q[0]].p[2]; // back brush.face[1].v[0][0] = Node[q[0]].p[0]; brush.face[1].v[0][1] = (float)backface; brush.face[1].v[0][2] = Node[q[0]].p[2]; brush.face[1].v[1][0] = Node[q[1]].p[0]; brush.face[1].v[1][1] = (float)backface; brush.face[1].v[1][2] = Node[q[1]].p[2]; brush.face[1].v[2][0] = Node[q[2]].p[0]; brush.face[1].v[2][1] = (float)backface; brush.face[1].v[2][2] = Node[q[2]].p[2]; for(k0=0; k0= 0) { if(!Node[j].used) // Shouldn't be used, but... { NumNodesUsed[0]++; Node[j].used++; } } } } if(NumNodesUsed[0] > N) { free(*pTri); tricall(NumNodes, Node, NumTris, NULL, pTri, "cnzBNPY"); Tri = *pTri; } return (NumNodesUsed[0] - N); }