mirror of https://github.com/SolarLune/tetra3d.git
Adding LightGroups.
LightGroups exist to light Models using specific Lights, rather than all lights in a Scene. Fixing Lighting blend file's textures (the blend file pointed to an atlas rather than individual textures). Adding LightGroups lighting example.CacheOptimizations2
parent
50c6e5020f
commit
1e435319c2
20
camera.go
20
camera.go
|
@ -602,23 +602,24 @@ func (camera *Camera) Render(scene *Scene, models ...*Model) {
|
|||
|
||||
frametimeStart := time.Now()
|
||||
|
||||
lights := []ILight{}
|
||||
sceneLights := []ILight{}
|
||||
lights := sceneLights
|
||||
|
||||
if scene.World != nil && scene.World.LightingOn {
|
||||
if scene.World == nil || scene.World.LightingOn {
|
||||
|
||||
for _, l := range scene.Root.ChildrenRecursive() {
|
||||
if light, isLight := l.(ILight); isLight {
|
||||
camera.DebugInfo.LightCount++
|
||||
if light.IsOn() {
|
||||
lights = append(lights, light)
|
||||
sceneLights = append(sceneLights, light)
|
||||
light.beginRender()
|
||||
camera.DebugInfo.ActiveLightCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if scene.World.AmbientLight != nil && scene.World.AmbientLight.IsOn() {
|
||||
lights = append(lights, scene.World.AmbientLight)
|
||||
if scene.World != nil && scene.World.AmbientLight != nil && scene.World.AmbientLight.IsOn() {
|
||||
sceneLights = append(sceneLights, scene.World.AmbientLight)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -807,6 +808,15 @@ func (camera *Camera) Render(scene *Scene, models ...*Model) {
|
|||
|
||||
t := time.Now()
|
||||
|
||||
lights = sceneLights
|
||||
|
||||
if model.LightGroup != nil && model.LightGroup.Active {
|
||||
lights = model.LightGroup.Lights
|
||||
for _, l := range model.LightGroup.Lights {
|
||||
l.beginRender() // Call this because it's relatively cheap and necessary if a light doesn't exist in the Scene
|
||||
}
|
||||
}
|
||||
|
||||
for _, light := range lights {
|
||||
light.beginModel(model)
|
||||
}
|
||||
|
|
|
@ -308,7 +308,7 @@ func (g *Game) Layout(w, h int) (int, int) {
|
|||
}
|
||||
|
||||
func main() {
|
||||
ebiten.SetWindowTitle("Tetra3d - Lighting Test")
|
||||
ebiten.SetWindowTitle("Tetra3d - Baked Lighting Test")
|
||||
ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
|
||||
|
||||
game := NewGame()
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 459 B |
Binary file not shown.
After Width: | Height: | Size: 670 B |
Binary file not shown.
After Width: | Height: | Size: 350 B |
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,252 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"math"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
"time"
|
||||
|
||||
_ "embed"
|
||||
|
||||
"github.com/kvartborg/vector"
|
||||
"github.com/solarlune/tetra3d"
|
||||
"github.com/solarlune/tetra3d/colors"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
)
|
||||
|
||||
//go:embed lighting.gltf
|
||||
var gltfData []byte
|
||||
|
||||
type Game struct {
|
||||
Width, Height int
|
||||
Library *tetra3d.Library
|
||||
Scene *tetra3d.Scene
|
||||
|
||||
Camera *tetra3d.Camera
|
||||
CameraTilt float64
|
||||
CameraRotate float64
|
||||
|
||||
DrawDebugText bool
|
||||
DrawDebugDepth bool
|
||||
PrevMousePosition vector.Vector
|
||||
|
||||
Time float64
|
||||
}
|
||||
|
||||
func NewGame() *Game {
|
||||
game := &Game{
|
||||
Width: 796,
|
||||
Height: 448,
|
||||
PrevMousePosition: vector.Vector{},
|
||||
}
|
||||
|
||||
game.Init()
|
||||
|
||||
return game
|
||||
}
|
||||
|
||||
func (g *Game) Init() {
|
||||
|
||||
opt := tetra3d.DefaultGLTFLoadOptions()
|
||||
opt.CameraWidth = g.Width
|
||||
opt.CameraHeight = g.Height
|
||||
library, err := tetra3d.LoadGLTFData(gltfData, opt)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
g.Library = library
|
||||
g.Scene = library.Scenes[0]
|
||||
|
||||
g.Camera = tetra3d.NewCamera(g.Width, g.Height)
|
||||
g.Camera.SetLocalPosition(vector.Vector{0, 2, 15})
|
||||
g.Scene.Root.AddChildren(g.Camera)
|
||||
|
||||
gt := g.Scene.Root.Get("OnlyGreen").(*tetra3d.Model)
|
||||
gt.LightGroup = tetra3d.NewLightGroup(g.Scene.Root.Get("GreenLight").(*tetra3d.PointLight))
|
||||
|
||||
rt := g.Scene.Root.Get("OnlyRed").(*tetra3d.Model)
|
||||
rt.LightGroup = tetra3d.NewLightGroup(g.Scene.Root.Get("RedLight").(*tetra3d.PointLight))
|
||||
|
||||
ebiten.SetCursorMode(ebiten.CursorModeCaptured)
|
||||
|
||||
}
|
||||
|
||||
func (g *Game) Update() error {
|
||||
|
||||
g.Time += 1.0 / 60.0
|
||||
|
||||
var err error
|
||||
|
||||
moveSpd := 0.05
|
||||
|
||||
if ebiten.IsKeyPressed(ebiten.KeyEscape) {
|
||||
err = errors.New("quit")
|
||||
}
|
||||
|
||||
// Moving the Camera
|
||||
|
||||
// We use Camera.Rotation.Forward().Invert() because the camera looks down -Z (so its forward vector is inverted)
|
||||
forward := g.Camera.LocalRotation().Forward().Invert()
|
||||
right := g.Camera.LocalRotation().Right()
|
||||
|
||||
pos := g.Camera.LocalPosition()
|
||||
|
||||
if ebiten.IsKeyPressed(ebiten.KeyW) {
|
||||
pos = pos.Add(forward.Scale(moveSpd))
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyD) {
|
||||
pos = pos.Add(right.Scale(moveSpd))
|
||||
}
|
||||
|
||||
if ebiten.IsKeyPressed(ebiten.KeyS) {
|
||||
pos = pos.Add(forward.Scale(-moveSpd))
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyA) {
|
||||
pos = pos.Add(right.Scale(-moveSpd))
|
||||
}
|
||||
|
||||
if ebiten.IsKeyPressed(ebiten.KeySpace) {
|
||||
pos[1] += moveSpd
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyControl) {
|
||||
pos[1] -= moveSpd
|
||||
}
|
||||
|
||||
g.Camera.SetLocalPosition(pos)
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyF4) {
|
||||
ebiten.SetFullscreen(!ebiten.IsFullscreen())
|
||||
}
|
||||
|
||||
armature := g.Scene.Root.Get("Armature")
|
||||
|
||||
if ebiten.IsKeyPressed(ebiten.KeyLeft) {
|
||||
armature.Move(-0.1, 0, 0)
|
||||
} else if ebiten.IsKeyPressed(ebiten.KeyRight) {
|
||||
armature.Move(0.1, 0, 0)
|
||||
}
|
||||
|
||||
// Rotating the camera with the mouse
|
||||
|
||||
// Rotate and tilt the camera according to mouse movements
|
||||
mx, my := ebiten.CursorPosition()
|
||||
|
||||
mv := vector.Vector{float64(mx), float64(my)}
|
||||
|
||||
diff := mv.Sub(g.PrevMousePosition)
|
||||
|
||||
g.CameraTilt -= diff[1] * 0.005
|
||||
g.CameraRotate -= diff[0] * 0.005
|
||||
|
||||
g.CameraTilt = math.Max(math.Min(g.CameraTilt, math.Pi/2-0.1), -math.Pi/2+0.1)
|
||||
|
||||
tilt := tetra3d.NewMatrix4Rotate(1, 0, 0, g.CameraTilt)
|
||||
rotate := tetra3d.NewMatrix4Rotate(0, 1, 0, g.CameraRotate)
|
||||
|
||||
// Order of this is important - tilt * rotate works, rotate * tilt does not, lol
|
||||
g.Camera.SetLocalRotation(tilt.Mult(rotate))
|
||||
|
||||
g.PrevMousePosition = mv.Clone()
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyF12) {
|
||||
f, err := os.Create("screenshot" + time.Now().Format("2006-01-02 15:04:05") + ".png")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
defer f.Close()
|
||||
png.Encode(f, g.Camera.ColorTexture())
|
||||
}
|
||||
|
||||
if ebiten.IsKeyPressed(ebiten.KeyR) {
|
||||
g.Init()
|
||||
}
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyP) {
|
||||
g.StartProfiling()
|
||||
}
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyF1) {
|
||||
g.DrawDebugText = !g.DrawDebugText
|
||||
}
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyF5) {
|
||||
g.DrawDebugDepth = !g.DrawDebugDepth
|
||||
}
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.Key1) {
|
||||
green := g.Scene.Root.Get("OnlyGreen").(*tetra3d.Model)
|
||||
green.LightGroup.Active = !green.LightGroup.Active
|
||||
|
||||
red := g.Scene.Root.Get("OnlyRed").(*tetra3d.Model)
|
||||
red.LightGroup.Active = !red.LightGroup.Active
|
||||
}
|
||||
|
||||
if inpututil.IsKeyJustPressed(ebiten.Key2) {
|
||||
g.Scene.World.LightingOn = !g.Scene.World.LightingOn
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
|
||||
// Clear, but with a color - we can use the world lighting color for this.
|
||||
screen.Fill(g.Scene.World.ClearColor.ToRGBA64())
|
||||
|
||||
// Clear the Camera
|
||||
g.Camera.Clear()
|
||||
|
||||
// Render the scene
|
||||
g.Camera.RenderNodes(g.Scene, g.Scene.Root)
|
||||
|
||||
// We rescale the depth or color textures here just in case we render at a different resolution than the window's; this isn't necessary,
|
||||
// we could just draw the images straight.
|
||||
if g.DrawDebugDepth {
|
||||
screen.DrawImage(g.Camera.DepthTexture(), nil)
|
||||
} else {
|
||||
screen.DrawImage(g.Camera.ColorTexture(), nil)
|
||||
}
|
||||
|
||||
if g.DrawDebugText {
|
||||
g.Camera.DrawDebugRenderInfo(screen, 1, colors.White())
|
||||
txt := "F1 to toggle this text\nWASD: Move, Mouse: Look\n\nThis example shows light groups.\nLight groups allow you to control\nwhich lights influence Models\nwhile having them remain available for others.\n\nThere's two point lights in this scene - a red one\nand a green one. The left cube is only\nlit by the green light, while the right\nis only lit by the red one.\n\n1 Key: Toggle light groups being active\n2 Key: Toggle all lighting\nF5: Toggle depth debug view\nF4: Toggle fullscreen\nESC: Quit"
|
||||
g.Camera.DebugDrawText(screen, txt, 0, 150, 1, colors.LightGray())
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Game) StartProfiling() {
|
||||
outFile, err := os.Create("./cpu.pprof")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Beginning CPU profiling...")
|
||||
pprof.StartCPUProfile(outFile)
|
||||
go func() {
|
||||
time.Sleep(2 * time.Second)
|
||||
pprof.StopCPUProfile()
|
||||
fmt.Println("CPU profiling finished.")
|
||||
}()
|
||||
}
|
||||
|
||||
func (g *Game) Layout(w, h int) (int, int) {
|
||||
return g.Width, g.Height
|
||||
}
|
||||
|
||||
func main() {
|
||||
ebiten.SetWindowTitle("Tetra3d - LightGroup Test")
|
||||
ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
|
||||
|
||||
game := NewGame()
|
||||
|
||||
if err := ebiten.RunGame(game); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,28 @@
|
|||
package tetra3d
|
||||
|
||||
// LightGroup represents a grouping of lights. This is used on Models to control which lights are used to light them.
|
||||
type LightGroup struct {
|
||||
Lights []ILight // The collection of lights present in the LightGroup.
|
||||
Active bool // If the LightGroup is active or not; when a Model's LightGroup is inactive, Models will fallback to the lights present under the Scene's root.
|
||||
}
|
||||
|
||||
// NewLightGroup creates a new LightGroup.
|
||||
func NewLightGroup(lights ...ILight) *LightGroup {
|
||||
lg := &LightGroup{
|
||||
Active: true,
|
||||
}
|
||||
lg.Add(lights...)
|
||||
return lg
|
||||
}
|
||||
|
||||
// Add adds the passed ILights to the LightGroup.
|
||||
func (lg *LightGroup) Add(lights ...ILight) {
|
||||
lg.Lights = append(lg.Lights, lights...)
|
||||
}
|
||||
|
||||
// Clone clones the LightGroup.
|
||||
func (lg *LightGroup) Clone() *LightGroup {
|
||||
newLG := NewLightGroup(lg.Lights...)
|
||||
newLG.Active = lg.Active
|
||||
return newLG
|
||||
}
|
8
model.go
8
model.go
|
@ -28,6 +28,10 @@ type Model struct {
|
|||
skinMatrix Matrix4
|
||||
bones [][]*Node // The bones (nodes) of the Model, assuming it has been skinned. A Mesh's bones slice will point to indices indicating bones in the Model.
|
||||
skinVectorPool *VectorPool
|
||||
|
||||
// A LightGroup indicates if a Model should be lit by a specific group of Lights. This allows you to control the overall lighting of scenes more accurately.
|
||||
// If a Model has no LightGroup, the Model is lit by the lights present in the Scene.
|
||||
LightGroup *LightGroup
|
||||
}
|
||||
|
||||
// NewModel creates a new Model (or instance) of the Mesh and Name provided. A Model represents a singular visual instantiation of a Mesh.
|
||||
|
@ -86,6 +90,10 @@ func (model *Model) Clone() INode {
|
|||
|
||||
newModel.originalLocalPosition = model.originalLocalPosition.Clone()
|
||||
|
||||
if model.LightGroup != nil {
|
||||
newModel.LightGroup = model.LightGroup.Clone()
|
||||
}
|
||||
|
||||
return newModel
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue