+ /* start with entirely random samples */
+ for(i = 0; i < minimap.samples; ++i)
+ {
+ minimap.sample_offsets[2*i+0] = Random();
+ minimap.sample_offsets[2*i+1] = Random();
+ }
+
+ for(i = 0; i < 1000; ++i)
+ {
+ if(MiniMapEvaluateSampleOffsets(&j, &k, &val))
+ {
+ sx = minimap.sample_offsets[2*j+0];
+ sy = minimap.sample_offsets[2*j+1];
+ minimap.sample_offsets[2*j+0] = rx = Random();
+ minimap.sample_offsets[2*j+1] = ry = Random();
+ if(!MiniMapEvaluateSampleOffsets(&jj, &kk, &valj))
+ valj = -1;
+ minimap.sample_offsets[2*j+0] = sx;
+ minimap.sample_offsets[2*j+1] = sy;
+
+ sx = minimap.sample_offsets[2*k+0];
+ sy = minimap.sample_offsets[2*k+1];
+ minimap.sample_offsets[2*k+0] = rx;
+ minimap.sample_offsets[2*k+1] = ry;
+ if(!MiniMapEvaluateSampleOffsets(&jj, &kk, &valk))
+ valk = -1;
+ minimap.sample_offsets[2*k+0] = sx;
+ minimap.sample_offsets[2*k+1] = sy;
+
+ if(valj > valk)
+ {
+ if(valj > val)
+ {
+ /* valj is the greatest */
+ minimap.sample_offsets[2*j+0] = rx;
+ minimap.sample_offsets[2*j+1] = ry;
+ i = -1;
+ }
+ else
+ {
+ /* valj is the greater and it is useless - forget it */
+ }
+ }
+ else
+ {
+ if(valk > val)
+ {
+ /* valk is the greatest */
+ minimap.sample_offsets[2*k+0] = rx;
+ minimap.sample_offsets[2*k+1] = ry;
+ i = -1;
+ }
+ else
+ {
+ /* valk is the greater and it is useless - forget it */
+ }
+ }
+ }
+ else
+ break;
+ }
+}
+
+void MergeRelativePath(char *out, const char *absolute, const char *relative)
+{
+ const char *endpos = absolute + strlen(absolute);
+ while(endpos != absolute && (endpos[-1] == '/' || endpos[-1] == '\\'))
+ --endpos;
+ while(relative[0] == '.' && relative[1] == '.' && (relative[2] == '/' || relative[2] == '\\'))
+ {
+ relative += 3;
+ while(endpos != absolute)
+ {
+ --endpos;
+ if(*endpos == '/' || *endpos == '\\')
+ break;
+ }
+ while(endpos != absolute && (endpos[-1] == '/' || endpos[-1] == '\\'))
+ --endpos;
+ }
+ memcpy(out, absolute, endpos - absolute);
+ out[endpos - absolute] = '/';
+ strcpy(out + (endpos - absolute + 1), relative);
+}
+
+int MiniMapBSPMain( int argc, char **argv )
+{
+ char minimapFilename[1024];
+ char basename[1024];
+ char path[1024];
+ char relativeMinimapFilename[1024];
+ qboolean autolevel;
+ float minimapSharpen;
+ float border;
+ byte *data4b, *p;
+ float *q;
+ int x, y;
+ int i;
+ miniMapMode_t mode;
+ vec3_t mins, maxs;
+ qboolean keepaspect;
+
+ /* arg checking */
+ if( argc < 2 )
+ {
+ Sys_Printf( "Usage: q3map [-v] -minimap [-size n] [-sharpen f] [-samples n | -random n] [-o filename.tga] [-minmax Xmin Ymin Zmin Xmax Ymax Zmax] <mapname>\n" );
+ return 0;
+ }
+
+ /* load the BSP first */
+ strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
+ StripExtension( source );
+ DefaultExtension( source, ".bsp" );
+ Sys_Printf( "Loading %s\n", source );
+ BeginMapShaderFile( source );
+ LoadShaderInfo();
+ LoadBSPFile( source );
+
+ minimap.model = &bspModels[0];
+ VectorCopy(minimap.model->mins, mins);
+ VectorCopy(minimap.model->maxs, maxs);
+
+ *minimapFilename = 0;
+ minimapSharpen = game->miniMapSharpen;
+ minimap.width = minimap.height = game->miniMapSize;
+ border = game->miniMapBorder;
+ keepaspect = game->miniMapKeepAspect;
+ mode = game->miniMapMode;
+
+ autolevel = qfalse;
+ minimap.samples = 1;
+ minimap.sample_offsets = NULL;
+ minimap.boost = 1.0;
+ minimap.brightness = 0.0;
+ minimap.contrast = 1.0;
+
+ /* process arguments */
+ for( i = 1; i < (argc - 1); i++ )
+ {
+ if( !strcmp( argv[ i ], "-size" ) )
+ {
+ minimap.width = minimap.height = atoi(argv[i + 1]);
+ i++;
+ Sys_Printf( "Image size set to %i\n", minimap.width );
+ }
+ else if( !strcmp( argv[ i ], "-sharpen" ) )
+ {
+ minimapSharpen = atof(argv[i + 1]);
+ i++;
+ Sys_Printf( "Sharpening coefficient set to %f\n", minimapSharpen );
+ }
+ else if( !strcmp( argv[ i ], "-samples" ) )
+ {
+ minimap.samples = atoi(argv[i + 1]);
+ i++;
+ Sys_Printf( "Samples set to %i\n", minimap.samples );
+ if(minimap.sample_offsets)
+ free(minimap.sample_offsets);
+ minimap.sample_offsets = malloc(2 * sizeof(*minimap.sample_offsets) * minimap.samples);
+ MiniMapMakeSampleOffsets();
+ }
+ else if( !strcmp( argv[ i ], "-random" ) )
+ {
+ minimap.samples = atoi(argv[i + 1]);
+ i++;
+ Sys_Printf( "Random samples set to %i\n", minimap.samples );
+ if(minimap.sample_offsets)
+ free(minimap.sample_offsets);
+ minimap.sample_offsets = NULL;
+ }
+ else if( !strcmp( argv[ i ], "-border" ) )
+ {
+ border = atof(argv[i + 1]);
+ i++;
+ Sys_Printf( "Border set to %f\n", border );
+ }
+ else if( !strcmp( argv[ i ], "-keepaspect" ) )
+ {
+ keepaspect = qtrue;
+ Sys_Printf( "Keeping aspect ratio by letterboxing\n", border );
+ }
+ else if( !strcmp( argv[ i ], "-nokeepaspect" ) )
+ {
+ keepaspect = qfalse;
+ Sys_Printf( "Not keeping aspect ratio\n", border );
+ }
+ else if( !strcmp( argv[ i ], "-o" ) )
+ {
+ strcpy(minimapFilename, argv[i + 1]);
+ i++;
+ Sys_Printf( "Output file name set to %s\n", minimapFilename );
+ }
+ else if( !strcmp( argv[ i ], "-minmax" ) && i < (argc - 7) )
+ {
+ mins[0] = atof(argv[i + 1]);
+ mins[1] = atof(argv[i + 2]);
+ mins[2] = atof(argv[i + 3]);
+ maxs[0] = atof(argv[i + 4]);
+ maxs[1] = atof(argv[i + 5]);
+ maxs[2] = atof(argv[i + 6]);
+ i += 6;
+ Sys_Printf( "Map mins/maxs overridden\n" );
+ }
+ else if( !strcmp( argv[ i ], "-gray" ) )
+ {
+ mode = MINIMAP_MODE_GRAY;
+ Sys_Printf( "Writing as white-on-black image\n" );
+ }
+ else if( !strcmp( argv[ i ], "-black" ) )
+ {
+ mode = MINIMAP_MODE_BLACK;
+ Sys_Printf( "Writing as black alpha image\n" );
+ }
+ else if( !strcmp( argv[ i ], "-white" ) )
+ {
+ mode = MINIMAP_MODE_WHITE;
+ Sys_Printf( "Writing as white alpha image\n" );
+ }
+ else if( !strcmp( argv[ i ], "-boost" ) && i < (argc - 2) )
+ {
+ minimap.boost = atof(argv[i + 1]);
+ i++;
+ Sys_Printf( "Contrast boost set to %f\n", minimap.boost );
+ }
+ else if( !strcmp( argv[ i ], "-brightness" ) && i < (argc - 2) )
+ {
+ minimap.brightness = atof(argv[i + 1]);
+ i++;
+ Sys_Printf( "Brightness set to %f\n", minimap.brightness );
+ }
+ else if( !strcmp( argv[ i ], "-contrast" ) && i < (argc - 2) )
+ {
+ minimap.contrast = atof(argv[i + 1]);
+ i++;
+ Sys_Printf( "Contrast set to %f\n", minimap.contrast );
+ }
+ else if( !strcmp( argv[ i ], "-autolevel" ) )
+ {
+ autolevel = qtrue;
+ Sys_Printf( "Auto level enabled\n", border );
+ }
+ else if( !strcmp( argv[ i ], "-noautolevel" ) )
+ {
+ autolevel = qfalse;
+ Sys_Printf( "Auto level disabled\n", border );
+ }
+ }
+
+ MiniMapMakeMinsMaxs(mins, maxs, border, keepaspect);
+
+ if(!*minimapFilename)
+ {
+ ExtractFileBase(source, basename);
+ ExtractFilePath(source, path);
+ sprintf(relativeMinimapFilename, game->miniMapNameFormat, basename);
+ MergeRelativePath(minimapFilename, path, relativeMinimapFilename);
+ Sys_Printf("Output file name automatically set to %s\n", minimapFilename);
+ }
+ ExtractFilePath(minimapFilename, path);
+ Q_mkdir(path);
+
+ if(minimapSharpen >= 0)
+ {
+ minimap.sharpen_centermult = 8 * minimapSharpen + 1;
+ minimap.sharpen_boxmult = -minimapSharpen;
+ }
+
+ minimap.data1f = safe_malloc(minimap.width * minimap.height * sizeof(*minimap.data1f));
+ data4b = safe_malloc(minimap.width * minimap.height * 4);
+ if(minimapSharpen >= 0)
+ minimap.sharpendata1f = safe_malloc(minimap.width * minimap.height * sizeof(*minimap.data1f));
+
+ MiniMapSetupBrushes();
+
+ if(minimap.samples <= 1)
+ {
+ Sys_Printf( "\n--- MiniMapNoSupersampling (%d) ---\n", minimap.height );
+ RunThreadsOnIndividual(minimap.height, qtrue, MiniMapNoSupersampling);
+ }
+ else
+ {
+ if(minimap.sample_offsets)
+ {
+ Sys_Printf( "\n--- MiniMapSupersampled (%d) ---\n", minimap.height );
+ RunThreadsOnIndividual(minimap.height, qtrue, MiniMapSupersampled);
+ }
+ else
+ {
+ Sys_Printf( "\n--- MiniMapRandomlySupersampled (%d) ---\n", minimap.height );
+ RunThreadsOnIndividual(minimap.height, qtrue, MiniMapRandomlySupersampled);
+ }
+ }
+
+ if(minimap.boost != 1.0)
+ {
+ Sys_Printf( "\n--- MiniMapContrastBoost (%d) ---\n", minimap.height );
+ RunThreadsOnIndividual(minimap.height, qtrue, MiniMapContrastBoost);
+ }
+
+ if(autolevel)
+ {
+ Sys_Printf( "\n--- MiniMapAutoLevel (%d) ---\n", minimap.height );
+ float mi = 1, ma = 0;
+ float s, o;
+
+ // TODO threads!
+ q = minimap.data1f;
+ for(y = 0; y < minimap.height; ++y)
+ for(x = 0; x < minimap.width; ++x)
+ {
+ float v = *q++;
+ if(v < mi)
+ mi = v;
+ if(v > ma)
+ ma = v;
+ }
+ if(ma > mi)
+ {
+ s = 1 / (ma - mi);
+ o = mi / (ma - mi);
+
+ // equations:
+ // brightness + contrast * v
+ // after autolevel:
+ // brightness + contrast * (v * s - o)
+ // =
+ // (brightness - contrast * o) + (contrast * s) * v
+ minimap.brightness = minimap.brightness - minimap.contrast * o;
+ minimap.contrast *= s;
+
+ Sys_Printf( "Auto level: Brightness changed to %f\n", minimap.brightness );
+ Sys_Printf( "Auto level: Contrast changed to %f\n", minimap.contrast );
+ }
+ else
+ Sys_Printf( "Auto level: failed because all pixels are the same value\n" );
+ }
+
+ if(minimap.brightness != 0 || minimap.contrast != 1)
+ {
+ Sys_Printf( "\n--- MiniMapBrightnessContrast (%d) ---\n", minimap.height );
+ RunThreadsOnIndividual(minimap.height, qtrue, MiniMapBrightnessContrast);
+ }
+
+ if(minimap.sharpendata1f)
+ {
+ Sys_Printf( "\n--- MiniMapSharpen (%d) ---\n", minimap.height );
+ RunThreadsOnIndividual(minimap.height, qtrue, MiniMapSharpen);
+ q = minimap.sharpendata1f;
+ }
+ else
+ {
+ q = minimap.data1f;
+ }
+
+ Sys_Printf( "\nConverting...");
+
+ switch(mode)
+ {
+ case MINIMAP_MODE_GRAY:
+ p = data4b;
+ for(y = 0; y < minimap.height; ++y)
+ for(x = 0; x < minimap.width; ++x)
+ {
+ byte b;
+ float v = *q++;
+ if(v < 0) v = 0;
+ if(v > 255.0/256.0) v = 255.0/256.0;
+ b = v * 256;
+ *p++ = b;
+ }
+ Sys_Printf( " writing to %s...", minimapFilename );
+ WriteTGAGray(minimapFilename, data4b, minimap.width, minimap.height);
+ break;
+ case MINIMAP_MODE_BLACK:
+ p = data4b;
+ for(y = 0; y < minimap.height; ++y)
+ for(x = 0; x < minimap.width; ++x)
+ {
+ byte b;
+ float v = *q++;
+ if(v < 0) v = 0;
+ if(v > 255.0/256.0) v = 255.0/256.0;
+ b = v * 256;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = b;
+ }
+ Sys_Printf( " writing to %s...", minimapFilename );
+ WriteTGA(minimapFilename, data4b, minimap.width, minimap.height);
+ break;
+ case MINIMAP_MODE_WHITE:
+ p = data4b;
+ for(y = 0; y < minimap.height; ++y)
+ for(x = 0; x < minimap.width; ++x)
+ {
+ byte b;
+ float v = *q++;
+ if(v < 0) v = 0;
+ if(v > 255.0/256.0) v = 255.0/256.0;
+ b = v * 256;
+ *p++ = 255;
+ *p++ = 255;
+ *p++ = 255;
+ *p++ = b;
+ }
+ Sys_Printf( " writing to %s...", minimapFilename );
+ WriteTGA(minimapFilename, data4b, minimap.width, minimap.height);
+ break;
+ }
+
+ Sys_Printf( " done.\n" );
+
+ /* return to sender */
+ return 0;
+}
+
+
+
+
+
+/*
+MD4BlockChecksum()
+calculates an md4 checksum for a block of data
+*/
+
+static int MD4BlockChecksum( void *buffer, int length )
+{
+ return Com_BlockChecksum(buffer, length);
+}