Skip to content

Commit 69b3e87

Browse files
committed
Fixed light and billboard issues
1 parent 5087f6c commit 69b3e87

6 files changed

Lines changed: 141 additions & 101 deletions

File tree

engine/core/component/CameraComponent.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
#define DEFAULT_ORTHO_NEAR -10
1515
#define DEFAULT_ORTHO_FAR 10
16-
#define DEFAULT_PERSPECTIVE_NEAR 1
16+
#define DEFAULT_PERSPECTIVE_NEAR 0.2
1717
#define DEFAULT_PERSPECTIVE_FAR 200
1818

1919
namespace Supernova{

engine/core/component/LightComponent.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,16 @@ namespace Supernova{
4646
bool shadows = false;
4747
bool automaticShadowCamera = true;
4848
float shadowBias = 0.001f;
49-
unsigned int mapResolution = 1024;
49+
unsigned int mapResolution = 2048;
5050
Vector2 shadowCameraNearFar = Vector2(0.1f, 10.0f); // when automatic it gets value from scene camera or light range
5151
unsigned int numShadowCascades = 3;
5252

5353
LightCamera cameras[6];
5454
FramebufferRender framebuffer[MAX_SHADOWCASCADES];
5555
int shadowMapIndex;
5656

57-
bool needUpdateShadowMap = true;
57+
bool needUpdateShadowCamera = false;
58+
bool needUpdateShadowMap = false; // framebuffers
5859
};
5960

6061
}

engine/core/math/Matrix4.cpp

Lines changed: 81 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -664,104 +664,110 @@ Matrix4 Matrix4::perspectiveMatrix(float yfov, float aspect, float near, float f
664664
}
665665

