Przeglądaj źródła

Stefan Gustavsons 1D simplex noise implemented, the one wallpaper engine uses

UwU 4 dni temu
rodzic
commit
14f77d6371

+ 18 - 72
src/WallpaperEngine/Render/Objects/CParticle.cpp

@@ -892,93 +892,39 @@ InitializerFunc CParticle::createTurbulentVelocityRandomInitializer (const Turbu
     DynamicValue* audioStartValue = init.audioProcessingFrequencyStart->value.get ();
     DynamicValue* audioEndValue = init.audioProcessingFrequencyEnd->value.get ();
 
+    // same formula as wallpaper64.exe (sub_1401C8AF0)
     return [this, speedMin, speedMax, offsetVal, scaleVal, forwardVal, timeScaleVal, phaseMinVal, phaseMaxVal, rightVal,
 	    speedOverride, audioModeValue, audioBoundsValue, audioExponentValue, audioStartValue,
 	    audioEndValue] (ParticleInstance& p) {
-	glm::vec3 forward = forwardVal->getVec3 ();
-	glm::vec3 right = rightVal->getVec3 ();
-	// Y-flip for coordinate system conversion
-	forward.y = -forward.y;
-	right.y = -right.y;
-
-	if (glm::length (forward) > 0.0001f) {
-	    forward = glm::normalize (forward);
-	} else {
-	    // Default forward direction when not specified (up in centered space)
-	    forward = glm::vec3 (0.0f, 1.0f, 0.0f);
-	}
-	if (glm::length (right) > 0.0001f) {
-	    right = glm::normalize (right);
-	} else {
-	    right = glm::vec3 (1.0f, 0.0f, 0.0f);
-	}
-
-	float speed = WallpaperEngine::Maths::randomFloat (m_rng, speedMin->getFloat (), speedMax->getFloat ());
-	float scale = scaleVal->getFloat ();
-	float offset = offsetVal->getFloat ();
-	float timeScale = timeScaleVal->getFloat ();
-	float phaseMin = phaseMinVal->getFloat ();
-	float phaseMax = phaseMaxVal->getFloat ();
-
-	// Sample noise at position + time offset: timescale shifts the field over time so
-	// particles spawned at different times drift differently (evolving vapor stream);
-	// the position term gives spatial coherence between nearby particles.
-	glm::vec3 noisePos = p.position * 0.1f;
-	noisePos += glm::vec3 (static_cast<float> (m_time) * timeScale);
-
-	// Phase adds per-particle randomization to noise position, WE scales its random range by the audio level
 	const float audio = sampleAudio (
 	    audioModeValue->getInt (), audioBoundsValue->getVec2 (), audioExponentValue->getFloat (),
 	    audioStartValue->getInt (), audioEndValue->getInt ()
 	);
-	float phase = phaseMin + WallpaperEngine::Maths::randomFloat (m_rng, 0.0f, 1.0f) * (phaseMax - phaseMin) * audio;
-	glm::vec3 samplePos = noisePos + glm::vec3 (phase, phase * 0.7f, phase * 1.3f);
+	const float phaseMin = phaseMinVal->getFloat ();
+	const float phaseRange = (phaseMaxVal->getFloat () - phaseMin) * audio;
+	const float phase = phaseMin + WallpaperEngine::Maths::randomFloat (m_rng, 0.0f, 1.0f) * phaseRange
+	    + static_cast<float> (m_time);
 
-	glm::vec3 result = curlNoise (samplePos);
-	float len = glm::length (result);
-	if (len < 0.0001f) {
-	    result = forward;
-	} else {
-	    result = result / len;
-	}
+	const float angle = simplexNoise1D (phase * timeScaleVal->getFloat ()) * glm::pi<float> () * scaleVal->getFloat ()
+	    + offsetVal->getFloat ();
+	const float speed = WallpaperEngine::Maths::randomFloat (m_rng, speedMin->getFloat (), speedMax->getFloat ());
 
-	// Scale limits how far direction can deviate from forward
-	if (scale < 2.0f) {
-	    float cosAngle = glm::dot (result, forward);
-	    float angle = std::acos (glm::clamp (cosAngle, -1.0f, 1.0f)) / glm::pi<float> ();
-	    float maxAngle = scale / 2.0f;
-
-	    if (angle > maxAngle && maxAngle > 0.0001f) {
-		glm::vec3 axis = glm::cross (result, forward);
-		float axisLen = glm::length (axis);
-		if (axisLen > 0.0001f) {
-		    axis = axis / axisLen;
-		    float rotAngle = (angle - maxAngle) * glm::pi<float> ();
-		    glm::mat3 rot = glm::mat3 (glm::rotate (glm::mat4 (1.0f), rotAngle, axis));
-		    result = rot * result;
-		}
-	    }
-	}
+	glm::vec3 right = rightVal->getVec3 ();
 
-	// Offset rotates result around right axis (tilts up/down)
-	if (std::abs (offset) > 0.0001f) {
-	    glm::mat3 rot = glm::mat3 (glm::rotate (glm::mat4 (1.0f), -offset, right));
-	    result = rot * result;
+	if (glm::length (right) < 0.0001f) {
+	    right = glm::vec3 (0.0f, 0.0f, 1.0f);
 	}
 
-	// 2D/orthographic particles (flags & 4 == 0): project onto XY. curlNoise is 3D but
-	// z-drift is meaningless here and makes rope segments diverge in depth.
+	glm::vec3 direction = glm::mat3 (glm::rotate (glm::mat4 (1.0f), angle, right)) * forwardVal->getVec3 ();
+
+	direction.y = -direction.y;
+
+	// z moves nothing on screen in 2D systems but pulls rope segments apart in depth
 	if ((m_particle.flags & 4) == 0) {
-	    result.z = 0.0f;
-	    float len2d = glm::length (result);
-	    if (len2d > 0.0001f) {
-		result /= len2d;
-	    }
+	    direction.z = 0.0f;
 	}
 
-	glm::vec3 finalVel = result * speed * speedOverride->getFloat ();
-
-	p.velocity += finalVel;
+	p.velocity += direction * speed * speedOverride->getFloat ();
     };
 }
 

+ 22 - 0
src/WallpaperEngine/Render/Utils/NoiseUtils.h

@@ -144,4 +144,26 @@ inline glm::vec3 curlNoise (const glm::vec3& p) {
     return glm::vec3 (x, y, z) / (2.0f * e);
 }
 
+// Stefan Gustavson's 1D simplex noise, the one wallpaper64.exe uses for turbulentvelocityrandom (roughly -1..1)
+inline float simplexNoise1D (float x) {
+    const int i0 = static_cast<int> (std::floor (x));
+    const float x0 = x - static_cast<float> (i0);
+    const float x1 = x0 - 1.0f;
+
+    const auto grad = [] (int hash, float value) {
+	const float gradient = 1.0f + static_cast<float> (hash & 7);
+	return ((hash & 8) ? -gradient : gradient) * value;
+    };
+
+    float t0 = 1.0f - x0 * x0;
+    t0 *= t0;
+    float t1 = 1.0f - x1 * x1;
+    t1 *= t1;
+
+    const float n0 = t0 * t0 * grad (PERLIN_PERM[i0 & 0xff], x0);
+    const float n1 = t1 * t1 * grad (PERLIN_PERM[(i0 + 1) & 0xff], x1);
+
+    return 0.395f * (n0 + n1);
+}
+
 } // namespace WallpaperEngine::Render::Utils