Commit for 2024.12.01 23-07-01.7z

This commit is contained in:
mrq 2024-12-01 23:07:00 -06:00
parent f1ab22c4a0
commit bb356ee399
951 changed files with 357298 additions and 3163 deletions

9
.gitignore vendored
View File

@ -7,7 +7,6 @@
*.lo
*.o
*.obj
*.tmp
# Precompiled Headers
*.gch
@ -44,10 +43,4 @@
# ISOs
*.iso
*.cdi
*.gdi
# Temporary
/dep
*.sublime-*
*.html
myconfig.config
*.gdi

View File

@ -45,8 +45,9 @@ EXT_LIB_NAME += ext
#VULKAN_SDK_PATH += /c/VulkanSDK/1.3.211.0/
#VULKAN_SDK_PATH += /c/VulkanSDK/1.3.216.0/
#VULKAN_SDK_PATH += /c/VulkanSDK/1.3.224.1/
VULKAN_SDK_PATH += /c/VulkanSDK/1.3.231.1/
#VULKAN_SDK_PATH += /c/VulkanSDK/1.3.231.1/
#VULKAN_SDK_PATH += /c/VulkanSDK/1.3.261.1/
VULKAN_SDK_PATH += /c/VulkanSDK/1.3.296.0/
GLSLC += $(VULKAN_SDK_PATH)/Bin/glslc
SPV_OPTIMIZER += $(VULKAN_SDK_PATH)/Bin/spirv-opt
@ -59,10 +60,11 @@ INCS += -I$(ENGINE_INC_DIR) -I./dep/include/ #-I/mingw64/include/
LIBS += -L$(ENGINE_LIB_DIR) -L$(LIB_DIR)/$(PREFIX_PATH) -L$(LIB_DIR)/$(ARCH)/$(CC) -L$(LIB_DIR)/$(ARCH) #-L/mingw64/lib/
LINKS += $(UF_LIBS) $(EXT_LIBS) $(DEPS)
DEPS +=
DEPS +=
FLAGS +=
ifneq (,$(findstring win64,$(ARCH)))
REQ_DEPS += $(RENDERER) json:nlohmann toml png zlib luajit reactphysics meshoptimizer xatlas simd ctti gltf imgui fmt curl freetype openal ogg ffx:fsr # ncurses openvr draco discord bullet ultralight-ux
REQ_DEPS += $(RENDERER) json:nlohmann toml png zlib luajit reactphysics meshoptimizer xatlas simd ctti gltf imgui fmt curl freetype openal ogg ffx:fsr cpptrace # ncurses openvr draco discord bullet ultralight-ux
FLAGS += -DUF_ENV_WINDOWS -DUF_ENV_WIN64 -DWIN32_LEAN_AND_MEAN
DEPS += -lgdi32 -ldwmapi
LINKS += #-Wl,-subsystem,windows
@ -74,7 +76,7 @@ else ifneq (,$(findstring dreamcast,$(ARCH)))
endif
ifneq (,$(findstring vulkan,$(REQ_DEPS)))
FLAGS += -DVK_USE_PLATFORM_WIN32_KHR -DUF_USE_VULKAN
DEPS += -lvulkan -lspirv-cross-core -lspirv-cross-cpp #-lVulkanMemoryAllocator
DEPS += -lvulkan-1 -lspirv-cross-core -lspirv-cross-cpp #-lVulkanMemoryAllocator
INCS += -I$(VULKAN_SDK_PATH)/include -I./dep/include/spirv_cross/
LIBS += -L$(VULKAN_SDK_PATH)/Lib
endif
@ -118,6 +120,9 @@ ifneq (,$(findstring imgui,$(REQ_DEPS)))
INCS += -I./dep/include/imgui/
INCS += -I./dep/include/imgui/backends
endif
ifneq (,$(findstring imgui,$(REQ_DEPS)))
DEPS += -lcpptrace
endif
ifneq (,$(findstring json,$(REQ_DEPS)))
FLAGS += -DUF_USE_JSON
DEPS +=
@ -345,8 +350,8 @@ endif
%.spv: %.glsl
$(GLSLC) --target-env=vulkan1.2 -o $@ $<
$(SPV_LINTER) $@
$(SPV_OPTIMIZER) --preserve-bindings --preserve-spec-constants -O $@ -o $@
@-$(SPV_LINTER) $@
@-$(SPV_OPTIMIZER) --preserve-bindings --preserve-spec-constants -O $@ -o $@
shaders: $(TARGET_SHADERS)
@ -418,4 +423,4 @@ backup:
#make CC=zig RENDERER=opengl clean
#make CC=zig RENDERER=vulkan clean
@-rm $(shell find $(ENGINE_SRC_DIR) -name "*.o") $(shell find $(EXT_SRC_DIR) -name "*.o") $(shell find $(DEP_SRC_DIR) -name "*.o")
$(7Z) a -bsp1 -r ../misc/backups/$(shell date +"%Y.%m.%d\ %H-%M-%S").7z .
$(7Z) a -bsp1 -r ../misc/backups/$(shell date +"%Y.%m.%d\ %H-%M-%S").7z . -xr!.git

View File

@ -6,7 +6,7 @@
"meshes": { "interleaved": false },
"lights": { "enabled": true,
"useLightmaps": false,
"max": 32,
"max": 16,
"shadows": {
"enabled": true,
"update": 4,
@ -33,7 +33,7 @@
"limiter": 0.125,
"size": 256,
"dispatch": 8,
"cascades": 3,
"cascades": 4,
"cascadePower": 1.5,
"granularity": 12,
"voxelizeScale": 1,
@ -58,7 +58,8 @@
}
},
"graph": {
"initial buffer elements": 1024
"initial buffer elements": 1024,
"global storage": false
},
"ext": {
"vulkan": {
@ -68,9 +69,10 @@
"messages": false,
"checkpoints": false,
"filters": [
"0x71500fba" // VUID-vkDestroyDevice-device-00378 (don't care about a clean cleanup)
,"0xe5d1743c" // VUID-vkCmdDispatch-None-02699 (problem when using VXGI)
"0xe5d1743c" // VUID-vkCmdDispatch-None-02699 (problem when using VXGI)
,"0x6714bd0c" // VUID-vkCmdDispatch-format-07753 (for some dumb shit)
// ,"0x71500fba" // VUID-vkDestroyDevice-device-00378 (don't care about a clean cleanup)
// ,"0x141cb623" // UNASSIGNED-Threading-MultipleThreads ("false-positive" multithreading)
/*
@ -92,12 +94,15 @@
// "size": [ 640, 480 ]
},
// "gpu": 7817, // 2060
"gpu": 29631, // 6800XT
// "gpu": 29631, // 6800XT
"gpu": 10114, // 4070Ti
// "gpu": 5710, // iGPU
"experimental": {
"rebuild on tick begin": false,
"batch queue submissions": true,
"dedicated thread": false,
"memory budget": false
"memory budget": false,
"register render modes": false
},
"invariant": {
"default stage buffers": true,
@ -107,11 +112,12 @@
},
"pipelines": {
"deferred": true,
"vsync": true,
"hdr": true,
"gui": true,
"vsync": false,
"hdr": false,
"vxgi": true,
"culling": true,
"bloom": true,
"bloom": false,
"rt": false,
"fsr": false,
"postProcess": false // "postProcess.chromab" // false
@ -274,7 +280,7 @@
"compression": "gz"
},
"imgui": {
"enabled": true
"enabled": false
},
"fsr": {
"enabled": true,
@ -283,16 +289,17 @@
"preset": "native" // native (1x), quality (1.5x), balanced (1.7x), performance (2.0x), ultra (3.0x)
},
"reactphysics": {
"global storage": false,
"timescale": 0.01666666666,
"interpolate": false,
"interpolate": true,
"gravity": {
"mode": "universal",
"mode": "universal", // default / per-object / universal
"constant": 6.67408e-11
},
"debug draw": {
"enabled": false,
"line width": 8,
"layer": "Gui",
"layer": "",
"rate": 0.0125
}
},
@ -334,7 +341,7 @@
"render modes": { "gui": true, "deferred": true },
"limiters": {
"deltaTime": 5,
"framerate": "auto"
"framerate": 0 // "auto"
},
"threads": {
"workers" : "auto",
@ -343,17 +350,17 @@
"debug": {
"framerate": {
"print": true,
"every": 2
"every": 1
},
"garbage collection": {
"enabled": true,
"mode": 1,
"rate": 4,
"rate": 1,
"announce": true
},
"entity": {
"delete children on destroy": false,
"delete components on destroy": false
"delete components on destroy": true
},
"userdata": {
"auto destruct": true,
@ -373,7 +380,7 @@
"window" : {
"terminal" : {
"ncurses" : false,
"visible" : true
"visible" : false
},
"keyboard" : {
"repeat" : false
@ -382,7 +389,7 @@
"visible" : true,
"center" : false,
"sensitivity": [ 2, 2 ],
"smoothing": [ 4, 4 ]
"smoothing": [ 0, 0 ]
},
"mode" : "windowed", // fullscreen, borderless, windowed
"icon" : "./data/textures/icon.png",

View File

@ -0,0 +1,43 @@
{
"type": "Object",
"name": "Craeture",
"ignore": false,
"assets": [
"./craetureModel.json",
"./scripts/craeture.lua"
],
"behaviors": [
"CraetureBehavior"
],
"transform": {
"position": [ 0, 1.5, 21 ],
"rotation": {
"axis": [ 0, 1, 0 ],
"angle": 0
},
"scale": [ 1, 1, 1 ]
},
"system": {
"hot reload": {
"enabled": true
}
},
"metadata": {
"name": "Craeture",
"physics": {
"gravity": [ 0, -9.81, 0 ],
"inertia": [ 0, 0, 0 ],
"type": "capsule",
"radius": 2,
"height": 1.0,
"mass": 100,
"friction": 0.95,
"restitution": 0.0,
"shared": false
}
}
}

View File

@ -0,0 +1,60 @@
{
"type": "Object",
"name": "Craeture Model",
"ignore": false,
"import": "/model.json",
"assets": [
// "/player/bear.glb"
{ "filename": "/player/bear/graph.json" }
],
"behaviors": [
"CraetureBehavior"
],
"transform": {
"position": [ 0, -2.0, 0 ],
// "position": [ 12.5715, 3.53811, 7.6238 ],
// "position": [ 1.635, -0.384, -20.409 ], // -0.384
"rotation": {
"axis": [ 0, 1, 0 ],
"angle": 0
},
"scale": [ 0.16, 0.16, 0.16 ],
"reference": "parent"
},
"system": {
"hot reload": {
"enabled": true
}
},
"metadata": {
"graph": {
"key": "Craeture",
"debug": {
"print": {
"animations": true
}
},
"exporter": {
"enabled": false,
"unwrap": false,
"optimize": false
},
"baking": {
"enabled": false
},
"lights": {
"lightmap": false
},
"renderer": {
"cull mode": "front",
"filter": "linear",
"flip textures": false,
"invert": false,
"skinned": true
},
"animations": {
"animation": "idle"
}
}
}
}

View File

@ -4,5 +4,17 @@
"behaviors": [
"GuiManagerBehavior"
],
"ignore": false
"ignore": false,
"metadata": {
// GUI settings
"clickable": false,
"hoverable": false,
"uv": [ 0, 0, 1, 1 ],
"color": [ 1, 1, 1, 1 ],
"mode": 1
// Glyph settings
}
}

View File

@ -1,6 +1,6 @@
{
"name": "Gui: Text",
"type": "Object",
"name": "Gui: Menu",
"type": "Gui",
"behaviors": [
"GuiBehavior"
],
@ -13,10 +13,8 @@
},
"scale": [ 1, 1, 1 ]
},
"metadata": {
"uv": [ 0, 0, 1, 1 ],
"color": [ 1, 1, 1, 1 ],
"location": "",
"scaling": "relative"
}
"metadata": {},
"assets": [
"./scripts/main.lua"
]
}

View File

@ -0,0 +1,28 @@
{
"name": "Gui: Dialogue Name Box",
"type": "Gui",
"behaviors": [
"GuiBehavior"
],
"ignore": false,
"transform": {
"position": [ -0.2750, 0.28, 0 ],
"rotation": {
"axis": [ 0, 0, 1 ],
"angle": 0
},
"scale": [ 0.2062, 0.0917, 1 ]
},
"system": {
"hot reload": {
"enabled": true
}
},
"metadata": {
"color": [ 0.7, 0.7, 1, 1 ],
"scaling": "none"
},
"assets": [
"./textures/name_box.png"
]
}

View File

@ -0,0 +1,23 @@
{
"name": "Gui: Dialogue Name String",
"type": "Gui",
"behaviors": [
"GuiBehavior"
],
"ignore": false,
"transform": {
"position": [ -0.15, -0.035, 0 ],
"rotation": {
"axis": [ 0, 0, 1 ],
"angle": 0
}
},
"system": {
"hot reload": {
"enabled": true
}
},
"metadata": {
"string": " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
}
}

View File

@ -0,0 +1,141 @@
local ent = ent
local scene = entities.currentScene()
local controller = entities.controller()
local metadata = ent:getComponent("Metadata")
local masterdata = scene:getComponent("Metadata")
if metadata["dialogue"] == nil then
metadata["dialogue"] = {}
end
local children = {
text = {
box = ent:loadChild("./text-box.json",true),
string = ent:loadChild("./text-string.json",true),
},
name = {
box = ent:loadChild("./name-box.json",true),
string = ent:loadChild("./name-string.json",true),
}
}
local soundEmitter = ent:loadChild("./sound.json",true)
local timer = Timer.new()
if not timer:running() then timer:start() end
Static = {
values = {},
get = function( obj )
if obj == nil then
obj = scene
end
if Static.values[""..obj:uid()] == nil then
Static.values[""..obj:uid()] = {}
end
return Static.values[""..obj:uid()]
end
}
local bind = function( text, box, str, color )
local box_transform = box:getComponent("Transform")
local text_transform = text:getComponent("Transform")
text_transform:setReference( box_transform )
text_transform.scale.x = 1.0 / box_transform.scale.x
text_transform.scale.y = 1.0 / box_transform.scale.y
if color ~= nil then
local box_metadata = box:getComponent("Metadata")
box_metadata["color"] = color
box:setComponent("Metadata", box_metadata)
end
local text_metadata = text:getComponent("Metadata")
text_metadata["string"] = str
text:setComponent("Metadata", text_metadata)
end
-- bind references
if children.name.box:uid() > 0 and children.name.string:uid() > 0 then
bind( children.name.string, children.name.box, metadata["dialogue"]["name"] or "%name%", metadata["dialogue"]["name color"] )
end
if children.text.box:uid() > 0 and children.text.string:uid() > 0 then
bind( children.text.string, children.text.box, metadata["dialogue"]["text"] or "%text%", metadata["dialogue"]["text color"] )
end
--[[
local destination = function( obj, x, y, z )
local static = Static.get(obj)
local transform = obj:getComponent("Transform")
static.from = Vector3f(x or transform.position.x, y or transform.position.y, z or transform.position.z)
end
-- circleOut
destination(children.circleOut, nil, -2, 0)
destination(children.circleIn, nil, 2, 0)
destination(children.coverBar, -1.5, nil, 0)
destination(children.commandText, -1.5, nil, 0)
destination(children.tenkouseiOption, -1.5, nil, 0)
destination(children.closeOption, -1.5, nil, 0)
destination(children.quit, -1.5, nil, 0)
]]
--[[
local playSound = function( key )
local url = "/ui/" .. key .. ".ogg"
-- local assetLoader = scene:getComponent("Asset")
-- assetLoader:cache(soundEmitter:formatHookName("asset:Load.%UID%"), string.resolveURI(url), "")
end
ent:addHook("menu:Close.%UID%", function( json )
playSound("menu close")
if metadata["system"]["hooks"] == nil then metadata["system"]["hooks"] = {} end
metadata["system"]["hooks"]["onClose"] = json["callback"];
metadata["system"]["closing"] = true;
ent:setComponent("Metadata", metadata)
end )
playSound("menu open")
]]
local states = {}
ent:bind( "tick", function(self)
local closed = inputs.key("E") or inputs.key("R_Y")
-- close when finished
-- dialogue box
if children.text.string:uid() > 0 then
local child = children.text.string
if states[child:uid()] == nil then
states[child:uid()] = {
timer = 0,
finished = false,
speed = -1
}
end
local state = states[child:uid()]
if state.finished then
if closed then
entities.destroy(self)
end
else
local metadata = child:getComponent("Metadata")
if state.speed < 0 then
state.speed = metadata["speed"]
end
if metadata["range"] ~= nil and state.timer + state.speed < timer:elapsed() then
local pos = metadata["range"][2]
local str = metadata["string"]
state.finished = not (pos < string.len(str))
if pos < string.len(str) then
metadata["range"][2] = pos + 1
state.timer = timer:elapsed()
child:setComponent("Metadata", metadata)
end
end
end
end
end )

View File

@ -0,0 +1,38 @@
{
"type": "Object",
"name": "Sound Emitter",
"ignore": false,
"assets": [
],
"behaviors": [
"SoundEmitterBehavior"
],
"transform": {
"position": [ 0, 0, 0 ],
"rotation": {
"axis": [ 0, 1, 0 ],
"angle": 0
},
"scale": [ 1, 1, 1 ]
},
"system": {
"hot reload": {
"enabled": true
},
"defaults": {
"render": true,
"asset load": true
},
"load": {
"ignore": true
}
},
"metadata": {
"audio": {
"spatial": false,
"loop": false,
"volume": "sfx",
"rolloffFactor": 2
}
}
}

View File

@ -0,0 +1,27 @@
{
"name": "Gui: Dialogue Text Box",
"type": "Gui",
"behaviors": [
"GuiBehavior"
],
"ignore": false,
"transform": {
"position": [ 0, 0.5944, 0 ],
"rotation": {
"axis": [ 0, 0, 1 ],
"angle": 0
},
"scale": [ 0.5005, 0.3565, 1 ]
},
"system": {
"hot reload": {
"enabled": true
}
},
"metadata": {
"scaling": "none"
},
"assets": [
"./textures/text_box.png"
]
}

View File

@ -0,0 +1,25 @@
{
"name": "Gui: Dialogue Text String",
"type": "Gui",
"behaviors": [
"GuiBehavior"
],
"ignore": false,
"transform": {
"position": [ -0.4, -0.2, 0 ],
"rotation": {
"axis": [ 0, 0, 1 ],
"angle": 0
}
},
"system": {
"hot reload": {
"enabled": true
}
},
"metadata": {
"string": " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
"speed": 0.0125,
"range": [ 0, 0 ]
}
}

View File

@ -0,0 +1,22 @@
{
"name": "Menu: Left Rectangle",
"type": "Gui",
"ignore": false,
"transform": {
"position": [ -0.933374, 0, 0 ],
"rotation": {
"axis": [ 1, 0, 0 ],
"angle": 0
},
"scale": [ 0.067, 1, 1 ]
},
"metadata": {
"color": [ 1, 0.749, 0.368, 1 ],
"shader": 1,
"scaling": "none"
},
"assets": [
"./textures/square.png"
]
}

View File

@ -8,17 +8,11 @@
"axis": [ 1, 0, 0 ],
"angle": 0
},
"scale": [ 1, 1, 1 ]
"scale": [ 1.0, 1, 1 ]
},
"metadata": {
"clickable": false,
"hoverable": false,
"uv": [ 0, 0, 1, 1 ],
"color": [ 1, 1, 1, 0.8 ],
"location": "",
"scaling": [ 0.64, 1 ],
"mode": "flat"
"scaling": "relative-x"
},
"assets": [
"textures/circle-in.png"

View File

@ -11,14 +11,10 @@
"scale": [ 1, 1, 1 ]
},
"metadata": {
"clickable": false,
"hoverable": true,
"uv": [ 0, 0, 1, 1 ],
"color": [ 1, 1, 1, 0.8 ],
"location": "",
"scaling": [ 0.64, 1 ],
"mode": "flat"
"hoverable": true,
"scaling": "relative-x"
},
"assets": [
"textures/circle-out.png"

View File

@ -8,19 +8,15 @@
"axis": [ 0, 0, 1 ],
"angle": 0
},
"scale": [ 1, 1, 1 ]
"scale": [ 2, 2, 1 ]
},
"system": {
"hot reload": {
"enabled": true
}
},
"metadata": {
"uv": [ 0, 0, 1, 1 ],
"location": "",
"scaling": "relative",
"debug": {
"moveable": false
},
"text settings": {
"scale": 2,
"font": "Coolvetica.ttf",
"string": "Grimgram"
}
"font": "Coolvetica.ttf",
"string": "Menu"
}
}

View File

@ -15,14 +15,6 @@
"scale": [ 1, 1, 1 ]
},
"metadata": {
"clickable": true,
"hoverable": true,
"uv": [ 0, 0, 1, 1 ],
"color": [ 1, 1, 1, 1 ],
"location": "",
"scaling": "relative",
"depth": 0,
"mode": "flat"
"scaling": "none"
}
}

View File

@ -12,14 +12,7 @@
},
"metadata": {
"clickable": true,
"hoverable": true,
"uv": [ 0, 0, 1, 1 ],
"location": "",
"scaling": "relative",
"debug": {
"moveable": false
},
"events": {
"click": {
"name": "system:Quit",
@ -28,9 +21,7 @@
}
}
},
"text settings": {
"legacy": false,
"string": "Quit"
}
"string": "Quit"
}
}

View File

@ -86,7 +86,8 @@ if os.arch() == "Dreamcast" then
end )
else
ent:bind( "tick", function(self)
if (window.keyPressed("Enter") or inputs.key("START")) and timer:elapsed() >= 1 then
--if (window.keyPressed("Enter") or inputs.key("START")) and timer:elapsed() >= 1 then
if inputs.key("START") and timer:elapsed() >= 1 then
timer:reset()
children.start:callHook("gui:Clicked.%UID%", {})
end

View File

@ -11,24 +11,14 @@
"scale": [ 1, 1, 1 ]
},
"metadata": {
"clickable": true,
"hoverable": true,
"uv": [ 0, 0, 1, 1 ],
"location": "",
"scaling": "relative",
"debug": {
"moveable": false
},
"clickable": true,
"events": {
"click": {
"name": "game:Scene.Load",
"payload": { "scene": "SourceEngine" }
}
},
"text settings": {
"string": "Start",
"string1": "スタート"
}
"string": "Start"
}
}

View File

@ -11,11 +11,8 @@
"scale": [ 1, 1, 1 ]
},
"metadata": {
"uv": [ 0, 0, 1, 1 ],
"color": [ 1, 1, 1, 0.8 ],
"location": "",
"scaling": [ 0.64, 1 ],
"mode": "flat"
"scaling": "relative-x"
},
"assets": [
"./textures/circle-in.png"

View File

@ -11,14 +11,9 @@
"scale": [ 1, 1, 1 ]
},
"metadata": {
"clickable": false,
"hoverable": true,
"uv": [ 0, 0, 1, 1 ],
"color": [ 1, 1, 1, 0.8 ],
"location": "",
"scaling": [ 0.64, 1 ],
"mode": "flat"
"scaling": "relative-x"
},
"assets": [
"textures/circle-out.png"

View File

@ -8,24 +8,18 @@
"axis": [ 0, 0, 1 ],
"angle": 0
},
"scale": [ 0.156407, 0.0788377, 1 ]
"scale": [ 1, 1, 1 ]
},
"metadata": {
"clickable": true,
"hoverable": true,
"uv": [ 0, 0, 1, 1 ],
"location": "",
"scaling": "relative",
"events": {
"click": {
"name": "menu:Close.%P-UID%",
"payload": {}
}
},
"text settings": {
"string": "Close",
"string1": "クローズ"
}
"string": "Close",
"string1": "クローズ"
}
}

View File

@ -5,21 +5,18 @@
"transform": {
"position": [ -0.830591, -0.699509, 0 ],
"rotation": {
"axis": [ 1, 0, 0 ],
"axis": [ 0, 0, 1 ],
"angle": 0
},
"scale": [ 0.258737, 0.115371, 1 ]
"scale": [ 1, 1, 1 ]
},
"metadata": {
"uv": [ 0, 0, 1, 1 ],
"location": "",
"scaling": "relative",
"text settings": {
"stroke": [ 1, 0.749, 0.368, 1 ],
"color": [ 1, 0.749, 0.368, 1 ],
"stroke": [ 1, 0.749, 0.368, 1 ],
"color": [ 1, 0.749, 0.368, 1 ],
"string": "Menu",
"string1": "コマンド"
}
"scaling": "none",
"string": "Menu",
"string1": "コマンド"
}
}

View File

@ -1,25 +0,0 @@
{
"name": "Gui: Icon",
"type": "Gui",
"ignore": false,
"transform": {
"position": [ 0, 0, 0 ],
"rotation": {
"axis": [ 0, 0, 1 ],
"angle": 0
},
"scale": [ 1, 1, 1 ]
},
"metadata": {
"gui": {
"position": [ -0.875, -0.775, 0 ],
"uv": [ 0, 0, 1, 1 ],
"color": [ 1, 1, 1, 1 ],
"location": "",
"scaling": "fixed"
}
},
"assets": [
"https://cdn..xyz//unity/Android/icon/icon_agyou01_skin1.png"
]
}

View File

@ -1,23 +0,0 @@
{
"name": "Menu: Main Scroller",
"type": "Gui",
"ignore": true,
"transform": {
"position": [ 0.98, -2, 0 ],
"rotation": {
"axis": [ 0, 0, 1 ],
"angle": 1.5707963
},
"scale": [ 0.16, 1, 1 ]
},
"hoverable": true,
"metadata": {
"uv": [ 0, 0, 1, 1 ],
"color": [ 0.113, 0.756, 0.988, 0.4 ],
"location": "",
"scaling": "relative"
},
"assets": [
"./textures/main.png"
]
}

View File

@ -14,12 +14,7 @@
"scale": [ 1, 1, 1 ]
},
"metadata": {
"uv": [ 0, 0, 1, 1 ],
"color": [ 0.1, 0.05, 0.1, 0.5 ],
"location": "",
"scaling": "relative",
// "depth": 0.2,
"alpha": 1,
"mode": 1
},
"assets": [

View File

@ -8,18 +8,10 @@
"axis": [ 0, 0, 1 ],
"angle": 0
},
"scale": [ 0.156407, 0.0788377, 1 ]
"scale": [ 1, 1, 1 ]
},
"metadata": {
"clickable": true,
"hoverable": true,
"uv": [ 0, 0, 1, 1 ],
"location": "",
"scaling": "relative",
"debug": {
"moveable": true
},
"events": {
"click": [
{
@ -37,9 +29,8 @@
}
]
},
"text settings": {
"string": "Quit",
"string1": "終了する"
}
"string": "Quit",
"string1": "終了する"
}
}

View File

@ -242,7 +242,7 @@ else
if child:uid() > 0 then
local transform = child:getComponent("Transform")
local metadata = child:getComponent("Metadata")
local speed = metadata["gui"]["hovered"] and 0.75 or 0.5
local speed = metadata["hovered"] and 0.75 or 0.5
transform.position.y = transform.position.y + time.delta() * speed
if transform.position.y > 2 then
transform.position.y = -2
@ -256,7 +256,7 @@ else
local metadata = children.circleIn:getComponent("Metadata")
-- rotation
local speed = metadata["gui"]["hovered"] and 0.25 or 0.0125
local speed = metadata["hovered"] and 0.25 or 0.0125
static.time = (static.time or 0) + time.delta() * -speed
transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), static.time )
end
@ -268,7 +268,7 @@ else
local metadata = children.circleOut:getComponent("Metadata")
-- rotation
local speed = metadata["gui"]["hovered"] and 0.25 or 0.0125
local speed = metadata["hovered"] and 0.25 or 0.0125
static.time = (static.time or 0) + time.delta() * speed
transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), static.time )
end

View File

@ -1,25 +0,0 @@
{
"name": "Menu: Tenkousei Option",
"type": "Gui",
"ignore": true,
"transform": {
"position": [ -0.65544, -0.52853, 0 ],
"rotation": {
"axis": [ 0, 0, 1 ],
"angle": 0
},
"scale": [ 0.156407, 0.0788377, 1 ]
},
"metadata": {
"clickable": true,
"hoverable": true,
"uv": [ 0, 0, 1, 1 ],
"location": "",
"scaling": "relative",
"text settings": {
"string": "",
"string1": "「転光生」"
}
}
}

View File

@ -1,23 +0,0 @@
{
"name": "Menu: Transient Portrait",
"type": "Gui",
"ignore": true,
"transform": {
"position": [ 0.76, 0.2, 0 ],
"rotation": {
"axis": [ 1, 0, 0 ],
"angle": 0
},
"scale": [ 0.43, 1, 1 ]
},
"metadata": {
"clickable": true,
"hoverable": false,
"uv": [ 0, 0, 1, 1 ],
"color": [ 1, 1, 1, 0 ],
"location": "",
"scaling": "relative",
"depth": 0.1
}
}

View File

@ -1,21 +0,0 @@
{
"name": "Menu: Transient Shadow",
"type": "Gui",
"ignore": true,
"transform": {
"position": [ 0.46, 0, 0 ],
"rotation": {
"axis": [ 1, 0, 0 ],
"angle": 0
},
"scale": [ 0.43, 1, 1 ]
},
"metadata": {
"uv": [ 0, 0, 1, 1 ],
"color": [ 1, 1, 1, 0 ],
"location": "",
"scaling": "relative",
"shader": 1,
"depth": 0.1
}
}

View File

@ -11,13 +11,10 @@
"scale": [ 0.067, 1, 1 ]
},
"metadata": {
"uv": [ 0, 0, 1, 1 ],
// "color": [ 1, 0.749, 0.368, 1 ],
"color": [ 1, 0.749, 0.368, 1 ],
"location": "",
"scaling": "relative",
"shader": 1,
"mode": "flat"
"scaling": "none"
},
"assets": [
"./textures/square.png"

View File

@ -11,33 +11,32 @@
"scale": [ 1, 1, 1 ]
},
"metadata": {
"text settings": {
"legacy": false,
"padding": [ 1, 1 ],
"spread": 4,
"weight": 0.48,
// "size": 36, "scale": 3,
// "size": 45, "scale": 2,
"size": 60, "scale": 1.5,
// "size": 72, "scale": 1.25,
// "size": 90, "scale": 1,
"sdf": false,
"font": "TAZUGANEGOTHICSTDN-BOLD.otf",
"kerning": 24,
// "font": "Coolvetica.ttf",
"stroke": [ 0, 0, 0, 0 ],
"color": [ 1, 1, 1, 1 ],
"legacy": false,
"padding": [ 1, 1 ],
"spread": 4,
"weight": 0.48,
// "size": 36, "scale": 3,
// "size": 45, "scale": 2,
"size": 60, "scale": 1.5,
// "size": 72, "scale": 1.25,
// "size": 90, "scale": 1,
"sdf": false,
"font": "TAZUGANEGOTHICSTDN-BOLD.otf",
"kerning": 24,
"scaling": "none",
// "font": "Coolvetica.ttf",
"stroke": [ 0, 0, 0, 0 ],
"color": [ 1, 1, 1, 1 ],
"direction": "down",
"align": "left",
"origin": [ 0, 0 ],
"direction": "down",
"align": "left",
"origin": [ 0, 0 ],
"string": "",
"string": "",
"world": false,
// "depth": 0,
"wrap": true
}
"world": false,
// "depth": 0,
"wrap": true
}
}

View File

@ -26,10 +26,10 @@
"bias": {
"constant": 1.25,
"slope": 1.75,
"shader": 0.000005 // 0.000005 //0.000000005
"shader": 0.05 // 0.000005 //0.000000005
},
"radius": [0.5, 0],
"resolution": 512,
"resolution": 768,
"shadows": true,
"dynamic": true
}

View File

@ -34,12 +34,13 @@
"combined": false,
"encode buffers": true,
"unwrap": "tagged",
"conversion": "",
"quit": true
},
"baking": {
"enabled": true,
"resolution": 2048,
"shadows": 1024,
"shadows": 8192,
"layers": 1,
"trigger": { "mode": "rendered", "quit": true },
// "trigger": { "mode": "key", "value": "B" },
@ -67,7 +68,7 @@
"invert": true,
"skinned": false,
"render": true,
"separate": false
"separate": true
},
"lights": {
"lightmap": "auto",

View File

@ -17,8 +17,8 @@
},
*/
"assets": [
// { "filename": "./playerModel.json", "delay": 0 },
"./playerModel.json",
{ "filename": "./playerModel.json", "delay": 1 },
// "./playerModel.json",
"./playerLight.json",
"./playerHands.json",
"./scripts/player.lua"
@ -55,15 +55,15 @@
}
},
"movement": {
"walk": 8,
"move": 8,
"run": 16,
"walk": 12,
"move": 12,
"run": 20,
// "rotate": 1.5,
"rotate": 4,
"rotate": 6,
"air": 0.1,
"crouch": 1,
"jump": [ 0, 3, 0 ],
"jump": [ 0, 6, 0 ],
"look": 1,
"floored": {
"feet": [ 0, -1.5, 0 ],
@ -71,7 +71,7 @@
"floor": [ 0, -1.0, 0 ],
"print": false
},
"strafe": false
"strafe": true
},
"physics": {
"gravity": [ 0, -9.81, 0 ],
@ -88,8 +88,8 @@
"shared": false
},
"camera": {
"offset": [ 0, 5, 3 ],
"orientation": [ 0, 0.894427, -0.447214, 0 ],
// "offset": [ 0, 10, 6 ],
// "orientation": [ 0, 0.894427, -0.447214, 0 ],
"position" : [ 0, 1.8, 0 ],
"scale": [ 1, 1, 1 ],
"invert": [ false, false, false ],
@ -99,7 +99,7 @@
"current":[ null, 0, null ]
},
"settings": {
"fixed": true,
"fixed": false,
"fov" : 90.0,
"clip" : [ 0.1, 64.0 ],
"size" : [ 0, 0 ]

View File

@ -1,11 +1,11 @@
{
"type": "Object",
"name": "Player: Model",
"ignore": false,
"ignore": true,
"import": "/model.json",
"assets": [
"/player/bear.glb"
// { "filename": "/player/bear/graph.json" }
// "/player/bear.glb"
{ "filename": "/player/bear/graph.json" }
],
"behaviors": [
"PlayerModelBehavior"
@ -20,11 +20,6 @@
},
"scale": [ 0.16, 0.16, 0.16 ]
},
"system": {
"hot reload": {
"enabled": true
}
},
"metadata": {
"track": true,
"hide": false,
@ -35,7 +30,7 @@
}
},
"exporter": {
"enabled": false,
"enabled": true,
"unwrap": false,
"optimize": false
},
@ -53,7 +48,8 @@
"skinned": true
},
"animations": {
"animation": "wank"
"animation": "wank",
"speed": 2.0
}
}
}

