// objekte.h  Klassen fuer z.B. Sammelobjekte

#pragma once
#include "kleinkram.h"
#include "objekte_classdef.h"

#ifdef SPECTPLORER
#define MIT_GPX
#endif

void Box::check(glm::vec3& v0, glm::mat4& matrix)
{
 glm::vec4 v(v0, 1.0f);
 v = matrix * v;
 if(v.x < minv.x) minv.x = v.x;
 if(v.y < minv.y) minv.y = v.y;
 if(v.z < minv.z) minv.z = v.z;
 if(v.x > maxv.x) maxv.x = v.x;
 if(v.y > maxv.y) maxv.y = v.y;
 if(v.z > maxv.z) maxv.z = v.z;
}

void Box::check(glm::vec3& v)
{
 if(v.x < minv.x) minv.x = v.x;
 if(v.y < minv.y) minv.y = v.y;
 if(v.z < minv.z) minv.z = v.z;
 if(v.x > maxv.x) maxv.x = v.x;
 if(v.y > maxv.y) maxv.y = v.y;
 if(v.z > maxv.z) maxv.z = v.z;
}

void Meshdata::getMinMaxPos(glm::vec3 *minvec, glm::vec3 *maxvec)
{
 // Die beiden Vektoren muessen schon vorbelegt sein, z.B. mit 0-Werten
 if(numVertices==0) {fprintf(stderr,"Error: getMinMaxPos() no vertices\n"); return;}
 for(uint64 i=0;i<numVertices;i++)
  {
   glm::vec3 v = vertices[i].position;
   if(v.x < minvec->x) minvec->x = v.x;
   if(v.y < minvec->y) minvec->y = v.y;
   if(v.z < minvec->z) minvec->z = v.z;
   if(v.x > maxvec->x) maxvec->x = v.x;
   if(v.y > maxvec->y) maxvec->y = v.y;
   if(v.z > maxvec->z) maxvec->z = v.z;
  }
}

void Meshdata::getMinMaxPos(Box* box, glm::mat4& matrix)
{
 if(numVertices==0) {fprintf(stderr,"Error: getMinMaxPos() no vertices\n"); return;}
 for(uint64 i=0;i<numVertices;i++)
  {
   box->check(vertices[i].position, matrix);
  }
}

void Meshdata::getMinMaxPos(Box* box)
{
 if(numVertices==0) {fprintf(stderr,"Error: getMinMaxPos() no vertices\n"); return;}
 for(uint64 i=0;i<numVertices;i++)
  {
   box->check(vertices[i].position);
  }
}

void Meshdata::setoffset(float32 x,float32 y,float32 z)
{
 glm::vec3 offset(x,y,z);
 for(uint64 i=1;i<numVertices;i++)
  {
   vertices[i].position += offset;
  }
}

void Modeldata::setoffset(float32 x,float32 y,float32 z)
{
 for(uint32 i=0;i<numMeshes;i++)
  {
   meshdatas[i]->setoffset(x,y,z);
  }
}

void Klon::setoffset(glm::vec3 offset)
{
 position += offset;
 glm::mat4 translateMatrix = glm::translate(glm::mat4(1.0f), offset);
 modelMatrix = translateMatrix * modelMatrix;
}

void Klon::setposition(glm::vec3 pos)
{
 glm::mat4 translateMatrix = glm::translate(glm::mat4(1.0f), pos-position);
 modelMatrix = translateMatrix * modelMatrix;
 position = pos;
}

void Klon::scale(float32 faktor)
{
 glm::mat4 scaleMatrix = glm::scale(glm::mat4(1.0f), {faktor, faktor, faktor});
 modelMatrix = scaleMatrix * modelMatrix;
 //printf("glm::scale() gemacht\n");//test
 //printmatrix(modelMatrix);//test
}

void Klon::scale(float32 sx,float32 sy,float32 sz)
{
 glm::mat4 scaleMatrix = glm::scale(glm::mat4(1.0f), {sx, sy, sz});
 modelMatrix = scaleMatrix * modelMatrix;
}

void Klon::rotateX(float32 alfa)
{
 modelMatrix = glm::rotate(glm::mat4(1.0f), alfa, glm::vec3(1.0f, 0.0f, 0.0f)) * modelMatrix;
}

void Klon::rotateY(float32 alfa)
{
 modelMatrix = glm::rotate(glm::mat4(1.0f), alfa, glm::vec3(0.0f, 1.0f, 0.0f)) * modelMatrix;
}

void Klon::rotateZ(float32 alfa)
{
 //Koordinatensystem mitdrehend:
 //modelMatrix = glm::rotate(modelMatrix, alfa, glm::vec3(0.0f, 0.0f, 1.0f));

 //ohne mitdrehendes Koordinatensystem:
 modelMatrix = glm::rotate(glm::mat4(1.0f), alfa, glm::vec3(0.0f, 0.0f, 1.0f)) * modelMatrix;
}

uint64 Modeldata::getNumVertices()
{
 return (numMeshes==0) ? 0 : meshdatas[numMeshes-1]->numVertices;
}

void Modeldata::addVertex(Vertex& vertex)
{
 if(numMeshes==0) {fprintf(stderr,"Error: addVertex() missing meshes\n"); return;}
 meshdatas[numMeshes-1]->addVertex(vertex);
}

void Modeldata::addIndex(uint32 index)
{
 if(numMeshes==0) {fprintf(stderr,"Error: addIndex() missing meshes\n"); return;}
 meshdatas[numMeshes-1]->addIndex(index);
}

glm::vec3 Modeldata::getModelsize()
{
 // Groessebox ermitteln ohne modelMatrix
 // Fuer Kollisionsbox muesste von entsprechendem Klon die Vertexe
 // noch mit der modelMatrix multipliziert werden.
 glm::vec3 result={0.0f, 0.0f, 0.0f};
 if(numMeshes==0) {fprintf(stderr,"Error: getModelsize() no meshes\n"); return result;}
 // unrealistisch grosse Startwerde - werden dann beim ersten Vergleich richtig gesetzt
 glm::vec3 minvec(1000.0f,1000.0f,1000.0f), maxvec(-1000.0f,-1000.0f,-1000.0f);
 for(uint64 i=0;i<numMeshes;i++)
  {
   meshdatas[i]->getMinMaxPos(&minvec,&maxvec);
  }
 result = maxvec - minvec;
 return result;
}

Box Objekte::getModelbox(uint32 modelid,uint32 klonid)
{
 // Groessebox ermitteln mit Beruecksichtigung der modelMatrix eines Klons
 Box box;
 if(modeldatas[modelid]->numMeshes==0) {fprintf(stderr,"Error: getModelbox() no meshes\n"); return box;}
 for(uint64 i=0; i < modeldatas[modelid]->numMeshes; i++)
  {
   modeldatas[modelid]->meshdatas[i]->getMinMaxPos(&box, klonlists[modelid]->klones[klonid].modelMatrix);
  }
 return box;
}

uint32 Modeldata::addmesh(Material mat)
{
 Meshdata *meshdat=new Meshdata();
 meshdat->setMaterial(mat);
 meshdatas.push_back(meshdat);
 return numMeshes++;
}

void Modeldata::clear()
{
 for(uint64 i=0;i<numMeshes;i++) {meshdatas.pop_back();}
 numMeshes=0;
}

void Model::init2(Modeldata* modeldata, Shader *shader)
{
 for(Mesh* mesh : meshes)
  {delete mesh;}
 numMeshes = modeldata->numMeshes;
 for(uint64 i=0;i<numMeshes;i++)
  {
   Meshdata* md = modeldata->meshdatas[i];
   Mesh* mesh = new Mesh(md->vertices, md->numVertices, md->indices, md->numIndices, md->material, shader,
			 md->linienflag);
   meshes.push_back(mesh);
  }
}

void Meshdata::matrixmult(glm::mat4 matrix)
{
 for(uint64 i=0;i<numVertices;i++)
  {
   glm::vec4 vector = matrix * glm::vec4(vertices[i].position, 1.0f);
   vertices[i].position = vector;
  }
}

float read_float(const char *zeile,int *zi)
{
 int i= *zi;
 while(zeile[i]>=' ' && !isdigit(zeile[i])) {i++;}
 float x;
 sscanf(&zeile[i],"%f",&x);
 while(zeile[i]>' ') {i++;}
 *zi = i;
 return x;
}

