Improved logger, added a debug timer, other small stuff, and wrote more user guide
This commit is contained in:
parent
f66c3fb4e0
commit
21764d3f98
@ -61,6 +61,7 @@
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)src\include\;$(SolutionDir)NothinFancy\src\include\</AdditionalIncludeDirectories>
|
||||
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@ -90,6 +91,7 @@ if exist "base.nfpack" (copy "base.nfpack" "$(OutDir)assets\")</Command>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)src\include\;$(SolutionDir)NothinFancy\src\include\</AdditionalIncludeDirectories>
|
||||
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
@ -16,6 +16,8 @@ int main(int argc, char* argv[]) {
|
||||
app.setDefaultState("Main State");
|
||||
|
||||
app.run();
|
||||
|
||||
delete test;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -62,6 +62,7 @@
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)src\include\;$(ProjectDir)dep\include\</AdditionalIncludeDirectories>
|
||||
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@ -94,6 +95,7 @@
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)src\include\;$(ProjectDir)dep\include\</AdditionalIncludeDirectories>
|
||||
<ObjectFileName>$(IntDir)obj\</ObjectFileName>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
|
@ -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<std::string, Gamestate*> state : m_states) {
|
||||
Gamestate* curr = state.second;
|
||||
delete curr;
|
||||
}
|
||||
}
|
||||
|
||||
Application* Application::currentApp = nullptr;
|
||||
}
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -24,6 +24,7 @@ namespace nf {
|
||||
|
||||
if (physics)
|
||||
app->getPhysicsEngine()->newScene();
|
||||
NFTimerLoad;
|
||||
onEnter();
|
||||
|
||||
m_loading = false;
|
||||
|
@ -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<AModel*>(modelAsset)) == nullptr)
|
||||
|
@ -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!");
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
@ -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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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<float> 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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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<float> 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
|
||||
|
@ -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
|
||||
|
BIN
docs/images/applifetime.png
Normal file
BIN
docs/images/applifetime.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
BIN
docs/images/blankapp.png
Normal file
BIN
docs/images/blankapp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@ -4,11 +4,12 @@
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="yes" title=""/>
|
||||
<tab type="pages" visible="yes" title="" intro=""/>
|
||||
<tab type="modules" visible="yes" title="" intro=""/>
|
||||
<tab type="modules" visible="no" title="" intro=""/>
|
||||
<tab type="namespaces" visible="no" title="API">
|
||||
<tab type="namespacelist" visible="yes" title="API" intro="Here, you can find a list of every useful class to use in NF."/>
|
||||
<tab type="namespacemembers" visible="no" title="" intro=""/>
|
||||
<tab type="namespacelist" visible="yes" title="API" intro="Here, you can find a list of every useful class to use in NF."/>
|
||||
<tab type="namespacemembers" visible="no" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="user" url="@ref macros" title="Macros" intro=""/>
|
||||
<tab type="concepts" visible="yes" title="">
|
||||
</tab>
|
||||
<tab type="interfaces" visible="yes" title="">
|
||||
@ -35,7 +36,7 @@
|
||||
<tab type="filelist" visible="no" title="" intro=""/>
|
||||
<tab type="globals" visible="no" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="examples" visible="yes" title="" intro=""/>
|
||||
<tab type="examples" visible="yes" title="Snippets" intro="Here, you can find a list of snippets that showcase different features of the engine."/>
|
||||
</navindex>
|
||||
|
||||
<!-- Layout definition for a class page -->
|
||||
|
@ -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
|
||||
@section packaging Packaging Your App
|
@ -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 */
|
||||
|
Reference in New Issue
Block a user