@ -39,27 +39,20 @@ static float computeFaceArea(const world_t* world, const face_t* face)
{
const plane_t * plane = & world - > planes [ face - > plane_id ] ;
// Construct a tangent and bitangent for the plane using the face's first vertex
int edgeIdx = world - > edgeList [ face - > ledge_id ] ;
unsigned short vertIndex = edgeIdx > 0 ?
world - > edges [ edgeIdx ] . vertex0 :
world - > edges [ - edgeIdx ] . vertex1 ;
const vertex_t * vertex = & world - > vertices [ vertIndex ] ;
Vec3 refPoint = plane - > normal * plane - > dist ;
Vec3 tangent = ( vertex - > toVec ( ) - refPoint ) . normalized ( ) ;
Vec3 bitangent = plane - > normal . crossProduct ( tangent ) ;
// Construct a tangent and bitangent for the plane
Vec3 tangent , bitangent ;
plane - > getTangents ( tangent , bitangent ) ;
// Project all face vertices onto the face's plane
BoundBox bounds ;
for ( int edgeListIdx = 0 ; edgeListIdx < face - > ledge_num ; + + edgeListIdx )
{
edgeIdx = world - > edgeList [ face - > ledge_id + edgeListIdx ] ;
vertIndex = edgeIdx > 0 ?
int edgeIdx = world - > edgeList [ face - > ledge_id + edgeListIdx ] ;
int vertIndex = edgeIdx > 0 ?
world - > edges [ edgeIdx ] . vertex0 :
world - > edges [ - edgeIdx ] . vertex1 ;
vertex_t * vertex = & world - > vertices [ vertIndex ] ;
const vertex_t * vertex = & world - > vertices [ vertIndex ] ;
Vec3 vec = vertex - > toVec ( ) ;
double x = tangent . dotProduct ( vec ) ;
@ -71,6 +64,8 @@ static float computeFaceArea(const world_t* world, const face_t* face)
return extents . x * extents . y ;
}
typedef std : : unordered_map < const face_t * , std : : vector < Tesselator : : Polygon > > FacePolygons ;
int process_faces ( const world_t * world , const std : : vector < ps1bsp_texture_t > & textures )
{
// Write some data to a file
@ -83,32 +78,6 @@ int process_faces(const world_t* world, const std::vector<ps1bsp_texture_t>& tex
outHeader . version = 1 ;
fwrite ( & outHeader , sizeof ( ps1bsp_header_t ) , 1 , fbsp ) ;
auto edgeData = analyze_edges ( world ) ;
// Convert vertex data (no vertex splitting yet)
std : : vector < ps1bsp_vertex_t > outVertices ;
for ( unsigned short i = 0 ; i < world - > numVertices ; + + i )
{
vertex_t * inVertex = & world - > vertices [ i ] ;
ps1bsp_vertex_t outVertex = { 0 } ;
// Ensure we don't overflow 16-bit short values. Most Quake maps will stay within these bounds so it *should* be fine (for now).
if ( inVertex - > X > - 8192 & & inVertex - > X < 8192 & & inVertex - > Y > - 8192 & & inVertex - > Y < 8192 & & inVertex - > Z > - 8192 & & inVertex - > Z < 8192 )
{
outVertex . x = ( short ) ( inVertex - > X * 4 ) ;
outVertex . y = ( short ) ( inVertex - > Y * 4 ) ;
outVertex . z = ( short ) ( inVertex - > Z * 4 ) ;
}
else
{
printf ( " Error: vertices found outside of acceptable range: (%f, %f, %f) \n " , inVertex - > X , inVertex - > Y , inVertex - > Z ) ;
fclose ( fbsp ) ;
return 0 ;
}
outVertices . push_back ( outVertex ) ;
}
Tesselator tesselator ( world ) ;
// Convert faces defined by edges into faces defined by vertex indices
@ -117,20 +86,15 @@ int process_faces(const world_t* world, const std::vector<ps1bsp_texture_t>& tex
FaceBounds faceBounds ;
for ( int faceIdx = 0 ; faceIdx < world - > numFaces ; + + faceIdx )
{
face_t * face = & world - > faces [ faceIdx ] ;
const face_t * face = & world - > faces [ faceIdx ] ;
const texinfo_t * texinfo = & world - > texInfos [ face - > texinfo_id ] ;
const miptex_t * miptex = & world - > miptexes [ texinfo - > texture_id ] ;
const ps1bsp_texture_t & ps1tex = textures [ texinfo - > texture_id ] ;
ps1bsp_face_t outFace = { 0 } ;
outFace . planeId = face - > plane_id ;
outFace . side = face - > side ;
outFace . side = ( unsigned char ) face - > side ;
outFace . firstFaceVertex = ( unsigned short ) outFaceVertices . size ( ) ;
outFace . textureId = ( unsigned char ) texinfo - > texture_id ;
double minS = DBL_MAX , minT = DBL_MAX ;
double maxS = DBL_MIN , maxT = DBL_MIN ;
// Traverse the list of face edges to collect all of the face's vertices
Vec3 vertexSum ;
BoundBox bounds ;
@ -145,12 +109,6 @@ int process_faces(const world_t* world, const std::vector<ps1bsp_texture_t>& tex
const vertex_t * vertex = & world - > vertices [ vertIndex ] ;
Vec3 vertexPoint = vertex - > toVec ( ) ;
// Calculate texture UV bounds
double s = ( vertexPoint . dotProduct ( texinfo - > vectorS ) + texinfo - > distS ) / miptex - > width ;
double t = ( vertexPoint . dotProduct ( texinfo - > vectorT ) + texinfo - > distT ) / miptex - > height ;
if ( s > maxS ) maxS = s ; if ( s < minS ) minS = s ;
if ( t > maxT ) maxT = t ; if ( t < minT ) minT = t ;
// Calculate bounding box of this face
if ( edgeListIdx = = 0 )
bounds . init ( vertexPoint ) ;
@ -159,41 +117,11 @@ int process_faces(const world_t* world, const std::vector<ps1bsp_texture_t>& tex
// Sum all vertices to calculate an average center point
vertexSum = vertexSum + vertexPoint ;
}
// If the texture doesn't tile, we don't need to correct the UVs as much
double sRange = maxS - minS ;
double tRange = maxT - minT ;
if ( sRange < 1 ) sRange = 1 ;
if ( tRange < 1 ) tRange = 1 ;
// Go over the edges again to fudge some UVs for the vertices (this second pass is only necessary because we don't have texture tiling yet)
for ( int edgeListIdx = 0 ; edgeListIdx < face - > ledge_num ; + + edgeListIdx )
{
int edgeIdx = world - > edgeList [ face - > ledge_id + edgeListIdx ] ;
unsigned short vertIndex = edgeIdx > 0 ?
world - > edges [ edgeIdx ] . vertex0 :
world - > edges [ - edgeIdx ] . vertex1 ;
const vertex_t * vertex = & world - > vertices [ vertIndex ] ;
Vec3 vertexPoint = vertex - > toVec ( ) ;
ps1bsp_facevertex_t faceVertex = { 0 } ;
faceVertex . index = vertIndex ;
faceVertex . light = 0 ;
// Calculate texture UVs
double s = ( vertexPoint . dotProduct ( texinfo - > vectorS ) + texinfo - > distS ) / miptex - > width ;
double t = ( vertexPoint . dotProduct ( texinfo - > vectorT ) + texinfo - > distT ) / miptex - > height ;
if ( minS < 0 | | maxS > 1 ) s = ( s - minS ) / sRange ;
if ( minT < 0 | | maxT > 1 ) t = ( t - minT ) / tRange ;
// Rescale the UVs to the dimensions of the mipmap we've selected for our texture atlas
faceVertex . u = ( unsigned char ) ( s * ( ps1tex . w - 1 ) ) + ps1tex . uoffs ;
faceVertex . v = ( unsigned char ) ( t * ( ps1tex . h - 1 ) ) + ps1tex . voffs ;
outFaceVertices . push_back ( faceVertex ) ;
// TODO: face vertex indices will have to be updated to match the tesselator's vertex list
//ps1bsp_facevertex_t faceVertex = { 0 };
//faceVertex.index = vertIndex;
//outFaceVertices.push_back(faceVertex);
}
faceBounds [ face ] = bounds ;
@ -203,30 +131,84 @@ int process_faces(const world_t* world, const std::vector<ps1bsp_texture_t>& tex
// export_lightmap(world, face, bounds, faceIdx);
outFace . numFaceVertices = ( unsigned char ) ( outFaceVertices . size ( ) - outFace . firstFaceVertex ) ;
outFace . center = convertWorldPosition ( vertexSum / outFace . numFaceVertices ) ;
outFace . center = convertWorldPosition ( vertexSum / face - > ledge_num ) ;
float area = computeFaceArea ( world , face ) ;
outFace . center . pad = ( short ) ( sqrt ( area ) ) ;
outFaces . push_back ( outFace ) ;
auto tessPolys = tesselator . tesselateFace ( face ) ;
}
std : : vector < ps1bsp_surfvertex_t > outSurfVertices ;
std : : vector < ps1bsp_polygon_t > outPolygons ;
// Iterate over all faces again; now that we know the bounds of each face, we can calculate lighting for all of them
for ( int faceIdx = 0 ; faceIdx < world - > numFaces ; + + faceIdx )
{
face_t * face = & world - > faces [ faceIdx ] ;
ps1bsp_face_t & outFace = outFaces [ faceIdx ] ;
ps1bsp_face_t * outFace = & outFaces [ faceIdx ] ;
const texinfo_t * texinfo = & world - > texInfos [ face - > texinfo_id ] ;
const miptex_t * miptex = & world - > miptexes [ texinfo - > texture_id ] ;
const ps1bsp_texture_t & ps1tex = textures [ texinfo - > texture_id ] ;
auto polygons = tesselator . tesselateFace ( face ) ;
outFace - > firstPolygon = ( unsigned short ) outPolygons . size ( ) ;
for ( auto polyIter = polygons . begin ( ) ; polyIter ! = polygons . end ( ) ; + + polyIter )
{
ps1bsp_polygon_t outPoly = { 0 } ;
outPoly . firstPolyVertex = ( unsigned short ) outSurfVertices . size ( ) ;
// Sample lightmap contribution of this face on each vertex
for ( size_t faceVertIdx = 0 ; faceVertIdx < outFace . numFaceVertices ; + + faceVertIdx )
for ( auto polyVertIter = polyIter - > polyVertices . begin ( ) ; polyVertIter ! = polyIter - > polyVertices . end ( ) ; + + polyVertIter )
{
ps1bsp_facevertex_t & faceVertex = outFaceVertices [ outFace . firstFaceVertex + faceVertIdx ] ;
const vertex_t * vertex = & world - > vertices [ faceVertex . index ] ;
faceVertex . light = compute_faceVertex_light5 ( world , face , faceBounds , vertex - > toVec ( ) ) ;
faceVertex . light = ( short ) ( ( float ) faceVertex . light * 1.5f ) ; // Compromise between overbright and non-overbright lighting. Looks good in practice.
if ( faceVertex . light > 255 )
faceVertex . light = 255 ;
size_t vertIndex = polyVertIter - > vertexIndex ;
Vec3 normalizedUV = polyVertIter - > normalizedUV ;
Vec3 vertex = tesselator . getVertices ( ) [ vertIndex ] ;
ps1bsp_surfvertex_t surfVert = { 0 } ;
surfVert . index = ( unsigned short ) vertIndex ;
surfVert . u = ( unsigned char ) ( normalizedUV . x * ( ps1tex . w - 1 ) ) + ps1tex . uoffs ;
surfVert . v = ( unsigned char ) ( normalizedUV . y * ( ps1tex . h - 1 ) ) + ps1tex . voffs ;
int light = compute_faceVertex_light5 ( world , face , faceBounds , vertex ) ;
light = ( int ) ( ( float ) light * 1.5f ) ; // Compromise between overbright and non-overbright lighting. Looks good in practice.
if ( light > 255 )
light = 255 ;
surfVert . light = ( unsigned short ) light ;
outSurfVertices . push_back ( surfVert ) ;
}
outPoly . numPolyVertices = ( unsigned short ) ( outSurfVertices . size ( ) - outPoly . firstPolyVertex ) ;
outPolygons . push_back ( outPoly ) ;
}
outFace - > numPolygons = ( unsigned char ) ( outPolygons . size ( ) - outFace - > firstPolygon ) ;
}
// Convert vertex data
const auto & inVertices = tesselator . getVertices ( ) ;
std : : vector < ps1bsp_vertex_t > outVertices ;
for ( auto vertIter = inVertices . begin ( ) ; vertIter ! = inVertices . end ( ) ; + + vertIter )
{
const Vec3 & inVertex = * vertIter ;
ps1bsp_vertex_t outVertex = { 0 } ;
// Ensure we don't overflow 16-bit short values. Most Quake maps will stay within these bounds so it *should* be fine (for now).
if ( inVertex . x > - 8192 & & inVertex . x < 8192 & & inVertex . y > - 8192 & & inVertex . y < 8192 & & inVertex . z > - 8192 & & inVertex . z < 8192 )
{
outVertex . x = ( short ) ( inVertex . x * 4 ) ;
outVertex . y = ( short ) ( inVertex . y * 4 ) ;
outVertex . z = ( short ) ( inVertex . z * 4 ) ;
}
else
{
printf ( " Error: vertices found outside of acceptable range: (%f, %f, %f) \n " , inVertex . x , inVertex . y , inVertex . z ) ;
fclose ( fbsp ) ;
return 0 ;
}
outVertices . push_back ( outVertex ) ;
}
// Convert planes
@ -283,6 +265,8 @@ int process_faces(const world_t* world, const std::vector<ps1bsp_texture_t>& tex
// Write collected data to file and update header info
writeMapData ( textures , outHeader . textures , fbsp ) ;
writeMapData ( outVertices , outHeader . vertices , fbsp ) ;
writeMapData ( outSurfVertices , outHeader . surfVertices , fbsp ) ;
writeMapData ( outPolygons , outHeader . polygons , fbsp ) ;
writeMapData ( outFaces , outHeader . faces , fbsp ) ;
writeMapData ( outFaceVertices , outHeader . faceVertices , fbsp ) ;
writeMapData ( outPlanes , outHeader . planes , fbsp ) ;