float read_float(FILE *fp,char *zeile,const char *str)
{
 float x=0;
 int n=strlen(str);
 while((zeile[0]=='/' && zeile[1]=='/') || zeile[0]=='#') getline(fp,zeile,200);//Kommentarzeilen ueberlesen
 if(strncmp(zeile,str,n)!=0) {printf("Syntaxfehler: zeile=\"%s\", %s erwartet\n",zeile,str); return x;}
 sscanf(&zeile[n],"%f", &x);
 return x;
}

Vec3 read_vec3(FILE *fp,char *zeile,const char *str)
{
 Vec3 v(0.0f);
 int n=strlen(str);
 while((zeile[0]=='/' && zeile[1]=='/') || zeile[0]=='#') getline(fp,zeile,200);//Kommentarzeilen ueberlesen
 if(strncmp(zeile,str,n)!=0) {printf("Syntaxfehler: zeile=\"%s\", %s erwartet\n",zeile,str); return v;}
 sscanf(&zeile[n],"%f,%f,%f", &v.x, &v.y, &v.z);
 return v;
}

Vec3 read_vec3col(FILE *fp,char *zeile,const char *str)
{
 Vec3 v(0.0f);
 int n=strlen(str);
 while((zeile[0]=='/' && zeile[1]=='/') || zeile[0]=='#') getline(fp,zeile,200);//Kommentarzeilen ueberlesen
 if(strncmp(zeile,str,n)!=0) {printf("Syntaxfehler: zeile=\"%s\", %s erwartet\n",zeile,str); return v;}
 sscanf(&zeile[n],"%f,%f,%f", &v.x, &v.y, &v.z);
 if(v.x>1.0f || v.y>1.0f || v.z>1.0f)
  {
   v.x /= 255.0f;  v.y /= 255.0f;  v.z /= 255.0f;
  }
 return v;
}

bool grenzencheck(float xmini,float zmini,float xmaxi,float zmaxi)
{
 if(xmini>0 || zmini>0 || xmaxi<0 || zmaxi<0) return false;
 if(xmini < -250 || zmini < -250 || xmaxi>250 || zmaxi>250) return false;
 return true;
}

