speed-dreams/src/tools/trackgen/main.cpp

445 lines
14 KiB
C++

/***************************************************************************
file : trackgen.cpp
created : Sat Dec 23 09:27:43 CET 2000
copyright : (C) 2000 by Eric Espie
email : torcs@free.fr
version : $Id$
***************************************************************************/
/***************************************************************************
* *
* 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 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/** @file
@author <a href=mailto:torcs@free.fr>Eric Espie</a>
@version $Id$
*/
#include <cstdio>
#include <cctype>
#include <sstream>
#ifndef WIN32
#include <unistd.h>
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef WIN32
#include <getopt.h>
#endif
#ifdef WIN32
#ifndef HAVE_CONFIG_H
#define HAVE_CONFIG_H
#endif
#endif
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tgf.hpp>
#include <portability.h>
#include <itrackloader.h>
#include "ac3d.h"
#include "easymesh.h"
#include "objects.h"
#include "elevation.h"
#include "trackgen.h"
#include "util.h"
class Application : public GfApplication
{
//! Command line options.
int HeightSteps;
bool Bump;
bool Raceline;
bool UseBorder;
std::string TrackName;
std::string TrackCategory;
bool TrackOnly;
bool JustCalculate;
bool MergeAll;
bool MergeTerrain;
int DoSaveElevation;
bool Bridge;
bool DumpTrack;
bool MultipleMaterials;
std::string TrackXMLFilePath; // Full path to track XML input file to read from (mainly for unittests)
std::string TrackACFilePath; // Full path to track AC output file to write into (mainly for unittests)
public:
//! Constructor.
Application();
//! Initialization.
virtual void initialize(bool bLoggingEnabled, int argc = 0, char **argv = 0);
//! Parse the command line options.
bool parseOptions();
//! Generate the track.
int generate();
};
//! Constructor.
Application::Application()
: GfApplication("TrackGen", "1.6.0.41", "Terrain generator for tracks")
, HeightSteps(30)
, Bump(false)
, Raceline(false)
, UseBorder(true)
, TrackOnly(true)
, JustCalculate(false)
, MergeAll(true)
, MergeTerrain(true)
, DoSaveElevation(-1)
, Bridge(true)
, DumpTrack(false)
, MultipleMaterials(false)
{
}
void Application::initialize(bool bLoggingEnabled, int argc, char **argv)
{
// Base initialization first.
GfApplication::initialize(bLoggingEnabled, argc, argv);
// Specific options.
registerOption("c", "category", /* bHasValue = */ true);
registerOption("n", "name", /* bHasValue = */ true);
registerOption("b", "bump", /* bHasValue = */ false);
registerOption("r", "raceline", /* bHasValue = */ false);
registerOption("B", "noborder", /* bHasValue = */ false);
registerOption("a", "all", /* bHasValue = */ false);
registerOption("z", "calc", /* bHasValue = */ false);
registerOption("s", "split", /* bHasValue = */ false);
registerOption("S", "splitall", /* bHasValue = */ false);
registerOption("E", "saveelev", /* bHasValue = */ true);
registerOption("H", "height4", /* bHasValue = */ true);
registerOption("nb", "nobridge", /* bHasValue = */ false);
registerOption("dt", "dumptrack", /* bHasValue = */ false);
registerOption("i", "inpath", /* bHasValue = */ true);
registerOption("o", "outpath", /* bHasValue = */ true);
registerOption("m", "materials", /* bHasValue = */ false);
// Help on specific options.
addOptionsHelpSyntaxLine("-c|--category <cat> -n|--name <name> [-b|--bump] [-r|--raceline] [-B|--noborder] [-nb|--nobridge] [-m|--materials]");
addOptionsHelpSyntaxLine("[-a|--all] [-z|--calc] [-s|--split] [-S|--splitall]");
addOptionsHelpSyntaxLine("[-E|--saveelev <#ef> [-H|--height4 <#hs>]] [-dt|--dumptrack]");
addOptionsHelpSyntaxLine("[-i xml_path] [-o ac_path]");
addOptionsHelpExplainLine("<cat> : track category (road, speedway, dirt...)");
addOptionsHelpExplainLine("<name> : track name");
addOptionsHelpExplainLine("bump : draw bump track");
addOptionsHelpExplainLine("raceline : draw raceline track\n");
addOptionsHelpExplainLine("noborder : don't use terrain border "
"(relief supplied int clockwise, ext CC)");
addOptionsHelpExplainLine("all : draw all (default is track only)");
addOptionsHelpExplainLine("calc : only calculate track parameters and exit");
addOptionsHelpExplainLine("split : split the track and the terrain");
addOptionsHelpExplainLine("splitall : split all");
addOptionsHelpExplainLine("<#ef> : # of the elevation file to save");
addOptionsHelpExplainLine(" 0: all elevation files");
addOptionsHelpExplainLine(" 1: elevation file of terrain + track");
addOptionsHelpExplainLine(" 2: elevation file of terrain with track white");
addOptionsHelpExplainLine(" 3: track only");
addOptionsHelpExplainLine(" 4: track elevations with height steps");
addOptionsHelpExplainLine("<#hs> : nb of height steps for 4th elevation file [30]");
addOptionsHelpExplainLine("materials : mulitple materials");
addOptionsHelpExplainLine("dumptrack : dump track segments to file name.dump");
}
// Parse the command line options.
bool Application::parseOptions()
{
// Parse command line for registered options, and interpret standard ones.
if (!GfApplication::parseOptions())
return false;
std::list<Option>::const_iterator itOpt;
for (itOpt = _lstOptions.begin(); itOpt != _lstOptions.end(); ++itOpt)
{
// Not found in the command line => ignore / default value.
if (!itOpt->bFound)
continue;
if (itOpt->strLongName == "all")
{
TrackOnly = false;
}
else if (itOpt->strLongName == "calc")
{
JustCalculate = true;
}
else if (itOpt->strLongName == "bump")
{
Bump = true;
}
else if (itOpt->strLongName == "raceline")
{
Raceline = true;
}
else if (itOpt->strLongName == "split")
{
MergeAll = false;
MergeTerrain = true;
}
else if (itOpt->strLongName == "splitall")
{
MergeAll = false;
MergeTerrain = false;
}
else if (itOpt->strLongName == "noborder")
{
UseBorder = false;
}
else if (itOpt->strLongName == "name")
{
TrackName = itOpt->strValue;
}
else if (itOpt->strLongName == "saveelev")
{
DoSaveElevation = strtol(itOpt->strValue.c_str(), nullptr, 0);
TrackOnly = false;
}
else if (itOpt->strLongName == "category")
{
TrackCategory = itOpt->strValue;
}
else if (itOpt->strLongName == "height4")
{
HeightSteps = strtol(itOpt->strValue.c_str(), nullptr, 0);
}
else if (itOpt->strLongName == "nobridge")
{
Bridge = false;
}
else if (itOpt->strLongName == "dumptrack")
{
DumpTrack = true;
}
else if (itOpt->strLongName == "inpath")
{
TrackXMLFilePath = itOpt->strValue;
}
else if (itOpt->strLongName == "outpath")
{
TrackACFilePath = itOpt->strValue;
}
else if (itOpt->strLongName == "materials")
{
MultipleMaterials = true;
}
}
if (TrackName.empty() || TrackCategory.empty())
{
printUsage("No track name or category specified");
return false;
}
return true;
}
int Application::generate()
{
ssgAddTextureFormat(".rgb", ssgLoadSGI);
ssgAddTextureFormat(".rgba", ssgLoadSGI);
ssgAddTextureFormat(".int", ssgLoadSGI);
ssgAddTextureFormat(".inta", ssgLoadSGI);
ssgAddTextureFormat(".bw", ssgLoadSGI);
ssgAddTextureFormat(".png", loadPngTexture);
ssgAddTextureFormat(".jpg", loadJpegTexture);
// Get the trackgen paramaters.
void *CfgHandle = GfParmReadFile(CFG_FILE, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
// Load and initialize the track loader module.
GfLogInfo("Loading Track Loader ...\n");
std::ostringstream ossModLibName;
ossModLibName << GfLibDir() << "modules/track/" << "trackv1" << DLLEXT;
GfModule *pmodTrkLoader = GfModule::load(ossModLibName.str());
if (!pmodTrkLoader) {
GfLogError("Cannot find %s\n", ossModLibName.str().c_str());
GfParmReleaseHandle(CfgHandle);
return EXIT_FAILURE;
}
// Check that it implements ITrackLoader.
ITrackLoader* PiTrackLoader = pmodTrkLoader->getInterface<ITrackLoader>();
if (!PiTrackLoader) {
GfLogError("Cannot find ITrackLoader interface\n");
GfParmReleaseHandle(CfgHandle);
return EXIT_FAILURE;
}
// This is the track definition.
char trackdef[1024];
if (TrackXMLFilePath.empty())
snprintf(trackdef, sizeof(trackdef), "%stracks/%s/%s/%s.xml", GfDataDir(), TrackCategory.c_str(), TrackName.c_str(), TrackName.c_str());
else
snprintf(trackdef, sizeof(trackdef), "%s/%s.xml", TrackXMLFilePath.c_str(), TrackName.c_str());
void *TrackHandle = GfParmReadFile(trackdef, GFPARM_RMODE_STD);
if (!TrackHandle) {
GfLogError("Cannot find %s\n", trackdef);
GfParmReleaseHandle(CfgHandle);
return EXIT_FAILURE;
}
// Build the track structure with graphic extensions.
tTrack *Track = PiTrackLoader->load(trackdef, true);
if (JustCalculate) {
CalculateTrack(Track, TrackHandle, Bump, Raceline, Bridge);
GfParmReleaseHandle(CfgHandle);
GfParmReleaseHandle(TrackHandle);
return EXIT_SUCCESS;
}
// dump the track segments to a file for debugging
if (DumpTrack)
dumpTrackSegs(Track);
// Get the output file radix.
char buf2[1024];
if (TrackACFilePath.empty())
snprintf(buf2, sizeof(buf2), "%stracks/%s/%s/%s", GfDataDir(), Track->category, Track->internalname, Track->internalname);
else
snprintf(buf2, sizeof(buf2), "%s/%s", TrackACFilePath.c_str(), Track->internalname);
std::string OutputFileName(buf2);
// Number of groups for the complete track.
Ac3d allAc3d;
allAc3d.addDefaultMaterial();
bool all = false;
if (TrackOnly) {
// Track.
if (!Bump && !Raceline)
all = true;
} else if (MergeAll) {
// track + terrain + objects.
all = true;
}
// Main Track.
const char *extName;
if (Bump) {
extName = "trk-bump";
} else if (Raceline) {
extName = "trk-raceline";
} else {
extName = "trk";
}
sprintf(buf2, "%s-%s.ac", OutputFileName.c_str(), extName);
std::string OutTrackName(buf2);
GenerateTrack(Track, TrackHandle, OutTrackName, allAc3d, all, Bump, Raceline, Bridge);
if (TrackOnly) {
GfParmReleaseHandle(CfgHandle);
GfParmReleaseHandle(TrackHandle);
return EXIT_SUCCESS;
}
// Terrain.
if (MergeTerrain && !MergeAll) {
/* terrain + objects */
all = true;
}
std::string OutMeshName(OutputFileName + "-msh.ac");
GenerateTerrain(Track, TrackHandle, OutMeshName, allAc3d, all, DoSaveElevation, UseBorder);
if (DoSaveElevation != -1) {
if (all) {
//Ac3dClose(outfd);
allAc3d.writeFile(OutputFileName + ".ac", false);
}
switch (DoSaveElevation) {
case 0:
case 1:
SaveElevation(Track, TrackHandle, OutputFileName + "-elv.png", OutputFileName + ".ac", 1, HeightSteps);
if (DoSaveElevation) {
break;
}
SD_FALLTHROUGH // [[fallthrough]]
case 2:
SaveElevation(Track, TrackHandle, OutputFileName + "-elv2.png", OutMeshName, 1, HeightSteps);
if (DoSaveElevation) {
break;
}
SD_FALLTHROUGH // [[fallthrough]]
case 3:
SaveElevation(Track, TrackHandle, OutputFileName + "-elv3.png", OutMeshName, 0, HeightSteps);
if (DoSaveElevation) {
break;
}
SD_FALLTHROUGH // [[fallthrough]]
case 4:
SaveElevation(Track, TrackHandle, OutputFileName + "-elv4.png", OutTrackName, 2, HeightSteps);
break;
}
GfParmReleaseHandle(TrackHandle);
GfParmReleaseHandle(CfgHandle);
return EXIT_SUCCESS;
}
// check if we should use the object's materials
const std::string useObjectMaterials = GfParmGetStr(TrackHandle, TRK_SECT_TERRAIN, TRK_ATT_USE_OBJ_MATERIALS, "no");
if (useObjectMaterials == "yes")
MultipleMaterials = true;
GenerateObjects(Track, TrackHandle, CfgHandle, allAc3d, all, OutMeshName, OutTrackName, OutputFileName, MultipleMaterials);
allAc3d.writeFile(OutputFileName + ".ac", false);
GfParmReleaseHandle(TrackHandle);
GfParmReleaseHandle(CfgHandle);
return EXIT_SUCCESS;
}
int main(int argc, char **argv)
{
// Create and initialize the application
Application app;
app.initialize(/*bLoggingEnabled=*/true, argc, argv);
// Parse the command line options
if (!app.parseOptions())
return EXIT_FAILURE;
// If "data dir" specified in any way, cd to it.
if (chdir(GfDataDir()))
{
GfLogError("Could not start %s : failed to cd to the datadir '%s' (%s)\n",
app.name().c_str(), GfDataDir(), strerror(errno));
return EXIT_FAILURE;
}
// Do the requested job.
return app.generate();
}