/*
 *  Copyright (C) 2008 Kristian Kristola
 *
 *  This program is distributed under the terms of the
 *  GNU General Public License.
 *
 *  This file is part of Dave the Ordinary Spaceman.
 *
 *  Dave the Ordinary Spaceman 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 3 of the License, or (at your option) any later version.
 *
 *  Dave the Ordinary Spaceman is distributed in the hope that it
 *  will be useful, but WITHOUT ANY WARRANTY; without even the
 *  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE.  See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Dave the Ordinary Spaceman. If not, see
 *  <http://www.gnu.org/licenses/>.
 *
 */

#include <iostream>
#include <assert.h>
#include <lib3ds/chunk.h>
#include <lib3ds/file.h>
#include <lib3ds/node.h>
#include <lib3ds/mesh.h>
#include <lib3ds/vector.h>
#include <lib3ds/matrix.h>
#include <lib3ds/material.h>
#include <lib3ds/light.h>
#include <SDL/SDL.h>
#include "model.h"
#include "image.h"
#include "kdtree.h"

Model::Model()
{
  init();
}

Model::~Model()
{
}

Model::Model(const string &name)
{
  init();
  load(name);
}

void Model::init()
{
  scene = NULL;

  texture           = NULL;
  self_illumination = NULL;
  environment       = NULL;
  reflectivity      = NULL;

  shader_built = false;
  using_shader = false;

  position[0] = 0.0f;
  position[1] = 0.0f;
  position[2] = 0.0f;
  rotation[0] = 0.0f;
  rotation[1] = 0.0f;
  rotation[2] = 0.0f;
  scale[0] = 1.0f;
  scale[1] = 1.0f;
  scale[2] = 1.0f;

  is_3ds = false;
  blend = 0;
  lighting = true;

  has_ao = false;
}

void Model::set_position(float x, float y, float z)
{
  position[0] = x;
  position[1] = y;
  position[2] = z;
}

void Model::set_rotation(float x, float y, float z)
{
  rotation[0] = x;
  rotation[1] = y;
  rotation[2] = z;
}

void Model::set_scale(float x, float y, float z)
{
  scale[0] = x;
  scale[1] = y;
  scale[2] = z;
}

void Model::allocate(unsigned int vertex_count, unsigned int index_count, bool normals, bool texcoords, bool colors)
{
  Model::vertex_count = vertex_count;
  Model::index_count = index_count;
  Model::indices   = index_count ? new GLuint [index_count]       : NULL;
  Model::vertices  =               new GLfloat [3 * vertex_count];
  Model::normals   = normals     ? new GLfloat [3 * vertex_count] : NULL;
  Model::texcoords = texcoords   ? new GLfloat [2 * vertex_count] : NULL;
  Model::colors    = colors      ? new GLfloat [4 * vertex_count] : NULL;
}

void Model::upload()
{
  glGenBuffersARB(1, &vertex_buf);
  glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertex_buf);
  glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertex_count * 3 * sizeof(GLfloat), vertices, GL_STATIC_DRAW_ARB);

  if (texcoords) {
    glGenBuffersARB(1, &texcoord_buf);
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, texcoord_buf);
    glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertex_count * 2 * sizeof(GLfloat), texcoords, GL_STATIC_DRAW_ARB);
  }
  else
    texcoord_buf = 0;

  if (normals) {
    glGenBuffersARB(1, &normal_buf);
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, normal_buf);
    glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertex_count * 3 * sizeof(GLfloat), normals, GL_STATIC_DRAW_ARB);
  }
  else
    normal_buf = 0;

  if (colors) {
    glGenBuffersARB(1, &color_buf);
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, color_buf);
    glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertex_count * 4 * sizeof(GLfloat), colors, GL_STATIC_DRAW_ARB);
  }
  else
    color_buf = 0;

  if (indices) {
    glGenBuffersARB(1, &index_buf);
    glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, index_buf);
    glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, index_count * sizeof(GLuint), indices, GL_STATIC_DRAW_ARB);
  }
  else
    index_buf = 0;
}