void Modeldata::initcalc(const char* filename)
{
#ifdef _DEBUG
 printf("Modeldata::initcalc(%s)\n",filename);//test
#endif
 FILE *fp=myfopen(filename,"r");
 if(fp==NULL) printf("Error: initcalc(%s) file not found\n",filename);
 char zeile[200];
 Vec3 position0;
 while(getline(fp,zeile,200))
  {
   if(zeile[0]=='#' || (zeile[0]=='/' && zeile[1]=='/')) continue;//Kommentare ueberlesen
   kommentare_entfernen(zeile);
   if(zeile[0]==0) continue;//Leerzeilen ignorieren
   if(strcmp(zeile,"name: Landschaft")==0)
    {
     Vec3 position; int nhuegel;
     getline(fp,zeile,200);
     if(strncmp(zeile,"grenzen:",8)==0)
      {
       float xmini,zmini,xmaxi,zmaxi;
       int n=sscanf(&zeile[8],"%f,%f,%f,%f",&xmini,&zmini,&xmaxi,&zmaxi);
       if(n==4 && grenzencheck(xmini,zmini,xmaxi,zmaxi)) landschaft1.setGrenzen(xmini,zmini,xmaxi,zmaxi);
       else printf("Error: fehlerhafte Grenzen: %s\n Werte auf +-250 begrenzt\n",zeile);//test
       getline(fp,zeile,200);
      }
     position0=read_vec3(fp,zeile,"position:");
     getline(fp,zeile,200); nhuegel = read_float(fp,zeile,"nhuegel:");
     float hoehe,breite,laenge, posx,posz;
     float *ktab=new float[nhuegel*3];
     float *postabzx=new float[nhuegel*2];
     for(int i=0;i<nhuegel;i++)
      {
       getline(fp,zeile,200);
       int n=sscanf(zeile,"%f,%f,%f",&hoehe,&breite,&laenge);
       if(n!=3) printf("Error: missing 3 parameters - zeile=\"%s\"\n",zeile);//test
       ktab[i*3] = hoehe;
       ktab[i*3+1] = breite;
       ktab[i*3+2] = laenge;
      }
     for(int i=0;i<nhuegel;i++)
      {
       getline(fp,zeile,200);
       int n=sscanf(zeile,"%f,%f",&posz,&posx);
       if(n!=2) printf("Error: missing 2 parameters - zeile=\"%s\"\n",zeile);//test
       postabzx[i*2] = posz;
       postabzx[i*2+1] = posx;
      }
     int nsighuegel=0;
     getline(fp,zeile,200); nsighuegel = read_float(fp,zeile,"nsigmoidhuegel:");
     float *sigmktab=new float[nsighuegel*5];
     float *sigmpostabzx=new float[nsighuegel*2];
     for(int i=0;i<nsighuegel;i++)
      {
       float k1,k2;
       getline(fp,zeile,200);
       int n=sscanf(zeile,"%f,%f,%f,%f,%f",&hoehe,&breite,&laenge,&k1,&k2);
       if(n!=5) printf("Error: missing 5 parameters - zeile=\"%s\"\n",zeile);//test
       sigmktab[i*5] = hoehe;
       sigmktab[i*5+1] = breite;
       sigmktab[i*5+2] = laenge;
       sigmktab[i*5+3] = k1;
       sigmktab[i*5+4] = k2;
      }
     for(int i=0;i<nsighuegel;i++)
      {
       getline(fp,zeile,200);
       int n=sscanf(zeile,"%f,%f",&posz,&posx);
       if(n!=2) printf("Error: missing 2 parameters - zeile=\"%s\"\n",zeile);//test
       sigmpostabzx[i*2] = posz;
       sigmpostabzx[i*2+1] = posx;
      }
     landschaft1_erstellen(nhuegel,ktab,postabzx, nsighuegel,sigmktab,sigmpostabzx);
     landschaft1.set_y0(position0.y);
     delete[] ktab;
     delete[] postabzx;
     delete[] sigmktab;
     delete[] sigmpostabzx;
     //landschaft1.minmaxtest();//test
     
     //Flaeche der Funktion: von plotter2.cc kopiert, aber direkt in Modeldata gespeichert:
//#define NOSMOOTH  //test: Landschaft deutlich in Vierecke eingeteilt
     Material mat;
#ifdef HELLERE_SONNE
     mat.diffuse  = Vec3(0.4f, 0.8f, 0.4f); //TODO: Farbe der Landschaft (vorlaeufig helles gruen)
     mat.ambient  = mat.diffuse*0.5f;
#else
     mat.ambient  = Vec3(0.471f, 0.941f, 0.471f); //TODO: Farbe der Landschaft (vorlaeufig helles gruen)
     mat.diffuse  = Vec3(0.471f, 0.941f, 0.471f); //TODO: Farbe der Landschaft (vorlaeufig helles gruen)
#endif
     mat.specular = Vec3(0.0f); //TODO: Glanz der Landschaft?
     mat.emissive = Vec3(0.0f);
     mat.shininess = 1.0f; //TODO: Glanz der Landschaft?
     mat.texturflag = 0;//TODO: Textur fuer Karte oder z.B. Graslandschaft
     mat.diffuseMap = 0;//TODO: Textur fuer Karte oder z.B. Graslandschaft
     mat.normalMap = 0;//TODO: Textur fuer Karte oder z.B. Graslandschaft
     if(kflag && kartenname[0]!=0)
      {
       mat.diffuseMap = karte_laden(kartenname);
       if(mat.diffuseMap!=0) mat.texturflag=1;
       else fprintf(stderr,"Fehler: konnte \"%s\" nicht laden\n",kartenname);//test
      }
     //uint32 meshid=addmesh(mat);
     addmesh(mat);
#ifdef _DEBUG
     printf("initcalc() Landschaft1: numMeshes=%ld\n",(long)numMeshes);//test
#endif
     double xmin = landschaft1.xmini, xmax = landschaft1.xmaxi; //Limits for plotted area
     double zmin = landschaft1.zmini; //Limits for plotted area
     //double zmin = landschaft1.zmini, zmax = landschaft1.zmaxi; //Limits for plotted area
     //printf(" Grenzen xmin,xmax,zmin,zmax: %f %f %f %f\n",xmin,xmax,zmin,zmax);//test
     //double x,z, dx=1.0, dz=1.0;
     double x,z, dx=100.0/N100, dz=100.0/N100; //TODO
     //int nmax=100; //Number of data point in x- and z-axis
     int nmax = (int)((xmax-xmin)/dx+0.5); //Number of data point in x- and z-axis
     if(nmax!=200) printf("xmin=%f xmax=%f nmax=%d\n",xmin,xmax,nmax);//test
     //double z, dz=(zmax-zmin)/nmax;
     //double x, dx=(xmax-xmin)/nmax;
     int jx,jz;
     Vertex vertex1,vertex2,vertex3,vertex4;
     Vec3 v1,v2,v3,v4,nv1,nv2, normalenvektor;
//#ifdef TEST_4DREIECKE_PRO_QUADRAT //Test fuer etwas bessere Aufloesung: doppelt soviele Dreiecke
     Vertex vertex5;
     Vec3 v5;
//#endif
     int index=0;
     float du,dv,u00,v00;
     
#ifdef SPECTPLORER
     if(Gxmax==0 || Kxmax==0)
      {
       du =  1.0/nmax;  dv =  1.0/nmax;
       u00 = 0.0f;  v00 = 1.0f;
      }
     else
      {
       double gxmin,gymin,gxmax,gymax,kxmin,kymin,kxmax,kymax;
       if(swissflag)
	{
	 wgs2swiss(Gxmin,Gymin,gxmin,gymin);
	 wgs2swiss(Gxmax,Gymax,gxmax,gymax);
	 wgs2swiss(Kxmin,Kymin,kxmin,kymin);
	 wgs2swiss(Kxmax,Kymax,kxmax,kymax);
	}
       else
	{
	 gxmin=Gxmin; gymin=Gymin; gxmax=Gxmax; gymax=Gymax;
	 kxmin=Kxmin; kymin=Kymin; kxmax=Kxmax; kymax=Kymax;
	}
       du = (gxmax-gxmin)/nmax/(kxmax-kxmin);
       dv = (gymax-gymin)/nmax/(kymax-kymin);
       u00 = (gxmin-kxmin)/(kxmax-kxmin);
       v00 = (gymax-kymin)/(kymax-kymin);
      }
#else //SPECTPLORER
     du =  1.0/nmax;  dv =  1.0/nmax;
     u00 = 0.0f;  v00 = 1.0f;
#endif //SPECTPLORER

     //printf("Beginn der Doppelschlaufe: zmin=%f bis jz<%d, xmin=%f bis jx<%d\n",zmin,nmax,xmin,nmax);//test
     for(jz=0,z=zmin;jz<nmax;jz++,z+=dz)
      {
       float v0 = v00 - dv*jz; //dv*(nmax-jz); //TODO
       //if(v0<0.0 || v0>1.0) printf("ungewoehnlicher Wert: v0 = %f\n",v0);//test
       //if(v0<0.0) v0=0.0; else if(v0>1.0) v0=1.0;//test
       //if(jz==0) printf("v0=%f\n",v0);//test
       for(jx=0,x=xmin;jx<nmax;jx++,x+=dx)
	{
	 float u0 = u00 + du*jx;
	 //if(u0<0.0 || u0>1.0) printf("ungewoehnlicher Wert: u0 = %f\n",u0);//test
	 //if(u0<0.0) u0=0.0; else if(u0>1.0) u0=1.0;//test
	 //if(jz==0 && jx==0) printf("u0=%f\n",u0);//test
	 v1.x=x;    v1.z=z;    v1.y=landschaft1.yf(x,z);
	 v2.x=x;    v2.z=z+dz; v2.y=landschaft1.yf(v2.x,v2.z);
	 v3.x=x+dx; v3.z=z+dz; v3.y=landschaft1.yf(v3.x,v3.z);
	 v4.x=x+dx; v4.z=z;    v4.y=landschaft1.yf(v4.x,v4.z);
//#ifdef TEST_4DREIECKE_PRO_QUADRAT //Test fuer etwas bessere Aufloesung: doppelt soviele Dreiecke
	 if(eflag)
	  {
	   v5.x=x+dx*0.5; v5.z=z+dz*0.5; v5.y=landschaft1.yf(v5.x,v5.z);
	   vertex5.position=v5;
	  }
//#endif
	 vertex1.position=v1; vertex2.position=v2; vertex3.position=v3; vertex4.position=v4;
#ifdef NOSMOOTH
	 nv1 = v2 - v1;
	 nv2 = v3 - v2;
	 normalenvektor = glm::normalize(glm::cross(nv1,nv2));
	 vertex1.normal = vertex2.normal = vertex3.normal = vertex4.normal = normalenvektor;
 //#ifdef TEST_4DREIECKE_PRO_QUADRAT
	 if(eflag) vertex5.normal = normalenvektor;
 //#endif
#else
	 vertex1.normal = landschaft1.normalenvektor_berechnen(v1,dx);
	 vertex2.normal = landschaft1.normalenvektor_berechnen(v2,dx);
	 vertex3.normal = landschaft1.normalenvektor_berechnen(v3,dx);
	 vertex4.normal = landschaft1.normalenvektor_berechnen(v4,dx);
 //#ifdef TEST_4DREIECKE_PRO_QUADRAT
	 if(eflag) vertex5.normal = landschaft1.normalenvektor_berechnen(v5,dx);
 //#endif
#endif
	 vertex1.uv.x = u0; vertex1.uv.y = v0;
	 vertex2.uv.x = u0; vertex2.uv.y = v0-dv;
	 vertex3.uv.x = u0+du; vertex3.uv.y = v0-dv;
	 vertex4.uv.x = u0+du; vertex4.uv.y = v0;
	 addVertex(vertex1); addVertex(vertex2); addVertex(vertex3); addVertex(vertex4);
//#ifdef TEST_4DREIECKE_PRO_QUADRAT
	 if(eflag)
	  {
	   vertex5.uv.x = u0+du*0.5; vertex5.uv.y = v0-dv*0.5;//TODO
	   addVertex(vertex5);
	   addIndex(index); addIndex(index+1); addIndex(index+4); //Dreieck p1 p2 p5
	   addIndex(index+1); addIndex(index+2); addIndex(index+4); //Dreieck p2 p3 p5
	   addIndex(index+2); addIndex(index+3); addIndex(index+4); //Dreieck p3 p4 p5
	   addIndex(index+3); addIndex(index); addIndex(index+4); //Dreieck p4 p1 p5
	   index+=5;
	  }
//#else
	 else
	  {
	   addIndex(index); addIndex(index+1); addIndex(index+2); //Dreieck p1 p2 p3
	   addIndex(index); addIndex(index+2); addIndex(index+3); //Dreieck p1 p3 p4
	   index+=4;
	  }
//#endif
	}
      }
     //printf("Ende Doppelschlaufe: total %d Vertexe erstellt.\n",index);//test
     //fertig von plotter2.cc kopiert.
    }
   else if(strcmp(zeile,"name: Teichgrund")==0)
    {
     //TODO
     printf("TODO: Teichgrund woanders definiert?\n");//test
    }
   else if(strncmp(zeile,"name: Teich",11)==0 || strcmp(zeile, "name: Meer")==0)
    {
     float wellenhoehe, x,y,z;
     Material mat;
     Vec3 mitte, vmin(0.0f), vmax(0.0f);
     float dx=0, dz;
     int teichnr=1;
     bool meerflag = (zeile[6]=='M');//test
     if(meerflag) teichnr=3;//test
     else if(isdigit(zeile[11])) sscanf(&zeile[11],"%d",&teichnr);
     --teichnr; //Zaehlung von 0 beginnend
#ifdef _DEBUG
     printf("teichnr=%d\n",teichnr);//test
#endif
     //provi. die Zeilen muessen genau in der Reihenfolge kommen, ohne Leerstellen am Anfang
     getline(fp,zeile,200); wellenhoehe=read_float(fp,zeile,"wasserwellenhoehe:");
     getline(fp,zeile,200); dx=read_float(fp,zeile,"wasserwellenlaenge:");
     if(dx<0.1f) {printf("Fehler: zu kurze Wasserwellenlange (%f), auf 0.1 gesetzt\n",dx); dx=0.1f;}
     dz=dx*wurzel3halbe;
     getline(fp,zeile,200); mitte=read_vec3(fp,zeile,"mitte:");
     float wasserstand=mitte.y;
     getline(fp,zeile,200); mat.diffuse=read_vec3col(fp,zeile,"diffuse:");
     mat.ambient = mat.diffuse;//TODO
     getline(fp,zeile,200); mat.specular=read_vec3col(fp,zeile,"specular:");
     mat.emissive = Vec3(0.0f); //provi. vorlaeufig kein leuchtendes Wasser
     getline(fp,zeile,200); mat.shininess=read_float(fp,zeile,"shininess:");
     //landschaft1.minmaxtest();//test
     uint32 meshid=addmesh(mat);
#ifdef _DEBUG
     printf("initcalc() Teich: numMeshes=%ld\n",(long)numMeshes);//test
#endif
     if(meerflag)
      {
       vmin.x=landschaft1.xmini2; vmax.x=landschaft1.xmaxi2;
       vmin.z=landschaft1.zmini2; vmax.z=landschaft1.zmaxi2;
      }
     else
      landschaft1.teich_minmax(wellenhoehe, mitte, dx, &vmin, &vmax);
     //in vmin vmax sollten jetzt die x-z-Eckpunkte relativ zu mitte stehen
#ifdef _DEBUG
     printf("vmin: %f %f %f  vmax: %f %f %f\n",vmin.x,vmin.y,vmin.z,vmax.x,vmax.y,vmax.z);//test
#endif
     uint32 xnmax=(int)((vmax.x-vmin.x)/dx+0.9f);
     uint32 znmax=(int)((vmax.z-vmin.z)/dz+0.9f);
     
     landschaft1.set(meshid,xnmax,znmax,wellenhoehe,wasserstand,teichnr,vmin+mitte,vmax+mitte);
     //float wastand = wasserstand+wellenhoehe;
     float wastand = wasserstand+wellenhoehe*3;//test
     Vertex vertex;
     vertex.normal = Vec3(0.0f);//muss spaeter noch gesetzt werden
     uint32 iz,ix;
     for(iz=0,z=vmin.z; iz<=znmax;iz++,z+=dz)
      {
       if(iz&1) x=vmin.x+dx*0.5f;//bei ungerader Zeile
       else     x=vmin.x;
       for(ix=0; ix<=xnmax; ix++,x+=dx)
	{
	 y = landschaft1.yf(mitte.x+x,mitte.z+z);
	 Wave wave;
	 //wave.alfa=0.0f; wave.period=0.25f;//test
	 if(y>wastand)
	  {
	   y=1e6f; //grosser Wert fuer unbenutzten Vertex
	   wave.alfa=0.0f; wave.period=1e9f;
	  }
	 else
	  {
	   if(wasserstand-y >= wellenhoehe)
	    {
	     wave.alfa = zufall(0.0f, 2*PI);
	     wave.period = zufall(0.2f, 0.4f);
	     y = sinf(wave.alfa)*wellenhoehe + wasserstand;
	    }
	   else
	    {
	     wave.alfa=0.0f; wave.period=1e9f;
	     y=wasserstand;
	    }
	  }
	 vertex.position = Vec3(x,y,z);
	 addVertex(vertex);
	 landschaft1.addWave(wave,teichnr);
	}
      }
#ifdef _DEBUG
     printf("initcalc() Teich: numVertices=%ld\n",(long)getNumVertices());//test
#endif

     uint32 j1,j2,j3,j4;
     Vec3 v1,v2,normalenvektor;
     Meshdata* meda=meshdatas[meshid];
     for(iz=0; iz<znmax-1; iz++)
      for(ix=0; ix<xnmax; ix++)
       {
	j1 = ix+iz*(xnmax+1);
	j2 = ix+(iz+1)*(xnmax+1);
	j3 = ix+1+(iz+1)*(xnmax+1);
	j4 = ix+1+iz*(xnmax+1);
	if((iz&1)==0) //wenn iz gerade: Dreiecke 1-2-4 und 2-3-4
	 {
	  if(meda->vertices[j1].position.y!=1e6f &&
	     meda->vertices[j2].position.y!=1e6f &&
	     meda->vertices[j4].position.y!=1e6f)
	   {
	    addIndex(j1); addIndex(j2); addIndex(j4);
	    v1 = meda->vertices[j2].position - meda->vertices[j1].position;
	    v2 = meda->vertices[j4].position - meda->vertices[j2].position;
	    normalenvektor = glm::normalize(glm::cross(v1,v2));
	    meda->vertices[j1].normal += normalenvektor;
	    meda->vertices[j2].normal += normalenvektor;
	    meda->vertices[j4].normal += normalenvektor;
	   }
	  if(meda->vertices[j2].position.y!=1e6f &&
	     meda->vertices[j3].position.y!=1e6f &&
	     meda->vertices[j4].position.y!=1e6f)
	   {
	    addIndex(j2); addIndex(j3); addIndex(j4);
	    v1 = meda->vertices[j3].position - meda->vertices[j2].position;
	    v2 = meda->vertices[j4].position - meda->vertices[j3].position;
	    normalenvektor = glm::normalize(glm::cross(v1,v2));
	    meda->vertices[j2].normal += normalenvektor;
	    meda->vertices[j3].normal += normalenvektor;
	    meda->vertices[j4].normal += normalenvektor;
	   }
	 }
	else //wenn iz ungerade: Dreiecke 1-2-3 und 1-3-4
	 {
	  if(meda->vertices[j1].position.y!=1e6f &&
	     meda->vertices[j2].position.y!=1e6f &&
	     meda->vertices[j3].position.y!=1e6f)
	   {
	    addIndex(j1); addIndex(j2); addIndex(j3);
	    v1 = meda->vertices[j2].position - meda->vertices[j1].position;
	    v2 = meda->vertices[j3].position - meda->vertices[j2].position;
	    normalenvektor = glm::normalize(glm::cross(v1,v2));
	    meda->vertices[j1].normal += normalenvektor;
	    meda->vertices[j2].normal += normalenvektor;
	    meda->vertices[j3].normal += normalenvektor;
	   }
	  if(meda->vertices[j1].position.y!=1e6f &&
	     meda->vertices[j3].position.y!=1e6f &&
	     meda->vertices[j4].position.y!=1e6f)
	   {
	    addIndex(j1); addIndex(j3); addIndex(j4);
	    v1 = meda->vertices[j3].position - meda->vertices[j1].position;
	    v2 = meda->vertices[j4].position - meda->vertices[j3].position;
	    normalenvektor = glm::normalize(glm::cross(v1,v2));
	    meda->vertices[j1].normal += normalenvektor;
	    meda->vertices[j3].normal += normalenvektor;
	    meda->vertices[j4].normal += normalenvektor;
	   }
	 }
       }
     //TODO: in jedem vertices noch Durchschnitt der Normalenvektoren berechnen
     //      geht anscheinend auch ohne!?
     //glm::normalize();
#ifdef _DEBUG
     printf("initcalc() Teich: numIndices=%ld\n", (long)meshdatas[numMeshes-1]->numIndices);//test
#endif
    }
   else if(strcmp(zeile,"name: Meer")==0)
    {
     printf("name: Meer - geht noch nicht\n");//test
    }
   else if(strncmp(zeile,"art:",4)==0)
    {
     printf("TODO: art:%s - noch nicht ausgewertet\n",&zeile[4]);//test
    }
   else
    {
     printf("Modeldata::initcalc() unbekannte Zeile: %s\n",zeile);//test
    }
  }
}

