static RAD_Model RAD_ReadFile( const char *filename ) // Read radiosity solution model from input file. // The axis-aligned bounding box is computed. { char badFile[] = "Invalid input model file"; // Open input file FILE *fp = fopen( filename, "r" ); if ( fp == NULL ) ShowFatalError( __FILE__, __LINE__, "Cannot open input model file \"%s\"", filename ); RAD_Model m; if ( fscanf( fp, "%d", &(m.numQuads) ) != 1 || m.numQuads < 0 ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\"", badFile, filename ); m.quads = (RAD_Quad *) CheckedMalloc( sizeof(RAD_Quad) * m.numQuads ); float minIntensity = FLT_MAX; float maxIntensity = 0.0f; float max_rgb[3] = { 0.0f, 0.0f, 0.0f }; for ( int q = 0; q < m.numQuads; q++ ) { for ( int i = 0; i < 4; i++ ) { float vert[3], rgb[3]; if ( fscanf( fp, "%f %f %f", &vert[0], &vert[1], &vert[2] ) != 3 ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\"", badFile, filename ); if ( fscanf( fp, "%f %f %f", &rgb[0], &rgb[1], &rgb[2] ) != 3 ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\"", badFile, filename ); CopyArray3( m.quads[q].v[i], vert ); CopyArray3( m.quads[q].rgb[i], rgb ); float intensity = rgb[0] + rgb[1] + rgb[2]; if ( intensity > maxIntensity ) maxIntensity = intensity; if ( intensity < minIntensity ) minIntensity = intensity; if ( rgb[0] > max_rgb[0] ) max_rgb[0] = rgb[0]; if ( rgb[1] > max_rgb[1] ) max_rgb[1] = rgb[1]; if ( rgb[2] > max_rgb[2] ) max_rgb[2] = rgb[2]; } } fclose( fp ); m.minIntensity = minIntensity; m.maxIntensity = maxIntensity; CopyArray3( m.max_rgb, max_rgb ); ComputeBoundingBox( &m ); return m; }
void QM_SurfaceInit( QM_Surface *s ) { if ( s == NULL ) return; CopyArray3( s->reflectivity, ZERO_VEC_3F ); CopyArray3( s->emission, ZERO_VEC_3F ); s->numOrigQuads = 0; s->origQuads = NULL; s->numShooterQuads = 0; s->shooters = NULL; s->numGathererQuads = 0; s->gatherers = NULL; }
void QM_ModelInit( QM_Model *m ) { if ( m == NULL ) return; m->numSurfaces = 0; m->surfaces = NULL; m->totalShooters = 0; m->shooters = NULL; m->totalGatherers = 0; m->gatherers = NULL; CopyArray3( m->min_xyz, ZERO_VEC_3F ); CopyArray3( m->max_xyz, ZERO_VEC_3F ); CopyArray3( m->dim_xyz, ZERO_VEC_3F ); CopyArray3( m->center, ZERO_VEC_3F ); m->radius = 0.0f; }
static GLuint MakeGathererQuadsDisplayList( const QM_Model *m ) { GLuint dlist = glGenLists( 1 ); if ( dlist == 0 ) ShowFatalError( __FILE__, __LINE__, "Cannot create display list" ); glNewList( dlist, GL_COMPILE ); for ( int s = 0; s < m->numSurfaces; s++ ) { float am[4], di[4], sp[4], em[4], shininess = 32.0; CopyArray3( am, m->surfaces[s].reflectivity ); am[3] = 1.0f; CopyArray3( di, m->surfaces[s].reflectivity ); di[3] = 1.0f; CopyArray3( em, m->surfaces[s].emission ); em[3] = 1.0f; sp[0] = sp[1] = sp[2] = sp[3] = 0.5f; glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, am ); glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, di ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, sp ); glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, em ); glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, shininess ); glBegin( GL_QUADS ); for ( int q = 0; q < m->surfaces[s].numGathererQuads; q++ ) { QM_GathererQuad *quad = &(m->surfaces[s].gatherers[q]); glNormal3fv( quad->normal ); glVertex3fv( quad->v[0] ); glVertex3fv( quad->v[1] ); glVertex3fv( quad->v[2] ); glVertex3fv( quad->v[3] ); } glEnd(); } glEndList(); return dlist; }
void QM_ComputeVertexRadiosities( QM_Model *m ) // Compute the radiosities at the vertices by averaging // the radiosities of the quads that use the vertex. { if ( m == NULL || m->numSurfaces <= 0 ) return; for ( int s = 0; s < m->numSurfaces; s++ ) { QM_Surface *surface = &(m->surfaces[s]); for ( int g = 0; g < surface->numGathererQuads; g++ ) { QM_GathererQuad *gatherer = &(surface->gatherers[g]); for ( int i = 0; i < 4; i++ ) { int numQuadsUsingVertex = 0; CopyArray3( gatherer->vRadiosity[i], ZERO_VEC_3F ); for ( int g2 = 0; g2 < surface->numGathererQuads; g2++ ) { QM_GathererQuad *gatherer2 = &(surface->gatherers[g2]); for ( int i2 = 0; i2 < 4; i2++ ) { float sqrDist = VecSqrDist( gatherer->v[i], gatherer2->v[i2] ); if ( sqrDist <= EQUAL_VERTEX_THRESHOLD ) { gatherer->vRadiosity[i][0] += gatherer2->radiosity[0]; gatherer->vRadiosity[i][1] += gatherer2->radiosity[1]; gatherer->vRadiosity[i][2] += gatherer2->radiosity[2]; numQuadsUsingVertex++; } } } gatherer->vRadiosity[i][0] /= numQuadsUsingVertex; gatherer->vRadiosity[i][1] /= numQuadsUsingVertex; gatherer->vRadiosity[i][2] /= numQuadsUsingVertex; } } } }
void QM_Subdivide( QM_Model *m, float maxShooterQuadEdgeLength, float maxGathererQuadEdgeLength ) // Subdivide the original quads in the model to smaller // shooter quads and even-smaller gatherer quads. // Each shooter quad cannot have edge longer than maxShooterQuadEdgeLength, and // each gatherer quad cannot have edge longer than maxGathererQuadEdgeLength. { if ( m == NULL || m->numSurfaces <= 0 ) return; // Subdivide original quads to get shooter quads. int modelTotalShooters = 0; // This will contain the total number of shooter quads in model. for ( int s = 0; s < m->numSurfaces; s++ ) { QM_Surface *surface = &(m->surfaces[s]); // Find longest edge length of all original quads in the current surface. float maxEdgeLen = 0.0f; for ( int q = 0; q < surface->numOrigQuads; q++ ) { QM_OrigQuad *origQuad = &(surface->origQuads[q]); float edgeLen; edgeLen = VecDist( origQuad->v[0], origQuad->v[1] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; edgeLen = VecDist( origQuad->v[1], origQuad->v[2] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; edgeLen = VecDist( origQuad->v[2], origQuad->v[3] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; edgeLen = VecDist( origQuad->v[3], origQuad->v[0] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; } // Compute how many regular segments to divide the longest edge into so that // every resulting segment is not longer than maxShooterQuadEdgeLength. int numSegments = (int) ceil( maxEdgeLen / maxShooterQuadEdgeLength ); // Each original quad on the current surface is going to be subdivided into // (numSegments*numSegments) shooter quads. // Therefore, the total number of shooter quads for this surface is... surface->numShooterQuads = surface->numOrigQuads * numSegments * numSegments; surface->shooters = (QM_ShooterQuad *) CheckedMalloc( sizeof(QM_ShooterQuad) * surface->numShooterQuads ); int surfShootersCount = 0; // This will contain the number of shooters in this surface. for ( int q = 0; q < surface->numOrigQuads; q++ ) { QM_OrigQuad *origQuad = &(surface->origQuads[q]); for ( int y = 0; y < numSegments; y++ ) for ( int x = 0; x < numSegments; x++ ) { float newv[4][3]; QuadBilinearInterpolate( newv[0], (float) x / numSegments, (float) y / numSegments, origQuad->v ); QuadBilinearInterpolate( newv[1], (float) (x+1) / numSegments, (float) y / numSegments, origQuad->v ); QuadBilinearInterpolate( newv[2], (float) (x+1) / numSegments, (float) (y+1) / numSegments, origQuad->v ); QuadBilinearInterpolate( newv[3], (float) x / numSegments, (float) (y+1) / numSegments, origQuad->v ); QM_ShooterQuad *shooterQuad = &(surface->shooters[ surfShootersCount ]); for ( int i = 0; i < 4; i++ ) CopyArray3( shooterQuad->v[i], newv[i] ); QuadCentroid( shooterQuad->centroid, shooterQuad->v ); CopyArray3( shooterQuad->normal, origQuad->normal ); shooterQuad->area = QuadArea( shooterQuad->v ); // Initialize the unshot power of the shooter quad. shooterQuad->unshotPower[0] = surface->emission[0] * shooterQuad->area; shooterQuad->unshotPower[1] = surface->emission[1] * shooterQuad->area; shooterQuad->unshotPower[2] = surface->emission[2] * shooterQuad->area; shooterQuad->surface = surface; surfShootersCount++; modelTotalShooters++; } } } // Build an array of pointers to all the shooters in the model. m->totalShooters = modelTotalShooters; m->shooters = (QM_ShooterQuad **) CheckedMalloc( sizeof(QM_ShooterQuad *) * modelTotalShooters ); int modelTotalShootersCount = 0; for ( int s = 0; s < m->numSurfaces; s++ ) for ( int q = 0; q < m->surfaces[s].numShooterQuads; q++ ) { m->shooters[ modelTotalShootersCount ] = &(m->surfaces[s].shooters[q]); modelTotalShootersCount++; } // Subdivide shooter quads to get gatherer quads. int modelTotalGatherers = 0; // This will contain the total number of gatherers quads in model. for ( int s = 0; s < m->numSurfaces; s++ ) { QM_Surface *surface = &(m->surfaces[s]); // Find longest edge length of all shooter quads in the current surface. float maxEdgeLen = 0.0f; for ( int q = 0; q < surface->numShooterQuads; q++ ) { QM_ShooterQuad *shooterQuad = &(surface->shooters[q]); float edgeLen; edgeLen = VecDist( shooterQuad->v[0], shooterQuad->v[1] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; edgeLen = VecDist( shooterQuad->v[1], shooterQuad->v[2] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; edgeLen = VecDist( shooterQuad->v[2], shooterQuad->v[3] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; edgeLen = VecDist( shooterQuad->v[3], shooterQuad->v[0] ); if ( edgeLen > maxEdgeLen ) maxEdgeLen = edgeLen; } // Compute how many regular segments to divide the longest edge into so that // every resulting segment is not longer than maxGathererQuadEdgeLength. int numSegments = (int) ceil( maxEdgeLen / maxGathererQuadEdgeLength ); // Each shooter quad on the current surface is going to be subdivided into // (numSegments*numSegments) gatherer quads. // Therefore, the total number of gatherer quads for this surface is... surface->numGathererQuads = surface->numShooterQuads * numSegments * numSegments; surface->gatherers = (QM_GathererQuad *) CheckedMalloc( sizeof(QM_GathererQuad) * surface->numGathererQuads ); int surfGatherersCount = 0; // This will contain the number of gatherers in this surface. for ( int q = 0; q < surface->numShooterQuads; q++ ) { QM_ShooterQuad *shooterQuad = &(surface->shooters[q]); for ( int y = 0; y < numSegments; y++ ) for ( int x = 0; x < numSegments; x++ ) { float newv[4][3]; QuadBilinearInterpolate( newv[0], (float) x / numSegments, (float) y / numSegments, shooterQuad->v ); QuadBilinearInterpolate( newv[1], (float) (x+1) / numSegments, (float) y / numSegments, shooterQuad->v ); QuadBilinearInterpolate( newv[2], (float) (x+1) / numSegments, (float) (y+1) / numSegments, shooterQuad->v ); QuadBilinearInterpolate( newv[3], (float) x / numSegments, (float) (y+1) / numSegments, shooterQuad->v ); QM_GathererQuad *gathererQuad = &(surface->gatherers[ surfGatherersCount ]); for ( int i = 0; i < 4; i++ ) CopyArray3( gathererQuad->v[i], newv[i] ); CopyArray3( gathererQuad->normal, shooterQuad->normal ); gathererQuad->area = QuadArea( gathererQuad->v ); // Initialize the radiosity of the gatherer quad. gathererQuad->radiosity[0] = surface->emission[0]; gathererQuad->radiosity[1] = surface->emission[1]; gathererQuad->radiosity[2] = surface->emission[2]; CopyArray3( gathererQuad->vRadiosity[0], ZERO_VEC_3F ); CopyArray3( gathererQuad->vRadiosity[1], ZERO_VEC_3F ); CopyArray3( gathererQuad->vRadiosity[2], ZERO_VEC_3F ); CopyArray3( gathererQuad->vRadiosity[3], ZERO_VEC_3F ); gathererQuad->shooter = shooterQuad; gathererQuad->surface = surface; surfGatherersCount++; modelTotalGatherers++; } } } // Build an array of pointers to all the gatherers in the model. m->totalGatherers = modelTotalGatherers; m->gatherers = (QM_GathererQuad **) CheckedMalloc( sizeof(QM_GathererQuad *) * modelTotalGatherers ); int modelTotalGatherersCount = 0; for ( int s = 0; s < m->numSurfaces; s++ ) for ( int q = 0; q < m->surfaces[s].numGathererQuads; q++ ) { m->gatherers[ modelTotalGatherersCount ] = &(m->surfaces[s].gatherers[q]); modelTotalGatherersCount++; } return; }
QM_Model QM_ReadFile( const char *filename ) // Read model from input file. // The output QM_Model has only QM_OrigQuad. // The axis-aligned bounding box is computed. { char badFile[] = "Invalid input model file"; char badEOF[] = "Unexpected end of file"; char lineBuf[ MAX_LINE_LEN + 1 ]; // Open input file FILE *fp = fopen( filename, "r" ); if ( fp == NULL ) ShowFatalError( __FILE__, __LINE__, "Cannot open input model file \"%s\"", filename ); int lineNum = 0; //=== VERTICES === int numVertices = 0; float *vertexTable = NULL; // An array of 3D vertices. // Read number of vertices. if ( !ReadDataLine( lineBuf, &lineNum, filename, fp ) ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\"", badEOF, filename ); if ( sscanf( lineBuf, "%d", &numVertices ) != 1 || numVertices < 0 ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\" at line %d", badFile, filename, lineNum ); vertexTable = (float *) CheckedMalloc( sizeof(float) * 3 * numVertices ); // Read the vertices. for ( int v = 0; v < numVertices; v++ ) { float x, y, z; if ( !ReadDataLine( lineBuf, &lineNum, filename, fp ) ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\"", badEOF, filename ); if ( sscanf( lineBuf, "%f %f %f", &x, &y, &z ) != 3 ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\" at line %d", badFile, filename, lineNum ); vertexTable[ 3 * v + 0 ] = x; vertexTable[ 3 * v + 1 ] = y; vertexTable[ 3 * v + 2 ] = z; } //=== MATERIALS === int numMaterials = 0; float *reflectivityTable = NULL; // An array of RGB reflectivity values. float *emissionTable = NULL; // An array of RGB emission values. // Read number of materials. if ( !ReadDataLine( lineBuf, &lineNum, filename, fp ) ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\"", badEOF, filename ); if ( sscanf( lineBuf, "%d", &numMaterials ) != 1 || numMaterials < 0 ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\" at line %d", badFile, filename, lineNum ); reflectivityTable = (float *) CheckedMalloc( sizeof(float) * 3 * numMaterials ); emissionTable = (float *) CheckedMalloc( sizeof(float) * 3 * numMaterials ); // Read the materials. for ( int m = 0; m < numMaterials; m++ ) { float r, g, b; if ( !ReadDataLine( lineBuf, &lineNum, filename, fp ) ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\"", badEOF, filename ); if ( sscanf( lineBuf, "%f %f %f", &r, &g, &b ) != 3 ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\" at line %d", badFile, filename, lineNum ); reflectivityTable[ 3 * m + 0 ] = r; reflectivityTable[ 3 * m + 1 ] = g; reflectivityTable[ 3 * m + 2 ] = b; if ( !ReadDataLine( lineBuf, &lineNum, filename, fp ) ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\"", badEOF, filename ); if ( sscanf( lineBuf, "%f %f %f", &r, &g, &b ) != 3 ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\" at line %d", badFile, filename, lineNum ); emissionTable[ 3 * m + 0 ] = r; emissionTable[ 3 * m + 1 ] = g; emissionTable[ 3 * m + 2 ] = b; } //=== SURFACES === int numSurfaces = 0; QM_Surface *surfaceTable = NULL; // An array of surfaces. // Read number of surfaces. if ( !ReadDataLine( lineBuf, &lineNum, filename, fp ) ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\"", badEOF, filename ); if ( sscanf( lineBuf, "%d", &numSurfaces ) != 1 || numSurfaces < 0 ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\" at line %d", badFile, filename, lineNum ); surfaceTable = (QM_Surface *) CheckedMalloc( sizeof(QM_Surface) * numSurfaces ); // Read the surfaces. for ( int s = 0; s < numSurfaces; s++ ) { QM_SurfaceInit( &surfaceTable[s] ); int matID, numQuads; // Read material index. if ( !ReadDataLine( lineBuf, &lineNum, filename, fp ) ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\"", badEOF, filename ); if ( sscanf( lineBuf, "%d", &matID ) != 1 || matID < 0 || matID >= numMaterials ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\" at line %d", badFile, filename, lineNum ); CopyArray3( surfaceTable[s].reflectivity, &reflectivityTable[3*matID] ); CopyArray3( surfaceTable[s].emission, &emissionTable[3*matID] ); // Read number of quadrilaterals in the surface. if ( !ReadDataLine( lineBuf, &lineNum, filename, fp ) ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\"", badEOF, filename ); if ( sscanf( lineBuf, "%d", &numQuads ) != 1 || numQuads < 0 ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\" at line %d", badFile, filename, lineNum ); surfaceTable[s].numOrigQuads = numQuads; surfaceTable[s].origQuads = (QM_OrigQuad *) CheckedMalloc( sizeof(QM_OrigQuad) * numQuads ); // Read vertex indices for each quadrilateral. for ( int q = 0; q < numQuads; q++ ) { int vertID[4]; if ( !ReadDataLine( lineBuf, &lineNum, filename, fp ) ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\"", badEOF, filename ); if ( sscanf( lineBuf, "%d %d %d %d", &vertID[0], &vertID[1], &vertID[2], &vertID[3] ) != 4 || vertID[0] < 0 || vertID[0] >= numVertices || vertID[1] < 0 || vertID[1] >= numVertices || vertID[2] < 0 || vertID[2] >= numVertices || vertID[3] < 0 || vertID[3] >= numVertices ) ShowFatalError( __FILE__, __LINE__, "%s \"%s\" at line %d", badFile, filename, lineNum ); CopyArray3( surfaceTable[s].origQuads[q].v[0], &vertexTable[ 3*vertID[0] ] ); CopyArray3( surfaceTable[s].origQuads[q].v[1], &vertexTable[ 3*vertID[1] ] ); CopyArray3( surfaceTable[s].origQuads[q].v[2], &vertexTable[ 3*vertID[2] ] ); CopyArray3( surfaceTable[s].origQuads[q].v[3], &vertexTable[ 3*vertID[3] ] ); // Compute normal vector to the quadrilateral. VecTriNormal( surfaceTable[s].origQuads[q].normal, surfaceTable[s].origQuads[q].v[0], surfaceTable[s].origQuads[q].v[1], surfaceTable[s].origQuads[q].v[2] ); VecNormalize( surfaceTable[s].origQuads[q].normal, surfaceTable[s].origQuads[q].normal ); } } QM_Model model = QM_ModelInit(); model.numSurfaces = numSurfaces; model.surfaces = surfaceTable; ComputeBoundingBox( &model ); fclose( fp ); free( vertexTable ); free( reflectivityTable ); free( emissionTable ); return model; }