void Model::calculate_ao()
{
  KDTree kdtree;
  int hash = 0;

  build_kdtree(kdtree, hash);

  char buf[64];
  sprintf(buf, "aocache%x.tmp", hash);

  FILE* fp = fopen(buf, "rb");
  if (fp)
  {
    if (!color_buf)
    {
      glGenBuffersARB(1, &color_buf);
      glBindBufferARB(GL_ARRAY_BUFFER_ARB, color_buf);
      glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertex_count * 4 * sizeof(GLfloat), colors, GL_STATIC_DRAW_ARB);
    }

    glBindBufferARB(GL_ARRAY_BUFFER_ARB, color_buf);
    float* ptr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
    int n = fread(ptr, 4 * sizeof(float), vertex_count, fp);
    glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
    fclose(fp);
    if (n == vertex_count)
    {
      printf("AO loaded from cache\n");
      return;
    }
  }

  kdtree.update();

  shoot_ao_rays(kdtree);

  fp = fopen(buf, "wb");
  if (fp)
  {
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, color_buf);
    float* ptr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_READ_ONLY_ARB);
    fwrite(ptr, 4 * sizeof(float), vertex_count, fp);
    glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
    fclose(fp);
  }
}

void Model::build_kdtree(KDTree& kd, int& hash)
{
  has_ao = true;

  glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertex_buf);
  float* ptr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_READ_ONLY_ARB);

  std::vector<Vector3> verts;
  int* tris = new int [vertex_count];

  for (int i = 0; i < (int)vertex_count; i++)
  {
    verts.push_back(Vector3(ptr[i*3], ptr[i*3+1], ptr[i*3+2]));
    tris[i] = i;

    hash ^= *(int*)&ptr[i*3];
    hash ^= *(int*)&ptr[i*3+1];
    hash ^= *(int*)&ptr[i*3+2];
  }

  glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
  glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

  kd.feedTriangles(verts, tris, vertex_count / 3);

  for (list<Model *>::const_iterator i = children.begin(); i != children.end(); ++i)
    (*i)->build_kdtree(kd, hash);
}

inline float halton2(int k)
{
	int ret = 0;
	int n = 1;

	while (k)
	{
		ret <<= 1;
		if (k & 1)
			ret |= 1;
		k >>= 1;
		n <<= 1;
	}

	return ret / (float)n;
}

void Model::shoot_ao_rays(KDTree& kd)
{
  if (!color_buf)
  {
    glGenBuffersARB(1, &color_buf);
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, color_buf);
    glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertex_count * 4 * sizeof(GLfloat), colors, GL_STATIC_DRAW_ARB);
  }

  glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertex_buf);
  float* ptr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_READ_ONLY_ARB);
  std::vector<Vector3> verts;
  for (int i = 0; i < vertex_count; i++)
    verts.push_back(Vector3(ptr[i*3], ptr[i*3+1], ptr[i*3+2]));
  glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);

  glBindBufferARB(GL_ARRAY_BUFFER_ARB, normal_buf);
  ptr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_READ_ONLY_ARB);
  std::vector<Vector3> norms;
  for (int i = 0; i < vertex_count; i++)
    norms.push_back(Vector3(ptr[i*3], ptr[i*3+1], ptr[i*3+2]));
  glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);

  glBindBufferARB(GL_ARRAY_BUFFER_ARB, color_buf);
  ptr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);

  for (int i = 0; i < vertex_count; i++)
  {
    printf("Calculating AO: %.1f%\r", i / (float)vertex_count * 100.f);

    Vector3 zv = normalize(norms[i]);
    Vector3 xv = normalize(cross(Vector3(.123f, .166f, .643f), zv));
    Vector3 yv = normalize(cross(zv, xv));

    int ao = 0;
    const int num_samples = 2048;

    for (int j = 0; j < num_samples; j++)
    {
      float u, v;
      //u = rand() / (float)RAND_MAX;
      //v = rand() / (float)RAND_MAX;
      u = (j + 0.5f) / (float)num_samples;
      v = halton2(j+1);

      u *= 2.f * 3.14159265f;
      v = sqrtf(v);

      float x = sinf(u) * v;
      float y = cosf(u) * v;
      float z = sqrtf(1.f - v*v);

      Vector3 d = xv * x + yv * y + zv * z;
      Vector3 p = verts[i] + zv * 0.001f;

      if (kd.shadowIntersect(p, d, 1000.0f))
        ao++;
    }

    // sqrtf to make AO a bit stronger
    float v = 1.f - sqrtf(ao / (float)num_samples);

    ptr[i*4+0] = v;
    ptr[i*4+1] = v;
    ptr[i*4+2] = v;
    ptr[i*4+3] = 1.f;
  }

  printf("AO calculated                              \n");

  glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
  glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

  for (list<Model *>::const_iterator i = children.begin(); i != children.end(); ++i)
    (*i)->shoot_ao_rays(kd);
}