void Modeldata::recalc(float delta,int teichnr)
{
 uint32 meshid,xnmax,znmax;
 float wellenhoehe,wasserstand;
 landschaft1.get(&meshid,&xnmax,&znmax,&wellenhoehe,&wasserstand,teichnr);
 //printf("landschaft1.get(...) wasserstand=%f meshid=%d\n",wasserstand,meshid);//test
 Meshdata* meda=meshdatas[meshid];
 Vec3 v1,v2,normalenvektor;
 uint32 j,jmax=(znmax+1)*(xnmax+1);
 for(j=0; j<jmax; j++)
  if(meda->vertices[j].position.y!=1e6f)
    {
     //meda->vertices[j].position.y = zufall(-wellenhoehe,wellenhoehe) + wasserstand;//test
     meda->vertices[j].position.y = sinf(landschaft1.getalfa(j,delta,teichnr))*wellenhoehe + wasserstand;
     meda->vertices[j].normal = Vec3(0.0f);
    }
 uint32 numind=meda->numIndices, j1,j2,j3;
 for(j=0;j<numind;j+=3)
  {
   j1 = meda->indices[j];
   j2 = meda->indices[j+1];
   j3 = meda->indices[j+2];
   v1 = meda->vertices[j2].position - meda->vertices[j1].position;
   v2 = meda->vertices[j3].position - meda->vertices[j2].position;
   normalenvektor = glm::normalize(glm::cross(v1,v2));
   meda->vertices[j1].normal += normalenvektor;
   meda->vertices[j2].normal += normalenvektor;
   meda->vertices[j3].normal += normalenvektor;
  }
}

