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 */