/**
 * Using the Storm 3D class Model to represent 3D models.
 */

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

/**
 * Intermediate vertex type.
 */
vertex ModelIntermediate {
	// Position.
	Vector4 pos : position, perspective;

	// Color.
	Color color : perspective;
}

/**
 * Pipeline for rendering models.
 *
 * Using ui:s3d:Vertex as the input allows passing a Model to the 'render' function of the pipeline.
 */
pipeline ModelPipeline {
	// Here, the source vertex type is ui:s3d:Vertex to make it possible to use Model.
	data (Vertex vertex) -> ModelIntermediate -> Output;

	// Camera transform.
	Transform camera;
}


// Vertex shader - transforms from model space to camera space.
vertex shader for ModelPipeline: ModelIntermediate modelVertexShader(Vertex point) {
	ModelIntermediate(Vector4(point.pos) * camera, point.color);
}

// Pixel shader - performs lightning.
pixel shader for ModelPipeline: Output modelPixelShader(ModelIntermediate point) {
	Output(point.color);
}


/**
 * The painter.
 */
class ModelPainter extends SamplePainter3D {
	// The pipeline itself.
	ModelPipeline pipeline;

	// Model to render.
	Model model;

	// Transform to account for the camera position.
	Transform cameraPos;

	// Create.
	init() {
		init {
			// Create the pipeline and attach shaders.
			pipeline = ModelPipeline:create(modelVertexShader, modelPixelShader);

			// Create the default camera transform.
			cameraPos = rotateX(-35 deg) * translate(Vector(0, 0, 8));

			// Start with a teapot to render.
			model = resUrl("teapot.obj").loadOBJ();
		}

		// Turn the teapot red.
		model.color = red;

		// Append a translated green cube.
		model.append(Model:cube(2).withColor(green).transform(translate(Vector(5, 0, 0))));

		// Append a translated blue sphere.
		model.append(Model:sphere(2, 30, 30).withColor(blue).transform(translate(Vector(-5, 0, 0))));
	}

	// 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, model);

		true;
	}
}


void model() {
	runSample("3D Models", ModelPainter());
}