bool isttext(const char *str,int n)
{
 uint8_t c;
 for(int i=0;i<n;i++)
  {if((c=str[i])<' ' || c>'~') return false;}
 return true;
}

void Modeldata::init(const char* filename)
{
#ifdef AMBIENT_TEST
 printf("Modeldata::init(%s)\n",filename);//test
#endif
 std::ifstream input = std::ifstream(filename, std::ios::in | std::ios::binary);
 if(!input.is_open())
  {
   char filename2[400]="models/";
   char *s; int max=400; for(s=filename2;*s!=0;s++) {max--;}
   mystrncpy(s,filename,max);
   input = std::ifstream(filename2, std::ios::in | std::ios::binary);
   if(!input.is_open()) {printf("Error Modeldata::init(%s)\n",filename2); return;}
  }
 char bmfkopf[9];
 int bmfversion=0;
 uint32 numUvs=0;
 
 input.read(bmfkopf,8); bmfkopf[8]=0;
 if(isttext(bmfkopf,8) && strncmp(bmfkopf,"BMF",3)==0)
  {
   float bmfv=1.0; sscanf(&bmfkopf[3],"%f",&bmfv); bmfversion=(int)(bmfv*100+0.5);
   uint32 num=0;
   input.read((char*)&num, sizeof(uint32));
   numMeshes=num; //TODO: numMeshes direkt als uint32 definieren
   input.read((char*)&numUvs, sizeof(uint32));
  }
 else
  {
   numMeshes=0; //erste BMF-Version ohne Kopfdaten, erste 8 Bytes direkt numMeshes
   uint64 mask=0xFF;
   for(int i=0;i<8;i++)
    {numMeshes += ((bmfkopf[i]&mask)<<(i*8));}
   if(numMeshes>1000000) printf("Warnung: ungewoehnlich viele Meshes: %ld\n",(long)numMeshes);//test
   numUvs=0;
  }
#ifdef _DEBUG
 if(bmfversion>=200) //test
   printf("Modeldata::init() bmfversion=%d numMeshes=%ld\n",bmfversion,numMeshes);//test
 if(numUvs!=0) printf(" numUvs=%d\n",numUvs);//test
#endif
 
 for(uint64 j=0; j<numMeshes; j++)
  {
   Meshdata* mdata=new Meshdata();
   if(bmfversion>=200)
    {
     input.read((char*)&mdata->material, sizeof(MaterialOhneTextur));
     char texturfile[200], normtexturfile[200];
     int c=0,i;
     for(i=0; i<199 && (c=input.get())!=0; i++) {texturfile[i]=c;}
     texturfile[i]=0;
     for(i=0; i<199 && (c=input.get())!=0; i++) {normtexturfile[i]=c;}
     normtexturfile[i]=0;
     //if(*texturfile!=0) printf("Textur: %s\n",texturfile);//test
     if(*normtexturfile!=0) printf("Normalen-Textur: %s //TODO\n",normtexturfile);//test
     //if(*texturfile==0) printf(" ohne Textur\n");//test
     if(*texturfile)
      {
       mdata->material.texturflag = 1;//TODO
       mdata->material.diffuseMap = texturen_laden(texturfile);
      }
     if(*normtexturfile)
      {
       mdata->material.texturflag |= 2;//TODO
       mdata->material.normalMap = texturen_laden(normtexturfile);
      }
     }
   else 
    {//alte BMF-Version: ambient fehlt
     input.read((char*)&mdata->material.diffuse, sizeof(oldMaterial));
     mdata->material.ambient = mdata->material.diffuse;
     mdata->material.texturflag=0; //und keine Texturen
    }
#ifdef AMBIENT_TEST
   printf("mdata->ambient={%f %f %f}\n",mdata->material.ambient.x, mdata->material.ambient.y, mdata->material.ambient.z);//test
   printf("mdata->diffuse={%f %f %f}\n",mdata->material.diffuse.x, mdata->material.diffuse.y, mdata->material.diffuse.z);//test
#endif
   input.read((char*)&mdata->numVertices, sizeof(uint64));
   input.read((char*)&mdata->numIndices, sizeof(uint64));
   for(uint64 i=0; i<mdata->numVertices; i++)
     {
      Vertex vertex;
      input.read((char*)&vertex.position.x, sizeof(float));
      input.read((char*)&vertex.position.y, sizeof(float));
      input.read((char*)&vertex.position.z, sizeof(float));
      input.read((char*)&vertex.normal.x, sizeof(float));
      input.read((char*)&vertex.normal.y, sizeof(float));
      input.read((char*)&vertex.normal.z, sizeof(float));
      if(numUvs!=0) //nur wenn Textur vorhanden
       {
	input.read((char*)&vertex.uv.x, sizeof(float));
	input.read((char*)&vertex.uv.y, sizeof(float));
	//if(i<10) printf("vertex.uv={%f %f}\n",vertex.uv.x,vertex.uv.y);//test
       }
      mdata->vertices.push_back(vertex);
     }
   for(uint64 i=0; i<mdata->numIndices; i++)
     {
      uint32 index;
      input.read((char*)&index, sizeof(uint32));
      mdata->indices.push_back(index);
     }
   meshdatas.push_back(mdata);
  }
}

glm::vec3 Meshdata::summe_aller_vertices()
{
 glm::vec3 summe(0.0f);
 for(uint32 i=0;i<numVertices;i++)
   summe += vertices[i].position;
 return summe;
}

glm::vec3 Modeldata::mittelwert_aller_vertices()
{
 glm::vec3 summe(0.0f);
 uint32 sumNumVertices=0;
 for(uint32 i=0;i<numMeshes;i++)
  {
   summe += meshdatas[i]->summe_aller_vertices();
   sumNumVertices += meshdatas[i]->numVertices;
  }
 summe *= 1.0f/sumNumVertices;
 return summe;
}