View File

@ -0,0 +1,12 @@
{
"name": "Chunk",
"assets": [
"/missing.png"
],
"behaviors": [
"RegionChunkBehavior"
],
"metadata": {
}
}

View File

@ -5,6 +5,8 @@
],
"behaviors": [
"SceneBehavior",
"ExtSceneBehavior"
"ExtSceneBehavior",
"BgmEmitterBehavior",
"RegionBehavior"
]
}

View File

@ -0,0 +1,146 @@
local ent = ent
local scene = entities.currentScene()
local metadata = ent:getComponent("Metadata")
local transform = ent:getComponent("Transform")
local physicsState = ent:getComponent("PhysicsState")
local camera = ent:getComponent("Camera")
local cameraTransform = camera:getTransform()
-- setup all timers
local timers = {
lookat = Timer.new(),
}
if not timers.lookat:running() then timers.lookat:start(); end
--[[
-- setup held object locals
local heldObject = {
uid = 0,
distance = 0,
smoothSpeed = 4,
scrollSpeed = 16,
momentum = Vector3f(0,0,0),
rotate = false,
}
-- setup light locals
local light = {
entity = nil
}
for k, v in pairs(ent:getChildren()) do
if v:name() == "Light" then
light.entity = v
end
end
if light.entity == nil then
light.entity = ent:loadChild("./playerLight.json",true)
end
light.metadata = light.entity:getComponent("Metadata")
light.transform = light.entity:getComponent("Transform")
light.power = light.metadata["light"]["power"]
light.origin = Vector3f(light.transform.position)
light.entity:setComponent("Metadata", { light = { power = 0 } })
-- sound emitter
local playSound = function( key, loop )
if not loop then loop = false end
local url = "/ui/" .. key .. ".ogg"
ent:callHook("sound:Emit.%UID%", {
filename = string.resolveURI(url, metadata["system"]["root"]),
spatial = true,
streamed = true,
volume = "sfx",
loop = loop
}, 0)
end
local stopSound = function( key )
local url = "/ui/" .. key .. ".ogg"
ent:callHook("sound:Stop.%UID%", {
filename = string.resolveURI(url, metadata["system"]["root"])
}, 0)
end
]]
local collider = ent:getComponent("PhysicsState")
local target_transform = nil
-- on tick
ent:bind( "tick", function(self)
-- rotate to target
if target_transform ~= nil then
local target = (target_transform.position - transform.position):normalize()
local dot = transform.forward:dot( target )
if dot < 1.0 then
local cross = Vector3f.cross( transform.forward, target ):normalize()
local axis = transform.up
local angle = Vector3f.signedAngle( transform.forward, target, axis )
local rot = Quaternion.axisAngle( axis, angle * time.delta() * 4 )
if collider:hasBody() then
collider:applyRotation( rot )
else
transform:rotate( rot )
end
end
if timers.lookat:elapsed() > 2.0 then
timers.lookat:reset()
target_transform = nil
end
end
end )
-- on use
ent:addHook( "entity:Use.%UID%", function( payload )
-- get dialogue text
local texts = {
"Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit,\nsed do eiusmod tempor incididunt\nut labore et dolore magna aliqua.",
"The birch canoe slid on\nthe smooth planks.",
"Glue the sheet to the dark\nblue background.",
"It's easy to tell the depth\nof a well.",
"These days a chicken leg\nis a rare dish.",
"Rice is often served in\nround bowls.",
"The juice of lemons makes\nfine punch.",
"The box was thrown beside\nthe parked truck.",
"The hogs were fed chopped\ncorn and garbage.",
"Four hours of steady work\nfaced us.",
"A large size in stockings\nis hard to sell.",
"The boy was there when\nthe sun rose.",
"A rod is used to catch\npink salmon.",
"The source of the huge river\nis the clear spring.",
"Kick the ball straight and\nfollow through.",
"Help the woman get back to\nher feet.",
"A pot of tea helps to pass\nthe evening.",
"Smoky fires lack flame\nand heat.",
"The soft cushion broke the\nman's fall.",
"The salt breeze came across\nfrom the sea.",
"The girl at the booth sold\nfifty bonds.",
"The small pup gnawed a hole\nin the sock.",
"The fish twisted and turned\non the bent hook.",
"Press the pants and sew a\nbutton on the vest.",
"The swan dive was far short\nof perfect.",
"The beauty of the view\nstunned the young boy.",
"Two blue fish swam in\nthe tank.",
"Her purse was full of\nuseless trash.",
"The colt reared and threw\nthe tall rider.",
"It snowed, rained, and\nhailed the same morning.",
"Read verse out loud\nfor pleasure.",
}
local text = texts[math.random( #texts )] or "!! Test Message !!"
local forward = {
name = "dialogue",
metadata = {
dialogue = {
name = metadata["name"],
text = text
}
}
}
scene:callHook("menu:Open", forward)
-- rotate to target
local user = scene:globalFindByUid(payload["user"])
target_transform = user:getComponent("Transform")
timers.lookat:reset()
end )

View File

@ -70,8 +70,33 @@ local pullDistance = useDistance * 4
-- on tick
ent:bind( "tick", function(self)
local inControl = scene:globalFindByName("Gui: Menu"):uid() == 0
local keyE = inputs.key("E") or inputs.key("R_Y")
local keyF = inputs.key("F")
local mouse1 = inputs.key("Mouse1") or inputs.key("L_TRIGGER");
local mouse2 = inputs.key("Mouse2") or inputs.key("R_TRIGGER");
local mouse3 = inputs.key("Mouse3");
local wheel = inputs.analog("MouseWheel")
if not inControl then
keyE = false
keyF = false
mouse1 = false
mouse2 = false
mouse3 = false
wheel = 0
end
-- eye transform
local flattenedTransform = cameraTransform:flatten()
local flattenedTransform = nil
if metadata["camera"]["settings"]["fixed"] then
flattenedTransform = transform:flatten()
-- flattenedTransform.position.y = flattenedTransform.position.y
else
flattenedTransform = cameraTransform:flatten()
end
flattenedTransform.forward = ( transform.forward + Vector3f( 0, cameraTransform.forward.y, 0 ) ):normalize();
-- toggle flashlight
@ -89,7 +114,7 @@ ent:bind( "tick", function(self)
light.transform.position = center + direction * (depth - offset)
end
if timers.flashlight:elapsed() > 0.5 and inputs.key("F") then
if timers.flashlight:elapsed() > 0.5 and keyF then
timers.flashlight:reset()
local metadata = { light = { power = light.power } }
@ -106,7 +131,7 @@ ent:bind( "tick", function(self)
end
-- fire use ray
if timers.use:elapsed() > 0.5 and (inputs.key("E") or inputs.key("R_Y")) then
if timers.use:elapsed() > 0.5 and keyE and inControl then
timers.use:reset()
local center = flattenedTransform.position
@ -126,7 +151,6 @@ ent:bind( "tick", function(self)
-- update HOLP
if heldObject.uid == 0 then
local mouse2 = inputs.key("Mouse2") or inputs.key("R_TRIGGER");
if mouse2 then
--[[
local center = transform.position + cameraTransform.position
@ -152,9 +176,6 @@ ent:bind( "tick", function(self)
end
end
else
local mouse1 = inputs.key("Mouse1") or inputs.key("L_TRIGGER");
local mouse3 = inputs.key("Mouse3");
local wheel = inputs.analog("MouseWheel")
if wheel ~= 0 then
heldObject.distance = heldObject.distance + (wheel / 120 * heldObject.scrollSpeed) * time.delta()

View File

@ -4,8 +4,10 @@
"/gui.json"
],
"behaviors": [
"SceneBehavior",
"ExtSceneBehavior"
"SceneBehavior"
,"ExtSceneBehavior"
,"BgmEmitterBehavior"
// ,"RegionBehavior"
],
"system": {
"hot reload": {
@ -31,7 +33,8 @@
}
},
"menus": {
"pause": "/gui/pause/menu.json"
"pause": "/gui/pause/main.json",
"dialogue": "/gui/dialogue/main.json"
},
"light": {
"enabled": true,
@ -39,8 +42,9 @@
"ambient": [ 0.0, 0.0, 0.0 ],
// "ambient": [ 0.1, 0.1, 0.2 ],
"exposure": 0.125,
"gamma": 2.2,
"exposure": 1.0,
// "gamma": 2.2,
"gamma": 1.0,
"bloom": {
"threshold": 1.0,

View File

@ -1,15 +1,18 @@
{
"import": "./base_sourceengine.json",
"assets": [
// { "filename": "./models/animal_crossing.glb" }
{ "filename": "./models/animal_crossing/graph.json" }
// { "filename": "/burger.json", "delay": 1 }
// { "filename": "./models/animal_crossing.glb" },
// { "filename": "./models/animal_crossing/graph.json" },
// { "filename": "./models/animal_crossing_small.glb" },
{ "filename": "./models/animal_crossing_small/graph.json" },
{ "filename": "/craeture.json", "delay": 2.0 }
],
"metadata": {
"graph": {
"assets": [
"./audio/music/5pm.ogg"
],
"bgm": {
"load": "./audio/music/5pm.ogg"
},
"tags": {
// exact matches
// "func_door_rotating_5473": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
@ -25,7 +28,7 @@
"position": [250.3781, 148.704, 202.286]
},
"light": {
"color": [0.95, 0.25, 0.25],
// "color": [0.95, 0.25, 0.25],
"power": 1000000,
"global": true,
"bias": {
@ -34,7 +37,7 @@
"shader": 0.0000175
},
"radius": [0.9999999, 0],
"resolution": 4096
"resolution": 2048
}
},
"/^tools\\/toolsnodraw/": { "material": {

View File

@ -3,19 +3,22 @@
"metadata": {
"graph": {
// "renderer": { "separate": true },
"exporter": {
"optimize": "tagged"
},
"baking": { "enabled": true },
"tags": {
// exact matches
"worldspawn": {
"physics": { "type": "mesh", "static": true },
"grid": { "size": [3,1,3], "epsilon": 1.0, "cleanup": true, "print": true },
"unwrap mesh": true,
"optimize mesh": { "simplify": 0 }
"grid": { "size": [3,1,3], "epsilon": 0.001, "cleanup": true, "print": true },
// "optimize mesh": { "simplify": 0 },
"unwrap mesh": true
},
"worldspawn_skybox": {
"grid": { "size": [3,1,3], "epsilon": 1.0, "cleanup": true, "print": true },
"unwrap mesh": true,
"optimize mesh": { "simplify": 0 }
"grid": { "size": [3,1,3], "epsilon": 0.001, "cleanup": true, "print": true },
// "optimize mesh": { "simplify": 0 },
"unwrap mesh": true
},
"info_player_spawn": { "action": "attach", "filename": "./player.json", "transform": { "orientation": [ 0, 1, 0, 0 ] } },
"light_environment": { "ignore": false, "light": {
@ -28,7 +31,7 @@
"shader": 0.000025
},
"radius": [0.9999999, 0],
"resolution": 4096
"resolution": 1024
} },
// "/^light_[^e]/": { "ignore": true },

View File

@ -1,14 +1,12 @@
{
"import": "./base_sourceengine.json",
"assets": [
// { "filename": "./models/gm_construct.glb" }
{ "filename": "./models/gm_construct/graph.json" }
{ "filename": "./models/gm_construct.glb" }
// { "filename": "./models/gm_construct/graph.json" }
],
"metadata": {
"graph": {
"assets": [
"./audio/soundscape/ambience.ogg"
],
"bgm": "./audio/soundscape/ambience.ogg",
"tags": {
// exact matches
// "worldspawn_skybox": { "ignore": true },

View File

@ -1,15 +1,13 @@
{
"import": "./base_sourceengine.json",
"assets": [
// { "filename": "./models/mds_mcdonalds.glb" }
{ "filename": "./models/mds_mcdonalds/graph.json" },
{ "filename": "./models/mds_mcdonalds.glb" },
// { "filename": "./models/mds_mcdonalds/graph.json" },
{ "filename": "/burger.json", "delay": 1 }
],
"metadata": {
"graph": {
"assets": [
"./audio/soundscape/sh2_ambience.ogg"
],
"bgm": "./audio/soundscape/sh2_ambience.ogg",
"tags": {
// exact matches
"func_door_rotating_5473": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
@ -30,7 +28,9 @@
"fAlphaCutoff": 0.5,
"iAlbedo": 27,
"modeAlpha": 1
} }
} },
"/alpha_mtl/": { "material": { "modeAlpha": 1 } },
"/offwndwb/": { "material": { "modeAlpha": 1 } }
}
}
}

View File

@ -4,7 +4,7 @@
// { "filename": "/gui/hud/hud.json", "delay": 0 }
],
"transform": {
"orientation": [ 0, 1, 0, 0 ]
// "orientation": [ 0, 1, 0, 0 ]
}
// "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } }
}

View File

@ -1,14 +1,12 @@
{
"import": "./base_sourceengine.json",
"assets": [
// { "filename": "./models/rp_downtown_v2.glb" }
{ "filename": "./models/rp_downtown_v2/graph.json" }
{ "filename": "./models/rp_downtown_v2.glb" }
// { "filename": "./models/rp_downtown_v2/graph.json" }
],
"metadata": {
"graph": {
"assets": [
"./audio/soundscape/ambience.ogg"
],
"bgm": "./audio/soundscape/ambience.ogg",
"renderer": { "separate": true },
"tags": {
// exact matches

View File

@ -13,8 +13,10 @@
// "ambient": [ 0.8, 0.8, 0.8 ],
// "ambient": [ 0.1, 0.1, 0.2 ],
"exposure": 0.125,
"gamma": 2.2, // 2.2,
// "exposure": 0.125,
// "gamma": 2.2,
"exposure": 1.0,
"gamma": 1.0,
"bloom": {
"threshold": 1.2,
@ -24,7 +26,7 @@
"samples": 4
},
"fog": {
"fog-": {
// "color": [ 0.1, 0.1, 0.1 ],
// "color": [ 0.2, 0.2, 0.2 ],
"color": [ 0.3, 0.3, 0.3 ],

View File

@ -1,17 +1,17 @@
{
"import": "./base_sourceengine.json",
"assets": [
// { "filename": "./models/sh2_mcdonalds.glb" }
{ "filename": "./models/sh2_mcdonalds/graph.json" }
{ "filename": "./models/sh2_mcdonalds.glb" }
// { "filename": "./models/sh2_mcdonalds/graph.json" }
],
"metadata": {
"graph": {
"lights": {
"scale": 2
},
"assets": [
"./audio/soundscape/sh2_ambience.ogg"
],
"bgm": {
"load": "./audio/soundscape/sh2_ambience.ogg"
},
"tags": {
// exact matches
/*

View File

@ -2,7 +2,7 @@
// "import": "./rp_downtown_v2.json"
// "import": "./ss2_medsci1.json"
// "import": "./sh2_mcdonalds.json"
"import": "./animal_crossing.json"
// "import": "./mds_mcdonalds.json"
// "import": "./animal_crossing.json"
"import": "./mds_mcdonalds.json"
// "import": "./gm_construct.json"
}

View File

@ -1,14 +1,12 @@
{
"import": "./base_sourceengine.json",
"assets": [
// { "filename": "./models/ss2_medsci1_small.glb" }
{ "filename": "./models/ss2_medsci1_small/graph.json" }
// { "filename": "./models/ss2_medsci1.glb" }
{ "filename": "./models/ss2_medsci1/graph.json" }
],
"metadata": {
"graph": {
"assets": [
"./audio/music/medsci1.ogg"
],
"bgm": "./audio/music/medsci1.ogg",
"tags": {
"/^prop_/": { "action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } } },
"/^func_/": { "action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } } }

View File

@ -2,11 +2,11 @@
"type": "Main Menu",
"behaviors": [
"SceneBehavior",
"ExtSceneBehavior"
"ExtSceneBehavior",
"BgmEmitterBehavior"
],
"assets": [
"/gui.json",
"/ui/main menu.ogg",
"/gui/mainmenu/menu.json"
],
"metadata": {
@ -14,6 +14,23 @@
"sfx": 0.25,
"bgm": 0.15,
"voice": 1.0
},
"bgm": {
"tracks": {
// "/ui/main menu.ogg": {},
"/ui/tainted.ogg": {
"intro": "/ui/tainted_intro.ogg",
"epsilon": 0.25,
"fade": true
}
}
}
},
"system": {
"renderer": {
"shader": {
"init 3D": false
}
}
}
}

View File

@ -1,2 +1 @@
local orientation = Quaternion()
print( orientation, orientation.x )
-- P

View File

@ -229,7 +229,8 @@ vec3 decodeBarycentrics( vec2 attributes ) {
}
#if DEFERRED_SAMPLING
void populateSurfaceMaterial() {
const Material material = materials[surface.instance.materialID];
const Material material = materials[surface.instance.materialID >= materials.length() ? 0 : surface.instance.materialID];
#if 1
surface.material.albedo = material.colorBase;
surface.material.metallic = material.factorMetallic;
surface.material.roughness = material.factorRoughness;
@ -249,14 +250,27 @@ void populateSurfaceMaterial() {
} else if ( material.modeAlpha == 2 ) {
}
#if 1
// Lightmap
if ( (/*surface.subID++ > 0 ||*/ bool(ubo.settings.lighting.useLightmaps)) && validTextureIndex( surface.instance.lightmapID ) ) {
vec4 light = sampleTexture( surface.instance.lightmapID, surface.st.xy, 0 );
/*surface.material.lightmapped = light.a > 0.000000001;
if ( surface.material.lightmapped )*/ surface.light += surface.material.albedo * light;
vec4 light = sampleTexture( surface.instance.lightmapID, surface.st.xy );
surface.material.lightmapped = light.a > 0.000000001;
if ( surface.material.lightmapped ) {
float exposure = 1.0;
float gamma = 2.2;
light.rgb = vec3(1.0) - exp(-light.rgb * exposure);
light.rgb = pow(light.rgb, vec3(1.0 / gamma));
surface.light += surface.material.albedo * light;
}
} else {
surface.material.lightmapped = false;
}
#else
surface.material.lightmapped = false;
#endif
#if 1
// Emissive textures
if ( validTextureIndex( material.indexEmissive ) ) {
surface.light += sampleTexture( material.indexEmissive );
@ -275,11 +289,13 @@ void populateSurfaceMaterial() {
if ( validTextureIndex( material.indexNormal ) && surface.tangent.world != vec3(0) ) {
surface.normal.world = surface.tbn * normalize( sampleTexture( material.indexNormal ).xyz * 2.0 - vec3(1.0));
}
#endif
{
surface.normal.eye = normalize(vec3( ubo.eyes[surface.pass].view * vec4(surface.normal.world, 0.0) ));
}
surface.light *= surface.material.albedo;
#endif
}
bool isValidAddress( uint64_t address ) {
@ -307,35 +323,34 @@ void populateSurface( InstanceAddresses instanceAddresses, uvec3 indices ) {
Vertex points[3];
if ( isValidAddress(instanceAddresses.vertex) ) {
Vertices vertices = Vertices(nonuniformEXT(instanceAddresses.vertex));
#pragma unroll 3
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_] = vertices.v[/*triangle.*/indices[_]];
// Vertices vertices = Vertices(nonuniformEXT(instanceAddresses.vertex));
// #pragma unroll 3
// for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_] = vertices.v[/*triangle.*/indices[_]];
} else {
if ( isValidAddress(instanceAddresses.position) ) {
VPos buf = VPos(nonuniformEXT(instanceAddresses.position));
#pragma unroll 3
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].position = buf.v[/*triangle.*/indices[_]];
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].position[_] = buf.v[/*triangle.*/indices[_]*3+_];
}
if ( isValidAddress(instanceAddresses.uv) ) {
VUv buf = VUv(nonuniformEXT(instanceAddresses.uv));
#pragma unroll 3
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].uv = buf.v[/*triangle.*/indices[_]];
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].uv/*[_]*/ = buf.v[/*triangle.*/indices[_]];
}
if ( isValidAddress(instanceAddresses.st) ) {
VSt buf = VSt(nonuniformEXT(instanceAddresses.st));
#pragma unroll 3
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].st = buf.v[/*triangle.*/indices[_]];
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].st/*[_]*/ = buf.v[/*triangle.*/indices[_]];
}
if ( isValidAddress(instanceAddresses.normal) ) {
VNormal buf = VNormal(nonuniformEXT(instanceAddresses.normal));
#pragma unroll 3
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].normal = buf.v[/*triangle.*/indices[_]];
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].normal[_] = buf.v[/*triangle.*/indices[_]*3+_];
}
if ( isValidAddress(instanceAddresses.tangent) ) {
VTangent buf = VTangent(nonuniformEXT(instanceAddresses.tangent));
#pragma unroll 3
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].tangent = buf.v[/*triangle.*/indices[_]];
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].tangent[_] = buf.v[/*triangle.*/indices[_]*3+_];
}
}
@ -411,7 +426,15 @@ void populateSurface( uint instanceID, uint primitiveID ) {
if ( !isValidAddress(instanceAddresses.index) ) return;
const DrawCommand drawCommand = Indirects(nonuniformEXT(instanceAddresses.indirect)).dc[instanceAddresses.drawID];
const uint triangleID = primitiveID + (drawCommand.indexID / 3);
uvec3 indices = Indices(nonuniformEXT(instanceAddresses.index)).i[triangleID];
//uvec3 indices = Indices(nonuniformEXT(instanceAddresses.index)).i[triangleID];
uvec3 indices = uvec3(
Indices(nonuniformEXT(instanceAddresses.index)).i[triangleID*3+0],
Indices(nonuniformEXT(instanceAddresses.index)).i[triangleID*3+1],
Indices(nonuniformEXT(instanceAddresses.index)).i[triangleID*3+2]
);
#pragma unroll 3
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/indices[_] += drawCommand.vertexID;

View File

@ -13,9 +13,8 @@ void lambert() {
// skip if surface is already baked, and this isn't a dynamic light
if ( surface.material.lightmapped && lights[i].type >= 0 ) continue;
#endif
if ( lights[i].power <= LIGHT_POWER_CUTOFF ) continue;
if ( surface.material.lightmapped && lights[i].type >= 0 ) continue;
// incoming light to surface (non-const to normalize it later)
// vec3 Li = lights[i].position - surface.position.world;
vec3 Li = vec3(VIEW_MATRIX * vec4(lights[i].position, 1)) - surface.position.eye;
// magnitude of incoming light vector (for inverse-square attenuation)
const float Lmagnitude = dot(Li, Li);
@ -29,9 +28,9 @@ void lambert() {
// skip if attenuation factor is too low
// if ( Lattenuation <= LIGHT_POWER_CUTOFF ) continue;
// ray cast if our surface is occluded from the light
const float Lshadow = 1; // ( shadows++ < MAX_SHADOWS ) ? shadowFactor( lights[i], 0.0 ) : 1;
const float Lshadow = ( shadows++ < MAX_SHADOWS ) ? shadowFactor( lights[i], 0.0 ) : 1;
// skip if our shadow factor is too low
if ( Lshadow <= LIGHT_POWER_CUTOFF ) continue;
// if ( Lshadow <= LIGHT_POWER_CUTOFF ) continue; // in case of any divergence
// light radiance
const vec3 Lr = lights[i].color.rgb * lights[i].power * Lattenuation * Lshadow;
// skip if our radiance is too low

View File

@ -75,7 +75,7 @@
// easy and accessible in one place
#ifndef BARYCENTRIC
#define BARYCENTRIC 1
#define BARYCENTRIC 0
#endif
#if BARYCENTRIC
#ifndef BARYCENTRIC_CALCULATE

View File

@ -17,26 +17,9 @@ void pbr() {
if ( lights[i].type < 0 ) continue;
#else
// skip if surface is already baked, and this isn't a dynamic light
if ( surface.material.lightmapped && lights[i].type >= 0 ) continue;
// if ( surface.material.lightmapped && lights[i].type >= 0 ) continue;
if ( surface.material.lightmapped ) continue;
#endif
/*
// skip if light power is too low
if ( lights[i].power <= LIGHT_POWER_CUTOFF ) continue;
if ( surface.material.lightmapped && lights[i].type >= 0 ) continue;
const vec3 Liu = vec3(VIEW_MATRIX * vec4(lights[i].position, 1)) - surface.position.eye;
const vec3 Li = normalize(Liu);
const float Lshadow = ( shadows++ < MAX_SHADOWS ) ? shadowFactor( lights[i], 0.0 ) : 1;
// const float Lattenuation = 1.0 / (PI * pow(length(Liu), 2.0));
// const float Lattenuation = 1.0 / (1 + (PI * pow(length(Liu), 2.0)));
const float Lattenuation = 1.0 / (1 + pow(length(Liu), 2.0));
if ( lights[i].power * Lattenuation * Lshadow <= LIGHT_POWER_CUTOFF ) continue;
const float cosLi = max(0.0, dot(surface.normal.eye, Li));
const vec3 Lr = lights[i].color.rgb * lights[i].power * Lattenuation * Lshadow;
const vec3 Lh = normalize(Li + Lo);
const float cosLh = max(0.0, dot(surface.normal.eye, Lh));
*/
// incoming light to surface (non-const to normalize it later)
// vec3 Li = lights[i].position - surface.position.world;
vec3 Li = vec3(VIEW_MATRIX * vec4(lights[i].position, 1)) - surface.position.eye;
@ -54,7 +37,7 @@ void pbr() {
// ray cast if our surface is occluded from the light
const float Lshadow = ( shadows++ < MAX_SHADOWS ) ? shadowFactor( lights[i], 0.0 ) : 1;
// skip if our shadow factor is too low
if ( Lshadow <= LIGHT_POWER_CUTOFF ) continue;
// if ( Lshadow <= LIGHT_POWER_CUTOFF ) continue; // in case of any divergence
// light radiance
const vec3 Lr = lights[i].color.rgb * lights[i].power * Lattenuation * Lshadow;
// skip if our radiance is too low

View File

@ -104,16 +104,22 @@ layout (binding = 19) uniform sampler3D samplerNoise;
#endif
#if BUFFER_REFERENCE
layout(buffer_reference, scalar) buffer Vertices { Vertex v[]; };
layout(buffer_reference, scalar) buffer Indices { uvec3 i[]; };
//layout(buffer_reference, scalar) buffer Vertices { Vertex v[]; };
layout(buffer_reference, scalar) buffer Indices { uint i[]; };
//layout(buffer_reference, scalar) buffer Indices { uvec3 i[]; };
layout(buffer_reference, scalar) buffer Indirects { DrawCommand dc[]; };
layout(buffer_reference, scalar) buffer VPos { vec3 v[]; };
//layout(buffer_reference, scalar) buffer VPos { vec3 v[]; };
layout(buffer_reference, scalar) buffer VPos { float v[]; };
layout(buffer_reference, scalar) buffer VUv { vec2 v[]; };
//layout(buffer_reference, scalar) buffer VUv { float v[]; };
layout(buffer_reference, scalar) buffer VColor { uint v[]; };
layout(buffer_reference, scalar) buffer VSt { vec2 v[]; };
layout(buffer_reference, scalar) buffer VNormal { vec3 v[]; };
layout(buffer_reference, scalar) buffer VTangent { vec3 v[]; };
//layout(buffer_reference, scalar) buffer VSt { float v[]; };
//layout(buffer_reference, scalar) buffer VNormal { vec3 v[]; };
layout(buffer_reference, scalar) buffer VNormal { float v[]; };
//layout(buffer_reference, scalar) buffer VTangent { vec3 v[]; };
layout(buffer_reference, scalar) buffer VTangent { float v[]; };
layout(buffer_reference, scalar) buffer VID { uint v[]; };
#endif
@ -251,7 +257,7 @@ void populateSurface() {
surface.normal.world = decodeNormals(normaltangent.xy);
// surface.tangent.world = decodeNormals(normaltangent.zw);
surface.instance = instances[instanceID];
surface.instance = instances[instanceID >= instances.length() ? 0 : instanceID];
populateSurfaceMaterial();
#endif

View File

@ -101,7 +101,7 @@ void main() {
if ( A.a < 0.0001 ) discard;
#if !BARYCENTRIC
outUv = vec4(surface.uv, surface.st);
outUv = vec4(surface.uv.xy, surface.st.xy);
outNormal = vec4( encodeNormals(inNormal), encodeNormals(inTangent) );
#endif

View File

@ -77,8 +77,8 @@ void main() {
#else
const mat4 skinned = mat4(1.0);
#endif
// const mat4 model = instances.length() <= 0 ? skinned : (instance.model * skinned);
const mat4 model = instance.model * skinned;
const mat4 model = instances.length() <= 0 ? skinned : (instance.model * skinned);
// const mat4 model = instance.model * skinned;
#if BAKING

View File

@ -6,11 +6,14 @@
#include "../../common/functions.h"
#include "./gui.h"
layout (binding = 1) uniform sampler2D samplerTexture;
layout (binding = 2) uniform sampler2D samplerTexture;
layout (location = 0) in vec2 inUv;
layout (location = 1) in flat Gui inGui;
layout (location = 7) in flat Glyph inGlyph;
#if GLYPH
layout (location = 7) in flat uint inVertexID;
layout (location = 8) in flat Glyph inGlyph;
#endif
layout (location = 0) out vec4 outAlbedo;
@ -21,6 +24,18 @@ void main() {
const float mip = mipLevel(dFdx(inUv), dFdy(inUv));
vec4 C = inGui.color;
#if GLYPH
if ( !(inGlyph.range.x < 0 || inGlyph.range.y < 0) ) {
if ( !(inGlyph.range.x <= (inVertexID / 6) && (inVertexID / 6) < inGlyph.range.y ) ) discard;
}
const float sampled = texture(samplerTexture, inUv).r;
const float smoothing = ( inGlyph.spread > 0 && inGlyph.scale > 0 ) ? 0.25 / (inGlyph.spread * inGlyph.scale) : 0.25 / (4 * 1.5);
const float outlining = smoothstep(0.5 - smoothing, 0.5 + smoothing, sampled);
const float alpha = smoothstep(inGlyph.weight - smoothing, inGlyph.weight + smoothing, sampled);
if ( alpha < 0.001 || alpha > 1 ) discard;
C = mix(inGlyph.stroke, inGui.color, outlining);
C.a = inGui.color.a * alpha;
/*
if ( enabled(inGui.mode, 1) ) {
outAlbedo = inGui.color;
return;
@ -37,6 +52,7 @@ void main() {
if ( sampled < 0.001 || sampled > 1 ) discard;
C *= sampled;
}
*/
#else
if ( enabled(inGui.mode, 0) ) C = inGui.color;
else C *= textureLod( samplerTexture, uv, mip );

View File

@ -10,9 +10,13 @@ struct Gui {
struct Glyph {
vec4 stroke;
ivec2 range;
int spread;
float weight;
float scale;
float padding;
uint padding1;
uint padding2;
uint padding3;
};

View File

@ -15,22 +15,27 @@ struct Matrices {
layout (binding = 0) uniform UBO {
Matrices matrices;
Gui gui;
#if GLYPH
Glyph glyph;
#endif
} ubo;
#if GLYPH
layout (binding = 1) uniform UBO_Glyph {
Glyph glyph;
} uboGlyph;
#endif
layout (location = 0) out vec2 outUv;
layout (location = 1) out flat Gui outGui;
#if GLYPH
layout (location = 7) out flat Glyph outGlyph;
layout (location = 7) out flat uint outVertexID;
layout (location = 8) out flat Glyph outGlyph;
#endif
void main() {
outUv = inUv;
outGui = ubo.gui;
#if GLYPH
outGlyph = ubo.glyph;
outVertexID = gl_VertexIndex;
outGlyph = uboGlyph.glyph;
#endif
gl_Position = ubo.matrices.model[PushConstant.pass] * vec4(inPos.xy, ubo.gui.depth, 1.0);

View File

@ -67,16 +67,22 @@ layout (binding = 10) uniform sampler3D samplerNoise;
layout (location = 0) rayPayloadEXT RayTracePayload payload;
layout(buffer_reference, scalar) buffer Vertices { Vertex v[]; };
layout(buffer_reference, scalar) buffer Indices { uvec3 i[]; };
//layout(buffer_reference, scalar) buffer Vertices { Vertex v[]; };
layout(buffer_reference, scalar) buffer Indices { uint i[]; };
//layout(buffer_reference, scalar) buffer Indices { uvec3 i[]; };
layout(buffer_reference, scalar) buffer Indirects { DrawCommand dc[]; };
layout(buffer_reference, scalar) buffer VPos { vec3 v[]; };
//layout(buffer_reference, scalar) buffer VPos { vec3 v[]; };
layout(buffer_reference, scalar) buffer VPos { float v[]; };
layout(buffer_reference, scalar) buffer VUv { vec2 v[]; };
//layout(buffer_reference, scalar) buffer VUv { float v[]; };
layout(buffer_reference, scalar) buffer VColor { uint v[]; };
layout(buffer_reference, scalar) buffer VSt { vec2 v[]; };
layout(buffer_reference, scalar) buffer VNormal { vec3 v[]; };
layout(buffer_reference, scalar) buffer VTangent { vec3 v[]; };
//layout(buffer_reference, scalar) buffer VSt { float v[]; };
//layout(buffer_reference, scalar) buffer VNormal { vec3 v[]; };
layout(buffer_reference, scalar) buffer VNormal { float v[]; };
//layout(buffer_reference, scalar) buffer VTangent { vec3 v[]; };
layout(buffer_reference, scalar) buffer VTangent { float v[]; };
layout(buffer_reference, scalar) buffer VID { uint v[]; };
#include "../common/functions.h"

View File

@ -1,11 +1,11 @@
{
"engine": {
"scenes": {
"start": "StartMenu",
"start": "SourceEngine",
"matrix": { "reverseInfinite": true },
"meshes": { "interleaved": false },
"lights": { "enabled": true,
"useLightmaps": true,
"useLightmaps": false,
"max": 1,
"shadows": {
"enabled": false,
@ -75,13 +75,13 @@
"timescale": 0.01666666666,
"interpolate": false,
"gravity": {
"mode": "per-object", // "universal",
"mode": "universal",
"constant": 6.67408e-11
},
"debug draw": {
"enabled": false,
"line width": 8,
// "layer": "Gui",
"layer": "",
"rate": 0.0125
}
},

View File

@ -81,7 +81,10 @@ void client::initialize() {
});
uf::hooks.addHook( "window:Mouse.Lock", [&](){
if ( client::window.hasFocus() ) {
client::window.setMousePosition(client::window.getSize()/2);
client::window.setMousePosition({
client::window.getSize().x * 0.5f,
client::window.getSize().y * 0.5f,
});
}
});
uf::hooks.addHook( "window:Closed", [&]( pod::payloads::windowEvent& json ){
@ -141,8 +144,11 @@ void client::tick() {
if ( client::config["window"]["mouse"]["center"].as<bool>(false) ) {
auto size = client::window.getSize();
auto current = client::window.getMousePosition();
auto center = client::window.getSize() / 2.0f;
client::window.setMousePosition(client::window.getSize() / 2.0f);
pod::Vector2i center = {
client::window.getSize().x * 0.5f,
client::window.getSize().y * 0.5f,
};
client::window.setMousePosition( center );
client::window.setCursorVisible(false);
#if UF_INPUT_USE_ENUM_MOUSE

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,602 @@
// Formatting library for C++ - color support
//
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COLOR_H_
#define FMT_COLOR_H_
#include "format.h"
FMT_BEGIN_NAMESPACE
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color
enum class terminal_color : uint8_t {
black = 30,
red,
green,
yellow,
blue,
magenta,
cyan,
white,
bright_black = 90,
bright_red,
bright_green,
bright_yellow,
bright_blue,
bright_magenta,
bright_cyan,
bright_white
};
enum class emphasis : uint8_t {
bold = 1,
italic = 1 << 1,
underline = 1 << 2,
strikethrough = 1 << 3
};
// rgb is a struct for red, green and blue colors.
// Using the name "rgb" makes some editors show the color in a tooltip.
struct rgb {
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
FMT_CONSTEXPR rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
FMT_CONSTEXPR rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF),
g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {}
uint8_t r;
uint8_t g;
uint8_t b;
};
namespace detail {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color);
}
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
}
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
value{} {
value.term_color = static_cast<uint8_t>(term_color);
}
bool is_rgb;
union color_union {
uint8_t term_color;
uint32_t rgb_color;
} value;
};
} // namespace detail
// Experimental text formatting support.
class text_style {
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
const text_style& rhs) {
return lhs |= rhs;
}
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
const text_style& rhs) {
return lhs &= rhs;
}
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
return set_foreground_color;
}
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
return set_background_color;
}
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
private:
FMT_CONSTEXPR text_style(bool is_foreground,
detail::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems() {
if (is_foreground) {
foreground_color = text_color;
set_foreground_color = true;
} else {
background_color = text_color;
set_background_color = true;
}
}
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
FMT_NOEXCEPT;
detail::color_type foreground_color;
detail::color_type background_color;
bool set_foreground_color;
bool set_background_color;
emphasis ems;
};
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground);
}
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background);
}
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs;
}
namespace detail {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
const char* esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code
// sequence.
if (!text_color.is_rgb) {
bool is_background = esc == detail::data::background_color;
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background) value += 10u;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
if (value >= 100u) {
buffer[index++] = static_cast<Char>('1');
value %= 100u;
}
buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
return;
}
for (int i = 0; i < 7; i++) {
buffer[i] = static_cast<Char>(esc[i]);
}
rgb color(text_color.value.rgb_color);
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
}
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
uint8_t em_codes[4] = {};
uint8_t em_bits = static_cast<uint8_t>(em);
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9;
size_t index = 0;
for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
}
buffer[index++] = static_cast<Char>(0);
}
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
return buffer + std::char_traits<Char>::length(buffer);
}
private:
Char buffer[7u + 3u * 4u + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT {
out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10);
out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter);
}
};
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
detail::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, detail::data::foreground_color);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
detail::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, detail::data::background_color);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
return ansi_color_escape<Char>(em);
}
template <typename Char>
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
std::fputs(chars, stream);
}
template <>
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
std::fputws(chars, stream);
}
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(detail::data::reset_color, stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(detail::data::wreset_color, stream);
}
template <typename Char>
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
const char* begin = data::reset_color;
const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end);
}
template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
auto background = detail::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
detail::vformat_to(buf, format_str, args);
if (has_style) detail::reset_color<Char>(buf);
}
} // namespace detail
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
}
/**
\rst
Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting.
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
vprint(f, ts, format_str,
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
Formats a string and prints it to stdout using ANSI escape sequences to
specify text formatting.
Example:
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
}
/**
\rst
Formats arguments and returns the result as a string using ANSI
escape sequences to specify text formatting.
**Example**::
#include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return vformat(ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
Formats a string with the given text_style and writes the output to ``out``.
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
}
/**
\rst
Formats arguments with the given text_style, writes the result to the output
iterator ``out`` and returns the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char_t<S>>::value&&
detail::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const text_style& ts,
const S& format_str, Args&&... args) {
return vformat_to(out, ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_

View File

@ -0,0 +1,700 @@
// Formatting library for C++ - experimental format string compilation
//
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#include <vector>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/**
\rst
Converts a string literal *s* into a format string that will be parsed at
compile time and converted into efficient formatting code. Requires C++17
``constexpr if`` compiler support.
**Example**::
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
return value;
}
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part {
enum class kind { arg_index, arg_name, text, replacement };
struct replacement {
arg_ref<Char> arg_id;
dynamic_format_specs<Char> specs;
};
kind part_kind;
union value {
int arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
// Position past the end of the argument id.
const Char* arg_id_end = nullptr;
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
static FMT_CONSTEXPR format_part make_arg_index(int index) {
return format_part(kind::arg_index, index);
}
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
return format_part(kind::arg_name, name);
}
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
return format_part(kind::text, text);
}
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
return format_part(kind::replacement, repl);
}
};
template <typename Char> struct part_counter {
unsigned num_parts = 0;
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end) ++num_parts;
}
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
return ++num_parts, 0;
}
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++brace_counter;
} else if (*begin == '}') {
if (brace_counter == 0u) break;
--brace_counter;
}
}
return begin;
}
FMT_CONSTEXPR void on_error(const char*) {}
};
// Counts the number of parts in a format string.
template <typename Char>
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
part_counter<Char> counter;
parse_format_string<true>(format_str, counter);
return counter.num_parts;
}
template <typename Char, typename PartHandler>
class format_string_compiler : public error_handler {
private:
using part = format_part<Char>;
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_format_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
PartHandler handler)
: handler_(handler),
format_str_(format_str),
parse_context_(format_str) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR int on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
return 0;
}
FMT_CONSTEXPR int on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
return 0;
}
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
return 0;
}
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
? arg_ref<Char>(part_.val.arg_index)
: arg_ref<Char>(part_.val.str);
auto part = part::make_replacement(repl);
part.arg_id_end = begin;
handler_(part);
return it;
}
};
// Compiles a format string and invokes handler(part) for each parsed part.
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
PartHandler handler) {
parse_format_string<IS_CONSTEXPR>(
format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename OutputIt, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Context::char_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(visit_format_arg(
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
ctx.arg(arg_id)));
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename OutputIt, typename CompiledFormat>
auto vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) -> typename Context::iterator {
using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out, args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
using format_part_t = format_part<char_type>;
switch (part.part_kind) {
case format_part_t::kind::text: {
const auto text = value.str;
auto output = ctx.out();
auto&& it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
break;
}
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision,
specs.precision_ref, ctx);
error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
ctx, nullptr, &specs),
arg));
break;
}
}
}
return ctx.out();
}
} // namespace cf
struct basic_compiled_format {};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<detail::format_part<char_type>>;
parts_container compiled_parts;
explicit compiled_format_base(basic_string_view<char_type> format_str) {
compile_format_string<false>(format_str,
[this](const format_part<char_type>& part) {
compiled_parts.push_back(part);
});
}
const parts_container& parts() const { return compiled_parts; }
};
template <typename Char, unsigned N> struct format_part_array {
format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default;
};
template <typename Char, unsigned N>
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
basic_string_view<Char> format_str) {
format_part_array<Char, N> parts;
unsigned counter = 0;
// This is not a lambda for compatibility with older compilers.
struct {
format_part<Char>* parts;
unsigned* counter;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
parts[(*counter)++] = part;
}
} collector{parts.data, &counter};
compile_format_string<true>(format_str, collector);
if (counter < N) {
parts.data[counter] =
format_part<Char>::make_text(basic_string_view<Char>());
}
return parts;
}
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template <typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
: basic_compiled_format {
using char_type = char_t<S>;
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
// Workaround for old compilers. Format string compilation will not be
// performed there anyway.
#if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
constexpr_max(count_parts(to_string_view(S())), 1u);
#else
static const unsigned num_format_parts = 1;
#endif
using parts_container = format_part<char_type>[num_format_parts];
const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>(
detail::to_string_view(S()));
return compiled_parts.data;
}
};
template <typename S, typename... Args>
class compiled_format : private compiled_format_base<S> {
public:
using typename compiled_format_base<S>::char_type;
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename OutputIt, typename CompiledFormat>
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
public:
compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {}
};
#ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return get<N - 1>(rest...);
}
template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
};
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename T> struct is_compiled_format : std::false_type {};
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, data);
}
};
template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
return {{&s[pos], size}};
}
template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, value);
}
};
template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
return write<Char>(out, arg);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
using char_type = Char;
mutable formatter<T, Char> fmt;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
const auto& vargs =
make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(arg, ctx);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
};
template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS !=
basic_string_view<typename S::char_type>(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
else
return make_concat(head, tail);
} else {
return head;
}
}
template <typename T, typename Char> struct parse_specs_result {
formatter<T, Char> fmt;
size_t end;
int next_arg_id;
};
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int arg_id) {
str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()};
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
using char_type = typename S::char_type;
constexpr basic_string_view<char_type> str = format_str;
if constexpr (str[POS] == '{') {
if (POS + 1 == str.size())
throw format_error("unmatched '{' in format string");
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') {
using type = get_type<ID, Args>;
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
format_str);
} else if constexpr (str[POS + 1] == ':') {
using type = get_type<ID, Args>;
constexpr auto result = parse_specs<type>(str, POS + 2, ID);
return parse_tail<Args, result.end, result.next_arg_id>(
spec_field<char_type, type, ID>{result.fmt}, format_str);
} else {
return unknown_format();
}
} else if constexpr (str[POS] == '}') {
if (POS + 1 == str.size())
throw format_error("unmatched '}' in format string");
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else {
constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
format_str);
}
}
}
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value ||
detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
detail::unknown_format>()) {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
} else {
return result;
}
}
}
#else
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
}
#endif // __cpp_if_constexpr
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N])
-> detail::compiled_format<const Char*, Args...> {
return detail::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
} // namespace detail
// DEPRECATED! use FMT_COMPILE instead.
template <typename... Args>
FMT_DEPRECATED auto compile(const Args&... args)
-> decltype(detail::compile(args...)) {
return detail::compile(args...);
}
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
basic_memory_buffer<Char> buffer;
cf.format(detail::buffer_appender<Char>(buffer), args...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using context = buffer_context<Char>;
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
make_format_args<context>(args...));
return to_string(buffer);
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
#ifdef __cpp_if_constexpr
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr basic_string_view<typename S::char_type> str = S();
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
return fmt::to_string(detail::first(args...));
}
#endif
constexpr auto compiled = detail::compile<Args...>(S());
return format(compiled, std::forward<Args>(args)...);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using context = format_context_t<OutputIt, char_type>;
return detail::cf::vformat_to<context>(out, cf,
make_format_args<context>(args...));
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
OutputIt format_to(OutputIt out, const S&, const Args&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
return format_to(out, compiled, args...);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<
OutputIt, typename CompiledFormat::char_type>::value&&
std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const CompiledFormat& cf,
const Args&... args) {
auto it =
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()};
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&,
const Args&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled,
args...);
return {it.base(), it.count()};
}
template <typename CompiledFormat, typename... Args>
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(detail::counting_iterator(), cf, args...).count();
}
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,64 @@
// Formatting library for C++ - std::locale support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_
#include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char>
std::basic_string<Char> vformat(
const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
return fmt::to_string(buffer);
}
} // namespace detail
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return detail::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return detail::vformat(loc, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
inline OutputIt vformat_to(
OutputIt out, const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
}
template <typename OutputIt, typename S, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
inline OutputIt format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
}
FMT_END_NAMESPACE
#endif // FMT_LOCALE_H_

View File

@ -0,0 +1,480 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OS_H_
#define FMT_OS_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <cerrno>
#include <clocale> // for locale_t
#include <cstddef>
#include <cstdio>
#include <cstdlib> // for strtod_l
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) ::call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
#ifdef _WIN32
namespace detail {
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(wstring_view s);
operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(wstring_view s);
};
FMT_API void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
} // namespace detail
/** A Windows error. */
class windows_error : public system_error {
private:
FMT_API void init(int error_code, string_view format_str, format_args args);
public:
/**
\rst
Constructs a :class:`fmt::windows_error` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
**Example**::
// This throws a windows_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
template <typename... Args>
windows_error(int error_code, string_view message, const Args&... args) {
init(error_code, message, make_format_args(args...));
}
};
// Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code,
string_view message) FMT_NOEXCEPT;
#endif // _WIN32
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...));
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
APPEND = FMT_POSIX(O_APPEND) // Open in append mode.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
public:
file(const file&) = delete;
void operator=(const file&) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) FMT_NOEXCEPT {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API size_t read(void* buffer, size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API size_t write(const void* buffer, size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char* mode);
};
// Returns the memory page size.
long getpagesize();
namespace detail {
struct buffer_size {
size_t value = 0;
buffer_size operator=(size_t val) const {
auto bs = buffer_size();
bs.value = val;
return bs;
}
};
struct ostream_params {
int oflag = file::WRONLY | file::CREATE;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
ostream_params() {}
template <typename... T>
ostream_params(T... params, int oflag) : ostream_params(params...) {
this->oflag = oflag;
}
template <typename... T>
ostream_params(T... params, detail::buffer_size bs)
: ostream_params(params...) {
this->buffer_size = bs.value;
}
};
} // namespace detail
static constexpr detail::buffer_size buffer_size;
// A fast output stream which is not thread-safe.
class ostream final : private detail::buffer<char> {
private:
file file_;
void flush() {
if (size() == 0) return;
file_.write(data(), size());
clear();
}
void grow(size_t) final;
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
public:
ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
template <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() {
flush();
file_.close();
}
template <typename S, typename... Args>
void print(const S& format_str, const Args&... args) {
format_to(detail::buffer_appender<char>(*this), format_str, args...);
}
};
/**
Opens a file for writing. Supported parameters passed in `params`:
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
* ``buffer_size=<integer>``: Output buffer size
*/
template <typename... T>
inline ostream output_file(cstring_view path, T... params) {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
static void freelocale(locale_t loc) { _free_locale(loc); }
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
locale_t locale_;
public:
using type = locale_t;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
locale() {
# ifndef _WIN32
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_OS_H_

View File

@ -0,0 +1,177 @@
// Formatting library for C++ - std::ostream support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
template <typename Char> class basic_printf_parse_context;
template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail {
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private:
using int_type = typename std::basic_streambuf<Char>::int_type;
using traits_type = typename std::basic_streambuf<Char>::traits_type;
buffer<Char>& buffer_;
public:
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
protected:
// The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call
// to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
};
struct converter {
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
};
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
void_t<> operator<<(converter);
};
// Hide insertion operators for built-in types.
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
private:
template <typename U>
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
void_t<>>::value>
test(int);
template <typename> static std::false_type test(...);
using result = decltype(test<T>(0));
public:
static const bool value = result::value;
};
// Write the content of buf to os.
template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
buf_data += n;
size -= n;
} while (size != 0);
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.try_resize(buf.size());
}
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: private formatter<basic_string_view<Char>, Char> {
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return formatter<basic_string_view<Char>, Char>::parse(ctx);
}
template <typename ParseCtx,
FMT_ENABLE_IF(std::is_same<
ParseCtx, basic_printf_parse_context<Char>>::value)>
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
}
};
} // namespace detail
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer);
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_

