/***************************************************************************
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: objects.cpp,v 1.5.2.7 2012/07/13 15:27:31 berniw Exp $
***************************************************************************/
/***************************************************************************
* *
* 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 Eric Espie
@version $Id: objects.cpp,v 1.5.2.7 2012/07/13 15:27:31 berniw Exp $
*/
#include
#include
#include
#ifndef WIN32
#include
#endif
#include
#include
#include
#include
#include
#include
#include "trackgen.h"
#include "util.h"
#include "elevation.h"
#include "ac3d.h"
#include "objects.h"
static ssgRoot *Root = NULL;
static ssgRoot *GroupRoot = NULL;
static ssgRoot *TrackRoot = NULL;
struct group {
ssgBranch *br;
};
static struct group *Groups;
static float GroupSize;
static float XGroupOffset;
static float YGroupOffset;
static int XGroupNb;
static int GroupNb;
static int ObjUniqId = 0;
class myLoaderOptions : public ssgLoaderOptions {
public:
virtual void makeModelPath ( char *path, const char *fname ) const {
ulFindFile ( path, model_dir, fname, NULL ) ;
}
virtual void makeTexturePath ( char *path, const char *fname ) const {
ulFindFile ( path, texture_dir, fname, NULL ) ;
}
} ;
typedef struct objdef {
GF_TAILQ_ENTRY(objdef) link;
int random;
unsigned int color;
ssgEntity *obj;
tdble deltaHeight;
tdble deltaVert;
} tobjdef;
GF_TAILQ_HEAD(objlist, objdef);
tobjlist objhead;
int
GetObjectsNb(void *TrackHandle)
{
const int BUFSIZE = 1024;
char path[BUFSIZE];
snprintf(path, BUFSIZE, "%s/%s", TRK_SECT_TERRAIN, TRK_SECT_OBJMAP);
return GfParmGetEltNb(TrackHandle, path);
}
static void
ApplyTransform(sgMat4 m, ssgBase *node)
{
int i;
ssgBranch *br;
if (node->isAKindOf(ssgTypeLeaf())) {
((ssgLeaf *)node)->transform(m);
} else {
br = (ssgBranch *)node;
for (i = 0; i < br->getNumKids(); i++) {
ApplyTransform(m, br->getKid(i));
}
}
}
static void
InitObjects(tTrack *track, void *TrackHandle)
{
int objnb, i;
struct objdef *curObj;
const char *objName;
static char *search;
myLoaderOptions options ;
sgMat4 m;
const int BUFSIZE = 1024;
char buf[BUFSIZE];
char path[BUFSIZE];
ObjUniqId = 0;
srand((unsigned int)GfParmGetNum(TrackHandle, TRK_SECT_TERRAIN, TRK_ATT_SEED, NULL, 1));
ssgSetCurrentOptions ( &options ) ;
GF_TAILQ_INIT(&objhead);
snprintf(buf, BUFSIZE, "tracks/%s/%s;data/objects", track->category, track->internalname);
search = strdup(buf);
snprintf(path, BUFSIZE, "tracks/%s/%s;data/objects;data/textures;.", track->category, track->internalname);
ssgTexturePath(path);
ssgModelPath(path);
objnb = GfParmGetEltNb(TrackHandle, TRK_SECT_OBJECTS);
GfParmListSeekFirst(TrackHandle, TRK_SECT_OBJECTS);
for (i = 0; i < objnb; i++) {
curObj = (struct objdef *)malloc(sizeof(struct objdef));
curObj->color = (unsigned int)GfParmGetCurNum(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_COLOR, NULL, 0);
objName = GfParmGetCurStr(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_OBJECT, NULL);
if (!objName) {
GfOut("Missing %s in %s/%s", TRK_ATT_OBJECT, TRK_SECT_OBJECTS, GfParmListGetCurEltName(TrackHandle, TRK_SECT_OBJECTS));
exit(1);
}
GetFilename(objName, search, buf, BUFSIZE);
curObj->obj = ssgLoadAC(buf);
ssgFlatten(curObj->obj);
if (strcmp(GfParmGetCurStr(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_ORIENTATION_TYPE, ""), "random") == 0) {
curObj->deltaHeight = GfParmGetCurNum(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_DH, NULL, 0);
curObj->deltaVert = GfParmGetCurNum(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_DV, NULL, 5.0);
curObj->random = 1;
} else {
curObj->random = 0;
sgMakeRotMat4(m, GfParmGetCurNum(TrackHandle, TRK_SECT_OBJECTS, TRK_ATT_ORIENTATION, "deg", 0), 0.0, 0.0);
ApplyTransform(m, curObj->obj);
}
GF_TAILQ_INSERT_HEAD(&objhead, curObj, link);
GfParmListSeekNext(TrackHandle, TRK_SECT_OBJECTS);
}
free(search);
}
/* Prune the group tree */
static void
AddToRoot(ssgEntity *node)
{
int i;
ssgBranch *br;
if (node->isAKindOf(ssgTypeLeaf())) {
Root->addKid(node);
} else {
br = (ssgBranch *)node;
for (i = 0; i < br->getNumKids(); i++) {
AddToRoot(br->getKid(i));
}
}
}
static void
AddObject(unsigned int clr, tdble x, tdble y)
{
struct objdef *curObj;
ssgEntity *obj;
sgMat4 m;
tdble dv;
for (curObj = GF_TAILQ_FIRST(&objhead); curObj; curObj = GF_TAILQ_NEXT(curObj, link)) {
if (clr == curObj->color) {
obj = (ssgEntity *)curObj->obj->clone(SSG_CLONE_RECURSIVE | SSG_CLONE_GEOMETRY | SSG_CLONE_STATE);
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;
sgMakeRotMat4 (m, 360.0 * rand() / (RAND_MAX + 1.0), dv / 2.0 - dv * rand() / (RAND_MAX + 1.0), dv / 2.0 - dv * rand() / (RAND_MAX + 1.0));
ApplyTransform (m, obj);
}
sgMakeTransMat4(m, x, y, getHOT(TrackRoot, x, y));
ApplyTransform(m, obj);
AddToRoot(obj);
return;
}
}
}
/* Code extracted from PLIB-ssg */
static const int writeTextureWithoutPath = TRUE;
struct saveTriangle {
int v[3];
sgVec2 t[3];
};
static int
ssgSaveLeaf (ssgEntity *ent, FILE *save_fd)
{
int i;
static sgVec3 *vlist;
static saveTriangle *tlist;
const int BUFSIZE = 1024;
char buf[1024];
const char *surf;
ssgLeaf *vt = (ssgLeaf *)ent;
if (vt->getCullFace() == TRUE) {
surf = "SURF 0x10\n";
} else {
surf = "SURF 0x30\n";
}
int num_verts = vt->getNumVertices();
int num_tris = vt->getNumTriangles();
vlist = new sgVec3[num_verts];
tlist = new saveTriangle[num_tris];
for (i = 0; i < num_verts; i++) {
sgCopyVec3 (vlist[i], vt->getVertex (i));
}
for (i = 0; i < num_tris; i++) {
short vv0, vv1, vv2;
vt->getTriangle (i, &vv0, &vv1, &vv2);
tlist[i].v[0] = vv0;
tlist[i].v[1] = vv1;
tlist[i].v[2] = vv2;
sgCopyVec2 (tlist[i].t[0], vt->getTexCoord (vv0));
sgCopyVec2 (tlist[i].t[1], vt->getTexCoord (vv1));
sgCopyVec2 (tlist[i].t[2], vt->getTexCoord (vv2));
}
fprintf (save_fd, "OBJECT poly\n");
snprintf(buf, BUFSIZE, "obj%d", ObjUniqId++);
fprintf (save_fd, "name \"%s\"\n", buf);
ssgState *st = vt->getState ();
if (st && st->isAKindOf (ssgTypeSimpleState())) {
ssgSimpleState *ss = (ssgSimpleState *) vt->getState ();
if (ss->isEnabled (GL_TEXTURE_2D)) {
const char *tfname = ss->getTextureFilename ();
if ((tfname != NULL) && (tfname[0] != 0)) {
if (writeTextureWithoutPath) {
char *s = strrchr ((char *)tfname, '\\');
if (s == NULL) {
s = strrchr ((char *)tfname, '/');
}
if (s == NULL) {
fprintf (save_fd, "texture \"%s\"\n", tfname);
} else {
fprintf (save_fd, "texture \"%s\"\n", ++s);
}
} else {
fprintf (save_fd, "texture \"%s\"\n", tfname);
}
}
}
}
fprintf (save_fd, "numvert %d\n", num_verts);
for (i = 0; i < num_verts; i++) {
fprintf (save_fd, "%g %g %g\n", vlist[i][0],vlist[i][2],-vlist[i][1]);
}
fprintf (save_fd, "numsurf %d\n", num_tris);
for (i = 0; i < num_tris; i++) {
fprintf (save_fd, surf);
fprintf (save_fd, "mat 0\n");
fprintf (save_fd, "refs 3\n");
fprintf (save_fd, "%d %g %g\n",
tlist[i].v[0],tlist[i].t[0][0],tlist[i].t[0][1]);
fprintf (save_fd, "%d %g %g\n",
tlist[i].v[1],tlist[i].t[1][0],tlist[i].t[1][1]);
fprintf (save_fd, "%d %g %g\n",
tlist[i].v[2],tlist[i].t[2][0],tlist[i].t[2][1]);
}
fprintf (save_fd, "kids 0\n");
delete[] vlist;
delete tlist;
return TRUE;
}
static int
ssgSaveACInner (ssgEntity *ent, FILE *save_fd)
{
/* WARNING - RECURSIVE! */
const int BUFSIZE = 1024;
char buf[BUFSIZE];
if (ent->isAKindOf (ssgTypeBranch())) {
ssgBranch *br = (ssgBranch *) ent;
snprintf(buf, BUFSIZE, "objg%d", ObjUniqId++);
Ac3dGroup (save_fd, buf, ent->getNumKids());
for (int i = 0; i < br->getNumKids (); i++) {
if (! ssgSaveACInner(br->getKid (i), save_fd)) {
return FALSE;
}
}
return TRUE;
}
return ssgSaveLeaf (ent, save_fd);
}
/* insert one leaf in group */
static void
InsertInGroup(ssgEntity *ent)
{
int grIdx;
struct group *curGrp;
float *center;
ent->recalcBSphere();
center = (float *)ent->getBSphere()->getCenter();
grIdx = (int)((center[0] - XGroupOffset) / GroupSize) +
XGroupNb * (int)((center[1] - YGroupOffset) / GroupSize);
curGrp = &(Groups[grIdx]);
if (curGrp->br == 0) {
curGrp->br = new ssgBranch();
GroupRoot->addKid(curGrp->br);
}
curGrp->br->addKid(ent);
}
/* insert leaves in groups */
static void
InsertInner(ssgEntity *ent)
{
/* WARNING - RECURSIVE! */
if (ent->isAKindOf (ssgTypeBranch())) {
ssgBranch *br = (ssgBranch *) ent;
for (int i = 0; i < br->getNumKids (); i++) {
InsertInner(br->getKid (i));
}
return;
}
InsertInGroup (ent);
}
static void
Group(tTrack *track, void *TrackHandle, ssgEntity *ent)
{
tdble Margin;
if (GroupRoot) {
delete (GroupRoot);
}
GroupRoot = new ssgRoot();
Margin = GfParmGetNum(TrackHandle, TRK_SECT_TERRAIN, TRK_ATT_BMARGIN, NULL, 100.0);
GroupSize = GfParmGetNum(TrackHandle, TRK_SECT_TERRAIN, TRK_ATT_GRPSZ, NULL, 100.0);
XGroupOffset = track->min.x - Margin;
YGroupOffset = track->min.y - Margin;
XGroupNb = (int)((track->max.x + Margin - (track->min.x - Margin)) / GroupSize) + 1;
GroupNb = XGroupNb * ((int)((track->max.y + Margin - (track->min.y - Margin)) / GroupSize) + 1);
if (Groups) {
free(Groups);
}
Groups = (struct group *)calloc(GroupNb, sizeof (struct group));
InsertInner(Root);
}
void
GenerateObjects(tTrack *track, void *TrackHandle, void *CfgHandle, FILE *save_fd, char *meshFile)
{
ssgLoaderOptionsEx options;
int i, j;
const char *map;
unsigned char *MapImage;
int width, height;
tdble xmin, xmax, ymin, ymax;
tdble Margin;
tdble kX, kY, dX, dY;
unsigned int clr;
int index;
const char *extName;
FILE *curFd;
const int BUFSIZE = 1024;
char buf[BUFSIZE];
char path[BUFSIZE];
ssgSetCurrentOptions(&options);
snprintf(buf, BUFSIZE, "tracks/%s/%s;data/textures;data/img;.", track->category, track->internalname);
ssgTexturePath(buf);
snprintf(buf, BUFSIZE, ".;tracks/%s/%s", track->category, track->internalname);
ssgModelPath(buf);
TrackRoot = (ssgRoot *)ssgLoadAC(meshFile);
InitObjects(track, TrackHandle);
Margin = GfParmGetNum(TrackHandle, TRK_SECT_TERRAIN, TRK_ATT_BMARGIN, NULL, 0);
xmin = track->min.x - Margin;
xmax = track->max.x + Margin;
ymin = track->min.y - Margin;
ymax = track->max.y + Margin;
snprintf(path, BUFSIZE, "%s/%s", TRK_SECT_TERRAIN, TRK_SECT_OBJMAP);
if (GfParmGetEltNb(TrackHandle, path) == 0) {
return;
}
GfParmListSeekFirst(TrackHandle, path);
index = 0;
do {
Root = new ssgRoot();
index++;
map = GfParmGetCurStr(TrackHandle, path, TRK_ATT_OBJMAP, "");
snprintf(buf, BUFSIZE, "tracks/%s/%s/%s", track->category, track->internalname, map);
printf("Processing object map %s\n", buf);
MapImage = GfImgReadPng(buf, &width, &height, 2.0);
if (!MapImage) {
return;
}
kX = (xmax - xmin) / width;
dX = xmin;
kY = (ymax - ymin) / height;
dY = ymin;
for (j = 0; j < height; j++) {
for (i = 0; i < width; i++) {
clr = (MapImage[4 * (i + width * j)] << 16) + (MapImage[4 * (i + width * j) + 1] << 8) + MapImage[4 * (i + width * j) + 2];
if (clr) {
AddObject(clr, i * kX + dX, j * kY + dY);
}
}
}
Group(track, TrackHandle, Root);
extName = GfParmGetStr(CfgHandle, "Files", "object", "obj");
snprintf(buf, BUFSIZE, "%s-%s-%d.ac", OutputFileName, extName, index);
curFd = Ac3dOpen(buf, 1);
ssgSaveACInner(GroupRoot, curFd);
Ac3dClose(curFd);
if (save_fd) {
ssgSaveACInner(GroupRoot, save_fd);
}
delete (Root);
} while (!GfParmListSeekNext(TrackHandle, path));
}