uint32 Objekte::moveToScreen(uint32 id,uint32 klonid,Screen* screen)
{
 uint32 typ_herz=calc_typ("Herz");
 uint32 typ_ring=calc_typ("Ring");
 uint32 typ_pilz=calc_typ("Pilz");
 uint32 typ_tier=calc_typ("Tier");
 uint32 typ = modeldatas[id]->typ;
 //Matrix des entsprechenden Klones auf Identitaet setzen (und Position auf 0):
 klonlists[id]->klones[klonid].clearMatrix();
 
 Box box;
 box = getModelbox(id,klonid);
 glm::vec3 drehpunkt = box.getMitte();
 klonlists[id]->klones[klonid].setposition(-drehpunkt);
 //printf("box: minv={%f %f %f} maxv={%f %f %f}\n", box.minv.x,box.minv.y,box.minv.z,  box.maxv.x,box.maxv.y,box.maxv.z);//test
 //printf("box: Breite=%f Hoehe=%f Tiefe=%f\n",box.getBreite(),box.getHoehe(),box.getTiefe());//test
 //printf("drehpunkt={%f %f %f}\n",drehpunkt.x,drehpunkt.y,drehpunkt.z);//test
 
 //rotieren, skalieren, verschieben:
 //zuerst rotieren:
 if(typ==typ_pilz) //Pilze etwas anders rotieren
  { //Pilz von schraeg oben betrachtet dargestellt:
   klonlists[id]->klones[klonid].rotateXgrad(20);
  }
 else if(typ==typ_tier) //Tiere nochmals etwas anders rotieren
  { //Tier von der Seite gesehen dargestellt:
   klonlists[id]->klones[klonid].rotateYgrad(180);
  }
 else
  { //Herz und Ring von oben betrachtet dargestellt:
   klonlists[id]->klones[klonid].rotateXgrad(90); //Rotation der Matrix
   //klonlists[id]->klones[klonid].rotateYgrad(90); //ohne mitrotierendes Koord. waere rotateZgrad richtig
   //angepasst ohne mitrotierendes Koordinatensystem  (siehe Klon::rotateZ() Kommentare)
   klonlists[id]->klones[klonid].rotateZgrad(90); //test
  }
 
 //Wert fuer Skalierung berechnen:
 int maxbreite,maxhoehe; getmaxsize(&maxbreite, &maxhoehe);
 int breite = (typ==typ_ring) ? orte.symbolsizeBig[0] : orte.symbolsize[0];
 float sollbreite;
 screen->pixel2welt(breite, &sollbreite);
 glm::vec3 modelSize = modeldatas[id]->getModelsize();
 float istbreite = modelSize.x;
 //printf("sollbreite=%f istbreite=%f\n",sollbreite,istbreite);//test
 float faktor = sollbreite/istbreite;
//printf("Skalierfaktor zum auf Status-Screen anzeigen: %f\n",faktor);//test
 klonlists[id]->klones[klonid].scale(faktor);
 
 //Werte fuer Verschiebung an korrekte Position auf Status-Screen berechnen:
 float posx,posy;
 if(typ==typ_herz)
  {
   posy = orte.herzpos[1];
   posx = orte.herzpos[0] + orte.anzahlHerze * orte.herzabstand;
   orte.anzahlHerze++;
  }
 else if(typ==typ_ring)
  {
   posy = orte.ringpos[1];
   posx = orte.ringpos[0] - orte.anzahlRinge * orte.symbolabstandBig;
   orte.anzahlRinge++;
  }
 else
  {
   //printf("anderes Objekt: typ = %d = \"%s\"\n",typ,typ2str(typ));//test
   posy = orte.untenlinks[1]; //TODO: Positionen fuer andere Objekte ev. anders definieren
   posx = orte.untenlinks[0] + orte.anzahlAndere * orte.symbolabstand;
   orte.anzahlAndere++;
  }
 //printf("posx=%f posy=%f\n",posx,posy);//test
 klonlists[id]->klones[klonid].setoffset(glm::vec3(posx, posy, 0.0f));
 klonlists[id]->klones[klonid].screenId = screen->screenId;
 
 //printf("Testpunkt in moveToScreen(): update(id=%d, klonid=%d, screen->shader)\n",id,klonid);//test
 //update(id, klonid, screen->shader); //nicht mehr noetig
 return typ;
}

uint32 Objekte::addKlon(uint32 modelid, glm::vec3 position, glm::mat4 matrix)
{
 if(modelid>=numModels)
  {fprintf(stderr,"Error: Objekte::addKlon(%d ...)\n",modelid); return 0;}
 int klonid=klonlists[modelid]->numKlones;
 Klon klon;
 //klon.clearMatrix();
 klon.modelMatrix = matrix;
 klon.position = position;
 klon.typ = modeldatas[modelid]->typ;
 klonlists[modelid]->add(klon);
 return klonid;
}

uint32 Objekte::removeKlon(uint32 modelid) //TODO: rueckgaengig machen von addKlon()
{
 int klonid=klonlists[modelid]->numKlones;
 if(klonid==0) {printf("Fehler: removeKlon() kein Klon vorhanden\n"); return klonid;}//test
 klonlists[modelid]->remove();
 return klonid-1;
}

int wassertestflag=0;//test

//zeichne alle Klone mit entsprechender screenId mit Beruecksichtigung von modelMatrix
void Objekte::render(uint8 screenid,MyCamera* camera, Shader* shader)
{
 if(wassertestflag==1) printf("Objekte::render()\n");//test
 for(uint32 i=0; i<numModels; i++)
  for(uint32 k=0; k<klonlists[i]->numKlones; k++)
   {
    if(klonlists[i]->klones[k].screenId==screenid)
     {
      if(wassertestflag==1) //test
       {printf("i=%d k=%d\n",i,k);//test
	printf("modelMatrix="); printmatrix(klonlists[i]->klones[k].modelMatrix);//test
       }
      matrizen_an_GPU_senden(camera, klonlists[i]->klones[k].modelMatrix, shader);
      models[i]->render();
     }
   }
}

bool gueltigeposition(float x,float y,float z)
{
 return (x>= -100 && x<=100 && y>= -100 && y<=100);
}