666666
void Matrix4::decomposeStandard(Vector3& position, Vector3& scale, Quaternion& rotation) const {
667-
// Extract position
667+
// Extract translation (assuming column-major, last column is translation)
668668
position.x = matrix[3][0];
669669
position.y = matrix[3][1];
670670
position.z = matrix[3][2];
671671

672-
// Extract scale (column lengths)
673-
scale.x = Vector3(matrix[0][0], matrix[0][1], matrix[0][2]).length();
674-
scale.y = Vector3(matrix[1][0], matrix[1][1], matrix[1][2]).length();
675-
scale.z = Vector3(matrix[2][0], matrix[2][1], matrix[2][2]).length();
672+
// Extract basis vectors (upper-left 3x3 matrix)
673+
Vector3 col0(matrix[0][0], matrix[0][1], matrix[0][2]);
674+
Vector3 col1(matrix[1][0], matrix[1][1], matrix[1][2]);
675+
Vector3 col2(matrix[2][0], matrix[2][1], matrix[2][2]);
676676

677-
// Handle negative determinant (reflection)
678-
if (determinant() < 0) scale = -scale;
679-
// Create rotation matrix - MODIFIED to handle zero scales
680-
Matrix4 rotationM;
677+
// Extract scale (length of basis vectors)
678+
scale.x = col0.length();
679+
scale.y = col1.length();
680+
scale.z = col2.length();
681681

682-
// Check for near-zero scales to avoid division by zero
683682
const float EPSILON = 1e-6f;
684683

685-
// Initialize rotation matrix with identity
686-
rotationM.identity();
687-
688-
// For each column with non-zero scale, normalize and copy to rotation matrix
689-
if (std::abs(scale.x) > EPSILON) {
690-
rotationM.set(0, 0, matrix[0][0] / scale.x);
691-
rotationM.set(0, 1, matrix[0][1] / scale.x);
692-
rotationM.set(0, 2, matrix[0][2] / scale.x);
693-
}
694-
695-
if (std::abs(scale.y) > EPSILON) {
696-
rotationM.set(1, 0, matrix[1][0] / scale.y);
697-
rotationM.set(1, 1, matrix[1][1] / scale.y);
698-
rotationM.set(1, 2, matrix[1][2] / scale.y);
684+
// If all scales are degenerate, just return identity rotation and unit scale
685+
if (scale.x < EPSILON && scale.y < EPSILON && scale.z < EPSILON) {
686+
scale = Vector3(1.0f, 1.0f, 1.0f);
687+
rotation = Quaternion(); // identity
688+
return;
699689
}
700690

701-
if (std::abs(scale.z) > EPSILON) {
702-
rotationM.set(2, 0, matrix[2][0] / scale.z);
703-
rotationM.set(2, 1, matrix[2][1] / scale.z);
704-
rotationM.set(2, 2, matrix[2][2] / scale.z);
691+
// Handle negative determinant (reflection): flip one axis only
692+
if (determinant() < 0) {
693+
scale.x = -scale.x;
694+
col0 = -col0;
705695
}
706696

707-
// Special case: if we have degenerate scaling, ensure we still return a valid rotation
708-
if (std::abs(scale.x) <= EPSILON || std::abs(scale.y) <= EPSILON || std::abs(scale.z) <= EPSILON) {
709-
// If any scale is zero, we need to rebuild a valid rotation matrix
710-
// This approach constructs an orthonormal basis where possible
711-
Vector3 xAxis(rotationM.get(0, 0), rotationM.get(0, 1), rotationM.get(0, 2));
712-
Vector3 yAxis(rotationM.get(1, 0), rotationM.get(1, 1), rotationM.get(1, 2));
713-
Vector3 zAxis(rotationM.get(2, 0), rotationM.get(2, 1), rotationM.get(2, 2));
714-
715-
// Find the first non-zero axis
697+
// Clamp scale to avoid division by zero
698+
if (std::abs(scale.x) < EPSILON) scale.x = 1.0f;
699+
if (std::abs(scale.y) < EPSILON) scale.y = 1.0f;
700+
if (std::abs(scale.z) < EPSILON) scale.z = 1.0f;
701+
702+
// Normalize columns to get rotation axes
703+
Vector3 xAxis = col0 / scale.x;
704+
Vector3 yAxis = col1 / scale.y;
705+
Vector3 zAxis = col2 / scale.z;
706+
707+
// Validate orthogonality
708+
// If not, reconstruct
709+
float dot01 = std::abs(xAxis.dotProduct(yAxis));
710+
float dot02 = std::abs(xAxis.dotProduct(zAxis));
711+
float dot12 = std::abs(yAxis.dotProduct(zAxis));
712+
if (dot01 > 0.01f || dot02 > 0.01f || dot12 > 0.01f) {
713+
// Orthonormalize using Gram-Schmidt process
716714
if (xAxis.length() > EPSILON) {
717715
xAxis = xAxis.normalize();
716+
} else {
717+
xAxis = Vector3(1, 0, 0);
718+
}
718719

719-
// Find or create a valid y-axis
720-
if (yAxis.length() > EPSILON) {
721-
yAxis = yAxis.normalize();
720+
// Make yAxis orthogonal to xAxis
721+
yAxis = yAxis - xAxis * yAxis.dotProduct(xAxis);
722+
if (yAxis.length() > EPSILON) {
723+
yAxis = yAxis.normalize();
724+
} else {
725+
// Safe perpendicular vector generation
726+
Vector3 perp = xAxis.perpendicular();
727+
if (perp.length() > EPSILON) {
728+
yAxis = perp.normalize();
722729
} else {
723-
// Create orthogonal vector
724-
yAxis = xAxis.perpendicular().normalize();
730+
// Fallback: choose an axis that's not parallel to xAxis
731+
if (std::abs(xAxis.x) < 0.9f) {
732+
yAxis = Vector3(1, 0, 0);
733+
} else {
734+
yAxis = Vector3(0, 1, 0);
735+
}
736+
737+
// Make it orthogonal to xAxis
738+
yAxis = yAxis - xAxis * yAxis.dotProduct(xAxis);
739+
if (yAxis.length() > EPSILON) {
740+
yAxis = yAxis.normalize();
741+
} else {
742+
yAxis = Vector3(0, 1, 0); // Ultimate fallback
743+
}
725744
}
726-
727-
// Ensure z-axis is orthogonal to both
728-
zAxis = xAxis.crossProduct(yAxis).normalize();
729-
730-
// Ensure y-axis is truly orthogonal (eliminate any drift)
731-
yAxis = zAxis.crossProduct(xAxis).normalize();
732745
}
733-
else if (yAxis.length() > EPSILON) {
734-
yAxis = yAxis.normalize();
735-
736-
// Create orthogonal vector
737-
zAxis = yAxis.perpendicular().normalize();
738746

739-
// Complete the basis
740-
xAxis = yAxis.crossProduct(zAxis).normalize();
741-
}
742-
else if (zAxis.length() > EPSILON) {
747+
// Calculate zAxis as cross product
748+
zAxis = xAxis.crossProduct(yAxis);
749+
if (zAxis.length() > EPSILON) {
743750
zAxis = zAxis.normalize();
744-
745-
// Create orthogonal vector
746-
xAxis = zAxis.perpendicular().normalize();
747-
748-
// Complete the basis
749-
yAxis = zAxis.crossProduct(xAxis).normalize();
750-
}
751-
else {
752-
// All axes are degenerate, just use identity rotation
753-
rotation = Quaternion();
754-
return;
751+
} else {
752+
zAxis = Vector3(0, 0, 1); // Fallback
755753
}
756-
757-
// Set the rotation matrix with our orthonormal basis
758-
rotationM.set(0, 0, xAxis.x); rotationM.set(0, 1, xAxis.y); rotationM.set(0, 2, xAxis.z);
759-
rotationM.set(1, 0, yAxis.x); rotationM.set(1, 1, yAxis.y); rotationM.set(1, 2, yAxis.z);
760-
rotationM.set(2, 0, zAxis.x); rotationM.set(2, 1, zAxis.y); rotationM.set(2, 2, zAxis.z);
761754
}
762755

763-
// Convert to quaternion and normalize
764-
rotation = Quaternion().fromRotationMatrix(rotationM).normalize();
756+
// Build rotation matrix (column-major)
757+
Matrix4 rotM;
758+
rotM.identity();
759+
rotM.set(0, 0, xAxis.x); rotM.set(0, 1, xAxis.y); rotM.set(0, 2, xAxis.z);
760+
rotM.set(1, 0, yAxis.x); rotM.set(1, 1, yAxis.y); rotM.set(1, 2, yAxis.z);
761+
rotM.set(2, 0, zAxis.x); rotM.set(2, 1, zAxis.y); rotM.set(2, 2, zAxis.z);
762+
763+
// Convert to quaternion
764+
rotation = Quaternion().fromRotationMatrix(rotM);
765+
float qNorm = rotation.norm();
766+
if (qNorm < EPSILON * EPSILON) {
767+
rotation = Quaternion(); // identity fallback
768+
} else {
769+
rotation = rotation.normalize();
770+
}
765771
}
766772

767773
void Matrix4::decomposeQDU(Vector3& position, Vector3& scale, Quaternion& rotation) const{

engine/core/math/Vector3.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -210,18 +210,24 @@ void Vector3::makeCeil( const Vector3& v ){
210210

211211
Vector3 Vector3::perpendicular(void){
212212
static float fSquareZero = 1e-06f * 1e-06f;
213-
213+
214214
Vector3 perp = this->crossProduct( Vector3::UNIT_X );
215-
215+
216216
// Check length
217-
if( perp.squaredLength() < fSquareZero )
218-
{
219-
/* This vector is the Y axis multiplied by a scalar, so we have
220-
to use another axis.
221-
*/
217+
if( perp.squaredLength() < fSquareZero ) {
222218
perp = this->crossProduct( Vector3::UNIT_Y );
223219
}
224-
220+
221+
// If still degenerate, return a default perpendicular vector
222+
if( perp.squaredLength() < fSquareZero ) {
223+
if (std::abs(x) < std::abs(y) && std::abs(x) < std::abs(z))
224+
perp = Vector3(1, 0, 0);
225+
else if (std::abs(y) < std::abs(z))
226+
perp = Vector3(0, 1, 0);
227+
else
228+
perp = Vector3(0, 0, 1);
229+
}
230+
225231
return perp;
226232
}
227233

engine/core/object/Light.cpp

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ void Light::setType(LightType type){
2626

2727
if (lightcomp.type != type){
2828
lightcomp.type = type;
29+
2930
scene->getSystem<RenderSystem>()->needReloadMeshes();
3031
}
3132
}
@@ -42,6 +43,7 @@ void Light::setDirection(Vector3 direction){
4243

4344
if (lightcomp.direction != direction){
4445
lightcomp.direction = direction;
46+
4547
transform.needUpdate = true;
4648
}
4749
}
@@ -77,7 +79,8 @@ void Light::setRange(float range){
7779

7880
if (lightcomp.range != range){
7981
lightcomp.range = range;
80-
lightcomp.needUpdateShadowMap = true;
82+
83+
lightcomp.needUpdateShadowCamera = true;
8184
}
8285
}
8386

@@ -92,7 +95,7 @@ void Light::setIntensity(float intensity){
9295
Transform& transform = getComponent<Transform>();
9396

9497
if (intensity > 0 && lightcomp.intensity == 0){
95-
lightcomp.needUpdateShadowMap = true;
98+
lightcomp.needUpdateShadowCamera = true;
9699
}
97100

98101
lightcomp.intensity = intensity;
@@ -113,7 +116,8 @@ void Light::setConeAngle(float inner, float outer){
113116
if (lightcomp.innerConeCos != innerConeCos || lightcomp.outerConeCos != outerConeCos){
114117
lightcomp.innerConeCos = innerConeCos;
115118
lightcomp.outerConeCos = outerConeCos;
116-
lightcomp.needUpdateShadowMap = true;
119+
120+
lightcomp.needUpdateShadowCamera = true;
117121
}
118122
}
119123

@@ -124,7 +128,8 @@ void Light::setInnerConeAngle(float inner){
124128

125129
if (lightcomp.innerConeCos != innerConeCos){
126130
lightcomp.innerConeCos = innerConeCos;
127-
lightcomp.needUpdateShadowMap = true;
131+
132+
lightcomp.needUpdateShadowCamera = true;
128133
}
129134
}
130135

@@ -141,7 +146,8 @@ void Light::setOuterConeAngle(float outer){
141146

142147
if (lightcomp.outerConeCos != outerConeCos){
143148
lightcomp.outerConeCos = outerConeCos;
144-
lightcomp.needUpdateShadowMap = true;
149+
150+
lightcomp.needUpdateShadowCamera = true;
145151
}
146152
}
147153

@@ -157,7 +163,7 @@ void Light::setShadows(bool shadows){
157163
if (lightcomp.shadows != shadows){
158164
lightcomp.shadows = shadows;
159165

160-
lightcomp.needUpdateShadowMap = true;
166+
lightcomp.needUpdateShadowCamera = true;
161167
scene->getSystem<RenderSystem>()->needReloadMeshes();
162168
}
163169
}
@@ -183,7 +189,12 @@ float Light::getBias() const{
183189
void Light::setShadowMapSize(unsigned int size){
184190
LightComponent& lightcomp = getComponent<LightComponent>();
185191

186-
lightcomp.mapResolution = size;
192+
if (lightcomp.mapResolution != size){
193+
lightcomp.mapResolution = size;
194+
195+
lightcomp.needUpdateShadowMap = true;
196+
scene->getSystem<RenderSystem>()->needReloadMeshes();
197+
}
187198
}
188199

189200
unsigned int Light::getShadowMapSize() const{
@@ -197,8 +208,9 @@ void Light::setShadowCameraNearFar(float near, float far){
197208

198209
if (lightcomp.shadowCameraNearFar != Vector2(near, far)){
199210
lightcomp.shadowCameraNearFar = Vector2(near, far);
211+
200212
lightcomp.automaticShadowCamera = false;
201-
lightcomp.needUpdateShadowMap = true;
213+
lightcomp.needUpdateShadowCamera = true;
202214
}
203215
}
204216

@@ -207,8 +219,9 @@ void Light::setCameraNear(float near){
207219

208220
if (lightcomp.shadowCameraNearFar.x != near){
209221
lightcomp.shadowCameraNearFar.x = near;
222+
210223
lightcomp.automaticShadowCamera = false;
211-
lightcomp.needUpdateShadowMap = true;
224+
lightcomp.needUpdateShadowCamera = true;
212225
}
213226
}
214227

@@ -223,8 +236,9 @@ void Light::setCameraFar(float far){
223236

224237
if (lightcomp.shadowCameraNearFar.y != far){
225238
lightcomp.shadowCameraNearFar.y = far;
239+
226240
lightcomp.automaticShadowCamera = false;
227-
lightcomp.needUpdateShadowMap = true;
241+
lightcomp.needUpdateShadowCamera = true;
228242
}
229243
}
230244

@@ -239,7 +253,8 @@ void Light::setAutomaticShadowCamera(bool automatic){
239253

240254
if (lightcomp.automaticShadowCamera != automatic){
241255
lightcomp.automaticShadowCamera = automatic;
242-
lightcomp.needUpdateShadowMap = true;
256+
257+
lightcomp.needUpdateShadowCamera = true;
243258
}
244259
}
245260

@@ -254,7 +269,8 @@ void Light::setNumCascades(unsigned int numCascades){
254269

255270
if (lightcomp.numShadowCascades != numCascades){
256271
lightcomp.numShadowCascades = numCascades;
257-
lightcomp.needUpdateShadowMap = true;
272+
273+
lightcomp.needUpdateShadowCamera = true;
258274
}
259275
}
260276

0 commit comments

Comments
 (0)