Skip to content

Commit 53dba1f

Browse files
authored
Merge pull request #29 from alt-romes/wip/romes/planets
- Mesh welding - Better texture support and CompatiblePixel - Planet demo now uses a texture generated at runtime Fixes #27
2 parents c6bf1a3 + d84a908 commit 53dba1f

21 files changed

Lines changed: 480 additions & 189 deletions

File tree

examples/ghengin-games.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ executable planets-core
141141
main-is: Main.hs
142142
other-modules: Planet, Planet.Noise, Planet.UI, Shaders
143143
hs-source-dirs: planets-core
144-
build-depends: ghengin:dear-imgui
144+
build-depends: ghengin:dear-imgui, JuicyPixels
145145

146146
-- Load and render teapot.obj with blinn-phong
147147
executable teapot-obj

examples/oscilloscope/Main.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ main = do
7070

7171
rp1 <- clearImages rp1
7272

73-
pipeline <- (makeRenderPipelineWith GPS{cullMode=CullBack, blendMode=BlendAdd} rp1 shaderPipelineSimple (DynamicBinding (Ur (InStruct 0)) :## GHNil) )
73+
pipeline <- (makeRenderPipelineWith defaultGraphicsPipelineSettings{cullMode=CullBack, blendMode=BlendAdd} rp1 shaderPipelineSimple (DynamicBinding (Ur (InStruct 0)) :## GHNil) )
7474
(rq, Ur pkey) <- pure (insertPipeline pipeline LMon.mempty)
7575

7676
rq <- gameLoop pkey rp2 rq

examples/planets-core/Main.hs

Lines changed: 82 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import Ghengin.Core.Render.Pipeline
4343
import Ghengin.Core.Render.Property
4444
import Ghengin.Vulkan.Renderer.Sampler
4545
import Ghengin.Core.Render.Queue
46+
import Ghengin.Core.Input
4647
import Ghengin.Core.Shader (StructVec3(..), StructMat4(..))
4748
import Vulkan.Core10.FundamentalTypes (Extent2D(..))
4849
import qualified Data.Monoid.Linear as LMon
@@ -53,6 +54,7 @@ import qualified Data.Linear.Alias as Alias
5354

5455
import Ghengin.Camera
5556
import Ghengin.Core.Type.Compatible
57+
import Ghengin.Core.Type.Compatible.Pixel
5658
import qualified Ghengin.DearImGui.Vulkan as ImGui
5759
import qualified Ghengin.DearImGui.UI as ImGui
5860

@@ -63,26 +65,50 @@ import Planet.Noise
6365
import Planet.UI
6466

6567
gameLoop :: Compatible '[Vec3, Vec3] '[Transform] '[MinMax, Texture2D (RGBA8 UNorm)] '[Camera "view_matrix" "proj_matrix"] π
66-
=> UTCTime
68+
=> CharStream
6769
-> _
68-
-- -> MeshKey π _ _ _ _
6970
-> MeshKey π '[Camera "view_matrix" "proj_matrix"] '[MinMax, Texture2D (RGBA8 UNorm)] '[Vec3, Vec3] '[Transform]
70-
-> _
71-
-> Alias RenderPass RenderQueue () Core (RenderQueue ())
72-
gameLoop currentTime planet mkey rot rp rq = Linear.do
71+
-> Alias RenderPass
72+
RenderQueue ()
73+
Core (RenderQueue ())
74+
gameLoop charStream planet mkey rp rq = Linear.do
7375
logT "New frame"
7476
should_close <- (shouldCloseWindow )
7577
if should_close then (Alias.forget rp ) >> return rq else Linear.do
7678
(pollWindowEvents )
7779

78-
Ur (newPlanet, changed) <- preparePlanetUI planet
79-
80-
Ur newTime <- liftSystemIOU getCurrentTime
81-
82-
-- Fix Your Timestep: A Very Hard Thing To Get Right. For now, the simplest approach:
83-
-- let frameTime = diffUTCTime newTime currentTime
84-
-- deltaTime = Prelude.min MAX_FRAME_TIME $ realToFrac frameTime
80+
-- Update planet mesh according to UI
81+
Ur (newPlanet, changedShape, changedColor) <- preparePlanetUI planet -- must happen before the first render
82+
rq <-
83+
if changedShape then
84+
(editAtMeshesKey mkey rq (\pipeline mat [(msh, x)] -> Linear.do
85+
( (pmesh, pipeline),
86+
Ur minmax ) <- newPlanetMesh pipeline newPlanet
87+
mat <- propertyAt @0 @MinMax (\(Ur _) -> pure (Ur minmax)) mat
88+
freeMesh msh
89+
return (pipeline, (mat, [(pmesh, x)]))
90+
) )
91+
else if changedColor then
92+
-- TODO: EditAtMaterialKey (materialKeyOfMeshKey)
93+
(editAtMeshesKey mkey rq (\pipeline mat [(msh, x)] -> Linear.do
94+
mat <- propertyAt @1 @_ (\tex -> Alias.forget tex >> planetTexture (planetColor newPlanet)) mat
95+
return (pipeline, (mat, [(msh, x)]))
96+
) )
97+
else
98+
pure rq
8599

100+
Ur mb_in <- readCharInput charStream
101+
rq <- case mb_in of
102+
Just ch -> Linear.do
103+
let doRotate 'd' = rotateY 0.01
104+
doRotate 'a' = rotateY (-0.01)
105+
doRotate 'w' = rotateX (0.01)
106+
doRotate 's' = rotateX (-0.01)
107+
doRotate _ = mempty
108+
(editMeshes mkey rq (traverse' $ propertyAt @0 (\(Ur tr) -> pure $ Ur $ doRotate ch <> tr)) )
109+
Nothing -> pure rq
110+
111+
-- Render!
86112
(rp, rq) <- renderWith $ Linear.do
87113

88114
(rp1, rp2) <- lift (Alias.share rp)
@@ -97,32 +123,20 @@ gameLoop currentTime planet mkey rot rp rq = Linear.do
97123

98124
return (rp2, rq)
99125

100-
101-
rq <-
102-
if changed then
103-
(editAtMeshesKey mkey rq (\pipeline mat [(msh, x)] -> Linear.do
104-
( (pmesh, pipeline),
105-
Ur minmax ) <- newPlanetMesh pipeline newPlanet
106-
mat' <- propertyAt @0 @MinMax (\(Ur _) -> pure (Ur minmax)) mat
107-
freeMesh msh
108-
return (pipeline, (mat', [(pmesh, x)]))
109-
) )
110-
else
111-
pure rq
112-
113-
-- rq <- (editMeshes mkey rq (traverse' $ propertyAt @0 (\(Ur tr) -> pure $ Ur $
114-
-- rotateY rot <> rotateX (-rot))) ↑)
115-
116126
-- Loop!
117-
gameLoop newTime newPlanet mkey (rot+(0.01::Float)) rp rq
127+
gameLoop charStream newPlanet mkey rp rq
128+
129+
dimensions :: Num a => (a, a)
130+
dimensions = (1920, 1080)
118131

119132
main :: Prelude.IO ()
120133
main = do
121-
currTime <- getCurrentTime
122134
withLinearIO $
123-
runCore (1280, 720) Linear.do
124-
sampler <- ( createSampler FILTER_NEAREST SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE )
125-
tex <- ( texture "examples/planets-core/assets/planet_gradient.png" sampler )
135+
runCore dimensions Linear.do
136+
Ur charStream <- registerCharStream
137+
138+
-- sampler <- ( createSampler FILTER_NEAREST SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE ↑)
139+
-- tex <- ( texture "examples/planets-core/assets/planet_gradient.png" sampler ↑)
126140

127141
(rp1, rp2) <- (Alias.share =<< createSimpleRenderPass )
128142

@@ -133,50 +147,66 @@ main = do
133147
-- todo: minmax should be per-mesh
134148
( (pmesh, pipeline),
135149
Ur minmax ) <- (newPlanetMesh pipeline defaultPlanet )
136-
(pmat, pipeline) <- (newPlanetMaterial minmax tex pipeline )
150+
(pmat, pipeline) <- (newPlanetMaterial minmax pipeline defaultPlanet )
137151

138152
-- remember to provide helper function in ghengin to insert meshes with pipelines and mats, without needing to do this:
139153
(rq, Ur pkey) <- pure (insertPipeline pipeline LMon.mempty)
140154
(rq, Ur mkey) <- pure (insertMaterial pkey pmat rq)
141155
(rq, Ur mshkey) <- pure (insertMesh mkey pmesh rq)
142156

143-
rq <- gameLoop currTime defaultPlanet mshkey 0 rp2 rq
157+
rq <- gameLoop charStream defaultPlanet mshkey rp2 rq
144158

145159
(freeRenderQueue rq )
146160
(ImGui.destroyImCtx imctx )
147161

148162
return (Ur ())
149163

150164
camera :: Camera "view_matrix" "proj_matrix"
151-
camera = cameraLookAt (vec3 0 0 (-5){- move camera "back"-}) (vec3 0 0 0) (1280, 720)
165+
camera = cameraLookAt (vec3 0 0 (-5){- move camera "back"-}) (vec3 0 0 0) dimensions
152166

153167
defaultPlanet :: Planet
154168
defaultPlanet = Planet
155-
{ resolution = 10
169+
{ resolution = 100
156170
, planetShape = PlanetShape
157-
{ planetRadius = 2.50
158-
, planetNoise = AddNoiseMasked
159-
[ StrengthenNoise 0.12 $ MinValueNoise
160-
{ minNoiseVal = 1.1
171+
{ planetRadius = 3
172+
, planetNoise = ImGui.Collapsible $ AddNoiseMasked
173+
[ StrengthenNoise 0.110 $ MinValueNoise
174+
{ minNoiseVal = 0.930
161175
, baseNoise = LayersCoherentNoise
162-
{ centre = ImGui.Color $ vec3 0 0 0
163-
, baseRoughness = 0.71
164-
, roughness = 1.83
165-
, numLayers = 5
166-
, persistence = 0.54
176+
-- { centre = ImGui.WithTooltip $ ImGui.Color $ vec3 0 0 0
177+
{ centre = ImGui.WithTooltip $ ImGui.Color $ vec3 255 147 0
178+
, baseRoughness = 1.5
179+
, roughness = 2.5
180+
, numLayers = 20
181+
, persistence = 0.4
167182
}
168183
}
169-
, StrengthenNoise 2.5 $ MinValueNoise
170-
{ minNoiseVal = 0
184+
, StrengthenNoise 5 $ MinValueNoise
185+
{ minNoiseVal = 0.120
171186
, baseNoise = RidgedNoise
172-
{ seed = 123
173-
, octaves = 5
174-
, scale = 1
187+
{ seed = 25
188+
, octaves = 10
189+
, scale = 0.59
175190
, frequency = 2
176-
, lacunarity = 3
191+
, lacunarity = 5.2
177192
}
178193
}
179194
]
180195
}
196+
, planetColor = PlanetColor
197+
{ planetColors =
198+
Prelude.map
199+
(\(bnd, WithVec3 rn gn bn) -> (ImGui.InRange bnd, ImGui.Color (vec3 (rn/255) (gn/255) (bn/255))))
200+
[ (1, vec3 0 83 255)
201+
, (2, vec3 255 218 0)
202+
, (5, vec3 255 120 0)
203+
, (10, vec3 60 255 0)
204+
, (20, vec3 27 183 0)
205+
, (30, vec3 10 163 0)
206+
, (40, vec3 158 37 0)
207+
, (85, vec3 108 13 0)
208+
, (100, vec3 231 231 231)
209+
]
210+
}
181211
}
182212

examples/planets-core/Planet.hs

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,23 @@ import qualified Math.Linear as FIR
3434
import Ghengin.DearImGui.UI
3535

3636
import Ghengin.Core.Shader.Data
37+
import Ghengin.Vulkan.Renderer.Sampler
38+
import Ghengin.Vulkan.Renderer.Texture
3739

3840
import Generics.SOP
41+
-- JuicyPixels
42+
import Codec.Picture
43+
import Codec.Picture.Types (promotePixel)
3944

4045
import Planet.Noise
4146

4247
--------------------------------------------------------------------------------
4348
-- * Planet
4449
--------------------------------------------------------------------------------
4550

46-
data Planet = Planet { resolution :: !(InRange 1 300 Int)
51+
data Planet = Planet { resolution :: !(InRange 2 512 Int)
4752
, planetShape :: !PlanetShape
53+
, planetColor :: !PlanetColor
4854
}
4955
deriving Eq
5056
deriving GHC.Generic
@@ -71,7 +77,7 @@ type PlanetMesh = Mesh '[Vec3, Vec3] '[Transform]
7177

7278
data PlanetShape = PlanetShape
7379
{ planetRadius :: !(InRange 0 100 Float)
74-
, planetNoise :: !Noise
80+
, planetNoise :: !(Collapsible "Noise section" Noise)
7581
}
7682
deriving Eq
7783
deriving GHC.Generic
@@ -85,7 +91,7 @@ data PlanetShape = PlanetShape
8591
-- Returns the updated point and the elevation of that point
8692
pointOnPlanet :: PlanetShape -> Vec3 -> (Vec3, Float)
8793
pointOnPlanet PlanetShape{..} pointOnUnitSphere =
88-
let elevation = evalNoise planetNoise pointOnUnitSphere
94+
let elevation = evalNoise (unCollapsible planetNoise) pointOnUnitSphere
8995
finalElevation = inRangeVal planetRadius * (1+elevation)
9096
in (pointOnUnitSphere ^* finalElevation, finalElevation)
9197

@@ -98,16 +104,17 @@ newPlanetMesh :: _ -- more constraints
98104
-> Renderer ((PlanetMesh, RenderPipeline π bs), Ur MinMax)
99105
newPlanetMesh rp Planet{..} = Linear.do
100106

101-
let UnitSphere us is = newUnitSphereFace False (inRangeVal resolution)
107+
let UnitSphere us is0 = newUnitSphere (inRangeVal resolution)
102108

103109
(planetPs, elevations)
104110
= V.unzip $ V.map (\(p :&: _) -> pointOnPlanet planetShape p) (V.convert us)
105111
planetNs = computeNormals (SV.map fromIntegral is) planetPs
106112
planetVs = V.zipWith (:&:) (planetPs) planetNs
113+
is = weldVertices planetPs (SV.map fromIntegral is0)
107114

108115
minmax = MinMax (P.minimum elevations) (P.maximum elevations)
109116

110-
in (, Ur minmax) <$> createMeshWithIxsSV rp (DynamicBinding (Ur mempty) :## GHNil) (V.convert planetVs) is
117+
in (, Ur minmax) <$> createMeshWithIxsSV rp (DynamicBinding (Ur mempty) :## GHNil) (V.convert planetVs) (SV.map fromIntegral is)
111118

112119
--------------------------------------------------------------------------------
113120
-- * Material
@@ -116,13 +123,38 @@ newPlanetMesh rp Planet{..} = Linear.do
116123
type PlanetMaterialAttrs = '[MinMax, Texture2D (RGBA8 UNorm)]
117124
type PlanetMaterial = Material PlanetMaterialAttrs
118125

126+
data PlanetColor = PlanetColor
127+
{ planetColors :: [(InRange 0 100 Float, Color)]
128+
-- ^ A list of a percentage value and the color to use up to that percentage
129+
}
130+
deriving Eq
131+
deriving GHC.Generic
132+
deriving anyclass Generic
133+
deriving anyclass HasDatatypeInfo
134+
deriving anyclass Widget
135+
deriving anyclass Default
136+
119137
newPlanetMaterial :: forall π p
120138
. CompatibleMaterial '[MinMax, Texture2D (RGBA8 UNorm)] π
121139
=> MinMax
122-
-> Alias (Texture2D (RGBA8 UNorm))
123-
RenderPipeline π p
124-
Renderer (PlanetMaterial, RenderPipeline π p)
125-
newPlanetMaterial mm t pl = material @_ @π (StaticBinding (Ur mm) :## Texture2DBinding t :## GHNil) pl
140+
-> RenderPipeline π p
141+
Planet
142+
-> Renderer (PlanetMaterial, RenderPipeline π p)
143+
newPlanetMaterial mm pl planet = Linear.do
144+
tex <- planetTexture (planetColor planet)
145+
material @_ @π (StaticBinding (Ur mm) :## Texture2DBinding tex :## GHNil) pl
146+
147+
-- | Make a Texture from the planet color
148+
planetTexture :: PlanetColor -> Renderer (Alias (Texture2D (RGBA8 UNorm)))
149+
planetTexture PlanetColor{planetColors} = Linear.do
150+
sampler <- createSampler FILTER_NEAREST SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE
151+
-- Generate a gradient image. Single pixel height, one pixel of a color per percent.
152+
let gradientImg = generateImage pixelPaint 100{-width=100%-} 100{-height=1 pixel-}
153+
pixelPaint x _ = case find (\(InRange limit, _) -> x < round limit) planetColors of
154+
Nothing -> PixelRGBA8 255 255 255 255
155+
Just (_, Color (WithVec3 r g b)) -> PixelRGBA8 (round (r*255)) (round (g*255)) (round (b*255)) 255
156+
liftSystemIO $ savePngImage "my_generated_gradient_texture.png" (ImageRGBA8 gradientImg)
157+
newTexture gradientImg sampler
126158

127159
--------------------------------------------------------------------------------
128160

examples/planets-core/Planet/Noise.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ data Noise
2525
--
2626
-- You can have simple coherent noise by having numLayers = 1
2727
= LayersCoherentNoise
28-
{ centre :: !Color
28+
{ centre :: !(WithTooltip "A seed" Color)
2929
-- Offset noise point
3030
, baseRoughness :: !Float
3131
-- ^ Roughness for the first layer
@@ -39,7 +39,7 @@ data Noise
3939
-- | Ridged multi-fractal noise
4040
| RidgedNoise
4141
{ seed :: !Int
42-
, octaves :: !Int
42+
, octaves :: !(InRange 0 64 Int)
4343
, scale :: !Double
4444
, frequency :: !Double
4545
, lacunarity :: !Double
@@ -82,15 +82,15 @@ evalNoise LayersCoherentNoise{..} p
8282
frequencies = baseRoughness : map (*roughness) frequencies
8383
amplitudes = 1 : map (*persistence) amplitudes
8484
layerNoise frequency amplitude =
85-
let v = coherentNoise seed (vec3Point (p ^* frequency ^+^ colorVec centre))
85+
let v = coherentNoise seed (vec3Point (p ^* frequency ^+^ (colorVec (unTooltip centre))))
8686
in (v + 1) * 0.5 * amplitude {-from [-1 to 1] to [0 to 1], then * amplitude-}
8787
noiseVal =
8888
sum $ take (inRangeVal numLayers) $
8989
zipWith layerNoise frequencies amplitudes
9090
evalNoise StrengthenNoise{..} p = evalNoise baseNoise p * strength
9191
evalNoise MinValueNoise{..} p = max 0 (evalNoise baseNoise p - minNoiseVal)
9292
evalNoise RidgedNoise{..} p = double2Float $
93-
Ridged.noiseValue (ridged seed octaves scale frequency lacunarity) (vec3Point p)
93+
Ridged.noiseValue (ridged seed (inRangeVal octaves) scale frequency lacunarity) (vec3Point p)
9494
evalNoise AddNoiseMasked{..} p =
9595
case NonEmpty.nonEmpty noiseLayers of
9696
Nothing -> 0

0 commit comments

Comments
 (0)