void Objekte::init(const char *filename, Shader *shader)
{
 FILE *fp=myfopen(filename,"r");
 if(fp==NULL) {printf("Error: %s not found\n",filename); return;}
 char zeile[200];
 uint32 klonid=0;
 uint32 art=0; //Art des Objekts zum unterscheiden zwischen Sammelobjekt und anderen
 glm::vec3 position0(0.0f);
 glm::vec3 position(0.0f);
 glm::mat4 identMatrix(1.0f);
 if(argflag['V']) printf("Objekte::init(%s, ...)\n",filename);//test
 while(getline(fp,zeile,200))
  {
   //printf("Zeile mit Kommentaren: \"%s\"\n",zeile);//test
   if(zeile[0]=='#' || (zeile[0]=='/' && zeile[1]=='/')) continue;//Kommentare ueberlesen
   kommentare_entfernen(zeile);
   //printf("Zeile ohne Kommentare: \"%s\"\n",zeile);//test
   if(zeile[0]==0) continue;//Leerzeilen ignorieren
   if(ist_stlname(zeile))
    {
     printf("Fehler in Objekte::init(\"%s\", ...)\n",filename);
     printf("STL-Dateien einlesen geht nicht - BMF-Dateien verwenden.\n");
     fclose(fp); return;//provi.
    }
   else if(ist_bmfname(zeile))
    {
     Modeldata *modeldata=new Modeldata;
     modeldata->init(zeile); //bmf-Datei Modell-Daten einlesen (Daten fuer CPU speichern)
     modeldatas.push_back(modeldata);
     numModeldatas++;
     Model* model=new Model;
     model->init2(modeldata,shader);
     models.push_back(model);
     uint32 modelid=numModels;
     numModels++;
     Klonlist* klonlist=new Klonlist;
     klonlists.push_back(klonlist);
     klonid = addKlon(modelid, position0, identMatrix);
     if(argflag['V']) printf("BMF-Datei eingelesen, klonid=%d\n",klonid);//test
    }
   else if(ist_calcname(zeile))
    {
     Modeldata *modeldata=new Modeldata;
     modeldata->initcalc(zeile); //calc-Datei Modell-Daten einlesen (Daten fuer CPU speichern)
     modeldatas.push_back(modeldata);
     numModeldatas++;
     Model* model=new Model;
     model->init2(modeldata,shader);
     models.push_back(model);
     uint32 modelid=numModels;
     numModels++;
     Klonlist* klonlist=new Klonlist;
     klonlists.push_back(klonlist);
     klonid = addKlon(modelid, position0, identMatrix);
     if(argflag['V']) printf("CALC-Datei eingelesen, klonid=%d\n",klonid);//test
    }
   else if(strncmp(zeile,"klon",4)==0)
    {
     //Klon vom vorherigen Objekt erzeugen:
     bool err = (numModels==0);
     if(err) printf("Error: no Modeldatas to create klon\n");
     uint32 modelid=numModels-1;
     if(!err)
      {
       int i=4; if(zeile[i]==':') i++;
       for(; zeile[i]==' '; i++) {}
       if(zeile[i]!=0)
	{
	 uint32 typ=calc_typ(&zeile[i]);
	 err = (modeldatas[modelid]->typ != typ);
	 if(err) printf("Error: nur Klon vom zuletzt eigelesenen File moeglich\n");
	}
      }
     if(!err)
      {
       klonid = addKlon(modelid, position0, identMatrix);
       if(argflag['V']) printf(" Klon erstellt, klonid=%d\n",klonid);//test
      }
    }
   else if(strncmp(zeile,"position:",9)==0 ||
	   strncmp(zeile,"gpsposition:",12)==0 || strncmp(zeile,"gpxposition:",12)==0)
    {
     const char *zz= &zeile[9];
#ifdef MIT_GPX
     int gpsflag=0;
     if(strncmp(zeile,"gp",2)==0) {zz= &zeile[12]; gpsflag=1;}
#endif
     float32 x,y,z;
     bool klonignore=false; //zum ignorieren zu vieler Faehnchen //TODO
     bool positionok=true; //TODO: false wenn gpsposition nicht im dargestellten Bereich
     if(strncmp(zz," gpx_startpos",13)==0)
      {
       x=gpx_startpos.x; z=gpx_startpos.z; y=landschaft1.yf(x,z);
      }
     else if(strncmp(zz," gpx_zielpos",12)==0)
      {
       x=gpx_zielpos.x; z=gpx_zielpos.z; y=landschaft1.yf(x,z);
      }
     else if(strncmp(zz," gpx_gipfelpos",14)==0)
      {
       x=gpx_gipfelpos.x; z=gpx_gipfelpos.z;
       if(argflag['D']) y=landschaft1.yf(x,z);
       else y=gpx_gipfelpos.y; 
      }
     else if(strncmp(zz," gpx_tiefpunkt",14)==0)
      {
       x=gpx_tiefpunkt.x; y=gpx_tiefpunkt.y; z=gpx_tiefpunkt.z;
       y = landschaft1.yf(x,z);
       float xmi=x,ymi=y,zmi=z;
       //tiefster Punkt in der Umgebung suchen:
       float dd=0.125;
       for(int ix= -12; ix<=12; ix++)
	for(int iz= -12; iz<=12; iz++)
	 if(ix!=0 || iz!=0)
	  {
	   float x2 = x+ix*dd, z2 = z+iz*dd;
	   float y2 = landschaft1.yf(x2,z2);
	   if(y2<ymi) {ymi=y2; xmi=x2; zmi=z2;}
	  }
       x=xmi; y=ymi; z=zmi;
      }
     else if(strncmp(zz," gpx_wegpunkte",14)==0)
      {
#ifdef MIT_GPX
       int i=0;
       if(zeile[9+14]=='[') sscanf(&zeile[9+14+1],"%d",&i);
       if((int)gpx_wegpunkte.size() > i)
	{
	 x=gpx_wegpunkte[i].x; z=gpx_wegpunkte[i].z;
	 y = landschaft1.yf(x,z);
	 if(i>=iwpkt) klonignore=true;
	}
       else
	{
	 klonignore=true;
	 //printf("keine gpx_wegpunkte[] gesetzt\n");//test
	}
#else
       printf("Fehler: keine GPX-Unterstuetzung hier\n");//test
#endif
      }
     else
      {
       sscanf(zz,"%f%*c%f%*c%f",&x,&y,&z);
#ifdef MIT_GPX
       if(gpsflag)
	{
	 //printf("gpsposition vor Umrechnung: %f %f %f\n",x,y,z);//test
	 if(bflag) {wgs2swiss(x,y);}
	 glm::vec3 v(x,y,z);
	 vec3scal(v);
	 x=v.x; y=v.y; z=v.z;
	 if(gueltigeposition(x,y,z)) {y=landschaft1.yf(x,z); positionok=true;}
	 else positionok=false;
	 //printf("gpsposition umgerechnet: %f %f %f\n",x,y,z);//test
	}
#endif
      }
     if(numModels==0) printf("Error: no Modeldatas to set position\n");
     else if(positionok)
      {
       uint32 modelid=numModels-1;
       if(klonignore)
	{removeKlon(modelid);} //vorheriges addKlon() rueckgaengig machen
       else
	{
	 klonlists[modelid]->klones[klonid].setposition(glm::vec3(x,y,z));
	 position.x=x; position.y=y; position.z=z;
	}
      }
     else //if(positionok==false)
      {//Position ausserhalb Bereich, also Modell wieder entfernen:
       uint32 modelid=numModels-1;
       removeKlon(modelid);
      }
    }
   else if(strncmp(zeile,"name:",5)==0)
    {
     int i;
     for(i=5;zeile[i]==' ';i++) {}
     if(argflag['V']) printf("eingelesenes Modell: \"%s\"\n",&zeile[i]);//test
     modeldatas[numModels-1]->setName(&zeile[i]);
     modeldatas[numModels-1]->setArt(art);
     if(strcmp(&zeile[i],"Sonne")==0)
      {
       sonne_id = numModels-1;
      }
    }
   else if(strncmp(zeile,"art:",4)==0)
    {
     int i=4; while(zeile[i]==' ') i++;
     if(strncmp(&zeile[i],"sammelobjekt",12)==0) art=ART_SAMMELOBJEKT;
     else if(strncmp(&zeile[i],"fixesobjekt",11)==0) art=ART_FIXESOBJEKT;
     else if(strncmp(&zeile[i],"losesobjekt",11)==0) art=ART_LOSESOBJEKT;
     else if(strncmp(&zeile[i],"wasserobjekt",11)==0) art=ART_WASSEROBJEKT;
     else printf("Error: wrong art \"%s\"\n",&zeile[i]);
    }
   else if(strncmp(zeile,"kollform:",9)==0)
    {
     int i=9; while(zeile[i]==' ') i++;
     Kollform form;
     if(strncmp(&zeile[i],"kug",3)==0)
      {form.form=FORM_KUGEL; form.radius=read_float(zeile,&i);}
     else if(strncmp(&zeile[i],"zyl",3)==0)
      {
       form.form=FORM_ZYLINDER; form.radius=read_float(zeile,&i); form.hoehe=read_float(zeile,&i);
       form.fusspunkt = form.kopfpunkt = position; form.kopfpunkt.y += form.hoehe;
      }
     else if(strncmp(&zeile[i],"qua",3)==0)
      {
       form.form=FORM_QUADER; form.beta=read_float(zeile,&i);
       form.laenge=read_float(zeile,&i); form.hoehe=read_float(zeile,&i); form.breite=read_float(zeile,&i);
       form.fusspunkt = form.kopfpunkt = position; form.kopfpunkt.y += form.hoehe;
      }
     else printf("Error: wrong kollform \"%s\"\n",&zeile[i]);
     if(numModels==0) printf("Error: no Modeldatas to set kollform\n");
     else
      {
       uint32 modelid=numModels-1;
       klonlists[modelid]->klones[klonid].setkollform(form);
      }
    }
   else if(strncmp(zeile,"scale:",6)==0)
    {
     if(numModels==0) printf("Error: no Modeldatas to scale\n");
     float sx=1,sy=1,sz=1;
     int n=sscanf(&zeile[6],"%f%*c%f%*c%f",&sx,&sy,&sz);
     //if(n==3 && sx==0.4) printf("Test: scale: vermutlich vom Loorenkopfturm\n");//test
     //if(n==3 && sx==0.5) printf("Test: scale: vermutlich vom Forchdenkmal\n");//test
     if(n!=3)
      {if(n==1) sy=sz=sx; else printf("Error: missing data for scale\n");}
     uint32 modelid=numModels-1;
     if(globalScale!=1.0) {sx *= globalScale; sy *= globalScale; sz *= globalScale;}//TODO
     klonlists[modelid]->klones[klonid].scale(sx,sy,sz);
    }
   else if(strncmp(zeile,"rotatex:",8)==0) //test
    {
     if(numModels==0) printf("Error: no Modeldatas for rotateX\n");
     float winkel=0;
     sscanf(&zeile[8],"%f",&winkel);
     uint32 modelid=numModels-1;
     klonlists[modelid]->klones[klonid].rotateX(winkel*GRAD);
    }
   else if(strncmp(zeile,"rotatey:",8)==0) //test
    {
     if(numModels==0) printf("Error: no Modeldatas for rotateY\n");
     float winkel=0;
     sscanf(&zeile[8],"%f",&winkel);
     uint32 modelid=numModels-1;
     klonlists[modelid]->klones[klonid].rotateY(winkel*GRAD);
    }
   else if(strncmp(zeile,"rotatez:",8)==0) //test
    {
     if(numModels==0) printf("Error: no Modeldatas for rotateZ\n");
     float winkel=0;
     sscanf(&zeile[8],"%f",&winkel);
     uint32 modelid=numModels-1;
     klonlists[modelid]->klones[klonid].rotateZ(winkel*GRAD);
    }
   else
    {
     printf("Objekte::init() unbekannte Zeile: \"%s\"\n",zeile);//test
    }
  }
 fclose(fp);
 calculate_kollBoxes();
#ifdef MIT_GPX
 if(linienzuege.size()!=0)
  {
   if(argflag['V']) printf("Objekte::init(%s,..) linienzuege.size()=%d\n",filename,(int)linienzuege.size());//test
   art=ART_LINIENOBJEKT;
   Modeldata *modeldata=new Modeldata;
   modeldata->initline();
   modeldatas.push_back(modeldata);
   numModeldatas++;
   Model* model=new Model;
   model->init2(modeldata,shader);
   models.push_back(model);
   uint32 modelid=numModels;
   numModels++;
   Klonlist* klonlist=new Klonlist;
   klonlists.push_back(klonlist);
   klonid = addKlon(modelid, position0, identMatrix);
   modeldatas[modelid]->setName("Linienzug");
   modeldatas[modelid]->setArt(art);
   if(argflag['V']) printf("Linienobjekt erstellt, klonid=%d\n",klonid);//test
   linienzuege.clear();//test
  }
#endif
}

