#include "triangle.h"
void TriangleSet::_bind_methods() {
	ClassDB::bind_method(D_METHOD("create_from_array_shape", "arrays_base", "shape_arrays"), &TriangleSet::create_from_array_shape);
	ClassDB::bind_method(D_METHOD("create_from_mesh_difference", "arrays_base", "uv_index1", "arrays_shape", "uv_index2"), &TriangleSet::create_from_array_difference);
	ClassDB::bind_method(D_METHOD("draw", "vimage", "nimage", "uv_index"), &TriangleSet::draw);
	ClassDB::bind_method(D_METHOD("get_data", "vimage", "nimage"), &TriangleSet::get_data);
	ClassDB::bind_method(D_METHOD("get_min"), &TriangleSet::get_min);
	ClassDB::bind_method(D_METHOD("get_max"), &TriangleSet::get_max);
	ClassDB::bind_method(D_METHOD("get_min_normal"), &TriangleSet::get_min_normal);
	ClassDB::bind_method(D_METHOD("get_max_normal"), &TriangleSet::get_max_normal);
	ClassDB::bind_method(D_METHOD("save", "fd"), &TriangleSet::save);
}
void TriangleSet::create_from_array_shape(const Array &arrays_base, const Array &shape_array) {
	const PoolVector<int> &data_indices = shape_array[Mesh::ARRAY_INDEX];
	const PoolVector<Vector3> &base_vertices = arrays_base[Mesh::ARRAY_VERTEX];
	const PoolVector<Vector3> &shape_vertices = shape_array[Mesh::ARRAY_VERTEX];
	const PoolVector<Vector3> &base_normals = arrays_base[Mesh::ARRAY_NORMAL];
	const PoolVector<Vector3> &shape_normals = shape_array[Mesh::ARRAY_NORMAL];
	const PoolVector<Vector2> &uvs1 = shape_array[Mesh::ARRAY_TEX_UV];
	const PoolVector<Vector2> &uvs2 = shape_array[Mesh::ARRAY_TEX_UV2];
	int i;
	indices = data_indices;
	vertices.resize(base_vertices.size());
	normals.resize(base_vertices.size());
	this->uvs1.resize(base_vertices.size());
	this->uvs2.resize(base_vertices.size());
	PoolVector<Vector3>::Write vertices_write = this->vertices.write();
	PoolVector<Vector3>::Write normals_write = this->normals.write();
	PoolVector<Vector2>::Write uvs1_w = this->uvs1.write();
	PoolVector<Vector2>::Write uvs2_w = this->uvs2.write();
	for (i = 0; i < base_vertices.size(); i++) {
		vertices_write[i] = shape_vertices[i] - base_vertices[i];
		normals_write[i] = shape_normals[i] - base_normals[i];
		uvs1_w[i] = uvs1.read()[i];
		uvs2_w[i] = uvs2.read()[i];
	}
	printf("create done\n");
}
void TriangleSet::create_from_array_difference(const Array &arrays_base, int uv_index1, const Array &arrays_shape, int uv_index2) {
	Vector<int> missing_vertices;
	const PoolVector<Vector3> &base_vertices = arrays_base[Mesh::ARRAY_VERTEX];
	const PoolVector<Vector3> &shape_vertices = arrays_shape[Mesh::ARRAY_VERTEX];
	const PoolVector<Vector3> &base_normals = arrays_base[Mesh::ARRAY_NORMAL];
	const PoolVector<Vector3> &shape_normals = arrays_shape[Mesh::ARRAY_NORMAL];
	const PoolVector<int> &shape_index = arrays_shape[Mesh::ARRAY_INDEX];
	switch (uv_index1) {
		case 0:
			uv_index1 = Mesh::ARRAY_TEX_UV;
			break;
		case 1:
			uv_index1 = Mesh::ARRAY_TEX_UV2;
			break;
		default:
			uv_index1 = Mesh::ARRAY_TEX_UV;
	}
	switch (uv_index2) {
		case 0:
			uv_index2 = Mesh::ARRAY_TEX_UV;
			break;
		case 1:
			uv_index2 = Mesh::ARRAY_TEX_UV2;
			break;
		default:
			uv_index2 = Mesh::ARRAY_TEX_UV;
	}
	const PoolVector<Vector2> &base_uvs = arrays_base[uv_index1];
	const PoolVector<Vector2> &shape_uvs = arrays_shape[uv_index1];
	indices = arrays_base[Mesh::ARRAY_INDEX];
	this->vertices.resize(base_vertices.size());
	this->uvs1 = arrays_base[Mesh::ARRAY_TEX_UV];
	this->uvs2 = arrays_base[Mesh::ARRAY_TEX_UV2];
	normals.resize(base_vertices.size());
	const float okdist = 0.001f;
	int i, j;
	PoolVector<Vector3>::Write vertices_write = this->vertices.write();
	PoolVector<Vector3>::Write normals_write = this->normals.write();
	for (i = 0; i < base_vertices.size(); i++) {
		bool ok = false;
		for (j = 0; j < shape_vertices.size(); j++) {
			if (base_uvs[i].distance_squared_to(shape_uvs[j]) < okdist * okdist) {
				vertices_write[i] = shape_vertices[j] - base_vertices[i];
				normals_write[i] = shape_normals[j] - base_normals[i];
				ok = true;
				break;
			}
		}
		if (!ok)
			missing_vertices.push_back(i);
	}
	for (i = 0; i < missing_vertices.size(); i++) {
		int base_index = missing_vertices[i];
		Vector2 pt2 = base_uvs[base_index];
		Vector3 pt = Vector3(pt2.x, pt2.y, 0.0f);
		for (j = 0; j < shape_index.size(); j += 3) {
			Vector3 p1 = Vector3(shape_uvs[shape_index[j + 0]].x,
					shape_uvs[shape_index[j + 0]].y,
					0.0f);
			Vector3 p2 = Vector3(shape_uvs[shape_index[j + 1]].x,
					shape_uvs[shape_index[j + 1]].y,
					0.0f);
			Vector3 p3 = Vector3(shape_uvs[shape_index[j + 2]].x,
					shape_uvs[shape_index[j + 2]].y,
					0.0f);
			Vector3 b = get_baricentric(pt, p1, p2, p3);
			if (b.x < 0 || b.y < 0 || b.z < 0)
				continue;
			Vector3 newpt = shape_vertices[shape_index[j + 0]] * b.x + shape_vertices[shape_index[j + 1]] * b.y + shape_vertices[shape_index[j + 2]] * b.z;
			Vector3 newn = shape_normals[shape_index[j + 0]] * b.x + shape_normals[shape_index[j + 1]] * b.y + shape_normals[shape_index[j + 2]] * b.z;
			vertices_write[base_index] = newpt - base_vertices[base_index];
			normals_write[base_index] = newn - base_normals[base_index];
		}
	}
	vertices_write.release();
	normals_write.release();
}
void TriangleSet::draw(Ref<Image> vimage, Ref<Image> nimage, int uv_index) {
	int i, j;
	int width = vimage->get_width();
	int height = vimage->get_height();
	int nwidth = nimage->get_width();
	int nheight = nimage->get_height();
	minx = width;
	miny = height;
	maxx = -1;
	minx = -1;
	Vector2 mulv(width, height);
	Vector2 muln(nwidth, nheight);
	normalize_deltas();
	vimage->lock();
	nimage->lock();
	PoolVector<Vector2>::Read uvs_r;
	PoolVector<int>::Read indices_r = indices.read();
	PoolVector<Vector3>::Read vertices_r = vertices.read();
	PoolVector<Vector3>::Read normals_r = normals.read();
	switch (uv_index) {
		case 0:
			uvs_r = uvs1.read();
			break;
		case 1:
			uvs_r = uvs2.read();
			break;
	}
	for (i = 0; i < vimage->get_height(); i++)
		for (j = 0; j < vimage->get_width(); j++)
			vimage->set_pixel(j, i, Color(0.5f, 0.5f, 0.5f));
	for (i = 0; i < nimage->get_height(); i++)
		for (j = 0; j < nimage->get_width(); j++)
			nimage->set_pixel(j, i, Color(0.5f, 0.5f, 0.5f));
	for (i = 0; i < indices.size(); i += 3) {
		const Vector2 &vp1 = uvs_r[indices_r[i + 0]];
		const Vector2 &vp2 = uvs_r[indices_r[i + 1]];
		const Vector2 &vp3 = uvs_r[indices_r[i + 2]];
		const float eps = 0.001f;
		Vector2 center = (vp1 + vp2 + vp3) / 3.0;
		Vector2 c1 = (vp1 - center).normalized() * 8.0;
		Vector2 c2 = (vp2 - center).normalized() * 8.0;
		Vector2 c3 = (vp3 - center).normalized() * 8.0;
		const Vector3 &vc1 = vertices_r[indices_r[i + 0]];
		const Vector3 &vc2 = vertices_r[indices_r[i + 1]];
		const Vector3 &vc3 = vertices_r[indices_r[i + 2]];
		if (vc1.length_squared() + vc2.length_squared() + vc3.length_squared() > eps * eps) {
			update_bounds(vp1.x * mulv.x, vp1.y * mulv.y);
			update_bounds(vp2.x * mulv.x, vp2.y * mulv.y);
			update_bounds(vp3.x * mulv.x, vp3.y * mulv.y);
		}
		const Vector3 &nc1 = normals_r[indices_r[i + 0]];
		const Vector3 &nc2 = normals_r[indices_r[i + 1]];
		const Vector3 &nc3 = normals_r[indices_r[i + 2]];
		float v1[] = { vp1.x * mulv.x + c1.x, vp1.y * mulv.y + c1.y, vc1.x, vc1.y, vc1.z };
		float v2[] = { vp2.x * mulv.x + c2.x, vp2.y * mulv.y + c2.y, vc2.x, vc2.y, vc2.z };
		float v3[] = { vp3.x * mulv.x + c3.x, vp3.y * mulv.y + c3.x, vc3.x, vc3.y, vc3.z };
		float n1[] = { vp1.x * muln.x + c1.x, vp1.y * muln.y + c1.y, nc1.x, nc1.y, nc1.z };
		float n2[] = { vp2.x * muln.x + c2.x, vp2.y * muln.y + c2.y, nc2.x, nc2.y, nc2.z };
		float n3[] = { vp3.x * muln.x + c3.x, vp3.y * muln.y + c3.y, nc3.x, nc3.y, nc3.z };
		draw_triangle(vimage.ptr(), v1, v2, v3);
		draw_triangle(nimage.ptr(), n1, n2, n3);
	}
	for (i = 0; i < indices.size(); i += 3) {
		const Vector2 &vp1 = uvs_r[indices_r[i + 0]];
		const Vector2 &vp2 = uvs_r[indices_r[i + 1]];
		const Vector2 &vp3 = uvs_r[indices_r[i + 2]];
		const Vector3 &vc1 = vertices_r[indices_r[i + 0]];
		const Vector3 &vc2 = vertices_r[indices_r[i + 1]];
		const Vector3 &vc3 = vertices_r[indices_r[i + 2]];
		const Vector3 &nc1 = normals_r[indices_r[i + 0]];
		const Vector3 &nc2 = normals_r[indices_r[i + 1]];
		const Vector3 &nc3 = normals_r[indices_r[i + 2]];
		float v1[] = { vp1.x * mulv.x, vp1.y * mulv.y, vc1.x, vc1.y, vc1.z };
		float v2[] = { vp2.x * mulv.x, vp2.y * mulv.y, vc2.x, vc2.y, vc2.z };
		float v3[] = { vp3.x * mulv.x, vp3.y * mulv.y, vc3.x, vc3.y, vc3.z };
		float n1[] = { vp1.x * muln.x, vp1.y * muln.y, nc1.x, nc1.y, nc1.z };
		float n2[] = { vp2.x * muln.x, vp2.y * muln.y, nc2.x, nc2.y, nc2.z };
		float n3[] = { vp3.x * muln.x, vp3.y * muln.y, nc3.x, nc3.y, nc3.z };
		draw_triangle(vimage.ptr(), v1, v2, v3);
		draw_triangle(nimage.ptr(), n1, n2, n3);
	}
	if (maxx >= width)
		maxx = width - 1;
	if (maxy >= height)
		maxy = height - 1;
	if (minx < 0)
		minx = 0;
	if (miny < 0)
		miny = 0;
	vimage->unlock();
	nimage->unlock();
}

