#include #include #include #include #include #include #include #include #include #include #include #include #include #include "addSphereToScene.h" #include "pointOnSphere.h" #include "Sphere.h" //////////////////////////////////////////////////////////// // // (C) Copyright 2001 // David C. Banks // Florida State University // // Create samples on a spherical surface. // // Randomly update the sample positions. // // Dynamically recompute the emittance according to the // viewpoint (as contained in the camera position). Sphere *SphereSurface; SoSeparator *scene; SoComplexity *complexity; int NumSamples = 300; int SampleChildNumber = 0; // This keeps track of where in the scene graph // to find the surface samples. SoCamera *Camera; SoFieldSensor *CameraSensor; //////////////////////////////////////////////////////////// // // Calculate the emittance for a sample on a spherical // surface. The emittance distribution is given by // // L = vOut . N // // where vOut is the direction from the sample to the // viewpoint (the camera position) and N is the normal // to the spherical surface given by the vector from // the sphere's center to the sample point. // // Both vOut and N are unit vectors. void updateEmittance(int sphereNum) { SoSeparator *sphereSep; SoMaterial *sphereMaterial; SoTransform *sphereTransform; double dotProd; SbVec3f normal; SbVec3f vOut; sphereSep = (SoSeparator *) scene->getChild(SampleChildNumber + sphereNum); sphereMaterial = (SoMaterial *) sphereSep->getChild(0); sphereTransform = (SoTransform *) sphereSep->getChild(1); if (sphereMaterial != NULL) { normal = sphereTransform->translation.getValue() - SphereSurface->getCenter(); normal = sphereTransform->translation.getValue(); normal.normalize(); vOut = Camera->position.getValue() - sphereTransform->translation.getValue(); vOut.normalize(); dotProd = normal.dot(vOut); if (dotProd < 0.0) { dotProd = 0.0; } sphereMaterial->emissiveColor = SphereSurface->getEmissiveColor() * dotProd; } } //////////////////////////////////////////////////////////// // // This callback function is called when the camera moves. void cameraUpdate(void *data, SoSensor *) { for (int i = 0; i < NumSamples; i++) { updateEmittance(i); } } //////////////////////////////////////////////////////////// // // This callback function is repeatedly called through // the Inventor main loop. It randomly samples a point on // the sphere surface and puts a sphere around the sample. // void animate(void *data, SoSensor *) { static int count = 0; // which sample to recompute // Check to see if the viewer has changed cameras, // like from perspective to orthographic. SoXtExaminerViewer *myViewer = (SoXtExaminerViewer *) data; SoCamera *currentCamera = myViewer->getCamera(); if (currentCamera != Camera) { Camera = currentCamera; // reset the camera and its callback. delete CameraSensor; CameraSensor = new SoFieldSensor(cameraUpdate, myViewer); CameraSensor->attach(&Camera->position); CameraSensor->setPriority(myViewer->getRedrawPriority() -1); } SoSeparator *sphereSep; SoTransform *sphereTransform; SbVec3f samplePoint; sphereSep = (SoSeparator *) scene->getChild(SampleChildNumber + count); sphereTransform = (SoTransform *) sphereSep->getChild(1); while (pointOnSphere(samplePoint) ) { } sphereTransform->translation = samplePoint*SphereSurface->getRadius(); updateEmittance(count); count = (count + 1) % NumSamples; } //////////////////////////////////////////////////////////// // // Continually add little spheres at randomly-chosen points // on a unit sphere. // // scene // | // ---------------------------------- // | | | // complexity ----- ... ----- // | | | | // ----- ----- // sphere // subgraph void main(int, char **argv) { // Initialize the random number generator. // It is used to get locations for the surface samples. srand48(getpid()); // Initialize Inventor and Xt Widget myWindow = SoXt::init(argv[0]); if (myWindow == NULL) exit(1); SoXtExaminerViewer *myViewer = new SoXtExaminerViewer(myWindow); // Create a scene. scene = new SoSeparator(); // Create a spherical surface to be sampled. SphereSurface = new Sphere(); SphereSurface->setEmissiveColor(0.95, 0.75, 0.5); SphereSurface->setRadius(1.5); SoSphere *sphereSample = new SoSphere(); sphereSample->radius = 0.2; // this sphere is re-used for each sample point. // Set the resolution of the mesh that defines the sphere. complexity = new SoComplexity(); complexity->value = 0.30; scene->addChild(complexity); SampleChildNumber ++; SoCube *cube = new SoCube(); scene->addChild(cube); SampleChildNumber ++; // The camera's position is needed in order to update the emittance. // But evidently the camera doesn't exist until the viewer's scene graph // is set. Why? myViewer->setSceneGraph(scene); Camera = myViewer->getCamera(); // Create random samples on the surface. for (int i = 0; i < NumSamples; i++) { addEmissiveSphereToScene(sphereSample, scene, SphereSurface->getRadius()); updateEmittance(i); } // Continually re-sample the surface. SoTimerSensor *sensor = new SoTimerSensor(animate, myViewer); sensor->setInterval(SbTime(0.2)); // updates per second sensor->schedule(); // When the camera moves, recompute the emittance. SoFieldSensor *CameraSensor = new SoFieldSensor(cameraUpdate, myViewer); CameraSensor->attach(&Camera->position); CameraSensor->setPriority(myViewer->getRedrawPriority() -1); //CameraSensor->schedule(); // The callback works even when the CameraSensor is not scheduled. Why? myViewer->setTitle("Emittance"); myViewer->show(); myViewer->viewAll(); SoXt::show(myWindow); SoXt::mainLoop(); }