/*****************************************************************************\
*
*  Module Name    simple_render.cpp
*  Project        Radeon ProRender rendering tutorial
*
*  Description    Radeon ProRender SDK tutorials 
*
*  Copyright 2011 - 2017 Advanced Micro Devices, Inc. (unpublished)
*
*  All rights reserved.  This notice is intended as a precaution against
*  inadvertent publication and does not imply publication or any waiver
*  of confidentiality.  The year included in the foregoing notice is the
*  year of creation of the work.
*
\*****************************************************************************/
#include "RadeonProRender.h"
#include "rprDeprecatedApi.h"
#include "Math\mathutils.h"
#include "../common/common.h"
#include <cassert>
#include <iostream>

int main()
{
	//	enable Radeon ProRender API trace
	//	set this before any rpr API calls
	//	rprContextSetParameter1u(0,"tracing",1);

	std::cout << "Radeon ProRender SDK simple rendering tutorial.\n";
	// Indicates whether the last operation has suceeded or not
	rpr_int status = RPR_SUCCESS;
	// Create OpenCL context using a single GPU 
	rpr_context context = NULL;

	// Register Tahoe ray tracing plugin.
	rpr_int tahoePluginID = rprRegisterPlugin("Tahoe64.dll"); 
	assert(tahoePluginID != -1);
	rpr_int plugins[] = { tahoePluginID };
	size_t pluginCount = sizeof(plugins) / sizeof(plugins[0]);

	// Create context using a single GPU 
	CHECK( rprCreateContext(RPR_API_VERSION, plugins, pluginCount, RPR_CREATION_FLAGS_ENABLE_GPU0, NULL, NULL, &context) );

	// Set active plugin.
	CHECK(  rprContextSetActivePlugin(context, plugins[0]) );


	rpr_material_system matsys;
	CHECK( rprContextCreateMaterialSystem(context, 0, &matsys) );
	// Check if it is created successfully
	if (status != RPR_SUCCESS)
	{
		std::cout << "Context creation failed: check your OpenCL runtime and driver versions.\n";
		return -1;
	}

	std::cout << "Context successfully created.\n";

	// Create a scene
	rpr_scene scene;
	CHECK( rprContextCreateScene(context, &scene) );

	// Create point light
	rpr_light light;
	{
		CHECK(rprContextCreatePointLight(context, &light));
		// Create a transform: move 5 units in X axis, 8 units up Y axis, -2 units in Z axis
		RadeonProRender::matrix lightm = RadeonProRender::translation(RadeonProRender::float3(0, 8, 2));
		// Set transform for the light
		CHECK(rprLightSetTransform(light, RPR_TRUE, &lightm.m00));
		// Set light radiant power in Watts
		CHECK(rprPointLightSetRadiantPower3f(light, 255, 241, 224));
		// Attach the light to the scene
		CHECK(rprSceneAttachLight(scene, light));
	}
	// Create camera
	rpr_camera camera;
	{
		CHECK( rprContextCreateCamera(context, &camera) );

		// Position camera in world space: 
		// Camera position is (5,5,20)
		// Camera aimed at (0,0,0)
		// Camera up vector is (0,1,0)
		CHECK( rprCameraLookAt(camera, 0, 5, 20, 0, 1, 0, 0, 1, 0) );

		CHECK( rprCameraSetFocalLength(camera, 75.f) );

		// Set camera for the scene
		CHECK( rprSceneSetCamera(scene, camera) );
	}
	// Set scene to render for the context
	CHECK( rprContextSetScene(context, scene) );


	// Create cube mesh
	rpr_shape cube;
	{
		CHECK(rprContextCreateMesh(context,
			(rpr_float const*)&cube_data[0], 24, sizeof(vertex),
			(rpr_float const*)((char*)&cube_data[0] + sizeof(rpr_float) * 3), 24, sizeof(vertex),
			(rpr_float const*)((char*)&cube_data[0] + sizeof(rpr_float) * 6), 24, sizeof(vertex),
			(rpr_int const*)indices, sizeof(rpr_int),
			(rpr_int const*)indices, sizeof(rpr_int),
			(rpr_int const*)indices, sizeof(rpr_int),
			num_face_vertices, 12, &cube));

		// Add cube into the scene
		CHECK(rprSceneAttachShape(scene, cube));

		// Create a transform: -2 unit along X axis and 1 unit up Y axis
		RadeonProRender::matrix m = RadeonProRender::translation(RadeonProRender::float3(-2, 1, 0));

		// Set the transform 
		CHECK(rprShapeSetTransform(cube, RPR_TRUE, &m.m00));
	}
	// Create plane mesh
	rpr_shape plane;
	{
		CHECK(rprContextCreateMesh(context,
			(rpr_float const*)&plane_data[0], 4, sizeof(vertex),
			(rpr_float const*)((char*)&plane_data[0] + sizeof(rpr_float) * 3), 4, sizeof(vertex),
			(rpr_float const*)((char*)&plane_data[0] + sizeof(rpr_float) * 6), 4, sizeof(vertex),
			(rpr_int const*)indices, sizeof(rpr_int),
			(rpr_int const*)indices, sizeof(rpr_int),
			(rpr_int const*)indices, sizeof(rpr_int),
			num_face_vertices, 2, &plane));

		// Add plane into the scene
		CHECK(rprSceneAttachShape(scene, plane));
	}

	// Create simple diffuse shader
	rpr_material_node diffuse;
	{
		CHECK(rprMaterialSystemCreateNode(matsys, RPR_MATERIAL_NODE_DIFFUSE, &diffuse));

		// Set diffuse color parameter to gray
		CHECK(rprMaterialNodeSetInputF(diffuse, "color", 0.5f, 0.5f, 0.5f, 1.f));

		// Set shader for cube & plane meshes
		CHECK(rprShapeSetMaterial(cube, diffuse));

		CHECK(rprShapeSetMaterial(plane, diffuse));
	}


	///////// AOV Tutorial //////////

	// Create framebuffer to store rendering result
	rpr_framebuffer_desc desc;
	desc.fb_width = 800;
	desc.fb_height = 600;

	// 4 component 32-bit float value each
	rpr_framebuffer_format fmt = { 4, RPR_COMPONENT_TYPE_FLOAT32 };
	rpr_framebuffer frame_buffer, frame_buffer2;
	CHECK(rprContextCreateFrameBuffer(context, fmt, &desc, &frame_buffer));
	CHECK(rprContextCreateFrameBuffer(context, fmt, &desc, &frame_buffer2));

	// Clear framebuffer to black color
	CHECK(rprFrameBufferClear(frame_buffer));
	CHECK(rprFrameBufferClear(frame_buffer2));

	// Set framebuffer for the context
	CHECK(rprContextSetAOV(context, RPR_AOV_COLOR, frame_buffer));
	CHECK(rprContextSetAOV(context, RPR_AOV_SHADING_NORMAL, frame_buffer2));
	//CHECK(rprContextSetAOV(context, RPR_AOV_WORLD_COORDINATE, frame_buffer2));
	//CHECK(rprContextSetAOV(context, RPR_AOV_DEPTH, frame_buffer2));
	//CHECK(rprContextSetAOV(context, RPR_AOV_OBJECT_ID, frame_buffer2));

	// Progressively render an image
	for (int i = 0; i < NUM_ITERATIONS; ++i)
	{
		CHECK( rprContextRender(context) );
	}

	std::cout << "Rendering finished.\n";

	// Save the result to file
	CHECK( rprFrameBufferSaveToFile(frame_buffer, "33.png") );
	CHECK( rprFrameBufferSaveToFile(frame_buffer2, "33_1.png") );

	// Release the stuff we created
	CHECK(rprObjectDelete(matsys));
	CHECK(rprObjectDelete(plane));
	CHECK(rprObjectDelete(cube));
	CHECK(rprObjectDelete(light));
	CHECK(rprObjectDelete(diffuse));
	CHECK(rprObjectDelete(scene));
	CHECK(rprObjectDelete(camera));
	CHECK(rprObjectDelete(frame_buffer));
	CHECK(rprObjectDelete(frame_buffer2));
	CHECK(rprObjectDelete(context));
	return 0;
}


// Things to try in this tutorial:
// 1) You could try to change the AOV binding of the framebuffer2 and check the result
// 2) You should have notice that some buffer are dark or white, this is because buffer isn't normalize