void Model::load(const string &name)
{
  ////////////// ASDF ////////////////
#if 0
  Lib3dsFile *asdf = lib3ds_file_load("asdf.3ds");
  assert(asdf);
  Lib3dsMesh *bsdf = asdf->meshes;
  assert(bsdf);
  cerr << "Näin monta verteksiä: " << bsdf->points << endl;
  return;
#endif
  ////////////////////////////////////

  cerr << "Loading " << name << "...";

  //lib3ds_chunk_enable_dump(1, 1);

  char cname[1024];
  strcpy(cname, name.c_str());
  Lib3dsFile *file = lib3ds_file_load(cname);
  if (!file) {
    cerr << "ERROR" << endl;
    throw;
  }

  //cerr << "dumpataan\n";
  //cout << "dumpataan\n";

  //lib3ds_file_dump_meshes(file);

  lib3ds_file_eval(file, 0);

  Lib3dsMesh *mesh;
  int count = 0;
  Model *submesh = this;
  for (mesh = file->meshes; mesh != NULL; mesh = mesh->next, count++) {
    if (count)
      submesh = new Model;

    Lib3dsVector *normals = static_cast<float(*)[3]>(malloc(3 * sizeof(Lib3dsVector) * mesh->faces));
    Lib3dsMatrix m;
    lib3ds_matrix_copy(m, mesh->matrix);
    lib3ds_matrix_inv(m);
    lib3ds_mesh_calculate_normals(mesh, normals);
    cerr << "[" << count << " "
	 << mesh->name << ": "
	 << mesh->faces << " faces, "
	 << mesh->points << " vertices, "
	 << mesh->texels << " texels] ";

    assert(mesh->points == mesh->texels);

    //submesh->allocate(mesh->points, 3 * mesh->faces, false, false, false);
    submesh->allocate(3 * mesh->faces, 0, true, true, false);

    //for (unsigned int vert = 0; vert < mesh->points; vert++) {
    //  submesh->vertices[3 * vert + 0] = mesh->pointL[vert].pos[0];
    //  submesh->vertices[3 * vert + 1] = mesh->pointL[vert].pos[1];
    //  submesh->vertices[3 * vert + 2] = mesh->pointL[vert].pos[2];
    //}
    //for (unsigned int face = 0; face < mesh->faces; face++) {
    //  submesh->indices[3 * face + 0] = mesh->faceL[face].points[0];
    //  submesh->indices[3 * face + 1] = mesh->faceL[face].points[1];
    //  submesh->indices[3 * face + 2] = mesh->faceL[face].points[2];
    //}
    unsigned int smvc = 0, smvct = 0;
    for (unsigned int face = 0; face < mesh->faces; face++) {
      for (unsigned int i=0; i<3; i++) {
	const unsigned int vert = mesh->faceL[face].points[i];
	for (unsigned int com=0; com<3; com++) {
	  submesh->vertices[smvc + com] = mesh->pointL[vert].pos[com];
	  submesh->normals [smvc + com] = normals[3 * face + i][com];
	  //if (com < 2)
	  //  submesh->texcoords[smvct + com] = mesh->texelL[vert][com];
	  if (com == 0)
	    submesh->texcoords[smvct + com] = mesh->texelL[vert][com];
	  if (com == 1)
	    submesh->texcoords[smvct + com] = 1.0f - mesh->texelL[vert][com];
	}
	smvc  += 3;
	smvct += 2;
      }
    }

    free(normals);

    if (count)
      children.push_back(submesh);

    submesh->upload();
  }

  cerr << "OK" << endl;
  is_3ds = true;
}