void TriangleSet::normalize_deltas() {
	int i, j;
	minp[0] = 100.0f;
	minp[1] = 100.0f;
	minp[2] = 100.0f;
	maxp[0] = -100.0f;
	maxp[1] = -100.0f;
	maxp[2] = -100.0f;
	minn[0] = 100.0f;
	minn[1] = 100.0f;
	minn[2] = 100.0f;
	maxn[0] = -100.0f;
	maxn[1] = -100.0f;
	maxn[2] = -100.0f;
	for (i = 0; i < vertices.size(); i++) {
		for (j = 0; j < 3; j++) {
			if (minp[j] > vertices[i][j])
				minp[j] = vertices[i][j];
			if (maxp[j] < vertices[i][j])
				maxp[j] = vertices[i][j];
			if (minn[j] > normals[i][j])
				minn[j] = normals[i][j];
			if (maxn[j] < normals[i][j])
				maxn[j] = normals[i][j];
		}
	}
	for (i = 0; i < 3; i++) {
		cd[i] = maxp[i] - minp[i];
		cdn[i] = maxn[i] - minn[i];
	}
	PoolVector<Vector3>::Write vertices_w = vertices.write();
	PoolVector<Vector3>::Write normals_w = normals.write();
	for (i = 0; i < vertices.size(); i++)
		for (j = 0; j < 3; j++) {
			if (cd[j] == 0.0f)
				vertices_w[i][j] = 0.0f;
			else
				vertices_w[i][j] = (vertices_w[i][j] - minp[j]) / cd[j];
			if (cdn[j] == 0.0f)
				normals_w[i][j] = 0.0f;
			else
				normals_w[i][j] = (normals_w[i][j] - minn[j]) / cdn[j];
		}
}
void TriangleSet::save(Ref<_File> fd, const String &shape_name, Ref<Image> vimage, Ref<Image> nimage) {
	int csize;
	fd->store_pascal_string(shape_name);
	fd->store_float(minp[0]);
	fd->store_float(minp[1]);
	fd->store_float(minp[2]);
	fd->store_float(maxp[0]);
	fd->store_float(maxp[1]);
	fd->store_float(maxp[2]);
	fd->store_32(vimage->get_width());
	fd->store_32(vimage->get_height());
	fd->store_32(vimage->get_format());
	PoolVector<uint8_t> imgbuf = vimage->get_data();
	fd->store_32(imgbuf.size());
	PoolVector<uint8_t> imgbuf_comp;
	imgbuf_comp.resize(imgbuf.size());
	csize = Compression::compress(imgbuf_comp.write().ptr(),
			imgbuf.read().ptr(), imgbuf.size(),
			Compression::MODE_FASTLZ);
	imgbuf_comp.resize(csize);
	fd->store_32(csize);
	fd->store_buffer(imgbuf_comp);
	fd->store_float(minn[0]);
	fd->store_float(minn[1]);
	fd->store_float(minn[2]);
	fd->store_float(maxn[0]);
	fd->store_float(maxn[1]);
	fd->store_float(maxn[2]);
	fd->store_32(nimage->get_width());
	fd->store_32(nimage->get_height());
	fd->store_32(nimage->get_format());
	PoolVector<uint8_t> imgbufn = nimage->get_data();
	fd->store_32(imgbufn.size());
	PoolVector<uint8_t> imgbuf_compn;
	imgbuf_compn.resize(imgbufn.size());
	csize = Compression::compress(imgbuf_compn.write().ptr(),
			imgbufn.read().ptr(), imgbufn.size(),
			Compression::MODE_DEFLATE);
	imgbuf_compn.resize(csize);
	fd->store_32(csize);
	fd->store_buffer(imgbuf_compn);
}
