mirror of
https://github.com/leozide/leocad.git
synced 2026-10-26 22:10:32 +00:00
Use PieceData consistently for the shared part/model record, its related APIs and variables, and its source files. Keep the existing reference counting and loading behavior so ownership and readiness changes can be reviewed separately.
1569 lines
43 KiB
C++
1569 lines
43 KiB
C++
#include "lc_global.h"
|
|
#include "lc_math.h"
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include "light.h"
|
|
#include "lc_application.h"
|
|
#include "lc_context.h"
|
|
|
|
#define LC_LIGHT_SPHERE_RADIUS 5.0f
|
|
#define LC_LIGHT_TARGET_RADIUS 2.5f
|
|
#define LC_LIGHT_SPOT_CONE_HEIGHT 10.0f
|
|
#define LC_LIGHT_SPOT_CONE_RADIUS 7.5f
|
|
#define LC_LIGHT_DIRECTIONAL_RADIUS 5.0f
|
|
#define LC_LIGHT_DIRECTIONAL_HEIGHT 7.5f
|
|
|
|
#define LC_LIGHT_POSITION_EDGE 7.5f
|
|
|
|
static const std::array<QLatin1String, static_cast<int>(lcLightType::Count)> gLightTypes = { QLatin1String("POINT"), QLatin1String("SPOT"), QLatin1String("DIRECTIONAL"), QLatin1String("AREA") };
|
|
static const std::array<QLatin1String, static_cast<int>(lcLightAreaShape::Count)> gLightAreaShapes = { QLatin1String("RECTANGLE"), QLatin1String("SQUARE"), QLatin1String("DISK"), QLatin1String("ELLIPSE") };
|
|
|
|
lcLight::lcLight()
|
|
: lcObject(lcObjectType::Light)
|
|
{
|
|
}
|
|
|
|
lcLight::lcLight(const lcVector3& Position, lcLightType LightType)
|
|
: lcObject(lcObjectType::Light), mLightType(LightType)
|
|
{
|
|
if (LightType == lcLightType::Directional)
|
|
mBlenderPower = lcObjectProperty<float>(1.0f);
|
|
|
|
mPosition.SetValue(Position);
|
|
|
|
lcLight::UpdatePosition(1);
|
|
}
|
|
|
|
QString lcLight::GetLightTypeString(lcLightType LightType)
|
|
{
|
|
switch (LightType)
|
|
{
|
|
case lcLightType::Point:
|
|
return QT_TRANSLATE_NOOP("Light Types", "Point Light");
|
|
|
|
case lcLightType::Spot:
|
|
return QT_TRANSLATE_NOOP("Light Types", "Spot Light");
|
|
|
|
case lcLightType::Directional:
|
|
return QT_TRANSLATE_NOOP("Light Types", "Directional Light");
|
|
|
|
case lcLightType::Area:
|
|
return QT_TRANSLATE_NOOP("Light Types", "Area Light");
|
|
|
|
case lcLightType::Count:
|
|
break;
|
|
}
|
|
|
|
return QString();
|
|
}
|
|
|
|
QStringList lcLight::GetLightTypeStrings()
|
|
{
|
|
QStringList LightTypes;
|
|
|
|
for (int LightTypeIndex = 0; LightTypeIndex < static_cast<int>(lcLightType::Count); LightTypeIndex++)
|
|
LightTypes.push_back(GetLightTypeString(static_cast<lcLightType>(LightTypeIndex)));
|
|
|
|
return LightTypes;
|
|
}
|
|
|
|
QString lcLight::GetAreaShapeString(lcLightAreaShape LightAreaShape)
|
|
{
|
|
switch (LightAreaShape)
|
|
{
|
|
case lcLightAreaShape::Rectangle:
|
|
return QT_TRANSLATE_NOOP("Light Shapes", "Rectangle");
|
|
|
|
case lcLightAreaShape::Square:
|
|
return QT_TRANSLATE_NOOP("Light Shapes", "Square");
|
|
|
|
case lcLightAreaShape::Disk:
|
|
return QT_TRANSLATE_NOOP("Light Shapes", "Disk");
|
|
|
|
case lcLightAreaShape::Ellipse:
|
|
return QT_TRANSLATE_NOOP("Light Shapes", "Ellipse");
|
|
|
|
case lcLightAreaShape::Count:
|
|
break;
|
|
}
|
|
|
|
return QString();
|
|
}
|
|
|
|
QStringList lcLight::GetAreaShapeStrings()
|
|
{
|
|
QStringList AreaShapes;
|
|
|
|
for (int AreaShapeIndex = 0; AreaShapeIndex < static_cast<int>(lcLightAreaShape::Count); AreaShapeIndex++)
|
|
AreaShapes.push_back(GetAreaShapeString(static_cast<lcLightAreaShape>(AreaShapeIndex)));
|
|
|
|
return AreaShapes;
|
|
}
|
|
|
|
void lcLight::SaveLDraw(QTextStream& Stream) const
|
|
{
|
|
const QLatin1String LineEnding("\r\n");
|
|
|
|
if (!mCastShadow)
|
|
Stream << QLatin1String("0 !LEOCAD LIGHT SHADOWLESS") << LineEnding;
|
|
|
|
mPosition.Save(Stream, "LIGHT", "POSITION", true);
|
|
mRotation.Save(Stream, "LIGHT", "ROTATION", true);
|
|
mColor.Save(Stream, "LIGHT", "COLOR", true);
|
|
mBlenderRadius.Save(Stream, "LIGHT", "BLENDER_RADIUS", true);
|
|
mBlenderAngle.Save(Stream, "LIGHT", "BLENDER_ANGLE", true);
|
|
mAreaSizeX.Save(Stream, "LIGHT", "AREA_SIZE_X", true);
|
|
mAreaSizeY.Save(Stream, "LIGHT", "AREA_SIZE_Y", true);
|
|
mBlenderPower.Save(Stream, "LIGHT", "BLENDER_POWER", true);
|
|
mPOVRayPower.Save(Stream, "LIGHT", "POVRAY_POWER", true);
|
|
mPOVRayFadeDistance.Save(Stream, "LIGHT", "POVRAY_FADE_DISTANCE", true);
|
|
mPOVRayFadePower.Save(Stream, "LIGHT", "POVRAY_FADE_POWER", true);
|
|
|
|
switch (mLightType)
|
|
{
|
|
case lcLightType::Count:
|
|
case lcLightType::Point:
|
|
break;
|
|
|
|
case lcLightType::Spot:
|
|
mSpotConeAngle.Save(Stream, "LIGHT", "SPOT_CONE_ANGLE", true);
|
|
mSpotPenumbraAngle.Save(Stream, "LIGHT", "SPOT_PENUMBRA_ANGLE", true);
|
|
mPOVRaySpotTightness.Save(Stream, "LIGHT", "POVRAY_SPOT_TIGHTNESS", true);
|
|
break;
|
|
|
|
case lcLightType::Directional:
|
|
break;
|
|
|
|
case lcLightType::Area:
|
|
Stream << QLatin1String("0 !LEOCAD LIGHT AREA_SHAPE ") << gLightAreaShapes[static_cast<int>(mAreaShape)] << LineEnding;
|
|
mPOVRayAreaGridX.Save(Stream, "LIGHT", "POVRAY_AREA_GRID_X", true);
|
|
mPOVRayAreaGridY.Save(Stream, "LIGHT", "POVRAY_AREA_GRID_Y", true);
|
|
break;
|
|
}
|
|
|
|
Stream << QLatin1String("0 !LEOCAD LIGHT TYPE ") << gLightTypes[static_cast<int>(mLightType)] << QLatin1String(" NAME ") << mName << LineEnding;
|
|
}
|
|
|
|
void lcLight::CreateName(const std::vector<std::unique_ptr<lcLight>>& Lights)
|
|
{
|
|
if (!mName.isEmpty())
|
|
{
|
|
bool Found = false;
|
|
|
|
for (const std::unique_ptr<lcLight>& Light : Lights)
|
|
{
|
|
if (Light->GetName() == mName)
|
|
{
|
|
Found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Found)
|
|
return;
|
|
}
|
|
|
|
int MaxLightNumber = 0;
|
|
|
|
QString Prefix;
|
|
|
|
switch (mLightType)
|
|
{
|
|
case lcLightType::Point:
|
|
Prefix = QLatin1String("Point Light ");
|
|
break;
|
|
|
|
case lcLightType::Spot:
|
|
Prefix = QLatin1String("Spot Light ");
|
|
break;
|
|
|
|
case lcLightType::Directional:
|
|
Prefix = QLatin1String("Directional Light ");
|
|
break;
|
|
|
|
case lcLightType::Area:
|
|
Prefix = QLatin1String("Area Light ");
|
|
break;
|
|
|
|
case lcLightType::Count:
|
|
break;
|
|
}
|
|
|
|
for (const std::unique_ptr<lcLight>& Light : Lights)
|
|
{
|
|
QString LightName = Light->GetName();
|
|
|
|
if (LightName.startsWith(Prefix))
|
|
{
|
|
bool Ok = false;
|
|
int LightNumber = LightName.mid(Prefix.size()).toInt(&Ok);
|
|
|
|
if (Ok && LightNumber > MaxLightNumber)
|
|
MaxLightNumber = LightNumber;
|
|
}
|
|
}
|
|
|
|
mName = Prefix + QString::number(MaxLightNumber + 1);
|
|
}
|
|
|
|
bool lcLight::ParseLDrawLine(QTextStream& Stream)
|
|
{
|
|
while (!Stream.atEnd())
|
|
{
|
|
QString Token;
|
|
Stream >> Token;
|
|
|
|
if (mPosition.Load(Stream, Token, "POSITION"))
|
|
continue;
|
|
else if (mRotation.Load(Stream, Token, "ROTATION"))
|
|
continue;
|
|
else if (mColor.Load(Stream, Token, "COLOR"))
|
|
continue;
|
|
else if (mBlenderRadius.Load(Stream, Token, "BLENDER_RADIUS"))
|
|
continue;
|
|
else if (mBlenderAngle.Load(Stream, Token, "BLENDER_ANGLE"))
|
|
continue;
|
|
else if (mAreaSizeX.Load(Stream, Token, "AREA_SIZE_X"))
|
|
continue;
|
|
else if (mAreaSizeY.Load(Stream, Token, "AREA_SIZE_Y"))
|
|
continue;
|
|
else if (mBlenderPower.Load(Stream, Token, "BLENDER_POWER"))
|
|
continue;
|
|
else if (mPOVRayPower.Load(Stream, Token, "POVRAY_POWER"))
|
|
continue;
|
|
else if (mPOVRayFadeDistance.Load(Stream, Token, "POVRAY_FADE_DISTANCE"))
|
|
continue;
|
|
else if (mPOVRayFadePower.Load(Stream, Token, "POVRAY_FADE_POWER"))
|
|
continue;
|
|
else if (mSpotConeAngle.Load(Stream, Token, "SPOT_CONE_ANGLE"))
|
|
continue;
|
|
else if (mSpotPenumbraAngle.Load(Stream, Token, "SPOT_PENUMBRA_ANGLE"))
|
|
continue;
|
|
else if (mPOVRaySpotTightness.Load(Stream, Token, "POVRAY_SPOT_TIGHTNESS"))
|
|
continue;
|
|
else if (mPOVRayAreaGridX.Load(Stream, Token, "POVRAY_AREA_GRID_X"))
|
|
continue;
|
|
else if (mPOVRayAreaGridY.Load(Stream, Token, "POVRAY_AREA_GRID_Y"))
|
|
continue;
|
|
else if (Token == QLatin1String("AREA_SHAPE"))
|
|
{
|
|
QString AreaShape;
|
|
Stream >> AreaShape;
|
|
|
|
for (size_t ShapeIndex = 0; ShapeIndex < gLightAreaShapes.size(); ShapeIndex++)
|
|
{
|
|
if (AreaShape == gLightAreaShapes[ShapeIndex])
|
|
{
|
|
mAreaShape = static_cast<lcLightAreaShape>(ShapeIndex);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (Token == QLatin1String("TYPE"))
|
|
{
|
|
QString Type;
|
|
Stream >> Type;
|
|
|
|
for (size_t TypeIndex = 0; TypeIndex < gLightTypes.size(); TypeIndex++)
|
|
{
|
|
if (Type == gLightTypes[TypeIndex])
|
|
{
|
|
mLightType = static_cast<lcLightType>(TypeIndex);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (Token == QLatin1String("SHADOWLESS"))
|
|
{
|
|
mCastShadow = false;
|
|
}
|
|
else if (Token == QLatin1String("NAME"))
|
|
{
|
|
mName = Stream.readAll().trimmed();
|
|
mName.replace("\"", "");
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void lcLight::CompareBoundingBox(lcVector3& Min, lcVector3& Max)
|
|
{
|
|
const lcVector3 Point = mWorldMatrix.GetTranslation();
|
|
|
|
// TODO: this should check the entire mesh
|
|
|
|
Min = lcMin(Point, Min);
|
|
Max = lcMax(Point, Max);
|
|
}
|
|
|
|
void lcLight::RayTest(lcObjectRayTest& ObjectRayTest) const
|
|
{
|
|
if (IsPointLight())
|
|
{
|
|
float Distance;
|
|
|
|
if (lcSphereRayMinIntersectDistance(mWorldMatrix.GetTranslation(), LC_LIGHT_SPHERE_RADIUS, ObjectRayTest.Start, ObjectRayTest.End, &Distance) && (Distance < ObjectRayTest.Distance))
|
|
{
|
|
ObjectRayTest.ObjectSection.Object = const_cast<lcLight*>(this);
|
|
ObjectRayTest.ObjectSection.Section = LC_LIGHT_SECTION_POSITION;
|
|
ObjectRayTest.Distance = Distance;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (mLightType == lcLightType::Spot)
|
|
{
|
|
const lcVector3 Direction = -lcVector3(mWorldMatrix[2]);
|
|
const lcVector3 Position = mWorldMatrix.GetTranslation() - Direction * LC_LIGHT_SPOT_CONE_HEIGHT;
|
|
float Distance;
|
|
|
|
if (lcConeRayMinIntersectDistance(Position, Direction, LC_LIGHT_SPOT_CONE_RADIUS, LC_LIGHT_SPOT_CONE_HEIGHT, ObjectRayTest.Start, ObjectRayTest.End, &Distance) && (Distance < ObjectRayTest.Distance))
|
|
{
|
|
ObjectRayTest.ObjectSection.Object = const_cast<lcLight*>(this);
|
|
ObjectRayTest.ObjectSection.Section = LC_LIGHT_SECTION_POSITION;
|
|
ObjectRayTest.Distance = Distance;
|
|
}
|
|
}
|
|
else if (mLightType == lcLightType::Area)
|
|
{
|
|
const lcVector3 Direction = -lcVector3(mWorldMatrix[2]);
|
|
const lcVector3 Position = mWorldMatrix.GetTranslation();
|
|
const lcVector4 Plane(Direction, -lcDot(Direction, Position));
|
|
lcVector3 Intersection;
|
|
|
|
if (lcLineSegmentPlaneIntersection(&Intersection, ObjectRayTest.Start, ObjectRayTest.End, Plane))
|
|
{
|
|
const lcVector3 XAxis = lcVector3(mWorldMatrix[0]);
|
|
const lcVector3 YAxis = lcVector3(mWorldMatrix[1]);
|
|
lcVector3 IntersectionDirection = Intersection - Position;
|
|
|
|
float x = lcDot(IntersectionDirection, XAxis);
|
|
float y = lcDot(IntersectionDirection, YAxis);
|
|
const lcVector2 Size = { mAreaSizeX, mAreaSizeY };
|
|
|
|
if (fabsf(x) < Size.x / 2.0f && fabsf(y) < Size.y / 2.0f)
|
|
{
|
|
float Distance = lcLength(Intersection - ObjectRayTest.Start);
|
|
|
|
if (Distance < ObjectRayTest.Distance)
|
|
{
|
|
ObjectRayTest.ObjectSection.Object = const_cast<lcLight*>(this);
|
|
ObjectRayTest.ObjectSection.Section = LC_LIGHT_SECTION_POSITION;
|
|
ObjectRayTest.Distance = Distance;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const lcMatrix44 InverseWorldMatrix = lcMatrix44AffineInverse(mWorldMatrix);
|
|
lcVector3 Start = lcMul31(ObjectRayTest.Start, InverseWorldMatrix);
|
|
lcVector3 End = lcMul31(ObjectRayTest.End, InverseWorldMatrix);
|
|
|
|
float Distance;
|
|
lcVector3 Plane;
|
|
|
|
if (mLightType == lcLightType::Directional)
|
|
{
|
|
if (lcCylinderRayMinIntersectDistance(LC_LIGHT_DIRECTIONAL_RADIUS, LC_LIGHT_DIRECTIONAL_HEIGHT, Start, End, &Distance) && (Distance < ObjectRayTest.Distance))
|
|
{
|
|
ObjectRayTest.ObjectSection.Object = const_cast<lcLight*>(this);
|
|
ObjectRayTest.ObjectSection.Section = LC_LIGHT_SECTION_POSITION;
|
|
ObjectRayTest.Distance = Distance;
|
|
ObjectRayTest.PieceRayTest.Plane = Plane;
|
|
}
|
|
}
|
|
|
|
if (IsSelected())
|
|
{
|
|
if (lcSphereRayMinIntersectDistance(lcMul31(lcVector3(0,0,-mTargetDistance), mWorldMatrix), LC_LIGHT_TARGET_RADIUS, ObjectRayTest.Start, ObjectRayTest.End, &Distance) && (Distance < ObjectRayTest.Distance))
|
|
{
|
|
ObjectRayTest.ObjectSection.Object = const_cast<lcLight*>(this);
|
|
ObjectRayTest.ObjectSection.Section = LC_LIGHT_SECTION_TARGET;
|
|
ObjectRayTest.Distance = Distance;
|
|
}
|
|
}
|
|
}
|
|
|
|
void lcLight::BoxTest(lcObjectBoxTest& ObjectBoxTest) const
|
|
{
|
|
if (IsPointLight())
|
|
{
|
|
for (int PlaneIdx = 0; PlaneIdx < 6; PlaneIdx++)
|
|
if (lcDot3(mWorldMatrix.GetTranslation(), ObjectBoxTest.Planes[PlaneIdx]) + ObjectBoxTest.Planes[PlaneIdx][3] > LC_LIGHT_SPHERE_RADIUS)
|
|
return;
|
|
|
|
ObjectBoxTest.Objects.emplace_back(const_cast<lcLight*>(this));
|
|
return;
|
|
}
|
|
|
|
lcMatrix44 InverseWorldMatrix = lcMatrix44AffineInverse(mWorldMatrix);
|
|
lcVector4 LocalPlanes[6];
|
|
|
|
for (int PlaneIndex = 0; PlaneIndex < 6; PlaneIndex++)
|
|
{
|
|
const lcVector3 PlaneNormal = lcMul30(ObjectBoxTest.Planes[PlaneIndex], InverseWorldMatrix);
|
|
LocalPlanes[PlaneIndex] = lcVector4(PlaneNormal, ObjectBoxTest.Planes[PlaneIndex][3] - lcDot3(InverseWorldMatrix[3], PlaneNormal));
|
|
}
|
|
|
|
// TODO: Check meshes after the bounding fox test.
|
|
if (mLightType == lcLightType::Spot)
|
|
{
|
|
lcVector3 Min(-LC_LIGHT_SPOT_CONE_RADIUS, -LC_LIGHT_SPOT_CONE_RADIUS, 0);
|
|
lcVector3 Max( LC_LIGHT_SPOT_CONE_RADIUS, LC_LIGHT_SPOT_CONE_RADIUS, LC_LIGHT_SPOT_CONE_HEIGHT);
|
|
|
|
if (lcBoundingBoxIntersectsVolume(Min, Max, LocalPlanes))
|
|
{
|
|
ObjectBoxTest.Objects.emplace_back(const_cast<lcLight*>(this));
|
|
return;
|
|
}
|
|
}
|
|
else if (mLightType == lcLightType::Area)
|
|
{
|
|
lcVector3 Min(-mAreaSizeX / 2.0f, -mAreaSizeY / 2.0f, 0);
|
|
lcVector3 Max( mAreaSizeX / 2.0f, mAreaSizeY / 2.0f, 0);
|
|
|
|
if (lcBoundingBoxIntersectsVolume(Min, Max, LocalPlanes))
|
|
{
|
|
ObjectBoxTest.Objects.emplace_back(const_cast<lcLight*>(this));
|
|
return;
|
|
}
|
|
}
|
|
else if (mLightType == lcLightType::Directional)
|
|
{
|
|
lcVector3 Min(-LC_LIGHT_DIRECTIONAL_RADIUS, -LC_LIGHT_DIRECTIONAL_RADIUS, 0);
|
|
lcVector3 Max( LC_LIGHT_DIRECTIONAL_RADIUS, LC_LIGHT_DIRECTIONAL_RADIUS, LC_LIGHT_DIRECTIONAL_HEIGHT);
|
|
|
|
if (lcBoundingBoxIntersectsVolume(Min, Max, LocalPlanes))
|
|
{
|
|
ObjectBoxTest.Objects.emplace_back(const_cast<lcLight*>(this));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (IsSelected())
|
|
{
|
|
lcVector3 TargetPosition = lcMul31(lcVector3(0.0f, 0.0f, -mTargetDistance), mWorldMatrix);
|
|
bool Outside = false;
|
|
|
|
for (int PlaneIdx = 0; PlaneIdx < 6; PlaneIdx++)
|
|
{
|
|
if (lcDot3(TargetPosition, ObjectBoxTest.Planes[PlaneIdx]) + ObjectBoxTest.Planes[PlaneIdx][3] > LC_LIGHT_TARGET_RADIUS)
|
|
{
|
|
Outside = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Outside)
|
|
{
|
|
ObjectBoxTest.Objects.emplace_back(const_cast<lcLight*>(this));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void lcLight::MoveSelected(lcStep Step, bool AddKey, const lcVector3& Distance, bool FirstMove)
|
|
{
|
|
const quint32 Section = GetFocusSection();
|
|
|
|
if (Section == LC_LIGHT_SECTION_POSITION || Section == LC_LIGHT_SECTION_INVALID)
|
|
{
|
|
const lcVector3 Position = mWorldMatrix.GetTranslation() + Distance;
|
|
|
|
SetPosition(Position, Step, AddKey);
|
|
|
|
mWorldMatrix.SetTranslation(Position);
|
|
}
|
|
else
|
|
{
|
|
if (FirstMove)
|
|
mTargetMovePosition = lcMul31(lcVector3(0.0f, 0.0f, -mTargetDistance), mWorldMatrix);
|
|
|
|
mTargetMovePosition += Distance;
|
|
|
|
lcVector3 CurrentDirection = -lcNormalize(mTargetMovePosition - mWorldMatrix.GetTranslation());
|
|
lcMatrix33 WorldMatrix;
|
|
|
|
WorldMatrix.r[0] = lcCross(lcVector3(mWorldMatrix.r[1]), CurrentDirection);
|
|
WorldMatrix.r[1] = lcCross(CurrentDirection, WorldMatrix.r[0]);
|
|
WorldMatrix.r[2] = CurrentDirection;
|
|
|
|
WorldMatrix.Orthonormalize();
|
|
|
|
SetRotation(WorldMatrix, Step, AddKey);
|
|
|
|
mWorldMatrix = lcMatrix44(WorldMatrix, mWorldMatrix.GetTranslation());
|
|
}
|
|
}
|
|
|
|
void lcLight::Rotate(lcStep Step, bool AddKey, const lcMatrix33& RotationMatrix, const lcVector3& Center, const lcMatrix33& RotationFrame)
|
|
{
|
|
if (IsPointLight())
|
|
return;
|
|
|
|
if (GetFocusSection() != LC_LIGHT_SECTION_POSITION && GetFocusSection() != LC_LIGHT_SECTION_INVALID)
|
|
return;
|
|
|
|
lcVector3 Distance = mWorldMatrix.GetTranslation() - Center;
|
|
const lcMatrix33 LocalToWorldMatrix = lcMatrix33(mWorldMatrix);
|
|
|
|
const lcMatrix33 LocalToFocusMatrix = lcMul(LocalToWorldMatrix, RotationFrame);
|
|
lcMatrix33 NewLocalToWorldMatrix = lcMul(LocalToFocusMatrix, RotationMatrix);
|
|
|
|
const lcMatrix33 WorldToLocalMatrix = lcMatrix33AffineInverse(LocalToWorldMatrix);
|
|
|
|
Distance = lcMul(Distance, WorldToLocalMatrix);
|
|
Distance = lcMul(Distance, NewLocalToWorldMatrix);
|
|
|
|
NewLocalToWorldMatrix.Orthonormalize();
|
|
|
|
SetPosition(Center + Distance, Step, AddKey);
|
|
SetRotation(NewLocalToWorldMatrix, Step, AddKey);
|
|
}
|
|
|
|
bool lcLight::SetName(const QString& Name)
|
|
{
|
|
if (mName == Name)
|
|
return false;
|
|
|
|
mName = Name;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool lcLight::SetLightType(lcLightType LightType)
|
|
{
|
|
if (static_cast<int>(LightType) < 0 || LightType >= lcLightType::Count)
|
|
return false;
|
|
|
|
if (mLightType == LightType)
|
|
return false;
|
|
|
|
mLightType = LightType;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool lcLight::SetColor(const lcVector3& Color, lcStep Step, bool AddKey)
|
|
{
|
|
return mColor.ChangeKey(Color, Step, AddKey);
|
|
}
|
|
|
|
bool lcLight::SetPOVRayFadeDistance(float Distance, lcStep Step, bool AddKey)
|
|
{
|
|
return mPOVRayFadeDistance.ChangeKey(Distance, Step, AddKey);
|
|
}
|
|
|
|
bool lcLight::SetPOVRayFadePower(float Power, lcStep Step, bool AddKey)
|
|
{
|
|
return mPOVRayFadePower.ChangeKey(Power, Step, AddKey);
|
|
}
|
|
|
|
bool lcLight::SetSpotConeAngle(float Angle, lcStep Step, bool AddKey)
|
|
{
|
|
return mSpotConeAngle.ChangeKey(Angle, Step, AddKey);
|
|
}
|
|
|
|
bool lcLight::SetSpotPenumbraAngle(float Angle, lcStep Step, bool AddKey)
|
|
{
|
|
return mSpotPenumbraAngle.ChangeKey(Angle, Step, AddKey);
|
|
}
|
|
|
|
bool lcLight::SetSpotPOVRayTightness(float Tightness, lcStep Step, bool AddKey)
|
|
{
|
|
return mPOVRaySpotTightness.ChangeKey(Tightness, Step, AddKey);
|
|
}
|
|
|
|
bool lcLight::SetAreaShape(lcLightAreaShape AreaShape)
|
|
{
|
|
if (static_cast<int>(AreaShape) < 0 || AreaShape >= lcLightAreaShape::Count)
|
|
return false;
|
|
|
|
if (mAreaShape != AreaShape)
|
|
{
|
|
mAreaShape = AreaShape;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool lcLight::SetAreaPOVRayGridX(int AreaGrid, lcStep Step, bool AddKey)
|
|
{
|
|
mPOVRayAreaGridX.ChangeKey(AreaGrid, Step, AddKey);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool lcLight::SetAreaPOVRayGridY(int AreaGrid, lcStep Step, bool AddKey)
|
|
{
|
|
mPOVRayAreaGridY.ChangeKey(AreaGrid, Step, AddKey);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool lcLight::SetBlenderRadius(float Radius, lcStep Step, bool AddKey)
|
|
{
|
|
return mBlenderRadius.ChangeKey(Radius, Step, AddKey);
|
|
}
|
|
|
|
bool lcLight::SetBlenderAngle(float Angle, lcStep Step, bool AddKey)
|
|
{
|
|
return mBlenderAngle.ChangeKey(Angle * LC_DTOR, Step, AddKey);
|
|
}
|
|
|
|
bool lcLight::SetAreaSizeX(float Size, lcStep Step, bool AddKey)
|
|
{
|
|
return mAreaSizeX.ChangeKey(Size, Step, AddKey);
|
|
}
|
|
|
|
bool lcLight::SetAreaSizeY(float Size, lcStep Step, bool AddKey)
|
|
{
|
|
return mAreaSizeY.ChangeKey(Size, Step, AddKey);
|
|
}
|
|
|
|
bool lcLight::SetBlenderPower(float Power, lcStep Step, bool AddKey)
|
|
{
|
|
return mBlenderPower.ChangeKey(Power, Step, AddKey);
|
|
}
|
|
|
|
bool lcLight::SetPOVRayPower(float Power, lcStep Step, bool AddKey)
|
|
{
|
|
return mPOVRayPower.ChangeKey(Power, Step, AddKey);
|
|
}
|
|
|
|
bool lcLight::SetCastShadow(bool CastShadow)
|
|
{
|
|
if (mCastShadow != CastShadow)
|
|
{
|
|
mCastShadow = CastShadow;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void lcLight::InsertTime(lcStep Start, lcStep Time)
|
|
{
|
|
mPosition.InsertTime(Start, Time);
|
|
mRotation.InsertTime(Start, Time);
|
|
mColor.InsertTime(Start, Time);
|
|
mSpotConeAngle.InsertTime(Start, Time);
|
|
mSpotPenumbraAngle.InsertTime(Start, Time);
|
|
mPOVRaySpotTightness.InsertTime(Start, Time);
|
|
mPOVRayAreaGridX.InsertTime(Start, Time);
|
|
mPOVRayAreaGridY.InsertTime(Start, Time);
|
|
mBlenderRadius.InsertTime(Start, Time);
|
|
mBlenderAngle.InsertTime(Start, Time);
|
|
mAreaSizeX.InsertTime(Start, Time);
|
|
mAreaSizeY.InsertTime(Start, Time);
|
|
mBlenderPower.InsertTime(Start, Time);
|
|
mPOVRayPower.InsertTime(Start, Time);
|
|
mPOVRayFadeDistance.InsertTime(Start, Time);
|
|
mPOVRayFadePower.InsertTime(Start, Time);
|
|
}
|
|
|
|
void lcLight::RemoveTime(lcStep Start, lcStep Time)
|
|
{
|
|
mPosition.RemoveTime(Start, Time);
|
|
mRotation.RemoveTime(Start, Time);
|
|
mColor.RemoveTime(Start, Time);
|
|
mSpotConeAngle.RemoveTime(Start, Time);
|
|
mSpotPenumbraAngle.RemoveTime(Start, Time);
|
|
mPOVRaySpotTightness.RemoveTime(Start, Time);
|
|
mPOVRayAreaGridX.RemoveTime(Start, Time);
|
|
mPOVRayAreaGridY.RemoveTime(Start, Time);
|
|
mBlenderRadius.RemoveTime(Start, Time);
|
|
mBlenderAngle.RemoveTime(Start, Time);
|
|
mAreaSizeX.RemoveTime(Start, Time);
|
|
mAreaSizeY.RemoveTime(Start, Time);
|
|
mBlenderPower.RemoveTime(Start, Time);
|
|
mPOVRayPower.RemoveTime(Start, Time);
|
|
mPOVRayFadeDistance.RemoveTime(Start, Time);
|
|
mPOVRayFadePower.RemoveTime(Start, Time);
|
|
}
|
|
|
|
void lcLight::UpdatePosition(lcStep Step)
|
|
{
|
|
mPosition.Update(Step);
|
|
mRotation.Update(Step);
|
|
mColor.Update(Step);
|
|
mSpotConeAngle.Update(Step);
|
|
mSpotPenumbraAngle.Update(Step);
|
|
mPOVRaySpotTightness.Update(Step);
|
|
mPOVRayAreaGridX.Update(Step);
|
|
mPOVRayAreaGridY.Update(Step);
|
|
mBlenderRadius.Update(Step);
|
|
mBlenderAngle.Update(Step);
|
|
mAreaSizeX.Update(Step);
|
|
mAreaSizeY.Update(Step);
|
|
mBlenderPower.Update(Step);
|
|
mPOVRayPower.Update(Step);
|
|
mPOVRayFadeDistance.Update(Step);
|
|
mPOVRayFadePower.Update(Step);
|
|
|
|
if (IsPointLight())
|
|
{
|
|
mWorldMatrix = lcMatrix44Translation(mPosition);
|
|
}
|
|
else
|
|
{
|
|
mWorldMatrix = lcMatrix44(mRotation, mPosition);
|
|
}
|
|
}
|
|
|
|
void lcLight::DrawInterface(lcContext* Context, const lcScene& Scene) const
|
|
{
|
|
Q_UNUSED(Scene);
|
|
Context->SetMaterial(lcMaterialType::UnlitColor);
|
|
|
|
switch (mLightType)
|
|
{
|
|
case lcLightType::Point:
|
|
DrawPointLight(Context);
|
|
break;
|
|
|
|
case lcLightType::Spot:
|
|
DrawSpotLight(Context);
|
|
break;
|
|
|
|
case lcLightType::Directional:
|
|
DrawDirectionalLight(Context);
|
|
break;
|
|
|
|
case lcLightType::Area:
|
|
DrawAreaLight(Context);
|
|
break;
|
|
|
|
case lcLightType::Count:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void lcLight::DrawPointLight(lcContext* Context) const
|
|
{
|
|
SetupLightMatrix(Context);
|
|
|
|
DrawSphere(Context, lcVector3(0.0f, 0.0f, 0.0f), LC_LIGHT_SPHERE_RADIUS);
|
|
}
|
|
|
|
void lcLight::DrawSpotLight(lcContext* Context) const
|
|
{
|
|
SetupLightMatrix(Context);
|
|
|
|
constexpr int ConeEdges = 8;
|
|
float Verts[(ConeEdges + 1) * 3];
|
|
float* CurVert = Verts;
|
|
|
|
for (int EdgeIndex = 0; EdgeIndex < ConeEdges; EdgeIndex++)
|
|
{
|
|
float c = cosf((float)EdgeIndex / ConeEdges * LC_2PI) * LC_LIGHT_SPOT_CONE_RADIUS;
|
|
float s = sinf((float)EdgeIndex / ConeEdges * LC_2PI) * LC_LIGHT_SPOT_CONE_RADIUS;
|
|
|
|
*CurVert++ = c;
|
|
*CurVert++ = s;
|
|
*CurVert++ = 0.0f;
|
|
}
|
|
|
|
*CurVert++ = 0.0f;
|
|
*CurVert++ = 0.0f;
|
|
*CurVert++ = LC_LIGHT_SPOT_CONE_HEIGHT;
|
|
|
|
Context->SetVertexBufferPointer(Verts);
|
|
Context->SetVertexFormatPosition(3);
|
|
|
|
const GLushort Indices[(ConeEdges + 4) * 2] =
|
|
{
|
|
0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 0,
|
|
0, 8, 2, 8, 4, 8, 6, 8,
|
|
};
|
|
|
|
Context->SetIndexBufferPointer(Indices);
|
|
|
|
Context->DrawIndexedPrimitives(GL_LINES, (ConeEdges + 4) * 2, GL_UNSIGNED_SHORT, 0);
|
|
|
|
if (IsSelected())
|
|
{
|
|
DrawCone(Context, mTargetDistance);
|
|
DrawTarget(Context);
|
|
}
|
|
}
|
|
|
|
void lcLight::DrawDirectionalLight(lcContext* Context) const
|
|
{
|
|
SetupLightMatrix(Context);
|
|
|
|
DrawCylinder(Context, LC_LIGHT_DIRECTIONAL_RADIUS, LC_LIGHT_DIRECTIONAL_HEIGHT);
|
|
|
|
if (IsSelected())
|
|
DrawTarget(Context);
|
|
}
|
|
|
|
void lcLight::DrawAreaLight(lcContext* Context) const
|
|
{
|
|
SetupLightMatrix(Context);
|
|
|
|
if (mAreaShape == lcLightAreaShape::Square || mAreaShape == lcLightAreaShape::Rectangle)
|
|
{
|
|
float Verts[4 * 3];
|
|
float* CurVert = Verts;
|
|
const lcVector2 Size = { mAreaSizeX, mAreaShape == lcLightAreaShape::Square ? mAreaSizeX : mAreaSizeY };
|
|
|
|
*CurVert++ = -Size.x / 2.0f;
|
|
*CurVert++ = -Size.y / 2.0f;
|
|
*CurVert++ = 0.0f;
|
|
|
|
*CurVert++ = Size.x / 2.0f;
|
|
*CurVert++ = -Size.y / 2.0f;
|
|
*CurVert++ = 0.0f;
|
|
|
|
*CurVert++ = Size.x / 2.0f;
|
|
*CurVert++ = Size.y / 2.0f;
|
|
*CurVert++ = 0.0f;
|
|
|
|
*CurVert++ = -Size.x / 2.0f;
|
|
*CurVert++ = Size.y / 2.0f;
|
|
*CurVert++ = 0.0f;
|
|
|
|
Context->SetVertexBufferPointer(Verts);
|
|
Context->SetVertexFormatPosition(3);
|
|
|
|
const GLushort Indices[(4 + 2) * 2] =
|
|
{
|
|
0, 1, 1, 2, 2, 3, 3, 0,
|
|
0, 2, 1, 3,
|
|
};
|
|
|
|
Context->SetIndexBufferPointer(Indices);
|
|
|
|
Context->DrawIndexedPrimitives(GL_LINES, (4 + 2) * 2, GL_UNSIGNED_SHORT, 0);
|
|
}
|
|
else
|
|
{
|
|
constexpr int CircleEdges = 16;
|
|
|
|
float Verts[CircleEdges * 3];
|
|
float* CurVert = Verts;
|
|
|
|
for (int EdgeIndex = 0; EdgeIndex < CircleEdges; EdgeIndex++)
|
|
{
|
|
const lcVector2 Size = { mAreaSizeX, mAreaShape == lcLightAreaShape::Disk ? mAreaSizeX : mAreaSizeY };
|
|
float c = cosf((float)EdgeIndex / CircleEdges * LC_2PI) * Size.x / 2.0f;
|
|
float s = sinf((float)EdgeIndex / CircleEdges * LC_2PI) * Size.y / 2.0f;
|
|
|
|
*CurVert++ = c;
|
|
*CurVert++ = s;
|
|
*CurVert++ = 0.0f;
|
|
}
|
|
|
|
Context->SetVertexBufferPointer(Verts);
|
|
Context->SetVertexFormatPosition(3);
|
|
|
|
const GLushort Indices[(CircleEdges + 2) * 2] =
|
|
{
|
|
0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8,
|
|
8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 0,
|
|
0, 8, 4, 12
|
|
};
|
|
|
|
Context->SetIndexBufferPointer(Indices);
|
|
|
|
Context->DrawIndexedPrimitives(GL_LINES, (CircleEdges + 2) * 2, GL_UNSIGNED_SHORT, 0);
|
|
}
|
|
|
|
if (IsSelected())
|
|
DrawTarget(Context);
|
|
}
|
|
|
|
void lcLight::SetupLightMatrix(lcContext* Context) const
|
|
{
|
|
Context->SetWorldMatrix(mWorldMatrix);
|
|
|
|
const lcPreferences& Preferences = lcGetPreferences();
|
|
const float LineWidth = Preferences.mLineWidth;
|
|
|
|
if (IsSelected())
|
|
{
|
|
const lcVector4 SelectedColor = lcVector4FromColor(Preferences.mObjectSelectedColor);
|
|
const lcVector4 FocusedColor = lcVector4FromColor(Preferences.mObjectFocusedColor);
|
|
|
|
Context->SetLineWidth(2.0f * LineWidth);
|
|
|
|
if (IsFocused(LC_LIGHT_SECTION_POSITION))
|
|
Context->SetColor(FocusedColor);
|
|
else
|
|
Context->SetColor(SelectedColor);
|
|
}
|
|
else
|
|
{
|
|
const lcVector4 LightColor = lcVector4FromColor(Preferences.mLightColor);
|
|
|
|
Context->SetLineWidth(LineWidth);
|
|
Context->SetColor(LightColor);
|
|
}
|
|
}
|
|
|
|
void lcLight::DrawSphere(lcContext* Context, const lcVector3& Center, float Radius) const
|
|
{
|
|
constexpr int Slices = 6;
|
|
constexpr int NumIndices = 3 * Slices + 6 * Slices * (Slices - 2) + 3 * Slices;
|
|
constexpr int NumVertices = (Slices - 1) * Slices + 2;
|
|
lcVector3 Vertices[NumVertices];
|
|
quint16 Indices[NumIndices];
|
|
|
|
lcVector3* Vertex = Vertices;
|
|
quint16* Index = Indices;
|
|
|
|
*Vertex++ = Center + lcVector3(0, 0, Radius);
|
|
|
|
for (int i = 1; i < Slices; i++)
|
|
{
|
|
const float r0 = Radius * sinf(i * (LC_PI / Slices));
|
|
const float z0 = Radius * cosf(i * (LC_PI / Slices));
|
|
|
|
for (int j = 0; j < Slices; j++)
|
|
{
|
|
const float x0 = r0 * sinf(j * (LC_2PI / Slices));
|
|
const float y0 = r0 * cosf(j * (LC_2PI / Slices));
|
|
|
|
*Vertex++ = Center + lcVector3(x0, y0, z0);
|
|
}
|
|
}
|
|
|
|
*Vertex++ = Center + lcVector3(0, 0, -Radius);
|
|
|
|
for (quint16 i = 0; i < Slices - 1; i++)
|
|
{
|
|
*Index++ = 0;
|
|
*Index++ = 1 + i;
|
|
*Index++ = 1 + i + 1;
|
|
}
|
|
|
|
*Index++ = 0;
|
|
*Index++ = 1;
|
|
*Index++ = 1 + Slices - 1;
|
|
|
|
for (quint16 i = 0; i < Slices - 2; i++)
|
|
{
|
|
quint16 Row1 = 1 + i * Slices;
|
|
quint16 Row2 = 1 + (i + 1) * Slices;
|
|
|
|
for (quint16 j = 0; j < Slices - 1; j++)
|
|
{
|
|
*Index++ = Row1 + j;
|
|
*Index++ = Row2 + j + 1;
|
|
*Index++ = Row2 + j;
|
|
|
|
*Index++ = Row1 + j;
|
|
*Index++ = Row1 + j + 1;
|
|
*Index++ = Row2 + j + 1;
|
|
}
|
|
|
|
*Index++ = Row1 + Slices - 1;
|
|
*Index++ = Row2 + 0;
|
|
*Index++ = Row2 + Slices - 1;
|
|
|
|
*Index++ = Row1 + Slices - 1;
|
|
*Index++ = Row2 + 0;
|
|
*Index++ = Row1 + 0;
|
|
}
|
|
|
|
for (quint16 i = 0; i < Slices - 1; i++)
|
|
{
|
|
*Index++ = (Slices - 1) * Slices + 1;
|
|
*Index++ = (Slices - 1) * (Slices - 1) + i;
|
|
*Index++ = (Slices - 1) * (Slices - 1) + i + 1;
|
|
}
|
|
|
|
*Index++ = (Slices - 1) * Slices + 1;
|
|
*Index++ = (Slices - 1) * (Slices - 1) + (Slices - 2) + 1;
|
|
*Index++ = (Slices - 1) * (Slices - 1);
|
|
|
|
Context->SetVertexBufferPointer(Vertices);
|
|
Context->SetVertexFormatPosition(3);
|
|
Context->SetIndexBufferPointer(Indices);
|
|
|
|
Context->DrawIndexedPrimitives(GL_TRIANGLES, NumIndices, GL_UNSIGNED_SHORT, 0);
|
|
}
|
|
|
|
void lcLight::DrawCylinder(lcContext* Context, float Radius, float Height) const
|
|
{
|
|
constexpr int Slices = 8;
|
|
|
|
float Verts[(Slices * 2) * 3];
|
|
float* CurVert = Verts;
|
|
|
|
for (int EdgeIndex = 0; EdgeIndex < Slices; EdgeIndex++)
|
|
{
|
|
float c = cosf((float)EdgeIndex / Slices * LC_2PI) * Radius;
|
|
float s = sinf((float)EdgeIndex / Slices * LC_2PI) * Radius;
|
|
|
|
*CurVert++ = c;
|
|
*CurVert++ = s;
|
|
*CurVert++ = Height;
|
|
*CurVert++ = c;
|
|
*CurVert++ = s;
|
|
*CurVert++ = 0.0f;
|
|
}
|
|
|
|
const GLushort Indices[48] =
|
|
{
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14, 0,
|
|
1, 3, 3, 5, 5, 7, 7, 9, 9, 11, 11, 13, 13, 15, 15, 1,
|
|
};
|
|
|
|
Context->SetVertexBufferPointer(Verts);
|
|
Context->SetVertexFormatPosition(3);
|
|
Context->SetIndexBufferPointer(Indices);
|
|
|
|
Context->DrawIndexedPrimitives(GL_LINES, 48, GL_UNSIGNED_SHORT, 0);
|
|
}
|
|
|
|
void lcLight::DrawTarget(lcContext* Context) const
|
|
{
|
|
float Verts[2 * 3];
|
|
float* CurVert = Verts;
|
|
|
|
*CurVert++ = 0.0f; *CurVert++ = 0.0f; *CurVert++ = 0.0f;
|
|
*CurVert++ = 0.0f; *CurVert++ = 0.0f; *CurVert++ = -mTargetDistance;
|
|
|
|
Context->SetVertexBufferPointer(Verts);
|
|
Context->SetVertexFormatPosition(3);
|
|
|
|
const GLushort Indices[2] =
|
|
{
|
|
0, 1
|
|
};
|
|
|
|
Context->SetIndexBufferPointer(Indices);
|
|
|
|
Context->DrawIndexedPrimitives(GL_LINES, 2, GL_UNSIGNED_SHORT, 0);
|
|
|
|
const lcPreferences& Preferences = lcGetPreferences();
|
|
const float LineWidth = Preferences.mLineWidth;
|
|
|
|
if (IsSelected())
|
|
{
|
|
const lcVector4 SelectedColor = lcVector4FromColor(Preferences.mObjectSelectedColor);
|
|
const lcVector4 FocusedColor = lcVector4FromColor(Preferences.mObjectFocusedColor);
|
|
|
|
Context->SetLineWidth(2.0f * LineWidth);
|
|
|
|
if (IsFocused(LC_LIGHT_SECTION_TARGET))
|
|
Context->SetColor(FocusedColor);
|
|
else
|
|
Context->SetColor(SelectedColor);
|
|
}
|
|
else
|
|
{
|
|
const lcVector4 LightColor = lcVector4FromColor(Preferences.mLightColor);
|
|
|
|
Context->SetLineWidth(LineWidth);
|
|
Context->SetColor(LightColor);
|
|
}
|
|
|
|
DrawSphere(Context, lcVector3(0.0f, 0.0f, -mTargetDistance), LC_LIGHT_TARGET_RADIUS);
|
|
}
|
|
|
|
void lcLight::DrawCone(lcContext* Context, float TargetDistance) const
|
|
{
|
|
constexpr int ConeEdges = 16;
|
|
const float OuterRadius = tanf(LC_DTOR * mSpotConeAngle / 2.0f) * TargetDistance;
|
|
|
|
float Verts[(ConeEdges * 2 + 1) * 3];
|
|
float* CurVert = Verts;
|
|
|
|
for (int EdgeIndex = 0; EdgeIndex < ConeEdges; EdgeIndex++)
|
|
{
|
|
const float c = cosf((float)EdgeIndex / ConeEdges * LC_2PI);
|
|
const float s = sinf((float)EdgeIndex / ConeEdges * LC_2PI);
|
|
|
|
*CurVert++ = c * OuterRadius;
|
|
*CurVert++ = s * OuterRadius;
|
|
*CurVert++ = -TargetDistance;
|
|
}
|
|
|
|
*CurVert++ = 0.0f;
|
|
*CurVert++ = 0.0f;
|
|
*CurVert++ = 0.0f;
|
|
|
|
const bool DrawPenumbra = mSpotPenumbraAngle > 1.0f;
|
|
|
|
if (DrawPenumbra)
|
|
{
|
|
const float InnerRadius = tanf(LC_DTOR * (mSpotConeAngle / 2.0f - mSpotPenumbraAngle)) * TargetDistance;
|
|
|
|
for (int EdgeIndex = 0; EdgeIndex < ConeEdges; EdgeIndex++)
|
|
{
|
|
const float c = cosf((float)EdgeIndex / ConeEdges * LC_2PI);
|
|
const float s = sinf((float)EdgeIndex / ConeEdges * LC_2PI);
|
|
|
|
*CurVert++ = c * InnerRadius;
|
|
*CurVert++ = s * InnerRadius;
|
|
*CurVert++ = -TargetDistance;
|
|
}
|
|
}
|
|
|
|
Context->SetVertexBufferPointer(Verts);
|
|
Context->SetVertexFormatPosition(3);
|
|
|
|
constexpr GLushort Indices[(ConeEdges * 2 + 4) * 2] =
|
|
{
|
|
0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8,
|
|
8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 0,
|
|
16, 0, 16, 4, 16, 8, 16, 12,
|
|
17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25,
|
|
25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 17
|
|
};
|
|
|
|
Context->SetIndexBufferPointer(Indices);
|
|
|
|
Context->DrawIndexedPrimitives(GL_LINES, DrawPenumbra ? (ConeEdges * 2 + 4) * 2 : (ConeEdges + 4) * 2, GL_UNSIGNED_SHORT, 0);
|
|
}
|
|
|
|
QVariant lcLight::GetPropertyValue(lcObjectPropertyId PropertyId) const
|
|
{
|
|
switch (PropertyId)
|
|
{
|
|
case lcObjectPropertyId::PieceId:
|
|
case lcObjectPropertyId::PieceColor:
|
|
case lcObjectPropertyId::PieceStepShow:
|
|
case lcObjectPropertyId::PieceStepHide:
|
|
case lcObjectPropertyId::CameraName:
|
|
case lcObjectPropertyId::CameraProjection:
|
|
case lcObjectPropertyId::CameraFOV:
|
|
case lcObjectPropertyId::CameraNear:
|
|
case lcObjectPropertyId::CameraFar:
|
|
case lcObjectPropertyId::CameraPositionX:
|
|
case lcObjectPropertyId::CameraPositionY:
|
|
case lcObjectPropertyId::CameraPositionZ:
|
|
case lcObjectPropertyId::CameraTargetX:
|
|
case lcObjectPropertyId::CameraTargetY:
|
|
case lcObjectPropertyId::CameraTargetZ:
|
|
case lcObjectPropertyId::CameraUpX:
|
|
case lcObjectPropertyId::CameraUpY:
|
|
case lcObjectPropertyId::CameraUpZ:
|
|
break;
|
|
|
|
case lcObjectPropertyId::LightName:
|
|
return GetName();
|
|
|
|
case lcObjectPropertyId::LightType:
|
|
return static_cast<int>(GetLightType());
|
|
|
|
case lcObjectPropertyId::LightColor:
|
|
return QVariant::fromValue<lcVector3>(GetColor());
|
|
|
|
case lcObjectPropertyId::LightBlenderPower:
|
|
return GetBlenderPower();
|
|
|
|
case lcObjectPropertyId::LightPOVRayPower:
|
|
return GetPOVRayPower();
|
|
|
|
case lcObjectPropertyId::LightCastShadow:
|
|
return GetCastShadow();
|
|
|
|
case lcObjectPropertyId::LightPOVRayFadeDistance:
|
|
return GetPOVRayFadeDistance();
|
|
|
|
case lcObjectPropertyId::LightPOVRayFadePower:
|
|
return GetPOVRayFadePower();
|
|
|
|
case lcObjectPropertyId::LightBlenderRadius:
|
|
return GetBlenderRadius();
|
|
|
|
case lcObjectPropertyId::LightBlenderAngle:
|
|
return GetBlenderAngle();
|
|
|
|
case lcObjectPropertyId::LightAreaSizeX:
|
|
return GetAreaSizeX();
|
|
|
|
case lcObjectPropertyId::LightAreaSizeY:
|
|
return GetAreaSizeY();
|
|
|
|
case lcObjectPropertyId::LightSpotConeAngle:
|
|
return GetSpotConeAngle();
|
|
|
|
case lcObjectPropertyId::LightSpotPenumbraAngle:
|
|
return GetSpotPenumbraAngle();
|
|
|
|
case lcObjectPropertyId::LightPOVRaySpotTightness:
|
|
return GetSpotPOVRayTightness();
|
|
|
|
case lcObjectPropertyId::LightAreaShape:
|
|
return static_cast<int>(GetAreaShape());
|
|
|
|
case lcObjectPropertyId::LightPOVRayAreaGridX:
|
|
return GetAreaPOVRayGridX();
|
|
|
|
case lcObjectPropertyId::LightPOVRayAreaGridY:
|
|
return GetAreaPOVRayGridY();
|
|
|
|
case lcObjectPropertyId::ObjectPositionX:
|
|
case lcObjectPropertyId::ObjectPositionY:
|
|
case lcObjectPropertyId::ObjectPositionZ:
|
|
case lcObjectPropertyId::ObjectRotationX:
|
|
case lcObjectPropertyId::ObjectRotationY:
|
|
case lcObjectPropertyId::ObjectRotationZ:
|
|
case lcObjectPropertyId::Count:
|
|
break;
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
bool lcLight::SetPropertyValue(lcObjectPropertyId PropertyId, lcStep Step, bool AddKey, QVariant Value)
|
|
{
|
|
switch (PropertyId)
|
|
{
|
|
case lcObjectPropertyId::PieceId:
|
|
case lcObjectPropertyId::PieceColor:
|
|
case lcObjectPropertyId::PieceStepShow:
|
|
case lcObjectPropertyId::PieceStepHide:
|
|
case lcObjectPropertyId::CameraName:
|
|
case lcObjectPropertyId::CameraProjection:
|
|
case lcObjectPropertyId::CameraFOV:
|
|
case lcObjectPropertyId::CameraNear:
|
|
case lcObjectPropertyId::CameraFar:
|
|
case lcObjectPropertyId::CameraPositionX:
|
|
case lcObjectPropertyId::CameraPositionY:
|
|
case lcObjectPropertyId::CameraPositionZ:
|
|
case lcObjectPropertyId::CameraTargetX:
|
|
case lcObjectPropertyId::CameraTargetY:
|
|
case lcObjectPropertyId::CameraTargetZ:
|
|
case lcObjectPropertyId::CameraUpX:
|
|
case lcObjectPropertyId::CameraUpY:
|
|
case lcObjectPropertyId::CameraUpZ:
|
|
break;
|
|
|
|
case lcObjectPropertyId::LightName:
|
|
return SetName(Value.toString());
|
|
|
|
case lcObjectPropertyId::LightType:
|
|
return SetLightType(static_cast<lcLightType>(Value.toInt()));
|
|
|
|
case lcObjectPropertyId::LightColor:
|
|
return SetColor(Value.value<lcVector3>(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::LightBlenderPower:
|
|
return SetBlenderPower(Value.toFloat(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::LightPOVRayPower:
|
|
return SetPOVRayPower(Value.toFloat(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::LightCastShadow:
|
|
return SetCastShadow(Value.toBool());
|
|
|
|
case lcObjectPropertyId::LightPOVRayFadeDistance:
|
|
return SetPOVRayFadeDistance(Value.toFloat(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::LightPOVRayFadePower:
|
|
return SetPOVRayFadePower(Value.toFloat(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::LightBlenderRadius:
|
|
return SetBlenderRadius(Value.toFloat(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::LightBlenderAngle:
|
|
return SetBlenderAngle(Value.toFloat(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::LightAreaSizeX:
|
|
return SetAreaSizeX(Value.toFloat(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::LightAreaSizeY:
|
|
return SetAreaSizeY(Value.toFloat(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::LightSpotConeAngle:
|
|
return SetSpotConeAngle(Value.toFloat(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::LightSpotPenumbraAngle:
|
|
return SetSpotPenumbraAngle(Value.toFloat(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::LightPOVRaySpotTightness:
|
|
return SetSpotPOVRayTightness(Value.toFloat(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::LightAreaShape:
|
|
return SetAreaShape(static_cast<lcLightAreaShape>(Value.toInt()));
|
|
|
|
case lcObjectPropertyId::LightPOVRayAreaGridX:
|
|
return SetAreaPOVRayGridX(Value.toInt(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::LightPOVRayAreaGridY:
|
|
return SetAreaPOVRayGridY(Value.toInt(), Step, AddKey);
|
|
|
|
case lcObjectPropertyId::ObjectPositionX:
|
|
case lcObjectPropertyId::ObjectPositionY:
|
|
case lcObjectPropertyId::ObjectPositionZ:
|
|
case lcObjectPropertyId::ObjectRotationX:
|
|
case lcObjectPropertyId::ObjectRotationY:
|
|
case lcObjectPropertyId::ObjectRotationZ:
|
|
case lcObjectPropertyId::Count:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool lcLight::HasKeyFrame(lcObjectPropertyId PropertyId, lcStep Time) const
|
|
{
|
|
switch (PropertyId)
|
|
{
|
|
case lcObjectPropertyId::PieceId:
|
|
case lcObjectPropertyId::PieceColor:
|
|
case lcObjectPropertyId::PieceStepShow:
|
|
case lcObjectPropertyId::PieceStepHide:
|
|
case lcObjectPropertyId::CameraName:
|
|
case lcObjectPropertyId::CameraProjection:
|
|
case lcObjectPropertyId::CameraFOV:
|
|
case lcObjectPropertyId::CameraNear:
|
|
case lcObjectPropertyId::CameraFar:
|
|
case lcObjectPropertyId::CameraPositionX:
|
|
case lcObjectPropertyId::CameraPositionY:
|
|
case lcObjectPropertyId::CameraPositionZ:
|
|
case lcObjectPropertyId::CameraTargetX:
|
|
case lcObjectPropertyId::CameraTargetY:
|
|
case lcObjectPropertyId::CameraTargetZ:
|
|
case lcObjectPropertyId::CameraUpX:
|
|
case lcObjectPropertyId::CameraUpY:
|
|
case lcObjectPropertyId::CameraUpZ:
|
|
case lcObjectPropertyId::LightName:
|
|
case lcObjectPropertyId::LightType:
|
|
return false;
|
|
|
|
case lcObjectPropertyId::LightColor:
|
|
return mColor.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::LightBlenderPower:
|
|
return mBlenderPower.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::LightPOVRayPower:
|
|
return mPOVRayPower.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::LightCastShadow:
|
|
return false;
|
|
|
|
case lcObjectPropertyId::LightPOVRayFadeDistance:
|
|
return mPOVRayFadeDistance.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::LightPOVRayFadePower:
|
|
return mPOVRayFadePower.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::LightBlenderRadius:
|
|
return mBlenderRadius.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::LightBlenderAngle:
|
|
return mBlenderAngle.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::LightAreaSizeX:
|
|
return mAreaSizeX.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::LightAreaSizeY:
|
|
return mAreaSizeY.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::LightSpotConeAngle:
|
|
return mSpotConeAngle.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::LightSpotPenumbraAngle:
|
|
return mSpotPenumbraAngle.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::LightPOVRaySpotTightness:
|
|
return mPOVRaySpotTightness.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::LightAreaShape:
|
|
return false;
|
|
|
|
case lcObjectPropertyId::LightPOVRayAreaGridX:
|
|
return mPOVRayAreaGridX.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::LightPOVRayAreaGridY:
|
|
return mPOVRayAreaGridY.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::ObjectPositionX:
|
|
case lcObjectPropertyId::ObjectPositionY:
|
|
case lcObjectPropertyId::ObjectPositionZ:
|
|
return mPosition.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::ObjectRotationX:
|
|
case lcObjectPropertyId::ObjectRotationY:
|
|
case lcObjectPropertyId::ObjectRotationZ:
|
|
return mRotation.HasKeyFrame(Time);
|
|
|
|
case lcObjectPropertyId::Count:
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool lcLight::SetKeyFrame(lcObjectPropertyId PropertyId, lcStep Time, bool KeyFrame)
|
|
{
|
|
switch (PropertyId)
|
|
{
|
|
case lcObjectPropertyId::PieceId:
|
|
case lcObjectPropertyId::PieceColor:
|
|
case lcObjectPropertyId::PieceStepShow:
|
|
case lcObjectPropertyId::PieceStepHide:
|
|
case lcObjectPropertyId::CameraName:
|
|
case lcObjectPropertyId::CameraProjection:
|
|
case lcObjectPropertyId::CameraFOV:
|
|
case lcObjectPropertyId::CameraNear:
|
|
case lcObjectPropertyId::CameraFar:
|
|
case lcObjectPropertyId::CameraPositionX:
|
|
case lcObjectPropertyId::CameraPositionY:
|
|
case lcObjectPropertyId::CameraPositionZ:
|
|
case lcObjectPropertyId::CameraTargetX:
|
|
case lcObjectPropertyId::CameraTargetY:
|
|
case lcObjectPropertyId::CameraTargetZ:
|
|
case lcObjectPropertyId::CameraUpX:
|
|
case lcObjectPropertyId::CameraUpY:
|
|
case lcObjectPropertyId::CameraUpZ:
|
|
case lcObjectPropertyId::LightName:
|
|
case lcObjectPropertyId::LightType:
|
|
return false;
|
|
|
|
case lcObjectPropertyId::LightColor:
|
|
return mColor.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::LightBlenderPower:
|
|
return mBlenderPower.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::LightPOVRayPower:
|
|
return mPOVRayPower.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::LightCastShadow:
|
|
return false;
|
|
|
|
case lcObjectPropertyId::LightPOVRayFadeDistance:
|
|
return mPOVRayFadeDistance.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::LightPOVRayFadePower:
|
|
return mPOVRayFadePower.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::LightBlenderRadius:
|
|
return mBlenderRadius.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::LightBlenderAngle:
|
|
return mBlenderAngle.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::LightAreaSizeX:
|
|
return mAreaSizeX.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::LightAreaSizeY:
|
|
return mAreaSizeY.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::LightSpotConeAngle:
|
|
return mSpotConeAngle.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::LightSpotPenumbraAngle:
|
|
return mSpotPenumbraAngle.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::LightPOVRaySpotTightness:
|
|
return mPOVRaySpotTightness.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::LightAreaShape:
|
|
return false;
|
|
|
|
case lcObjectPropertyId::LightPOVRayAreaGridX:
|
|
return mPOVRayAreaGridX.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::LightPOVRayAreaGridY:
|
|
return mPOVRayAreaGridY.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::ObjectPositionX:
|
|
case lcObjectPropertyId::ObjectPositionY:
|
|
case lcObjectPropertyId::ObjectPositionZ:
|
|
return mPosition.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::ObjectRotationX:
|
|
case lcObjectPropertyId::ObjectRotationY:
|
|
case lcObjectPropertyId::ObjectRotationZ:
|
|
return mRotation.SetKeyFrame(Time, KeyFrame);
|
|
|
|
case lcObjectPropertyId::Count:
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void lcLight::RemoveKeyFrames()
|
|
{
|
|
mPosition.RemoveAllKeys();
|
|
mRotation.RemoveAllKeys();
|
|
mColor.RemoveAllKeys();
|
|
mSpotConeAngle.RemoveAllKeys();
|
|
mSpotPenumbraAngle.RemoveAllKeys();
|
|
mPOVRaySpotTightness.RemoveAllKeys();
|
|
mPOVRayAreaGridX.RemoveAllKeys();
|
|
mPOVRayAreaGridY.RemoveAllKeys();
|
|
mBlenderRadius.RemoveAllKeys();
|
|
mBlenderAngle.RemoveAllKeys();
|
|
mAreaSizeX.RemoveAllKeys();
|
|
mAreaSizeY.RemoveAllKeys();
|
|
mBlenderPower.RemoveAllKeys();
|
|
mPOVRayPower.RemoveAllKeys();
|
|
mPOVRayFadeDistance.RemoveAllKeys();
|
|
mPOVRayFadePower.RemoveAllKeys();
|
|
}
|
|
|
|
lcLightHistoryState lcLight::GetHistoryState([[maybe_unused]] const lcModel* Model) const
|
|
{
|
|
lcLightHistoryState State;
|
|
|
|
State.Id = mId;
|
|
State.Hidden = mHidden;
|
|
State.Name = mName;
|
|
State.LightType = mLightType;
|
|
State.CastShadow = mCastShadow;
|
|
State.Position = mPosition;
|
|
State.Rotation = mRotation;
|
|
State.Color = mColor;
|
|
State.BlenderPower = mBlenderPower;
|
|
State.BlenderRadius = mBlenderRadius;
|
|
State.BlenderAngle = mBlenderAngle;
|
|
State.POVRayPower = mPOVRayPower;
|
|
State.POVRayFadeDistance = mPOVRayFadeDistance;
|
|
State.POVRayFadePower = mPOVRayFadePower;
|
|
State.SpotConeAngle = mSpotConeAngle;
|
|
State.SpotPenumbraAngle = mSpotPenumbraAngle;
|
|
State.POVRaySpotTightness = mPOVRaySpotTightness;
|
|
State.AreaShape = mAreaShape;
|
|
State.AreaSizeX = mAreaSizeX;
|
|
State.AreaSizeY = mAreaSizeY;
|
|
State.POVRayAreaGridX = mPOVRayAreaGridX;
|
|
State.POVRayAreaGridY = mPOVRayAreaGridY;
|
|
|
|
return State;
|
|
}
|
|
|
|
void lcLight::SetHistoryState(const lcLightHistoryState& State, [[maybe_unused]] const lcModel* Model)
|
|
{
|
|
mId = State.Id;
|
|
mHidden = State.Hidden;
|
|
mName = State.Name;
|
|
mLightType = State.LightType;
|
|
mCastShadow = State.CastShadow;
|
|
mPosition = State.Position;
|
|
mRotation = State.Rotation;
|
|
mColor = State.Color;
|
|
mBlenderPower = State.BlenderPower;
|
|
mBlenderRadius = State.BlenderRadius;
|
|
mBlenderAngle = State.BlenderAngle;
|
|
mPOVRayPower = State.POVRayPower;
|
|
mPOVRayFadeDistance = State.POVRayFadeDistance;
|
|
mPOVRayFadePower = State.POVRayFadePower;
|
|
mSpotConeAngle = State.SpotConeAngle;
|
|
mSpotPenumbraAngle = State.SpotPenumbraAngle;
|
|
mPOVRaySpotTightness = State.POVRaySpotTightness;
|
|
mAreaShape = State.AreaShape;
|
|
mAreaSizeX = State.AreaSizeX;
|
|
mAreaSizeY = State.AreaSizeY;
|
|
mPOVRayAreaGridX = State.POVRayAreaGridX;
|
|
mPOVRayAreaGridY = State.POVRayAreaGridY;
|
|
}
|