void Model::render()
{
  assert(vertex_buf != 0);

  if (!shader_built)
    build_shader();
  if (using_shader)
    use_shader();

  glPushMatrix();

  glTranslatef(position[0],
	       position[1],
	       position[2]);
  glRotatef(rotation[0], 1,0,0);
  glRotatef(rotation[1], 0,1,0);
  glRotatef(rotation[2], 0,0,1);
  glScalef(scale[0],
	   scale[1],
	   scale[2]);
  static int recursion;
  recursion++;
  if (is_3ds /*&& recursion == 1*/) {
    glPushMatrix();
    glRotatef(-90.0f, 1,0,0);
  }

  if (color_buf)
    glDisable(GL_LIGHTING);
  else
    glEnable(GL_LIGHTING);

  if (lighting == false)
    glDisable(GL_LIGHTING);

  glActiveTextureARB(GL_TEXTURE0);
  if (texture)
    texture->bind();
  else
    glDisable(GL_TEXTURE_2D);

  glActiveTextureARB(GL_TEXTURE1);
  if (self_illumination)
    self_illumination->bind();
  else
    glDisable(GL_TEXTURE_2D);

  glActiveTextureARB(GL_TEXTURE2);
  if (environment)
    environment->bind();
  else
    glDisable(GL_TEXTURE_2D);

  glActiveTextureARB(GL_TEXTURE3);
  if (reflectivity)
    reflectivity->bind();
  else
    glDisable(GL_TEXTURE_2D);

  glActiveTextureARB(GL_TEXTURE0);

  if (blend == 1) {
    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE);
    glDepthMask(GL_FALSE);
    glDisable(GL_DEPTH_TEST);
  } else if (blend == 2) {
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glDepthMask(GL_FALSE);
  } else {
    glDisable(GL_BLEND);
    glDepthMask(GL_TRUE);
    glEnable(GL_DEPTH_TEST);
  }

  {
    static const GLfloat emission[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
    static const GLfloat ambient[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
    static const GLfloat diffuse[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
    static const GLfloat specular[4] = { 0.8f, 0.6f, 0.6f, 1.0f };

    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emission);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 80.0f);
  }

  glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertex_buf);
  glVertexPointer(3, GL_FLOAT, 0, 0);
  glEnableClientState(GL_VERTEX_ARRAY);

  if (normal_buf) {
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, normal_buf);
    glNormalPointer(GL_FLOAT, 0, NULL);
    glEnableClientState(GL_NORMAL_ARRAY);
  }
  else
    glDisableClientState(GL_NORMAL_ARRAY);

  if (texcoord_buf) {
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, texcoord_buf);
    glTexCoordPointer(2, GL_FLOAT, 0, 0);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  }
  else
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

  if (color_buf) {
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, color_buf);
    glColorPointer(4, GL_FLOAT, 0, 0);
    glEnableClientState(GL_COLOR_ARRAY);
  }
  else
    glDisableClientState(GL_COLOR_ARRAY);

  glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

  if (has_ao)
  {
    glEnable(GL_LIGHTING);
    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT);
  }

  if (index_buf) {
    glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, index_buf);
    glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, NULL);
  }
  else {
    glDrawArrays(GL_TRIANGLES, 0, vertex_count);
  }

  glDisable(GL_COLOR_MATERIAL);
  glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

  glDisableClientState(GL_COLOR_ARRAY);
  glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  glDisableClientState(GL_VERTEX_ARRAY);
  glDisableClientState(GL_NORMAL_ARRAY);

  assert(glGetError() == GL_NO_ERROR);

  if (using_shader)
    unuse_shader();

  if (is_3ds)
    glPopMatrix();

  for (list<Model *>::const_iterator i = children.begin(); i != children.end(); ++i)
    (*i)->render();

  glPopMatrix();

  recursion--;
}