View File

@ -0,0 +1,2 @@
#include "os.h"
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"

View File

@ -0,0 +1,751 @@
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include "ostream.h"
FMT_BEGIN_NAMESPACE
namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) {
unsigned max = max_value<int>();
return value <= max;
}
static bool fits_in_int(bool) { return true; }
};
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
};
class printf_precision_handler {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big"));
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
int operator()(T) {
FMT_THROW(format_error("precision is not integer"));
return 0;
}
};
// An argument visitor that returns true iff arg is a zero integer.
class is_zero_int {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
bool operator()(T value) {
return value == 0;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
bool operator()(T) {
return false;
}
};
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
template <typename T, typename Context> class arg_converter {
private:
using char_type = typename Context::char_type;
basic_format_arg<Context>& arg_;
char_type type_;
public:
arg_converter(basic_format_arg<Context>& arg, char_type type)
: arg_(arg), type_(type) {}
void operator()(bool value) {
if (type_ != 's') operator()<bool>(value);
}
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
void operator()(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_ = detail::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value)));
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = detail::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value)));
}
} else {
if (is_signed) {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
} else {
arg_ = detail::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value));
}
}
}
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
void operator()(U) {} // No conversion needed for non-integral types.
};
// Converts an integer argument to T for printf, if T is an integral type.
// If T is void, the argument is converted to corresponding signed or unsigned
// type depending on the type specifier: 'd' and 'i' - signed, other -
// unsigned).
template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) {
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
}
// Converts an integer argument to char for printf.
template <typename Context> class char_converter {
private:
basic_format_arg<Context>& arg_;
public:
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
arg_ = detail::make_arg<Context>(
static_cast<typename Context::char_type>(value));
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
void operator()(T) {} // No conversion needed for non-integral types.
};
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; }
const Char* operator()(const Char* s) { return s; }
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
private:
using format_specs = basic_format_specs<Char>;
format_specs& specs_;
public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) {
FMT_THROW(format_error("width is not integer"));
return 0;
}
};
template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(buffer_appender<Char>(buf), format, args).format();
}
} // namespace detail
// For printing into memory_buffer.
template <typename Char, typename Context>
FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
basic_string_view<Char> format,
basic_format_args<Context> args) {
return detail::vprintf(buf, format, args);
}
using detail::vprintf;
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context;
/**
\rst
The ``printf`` argument formatter.
\endrst
*/
template <typename OutputIt, typename Char>
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
public:
using iterator = OutputIt;
private:
using char_type = Char;
using base = detail::arg_formatter_base<OutputIt, Char>;
using context_type = basic_printf_context<OutputIt, Char>;
context_type& context_;
void write_null_pointer(char) {
this->specs()->type = 0;
this->write("(nil)");
}
void write_null_pointer(wchar_t) {
this->specs()->type = 0;
this->write(L"(nil)");
}
public:
using format_specs = typename base::format_specs;
/**
\rst
Constructs an argument formatter object.
*buffer* is a reference to the output buffer and *specs* contains format
specifier information for standard argument types.
\endrst
*/
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(iter, &specs, detail::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
if (std::is_same<T, bool>::value) {
format_specs& fmt_specs = *this->specs();
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
fmt_specs.type = 0;
this->write(value != 0);
} else if (std::is_same<T, char_type>::value) {
format_specs& fmt_specs = *this->specs();
if (fmt_specs.type && fmt_specs.type != 'c')
return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return base::operator()(value);
} else {
return base::operator()(value);
}
return this->out();
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) {
return base::operator()(value);
}
/** Formats a null-terminated C string. */
iterator operator()(const char* value) {
if (value)
base::operator()(value);
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write("(null)");
return this->out();
}
/** Formats a null-terminated wide C string. */
iterator operator()(const wchar_t* value) {
if (value)
base::operator()(value);
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write(L"(null)");
return this->out();
}
iterator operator()(basic_string_view<char_type> value) {
return base::operator()(value);
}
iterator operator()(monostate value) { return base::operator()(value); }
/** Formats a pointer. */
iterator operator()(const void* value) {
if (value) return base::operator()(value);
this->specs()->type = 0;
write_null_pointer(char_type());
return this->out();
}
/** Formats an argument of a custom (user-defined) type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
handle.format(context_.parse_context(), context_);
return this->out();
}
};
template <typename T> struct printf_formatter {
printf_formatter() = delete;
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
detail::format_value(detail::get_container(ctx.out()), value);
return ctx.out();
}
};
/**
This template formats data and writes the output through an output iterator.
*/
template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
using char_type = Char;
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
private:
using format_specs = basic_format_specs<char_type>;
OutputIt out_;
basic_format_args<basic_printf_context> args_;
parse_context_type parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
format_arg get_arg(int arg_index = -1);
// Parses argument index, flags and width and returns the argument index.
int parse_header(const Char*& it, const Char* end, format_specs& specs);
public:
/**
\rst
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args), parse_ctx_(format_str) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
parse_context_type& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
OutputIt format();
};
template <typename OutputIt, typename Char>
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
const Char*& it,
const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-':
specs.align = align::left;
break;
case '+':
specs.sign = sign::plus;
break;
case '0':
specs.fill[0] = '0';
break;
case ' ':
if (specs.sign != sign::plus) {
specs.sign = sign::space;
}
break;
case '#':
specs.alt = true;
break;
default:
return;
}
}
}
template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
return detail::get_arg(*this, arg_index);
}
template <typename OutputIt, typename Char>
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
const Char* end,
format_specs& specs) {
int arg_index = -1;
char_type c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
detail::error_handler eh;
int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value;
} else {
if (c == '0') specs.fill[0] = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
specs.width = value;
return arg_index;
}
}
}
parse_flags(specs, it, end);
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
detail::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(visit_format_arg(
detail::printf_width_handler<char_type>(specs), get_arg()));
}
}
return arg_index;
}
template <typename OutputIt, typename Char>
template <typename ArgFormatter>
OutputIt basic_printf_context<OutputIt, Char>::format() {
auto out = this->out();
const Char* start = parse_ctx_.begin();
const Char* end = parse_ctx_.end();
auto it = start;
while (it != end) {
char_type c = *it++;
if (c != '%') continue;
if (it != end && *it == c) {
out = std::copy(start, it, out);
start = ++it;
continue;
}
out = std::copy(start, it - 1, out);
format_specs specs;
specs.align = align::right;
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument not found");
// Parse precision.
if (it != end && *it == '.') {
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
detail::error_handler eh;
specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') {
++it;
specs.precision = static_cast<int>(
visit_format_arg(detail::printf_precision_handler(), get_arg()));
} else {
specs.precision = 0;
}
}
format_arg arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral())
specs.fill[0] =
' '; // Ignore '0' flag for non-numeric types or if '-' present.
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>(
str,
detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
}
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false;
if (specs.fill[0] == '0') {
if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric;
else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0;
using detail::convert_arg;
switch (c) {
case 'h':
if (t == 'h') {
++it;
t = it != end ? *it : 0;
convert_arg<signed char>(arg, t);
} else {
convert_arg<short>(arg, t);
}
break;
case 'l':
if (t == 'l') {
++it;
t = it != end ? *it : 0;
convert_arg<long long>(arg, t);
} else {
convert_arg<long>(arg, t);
}
break;
case 'j':
convert_arg<intmax_t>(arg, t);
break;
case 'z':
convert_arg<size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, t);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--it;
convert_arg<void>(arg, c);
}
// Parse type.
if (it == end) FMT_THROW(format_error("invalid format string"));
specs.type = static_cast<char>(*it++);
if (arg.is_integral()) {
// Normalize type.
switch (specs.type) {
case 'i':
case 'u':
specs.type = 'd';
break;
case 'c':
visit_format_arg(detail::char_converter<basic_printf_context>(arg),
arg);
break;
}
}
start = it;
// Format argument.
out = visit_format_arg(ArgFormatter(out, specs, *this), arg);
}
return std::copy(start, it, out);
}
template <typename Char>
using basic_printf_context_t =
basic_printf_context<detail::buffer_appender<Char>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>;
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst
*/
template <typename... Args>
inline format_arg_store<printf_context, Args...> make_printf_args(
const Args&... args) {
return {args...};
}
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst
*/
template <typename... Args>
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
const Args&... args) {
return {args...};
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf(
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(format), args);
return to_string(buffer);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(
std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(format), args);
size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
}
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format),
make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vprintf(
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
return vfprintf(stdout, to_string_view(format), args);
}
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
inline int printf(const S& format_str, const Args&... args) {
using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str),
make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(
std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(format), args);
detail::write_buffer(os, buffer);
return static_cast<int>(buffer.size());
}
/** Formats arguments and writes the output to the range. */
template <typename ArgFormatter, typename Char,
typename Context =
basic_printf_context<typename ArgFormatter::iterator, Char>>
typename ArgFormatter::iterator vprintf(
detail::buffer<Char>& out, basic_string_view<Char> format_str,
basic_format_args<type_identity_t<Context>> args) {
typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>();
return iter;
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str),
make_format_args<context>(args...));
}
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_

