#include "ProxyHeaders.hpp" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BurnLightsProcess::BurnLightsProcess(): materialsAreWhite(true), lightsToBurn(NULL), matrixStack(30, 20, "Light Burning Stack") { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BurnLightsProcess::BurnLightsProcess(NotationFile *data_file): Process(data_file), lightsToBurn(NULL), matrixStack(30, 20, "Light Burning Stack") { Check_Object(data_file); Page *page = data_file->FindPage("BurnLights"); if (page) page->GetEntry("MaterialsAreWhite", &materialsAreWhite); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ChildProxy::FindLights(BurnLightsProcess *process) { Check_Object(this); Check_Object(process); process->FindLightsCallback(this); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void GroupProxy::FindLights(BurnLightsProcess *process) { Check_Object(this); Check_Object(process); // //-------------------------- // Call the control callback //-------------------------- // process->FindLightsCallback(this); if (!process->continueProcess) return; // //--------------------------------------------------- // If the proxy is a group, look in it for lights //--------------------------------------------------- // ChildProxy *child = UseFirstChildProxy(); while (child) { Check_Object(child); ChildProxy *next = child->UseNextSiblingProxy(); child->FindLights(process); child->DetachReference(); child = next; if (!process->continueProcess) { if (child) child->DetachReference(); break; } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void LightProxy::FindLights(BurnLightsProcess *process) { Check_Object(this); Check_Object(process); // //-------------------------- // Call the control callback //-------------------------- // process->FindLightsCallback(this); if (!process->continueProcess) return; // //-------------------------------------------------- // If the proxy is a light, add it to the light list //-------------------------------------------------- // AttachReference(); process->lightsToBurn.Add(this); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void SceneProxy::FindLights(BurnLightsProcess *process) { Check_Object(this); Check_Object(process); // //-------------------------- // Call the control callback //-------------------------- // process->FindLightsCallback(this); if (!process->continueProcess) return; // //----------------------------------------------- // If the proxy is a scene, look in it for lights //----------------------------------------------- // ChildProxy *child = UseFirstChildProxy(); while (child) { Check_Object(child); ChildProxy *next = child->UseNextSiblingProxy(); child->FindLights(process); child->DetachReference(); child = next; if (!process->continueProcess) { if (child) child->DetachReference(); break; } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VertexProxy::BurnLights( BurnLightsProcess *process, DynamicArrayOf &lights ) { Check_Object(this); Check_Object(process); Check_Object(&lights); // //-------------------------- // Call the control callback //-------------------------- // process->BurnLightsCallback(this); if (!process->continueProcess) return; // //------------------------------------------------------------- // Make sure that this vertex can be lit (i.e. it has a normal) //------------------------------------------------------------- // Normal3D normal; if (!GetNormal(&normal)) return; // //-------------------------------------------------------------------- // Get the current color for its alpha, then initialize to the ambient // color //-------------------------------------------------------------------- // unsigned light_count = lights.GetLength(); RGBAColor total_color; GetColor(&total_color); total_color.red = 0.0f; total_color.green = 0.0f; total_color.blue = 0.0f; // //----------------------------------- // Test each light against the vertex //----------------------------------- // for (unsigned i=0; iGetColor(&light_color); if (light->IsAmbient()) { total_color.red += light_color.red; total_color.green += light_color.green; total_color.blue += light_color.blue; continue; } // //--------------------------------------------------------------------- // Get the falloff distances. If there are no falloff distances, it is // an infinite light, so set the light normal directly from the matrix //--------------------------------------------------------------------- // Scalar n,f; UnitVector3D light_z; if (!light->GetFalloffDistance(&n, &f)) lights[i].lightToLocal.GetLocalForwardInWorld(&light_z); // //--------------------------------------------------------------- // Otherwise, it will be a point or spot light, in which case the // translation component of the lightToLocal matrix contains the // vertex to light vector //--------------------------------------------------------------- // else { Point3D vertex_to_light; vertex_to_light = lights[i].lightToLocal; Point3D position; GetPosition(&position); vertex_to_light -= position; // //-------------------------------------------------------------- // If the distance to the vertex is zero, the light will not // contribute to the vertex coloration. Otherwise, decrease the // light level as appropriate to the distance //-------------------------------------------------------------- // Scalar length = vertex_to_light.GetLength(); if (Small_Enough(length) || length>=f) continue; else if (length > n) { Verify(f - n > SMALL); Scalar falloff = (length - n) / (f - n); light_color.red *= falloff; light_color.green *= falloff; light_color.blue *= falloff; } // //-------------------------------------------------------------- // If this is a point light, set the light vector to the negated // normal of vertex to light //-------------------------------------------------------------- // Radian spread_angle; if (!light->GetSpreadAngle(&spread_angle)) { length = -1.0f / length; light_z.Vector3D::Multiply(vertex_to_light, length); } // //--------------------------------------------------------------- // Otherwise, this is a spotlight, so we need to reduce the light // level further based upon the spread angle of the light //--------------------------------------------------------------- // else { lights[i].lightToLocal.GetLocalForwardInWorld(&light_z); length = -1.0f / length; vertex_to_light *= length; Scalar spread = Cos(spread_angle); Scalar t = vertex_to_light * light_z; if (t <= spread) { continue; } Verify(!Close_Enough(spread, 1.0f)); spread = 1.0f - ((1.0f - t) / (1.0f - spread)); light_color.red *= spread; light_color.green *= spread; light_color.blue *= spread; light_z.x = vertex_to_light.x; light_z.y = vertex_to_light.y; light_z.z = vertex_to_light.z; } } // //------------------------------------------------------------------- // Now we reduce the light level falling on the vertex based upon the // cosine of the angle between light and normal //------------------------------------------------------------------- // Scalar cosine = -(light_z * normal); if (cosine > SMALL) { light_color.red *= cosine; light_color.green *= cosine; light_color.blue *= cosine; total_color.red += light_color.red; total_color.green += light_color.green; total_color.blue += light_color.blue; } } // //----------------------------------------------------------------- // We now have the total color on the vertex established, so set it //----------------------------------------------------------------- // Clamp(total_color.red, 0.0f, 1.0f); Clamp(total_color.green, 0.0f, 1.0f); Clamp(total_color.blue, 0.0f, 1.0f); SetColor(total_color); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ChildProxy::BurnLights(BurnLightsProcess *process) { Check_Object(this); Check_Object(process); process->BurnLightsCallback(this); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void PolygonMeshProxy::BurnLights(BurnLightsProcess *process) { Check_Object(this); Check_Object(process); // //-------------------------- // Call the control callback //-------------------------- // process->BurnLightsCallback(this); if (!process->continueProcess) return; // //----------------------------------------------------------------- // Apply the local to parent matrix to the stack, then invert it in // preparation for the polygon meshes //----------------------------------------------------------------- // LinearMatrix4D local_to_parent; GetLocalToParent(&local_to_parent); process->matrixStack.Concatenate(local_to_parent); LinearMatrix4D world_to_local; world_to_local.Invert(process->matrixStack); // //----------------- // Count the lights //----------------- // ChainIteratorOf light_itr(&process->lightsToBurn); LightProxy* light; int light_count = 0; while ((light = light_itr.ReadAndNext()) != NULL) { Check_Object(light); ++light_count; } // //------------------------------------------ // transform all the lights into local space //------------------------------------------ // DynamicArrayOf lights(light_count); light_itr.First(); unsigned i; for (i=0; iGetLocalToWorld(&light_to_world); lights[i].lightToLocal.Multiply(light_to_world, world_to_local); } // //------------------------------------------ // Recurse the children, and stop if told to //------------------------------------------ // DynamicArrayOf vertices; unsigned vertex_count = UseVertexArray(&vertices); Verify(vertex_count == vertices.GetLength()); for (i=0; iBurnLights(process, lights); if (!process->continueProcess) break; } DetachArrayReferences(&vertices); process->matrixStack.Pop(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void GroupProxy::BurnLights(BurnLightsProcess *process) { Check_Object(this); Check_Object(process); // //-------------------------- // Call the control callback //-------------------------- // process->BurnLightsCallback(this); if (!process->continueProcess) return; // //---------------------------------------------- // Apply the local to parent matrix to the stack //---------------------------------------------- // LinearMatrix4D local_to_parent; GetLocalToParent(&local_to_parent); process->matrixStack.Concatenate(local_to_parent); // //------------------------------------------ // Recurse the children, and stop if told to //------------------------------------------ // ChildProxy *child = UseFirstChildProxy(); while (child) { Check_Object(child); ChildProxy *next = child->UseNextSiblingProxy(); child->BurnLights(process); child->DetachReference(); child = next; if (!process->continueProcess) { if (child) child->DetachReference(); break; } } process->matrixStack.Pop(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void SceneProxy::BurnLights(BurnLightsProcess *process) { Check_Object(this); Check_Object(process); // //-------------------------- // Call the control callback //-------------------------- // process->BurnLightsCallback(this); if (!process->continueProcess) return; // //------------------------------------------------------------- // Get the ambient color and put a identity matrix on the stack //------------------------------------------------------------- // process->matrixStack.Push(LinearMatrix4D::Identity); // //--------------------------------------------- // Burn the lights into each child of the scene //--------------------------------------------- // ChildProxy *child = UseFirstChildProxy(); while (child) { Check_Object(child); ChildProxy *next = child->UseNextSiblingProxy(); child->BurnLights(process); child->DetachReference(); child = next; if (!process->continueProcess) { if (child) child->DetachReference(); break; } } // //----------------------- // Remove the last matrix //----------------------- // process->matrixStack.Pop(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BurnLightsProcess::DiscardLights() { Check_Object(this); ChainIteratorOf lights(&lightsToBurn); LightProxy* light; while ((light = lights.ReadAndNext()) != NULL) { Check_Object(light); light->DetachReference(); } }