void Modeldata::initline()
{
#ifdef MIT_GPX
 //std::vector<Linienzug> linienzuege; //als globale Variable in gpxread.h
 numMeshes=linienzuege.size();
 for(uint32 j=0; j<numMeshes; j++)
  {
   Meshdata* mdata=new Meshdata();
   mdata->material = linienzuege[j].material;

   mdata->numVertices = linienzuege[j].numPunkte;
   mdata->numIndices = linienzuege[j].numPunkte; //fuer GL_LINE_STRIP
   //mdata->numIndices = linienzuege[j].numPunkte*2-2; //fuer GL_LINES
   for(uint32 i=0; i<mdata->numVertices; i++)
     {
      Vertex vertex;
      vertex.position = linienzuege[j].punkte[i];
      vertex.normal = Vec3{0.0f, 1.0f, 0.0f};
      mdata->vertices.push_back(vertex);
     }
   for(uint32 i=0; i<mdata->numIndices; i++) //fuer GL_LINE_STRIP
     {
      uint32 index=i;
      mdata->indices.push_back(index);
     }
   mdata->linienflag=1;//test
   meshdatas.push_back(mdata);
  }
#else
 printf("Fehler: Linienzuege hier nicht gebraucht\n");//test
#endif
}

void Klon::setkollform(Kollform& form)
{
 kollform=form;
 //TODO
}

void Objekte::calculate_kollBoxes()
{
 Box box;
 for(uint32 modelid=0; modelid<numModels; modelid++)
  for(uint32 klonid=0; klonid < klonlists[modelid]->numKlones; klonid++)
   {
    box = getModelbox(modelid, klonid);
    klonlists[modelid]->klones[klonid].kollBox = box;
   }
}

glm::vec3 Objekte::getPosition(uint32 modelid,uint32 klonid)
{
 return klonlists[modelid]->klones[klonid].position;
}

void Objekte::setPosition(uint32 modelid,uint32 klonid,glm::vec3 pos)
{
 klonlists[modelid]->klones[klonid].setposition(pos);
}

void Objekte::setColor(uint32 modelid,glm::vec3 color, Shader *shader)
{
 modeldatas[modelid]->setColor(color);
 delete models[modelid];
 models[modelid] = new Model;
 models[modelid]->init2(modeldatas[modelid], shader);
}

void Meshdata::setColor(glm::vec3 color) //TODO
{
 material.ambient = color;
 material.diffuse = color;
 material.specular = color;
 material.emissive = color;
}

void Animobjekt::init(const char *filename, Shader *shader)
{
 FILE *fp=myfopen(filename,"r");
 if(fp==NULL) {printf("Error: %s not found\n",filename); return;}
 char zeile[200];
 bool modflag=false;
 if(argflag['V']) printf("Testpunkt Animobjekt::init(%s, ...)\n",filename);//test
 while(getline(fp,zeile,200))
  {
   if(zeile[0]=='#' || (zeile[0]=='/' && zeile[1]=='/')) continue;//Kommentare ueberlesen
   kommentare_entfernen(zeile);
   if(zeile[0]==0) continue;//Leerzeilen ignorieren
   //printf("Testpunkt2 zeile=\"%s\"\n",zeile);//test
   if(myindex(zeile,':')==NULL && modflag) //wenn Zeile keinen Doppelpunkt enthaelt
    {
     delete models[numModels-1];
     models[numModels-1] = new Model; //model nochmals neu erzeugen mit korrigierten Daten
     models[numModels-1]->init2(modeldatas[numModels-1], shader); //in mesh.h in class Model
     modflag=false;
    }
   if(ist_stlname(zeile))
    {
     //TODO
     printf("Fehler in Animobjekt::init(\"%s\", ...)\n",filename);
     printf("STL-Dateien einlesen geht noch nicht - BMF-Dateien verwenden.\n");
     fclose(fp); return;//provi.
    }
   else if(ist_bmfname(zeile))
    {
     Modeldata *modeldata=new Modeldata;
     modeldata->init(zeile); //bmf-Datei Modell-Daten einlesen (Daten fuer CPU speichern)
     modeldatas.push_back(modeldata);
     numDatas++;
     Model* model=new Model;
     model->init2(modeldata,shader);
     models.push_back(model);
     numModels++;
    }
   else if(strncmp(zeile,"offset:",7)==0)
    {
     float32 x,y,z;
     sscanf(&zeile[7],"%f, %f, %f",&x,&y,&z);
     modeldatas[numModels-1]->setoffset(x,y,z);
     modflag=true;
    }
   else if(strncmp(zeile,"name:",5)==0)
    {
     int i;
     for(i=5;zeile[i]==' ';i++) {}
     if(argflag['V']) printf("eingelesenes Modell: \"%s\"\n",&zeile[i]);//test
     modeldatas[numModels-1]->setName(&zeile[i]);
    }
   else if(strncmp(zeile,"animsteps:",10)==0)
    {
     //int steps;
     //uint16 usteps;
     //sscanf(&zeile[10],"%d",&steps); usteps=(uint16)steps;
     //TODO: wenn mehrere Animierte Objekte eingelesen werden sollen
     printf("TODO: animsteps:\n");//test
    }
   else
    {
     printf("unbekannte Zeile: \"%s\"\n",zeile);//test
    }
  }
 if(modflag)
  {
   delete models[numModels-1];
   models[numModels-1] = new Model;
   models[numModels-1]->init2(modeldatas[numModels-1], shader);
  }
 fclose(fp);
 calculate_kollBox();
}

void Animobjekt::calculate_kollBox()
{
 Box box;
 //for(uint32 modelid=0; modelid<numDatas; modelid++)
 uint32 modelid=0; //provisorisch nur Ruhezustand fuer kollBox verwenden
  {
   for(uint32 i=0; i < modeldatas[modelid]->numMeshes; i++)
    {
     modeldatas[modelid]->meshdatas[i]->getMinMaxPos(&box);
    }
  }
 kollBox=box;
}

/*
void Objekte::update(uint32 modelid, uint32 klonid, Shader *shader)
{
 //printf("Objekte::update(modelid=%d, klonid=%d, shader)\n",modelid,klonid);//test
 delete models[modelid];
 models[modelid] = new Model;
 models[modelid]->init2(modeldatas[modelid], shader);
}
*/

float absvec3(glm::vec3 v)
{
 return sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);
}

void Animobjekt::animate(uint32 frameid, glm::vec3 animOffset, float animRotate)
{
 if(frameid>=numDatas)
  {fprintf(stderr,"Error: Animobjekt::animate(%d, ...) numDatas=%d\n",frameid,numDatas);
   index=0;
   return;
  }
 //printf("Animobjekt::animate(frameid=%d, animOffset={%f %f %f}, animRotate=%f)\n",
 //       frameid, animOffset.x, animOffset.y, animOffset.z, animRotate);//test
 index = frameid;
 glm::vec3 diffposition = animOffset - position;
 position = animOffset;
 yrotation = animRotate;
 modelMatrix = glm::rotate(glm::mat4(1.0f), yrotation, glm::vec3(0, 1, 0)); 
 glm::mat4 translateMatrix = glm::translate(glm::mat4(1.0f), position);
 modelMatrix = translateMatrix * modelMatrix;
 kollBox.addOffset(diffposition);
}

void Animobjekt::render(MyCamera* camera, Shader* shader)
{
 //printf("Animobjekt::render() index=%d status=%d\n",index,status);//test
 if(status==1) //TODO
  {
   matrizen_an_GPU_senden(camera, modelMatrix, shader);
   models[index]->render();
  }
 else printf("TODO: Animobjekt::status richtig auswerten\n");//test
}
