Incorporating flask element.
This commit is contained in:
157
elements/flaskRoundedBottom.go
Normal file
157
elements/flaskRoundedBottom.go
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package elements
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fluids/fluid"
|
||||||
|
"fluids/gamedata"
|
||||||
|
"fluids/resources"
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoundedBottomFlaskWidth = 32 //pixels
|
||||||
|
RoundedBottomFlaskHeight = 32 //pixels
|
||||||
|
RoundedBottomFlaskFluidRadius = 9 //pixels: represents spherical portion of the flask where fluid will be contained
|
||||||
|
RoundedBottomFlaskFluidOriginX = 16 //pixels: x origin of fluid area
|
||||||
|
RoundedBottomFlaskFluidOriginY = 20 //pixels: y origin of fluid area
|
||||||
|
RoundedBottomFlaskFluidWidth = 0.5 //meters
|
||||||
|
RoundedBottomFlaskFluidHeight = 0.5 //meters
|
||||||
|
RoundedBottomFlaskFluidResolution = 20
|
||||||
|
)
|
||||||
|
|
||||||
|
type RoundedBottomFlask struct {
|
||||||
|
MappedEntityBase
|
||||||
|
fluid *fluid.Fluid //our physical representation of the fluid
|
||||||
|
fluidbuff *ebiten.Image //predraw for the fluid
|
||||||
|
fluidcellbuff *ebiten.Image //persistent fluid sprite, we redraw this everywhere we want to represent fluid
|
||||||
|
flaskbase *ebiten.Image //flask background (container)
|
||||||
|
flaskhighlight *ebiten.Image //flask foreground (glassware highlights)
|
||||||
|
fieldscale gamedata.Vector //used for transforming from fluid-space to sprite-space
|
||||||
|
angle float64
|
||||||
|
|
||||||
|
//fluid color business
|
||||||
|
fluidcolor color.RGBA //premultiplied fluid color, as set by external sources
|
||||||
|
fluidcolorF []float32 //for caching of individual color values, we compute rarely
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRoundedBottomFlask() *RoundedBottomFlask {
|
||||||
|
flask := &RoundedBottomFlask{
|
||||||
|
fluidbuff: ebiten.NewImage(RoundedBottomFlaskFluidRadius*2, RoundedBottomFlaskFluidRadius*2),
|
||||||
|
fluidcellbuff: ebiten.NewImage(1, 1),
|
||||||
|
flaskbase: ebiten.NewImageFromImage(resources.ImageBank[resources.RoundedBottomFlaskBase]),
|
||||||
|
flaskhighlight: ebiten.NewImageFromImage(resources.ImageBank[resources.RoundedBottomFlaskHighlights]),
|
||||||
|
angle: 0,
|
||||||
|
fluidcolorF: make([]float32, 4), //one field each for R,G,B,A
|
||||||
|
}
|
||||||
|
flask.Initialize()
|
||||||
|
return flask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flask *RoundedBottomFlask) Initialize() {
|
||||||
|
//prepare our internal data
|
||||||
|
flask.dimensions = gamedata.Vector{
|
||||||
|
X: RoundedBottomFlaskWidth,
|
||||||
|
Y: RoundedBottomFlaskHeight,
|
||||||
|
}
|
||||||
|
flask.Sprite = ebiten.NewImage(RoundedBottomFlaskWidth, RoundedBottomFlaskHeight)
|
||||||
|
flask.fluidcellbuff.Fill(color.White)
|
||||||
|
|
||||||
|
//prepare and initialize the fluid
|
||||||
|
fluiddimensions := fluid.FieldVector{
|
||||||
|
X: RoundedBottomFlaskFluidWidth,
|
||||||
|
Y: RoundedBottomFlaskFluidHeight,
|
||||||
|
}
|
||||||
|
flask.fluid = fluid.NewFluid(fluiddimensions, RoundedBottomFlaskFluidHeight/RoundedBottomFlaskFluidResolution)
|
||||||
|
flask.fluid.Initialize()
|
||||||
|
flask.fluid.Block = false //rounded flask, not a rect volume
|
||||||
|
|
||||||
|
//compute fieldscale using newly created fluid
|
||||||
|
flask.fieldscale = gamedata.Vector{
|
||||||
|
X: RoundedBottomFlaskFluidRadius * 2 / float64(flask.fluid.Field.Nx-1),
|
||||||
|
Y: RoundedBottomFlaskFluidRadius * 2 / float64(flask.fluid.Field.Ny-1),
|
||||||
|
}
|
||||||
|
|
||||||
|
//setup default fluid color
|
||||||
|
flask.SetFluidColor(color.RGBA{R: 0x0, G: 0x0, B: 0xff, A: 0xff})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flask *RoundedBottomFlask) Update() {
|
||||||
|
if flask.paused {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
flask.fluid.Step()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flask *RoundedBottomFlask) Draw() {
|
||||||
|
flask.Sprite.Clear()
|
||||||
|
|
||||||
|
//render flask background
|
||||||
|
flask.Sprite.DrawImage(flask.flaskbase, nil)
|
||||||
|
|
||||||
|
//render fluid
|
||||||
|
flask.RenderFluid()
|
||||||
|
|
||||||
|
//render flask foreground
|
||||||
|
flask.Sprite.DrawImage(flask.flaskhighlight, nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flask *RoundedBottomFlask) SetAngle(angle float64) {
|
||||||
|
flask.angle = angle
|
||||||
|
flask.fluid.SetAngle(float32(flask.angle))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flask *RoundedBottomFlask) GetAngle() float64 {
|
||||||
|
return flask.angle
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flask *RoundedBottomFlask) RenderFluid() {
|
||||||
|
flask.fluidbuff.Clear()
|
||||||
|
|
||||||
|
//construct fluid buffer from fluid simulation
|
||||||
|
for i := range flask.fluid.Field.Nx {
|
||||||
|
for j := range flask.fluid.Field.Ny {
|
||||||
|
|
||||||
|
idx := i*flask.fluid.Field.Ny + j
|
||||||
|
|
||||||
|
if flask.fluid.Field.CellType[idx] != fluid.CellTypeFluid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
celldensity := flask.fluid.ParticleDensity[idx] / flask.fluid.ParticleRestDensity
|
||||||
|
/*if celldensity > 0.8 {
|
||||||
|
celldensity = 1
|
||||||
|
}*/
|
||||||
|
|
||||||
|
ox := float64(i) * flask.fieldscale.X
|
||||||
|
oy := float64(j) * flask.fieldscale.Y
|
||||||
|
op := &ebiten.DrawImageOptions{}
|
||||||
|
op.GeoM.Translate(-.5, -.5)
|
||||||
|
op.GeoM.Scale(flask.fieldscale.X, flask.fieldscale.Y)
|
||||||
|
op.GeoM.Translate(ox, oy)
|
||||||
|
op.ColorScale.ScaleAlpha(celldensity)
|
||||||
|
op.ColorScale.Scale(flask.fluidcolorF[0], flask.fluidcolorF[1], flask.fluidcolorF[2], flask.fluidcolorF[3])
|
||||||
|
// op.ColorM.Scale(0, 0, 1, 1)
|
||||||
|
//flask.Sprite.DrawImage(flask.fluidcellbuff, op)
|
||||||
|
flask.fluidbuff.DrawImage(flask.fluidcellbuff, op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//transform buffer for our flask space
|
||||||
|
op := &ebiten.DrawImageOptions{}
|
||||||
|
|
||||||
|
op.GeoM.Translate(-RoundedBottomFlaskFluidRadius, -RoundedBottomFlaskFluidRadius)
|
||||||
|
op.GeoM.Scale(1, -1)
|
||||||
|
op.GeoM.Translate(RoundedBottomFlaskFluidOriginX, RoundedBottomFlaskFluidOriginY)
|
||||||
|
flask.Sprite.DrawImage(flask.fluidbuff, op)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (flask *RoundedBottomFlask) SetFluidColor(c color.RGBA) {
|
||||||
|
flask.fluidcolor = c
|
||||||
|
flask.fluidcolorF[0] = float32(flask.fluidcolor.R) / float32(flask.fluidcolor.A)
|
||||||
|
flask.fluidcolorF[1] = float32(flask.fluidcolor.G) / float32(flask.fluidcolor.A)
|
||||||
|
flask.fluidcolorF[2] = float32(flask.fluidcolor.B) / float32(flask.fluidcolor.A)
|
||||||
|
flask.fluidcolorF[3] = float32(flask.fluidcolor.A) / 0xff
|
||||||
|
}
|
||||||
99
game/game.go
99
game/game.go
@@ -4,6 +4,7 @@ import (
|
|||||||
"fluids/elements"
|
"fluids/elements"
|
||||||
"fluids/gamedata"
|
"fluids/gamedata"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"image/color"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
@@ -16,6 +17,7 @@ const (
|
|||||||
GameHeight = 360
|
GameHeight = 360
|
||||||
GameFSDW = 200
|
GameFSDW = 200
|
||||||
GameFSDH = 100
|
GameFSDH = 100
|
||||||
|
GameSims = 3 //number of total sims
|
||||||
)
|
)
|
||||||
|
|
||||||
type Game struct {
|
type Game struct {
|
||||||
@@ -28,6 +30,7 @@ type Game struct {
|
|||||||
fluidsimd *elements.FluidSimD
|
fluidsimd *elements.FluidSimD
|
||||||
fluidsim10 []*elements.FluidSim10
|
fluidsim10 []*elements.FluidSim10
|
||||||
alertbox *elements.Alert
|
alertbox *elements.Alert
|
||||||
|
flask *elements.RoundedBottomFlask
|
||||||
|
|
||||||
//cache elements
|
//cache elements
|
||||||
fluidsim10width float64
|
fluidsim10width float64
|
||||||
@@ -37,6 +40,11 @@ type Game struct {
|
|||||||
fluidsim10angle float64 //purely for debugging
|
fluidsim10angle float64 //purely for debugging
|
||||||
mdown bool
|
mdown bool
|
||||||
mdx, mdy int
|
mdx, mdy int
|
||||||
|
mdangle float64 //angle at mousedown
|
||||||
|
mfangle float64 //last flask angle
|
||||||
|
|
||||||
|
//colors
|
||||||
|
flaskcolor color.RGBA
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGame() *Game {
|
func NewGame() *Game {
|
||||||
@@ -45,6 +53,7 @@ func NewGame() *Game {
|
|||||||
fluidsimidx: 0,
|
fluidsimidx: 0,
|
||||||
alertbox: elements.NewAlert(),
|
alertbox: elements.NewAlert(),
|
||||||
fluidsimd: elements.NewFluidSimD(),
|
fluidsimd: elements.NewFluidSimD(),
|
||||||
|
flask: elements.NewRoundedBottomFlask(),
|
||||||
//fluidsim10: elements.NewFluidSim10(gamedata.Vector{X: GameFSDW, Y: GameFSDH}),
|
//fluidsim10: elements.NewFluidSim10(gamedata.Vector{X: GameFSDW, Y: GameFSDH}),
|
||||||
//fluidsimgpt: elements.NewFlipFluidEntity(640, 480, 2, 1, 100),
|
//fluidsimgpt: elements.NewFlipFluidEntity(640, 480, 2, 1, 100),
|
||||||
|
|
||||||
@@ -66,6 +75,8 @@ func (g *Game) Update() error {
|
|||||||
case 1:
|
case 1:
|
||||||
//g.fluidsimgpt.Update()
|
//g.fluidsimgpt.Update()
|
||||||
g.UpdateFluidsim10()
|
g.UpdateFluidsim10()
|
||||||
|
case 2:
|
||||||
|
g.UpdateFlask()
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -90,6 +101,8 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
|||||||
g.RenderFluidSimD(screen)
|
g.RenderFluidSimD(screen)
|
||||||
case 1:
|
case 1:
|
||||||
g.RenderFluidSim10(screen)
|
g.RenderFluidSim10(screen)
|
||||||
|
case 2:
|
||||||
|
g.RenderFlask(screen)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -144,8 +157,9 @@ func (g *Game) ParseInputs() {
|
|||||||
case 0:
|
case 0:
|
||||||
g.ManageFluidSimDInputs()
|
g.ManageFluidSimDInputs()
|
||||||
case 1:
|
case 1:
|
||||||
//g.ManageFluidSimGPTInputs()
|
|
||||||
g.ManageFluidSim10Inputs()
|
g.ManageFluidSim10Inputs()
|
||||||
|
case 2:
|
||||||
|
g.ManageFlaskInputs()
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -159,13 +173,13 @@ func (g *Game) ParseInputs() {
|
|||||||
|
|
||||||
//swap fluid simulations
|
//swap fluid simulations
|
||||||
if inpututil.IsKeyJustPressed(ebiten.KeyPageUp) {
|
if inpututil.IsKeyJustPressed(ebiten.KeyPageUp) {
|
||||||
g.fluidsimidx = (g.fluidsimidx + 1) % 2
|
g.fluidsimidx = (g.fluidsimidx + 1) % GameSims
|
||||||
}
|
}
|
||||||
|
|
||||||
if inpututil.IsKeyJustPressed(ebiten.KeyPageDown) {
|
if inpututil.IsKeyJustPressed(ebiten.KeyPageDown) {
|
||||||
g.fluidsimidx = g.fluidsimidx - 1
|
g.fluidsimidx = g.fluidsimidx - 1
|
||||||
if g.fluidsimidx < 0 {
|
if g.fluidsimidx < 0 {
|
||||||
g.fluidsimidx = 1
|
g.fluidsimidx = GameSims - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,9 +264,9 @@ func (g *Game) ManageFluidSim10Inputs() {
|
|||||||
|
|
||||||
func (g *Game) Initialize() {
|
func (g *Game) Initialize() {
|
||||||
|
|
||||||
|
//10MP Fluid Simulation Initialization
|
||||||
g.fluidsim10 = append(g.fluidsim10, elements.NewFluidSim10())
|
g.fluidsim10 = append(g.fluidsim10, elements.NewFluidSim10())
|
||||||
g.fluidsim10 = append(g.fluidsim10, elements.NewFluidSim10())
|
g.fluidsim10 = append(g.fluidsim10, elements.NewFluidSim10())
|
||||||
//g.fluidsim10 = append(g.fluidsim10, elements.NewFluidSim10())
|
|
||||||
|
|
||||||
g.fluidsim10width = float64(g.fluidsim10[0].GetSprite().Bounds().Dx())
|
g.fluidsim10width = float64(g.fluidsim10[0].GetSprite().Bounds().Dx())
|
||||||
g.fluidsim10height = float64(g.fluidsim10[0].GetSprite().Bounds().Dy())
|
g.fluidsim10height = float64(g.fluidsim10[0].GetSprite().Bounds().Dy())
|
||||||
@@ -268,4 +282,81 @@ func (g *Game) Initialize() {
|
|||||||
sim.SetPosition(pos)
|
sim.SetPosition(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Flask Initialization
|
||||||
|
pos := gamedata.Vector{
|
||||||
|
X: GameWidth / 2,
|
||||||
|
Y: GameHeight / 2,
|
||||||
|
}
|
||||||
|
g.flask.SetPosition(pos)
|
||||||
|
g.flaskcolor = color.RGBA{R: 0xff, G: 0x00, B: 0x00, A: 0xff}
|
||||||
|
g.flask.SetFluidColor(g.flaskcolor)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) ManageFlaskInputs() {
|
||||||
|
if inpututil.IsKeyJustPressed(ebiten.KeyR) {
|
||||||
|
g.flask.Initialize()
|
||||||
|
g.mfangle = 0
|
||||||
|
g.flask.SetAngle(g.mfangle)
|
||||||
|
g.flask.SetFluidColor(g.flaskcolor)
|
||||||
|
}
|
||||||
|
|
||||||
|
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
|
||||||
|
//angle at mouse down is the zero angle
|
||||||
|
g.mdown = true
|
||||||
|
g.mdx, g.mdy = ebiten.CursorPosition()
|
||||||
|
|
||||||
|
dx := float64(g.mdx) - g.flask.GetPosition().X
|
||||||
|
dy := float64(g.mdy) - g.flask.GetPosition().Y
|
||||||
|
g.mdangle = math.Atan2(dy, dx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
|
||||||
|
|
||||||
|
if g.mdown {
|
||||||
|
mx, my := ebiten.CursorPosition()
|
||||||
|
|
||||||
|
dx := float64(mx) - g.flask.GetPosition().X
|
||||||
|
dy := float64(my) - g.flask.GetPosition().Y
|
||||||
|
angle := math.Atan2(dy, dx)
|
||||||
|
|
||||||
|
g.flask.SetAngle(g.mfangle + (angle - g.mdangle))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if inpututil.IsMouseButtonJustReleased(ebiten.MouseButtonLeft) {
|
||||||
|
g.mdown = false
|
||||||
|
|
||||||
|
mx, my := ebiten.CursorPosition()
|
||||||
|
|
||||||
|
dx := float64(mx) - g.flask.GetPosition().X
|
||||||
|
dy := float64(my) - g.flask.GetPosition().Y
|
||||||
|
angle := math.Atan2(dy, dx)
|
||||||
|
|
||||||
|
g.mfangle = g.mfangle + (angle - g.mdangle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) UpdateFlask() {
|
||||||
|
g.flask.Update()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) RenderFlask(img *ebiten.Image) {
|
||||||
|
|
||||||
|
g.flask.Draw()
|
||||||
|
|
||||||
|
angle := g.flask.GetAngle()
|
||||||
|
dim := g.flask.GetDimensions()
|
||||||
|
|
||||||
|
// //TODO: use flask position for rendering, not screen
|
||||||
|
pos := g.flask.GetPosition()
|
||||||
|
|
||||||
|
op := &ebiten.DrawImageOptions{}
|
||||||
|
op.GeoM.Translate(-dim.X/2, -dim.Y/2)
|
||||||
|
op.GeoM.Rotate(angle)
|
||||||
|
op.GeoM.Scale(2, 2)
|
||||||
|
op.GeoM.Translate(pos.X, pos.Y)
|
||||||
|
|
||||||
|
img.DrawImage(g.flask.GetSprite(), op)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
5
main.go
5
main.go
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fluids/game"
|
"fluids/game"
|
||||||
|
"fluids/resources"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
@@ -16,6 +17,10 @@ const (
|
|||||||
func main() {
|
func main() {
|
||||||
fmt.Println("fluid experiments")
|
fmt.Println("fluid experiments")
|
||||||
|
|
||||||
|
//preload assets
|
||||||
|
resources.LoadImages()
|
||||||
|
|
||||||
|
//initialize new game instance
|
||||||
g := game.NewGame()
|
g := game.NewGame()
|
||||||
|
|
||||||
ebiten.SetWindowTitle("fluids")
|
ebiten.SetWindowTitle("fluids")
|
||||||
|
|||||||
41
resources/images.go
Normal file
41
resources/images.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package resources
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"image"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ImageName string
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoundedBottomFlaskBase ImageName = "RoundedBottomFlaskBase"
|
||||||
|
RoundedBottomFlaskHighlights ImageName = "RoundedBottomFlaskHighlights"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ImageBank map[ImageName]*ebiten.Image
|
||||||
|
|
||||||
|
//go:embed rounded_bottom_flask_base.png
|
||||||
|
rounded_bottom_flask_base []byte
|
||||||
|
//go:embed rounded_bottom_flask_highlights.png
|
||||||
|
rounded_bottom_flask_highlitsh []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func LoadImages() {
|
||||||
|
ImageBank = make(map[ImageName]*ebiten.Image)
|
||||||
|
ImageBank[RoundedBottomFlaskBase] = LoadImagesFatal(rounded_bottom_flask_base)
|
||||||
|
ImageBank[RoundedBottomFlaskHighlights] = LoadImagesFatal(rounded_bottom_flask_highlitsh)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadImagesFatal(b []byte) *ebiten.Image {
|
||||||
|
img, _, err := image.Decode(bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return ebiten.NewImageFromImage(img)
|
||||||
|
}
|
||||||
BIN
resources/rounded_bottom_flask_base.png
Normal file
BIN
resources/rounded_bottom_flask_base.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 302 B |
BIN
resources/rounded_bottom_flask_highlights.png
Normal file
BIN
resources/rounded_bottom_flask_highlights.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 137 B |
Reference in New Issue
Block a user