Particle color

Ask for help about creating mods and scripts for Grimrock 2 or share your tips, scripts, tools and assets with other modders here. Warning: forum contains spoilers!
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Particle color

Post by AndakRainor »

Thank you for the advice. I tried with fadeIn() and fadeOut(), it really looks better with those transitions.

Getting the right values from GameMode.getTimeOfDay() to start and set duration of those transitions was the hard part... Luckily in my mod I decided to use physical time for this parameter instead of tiles walked, so I can calculate a correctly scaled value for the duration of a transition. But I had to do many tries so I used a fast day/night cycle as you can imagine (from 12 sec to 2 minutes, no way I could stand the 24 hours test :o )

I hope the values I found will scale nicely for the normal cycle (24h). Here are the values I found, correct me if this is wrong;

- 0.04 to 0.08 : end of dawn, transition from orange to light blue clouds
- 0.90 to 0.94 : evening, transition from light blue to orange clouds
- 0.98 to 1.02 : night begins, transition from orange to dark blue clouds
- 1.96 to 2.00 : dawn begins, transition from dark blue to orange clouds
(time of day is in range [0,2[)

I was expecting more symmetry for those values, seems the night is longer than the day... I am glad there is no seasons in the game :lol:
minmay
Posts: 2790
Joined: Mon Sep 23, 2013 2:24 am

Re: Particle color

Post by minmay »

0 and 1 are the points where the sun is coplanar with the horizon, and the intensity of the light and sky shader is identical at the two points as far as I can tell with my eyes. So I wouldn't say that night is longer than day, just that the transitions from sunrise/sunset to/from day are smoother than the transitions from sunrise/sunset from/to night.
Note that the directional light follows a trajectory very different from that of the sun/moon circle (in fact, the angle doesn't change at all during night), presumably because an angle parallel to any of the axes would look silly.
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Particle color

Post by AndakRainor »

I will look at it tomorrow, but it seems resting is messing with all of this... I use a timer component
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Particle color

Post by AndakRainor »

I have a problem with those particle systems, they have very inconsistent behavior. They can randomly disappear or twinkle while moving or mouse looking.

here for example the particle system used for dusk:

Code: Select all

defineParticleSystem{
  name = "inter_clouds_effects",
  emitters = {
    -- fog under 1
    {
      emissionRate = 500,
      emissionTime = 0,
      maxParticles = 500,
      spawnBurst = true,
      boxMin = {-96*4, -175, -96*4},
      boxMax = { 96*5, -150,  96*5},
      sprayAngle = {0,360},
      velocity = {0,0},
      objectSpace = false,
      --texture = "assets/textures/particles/fog.tga",
      texture = "assets/textures/particles/ice_guardian_smoke.tga",
      lifetime = {100,500},
      color0 = {2,0.5,0.2},
      opacity = 1,
      fadeIn = 2.5,
      fadeOut = 2.5,
      size = {100, 150},
      gravity = {0,0,0},
      airResistance = 0.5,
      rotationSpeed = -0.1,
      randomInitialRotation = true,
      blendMode = "Translucent",
    },
    -- fog under 2
    {
      emissionRate = 500,
      emissionTime = 0,
      maxParticles = 500,
      spawnBurst = true,
      boxMin = {-96*4, -175, -96*4},
      boxMax = { 96*5, -150,  96*5},
      sprayAngle = {0,360},
      velocity = {0,0},
      objectSpace = false,
      --texture = "assets/textures/particles/fog.tga",
      texture = "assets/textures/particles/ice_guardian_smoke.tga",
      lifetime = {100,500},
      color0 = {2,0.5,0.2},
      opacity = 1,
      fadeIn = 2.5,
      fadeOut = 2.5,
      size = {100, 150},
      gravity = {0,0,0},
      airResistance = 0.5,
      rotationSpeed = 0.1,
      randomInitialRotation = true,
      blendMode = "Translucent",
    },
    -- fog under 3
    {
      emissionRate = 500,
      emissionTime = 0,
      maxParticles = 500,
      spawnBurst = true,
      boxMin = {-96*10, -125, -96*10},
      boxMax = { 96*11, -100,  96*11},
      sprayAngle = {0,360},
      velocity = {0,0},
      objectSpace = false,
      --texture = "assets/textures/particles/fog.tga",
      texture = "assets/textures/particles/ice_guardian_smoke.tga",
      lifetime = {100,500},
      color0 = {2,0.5,0.2},
      opacity = 0.5,
      fadeIn = 2.5,
      fadeOut = 2.5,
      size = {100, 150},
      gravity = {0,0,0},
      airResistance = 0.5,
      rotationSpeed = -0.1,
      randomInitialRotation = true,
      blendMode = "Translucent",
    },
    -- fog under 4
    {
      emissionRate = 500,
      emissionTime = 0,
      maxParticles = 500,
      spawnBurst = true,
      boxMin = {-96*10, -175, -96*10},
      boxMax = { 96*11, -150,  96*11},
      sprayAngle = {0,360},
      velocity = {0,0},
      objectSpace = false,
      --texture = "assets/textures/particles/fog.tga",
      texture = "assets/textures/particles/ice_guardian_smoke.tga",
      lifetime = {100,500},
      color0 = {2,0.5,0.2},
      opacity = 0.5,
      fadeIn = 2.5,
      fadeOut = 2.5,
      size = {100, 150},
      gravity = {0,0,0},
      airResistance = 0.5,
      rotationSpeed = 0.1,
      randomInitialRotation = true,
      blendMode = "Translucent",
    },
  }
}
The sky object:

Code: Select all

defineObject{
  name = "temple_of_air_sky",
  baseObject = "forest_day_sky",
  components = {
    {
      class = "Sky",
      farClip = 1000,
    },
    {
      class = "Particle",
      name = "daily",
      particleSystem = "daily_clouds_effects",
    },
    {
      class = "Particle",
      name = "inter",
      particleSystem = "inter_clouds_effects",
    },
    {
      class = "Particle",
      name = "night",
      particleSystem = "night_clouds_effects",
    },
    {
      class = "Timer",
      timerInterval = 0,
      triggerOnStart = true,
      currentLevelOnly = true,
      onActivate = function(self)
        local d = {0.025, 0.100, 0.900, 0.975, 1.000, 1.025, 1.975}
        local r = utils.script.timeOfDayRatio 
        local t = GameMode.getTimeOfDay()
        self.go.daily:start()
        self.go.inter:start()
        self.go.night:start()
        if t < d[1] then
          self.go.daily:fadeOut(0)
          self.go.inter:fadeIn(0)
          self.go.night:fadeOut(0)
        elseif t < d[2] then
          self.go.daily:fadeIn((d[2]-t)*r)
          self.go.inter:fadeOut((d[2]-t)*r)
          self.go.night:fadeOut(0)
        elseif t < d[3] then
          self.go.daily:fadeIn(0)
          self.go.inter:fadeOut(0)
          self.go.night:fadeOut(0)
        elseif t < d[4] then
          self.go.daily:fadeOut((d[4]-t)*r)
          self.go.inter:fadeIn((d[4]-t)*r)
          self.go.night:fadeOut(0)
        elseif t < d[5] then
          self.go.daily:fadeOut(0)
          self.go.inter:fadeIn(0)
          self.go.night:fadeOut(0)
        elseif t < d[6] then
          self.go.daily:fadeOut(0)
          self.go.inter:fadeOut((d[6]-t)*r)
          self.go.night:fadeIn((d[6]-t)*r)
        elseif t < d[7] then
          self.go.daily:fadeOut(0)
          self.go.inter:fadeOut(0)
          self.go.night:fadeIn(0)
        else
          self.go.daily:fadeOut(0)
          self.go.inter:fadeIn((2-t)*r)
          self.go.night:fadeOut((2-t)*r)
        end
        print("time of day = "..t)
      end,
    },
  },
}
Even after I added the start() calls each frame, it does not prevent them from disappearing randomly. The goal is to hide the "black hole" of a normal sky with clouds to simulate a map with no ground. Do I use too much particles? When I tried with not as many, the black hole was still visible...

Edit: also, the particle systems ignore the fadeIn and fadeOut commands while the party is resting...
minmay
Posts: 2790
Joined: Mon Sep 23, 2013 2:24 am

Re: Particle color

Post by minmay »

First, your emitter definitions are odd - lifetimes of 100 to 500 seconds for each particle?
As for moving or mouse looking, I can think of four possibilities. Possibility 1: your farClip is still too low. Here's a fragment of a modding guide I'm working on:
SpoilerShow

Code: Select all

		-- I have added the default values for all the SkyComponent fields here.
		{
			class = "Sky",
			ambientIntensity = 1,
			-- farClip is two things.
			-- First, farClip is the scale of the sky model. I don't call
			-- it a skybox because it isn't a box, it's about 75% of a sphere.
			-- The model follows the party on the world x and z axes, but not
			-- on the y axis; it is possible for the party to ascend above
			-- the sky model or descend below it.
			-- The default sky model is about 75% of a sphere with a
			-- diameter of exactly 1, so the farClip will be equal to the
			-- diameter.
			--
			-- Second, farClip is the distance from the camera at which
			-- the sky will draw. Anything that is not within this distance
			-- from the party - even if the party has ascended above the sky
			-- model like I mentioned - is occluded, and replaced with the sky
			-- if the sky is "behind" it, or replaced with black otherwise.
			-- This means farClip can be used as a maximum draw distance.
			-- You may be wondering why I bother to distinguish between
			-- these two effects at all; aren't they the same as long as
			-- the party is inside the sky model, which they always should be?
			-- Well, no, and you'll find out why soon.
			--
			-- It's fine to change it dynamically. Remember, a 32x32 level
			-- is 96x96 meters.
			farClip = 130,

			-- There are four fog modes. "linear_lit" is the default, and
			-- it only extends to a distance of about 129.884297 meters;
			-- anything further from the camera than that will be
			-- unaffected by the fog. When this happens to a normal model,
			-- it's very ugly, but it has the advantage of allowing
			-- fog to be used without affecting the sky at all, so long as
			-- the sky's farClip is above approximately 129.884297.
			-- (Remember, farClip is "the distance from the party at
			-- which the sky will draw"; fog will *always* affect every
			-- pixel of the sky the same way, because the sky is always
			-- treated as being that distance away, even if parts of the
			-- sky model are actually closer or further away).
			--
			-- "dense" is the other one that most people know about, used
			-- in places like Keelbreach Bog.
			--
			-- But there are two more, overlooked fog modes:
			-- "linear" is the same as "linear_lit" except it continues
			-- beyond range 128.
			--
			-- "exp" seems to completely ignore fog range unless it's set
			-- to turn fog off entirely, and instead forces a very short
			-- range of about 3 meters; it is not useful as far as I can
			-- tell.
			--
			-- No matter which mode you choose, fog does not perform
			-- occlusion. Try farClip for that.
			fogMode = "linear_lit",

			-- The first number is the distance in meters from the camera
			-- at which the fog begins; the second is the distance at
			-- which it reaches 100% opacity.
			-- If you set both numbers to the same value, e.g.
			-- fogRange = {1,1}, then fog will be disabled entirely
			-- (unless the fog mode is "exp").
			-- Works backwards! If you set it to {8,1} then a halo of fog
			-- will follow the camera, with anything closer than 1 meter
			-- being fully fogged up, and anything more than 8 meters away
			-- being completely clear.
			-- Fine to change dynamically.
			fogRange = {5, 300},

			-- tonemapSaturation multiplies the saturation of all pixels
			-- in the 3d world (GUI elements are not affected). A value
			-- of 1 is the default, and doesn't change colours at all
			-- (since 1 is the multipilicative identity).
			--
			-- A value of 0 will make the world completely greyscale.
			--
			-- A sufficiently high value (such as math.huge) will make the
			-- world render with only the 3 primary colours at maximum
			-- brightness (and their combinations).
			--
			-- Negative values work, and will have the effect of inverting
			-- the hues on screen (because that's what happens when you
			-- multiply saturation by a negative number). So a
			-- tonemapSaturation of -1 would invert the
			-- hues of the pixels without changing their saturation.
			-- A tonemapSaturation of -2 looks just like an inverted
			-- version of tonemapSaturation = 2.
			--
			-- Finally, a tonemapSaturation of NaN will turn the entire 3D
			-- view completely black. This is probably not safe, and is
			-- completely useless in any case. If you want to turn the
			-- screen black, you should put a CameraComponent inside a
			-- black cube and point it outside the level; that way you
			-- will render as little as possible, no point in rendering
			-- things that won't be seen.
			--
			-- Fine to change dynamically.
			tonemapSaturation = 1,

			-- Okay, now one last thing. For both fog and farClip, the
			-- distance used isn't the real, visual, Euclidean distance
			-- between two points. Instead, it seems to be the Chebyshev
			-- distance, with the axes adopting the camera's rotation.
			--
			-- Along with fog, this is why I emphasized that farClip does
			-- two things.
			--
			-- Suppose the sky has a farClip of 100. Imagine a cube that
			-- is centered on the camera. Also imagine that this cube has
			-- the same rotation as the camera. Each edge of the cube is
			-- 200 meters long (radius of 100 meters).
			--
			-- Anything inside this cube is drawn. Anything outside this
			-- cube is replaced with the sky (or black).
			--
			-- So the sky's farClip actually has a different shape than
			-- the sky's spherical model. Fog is the same. This shape
			-- is also different from what you're used to seeing in the
			-- rest of the game and life in general (Euclidean distance).
			-- If you make your fog too noticeable (short range and/or
			-- extreme colours) the player will likely see the square
			-- shape of it.
		},
		{ -- LensFlareComponent has no fields or methods available. It just
		-- handles the lens flare that appears when you look at the sun.
		-- This is the only component on skies that is optional; you can
		-- completely remove it from the definition and the sky will still
		-- work without complaining (and, obviously, it will not have a lens
		-- flare).
			class = "LensFlare",
		},
	},
	placement = "floor",
	editorIcon = 100,
	reflectionMode = "always", -- This won't actually matter unless you added
		-- extra visual components. Skies ALWAYS get reflected.
}
That's the basics of skies. Now for some more stuff about skies.
Above, I used "camera" and "party" interchangeably. Generally a bad idea, since
the default camera doesn't even have the same position/rotation as the party
object does.
Here's the truth. If you use a custom camera instead of the default one,
SkyComponent doesn't really know what to do. The sky model still follows the
camera on the x and z axes, albeit with some jerkiness, but it does something
very strange with the y axis. The higher the camera's y position, the lower
the sky model's y position. It doesn't just remain stationary as the camera ascends
like it does for the default camera; the sky model actually moves downwards.
Furthermore, farClip doesn't work. It still scales the model, but no actual
clipping takes place; the sky will always be behind everything and never occlude
anything. And fog treats the sky as if it's 100 meters away, regardless of the
farClip value and (I believe) the model.

So, uh, watch out for that if you're using custom cameras I guess.
Possibility 2: the particles are getting occluded when they shouldn't, I don't think this is it, but if it is, you could move the ParticleComponents to the party object I guess?

Possibility 3: I've noticed some odd behaviour from maxParticles where it seems to delete particles instead of just not making new ones. Try using shorter lifetimes instead of clamping the number of particles with maxParticles.

Possibility 4: You're somehow generating too many particles with one emitter, which causes the emitter to disappear. I don't think this is what's happening unless maxParticles isn't working, because that limit is about 5000.



Have you considered using different textures for the sky instead? You can change all of the following textures in the MaterialEx:onUpdate() hook for day_sky and night_sky:
clouds0Map
clouds1Map
clouds2Map
clouds3Map
cloudsRim0Map
cloudsRim1Map
cloudsRim2Map
cloudsRim3Map
rayleighTex (needs to be passed with .dds extension instead of .tga)
mieTex (needs to be passed with .dds extension instead of .tga)

The default rayleighTex and mieTex are assets/atmosphere/rayleigh_scatter_opt.dds and assets/atmosphere/mie_scatter_opt.dds, which are 64x64x64 volume maps in D3DFMT_A16B16G16R16F. I think smaller volume maps should work fine, maybe other colour formats will work too.

You can also change the "starIntensity" and "time" params.
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Particle color

Post by AndakRainor »

Thanks, I will try that... But with textures only, how could the open black hole at the bottom of the sky be hidden? Unless the texture has a gradient to black... But clouds would be better under the party than this hole!
minmay
Posts: 2790
Joined: Mon Sep 23, 2013 2:24 am

Re: Particle color

Post by minmay »

AndakRainor wrote:Thanks, I will try that... But with textures only, how could the open black hole at the bottom of the sky be hidden? Unless the texture has a gradient to black... But clouds would be better under the party than this hole!
Oh, that's what you wanted to do. In that case, no need to involve custom textures or particles, just use a sky model that is a full sphere. Here is a symmetrical one; the lower hemisphere of the sky will be a mirror image of the upper hemisphere. It's easy to change the UVs if you want a different pattern.
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Particle color

Post by AndakRainor »

Thank you for the model, it looks better. Before giving up the particle components, a few more details about the twinkle/disappear issue:

- I used spawnBurst = true in the emitters, to get all the clouds as soon as the player enters the zone.
- From what I see, with spawnBurst = true, emissionRate is ignored and no more clouds are emitted unless I use ParticleComponent:restart()
- So when the lifetime of the clouds expires, the particles system simply disappears unless restart.
- I tested lifetime = {math.huge, math.huge}, with this value the emitters do not emit anything.
- During a transition, one particle components fades in and another fades out, that is when the twinkle effect can occur. For example the orange clouds can randomly disappear and reappear (all of them, it is not just a z-buffer problem for some clouds at the same place) when moving or mouse looking while the light blue clouds stay visible, which just screw the transition. They both have the same boxMin and boxMax.

I did not find a way to get a spawnBurst and an emissionRate to work with the same emitter, so taking advantage of both would require more emitters / components and a more complex code for the timer component. But as long as the twinkle effect is still not resolved, it would be useless to do it.

edit: also not sure at what point the fadeIn and fadeOut functions stop or start the emission (at the beginning or the end for fadeOut) when spawnBurst is false, but it is clear that a a faded out emitter is stopped (don't even know if its particles lifetime are also "freezed" during that time)
Post Reply