View File

@ -0,0 +1,393 @@
// Formatting library for C++ - experimental range support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
//
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
// All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface.
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include <initializer_list>
#include <type_traits>
#include "format.h"
// output only up to N items from the range.
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif
FMT_BEGIN_NAMESPACE
template <typename Char> struct formatting_base {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix;
Char delimiter;
Char postfix;
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
template <typename Char, typename Enable = void>
struct formatting_tuple : formatting_base<Char> {
Char prefix;
Char delimiter;
Char postfix;
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
namespace detail {
template <typename RangeT, typename OutputIterator>
OutputIterator copy(const RangeT& range, OutputIterator out) {
for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it;
return out;
}
template <typename OutputIterator>
OutputIterator copy(const char* str, OutputIterator out) {
while (*str) *out++ = *str++;
return out;
}
template <typename OutputIterator>
OutputIterator copy(char ch, OutputIterator out) {
*out++ = ch;
return out;
}
/// Return true value if T has std::string interface, like std::string_view.
template <typename T> class is_like_std_string {
template <typename U>
static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Char>
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
template <typename T>
struct is_range_<
T, conditional_t<false,
conditional_helper<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>,
void>> : std::true_type {};
#endif
/// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
template <typename U>
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
};
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
#endif
template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
using std::get;
// using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
(void)_; // blocks warnings
}
template <class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
T const&) {
return {};
}
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
template <typename Range>
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
return add_space ? " {}" : "{}";
}
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
return add_space ? " \"{}\"" : "\"{}\"";
}
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
return add_space ? " \"{}\"" : "\"{}\"";
}
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
return add_space ? L" \"{}\"" : L"\"{}\"";
}
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
return add_space ? " '{}'" : "'{}'";
}
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace detail
template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
template <typename TupleT, typename Char>
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
private:
// C++11 generic lambda for format()
template <typename FormatContext> struct format_each {
template <typename T> void operator()(const T& v) {
if (i > 0) {
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i;
}
formatting_tuple<Char>& formatting;
size_t& i;
typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
};
public:
formatting_tuple<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
size_t i = 0;
detail::copy(formatting.prefix, out);
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
detail::copy(formatting.postfix, out);
return ctx.out();
}
};
template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<detail::std_string_view<Char>, T>::value;
};
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<fmt::is_range<T, Char>::value
// Workaround a bug in MSVC 2017 and earlier.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
&& has_formatter<detail::value_type<T>, format_context>::value
#endif
>> {
formatting_range<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext>
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0;
auto it = values.begin();
auto end = values.end();
for (; it != end; ++it) {
if (i > 0) {
if (formatting.add_prepostfix_space) *out++ = ' ';
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
out = format_to(out, " ... <other elements>");
break;
}
}
if (formatting.add_prepostfix_space) *out++ = ' ';
return detail::copy(formatting.postfix, out);
}
};
template <typename Char, typename... T> struct tuple_arg_join : detail::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple{t}, sep{s} {}
};
template <typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
detail::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
template <typename FormatContext>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out();
}
template <typename FormatContext, typename Arg, typename... Args>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) {
using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = ctx.out();
out = base{}.format(arg, ctx);
if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return format_args(value, ctx, args...);
}
return out;
}
};
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
*/
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
string_view sep) {
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
wstring_view sep) {
return {tuple, sep};
}
/**
\rst
Returns an object that formats `initializer_list` with elements separated by
`sep`.
**Example**::
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
// Output: "1, 2, 3"
\endrst
*/
template <typename T>
arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
string_view sep) {
return join(std::begin(list), std::end(list), sep);
}
template <typename T>
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
wstring_view sep) {
return join(std::begin(list), std::end(list), sep);
}
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_

