base
This commit is contained in:
+282
-305
@@ -10,6 +10,7 @@
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
|
||||
// ============================================================================
|
||||
@@ -68,7 +69,6 @@
|
||||
#include "Sound.h"
|
||||
#include "Gui.h"
|
||||
#include "font.h"
|
||||
#include "TexturePainter.h"
|
||||
|
||||
// Character / animation
|
||||
#include "Rig.h"
|
||||
@@ -76,8 +76,11 @@
|
||||
#include "Character.h"
|
||||
#include "CharacterManager.h"
|
||||
#include "Ragdoll.h"
|
||||
#include "DefaultBodyComponents.h"
|
||||
#include "BodyComponentActions.h"
|
||||
#include "Console.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include "CharacterMind.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
@@ -93,6 +96,7 @@ Engine::Engine()
|
||||
glfwSetCursorPosCallback(window->handle, mouse_callback);
|
||||
glfwSetScrollCallback(window->handle, scroll_callback);
|
||||
glfwSetKeyCallback(window->handle, key_callback);
|
||||
glfwSetCharCallback(window->handle, char_callback);
|
||||
glfwSetMouseButtonCallback(window->handle, mouse_button_callback);
|
||||
glfwSwapInterval(1);
|
||||
|
||||
@@ -138,12 +142,14 @@ void Engine::Initiate()
|
||||
charactermanager = new CharacterManager();
|
||||
charactermanager->LoadRig("humanoid", "assets/rig.glb");
|
||||
charactermanager->LoadAnimation("walk", "assets/walkanim.glb", "humanoid");
|
||||
charactermanager->LoadAnimation("idle", "assets/idle.glb", "humanoid");
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Player character
|
||||
// ------------------------------------------------------------------------
|
||||
Character& testcharacter = *charactermanager->CreateCharacter("character", "humanoid", "walk");
|
||||
charactermanager->SetControlledCharacter("character");
|
||||
// Idle is the base pose, walking fades in on top of it while moving
|
||||
Character& testcharacter = *charactermanager->CreateCharacter("character", "humanoid", "idle");
|
||||
testcharacter.body->SetWalkAnimation(charactermanager->GetAnimation("walk"));
|
||||
testcharacter.castshadow = true;
|
||||
testcharacter.colorOverride = glm::vec4(0.62f, 0.64f, 0.68f, 1.0f);
|
||||
testcharacter.roughness = 0.65f;
|
||||
@@ -151,17 +157,13 @@ void Engine::Initiate()
|
||||
testcharacter.body->t.TranslateTo({ 0.0f, 1.5f, 0.0f });
|
||||
testcharacter.body->animator->loops = true;
|
||||
testcharacter.BindToRenderSystem();
|
||||
|
||||
p* characterBody = new p(testcharacter.body);
|
||||
characterBody->mass = 80.0f;
|
||||
characterBody->lockRotation = true;
|
||||
characterBody->friction = 0.0f;
|
||||
physicsengine->AddObject(characterBody);
|
||||
testcharacter.CreatePhysicsBody(*physicsengine, 80.0f);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// NPC
|
||||
// ------------------------------------------------------------------------
|
||||
Character& npc = *charactermanager->CreateCharacter("npc", "humanoid", "walk");
|
||||
Character& npc = *charactermanager->CreateCharacter("npc", "humanoid", "idle");
|
||||
npc.body->SetWalkAnimation(charactermanager->GetAnimation("walk"));
|
||||
npc.castshadow = true;
|
||||
npc.colorOverride = glm::vec4(0.10f, 0.20f, 0.48f, 1.0f);
|
||||
npc.metallic = 0.1f;
|
||||
@@ -169,10 +171,96 @@ void Engine::Initiate()
|
||||
npc.body->t.ScaleBy({ 0.7f, 2.5f, 0.7f });
|
||||
npc.body->t.TranslateTo({ 3.0f, 1.25f, 3.0f });
|
||||
npc.BindToRenderSystem();
|
||||
npc.CreatePhysicsBody(*physicsengine, 80.0f);
|
||||
|
||||
testcharacter.SetWalking(false);
|
||||
npc.SetWalking(false);
|
||||
npc.EnableRagdoll(*physicsengine);
|
||||
testcharacter.body->SetWalking(false);
|
||||
npc.body->SetWalking(false);
|
||||
|
||||
// The keyboard is the player's mind: it turns WASD into actions for whichever character it's in.
|
||||
// The camera follows that character too, C moves both to the other one.
|
||||
KeyboardMind keyboardMind(*camera);
|
||||
Character* controlled = &testcharacter;
|
||||
controlled->mind = &keyboardMind;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Console commands. help and clear are built in.
|
||||
// ------------------------------------------------------------------------
|
||||
console = new Console(arial);
|
||||
|
||||
// From the camera, out through a point on the screen in pixels
|
||||
auto MouseRay = [&](const glm::vec2& mousePos) {
|
||||
const glm::mat4 inverseViewProjection = glm::inverse(camera->GetProjectionMatrix(0.05f, 2000.0f) * camera->GetViewMatrix());
|
||||
const glm::vec2 ndc(mousePos.x / window->width * 2.0f - 1.0f, 1.0f - mousePos.y / window->height * 2.0f);
|
||||
glm::vec4 nearPoint = inverseViewProjection * glm::vec4(ndc, -1.0f, 1.0f);
|
||||
glm::vec4 farPoint = inverseViewProjection * glm::vec4(ndc, 1.0f, 1.0f);
|
||||
nearPoint /= nearPoint.w;
|
||||
farPoint /= farPoint.w;
|
||||
|
||||
Ray ray;
|
||||
ray.origin = glm::vec3(nearPoint);
|
||||
ray.direction = glm::normalize(glm::vec3(farPoint - nearPoint));
|
||||
return ray;
|
||||
};
|
||||
|
||||
// Debug cubes G throws, each a mesh and the physics body moving it. Cleared once they fall off the world.
|
||||
struct DebugCube {
|
||||
Mesh* mesh = nullptr;
|
||||
p* body = nullptr;
|
||||
};
|
||||
std::vector<DebugCube> debugCubes;
|
||||
auto SpawnDebugCube = [&](const glm::vec3& position, const glm::vec3& velocity) {
|
||||
DebugCube cube;
|
||||
cube.mesh = CreateCubeMesh();
|
||||
cube.mesh->name = "debug_cube";
|
||||
cube.mesh->colorOverride = glm::vec4(1.0f, 0.4f, 0.1f, 1.0f);
|
||||
cube.mesh->roughness = 0.7f;
|
||||
cube.mesh->castshadow = true;
|
||||
cube.mesh->t.ScaleTo(0.3f);
|
||||
cube.mesh->t.RotateToQuaternion(glm::quat(1.0f, 0.0f, 0.0f, 0.0f));
|
||||
cube.mesh->t.TranslateTo(position);
|
||||
cube.mesh->BindToRenderSystem();
|
||||
|
||||
cube.body = new p(cube.mesh);
|
||||
cube.body->mass = 2.0f;
|
||||
cube.body->restitution = 0.1f;
|
||||
cube.body->friction = 0.7f;
|
||||
cube.body->maxSpeed = 40.0f;
|
||||
cube.body->linearvelocity = velocity;
|
||||
physicsengine->AddObject(cube.body);
|
||||
debugCubes.push_back(cube);
|
||||
};
|
||||
|
||||
auto ParseFloat = [](const std::string& text, float& value) {
|
||||
try { size_t used = 0; value = std::stof(text, &used); return used == text.size(); }
|
||||
catch (...) { return false; }
|
||||
};
|
||||
|
||||
console->Register("tp", "tp <x> <y> <z>", "teleports you", [&](const Console::Arguments& args) {
|
||||
glm::vec3 position;
|
||||
if (args.size() < 3 || !ParseFloat(args[0], position.x) || !ParseFloat(args[1], position.y) || !ParseFloat(args[2], position.z)) {
|
||||
console->Print("Usage: tp <x> <y> <z>");
|
||||
return;
|
||||
}
|
||||
controlled->body->t.TranslateTo(position);
|
||||
controlled->body->physics->linearvelocity = glm::vec3(0.0f);
|
||||
controlled->body->physics->WakeUp();
|
||||
console->Print("Teleported");
|
||||
});
|
||||
|
||||
console->Register("gravity", "gravity [strength]", "shows or sets how hard things fall, in m/s^2", [&](const Console::Arguments& args) {
|
||||
float strength = 0.0f;
|
||||
if (!args.empty()) {
|
||||
if (!ParseFloat(args[0], strength)) { console->Print("Usage: gravity [strength]"); return; }
|
||||
physicsengine->SetGravity(glm::vec3(0.0f, -strength, 0.0f));
|
||||
}
|
||||
std::ostringstream text;
|
||||
text << "Gravity is " << std::fixed << std::setprecision(2) << -physicsengine->GetGravity().y << " m/s^2";
|
||||
console->Print(text.str());
|
||||
});
|
||||
|
||||
console->Register("quit", "quit", "closes the game", [&](const Console::Arguments&) {
|
||||
glfwSetWindowShouldClose(window->handle, 1);
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 3D audio
|
||||
@@ -211,7 +299,7 @@ void Engine::Initiate()
|
||||
float hover = 0.0f; // eased 0-1
|
||||
};
|
||||
|
||||
enum class MenuScreen { Main, Paused, Settings, Graphics, Drawer };
|
||||
enum class MenuScreen { Main, Paused, Settings, Graphics };
|
||||
MenuScreen menuScreen = MenuScreen::Main;
|
||||
MenuScreen settingsReturn = MenuScreen::Main;
|
||||
bool menuActive = true;
|
||||
@@ -277,7 +365,7 @@ void Engine::Initiate()
|
||||
box->t2d.position = { 0.0f, 0.5f, x, y };
|
||||
box->t2d.center = { 0.0f, 0.5f };
|
||||
box->t2d.size = { 0.0f, 0.0f, width, height };
|
||||
box->rounding = 0.15f;
|
||||
box->rounding = 0.05f;
|
||||
box->BindToRenderSystem();
|
||||
menuItems.push_back(box);
|
||||
|
||||
@@ -301,11 +389,9 @@ void Engine::Initiate()
|
||||
const float mainButtonHeight = 60.0f;
|
||||
MenuButton playButton = MakeMenuButton("PLAY", menuMargin, -60.0f, menuContentWidth, mainButtonHeight, 0.75f,
|
||||
glm::vec3(0.82f, 0.45f, 0.10f), menuAccent, 0.95f);
|
||||
MenuButton drawerButton = MakeMenuButton("TEXTURE DRAWER", menuMargin, 12.0f, menuContentWidth, mainButtonHeight, 0.75f,
|
||||
MenuButton settingsButton = MakeMenuButton("SETTINGS", menuMargin, 12.0f, menuContentWidth, mainButtonHeight, 0.75f,
|
||||
menuButtonColor, menuButtonHover);
|
||||
MenuButton settingsButton = MakeMenuButton("SETTINGS", menuMargin, 84.0f, menuContentWidth, mainButtonHeight, 0.75f,
|
||||
menuButtonColor, menuButtonHover);
|
||||
MenuButton quitButton = MakeMenuButton("QUIT", menuMargin, 156.0f, menuContentWidth, mainButtonHeight, 0.75f,
|
||||
MenuButton quitButton = MakeMenuButton("QUIT", menuMargin, 84.0f, menuContentWidth, mainButtonHeight, 0.75f,
|
||||
menuButtonColor, glm::vec3(0.42f, 0.14f, 0.16f));
|
||||
|
||||
// Settings rows: LABEL [-] VALUE [+]
|
||||
@@ -349,117 +435,12 @@ void Engine::Initiate()
|
||||
MenuButton backButton = MakeMenuButton("BACK", menuMargin, 120.0f, menuContentWidth, 50.0f, 0.6f,
|
||||
glm::vec3(0.82f, 0.45f, 0.10f), menuAccent, 0.95f);
|
||||
|
||||
// Texture drawer. Filled in further down, once the things it paints on exist.
|
||||
struct DrawableSurface {
|
||||
const char* name;
|
||||
SurfaceTexture* surface;
|
||||
glm::vec4* colorOverride;
|
||||
bool hasCanvas = false;
|
||||
};
|
||||
std::vector<DrawableSurface> drawableSurfaces;
|
||||
int selectedSurface = 0;
|
||||
int selectedSwatch = 0;
|
||||
TexturePainter painter;
|
||||
bool painting = false;
|
||||
|
||||
// The first edit swaps in a private copy, so painting never touches the shared image file.
|
||||
// A colour override would hide the paint, so that goes too.
|
||||
auto EnsureCanvas = [&](DrawableSurface& drawable) {
|
||||
if (drawable.hasCanvas)
|
||||
return;
|
||||
|
||||
SurfaceTexture& surface = *drawable.surface;
|
||||
surface.texture = surface.texture ? resourcemanager->CopyTexture(*surface.texture)
|
||||
: resourcemanager->CreateTexture(256, 256);
|
||||
drawable.colorOverride->w = 0.0f;
|
||||
drawable.hasCanvas = true;
|
||||
};
|
||||
|
||||
const float surfaceRowY = -120.0f;
|
||||
const float swatchRowY = -64.0f;
|
||||
const float brushRowY = -8.0f;
|
||||
|
||||
MenuButton surfacePrevious = MakeMenuButton("<", menuMargin, surfaceRowY, 44.0f, 44.0f, 0.6f,
|
||||
menuButtonColor, menuButtonHover, 0.6f, true);
|
||||
MenuButton surfaceNext = MakeMenuButton(">", stepperUpX, surfaceRowY, 44.0f, 44.0f, 0.6f,
|
||||
menuButtonColor, menuButtonHover, 0.6f, true);
|
||||
TextBox* surfaceName = MakeMenuText("", menuMargin + 44.0f, surfaceRowY, menuContentWidth - 88.0f, 44.0f, 0.5f, menuText, 0.5f);
|
||||
|
||||
const glm::vec3 swatchColors[] = {
|
||||
{ 0.0f, 0.0f, 0.0f }, { 1.0f, 1.0f, 1.0f }, { 0.90f, 0.15f, 0.15f },
|
||||
{ 0.96f, 0.58f, 0.16f }, { 0.20f, 0.75f, 0.30f }, { 0.20f, 0.45f, 0.95f }
|
||||
};
|
||||
const float swatchSize = 52.0f;
|
||||
const float swatchSpacing = 60.0f;
|
||||
|
||||
std::vector<MenuButton> swatches;
|
||||
swatches.reserve(std::size(swatchColors));
|
||||
for (int i = 0; i < (int)std::size(swatchColors); i++) {
|
||||
swatches.push_back(MakeMenuButton("", menuMargin + swatchSpacing * i, swatchRowY, swatchSize, 44.0f, 0.5f,
|
||||
swatchColors[i], glm::mix(swatchColors[i], glm::vec3(1.0f), 0.2f), 1.0f, true));
|
||||
}
|
||||
|
||||
Box* swatchMarker = new Box();
|
||||
swatchMarker->Color = menuAccent;
|
||||
swatchMarker->t2d.center = { 0.0f, 0.5f };
|
||||
swatchMarker->t2d.size = { 0.0f, 0.0f, swatchSize, 4.0f };
|
||||
swatchMarker->BindToRenderSystem();
|
||||
menuItems.push_back(swatchMarker);
|
||||
|
||||
TextBox* brushLabel = MakeMenuText("BRUSH SIZE", menuMargin, brushRowY, 172.0f, 44.0f, 0.5f, menuMutedText);
|
||||
TextBox* brushValue = MakeMenuText("", stepperValueX, brushRowY, stepperValueWidth, 44.0f, 0.5f, menuText, 0.5f);
|
||||
MenuButton brushDown = MakeMenuButton("-", stepperDownX, brushRowY, 44.0f, 44.0f, 0.7f,
|
||||
menuButtonColor, menuButtonHover, 0.6f, true);
|
||||
MenuButton brushUp = MakeMenuButton("+", stepperUpX, brushRowY, 44.0f, 44.0f, 0.7f,
|
||||
menuButtonColor, menuButtonHover, 0.6f, true);
|
||||
MenuButton clearButton = MakeMenuButton("CLEAR", menuMargin, 52.0f, menuContentWidth, 50.0f, 0.6f,
|
||||
menuButtonColor, menuButtonHover);
|
||||
|
||||
// The canvas sits in the middle of the space right of the panel
|
||||
const float canvasSize = 640.0f;
|
||||
|
||||
Box* canvasBorder = new Box();
|
||||
canvasBorder->Color = menuAccent;
|
||||
canvasBorder->t2d.position = { 0.5f, 0.5f, menuPanelWidth * 0.5f, 0.0f };
|
||||
canvasBorder->t2d.center = { 0.5f, 0.5f };
|
||||
canvasBorder->t2d.size = { 0.0f, 0.0f, canvasSize + 8.0f, canvasSize + 8.0f };
|
||||
canvasBorder->BindToRenderSystem();
|
||||
menuItems.push_back(canvasBorder);
|
||||
|
||||
Box* canvasBlank = new Box(); // shows through until the surface has a texture
|
||||
canvasBlank->t2d.position = canvasBorder->t2d.position;
|
||||
canvasBlank->t2d.center = { 0.5f, 0.5f };
|
||||
canvasBlank->t2d.size = { 0.0f, 0.0f, canvasSize, canvasSize };
|
||||
canvasBlank->BindToRenderSystem();
|
||||
menuItems.push_back(canvasBlank);
|
||||
|
||||
ImageBox* canvasImage = new ImageBox();
|
||||
canvasImage->t2d.position = canvasBorder->t2d.position;
|
||||
canvasImage->t2d.center = { 0.5f, 0.5f };
|
||||
canvasImage->t2d.size = { 0.0f, 0.0f, canvasSize, canvasSize };
|
||||
canvasImage->BindToRenderSystem();
|
||||
menuItems.push_back(canvasImage);
|
||||
|
||||
std::vector<MenuButton*> menuButtons = {
|
||||
&playButton, &drawerButton, &settingsButton, &quitButton,
|
||||
&playButton, &settingsButton, &quitButton,
|
||||
&sensitivityDown, &sensitivityUp, &fovDown, &fovUp,
|
||||
&hitboxToggle, &graphicsButton, &resetButton, &backButton,
|
||||
&bloomToggle, &shadowToggle, &lightShadowToggle, &vsyncToggle, &cullingToggle,
|
||||
&surfacePrevious, &surfaceNext, &brushDown, &brushUp, &clearButton
|
||||
&bloomToggle, &shadowToggle, &lightShadowToggle, &vsyncToggle, &cullingToggle
|
||||
};
|
||||
for (MenuButton& swatch : swatches)
|
||||
menuButtons.push_back(&swatch);
|
||||
|
||||
TextBox* menuHint = new TextBox();
|
||||
menuHint->font = arial;
|
||||
menuHint->fontsize = 0.42f;
|
||||
menuHint->Color = menuMutedText;
|
||||
menuHint->t2d.position = { 0.0f, 1.0f, menuMargin, -28.0f };
|
||||
menuHint->t2d.center = { 0.0f, 1.0f };
|
||||
menuHint->t2d.size = { 0.0f, 0.0f, menuContentWidth, 110.0f };
|
||||
menuHint->textCenter = { 0.0f, 1.0f };
|
||||
menuHint->BindToRenderSystem();
|
||||
menuItems.push_back(menuHint);
|
||||
|
||||
auto SetButtonVisible = [](MenuButton& button, bool visible) {
|
||||
button.box->Opacity = visible ? button.opacity : 0.0f;
|
||||
@@ -472,7 +453,6 @@ void Engine::Initiate()
|
||||
const bool paused = menuScreen == MenuScreen::Paused;
|
||||
const bool settings = menuScreen == MenuScreen::Settings;
|
||||
const bool graphics = menuScreen == MenuScreen::Graphics;
|
||||
const bool drawer = menuScreen == MenuScreen::Drawer;
|
||||
const bool mainButtons = main || paused;
|
||||
|
||||
menuVeil->Opacity = 0.22f;
|
||||
@@ -480,39 +460,13 @@ void Engine::Initiate()
|
||||
menuPanelEdge->Opacity = 1.0f;
|
||||
menuTitle->Opacity = 1.0f;
|
||||
menuSubtitle->Opacity = 1.0f;
|
||||
menuHint->Opacity = 1.0f;
|
||||
menuSubtitle->text = main ? "MAIN MENU" : (paused ? "PAUSED"
|
||||
: (drawer ? "TEXTURE DRAWER" : (graphics ? "GRAPHICS" : "SETTINGS")));
|
||||
menuSubtitle->text = main ? "MAIN MENU" : (paused ? "PAUSED" : (graphics ? "GRAPHICS" : "SETTINGS"));
|
||||
playButton.label->text = paused ? "RESUME" : "PLAY";
|
||||
quitButton.label->text = paused ? "QUIT TO MENU" : "QUIT";
|
||||
menuHint->text = drawer
|
||||
? "DRAG ON THE CANVAS TO DRAW\nESC TO GO BACK"
|
||||
: (settings || graphics ? "ESC TO GO BACK" : "WASD TO MOVE\nESC FOR MENU");
|
||||
|
||||
SetButtonVisible(playButton, mainButtons);
|
||||
SetButtonVisible(drawerButton, mainButtons);
|
||||
SetButtonVisible(settingsButton, mainButtons);
|
||||
SetButtonVisible(quitButton, mainButtons);
|
||||
SetButtonVisible(surfacePrevious, drawer);
|
||||
SetButtonVisible(surfaceNext, drawer);
|
||||
SetButtonVisible(brushDown, drawer);
|
||||
SetButtonVisible(brushUp, drawer);
|
||||
SetButtonVisible(clearButton, drawer);
|
||||
for (MenuButton& swatch : swatches)
|
||||
SetButtonVisible(swatch, drawer);
|
||||
|
||||
Texture* canvasTexture = drawableSurfaces[selectedSurface].surface->texture;
|
||||
canvasImage->surface = SurfaceTexture::Stretched(canvasTexture);
|
||||
canvasImage->Opacity = drawer && canvasTexture ? 1.0f : 0.0f;
|
||||
canvasBlank->Opacity = drawer ? 1.0f : 0.0f;
|
||||
canvasBorder->Opacity = drawer ? 1.0f : 0.0f;
|
||||
surfaceName->Opacity = drawer ? 1.0f : 0.0f;
|
||||
surfaceName->text = drawableSurfaces[selectedSurface].name;
|
||||
brushLabel->Opacity = drawer ? 1.0f : 0.0f;
|
||||
brushValue->Opacity = drawer ? 1.0f : 0.0f;
|
||||
brushValue->text = std::to_string(painter.brushSize);
|
||||
swatchMarker->Opacity = drawer ? 1.0f : 0.0f;
|
||||
swatchMarker->t2d.position = { 0.0f, 0.5f, menuMargin + swatchSpacing * selectedSwatch, swatchRowY + 28.0f };
|
||||
|
||||
SetButtonVisible(sensitivityDown, settings);
|
||||
SetButtonVisible(sensitivityUp, settings);
|
||||
@@ -521,7 +475,7 @@ void Engine::Initiate()
|
||||
SetButtonVisible(hitboxToggle, settings);
|
||||
SetButtonVisible(graphicsButton, settings);
|
||||
SetButtonVisible(resetButton, settings);
|
||||
SetButtonVisible(backButton, settings || drawer || graphics);
|
||||
SetButtonVisible(backButton, settings || graphics);
|
||||
|
||||
auto ShowToggle = [&](MenuButton& button, const char* name, bool enabled) {
|
||||
SetButtonVisible(button, graphics);
|
||||
@@ -549,9 +503,8 @@ void Engine::Initiate()
|
||||
auto OpenMenu = [&](MenuScreen screen) {
|
||||
menuActive = true;
|
||||
menuScreen = screen;
|
||||
charactermanager->ClearInput();
|
||||
testcharacter.SetWalking(false);
|
||||
npc.SetWalking(false);
|
||||
testcharacter.body->SetWalking(false);
|
||||
npc.body->SetWalking(false);
|
||||
fpsCounter->Opacity = 0.0f;
|
||||
camera->lockedcursor = false;
|
||||
glfwSetInputMode(window->handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
@@ -560,7 +513,6 @@ void Engine::Initiate()
|
||||
|
||||
auto ResumeGame = [&]() {
|
||||
menuActive = false;
|
||||
charactermanager->ClearInput();
|
||||
for (Box* item : menuItems) item->Opacity = 0.0f;
|
||||
fpsCounter->Opacity = 1.0f;
|
||||
camera->lockedcursor = true;
|
||||
@@ -575,21 +527,21 @@ void Engine::Initiate()
|
||||
glfwSetInputMode(window->handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Baseplate
|
||||
// Baseplate, a flat slab with its top at y = 0
|
||||
// ------------------------------------------------------------------------
|
||||
Mesh* floor = CreateCubeMesh();
|
||||
floor->name = "baseplate";
|
||||
floor->surface = SurfaceTexture::Tiled(blanktex, 8.0f);
|
||||
floor->roughness = 0.9f;
|
||||
floor->t.ScaleTo({ 200.0f, 2.0f, 200.0f });
|
||||
floor->t.TranslateTo({ 0.0f, -1.0f, 0.0f });
|
||||
floor->BindToRenderSystem();
|
||||
mainf->AddChild(floor);
|
||||
Mesh* baseplate = CreateCubeMesh();
|
||||
baseplate->name = "baseplate";
|
||||
baseplate->t.ScaleTo({ 200.0f, 2.0f, 200.0f });
|
||||
baseplate->t.TranslateTo({ 0.0f, -1.0f, 0.0f });
|
||||
baseplate->t.RotateToQuaternion(glm::quat(1.0f, 0.0f, 0.0f, 0.0f));
|
||||
baseplate->surface = SurfaceTexture::Tiled(blanktex, 8.0f);
|
||||
baseplate->BindToRenderSystem();
|
||||
mainf->AddChild(baseplate);
|
||||
|
||||
p* floorBody = new p(floor);
|
||||
floorBody->velocity = false;
|
||||
physicsengine->AddObject(floorBody);
|
||||
const BoundingBox floorBounds = floor->t.GetRotationlessAABB();
|
||||
p* baseplateBody = new p(baseplate);
|
||||
baseplateBody->velocity = false;
|
||||
baseplateBody->friction = 0.7f;
|
||||
physicsengine->AddObject(baseplateBody);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Light cubes
|
||||
@@ -616,22 +568,19 @@ void Engine::Initiate()
|
||||
Mesh* pointLightCube = AddLightCube("point_light_cube", pointLightPosition, 0.8f, pointLightColor);
|
||||
rendersystem->lighting->AddPointLight(pointLightPosition, pointLightColor, 300.0f, 30.0f);
|
||||
|
||||
drawableSurfaces.push_back({ "BASEPLATE", &floor->surface, &floor->colorOverride });
|
||||
drawableSurfaces.push_back({ "PLAYER", &testcharacter.surface, &testcharacter.colorOverride });
|
||||
drawableSurfaces.push_back({ "DUMMY", &npc.surface, &npc.colorOverride });
|
||||
drawableSurfaces.push_back({ "SKY LIGHT CUBE", &skyLightCube->surface, &skyLightCube->colorOverride });
|
||||
drawableSurfaces.push_back({ "POINT LIGHT CUBE", &pointLightCube->surface, &pointLightCube->colorOverride });
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Ground check + respawn
|
||||
// ------------------------------------------------------------------------
|
||||
auto IsGrounded = [&]() -> bool {
|
||||
auto IsGrounded = [&](Character& character) -> bool {
|
||||
Ray downRay;
|
||||
downRay.origin = testcharacter.body->t.GetTranslation();
|
||||
downRay.origin = character.body->t.GetTranslation();
|
||||
downRay.direction = glm::vec3(0.0f, -1.0f, 0.0f);
|
||||
const float halfHeight = testcharacter.body->t.GetScale().y * 0.5f;
|
||||
const float halfHeight = character.body->t.GetScale().y * 0.5f;
|
||||
|
||||
for (p* obj : physicsengine->GetObjects()) {
|
||||
if (obj->pointer == testcharacter.body) continue;
|
||||
if (obj->pointer == character.body) continue;
|
||||
if (obj->collisionGroup != 0 && obj->collisionGroup == character.GetCollisionGroup()) continue; // anything else of its own
|
||||
if (!obj->collision) continue;
|
||||
auto hit = IsRayInT(downRay, obj->pointer->t);
|
||||
if (hit.has_value()) {
|
||||
@@ -677,6 +626,16 @@ void Engine::Initiate()
|
||||
prevFrameTime = frameNow;
|
||||
const float refDt = 1.0f / float(fps);
|
||||
|
||||
static bool prevSwapDown = false;
|
||||
const bool swapDown = keyboard->IsKeyDown(GLFW_KEY_C);
|
||||
if (swapDown && !prevSwapDown && !menuActive) {
|
||||
// The keyboard moves over, the one left behind has no mind and stands still
|
||||
controlled->mind = nullptr;
|
||||
controlled = controlled == &testcharacter ? &npc : &testcharacter;
|
||||
controlled->mind = &keyboardMind;
|
||||
}
|
||||
prevSwapDown = swapDown;
|
||||
|
||||
static bool prevEscapeDown = false;
|
||||
const bool escapeDown = keyboard->IsKeyDown(GLFW_KEY_ESCAPE);
|
||||
const bool escapePressed = escapeDown && !prevEscapeDown;
|
||||
@@ -685,7 +644,7 @@ void Engine::Initiate()
|
||||
if (escapePressed) {
|
||||
if (!menuActive)
|
||||
OpenMenu(MenuScreen::Paused);
|
||||
else if (menuScreen == MenuScreen::Settings || menuScreen == MenuScreen::Drawer)
|
||||
else if (menuScreen == MenuScreen::Settings)
|
||||
menuScreen = settingsReturn;
|
||||
else if (menuScreen == MenuScreen::Graphics)
|
||||
menuScreen = MenuScreen::Settings;
|
||||
@@ -696,32 +655,75 @@ void Engine::Initiate()
|
||||
// --------------------------------------------------------------------
|
||||
// Mouse look
|
||||
// --------------------------------------------------------------------
|
||||
int wheelSteps = 0; // up is positive
|
||||
while (Mouse::Event buffer = mouse->Read()) {
|
||||
if (menuActive) continue;
|
||||
if (buffer.GetType() == Mouse::Event::Type::WheelUp) wheelSteps++;
|
||||
else if (buffer.GetType() == Mouse::Event::Type::WheelDown) wheelSteps--;
|
||||
|
||||
if (menuActive || console->IsOpen()) continue;
|
||||
if (buffer.GetType() == Mouse::Event::Type::Move && camera->lockedcursor) {
|
||||
float xoffset = mouse->GetX() - mouse->GetLastX();
|
||||
float yoffset = mouse->GetLastY() - mouse->GetY();
|
||||
camera->ProcessMouseMovement(xoffset, yoffset);
|
||||
}
|
||||
else if (buffer.GetType() == Mouse::Event::Type::RPress) {
|
||||
camera->lockedcursor = !camera->lockedcursor;
|
||||
if (camera->lockedcursor) {
|
||||
glfwSetInputMode(window->handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
}
|
||||
|
||||
const glm::vec2 mousePos((float)mouse->GetX(), (float)mouse->GetY());
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Debug: G throws a cube at the cursor, or straight ahead with the cursor locked
|
||||
// --------------------------------------------------------------------
|
||||
static bool prevLaunchDown = false;
|
||||
const bool launchDown = keyboard->IsKeyDown(GLFW_KEY_G);
|
||||
if (launchDown && !prevLaunchDown && !menuActive) {
|
||||
const float launchSpeed = 40.0f;
|
||||
const glm::vec2 aim = camera->lockedcursor ? glm::vec2(window->width * 0.5f, window->height * 0.5f) : mousePos;
|
||||
const Ray ray = MouseRay(aim);
|
||||
|
||||
// The camera sits behind the player and looks over them, so start past them rather than hitting them on the way out
|
||||
const float pastPlayer = glm::dot(controlled->body->t.GetTranslation() - ray.origin, ray.direction) + 1.0f;
|
||||
const glm::vec3 start = ray.origin + ray.direction * std::max(pastPlayer, 1.0f);
|
||||
SpawnDebugCube(start, ray.direction * launchSpeed);
|
||||
}
|
||||
prevLaunchDown = launchDown;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Cursor. Locked for looking around, unless Alt has freed it or the console needs the mouse.
|
||||
// The menus handle it themselves.
|
||||
// --------------------------------------------------------------------
|
||||
static bool mouseFreed = false;
|
||||
static bool prevAltDown = false;
|
||||
const bool altDown = keyboard->IsKeyDown(GLFW_KEY_LEFT_ALT) || keyboard->IsKeyDown(GLFW_KEY_RIGHT_ALT);
|
||||
if (altDown && !prevAltDown && !menuActive)
|
||||
mouseFreed = !mouseFreed;
|
||||
prevAltDown = altDown;
|
||||
|
||||
if (!menuActive) {
|
||||
const bool wantLocked = !mouseFreed && !console->IsOpen();
|
||||
if (camera->lockedcursor != wantLocked) {
|
||||
camera->lockedcursor = wantLocked;
|
||||
glfwSetInputMode(window->handle, GLFW_CURSOR, wantLocked ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
|
||||
if (wantLocked) {
|
||||
int ww, wh;
|
||||
glfwGetWindowSize(window->handle, &ww, &wh);
|
||||
glfwSetCursorPos(window->handle, ww * 0.5, wh * 0.5);
|
||||
}
|
||||
else {
|
||||
glfwSetInputMode(window->handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
|
||||
// Otherwise the jump back to the middle reads as one big mouse movement and the camera snaps
|
||||
mouse->OnMouseMove((unsigned int)(ww / 2), (unsigned int)(wh / 2));
|
||||
mouse->OnMouseMove((unsigned int)(ww / 2), (unsigned int)(wh / 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (console->IsOpen() && wheelSteps != 0)
|
||||
console->Scroll(wheelSteps * 3);
|
||||
console->Update(deltatime);
|
||||
|
||||
if (menuActive) {
|
||||
ApplyMenuLayout();
|
||||
|
||||
const bool liveBackdrop = menuScreen == MenuScreen::Main ||
|
||||
((menuScreen == MenuScreen::Settings || menuScreen == MenuScreen::Graphics || menuScreen == MenuScreen::Drawer)
|
||||
((menuScreen == MenuScreen::Settings || menuScreen == MenuScreen::Graphics)
|
||||
&& settingsReturn == MenuScreen::Main);
|
||||
if (liveBackdrop) {
|
||||
static float menuAngle = 0.6f;
|
||||
@@ -817,42 +819,9 @@ void Engine::Initiate()
|
||||
menuScreen = MenuScreen::Settings;
|
||||
}
|
||||
}
|
||||
else if (menuScreen == MenuScreen::Drawer) {
|
||||
const int surfaceCount = (int)drawableSurfaces.size();
|
||||
if (surfacePrevious.hovered) {
|
||||
selectedSurface = (selectedSurface + surfaceCount - 1) % surfaceCount;
|
||||
}
|
||||
else if (surfaceNext.hovered) {
|
||||
selectedSurface = (selectedSurface + 1) % surfaceCount;
|
||||
}
|
||||
else if (brushDown.hovered) {
|
||||
painter.brushSize = glm::max(painter.brushSize - 2, 2);
|
||||
}
|
||||
else if (brushUp.hovered) {
|
||||
painter.brushSize = glm::min(painter.brushSize + 2, 64);
|
||||
}
|
||||
else if (clearButton.hovered) {
|
||||
DrawableSurface& drawable = drawableSurfaces[selectedSurface];
|
||||
EnsureCanvas(drawable);
|
||||
painter.Clear(*drawable.surface->texture);
|
||||
}
|
||||
else if (backButton.hovered) {
|
||||
menuScreen = settingsReturn;
|
||||
}
|
||||
for (int i = 0; i < (int)swatches.size(); i++) {
|
||||
if (swatches[i].hovered) {
|
||||
selectedSwatch = i;
|
||||
painter.color = swatchColors[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (playButton.hovered) {
|
||||
ResumeGame();
|
||||
}
|
||||
else if (drawerButton.hovered) {
|
||||
settingsReturn = menuScreen;
|
||||
menuScreen = MenuScreen::Drawer;
|
||||
}
|
||||
else if (settingsButton.hovered) {
|
||||
settingsReturn = menuScreen;
|
||||
menuScreen = MenuScreen::Settings;
|
||||
@@ -864,25 +833,6 @@ void Engine::Initiate()
|
||||
menuScreen = MenuScreen::Main;
|
||||
}
|
||||
}
|
||||
// A stroke has to start on the canvas, but carries on if the mouse wanders off it
|
||||
canvasImage->t2d.Recalculate();
|
||||
const glm::vec2 canvasUV = (mousePos - canvasImage->t2d.pixelposition) / canvasImage->t2d.pixelsize;
|
||||
if (!mouseDown || menuScreen != MenuScreen::Drawer) {
|
||||
painting = false;
|
||||
}
|
||||
else if (!prevMouseDown) {
|
||||
painting = canvasUV.x >= 0.0f && canvasUV.x <= 1.0f && canvasUV.y >= 0.0f && canvasUV.y <= 1.0f;
|
||||
}
|
||||
|
||||
if (painting) {
|
||||
DrawableSurface& drawable = drawableSurfaces[selectedSurface];
|
||||
EnsureCanvas(drawable);
|
||||
painter.Stroke(*drawable.surface->texture, glm::clamp(canvasUV, glm::vec2(0.0f), glm::vec2(1.0f)));
|
||||
}
|
||||
else {
|
||||
painter.EndStroke();
|
||||
}
|
||||
|
||||
prevMouseDown = mouseDown;
|
||||
|
||||
UpdateHitboxDebug();
|
||||
@@ -891,74 +841,84 @@ void Engine::Initiate()
|
||||
continue;
|
||||
}
|
||||
|
||||
const glm::vec3 camFront = camera->t.GetFrontVector();
|
||||
const glm::vec3 camRight = camera->t.GetRightVector();
|
||||
const float movespeed = 3.0f * 1.3f * 0.6f;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// WASD movement
|
||||
// Characters: minds decide actions, bodies carry them out
|
||||
// --------------------------------------------------------------------
|
||||
const glm::vec3 inputDirection = testcharacter.GetMovementDirection(camFront, camRight);
|
||||
const bool hasMoveInput = glm::length(inputDirection) > 0.0f;
|
||||
keyboardMind.enabled = !console->IsOpen(); // typing isn't walking
|
||||
charactermanager->Think();
|
||||
|
||||
if (hasMoveInput)
|
||||
testcharacter.SetTurnDirection(inputDirection);
|
||||
testcharacter.SetWalking(hasMoveInput);
|
||||
for (Character* character : charactermanager->GetCharacters()) {
|
||||
if (character->ragdoll) continue;
|
||||
character->body->ApplyMovement(IsGrounded(*character), 1.0f, deltatime, refDt);
|
||||
}
|
||||
|
||||
charactermanager->Step(deltatime);
|
||||
|
||||
const glm::vec3 inputVel = hasMoveInput
|
||||
? testcharacter.GetForwardDirection() * movespeed
|
||||
: glm::vec3(0.0f);
|
||||
|
||||
const float accel = 40.0f;
|
||||
const float damp = 0.75f;
|
||||
const float airControl = 0.5f;
|
||||
const float groundTopSpeed = movespeed * accel * refDt * damp / (1.0f - damp);
|
||||
|
||||
if (IsGrounded()) {
|
||||
const float dampStep = std::pow(damp, deltatime / refDt);
|
||||
const float accelScale = refDt * damp * (1.0f - dampStep) / (deltatime * dampStep * (1.0f - damp));
|
||||
characterBody->linearvelocity.x *= dampStep;
|
||||
characterBody->linearvelocity.z *= dampStep;
|
||||
characterBody->linearvelocity.x += inputVel.x * accel * deltatime * accelScale;
|
||||
characterBody->linearvelocity.z += inputVel.z * accel * deltatime * accelScale;
|
||||
}
|
||||
else if (glm::length(inputVel) > 0.0f) {
|
||||
const glm::vec3 wishDir = glm::normalize(inputVel);
|
||||
const float current = glm::dot(glm::vec3(characterBody->linearvelocity.x, 0.0f,
|
||||
characterBody->linearvelocity.z), wishDir);
|
||||
const float add = glm::clamp(groundTopSpeed - current, 0.0f,
|
||||
accel * airControl * movespeed * deltatime);
|
||||
characterBody->linearvelocity.x += wishDir.x * add;
|
||||
characterBody->linearvelocity.z += wishDir.z * add;
|
||||
}
|
||||
|
||||
float physicsAlpha = 0.0f; // how far this frame is between the last tick and the next
|
||||
{
|
||||
const int substeps = std::max(1, (int)std::ceil(deltatime * 120.0f));
|
||||
const float subDt = deltatime / float(substeps);
|
||||
for (int i = 0; i < substeps; i++) physicsengine->Step(subDt);
|
||||
}
|
||||
npc.ragdoll->Update(deltatime);
|
||||
// Physics ticks a fixed 30 times a second of real time, whatever the frame rate.
|
||||
// deltatime is real time sped up by timeScale, so a tick covers that much more game time.
|
||||
const float tickRate = 30.0f;
|
||||
const float tickSeconds = 1.0f / tickRate;
|
||||
const float timeScale = 144.0f / float(fps);
|
||||
const float tickDt = tickSeconds * timeScale;
|
||||
const int substepsPerTick = std::max(1, (int)std::ceil(tickDt * 120.0f)); // keeps fast things from passing through
|
||||
const int maxTicksPerFrame = 4;
|
||||
|
||||
if (testcharacter.body->t.GetTranslation().y < -20.0f) {
|
||||
testcharacter.body->t.TranslateTo(RandomSpawn());
|
||||
characterBody->linearvelocity = glm::vec3(0.0f);
|
||||
characterBody->WakeUp();
|
||||
static float physicsAccumulator = 0.0f;
|
||||
physicsAccumulator += deltatime / timeScale;
|
||||
int ticks = 0;
|
||||
while (physicsAccumulator >= tickSeconds && ticks < maxTicksPerFrame) {
|
||||
physicsengine->Tick(tickDt, substepsPerTick);
|
||||
physicsAccumulator -= tickSeconds;
|
||||
ticks++;
|
||||
}
|
||||
// Fell too far behind, a long hitch. Drop the backlog rather than spending frames catching up.
|
||||
if (physicsAccumulator >= tickSeconds)
|
||||
physicsAccumulator = 0.0f;
|
||||
physicsAlpha = physicsAccumulator / tickSeconds;
|
||||
}
|
||||
|
||||
// Nothing reads the contacts physics records yet, so they're let go every frame
|
||||
physicsengine->ClearImpacts();
|
||||
|
||||
// Debug cubes that fell off the world go
|
||||
for (int i = (int)debugCubes.size() - 1; i >= 0; i--) {
|
||||
if (debugCubes[i].mesh->t.GetTranslationY() > -50.0f)
|
||||
continue;
|
||||
physicsengine->RemoveP(debugCubes[i].body);
|
||||
delete debugCubes[i].body;
|
||||
delete debugCubes[i].mesh; // takes itself out of the render system
|
||||
debugCubes.erase(debugCubes.begin() + i);
|
||||
}
|
||||
|
||||
for (Character* character : charactermanager->GetCharacters()) {
|
||||
p* body = character->body->physics;
|
||||
if (body && body->pointer->t.GetTranslation().y < -20.0f) {
|
||||
body->pointer->t.TranslateTo(RandomSpawn());
|
||||
body->linearvelocity = glm::vec3(0.0f);
|
||||
body->WakeUp();
|
||||
}
|
||||
}
|
||||
|
||||
// Drawn partway to the next tick from here until after Render, so nothing may move bodies in between
|
||||
physicsengine->ApplyInterpolation(physicsAlpha);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Third person camera
|
||||
// --------------------------------------------------------------------
|
||||
{
|
||||
const glm::vec3 playerPos = testcharacter.body->t.GetTranslation();
|
||||
const glm::vec3 playerPos = controlled->body->t.GetTranslation();
|
||||
const glm::vec3 target = playerPos + glm::vec3(0.0f, 0.8f, 0.0f);
|
||||
glm::vec3 cameraPos = target - camera->t.GetFrontVector() * 5.0f;
|
||||
|
||||
const bool playerOffFloor =
|
||||
playerPos.x < floorBounds.min.x || playerPos.x > floorBounds.max.x ||
|
||||
playerPos.z < floorBounds.min.z || playerPos.z > floorBounds.max.z;
|
||||
const bool ignoreFloorCollision = playerOffFloor || playerPos.y < floorBounds.max.y;
|
||||
// Scroll zooms, unless something on screen is using the wheel. Eases toward the new distance.
|
||||
static float zoomTarget = 5.0f;
|
||||
static float zoomDistance = 5.0f;
|
||||
if (!console->IsOpen() && wheelSteps != 0)
|
||||
zoomTarget = glm::clamp(zoomTarget * std::pow(0.85f, (float)wheelSteps), 1.5f, 15.0f);
|
||||
zoomDistance += (zoomTarget - zoomDistance) * (1.0f - std::exp(-12.0f * deltatime));
|
||||
|
||||
glm::vec3 cameraPos = target - camera->t.GetFrontVector() * zoomDistance;
|
||||
|
||||
const glm::vec3 cameraRay = cameraPos - target;
|
||||
const float desiredDistance = glm::length(cameraRay);
|
||||
@@ -969,12 +929,10 @@ void Engine::Initiate()
|
||||
cameraSweep.direction = cameraDir;
|
||||
float nearestHit = desiredDistance;
|
||||
for (p* obj : physicsengine->GetObjects()) {
|
||||
if (obj->pointer == testcharacter.body || !obj->collision)
|
||||
if (obj->pointer == controlled->body || !obj->collision)
|
||||
continue;
|
||||
if (obj->collisionGroup != 0) // flailing ragdoll limbs would make the camera jump
|
||||
continue;
|
||||
if (obj == floorBody && ignoreFloorCollision)
|
||||
continue;
|
||||
auto hit = IsRayInT(cameraSweep, obj->pointer->t);
|
||||
if (!hit.has_value())
|
||||
continue;
|
||||
@@ -1024,6 +982,7 @@ void Engine::Initiate()
|
||||
// --------------------------------------------------------------------
|
||||
UpdateHitboxDebug();
|
||||
rendersystem->Render(*camera);
|
||||
physicsengine->RemoveInterpolation(); // back to the real positions before anything moves them
|
||||
|
||||
glfwPollEvents();
|
||||
}
|
||||
@@ -1044,6 +1003,22 @@ void framebuffer_size_callback(GLFWwindow* window, int w, int h)
|
||||
|
||||
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
|
||||
{
|
||||
Console* console = engine ? engine->console : nullptr;
|
||||
|
||||
if (console && key == GLFW_KEY_GRAVE_ACCENT && action == GLFW_PRESS) {
|
||||
console->Toggle();
|
||||
return;
|
||||
}
|
||||
|
||||
// While typing, keys belong to the console. Releases still go through, so nothing held stays stuck down.
|
||||
if (console && console->IsOpen()) {
|
||||
if (action == GLFW_PRESS || action == GLFW_REPEAT)
|
||||
console->OnKey(key);
|
||||
else if (action == GLFW_RELEASE)
|
||||
keyboard->KeyUp(key);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == GLFW_KEY_F11 && action == GLFW_PRESS) {
|
||||
static int wx = 100, wy = 100, ww = 1920, wh = 1080;
|
||||
if (glfwGetWindowMonitor(window)) {
|
||||
@@ -1064,16 +1039,18 @@ void key_callback(GLFWwindow* window, int key, int scancode, int action, int mod
|
||||
|
||||
if (action == GLFW_PRESS) keyboard->KeyDown(key);
|
||||
else if (action == GLFW_RELEASE) keyboard->KeyUp(key);
|
||||
}
|
||||
|
||||
if (key >= GLFW_KEY_A && key <= GLFW_KEY_Z && engine && engine->charactermanager) {
|
||||
const char letter = (char)('A' + (key - GLFW_KEY_A));
|
||||
if (action == GLFW_PRESS) engine->charactermanager->PressInput(letter);
|
||||
else if (action == GLFW_RELEASE) engine->charactermanager->ReleaseInput(letter);
|
||||
}
|
||||
void char_callback(GLFWwindow* window, unsigned int codepoint)
|
||||
{
|
||||
if (engine && engine->console && engine->console->IsOpen())
|
||||
engine->console->OnChar(codepoint);
|
||||
}
|
||||
|
||||
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
|
||||
{
|
||||
// Mouse counts wheel notches in Windows units, 120 to a notch, and carries the remainder for touchpads
|
||||
mouse->OnWheelDelta(mouse->GetX(), mouse->GetY(), (int)std::lround(yoffset * 120.0));
|
||||
}
|
||||
|
||||
void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)
|
||||
|
||||
Reference in New Issue
Block a user