mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 03:26:09 +08:00
971 lines
22 KiB
C++
971 lines
22 KiB
C++
//
|
|
// Copyright (c) 1993-2022 Robert McNeel & Associates. All rights reserved.
|
|
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
|
|
// McNeel & Associates.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
|
|
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
|
|
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
|
|
//
|
|
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
#include "opennurbs.h"
|
|
|
|
#if !defined(ON_COMPILING_OPENNURBS)
|
|
// This check is included in all opennurbs source .c and .cpp files to insure
|
|
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
|
|
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
|
|
// and the opennurbs .h files alter what is declared and how it is declared.
|
|
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
|
|
#endif
|
|
|
|
ON_OBJECT_IMPLEMENT(ON_Light,ON_Geometry,"85A08513-F383-11d3-BFE7-0010830122F0");
|
|
|
|
void ON_Light::Default()
|
|
{
|
|
m_light_name.Destroy();
|
|
m_bOn = true;
|
|
m_intensity = 1.0;
|
|
m_watts = 0.0;
|
|
m_style = ON::camera_directional_light;
|
|
m_ambient = ON_Color(0,0,0);
|
|
m_diffuse = ON_Color(255,255,255);
|
|
m_specular = ON_Color(255,255,255);
|
|
m_direction = ON_3dVector(0.0,0.0,-1.0);
|
|
m_location = ON_3dPoint(0.0,0.0,0.0);
|
|
m_length = ON_3dVector(0.0,0.0,0.0);
|
|
m_width = ON_3dVector(0.0,0.0,0.0);
|
|
m_spot_angle = 180.0;
|
|
m_spot_exponent = 0.0;
|
|
m_hotspot = 1.0;
|
|
m_attenuation = ON_3dVector(1.0,0.0,0.0);
|
|
m_shadow_intensity = 1.0;
|
|
m_light_index = 0;
|
|
memset(&m_light_id,0,sizeof(m_light_id));
|
|
}
|
|
|
|
ON_Light::ON_Light()
|
|
{
|
|
Default();
|
|
}
|
|
|
|
ON_Light::~ON_Light()
|
|
{
|
|
}
|
|
|
|
bool ON_Light::IsValid( ON_TextLog* text_log ) const
|
|
{
|
|
int s = Style();
|
|
if ( s <= ON::unknown_light_style || s >= ON::light_style_count ) {
|
|
ON_ERROR("ON_Light::IsValid(): illegal light style.");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ON_Light::Dump( ON_TextLog& dump ) const
|
|
{
|
|
bool bDumpDir = false;
|
|
bool bDumpLength = false;
|
|
bool bDumpWidth = false;
|
|
|
|
const char* sStyle = "unknown";
|
|
switch(Style())
|
|
{
|
|
//case ON::view_directional_light:
|
|
// sStyle = "view_directional_light";
|
|
// bDumpDir = true;
|
|
// break;
|
|
//case ON::view_point_light:
|
|
// sStyle = "view_point_light";
|
|
// break;
|
|
//case ON::view_spot_light:
|
|
// sStyle = "view_spot_light";
|
|
// bDumpDir = true;
|
|
// break;
|
|
case ON::camera_directional_light:
|
|
sStyle = "camera_directional_light";
|
|
bDumpDir = true;
|
|
break;
|
|
case ON::camera_point_light:
|
|
sStyle = "camera_point_light";
|
|
break;
|
|
case ON::camera_spot_light:
|
|
sStyle = "camera_spot_light";
|
|
bDumpDir = true;
|
|
break;
|
|
case ON::world_directional_light:
|
|
sStyle = "world_directional_light";
|
|
bDumpDir = true;
|
|
break;
|
|
case ON::world_point_light:
|
|
sStyle = "world_point_light";
|
|
break;
|
|
case ON::world_spot_light:
|
|
sStyle = "world_spot_light";
|
|
bDumpDir = true;
|
|
break;
|
|
case ON::world_linear_light:
|
|
sStyle = "linear_light";
|
|
bDumpDir = true;
|
|
bDumpLength = true;
|
|
break;
|
|
case ON::world_rectangular_light:
|
|
sStyle = "rectangular_light";
|
|
bDumpDir = true;
|
|
bDumpLength = true;
|
|
bDumpWidth = true;
|
|
break;
|
|
case ON::ambient_light:
|
|
sStyle = "ambient_light";
|
|
break;
|
|
case ON::unknown_light_style:
|
|
sStyle = "unknown";
|
|
break;
|
|
default:
|
|
sStyle = "unknown";
|
|
break;
|
|
}
|
|
dump.Print("index = %d style = %s\n",LightIndex(),sStyle);
|
|
|
|
dump.Print("location = "); dump.Print(Location()); dump.Print("\n");
|
|
if ( bDumpDir )
|
|
dump.Print("direction = "); dump.Print(Direction()); dump.Print("\n");
|
|
if ( bDumpLength )
|
|
dump.Print("length = "); dump.Print(Length()); dump.Print("\n");
|
|
if ( bDumpWidth )
|
|
dump.Print("width = "); dump.Print(Width()); dump.Print("\n");
|
|
|
|
dump.Print("intensity = %g%%\n",Intensity()*100.0);
|
|
|
|
dump.Print("ambient rgb = "); dump.PrintRGB(Ambient()); dump.Print("\n");
|
|
dump.Print("diffuse rgb = "); dump.PrintRGB(Diffuse()); dump.Print("\n");
|
|
dump.Print("specular rgb = "); dump.PrintRGB(Specular()); dump.Print("\n");
|
|
|
|
dump.Print("spot angle = %g degrees\n",SpotAngleDegrees());
|
|
}
|
|
|
|
bool ON_Light::Write(
|
|
ON_BinaryArchive& file
|
|
) const
|
|
{
|
|
int i;
|
|
bool rc = file.Write3dmChunkVersion(1,2);
|
|
// version 1.0 fields
|
|
if ( rc ) rc = file.WriteInt( m_bOn?1:0 );
|
|
i = m_style;
|
|
if ( rc ) rc = file.WriteInt( i );
|
|
if ( rc ) rc = file.WriteDouble( m_intensity );
|
|
if ( rc ) rc = file.WriteDouble( m_watts );
|
|
if ( rc ) rc = file.WriteColor( m_ambient );
|
|
if ( rc ) rc = file.WriteColor( m_diffuse );
|
|
if ( rc ) rc = file.WriteColor( m_specular );
|
|
if ( rc ) rc = file.WriteVector( m_direction );
|
|
if ( rc ) rc = file.WritePoint( m_location );
|
|
if ( rc ) rc = file.WriteDouble( m_spot_angle );
|
|
if ( rc ) rc = file.WriteDouble( m_spot_exponent );
|
|
if ( rc ) rc = file.WriteVector( m_attenuation );
|
|
if ( rc ) rc = file.WriteDouble( m_shadow_intensity );
|
|
if ( rc ) rc = file.Write3dmReferencedComponentIndex( ON_ModelComponent::Type::RenderLight, m_light_index );
|
|
if ( rc ) rc = file.WriteUuid( m_light_id );
|
|
if ( rc ) rc = file.WriteString( m_light_name );
|
|
// version 1.1 added support for linear and rectangular
|
|
if ( rc ) rc = file.WriteVector( m_length );
|
|
if ( rc ) rc = file.WriteVector( m_width );
|
|
// version 1.2 added m_hotspot support
|
|
if ( rc ) rc = file.WriteDouble( m_hotspot );
|
|
return rc;
|
|
}
|
|
|
|
bool ON_Light::Read(
|
|
ON_BinaryArchive& file
|
|
)
|
|
{
|
|
Default();
|
|
int major_version = 0;
|
|
int minor_version = 0;
|
|
bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
|
|
if ( rc && major_version == 1 ) {
|
|
int i;
|
|
// version 1.0 fields
|
|
i = 0;
|
|
if ( rc ) rc = file.ReadInt( &i );
|
|
if ( rc ) Enable(0 != i);
|
|
if ( rc ) rc = file.ReadInt( &i );
|
|
if ( rc ) SetStyle(ON::LightStyle(i));
|
|
if ( rc ) rc = file.ReadDouble( &m_intensity );
|
|
if ( rc ) rc = file.ReadDouble( &m_watts );
|
|
if ( rc ) rc = file.ReadColor( m_ambient );
|
|
if ( rc ) rc = file.ReadColor( m_diffuse );
|
|
if ( rc ) rc = file.ReadColor( m_specular );
|
|
if ( rc ) rc = file.ReadVector( m_direction );
|
|
if ( rc ) rc = file.ReadPoint( m_location );
|
|
if ( rc ) rc = file.ReadDouble( &m_spot_angle );
|
|
if ( rc ) rc = file.ReadDouble( &m_spot_exponent );
|
|
if ( rc ) rc = file.ReadVector( m_attenuation );
|
|
if ( rc ) rc = file.ReadDouble( &m_shadow_intensity );
|
|
if ( rc ) rc = file.ReadInt( &m_light_index );
|
|
if ( rc ) rc = file.ReadUuid( m_light_id );
|
|
if ( rc ) rc = file.ReadString( m_light_name );
|
|
|
|
if ( minor_version < 2 ) {
|
|
// set hotspot from 1.0 or 1.1 m_spot_exponent
|
|
double h = 1.0 - m_spot_exponent/128.0;
|
|
if ( h < 0.0 )
|
|
h = 0.0;
|
|
else if ( h > 1.0 )
|
|
h = 1.0;
|
|
m_hotspot = h;
|
|
m_spot_exponent = 0.0;
|
|
}
|
|
|
|
if ( minor_version >= 1 ) {
|
|
// version 1.1 fields
|
|
if ( rc ) rc = file.ReadVector( m_length );
|
|
if ( rc ) rc = file.ReadVector( m_width );
|
|
if ( minor_version >= 2 ) {
|
|
// version 1.2 fields
|
|
if ( rc ) rc = file.ReadDouble( &m_hotspot );
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
ON::object_type ON_Light::ObjectType() const
|
|
{
|
|
return ON::light_object;
|
|
}
|
|
|
|
int ON_Light::Dimension() const
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
bool ON_Light::GetBBox( // returns true if successful
|
|
double* boxmin, // boxmin[dim]
|
|
double* boxmax, // boxmax[dim]
|
|
bool bGrowBox
|
|
) const
|
|
{
|
|
bool rc = true;
|
|
ON_3dPointArray points(16);
|
|
|
|
switch(m_style)
|
|
{
|
|
case ON::camera_directional_light:
|
|
case ON::world_directional_light:
|
|
points.Append(m_location);
|
|
points.Append(m_location+m_direction);
|
|
break;
|
|
|
|
case ON::camera_point_light:
|
|
case ON::world_point_light:
|
|
points.Append(m_location);
|
|
break;
|
|
|
|
case ON::camera_spot_light:
|
|
case ON::world_spot_light:
|
|
if ( m_spot_angle > 0.0 && m_spot_angle < 90.0 )
|
|
{
|
|
double r = m_direction.Length()*tan(ON_PI*m_spot_angle/180.0);
|
|
ON_Circle c(ON_Plane(m_location+m_direction,m_direction),r);
|
|
ON_BoundingBox cbox = c.BoundingBox();
|
|
cbox.GetCorners( points );
|
|
}
|
|
else
|
|
{
|
|
points.Append(m_location+m_direction);
|
|
}
|
|
points.Append(m_location);
|
|
break;
|
|
|
|
case ON::ambient_light:
|
|
points.Append(m_location);
|
|
rc = false;
|
|
break;
|
|
|
|
case ON::world_linear_light:
|
|
points.Append(m_location);
|
|
points.Append(m_location+m_length);
|
|
break;
|
|
|
|
case ON::world_rectangular_light:
|
|
points.Append(m_location);
|
|
points.Append(m_location+m_length);
|
|
points.Append(m_location+m_width);
|
|
points.Append(m_location+m_width+m_length);
|
|
{
|
|
// include target and direction marker to avoid display clipping
|
|
ON_3dPoint center(m_location+(m_width+m_length)*0.5);
|
|
points.Append(center+m_direction);
|
|
ON_3dVector marker(m_direction);
|
|
marker.Unitize();
|
|
marker *= (m_width+m_length).Length()/12.0; // from GetRectangularLightSegments
|
|
points.Append(center+marker);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
rc = false;
|
|
break;
|
|
}
|
|
|
|
if ( rc && points.Count() > 0 )
|
|
{
|
|
rc = ON_GetPointListBoundingBox( 3, 0, points.Count(), 3,
|
|
(double*)points.Array(),
|
|
boxmin, boxmax,
|
|
bGrowBox?true:false )
|
|
? true
|
|
: false;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_Light::GetTightBoundingBox(
|
|
ON_BoundingBox& tight_bbox,
|
|
bool bGrowBox,
|
|
const ON_Xform* xform
|
|
) const
|
|
{
|
|
// only spotlights have a tight bbox that differs from regular bbox
|
|
if (ON::world_spot_light != m_style && ON::camera_spot_light != m_style)
|
|
return ON_Geometry::GetTightBoundingBox(tight_bbox, bGrowBox, xform);
|
|
|
|
if (bGrowBox && !tight_bbox.IsValid())
|
|
{
|
|
bGrowBox = false;
|
|
}
|
|
if (!bGrowBox)
|
|
{
|
|
tight_bbox.Destroy();
|
|
}
|
|
|
|
bool rc = true;
|
|
ON_3dPointArray points(16);
|
|
if (m_spot_angle > 0.0 && m_spot_angle < 90.0)
|
|
{
|
|
double r = m_direction.Length() * tan(ON_PI * m_spot_angle / 180.0);
|
|
ON_Circle c(ON_Plane(m_location + m_direction, m_direction), r);
|
|
ON_BoundingBox cbox;
|
|
c.GetTightBoundingBox(cbox);
|
|
cbox.GetCorners(points);
|
|
}
|
|
else
|
|
{
|
|
points.Append(m_location + m_direction);
|
|
}
|
|
points.Append(m_location);
|
|
|
|
if (points.Count() > 0)
|
|
{
|
|
rc = ON_GetPointListBoundingBox(3, 0, points.Count(), 3,
|
|
(double*)points.Array(),
|
|
tight_bbox,
|
|
bGrowBox ? true : false,
|
|
xform)
|
|
? true
|
|
: false;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_Light::Transform(
|
|
const ON_Xform& xform
|
|
)
|
|
{
|
|
ON_3dVector v;
|
|
double vlen;
|
|
TransformUserData(xform);
|
|
m_location = xform*m_location;
|
|
|
|
v = xform*m_direction;
|
|
vlen = v.Length();
|
|
if ( vlen > 0.0 ) {
|
|
m_direction = v;
|
|
}
|
|
|
|
v = xform*m_length;
|
|
vlen = v.Length();
|
|
if ( vlen > 0.0 ) {
|
|
m_length = v;
|
|
}
|
|
|
|
v = xform*m_width;
|
|
vlen = v.Length();
|
|
if ( vlen > 0.0 ) {
|
|
m_width = v;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ON_Light::Enable(bool b)
|
|
{
|
|
bool oldb = m_bOn;
|
|
m_bOn = (b)?true:false;
|
|
return oldb;
|
|
}
|
|
|
|
bool ON_Light::IsEnabled() const
|
|
{
|
|
return m_bOn;
|
|
}
|
|
|
|
ON::light_style ON_Light::Style() const
|
|
{
|
|
return m_style;
|
|
}
|
|
|
|
const bool ON_Light::IsPointLight() const
|
|
{
|
|
bool rc;
|
|
switch(m_style)
|
|
{
|
|
//case ON::view_point_light:
|
|
case ON::camera_point_light:
|
|
case ON::world_point_light:
|
|
rc = true;
|
|
break;
|
|
default:
|
|
rc = false;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
const bool ON_Light::IsDirectionalLight() const
|
|
{
|
|
bool rc;
|
|
switch(m_style)
|
|
{
|
|
//case ON::view_directional_light:
|
|
case ON::camera_directional_light:
|
|
case ON::world_directional_light:
|
|
rc = true;
|
|
break;
|
|
default:
|
|
rc = false;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
const bool ON_Light::IsSpotLight() const
|
|
{
|
|
bool rc;
|
|
switch(m_style)
|
|
{
|
|
//case ON::view_spot_light:
|
|
case ON::camera_spot_light:
|
|
case ON::world_spot_light:
|
|
rc = true;
|
|
break;
|
|
default:
|
|
rc = false;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
const bool ON_Light::IsLinearLight() const
|
|
{
|
|
bool rc;
|
|
switch(m_style)
|
|
{
|
|
//case ON::view_linear_light:
|
|
//case ON::camera_linear_light:
|
|
case ON::world_linear_light:
|
|
rc = true;
|
|
break;
|
|
default:
|
|
rc = false;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
const bool ON_Light::IsRectangularLight() const
|
|
{
|
|
bool rc;
|
|
switch(m_style)
|
|
{
|
|
//case ON::view_rectangular_light:
|
|
//case ON::camera_rectangular_light:
|
|
case ON::world_rectangular_light:
|
|
rc = true;
|
|
break;
|
|
default:
|
|
rc = false;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
void ON_Light::SetStyle(ON::light_style s )
|
|
{
|
|
m_style = s;
|
|
}
|
|
|
|
void ON_Light::SetLightName( const char* s )
|
|
{
|
|
m_light_name = s;
|
|
m_light_name.TrimLeftAndRight();
|
|
}
|
|
|
|
void ON_Light::SetLightName( const wchar_t* s )
|
|
{
|
|
m_light_name = s;
|
|
m_light_name.TrimLeftAndRight();
|
|
}
|
|
|
|
const ON_wString& ON_Light::LightName() const
|
|
{
|
|
return m_light_name;
|
|
}
|
|
|
|
//void ON_Light::SetLightUuid( const ON_UUID& uuid )
|
|
//{
|
|
// // m_light_id is set and maintained by Rhino - if your
|
|
// // plug-in is messing with this field, fix the plugin
|
|
// m_light_id = uuid;
|
|
//}
|
|
|
|
//const ON_UUID& ON_Light::LightUuid() const
|
|
//{
|
|
// // m_light_id is set and maintained by Rhino - if your
|
|
// // plug-in is messing with this field, fix the plugin
|
|
// return m_light_id;
|
|
//}
|
|
|
|
|
|
void ON_Light::SetAttenuation(double a,double b,double c)
|
|
{
|
|
m_attenuation = ON_3dVector(a,b,c);
|
|
}
|
|
|
|
void ON_Light::SetAttenuation(const ON_3dVector& att )
|
|
{
|
|
m_attenuation = att;
|
|
}
|
|
|
|
ON_3dVector ON_Light::Attenuation() const
|
|
{
|
|
return m_attenuation;
|
|
}
|
|
|
|
double ON_Light::Attenuation(double d) const
|
|
{
|
|
// computes 1/(a[0] + d*a[1] + d^2*a[2]) where d = argument
|
|
// returns 0 if a[0] + d*a[1] + d^2*a[2] <= 0
|
|
double a = m_attenuation.x + d*(m_attenuation.y + d*m_attenuation.z);
|
|
if ( a > 0.0 )
|
|
a = 1.0/a;
|
|
else
|
|
a = 0.0;
|
|
return a;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////
|
|
//
|
|
// spot light parameters (ignored for non-spot lights)
|
|
//
|
|
// angle = 0 to 90 degrees
|
|
// exponent = 0 to 128 (0=uniform, 128=high focus)
|
|
//
|
|
void ON_Light::SetSpotAngleRadians( double a )
|
|
{
|
|
a *= 180.0/ON_PI;
|
|
if ( a > 90.0 )
|
|
m_spot_angle = 90.0;
|
|
else if ( a > 0.0 )
|
|
m_spot_angle = a;
|
|
}
|
|
|
|
double ON_Light::SpotAngleRadians() const
|
|
{
|
|
return m_spot_angle*ON_PI/180.0;
|
|
}
|
|
|
|
void ON_Light::SetSpotAngleDegrees( double a )
|
|
{
|
|
if ( a >= 90.0 )
|
|
m_spot_angle = 90.0;
|
|
else if ( a > 0.0 )
|
|
m_spot_angle = a;
|
|
}
|
|
|
|
double ON_Light::SpotAngleDegrees() const
|
|
{
|
|
return m_spot_angle;
|
|
}
|
|
|
|
// The spot exponent "e", hot spot "h", and spotlight cone angle "a"
|
|
// are mutually constrained by the formula
|
|
// cos(h*angle)^e = hotspot_min
|
|
// where hotspot_min = value of spotlight exponential attenuation factor
|
|
// at the hot spot radius. hotspot_min must be >0, < 1, and should be >= 1/2;
|
|
//static double log_hotspot_min = log(0.5);
|
|
static double log_hotspot_min = log(0.70710678118654752440084436210485);
|
|
|
|
void ON_Light::SetSpotExponent( double e )
|
|
{
|
|
// cos(h)^e = 0.5
|
|
if ( e < 0.0 || !ON_IsValid(e) )
|
|
m_spot_exponent = 0.0;
|
|
else
|
|
m_spot_exponent = e;
|
|
m_hotspot = ON_UNSET_VALUE; // indicates hotspot should be computed from m_spot_exponent
|
|
}
|
|
|
|
void ON_Light::SetHotSpot( double h )
|
|
{
|
|
if ( h == ON_UNSET_VALUE || !ON_IsValid(h) )
|
|
m_hotspot = ON_UNSET_VALUE;
|
|
else if ( h <= 0.0 )
|
|
m_hotspot = 0.0;
|
|
else if ( h >= 1.0 )
|
|
m_hotspot = 1.0;
|
|
else
|
|
m_hotspot = h;
|
|
}
|
|
|
|
double ON_Light::SpotExponent() const
|
|
{
|
|
double e = m_spot_exponent;
|
|
if ( m_hotspot >= 0.0 && m_hotspot <= 1.0 ) {
|
|
// spotlight is using hot spot interface
|
|
double h = m_hotspot;
|
|
if ( h < 0.015 )
|
|
h = 0.015;
|
|
if ( h >= 1.0 || m_spot_angle <= 0.0 || m_spot_angle > 90.0)
|
|
e = 0.0;
|
|
else {
|
|
// compute SpotExponent() from cos(h*angle)^e = hotspot_min
|
|
double a, c;
|
|
a = h*SpotAngleRadians();
|
|
c = cos(a);
|
|
if ( c <= 0.0 )
|
|
e = 1.0;
|
|
else {
|
|
e = log_hotspot_min/log(c);
|
|
if ( e < 0.0 )
|
|
e = 0.0;
|
|
}
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
|
|
double ON_Light::HotSpot() const
|
|
{
|
|
double h = m_hotspot;
|
|
if ( h < 0.0 || h > 1.0 ) {
|
|
// spotlight is using spot exponent interface
|
|
if ( m_spot_exponent >= 65536.0 )
|
|
h = 0.0;
|
|
else if ( m_spot_exponent <= 0.0 || m_spot_angle <= 0.0 || m_spot_angle > 90.0 )
|
|
h = 1.0;
|
|
else {
|
|
// compute HotSpot() from cos(h*angle)^e = hotspot_min
|
|
double x, a, cos_ha;
|
|
x = log_hotspot_min/m_spot_exponent; // note that x < 0.0
|
|
if ( x < -690.0 ) {
|
|
// prevent underflow. cos_ha is close to zero so
|
|
h = 1.0;
|
|
}
|
|
else
|
|
{
|
|
cos_ha = exp(x);
|
|
if (!ON_IsValid(cos_ha)) cos_ha = 0.0;
|
|
else if ( cos_ha > 1.0 ) cos_ha = 1.0;
|
|
else if ( cos_ha < -1.0 ) cos_ha = -1.0;
|
|
a = SpotAngleRadians();
|
|
h = acos(cos_ha)/a;
|
|
if ( h < 0.0 )
|
|
h = 0.0;
|
|
else if ( h > 1.0 ) {
|
|
// happens for smaller e
|
|
h = 1.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return h;
|
|
}
|
|
|
|
void ON_Light::SetLength( const ON_3dVector& v )
|
|
{
|
|
m_length = v;
|
|
}
|
|
|
|
ON_3dVector ON_Light::Length() const
|
|
{
|
|
return m_length;
|
|
}
|
|
|
|
void ON_Light::SetWidth( const ON_3dVector& v )
|
|
{
|
|
m_width = v;
|
|
}
|
|
|
|
ON_3dVector ON_Light::Width() const
|
|
{
|
|
return m_width;
|
|
}
|
|
|
|
|
|
void ON_Light::SetShadowIntensity(double s )
|
|
{
|
|
if ( s < 0.0 )
|
|
s = 0.0;
|
|
else if (s > 1.0 )
|
|
s = 1.0;
|
|
m_shadow_intensity = s;
|
|
}
|
|
|
|
double ON_Light::ShadowIntensity() const
|
|
{
|
|
return m_shadow_intensity;
|
|
}
|
|
|
|
void ON_Light::SetLightIndex( int i )
|
|
{
|
|
m_light_index = i;
|
|
}
|
|
|
|
int ON_Light::LightIndex() const
|
|
{
|
|
return m_light_index;
|
|
}
|
|
|
|
void ON_Light::SetAmbient( ON_Color c )
|
|
{
|
|
m_ambient = c;
|
|
}
|
|
|
|
void ON_Light::SetDiffuse( ON_Color c )
|
|
{
|
|
m_diffuse = c;
|
|
}
|
|
|
|
void ON_Light::SetSpecular( ON_Color c )
|
|
{
|
|
m_specular = c;
|
|
}
|
|
|
|
ON_Color ON_Light::Ambient() const
|
|
{
|
|
return m_ambient;
|
|
}
|
|
|
|
ON_Color ON_Light::Diffuse() const
|
|
{
|
|
return m_diffuse;
|
|
}
|
|
|
|
ON_Color ON_Light::Specular() const
|
|
{
|
|
return m_specular;
|
|
}
|
|
|
|
ON::coordinate_system ON_Light::CoordinateSystem() const // determined by style
|
|
{
|
|
ON::coordinate_system cs = ON::world_cs;
|
|
switch( m_style ) {
|
|
case ON::unknown_light_style:
|
|
cs = ON::world_cs;
|
|
break;
|
|
//case ON::view_directional_light:
|
|
//case ON::view_point_light:
|
|
//case ON::view_spot_light:
|
|
// cs = ON::clip_cs;
|
|
// break;
|
|
case ON::camera_directional_light:
|
|
case ON::camera_point_light:
|
|
case ON::camera_spot_light:
|
|
cs = ON::camera_cs;
|
|
break;
|
|
case ON::world_directional_light:
|
|
case ON::world_point_light:
|
|
case ON::world_spot_light:
|
|
case ON::world_linear_light:
|
|
case ON::world_rectangular_light:
|
|
case ON::ambient_light:
|
|
cs = ON::world_cs;
|
|
break;
|
|
default:
|
|
cs = ON::world_cs;
|
|
break;
|
|
}
|
|
return cs;
|
|
}
|
|
|
|
bool ON_Light::GetLightXform(
|
|
const ON_Viewport& vp,
|
|
ON::coordinate_system dest_cs,
|
|
ON_Xform& xform
|
|
) const
|
|
{
|
|
ON::coordinate_system src_cs = CoordinateSystem();
|
|
return vp.GetXform( src_cs, dest_cs, xform );
|
|
}
|
|
|
|
|
|
|
|
void ON_Light::SetLocation( const ON_3dPoint& loc )
|
|
{
|
|
m_location = loc;
|
|
}
|
|
|
|
void ON_Light::SetDirection( const ON_3dVector& dir )
|
|
{
|
|
m_direction = dir;
|
|
}
|
|
|
|
ON_3dPoint ON_Light::Location() const
|
|
{
|
|
return m_location;
|
|
}
|
|
|
|
ON_3dVector ON_Light::Direction() const
|
|
{
|
|
return m_direction;
|
|
}
|
|
|
|
ON_3dVector ON_Light::PerpindicularDirection() const
|
|
{
|
|
// returns a consistent vector perpendicular to the
|
|
// light's direction. This vector is useful for
|
|
// user interface display.
|
|
ON_3dVector dir = m_direction;
|
|
if ( !dir.IsValid() || !dir.Unitize() )
|
|
return ON_3dVector::UnsetVector;
|
|
|
|
ON_3dVector xdir;
|
|
if ( IsLinearLight() || IsRectangularLight() )
|
|
{
|
|
xdir = m_length;
|
|
if ( xdir.IsValid() && xdir.Unitize() && fabs(xdir*dir) <= ON_SQRT_EPSILON )
|
|
return xdir;
|
|
}
|
|
|
|
if( dir.IsParallelTo( ON_3dVector::ZAxis, ON_DEGREES_TO_RADIANS * 3.0))
|
|
xdir = ON_CrossProduct( dir, ON_3dVector::XAxis);
|
|
else
|
|
xdir = ON_CrossProduct( dir, ON_3dVector::ZAxis);
|
|
xdir.Unitize();
|
|
ON_3dVector ydir = ON_CrossProduct(dir,xdir);
|
|
ydir.Unitize();
|
|
ON_3dVector right;
|
|
|
|
switch(dir.MaximumCoordinateIndex())
|
|
{
|
|
case 0:
|
|
right = (fabs(xdir.y) > fabs(ydir.y)) ? xdir : ydir;
|
|
if (right.y < 0.0)
|
|
right = -right;
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
right = (fabs(xdir.x) > fabs(ydir.x)) ? xdir : ydir;
|
|
if (right.x < 0.0)
|
|
right = -right;
|
|
break;
|
|
default:
|
|
right = xdir;
|
|
break;
|
|
}
|
|
|
|
if (right[right.MaximumCoordinateIndex()] < 0.0)
|
|
right = -right;
|
|
|
|
return right;
|
|
}
|
|
|
|
bool ON_Light::GetSpotLightRadii( double* inner_radius, double* outer_radius ) const
|
|
{
|
|
bool rc = IsSpotLight()?true:false;
|
|
if (rc)
|
|
{
|
|
double angle = SpotAngleRadians();
|
|
if ( !ON_IsValid(angle) || angle <= 0.0 || angle >= 0.5*ON_PI )
|
|
angle = 0.25*ON_PI;
|
|
double spot = HotSpot();
|
|
if ( !ON_IsValid(spot) || spot < 0.0 || spot > 1.0 )
|
|
spot = 0.5;
|
|
double cone_height = Direction().Length();
|
|
if ( !ON_IsValid(cone_height) || cone_height <= 0.0 )
|
|
cone_height = 1.0;
|
|
|
|
if ( outer_radius )
|
|
*outer_radius = tan( angle) * cone_height;
|
|
if ( inner_radius )
|
|
*inner_radius = tan( angle * spot) * cone_height;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
double ON_Light::Intensity() const
|
|
{
|
|
// 0.0 = 0%
|
|
// 1.0 = 100%
|
|
// > 1.0 is used by renderers that support high dynamic range inputs
|
|
return m_intensity;
|
|
}
|
|
|
|
void ON_Light::SetIntensity(double v)
|
|
{
|
|
//ALB 2014.04.28
|
|
//Fixes http://mcneel.myjetbrains.com/youtrack/issue/RH-26585
|
|
//Lights should not be clamped to 1.0. There is absolutely no reason to do so.
|
|
if ( ON_IsValid(v) )
|
|
{
|
|
if (v <= 0.0)
|
|
m_intensity = 0.0;
|
|
else
|
|
m_intensity = v;
|
|
}
|
|
}
|
|
|
|
double ON_Light::PowerWatts() const
|
|
{
|
|
return m_watts;
|
|
}
|
|
|
|
double ON_Light::PowerLumens() const
|
|
{
|
|
return m_watts/683.0;
|
|
}
|
|
|
|
double ON_Light::PowerCandela() const
|
|
{
|
|
return m_watts/683.0;
|
|
}
|
|
|
|
void ON_Light::SetPowerWatts( double watts )
|
|
{
|
|
m_watts = (watts > 0.0) ? watts : 0.0;
|
|
}
|
|
|
|
void ON_Light::SetPowerLumens( double lumens )
|
|
{
|
|
m_watts = (lumens > 0.0) ? lumens*683.0 : 0.0;
|
|
}
|
|
|
|
void ON_Light::SetPowerCandela( double candela )
|
|
{
|
|
m_watts = (candela > 0.0) ? candela*683.0 : 0.0;
|
|
}
|
|
|
|
|
|
|