
/*

RPRS : architecture of file containing the RPR scene.
you can import and export RPRS.
this demo will create a scene, export it to RPRS, import the RPRS file, and render it.

*/



#include "MultiTutorials.h"

#define CHECK assert(status == RPR_SUCCESS);



// Create layered shader give two shaders and respective IORs
rpr_material_node create_layered_shader(rpr_context context, rpr_material_system matsys, rpr_material_node base, rpr_material_node top, rpr_float baseior, rpr_float topior)
{
	rpr_int status = RPR_SUCCESS;

	// Create a shader
	rpr_material_node layered = 0;

	// Create a node
	status = rprMaterialSystemCreateNode(matsys, RPR_MATERIAL_NODE_BLEND, &layered) ; CHECK;

	// Set shader for base layer
	status = rprMaterialNodeSetInputN(layered, "color0", base) ; CHECK;

	// Set shader for top layer
	status = rprMaterialNodeSetInputN(layered, "color1", top) ; CHECK;

	// Set index of refraction for base layer
	status = rprMaterialNodeSetInputF(layered, "weight", 0.5f, 0.5f, 0.5f, 1.f) ; CHECK;

	return layered;
}


void MultiDemos::RPRS()
{

	//
	// FIRST : we generate a scene :
	//
	//

    std::srand(123);

    std::cout << "Radeon ProRender SDK advanced 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 
	status = rprCreateContext(RPR_API_VERSION, plugins, pluginCount, RPR_CREATION_FLAGS_ENABLE_GPU0, NULL, NULL, &context) ; CHECK;

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

	rpr_material_system matsys;
	status = 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 ;
    }

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

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

    // Load the mesh from raw binary file
    size_t num_vertices, num_normals, num_texcoords, num_faces;
    rpr_int vertex_stride, normal_stride, texcoord_stride;
	const std::string fileMeshPath = "../../Resources/Meshes/sphere.bin";
    std::ifstream fs(fileMeshPath, std::ios::in | std::ios::binary);
	if ( fs.fail() || !fs.is_open() )
	{
		std::cout << "Error : " << fileMeshPath << " not found.\n";
		return ;
	}

    fs.read((char*)&num_vertices, sizeof(size_t));
    fs.read((char*)&num_normals, sizeof(size_t));
    fs.read((char*)&num_texcoords, sizeof(size_t));
    fs.read((char*)&vertex_stride, sizeof(rpr_int));
    fs.read((char*)&normal_stride, sizeof(rpr_int));
    fs.read((char*)&texcoord_stride, sizeof(rpr_int));

    std::vector<rpr_float> vertices(num_vertices * 3);
    std::vector<rpr_float> normals(num_normals * 3);
    std::vector<rpr_float> texcoords(num_texcoords * 2);

    fs.read((char*)&vertices[0], vertex_stride * num_vertices);
    fs.read((char*)&normals[0], normal_stride * num_normals);
    fs.read((char*)&texcoords[0], texcoord_stride * num_texcoords);

    fs.read((char*)&num_faces, sizeof(size_t));

    std::vector<rpr_int > nfv(num_faces);
    fs.read((char*)&nfv[0], num_faces * sizeof(rpr_int));

    // This mesh is composed of quads, so reserve 4 indices per face
    std::vector<rpr_int> vertex_indices(4*num_faces);
    fs.read((char*)&vertex_indices[0], 4 * num_faces * sizeof(rpr_int));
    fs.close();

	rpr_shape sphere;
	status = rprContextCreateMesh(context,
        (rpr_float const*)&vertices[0], num_vertices, vertex_stride,
        (rpr_float const*)&normals[0], num_normals, normal_stride,
        (rpr_float const*)&texcoords[0], num_texcoords, texcoord_stride,
        (rpr_int const*)&vertex_indices[0], sizeof(rpr_int),
        (rpr_int const*)&vertex_indices[0], sizeof(rpr_int),
        (rpr_int const*)&vertex_indices[0], sizeof(rpr_int),
        &nfv[0], num_faces, &sphere);
    assert(status ==  RPR_SUCCESS);

    // Create a scaling transform transform
    RadeonProRender::matrix m = RadeonProRender::scale(RadeonProRender::float3(0.03f, 0.03f, 0.03f));

    // Set the transform 
    status = rprShapeSetTransform(sphere, RPR_TRUE, &m.m00);CHECK;

    // Add the sphere into the scene
    status = rprSceneAttachShape(scene, sphere);CHECK;

    // Create simple reflection shader
	rpr_material_node shader;
	status = rprMaterialSystemCreateNode(matsys, RPR_MATERIAL_NODE_DIFFUSE, &shader);CHECK;

    // Set specular color parameter to yellow
    status = rprMaterialNodeSetInputF(shader, "color", 0.7f, 0.7f, 0.0f, 1.f); CHECK;

    // Set shader for shpere mesh
    status = rprShapeSetMaterial(sphere, shader);CHECK;


    // Generate random spheres using instancing
    for (int i = 0; i<64; ++i)
    {
        // Create a scaling transform
        rpr_float r = 0.1f * rand_float();
        rpr_float x = (i & 7) - 3.5f;
        rpr_float y = (i >> 3) - 3.5f;
        rpr_float z = 10 * rand_float() - 5.f;

        m = RadeonProRender::translation(RadeonProRender::float3(x,y,z)) * RadeonProRender::scale(RadeonProRender::float3(r + 0.01f, r + 0.01f, r + 0.01f));
		rpr_shape s;
		status = rprContextCreateInstance(context, sphere, &s);CHECK;

        rprShapeSetTransform(s, RPR_TRUE, &m.m00);CHECK;

        //if (rand_float() > 0.1f)
        {
            // Create sphere with layered shader
            float t = rand_float();
			rpr_material_node base;
			status = rprMaterialSystemCreateNode(matsys, RPR_MATERIAL_NODE_DIFFUSE, &base);CHECK;

			rpr_material_node top;
			status = rprMaterialSystemCreateNode(matsys, RPR_MATERIAL_NODE_MICROFACET, &top);CHECK;

            // Set shader parameters
            rpr_float baseior = 3 * rand_float() + 1.f;
            rpr_float topior = 3 * rand_float() + 1.f;

            // Diffuse color
            status = rprMaterialNodeSetInputF(base, "color", rand_float(), rand_float(), rand_float(), 1.f); CHECK;

            // Specular color
            status = rprMaterialNodeSetInputF(top, "color", rand_float(), rand_float(), rand_float(), 1.f); CHECK;

            // Roughness
            status = rprMaterialNodeSetInputF(top, "roughness", 0.1f + rand_float() * 0.8f, 0.f, 0.f, 1.f); CHECK;

            // Create layered shader
            rpr_material_node layered = create_layered_shader(context, matsys, base, top, baseior, topior);

            // Set the shader for the instance
            status = rprShapeSetMaterial(s, layered); CHECK;
        }


        // Attach the shape to the scene
        status = rprSceneAttachShape(scene, s); CHECK;
    }

    // Create camera
	rpr_camera camera = 0;
	status = rprContextCreateCamera(context, &camera); CHECK;

    status = rprCameraLookAt(camera,     0, 0, 13,     0, 0, 0,       0, 1, 0);CHECK;


    // Set camera for the scene
    status = rprSceneSetCamera(scene, camera); CHECK;

    // Set scene to render for the context
    status = rprContextSetScene(context, scene); CHECK;

    // Create image-based environment light
	rpr_light ibl = 0;
	status = rprContextCreateEnvironmentLight(context, &ibl);  CHECK;

    // Set an image for the light to take the radiance values from
	const std::string pathImageFile = "../../Resources/Textures/Apartment.hdr";
	rpr_image iblimg;
	status = rprContextCreateImageFromFile(context, pathImageFile.c_str(), &iblimg);
	if ( status == RPR_ERROR_IO_ERROR )
	{
		std::cout << "Error : " << pathImageFile << " not found.\n";
		return ;
	}
	

    status = rprEnvironmentLightSetImage(ibl, iblimg); CHECK;

	status = rprEnvironmentLightSetIntensityScale(ibl,(rpr_float)1.000000f);  CHECK;

    // Set IBL as a background for the scene
    status = rprSceneAttachLight(scene, ibl);  CHECK;

    // Create framebuffer to store rendering result
    rpr_framebuffer_desc desc;
    desc.fb_width = m_frameBufferSizeWidth;
    desc.fb_height = m_frameBufferSizeHeight;

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

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

    // Set framebuffer for the context
	status = rprContextSetAOV(context, RPR_AOV_COLOR, frame_buffer); CHECK;


    // Set antialising options
    // 2 AA samples per subpixel
    status = rprContextSetParameter1u(context, "aasamples", 2); CHECK;

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

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

    // Save the result to file
	GenerateOutImageFile("OriginalScene.png"  , frame_buffer );


	const char rprsFileName[] = "rprsfiletest_.rprs";


	//
	// now, we export the scene to an RPRS file with   rprsExport
	//
	//
	status = rprsExport(rprsFileName,context, scene,
		0,0,0,
		0,0,0, 
		0);
	CHECK;

	//
	//  we import the scene from the RPRS file with   rprsImport
	//
	//
	{


		fr_context context__temp = NULL;
		fr_int tahoePluginID = rprRegisterPlugin("Tahoe64.dll");
		assert(tahoePluginID != -1);
		fr_int plugins[] = { tahoePluginID};
		size_t pluginCount = sizeof(plugins) / sizeof(plugins[0]);
		status = rprCreateContext(FR_API_VERSION, plugins, pluginCount, FR_CREATION_FLAGS_ENABLE_GPU0, NULL, NULL, &context__temp);CHECK;
		status = rprContextSetActivePlugin(context__temp, plugins[0]);CHECK;
	
		rpr_framebuffer frame_buffer__temp = 0;
		status = rprContextCreateFrameBuffer(context__temp, fmt, &desc, &frame_buffer__temp);  CHECK;
		status = rprFrameBufferClear(frame_buffer__temp); CHECK;
		status = rprContextSetAOV(context__temp, RPR_AOV_COLOR, frame_buffer__temp); CHECK;

		rpr_scene scene__temp = 0;
		rpr_material_system matsys__temp = NULL;
		status = rprContextCreateMaterialSystem(context__temp, 0, &matsys__temp);CHECK;
		status = rprsImport(rprsFileName, context__temp, matsys__temp,&scene__temp,false);CHECK;

		for(int i=0; i<m_maxIterationRendering; i++)
		{
			status = rprContextRender(context__temp);
			CHECK;
		}
		GenerateOutImageFile("RPRSScene.png"  , frame_buffer__temp ); // RPRSScene.png should be the same that : OriginalScene.png

		status = rprObjectDelete(frame_buffer__temp); CHECK; frame_buffer__temp = 0;
		status = rprObjectDelete(matsys__temp); CHECK; matsys__temp = 0;
		status = rprObjectDelete(scene__temp); CHECK; scene__temp = 0;
		status = rprObjectDelete(context__temp); CHECK; context__temp = 0;
	}




	// RPRS has function to get the imported objects : 
	//
	//rprsListImportedCameras
	//rprsListImportedMaterialNodes
	//rprsListImportedLights
	//rprsListImportedShapes
	//rprsListImportedImages
	//
	// example with  rprsListImportedShapes 
	int nbOfObjs = 0;
	status = rprsListImportedShapes(0,0,&nbOfObjs); CHECK;
	rpr_shape* objs = new rpr_shape[nbOfObjs];
	status = rprsListImportedShapes(objs,nbOfObjs*sizeof(rpr_shape),0); CHECK;
	for(int i=0; i<nbOfObjs; i++)
	{
		//enum each imported objects with  rprsfiletest_.rprs
	}


	// another RPRS function :
	// export the RPRS file scene to XML file 
	// for the moment, we can only export to XML, but not import from XML
	status = rprsExportToXML(rprsFileName,"rprs.xml");CHECK;


    // Release the stuff we created
	status = rprObjectDelete(matsys); CHECK;
	status = rprObjectDelete(ibl); CHECK;
	status = rprObjectDelete(sphere); CHECK;
	status = rprObjectDelete(shader); CHECK;
	status = rprObjectDelete(scene); CHECK;
	status = rprObjectDelete(camera); CHECK;
	status = rprObjectDelete(frame_buffer); CHECK;
	status = rprObjectDelete(context); CHECK;
    return ;


}