View File

@ -0,0 +1,204 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_COLLISION_BODY_H
#define REACTPHYSICS3D_COLLISION_BODY_H
// Libraries
#include <cassert>
#include <reactphysics3d/engine/Entity.h>
#include <reactphysics3d/collision/shapes/AABB.h>
#include <reactphysics3d/mathematics/Transform.h>
#include <reactphysics3d/configuration.h>
/// Namespace reactphysics3d
namespace reactphysics3d {
// Declarations
class Collider;
class CollisionShape;
class PhysicsWorld;
struct RaycastInfo;
class DefaultPoolAllocator;
class Profiler;
class Logger;
// Class CollisionBody
/**
* This class represents a body that is able to collide with others
* bodies.
*/
class CollisionBody {
protected :
// -------------------- Attributes -------------------- //
/// Identifier of the entity in the ECS
Entity mEntity;
/// Reference to the world the body belongs to
PhysicsWorld& mWorld;
#ifdef IS_RP3D_PROFILING_ENABLED
/// Pointer to the profiler
Profiler* mProfiler;
#endif
// -------------------- Methods -------------------- //
/// Remove all the collision shapes
void removeAllColliders();
/// Update the broad-phase state for this body (because it has moved for instance)
void updateBroadPhaseState(decimal timeStep) const;
/// Ask the broad-phase to test again the collision shapes of the body for collision
/// (as if the body has moved).
void askForBroadPhaseCollisionCheck() const;
public :
// -------------------- Methods -------------------- //
/// Constructor
CollisionBody(PhysicsWorld& world, Entity entity);
/// Destructor
virtual ~CollisionBody();
/// Deleted copy-constructor
CollisionBody(const CollisionBody& body) = delete;
/// Deleted assignment operator
CollisionBody& operator=(const CollisionBody& body) = delete;
/// Return the corresponding entity of the body
Entity getEntity() const;
/// Return true if the body is active
bool isActive() const;
/// Return a pointer to the user data attached to this body
void* getUserData() const;
/// Attach user data to this body
void setUserData(void* userData);
/// Set whether or not the body is active
virtual void setIsActive(bool isActive);
/// Return the current position and orientation
const Transform& getTransform() const;
/// Set the current position and orientation
virtual void setTransform(const Transform& transform);
/// Create a new collider and add it to the body.
virtual Collider* addCollider(CollisionShape* collisionShape, const Transform& transform);
/// Remove a collider from the body
virtual void removeCollider(Collider* collider);
/// Return true if a point is inside the collision body
bool testPointInside(const Vector3& worldPoint) const;
/// Raycast method with feedback information
bool raycast(const Ray& ray, RaycastInfo& raycastInfo);
/// Test if the collision body overlaps with a given AABB
bool testAABBOverlap(const AABB& worldAABB) const;
/// Compute and return the AABB of the body by merging all colliders AABBs
AABB getAABB() const;
/// Return a const pointer to a given collider of the body
const Collider* getCollider(uint colliderIndex) const;
/// Return a pointer to a given collider of the body
Collider* getCollider(uint colliderIndex);
/// Return the number of colliders associated with this body
uint getNbColliders() const;
/// Return the world-space coordinates of a point given the local-space coordinates of the body
Vector3 getWorldPoint(const Vector3& localPoint) const;
/// Return the world-space vector of a vector given in local-space coordinates of the body
Vector3 getWorldVector(const Vector3& localVector) const;
/// Return the body local-space coordinates of a point given in the world-space coordinates
Vector3 getLocalPoint(const Vector3& worldPoint) const;
/// Return the body local-space coordinates of a vector given in the world-space coordinates
Vector3 getLocalVector(const Vector3& worldVector) const;
#ifdef IS_RP3D_PROFILING_ENABLED
/// Set the profiler
virtual void setProfiler(Profiler* profiler);
#endif
// -------------------- Friendship -------------------- //
friend class PhysicsWorld;
friend class CollisionDetectionSystem;
friend class BroadPhaseAlgorithm;
friend class ConvexMeshShape;
friend class Collider;
};
/// Test if the collision body overlaps with a given AABB
/**
* @param worldAABB The AABB (in world-space coordinates) that will be used to test overlap
* @return True if the given AABB overlaps with the AABB of the collision body
*/
inline bool CollisionBody::testAABBOverlap(const AABB& worldAABB) const {
return worldAABB.testCollision(getAABB());
}
// Return the corresponding entity of the body
/**
* @return The entity of the body
*/
inline Entity CollisionBody::getEntity() const {
return mEntity;
}
#ifdef IS_RP3D_PROFILING_ENABLED
// Set the profiler
inline void CollisionBody::setProfiler(Profiler* profiler) {
mProfiler = profiler;
}
#endif
}
#endif

View File

@ -0,0 +1,211 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_RIGID_BODY_H
#define REACTPHYSICS3D_RIGID_BODY_H
// Libraries
#include <cassert>
#include <reactphysics3d/body/CollisionBody.h>
#include <reactphysics3d/mathematics/mathematics.h>
/// Namespace reactphysics3d
namespace reactphysics3d {
// Class declarations
struct JointListElement;
class PhysicsWorld;
class MemoryManager;
enum class BodyType;
// Class RigidBody
/**
* This class represents a rigid body of the physics
* engine. A rigid body is a non-deformable body that
* has a constant mass. This class inherits from the
* CollisionBody class.
*/
class RigidBody : public CollisionBody {
protected :
// -------------------- Methods -------------------- //
/// Set the variable to know whether or not the body is sleeping
void setIsSleeping(bool isSleeping);
/// Update whether the current overlapping pairs where this body is involed are active or not
void updateOverlappingPairs();
/// Compute and return the local-space center of mass of the body using its colliders
Vector3 computeCenterOfMass() const;
/// Compute the local-space inertia tensor and total mass of the body using its colliders
void computeMassAndInertiaTensorLocal(Vector3& inertiaTensorLocal, decimal& totalMass) const;
/// Return the inverse of the inertia tensor in world coordinates.
static const Matrix3x3 getWorldInertiaTensorInverse(PhysicsWorld& world, Entity bodyEntity);
public :
// -------------------- Methods -------------------- //
/// Constructor
RigidBody(PhysicsWorld& world, Entity entity);
/// Destructor
virtual ~RigidBody() override = default;
/// Deleted copy-constructor
RigidBody(const RigidBody& body) = delete;
/// Deleted assignment operator
RigidBody& operator=(const RigidBody& body) = delete;
/// Set the current position and orientation
virtual void setTransform(const Transform& transform) override;
/// Return the mass of the body
decimal getMass() const;
/// Set the mass of the rigid body
void setMass(decimal mass);
/// Return the linear velocity
Vector3 getLinearVelocity() const;
/// Set the linear velocity of the body.
void setLinearVelocity(const Vector3& linearVelocity);
/// Return the angular velocity
Vector3 getAngularVelocity() const;
/// Set the angular velocity.
void setAngularVelocity(const Vector3& angularVelocity);
/// Return the local inertia tensor of the body (in body coordinates)
const Vector3& getLocalInertiaTensor() const;
/// Set the local inertia tensor of the body (in body coordinates)
void setLocalInertiaTensor(const Vector3& inertiaTensorLocal);
/// Return the center of mass of the body (in local-space coordinates)
const Vector3& getLocalCenterOfMass() const;
/// Set the center of mass of the body (in local-space coordinates)
void setLocalCenterOfMass(const Vector3& centerOfMass);
/// Compute and set the local-space center of mass of the body using its colliders
void updateLocalCenterOfMassFromColliders();
/// Compute and set the local-space inertia tensor of the body using its colliders
void updateLocalInertiaTensorFromColliders();
/// Compute and set the mass of the body using its colliders
void updateMassFromColliders();
/// Compute and set the center of mass, the mass and the local-space inertia tensor of the body using its colliders
void updateMassPropertiesFromColliders();
/// Return the type of the body
BodyType getType() const;
/// Set the type of the body
void setType(BodyType type);
/// Return true if the gravity needs to be applied to this rigid body
bool isGravityEnabled() const;
/// Set the variable to know if the gravity is applied to this rigid body
void enableGravity(bool isEnabled);
/// Return the linear velocity damping factor
decimal getLinearDamping() const;
/// Set the linear damping factor
void setLinearDamping(decimal linearDamping);
/// Return the angular velocity damping factor
decimal getAngularDamping() const;
/// Set the angular damping factor
void setAngularDamping(decimal angularDamping);
/// Apply an external force to the body at its center of mass.
void applyForceToCenterOfMass(const Vector3& force);
/// Apply an external force to the body at a given point (in local-space coordinates).
void applyForceAtLocalPosition(const Vector3& force, const Vector3& point);
/// Apply an external force to the body at a given point (in world-space coordinates).
void applyForceAtWorldPosition(const Vector3& force, const Vector3& point);
/// Apply an external torque to the body.
void applyTorque(const Vector3& torque);
/// Return whether or not the body is allowed to sleep
bool isAllowedToSleep() const;
/// Set whether or not the body is allowed to go to sleep
void setIsAllowedToSleep(bool isAllowedToSleep);
/// Return whether or not the body is sleeping
bool isSleeping() const;
/// Set whether or not the body is active
virtual void setIsActive(bool isActive) override;
/// Create a new collider and add it to the body
virtual Collider* addCollider(CollisionShape* collisionShape, const Transform& transform) override;
/// Remove a collider from the body
virtual void removeCollider(Collider* collider) override;
#ifdef IS_RP3D_PROFILING_ENABLED
/// Set the profiler
void setProfiler(Profiler* profiler) override;
#endif
// -------------------- Friendship -------------------- //
friend class PhysicsWorld;
friend class ContactSolverSystem;
friend class DynamicsSystem;
friend class BallAndSocketJoint;
friend class SliderJoint;
friend class HingeJoint;
friend class FixedJoint;
friend class SolveBallAndSocketJointSystem;
friend class SolveFixedJointSystem;
friend class SolveHingeJointSystem;
friend class SolveSliderJointSystem;
friend class Joint;
};
}
#endif

View File

