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

563 lines
19 KiB
C++

/***************************************************************************
file : objects.cpp
created : Fri May 24 20:09:20 CEST 2002
copyright : (C) 2001 by Eric Espie
email : eric.espie@torcs.org
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:eric.espie@torcs.org>Eric Espie</a>
@version $Id$
*/
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cstring>
#ifndef WIN32
#include <unistd.h>
#endif
#include <cmath>
#include <tgfclient.h>
#include <track.h>
#include <portability.h>
#include "ac3d.h"
#include "util.h"
#include "objects.h"
#include "trackgen.h"
#include "robottools.h"
namespace
{
float GroupSize = 0;
float XGroupOffset = 0;
float YGroupOffset = 0;
int XGroupNb = 0;
int ObjUniqId = 0;
struct objdef
{
bool random = false;
bool trackOriented = false;
bool terrainOriented = false;
bool borderOriented = false;
unsigned int color = 0;
Ac3d ac3d;
tdble deltaHeight = 0;
tdble deltaVert = 0;
float distance = 0;
bool scaleRandom = false;
bool scaleFixed = false;
tdble scale = 0;
tdble scaleMin = 0;
tdble scaleMax = 0;
std::string fileName;
std::string name;
};
std::vector<objdef> objects;
tdble trackOffsetX = 0;
tdble trackOffsetY = 0;
void
InitObjects(tTrack *track, void *TrackHandle)
{
tTrkLocPos pos;
pos.seg = track->seg;
pos.type = TR_LPOS_MAIN;
pos.toStart = 0;
pos.toRight = 0;
pos.toMiddle = 0;
pos.toLeft = 0;
RtTrackLocal2Global(&pos, &trackOffsetX, &trackOffsetY, TR_TOMIDDLE);
ObjUniqId = 0;
srand((unsigned int)GfParmGetNum(TrackHandle, TRK_SECT_TERRAIN, TRK_ATT_SEED, nullptr, 1));
std::string inputPath(track->filename);
inputPath.resize(inputPath.find_last_of('/'));
const std::string modelPath(inputPath + ";" + GfDataDir() + "data/objects");
const int objnb = GfParmGetEltNb(TrackHandle, TRK_SECT_OBJECTS);
GfParmListSeekFirst(TrackHandle, TRK_SECT_OBJECTS);
objects.resize(objnb);
for (int i = 0; i < objnb; i++)
{
objdef *curObj = &objects[objnb - 1 - i]; // iterate backwards to match old behaviour
curObj->color = (unsigned int)GfParmGetCurNum(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_COLOR, nullptr, 0);
const char *objName = GfParmGetCurStr(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_OBJECT, nullptr);
if (!objName)
{
GfError("Missing %s in section %s/%s", TRK_ATT_OBJECT, TRK_SECT_OBJECTS, GfParmListGetCurEltName(TrackHandle, TRK_SECT_OBJECTS));
exit(1);
}
curObj->name = GfParmListGetCurEltName(TrackHandle, TRK_SECT_OBJECTS);
char filename[1024];
if (!GetFilename(objName, modelPath.c_str(), filename, sizeof(filename)))
{
GfError("Couldn't find object %s in %s\n", objName, modelPath.c_str());
GfParmListSeekNext(TrackHandle, TRK_SECT_OBJECTS);
continue;
}
curObj->fileName = filename;
try
{
curObj->ac3d.readFile(curObj->fileName);
curObj->ac3d.generateTriangles(); // convert quads to triangles
curObj->ac3d.flattenGeometry();
curObj->ac3d.flipAxes(true); // convert to track coordinate system
}
catch (const Ac3d::Exception &e)
{
GfError("Reading object %s: %s\n", curObj->fileName.c_str(), e.what());
GfParmListSeekNext(TrackHandle, TRK_SECT_OBJECTS);
continue;
}
const std::string scaleType = GfParmGetCurStr(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_SCALE_TYPE, "");
if (scaleType == "random")
{
curObj->scaleFixed = false;
curObj->scaleRandom = true;
curObj->scaleMin = GfParmGetCurNum(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_SCALE_MIN, nullptr, 0.5);
curObj->scaleMax = GfParmGetCurNum(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_SCALE_MAX, nullptr, 2.0);
}
else if (scaleType == "fixed")
{
curObj->scaleFixed = true;
curObj->scaleRandom = false;
curObj->scale = GfParmGetCurNum(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_SCALE, nullptr, 1.0);
}
else
{
curObj->scaleFixed = false;
curObj->scaleRandom = false;
}
if (strcmp(GfParmGetCurStr(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_ORIENTATION_TYPE, ""), "random") == 0)
{
curObj->deltaHeight = GfParmGetCurNum(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_DH, nullptr, 0);
curObj->deltaVert = GfParmGetCurNum(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_DV, nullptr, 5.0);
curObj->random = true;
}
else
{
curObj->random = false;
Ac3d::Matrix m1;
m1.makeRotation(GfParmGetCurNum(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_ORIENTATION, "deg", 0), 0.0, 0.0);
curObj->ac3d.transform(m1);
}
if (strcmp(GfParmGetCurStr(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_ORIENTATION_TYPE, ""), "track") == 0)
{
curObj->trackOriented = true;
}
else
{
curObj->trackOriented = false;
}
if (strcmp(GfParmGetCurStr(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_ORIENTATION_TYPE, ""), "terrain") == 0)
{
curObj->terrainOriented = true;
}
else
{
curObj->terrainOriented = false;
}
if (strcmp(GfParmGetCurStr(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_ORIENTATION_TYPE, ""), "border") == 0)
{
curObj->borderOriented = true;
curObj->distance = GfParmGetCurNum(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_BORDER_DISTANCE, nullptr, 1.0);
}
else
{
curObj->borderOriented = false;
}
GfParmListSeekNext(TrackHandle, TRK_SECT_OBJECTS);
}
}
void
AddObject(tTrack *track, void *trackHandle, const Ac3d &terrainRoot, const Ac3d &trackRoot, Ac3d &objectsRoot, unsigned int clr, tdble x, tdble y, bool multipleMaterials, bool individual)
{
for (auto &curObj : objects)
{
if (clr == curObj.color)
{
Ac3d::Matrix m;
tdble dv = 0;
tdble angle = 0;
float z = 0;
tdble orientation = 0;
tdble height = 0;
std::string name;
if (individual)
{
orientation = GfParmGetCurNum(trackHandle, TRK_SECT_TERRAIN_OBJECTS, TRK_ATT_ORIENTATION, "deg", 0);
height = GfParmGetCurNum(trackHandle, TRK_SECT_TERRAIN_OBJECTS, TRK_ATT_HEIGHT, "m", 0);
name = GfParmListGetCurEltName(trackHandle, TRK_SECT_TERRAIN_OBJECTS);
}
else
name = curObj.name;
Ac3d obj(curObj.ac3d);
if (curObj.random)
{
/* random rotations */
/* sgMakeCoordMat4 (m, 0.0, 0.0, curObj.deltaHeight * rand() / (RAND_MAX + 1.0), 0.0, 0.0, 0.0); */
/* ApplyTransform (m, obj); */
dv = curObj.deltaVert;
angle = 360.0 * rand() / (RAND_MAX + 1.0);
}
if (curObj.scaleRandom)
{
const tdble random = ((tdble)rand()) / (tdble)RAND_MAX;
if (curObj.scaleMin > curObj.scaleMax)
std::swap(curObj.scaleMin, curObj.scaleMax);
const tdble scale = (random * (curObj.scaleMax - curObj.scaleMin)) + curObj.scaleMin;
m.makeScale(scale);
obj.transform(m);
}
else if (curObj.scaleFixed)
{
m.makeScale(curObj.scale);
obj.transform(m);
}
if (curObj.terrainOriented)
{
angle = terrainRoot.getTerrainAngle(x, y);
}
if (curObj.trackOriented)
{
/* NEW: calculate angle for track-aligned orientation */
angle = getTrackAngle(track, trackHandle, x, y);
}
if (curObj.borderOriented)
{
/* NEW: calculate angle for border-aligned orientation */
float xNeu, yNeu, zNeu;
angle = getBorderAngle(track, trackHandle, x, y, curObj.distance, &xNeu, &yNeu, &zNeu);
// do something else with x and y
x = xNeu;
y = yNeu;
z = zNeu;
printf("tried to align to border: x: %g y: %g z: %g angle: %g \n", x, y, z, angle);
}
else
{
z = terrainRoot.getTerrainHeight(x, y);
if (z == -1000000.0f)
{
z = trackRoot.getTerrainHeight(x, y);
if (z == -1000000.0f)
{
if (individual)
{
printf("WARNING: failed to find elevation for individual object: %s x: %g y: %g (track x: %g track y: %g)\n",
name.c_str(), x, y, x - trackOffsetX, y - trackOffsetY);
}
else
{
printf("WARNING: failed to find elevation for object map object: %s x: %g y: %g (track x: %g track y: %g)\n",
name.c_str(), x, y, x - trackOffsetX, y - trackOffsetY);
}
return;
}
}
}
if (individual)
printf("placing individual object: %s x: %g y: %g z: %g \n", name.c_str(), x, y, z);
else
printf("placing object map object: %s x: %g y: %g z: %g \n", name.c_str(), x, y, z);
m.makeRotation(angle + orientation, dv / 2.0 - dv * rand() / (RAND_MAX + 1.0), dv / 2.0 - dv * rand() / (RAND_MAX + 1.0));
obj.transform(m);
m.makeLocation(x, y, z + height);
obj.transform(m);
obj.splitBySURF();
obj.splitByMaterial();
obj.splitByUV();
objectsRoot.merge(obj, multipleMaterials);
return;
}
}
}
bool
saveACInner(Ac3d::Object *ent, Ac3d &ac3d)
{
/* WARNING - RECURSIVE! */
if (ent->type != "poly")
{
ent->name = "objg" + std::to_string(ObjUniqId++);
for (auto & kid : ent->kids)
{
if (!saveACInner(&kid, ac3d))
{
return false;
}
}
return true;
}
ent->name = "obj" + std::to_string(ObjUniqId++);;
return true;
}
/* insert one leaf in group */
void
InsertInGroup(Ac3d::Object *leaf, Ac3d::Object *GroupRoot, std::vector<Ac3d::Object *> &Groups)
{
Ac3d::BoundingSphere boundingSphere = leaf->getBoundingSphere();
const int grIdx = (int)((boundingSphere.center[0] - XGroupOffset) / GroupSize) +
XGroupNb * (int)((boundingSphere.center[1] - YGroupOffset) / GroupSize);
if (Groups[grIdx] == nullptr)
{
GroupRoot->kids.push_back(Ac3d::Object("group", ""));
Groups[grIdx] = &GroupRoot->kids.back();
}
Groups[grIdx]->kids.push_back(*leaf);
}
/* insert leaves in groups */
void
InsertInner(Ac3d::Object *ent, Ac3d::Object *GroupRoot, std::vector<Ac3d::Object *> &Groups)
{
/* WARNING - RECURSIVE! */
if (ent->type != "poly")
{
for (auto & kid : ent->kids)
InsertInner(&kid, GroupRoot, Groups);
return;
}
InsertInGroup(ent, GroupRoot, Groups);
}
void
Group(tTrack *track, void *TrackHandle, Ac3d::Object *Root, Ac3d::Object *GroupRoot, std::vector<Ac3d::Object *> &Groups)
{
const tdble Margin = GfParmGetNum(TrackHandle, TRK_SECT_TERRAIN, TRK_ATT_BMARGIN, nullptr, DEFAULT_BORDER_MARGIN);
GroupSize = GfParmGetNum(TrackHandle, TRK_SECT_TERRAIN, TRK_ATT_GRPSZ, nullptr, DEFAULT_GROUP_SIZE);
XGroupOffset = track->min.x - Margin;
YGroupOffset = track->min.y - Margin;
XGroupNb = (int)((track->max.x + Margin - (track->min.x - Margin)) / GroupSize) + 1;
const int GroupNb = XGroupNb * ((int)((track->max.y + Margin - (track->min.y - Margin)) / GroupSize) + 1);
Groups.resize(GroupNb);
for (size_t i = 0; i < Groups.size(); ++i)
Groups[i] = nullptr;
InsertInner(Root, GroupRoot, Groups);
int used = 0;
for (size_t i = 0; i < Groups.size(); i++)
{
if (Groups[i] != nullptr)
used++;
}
printf("%d groups : %d used\n", GroupNb, used);
}
} // end anonymous namespace
void
GenerateObjects(tTrack *track, void *TrackHandle, void *CfgHandle, Ac3d &allAc3d, bool all, const std::string &terrainFile, const std::string &trackFile, const std::string &outputFile, bool multipleMaterials)
{
std::string inputPath(track->filename);
inputPath.resize(inputPath.find_last_of('/'));
Ac3d TerrainRoot;
try
{
TerrainRoot.readFile(terrainFile);
TerrainRoot.flipAxes(true); // convert to track coordinate system
}
catch (const Ac3d::Exception &e)
{
GfOut("Reading terrain file %s: %s\n", terrainFile.c_str(), e.what());
exit(1);
}
Ac3d TrackRoot;
if (!trackFile.empty())
{
try
{
TrackRoot.readFile(trackFile);
TrackRoot.flipAxes(true); // convert to track coordinate system
}
catch (const Ac3d::Exception &e)
{
GfOut("Reading track file %s: %s\n", trackFile.c_str(), e.what());
exit(1);
}
}
InitObjects(track, TrackHandle);
const tdble Margin = GfParmGetNum(TrackHandle, TRK_SECT_TERRAIN, TRK_ATT_BMARGIN, nullptr, DEFAULT_BORDER_MARGIN);
const tdble xmin = track->min.x - Margin;
const tdble xmax = track->max.x + Margin;
const tdble ymin = track->min.y - Margin;
const tdble ymax = track->max.y + Margin;
int index = 0;
if (GfParmGetEltNb(TrackHandle, TRK_SECT_TERRAIN_OBJMAP) != 0)
{
GfParmListSeekFirst(TrackHandle, TRK_SECT_TERRAIN_OBJMAP);
do
{
Ac3d ObjectsRoot;
index++;
const char *map = GfParmGetCurStr(TrackHandle, TRK_SECT_TERRAIN_OBJMAP, TRK_ATT_OBJMAP, "");
const std::string imageFile(inputPath + "/" + map);
printf("Processing object map %s\n", imageFile.c_str());
int width, height;
unsigned char *MapImage = GfTexReadImageFromPNG(imageFile.c_str(), 2.2, &width, &height, 0, 0, false);
if (!MapImage)
{
return;
}
const tdble kX = (xmax - xmin) / width;
const tdble dX = xmin;
const tdble kY = (ymax - ymin) / height;
const tdble dY = ymin;
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++)
{
const unsigned int clr = (MapImage[4 * (i + width * j)] << 16) + (MapImage[4 * (i + width * j) + 1] << 8) + MapImage[4 * (i + width * j) + 2];
if (clr)
{
printf("found color: 0x%X x: %d y: %d\n", clr, i, j);
AddObject(track, TrackHandle, TerrainRoot, TrackRoot, ObjectsRoot, clr, i * kX + dX, j * kY + dY, multipleMaterials, false);
}
}
}
free(MapImage);
Ac3d GroupRoot;
GroupRoot.materials = ObjectsRoot.materials;
Ac3d::Object object("group", "");
GroupRoot.addObject(object);
std::vector<Ac3d::Object *> Groups;
Group(track, TrackHandle, &ObjectsRoot.root, &GroupRoot.root.kids.front(), Groups);
const char *extName = GfParmGetStr(CfgHandle, "Files", "object", "obj");
const std::string objectFile(outputFile + "-" + extName + "-" + std::to_string(index) + ".ac");
saveACInner(&GroupRoot.root.kids.front(), GroupRoot);
GroupRoot.flipAxes(false); // convert to track coordinate system
GroupRoot.writeFile(objectFile, false);
if (all)
{
allAc3d.merge(GroupRoot, multipleMaterials);
}
} while (!GfParmListSeekNext(TrackHandle, TRK_SECT_TERRAIN_OBJMAP));
}
if (GfParmGetEltNb(TrackHandle, TRK_SECT_TERRAIN_OBJECTS) != 0)
{
GfParmListSeekFirst(TrackHandle, TRK_SECT_TERRAIN_OBJECTS);
Ac3d ObjectsRoot;
index++;
do
{
const tdble x = GfParmGetCurNum(TrackHandle, TRK_SECT_TERRAIN_OBJECTS, TRK_ATT_X, "m", 0);
const tdble y = GfParmGetCurNum(TrackHandle, TRK_SECT_TERRAIN_OBJECTS, TRK_ATT_Y, "m", 0);
const unsigned int color = (unsigned int)GfParmGetCurNum(TrackHandle, TRK_SECT_TERRAIN_OBJECTS, TRK_ATT_COLOR, nullptr, 0);
printf("found color: 0x%X x: %f y: %f\n", color, x, y);
AddObject(track, TrackHandle, TerrainRoot, TrackRoot, ObjectsRoot, color, x + trackOffsetX, y + trackOffsetY, multipleMaterials, true);
} while (!GfParmListSeekNext(TrackHandle, TRK_SECT_TERRAIN_OBJECTS));
Ac3d GroupRoot;
GroupRoot.materials = ObjectsRoot.materials;
Ac3d::Object object("group", "");
GroupRoot.addObject(object);
std::vector<Ac3d::Object *> Groups;
Group(track, TrackHandle, &ObjectsRoot.root, &GroupRoot.root.kids.front(), Groups);
const char *extName = GfParmGetStr(CfgHandle, "Files", "object", "obj");
const std::string objectFile(outputFile + "-" + extName + "-" + std::to_string(index) + ".ac");
saveACInner(&GroupRoot.root.kids.front(), GroupRoot);
GroupRoot.flipAxes(false); // convert to track coordinate system
GroupRoot.writeFile(objectFile, false);
if (all)
{
allAc3d.merge(GroupRoot, multipleMaterials);
}
}
}