objloader
This commit is contained in:
		
						commit
						d5ac5461e2
					
				
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| *.log | ||||
| .idea/* | ||||
| */.ipynb_checkpoints/* | ||||
							
								
								
									
										84
									
								
								asset/model/cube.obj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								asset/model/cube.obj
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,84 @@ | ||||
| v -0.500000 0.500000 0.500000 | ||||
| v -0.500000 -0.500000 0.500000 | ||||
| v 0.500000 -0.500000 0.500000 | ||||
| v 0.500000 0.500000 0.500000 | ||||
| v -0.500000 0.500000 -0.500000 | ||||
| v -0.500000 -0.500000 -0.500000 | ||||
| v 0.500000 -0.500000 -0.500000 | ||||
| v 0.500000 0.500000 -0.500000 | ||||
| v 0.500000 0.500000 0.500000 | ||||
| v 0.500000 0.500000 -0.500000 | ||||
| v 0.500000 -0.500000 0.500000 | ||||
| v 0.500000 -0.500000 -0.500000 | ||||
| v -0.500000 0.500000 0.500000 | ||||
| v -0.500000 -0.500000 0.500000 | ||||
| v -0.500000 0.500000 -0.500000 | ||||
| v -0.500000 -0.500000 -0.500000 | ||||
| v 0.500000 0.500000 0.500000 | ||||
| v 0.500000 0.500000 -0.500000 | ||||
| v -0.500000 0.500000 0.500000 | ||||
| v -0.500000 0.500000 -0.500000 | ||||
| v 0.500000 -0.500000 0.500000 | ||||
| v 0.500000 -0.500000 -0.500000 | ||||
| v -0.500000 -0.500000 0.500000 | ||||
| v -0.500000 -0.500000 -0.500000 | ||||
| vn 0.000000 0.000000 1.000000 | ||||
| vn 0.000000 0.000000 1.000000 | ||||
| vn 0.000000 0.000000 1.000000 | ||||
| vn 0.000000 0.000000 1.000000 | ||||
| vn 0.000000 0.000000 -1.000000 | ||||
| vn 0.000000 0.000000 -1.000000 | ||||
| vn 0.000000 0.000000 -1.000000 | ||||
| vn 0.000000 0.000000 -1.000000 | ||||
| vn 1.000000 0.000000 0.000000 | ||||
| vn 1.000000 0.000000 0.000000 | ||||
| vn 1.000000 0.000000 0.000000 | ||||
| vn 1.000000 0.000000 0.000000 | ||||
| vn -1.000000 0.000000 0.000000 | ||||
| vn -1.000000 0.000000 0.000000 | ||||
| vn -1.000000 0.000000 0.000000 | ||||
| vn -1.000000 0.000000 0.000000 | ||||
| vn 0.000000 1.000000 0.000000 | ||||
| vn 0.000000 1.000000 0.000000 | ||||
| vn 0.000000 1.000000 0.000000 | ||||
| vn 0.000000 1.000000 0.000000 | ||||
| vn 0.000000 -1.000000 0.000000 | ||||
| vn 0.000000 -1.000000 0.000000 | ||||
| vn 0.000000 -1.000000 0.000000 | ||||
| vn 0.000000 -1.000000 0.000000 | ||||
| vt 0.000000 0.000000 | ||||
| vt 0.000000 1.000000 | ||||
| vt 1.000000 1.000000 | ||||
| vt 1.000000 0.000000 | ||||
| vt 0.000000 0.000000 | ||||
| vt 0.000000 1.000000 | ||||
| vt 1.000000 1.000000 | ||||
| vt 1.000000 0.000000 | ||||
| vt 0.000000 0.000000 | ||||
| vt 0.000000 1.000000 | ||||
| vt 1.000000 1.000000 | ||||
| vt 1.000000 0.000000 | ||||
| vt 0.000000 0.000000 | ||||
| vt 0.000000 1.000000 | ||||
| vt 1.000000 1.000000 | ||||
| vt 1.000000 0.000000 | ||||
| vt 0.000000 0.000000 | ||||
| vt 0.000000 1.000000 | ||||
| vt 1.000000 1.000000 | ||||
| vt 1.000000 0.000000 | ||||
| vt 0.000000 0.000000 | ||||
| vt 0.000000 1.000000 | ||||
| vt 1.000000 1.000000 | ||||
| vt 1.000000 0.000000 | ||||
| f 1/1/1 2/2/2 4/4/4 | ||||
| f 4/4/4 2/2/2 3/3/3 | ||||
| f 5/5/5 8/8/8 6/6/6 | ||||
| f 8/8/8 7/7/7 6/6/6 | ||||
| f 9/9/9 11/11/11 10/10/10 | ||||
| f 10/10/10 11/11/11 12/12/12 | ||||
| f 13/13/13 15/15/15 14/14/14 | ||||
| f 14/14/14 15/15/15 16/16/16 | ||||
| f 17/17/17 18/18/18 19/19/19 | ||||
| f 18/18/18 20/20/20 19/19/19 | ||||
| f 21/21/21 23/23/23 22/22/22 | ||||
| f 22/22/22 23/23/23 24/24/24 | ||||
							
								
								
									
										4811
									
								
								asset/model/sphere.obj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4811
									
								
								asset/model/sphere.obj
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										17
									
								
								cmd/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								cmd/main.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"zworld/engine" | ||||
| 	"zworld/engine/core/zlog" | ||||
| 	"zworld/engine/rule" | ||||
| 	"zworld/plugin/objloader" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	w := engine.NewWorld(nil, engine.FTimeRuleOption(&rule.FTimeRule{}), engine.FActorRuleOption(&rule.FActorRule{})) | ||||
| 	for i := 1; i < 100; i++ { | ||||
| 		w.Tick() | ||||
| 	} | ||||
| 	err, mesh := objloader.LoadObj("asset/model/sphere.obj") | ||||
| 	zlog.Infof("err", err, len(mesh.Positions)) | ||||
| } | ||||
							
								
								
									
										1
									
								
								engine/actor/Component/mesh.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								engine/actor/Component/mesh.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| package Component | ||||
							
								
								
									
										1
									
								
								engine/actor/Component/root.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								engine/actor/Component/root.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| package Component | ||||
							
								
								
									
										4
									
								
								engine/actor/Component/type.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								engine/actor/Component/type.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| package Component | ||||
| 
 | ||||
| type IComponent interface { | ||||
| } | ||||
							
								
								
									
										17
									
								
								engine/actor/actor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								engine/actor/actor.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| package actor | ||||
| 
 | ||||
| import ( | ||||
| 	"zworld/engine/actor/Component" | ||||
| ) | ||||
| 
 | ||||
| type FActorCompWrap struct { | ||||
| 	Render Component.IComponent | ||||
| } | ||||
| type FActor struct { | ||||
| 	CompList []Component.IComponent | ||||
| 	CompWrap *FActorCompWrap | ||||
| } | ||||
| 
 | ||||
| func (r *FActor) Tick(delta float32) { | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										16
									
								
								engine/core/zlog/type.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								engine/core/zlog/type.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| package zlog | ||||
| 
 | ||||
| import ( | ||||
| 	"go.uber.org/zap/zapcore" | ||||
| ) | ||||
| 
 | ||||
| // Level is type of log levels
 | ||||
| type Level = zapcore.Level | ||||
| 
 | ||||
| type FLogf = func(template string, args ...interface{}) | ||||
| type FLog = func(args ...interface{}) | ||||
| 
 | ||||
| var ( | ||||
| 	Debugf, Infof, Warnf, Errorf, Panicf, Fatalf FLogf | ||||
| 	Debug, Error, Panic, Fatal                   FLog | ||||
| ) | ||||
							
								
								
									
										98
									
								
								engine/core/zlog/zlog.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								engine/core/zlog/zlog.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | ||||
| package zlog | ||||
| 
 | ||||
| import ( | ||||
| 	"go.uber.org/zap" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	cfg          zap.Config | ||||
| 	logger       *zap.Logger | ||||
| 	sugar        *zap.SugaredLogger | ||||
| 	source       string | ||||
| 	currentLevel Level | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	currentLevel = zap.DebugLevel | ||||
| 	cfg = zap.NewDevelopmentConfig() | ||||
| 	cfg.Development = true | ||||
| 	rebuildLoggerFromCfg() | ||||
| } | ||||
| 
 | ||||
| // SetSource sets the component name (dispatcher/gate/game) of gwlog module
 | ||||
| func SetSource(source_ string) { | ||||
| 	source = source_ | ||||
| 	rebuildLoggerFromCfg() | ||||
| } | ||||
| 
 | ||||
| func SetParseLevel(slv string) { | ||||
| 	lv := ParseLevel(slv) | ||||
| 	SetLevel(lv) | ||||
| } | ||||
| 
 | ||||
| // SetLevel sets the zlog level
 | ||||
| func SetLevel(lv Level) { | ||||
| 	currentLevel = lv | ||||
| 	cfg.Level.SetLevel(lv) | ||||
| } | ||||
| 
 | ||||
| // GetLevel get the current zlog level
 | ||||
| func GetLevel() Level { | ||||
| 	return currentLevel | ||||
| } | ||||
| 
 | ||||
| // SetOutput sets the output writer
 | ||||
| func SetOutput(outputs []string) { | ||||
| 	cfg.OutputPaths = outputs | ||||
| 	rebuildLoggerFromCfg() | ||||
| } | ||||
| 
 | ||||
| // ParseLevel converts string to Levels
 | ||||
| func ParseLevel(s string) Level { | ||||
| 	if strings.ToLower(s) == "debug" { | ||||
| 		return zap.DebugLevel | ||||
| 	} else if strings.ToLower(s) == "info" { | ||||
| 		return zap.InfoLevel | ||||
| 	} else if strings.ToLower(s) == "warn" || strings.ToLower(s) == "warning" { | ||||
| 		return zap.WarnLevel | ||||
| 	} else if strings.ToLower(s) == "error" { | ||||
| 		return zap.ErrorLevel | ||||
| 	} else if strings.ToLower(s) == "panic" { | ||||
| 		return zap.PanicLevel | ||||
| 	} else if strings.ToLower(s) == "fatal" { | ||||
| 		return zap.FatalLevel | ||||
| 	} | ||||
| 	Errorf("ParseLevel: unknown level: %s", s) | ||||
| 	return zap.DebugLevel | ||||
| } | ||||
| 
 | ||||
| func rebuildLoggerFromCfg() { | ||||
| 	newLogger, err := cfg.Build() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 		return | ||||
| 	} | ||||
| 	if logger != nil { | ||||
| 		logger.Sync() | ||||
| 	} | ||||
| 	logger = newLogger | ||||
| 	if source != "" { | ||||
| 		logger = logger.With(zap.String("source", source)) | ||||
| 	} | ||||
| 	sugar = logger.Sugar() | ||||
| 	initFLog() | ||||
| } | ||||
| func initFLog() { | ||||
| 	Debugf = sugar.Debugf | ||||
| 	Infof = sugar.Infof | ||||
| 	Warnf = sugar.Warnf | ||||
| 	Errorf = sugar.Errorf | ||||
| 	Panicf = sugar.Panicf | ||||
| 	Fatalf = sugar.Fatalf | ||||
| 
 | ||||
| 	Debug = sugar.Debug | ||||
| 	Error = sugar.Error | ||||
| 	Panic = sugar.Panic | ||||
| 	Fatal = sugar.Fatal | ||||
| } | ||||
							
								
								
									
										15
									
								
								engine/render/vulkan/instance/ext_linux.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								engine/render/vulkan/instance/ext_linux.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| package instance | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/vkngwrapper/extensions/v2/ext_debug_utils" | ||||
| 	"github.com/vkngwrapper/extensions/v2/khr_get_physical_device_properties2" | ||||
| 	"github.com/vkngwrapper/extensions/v2/khr_surface" | ||||
| ) | ||||
| 
 | ||||
| var extensions = []string{ | ||||
| 	khr_surface.ExtensionName, | ||||
| 	khr_get_physical_device_properties2.ExtensionName, | ||||
| 	ext_debug_utils.ExtensionName, | ||||
| 
 | ||||
| 	"VK_KHR_xcb_surface", | ||||
| } | ||||
							
								
								
									
										16
									
								
								engine/render/vulkan/instance/ext_windows.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								engine/render/vulkan/instance/ext_windows.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| package instance | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/vkngwrapper/extensions/v2/ext_debug_utils" | ||||
| 	"github.com/vkngwrapper/extensions/v2/khr_get_physical_device_properties2" | ||||
| 	"github.com/vkngwrapper/extensions/v2/khr_surface" | ||||
| ) | ||||
| 
 | ||||
| var extensions = []string{ | ||||
| 	khr_surface.ExtensionName, | ||||
| 	ext_debug_utils.ExtensionName, | ||||
| 	khr_get_physical_device_properties2.ExtensionName, | ||||
| 
 | ||||
| 	"VK_EXT_debug_report", | ||||
| 	"VK_EXT_metal_surface", | ||||
| } | ||||
							
								
								
									
										62
									
								
								engine/render/vulkan/instance/instance.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								engine/render/vulkan/instance/instance.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | ||||
| package instance | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/go-gl/glfw/v3.3/glfw" | ||||
| 	"github.com/vkngwrapper/core/v2" | ||||
| 	"github.com/vkngwrapper/core/v2/common" | ||||
| 	"github.com/vkngwrapper/core/v2/core1_0" | ||||
| ) | ||||
| 
 | ||||
| var layers = []string{ | ||||
| 	"VK_LAYER_KHRONOS_validation", | ||||
| 	//"VK_LAYER_LUNARG_api_dump",
 | ||||
| } | ||||
| 
 | ||||
| type T interface { | ||||
| 	EnumeratePhysicalDevices() []core1_0.PhysicalDevice | ||||
| 	Destroy() | ||||
| 	Ptr() core1_0.Instance | ||||
| } | ||||
| 
 | ||||
| type instance struct { | ||||
| 	ptr core1_0.Instance | ||||
| } | ||||
| 
 | ||||
| func New(appName string) T { | ||||
| 	loader, err := core.CreateLoaderFromProcAddr(glfw.GetVulkanGetInstanceProcAddress()) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	handle, _, err := loader.CreateInstance(nil, core1_0.InstanceCreateInfo{ | ||||
| 		APIVersion:            common.APIVersion(common.CreateVersion(1, 1, 0)), | ||||
| 		ApplicationName:       appName, | ||||
| 		ApplicationVersion:    common.CreateVersion(0, 1, 0), | ||||
| 		EngineName:            "goworld", | ||||
| 		EngineVersion:         common.CreateVersion(0, 2, 1), | ||||
| 		EnabledLayerNames:     layers, | ||||
| 		EnabledExtensionNames: extensions, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return &instance{ | ||||
| 		ptr: handle, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (i *instance) Ptr() core1_0.Instance { | ||||
| 	return i.ptr | ||||
| } | ||||
| 
 | ||||
| func (i *instance) Destroy() { | ||||
| 	i.ptr.Destroy(nil) | ||||
| 	i.ptr = nil | ||||
| } | ||||
| 
 | ||||
| func (i *instance) EnumeratePhysicalDevices() []core1_0.PhysicalDevice { | ||||
| 	r, _, err := i.ptr.EnumeratePhysicalDevices() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return r | ||||
| } | ||||
							
								
								
									
										21
									
								
								engine/rule/actor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								engine/rule/actor.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| package rule | ||||
| 
 | ||||
| type FActor struct { | ||||
| } | ||||
| 
 | ||||
| func (r *FActor) Tick(delta float32) { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| type FActorRule struct { | ||||
| 	actorList []*FActor | ||||
| } | ||||
| 
 | ||||
| func (r *FActorRule) Tick(w IWorld, delta float32) { | ||||
| 	for _, actor := range r.actorList { | ||||
| 		actor.Tick(delta) | ||||
| 	} | ||||
| } | ||||
| func (r *FActorRule) GetPriority() RulePriority { | ||||
| 	return EActorRule | ||||
| } | ||||
							
								
								
									
										11
									
								
								engine/rule/render.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								engine/rule/render.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| package rule | ||||
| 
 | ||||
| type FRenderRule struct { | ||||
| } | ||||
| 
 | ||||
| func (r *FRenderRule) Tick(w IWorld, delta float32) { | ||||
| 
 | ||||
| } | ||||
| func (r *FRenderRule) GetPriority() RulePriority { | ||||
| 	return ERenderRule | ||||
| } | ||||
							
								
								
									
										21
									
								
								engine/rule/time.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								engine/rule/time.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| package rule | ||||
| 
 | ||||
| type FTimeRule struct { | ||||
| 	isPause  bool | ||||
| 	interval float32 | ||||
| } | ||||
| 
 | ||||
| func (r *FTimeRule) GetInterval() float32 { | ||||
| 	if r.isPause { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	return r.interval | ||||
| } | ||||
| func (r *FTimeRule) Tick(w IWorld, delta float32) { | ||||
| 	if r.isPause { | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
| func (r *FTimeRule) GetPriority() RulePriority { | ||||
| 	return ETimeRule | ||||
| } | ||||
							
								
								
									
										16
									
								
								engine/rule/type.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								engine/rule/type.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| package rule | ||||
| 
 | ||||
| type IWorld interface { | ||||
| } | ||||
| type IRule interface { | ||||
| 	Tick(w IWorld, delta float32) | ||||
| 	GetPriority() RulePriority | ||||
| } | ||||
| 
 | ||||
| type RulePriority int | ||||
| 
 | ||||
| const ( | ||||
| 	ETimeRule = iota | ||||
| 	ERenderRule | ||||
| 	EActorRule | ||||
| ) | ||||
							
								
								
									
										7
									
								
								engine/type.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								engine/type.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| package engine | ||||
| 
 | ||||
| import "zworld/engine/rule" | ||||
| 
 | ||||
| type ( | ||||
| 	IRule = rule.IRule | ||||
| ) | ||||
							
								
								
									
										26
									
								
								engine/world.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								engine/world.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| package engine | ||||
| 
 | ||||
| import ( | ||||
| 	"zworld/engine/rule" | ||||
| ) | ||||
| 
 | ||||
| type FWorldRuleWrap struct { | ||||
| 	TimeRule  *rule.FTimeRule | ||||
| 	ActorRule *rule.FActorRule | ||||
| } | ||||
| type FWorld struct { | ||||
| 	Name     string | ||||
| 	RuleList []IRule | ||||
| 	RuleWrap *FWorldRuleWrap | ||||
| } | ||||
| 
 | ||||
| func (w *FWorld) Tick() { | ||||
| 	delta := w.RuleWrap.TimeRule.GetInterval() | ||||
| 	if delta == 0 { | ||||
| 		w.RuleWrap.TimeRule.Tick(w, delta) | ||||
| 		return | ||||
| 	} | ||||
| 	for _, rule := range w.RuleList { | ||||
| 		rule.Tick(w, delta) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										39
									
								
								engine/world_new.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								engine/world_new.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| package engine | ||||
| 
 | ||||
| import ( | ||||
| 	"sort" | ||||
| 	"zworld/engine/rule" | ||||
| ) | ||||
| 
 | ||||
| type FWorldOption func(*FWorld) | ||||
| 
 | ||||
| func FTimeRuleOption(rule *rule.FTimeRule) FWorldOption { | ||||
| 	return func(world *FWorld) { | ||||
| 		world.RuleWrap.TimeRule = rule | ||||
| 		world.RuleList = append(world.RuleList, rule) | ||||
| 	} | ||||
| } | ||||
| func FActorRuleOption(rule *rule.FActorRule) FWorldOption { | ||||
| 	return func(world *FWorld) { | ||||
| 		world.RuleWrap.ActorRule = rule | ||||
| 		world.RuleList = append(world.RuleList, rule) | ||||
| 	} | ||||
| } | ||||
| func NewWorld(wrap *FWorldRuleWrap, ops ...FWorldOption) *FWorld { | ||||
| 	w := FWorld{} | ||||
| 	if wrap == nil { | ||||
| 		wrap = &FWorldRuleWrap{} | ||||
| 	} | ||||
| 	w.RuleWrap = wrap | ||||
| 	for _, op := range ops { | ||||
| 		op(&w) | ||||
| 	} | ||||
| 	sort.Slice(w.RuleList, func(i, j int) bool { | ||||
| 		pi, pj := w.RuleList[i].GetPriority(), w.RuleList[j].GetPriority() | ||||
| 		if pi != pj { | ||||
| 			return pi < pj | ||||
| 		} | ||||
| 		return i < j | ||||
| 	}) | ||||
| 	return &w | ||||
| } | ||||
							
								
								
									
										18
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| module zworld | ||||
| 
 | ||||
| go 1.20 | ||||
| 
 | ||||
| require ( | ||||
| 	github.com/CannibalVox/cgoparam v1.1.0 // indirect | ||||
| 	github.com/go-gl/glfw v0.0.0-20221017161538-93cebf72946b // indirect | ||||
| 	github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b // indirect | ||||
| 	github.com/golang/mock v1.6.0 // indirect | ||||
| 	github.com/google/uuid v1.3.0 // indirect | ||||
| 	github.com/ojrac/opensimplex-go v1.0.2 // indirect | ||||
| 	github.com/pkg/errors v0.9.1 // indirect | ||||
| 	github.com/vkngwrapper/core/v2 v2.2.1 // indirect | ||||
| 	github.com/vkngwrapper/extensions/v2 v2.2.0 // indirect | ||||
| 	go.uber.org/multierr v1.10.0 // indirect | ||||
| 	go.uber.org/zap v1.26.0 // indirect | ||||
| 	golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb // indirect | ||||
| ) | ||||
							
								
								
									
										55
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| github.com/CannibalVox/VKng v0.0.0-20220707035000-0931f864c378 h1:VT+Uklgvu+BMI2MLouYtTd/HL7uqE/ds3n9yeP8bj8I= | ||||
| github.com/CannibalVox/cgoparam v1.1.0 h1:6UDDhOpT06csFE2vkcanXsIJmebMc9o+6Vzhvi4i0wY= | ||||
| github.com/CannibalVox/cgoparam v1.1.0/go.mod h1:9LDFLuHVgE+IIBDd1QFN3dPqmGQN9bS6H+NPizMv2fA= | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| github.com/go-gl/glfw v0.0.0-20221017161538-93cebf72946b h1:2hdUMUOJuLQkhaPAwoyOeSzoaBydYEkXkBEuqDuDBfg= | ||||
| github.com/go-gl/glfw v0.0.0-20221017161538-93cebf72946b/go.mod h1:wyvWpaEu9B/VQiV1jsPs7Mha9I7yto/HqIBw197ZAzk= | ||||
| github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b h1:GgabKamyOYguHqHjSkDACcgoPIz3w0Dis/zJ1wyHHHU= | ||||
| github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= | ||||
| github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= | ||||
| github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= | ||||
| github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= | ||||
| github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/ojrac/opensimplex-go v1.0.2 h1:l4vs0D+JCakcu5OV0kJ99oEaWJfggSc9jiLpxaWvSzs= | ||||
| github.com/ojrac/opensimplex-go v1.0.2/go.mod h1:NwbXFFbXcdGgIFdiA7/REME+7n/lOf1TuEbLiZYOWnM= | ||||
| github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= | ||||
| github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= | ||||
| github.com/vkngwrapper/core/v2 v2.2.1 h1:8xw2tuIXAeyNQj4mnDA7BHO6T6f7ba08UbJZB7UM6xg= | ||||
| github.com/vkngwrapper/core/v2 v2.2.1/go.mod h1:EWABLJZGHa8nyeO4Bh9eR/V862HAz+Fvk5DitkOvYF4= | ||||
| github.com/vkngwrapper/extensions/v2 v2.2.0 h1:2ZP+Nom2EbefqgR2EPherJRS836wSWPoXeOLvV7aUuY= | ||||
| github.com/vkngwrapper/extensions/v2 v2.2.0/go.mod h1:55exjYwTUyQVNS/zhrC/Or/c2CA4Q9Cj/88Tu9EqlJ0= | ||||
| github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= | ||||
| go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= | ||||
| go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= | ||||
| go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= | ||||
| go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= | ||||
| go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8= | ||||
| golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= | ||||
| golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU= | ||||
| golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= | ||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||
							
								
								
									
										8
									
								
								library/zos/error.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								library/zos/error.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| package zos | ||||
| 
 | ||||
| import "errors" | ||||
| 
 | ||||
| var ( | ||||
| 	ErrBufferFull = errors.New("read: buffer full") | ||||
| 	ErrBufferEOF  = errors.New("read: buffer EOF") | ||||
| ) | ||||
							
								
								
									
										77
									
								
								library/zos/line.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								library/zos/line.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,77 @@ | ||||
| package zos | ||||
| 
 | ||||
| import "strconv" | ||||
| 
 | ||||
| type FByteView struct { | ||||
| 	Line []byte | ||||
| 	sign byte | ||||
| 	err  error | ||||
| } | ||||
| 
 | ||||
| func NewByteView(line []byte) *FByteView { | ||||
| 	return &FByteView{ | ||||
| 		Line: line, | ||||
| 		sign: ' ', | ||||
| 		err:  nil, | ||||
| 	} | ||||
| } | ||||
| func (v *FByteView) Err() error { | ||||
| 	return v.err | ||||
| } | ||||
| func (v *FByteView) SetSign(sign byte) { | ||||
| 	v.sign = sign | ||||
| } | ||||
| func (v *FByteView) GetToken() []byte { | ||||
| 	if v.sign == ' ' { | ||||
| 		return v.ReadTokenBySpace() | ||||
| 	} | ||||
| 	return v.ReadTokenByChar(v.sign) | ||||
| } | ||||
| func (v *FByteView) GetInt() int32 { | ||||
| 	if v.err != nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	token := string(v.GetToken()) | ||||
| 	num, err := strconv.ParseInt(token, 10, 32) | ||||
| 	v.err = err | ||||
| 	return int32(num) | ||||
| } | ||||
| func (v *FByteView) GetFloat() float32 { | ||||
| 	if v.err != nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	token := string(v.GetToken()) | ||||
| 	num, err := strconv.ParseFloat(token, 32) | ||||
| 	v.err = err | ||||
| 	return float32(num) | ||||
| } | ||||
| 
 | ||||
| func (v *FByteView) SkipWhitespace() { | ||||
| 	r, w := 0, len(v.Line) | ||||
| 	for r < w && (v.Line[r] == ' ' || v.Line[r] == '\t') { | ||||
| 		r++ | ||||
| 	} | ||||
| 	if r > 0 { | ||||
| 		v.Line = v.Line[r:w] | ||||
| 	} | ||||
| } | ||||
| func (v *FByteView) ReadTokenBySpace() []byte { | ||||
| 	v.SkipWhitespace() | ||||
| 	r, w := 0, len(v.Line) | ||||
| 	for r < w && (v.Line[r] != ' ' && v.Line[r] != '\t') { | ||||
| 		r++ | ||||
| 	} | ||||
| 	token := v.Line[0:r] | ||||
| 	v.Line = v.Line[r:w] | ||||
| 	return token | ||||
| } | ||||
| func (v *FByteView) ReadTokenByChar(char byte) []byte { | ||||
| 	v.SkipWhitespace() | ||||
| 	r, w := 0, len(v.Line) | ||||
| 	for r < w && (v.Line[r] != char) { | ||||
| 		r++ | ||||
| 	} | ||||
| 	token := v.Line[0:r] | ||||
| 	v.Line = v.Line[r:w] | ||||
| 	return token | ||||
| } | ||||
							
								
								
									
										127
									
								
								library/zos/reader.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								library/zos/reader.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,127 @@ | ||||
| package zos | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"io" | ||||
| 	"os" | ||||
| ) | ||||
| 
 | ||||
| func min(a int, b int) int { | ||||
| 	if a < b { | ||||
| 		return a | ||||
| 	} | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
| type FReader struct { | ||||
| 	io   io.Reader | ||||
| 	buf  []byte | ||||
| 	r, w int | ||||
| 	err  error | ||||
| } | ||||
| 
 | ||||
| func NewReader(io io.Reader) *FReader { | ||||
| 	return &FReader{ | ||||
| 		io:  io, | ||||
| 		buf: make([]byte, 4096), | ||||
| 		r:   0, | ||||
| 		w:   0, | ||||
| 	} | ||||
| } | ||||
| func (b *FReader) Read(data []byte) (int, error) { | ||||
| 	n, rn := len(data), 0 | ||||
| 	for rn < n { | ||||
| 		d := min(b.w-b.r, n-rn) | ||||
| 		if d > 0 { | ||||
| 			copy(data[rn:rn+d], b.buf[b.r:b.r+d]) | ||||
| 			b.r += d | ||||
| 			rn += d | ||||
| 			if rn >= n { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		err := b.fill() | ||||
| 		if err != nil { | ||||
| 			return rn, err | ||||
| 		} | ||||
| 	} | ||||
| 	return rn, nil | ||||
| 
 | ||||
| } | ||||
| func (b *FReader) ReadLine() ([]byte, error) { | ||||
| 	line, err := b.ReadSlice('\n') | ||||
| 	i := len(line) - 1 | ||||
| 	if i >= 0 && line[i] == '\r' { | ||||
| 		return line[:i], err | ||||
| 	} | ||||
| 	return line, err | ||||
| } | ||||
| func (b *FReader) ReadLineFast() ([]byte, error) { | ||||
| 	line, err := b.ReadSliceFast('\n') | ||||
| 	i := len(line) - 1 | ||||
| 	if i >= 0 && line[i] == '\r' { | ||||
| 		return line[:i], err | ||||
| 	} | ||||
| 	return line, err | ||||
| } | ||||
| func (b *FReader) ReadSlice(delim byte) ([]byte, error) { | ||||
| 	var lines []byte | ||||
| 	for { | ||||
| 		line, err := b.ReadSliceFast(delim) | ||||
| 		if err == nil || err != ErrBufferFull { | ||||
| 			return line, err | ||||
| 		} | ||||
| 		lines = append(lines, line...) | ||||
| 	} | ||||
| 	return lines, nil | ||||
| } | ||||
| func (b *FReader) ReadSliceFast(delim byte) ([]byte, error) { | ||||
| 	for { | ||||
| 		// Search buffer.
 | ||||
| 		if i := bytes.IndexByte(b.buf[b.r+0:b.w], delim); i >= 0 { | ||||
| 			i += 1 | ||||
| 			b.r += i | ||||
| 			return b.buf[b.r-i : b.r-1], nil | ||||
| 		} | ||||
| 		err := b.fill() | ||||
| 		if err != nil { | ||||
| 			return b.buf[b.r:], err | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| func (b *FReader) fill() error { | ||||
| 	d := b.w - b.r | ||||
| 	if d >= len(b.buf) { | ||||
| 		b.r = 0 | ||||
| 		b.w = 0 | ||||
| 		return ErrBufferFull | ||||
| 	} | ||||
| 	// Slide existing data to beginning.
 | ||||
| 	if b.r > 0 { | ||||
| 		copy(b.buf, b.buf[b.r:b.w]) | ||||
| 		b.w -= b.r | ||||
| 		b.r = 0 | ||||
| 	} | ||||
| 	// Read new data: try a limited number of times.
 | ||||
| 	n, err := b.io.Read(b.buf[b.w:]) | ||||
| 	b.w += n | ||||
| 	return err | ||||
| } | ||||
| func (b *FReader) Clear() { | ||||
| 	b.r = 0 | ||||
| 	b.w = 0 | ||||
| } | ||||
| 
 | ||||
| func (b *FReader) Seek(pos, offset int) { | ||||
| 	r := b.r + offset | ||||
| 	if r >= 0 && r <= b.w { | ||||
| 		b.r = r | ||||
| 		return | ||||
| 	} | ||||
| 	f, ok := b.io.(*os.File) | ||||
| 	if ok { | ||||
| 		f.Seek(int64(pos), 0) | ||||
| 		b.r = 0 | ||||
| 		b.w = 0 | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										10
									
								
								plugin/math/byte4/byte4.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								plugin/math/byte4/byte4.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| package byte4 | ||||
| 
 | ||||
| // T is a 4-component vector of uint8 (bytes)
 | ||||
| type T struct { | ||||
| 	X, Y, Z, W byte | ||||
| } | ||||
| 
 | ||||
| func New(x, y, z, w byte) T { | ||||
| 	return T{x, y, z, w} | ||||
| } | ||||
							
								
								
									
										32
									
								
								plugin/math/ivec2/ivec2.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								plugin/math/ivec2/ivec2.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| package ivec2 | ||||
| 
 | ||||
| var Zero = T{} | ||||
| var One = T{X: 1, Y: 1} | ||||
| var UnitX = T{X: 1} | ||||
| var UnitY = T{Y: 1} | ||||
| 
 | ||||
| type T struct { | ||||
| 	X int | ||||
| 	Y int | ||||
| } | ||||
| 
 | ||||
| func New(x, y int) T { | ||||
| 	return T{ | ||||
| 		X: x, | ||||
| 		Y: y, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (v T) Add(v2 T) T { | ||||
| 	return T{ | ||||
| 		X: v.X + v2.X, | ||||
| 		Y: v.Y + v2.Y, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (v T) Sub(v2 T) T { | ||||
| 	return T{ | ||||
| 		X: v.X - v2.X, | ||||
| 		Y: v.Y - v2.Y, | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										317
									
								
								plugin/math/mat4/mat4.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								plugin/math/mat4/mat4.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,317 @@ | ||||
| // Based on code from github.com/go-gl/mathgl:
 | ||||
| // Copyright 2014 The go-gl Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| package mat4 | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"text/tabwriter" | ||||
| 
 | ||||
| 	"golang.org/x/image/math/f32" | ||||
| 
 | ||||
| 	"zworld/plugin/math" | ||||
| 	"zworld/plugin/math/vec3" | ||||
| 	"zworld/plugin/math/vec4" | ||||
| ) | ||||
| 
 | ||||
| // T holds a 4x4 float32 matrix
 | ||||
| type T f32.Mat4 | ||||
| 
 | ||||
| // Add performs an element-wise addition of two matrices, this is
 | ||||
| // equivalent to iterating over every element of m and adding the corresponding value of m2.
 | ||||
| func (m *T) Add(m2 *T) T { | ||||
| 	return T{ | ||||
| 		m[0] + m2[0], m[1] + m2[1], m[2] + m2[2], m[3] + m2[3], | ||||
| 		m[4] + m2[4], m[5] + m2[5], m[6] + m2[6], m[7] + m2[7], | ||||
| 		m[8] + m2[8], m[9] + m2[9], m[10] + m2[10], m[11] + m2[11], | ||||
| 		m[12] + m2[12], m[13] + m2[13], m[14] + m2[14], m[15] + m2[15], | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Sub performs an element-wise subtraction of two matrices, this is
 | ||||
| // equivalent to iterating over every element of m and subtracting the corresponding value of m2.
 | ||||
| func (m *T) Sub(m2 *T) T { | ||||
| 	return T{ | ||||
| 		m[0] - m2[0], m[1] - m2[1], m[2] - m2[2], m[3] - m2[3], | ||||
| 		m[4] - m2[4], m[5] - m2[5], m[6] - m2[6], m[7] - m2[7], | ||||
| 		m[8] - m2[8], m[9] - m2[9], m[10] - m2[10], m[11] - m2[11], | ||||
| 		m[12] - m2[12], m[13] - m2[13], m[14] - m2[14], m[15] - m2[15], | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Scale performs a scalar multiplcation of the matrix. This is equivalent to iterating
 | ||||
| // over every element of the matrix and multiply it by c.
 | ||||
| func (m T) Scale(c float32) T { | ||||
| 	return T{ | ||||
| 		m[0] * c, m[1] * c, m[2] * c, m[3] * c, | ||||
| 		m[4] * c, m[5] * c, m[6] * c, m[7] * c, | ||||
| 		m[8] * c, m[9] * c, m[10] * c, m[11] * c, | ||||
| 		m[12] * c, m[13] * c, m[14] * c, m[15] * c, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // VMul multiplies a vec4 with the matrix
 | ||||
| func (m *T) VMul(v vec4.T) vec4.T { | ||||
| 	return vec4.T{ | ||||
| 		X: m[0]*v.X + m[4]*v.Y + m[8]*v.Z + m[12]*v.W, | ||||
| 		Y: m[1]*v.X + m[5]*v.Y + m[9]*v.Z + m[13]*v.W, | ||||
| 		Z: m[2]*v.X + m[6]*v.Y + m[10]*v.Z + m[14]*v.W, | ||||
| 		W: m[3]*v.X + m[7]*v.Y + m[11]*v.Z + m[15]*v.W, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // TransformPoint transforms a point to world space
 | ||||
| func (m *T) TransformPoint(v vec3.T) vec3.T { | ||||
| 	p := vec4.Extend(v, 1) | ||||
| 	vt := m.VMul(p) | ||||
| 	return vt.XYZ().Scaled(1 / vt.W) | ||||
| } | ||||
| 
 | ||||
| // TransformDir transforms a direction vector to world space
 | ||||
| func (m *T) TransformDir(v vec3.T) vec3.T { | ||||
| 	p := vec4.Extend(v, 0) | ||||
| 	vt := m.VMul(p) | ||||
| 	return vt.XYZ() | ||||
| } | ||||
| 
 | ||||
| // Mul performs a "matrix product" between this matrix and another of the same dimension
 | ||||
| func (a *T) Mul(b *T) T { | ||||
| 	return T{ | ||||
| 		a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3], | ||||
| 		a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3], | ||||
| 		a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3], | ||||
| 		a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3], | ||||
| 
 | ||||
| 		a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7], | ||||
| 		a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7], | ||||
| 		a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7], | ||||
| 		a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7], | ||||
| 
 | ||||
| 		a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11], | ||||
| 		a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11], | ||||
| 		a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11], | ||||
| 		a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11], | ||||
| 
 | ||||
| 		a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15], | ||||
| 		a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15], | ||||
| 		a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15], | ||||
| 		a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15], | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Transpose produces the transpose of this matrix. For any MxN matrix
 | ||||
| // the transpose is an NxM matrix with the rows swapped with the columns. For instance
 | ||||
| // the transpose of the Mat3x2 is a Mat2x3 like so:
 | ||||
| //
 | ||||
| //	[[a b]]    [[a c e]]
 | ||||
| //	[[c d]] =  [[b d f]]
 | ||||
| //	[[e f]]
 | ||||
| func (m *T) Transpose() T { | ||||
| 	return T{ | ||||
| 		m[0], m[4], m[8], m[12], | ||||
| 		m[1], m[5], m[9], m[13], | ||||
| 		m[2], m[6], m[10], m[14], | ||||
| 		m[3], m[7], m[11], m[15], | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Det returns the determinant of a matrix. It is a measure of a square matrix's
 | ||||
| // singularity and invertability, among other things. In this library, the
 | ||||
| // determinant is hard coded based on pre-computed cofactor expansion, and uses
 | ||||
| // no loops. Of course, the addition and multiplication must still be done.
 | ||||
| func (m *T) Det() float32 { | ||||
| 	return m[0]*m[5]*m[10]*m[15] - m[0]*m[5]*m[11]*m[14] - m[0]*m[6]*m[9]*m[15] + m[0]*m[6]*m[11]*m[13] + | ||||
| 		m[0]*m[7]*m[9]*m[14] - m[0]*m[7]*m[10]*m[13] - m[1]*m[4]*m[10]*m[15] + m[1]*m[4]*m[11]*m[14] + | ||||
| 		m[1]*m[6]*m[8]*m[15] - m[1]*m[6]*m[11]*m[12] - m[1]*m[7]*m[8]*m[14] + m[1]*m[7]*m[10]*m[12] + | ||||
| 		m[2]*m[4]*m[9]*m[15] - m[2]*m[4]*m[11]*m[13] - m[2]*m[5]*m[8]*m[15] + m[2]*m[5]*m[11]*m[12] + | ||||
| 		m[2]*m[7]*m[8]*m[13] - m[2]*m[7]*m[9]*m[12] - m[3]*m[4]*m[9]*m[14] + m[3]*m[4]*m[10]*m[13] + | ||||
| 		m[3]*m[5]*m[8]*m[14] - m[3]*m[5]*m[10]*m[12] - m[3]*m[6]*m[8]*m[13] + m[3]*m[6]*m[9]*m[12] | ||||
| } | ||||
| 
 | ||||
| // Invert computes the inverse of a square matrix. An inverse is a square matrix such that when multiplied by the
 | ||||
| // original, yields the identity.
 | ||||
| //
 | ||||
| // M_inv * M = M * M_inv = I
 | ||||
| //
 | ||||
| // In this library, the math is precomputed, and uses no loops, though the multiplications, additions, determinant calculation, and scaling
 | ||||
| // are still done. This can still be (relatively) expensive for a 4x4.
 | ||||
| //
 | ||||
| // This function checks the determinant to see if the matrix is invertible.
 | ||||
| // If the determinant is 0.0, this function returns the zero matrix. However, due to floating point errors, it is
 | ||||
| // entirely plausible to get a false positive or negative.
 | ||||
| // In the future, an alternate function may be written which takes in a pre-computed determinant.
 | ||||
| func (m *T) Invert() T { | ||||
| 	det := m.Det() | ||||
| 	if math.Equal(det, float32(0.0)) { | ||||
| 		return T{} | ||||
| 	} | ||||
| 
 | ||||
| 	retMat := T{ | ||||
| 		-m[7]*m[10]*m[13] + m[6]*m[11]*m[13] + m[7]*m[9]*m[14] - m[5]*m[11]*m[14] - m[6]*m[9]*m[15] + m[5]*m[10]*m[15], | ||||
| 		m[3]*m[10]*m[13] - m[2]*m[11]*m[13] - m[3]*m[9]*m[14] + m[1]*m[11]*m[14] + m[2]*m[9]*m[15] - m[1]*m[10]*m[15], | ||||
| 		-m[3]*m[6]*m[13] + m[2]*m[7]*m[13] + m[3]*m[5]*m[14] - m[1]*m[7]*m[14] - m[2]*m[5]*m[15] + m[1]*m[6]*m[15], | ||||
| 		m[3]*m[6]*m[9] - m[2]*m[7]*m[9] - m[3]*m[5]*m[10] + m[1]*m[7]*m[10] + m[2]*m[5]*m[11] - m[1]*m[6]*m[11], | ||||
| 		m[7]*m[10]*m[12] - m[6]*m[11]*m[12] - m[7]*m[8]*m[14] + m[4]*m[11]*m[14] + m[6]*m[8]*m[15] - m[4]*m[10]*m[15], | ||||
| 		-m[3]*m[10]*m[12] + m[2]*m[11]*m[12] + m[3]*m[8]*m[14] - m[0]*m[11]*m[14] - m[2]*m[8]*m[15] + m[0]*m[10]*m[15], | ||||
| 		m[3]*m[6]*m[12] - m[2]*m[7]*m[12] - m[3]*m[4]*m[14] + m[0]*m[7]*m[14] + m[2]*m[4]*m[15] - m[0]*m[6]*m[15], | ||||
| 		-m[3]*m[6]*m[8] + m[2]*m[7]*m[8] + m[3]*m[4]*m[10] - m[0]*m[7]*m[10] - m[2]*m[4]*m[11] + m[0]*m[6]*m[11], | ||||
| 		-m[7]*m[9]*m[12] + m[5]*m[11]*m[12] + m[7]*m[8]*m[13] - m[4]*m[11]*m[13] - m[5]*m[8]*m[15] + m[4]*m[9]*m[15], | ||||
| 		m[3]*m[9]*m[12] - m[1]*m[11]*m[12] - m[3]*m[8]*m[13] + m[0]*m[11]*m[13] + m[1]*m[8]*m[15] - m[0]*m[9]*m[15], | ||||
| 		-m[3]*m[5]*m[12] + m[1]*m[7]*m[12] + m[3]*m[4]*m[13] - m[0]*m[7]*m[13] - m[1]*m[4]*m[15] + m[0]*m[5]*m[15], | ||||
| 		m[3]*m[5]*m[8] - m[1]*m[7]*m[8] - m[3]*m[4]*m[9] + m[0]*m[7]*m[9] + m[1]*m[4]*m[11] - m[0]*m[5]*m[11], | ||||
| 		m[6]*m[9]*m[12] - m[5]*m[10]*m[12] - m[6]*m[8]*m[13] + m[4]*m[10]*m[13] + m[5]*m[8]*m[14] - m[4]*m[9]*m[14], | ||||
| 		-m[2]*m[9]*m[12] + m[1]*m[10]*m[12] + m[2]*m[8]*m[13] - m[0]*m[10]*m[13] - m[1]*m[8]*m[14] + m[0]*m[9]*m[14], | ||||
| 		m[2]*m[5]*m[12] - m[1]*m[6]*m[12] - m[2]*m[4]*m[13] + m[0]*m[6]*m[13] + m[1]*m[4]*m[14] - m[0]*m[5]*m[14], | ||||
| 		-m[2]*m[5]*m[8] + m[1]*m[6]*m[8] + m[2]*m[4]*m[9] - m[0]*m[6]*m[9] - m[1]*m[4]*m[10] + m[0]*m[5]*m[10], | ||||
| 	} | ||||
| 
 | ||||
| 	return retMat.Scale(1 / det) | ||||
| } | ||||
| 
 | ||||
| // ApproxEqual performs an element-wise approximate equality test between two matrices,
 | ||||
| // as if FloatEqual had been used.
 | ||||
| func (m *T) ApproxEqual(m2 *T) bool { | ||||
| 	for i := range m { | ||||
| 		if !math.Equal(m[i], m2[i]) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // ApproxEqualThreshold performs an element-wise approximate equality test between two matrices
 | ||||
| // with a given epsilon threshold, as if FloatEqualThreshold had been used.
 | ||||
| func (m *T) ApproxEqualThreshold(m2 *T, threshold float32) bool { | ||||
| 	for i := range m { | ||||
| 		if !math.EqualThreshold(m[i], m2[i], threshold) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // At returns the matrix element at the given row and column.
 | ||||
| // This is equivalent to mat[col * numRow + row] where numRow is constant
 | ||||
| // (E.G. for a Mat3x2 it's equal to 3)
 | ||||
| //
 | ||||
| // This method is garbage-in garbage-out. For instance, on a T asking for
 | ||||
| // At(5,0) will work just like At(1,1). Or it may panic if it's out of bounds.
 | ||||
| func (m *T) At(row, col int) float32 { | ||||
| 	return m[col*4+row] | ||||
| } | ||||
| 
 | ||||
| // Set sets the corresponding matrix element at the given row and column.
 | ||||
| func (m *T) Set(row, col int, value float32) { | ||||
| 	m[col*4+row] = value | ||||
| } | ||||
| 
 | ||||
| // Index returns the index of the given row and column, to be used with direct
 | ||||
| // access. E.G. Index(0,0) = 0.
 | ||||
| func (m *T) Index(row, col int) int { | ||||
| 	return col*4 + row | ||||
| } | ||||
| 
 | ||||
| // Row returns a vector representing the corresponding row (starting at row 0).
 | ||||
| // This package makes no distinction between row and column vectors, so it
 | ||||
| // will be a normal VecM for a MxN matrix.
 | ||||
| func (m *T) Row(row int) vec4.T { | ||||
| 	return vec4.T{ | ||||
| 		X: m[row+0], | ||||
| 		Y: m[row+4], | ||||
| 		Z: m[row+8], | ||||
| 		W: m[row+12], | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Rows decomposes a matrix into its corresponding row vectors.
 | ||||
| // This is equivalent to calling mat.Row for each row.
 | ||||
| func (m *T) Rows() (row0, row1, row2, row3 vec4.T) { | ||||
| 	return m.Row(0), m.Row(1), m.Row(2), m.Row(3) | ||||
| } | ||||
| 
 | ||||
| // Col returns a vector representing the corresponding column (starting at col 0).
 | ||||
| // This package makes no distinction between row and column vectors, so it
 | ||||
| // will be a normal VecN for a MxN matrix.
 | ||||
| func (m *T) Col(col int) vec4.T { | ||||
| 	return vec4.T{ | ||||
| 		X: m[col*4+0], | ||||
| 		Y: m[col*4+1], | ||||
| 		Z: m[col*4+2], | ||||
| 		W: m[col*4+3], | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Cols decomposes a matrix into its corresponding column vectors.
 | ||||
| // This is equivalent to calling mat.Col for each column.
 | ||||
| func (m *T) Cols() (col0, col1, col2, col3 vec4.T) { | ||||
| 	return m.Col(0), m.Col(1), m.Col(2), m.Col(3) | ||||
| } | ||||
| 
 | ||||
| // Trace is a basic operation on a square matrix that simply
 | ||||
| // sums up all elements on the main diagonal (meaning all elements such that row==col).
 | ||||
| func (m *T) Trace() float32 { | ||||
| 	return m[0] + m[5] + m[10] + m[15] | ||||
| } | ||||
| 
 | ||||
| // Abs returns the element-wise absolute value of this matrix
 | ||||
| func (m *T) Abs() T { | ||||
| 	return T{ | ||||
| 		math.Abs(m[0]), math.Abs(m[1]), math.Abs(m[2]), math.Abs(m[3]), | ||||
| 		math.Abs(m[4]), math.Abs(m[5]), math.Abs(m[6]), math.Abs(m[7]), | ||||
| 		math.Abs(m[8]), math.Abs(m[9]), math.Abs(m[10]), math.Abs(m[11]), | ||||
| 		math.Abs(m[12]), math.Abs(m[13]), math.Abs(m[14]), math.Abs(m[15]), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // String pretty prints the matrix
 | ||||
| func (m T) String() string { | ||||
| 	buf := new(bytes.Buffer) | ||||
| 	w := tabwriter.NewWriter(buf, 4, 4, 1, ' ', tabwriter.AlignRight) | ||||
| 	for i := 0; i < 4; i++ { | ||||
| 		r := m.Row(i) | ||||
| 		fmt.Fprintf(w, "%f\t", r.X) | ||||
| 		fmt.Fprintf(w, "%f\t", r.Y) | ||||
| 		fmt.Fprintf(w, "%f\t", r.Z) | ||||
| 		fmt.Fprintf(w, "%f\t", r.W) | ||||
| 	} | ||||
| 	w.Flush() | ||||
| 	return buf.String() | ||||
| } | ||||
| 
 | ||||
| // Right extracts the right vector from a transformation matrix
 | ||||
| func (m *T) Right() vec3.T { | ||||
| 	return vec3.T{ | ||||
| 		X: m[4*0+0], | ||||
| 		Y: m[4*1+0], | ||||
| 		Z: m[4*2+0], | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Up extracts the up vector from a transformation matrix
 | ||||
| func (m *T) Up() vec3.T { | ||||
| 	return vec3.T{ | ||||
| 		X: m[4*0+1], | ||||
| 		Y: m[4*1+1], | ||||
| 		Z: m[4*2+1], | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Forward extracts the forward vector from a transformation matrix
 | ||||
| func (m *T) Forward() vec3.T { | ||||
| 	return vec3.T{ | ||||
| 		X: m[4*0+2], | ||||
| 		Y: m[4*1+2], | ||||
| 		Z: m[4*2+2], | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Origin extracts origin point of the coordinate system represented by the matrix
 | ||||
| func (m *T) Origin() vec3.T { | ||||
| 	return vec3.T{ | ||||
| 		X: m[4*3+0], | ||||
| 		Y: m[4*3+1], | ||||
| 		Z: m[4*3+2], | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										11
									
								
								plugin/math/mat4/operations.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								plugin/math/mat4/operations.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| package mat4 | ||||
| 
 | ||||
| // Ident returns a new 4x4 identity matrix
 | ||||
| func Ident() T { | ||||
| 	return T{ | ||||
| 		1, 0, 0, 0, | ||||
| 		0, 1, 0, 0, | ||||
| 		0, 0, 1, 0, | ||||
| 		0, 0, 0, 1, | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										67
									
								
								plugin/math/mat4/project.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								plugin/math/mat4/project.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| package mat4 | ||||
| 
 | ||||
| import ( | ||||
| 	"zworld/plugin/math" | ||||
| 	"zworld/plugin/math/vec3" | ||||
| ) | ||||
| 
 | ||||
| // Orthographic generates a left-handed orthographic projection matrix.
 | ||||
| // Outputs depth values in the range [0, 1]
 | ||||
| func Orthographic(left, right, bottom, top, near, far float32) T { | ||||
| 	rml, tmb, fmn := (right - left), (top - bottom), (far - near) | ||||
| 	return T{ | ||||
| 		2 / rml, 0, 0, 0, | ||||
| 		0, 2 / tmb, 0, 0, | ||||
| 		0, 0, 1 / fmn, 0, | ||||
| 		(right + left) / rml, | ||||
| 		-(top + bottom) / tmb, | ||||
| 		-near / fmn, | ||||
| 		1, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // OrthographicRZ generates a left-handed orthographic projection matrix.
 | ||||
| // Outputs depth values in the range [1, 0] (reverse Z)
 | ||||
| func OrthographicRZ(left, right, bottom, top, near, far float32) T { | ||||
| 	rml, tmb, fmn := (right - left), (top - bottom), (near - far) | ||||
| 
 | ||||
| 	return T{ | ||||
| 		2 / rml, 0, 0, 0, | ||||
| 		0, 2 / tmb, 0, 0, | ||||
| 		0, 0, 1 / fmn, 0, | ||||
| 		-(right + left) / rml, | ||||
| 		-(top + bottom) / tmb, | ||||
| 		near / fmn, | ||||
| 		1, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Perspective generates a left-handed perspective projection matrix with reversed depth.
 | ||||
| // Outputs depth values in the range [0, 1]
 | ||||
| func Perspective(fovy, aspect, near, far float32) T { | ||||
| 	fovy = math.DegToRad(fovy) | ||||
| 	tanHalfFov := math.Tan(fovy) / 2 | ||||
| 
 | ||||
| 	return T{ | ||||
| 		1 / (aspect * tanHalfFov), 0, 0, 0, | ||||
| 		0, -1 / tanHalfFov, 0, 0, | ||||
| 		0, 0, far / (far - near), 1, | ||||
| 		0, 0, -(far * near) / (far - near), 0, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func LookAt(eye, center, up vec3.T) T { | ||||
| 	f := center.Sub(eye).Normalized() | ||||
| 	r := vec3.Cross(up, f).Normalized() | ||||
| 	u := vec3.Cross(f, r) | ||||
| 
 | ||||
| 	M := T{ | ||||
| 		r.X, u.X, f.X, 0, | ||||
| 		r.Y, u.Y, f.Y, 0, | ||||
| 		r.Z, u.Z, f.Z, 0, | ||||
| 		0, 0, 0, 1, | ||||
| 	} | ||||
| 
 | ||||
| 	et := Translate(eye.Scaled(-1)) | ||||
| 	return M.Mul(&et) | ||||
| } | ||||
							
								
								
									
										56
									
								
								plugin/math/mat4/project_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								plugin/math/mat4/project_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| package mat4_test | ||||
| 
 | ||||
| import ( | ||||
| 	"testing" | ||||
| 
 | ||||
| 	. "zworld/plugin/math/mat4" | ||||
| 	"zworld/plugin/math/vec3" | ||||
| 
 | ||||
| 	. "github.com/onsi/ginkgo/v2" | ||||
| 	. "github.com/onsi/gomega" | ||||
| ) | ||||
| 
 | ||||
| func TestMat4(t *testing.T) { | ||||
| 	RegisterFailHandler(Fail) | ||||
| 	RunSpecs(t, "math/mat4") | ||||
| } | ||||
| 
 | ||||
| type TransformTest struct { | ||||
| 	Input  vec3.T | ||||
| 	Output vec3.T | ||||
| } | ||||
| 
 | ||||
| func AssertTransforms(t *testing.T, transform T, cases []TransformTest) { | ||||
| 	t.Helper() | ||||
| 	for _, c := range cases { | ||||
| 		point := transform.TransformPoint(c.Input) | ||||
| 		if !point.ApproxEqual(c.Output) { | ||||
| 			t.Errorf("expected %v was %v", c.Output, point) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestOrthographicRZ(t *testing.T) { | ||||
| 	proj := OrthographicRZ(0, 10, 0, 10, -1, 1) | ||||
| 	AssertTransforms(t, proj, []TransformTest{ | ||||
| 		{vec3.New(5, 5, 0), vec3.New(0, 0, 0.5)}, | ||||
| 		{vec3.New(5, 5, 1), vec3.New(0, 0, 0)}, | ||||
| 		{vec3.New(5, 5, -1), vec3.New(0, 0, 1)}, | ||||
| 		{vec3.New(0, 0, -1), vec3.New(-1, -1, 1)}, | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func TestPerspectiveVK(t *testing.T) { | ||||
| 	proj := Perspective(45, 1, 1, 100) | ||||
| 	AssertTransforms(t, proj, []TransformTest{ | ||||
| 		{vec3.New(0, 0, 1), vec3.New(0, 0, 0)}, | ||||
| 		{vec3.New(0, 0, 100), vec3.New(0, 0, 1)}, | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| var _ = Describe("LookAt (LH)", func() { | ||||
| 	It("correctly projects", func() { | ||||
| 		proj := LookAt(vec3.Zero, vec3.UnitZ, vec3.UnitY) | ||||
| 		Expect(proj.Forward().ApproxEqual(vec3.UnitZ)).To(BeTrue()) | ||||
| 	}) | ||||
| }) | ||||
							
								
								
									
										20
									
								
								plugin/math/mat4/translation.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								plugin/math/mat4/translation.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| // Based on code from github.com/go-gl/mathgl:
 | ||||
| // Copyright 2014 The go-gl Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| package mat4 | ||||
| 
 | ||||
| import ( | ||||
| 	"zworld/plugin/math/vec3" | ||||
| ) | ||||
| 
 | ||||
| // Translate returns a homogeneous (4x4 for 3D-space) Translation matrix that moves a point by Tx units in the x-direction, Ty units in the y-direction,
 | ||||
| // and Tz units in the z-direction
 | ||||
| func Translate(translation vec3.T) T { | ||||
| 	return T{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, translation.X, translation.Y, translation.Z, 1} | ||||
| } | ||||
| 
 | ||||
| // Scale creates a homogeneous 3D scaling matrix
 | ||||
| func Scale(scale vec3.T) T { | ||||
| 	return T{scale.X, 0, 0, 0, 0, scale.Y, 0, 0, 0, 0, scale.Z, 0, 0, 0, 0, 1} | ||||
| } | ||||
							
								
								
									
										177
									
								
								plugin/math/math32.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								plugin/math/math32.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,177 @@ | ||||
| package math | ||||
| 
 | ||||
| import ( | ||||
| 	"math" | ||||
| 
 | ||||
| 	"golang.org/x/exp/constraints" | ||||
| ) | ||||
| 
 | ||||
| // Various useful constants.
 | ||||
| var ( | ||||
| 	MinNormal = float32(1.1754943508222875e-38) // 1 / 2**(127 - 1)
 | ||||
| 	MinValue  = float32(math.SmallestNonzeroFloat32) | ||||
| 	MaxValue  = float32(math.MaxFloat32) | ||||
| 
 | ||||
| 	InfPos = float32(math.Inf(1)) | ||||
| 	InfNeg = float32(math.Inf(-1)) | ||||
| 	NaN    = float32(math.NaN()) | ||||
| 
 | ||||
| 	E       = float32(math.E) | ||||
| 	Pi      = float32(math.Pi) | ||||
| 	PiOver2 = Pi / 2 | ||||
| 	PiOver4 = Pi / 4 | ||||
| 	Sqrt2   = float32(math.Sqrt2) | ||||
| 
 | ||||
| 	Epsilon = float32(1e-10) | ||||
| ) | ||||
| 
 | ||||
| // Abs returns the absolute value of a number
 | ||||
| func Abs[T constraints.Float | constraints.Integer](v T) T { | ||||
| 	if v < 0 { | ||||
| 		return -v | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| // Min returns the smaller of two numbers
 | ||||
| func Min[T constraints.Ordered](a, b T) T { | ||||
| 	if a < b { | ||||
| 		return a | ||||
| 	} | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
| // Max returns the greater of two numbers
 | ||||
| func Max[T constraints.Ordered](a, b T) T { | ||||
| 	if a > b { | ||||
| 		return a | ||||
| 	} | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
| // Clamp a value between a minimum and a maximum value
 | ||||
| func Clamp[T constraints.Ordered](v, min, max T) T { | ||||
| 	if v > max { | ||||
| 		return max | ||||
| 	} | ||||
| 	if v < min { | ||||
| 		return min | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| // Ceil a number to the closest integer
 | ||||
| func Ceil(x float32) float32 { | ||||
| 	return float32(math.Ceil(float64(x))) | ||||
| } | ||||
| 
 | ||||
| // Floor a number to the closest integer
 | ||||
| func Floor(x float32) float32 { | ||||
| 	return float32(math.Floor(float64(x))) | ||||
| } | ||||
| 
 | ||||
| // Mod returns the remainder of a floating point division
 | ||||
| func Mod(x, y float32) float32 { | ||||
| 	return float32(math.Mod(float64(x), float64(y))) | ||||
| } | ||||
| 
 | ||||
| // Sqrt returns the square root of a number
 | ||||
| func Sqrt(x float32) float32 { | ||||
| 	return float32(math.Sqrt(float64(x))) | ||||
| } | ||||
| 
 | ||||
| // Sin computes the sine of x
 | ||||
| func Sin(x float32) float32 { | ||||
| 	return float32(math.Sin(float64(x))) | ||||
| } | ||||
| 
 | ||||
| // Cos computes the cosine of x
 | ||||
| func Cos(x float32) float32 { | ||||
| 	return float32(math.Cos(float64(x))) | ||||
| } | ||||
| 
 | ||||
| // Tan computes the tangent of x
 | ||||
| func Tan(x float32) float32 { | ||||
| 	return float32(math.Tan(float64(x))) | ||||
| } | ||||
| 
 | ||||
| func Sincos(x float32) (float32, float32) { | ||||
| 	sin, cos := math.Sincos(float64(x)) | ||||
| 	return float32(sin), float32(cos) | ||||
| } | ||||
| 
 | ||||
| func Acos(x float32) float32 { | ||||
| 	return float32(math.Acos(float64(x))) | ||||
| } | ||||
| 
 | ||||
| func Asin(x float32) float32 { | ||||
| 	return float32(math.Asin(float64(x))) | ||||
| } | ||||
| 
 | ||||
| func Atan2(y, x float32) float32 { | ||||
| 	return float32(math.Atan2(float64(y), float64(x))) | ||||
| } | ||||
| 
 | ||||
| // Sign returns the sign of x (-1 or 1)
 | ||||
| func Sign(x float32) float32 { | ||||
| 	if x > 0 { | ||||
| 		return 1 | ||||
| 	} | ||||
| 	return -1 | ||||
| } | ||||
| 
 | ||||
| func Copysign(f, sign float32) float32 { | ||||
| 	return float32(math.Copysign(float64(f), float64(sign))) | ||||
| } | ||||
| 
 | ||||
| // DegToRad converts degrees to radians
 | ||||
| func DegToRad(deg float32) float32 { | ||||
| 	return Pi * deg / 180.0 | ||||
| } | ||||
| 
 | ||||
| // RadToDeg converts radians to degrees
 | ||||
| func RadToDeg(rad float32) float32 { | ||||
| 	return 180.0 * rad / Pi | ||||
| } | ||||
| 
 | ||||
| // Equal checks two floats for (approximate) equality
 | ||||
| func Equal(a, b float32) bool { | ||||
| 	return EqualThreshold(a, b, Epsilon) | ||||
| } | ||||
| 
 | ||||
| // EqualThreshold is a utility function to compare floats.
 | ||||
| // It's Taken from http://floating-point-gui.de/errors/comparison/
 | ||||
| //
 | ||||
| // It is slightly altered to not call Abs when not needed.
 | ||||
| //
 | ||||
| // This differs from FloatEqual in that it lets you pass in your comparison threshold, so that you can adjust the comparison value to your specific needs
 | ||||
| func EqualThreshold(a, b, epsilon float32) bool { | ||||
| 	if a == b { // Handles the case of inf or shortcuts the loop when no significant error has accumulated
 | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	diff := Abs(a - b) | ||||
| 	if a*b == 0 || diff < MinNormal { // If a or b are 0 or both are extremely close to it
 | ||||
| 		return diff < epsilon*epsilon | ||||
| 	} | ||||
| 
 | ||||
| 	// Else compare difference
 | ||||
| 	return diff/(Abs(a)+Abs(b)) < epsilon | ||||
| } | ||||
| 
 | ||||
| // Lerp performs linear interpolation between a and b
 | ||||
| func Lerp(a, b, f float32) float32 { | ||||
| 	return a + f*(b-a) | ||||
| } | ||||
| 
 | ||||
| func Round(f float32) float32 { | ||||
| 	return float32(math.Round(float64(f))) | ||||
| } | ||||
| 
 | ||||
| func Snap(f, multiple float32) float32 { | ||||
| 	return Ceil(f/multiple) * multiple | ||||
| } | ||||
| 
 | ||||
| func Pow(f, x float32) float32 { | ||||
| 	return float32(math.Pow(float64(f), float64(x))) | ||||
| } | ||||
							
								
								
									
										28
									
								
								plugin/math/noise.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								plugin/math/noise.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| package math | ||||
| 
 | ||||
| import ( | ||||
| 	opensimplex "github.com/ojrac/opensimplex-go" | ||||
| ) | ||||
| 
 | ||||
| // Noise utility to sample simplex noise
 | ||||
| type Noise struct { | ||||
| 	opensimplex.Noise | ||||
| 	Seed int | ||||
| 	Freq float32 | ||||
| } | ||||
| 
 | ||||
| // NewNoise creates a new noise struct from a seed value and a frequency factor.
 | ||||
| func NewNoise(seed int, freq float32) *Noise { | ||||
| 	return &Noise{ | ||||
| 		Noise: opensimplex.New(int64(seed)), | ||||
| 		Seed:  seed, | ||||
| 		Freq:  freq, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Sample the noise at a certain position
 | ||||
| func (n *Noise) Sample(x, y, z int) float32 { | ||||
| 	// jeez
 | ||||
| 	fx, fy, fz := float64(float32(x)*n.Freq), float64(float32(y)*n.Freq), float64(float32(z)*n.Freq) | ||||
| 	return float32(n.Eval3(fx, fy, fz)) | ||||
| } | ||||
							
								
								
									
										553
									
								
								plugin/math/quat/quat.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										553
									
								
								plugin/math/quat/quat.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,553 @@ | ||||
| // Based on code from github.com/go-gl/mathgl:
 | ||||
| // Copyright 2014 The go-gl Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| package quat | ||||
| 
 | ||||
| import ( | ||||
| 	"zworld/plugin/math" | ||||
| 	"zworld/plugin/math/mat4" | ||||
| 	"zworld/plugin/math/vec3" | ||||
| ) | ||||
| 
 | ||||
| // RotationOrder is the order in which rotations will be transformed for the
 | ||||
| // purposes of AnglesToQuat.
 | ||||
| type RotationOrder int | ||||
| 
 | ||||
| // The RotationOrder constants represent a series of rotations along the given
 | ||||
| // axes for the use of AnglesToQuat.
 | ||||
| const ( | ||||
| 	XYX RotationOrder = iota | ||||
| 	XYZ | ||||
| 	XZX | ||||
| 	XZY | ||||
| 	YXY | ||||
| 	YXZ | ||||
| 	YZY | ||||
| 	YZX | ||||
| 	ZYZ | ||||
| 	ZYX | ||||
| 	ZXZ | ||||
| 	ZXY | ||||
| ) | ||||
| 
 | ||||
| // T represents a Quaternion, which is an extension of the imaginary numbers;
 | ||||
| // there's all sorts of interesting theory behind it. In 3D graphics we mostly
 | ||||
| // use it as a cheap way of representing rotation since quaternions are cheaper
 | ||||
| // to multiply by, and easier to interpolate than matrices.
 | ||||
| //
 | ||||
| // A Quaternion has two parts: W, the so-called scalar component, and "V", the
 | ||||
| // vector component. The vector component is considered to be the part in 3D
 | ||||
| // space, while W (loosely interpreted) is its 4D coordinate.
 | ||||
| type T struct { | ||||
| 	W float32 | ||||
| 	V vec3.T | ||||
| } | ||||
| 
 | ||||
| // Ident returns the quaternion identity: W=1; V=(0,0,0).
 | ||||
| //
 | ||||
| // As with all identities, multiplying any quaternion by this will yield the same
 | ||||
| // quaternion you started with.
 | ||||
| func Ident() T { | ||||
| 	return T{1., vec3.New(0, 0, 0)} | ||||
| } | ||||
| 
 | ||||
| // Rotate creates an angle from an axis and an angle relative to that axis.
 | ||||
| //
 | ||||
| // This is cheaper than HomogRotate3D.
 | ||||
| func Rotate(angle float32, axis vec3.T) T { | ||||
| 	// angle = (float32(math.Pi) * angle) / 180.0
 | ||||
| 
 | ||||
| 	c, s := math.Cos(angle/2), math.Sin(angle/2) | ||||
| 
 | ||||
| 	return T{c, axis.Scaled(s)} | ||||
| } | ||||
| 
 | ||||
| // X is a convenient alias for q.V[0]
 | ||||
| func (q T) X() float32 { | ||||
| 	return q.V.X | ||||
| } | ||||
| 
 | ||||
| // Y is a convenient alias for q.V[1]
 | ||||
| func (q T) Y() float32 { | ||||
| 	return q.V.Y | ||||
| } | ||||
| 
 | ||||
| // Z is a convenient alias for q.V[2]
 | ||||
| func (q T) Z() float32 { | ||||
| 	return q.V.X | ||||
| } | ||||
| 
 | ||||
| // Add adds two quaternions. It's no more complicated than
 | ||||
| // adding their W and V components.
 | ||||
| func (q1 T) Add(q2 T) T { | ||||
| 	return T{q1.W + q2.W, q1.V.Add(q2.V)} | ||||
| } | ||||
| 
 | ||||
| // Sub subtracts two quaternions. It's no more complicated than
 | ||||
| // subtracting their W and V components.
 | ||||
| func (q1 T) Sub(q2 T) T { | ||||
| 	return T{q1.W - q2.W, q1.V.Sub(q2.V)} | ||||
| } | ||||
| 
 | ||||
| // Mul multiplies two quaternions. This can be seen as a rotation. Note that
 | ||||
| // Multiplication is NOT commutative, meaning q1.Mul(q2) does not necessarily
 | ||||
| // equal q2.Mul(q1).
 | ||||
| func (q1 T) Mul(q2 T) T { | ||||
| 	return T{q1.W*q2.W - vec3.Dot(q1.V, q2.V), vec3.Cross(q1.V, q2.V).Add(q2.V.Scaled(q1.W)).Add(q1.V.Scaled(q2.W))} | ||||
| } | ||||
| 
 | ||||
| // Scale every element of the quaternion by some constant factor.
 | ||||
| func (q1 T) Scale(c float32) T { | ||||
| 	return T{q1.W * c, vec3.New(q1.V.X*c, q1.V.Y*c, q1.V.Z*c)} | ||||
| } | ||||
| 
 | ||||
| // Conjugate returns the conjugate of a quaternion. Equivalent to
 | ||||
| // Quat{q1.W, q1.V.Mul(-1)}.
 | ||||
| func (q1 T) Conjugate() T { | ||||
| 	return T{q1.W, q1.V.Scaled(-1)} | ||||
| } | ||||
| 
 | ||||
| // Len gives the Length of the quaternion, also known as its Norm. This is the
 | ||||
| // same thing as the Len of a Vec4.
 | ||||
| func (q1 T) Len() float32 { | ||||
| 	return math.Sqrt(q1.W*q1.W + vec3.Dot(q1.V, q1.V)) | ||||
| } | ||||
| 
 | ||||
| // Norm is an alias for Len() since both are very common terms.
 | ||||
| func (q1 T) Norm() float32 { | ||||
| 	return q1.Len() | ||||
| } | ||||
| 
 | ||||
| // Normalize the quaternion, returning its versor (unit quaternion).
 | ||||
| //
 | ||||
| // This is the same as normalizing it as a Vec4.
 | ||||
| func (q1 T) Normalize() T { | ||||
| 	length := q1.Len() | ||||
| 
 | ||||
| 	if math.Equal(1, length) { | ||||
| 		return q1 | ||||
| 	} | ||||
| 	if length == 0 { | ||||
| 		return Ident() | ||||
| 	} | ||||
| 	if length == math.InfPos { | ||||
| 		length = math.MaxValue | ||||
| 	} | ||||
| 
 | ||||
| 	return T{q1.W * 1 / length, q1.V.Scaled(1 / length)} | ||||
| } | ||||
| 
 | ||||
| // Inverse of a quaternion. The inverse is equivalent
 | ||||
| // to the conjugate divided by the square of the length.
 | ||||
| //
 | ||||
| // This method computes the square norm by directly adding the sum
 | ||||
| // of the squares of all terms instead of actually squaring q1.Len(),
 | ||||
| // both for performance and precision.
 | ||||
| func (q1 T) Inverse() T { | ||||
| 	return q1.Conjugate().Scale(1 / q1.Dot(q1)) | ||||
| } | ||||
| 
 | ||||
| // Rotate a vector by the rotation this quaternion represents.
 | ||||
| // This will result in a 3D vector. Strictly speaking, this is
 | ||||
| // equivalent to q1.v.q* where the "."" is quaternion multiplication and v is interpreted
 | ||||
| // as a quaternion with W 0 and V v. In code:
 | ||||
| // q1.Mul(Quat{0,v}).Mul(q1.Conjugate()), and
 | ||||
| // then retrieving the imaginary (vector) part.
 | ||||
| //
 | ||||
| // In practice, we hand-compute this in the general case and simplify
 | ||||
| // to save a few operations.
 | ||||
| func (q1 T) Rotate(v vec3.T) vec3.T { | ||||
| 	cross := vec3.Cross(q1.V, v) | ||||
| 	// v + 2q_w * (q_v x v) + 2q_v x (q_v x v)
 | ||||
| 	return v.Add(cross.Scaled(2 * q1.W)).Add(vec3.Cross(q1.V.Scaled(2), cross)) | ||||
| } | ||||
| 
 | ||||
| // Mat4 returns the homogeneous 3D rotation matrix corresponding to the
 | ||||
| // quaternion.
 | ||||
| func (q1 T) Mat4() mat4.T { | ||||
| 	w, x, y, z := q1.W, q1.V.X, q1.V.Y, q1.V.Z | ||||
| 	return mat4.T{ | ||||
| 		1 - 2*y*y - 2*z*z, 2*x*y + 2*w*z, 2*x*z - 2*w*y, 0, | ||||
| 		2*x*y - 2*w*z, 1 - 2*x*x - 2*z*z, 2*y*z + 2*w*x, 0, | ||||
| 		2*x*z + 2*w*y, 2*y*z - 2*w*x, 1 - 2*x*x - 2*y*y, 0, | ||||
| 		0, 0, 0, 1, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Dot product between two quaternions, equivalent to if this was a Vec4.
 | ||||
| func (q1 T) Dot(q2 T) float32 { | ||||
| 	return q1.W*q2.W + vec3.Dot(q1.V, q2.V) | ||||
| } | ||||
| 
 | ||||
| // ApproxEqual returns whether the quaternions are approximately equal, as if
 | ||||
| // FloatEqual was called on each matching element
 | ||||
| func (q1 T) ApproxEqual(q2 T) bool { | ||||
| 	return math.Equal(q1.W, q2.W) && q1.V.ApproxEqual(q2.V) | ||||
| } | ||||
| 
 | ||||
| // OrientationEqual returns whether the quaternions represents the same orientation
 | ||||
| //
 | ||||
| // Different values can represent the same orientation (q == -q) because quaternions avoid singularities
 | ||||
| // and discontinuities involved with rotation in 3 dimensions by adding extra dimensions
 | ||||
| func (q1 T) OrientationEqual(q2 T) bool { | ||||
| 	return q1.OrientationEqualThreshold(q2, math.Epsilon) | ||||
| } | ||||
| 
 | ||||
| // OrientationEqualThreshold returns whether the quaternions represents the same orientation with a given tolerence
 | ||||
| func (q1 T) OrientationEqualThreshold(q2 T, epsilon float32) bool { | ||||
| 	return math.Abs(q1.Normalize().Dot(q2.Normalize())) > 1-math.Epsilon | ||||
| } | ||||
| 
 | ||||
| // Slerp is *S*pherical *L*inear Int*erp*olation, a method of interpolating
 | ||||
| // between two quaternions. This always takes the straightest path on the sphere between
 | ||||
| // the two quaternions, and maintains constant velocity.
 | ||||
| //
 | ||||
| // However, it's expensive and Slerp(q1,q2) is not the same as Slerp(q2,q1)
 | ||||
| func Slerp(q1, q2 T, amount float32) T { | ||||
| 	q1, q2 = q1.Normalize(), q2.Normalize() | ||||
| 	dot := q1.Dot(q2) | ||||
| 
 | ||||
| 	// If the inputs are too close for comfort, linearly interpolate and normalize the result.
 | ||||
| 	if dot > 0.9995 { | ||||
| 		return Nlerp(q1, q2, amount) | ||||
| 	} | ||||
| 
 | ||||
| 	// This is here for precision errors, I'm perfectly aware that *technically* the dot is bound [-1,1], but since Acos will freak out if it's not (even if it's just a liiiiitle bit over due to normal error) we need to clamp it
 | ||||
| 	dot = math.Clamp(dot, -1, 1) | ||||
| 
 | ||||
| 	theta := math.Acos(dot) * amount | ||||
| 	c, s := math.Cos(theta), math.Sin(theta) | ||||
| 	rel := q2.Sub(q1.Scale(dot)).Normalize() | ||||
| 
 | ||||
| 	return q1.Scale(c).Add(rel.Scale(s)) | ||||
| } | ||||
| 
 | ||||
| // Lerp is a *L*inear Int*erp*olation between two Quaternions, cheap and simple.
 | ||||
| //
 | ||||
| // Not excessively useful, but uses can be found.
 | ||||
| func Lerp(q1, q2 T, amount float32) T { | ||||
| 	return q1.Add(q2.Sub(q1).Scale(amount)) | ||||
| } | ||||
| 
 | ||||
| // Nlerp is a *Normalized* *L*inear Int*erp*olation between two Quaternions. Cheaper than Slerp
 | ||||
| // and usually just as good. This is literally Lerp with Normalize() called on it.
 | ||||
| //
 | ||||
| // Unlike Slerp, constant velocity isn't maintained, but it's much faster and
 | ||||
| // Nlerp(q1,q2) and Nlerp(q2,q1) return the same path. You should probably
 | ||||
| // use this more often unless you're suffering from choppiness due to the
 | ||||
| // non-constant velocity problem.
 | ||||
| func Nlerp(q1, q2 T, amount float32) T { | ||||
| 	return Lerp(q1, q2, amount).Normalize() | ||||
| } | ||||
| 
 | ||||
| // FromAngles performs a rotation in the specified order. If the order is not
 | ||||
| // a valid RotationOrder, this function will panic
 | ||||
| //
 | ||||
| // The rotation "order" is more of an axis descriptor. For instance XZX would
 | ||||
| // tell the function to interpret angle1 as a rotation about the X axis, angle2 about
 | ||||
| // the Z axis, and angle3 about the X axis again.
 | ||||
| //
 | ||||
| // Based off the code for the Matlab function "angle2quat", though this implementation
 | ||||
| // only supports 3 single angles as opposed to multiple angles.
 | ||||
| func FromAngles(angle1, angle2, angle3 float32, order RotationOrder) T { | ||||
| 	var s [3]float32 | ||||
| 	var c [3]float32 | ||||
| 
 | ||||
| 	s[0], c[0] = math.Sincos(angle1 / 2) | ||||
| 	s[1], c[1] = math.Sincos(angle2 / 2) | ||||
| 	s[2], c[2] = math.Sincos(angle3 / 2) | ||||
| 
 | ||||
| 	ret := T{} | ||||
| 	switch order { | ||||
| 	case ZYX: | ||||
| 		ret.W = c[0]*c[1]*c[2] + s[0]*s[1]*s[2] | ||||
| 		ret.V = vec3.T{ | ||||
| 			X: c[0]*c[1]*s[2] - s[0]*s[1]*c[2], | ||||
| 			Y: c[0]*s[1]*c[2] + s[0]*c[1]*s[2], | ||||
| 			Z: s[0]*c[1]*c[2] - c[0]*s[1]*s[2], | ||||
| 		} | ||||
| 	case ZYZ: | ||||
| 		ret.W = c[0]*c[1]*c[2] - s[0]*c[1]*s[2] | ||||
| 		ret.V = vec3.T{ | ||||
| 			X: c[0]*s[1]*s[2] - s[0]*s[1]*c[2], | ||||
| 			Y: c[0]*s[1]*c[2] + s[0]*s[1]*s[2], | ||||
| 			Z: s[0]*c[1]*c[2] + c[0]*c[1]*s[2], | ||||
| 		} | ||||
| 	case ZXY: | ||||
| 		ret.W = c[0]*c[1]*c[2] - s[0]*s[1]*s[2] | ||||
| 		ret.V = vec3.T{ | ||||
| 			X: c[0]*s[1]*c[2] - s[0]*c[1]*s[2], | ||||
| 			Y: c[0]*c[1]*s[2] + s[0]*s[1]*c[2], | ||||
| 			Z: c[0]*s[1]*s[2] + s[0]*c[1]*c[2], | ||||
| 		} | ||||
| 	case ZXZ: | ||||
| 		ret.W = c[0]*c[1]*c[2] - s[0]*c[1]*s[2] | ||||
| 		ret.V = vec3.T{ | ||||
| 			X: c[0]*s[1]*c[2] + s[0]*s[1]*s[2], | ||||
| 			Y: s[0]*s[1]*c[2] - c[0]*s[1]*s[2], | ||||
| 			Z: c[0]*c[1]*s[2] + s[0]*c[1]*c[2], | ||||
| 		} | ||||
| 	case YXZ: | ||||
| 		ret.W = c[0]*c[1]*c[2] + s[0]*s[1]*s[2] | ||||
| 		ret.V = vec3.T{ | ||||
| 			X: c[0]*s[1]*c[2] + s[0]*c[1]*s[2], | ||||
| 			Y: s[0]*c[1]*c[2] - c[0]*s[1]*s[2], | ||||
| 			Z: c[0]*c[1]*s[2] - s[0]*s[1]*c[2], | ||||
| 		} | ||||
| 	case YXY: | ||||
| 		ret.W = c[0]*c[1]*c[2] - s[0]*c[1]*s[2] | ||||
| 		ret.V = vec3.T{ | ||||
| 			X: c[0]*s[1]*c[2] + s[0]*s[1]*s[2], | ||||
| 			Y: s[0]*c[1]*c[2] + c[0]*c[1]*s[2], | ||||
| 			Z: c[0]*s[1]*s[2] - s[0]*s[1]*c[2], | ||||
| 		} | ||||
| 	case YZX: | ||||
| 		ret.W = c[0]*c[1]*c[2] - s[0]*s[1]*s[2] | ||||
| 		ret.V = vec3.T{ | ||||
| 			X: c[0]*c[1]*s[2] + s[0]*s[1]*c[2], | ||||
| 			Y: c[0]*s[1]*s[2] + s[0]*c[1]*c[2], | ||||
| 			Z: c[0]*s[1]*c[2] - s[0]*c[1]*s[2], | ||||
| 		} | ||||
| 	case YZY: | ||||
| 		ret.W = c[0]*c[1]*c[2] - s[0]*c[1]*s[2] | ||||
| 		ret.V = vec3.T{ | ||||
| 			X: s[0]*s[1]*c[2] - c[0]*s[1]*s[2], | ||||
| 			Y: c[0]*c[1]*s[2] + s[0]*c[1]*c[2], | ||||
| 			Z: c[0]*s[1]*c[2] + s[0]*s[1]*s[2], | ||||
| 		} | ||||
| 	case XYZ: | ||||
| 		ret.W = c[0]*c[1]*c[2] - s[0]*s[1]*s[2] | ||||
| 		ret.V = vec3.T{ | ||||
| 			X: c[0]*s[1]*s[2] + s[0]*c[1]*c[2], | ||||
| 			Y: c[0]*s[1]*c[2] - s[0]*c[1]*s[2], | ||||
| 			Z: c[0]*c[1]*s[2] + s[0]*s[1]*c[2], | ||||
| 		} | ||||
| 	case XYX: | ||||
| 		ret.W = c[0]*c[1]*c[2] - s[0]*c[1]*s[2] | ||||
| 		ret.V = vec3.T{ | ||||
| 			X: c[0]*c[1]*s[2] + s[0]*c[1]*c[2], | ||||
| 			Y: c[0]*s[1]*c[2] + s[0]*s[1]*s[2], | ||||
| 			Z: s[0]*s[1]*c[2] - c[0]*s[1]*s[2], | ||||
| 		} | ||||
| 	case XZY: | ||||
| 		ret.W = c[0]*c[1]*c[2] + s[0]*s[1]*s[2] | ||||
| 		ret.V = vec3.T{ | ||||
| 			X: s[0]*c[1]*c[2] - c[0]*s[1]*s[2], | ||||
| 			Y: c[0]*c[1]*s[2] - s[0]*s[1]*c[2], | ||||
| 			Z: c[0]*s[1]*c[2] + s[0]*c[1]*s[2], | ||||
| 		} | ||||
| 	case XZX: | ||||
| 		ret.W = c[0]*c[1]*c[2] - s[0]*c[1]*s[2] | ||||
| 		ret.V = vec3.T{ | ||||
| 			X: c[0]*c[1]*s[2] + s[0]*c[1]*c[2], | ||||
| 			Y: c[0]*s[1]*s[2] - s[0]*s[1]*c[2], | ||||
| 			Z: c[0]*s[1]*c[2] + s[0]*s[1]*s[2], | ||||
| 		} | ||||
| 	default: | ||||
| 		panic("Unsupported rotation order") | ||||
| 	} | ||||
| 	return ret | ||||
| } | ||||
| 
 | ||||
| // FromMat4 converts a pure rotation matrix into a quaternion
 | ||||
| func FromMat4(m mat4.T) T { | ||||
| 	// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
 | ||||
| 
 | ||||
| 	if tr := m[0] + m[5] + m[10]; tr > 0 { | ||||
| 		s := 0.5 / math.Sqrt(tr+1.0) | ||||
| 		return T{ | ||||
| 			0.25 / s, | ||||
| 			vec3.T{ | ||||
| 				X: (m[6] - m[9]) * s, | ||||
| 				Y: (m[8] - m[2]) * s, | ||||
| 				Z: (m[1] - m[4]) * s, | ||||
| 			}, | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (m[0] > m[5]) && (m[0] > m[10]) { | ||||
| 		s := 2.0 * math.Sqrt(1.0+m[0]-m[5]-m[10]) | ||||
| 		return T{ | ||||
| 			(m[6] - m[9]) / s, | ||||
| 			vec3.T{ | ||||
| 				X: 0.25 * s, | ||||
| 				Y: (m[4] + m[1]) / s, | ||||
| 				Z: (m[8] + m[2]) / s, | ||||
| 			}, | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if m[5] > m[10] { | ||||
| 		s := 2.0 * math.Sqrt(1.0+m[5]-m[0]-m[10]) | ||||
| 		return T{ | ||||
| 			(m[8] - m[2]) / s, | ||||
| 			vec3.T{ | ||||
| 				X: (m[4] + m[1]) / s, | ||||
| 				Y: 0.25 * s, | ||||
| 				Z: (m[9] + m[6]) / s, | ||||
| 			}, | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	s := 2.0 * math.Sqrt(1.0+m[10]-m[0]-m[5]) | ||||
| 	return T{ | ||||
| 		(m[1] - m[4]) / s, | ||||
| 		vec3.T{ | ||||
| 			X: (m[8] + m[2]) / s, | ||||
| 			Y: (m[9] + m[6]) / s, | ||||
| 			Z: 0.25 * s, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // LookAtV creates a rotation from an eye vector to a center vector
 | ||||
| //
 | ||||
| // It assumes the front of the rotated object at Z- and up at Y+
 | ||||
| func LookAtV(eye, center, up vec3.T) T { | ||||
| 	// http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/#I_need_an_equivalent_of_gluLookAt__How_do_I_orient_an_object_towards_a_point__
 | ||||
| 	// https://bitbucket.org/sinbad/ogre/src/d2ef494c4a2f5d6e2f0f17d3bfb9fd936d5423bb/OgreMain/src/OgreCamera.cpp?at=default#cl-161
 | ||||
| 
 | ||||
| 	direction := center.Sub(eye).Normalized() | ||||
| 
 | ||||
| 	// Find the rotation between the front of the object (that we assume towards Z-,
 | ||||
| 	// but this depends on your model) and the desired direction
 | ||||
| 	rotDir := BetweenVectors(vec3.UnitZN, direction) | ||||
| 
 | ||||
| 	// Recompute up so that it's perpendicular to the direction
 | ||||
| 	// You can skip that part if you really want to force up
 | ||||
| 	//right := direction.Cross(up)
 | ||||
| 	//up = right.Cross(direction)
 | ||||
| 
 | ||||
| 	// Because of the 1rst rotation, the up is probably completely screwed up.
 | ||||
| 	// Find the rotation between the "up" of the rotated object, and the desired up
 | ||||
| 	upCur := rotDir.Rotate(vec3.Zero) | ||||
| 	rotUp := BetweenVectors(upCur, up) | ||||
| 
 | ||||
| 	rotTarget := rotUp.Mul(rotDir) // remember, in reverse order.
 | ||||
| 	return rotTarget.Inverse()     // camera rotation should be inversed!
 | ||||
| } | ||||
| 
 | ||||
| // BetweenVectors calculates the rotation between two vectors
 | ||||
| func BetweenVectors(start, dest vec3.T) T { | ||||
| 	// http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/#I_need_an_equivalent_of_gluLookAt__How_do_I_orient_an_object_towards_a_point__
 | ||||
| 	// https://github.com/g-truc/glm/blob/0.9.5/glm/gtx/quaternion.inl#L225
 | ||||
| 	// https://bitbucket.org/sinbad/ogre/src/d2ef494c4a2f5d6e2f0f17d3bfb9fd936d5423bb/OgreMain/include/OgreVector3.h?at=default#cl-654
 | ||||
| 
 | ||||
| 	start = start.Normalized() | ||||
| 	dest = dest.Normalized() | ||||
| 	epsilon := float32(0.001) | ||||
| 
 | ||||
| 	cosTheta := vec3.Dot(start, dest) | ||||
| 	if cosTheta < -1.0+epsilon { | ||||
| 		// special case when vectors in opposite directions:
 | ||||
| 		// there is no "ideal" rotation axis
 | ||||
| 		// So guess one; any will do as long as it's perpendicular to start
 | ||||
| 		axis := vec3.Cross(vec3.UnitX, start) | ||||
| 		if vec3.Dot(axis, axis) < epsilon { | ||||
| 			// bad luck, they were parallel, try again!
 | ||||
| 			axis = vec3.Cross(vec3.UnitY, start) | ||||
| 		} | ||||
| 
 | ||||
| 		return Rotate(math.Pi, axis.Normalized()) | ||||
| 	} | ||||
| 
 | ||||
| 	axis := vec3.Cross(start, dest) | ||||
| 	s := float32(math.Sqrt(float32(1.0+cosTheta) * 2.0)) | ||||
| 
 | ||||
| 	return T{ | ||||
| 		s * 0.5, | ||||
| 		axis.Scaled(1.0 / s), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (q T) ToAngles(order RotationOrder) vec3.T { | ||||
| 	// this function was adapted from a Go port of Three.js math, github.com/tengge1/go-three-math
 | ||||
| 	// Copyright 2017-2020 The ShadowEditor Authors. All rights reserved.
 | ||||
| 	// Use of e source code is governed by a MIT-style
 | ||||
| 	// license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| 	// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
 | ||||
| 	te := q.Mat4() | ||||
| 	m11, m12, m13 := te[0], te[4], te[8] | ||||
| 	m21, m22, m23 := te[1], te[5], te[9] | ||||
| 	m31, m32, m33 := te[2], te[6], te[10] | ||||
| 
 | ||||
| 	e := vec3.Zero | ||||
| 	switch order { | ||||
| 	default: | ||||
| 		panic("unsupported rotation order") | ||||
| 	case XYZ: | ||||
| 		e.Y = math.Asin(math.Clamp(m13, -1, 1)) | ||||
| 
 | ||||
| 		if math.Abs(m13) < 0.9999999 { | ||||
| 			e.X = math.Atan2(-m23, m33) | ||||
| 			e.Z = math.Atan2(-m12, m11) | ||||
| 		} else { | ||||
| 			e.X = math.Atan2(m32, m22) | ||||
| 			e.Z = 0 | ||||
| 		} | ||||
| 	case YXZ: | ||||
| 		e.X = math.Asin(-math.Clamp(m23, -1, 1)) | ||||
| 
 | ||||
| 		if math.Abs(m23) < 0.9999999 { | ||||
| 			e.Y = math.Atan2(m13, m33) | ||||
| 			e.Z = math.Atan2(m21, m22) | ||||
| 		} else { | ||||
| 			e.Y = math.Atan2(-m31, m11) | ||||
| 			e.Z = 0 | ||||
| 		} | ||||
| 	case ZXY: | ||||
| 		e.X = math.Asin(math.Clamp(m32, -1, 1)) | ||||
| 
 | ||||
| 		if math.Abs(m32) < 0.9999999 { | ||||
| 			e.Y = math.Atan2(-m31, m33) | ||||
| 			e.Z = math.Atan2(-m12, m22) | ||||
| 		} else { | ||||
| 			e.Y = 0 | ||||
| 			e.Z = math.Atan2(m21, m11) | ||||
| 		} | ||||
| 	case ZYX: | ||||
| 		e.Y = math.Asin(-math.Clamp(m31, -1, 1)) | ||||
| 
 | ||||
| 		if math.Abs(m31) < 0.9999999 { | ||||
| 			e.X = math.Atan2(m32, m33) | ||||
| 			e.Z = math.Atan2(m21, m11) | ||||
| 		} else { | ||||
| 			e.X = 0 | ||||
| 			e.Z = math.Atan2(-m12, m22) | ||||
| 		} | ||||
| 	case YZX: | ||||
| 		e.Z = math.Asin(math.Clamp(m21, -1, 1)) | ||||
| 
 | ||||
| 		if math.Abs(m21) < 0.9999999 { | ||||
| 			e.X = math.Atan2(-m23, m22) | ||||
| 			e.Y = math.Atan2(-m31, m11) | ||||
| 		} else { | ||||
| 			e.X = 0 | ||||
| 			e.Y = math.Atan2(m13, m33) | ||||
| 		} | ||||
| 	case XZY: | ||||
| 		e.Z = math.Asin(-math.Clamp(m12, -1, 1)) | ||||
| 
 | ||||
| 		if math.Abs(m12) < 0.9999999 { | ||||
| 			e.X = math.Atan2(m32, m22) | ||||
| 			e.Y = math.Atan2(m13, m11) | ||||
| 		} else { | ||||
| 			e.X = math.Atan2(-m23, m33) | ||||
| 			e.Y = 0 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return e | ||||
| } | ||||
| 
 | ||||
| func (q T) Euler() vec3.T { | ||||
| 	// convert radians to degrees
 | ||||
| 	return q.ToAngles(YXZ).Scaled(180.0 / math.Pi) | ||||
| } | ||||
| 
 | ||||
| func Euler(x, y, z float32) T { | ||||
| 	return FromAngles(math.DegToRad(y), math.DegToRad(x), math.DegToRad(z), YXZ) | ||||
| } | ||||
							
								
								
									
										29
									
								
								plugin/math/quat/quat_suite_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								plugin/math/quat/quat_suite_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| package quat_test | ||||
| 
 | ||||
| import ( | ||||
| 	. "github.com/johanhenriksson/goworld/test" | ||||
| 	. "github.com/onsi/ginkgo/v2" | ||||
| 	. "github.com/onsi/gomega" | ||||
| 
 | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"zworld/plugin/math/quat" | ||||
| 	"zworld/plugin/math/vec3" | ||||
| ) | ||||
| 
 | ||||
| func TestQuat(t *testing.T) { | ||||
| 	RegisterFailHandler(Fail) | ||||
| 	RunSpecs(t, "math/quat") | ||||
| } | ||||
| 
 | ||||
| var _ = Describe("quaternion", func() { | ||||
| 	Context("euler angles", func() { | ||||
| 		It("converts back and forth", func() { | ||||
| 			x, y, z := float32(10), float32(20), float32(30) | ||||
| 			q := quat.Euler(x, y, z) | ||||
| 			r := q.Euler() | ||||
| 			GinkgoWriter.Println(x, y, z, r) | ||||
| 			Expect(r).To(ApproxVec3(vec3.New(x, y, z)), "wrong rotation") | ||||
| 		}) | ||||
| 	}) | ||||
| }) | ||||
							
								
								
									
										28
									
								
								plugin/math/random/random.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								plugin/math/random/random.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| package random | ||||
| 
 | ||||
| import ( | ||||
| 	"math/rand" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	seed := time.Now().Nanosecond() | ||||
| 	Seed(seed) | ||||
| } | ||||
| 
 | ||||
| func Seed(seed int) { | ||||
| 	rand.Seed(int64(seed)) | ||||
| } | ||||
| 
 | ||||
| func Range(min, max float32) float32 { | ||||
| 	return min + rand.Float32()*(max-min) | ||||
| } | ||||
| 
 | ||||
| func Chance(chance float32) bool { | ||||
| 	return Range(0, 1) <= chance | ||||
| } | ||||
| 
 | ||||
| func Choice[T any](slice []T) T { | ||||
| 	idx := rand.Intn(len(slice)) | ||||
| 	return slice[idx] | ||||
| } | ||||
							
								
								
									
										107
									
								
								plugin/math/shape/frustum.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								plugin/math/shape/frustum.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | ||||
| package shape | ||||
| 
 | ||||
| import ( | ||||
| 	"zworld/plugin/math/mat4" | ||||
| 	"zworld/plugin/math/vec3" | ||||
| ) | ||||
| 
 | ||||
| type Plane struct { | ||||
| 	Normal   vec3.T | ||||
| 	Distance float32 | ||||
| } | ||||
| 
 | ||||
| func (p *Plane) normalize() { | ||||
| 	length := p.Normal.LengthSqr() + p.Distance*p.Distance | ||||
| 	p.Normal = p.Normal.Scaled(1 / length) | ||||
| 	p.Distance /= length | ||||
| } | ||||
| 
 | ||||
| func (p *Plane) DistanceToPoint(point vec3.T) float32 { | ||||
| 	return vec3.Dot(p.Normal, point) + p.Distance | ||||
| } | ||||
| 
 | ||||
| type Frustum struct { | ||||
| 	Front, Back, Left, Right, Top, Bottom Plane | ||||
| } | ||||
| 
 | ||||
| func (f *Frustum) IntersectsSphere(s *Sphere) bool { | ||||
| 	if f.Left.DistanceToPoint(s.Center) <= -s.Radius { | ||||
| 		return false | ||||
| 	} | ||||
| 	if f.Right.DistanceToPoint(s.Center) <= -s.Radius { | ||||
| 		return false | ||||
| 	} | ||||
| 	if f.Top.DistanceToPoint(s.Center) <= -s.Radius { | ||||
| 		return false | ||||
| 	} | ||||
| 	if f.Bottom.DistanceToPoint(s.Center) <= -s.Radius { | ||||
| 		return false | ||||
| 	} | ||||
| 	if f.Front.DistanceToPoint(s.Center) <= -s.Radius { | ||||
| 		return false | ||||
| 	} | ||||
| 	if f.Back.DistanceToPoint(s.Center) <= -s.Radius { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func FrustumFromMatrix(vp mat4.T) Frustum { | ||||
| 	f := Frustum{ | ||||
| 		Left: Plane{ | ||||
| 			Normal: vec3.T{ | ||||
| 				X: vp[0+3] + vp[0+0], | ||||
| 				Y: vp[4+3] + vp[4+0], | ||||
| 				Z: vp[8+3] + vp[8+0], | ||||
| 			}, | ||||
| 			Distance: vp[12+3] + vp[12+0], | ||||
| 		}, | ||||
| 		Right: Plane{ | ||||
| 			Normal: vec3.T{ | ||||
| 				X: vp[0+3] - vp[0+0], | ||||
| 				Y: vp[4+3] - vp[4+0], | ||||
| 				Z: vp[8+3] - vp[8+0], | ||||
| 			}, | ||||
| 			Distance: vp[12+3] - vp[12+0], | ||||
| 		}, | ||||
| 		Top: Plane{ | ||||
| 			Normal: vec3.T{ | ||||
| 				X: vp[0+3] - vp[0+1], | ||||
| 				Y: vp[4+3] - vp[4+1], | ||||
| 				Z: vp[8+3] - vp[8+1], | ||||
| 			}, | ||||
| 			Distance: vp[12+3] - vp[12+1], | ||||
| 		}, | ||||
| 		Bottom: Plane{ | ||||
| 			Normal: vec3.T{ | ||||
| 				X: vp[0+3] + vp[0+1], | ||||
| 				Y: vp[4+3] + vp[4+1], | ||||
| 				Z: vp[8+3] + vp[8+1], | ||||
| 			}, | ||||
| 			Distance: vp[12+3] + vp[12+1], | ||||
| 		}, | ||||
| 		Back: Plane{ | ||||
| 			Normal: vec3.T{ | ||||
| 				X: vp[0+3] + vp[0+2], | ||||
| 				Y: vp[4+3] + vp[4+2], | ||||
| 				Z: vp[8+3] + vp[8+2], | ||||
| 			}, | ||||
| 			Distance: vp[12+3] + vp[12+2], | ||||
| 		}, | ||||
| 		Front: Plane{ | ||||
| 			Normal: vec3.T{ | ||||
| 				X: vp[0+3] - vp[0+2], | ||||
| 				Y: vp[4+3] - vp[4+2], | ||||
| 				Z: vp[8+3] - vp[8+2], | ||||
| 			}, | ||||
| 			Distance: vp[12+3] - vp[12+2], | ||||
| 		}, | ||||
| 	} | ||||
| 	f.Front.normalize() | ||||
| 	f.Back.normalize() | ||||
| 	f.Top.normalize() | ||||
| 	f.Bottom.normalize() | ||||
| 	f.Left.normalize() | ||||
| 	f.Right.normalize() | ||||
| 	return f | ||||
| } | ||||
							
								
								
									
										15
									
								
								plugin/math/shape/sphere.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								plugin/math/shape/sphere.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| package shape | ||||
| 
 | ||||
| import "zworld/plugin/math/vec3" | ||||
| 
 | ||||
| type Sphere struct { | ||||
| 	Center vec3.T | ||||
| 	Radius float32 | ||||
| } | ||||
| 
 | ||||
| func (s *Sphere) IntersectsSphere(other *Sphere) bool { | ||||
| 	sepAxis := s.Center.Sub(other.Center) | ||||
| 	radiiSum := s.Radius + other.Radius | ||||
| 	intersects := sepAxis.LengthSqr() < (radiiSum * radiiSum) | ||||
| 	return intersects | ||||
| } | ||||
							
								
								
									
										21
									
								
								plugin/math/vec2/array.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								plugin/math/vec2/array.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| package vec2 | ||||
| 
 | ||||
| import "unsafe" | ||||
| 
 | ||||
| // Array holds an array of 2-component vectors
 | ||||
| type Array []T | ||||
| 
 | ||||
| // Elements returns the number of elements in the array
 | ||||
| func (a Array) Elements() int { | ||||
| 	return len(a) | ||||
| } | ||||
| 
 | ||||
| // Size return the byte size of an element
 | ||||
| func (a Array) Size() int { | ||||
| 	return 8 | ||||
| } | ||||
| 
 | ||||
| // Pointer returns an unsafe pointer to the first element in the array
 | ||||
| func (a Array) Pointer() unsafe.Pointer { | ||||
| 	return unsafe.Pointer(&a[0]) | ||||
| } | ||||
							
								
								
									
										37
									
								
								plugin/math/vec2/operations.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								plugin/math/vec2/operations.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| package vec2 | ||||
| 
 | ||||
| import "zworld/plugin/math" | ||||
| 
 | ||||
| // New returns a vec2 from its components
 | ||||
| func New(x, y float32) T { | ||||
| 	return T{X: x, Y: y} | ||||
| } | ||||
| 
 | ||||
| // NewI returns a vec2 from integer components
 | ||||
| func NewI(x, y int) T { | ||||
| 	return T{X: float32(x), Y: float32(y)} | ||||
| } | ||||
| 
 | ||||
| // Dot returns the dot product of two vectors.
 | ||||
| func Dot(a, b T) float32 { | ||||
| 	return a.X*b.X + a.Y*b.Y | ||||
| } | ||||
| 
 | ||||
| // Distance returns the euclidian distance between two points.
 | ||||
| func Distance(a, b T) float32 { | ||||
| 	return a.Sub(b).Length() | ||||
| } | ||||
| 
 | ||||
| func Min(a, b T) T { | ||||
| 	return T{ | ||||
| 		X: math.Min(a.X, b.X), | ||||
| 		Y: math.Min(a.Y, b.Y), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func Max(a, b T) T { | ||||
| 	return T{ | ||||
| 		X: math.Max(a.X, b.X), | ||||
| 		Y: math.Max(a.Y, b.Y), | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										134
									
								
								plugin/math/vec2/vec2.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								plugin/math/vec2/vec2.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | ||||
| package vec2 | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"zworld/plugin/math" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// Zero is the zero vector
 | ||||
| 	Zero = T{0, 0} | ||||
| 
 | ||||
| 	// One is the one vector
 | ||||
| 	One = T{1, 1} | ||||
| 
 | ||||
| 	// UnitX is the unit vector in the X direction
 | ||||
| 	UnitX = T{1, 0} | ||||
| 
 | ||||
| 	// UnitY is the unit vector in the Y direction
 | ||||
| 	UnitY = T{0, 1} | ||||
| 
 | ||||
| 	InfPos = T{math.InfPos, math.InfPos} | ||||
| 	InfNeg = T{math.InfNeg, math.InfNeg} | ||||
| ) | ||||
| 
 | ||||
| // T holds a 2-component vector of 32-bit floats
 | ||||
| type T struct { | ||||
| 	X, Y float32 | ||||
| } | ||||
| 
 | ||||
| // Slice converts the vector into a 2-element slice of float32
 | ||||
| func (v T) Slice() [2]float32 { | ||||
| 	return [2]float32{v.X, v.Y} | ||||
| } | ||||
| 
 | ||||
| // Length returns the length of the vector.
 | ||||
| // See also LengthSqr and Normalize.
 | ||||
| func (v T) Length() float32 { | ||||
| 	return math.Sqrt(v.LengthSqr()) | ||||
| } | ||||
| 
 | ||||
| // LengthSqr returns the squared length of the vector.
 | ||||
| // See also Length and Normalize.
 | ||||
| func (v T) LengthSqr() float32 { | ||||
| 	return v.X*v.X + v.Y*v.Y | ||||
| } | ||||
| 
 | ||||
| // Abs sets every component of the vector to its absolute value.
 | ||||
| func (v T) Abs() T { | ||||
| 	return T{math.Abs(v.X), math.Abs(v.Y)} | ||||
| } | ||||
| 
 | ||||
| // Normalize normalizes the vector to unit length.
 | ||||
| func (v *T) Normalize() { | ||||
| 	sl := v.LengthSqr() | ||||
| 	if sl == 0 || sl == 1 { | ||||
| 		return | ||||
| 	} | ||||
| 	s := 1 / math.Sqrt(sl) | ||||
| 	v.X *= s | ||||
| 	v.Y *= s | ||||
| } | ||||
| 
 | ||||
| // Normalized returns a unit length normalized copy of the vector.
 | ||||
| func (v T) Normalized() T { | ||||
| 	v.Normalize() | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| // Scaled returns a scaled copy of the vector.
 | ||||
| func (v T) Scaled(f float32) T { | ||||
| 	return T{v.X * f, v.Y * f} | ||||
| } | ||||
| 
 | ||||
| // Scale the vector by a constant (in-place)
 | ||||
| func (v *T) Scale(f float32) { | ||||
| 	v.X *= f | ||||
| 	v.Y *= f | ||||
| } | ||||
| 
 | ||||
| // Swap returns a new vector with components swapped.
 | ||||
| func (v T) Swap() T { | ||||
| 	return T{v.Y, v.X} | ||||
| } | ||||
| 
 | ||||
| // Invert components in place
 | ||||
| func (v *T) Invert() { | ||||
| 	v.X = -v.X | ||||
| 	v.Y = -v.Y | ||||
| } | ||||
| 
 | ||||
| // Inverted returns a new vector with inverted components
 | ||||
| func (v T) Inverted() T { | ||||
| 	return T{-v.X, -v.Y} | ||||
| } | ||||
| 
 | ||||
| // Add each element of the vector with the corresponding element of another vector
 | ||||
| func (v T) Add(v2 T) T { | ||||
| 	return T{v.X + v2.X, v.Y + v2.Y} | ||||
| } | ||||
| 
 | ||||
| // Sub subtracts each element of the vector with the corresponding element of another vector
 | ||||
| func (v T) Sub(v2 T) T { | ||||
| 	return T{v.X - v2.X, v.Y - v2.Y} | ||||
| } | ||||
| 
 | ||||
| // Mul multiplies each element of the vector with the corresponding element of another vector
 | ||||
| func (v T) Mul(v2 T) T { | ||||
| 	return T{v.X * v2.X, v.Y * v2.Y} | ||||
| } | ||||
| 
 | ||||
| // Div divides each element of the vector with the corresponding element of another vector
 | ||||
| func (v T) Div(v2 T) T { | ||||
| 	return T{v.X / v2.X, v.Y / v2.Y} | ||||
| } | ||||
| 
 | ||||
| func (v T) ApproxEqual(v2 T) bool { | ||||
| 	epsilon := float32(0.0001) | ||||
| 	return Distance(v, v2) < epsilon | ||||
| } | ||||
| 
 | ||||
| func (v T) String() string { | ||||
| 	return fmt.Sprintf("%.3f,%.3f", v.X, v.Y) | ||||
| } | ||||
| 
 | ||||
| // Floor each components of the vector
 | ||||
| func (v T) Floor() T { | ||||
| 	return T{math.Floor(v.X), math.Floor(v.Y)} | ||||
| } | ||||
| 
 | ||||
| // Ceil each component of the vector
 | ||||
| func (v T) Ceil() T { | ||||
| 	return T{math.Ceil(v.X), math.Ceil(v.Y)} | ||||
| } | ||||
							
								
								
									
										21
									
								
								plugin/math/vec3/array.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								plugin/math/vec3/array.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| package vec3 | ||||
| 
 | ||||
| import "unsafe" | ||||
| 
 | ||||
| // Array holds an array of 3-component vectors
 | ||||
| type Array []T | ||||
| 
 | ||||
| // Elements returns the number of elements in the array
 | ||||
| func (a Array) Elements() int { | ||||
| 	return len(a) | ||||
| } | ||||
| 
 | ||||
| // Size return the byte size of an element
 | ||||
| func (a Array) Size() int { | ||||
| 	return 12 | ||||
| } | ||||
| 
 | ||||
| // Pointer returns an unsafe pointer to the first element in the array
 | ||||
| func (a Array) Pointer() unsafe.Pointer { | ||||
| 	return unsafe.Pointer(&a[0]) | ||||
| } | ||||
							
								
								
									
										85
									
								
								plugin/math/vec3/operations.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								plugin/math/vec3/operations.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,85 @@ | ||||
| package vec3 | ||||
| 
 | ||||
| import ( | ||||
| 	"zworld/plugin/math" | ||||
| 	"zworld/plugin/math/random" | ||||
| 	"zworld/plugin/math/vec2" | ||||
| ) | ||||
| 
 | ||||
| // New returns a Vec3 from its components
 | ||||
| func New(x, y, z float32) T { | ||||
| 	return T{x, y, z} | ||||
| } | ||||
| 
 | ||||
| func New1(v float32) T { | ||||
| 	return T{v, v, v} | ||||
| } | ||||
| 
 | ||||
| // NewI returns a Vec3 from integer components
 | ||||
| func NewI(x, y, z int) T { | ||||
| 	return T{float32(x), float32(y), float32(z)} | ||||
| } | ||||
| 
 | ||||
| func NewI1(v int) T { | ||||
| 	return T{float32(v), float32(v), float32(v)} | ||||
| } | ||||
| 
 | ||||
| func FromSlice(v []float32) T { | ||||
| 	if len(v) < 3 { | ||||
| 		panic("slice must have at least 3 components") | ||||
| 	} | ||||
| 	return T{v[0], v[1], v[2]} | ||||
| } | ||||
| 
 | ||||
| // Extend a vec2 to a vec3 by adding a Z component
 | ||||
| func Extend(v vec2.T, z float32) T { | ||||
| 	return T{v.X, v.Y, z} | ||||
| } | ||||
| 
 | ||||
| // Dot returns the dot product of two vectors.
 | ||||
| func Dot(a, b T) float32 { | ||||
| 	return a.X*b.X + a.Y*b.Y + a.Z*b.Z | ||||
| } | ||||
| 
 | ||||
| // Cross returns the cross product of two vectors.
 | ||||
| func Cross(a, b T) T { | ||||
| 	return T{ | ||||
| 		a.Y*b.Z - a.Z*b.Y, | ||||
| 		a.Z*b.X - a.X*b.Z, | ||||
| 		a.X*b.Y - a.Y*b.X, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Distance returns the euclidian distance between two points.
 | ||||
| func Distance(a, b T) float32 { | ||||
| 	return a.Sub(b).Length() | ||||
| } | ||||
| 
 | ||||
| func Mid(a, b T) T { | ||||
| 	return a.Add(b).Scaled(0.5) | ||||
| } | ||||
| 
 | ||||
| // Random vector, not normalized.
 | ||||
| func Random(min, max T) T { | ||||
| 	return T{ | ||||
| 		random.Range(min.X, max.X), | ||||
| 		random.Range(min.Y, max.Y), | ||||
| 		random.Range(min.Z, max.Z), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func Min(a, b T) T { | ||||
| 	return T{ | ||||
| 		X: math.Min(a.X, b.X), | ||||
| 		Y: math.Min(a.Y, b.Y), | ||||
| 		Z: math.Min(a.Z, b.Z), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func Max(a, b T) T { | ||||
| 	return T{ | ||||
| 		X: math.Max(a.X, b.X), | ||||
| 		Y: math.Max(a.Y, b.Y), | ||||
| 		Z: math.Max(a.Z, b.Z), | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										205
									
								
								plugin/math/vec3/vec3.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								plugin/math/vec3/vec3.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,205 @@ | ||||
| package vec3 | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"zworld/plugin/math" | ||||
| 
 | ||||
| 	"zworld/plugin/math/vec2" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// Zero is the zero vector
 | ||||
| 	Zero = T{0, 0, 0} | ||||
| 
 | ||||
| 	// One is the unit vector
 | ||||
| 	One = T{1, 1, 1} | ||||
| 
 | ||||
| 	// UnitX is the unit vector in the X direction (right)
 | ||||
| 	UnitX = T{1, 0, 0} | ||||
| 	Right = T{1, 0, 0} | ||||
| 
 | ||||
| 	// UnitXN is the unit vector in the negative X direction (left)
 | ||||
| 	UnitXN = T{-1, 0, 0} | ||||
| 	Left   = T{-1, 0, 0} | ||||
| 
 | ||||
| 	// UnitY is the unit vector in the Y direction (up)
 | ||||
| 	UnitY = T{0, 1, 0} | ||||
| 	Up    = T{0, 1, 0} | ||||
| 
 | ||||
| 	// UnitYN is the unit vector in the negative Y direction (down)
 | ||||
| 	UnitYN = T{0, -1, 0} | ||||
| 	Down   = T{0, -1, 0} | ||||
| 
 | ||||
| 	// UnitZ is the unit vector in the Z direction (forward)
 | ||||
| 	UnitZ   = T{0, 0, 1} | ||||
| 	Forward = T{0, 0, 1} | ||||
| 
 | ||||
| 	// UnitZN is the unit vector in the negative Z direction (backward)
 | ||||
| 	UnitZN   = T{0, 0, -1} | ||||
| 	Backward = T{0, 0, 1} | ||||
| 
 | ||||
| 	InfPos = T{math.InfPos, math.InfPos, math.InfPos} | ||||
| 	InfNeg = T{math.InfNeg, math.InfNeg, math.InfNeg} | ||||
| ) | ||||
| 
 | ||||
| // T holds a 3-component vector of 32-bit floats
 | ||||
| type T struct { | ||||
| 	X, Y, Z float32 | ||||
| } | ||||
| 
 | ||||
| // Slice converts the vector into a 3-element slice of float32
 | ||||
| func (v T) Slice() [3]float32 { | ||||
| 	return [3]float32{v.X, v.Y, v.Z} | ||||
| } | ||||
| 
 | ||||
| // Length returns the length of the vector.
 | ||||
| // See also LengthSqr and Normalize.
 | ||||
| func (v T) Length() float32 { | ||||
| 	return math.Sqrt(v.LengthSqr()) | ||||
| } | ||||
| 
 | ||||
| // LengthSqr returns the squared length of the vector.
 | ||||
| // See also Length and Normalize.
 | ||||
| func (v T) LengthSqr() float32 { | ||||
| 	return v.X*v.X + v.Y*v.Y + v.Z*v.Z | ||||
| } | ||||
| 
 | ||||
| // Abs returns a copy containing the absolute values of the vector components.
 | ||||
| func (v T) Abs() T { | ||||
| 	return T{math.Abs(v.X), math.Abs(v.Y), math.Abs(v.Z)} | ||||
| } | ||||
| 
 | ||||
| // Normalize normalizes the vector to unit length.
 | ||||
| func (v *T) Normalize() { | ||||
| 	sl := v.LengthSqr() | ||||
| 	if sl == 0 || sl == 1 { | ||||
| 		return | ||||
| 	} | ||||
| 	s := 1 / math.Sqrt(sl) | ||||
| 	v.X *= s | ||||
| 	v.Y *= s | ||||
| 	v.Z *= s | ||||
| } | ||||
| 
 | ||||
| // Normalized returns a unit length normalized copy of the vector.
 | ||||
| func (v T) Normalized() T { | ||||
| 	v.Normalize() | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| // Scale the vector by a constant (in-place)
 | ||||
| func (v *T) Scale(f float32) { | ||||
| 	v.X *= f | ||||
| 	v.Y *= f | ||||
| 	v.Z *= f | ||||
| } | ||||
| 
 | ||||
| // Scaled returns a scaled vector
 | ||||
| func (v T) Scaled(f float32) T { | ||||
| 	return T{v.X * f, v.Y * f, v.Z * f} | ||||
| } | ||||
| 
 | ||||
| // ScaleI returns a vector scaled by an integer factor
 | ||||
| func (v T) ScaleI(i int) T { | ||||
| 	return v.Scaled(float32(i)) | ||||
| } | ||||
| 
 | ||||
| // Invert the vector components
 | ||||
| func (v *T) Invert() { | ||||
| 	v.X = -v.X | ||||
| 	v.Y = -v.Y | ||||
| 	v.Z = -v.Z | ||||
| } | ||||
| 
 | ||||
| // Inverted returns an inverted vector
 | ||||
| func (v *T) Inverted() T { | ||||
| 	i := *v | ||||
| 	i.Invert() | ||||
| 	return i | ||||
| } | ||||
| 
 | ||||
| // Floor each components of the vector
 | ||||
| func (v T) Floor() T { | ||||
| 	return T{math.Floor(v.X), math.Floor(v.Y), math.Floor(v.Z)} | ||||
| } | ||||
| 
 | ||||
| // Ceil each component of the vector
 | ||||
| func (v T) Ceil() T { | ||||
| 	return T{math.Ceil(v.X), math.Ceil(v.Y), math.Ceil(v.Z)} | ||||
| } | ||||
| 
 | ||||
| // Round each component of the vector
 | ||||
| func (v T) Round() T { | ||||
| 	return T{math.Round(v.X), math.Round(v.Y), math.Round(v.Z)} | ||||
| } | ||||
| 
 | ||||
| // Add each element of the vector with the corresponding element of another vector
 | ||||
| func (v T) Add(v2 T) T { | ||||
| 	return T{ | ||||
| 		v.X + v2.X, | ||||
| 		v.Y + v2.Y, | ||||
| 		v.Z + v2.Z, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Sub subtracts each element of the vector with the corresponding element of another vector
 | ||||
| func (v T) Sub(v2 T) T { | ||||
| 	return T{ | ||||
| 		v.X - v2.X, | ||||
| 		v.Y - v2.Y, | ||||
| 		v.Z - v2.Z, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Mul multiplies each element of the vector with the corresponding element of another vector
 | ||||
| func (v T) Mul(v2 T) T { | ||||
| 	return T{ | ||||
| 		v.X * v2.X, | ||||
| 		v.Y * v2.Y, | ||||
| 		v.Z * v2.Z, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // XY returns a 2-component vector with the X, Y components of this vector
 | ||||
| func (v T) XY() vec2.T { | ||||
| 	return vec2.T{X: v.X, Y: v.Y} | ||||
| } | ||||
| 
 | ||||
| // XZ returns a 2-component vector with the X, Z components of this vector
 | ||||
| func (v T) XZ() vec2.T { | ||||
| 	return vec2.T{X: v.X, Y: v.Z} | ||||
| } | ||||
| 
 | ||||
| // YZ returns a 2-component vector with the Y, Z components of this vector
 | ||||
| func (v T) YZ() vec2.T { | ||||
| 	return vec2.T{X: v.Y, Y: v.Z} | ||||
| } | ||||
| 
 | ||||
| // Div divides each element of the vector with the corresponding element of another vector
 | ||||
| func (v T) Div(v2 T) T { | ||||
| 	return T{v.X / v2.X, v.Y / v2.Y, v.Z / v2.Z} | ||||
| } | ||||
| 
 | ||||
| // WithX returns a new vector with the X component set to a given value
 | ||||
| func (v T) WithX(x float32) T { | ||||
| 	return T{x, v.Y, v.Z} | ||||
| } | ||||
| 
 | ||||
| // WithY returns a new vector with the Y component set to a given value
 | ||||
| func (v T) WithY(y float32) T { | ||||
| 	return T{v.X, y, v.Z} | ||||
| } | ||||
| 
 | ||||
| // WithZ returns a new vector with the Z component set to a given value
 | ||||
| func (v T) WithZ(z float32) T { | ||||
| 	return T{v.X, v.Y, z} | ||||
| } | ||||
| 
 | ||||
| func (v T) ApproxEqual(v2 T) bool { | ||||
| 	epsilon := float32(0.0001) | ||||
| 	return Distance(v, v2) < epsilon | ||||
| } | ||||
| 
 | ||||
| func (v T) String() string { | ||||
| 	return fmt.Sprintf("%.3f,%.3f,%.3f", v.X, v.Y, v.Z) | ||||
| } | ||||
							
								
								
									
										21
									
								
								plugin/math/vec4/array.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								plugin/math/vec4/array.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| package vec4 | ||||
| 
 | ||||
| import "unsafe" | ||||
| 
 | ||||
| // Array holds an array of 4-component vectors
 | ||||
| type Array []T | ||||
| 
 | ||||
| // Elements returns the number of elements in the array
 | ||||
| func (a Array) Elements() int { | ||||
| 	return len(a) | ||||
| } | ||||
| 
 | ||||
| // Size return the byte size of an element
 | ||||
| func (a Array) Size() int { | ||||
| 	return 16 | ||||
| } | ||||
| 
 | ||||
| // Pointer returns an unsafe pointer to the first element in the array
 | ||||
| func (a Array) Pointer() unsafe.Pointer { | ||||
| 	return unsafe.Pointer(&a[0]) | ||||
| } | ||||
							
								
								
									
										56
									
								
								plugin/math/vec4/operations.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								plugin/math/vec4/operations.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| package vec4 | ||||
| 
 | ||||
| import ( | ||||
| 	"zworld/plugin/math" | ||||
| 	"zworld/plugin/math/random" | ||||
| 	"zworld/plugin/math/vec2" | ||||
| 	"zworld/plugin/math/vec3" | ||||
| ) | ||||
| 
 | ||||
| // New returns a new vec4 from its components
 | ||||
| func New(x, y, z, w float32) T { | ||||
| 	return T{x, y, z, w} | ||||
| } | ||||
| 
 | ||||
| // Extend a vec3 to a vec4 by adding a W component
 | ||||
| func Extend(v vec3.T, w float32) T { | ||||
| 	return T{v.X, v.Y, v.Z, w} | ||||
| } | ||||
| 
 | ||||
| // Extend2 a vec2 to a vec4 by adding the Z and W components
 | ||||
| func Extend2(v vec2.T, z, w float32) T { | ||||
| 	return T{v.X, v.Y, z, w} | ||||
| } | ||||
| 
 | ||||
| // Dot returns the dot product of two vectors.
 | ||||
| func Dot(a, b T) float32 { | ||||
| 	return a.X*b.X + a.Y*b.Y + a.Z*b.Z + a.W*b.W | ||||
| } | ||||
| 
 | ||||
| // Random vector, not normalized.
 | ||||
| func Random(min, max T) T { | ||||
| 	return T{ | ||||
| 		random.Range(min.X, max.X), | ||||
| 		random.Range(min.Y, max.Y), | ||||
| 		random.Range(min.Z, max.Z), | ||||
| 		random.Range(min.W, max.W), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func Min(a, b T) T { | ||||
| 	return T{ | ||||
| 		X: math.Min(a.X, b.X), | ||||
| 		Y: math.Min(a.Y, b.Y), | ||||
| 		Z: math.Min(a.Z, b.Z), | ||||
| 		W: math.Min(a.W, b.W), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func Max(a, b T) T { | ||||
| 	return T{ | ||||
| 		X: math.Max(a.X, b.X), | ||||
| 		Y: math.Max(a.Y, b.Y), | ||||
| 		Z: math.Max(a.Z, b.Z), | ||||
| 		W: math.Max(a.W, b.W), | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										144
									
								
								plugin/math/vec4/vec4.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								plugin/math/vec4/vec4.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,144 @@ | ||||
| package vec4 | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"zworld/plugin/math" | ||||
| 	"zworld/plugin/math/vec2" | ||||
| 	"zworld/plugin/math/vec3" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// Zero is the zero vector
 | ||||
| 	Zero = T{0, 0, 0, 0} | ||||
| 
 | ||||
| 	// One is the unit vector
 | ||||
| 	One = T{1, 1, 1, 1} | ||||
| 
 | ||||
| 	// UnitX returns a unit vector in the X direction
 | ||||
| 	UnitX = T{1, 0, 0, 0} | ||||
| 
 | ||||
| 	// UnitY returns a unit vector in the Y direction
 | ||||
| 	UnitY = T{0, 1, 0, 0} | ||||
| 
 | ||||
| 	// UnitZ returns a unit vector in the Z direction
 | ||||
| 	UnitZ = T{0, 0, 1, 0} | ||||
| 
 | ||||
| 	// UnitW returns a unit vector in the W direction
 | ||||
| 	UnitW = T{0, 0, 0, 1} | ||||
| 
 | ||||
| 	InfPos = T{math.InfPos, math.InfPos, math.InfPos, math.InfPos} | ||||
| 	InfNeg = T{math.InfNeg, math.InfNeg, math.InfNeg, math.InfNeg} | ||||
| ) | ||||
| 
 | ||||
| // T holds a 4-component vector of 32-bit floats
 | ||||
| type T struct { | ||||
| 	X, Y, Z, W float32 | ||||
| } | ||||
| 
 | ||||
| // Slice converts the vector into a 4-element slice of float32
 | ||||
| func (v T) Slice() [4]float32 { | ||||
| 	return [4]float32{v.X, v.Y, v.Z, v.W} | ||||
| } | ||||
| 
 | ||||
| // Length returns the length of the vector.
 | ||||
| // See also LengthSqr and Normalize.
 | ||||
| func (v T) Length() float32 { | ||||
| 	return math.Sqrt(v.LengthSqr()) | ||||
| } | ||||
| 
 | ||||
| // LengthSqr returns the squared length of the vector.
 | ||||
| // See also Length and Normalize.
 | ||||
| func (v T) LengthSqr() float32 { | ||||
| 	return v.X*v.X + v.Y*v.Y + v.Z*v.Z + v.W*v.W | ||||
| } | ||||
| 
 | ||||
| // Abs sets every component of the vector to its absolute value.
 | ||||
| func (v T) Abs() T { | ||||
| 	return T{ | ||||
| 		math.Abs(v.X), | ||||
| 		math.Abs(v.Y), | ||||
| 		math.Abs(v.Z), | ||||
| 		math.Abs(v.W), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Normalize normalizes the vector to unit length.
 | ||||
| func (v *T) Normalize() { | ||||
| 	sl := v.LengthSqr() | ||||
| 	if sl == 0 || sl == 1 { | ||||
| 		return | ||||
| 	} | ||||
| 	s := 1 / math.Sqrt(sl) | ||||
| 	v.X *= s | ||||
| 	v.Y *= s | ||||
| 	v.Z *= s | ||||
| 	v.W *= s | ||||
| } | ||||
| 
 | ||||
| // Normalized returns a unit length normalized copy of the vector.
 | ||||
| func (v T) Normalized() T { | ||||
| 	v.Normalize() | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| // Scaled the vector
 | ||||
| func (v T) Scaled(f float32) T { | ||||
| 	return T{v.X * f, v.Y * f, v.Z * f, v.W * f} | ||||
| } | ||||
| 
 | ||||
| // Scale the vector by a constant (in-place)
 | ||||
| func (v *T) Scale(f float32) { | ||||
| 	v.X *= f | ||||
| 	v.Y *= f | ||||
| 	v.Z *= f | ||||
| 	v.W *= f | ||||
| } | ||||
| 
 | ||||
| // Invert the vector components
 | ||||
| func (v *T) Invert() { | ||||
| 	v.X = -v.X | ||||
| 	v.Y = -v.Y | ||||
| 	v.Z = -v.Z | ||||
| 	v.W = -v.W | ||||
| } | ||||
| 
 | ||||
| // Inverted returns an inverted vector
 | ||||
| func (v T) Inverted() T { | ||||
| 	v.Invert() | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| // Add each element of the vector with the corresponding element of another vector
 | ||||
| func (v T) Add(v2 T) T { | ||||
| 	return T{v.X + v2.X, v.Y + v2.Y, v.Z + v2.Z, v.W + v2.W} | ||||
| } | ||||
| 
 | ||||
| // Sub subtracts each element of the vector with the corresponding element of another vector
 | ||||
| func (v T) Sub(v2 T) T { | ||||
| 	return T{v.X - v2.X, v.Y - v2.Y, v.Z - v2.Z, v.W - v2.W} | ||||
| } | ||||
| 
 | ||||
| // Mul multiplies each element of the vector with the corresponding element of another vector
 | ||||
| func (v T) Mul(v2 T) T { | ||||
| 	return T{v.X * v2.X, v.Y * v2.Y, v.Z * v2.Z, v.W * v2.W} | ||||
| } | ||||
| 
 | ||||
| // XY returns a 2-component vector with the X, Y components
 | ||||
| func (v T) XY() vec2.T { | ||||
| 	return vec2.T{X: v.X, Y: v.Y} | ||||
| } | ||||
| 
 | ||||
| // XYZ returns a 3-component vector with the X, Y, Z components
 | ||||
| func (v T) XYZ() vec3.T { | ||||
| 	return vec3.T{X: v.X, Y: v.Y, Z: v.Z} | ||||
| } | ||||
| 
 | ||||
| // Div divides each element of the vector with the corresponding element of another vector
 | ||||
| func (v T) Div(v2 T) T { | ||||
| 	return T{v.X / v2.X, v.Y / v2.Y, v.Z / v2.Z, v.W / v2.W} | ||||
| } | ||||
| 
 | ||||
| func (v T) String() string { | ||||
| 	return fmt.Sprintf("%.3f,%.3f,%.3f,%.3f", v.X, v.Y, v.Z, v.W) | ||||
| } | ||||
							
								
								
									
										111
									
								
								plugin/objloader/parser.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								plugin/objloader/parser.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,111 @@ | ||||
| package objloader | ||||
| 
 | ||||
| import ( | ||||
| 	"os" | ||||
| 	"zworld/library/zos" | ||||
| 	"zworld/plugin/math/vec3" | ||||
| ) | ||||
| 
 | ||||
| type FByteView = zos.FByteView | ||||
| type FVertex struct { | ||||
| 	pos int32 | ||||
| 	nor int32 | ||||
| 	tex int32 | ||||
| } | ||||
| type FMesh struct { | ||||
| 	Positions []vec3.T | ||||
| 	Normals   []vec3.T | ||||
| 	TexCoords []vec3.T | ||||
| 	Faces     [][]FVertex | ||||
| 	Indices   []uint16 | ||||
| } | ||||
| type FObjLoader struct { | ||||
| 	reader *zos.FReader | ||||
| 	mesh   *FMesh | ||||
| } | ||||
| 
 | ||||
| func (l *FObjLoader) GetTexCoord(bv *FByteView) error { | ||||
| 	x := bv.GetFloat() | ||||
| 	y := bv.GetFloat() | ||||
| 	if err := bv.Err(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	w := bv.GetFloat() | ||||
| 	if bv.Err() != nil { | ||||
| 		w = 0 | ||||
| 	} | ||||
| 	v := vec3.T{X: x, Y: y, Z: w} | ||||
| 	l.mesh.TexCoords = append(l.mesh.TexCoords, v) | ||||
| 	return nil | ||||
| } | ||||
| func (l *FObjLoader) GetPosition(bv *FByteView) error { | ||||
| 	x := bv.GetFloat() | ||||
| 	y := bv.GetFloat() | ||||
| 	z := bv.GetFloat() | ||||
| 	if err := bv.Err(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	v := vec3.T{X: x, Y: y, Z: z} | ||||
| 	l.mesh.Positions = append(l.mesh.Positions, v) | ||||
| 	return nil | ||||
| } | ||||
| func (l *FObjLoader) GetNormal(bv *FByteView) error { | ||||
| 	x := bv.GetFloat() | ||||
| 	y := bv.GetFloat() | ||||
| 	z := bv.GetFloat() | ||||
| 	if err := bv.Err(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	v := vec3.T{X: x, Y: y, Z: z} | ||||
| 	l.mesh.Normals = append(l.mesh.Normals, v) | ||||
| 	return nil | ||||
| } | ||||
| func (l *FObjLoader) GetFace(b *FByteView) error { | ||||
| 	var faces []FVertex | ||||
| 	for i := 0; i < 3; i++ { | ||||
| 		line := b.GetToken() | ||||
| 		bv := zos.NewByteView(line) | ||||
| 		bv.SetSign('/') | ||||
| 		v := FVertex{} | ||||
| 		v.pos = bv.GetInt() | ||||
| 		if err := bv.Err(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		v.nor = bv.GetInt() | ||||
| 		v.tex = bv.GetInt() | ||||
| 		faces = append(faces, v) | ||||
| 	} | ||||
| 	l.mesh.Faces = append(l.mesh.Faces, faces) | ||||
| 	return nil | ||||
| } | ||||
| func (l *FObjLoader) Parse() error { | ||||
| 	for line, err := l.reader.ReadLine(); err == nil; line, err = l.reader.ReadLine() { | ||||
| 		bv := zos.NewByteView(line) | ||||
| 		bv.SkipWhitespace() | ||||
| 		token := string(bv.ReadTokenBySpace()) | ||||
| 		//zlog.Infof("line {}", string(line))
 | ||||
| 		switch token { | ||||
| 		case "v": | ||||
| 			err = l.GetPosition(bv) | ||||
| 		case "vt": | ||||
| 			err = l.GetTexCoord(bv) | ||||
| 		case "vn": | ||||
| 			err = l.GetNormal(bv) | ||||
| 		case "f": | ||||
| 			err = l.GetFace(bv) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| func LoadObj(filename string) (err error, mesh *FMesh) { | ||||
| 	file, err := os.Open(filename) | ||||
| 	if err != nil { | ||||
| 		return err, nil | ||||
| 	} | ||||
| 	l := &FObjLoader{reader: zos.NewReader(file), mesh: &FMesh{}} | ||||
| 	err = l.Parse() | ||||
| 	return err, l.mesh | ||||
| } | ||||
							
								
								
									
										10
									
								
								test/vulkan_instance_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								test/vulkan_instance_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| package test | ||||
| 
 | ||||
| import ( | ||||
| 	"testing" | ||||
| 	"zworld/engine/render/vulkan/instance" | ||||
| ) | ||||
| 
 | ||||
| func TestInstance(t *testing.T) { | ||||
| 	instance.New("hello world") | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user