@ -0,0 +1,238 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_COLLIDER_H
#define REACTPHYSICS3D_COLLIDER_H
// Libraries
#include <reactphysics3d/body/CollisionBody.h>
#include <reactphysics3d/collision/shapes/CollisionShape.h>
#include <reactphysics3d/engine/Material.h>
#include <reactphysics3d/utils/Logger.h>
namespace reactphysics3d {
// Declarations
class MemoryManager;
// Class Collider
/**
* A collider has a collision shape (box, sphere, capsule, ...) and is attached to a CollisionBody or
* RigidBody. A body can have multiple colliders. The collider also have a mass value and a Material
* with many physics parameters like friction or bounciness. When you create a body, you need to attach
* at least one collider to it if you want that body to be able to collide in the physics world.
*/
class Collider {
protected:
// -------------------- Attributes -------------------- //
/// Reference to the memory manager
MemoryManager& mMemoryManager;
/// Identifier of the entity in the ECS
Entity mEntity;
/// Pointer to the parent body
CollisionBody* mBody;
/// Material properties of the rigid body
Material mMaterial;
/// Pointer to user data
void* mUserData;
#ifdef IS_RP3D_PROFILING_ENABLED
/// Pointer to the profiler
Profiler* mProfiler;
#endif
// -------------------- Methods -------------------- //
/// Notify the collider that the size of the collision shape has been
/// changed by the user
void setHasCollisionShapeChangedSize(bool hasCollisionShapeChangedSize);
public:
// -------------------- Methods -------------------- //
/// Constructor
Collider(Entity entity, CollisionBody* body, MemoryManager& memoryManager);
/// Destructor
virtual ~Collider();
/// Deleted copy-constructor
Collider(const Collider& collider) = delete;
/// Deleted assignment operator
Collider& operator=(const Collider& collider) = delete;
/// Return the corresponding entity of the collider
Entity getEntity() const;
/// Return a pointer to the collision shape
CollisionShape* getCollisionShape();
/// Return a const pointer to the collision shape
const CollisionShape* getCollisionShape() const;
/// Return the parent body
CollisionBody* getBody() const;
/// Return a pointer to the user data attached to this body
void* getUserData() const;
/// Attach user data to this body
void setUserData(void* userData);
/// Return the local to parent body transform
const Transform& getLocalToBodyTransform() const;
/// Set the local to parent body transform
void setLocalToBodyTransform(const Transform& transform);
/// Return the local to world transform
const Transform getLocalToWorldTransform() const;
/// Return the AABB of the collider in world-space
const AABB getWorldAABB() const;
/// Test if the collider overlaps with a given AABB
bool testAABBOverlap(const AABB& worldAABB) const;
/// Return true if a point is inside the collision shape
bool testPointInside(const Vector3& worldPoint);
/// Raycast method with feedback information
bool raycast(const Ray& ray, RaycastInfo& raycastInfo);
/// Return the collision bits mask
unsigned short getCollideWithMaskBits() const;
/// Set the collision bits mask
void setCollideWithMaskBits(unsigned short collideWithMaskBits);
/// Return the collision category bits
unsigned short getCollisionCategoryBits() const;
/// Set the collision category bits
void setCollisionCategoryBits(unsigned short collisionCategoryBits);
/// Return the broad-phase id
int getBroadPhaseId() const;
/// Return a reference to the material properties of the collider
Material& getMaterial();
/// Set a new material for this collider
void setMaterial(const Material& material);
/// Return true if the collider is a trigger
bool getIsTrigger() const;
/// Set whether the collider is a trigger
void setIsTrigger(bool isTrigger) const;
#ifdef IS_RP3D_PROFILING_ENABLED
/// Set the profiler
void setProfiler(Profiler* profiler);
#endif
// -------------------- Friendship -------------------- //
friend class OverlappingPair;
friend class CollisionBody;
friend class RigidBody;
friend class BroadPhaseAlgorithm;
friend class DynamicAABBTree;
friend class CollisionDetectionSystem;
friend class PhysicsWorld;
friend class GJKAlgorithm;
friend class ConvexMeshShape;
friend class CollisionShape;
friend class ContactManifoldSet;
friend class MiddlePhaseTriangleCallback;
};
// Return the corresponding entity of the collider
/**
* @return The entity of the collider
*/
inline Entity Collider::getEntity() const {
return mEntity;
}
// Return the parent body
/**
* @return Pointer to the parent body
*/
inline CollisionBody* Collider::getBody() const {
return mBody;
}
// Return a pointer to the user data attached to this body
/**
* @return A pointer to the user data stored into the collider
*/
inline void* Collider::getUserData() const {
return mUserData;
}
// Attach user data to this body
/**
* @param userData Pointer to the user data you want to store within the collider
*/
inline void Collider::setUserData(void* userData) {
mUserData = userData;
}
/// Test if the collider overlaps with a given AABB
/**
* @param worldAABB The AABB (in world-space coordinates) that will be used to test overlap
* @return True if the given AABB overlaps with the AABB of the collision body
*/
inline bool Collider::testAABBOverlap(const AABB& worldAABB) const {
return worldAABB.testCollision(getWorldAABB());
}
// Return a reference to the material properties of the collider
/**
* @return A reference to the material of the body
*/
inline Material& Collider::getMaterial() {
return mMaterial;
}
}
#endif

View File

@ -0,0 +1,345 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_COLLISION_CALLBACK_H
#define REACTPHYSICS3D_COLLISION_CALLBACK_H
// Libraries
#include <reactphysics3d/containers/List.h>
#include <reactphysics3d/collision/ContactPair.h>
#include <reactphysics3d/constraint/ContactPoint.h>
/// ReactPhysics3D namespace
namespace reactphysics3d {
// Declarations
class OverlappingPair;
class ContactManifold;
class CollisionBody;
class Collider;
class MemoryManager;
// Class CollisionCallback
/**
* This abstract class can be used to register a callback for collision test queries.
* You should implement your own class inherited from this one and implement
* the notifyContact() method. This method will called each time a contact
* point is reported.
*/
class CollisionCallback {
public:
// Class ContactPoint
/**
* This class represents a contact point between two colliders of the physics world.
*/
class ContactPoint {
private:
// -------------------- Attributes -------------------- //
const reactphysics3d::ContactPoint& mContactPoint;
// -------------------- Methods -------------------- //
/// Constructor
ContactPoint(const reactphysics3d::ContactPoint& contactPoint);
public:
// -------------------- Methods -------------------- //
/// Copy constructor
ContactPoint(const ContactPoint& contactPoint) = default;
/// Assignment operator
ContactPoint& operator=(const ContactPoint& contactPoint) = delete;
/// Destructor
~ContactPoint() = default;
/// Return the penetration depth
/**
* @return The penetration depth between the two colliders at this contact point
*/
decimal getPenetrationDepth() const;
/// Return the world-space contact normal
/**
* @return The world-space contact normal
*/
const Vector3& getWorldNormal() const;
/// Return the contact point on the first collider in the local-space of the first collider
/**
* @return The local-space contact point on the first collider
*/
const Vector3& getLocalPointOnCollider1() const;
/// Return the contact point on the second collider in the local-space of the second collider
/**
* @return The local-space contact point on the second collider
*/
const Vector3& getLocalPointOnCollider2() const;
// -------------------- Friendship -------------------- //
friend class CollisionCallback;
};
// Class ContactPair
/**
* This class represents the contact between two colliders of the physics world.
* A contact pair contains a list of contact points.
*/
class ContactPair {
public:
/// Enumeration EventType that describes the type of contact event
enum class EventType {
/// This contact is a new contact between the two
/// colliders (the colliders where not touching in the previous frame)
ContactStart,
/// The two colliders were already touching in the previous frame and this is a new or updated contact
ContactStay,
/// The two colliders were in contact in the previous frame and are not in contact anymore
ContactExit
};
private:
// -------------------- Attributes -------------------- //
const reactphysics3d::ContactPair& mContactPair;
/// Pointer to the contact points
List<reactphysics3d::ContactPoint>* mContactPoints;
/// Reference to the physics world
PhysicsWorld& mWorld;
/// True if this is a lost contact pair (contact pair colliding in previous frame but not in current one)
bool mIsLostContactPair;
// -------------------- Methods -------------------- //
/// Constructor
ContactPair(const reactphysics3d::ContactPair& contactPair, List<reactphysics3d::ContactPoint>* contactPoints,
PhysicsWorld& world, bool mIsLostContactPair);
public:
// -------------------- Methods -------------------- //
/// Copy constructor
ContactPair(const ContactPair& contactPair) = default;
/// Assignment operator
ContactPair& operator=(const ContactPair& contactPair) = delete;
/// Destructor
~ContactPair() = default;
/// Return the number of contact points in the contact pair
/**
* @return The number of contact points in the contact pair
*/
uint getNbContactPoints() const;
/// Return a given contact point
/**
* @param index Index of the contact point to retrieve
* @return A contact point object
*/
ContactPoint getContactPoint(uint index) const;
/// Return a pointer to the first body in contact
/**
* @return A pointer to the first colliding body of the pair
*/
CollisionBody* getBody1() const;
/// Return a pointer to the second body in contact
/**
* @return A pointer to the second colliding body of the pair
*/
CollisionBody* getBody2() const;
/// Return a pointer to the first collider in contact (in body 1)
/**
* @return A pointer to the first collider of the contact pair
*/
Collider* getCollider1() const;
/// Return a pointer to the second collider in contact (in body 2)
/**
* @return A pointer to the second collider of the contact pair
*/
Collider* getCollider2() const;
/// Return the corresponding type of event for this contact pair
/**
* @return The type of contact event for this contact pair
*/
EventType getEventType() const;
// -------------------- Friendship -------------------- //
friend class CollisionCallback;
};
// Class CallbackData
/**
* This class contains data about contacts between bodies
*/
class CallbackData {
private:
// -------------------- Attributes -------------------- //
/// Pointer to the list of contact pairs (contains contacts and triggers events)
List<reactphysics3d::ContactPair>* mContactPairs;
/// Pointer to the list of contact manifolds
List<ContactManifold>* mContactManifolds;
/// Pointer to the contact points
List<reactphysics3d::ContactPoint>* mContactPoints;
/// Pointer to the list of lost contact pairs (contains contacts and triggers events)
List<reactphysics3d::ContactPair>& mLostContactPairs;
/// List of indices of the mContactPairs list that are contact events (not overlap/triggers)
List<uint> mContactPairsIndices;
/// List of indices of the mLostContactPairs list that are contact events (not overlap/triggers)
List<uint> mLostContactPairsIndices;
/// Reference to the physics world
PhysicsWorld& mWorld;
// -------------------- Methods -------------------- //
/// Constructor
CallbackData(List<reactphysics3d::ContactPair>* contactPairs, List<ContactManifold>* manifolds,
List<reactphysics3d::ContactPoint>* contactPoints, List<reactphysics3d::ContactPair>& lostContactPairs,
PhysicsWorld& world);
/// Deleted copy constructor
CallbackData(const CallbackData& callbackData) = delete;
/// Deleted assignment operator
CallbackData& operator=(const CallbackData& callbackData) = delete;
/// Destructor
~CallbackData() = default;
public:
// -------------------- Methods -------------------- //
/// Return the number of contact pairs
/**
* @return The number of contact pairs
*/
uint getNbContactPairs() const;
/// Return a given contact pair
/**
* @param index Index of the contact pair to retrieve
* @return A contact pair object
*/
ContactPair getContactPair(uint index) const;
// -------------------- Friendship -------------------- //
friend class CollisionDetectionSystem;
};
/// Destructor
virtual ~CollisionCallback() = default;
/// This method is called when some contacts occur
virtual void onContact(const CallbackData& callbackData)=0;
};
// Return the number of contact pairs (there is a single contact pair between two bodies in contact)
/**
* @return The number of contact pairs
*/
inline uint CollisionCallback::CallbackData::getNbContactPairs() const {
return mContactPairsIndices.size() + mLostContactPairsIndices.size();
}
// Return the number of contact points in the contact pair
/**
* @return The number of contact points
*/
inline uint CollisionCallback::ContactPair::getNbContactPoints() const {
return mContactPair.nbToTalContactPoints;
}
// Return the penetration depth between the two bodies in contact
/**
* @return The penetration depth (larger than zero)
*/
inline decimal CollisionCallback::ContactPoint::getPenetrationDepth() const {
return mContactPoint.getPenetrationDepth();
}
// Return the world-space contact normal (vector from first body toward second body)
/**
* @return The contact normal direction at the contact point (in world-space)
*/
inline const Vector3& CollisionCallback::ContactPoint::getWorldNormal() const {
return mContactPoint.getNormal();
}
// Return the contact point on the first collider in the local-space of the first collider
/**
* @return The contact point in the local-space of the first collider (from body1) in contact
*/
inline const Vector3& CollisionCallback::ContactPoint::getLocalPointOnCollider1() const {
return mContactPoint.getLocalPointOnShape1();
}
// Return the contact point on the second collider in the local-space of the second collider
/**
* @return The contact point in the local-space of the second collider (from body2) in contact
*/
inline const Vector3& CollisionCallback::ContactPoint::getLocalPointOnCollider2() const {
return mContactPoint.getLocalPointOnShape2();
}
}
#endif

View File

@ -0,0 +1,134 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_CONTACT_MANIFOLD_H
#define REACTPHYSICS3D_CONTACT_MANIFOLD_H
// Libraries
#include <reactphysics3d/collision/Collider.h>
/// ReactPhysics3D namespace
namespace reactphysics3d {
// Class declarations
class ContactManifold;
class ContactManifoldInfo;
struct ContactPointInfo;
class CollisionBody;
class ContactPoint;
class DefaultPoolAllocator;
// Class ContactManifold
/**
* This class represents a set of contact points between two bodies that
* all have a similar contact normal direction. Usually, there is a single
* contact manifold when two convex shapes are in contact. However, when
* a convex shape collides with a concave shape, there might be several
* contact manifolds with different normal directions.
* The contact manifold is implemented in a way to cache the contact
* points among the frames for better stability (warm starting of the
* contact solver)
*/
class ContactManifold {
public:
// -------------------- Constants -------------------- //
/// Maximum number of contact points in a reduced contact manifold
const int MAX_CONTACT_POINTS_IN_MANIFOLD = 4;
// -------------------- Attributes -------------------- //
/// Index of the first contact point of the manifold in the list of contact points
uint contactPointsIndex;
/// Entity of the first body in contact
Entity bodyEntity1;
/// Entity of the second body in contact
Entity bodyEntity2;
/// Entity of the first collider in contact
Entity colliderEntity1;
/// Entity of the second collider in contact
Entity colliderEntity2;
/// Number of contacts in the cache
int8 nbContactPoints;
/// First friction vector of the contact manifold
Vector3 frictionVector1;
/// Second friction vector of the contact manifold
Vector3 frictionVector2;
/// First friction constraint accumulated impulse
decimal frictionImpulse1;
/// Second friction constraint accumulated impulse
decimal frictionImpulse2;
/// Twist friction constraint accumulated impulse
decimal frictionTwistImpulse;
/// Accumulated rolling resistance impulse
Vector3 rollingResistanceImpulse;
/// True if the contact manifold has already been added into an island
bool isAlreadyInIsland;
public:
// -------------------- Methods -------------------- //
/// Constructor
ContactManifold(Entity bodyEntity1, Entity bodyEntity2, Entity colliderEntity1, Entity colliderEntity2,
uint contactPointsIndex, int8 nbContactPoints);
/// Destructor
~ContactManifold();
/// Copy-constructor
ContactManifold(const ContactManifold& contactManifold) = default;
/// Assignment operator
ContactManifold& operator=(const ContactManifold& contactManifold) = default;
// -------------------- Friendship -------------------- //
friend class PhysicsWorld;
friend class Island;
friend class CollisionBody;
friend class ContactManifoldSet;
friend class ContactSolverSystem;
friend class CollisionDetectionSystem;
};
}
#endif

View File

@ -0,0 +1,67 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_CONTACT_MANIFOLD_INFO_H
#define REACTPHYSICS3D_CONTACT_MANIFOLD_INFO_H
// Libraries
#include <reactphysics3d/mathematics/mathematics.h>
#include <reactphysics3d/configuration.h>
#include <reactphysics3d/engine/OverlappingPairs.h>
/// ReactPhysics3D namespace
namespace reactphysics3d {
// Structure ContactManifoldInfo
/**
* This structure contains informations about a collision contact
* manifold computed during the narrow-phase collision detection.
*/
struct ContactManifoldInfo {
public:
// -------------------- Attributes -------------------- //
/// Indices of the contact points in the mPotentialContactPoints array
List<uint> potentialContactPointsIndices;
/// Overlapping pair id
uint64 pairId;
// -------------------- Methods -------------------- //
/// Constructor
ContactManifoldInfo(uint64 pairId, MemoryAllocator& allocator)
: potentialContactPointsIndices(allocator), pairId(pairId) {
}
};
}
#endif

View File

@ -0,0 +1,104 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_OVERLAPPING_PAIR_CONTACT_H
#define REACTPHYSICS3D_OVERLAPPING_PAIR_CONTACT_H
// Libraries
#include <reactphysics3d/mathematics/mathematics.h>
#include <reactphysics3d/configuration.h>
#include <reactphysics3d/engine/OverlappingPairs.h>
/// ReactPhysics3D namespace
namespace reactphysics3d {
// Structure ContactPair
/**
* This structure represents a pair of shapes that are in contact during narrow-phase.
*/
struct ContactPair {
public:
// -------------------- Attributes -------------------- //
/// Overlapping pair Id
uint64 pairId;
/// Indices of the potential contact manifolds
List<uint> potentialContactManifoldsIndices;
/// Entity of the first body of the contact
Entity body1Entity;
/// Entity of the second body of the contact
Entity body2Entity;
/// Entity of the first collider of the contact
Entity collider1Entity;
/// Entity of the second collider of the contact
Entity collider2Entity;
/// True if the manifold is already in an island
bool isAlreadyInIsland;
/// Index of the contact pair in the array of pairs
uint contactPairIndex;
/// Index of the first contact manifold in the array
uint contactManifoldsIndex;
/// Number of contact manifolds
int8 nbContactManifolds;
/// Index of the first contact point in the array of contact points
uint contactPointsIndex;
/// Total number of contact points in all the manifolds of the contact pair
uint nbToTalContactPoints;
/// True if the colliders of the pair were already colliding in the previous frame
bool collidingInPreviousFrame;
/// True if one of the two involved colliders is a trigger
bool isTrigger;
// -------------------- Methods -------------------- //
/// Constructor
ContactPair(uint64 pairId, Entity body1Entity, Entity body2Entity, Entity collider1Entity,
Entity collider2Entity, uint contactPairIndex, bool collidingInPreviousFrame, bool isTrigger, MemoryAllocator& allocator)
: pairId(pairId), potentialContactManifoldsIndices(allocator), body1Entity(body1Entity), body2Entity(body2Entity),
collider1Entity(collider1Entity), collider2Entity(collider2Entity),
isAlreadyInIsland(false), contactPairIndex(contactPairIndex), contactManifoldsIndex(0), nbContactManifolds(0),
contactPointsIndex(0), nbToTalContactPoints(0), collidingInPreviousFrame(collidingInPreviousFrame), isTrigger(isTrigger) {
}
};
}
#endif

View File

@ -0,0 +1,86 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_CONTACT_POINT_INFO_H
#define REACTPHYSICS3D_CONTACT_POINT_INFO_H
// Libraries
#include <reactphysics3d/mathematics/mathematics.h>
#include <reactphysics3d/configuration.h>
/// ReactPhysics3D namespace
namespace reactphysics3d {
// Declarations
class CollisionBody;
// Structure ContactPointInfo
/**
* This structure contains informations about a collision contact
* computed during the narrow-phase collision detection. Those
* informations are used to compute the contact set for a contact
* between two bodies.
*/
struct ContactPointInfo {
private:
// -------------------- Methods -------------------- //
public:
// -------------------- Attributes -------------------- //
/// Normalized normal vector of the collision contact in world space
Vector3 normal;
/// Penetration depth of the contact
decimal penetrationDepth;
/// Contact point of body 1 in local space of body 1
Vector3 localPoint1;
/// Contact point of body 2 in local space of body 2
Vector3 localPoint2;
// -------------------- Methods -------------------- //
/// Constructor
ContactPointInfo(const Vector3& contactNormal, decimal penDepth,
const Vector3& localPt1, const Vector3& localPt2)
: normal(contactNormal), penetrationDepth(penDepth),
localPoint1(localPt1), localPoint2(localPt2) {
assert(contactNormal.lengthSquare() > decimal(0.8));
assert(penDepth > decimal(0.0));
}
/// Destructor
~ContactPointInfo() = default;
};
}
#endif

View File

