214 lines
4.4 KiB
Go
214 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"math"
|
|
"os"
|
|
"time"
|
|
|
|
_ "image/jpeg"
|
|
_ "image/png"
|
|
|
|
"github.com/faiface/pixel"
|
|
"github.com/faiface/pixel/imdraw"
|
|
"github.com/faiface/pixel/pixelgl"
|
|
)
|
|
|
|
func loadPicture(path string) (pixel.Picture, error) {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
img, _, err := image.Decode(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return pixel.PictureDataFromImage(img), nil
|
|
}
|
|
|
|
type drawer interface {
|
|
Draw(pixel.Target)
|
|
}
|
|
|
|
type colorlight struct {
|
|
color pixel.RGBA
|
|
point pixel.Vec
|
|
angle float64
|
|
radius float64
|
|
dust float64
|
|
|
|
spread float64
|
|
|
|
imd *imdraw.IMDraw
|
|
}
|
|
|
|
func (cl *colorlight) apply(src, noise drawer, dst pixel.ComposeTarget) {
|
|
// create the light arc if not created already
|
|
if cl.imd == nil {
|
|
imd := imdraw.New(nil)
|
|
imd.Color(pixel.Alpha(1))
|
|
imd.Push(0)
|
|
imd.Color(pixel.Alpha(0))
|
|
for angle := -cl.spread / 2; angle <= cl.spread/2; angle += cl.spread / 64 {
|
|
imd.Push(pixel.X(1).Rotated(angle))
|
|
}
|
|
imd.Polygon(0)
|
|
cl.imd = imd
|
|
}
|
|
|
|
// draw the light arc
|
|
dst.SetMatrix(pixel.IM.Scaled(0, cl.radius).Rotated(0, cl.angle).Moved(cl.point))
|
|
dst.SetColorMask(pixel.Alpha(1))
|
|
dst.SetComposeMethod(pixel.ComposePlus)
|
|
cl.imd.Draw(dst)
|
|
|
|
// draw the noise inside the light
|
|
dst.SetMatrix(pixel.IM)
|
|
dst.SetComposeMethod(pixel.ComposeIn)
|
|
noise.Draw(dst)
|
|
|
|
// draw an image inside the noisy light
|
|
dst.SetColorMask(cl.color)
|
|
dst.SetComposeMethod(pixel.ComposeIn)
|
|
src.Draw(dst)
|
|
|
|
// draw the light reflected from the dust
|
|
dst.SetMatrix(pixel.IM.Scaled(0, cl.radius).Rotated(0, cl.angle).Moved(cl.point))
|
|
dst.SetColorMask(cl.color.Mul(pixel.Alpha(cl.dust)))
|
|
dst.SetComposeMethod(pixel.ComposePlus)
|
|
cl.imd.Draw(dst)
|
|
}
|
|
|
|
func run() {
|
|
pandaPic, err := loadPicture("panda.png")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
noisePic, err := loadPicture("noise.png")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
cfg := pixelgl.WindowConfig{
|
|
Title: "Lights",
|
|
Bounds: pixel.R(0, 0, 1024, 768),
|
|
VSync: true,
|
|
}
|
|
win, err := pixelgl.NewWindow(cfg)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
panda := pixel.NewSprite(pandaPic, pandaPic.Bounds())
|
|
panda.SetMatrix(pixel.IM.Moved(win.Bounds().Center()))
|
|
noise := pixel.NewSprite(noisePic, noisePic.Bounds())
|
|
noise.SetMatrix(pixel.IM.Moved(win.Bounds().Center()))
|
|
|
|
colors := []pixel.RGBA{
|
|
pixel.RGB(1, 0, 0),
|
|
pixel.RGB(0, 1, 0),
|
|
pixel.RGB(0, 0, 1),
|
|
pixel.RGB(1/math.Sqrt2, 1/math.Sqrt2, 0),
|
|
}
|
|
|
|
points := []pixel.Vec{
|
|
pixel.V(win.Bounds().Min.X(), win.Bounds().Min.Y()),
|
|
pixel.V(win.Bounds().Max.X(), win.Bounds().Min.Y()),
|
|
pixel.V(win.Bounds().Max.X(), win.Bounds().Max.Y()),
|
|
pixel.V(win.Bounds().Min.X(), win.Bounds().Max.Y()),
|
|
}
|
|
|
|
angles := []float64{
|
|
math.Pi / 4,
|
|
math.Pi/4 + math.Pi/2,
|
|
math.Pi/4 + 2*math.Pi/2,
|
|
math.Pi/4 + 3*math.Pi/2,
|
|
}
|
|
|
|
lights := make([]colorlight, 4)
|
|
for i := range lights {
|
|
lights[i] = colorlight{
|
|
color: colors[i],
|
|
point: points[i],
|
|
angle: angles[i],
|
|
radius: 800,
|
|
dust: 0.3,
|
|
spread: math.Pi / math.E,
|
|
}
|
|
}
|
|
|
|
speed := []float64{11.0 / 23, 13.0 / 23, 17.0 / 23, 19.0 / 23}
|
|
|
|
oneLight := pixelgl.NewCanvas(win.Bounds())
|
|
allLight := pixelgl.NewCanvas(win.Bounds())
|
|
|
|
var (
|
|
frames = 0
|
|
second = time.Tick(time.Second)
|
|
fps30 = time.Tick(time.Second / 30)
|
|
)
|
|
|
|
start := time.Now()
|
|
for !win.Closed() {
|
|
if win.Pressed(pixelgl.KeyW) {
|
|
for i := range lights {
|
|
lights[i].dust += 0.05
|
|
if lights[i].dust > 1 {
|
|
lights[i].dust = 1
|
|
}
|
|
}
|
|
}
|
|
if win.Pressed(pixelgl.KeyS) {
|
|
for i := range lights {
|
|
lights[i].dust -= 0.05
|
|
if lights[i].dust < 0 {
|
|
lights[i].dust = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
since := time.Since(start).Seconds()
|
|
for i := range lights {
|
|
lights[i].angle = angles[i] + math.Sin(since*speed[i])*math.Pi/8
|
|
}
|
|
|
|
win.Clear(pixel.RGB(0, 0, 0))
|
|
|
|
// draw the panda visible outside the light
|
|
win.SetColorMask(pixel.Alpha(0.4))
|
|
win.SetComposeMethod(pixel.ComposePlus)
|
|
panda.Draw(win)
|
|
|
|
allLight.Clear(pixel.Alpha(0))
|
|
allLight.SetComposeMethod(pixel.ComposePlus)
|
|
|
|
// accumulate all the lights
|
|
for i := range lights {
|
|
oneLight.Clear(pixel.Alpha(0))
|
|
lights[i].apply(panda, noise, oneLight)
|
|
oneLight.Draw(allLight)
|
|
}
|
|
|
|
// compose the final result
|
|
win.SetColorMask(pixel.Alpha(1))
|
|
allLight.Draw(win)
|
|
|
|
win.Update()
|
|
|
|
<-fps30 // maintain 30 fps, because my computer couldn't handle 60 here
|
|
frames++
|
|
select {
|
|
case <-second:
|
|
win.SetTitle(fmt.Sprintf("%s | FPS: %d", cfg.Title, frames))
|
|
frames = 0
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
pixelgl.Run(run)
|
|
}
|