diff --git a/include/mbgl/shaders/gl/particle_gpu_validation.hpp b/include/mbgl/shaders/gl/particle_gpu_validation.hpp index 90063fe67..f41041e09 100644 --- a/include/mbgl/shaders/gl/particle_gpu_validation.hpp +++ b/include/mbgl/shaders/gl/particle_gpu_validation.hpp @@ -42,6 +42,7 @@ void main() { uniform sampler2D u_previous; uniform sampler2D u_mask_texture; uniform sampler2D u_field_texture; +uniform sampler2D u_next_field_texture; uniform sampler2D u_state_texture; in vec2 v_uv; in vec2 v_world; @@ -57,9 +58,12 @@ void main() { // u_mask.w is the explicit State lifecycle flag: new RenderTargets are // cleared opaque by MapLibre, so sampled alpha cannot identify a seed. vec2 position = u_mask.w > 0.5 ? v_uv : previous.rg; - vec4 field = texture(u_field_texture, position); - vec2 velocity = field.rg * 2.0 - 1.0; - if (field.a < 0.5) { + // 40-02: decode each independent Field texture, then interpolate the + // velocity on GPU. u_marker.x is the clamped Layer timeMix property. + vec4 field0 = texture(u_field_texture, position); + vec4 field1 = texture(u_next_field_texture, position); + vec2 velocity = mix(field0.rg * 2.0 - 1.0, field1.rg * 2.0 - 1.0, u_marker.x); + if (field0.a < 0.5 || field1.a < 0.5) { position = v_uv; velocity = vec2(0.0); } diff --git a/include/mbgl/shaders/shader_defines.hpp b/include/mbgl/shaders/shader_defines.hpp index b7b033476..85a40fee0 100644 --- a/include/mbgl/shaders/shader_defines.hpp +++ b/include/mbgl/shaders/shader_defines.hpp @@ -342,6 +342,7 @@ enum { idParticleGpuValidationPreviousTexture, idParticleGpuValidationMaskTexture, idParticleGpuValidationFieldTexture, + idParticleGpuValidationNextFieldTexture, idParticleGpuValidationStateTexture, particleGpuValidationTextureCount }; diff --git a/include/mbgl/style/layers/raster_particle_layer.hpp b/include/mbgl/style/layers/raster_particle_layer.hpp index 55f0c5748..d497e0c9f 100644 --- a/include/mbgl/style/layers/raster_particle_layer.hpp +++ b/include/mbgl/style/layers/raster_particle_layer.hpp @@ -12,6 +12,8 @@ public: void setActive(bool); uint8_t getMaskMode() const; void setMaskMode(uint8_t); + float getTimeMix() const; + void setTimeMix(float); class Impl; const Impl& impl() const; Mutable mutableImpl() const; diff --git a/include/mbgl/style/sources/raster_array_source.hpp b/include/mbgl/style/sources/raster_array_source.hpp index 1a72d1935..e02d6c9cc 100644 --- a/include/mbgl/style/sources/raster_array_source.hpp +++ b/include/mbgl/style/sources/raster_array_source.hpp @@ -11,7 +11,13 @@ namespace style { class RasterArraySource final : public Source { public: - RasterArraySource(std::string id, std::string fixtureID, std::string metadataURL, std::string fieldURL); + RasterArraySource(std::string id, + std::string fixtureID, + std::string metadataURL, + std::string fieldURL, + std::string nextFixtureID = {}, + std::string nextMetadataURL = {}, + std::string nextFieldURL = {}); ~RasterArraySource() override; class Impl; @@ -28,8 +34,12 @@ private: std::unique_ptr metadataRequest; std::unique_ptr fieldRequest; + std::unique_ptr nextMetadataRequest; + std::unique_ptr nextFieldRequest; std::optional metadata; std::optional> fieldBytes; + std::optional nextMetadata; + std::optional> nextFieldBytes; mapbox::base::WeakPtrFactory weakFactory{this}; }; diff --git a/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/ParticleCoreLayerValidationActivity.kt b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/ParticleCoreLayerValidationActivity.kt index 30060d97e..6d1c0d542 100644 --- a/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/ParticleCoreLayerValidationActivity.kt +++ b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/particle/ParticleCoreLayerValidationActivity.kt @@ -22,12 +22,16 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() { private var enabled = true private var maskMode = MASK_GLOBAL private var fixtureMode = FIXTURE_UNIFORM_EAST + private var timeMix = 0.0f + private var timeWindowMode = false override fun onCreate(savedInstanceState: Bundle?) { active = savedInstanceState?.getBoolean(STATE_ACTIVE, true) ?: true enabled = savedInstanceState?.getBoolean(STATE_ENABLED, true) ?: true maskMode = savedInstanceState?.getInt(STATE_MASK_MODE, MASK_GLOBAL) ?: MASK_GLOBAL fixtureMode = savedInstanceState?.getInt(STATE_FIXTURE_MODE, FIXTURE_UNIFORM_EAST) ?: FIXTURE_UNIFORM_EAST + timeMix = savedInstanceState?.getFloat(STATE_TIME_MIX, 0.0f) ?: 0.0f + timeWindowMode = savedInstanceState?.getBoolean(STATE_TIME_WINDOW, false) ?: false super.onCreate(savedInstanceState) Log.i( TAG, @@ -72,13 +76,30 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() { if (this::maplibreMap.isInitialized) setValidationProperties(active = active, maskMode = MASK_OCEAN_B) } findViewById