/**
 * Using the Storm 3D library to render textured geometry.
 */

use ui;
use ui:s3d;
use core:geometry;
use graphics;

/**
 * Source vertex type.
 */
vertex TexVertex {
	// Position.
	Vector pos;

	// Texture coordinate.
	Point tex;
}

/**
 * The intermediate vertex needs to contain texture coordinates.
 */
vertex TexIntermediate {
	// Screen position.
	Vector4 pos : position, perspective;

	// Texture coordinate.
	Point tex : perspective;
}

/**
 * The pipeline.
 */
pipeline TexPipeline {
	data (TexVertex point) -> TexIntermediate -> Output;

	// Transform.
	Transform camera;

	// Texture. These are a bit special - they become texture samplers in shaders.
	Texture texture;
}

// Vertex shader: transform vertices as before.
vertex shader for TexPipeline: TexIntermediate texVertexShader(TexVertex point) {
	Vector4 pos = Vector4(point.pos) * camera;
	return TexIntermediate(pos, point.tex);
}

// Pixel shader: sample the texture.
pixel shader for TexPipeline: Output texPixelShader(TexIntermediate point) {
	Color tex = texture.sample(point.tex);
	return Output(tex);
}

/**
 * Rendering logic.
 */
class TexPainter extends SamplePainter3D {
	// Pipeline.
	TexPipeline pipeline;

	// Geometry to render.
	VertexBuffer<TexVertex> vertices;
	IndexBuffer indices;

	// Camera transform.
	Transform cameraPos;

	// Create.
	init() {
		init {
			pipeline = TexPipeline:create(texVertexShader, texPixelShader);
			cameraPos = rotateX(-15 deg) * translate(Vector(0, 0, 3));
		}

		// Set the texture.
		pipeline.texture.image = loadImage(resUrl("flower-raw.ppm"));

		// Create a cube:
		vertices << TexVertex(Vector(-1, 1, 1), Point(1, 0));
		vertices << TexVertex(Vector(1, 1, 1), Point(0, 0));
		vertices << TexVertex(Vector(-1, -1, 1), Point(1, 1));
		vertices << TexVertex(Vector(1, -1, 1), Point(0, 1));
		vertices << TexVertex(Vector(-1, 1, -1), Point(0, 0));
		vertices << TexVertex(Vector(1, 1, -1), Point(1, 0));
		vertices << TexVertex(Vector(-1, -1, -1), Point(0, 1));
		vertices << TexVertex(Vector(1, -1, -1), Point(1, 1));

		// Back face:
		indices << 0 << 2 << 1;
		indices << 1 << 2 << 3;

		// Top face:
		indices << 0 << 1 << 4;
		indices << 1 << 5 << 4;

		// Bottom face:
		indices << 2 << 6 << 3;
		indices << 3 << 6 << 7;

		// Left face:
		indices << 0 << 4 << 2;
		indices << 4 << 6 << 2;

		// Right face:
		indices << 1 << 3 << 5;
		indices << 5 << 3 << 7;

		// Front face:
		indices << 4 << 5 << 6;
		indices << 5 << 7 << 6;
	}

	// Render.
	Bool render(Size size, Graphics3D g) : override {
		Float time = animationTime(5 s);

		g.clearDepth();
		g.fill(black);

		pipeline.camera = rotateY(360 deg * time) * cameraPos * perspectiveProject(100 deg, size.aspect, 0.1, 100);
		pipeline.render(g, indices, vertices);

		true;
	}
}

// Main function.
void texture() {
	runSample("Textures", TexPainter());
}