void Model::set_texture          (Image *tex) {           texture = tex; }
void Model::set_self_illumination(Image *tex) { self_illumination = tex; }
void Model::set_environment      (Image *tex) {       environment = tex; }
void Model::set_reflectivity     (Image *tex) {      reflectivity = tex; }
Image *Model::get_texture()           const { return texture;            }
Image *Model::get_self_illumination() const { return self_illumination;  }
Image *Model::get_environment()       const { return environment;        }
Image *Model::get_reflectivity()      const { return reflectivity;       }

void Model::add_model(Model *m)
{
  children.push_back(m);
}

void Model::build_shader()
{
  using_shader |= self_illumination != NULL;

  if (using_shader) {
    if (game->get_ext()->simple_shaders == false)
      program = new GLProgram("graphics/telq_vert.glsl", "graphics/telq_frag.glsl");
    else
      program = new GLProgram("graphics/telq_vert.glsl", "graphics/telq_frag.mac.glsl");
    program->use();
    program->set_int("tex", 0);
    program->set_int("self_illum", 1);
    program->set_int("environ", 2);
    program->set_int("reflectivity", 3);
    program->unuse();
  }

  shader_built = true;
}

void Model::load_shader(const string &vert, const string &frag)
{
  program = new GLProgram(vert.c_str(), frag.c_str());
  shader_built = using_shader = true;
}

void Model::use_same_shader_as(Model *m)
{
  shader_built = m->shader_built;
  using_shader = m->using_shader;
  program = m->program;
}

void Model::use_shader()
{
  assert(using_shader);
  assert(scene);
  program->use();
  program->set_vector3("camera_pos", scene->camera_pos);
  program->set_float("seed", float(rand()) / float(RAND_MAX));
  program->set_float("time", 0.001f * SDL_GetTicks());
}

void Model::unuse_shader()
{
  assert(using_shader);
  program->unuse();
}

Scene::Scene() : fill_background(true)
{
  camera_pos[0] = 0.0f;
  camera_pos[1] = 0.0f;
  camera_pos[2] = 0.0f;
  background[0] = 0.1f;
  background[1] = 0.1f;
  background[2] = 0.1f;
}

Scene::~Scene()
{
}

void Scene::add_model(Model *model)
{
  model->scene = this;
  models.push_back(model);
}

void Scene::render() const
{
  if (fill_background) {
    glClearColor(background[0], background[1], background[2], 0.0);
    //glClearColor(0,0,0,0;)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  } else
    glClear(GL_DEPTH_BUFFER_BIT);

  glEnable(GL_DEPTH_TEST);
  glEnable(GL_LIGHTING);
  glDisable(GL_CULL_FACE);
  glDisable(GL_BLEND);

  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  gluPerspective(80.0, 4.0/3.0, 1.0, 1000.0);

  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  glTranslatef(-camera_pos[0], -camera_pos[1], -camera_pos[2]);

  glEnable(GL_LIGHT0);
  glLightfv(GL_LIGHT0, GL_AMBIENT , (float[4]){0.0f, 0.0f, 0.2f, 1.0f});
  glLightfv(GL_LIGHT0, GL_DIFFUSE , (float[4]){0.7f, 0.7f, 1.0f, 1.0f});
  glLightfv(GL_LIGHT0, GL_SPECULAR, (float[4]){1.0f, 1.0f, 1.0f, 1.0f});
  glLightfv(GL_LIGHT0, GL_POSITION, (float[4]){1.0f, 1.0f, 1.0f, 0.0f});

  for (list<Model *>::const_iterator i = models.begin(); i != models.end(); ++i)
    (*i)->render();

  for (int i=3; i>=0; i--) {
    glActiveTextureARB(GL_TEXTURE0 + i);
    glDisable(GL_TEXTURE_2D);
  }

  glDisable(GL_DEPTH_TEST);
  glDisable(GL_LIGHTING);
  glDisable(GL_CULL_FACE);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glDisable(GL_BLEND);
  glDepthMask(GL_TRUE);

  glMatrixMode(GL_MODELVIEW);
  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);

  assert(glGetError() == GL_NO_ERROR);
}

