diff --git a/common/lc_math.h b/common/lc_math.h index bc7eb1b1..4fcb0d4b 100644 --- a/common/lc_math.h +++ b/common/lc_math.h @@ -1991,6 +1991,125 @@ inline bool lcSphereRayMinIntersectDistance(const lcVector3& Center, float Radiu } } +inline bool lcConeRayMinIntersectDistance(const lcVector3& Tip, const lcVector3& Direction, float Radius, float Height, const lcVector3& Start, const lcVector3& End, float* Dist) +{ + const lcVector3 v = End - Start; + const lcVector3 h = Direction; + const lcVector3 w = Start - Tip; + const float vh = lcDot(v, h); + const float wh = lcDot(w, h); + const float m = (Radius * Radius) / (Height * Height); + + const float a = lcDot(v, v) - m * vh * vh - vh * vh; + const float b = 2 * (lcDot(v, w) - m * vh * wh - vh * wh); + const float c = lcDot(w, w) - m * wh * wh - wh * wh; + + const float delta = b * b - 4 * a * c; + + if (delta < 0.0f) + return false; + + float ts[2] = { (-b - sqrtf(delta)) / (2 * a), (-b + sqrtf(delta)) / (2 * a) }; + + for (int ti = 0; ti < 2; ti++) + { + float t = ts[ti]; + lcVector3 Intersection = Start + v * t; + + float ConeD = lcDot(Intersection - Tip, Direction); + + if (ConeD < 0.0f) + continue; + + if (ConeD < Height) + { + *Dist = lcLength(Intersection - Start); + + return true; + } + + lcVector3 Center(Tip + Direction * Height); + lcVector4 Plane(Direction, -lcDot(Direction, Center)); + + if (lcLineSegmentPlaneIntersection(&Intersection, Start, End, Plane)) + { + if (lcLengthSquared(Center - Intersection) < Radius * Radius) + { + *Dist = lcLength(Intersection - Start); + + return true; + } + } + } + + return false; +} + +inline bool lcCylinderRayMinIntersectDistance(float Radius, float Height, const lcVector3& Start, const lcVector3& End, float* Dist) +{ + lcVector4 BottomPlane(0.0f, 0.0f, 1.0f, 0.0f); + lcVector3 Intersection; + float MinDistance = FLT_MAX; + + if (lcLineSegmentPlaneIntersection(&Intersection, Start, End, BottomPlane)) + { + if (Intersection.x * Intersection.x + Intersection.y * Intersection.y < Radius * Radius) + { + float Distance = lcLength(Intersection - Start); + + if (Distance < MinDistance) + MinDistance = Distance; + } + } + + lcVector4 TopPlane(0.0f, 0.0f, 1.0f, -Height); + + if (lcLineSegmentPlaneIntersection(&Intersection, Start, End, TopPlane)) + { + if (Intersection.x * Intersection.x + Intersection.y * Intersection.y < Radius * Radius) + { + float Distance = lcLength(Intersection - Start); + + if (Distance < MinDistance) + MinDistance = Distance; + } + } + + lcVector3 Direction = End - Start; + + float a = (Direction.x * Direction.x) + (Direction.y * Direction.y); + float b = 2 * (Direction.x * Start.x + Direction.y * Start.y); + float c = (Start.x * Start.x) + (Start.y * Start.y) - (Radius * Radius); + + float delta = b * b - 4 * (a * c); + + if (delta > 0.0f) + { + float ts[2] = { (-b - sqrtf(delta)) / (2 * a), (-b + sqrtf(delta)) / (2 * a) }; + + for (int ti = 0; ti < 2; ti++) + { + float t = ts[ti]; + Intersection = Start + Direction * t; + + if (Intersection.z < 0.0f || Intersection.z > Height) + continue; + + float Distance = lcLength(Intersection - Start); + + if (Distance < MinDistance) + MinDistance = Distance; + } + } + + if (MinDistance == FLT_MAX) + return false; + + *Dist = MinDistance; + + return true; +} + inline lcVector3 lcRayPointClosestPoint(const lcVector3& Point, const lcVector3& Start, const lcVector3& End) { const lcVector3 Dir = Point - Start; diff --git a/common/light.cpp b/common/light.cpp index 290d0728..b3584a8c 100644 --- a/common/light.cpp +++ b/common/light.cpp @@ -8,11 +8,14 @@ #include "lc_application.h" #include "lc_context.h" -#define LC_LIGHT_POSITION_EDGE 7.5f -#define LC_LIGHT_TARGET_EDGE 5.0f #define LC_LIGHT_SPHERE_RADIUS 5.0f -#define LC_LIGHT_SUN_RADIUS 8.5f -#define LC_LIGHT_SPOT_BASE_EDGE 12.5f +#define LC_LIGHT_TARGET_EDGE 5.0f +#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 gLightTypes = { QLatin1String("POINT"), QLatin1String("SPOT"), QLatin1String("DIRECTIONAL"), QLatin1String("AREA") }; @@ -597,7 +600,7 @@ void lcLight::RayTest(lcObjectRayTest& ObjectRayTest) const { float Distance; - if (lcSphereRayMinIntersectDistance(mPosition, LC_LIGHT_SPHERE_RADIUS, ObjectRayTest.Start, ObjectRayTest.End, &Distance)) + if (lcSphereRayMinIntersectDistance(mPosition, LC_LIGHT_SPHERE_RADIUS, ObjectRayTest.Start, ObjectRayTest.End, &Distance) && (Distance < ObjectRayTest.Distance)) { ObjectRayTest.ObjectSection.Object = const_cast(this); ObjectRayTest.ObjectSection.Section = LC_LIGHT_SECTION_POSITION; @@ -607,8 +610,63 @@ void lcLight::RayTest(lcObjectRayTest& ObjectRayTest) const return; } - lcVector3 Min = lcVector3(-LC_LIGHT_POSITION_EDGE, -LC_LIGHT_POSITION_EDGE, -LC_LIGHT_POSITION_EDGE); - lcVector3 Max = lcVector3(LC_LIGHT_POSITION_EDGE, LC_LIGHT_POSITION_EDGE, LC_LIGHT_POSITION_EDGE); + if (mLightType == lcLightType::Spot) + { + float Distance; + lcVector3 Direction = lcNormalize(mTargetPosition - mPosition); + + if (lcConeRayMinIntersectDistance(mPosition - Direction * LC_LIGHT_SPOT_CONE_HEIGHT, Direction, LC_LIGHT_SPOT_CONE_RADIUS, LC_LIGHT_SPOT_CONE_HEIGHT, ObjectRayTest.Start, ObjectRayTest.End, &Distance) && (Distance < ObjectRayTest.Distance)) + { + ObjectRayTest.ObjectSection.Object = const_cast(this); + ObjectRayTest.ObjectSection.Section = LC_LIGHT_SECTION_POSITION; + ObjectRayTest.Distance = Distance; + } + } + else if (mLightType == lcLightType::Area) + { + lcVector3 FrontVector = mTargetPosition - mPosition; + lcVector4 Plane(FrontVector, -lcDot(FrontVector, mPosition)); + lcVector3 Intersection; + + if (lcLineSegmentPlaneIntersection(&Intersection, ObjectRayTest.Start, ObjectRayTest.End, Plane)) + { + lcVector3 UpVector(1, 1, 1); + + if (fabs(FrontVector[0]) < fabs(FrontVector[1])) + { + if (fabs(FrontVector[0]) < fabs(FrontVector[2])) + UpVector[0] = -(UpVector[1] * FrontVector[1] + UpVector[2] * FrontVector[2]); + else + UpVector[2] = -(UpVector[0] * FrontVector[0] + UpVector[1] * FrontVector[1]); + } + else + { + if (fabs(FrontVector[1]) < fabs(FrontVector[2])) + UpVector[1] = -(UpVector[0] * FrontVector[0] + UpVector[2] * FrontVector[2]); + else + UpVector[2] = -(UpVector[0] * FrontVector[0] + UpVector[1] * FrontVector[1]); + } + + lcVector3 XAxis = lcNormalize(lcCross(FrontVector, UpVector)); + lcVector3 YAxis = lcNormalize(lcCross(FrontVector, XAxis)); + lcVector3 IntersectionDirection = Intersection - mPosition; + + float x = lcDot(IntersectionDirection, XAxis); + float y = lcDot(IntersectionDirection, YAxis); + + if (fabsf(x) < mAreaSize.x / 2.0f && fabsf(y) < mAreaSize.y / 2.0f) + { + float Distance = lcLength(Intersection - ObjectRayTest.Start); + + if (Distance < ObjectRayTest.Distance) + { + ObjectRayTest.ObjectSection.Object = const_cast(this); + ObjectRayTest.ObjectSection.Section = LC_LIGHT_SECTION_POSITION; + ObjectRayTest.Distance = Distance; + } + } + } + } lcVector3 Start = lcMul31(ObjectRayTest.Start, mWorldLight); lcVector3 End = lcMul31(ObjectRayTest.End, mWorldLight); @@ -616,16 +674,19 @@ void lcLight::RayTest(lcObjectRayTest& ObjectRayTest) const float Distance; lcVector3 Plane; - if (lcBoundingBoxRayIntersectDistance(Min, Max, Start, End, &Distance, nullptr, &Plane) && (Distance < ObjectRayTest.Distance)) + if (mLightType == lcLightType::Directional) { - ObjectRayTest.ObjectSection.Object = const_cast(this); - ObjectRayTest.ObjectSection.Section = LC_LIGHT_SECTION_POSITION; - ObjectRayTest.Distance = Distance; - ObjectRayTest.PieceInfoRayTest.Plane = Plane; + if (lcCylinderRayMinIntersectDistance(LC_LIGHT_DIRECTIONAL_RADIUS, LC_LIGHT_DIRECTIONAL_HEIGHT, Start, End, &Distance) && (Distance < ObjectRayTest.Distance)) + { + ObjectRayTest.ObjectSection.Object = const_cast(this); + ObjectRayTest.ObjectSection.Section = LC_LIGHT_SECTION_POSITION; + ObjectRayTest.Distance = Distance; + ObjectRayTest.PieceInfoRayTest.Plane = Plane; + } } - Min = lcVector3(-LC_LIGHT_TARGET_EDGE, -LC_LIGHT_TARGET_EDGE, -LC_LIGHT_TARGET_EDGE); - Max = lcVector3(LC_LIGHT_TARGET_EDGE, LC_LIGHT_TARGET_EDGE, LC_LIGHT_TARGET_EDGE); + lcVector3 Min = lcVector3(-LC_LIGHT_TARGET_EDGE, -LC_LIGHT_TARGET_EDGE, -LC_LIGHT_TARGET_EDGE); + lcVector3 Max = lcVector3( LC_LIGHT_TARGET_EDGE, LC_LIGHT_TARGET_EDGE, LC_LIGHT_TARGET_EDGE); lcMatrix44 WorldTarget = mWorldLight; WorldTarget.SetTranslation(lcMul30(-mTargetPosition, WorldTarget)); @@ -654,7 +715,7 @@ void lcLight::BoxTest(lcObjectBoxTest& ObjectBoxTest) const return; } - lcVector3 Min(-LC_LIGHT_POSITION_EDGE, -LC_LIGHT_POSITION_EDGE, -LC_LIGHT_POSITION_EDGE); + lcVector3 Min(-LC_LIGHT_POSITION_EDGE, -LC_LIGHT_POSITION_EDGE, -LC_LIGHT_POSITION_EDGE); // todo: fix light box test lcVector3 Max(LC_LIGHT_POSITION_EDGE, LC_LIGHT_POSITION_EDGE, LC_LIGHT_POSITION_EDGE); lcVector4 LocalPlanes[6]; @@ -798,13 +859,175 @@ void lcLight::DrawInterface(lcContext* Context, const lcScene& Scene) const Q_UNUSED(Scene); Context->SetMaterial(lcMaterialType::UnlitColor); - if (IsPointLight()) + switch (mLightType) + { + case lcLightType::Point: DrawPointLight(Context); - else + break; + + case lcLightType::Spot: + DrawSpotLight(Context); + break; + + case lcLightType::Directional: DrawDirectionalLight(Context); + break; + + case lcLightType::Area: + DrawAreaLight(Context); + break; + } +} + +void lcLight::DrawPointLight(lcContext* Context) const +{ + Context->SetWorldMatrix(lcMatrix44Translation(mPosition)); + + const lcPreferences& Preferences = lcGetPreferences(); + + if (IsFocused(LC_LIGHT_SECTION_POSITION)) + { + const lcVector4 FocusedColor = lcVector4FromColor(Preferences.mObjectFocusedColor); + Context->SetColor(FocusedColor); + } + else if (IsSelected(LC_LIGHT_SECTION_POSITION)) + { + const lcVector4 SelectedColor = lcVector4FromColor(Preferences.mObjectSelectedColor); + Context->SetColor(SelectedColor); + } + else + { + const lcVector4 LightColor = lcVector4FromColor(Preferences.mLightColor); + Context->SetColor(LightColor); + } + + DrawSphere(Context, LC_LIGHT_SPHERE_RADIUS); +} + +void lcLight::DrawSpotLight(lcContext* Context) const +{ + constexpr int ConeEdges = 8; + float TargetDistance = SetupLightMatrix(Context); + + 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); + + DrawTarget(Context, TargetDistance); + + if (IsSelected()) + DrawCone(Context, TargetDistance); } void lcLight::DrawDirectionalLight(lcContext* Context) const +{ + float TargetDistance = SetupLightMatrix(Context); + + DrawCylinder(Context, LC_LIGHT_DIRECTIONAL_RADIUS, LC_LIGHT_DIRECTIONAL_HEIGHT); + + DrawTarget(Context, TargetDistance); +} + +void lcLight::DrawAreaLight(lcContext* Context) const +{ + float TargetDistance = SetupLightMatrix(Context); + + if (mLightShape == LC_LIGHT_SHAPE_SQUARE || mLightShape == LC_LIGHT_SHAPE_RECTANGLE) + { + float Verts[4 * 3]; + float* CurVert = Verts; + + *CurVert++ = -mAreaSize.x / 2.0f; + *CurVert++ = -mAreaSize.y / 2.0f; + *CurVert++ = 0.0f; + + *CurVert++ = mAreaSize.x / 2.0f; + *CurVert++ = -mAreaSize.y / 2.0f; + *CurVert++ = 0.0f; + + *CurVert++ = mAreaSize.x / 2.0f; + *CurVert++ = mAreaSize.y / 2.0f; + *CurVert++ = 0.0f; + + *CurVert++ = -mAreaSize.x / 2.0f; + *CurVert++ = mAreaSize.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++) + { + float c = cosf((float)EdgeIndex / CircleEdges * LC_2PI) * mAreaSize.x / 2.0f; + float s = sinf((float)EdgeIndex / CircleEdges * LC_2PI) * mAreaSize.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); + } + + DrawTarget(Context, TargetDistance); +} + +float lcLight::SetupLightMatrix(lcContext* Context) const { lcVector3 FrontVector(mTargetPosition - mPosition); lcVector3 UpVector(1, 1, 1); @@ -831,324 +1054,37 @@ void lcLight::DrawDirectionalLight(lcContext* Context) const const lcMatrix44 LightViewMatrix = lcMul(LightMatrix, lcMatrix44Translation(mPosition)); Context->SetWorldMatrix(LightViewMatrix); - float Length = FrontVector.Length(); - - float Verts[(20 + 8 + 2 + 16) * 3]; - float* CurVert = Verts; - - if (mLightType != lcLightType::Directional) - { - if (mLightType == lcLightType::Spot) - { - for (int EdgeIdx = 0; EdgeIdx < 8; EdgeIdx++) - { - float c = cosf((float)EdgeIdx / 4 * LC_PI) * LC_LIGHT_POSITION_EDGE; - float s = sinf((float)EdgeIdx / 4 * LC_PI) * LC_LIGHT_POSITION_EDGE; - - *CurVert++ = c; - *CurVert++ = s; - *CurVert++ = LC_LIGHT_POSITION_EDGE; - *CurVert++ = c; - *CurVert++ = s; - *CurVert++ = -LC_LIGHT_POSITION_EDGE; - } - - *CurVert++ = -LC_LIGHT_SPOT_BASE_EDGE; *CurVert++ = -LC_LIGHT_SPOT_BASE_EDGE; *CurVert++ = -LC_LIGHT_POSITION_EDGE; - *CurVert++ = LC_LIGHT_SPOT_BASE_EDGE; *CurVert++ = -LC_LIGHT_SPOT_BASE_EDGE; *CurVert++ = -LC_LIGHT_POSITION_EDGE; - *CurVert++ = LC_LIGHT_SPOT_BASE_EDGE; *CurVert++ = LC_LIGHT_SPOT_BASE_EDGE; *CurVert++ = -LC_LIGHT_POSITION_EDGE; - *CurVert++ = -LC_LIGHT_SPOT_BASE_EDGE; *CurVert++ = LC_LIGHT_SPOT_BASE_EDGE; *CurVert++ = -LC_LIGHT_POSITION_EDGE; - } - else if (mLightType == lcLightType::Area) - { - const float LC_LIGHT_AREA_EDGE = 5.0f; - const float LC_LIGHT_AREA_H_EDGE = 8.5f; - const float LC_LIGHT_AREA_W_EDGE = 17.0f; - - *CurVert++ = LC_LIGHT_AREA_EDGE; *CurVert++ = LC_LIGHT_AREA_EDGE; *CurVert++ = LC_LIGHT_POSITION_EDGE; - *CurVert++ = -LC_LIGHT_AREA_EDGE; *CurVert++ = LC_LIGHT_AREA_EDGE; *CurVert++ = LC_LIGHT_POSITION_EDGE; - *CurVert++ = -LC_LIGHT_AREA_EDGE; *CurVert++ = -LC_LIGHT_AREA_EDGE; *CurVert++ = LC_LIGHT_POSITION_EDGE; - *CurVert++ = LC_LIGHT_AREA_EDGE; *CurVert++ = -LC_LIGHT_AREA_EDGE; *CurVert++ = LC_LIGHT_POSITION_EDGE; - *CurVert++ = LC_LIGHT_AREA_EDGE; *CurVert++ = LC_LIGHT_AREA_EDGE; *CurVert++ = -LC_LIGHT_POSITION_EDGE; - *CurVert++ = -LC_LIGHT_AREA_EDGE; *CurVert++ = LC_LIGHT_AREA_EDGE; *CurVert++ = -LC_LIGHT_POSITION_EDGE; - *CurVert++ = -LC_LIGHT_AREA_EDGE; *CurVert++ = -LC_LIGHT_AREA_EDGE; *CurVert++ = -LC_LIGHT_POSITION_EDGE; - *CurVert++ = LC_LIGHT_AREA_EDGE; *CurVert++ = -LC_LIGHT_AREA_EDGE; *CurVert++ = -LC_LIGHT_POSITION_EDGE; - - *CurVert++ = -LC_LIGHT_AREA_H_EDGE; *CurVert++ = -LC_LIGHT_AREA_W_EDGE; *CurVert++ = -LC_LIGHT_POSITION_EDGE; - *CurVert++ = LC_LIGHT_AREA_H_EDGE; *CurVert++ = -LC_LIGHT_AREA_W_EDGE; *CurVert++ = -LC_LIGHT_POSITION_EDGE; - *CurVert++ = LC_LIGHT_AREA_H_EDGE; *CurVert++ = LC_LIGHT_AREA_W_EDGE; *CurVert++ = -LC_LIGHT_POSITION_EDGE; - *CurVert++ = -LC_LIGHT_AREA_H_EDGE; *CurVert++ = LC_LIGHT_AREA_W_EDGE; *CurVert++ = -LC_LIGHT_POSITION_EDGE; - } - - *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE - Length; - *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE - Length; - *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE - Length; - *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE - Length; - *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE - Length; - *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE - Length; - *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE - Length; - *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE - Length; - - *CurVert++ = 0.0f; *CurVert++ = 0.0f; *CurVert++ = 0.0f; - *CurVert++ = 0.0f; *CurVert++ = 0.0f; *CurVert++ = -Length; - - Context->SetVertexBufferPointer(Verts); - Context->SetVertexFormatPosition(3); - } - - int BaseIndices = 0; - - if (mLightType == lcLightType::Spot) - { - BaseIndices = 56; - const GLushort Indices[56 + 24 + 2 + 40] = - { - 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, - 16, 17, 17, 18, 18, 19, 19, 16, - 20, 21, 21, 22, 22, 23, 23, 20, - 24, 25, 25, 26, 26, 27, 27, 24, - 20, 24, 21, 25, 22, 26, 23, 27, - 28, 29, - 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, - 38, 39, 39, 40, 40, 41, 41, 42, 42, 43, 43, 44, 44, 45, 45, 30, - 28, 30, 28, 34, 28, 38, 28, 42 - }; - - Context->SetIndexBufferPointer(Indices); - } - else if (mLightType == lcLightType::Area) - { - BaseIndices = 32; - const GLushort Indices[32 + 24 + 2] = - { - 0, 1, 1, 2, 2, 3, 3, 0, - 4, 5, 5, 6, 6, 7, 7, 4, - 0, 4, 1, 5, 2, 6, 3, 7, - 8, 9, 9, 10, 10, 11, 11, 8, - 12, 13, 13, 14, 14, 15, 15, 12, - 16, 17, 17, 18, 18, 19, 19, 16, - 12, 16, 13, 17, 14, 18, 15, 19, - 20, 21 - }; - - Context->SetIndexBufferPointer(Indices); - } - else if (mLightType == lcLightType::Directional) - { - constexpr float Radius = LC_LIGHT_SUN_RADIUS; - constexpr int Slices = 9; // longitude - constexpr int Stacks = 9; // latitude - constexpr int NumSphereVertices = 918; // Slices * 2 * 3 * 3 + (Stacks - 2) * Slices * 4 * 3 - constexpr int NumSphereIndices = 306; // Slices * 2 + (Stacks - 1) * Slices * 2 * 2 - - BaseIndices = NumSphereIndices; - - float Vertices[NumSphereVertices + 24 + 6]; - float *Vert = Vertices; - - quint16 Indices[NumSphereIndices + 24 + 2]; - quint16 *Indx = Indices; - - auto AddVertex = [&](float x, float y, float z) - { - *Vert++ = x; - *Vert++ = y; - *Vert++ = z; - }; - - auto AddLineIndex = [&](quint16 v1, quint16 v2) - { - *Indx++ = v1; - *Indx++ = v2; - }; - - std::vector WrkVertices; - - float Slice = LC_2PI / Slices; - float Stack = LC_PI / Stacks; - float SliceAngle, StackAngle; - - for(int i = 0; i <= Stacks; ++i) - { - StackAngle = LC_PI / 2 - i * Stack; // starting from pi/2 to -pi/2 - float xy = Radius * cosf(StackAngle); - float z = Radius * sinf(StackAngle); - - for(int j = 0; j <= Slices; ++j) // add (Slices+1) vertices per stack - { - SliceAngle = j * Slice; - - lcVector3 Vertex; - Vertex.x = xy * cosf(SliceAngle); - Vertex.y = xy * sinf(SliceAngle); - Vertex.z = z; - - WrkVertices.push_back(Vertex); - } - } - - int Index = 0; - - lcVector3 v1, v2, v3, v4; - - for(int i = 0; i < Stacks; ++i) - { - int vi1 = i * (Slices + 1); - int vi2 = (i + 1) * (Slices + 1); - - for(int j = 0; j < Slices; ++j, ++vi1, ++vi2) - { - // 4 vertices per slice - // v1--v3 - // | | - // v2--v4 - v1 = WrkVertices[vi1]; - v2 = WrkVertices[vi2]; - v3 = WrkVertices[vi1 + 1]; - v4 = WrkVertices[vi2 + 1]; - - // if first stack or last stack, store 1 triangle per slice else, store 2 triangles (1 quad) per slice - if(i == 0) - { - // first stack triangle v1-v2-v4 - AddVertex(v1.x, v1.y, v1.z); - AddVertex(v2.x, v2.y, v2.z); - AddVertex(v4.x, v4.y, v4.z); - - // only vertical lines for first stack) - AddLineIndex(Index, Index+1); - - Index += 3; - } - else if(i == (Stacks-1)) - { - // last stack inverted triangle v1-v2-v3 - AddVertex(v1.x, v1.y, v1.z); - AddVertex(v2.x, v2.y, v2.z); - AddVertex(v3.x, v3.y, v3.z); - - // both vertical and horizontal lines for last stack - AddLineIndex(Index, Index+1); - AddLineIndex(Index, Index+2); - - Index += 3; - } - else - { - // 2 triangles (quad vertices v1-v2-v3-v4) for other stacks - AddVertex(v1.x, v1.y, v1.z); - AddVertex(v2.x, v2.y, v2.z); - AddVertex(v3.x, v3.y, v3.z); - AddVertex(v4.x, v4.y, v4.z); - - // both vertical and horizontal lines for other stacks - AddLineIndex(Index, Index+1); - AddLineIndex(Index, Index+2); - - Index += 4; - } - } - } - - AddVertex( LC_LIGHT_TARGET_EDGE, LC_LIGHT_TARGET_EDGE, LC_LIGHT_TARGET_EDGE - Length); - AddVertex(-LC_LIGHT_TARGET_EDGE, LC_LIGHT_TARGET_EDGE, LC_LIGHT_TARGET_EDGE - Length); - AddVertex(-LC_LIGHT_TARGET_EDGE, -LC_LIGHT_TARGET_EDGE, LC_LIGHT_TARGET_EDGE - Length); - AddVertex( LC_LIGHT_TARGET_EDGE, -LC_LIGHT_TARGET_EDGE, LC_LIGHT_TARGET_EDGE - Length); - AddVertex( LC_LIGHT_TARGET_EDGE, LC_LIGHT_TARGET_EDGE, -LC_LIGHT_TARGET_EDGE - Length); - AddVertex(-LC_LIGHT_TARGET_EDGE, LC_LIGHT_TARGET_EDGE, -LC_LIGHT_TARGET_EDGE - Length); - AddVertex(-LC_LIGHT_TARGET_EDGE, -LC_LIGHT_TARGET_EDGE, -LC_LIGHT_TARGET_EDGE - Length); - AddVertex( LC_LIGHT_TARGET_EDGE, -LC_LIGHT_TARGET_EDGE, -LC_LIGHT_TARGET_EDGE - Length); - AddVertex(0.0f, 0.0f, 0.0f); - AddVertex(0.0f, 0.0f, -Length); - - const int Idx[10] = { Index++, Index++, Index++, Index++, Index++, Index++, Index++, Index++, Index++, Index++ }; - AddLineIndex(Idx[0], Idx[1]); AddLineIndex(Idx[1], Idx[2]); AddLineIndex(Idx[2], Idx[3]); AddLineIndex(Idx[3], Idx[0]); - AddLineIndex(Idx[4], Idx[5]); AddLineIndex(Idx[5], Idx[6]); AddLineIndex(Idx[6], Idx[7]); AddLineIndex(Idx[7], Idx[4]); - AddLineIndex(Idx[0], Idx[4]); AddLineIndex(Idx[1], Idx[5]); AddLineIndex(Idx[2], Idx[6]); AddLineIndex(Idx[3], Idx[7]); - AddLineIndex(Idx[8], Idx[9]); - - Context->SetVertexBufferPointer(Vertices); - Context->SetVertexFormatPosition(3); - Context->SetIndexBufferPointer(Indices); - } - const lcPreferences& Preferences = lcGetPreferences(); const float LineWidth = Preferences.mLineWidth; - const lcVector4 SelectedColor = lcVector4FromColor(Preferences.mObjectSelectedColor); - const lcVector4 FocusedColor = lcVector4FromColor(Preferences.mObjectFocusedColor); - const lcVector4 LightColor = lcVector4FromColor(Preferences.mLightColor); - if (!IsSelected()) + if (IsSelected(LC_LIGHT_SECTION_POSITION)) { - Context->SetLineWidth(LineWidth); - Context->SetColor(LightColor); + const lcVector4 SelectedColor = lcVector4FromColor(Preferences.mObjectSelectedColor); + const lcVector4 FocusedColor = lcVector4FromColor(Preferences.mObjectFocusedColor); - Context->DrawIndexedPrimitives(GL_LINES, BaseIndices + 24 + 2, GL_UNSIGNED_SHORT, 0); + Context->SetLineWidth(2.0f * LineWidth); + + if (IsFocused(LC_LIGHT_SECTION_POSITION)) + Context->SetColor(FocusedColor); + else + Context->SetColor(SelectedColor); } else { - if (IsSelected(LC_LIGHT_SECTION_POSITION)) - { - Context->SetLineWidth(2.0f * LineWidth); - if (IsFocused(LC_LIGHT_SECTION_POSITION)) - Context->SetColor(FocusedColor); - else - Context->SetColor(SelectedColor); - } - else - { - Context->SetLineWidth(LineWidth); - Context->SetColor(LightColor); - } - - Context->DrawIndexedPrimitives(GL_LINES, BaseIndices, GL_UNSIGNED_SHORT, 0); - - if (IsSelected(LC_LIGHT_SECTION_TARGET)) - { - Context->SetLineWidth(2.0f * LineWidth); - if (IsFocused(LC_LIGHT_SECTION_TARGET)) - Context->SetColor(FocusedColor); - else - Context->SetColor(SelectedColor); - } - else - { - Context->SetLineWidth(LineWidth); - Context->SetColor(LightColor); - } - - Context->DrawIndexedPrimitives(GL_LINES, 24, GL_UNSIGNED_SHORT, BaseIndices * 2); + const lcVector4 LightColor = lcVector4FromColor(Preferences.mLightColor); Context->SetLineWidth(LineWidth); Context->SetColor(LightColor); - - int SpotCone = 0; - - if (mLightType == lcLightType::Spot) - { - SpotCone = 40; - - float Radius = tanf(LC_DTOR * mSpotCutoff) * Length; - - for (int EdgeIdx = 0; EdgeIdx < 16; EdgeIdx++) - { - *CurVert++ = cosf((float)EdgeIdx / 16 * LC_2PI) * Radius; - *CurVert++ = sinf((float)EdgeIdx / 16 * LC_2PI) * Radius; - *CurVert++ = -Length; - } - } - - Context->DrawIndexedPrimitives(GL_LINES, 2 + SpotCone, GL_UNSIGNED_SHORT, (BaseIndices + 24) * 2); } + + return FrontVector.Length(); } -void lcLight::DrawPointLight(lcContext* Context) const +void lcLight::DrawSphere(lcContext* Context, 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; - constexpr float Radius = LC_LIGHT_SPHERE_RADIUS; lcVector3 Vertices[NumVertices]; quint16 Indices[NumIndices]; @@ -1157,7 +1093,7 @@ void lcLight::DrawPointLight(lcContext* Context) const *Vertex++ = lcVector3(0, 0, Radius); - for (int i = 1; i < Slices; i++ ) + 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)); @@ -1173,7 +1109,7 @@ void lcLight::DrawPointLight(lcContext* Context) const *Vertex++ = lcVector3(0, 0, -Radius); - for (quint16 i = 0; i < Slices - 1; i++ ) + for (quint16 i = 0; i < Slices - 1; i++) { *Index++ = 0; *Index++ = 1 + i; @@ -1184,12 +1120,12 @@ void lcLight::DrawPointLight(lcContext* Context) const *Index++ = 1; *Index++ = 1 + Slices - 1; - for (quint16 i = 0; i < Slices - 2; i++ ) + 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++ ) + for (quint16 j = 0; j < Slices - 1; j++) { *Index++ = Row1 + j; *Index++ = Row2 + j + 1; @@ -1209,7 +1145,7 @@ void lcLight::DrawPointLight(lcContext* Context) const *Index++ = Row1 + 0; } - for (quint16 i = 0; i < Slices - 1; i++ ) + for (quint16 i = 0; i < Slices - 1; i++) { *Index++ = (Slices - 1) * Slices + 1; *Index++ = (Slices - 1) * (Slices - 1) + i; @@ -1220,32 +1156,144 @@ void lcLight::DrawPointLight(lcContext* Context) const *Index++ = (Slices - 1) * (Slices - 1) + (Slices - 2) + 1; *Index++ = (Slices - 1) * (Slices - 1); - Context->SetWorldMatrix(lcMatrix44Translation(mPosition)); - - const lcPreferences& Preferences = lcGetPreferences(); - - if (IsFocused(LC_LIGHT_SECTION_POSITION)) - { - const lcVector4 FocusedColor = lcVector4FromColor(Preferences.mObjectFocusedColor); - Context->SetColor(FocusedColor); - } - else if (IsSelected(LC_LIGHT_SECTION_POSITION)) - { - const lcVector4 SelectedColor = lcVector4FromColor(Preferences.mObjectSelectedColor); - Context->SetColor(SelectedColor); - } - else - { - const lcVector4 LightColor = lcVector4FromColor(Preferences.mLightColor); - Context->SetColor(LightColor); - } - 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, float TargetDistance) const +{ + float Verts[10 * 3]; + float* CurVert = Verts; + + *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE - TargetDistance; + *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE - TargetDistance; + *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE - TargetDistance; + *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE - TargetDistance; + *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE - TargetDistance; + *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE - TargetDistance; + *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE - TargetDistance; + *CurVert++ = LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE; *CurVert++ = -LC_LIGHT_TARGET_EDGE - TargetDistance; + + *CurVert++ = 0.0f; *CurVert++ = 0.0f; *CurVert++ = 0.0f; + *CurVert++ = 0.0f; *CurVert++ = 0.0f; *CurVert++ = -TargetDistance; + + Context->SetVertexBufferPointer(Verts); + Context->SetVertexFormatPosition(3); + + const GLushort Indices[(12 + 1) * 2] = + { + 0, 1, 1, 2, 2, 3, 3, 0, + 4, 5, 5, 6, 6, 7, 7, 4, + 0, 4, 1, 5, 2, 6, 3, 7, + 8, 9 + }; + + Context->SetIndexBufferPointer(Indices); + + const lcPreferences& Preferences = lcGetPreferences(); + const float LineWidth = Preferences.mLineWidth; + const lcVector4 LightColor = lcVector4FromColor(Preferences.mLightColor); + + if (IsSelected(LC_LIGHT_SECTION_TARGET)) + { + 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 + { + Context->SetLineWidth(LineWidth); + Context->SetColor(LightColor); + } + + Context->DrawIndexedPrimitives(GL_LINES, 12 * 2, GL_UNSIGNED_SHORT, 0); + + Context->SetLineWidth(LineWidth); + Context->SetColor(LightColor); + + Context->DrawIndexedPrimitives(GL_LINES, 2, GL_UNSIGNED_SHORT, 12 * 2 * 2); +} + +void lcLight::DrawCone(lcContext* Context, float TargetDistance) const +{ + constexpr int ConeEdges = 16; + const float Radius = tanf(LC_DTOR * mSpotCutoff) * TargetDistance; + + float Verts[(ConeEdges + 1) * 3]; + float* CurVert = Verts; + + for (int EdgeIndex = 0; EdgeIndex < ConeEdges; EdgeIndex++) + { + float c = cosf((float)EdgeIndex / ConeEdges * LC_2PI) * Radius; + float s = sinf((float)EdgeIndex / ConeEdges * LC_2PI) * Radius; + + *CurVert++ = c; + *CurVert++ = s; + *CurVert++ = -TargetDistance; + } + + *CurVert++ = 0.0f; + *CurVert++ = 0.0f; + *CurVert++ = 0.0f; + + 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, 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 + }; + + Context->SetIndexBufferPointer(Indices); + + Context->DrawIndexedPrimitives(GL_LINES, (ConeEdges + 4) * 2, GL_UNSIGNED_SHORT, 0); +} + void lcLight::RemoveKeyFrames() { mPositionKeys.RemoveAll(); diff --git a/common/light.h b/common/light.h index 5a4a121c..23c56300 100644 --- a/common/light.h +++ b/common/light.h @@ -331,8 +331,16 @@ protected: lcObjectKeyArray mSpotExponentKeys; lcObjectKeyArray mSpotTightnessKeys; - void DrawDirectionalLight(lcContext* Context) const; void DrawPointLight(lcContext* Context) const; + void DrawSpotLight(lcContext* Context) const; + void DrawDirectionalLight(lcContext* Context) const; + void DrawAreaLight(lcContext* Context) const; + + float SetupLightMatrix(lcContext* Context) const; + void DrawSphere(lcContext* Context, float Radius) const; + void DrawCylinder(lcContext* Context, float Radius, float Height) const; + void DrawTarget(lcContext* Context, float TargetDistance) const; + void DrawCone(lcContext* Context, float TargetDistance) const; quint32 mState; lcLightType mLightType; diff --git a/qt/lc_qpropertiestree.cpp b/qt/lc_qpropertiestree.cpp index f964abf5..dfb025ac 100644 --- a/qt/lc_qpropertiestree.cpp +++ b/qt/lc_qpropertiestree.cpp @@ -983,7 +983,7 @@ void lcQPropertiesTree::slotReturnPressed() { QString Value = Editor->text(); - Model->SetLightName(Light, Value.toLocal8Bit().data()); + Model->SetLightName(Light, Value); } } } @@ -1513,14 +1513,14 @@ void lcQPropertiesTree::SetLight(lcObject* Focus) { Factor = Light->mAreaSize; FactorALabel = tr("Width"); - FactorAToolTip = tr("The width (X direction) of the area light in units."); - FactorBToolTip = tr("The height (Y direction) of the area light in units."); + FactorAToolTip = tr("The width (X direction) of the area light."); + FactorBToolTip = tr("The height (Y direction) of the area light."); } else { - FactorALabel = tr("Width (m)"); - FactorAToolTip = tr("The width (X direction) of the area light in metres."); - FactorBToolTip = tr("The height (Y direction) of the area light in units."); + FactorALabel = tr("Width"); + FactorAToolTip = tr("The width (X direction) of the area light."); + FactorBToolTip = tr("The height (Y direction) of the area light."); } break; default: @@ -1623,7 +1623,7 @@ void lcQPropertiesTree::SetLight(lcObject* Focus) lightSpotTightness = addProperty(lightProperties, tr("Spot Tightness"), PropertyFloat); } } - else if (LightType == lcLightType::Area) + else if (LightType == lcLightType::Area) { lightShape = addProperty(lightProperties, tr("Shape"), PropertyLightShape); lightFactorA = addProperty(lightProperties, FactorALabel, PropertyFloat); @@ -1631,7 +1631,7 @@ void lcQPropertiesTree::SetLight(lcObject* Focus) if (ShapeIndex == LC_LIGHT_SHAPE_RECTANGLE || ShapeIndex == LC_LIGHT_SHAPE_ELLIPSE || POVRayLight) lightFactorB = addProperty(lightProperties, tr("Height"), PropertyFloat); else - FactorAToolTip = tr("The size of the area light grid in metres."); + FactorAToolTip = tr("The size of the area light grid."); if (POVRayLight) {