#include "StdAfx.h"
#include "3DObject.h"

const DWORD Vertex::FVF = D3DFVF_XYZ  | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1 ;

/// C3DObject
/// 
/// Implementation
C3DObject::C3DObject(IDirect3DDevice9* device):
     VB(0),device(0),IB(0),vertex_count(0),faces_count(0)
{
	this->device = device;
}

void C3DObject::CopyFrom(C3DObject &inst)
{
 device = NULL;
 VB = NULL;
 IB = NULL;

 device = inst.device;
 vertex_count = inst.vertex_count;
 faces_count = inst.faces_count;

 device->CreateVertexBuffer(
 	vertex_count * sizeof(Vertex),
	D3DUSAGE_WRITEONLY,
	Vertex::FVF,
	D3DPOOL_MANAGED,
	&VB,
	0);
 device->CreateIndexBuffer(
	3*faces_count * sizeof(WORD),
	D3DUSAGE_WRITEONLY,
	D3DFMT_INDEX16,
	D3DPOOL_MANAGED,
	&IB,
	0);


 Vertex* vertices;
 Vertex* inst_vertices; 
 VB->Lock(0, 0, (void**)&vertices, 0);
 inst.VB->Lock(0, 0, (void**)&inst_vertices, 0);
 for (long i=0; i<vertex_count; i++){
	  vertices[i] = inst_vertices[i];
 }
 VB->Unlock();
 inst.VB->Unlock();


 WORD* indices = 0;
 WORD* inst_indices = 0;
 IB->Lock(0, 0, (void**)&indices, 0);
 inst.IB->Lock(0, 0, (void**)&inst_indices, 0);
 for (long i=0; i<3*faces_count; i++)
  indices[i] = inst_indices[i];
 IB->Unlock();
 inst.IB->Unlock();
}

C3DObject::C3DObject(C3DObject &inst)
{
	CopyFrom(inst);
}


C3DObject::~C3DObject(void)
{
	if (device) device->Release();
	//if (IB) IB->Release();
	//if (VB) VB->Release();
}

void C3DObject::Translate(float x, float y, float z)
{
 Vertex* vertices;
 VB->Lock(0, 0, (void**)&vertices, 0);
 for (long i=0; i<vertex_count; i++)
 {
   vertices[i].position.x +=x;
   vertices[i].position.y +=y;
   vertices[i].position.z +=z;
 }
 VB->Unlock();
}

void C3DObject::Scale(float sx, float sy, float sz)
{
 if (sx == 0) sx = 1;
 if (sy == 0) sy = 1;
 if (sz == 0) sz = 1;

 Vertex* vertices;
 VB->Lock(0, 0, (void**)&vertices, 0);
 for (long i=0; i<vertex_count; i++)
 {
   vertices[i].position.x *= sx;
   vertices[i].position.y *= sy;
   vertices[i].position.z *= sz;
 }
 VB->Unlock();
}

void C3DObject::Rotate(float ax, float ay, float az)
{
	D3DXMATRIX x,y,z,rez; 
	D3DXVECTOR3 v;
	D3DXMatrixRotationX(&x,ax);
	D3DXMatrixRotationY(&y,ay);
	D3DXMatrixRotationZ(&z,az);
	rez = x*y*z;
	Vertex* vertices;
	VB->Lock(0, 0, (void**)&vertices, 0);
	for (long i=0; i<vertex_count; i++)
	{
	  D3DXVec3TransformCoord(&v,&vertices[i].position,&rez);
	  vertices[i].position = v;
	}
	VB->Unlock();
}

void C3DObject::Render()
{
if( device )
 {
   device->SetStreamSource(0, VB, 0, sizeof(Vertex));
   device->SetIndices(IB);
   device->SetFVF(Vertex::FVF);
   device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0, 0, vertex_count, 0, faces_count);
 }
}

D3DXVECTOR3 C3DObject::ObjectCenter()
{
	D3DXVECTOR3 rez(0,0,0);
	
	Vertex* vertices;
	VB->Lock(0, 0, (void**)&vertices, 0);
	for (long i=0; i<vertex_count; i++){
	 rez.x += vertices[i].position.x / vertex_count;
	 rez.y += vertices[i].position.y / vertex_count;
	 rez.z += vertices[i].position.z / vertex_count;
	}
	VB->Unlock();
	return rez;
}

void ComputeNormal(D3DXVECTOR3* p0, D3DXVECTOR3* p1, D3DXVECTOR3* p2, D3DXVECTOR3* out)
{
	D3DXVECTOR3 u = *p1 - *p0;
	D3DXVECTOR3 v = *p2 - *p0;
	D3DXVec3Cross(out, &u, &v);
	D3DXVec3Normalize(out, out);
}