void Scene::set_camera_pos(float x, float y, float z)
{
  camera_pos[0] = x;
  camera_pos[1] = y;
  camera_pos[2] = z;
}

float *Scene::get_camera_pos()
{
  return camera_pos;
}

void Starfield::load()
{
  const int stars = 1000;
  allocate(4 * stars, 6 * stars, false, true, true);
  for (int s=0; s<stars; s++) {
    const float x = (float(rand()) / RAND_MAX) * 200.0f - 100.0f;
    const float y = (float(rand()) / RAND_MAX) * 200.0f - 100.0f;
    const float z = (float(rand()) / RAND_MAX) * -300.0f - 40.0f;
    const float w = 2.0f + float(rand()) / RAND_MAX * 2.0f;
    vertices[3 * (4*s + 0) + 0] = x - w/2;
    vertices[3 * (4*s + 0) + 1] = y - w/2;
    vertices[3 * (4*s + 0) + 2] = z;
    vertices[3 * (4*s + 1) + 0] = x + w/2;
    vertices[3 * (4*s + 1) + 1] = y - w/2;
    vertices[3 * (4*s + 1) + 2] = z;
    vertices[3 * (4*s + 2) + 0] = x + w/2;
    vertices[3 * (4*s + 2) + 1] = y + w/2;
    vertices[3 * (4*s + 2) + 2] = z;
    vertices[3 * (4*s + 3) + 0] = x - w/2;
    vertices[3 * (4*s + 3) + 1] = y + w/2;
    vertices[3 * (4*s + 3) + 2] = z;
    indices[6*s + 0] = 4*s + 0;
    indices[6*s + 1] = 4*s + 1;
    indices[6*s + 2] = 4*s + 2;
    indices[6*s + 3] = 4*s + 0;
    indices[6*s + 4] = 4*s + 2;
    indices[6*s + 5] = 4*s + 3;
    texcoords[2 * (4*s + 0) + 0] = 0.0f; texcoords[2 * (4*s + 0) + 1] = 1.0f;
    texcoords[2 * (4*s + 1) + 0] = 1.0f; texcoords[2 * (4*s + 1) + 1] = 1.0f;
    texcoords[2 * (4*s + 2) + 0] = 1.0f; texcoords[2 * (4*s + 2) + 1] = 0.0f;
    texcoords[2 * (4*s + 3) + 0] = 0.0f; texcoords[2 * (4*s + 3) + 1] = 0.0f;
    const float col = float(rand()) / float(RAND_MAX) / 2.0f + 0.5f;
    const float red = float(rand()) / float(RAND_MAX) / 1.5f + 0.5f;
    colors[4 * (4*s + 0) + 0] = red*col; colors[4 * (4*s + 0) + 1] = red*col; colors[4 * (4*s + 0) + 2] = col; colors[4 * (4*s + 0) + 3] = 1.0f;
    colors[4 * (4*s + 1) + 0] = red*col; colors[4 * (4*s + 1) + 1] = red*col; colors[4 * (4*s + 1) + 2] = col; colors[4 * (4*s + 1) + 3] = 1.0f;
    colors[4 * (4*s + 2) + 0] = red*col; colors[4 * (4*s + 2) + 1] = red*col; colors[4 * (4*s + 2) + 2] = col; colors[4 * (4*s + 2) + 3] = 1.0f;
    colors[4 * (4*s + 3) + 0] = red*col; colors[4 * (4*s + 3) + 1] = red*col; colors[4 * (4*s + 3) + 2] = col; colors[4 * (4*s + 3) + 3] = 1.0f;
  }
  upload();
}
