/*
GenSpheresProc.cpp
  
A relatively simple example of a RiProcedural. For info see,
https://rmanwiki.pixar.com/display/REN22/Procedural+Primitives
Also refer to,
http://fundza.com/rfm/procedural/starter/index.html
  
M.A.Kesson
22 March 2020
*/
  
#include <ri.h>
#include <RixInterfaces.h>
#include <RiTypesHelper.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
  
#define DEFAULT_RAD 1
#define DEFAULT_NUM 1
  
// A RiProcedural must implement these functions.
extern "C"
{
    PRMANEXPORT RtPointer ConvertParameters ( RtString paramStr              );
    PRMANEXPORT RtVoid    Subdivide         ( RtPointer data, RtFloat detail );
    PRMANEXPORT RtVoid    Free              ( RtPointer data                 );
}
  
// A custom data structure for defining an arbitrary number of spheres
typedef struct {
        RtFloat radius;
        RtInt     num;
        RtFloat min_circumference;
        RtFloat max_circumference;
        RtFloat spread;
        } SpheresData;
  
// Declare our utility functions...This is the equivalent of pythons
// random.uniform() function.
RtFloat randBetween(RtFloat min, RtFloat max);
  
// ----------------------------------------------------
// A RiProcedural required function
// ----------------------------------------------------
//
RtPointer ConvertParameters(RtString paramStr) {
    // Allocate a block of memory to store one instance of our custom data structure.
    SpheresData *dataPtr = (SpheresData*)calloc(1, sizeof(SpheresData));
    
    sscanf(paramStr, "%f %d %f  %f %f", &dataPtr->radius, 
                                        &dataPtr->num, 
                                        &dataPtr->min_circumference, 
                                        &dataPtr->max_circumference, 
                                        &dataPtr->spread);
  
    return (RtPointer)dataPtr;
}
  
// ----------------------------------------------------
// A RiProcedural required function
// ----------------------------------------------------
RtVoid Subdivide(RtPointer data, RtFloat detail) {
    RtFloat radius = ((SpheresData*)data)->radius;
    RtInt     num = ((SpheresData*)data)->num;
    RtFloat min_circum = ((SpheresData*)data)->min_circumference;
    RtFloat max_circum = ((SpheresData*)data)->max_circumference;
    RtFloat spread = ((SpheresData*)data)->spread;
    srand(1);
    RtFloat value = 0.1;
    RiAttribute("displacementbound", "float sphere", &value, RI_NULL);
    
    // Output randomly located spheres
    for(int n = 0; n < num; n++) {
        RtFloat x = randBetween(-spread,spread);
        RtFloat y = randBetween(-spread,spread);
        RtFloat z = randBetween(-spread,spread);
        RtFloat circum = randBetween(min_circum, max_circum);
        
        // Unless we put each sphere inside its own group the translations
        // compound on each other.
        RiTransformBegin();
            RiTranslate(x,y,z);
            
            RtColor tint[1] = { {randBetween(0.8,1),randBetween(0.8,1),randBetween(0.8,1) } } ;
            RtFloat gain[1] = { randBetween(0.2, 1.0) };
            RtFloat disp[1] = { randBetween(0.0,0.2) };
            RiSphere(radius, -radius, radius, 360, 
                                        "constant color Tint", (RtPointer)tint, 
                                        "constant float Gain", (RtPointer)gain, 
                                        "constant float Disp", (RtPointer)disp, RI_NULL);
            //RiSphere(radius, -radius, radius, circum, RI_NULL);
        RiTransformEnd();
        }
    }
// ----------------------------------------------------
// A RiProcedural required function
// ----------------------------------------------------
RtVoid Free(RtPointer data) {
    free(data);
    }
    
// ----------------------------------------------------
// Our utility functions begin here 
// ----------------------------------------------------
RtFloat randBetween(RtFloat min, RtFloat max) {
    return ((RtFloat)rand()/RAND_MAX) * (max - min) + min;
    }