Browse Source

Editor: Add light component UI

Panagiotis Christopoulos Charitos 2 weeks ago
parent
commit
54df418793

+ 101 - 0
AnKi/Editor/EditorUi.cpp

@@ -771,6 +771,9 @@ void EditorUi::sceneNodePropertiesWindow()
 					case SceneComponentType::kParticleEmitter2:
 						particleEmitterComponent(static_cast<ParticleEmitter2Component&>(comp));
 						break;
+					case SceneComponentType::kLight:
+						lightComponent(static_cast<LightComponent&>(comp));
+						break;
 					default:
 						ImGui::Text("TODO");
 					}
@@ -1048,6 +1051,104 @@ void EditorUi::particleEmitterComponent(ParticleEmitter2Component& comp)
 	}
 }
 
+void EditorUi::lightComponent(LightComponent& comp)
+{
+	// Light type
+	if(ImGui::BeginCombo("Type", kLightComponentTypeNames[comp.getLightComponentType()]))
+	{
+		for(LightComponentType type : EnumIterable<LightComponentType>())
+		{
+			const Bool selected = type == comp.getLightComponentType();
+			if(ImGui::Selectable(kLightComponentTypeNames[type], selected))
+			{
+				comp.setLightComponentType(type);
+			}
+
+			// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
+			if(selected)
+			{
+				ImGui::SetItemDefaultFocus();
+			}
+		}
+		ImGui::EndCombo();
+	}
+
+	// Diffuse color
+	Vec4 diffuseCol = comp.getDiffuseColor();
+	if(ImGui::InputFloat4("Diffuse Color", &diffuseCol[0]))
+	{
+		comp.setDiffuseColor(diffuseCol.max(0.01f));
+	}
+
+	// Shadow
+	Bool shadow = comp.getShadowEnabled();
+	if(ImGui::Checkbox("Shadow", &shadow))
+	{
+		comp.setShadowEnabled(shadow);
+	}
+
+	if(comp.getLightComponentType() == LightComponentType::kPoint)
+	{
+		// Radius
+		F32 radius = comp.getRadius();
+		if(ImGui::SliderFloat("Radius", &radius, 0.01f, 100.0f))
+		{
+			comp.setRadius(max(radius, 0.01f));
+		}
+	}
+	else if(comp.getLightComponentType() == LightComponentType::kSpot)
+	{
+		// Radius
+		F32 distance = comp.getDistance();
+		if(ImGui::SliderFloat("Distance", &distance, 0.01f, 100.0f))
+		{
+			comp.setDistance(max(distance, 0.01f));
+		}
+
+		// Inner & outter angles
+		Vec2 angles(comp.getInnerAngle(), comp.getOuterAngle());
+		angles[0] = toDegrees(angles[0]);
+		angles[1] = toDegrees(angles[1]);
+		if(ImGui::SliderFloat2("Inner & Outer Angles", &angles[0], 1.0f, 89.0f))
+		{
+			angles[0] = clamp(toRad(angles[0]), 1.0_degrees, 80.0_degrees);
+			angles[1] = clamp(toRad(angles[1]), angles[0], 89.0_degrees);
+
+			comp.setInnerAngle(angles[0]);
+			comp.setOuterAngle(angles[1]);
+		}
+	}
+	else
+	{
+		ANKI_ASSERT(comp.getLightComponentType() == LightComponentType::kDirectional);
+
+		// Day of month
+		I32 month, day;
+		F32 hour;
+		comp.getTimeOfDay(month, day, hour);
+		Bool fieldChanged = false;
+		if(ImGui::SliderInt("Month", &month, 0, 11))
+		{
+			fieldChanged = true;
+		}
+
+		if(ImGui::SliderInt("Day", &day, 0, 30))
+		{
+			fieldChanged = true;
+		}
+
+		if(ImGui::SliderFloat("Hour (0-24)", &hour, 0.0f, 24.0f))
+		{
+			fieldChanged = true;
+		}
+
+		if(fieldChanged)
+		{
+			comp.setDirectionFromTimeOfDay(month, day, hour);
+		}
+	}
+}
+
 void EditorUi::cVarsWindow()
 {
 	if(!m_showCVarEditorWindow)

+ 1 - 0
AnKi/Editor/EditorUi.h

@@ -176,6 +176,7 @@ private:
 	void meshComponent(MeshComponent& comp);
 	void skinComponent(SkinComponent& comp);
 	void particleEmitterComponent(ParticleEmitter2Component& comp);
+	void lightComponent(LightComponent& comp);
 	void dirTree(const AssetPath& path);
 
 	// Widget/UI utils

+ 3 - 1
AnKi/Renderer/PrimaryNonRenderableVisibility.cpp

@@ -29,7 +29,9 @@ static WeakArray<TComponent*> gatherComponents(ConstWeakArray<UVec2> pairs, TArr
 		}
 
 		TComponent* comp = &array[pair.y()];
-		if(comp->getUuid() == pair.x())
+		const U32 uuid = pair.x();
+		ANKI_ASSERT(uuid != 0);
+		if(comp->getUuid() == uuid)
 		{
 			components.emplaceBack(comp);
 		}

+ 10 - 25
AnKi/Scene/Components/LightComponent.cpp

@@ -89,7 +89,6 @@ void LightComponent::setLightComponentType(LightComponentType newType)
 		m_shadowAtlasUvViewportCount = 0;
 		m_shapeDirty = true;
 		m_otherDirty = true;
-		m_uuid = 0;
 	}
 }
 
