diff --git a/Game/Game.vcxproj b/Game/Game.vcxproj
index 83eb4f0..f2386a9 100644
--- a/Game/Game.vcxproj
+++ b/Game/Game.vcxproj
@@ -61,6 +61,7 @@
true
$(ProjectDir)src\include\;$(SolutionDir)NothinFancy\src\include\
$(IntDir)obj\
+ true
Console
@@ -90,6 +91,7 @@ if exist "base.nfpack" (copy "base.nfpack" "$(OutDir)assets\")
true
$(ProjectDir)src\include\;$(SolutionDir)NothinFancy\src\include\
$(IntDir)obj\
+ true
Windows
diff --git a/Game/src/Game.cpp b/Game/src/Game.cpp
index b789e31..b9aacad 100644
--- a/Game/src/Game.cpp
+++ b/Game/src/Game.cpp
@@ -16,6 +16,8 @@ int main(int argc, char* argv[]) {
app.setDefaultState("Main State");
app.run();
+
+ delete test;
}
return 0;
diff --git a/Game/src/MainState.cpp b/Game/src/MainState.cpp
index 26910a7..0d0c6a4 100644
--- a/Game/src/MainState.cpp
+++ b/Game/src/MainState.cpp
@@ -6,11 +6,9 @@ void MainState::onEnter() {
camera->setType(currCamType);
ap.load("example.nfpack");
- test.create(ap.get("2mats.obj"), nf::Entity::Type::DYNAMIC);
- test.setPosition(nf::Vec3(0.0, 1.5, -5.0));
- plane.create(ap.get("env.obj"), nf::Entity::Type::MAP);
+ test.create(ap.get("2mats.obj"), nf::Vec3(0.0, 1.5, -5.0), nf::Entity::Type::DYNAMIC);
+ plane.create(ap.get("env.obj"), nf::Vec3(0.0, -20.0, 0.0), nf::Entity::Type::MAP);
plane.setScale(20.0);
- plane.setPosition(0.0, -20.0, 0.0);
light.create(nf::Vec3(0.0, 20.0, 0.0), nf::Vec3(1.0, 1.0, 1.0));
light2.create(nf::Vec3(-10.0, 20.0, -10.0), nf::Vec3(1.0, 1.0, 1.0));
@@ -42,8 +40,7 @@ void MainState::onEnter() {
for (int y = 0; y < 3; y++) {
for (int z = 0; z < 3; z++) {
entities.push_back(new nf::Entity);
- entities.back()->create(nf::BaseAssets::cube, nf::Entity::Type::DYNAMIC);
- entities.back()->setPosition(nf::Vec3(5.0 + x * 2.05, 1.0 + y * 2.05, -5.0 + z * 2.05));
+ entities.back()->create(nf::BaseAssets::cube, nf::Vec3(5.0 + x * 2.05, 1.0 + y * 2.05, -5.0 + z * 2.05), nf::Entity::Type::DYNAMIC);
}
}
}
@@ -51,7 +48,8 @@ void MainState::onEnter() {
grav = 2.0f;
setGravity(grav);
- amb = 0.5f;
+ amb = 0.1f;
+ setAmbientLight(amb);
camera->setPosition(-20.0, 7.0, 0.0);
camera->setRotation(85.0, -30.0);
@@ -97,8 +95,7 @@ void MainState::update(float deltaTime) {
if (camera->getType() == nf::Camera::Type::FIRST_PERSON && (app->isMouseClicked(NFI_LEFTMOUSE) || app->isMouseHeld(NFI_RIGHTMOUSE))) {
entities.push_back(new nf::Entity);
- entities.back()->create(nf::BaseAssets::sphere, nf::Entity::Type::DYNAMIC);
- entities.back()->setPosition(camera->getPosition() + camera->getRotation() * 5.0);
+ entities.back()->create(nf::BaseAssets::sphere, camera->getPosition() + camera->getRotation() * 5.0, nf::Entity::Type::DYNAMIC);
entities.back()->setVelocity(camera->getRotation() * 100.0f);
entities.back()->setMass(1000.0f);
}
diff --git a/NothinFancy/NothinFancy.vcxproj b/NothinFancy/NothinFancy.vcxproj
index c7d313b..c0d0ece 100644
--- a/NothinFancy/NothinFancy.vcxproj
+++ b/NothinFancy/NothinFancy.vcxproj
@@ -62,6 +62,7 @@
$(ProjectDir)src\include\;$(ProjectDir)dep\include\
$(IntDir)obj\
true
+ true
Console
@@ -94,6 +95,7 @@
$(ProjectDir)src\include\;$(ProjectDir)dep\include\
$(IntDir)obj\
true
+ true
Console
diff --git a/NothinFancy/src/Application.cpp b/NothinFancy/src/Application.cpp
index df83f48..f9342fd 100644
--- a/NothinFancy/src/Application.cpp
+++ b/NothinFancy/src/Application.cpp
@@ -16,8 +16,8 @@ namespace nf {
m_stateChange(false),
m_stateChangeStarted(false)
{
- NFLog("Creating NF application");
- NFLog("Width: " + std::to_string(m_currentConfig.width) + ", Height: " + std::to_string(m_currentConfig.height) + ", Fullscreen: " + std::to_string(m_currentConfig.fullscreen) + ", Title: " + m_currentConfig.title);
+ NFLog("Constructing application");
+ NFLog("Width: " + std::to_string(m_currentConfig.width) + ", Height: " + std::to_string(m_currentConfig.height) + ", Fullscreen: " + (m_currentConfig.fullscreen ? "true" : "false") + ", Title: " + m_currentConfig.title);
if (getApp(true) != nullptr)
NFError("Cannot create two NF Application objects!");
@@ -78,7 +78,9 @@ namespace nf {
}
void Application::run() {
+ NFLog("Running application...");
#ifdef _DEBUG
+ Debug::startTimer();
SetThreadDescription(GetCurrentThread(), L"Input Thread");
#endif
if (m_defaultState.empty())
@@ -111,6 +113,9 @@ namespace nf {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
mainThread.join();
+#ifdef _DEBUG
+ Debug::stopTimer();
+#endif
}
bool Application::hasCustomWindowIcon() {
@@ -118,10 +123,12 @@ namespace nf {
}
void Application::changeState(const std::string& stateName) {
+ if (m_stateChange) return;
if (m_states.find(stateName) == m_states.end())
NFError("State \"" + (std::string)stateName + (std::string)"\" doesn't exist!");
m_stateChange = true;
m_nextState = m_states[stateName];
+ NFLog("Changing to state \"" + stateName + (std::string)"\"");
}
Gamestate* Application::getCurrentState() {
@@ -300,7 +307,6 @@ namespace nf {
void Application::quit() {
m_quit = true;
- NFLog("Exiting NF application");
}
void Application::runMainGameThread() {
@@ -343,12 +349,11 @@ namespace nf {
}
}
}
- m_audio->stopAllSounds();
m_currentState->stop();
- delete sIntro;
delete m_physics;
delete m_audio;
delete m_renderer;
+ delete sIntro;
}
void Application::doStateChange() {
@@ -445,6 +450,7 @@ namespace nf {
break;
}
case WM_CLOSE: {
+ NFLog("Exiting NF application");
DestroyWindow(hWnd);
return 0;
}
@@ -456,12 +462,5 @@ namespace nf {
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
- Application::~Application() {
- for (std::pair state : m_states) {
- Gamestate* curr = state.second;
- delete curr;
- }
- }
-
Application* Application::currentApp = nullptr;
}
\ No newline at end of file
diff --git a/NothinFancy/src/Assets.cpp b/NothinFancy/src/Assets.cpp
index eb37763..21175c3 100644
--- a/NothinFancy/src/Assets.cpp
+++ b/NothinFancy/src/Assets.cpp
@@ -59,7 +59,6 @@ namespace nf {
unsigned int cubemapCount = 0;
unsigned int buttonCount = 0;
while (packContents.size()) {
- size_t startingPos = packContents.find_first_of("#NFASSET ") + 9;
packContents = packContents.substr(9);
size_t endAssetNamePos = packContents.find_first_of('\n');
std::string assetName = packContents.substr(0, endAssetNamePos);
@@ -206,7 +205,6 @@ namespace nf {
NFError("Could not find full button set in pack \"" + (std::string)packName + (std::string)"\"!");
while (packContentsOBJ.size()) {
- size_t startingPos = packContentsOBJ.find_first_of("#NFASSET ") + 9;
packContentsOBJ = packContentsOBJ.substr(9);
size_t endAssetNamePos = packContentsOBJ.find_first_of('\n');
std::string assetName = packContentsOBJ.substr(0, endAssetNamePos);
@@ -223,7 +221,6 @@ namespace nf {
assetContents = packContentsOBJ;
packContentsOBJ = "";
}
- size_t assetSize = assetContents.size();
if (extension == "obj") {
AModel* model = new AModel;
@@ -236,6 +233,7 @@ namespace nf {
}
}
assetContents = assetContents.substr(assetContents.find("\n") + 1);
+ size_t assetSize = assetContents.size();
model->data = new char[assetSize];
std::memcpy(model->data, &assetContents[0], assetSize);
model->size = assetSize;
diff --git a/NothinFancy/src/AudioEngine.cpp b/NothinFancy/src/AudioEngine.cpp
index 1fae0cf..0444040 100644
--- a/NothinFancy/src/AudioEngine.cpp
+++ b/NothinFancy/src/AudioEngine.cpp
@@ -17,7 +17,7 @@ namespace nf {
NFError("Could not initialize COM!");
hr = XAudio2Create(&m_engine);
if (FAILED(hr))
- NFError("Could not initialize the audio engine!");
+ NFError("Could not initialize audio engine!");
#ifdef _DEBUG
XAUDIO2_DEBUG_CONFIGURATION debug = { 0 };
debug.TraceMask = XAUDIO2_LOG_ERRORS | XAUDIO2_LOG_WARNINGS;
@@ -25,12 +25,16 @@ namespace nf {
m_engine->SetDebugConfiguration(&debug, 0);
#endif
hr = m_engine->CreateMasteringVoice(&m_masterVoice);
- if (hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND))
+ if (hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND)) {
m_isActive = false;
- else if (SUCCEEDED(hr))
+ NFLog("Audio engine not initialized since no audio devices found");
+ }
+ else if (SUCCEEDED(hr)) {
m_isActive = true;
+ NFLog("Initialized audio engine");
+ }
else
- NFError("Could not initialize the audio engine!");
+ NFError("Could not initialize audio engine!");
m_threadRunning = true;
m_thread = std::thread(&AudioEngine::runAudioThread, this);
}
@@ -42,10 +46,11 @@ namespace nf {
return false;
else if (hr == S_OK) {
m_isActive = true;
+ NFLog("Initialized audio engine");
return true;
}
else {
- NFError("Could not initialize audio!");
+ NFError("Could not initialize audio engine!");
return false;
}
}
@@ -62,6 +67,9 @@ namespace nf {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
+ if (!m_isActive)
+ return;
+
DWORD cm;
m_masterVoice->GetChannelMask(&cm);
X3DAUDIO_HANDLE x3d;
@@ -192,6 +200,7 @@ namespace nf {
}
AudioEngine::~AudioEngine() {
+ stopAllSounds();
m_threadRunning = false;
m_thread.join();
m_engine->Release();
diff --git a/NothinFancy/src/Gamestate.cpp b/NothinFancy/src/Gamestate.cpp
index 27d65af..579c783 100644
--- a/NothinFancy/src/Gamestate.cpp
+++ b/NothinFancy/src/Gamestate.cpp
@@ -24,6 +24,7 @@ namespace nf {
if (physics)
app->getPhysicsEngine()->newScene();
+ NFTimerLoad;
onEnter();
m_loading = false;
diff --git a/NothinFancy/src/NFObject/Entity.cpp b/NothinFancy/src/NFObject/Entity.cpp
index 93e90ad..b4b5f8c 100644
--- a/NothinFancy/src/NFObject/Entity.cpp
+++ b/NothinFancy/src/NFObject/Entity.cpp
@@ -22,10 +22,11 @@ namespace nf {
m_member = true;
}
- void Entity::create(Asset* modelAsset, Type type) {
+ void Entity::create(Asset* modelAsset, const Vec3& position, Type type) {
if (m_constructed)
NFError("Entity already created!");
m_constructed = true;
+ setPosition(position);
m_type = type;
AModel* model;
if ((model = dynamic_cast(modelAsset)) == nullptr)
diff --git a/NothinFancy/src/NFObject/Sound.cpp b/NothinFancy/src/NFObject/Sound.cpp
index 661eaaa..ef048cd 100644
--- a/NothinFancy/src/NFObject/Sound.cpp
+++ b/NothinFancy/src/NFObject/Sound.cpp
@@ -140,7 +140,7 @@ namespace nf {
callbacks.close_func = v_close;
callbacks.tell_func = v_tell;
- int open = ov_open_callbacks(&memFile, &file, nullptr, 0, callbacks);
+ ov_open_callbacks(&memFile, &file, nullptr, 0, callbacks);
char* buff = new char[65536 * 1000];
int stream = 0;
@@ -164,7 +164,6 @@ namespace nf {
}
size_t Sound::loadWAV(std::string& data) {
- unsigned int fileSize = *(unsigned int*)&data[4];
size_t fmtPos;
if ((fmtPos = data.find("fmt")) == std::string::npos)
NFError("WAV not of correct format!");
diff --git a/NothinFancy/src/PhysicsEngine.cpp b/NothinFancy/src/PhysicsEngine.cpp
index 9a5d31b..af0be3d 100644
--- a/NothinFancy/src/PhysicsEngine.cpp
+++ b/NothinFancy/src/PhysicsEngine.cpp
@@ -30,6 +30,7 @@ namespace nf {
m_stepSize(1.0f / 60.0f),
m_accumulator(0.0)
{
+ NFLog("Initializing physics engine...");
m_err = new PhysicsErrorCallback;
m_foundation = PxCreateFoundation(PX_PHYSICS_VERSION, m_alloc, *m_err);
if (!m_foundation)
@@ -54,6 +55,8 @@ namespace nf {
m_dispacher = PxDefaultCpuDispatcherCreate(threads);
m_defaultMat = m_phy->createMaterial(0.5f, 0.5f, 0.0f);
+
+ NFLog("Initialized physics engine");
}
void PhysicsEngine::newScene() {
diff --git a/NothinFancy/src/Renderer/Renderer.cpp b/NothinFancy/src/Renderer/Renderer.cpp
index 28c79e4..9b6bc88 100644
--- a/NothinFancy/src/Renderer/Renderer.cpp
+++ b/NothinFancy/src/Renderer/Renderer.cpp
@@ -34,6 +34,7 @@ namespace nf {
m_fadeText(true),
m_fadeOutComplete(false)
{
+ NFLog("Initializing renderer...");
m_hdc = GetDC(m_app->getWindow());
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),
@@ -123,6 +124,8 @@ namespace nf {
m_quadVAO->finishBufferLayout();
m_quadIB = new IndexBuffer(quadIB, 6);
m_loadingText.create("NFLoadingText", Vec2(0.025f, 0.044f), Vec3(0.7f));
+
+ NFLog("Initialized renderer");
}
void Renderer::setFade(bool in, bool out, bool text) {
@@ -165,59 +168,57 @@ namespace nf {
glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)m_app->getConfig().width / (float)m_app->getConfig().height, 0.1f, 1000.0f);
camera->update(m_gBufferShader, m_lightingShader, m_cubemapShader);
- //First, fill the gBuffer with entities
- m_gBufferShader->setUniform("proj", proj);
- m_gBuffer->render(m_lGame, m_gBufferShader);
+ if (m_lGame.size()) {
+ //First, fill the gBuffer with entities
+ m_gBufferShader->setUniform("proj", proj);
+ m_gBuffer->render(m_lGame, m_gBufferShader);
- //Light entities using the gBuffer
- size_t lightsRemaining = m_lights.size();
- if (!lightsRemaining) {
- m_quadVAO->bind();
- m_quadIB->bind();
- m_lightingShader->bind();
- m_gBuffer->bindTextures(m_lightingShader);
- glDrawElements(GL_TRIANGLES, m_quadIB->getCount(), GL_UNSIGNED_INT, nullptr);
+ //Light entities using the gBuffer
+ size_t lightsRemaining = m_lights.size();
+ unsigned int drawCount = 0;
+ do {
+ size_t currLightsDrawn;
+ if (lightsRemaining > m_texSlots)
+ currLightsDrawn = m_texSlots;
+ else
+ currLightsDrawn = lightsRemaining;
+ lightsRemaining -= currLightsDrawn;
+ m_lightingShader->setUniform("numberOfLights", (int)currLightsDrawn);
+ if (drawCount == 0)
+ m_lightingShader->setUniform("isContinued", false);
+ else {
+ m_lightingShader->setUniform("isContinued", true);
+ glBlendFunc(GL_ONE, GL_ONE);
+ glDepthFunc(GL_LEQUAL);
+ }
+ for (unsigned int i = 0; i < currLightsDrawn; i++)
+ m_lights[i]->bind(m_lightingShader, i);
+ renderShadowMaps(currLightsDrawn);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glViewport(0, 0, m_app->getConfig().width, m_app->getConfig().height);
+ m_quadVAO->bind();
+ m_quadIB->bind();
+ m_lightingShader->bind();
+ m_gBuffer->bindTextures(m_lightingShader);
+#ifdef _DEBUG
+ m_lightingShader->validate();
+#endif
+ glDrawElements(GL_TRIANGLES, m_quadIB->getCount(), GL_UNSIGNED_INT, nullptr);
+ m_lights.erase(m_lights.begin(), m_lights.begin() + currLightsDrawn);
+ drawCount++;
+ } while (lightsRemaining > 0);
+ m_lGame.clear();
+ m_lights.clear();
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDepthFunc(GL_LESS);
}
- unsigned int drawCount = 0;
- while (lightsRemaining > 0) {
- size_t currLightsDrawn;
- if (lightsRemaining > m_texSlots)
- currLightsDrawn = m_texSlots;
- else
- currLightsDrawn = lightsRemaining;
- lightsRemaining -= currLightsDrawn;
- m_lightingShader->setUniform("numberOfLights", (int)currLightsDrawn);
- if(drawCount == 0)
- m_lightingShader->setUniform("isContinued", false);
- else {
- m_lightingShader->setUniform("isContinued", true);
- glBlendFunc(GL_ONE, GL_ONE);
- glDepthFunc(GL_LEQUAL);
- }
- for (unsigned int i = 0; i < currLightsDrawn; i++)
- m_lights[i]->bind(m_lightingShader, i);
- renderShadowMaps(currLightsDrawn);
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- glViewport(0, 0, m_app->getConfig().width, m_app->getConfig().height);
- m_quadVAO->bind();
- m_quadIB->bind();
- m_lightingShader->bind();
- m_gBuffer->bindTextures(m_lightingShader);
- glDrawElements(GL_TRIANGLES, m_quadIB->getCount(), GL_UNSIGNED_INT, nullptr);
- m_lights.erase(m_lights.begin(), m_lights.begin() + currLightsDrawn);
- drawCount++;
- }
- m_lGame.clear();
- m_lights.clear();
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glDepthFunc(GL_LESS);
//Draw the cubemap if one is currently set
if (m_cubemap != nullptr) {
m_cubemapShader->setUniform("proj", proj);
m_cubemap->render(m_cubemapShader);
+ m_cubemap = nullptr;
}
- m_cubemap = nullptr;
//Draw UI elements
glDisable(GL_DEPTH_TEST);
@@ -297,6 +298,7 @@ namespace nf {
}
void Renderer::loadBaseAssets() {
+ NFLog("Loading base assets...");
m_baseAP.load("base.nfpack");
const char* gBufferVertex = m_baseAP.get("gBufferVertex.shader")->data;
const char* gBufferFragment = m_baseAP.get("gBufferFragment.shader")->data;
@@ -334,6 +336,8 @@ namespace nf {
BaseAssets::cubemap = (ACubemap*)m_baseAP.get("default.cm");
BaseAssets::font = (AFont*)m_baseAP.get("default.ttf");
BaseAssets::button = (AButton*)m_baseAP.get("default.button");
+
+ NFLog("Base assets loaded");
}
void Renderer::createShadowMaps() {
@@ -371,7 +375,6 @@ namespace nf {
glm::mat4 lightView;
glm::mat4 lightSpaceMat;
bool directionalRendered = false;
- unsigned int directionalSlot = 0; //TODO: Test this
glBindFramebuffer(GL_FRAMEBUFFER, m_shadowMapFBO);
for (unsigned int i = 0; i < count; i++) {
Light::Type type = m_lights[i]->getType();
diff --git a/NothinFancy/src/Utility.cpp b/NothinFancy/src/Utility.cpp
index aa8a7f9..bb121a0 100644
--- a/NothinFancy/src/Utility.cpp
+++ b/NothinFancy/src/Utility.cpp
@@ -19,47 +19,79 @@ namespace nf {
#ifdef _DEBUG
NFDEBUGINIT;
+ void Debug::startTimer() {
+ m_timerStarted = true;
+ m_initTime = std::chrono::steady_clock::now();
+ }
+
+ void Debug::stopTimer() {
+ m_timerStarted = false;
+ }
+
void Debug::LogImp(const char* in) {
- std::chrono::duration time = getCurrentTime();
- std::printf("[%.4f] NF Log: %s\n", time.count(), in);
+ if(m_timerStarted)
+ printCurrentTime();
+ std::printf("NF Log: %s\n", in);
}
void Debug::LogImp(const std::string& in) {
- std::chrono::duration time = getCurrentTime();
- std::printf("[%.4f] NF Log: ", time.count());
+ if (m_timerStarted)
+ printCurrentTime();
+ std::printf("NF Log: ");
std::cout << in << "\n";
}
void Debug::LogImp(int in) {
- std::chrono::duration time = getCurrentTime();
- std::printf("[%.4f] NF Log: %i\n", time.count(), in);
+ if (m_timerStarted)
+ printCurrentTime();
+ std::printf("NF Log: %i\n", in);
}
void Debug::LogImp(float in) {
- std::chrono::duration time = getCurrentTime();
- std::printf("[%.4f] NF Log: %.4f\n", time.count(), in);
+ if (m_timerStarted)
+ printCurrentTime();
+ std::printf("NF Log: %.4f\n", in);
}
//TODO: Test every Error in release mode
void Debug::ErrorImp(const char* in, const char* filename, int line) {
- std::chrono::duration time = getCurrentTime();
+ if (m_timerStarted)
+ printCurrentTime();
static HANDLE cmd = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(cmd, FOREGROUND_RED);
- std::printf("[%.4f] NF Error (%s, %i): %s\n", time.count(), filename, line, in);
+ std::printf("NF Error (%s, %i): %s\n", filename, line, in);
SetConsoleTextAttribute(cmd, 7);
}
void Debug::ErrorImp(const std::string& in, const char* filename, int line) {
- std::chrono::duration time = getCurrentTime();
+ if (m_timerStarted)
+ printCurrentTime();
static HANDLE cmd = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(cmd, FOREGROUND_RED);
- std::printf("[%.4f] NF Error (%s, %i): ", time.count(), filename, line);
+ std::printf("NF Error (%s, %i): ", filename, line);
std::cout << in << "\n";
SetConsoleTextAttribute(cmd, 7);
}
- std::chrono::duration Debug::getCurrentTime() {
+ void Debug::printCurrentTime() {
std::chrono::steady_clock::time_point now = std::chrono::high_resolution_clock::now();
- return now - m_initTime;
+ std::chrono::duration dur = now - m_initTime;
+ std::printf("[%.4f] ", dur.count());
+ }
+
+ Timer::Timer(const std::string& function, bool onEnter) {
+ m_funcName = function;
+ m_initTime = std::chrono::steady_clock::now();
+ m_loading = onEnter;
+ if (!m_loading)
+ NFLog("Started timing \"" + m_funcName + (std::string)"\"");
+ }
+
+ Timer::~Timer() {
+ std::chrono::duration dur = std::chrono::steady_clock::now() - m_initTime;
+ if (!m_loading)
+ NFLog("\"" + m_funcName + (std::string)"\" took " + std::to_string(dur.count()) + (std::string)" seconds.");
+ else
+ NFLog("Loading took " + std::to_string(dur.count()) + (std::string)" seconds.");
}
#endif
diff --git a/NothinFancy/src/include/NothinFancy.h b/NothinFancy/src/include/NothinFancy.h
index 2759301..73cb3a2 100644
--- a/NothinFancy/src/include/NothinFancy.h
+++ b/NothinFancy/src/include/NothinFancy.h
@@ -1,11 +1,8 @@
+//This is the main header for the entire engine.
#pragma once
-//TODO: Rework this file to only contain functions the frontend will need to access
-//Maybe a implementation define here?
-
-//TODO: Prevent including other headers other than this one
#ifndef NFIMPL
-#define NFIMPL 1
+#define NFIMPL
#endif
#include "nf/Config.h"
diff --git a/NothinFancy/src/include/nf/Application.h b/NothinFancy/src/include/nf/Application.h
index 95372e0..4685552 100644
--- a/NothinFancy/src/include/nf/Application.h
+++ b/NothinFancy/src/include/nf/Application.h
@@ -177,7 +177,6 @@ namespace nf {
void getMouseDiff(int& x, int& y);
static Application* getApp(bool first = false);
#endif
- ~Application();
private:
void registerWindowClass();
RECT getWindowRect() const;
diff --git a/NothinFancy/src/include/nf/Entity.h b/NothinFancy/src/include/nf/Entity.h
index db2edb4..5e50894 100644
--- a/NothinFancy/src/include/nf/Entity.h
+++ b/NothinFancy/src/include/nf/Entity.h
@@ -53,6 +53,7 @@ namespace nf {
/**
* @brief Creates an entity
* @param modelAsset A model Asset pointer
+ * @param position Initial position vector
* @param type Type of entity; Defaults to Type::STATIC
*
* This function will initialize an entity by loading its associated model from
@@ -61,7 +62,7 @@ namespace nf {
* @warning Calling this function twice before the state exits will result in an
* error. See @ref isConstructed.
*/
- void create(Asset* modelAsset, Type type = Type::STATIC);
+ void create(Asset* modelAsset, const Vec3& position, Type type = Type::STATIC);
/**
* @brief Queries whether or not the entity has been created
* @return If the entity has been created
diff --git a/NothinFancy/src/include/nf/Gamestate.h b/NothinFancy/src/include/nf/Gamestate.h
index 77acefe..35f02db 100644
--- a/NothinFancy/src/include/nf/Gamestate.h
+++ b/NothinFancy/src/include/nf/Gamestate.h
@@ -14,10 +14,10 @@ namespace nf {
class Texture;
/**
- * @brief A state NF can be in that includes a collection of objects and user-defined
+ * @brief An engine state that includes a world, objects, and user-defined
* behavior
*
- * Every user-defined state inherits from this class.
+ * Every state inherits from this class.
*/
class Gamestate {
public:
@@ -37,7 +37,7 @@ namespace nf {
* @brief Update function
* @param deltaTime Amount of time the previous frame took to produce in seconds
*
- * This function is called every frame. It is called before render.
+ * This function is called every frame. It is called before @ref render.
*
* The deltaTime parameter's purpose is to create non-frame-dependant gameplay. This
* number should be multiplied with user numbers likes velocities. Doing this will
diff --git a/NothinFancy/src/include/nf/Utility.h b/NothinFancy/src/include/nf/Utility.h
index af9a7e3..d54f743 100644
--- a/NothinFancy/src/include/nf/Utility.h
+++ b/NothinFancy/src/include/nf/Utility.h
@@ -7,7 +7,7 @@
/**
* @brief Nothin' Fancy namespace
*
- * Every class and struct lives inside of this namespace
+ * Every class and struct lives inside of this namespace.
*
* It could be useful to `using` this namespace:
*
@@ -20,7 +20,15 @@ namespace nf {
//Strips __FILE__ down to only the name of the file
#define __FILENAME__ strrchr(__FILE__, '\\') + 1
//Initializes static variables needed for debugging
-#define NFDEBUGINIT std::chrono::steady_clock::time_point Debug::m_initTime = std::chrono::high_resolution_clock::now();
+#define NFDEBUGINIT std::chrono::steady_clock::time_point Debug::m_initTime = std::chrono::high_resolution_clock::now(); \
+bool Debug::m_timerStarted = false
+/**
+* @defgroup macros Macros
+*
+* Macros to aid in debugging and developing with NF
+*
+* @{
+*/
/**
* Pauses the engine for a set amount of seconds
*/
@@ -43,11 +51,33 @@ namespace nf {
*/
#define NFError(x) {nf::Debug::ErrorImp(x,__FILENAME__, __LINE__);\
__debugbreak();}
- /**
- * @brief Handles NFLog and NFError calls
- */
+/**
+* A timer useful for timing functions
+*
+* To time a function, place this macro at the beginning of it:
+*
+* ~~~
+* void myFunc() {
+* NFTimer;
+* //Rest of function to be timed...
+* } //Prints here
+* ~~~
+*
+* The result will be logged when the scope it's declared in ends.
+*/
+#define NFTimer nf::Timer _nfTimer(__func__)
+/**
+* @}
+*/
+#ifndef NFIMPL
+#define NFTimerLoad nf::Timer _nfTimer(__func__, true);
+#endif
+
class Debug {
public:
+ static void startTimer();
+ static void stopTimer();
+
static void LogImp(const char* in);
static void LogImp(const std::string& in);
static void LogImp(int in);
@@ -57,8 +87,20 @@ __debugbreak();}
[[noreturn]]
static void ErrorImp(const std::string& in, const char* filename, int line);
private:
+ static void printCurrentTime();
static std::chrono::steady_clock::time_point m_initTime;
- static std::chrono::duration getCurrentTime();
+ static bool m_timerStarted;
+ };
+
+ class Timer {
+ public:
+ Timer(const std::string& function, bool onEnter = false);
+
+ ~Timer();
+ private:
+ std::chrono::steady_clock::time_point m_initTime;
+ bool m_loading;
+ std::string m_funcName;
};
#else
#define NFDEBUGINIT
@@ -67,6 +109,8 @@ __debugbreak();}
#define NFLog(x)
#define NFError(x) {MessageBox(FindWindow(L"NFClass", NULL), toWide(x).data(), L"NF Engine Error", MB_OK | MB_ICONERROR);\
std::exit(-1);}
+#define NFTimer
+#define NFTimerLoad
#endif
/**
@@ -364,10 +408,11 @@ std::exit(-1);}
float w;
};
+#ifndef NFIMPL
const std::wstring toWide(const char* in);
const std::wstring toWide(const std::string& in);
-
Vec4 degToQuat(const Vec3& in);
+#endif
/**
* @brief Writes a stream of bytes as as std::string into a file in a specified location
diff --git a/docs/Doxyfile b/docs/Doxyfile
index 2de5e61..8a15dc7 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -169,7 +169,7 @@ HTML_HEADER = header.html
HTML_FOOTER = footer.html
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET = theme.css
-HTML_EXTRA_FILES = favicon.png
+HTML_EXTRA_FILES = images/favicon.png
HTML_COLORSTYLE_HUE = 30
HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
@@ -199,7 +199,7 @@ QHP_SECT_FILTER_ATTRS =
QHG_LOCATION =
GENERATE_ECLIPSEHELP = NO
ECLIPSE_DOC_ID = org.doxygen.Project
-DISABLE_INDEX = NO
+DISABLE_INDEX = YES
GENERATE_TREEVIEW = YES
FULL_SIDEBAR = NO
ENUM_VALUES_PER_LINE = 4
diff --git a/docs/images/applifetime.png b/docs/images/applifetime.png
new file mode 100644
index 0000000..a405b23
Binary files /dev/null and b/docs/images/applifetime.png differ
diff --git a/docs/images/blankapp.png b/docs/images/blankapp.png
new file mode 100644
index 0000000..2a63b51
Binary files /dev/null and b/docs/images/blankapp.png differ
diff --git a/docs/favicon.png b/docs/images/favicon.png
similarity index 100%
rename from docs/favicon.png
rename to docs/images/favicon.png
diff --git a/docs/layout.xml b/docs/layout.xml
index 6d2b77b..8566064 100644
--- a/docs/layout.xml
+++ b/docs/layout.xml
@@ -4,11 +4,12 @@
-
+
-
-
+
+
+
@@ -35,7 +36,7 @@
-
+
diff --git a/docs/pages/2_tutorial.md b/docs/pages/2_tutorial.md
index 7a377ad..09510db 100644
--- a/docs/pages/2_tutorial.md
+++ b/docs/pages/2_tutorial.md
@@ -6,13 +6,127 @@ This tutorial aims to teach the basics of the engine and how to use it.
First, follow the steps on the @ref install page. Once the template MSVC project is setup,
you can begin here.
-@section nfArch NF Engine Architecture
+@section nfArch Engine Architecture
+An NF app is made up of a set of states represented by the nf::Gamestate class. When
+an nf::Application is running, it is either running a state or loading one. Below is an
+image that describes what the main thread of an NF app would be typically doing in
+the program's lifetime.
+@image html applifetime.png "The lifetime of a typical NF app" width=20%
+
+Using the engine's architecture, you might not even write any functions that are called
+from `main`. Most of the code that programs the engine's behavior should be called in
+your state's [update function](@ref nf::Gamestate::update).
+
+To allow a translate unit to use the engine, you must include `NothinFancy.h`. This
+header contains every class and function.
@section createConfig Creating a Config
-@todo The tutorial page
+The first step to creating an app is creating an nf::Config. A config describes how the
+engine should display on the screen. nf::Config has these fields:
+
+- `width` - The width of the window if `fullscreen` is set to `false`
+- `height` - The height of the window if `fullscreen` is set to `false`
+- `fullscreen` - `true` sets the display to the size of the monitor the app is
+- `title` - The title of the window shown on the caption bar and taskbar
+opened on
+
+To create a 1280 by 720 window with a title of "NF Example", you would write:
+
+~~~cpp
+nf::Config conf;
+conf.width = 1280;
+conf.height = 720;
+conf.fullscreen = false;
+conf.title = "NF Example";
+
+//Or...
+
+nf::Config conf2{ 1280, 720, false, "NF Example" };
+~~~
+
+We then pass this config to an nf::Application
+
+@section createApp Creating and Configuring an Application
+
+The nf::Application class represents an instance of the engine. This is the point where
+you will attach your states and run the engine.
+
+@note In a program and on a single machine, there can only be one single instance of
+this class at a time. Attempting to create mulitple will result in an error.
+
+The constructor takes in the nf::Config we previously created:
+
+~~~cpp
+nf::Application app(conf);
+~~~
+
+Constructing an application doesn't do much. It merely allows you to access the member
+functions to setup your application.
+
+Here are some functions you might want to call at this point:
+
+- [setWindowIcon](@ref nf::Application::setWindowIcon) - Sets the icon of the window
+- [setWindowCursor](@ref nf::Application::setWindowCursor) - Sets the cursor's image
+when it is visible and inside the window
+
+And here are the functions you **must** call before an app can run:
+
+- [addState](@ref nf::Application::addState) - Adds a state to an app so that it can
+access it later by a user-defined string identifier
+- [setDefaultState](@ref nf::Application::setDefaultState) - Sets the state to load
+after the logo state exits
+
+Once these functions have been called, the app can be run:
+
+~~~cpp
+CustomGamestate* customState = new CustomGamestate; //Inherits nf::Gamestate
+app.addState(customState, "State 1"); //"State One" is this state's identifier.
+app.setDefaultState("State 1"); //Will error without this
+app.run(); //Blocks until exit
+~~~
+
+@section createGamestate Creating a Gamestate
+
+To create a gamestate, you must create a class that publicly inherits nf::Gamestate
+and overrides its four virtual functions:
+
+- [onEnter](@ref nf::Gamestate::onEnter) - Called when the state is loading; Where member
+objects are initialized
+- [update](@ref nf::Gamestate::update) - Called every frame after loading is complete;
+Where custom behavior is defined
+- [render](@ref nf::Gamestate::render) - Called after update; Selects what to render
+- [onExit](@ref nf::Gamestate::onExit) - Called when the state is unloaded
+
+A gamestate class might look something like this:
+
+~~~cpp
+class CustomGamestate : public nf::Gamestate {
+public:
+ void onEnter() override;
+ void update(float deltaTime) override; //Note the parameter
+ void render(nf::Renderer& renderer) override;
+ void onExit() override;
+
+ //Implementations somewhere else...
+
+private:
+ //Member objects here...
+};
+~~~
+
+Once an app has been setup and run with an empty state, you should be met with a black screen
+after the logo state:
+
+@image html blankapp.png "A blank gamestate" width=50%
+
+Congratulations! You now have a basic NF app running. Now we can add objects to our world.
+
+@section createEntities Adding 3D Objects
+
+
@section createUI Creating a UI
@@ -20,4 +134,4 @@ you can begin here.
@todo Lighting page?
-@section packaging Packaging Your Game
\ No newline at end of file
+@section packaging Packaging Your App
\ No newline at end of file
diff --git a/docs/theme.css b/docs/theme.css
index 35e87cd..59fbbfb 100644
--- a/docs/theme.css
+++ b/docs/theme.css
@@ -314,7 +314,7 @@ pre.fragment {
}
div.fragment {
- padding: 0 0 1px 0; /*Fixed: last line underline overlap border*/
+ padding: 5px 0 5px 5px; /*Fixed: last line underline overlap border*/
margin: 4px 8px 4px 2px;
background-color: #FDFCFB;
border: 1px solid #E5D5C4;
@@ -322,9 +322,9 @@ div.fragment {
div.line {
font-family: monospace, fixed;
- font-size: 13px;
+ font-size: 15px;
min-height: 13px;
- line-height: 1.0;
+ line-height: 1.1;
text-wrap: unrestricted;
white-space: -moz-pre-wrap; /* Moz */
white-space: -pre-wrap; /* Opera 4-6 */