/**
 * Using the Storm 3D library for cel-shaded lighting.
 *
 * Note: This sample uses much of the same vertex types and pipelines as the light demo.
 */

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



// Pixel shader - performs lightning.
pixel shader for LightPipeline: Output celPixelShader(LightIntermediate point) {
	// Compute the direction vector to the light source and the normal. Note that we need to
	// normalize the normal at this point.
	Vector cameraDir = (cameraPos - point.world).normalized;
	Vector lightDir = (lightPos - point.world).normalized;
	Vector normal = point.normal.normalized;

	// Ambient light:
	Color light = point.color * ambientStrength;

	// Diffuse light:
	light += point.color * celShade(max(normal * lightDir, 0.0));

	// Specular light:
	Vector reflectDir = lightDir.reflect(normal);
	Float specular = pow(max(cameraDir * reflectDir, 0.0), specularExponent);
	light += specularColor * celShade(specularStrength * specular);

	Output(light);
}

shader Float celShade(Float strength) {
	Float steps = 5.0;
	(strength * steps).floor / steps;
}


/**
 * The painter.
 */
class CelPainter extends SamplePainter3D {
	// The pipeline itself.
	LightPipeline 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 = LightPipeline:create(lightVertexShader, celPixelShader);

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

			// Load a teapot to render.
			model = resUrl("teapot.obj").loadOBJ.withColor(red);
		}

		// Set ambient light strength.
		pipeline.ambientStrength = 0.1;

		// Set specular light properties.
		pipeline.specularStrength = 0.8;
		pipeline.specularExponent = 32.0;
		pipeline.specularColor = white;
	}

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

		// Clear the old buffer.
		g.clearDepth();
		g.fill(black);

		// Set the camera and world matrices.
		pipeline.camera = cameraPos * perspectiveProject(100 deg, size.aspect, 0.1, 100);
		pipeline.world = rotateY(360 deg * time * 4) * scale(0.5);

		// Let the light rotate around the model in a big circle, but slower.
		pipeline.lightPos = Vector(0, 10, -10) * rotateY(360 deg * -time);

		// Compute the camera position, for specular highlights.
		pipeline.cameraPos = Vector(0, 0, 0) * cameraPos.inverted;

		// Now we can render the model. Note that we don't have to access the vertex and index
		// buffers. The pipeline allows us to just pass the entire Model as a parameter.
		pipeline.render(g, model);

		true;
	}
}


void celshading() {
	runSample("3D Cel Shading", CelPainter());
}