@ -0,0 +1,204 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_HALF_EDGE_STRUCTURE_MESH_H
#define REACTPHYSICS3D_HALF_EDGE_STRUCTURE_MESH_H
// Libraries
#include <reactphysics3d/mathematics/mathematics.h>
namespace reactphysics3d {
// Class HalfEdgeStructure
/**
* This class describes a polyhedron mesh made of faces and vertices.
* The faces do not have to be triangle. Note that the half-edge structure
* is only valid if the mesh is closed (each edge has two adjacent faces).
*/
class HalfEdgeStructure {
public:
using VerticesPair = Pair<uint, uint>;
/// Edge
struct Edge {
uint vertexIndex; // Index of the vertex at the beginning of the edge
uint twinEdgeIndex; // Index of the twin edge
uint faceIndex; // Adjacent face index of the edge
uint nextEdgeIndex; // Index of the next edge
};
/// Face
struct Face {
uint edgeIndex; // Index of an half-edge of the face
List<uint> faceVertices; // Index of the vertices of the face
/// Constructor
Face(MemoryAllocator& allocator) : faceVertices(allocator) {}
/// Constructor
Face(List<uint> vertices) : faceVertices(vertices) {}
};
/// Vertex
struct Vertex {
uint vertexPointIndex; // Index of the vertex point in the origin vertex array
uint edgeIndex; // Index of one edge emanting from this vertex
/// Constructor
Vertex(uint vertexCoordsIndex) : vertexPointIndex(vertexCoordsIndex) { }
};
private:
/// Reference to a memory allocator
MemoryAllocator& mAllocator;
/// All the faces
List<Face> mFaces;
/// All the vertices
List<Vertex> mVertices;
/// All the half-edges
List<Edge> mEdges;
public:
/// Constructor
HalfEdgeStructure(MemoryAllocator& allocator, uint facesCapacity, uint verticesCapacity,
uint edgesCapacity) :mAllocator(allocator), mFaces(allocator, facesCapacity),
mVertices(allocator, verticesCapacity), mEdges(allocator, edgesCapacity) {}
/// Destructor
~HalfEdgeStructure() = default;
/// Initialize the structure (when all vertices and faces have been added)
void init();
/// Add a vertex
uint addVertex(uint vertexPointIndex);
/// Add a face
void addFace(List<uint> faceVertices);
/// Return the number of faces
uint getNbFaces() const;
/// Return the number of half-edges
uint getNbHalfEdges() const;
/// Return the number of vertices
uint getNbVertices() const;
/// Return a given face
const Face& getFace(uint index) const;
/// Return a given edge
const Edge& getHalfEdge(uint index) const;
/// Return a given vertex
const Vertex& getVertex(uint index) const;
};
// Add a vertex
/**
* @param vertexPointIndex Index of the vertex in the vertex data array
*/
inline uint HalfEdgeStructure::addVertex(uint vertexPointIndex) {
Vertex vertex(vertexPointIndex);
mVertices.add(vertex);
return mVertices.size() - 1;
}
// Add a face
/**
* @param faceVertices List of the vertices in a face (ordered in CCW order as seen from outside
* the polyhedron
*/
inline void HalfEdgeStructure::addFace(List<uint> faceVertices) {
// Create a new face
Face face(faceVertices);
mFaces.add(face);
}
// Return the number of faces
/**
* @return The number of faces in the polyhedron
*/
inline uint HalfEdgeStructure::getNbFaces() const {
return static_cast<uint>(mFaces.size());
}
// Return the number of edges
/**
* @return The number of edges in the polyhedron
*/
inline uint HalfEdgeStructure::getNbHalfEdges() const {
return static_cast<uint>(mEdges.size());
}
// Return the number of vertices
/**
* @return The number of vertices in the polyhedron
*/
inline uint HalfEdgeStructure::getNbVertices() const {
return static_cast<uint>(mVertices.size());
}
// Return a given face
/**
* @return A given face of the polyhedron
*/
inline const HalfEdgeStructure::Face& HalfEdgeStructure::getFace(uint index) const {
assert(index < mFaces.size());
return mFaces[index];
}
// Return a given edge
/**
* @return A given edge of the polyhedron
*/
inline const HalfEdgeStructure::Edge& HalfEdgeStructure::getHalfEdge(uint index) const {
assert(index < mEdges.size());
return mEdges[index];
}
// Return a given vertex
/**
* @return A given vertex of the polyhedron
*/
inline const HalfEdgeStructure::Vertex& HalfEdgeStructure::getVertex(uint index) const {
assert(index < mVertices.size());
return mVertices[index];
}
}
#endif

View File

@ -0,0 +1,213 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_OVERLAP_CALLBACK_H
#define REACTPHYSICS3D_OVERLAP_CALLBACK_H
// Libraries
#include <reactphysics3d/containers/List.h>
#include <reactphysics3d/collision/ContactPair.h>
/// ReactPhysics3D namespace
namespace reactphysics3d {
// Declarations
class CollisionBody;
class PhysicsWorld;
class Collider;
struct Entity;
// Class OverlapCallback
/**
* This class can be used to register a callback for collision overlap queries between bodies.
* You should implement your own class inherited from this one and implement the onOverlap() method.
*/
class OverlapCallback {
public:
// Class OverlapPair
/**
* This class represents the contact between two colliders of the physics world.
*/
class OverlapPair {
public:
/// Enumeration EventType that describes the type of overlapping event
enum class EventType {
/// This overlap is a new overlap between the two
/// colliders (the colliders where not overlapping in the previous frame)
OverlapStart,
/// The two colliders were already overlapping in the previous frame and this is a new or updated overlap
OverlapStay,
/// The two colliders were overlapping in the previous frame and are not overlapping anymore
OverlapExit
};
private:
// -------------------- Attributes -------------------- //
/// Contact pair
ContactPair& mContactPair;
/// Reference to the physics world
PhysicsWorld& mWorld;
/// True if the pair were overlapping in the previous frame but not in the current one
bool mIsLostOverlapPair;
// -------------------- Methods -------------------- //
/// Constructor
OverlapPair(ContactPair& contactPair, reactphysics3d::PhysicsWorld& world, bool isLostOverlappingPair);
public:
// -------------------- Methods -------------------- //
/// Copy constructor
OverlapPair(const OverlapPair& contactPair) = default;
/// Assignment operator
OverlapPair& operator=(const OverlapPair& contactPair) = default;
/// Destructor
~OverlapPair() = default;
/// Return a pointer to the first collider in contact
Collider* getCollider1() const;
/// Return a pointer to the second collider in contact
Collider* getCollider2() const;
/// Return a pointer to the first body in contact
CollisionBody* getBody1() const;
/// Return a pointer to the second body in contact
CollisionBody* getBody2() const;
/// Return the corresponding type of event for this overlapping pair
EventType getEventType() const;
// -------------------- Friendship -------------------- //
friend class OverlapCallback;
};
// Class CallbackData
/**
* This class contains data about overlap between bodies
*/
class CallbackData {
private:
// -------------------- Attributes -------------------- //
/// Reference to the list of contact pairs (contains contacts and triggers events)
List<ContactPair>& mContactPairs;
/// Reference to the list of lost contact pairs (contains contacts and triggers events)
List<ContactPair>& mLostContactPairs;
/// List of indices of the mContactPairs list that are overlap/triggers events (not contact events)
List<uint> mContactPairsIndices;
/// List of indices of the mLostContactPairs list that are overlap/triggers events (not contact events)
List<uint> mLostContactPairsIndices;
/// Reference to the physics world
PhysicsWorld& mWorld;
// -------------------- Methods -------------------- //
/// Constructor
CallbackData(List<ContactPair>& contactPairs, List<ContactPair>& lostContactPairs, bool onlyReportTriggers, PhysicsWorld& world);
/// Deleted copy constructor
CallbackData(const CallbackData& callbackData) = delete;
/// Deleted assignment operator
CallbackData& operator=(const CallbackData& callbackData) = delete;
/// Destructor
~CallbackData() = default;
public:
// -------------------- Methods -------------------- //
/// Return the number of overlapping pairs of bodies
uint getNbOverlappingPairs() const;
/// Return a given overlapping pair of bodies
OverlapPair getOverlappingPair(uint index) const;
// -------------------- Friendship -------------------- //
friend class CollisionDetectionSystem;
};
/// Destructor
virtual ~OverlapCallback() {
}
/// This method will be called to report bodies that overlap
virtual void onOverlap(CallbackData& callbackData)=0;
};
// Return the number of overlapping pairs of bodies
inline uint OverlapCallback::CallbackData::getNbOverlappingPairs() const {
return mContactPairsIndices.size() + mLostContactPairsIndices.size();
}
// Return a given overlapping pair of bodies
/// Note that the returned OverlapPair object is only valid during the call of the CollisionCallback::onOverlap()
/// method. Therefore, you need to get contact data from it and make a copy. Do not make a copy of the OverlapPair
/// object itself because it won't be valid after the CollisionCallback::onOverlap() call.
inline OverlapCallback::OverlapPair OverlapCallback::CallbackData::getOverlappingPair(uint index) const {
assert(index < getNbOverlappingPairs());
if (index < mContactPairsIndices.size()) {
// Return a contact pair
return OverlapCallback::OverlapPair((mContactPairs)[mContactPairsIndices[index]], mWorld, false);
}
else {
// Return a lost contact pair
return OverlapCallback::OverlapPair(mLostContactPairs[mLostContactPairsIndices[index - mContactPairsIndices.size()]], mWorld, true);
}
}
}
#endif

View File

@ -0,0 +1,216 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_POLYGON_VERTEX_ARRAY_H
#define REACTPHYSICS3D_POLYGON_VERTEX_ARRAY_H
// Libraries
#include <reactphysics3d/configuration.h>
#include <cassert>
namespace reactphysics3d {
// Class PolygonVertexArray
/**
* This class is used to describe the vertices and faces of a polyhedron mesh.
* A PolygonVertexArray represents an array of vertices and polygon faces
* of a polyhedron mesh. When you create a PolygonVertexArray, no data is copied
* into the array. It only stores pointer to the data. The purpose is to allow
* the user to share vertices data between the physics engine and the rendering
* part. Therefore, make sure that the data pointed by a PolygonVertexArray
* remains valid during the PolygonVertexArray life.
*/
class PolygonVertexArray {
public:
/// Data type for the vertices in the array
enum class VertexDataType {VERTEX_FLOAT_TYPE, VERTEX_DOUBLE_TYPE};
/// Data type for the indices in the array
enum class IndexDataType {INDEX_INTEGER_TYPE, INDEX_SHORT_TYPE};
/// Represent a polygon face of the polyhedron
struct PolygonFace {
/// Number of vertices in the polygon face
uint nbVertices;
/// Index of the first vertex of the polygon face
/// inside the array with all vertex indices
uint indexBase;
};
protected:
/// Number of vertices in the array
uint mNbVertices;
/// Pointer to the first vertex value in the array
const unsigned char* mVerticesStart;
/// Stride (number of bytes) between the beginning of two vertices
/// values in the array
int mVerticesStride;
/// Pointer to the first vertex index of the array
const unsigned char* mIndicesStart;
/// Stride (number of bytes) between the beginning of two indices in
/// the array
int mIndicesStride;
/// Number of polygon faces in the array
uint mNbFaces;
/// Pointer to the first polygon face in the polyhedron
PolygonFace* mPolygonFacesStart;
/// Data type of the vertices in the array
VertexDataType mVertexDataType;
/// Data type of the indices in the array
IndexDataType mIndexDataType;
public:
/// Constructor
PolygonVertexArray(uint nbVertices, const void* verticesStart, int verticesStride,
const void* indexesStart, int indexesStride,
uint nbFaces, PolygonFace* facesStart,
VertexDataType vertexDataType, IndexDataType indexDataType);
/// Destructor
~PolygonVertexArray() = default;
/// Return the vertex data type
VertexDataType getVertexDataType() const;
/// Return the index data type
IndexDataType getIndexDataType() const;
/// Return the number of vertices
uint getNbVertices() const;
/// Return the number of faces
uint getNbFaces() const;
/// Return the vertices stride (number of bytes)
int getVerticesStride() const;
/// Return the indices stride (number of bytes)
int getIndicesStride() const;
/// Return the vertex index of a given vertex in a face
uint getVertexIndexInFace(uint faceIndex, uint noVertexInFace) const;
/// Return a polygon face of the polyhedron
PolygonFace* getPolygonFace(uint faceIndex) const;
/// Return the pointer to the start of the vertices array
const unsigned char* getVerticesStart() const;
/// Return the pointer to the start of the indices array
const unsigned char* getIndicesStart() const;
};
// Return the vertex data type
/**
* @return The data type of the vertices in the array
*/
inline PolygonVertexArray::VertexDataType PolygonVertexArray::getVertexDataType() const {
return mVertexDataType;
}
// Return the index data type
/**
* @return The data type of the indices in the array
*/
inline PolygonVertexArray::IndexDataType PolygonVertexArray::getIndexDataType() const {
return mIndexDataType;
}
// Return the number of vertices
/**
* @return The number of vertices in the array
*/
inline uint PolygonVertexArray::getNbVertices() const {
return mNbVertices;
}
// Return the number of faces
/**
* @return The number of faces in the array
*/
inline uint PolygonVertexArray::getNbFaces() const {
return mNbFaces;
}
// Return the vertices stride (number of bytes)
/**
* @return The number of bytes between two vertices
*/
inline int PolygonVertexArray::getVerticesStride() const {
return mVerticesStride;
}
// Return the indices stride (number of bytes)
/**
* @return The number of bytes between two consecutive face indices
*/
inline int PolygonVertexArray::getIndicesStride() const {
return mIndicesStride;
}
// Return a polygon face of the polyhedron
/**
* @param faceIndex Index of a given face
* @return A polygon face
*/
inline PolygonVertexArray::PolygonFace* PolygonVertexArray::getPolygonFace(uint faceIndex) const {
assert(faceIndex < mNbFaces);
return &mPolygonFacesStart[faceIndex];
}
// Return the pointer to the start of the vertices array
/**
* @return A pointer to the start of the vertex array of the polyhedron
*/
inline const unsigned char* PolygonVertexArray::getVerticesStart() const {
return mVerticesStart;
}
// Return the pointer to the start of the indices array
/**
* @return A pointer to the start of the face indices array of the polyhedron
*/
inline const unsigned char* PolygonVertexArray::getIndicesStart() const {
return mIndicesStart;
}
}
#endif

View File

@ -0,0 +1,161 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_POLYHEDRON_MESH_H
#define REACTPHYSICS3D_POLYHEDRON_MESH_H
// Libraries
#include <reactphysics3d/mathematics/mathematics.h>
#include "HalfEdgeStructure.h"
namespace reactphysics3d {
// Declarations
class DefaultAllocator;
class PolygonVertexArray;
// Class PolyhedronMesh
/**
* This class describes a polyhedron mesh made of faces and vertices.
* The faces do not have to be triangles.
*/
class PolyhedronMesh {
private:
// -------------------- Attributes -------------------- //
/// Reference to the memory allocator
MemoryAllocator& mMemoryAllocator;
/// Pointer the the polygon vertex array with vertices and faces
/// of the mesh
PolygonVertexArray* mPolygonVertexArray;
/// Half-edge structure of the mesh
HalfEdgeStructure mHalfEdgeStructure;
/// Array with the face normals
Vector3* mFacesNormals;
/// Centroid of the polyhedron
Vector3 mCentroid;
// -------------------- Methods -------------------- //
/// Constructor
PolyhedronMesh(PolygonVertexArray* polygonVertexArray, MemoryAllocator& allocator);
/// Create the half-edge structure of the mesh
void createHalfEdgeStructure();
/// Compute the faces normals
void computeFacesNormals();
/// Compute the centroid of the polyhedron
void computeCentroid() ;
/// Compute and return the area of a face
decimal getFaceArea(uint faceIndex) const;
public:
// -------------------- Methods -------------------- //
/// Destructor
~PolyhedronMesh();
/// Return the number of vertices
uint getNbVertices() const;
/// Return a vertex
Vector3 getVertex(uint index) const;
/// Return the number of faces
uint getNbFaces() const;
/// Return a face normal
Vector3 getFaceNormal(uint faceIndex) const;
/// Return the half-edge structure of the mesh
const HalfEdgeStructure& getHalfEdgeStructure() const;
/// Return the centroid of the polyhedron
Vector3 getCentroid() const;
/// Compute and return the volume of the polyhedron
decimal getVolume() const;
// ---------- Friendship ---------- //
friend class PhysicsCommon;
};
// Return the number of vertices
/**
* @return The number of vertices in the mesh
*/
inline uint PolyhedronMesh::getNbVertices() const {
return mHalfEdgeStructure.getNbVertices();
}
// Return the number of faces
/**
* @return The number of faces in the mesh
*/
inline uint PolyhedronMesh::getNbFaces() const {
return mHalfEdgeStructure.getNbFaces();
}
// Return a face normal
/**
* @param faceIndex The index of a given face of the mesh
* @return The normal vector of a given face of the mesh
*/
inline Vector3 PolyhedronMesh::getFaceNormal(uint faceIndex) const {
assert(faceIndex < mHalfEdgeStructure.getNbFaces());
return mFacesNormals[faceIndex];
}
// Return the half-edge structure of the mesh
/**
* @return The Half-Edge structure of the mesh
*/
inline const HalfEdgeStructure& PolyhedronMesh::getHalfEdgeStructure() const {
return mHalfEdgeStructure;
}
// Return the centroid of the polyhedron
/**
* @return The centroid of the mesh
*/
inline Vector3 PolyhedronMesh::getCentroid() const {
return mCentroid;
}
}
#endif

View File

@ -0,0 +1,148 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_RAYCAST_INFO_H
#define REACTPHYSICS3D_RAYCAST_INFO_H
// Libraries
#include <reactphysics3d/mathematics/Vector3.h>
/// ReactPhysics3D namespace
namespace reactphysics3d {
// Declarations
class CollisionBody;
class Collider;
class CollisionShape;
struct Ray;
// Structure RaycastInfo
/**
* This structure contains the information about a raycast hit.
*/
struct RaycastInfo {
private:
public:
// -------------------- Attributes -------------------- //
/// Hit point in world-space coordinates
Vector3 worldPoint;
/// Surface normal at hit point in world-space coordinates
Vector3 worldNormal;
/// Fraction distance of the hit point between point1 and point2 of the ray
/// The hit point "p" is such that p = point1 + hitFraction * (point2 - point1)
decimal hitFraction;
/// Mesh subpart index that has been hit (only used for triangles mesh and -1 otherwise)
int meshSubpart;
/// Hit triangle index (only used for triangles mesh and -1 otherwise)
int triangleIndex;
/// Pointer to the hit collision body
CollisionBody* body;
/// Pointer to the hit collider
Collider* collider;
// -------------------- Methods -------------------- //
/// Constructor
RaycastInfo() : meshSubpart(-1), triangleIndex(-1), body(nullptr), collider(nullptr) {
}
/// Destructor
~RaycastInfo() = default;
/// Deleted copy constructor
RaycastInfo(const RaycastInfo& raycastInfo) = delete;
/// Deleted assignment operator
RaycastInfo& operator=(const RaycastInfo& raycastInfo) = delete;
};
// Class RaycastCallback
/**
* This class can be used to register a callback for ray casting queries.
* You should implement your own class inherited from this one and implement
* the notifyRaycastHit() method. This method will be called for each collider
* that is hit by the ray.
*/
class RaycastCallback {
public:
// -------------------- Methods -------------------- //
/// Destructor
virtual ~RaycastCallback() {
}
/// This method will be called for each collider that is hit by the
/// ray. You cannot make any assumptions about the order of the
/// calls. You should use the return value to control the continuation
/// of the ray. The returned value is the next maxFraction value to use.
/// If you return a fraction of 0.0, it means that the raycast should
/// terminate. If you return a fraction of 1.0, it indicates that the
/// ray is not clipped and the ray cast should continue as if no hit
/// occurred. If you return the fraction in the parameter (hitFraction
/// value in the RaycastInfo object), the current ray will be clipped
/// to this fraction in the next queries. If you return -1.0, it will
/// ignore this collider and continue the ray cast.
/**
* @param raycastInfo Information about the raycast hit
* @return Value that controls the continuation of the ray after a hit
*/
virtual decimal notifyRaycastHit(const RaycastInfo& raycastInfo)=0;
};
/// Structure RaycastTest
struct RaycastTest {
public:
/// User callback class
RaycastCallback* userCallback;
/// Constructor
RaycastTest(RaycastCallback* callback) {
userCallback = callback;
}
/// Ray cast test against a collider
decimal raycastAgainstShape(Collider* shape, const Ray& ray);
};
}
#endif

View File

@ -0,0 +1,106 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_TRIANGLE_MESH_H
#define REACTPHYSICS3D_TRIANGLE_MESH_H
// Libraries
#include <cassert>
#include <reactphysics3d/containers/List.h>
#include <reactphysics3d/memory/MemoryAllocator.h>
namespace reactphysics3d {
// Declarations
class TriangleVertexArray;
// Class TriangleMesh
/**
* This class represents a mesh made of triangles. A TriangleMesh contains
* one or several parts. Each part is a set of triangles represented in a
* TriangleVertexArray object describing all the triangles vertices of the part.
* A TriangleMesh object can be used to create a ConcaveMeshShape from a triangle
* mesh for instance.
*/
class TriangleMesh {
protected:
/// All the triangle arrays of the mesh (one triangle array per part)
List<TriangleVertexArray*> mTriangleArrays;
/// Constructor
TriangleMesh(reactphysics3d::MemoryAllocator& allocator);
public:
/// Destructor
~TriangleMesh();
/// Add a subpart of the mesh
void addSubpart(TriangleVertexArray* triangleVertexArray);
/// Return a pointer to a given subpart (triangle vertex array) of the mesh
TriangleVertexArray* getSubpart(uint indexSubpart) const;
/// Return the number of subparts of the mesh
uint getNbSubparts() const;
// ---------- Friendship ---------- //
friend class PhysicsCommon;
};
// Add a subpart of the mesh
/**
* @param triangleVertexArray Pointer to the TriangleVertexArray to add into the mesh
*/
inline void TriangleMesh::addSubpart(TriangleVertexArray* triangleVertexArray) {
mTriangleArrays.add(triangleVertexArray );
}
// Return a pointer to a given subpart (triangle vertex array) of the mesh
/**
* @param indexSubpart The index of the sub-part of the mesh
* @return A pointer to the triangle vertex array of a given sub-part of the mesh
*/
inline TriangleVertexArray* TriangleMesh::getSubpart(uint indexSubpart) const {
assert(indexSubpart < mTriangleArrays.size());
return mTriangleArrays[indexSubpart];
}
// Return the number of sub-parts of the mesh
/**
* @return The number of sub-parts of the mesh
*/
inline uint TriangleMesh::getNbSubparts() const {
return mTriangleArrays.size();
}
}
#endif

View File