void C3DObject::LoadFromFile (char *filename)
{
 fstream fs(filename,ios_base::in);

 fs >> vertex_count >> faces_count;
 D3DXVECTOR3 *normals = new D3DXVECTOR3[faces_count];
 // create vertex and index buffers
 device->CreateVertexBuffer(
 	vertex_count * sizeof(Vertex),
	D3DUSAGE_WRITEONLY,
	Vertex::FVF,
	D3DPOOL_MANAGED,
	&VB,
	0);
 device->CreateIndexBuffer(
	3*faces_count * sizeof(WORD),
	D3DUSAGE_WRITEONLY,
	D3DFMT_INDEX16,
	D3DPOOL_MANAGED,
	&IB,
	0);
 // fill the buffers with the cube data
 Vertex* vertices;
 VB->Lock(0, 0, (void**)&vertices, 0);
 // vertices of a unit cube
 for (long i=0; i<vertex_count; i++)
 {
  float x,y,z;
  fs >> x >> y >> z;
  vertices[i].position.x = x;
  vertices[i].position.y = y;
  vertices[i].position.z = z;
  vertices[i].color = D3DCOLOR_XRGB(255,255,255);
 }

 // define the triangles of the cube:
 WORD* indices = 0;
 IB->Lock(0, 0, (void**)&indices, 0);
 int a; 
 for (long i=0; i<3*faces_count; i++)
 {
  fs >> a;
  indices[i] = a-1;  
 }
 
 for (long i=0; i<faces_count; i++)
 {
  float nx,ny,nz;
  fs >> nx >> ny >> nz;
  normals[i] = D3DXVECTOR3(nx,ny,nz);

  vertices[indices[3*i]].normal = normals[i];
  vertices[indices[3*i+1]].normal = normals[i];
  vertices[indices[3*i+2]].normal = normals[i];
 }
 
 IB->Unlock();
 VB->Unlock();
 fs.close ();
 delete []  normals;
}

D3DXVECTOR3 C3DObject::getBack()
{
    D3DXVECTOR3 rez;
	
	Vertex* vertices;
	VB->Lock(0, 0, (void**)&vertices, 0);
	rez = vertices[0].position;
	for (long i=0; i<vertex_count; i++){
		if (rez.x < vertices[i].position.x) rez.x = vertices[i].position.x;
		if (rez.y < vertices[i].position.y) rez.y = vertices[i].position.y;
		if (rez.z < vertices[i].position.z) rez.z = vertices[i].position.z;
	}
	VB->Unlock();
	return rez;
}

D3DXVECTOR3 C3DObject::getFront()
{
    D3DXVECTOR3 rez;
	
	Vertex* vertices;
	VB->Lock(0, 0, (void**)&vertices, 0);
	rez = vertices[0].position;
	for (long i=0; i<vertex_count; i++){
		if (rez.x > vertices[i].position.x) rez.x = vertices[i].position.x;
		if (rez.y > vertices[i].position.y) rez.y = vertices[i].position.y;
		if (rez.z > vertices[i].position.z) rez.z = vertices[i].position.z;
	}
	VB->Unlock();
	return rez;
}

IDirect3DVertexBuffer9 * C3DObject::getVertexBuffer()
{
	return VB;
}

DWORD C3DObject::getVertexCount()
{
	return vertex_count;
}

DWORD C3DObject::getBytesPerVertex()
{
	return sizeof(Vertex);
}

double C3DObject::ObjectSpeed()
{
	return 0;
}

void DrawLine(IDirect3DDevice9* device, D3DXVECTOR3 a, D3DXVECTOR3 b)
{
 IDirect3DVertexBuffer9* VB1;

  device->CreateVertexBuffer(
	2 * sizeof(Vertex),
	D3DUSAGE_WRITEONLY,
	Vertex::FVF,
	D3DPOOL_MANAGED,
	&VB1,
	0);

 Vertex* vertices;   
 VB1->Lock(0, 0, (void**)&vertices, 0);
  vertices[0].position = a;
  vertices[1].position = b;
  vertices[0].color = D3DCOLOR_XRGB(255,255,255);
  vertices[1].color = D3DCOLOR_XRGB(255,255,255);
 VB1->Unlock();

 device->SetStreamSource(0, VB1, 0, sizeof(Vertex));
 device->SetFVF(Vertex::FVF);
 device->DrawPrimitive(D3DPT_LINELIST,0, 1);
}