@@ -105,15 +104,6 @@ void LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 
 	if(updated && m_type == LightComponentType::kPoint)
 	{
-		if(!m_shadow)
-		{
-			m_uuid = 0;
-		}
-		else if(m_uuid == 0)
-		{
-			m_uuid = SceneGraph::getSingleton().getNewUuid();
-		}
-
 		const Bool reallyShadow = m_shadow && m_shadowAtlasUvViewportCount == 6;
 
 		// Upload the hash
@@ -137,10 +127,12 @@ void LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 		gpuLight.m_radius = m_point.m_radius;
 		gpuLight.m_diffuseColor = m_diffColor.xyz();
 		gpuLight.m_visibleRenderablesHashIndex = (reallyShadow) ? m_hash.getIndex() : 0;
-		gpuLight.m_flags = GpuSceneLightFlag::kPointLight;
-		gpuLight.m_flags |= (reallyShadow) ? GpuSceneLightFlag::kShadow : GpuSceneLightFlag::kNone;
+		gpuLight.m_isPointLight = 1;
+		gpuLight.m_isSpotLight = 0;
+		gpuLight.m_shadow = reallyShadow;
+		gpuLight.m_cpuFeedback = m_shadow;
 		gpuLight.m_componentArrayIndex = getArrayIndex();
-		gpuLight.m_uuid = m_uuid;
+		gpuLight.m_uuid = getUuid();
 		for(U32 f = 0; f < m_shadowAtlasUvViewportCount; ++f)
 		{
 			gpuLight.m_spotLightMatrixOrPointLightUvViewports[f] = m_shadowAtlasUvViewports[f];
@@ -154,15 +146,6 @@ void LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 	}
 	else if(updated && m_type == LightComponentType::kSpot)
 	{
-		if(!m_shadow)
-		{
-			m_uuid = 0;
-		}
-		else if(m_uuid == 0)
-		{
-			m_uuid = SceneGraph::getSingleton().getNewUuid();
-		}
-
 		const Bool reallyShadow = m_shadow && m_shadowAtlasUvViewportCount == 1;
 
 		// Upload the hash
@@ -186,10 +169,12 @@ void LightComponent::update(SceneComponentUpdateInfo& info, Bool& updated)
 		gpuLight.m_radius = m_spot.m_distance;
 		gpuLight.m_diffuseColor = m_diffColor.xyz();
 		gpuLight.m_visibleRenderablesHashIndex = (reallyShadow) ? m_hash.getIndex() : 0;
-		gpuLight.m_flags = GpuSceneLightFlag::kSpotLight;
-		gpuLight.m_flags |= (reallyShadow) ? GpuSceneLightFlag::kShadow : GpuSceneLightFlag::kNone;
+		gpuLight.m_isPointLight = 0;
+		gpuLight.m_isSpotLight = 1;
+		gpuLight.m_shadow = reallyShadow;
+		gpuLight.m_cpuFeedback = m_shadow;
 		gpuLight.m_componentArrayIndex = getArrayIndex();
-		gpuLight.m_uuid = m_uuid;
+		gpuLight.m_uuid = getUuid();
 		gpuLight.m_innerCos = cos(m_spot.m_innerAngle / 2.0f);
 		gpuLight.m_direction = -m_worldTransform.getRotation().getZAxis();
 		gpuLight.m_outerCos = cos(m_spot.m_outerAngle / 2.0f);

+ 24 - 23
AnKi/Scene/Components/LightComponent.h

@@ -15,20 +15,19 @@ namespace anki {
 // Forward
 class Frustum;
 
-/// @addtogroup scene
-/// @{
-
 enum class LightComponentType : U8
 {
 	kPoint,
 	kSpot,
-	kDirectional, ///< Basically the sun.
+	kDirectional, // Basically the sun.
 
 	kCount,
 	kFirst = 0
 };
 
-/// Light component. Contains all the info of lights.
+inline constexpr Array<const Char*, U32(LightComponentType::kCount)> kLightComponentTypeNames = {"Point", "Spot", "Directional"};
+
+// Light component. Contains all the info of lights.
 class LightComponent : public SceneComponent
 {
 	ANKI_SCENE_COMPONENT(LightComponent)
@@ -141,30 +140,35 @@ public:
 		return m_spot.m_viewMat;
 	}
 
-	/// Set the direction of the directional light by setting the date and hour.
+	// Set the direction of the directional light by setting the date and hour.
 	void setDirectionFromTimeOfDay(I32 month, I32 day, F32 hour)
 	{
-		m_dir.m_month = month;
-		m_dir.m_day = day;
-		m_dir.m_hour = hour;
+		ANKI_EXPECT(month >= 0 && month < 12);
+		ANKI_EXPECT(day >= 0 && day < 31);
+		ANKI_EXPECT(hour >= 0.0f && hour <= 24.0f);
+		m_dir.m_month = clamp(month, 0, 11);
+		m_dir.m_day = clamp(day, 0, 30);
+		m_dir.m_hour = clamp(hour, 0.0f, 24.0f);
 		m_shapeDirty = true;
 	}
 
-	/// Calculate some matrices for each cascade. For dir lights.
-	/// @param cameraFrustum Who is looking at the light.
-	/// @param cascadeDistances The distances of the cascades.
-	/// @param cascadeProjMats View projection matrices for each cascade.
-	/// @param cascadeViewMats View matrices for each cascade. Optional.
+	// Get the fields which might or might not have come from the direction of the light
+	void getTimeOfDay(I32& month, I32& day, F32& hour) const
+	{
+		month = m_dir.m_month;
+		day = m_dir.m_day;
+		hour = m_dir.m_hour;
+	}
+
+	// Calculate some matrices for each cascade. For dir lights.
+	// cameraFrustum Who is looking at the light.
+	// cascadeDistances The distances of the cascades.
+	// cascadeProjMats View projection matrices for each cascade.
+	// cascadeViewMats View matrices for each cascade. Optional.
 	void computeCascadeFrustums(const Frustum& cameraFrustum, ConstWeakArray<F32> cascadeDistances, WeakArray<Mat4> cascadeProjMats,
 								WeakArray<Mat3x4> cascadeViewMats = {},
 								WeakArray<Array<F32, U32(FrustumPlaneType::kCount)>> cascadePlanes = {}) const;
 
-	U32 getUuid() const
-	{
-		ANKI_ASSERT(m_uuid);
-		return m_uuid;
-	}
-
 	ANKI_INTERNAL void setShadowAtlasUvViewports(ConstWeakArray<Vec4> viewports);
 
 	const GpuSceneArrays::Light::Allocation& getGpuSceneLightAllocation() const
@@ -209,8 +213,6 @@ private:
 
 	Array<Vec4, 6> m_shadowAtlasUvViewports;
 
-	U32 m_uuid = 0;
-
 	LightComponentType m_type;
 
 	U8 m_shadow : 1 = false;
@@ -220,6 +222,5 @@ private:
 
 	void update(SceneComponentUpdateInfo& info, Bool& updated) override;
 };
-/// @}
 
 } // end namespace anki

+ 5 - 5
AnKi/Shaders/ClusterBinning.ankiprog

@@ -160,7 +160,7 @@ constexpr UVec2 kSampleLocations[kSampleCount] = {LOCATION(1, -3), LOCATION(-1,
 	const GpuSceneType obj = g_objects[g_visibleObjectIds[visibleObjectIdx + 1]];
 
 #	if OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_LIGHT
-	if((U32)obj.m_flags & (U32)GpuSceneLightFlag::kPointLight)
+	if(obj.m_isPointLight)
 	{
 		collides = testRaySphere(rayOrigin, rayDir, obj.m_position, obj.m_radius, t0, t1);
 	}
@@ -229,7 +229,7 @@ constexpr UVec2 kSampleLocations[kSampleCount] = {LOCATION(1, -3), LOCATION(-1,
 
 		// Set the tile
 #	if OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_LIGHT
-		if((U32)obj.m_flags & (U32)GpuSceneLightFlag::kPointLight)
+		if(obj.m_isPointLight)
 		{
 			InterlockedOr(g_clusters[tileIdx].m_pointLightsMask[maskArrayIdx], mask);
 		}
@@ -273,7 +273,7 @@ constexpr UVec2 kSampleLocations[kSampleCount] = {LOCATION(1, -3), LOCATION(-1,
 		for(I32 i = startZSplit; i <= endZSplit; ++i)
 		{
 #	if OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_LIGHT
-			if((U32)obj.m_flags & (U32)GpuSceneLightFlag::kPointLight)
+			if(obj.m_isPointLight)
 			{
 				InterlockedOr(g_clusters[g_consts.m_tileCount + i].m_pointLightsMask[maskArrayIdx], mask);
 			}
@@ -339,7 +339,7 @@ StructuredBuffer<U32> g_visibles : register(t1);
 #	if OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_LIGHT
 	const GpuSceneLight input = g_inBuffer[g_visibles[idxOut + 1]];
 
-	const Bool isPoint = ((U32)input.m_flags & (U32)GpuSceneLightFlag::kPointLight) ? true : false;
+	const Bool isPoint = input.m_isPointLight;
 
 	LightUnion output = (LightUnion)0;
 	output.m_position = input.m_position;
@@ -348,7 +348,7 @@ StructuredBuffer<U32> g_visibles : register(t1);
 	output.m_diffuseColor = input.m_diffuseColor;
 	output.m_lightType = (isPoint) ? 0 : 1;
 
-	output.m_shadow = ((U32)input.m_flags & (U32)GpuSceneLightFlag::kShadow) ? 1 : 0;
+	output.m_shadow = input.m_shadow;
 	output.m_innerCos = input.m_innerCos;
 	output.m_outerCos = input.m_outerCos;
 

+ 4 - 4
AnKi/Shaders/GpuVisibilityLocalLights.ankiprog

@@ -58,7 +58,7 @@ void lightVsCellVisibility(StructuredBuffer<GpuSceneLight> lights, U32 cellIdx,
 		// Get the light bounds
 		Vec3 worldLightAabbMin;
 		Vec3 worldLightAabbMax;
-		if((U32)light.m_flags & (U32)GpuSceneLightFlag::kPointLight)
+		if(light.m_isPointLight)
 		{
 			worldLightAabbMin = light.m_position - light.m_radius;
 			worldLightAabbMax = light.m_position + light.m_radius;
@@ -83,7 +83,7 @@ void lightVsCellVisibility(StructuredBuffer<GpuSceneLight> lights, U32 cellIdx,
 		if(detailedTests)
 		{
 			Vec4 spotLightPlanes[5];
-			if((U32)light.m_flags & (U32)GpuSceneLightFlag::kSpotLight)
+			if(light.m_isSpotLight)
 			{
 				const Vec3 pe = light.m_position;
 				const Vec3 p0 = light.m_edgePoints[0];
@@ -97,11 +97,11 @@ void lightVsCellVisibility(StructuredBuffer<GpuSceneLight> lights, U32 cellIdx,
 				spotLightPlanes[4] = computePlane(p3, p0, p1);
 			}
 
-			if((U32)light.m_flags & (U32)GpuSceneLightFlag::kPointLight && !aabbSphereOverlap(cellMin, cellMax, light.m_position, light.m_radius))
+			if(light.m_isPointLight && !aabbSphereOverlap(cellMin, cellMax, light.m_position, light.m_radius))
 			{
 				continue;
 			}
-			else if((U32)light.m_flags & (U32)GpuSceneLightFlag::kSpotLight && !insideFrustum(spotLightPlanes, cellMin, cellMax))
+			else if(light.m_isSpotLight && !insideFrustum(spotLightPlanes, cellMin, cellMax))
 			{
 				continue;
 			}

+ 7 - 1
AnKi/Shaders/GpuVisibilityNonRenderables.ankiprog

@@ -133,7 +133,13 @@ Vec4 getSphere(GpuSceneGlobalIlluminationProbe l)
 	// Give feedback to the CPU
 	//
 #if CPU_FEEDBACK
-	if(!skip && g_objects[svDispatchThreadId].m_uuid != 0)
+#	if OBJECT_TYPE == ANKI_GPU_SCENE_NON_RENDERABLE_OBJECT_TYPE_LIGHT
+	const Bool doFeedback = g_objects[svDispatchThreadId].m_cpuFeedback;
+#	else
+	const Bool doFeedback = true;
+#	endif
+
+	if(!skip && doFeedback)
 	{
 		U32 idx;
 		InterlockedAdd(g_counterBuffer[0].m_feedbackObjectCount, 1, idx);

+ 6 - 12
AnKi/Shaders/Include/GpuSceneTypes.h

@@ -124,15 +124,6 @@ struct GpuSceneParticleEmitter2
 };
 static_assert(sizeof(GpuSceneParticleEmitter2) % sizeof(Vec4) == 0);
 
-enum class GpuSceneLightFlag : U32
-{
-	kNone = 0,
-	kPointLight = 1 << 0,
-	kSpotLight = 1 << 1,
-	kShadow = 1 << 2
-};
-ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(GpuSceneLightFlag)
-
 // A hash of all visible renderables. If it matches between vis tests then skip the drawcalls. Touched only by the GPU.
 struct GpuSceneLightVisibleRenderablesHash
 {
@@ -148,7 +139,11 @@ struct GpuSceneLight
 	Vec3 m_diffuseColor;
 	U32 m_visibleRenderablesHashIndex; // Points to a GpuSceneLightVisibleRenderablesHash
 
-	GpuSceneLightFlag m_flags;
+	U32 m_isPointLight : 1;
+	U32 m_isSpotLight : 1;
+	U32 m_shadow : 1;
+	U32 m_cpuFeedback : 1; // If true the GPU visibility will inform the CPU about it
+	U32 m_padding : 28;
 	U32 m_componentArrayIndex; // Array index of the LightComponent in the CPU scene.
 	U32 m_uuid; // The UUID of that light. If it's zero the GPU will not inform the CPU about it.
 	F32 m_innerCos; // Only for spot light.
@@ -158,8 +153,7 @@ struct GpuSceneLight
 
 	Vec4 m_edgePoints[4u]; // Edge points in world space. Only for spot light.
 
-	// If it's a spot light the 4 first rows are the texture matrix. If it's point light it's the UV viewports in the
-	// shadow atlas.
+	// If it's a spot light the 4 first rows are the texture matrix. If it's point light it's the UV viewports in the shadow atlas
 	Vec4 m_spotLightMatrixOrPointLightUvViewports[6u];
 };
 

+ 1 - 1
AnKi/Shaders/RtMaterialFetch.hlsl

@@ -257,7 +257,7 @@ vector<T, 3> directLighting(GBufferLight<T> gbuffer, Vec3 hitPos, Bool isSky, Bo
 			const T lambert = max(T(0), dot(nFrag2Light, gbuffer.m_worldNormal));
 
 			F32 attenuation = computeAttenuationFactor(light.m_radius, frag2Light);
-			if((U32)light.m_flags & (U32)GpuSceneLightFlag::kSpotLight)
+			if(light.m_isSpotLight)
 			{
 				attenuation *= computeSpotFactor(nFrag2Light, light.m_outerCos, light.m_innerCos, light.m_direction);
 			}

+ 1 - 3
AnKi/Shaders/TraditionalDeferredShading.ankiprog

@@ -145,9 +145,7 @@ Vec4 main(VertOut input) : SV_TARGET0
 
 		const F32 att = computeAttenuationFactor<F32>(light.m_radius, frag2Light);
 		const F32 lambert = nol;
-		const F32 spot = ((U32)light.m_flags & (U32)GpuSceneLightFlag::kSpotLight)
-							 ? computeSpotFactor<F32>(l, light.m_outerCos, light.m_innerCos, light.m_direction)
-							 : 1.0f;
+		const F32 spot = light.m_isSpotLight ? computeSpotFactor<F32>(l, light.m_outerCos, light.m_innerCos, light.m_direction) : 1.0f;
 		const F32 factor = att * spot * max(lambert, gbuffer.m_subsurface);
 
 #	if SPECULAR == 1