@ -0,0 +1,292 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_TRIANGLE_VERTEX_ARRAY_H
#define REACTPHYSICS3D_TRIANGLE_VERTEX_ARRAY_H
// Libraries
#include <reactphysics3d/configuration.h>
namespace reactphysics3d {
// Declarations
struct Vector3;
// Class TriangleVertexArray
/**
* This class is used to describe the vertices and faces of a triangular mesh.
* A TriangleVertexArray represents a continuous array of vertices and indexes
* of a triangular mesh. When you create a TriangleVertexArray, no data is copied
* into the array. It only stores pointer to the data. The purpose is to allow
* the user to share vertices data between the physics engine and the rendering
* part. Therefore, make sure that the data pointed by a TriangleVertexArray
* remains valid during the TriangleVertexArray life.
*/
class TriangleVertexArray {
public:
/// Data type for the vertices in the array
enum class VertexDataType {
VERTEX_SHORT_TYPE,
VERTEX_FLOAT_TYPE,
VERTEX_DOUBLE_TYPE,
#if __STDCPP_FLOAT16_T__
VERTEX_FLOAT16_TYPE,
#endif
#if __STDCPP_BFLOAT16_T__
VERTEX_BFLOAT16_TYPE,
#endif
};
/// Data type for the vertex normals in the array
enum class NormalDataType {
NORMAL_SHORT_TYPE,
NORMAL_FLOAT_TYPE,
NORMAL_DOUBLE_TYPE,
#if __STDCPP_FLOAT16_T__
NORMAL_FLOAT16_TYPE,
#endif
#if __STDCPP_BFLOAT16_T__
NORMAL_BFLOAT16_TYPE,
#endif
};
/// Data type for the indices in the array
enum class IndexDataType {INDEX_INTEGER_TYPE, INDEX_SHORT_TYPE};
protected:
// -------------------- Attributes -------------------- //
/// Number of vertices in the array
uint mNbVertices;
/// Pointer to the first vertex value in the array
const uchar* mVerticesStart;
/// Stride (number of bytes) between the beginning of two vertices
/// values in the array
uint mVerticesStride;
/// Pointer to the first vertex normal value in the array
const uchar* mVerticesNormalsStart;
/// Stride (number of bytes) between the beginning of two vertex normals
/// values in the array
uint mVerticesNormalsStride;
/// Number of triangles in the array
uint mNbTriangles;
/// Pointer to the first vertex index of the array
const uchar* mIndicesStart;
/// Stride (number of bytes) between the beginning of the three indices of two triangles
uint mIndicesStride;
/// Data type of the vertices in the array
VertexDataType mVertexDataType;
/// Data type of the vertex normals in the array
NormalDataType mVertexNormaldDataType;
/// Data type of the indices in the array
IndexDataType mIndexDataType;
/// True if the vertices normals are provided by the user
bool mAreVerticesNormalsProvidedByUser;
// -------------------- Methods -------------------- //
/// Compute the vertices normals when they are not provided by the user
void computeVerticesNormals();
public:
// -------------------- Methods -------------------- //
/// Constructor without vertices normals
TriangleVertexArray(uint nbVertices, const void* verticesStart, uint verticesStride,
uint nbTriangles, const void* indexesStart, uint indexesStride,
VertexDataType vertexDataType, IndexDataType indexDataType);
/// Constructor with vertices normals
TriangleVertexArray(uint nbVertices, const void* verticesStart, uint verticesStride,
const void* verticesNormalsStart, uint uverticesNormalsStride,
uint nbTriangles, const void* indexesStart, uint indexesStride,
VertexDataType vertexDataType, NormalDataType normalDataType,
IndexDataType indexDataType);
/// Destructor
~TriangleVertexArray();
/// Deleted assignment operator
TriangleVertexArray& operator=(const TriangleVertexArray& triangleVertexArray) = delete;
/// Deleted copy-constructor
TriangleVertexArray(const TriangleVertexArray& triangleVertexArray) = delete;
/// Return the vertex data type
VertexDataType getVertexDataType() const;
/// Return the vertex normal data type
NormalDataType getVertexNormalDataType() const;
/// Return the index data type
IndexDataType getIndexDataType() const;
/// Return the number of vertices
uint getNbVertices() const;
/// Return the number of triangles
uint getNbTriangles() const;
/// Return the vertices stride (number of bytes)
uint getVerticesStride() const;
/// Return the vertex normals stride (number of bytes)
uint getVerticesNormalsStride() const;
/// Return the indices stride (number of bytes)
uint getIndicesStride() const;
/// Return the pointer to the start of the vertices array
const void* getVerticesStart() const;
/// Return the pointer to the start of the vertex normals array
const void* getVerticesNormalsStart() const;
/// Return the pointer to the start of the indices array
const void* getIndicesStart() const;
/// Return the vertices coordinates of a triangle
void getTriangleVertices(uint triangleIndex, Vector3* outTriangleVertices) const;
/// Return the three vertices normals of a triangle
void getTriangleVerticesNormals(uint triangleIndex, Vector3* outTriangleVerticesNormals) const;
/// Return the indices of the three vertices of a given triangle in the array
void getTriangleVerticesIndices(uint triangleIndex, uint* outVerticesIndices) const;
/// Return a vertex of the array
void getVertex(uint vertexIndex, Vector3* outVertex);
/// Return a vertex normal of the array
void getNormal(uint vertexIndex, Vector3* outNormal);
};
// Return the vertex data type
/**
* @return The data type of the vertices in the array
*/
inline TriangleVertexArray::VertexDataType TriangleVertexArray::getVertexDataType() const {
return mVertexDataType;
}
// Return the vertex normal data type
/**
* @return The data type of the normals in the array
*/
inline TriangleVertexArray::NormalDataType TriangleVertexArray::getVertexNormalDataType() const {
return mVertexNormaldDataType;
}
// Return the index data type
/**
* @return The data type of the face indices in the array
*/
inline TriangleVertexArray::IndexDataType TriangleVertexArray::getIndexDataType() const {
return mIndexDataType;
}
// Return the number of vertices
/**
* @return The number of vertices in the array
*/
inline uint TriangleVertexArray::getNbVertices() const {
return mNbVertices;
}
// Return the number of triangles
/**
* @return The number of triangles in the array
*/
inline uint TriangleVertexArray::getNbTriangles() const {
return mNbTriangles;
}
// Return the vertices stride (number of bytes)
/**
* @return The number of bytes separating two consecutive vertices in the array
*/
inline uint TriangleVertexArray::getVerticesStride() const {
return mVerticesStride;
}
// Return the vertex normals stride (number of bytes)
/**
* @return The number of bytes separating two consecutive normals in the array
*/
inline uint TriangleVertexArray::getVerticesNormalsStride() const {
return mVerticesNormalsStride;
}
// Return the indices stride (number of bytes)
/**
* @return The number of bytes separating two consecutive face indices in the array
*/
inline uint TriangleVertexArray::getIndicesStride() const {
return mIndicesStride;
}
// Return the pointer to the start of the vertices array
/**
* @return A pointer to the start of the vertices data in the array
*/
inline const void* TriangleVertexArray::getVerticesStart() const {
return mVerticesStart;
}
// Return the pointer to the start of the vertex normals array
/**
* @return A pointer to the start of the normals data in the array
*/
inline const void* TriangleVertexArray::getVerticesNormalsStart() const {
return mVerticesNormalsStart;
}
// Return the pointer to the start of the indices array
/**
* @return A pointer to the start of the face indices data in the array
*/
inline const void* TriangleVertexArray::getIndicesStart() const {
return mIndicesStart;
}
}
#endif

View File

@ -0,0 +1,330 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_DYNAMIC_AABB_TREE_H
#define REACTPHYSICS3D_DYNAMIC_AABB_TREE_H
// Libraries
#include <reactphysics3d/configuration.h>
#include <reactphysics3d/collision/shapes/AABB.h>
#include <reactphysics3d/containers/Set.h>
/// Namespace ReactPhysics3D
namespace reactphysics3d {
// Declarations
class BroadPhaseSystem;
class BroadPhaseRaycastTestCallback;
class DynamicAABBTreeOverlapCallback;
class CollisionBody;
struct RaycastTest;
class AABB;
class Profiler;
class MemoryAllocator;
// Structure TreeNode
/**
* This structure represents a node of the dynamic AABB tree.
*/
struct TreeNode {
// -------------------- Constants -------------------- //
/// Null tree node constant
const static int32 NULL_TREE_NODE;
// -------------------- Attributes -------------------- //
// A node is either in the tree (has a parent) or in the free nodes list
// (has a next node)
union {
/// Parent node ID
int32 parentID;
/// Next allocated node ID
int32 nextNodeID;
};
// A node is either a leaf (has data) or is an internal node (has children)
union {
/// Left and right child of the node (children[0] = left child)
int32 children[2];
/// Two pieces of data stored at that node (in case the node is a leaf)
union {
int32 dataInt[2];
void* dataPointer;
};
};
/// Height of the node in the tree
int16 height;
/// Fat axis aligned bounding box (AABB) corresponding to the node
AABB aabb;
// -------------------- Methods -------------------- //
/// Return true if the node is a leaf of the tree
bool isLeaf() const;
};
// Class DynamicAABBTreeOverlapCallback
/**
* Overlapping callback method that has to be used as parameter of the
* reportAllShapesOverlappingWithNode() method.
*/
class DynamicAABBTreeOverlapCallback {
public :
// Called when a overlapping node has been found during the call to
// DynamicAABBTree:reportAllShapesOverlappingWithAABB()
virtual void notifyOverlappingNode(int nodeId)=0;
// Destructor
virtual ~DynamicAABBTreeOverlapCallback() = default;
};
// Class DynamicAABBTreeRaycastCallback
/**
* Raycast callback in the Dynamic AABB Tree called when the AABB of a leaf
* node is hit by the ray.
*/
class DynamicAABBTreeRaycastCallback {
public:
// Called when the AABB of a leaf node is hit by a ray
virtual decimal raycastBroadPhaseShape(int32 nodeId, const Ray& ray)=0;
virtual ~DynamicAABBTreeRaycastCallback() = default;
};
// Class DynamicAABBTree
/**
* This class implements a dynamic AABB tree that is used for broad-phase
* collision detection. The following implementation is
* based on the one from Erin Catto in Box2D as described in the book
* "Introduction to Game Physics with Box2D" by Ian Parberry.
*/
class DynamicAABBTree {
private:
// -------------------- Attributes -------------------- //
/// Memory allocator
MemoryAllocator& mAllocator;
/// Pointer to the memory location of the nodes of the tree
TreeNode* mNodes;
/// ID of the root node of the tree
int32 mRootNodeID;
/// ID of the first node of the list of free (allocated) nodes in the tree that we can use
int32 mFreeNodeID;
/// Number of allocated nodes in the tree
int32 mNbAllocatedNodes;
/// Number of nodes in the tree
int32 mNbNodes;
/// The fat AABB is the initial AABB inflated by a given percentage of its size.
decimal mFatAABBInflatePercentage;
#ifdef IS_RP3D_PROFILING_ENABLED
/// Pointer to the profiler
Profiler* mProfiler;
#endif
// -------------------- Methods -------------------- //
/// Allocate and return a node to use in the tree
int32 allocateNode();
/// Release a node
void releaseNode(int32 nodeID);
/// Insert a leaf node in the tree
void insertLeafNode(int32 nodeID);
/// Remove a leaf node from the tree
void removeLeafNode(int32 nodeID);
/// Balance the sub-tree of a given node using left or right rotations.
int32 balanceSubTreeAtNode(int32 nodeID);
/// Compute the height of a given node in the tree
int computeHeight(int32 nodeID);
/// Internally add an object into the tree
int32 addObjectInternal(const AABB& aabb);
/// Initialize the tree
void init();
#ifndef NDEBUG
/// Check if the tree structure is valid (for debugging purpose)
void check() const;
/// Check if the node structure is valid (for debugging purpose)
void checkNode(int32 nodeID) const;
#endif
public:
// -------------------- Methods -------------------- //
/// Constructor
DynamicAABBTree(MemoryAllocator& allocator, decimal fatAABBInflatePercentage = decimal(0.0));
/// Destructor
~DynamicAABBTree();
/// Add an object into the tree (where node data are two integers)
int32 addObject(const AABB& aabb, int32 data1, int32 data2);
/// Add an object into the tree (where node data is a pointer)
int32 addObject(const AABB& aabb, void* data);
/// Remove an object from the tree
void removeObject(int32 nodeID);
/// Update the dynamic tree after an object has moved.
bool updateObject(int32 nodeID, const AABB& newAABB, bool forceReinsert = false);
/// Return the fat AABB corresponding to a given node ID
const AABB& getFatAABB(int32 nodeID) const;
/// Return the pointer to the data array of a given leaf node of the tree
int32* getNodeDataInt(int32 nodeID) const;
/// Return the data pointer of a given leaf node of the tree
void* getNodeDataPointer(int32 nodeID) const;
/// Report all shapes overlapping with all the shapes in the map in parameter
void reportAllShapesOverlappingWithShapes(const List<int32>& nodesToTest, size_t startIndex,
size_t endIndex, List<Pair<int32, int32>>& outOverlappingNodes) const;
/// Report all shapes overlapping with the AABB given in parameter.
void reportAllShapesOverlappingWithAABB(const AABB& aabb, List<int32>& overlappingNodes) const;
/// Ray casting method
void raycast(const Ray& ray, DynamicAABBTreeRaycastCallback& callback) const;
/// Compute the height of the tree
int computeHeight();
/// Return the root AABB of the tree
AABB getRootAABB() const;
/// Clear all the nodes and reset the tree
void reset();
#ifdef IS_RP3D_PROFILING_ENABLED
/// Set the profiler
void setProfiler(Profiler* profiler);
#endif
};
// Return true if the node is a leaf of the tree
inline bool TreeNode::isLeaf() const {
return (height == 0);
}
// Return the fat AABB corresponding to a given node ID
inline const AABB& DynamicAABBTree::getFatAABB(int32 nodeID) const {
assert(nodeID >= 0 && nodeID < mNbAllocatedNodes);
return mNodes[nodeID].aabb;
}
// Return the pointer to the data array of a given leaf node of the tree
inline int32* DynamicAABBTree::getNodeDataInt(int32 nodeID) const {
assert(nodeID >= 0 && nodeID < mNbAllocatedNodes);
assert(mNodes[nodeID].isLeaf());
return mNodes[nodeID].dataInt;
}
// Return the pointer to the data pointer of a given leaf node of the tree
inline void* DynamicAABBTree::getNodeDataPointer(int32 nodeID) const {
assert(nodeID >= 0 && nodeID < mNbAllocatedNodes);
assert(mNodes[nodeID].isLeaf());
return mNodes[nodeID].dataPointer;
}
// Return the root AABB of the tree
inline AABB DynamicAABBTree::getRootAABB() const {
return getFatAABB(mRootNodeID);
}
// Add an object into the tree. This method creates a new leaf node in the tree and
// returns the ID of the corresponding node.
inline int32 DynamicAABBTree::addObject(const AABB& aabb, int32 data1, int32 data2) {
int32 nodeId = addObjectInternal(aabb);
mNodes[nodeId].dataInt[0] = data1;
mNodes[nodeId].dataInt[1] = data2;
return nodeId;
}
// Add an object into the tree. This method creates a new leaf node in the tree and
// returns the ID of the corresponding node.
inline int32 DynamicAABBTree::addObject(const AABB& aabb, void* data) {
int32 nodeId = addObjectInternal(aabb);
mNodes[nodeId].dataPointer = data;
return nodeId;
}
#ifdef IS_RP3D_PROFILING_ENABLED
// Set the profiler
inline void DynamicAABBTree::setProfiler(Profiler* profiler) {
mProfiler = profiler;
}
#endif
}
#endif

View File

@ -0,0 +1,76 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_CAPSULE_VS_CAPSULE_ALGORITHM_H
#define REACTPHYSICS3D_CAPSULE_VS_CAPSULE_ALGORITHM_H
// Libraries
#include <reactphysics3d/collision/narrowphase/NarrowPhaseAlgorithm.h>
#include <reactphysics3d/configuration.h>
/// Namespace ReactPhysics3D
namespace reactphysics3d {
// Declarations
struct CapsuleVsCapsuleNarrowPhaseInfoBatch;
class ContactPoint;
// Class CapsuleVsCapsuleAlgorithm
/**
* This class is used to compute the narrow-phase collision detection
* between two capsules collision shapes. We do not use the GJK or SAT
* algorithm here. We directly compute the contact points and contact normal.
* This is based on the "Robust Contact Creation for Physics Simulation"
* presentation by Dirk Gregorius.
*/
class CapsuleVsCapsuleAlgorithm : public NarrowPhaseAlgorithm {
protected :
public :
// -------------------- Methods -------------------- //
/// Constructor
CapsuleVsCapsuleAlgorithm() = default;
/// Destructor
virtual ~CapsuleVsCapsuleAlgorithm() override = default;
/// Deleted copy-constructor
CapsuleVsCapsuleAlgorithm(const CapsuleVsCapsuleAlgorithm& algorithm) = delete;
/// Deleted assignment operator
CapsuleVsCapsuleAlgorithm& operator=(const CapsuleVsCapsuleAlgorithm& algorithm) = delete;
/// Compute the narrow-phase collision detection between two capsules
bool testCollision(CapsuleVsCapsuleNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex,
uint batchNbItems, MemoryAllocator& memoryAllocator);
};
}
#endif

View File

@ -0,0 +1,78 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_CAPSULE_VS_CAPSULE_NARROW_PHASE_INFO_BATCH_H
#define REACTPHYSICS3D_CAPSULE_VS_CAPSULE_NARROW_PHASE_INFO_BATCH_H
// Libraries
#include <reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h>
/// Namespace ReactPhysics3D
namespace reactphysics3d {
// Struct CapsuleVsCapsuleNarrowPhaseInfoBatch
/**
* This structure collects all the potential collisions from the middle-phase algorithm
* that have to be tested during narrow-phase collision detection. This class collects all the
* capsule vs capsule collision detection tests.
*/
struct CapsuleVsCapsuleNarrowPhaseInfoBatch : public NarrowPhaseInfoBatch {
public:
/// List of radiuses for the first capsules
List<decimal> capsule1Radiuses;
/// List of radiuses for the second capsules
List<decimal> capsule2Radiuses;
/// List of heights for the first capsules
List<decimal> capsule1Heights;
/// List of heights for the second capsules
List<decimal> capsule2Heights;
/// Constructor
CapsuleVsCapsuleNarrowPhaseInfoBatch(MemoryAllocator& allocator, OverlappingPairs& overlappingPairs);
/// Destructor
virtual ~CapsuleVsCapsuleNarrowPhaseInfoBatch() override = default;
/// Add shapes to be tested during narrow-phase collision detection into the batch
virtual void addNarrowPhaseInfo(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1,
CollisionShape* shape2, const Transform& shape1Transform,
const Transform& shape2Transform, bool needToReportContacts, MemoryAllocator& shapeAllocator) override;
// Initialize the containers using cached capacity
virtual void reserveMemory() override;
/// Clear all the objects in the batch
virtual void clear() override;
};
}
#endif

View File

@ -0,0 +1,80 @@
/********************************************************************************
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
* Copyright (c) 2010-2020 Daniel Chappuis *
*********************************************************************************
* *
* This software is provided 'as-is', without any express or implied warranty. *
* In no event will the authors be held liable for any damages arising from the *
* use of this software. *
* *
* Permission is granted to anyone to use this software for any purpose, *
* including commercial applications, and to alter it and redistribute it *
* freely, subject to the following restrictions: *
* *
* 1. The origin of this software must not be misrepresented; you must not claim *
* that you wrote the original software. If you use this software in a *
* product, an acknowledgment in the product documentation would be *
* appreciated but is not required. *
* *
* 2. Altered source versions must be plainly marked as such, and must not be *
* misrepresented as being the original software. *
* *
* 3. This notice may not be removed or altered from any source distribution. *
* *
********************************************************************************/
#ifndef REACTPHYSICS3D_CAPSULE_VS_CONVEX_POLYHEDRON_ALGORITHM_H
#define REACTPHYSICS3D_CAPSULE_VS_CONVEX_POLYHEDRON_ALGORITHM_H
// Libraries
#include <reactphysics3d/collision/narrowphase/NarrowPhaseAlgorithm.h>
/// Namespace ReactPhysics3D
namespace reactphysics3d {
// Declarations
class ContactPoint;
// Class CapsuleVsConvexPolyhedronAlgorithm
/**
* This class is used to compute the narrow-phase collision detection
* between a capsule and a convex polyhedron. The capsule is basically
* a line segment with a margin around it. First we run the GJK algorithm.
* If GJK reports separation, we are done. If the objects overlap inside the
* capsule margin (radius), it will also report contact points and normal.
* However, if GJK report penetration of the capsule inner segment within
* the polyhedron, we run the SAT algorithm to get the contact points and
* normal.
* This is based on the "Robust Contact Creation for Physics Simulation"
* presentation by Dirk Gregorius.
*/
class CapsuleVsConvexPolyhedronAlgorithm : public NarrowPhaseAlgorithm {
protected :
public :
// -------------------- Methods -------------------- //
/// Constructor
CapsuleVsConvexPolyhedronAlgorithm() = default;
/// Destructor
virtual ~CapsuleVsConvexPolyhedronAlgorithm() override = default;
/// Deleted copy-constructor
CapsuleVsConvexPolyhedronAlgorithm(const CapsuleVsConvexPolyhedronAlgorithm& algorithm) = delete;
/// Deleted assignment operator
CapsuleVsConvexPolyhedronAlgorithm& operator=(const CapsuleVsConvexPolyhedronAlgorithm& algorithm) = delete;
/// Compute the narrow-phase collision detection between a capsule and a polyhedron
bool testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex,
uint batchNbItems, bool clipWithPreviousAxisIfStillColliding,
MemoryAllocator& memoryAllocator);
};
}
#endif

Some files were not shown because too many files have changed in this diff Show More