Merge branch 'develop' into sezz/attractors

This commit is contained in:
Sezz 2025-02-13 00:32:29 +11:00
commit 395345b112
15 changed files with 341 additions and 67 deletions

View file

@ -21,13 +21,14 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
* Fixed custom shatter sounds with custom sound IDs not playing correctly.
### New Features
* Added diary module.
* Added multithreading and an option for it to flow system settings.
* Added ability to use keys and puzzle items underwater.
- You must update your Lara object: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Lara/TEN_Lara.wad2
* Added a particle based waterfall emitter object and associated sprite slots.
- You must use this version: https://github.com/TombEngine/Resources/raw/refs/heads/main/Wad2%20Objects/Interactables/TEN_Waterfall_Emitter.wad2
### Lua API changes
* Added diary module.
* Added Effects.EmitAirBubble() function to spawn air bubbles.
* Added additional arguments for Sprite object slot and starting rotation value for EmitParticle function.
* Added various Translate() methods to Vec2 and Vec3 script objects.

View file

@ -305,6 +305,10 @@
<td class="summary">How should the application respond to script errors?</td>
</tr>
<tr>
<td class="name" ><a href="#multiThreaded">multiThreaded</a></td>
<td class="summary">Use multithreading in certain calculations.</td>
</tr>
<tr>
<td class="name" ><a href="#fastReload">fastReload</a></td>
<td class="summary">Can the game utilize the fast reload feature?</td>
</tr>
@ -1253,6 +1257,29 @@
</dd>
<dt>
<a name = "multiThreaded"></a>
<strong>multiThreaded</strong>
</dt>
<dd>
Use multithreading in certain calculations. <br>
When set to <code>true</code>, some performance-critical calculations will be performed in parallel, which can give
a significant performance boost. Don't disable unless you have problems with launching or using TombEngine.
<ul>
<li><span class="parameter">multiThreaded</span>
<span class="types"><span class="type">bool</span></span>
determines whether to use multithreading or not. */
</li>
</ul>
</dd>
<dt>
<a name = "fastReload"></a>

View file

@ -78,38 +78,38 @@ ObjectInfo& ObjectHandler::GetFirstAvailableObject()
void StaticHandler::Initialize()
{
_lookupTable.resize(0);
_lookupTable.reserve(_defaultLUTSize);
_lut.resize(0);
_lut.reserve(LUT_SIZE);
_statics.resize(0);
}
int StaticHandler::GetIndex(int staticID)
{
if (staticID < 0 || staticID >= _lookupTable.size())
if (staticID < 0 || staticID >= _lut.size())
{
TENLog("Attempt to get nonexistent static mesh ID slot index (" + std::to_string(staticID) + ")", LogLevel::Warning);
return _lookupTable.front();
TENLog("Attempted to get index of missing static object " + std::to_string(staticID) + ".", LogLevel::Warning);
return _lut.front();
}
return _lookupTable[staticID];
return _lut[staticID];
}
StaticInfo& StaticHandler::operator [](int staticID)
{
if (staticID < 0)
{
TENLog("Attempt to access illegal static mesh ID slot info", LogLevel::Warning);
TENLog("Attempted to access missing static object " + std::to_string(staticID) + ".", LogLevel::Warning);
return _statics.front();
}
if (staticID >= _lookupTable.size())
_lookupTable.resize(staticID + 1, NO_VALUE);
if (staticID >= _lut.size())
_lut.resize(staticID + 1, NO_VALUE);
if (_lookupTable[staticID] != NO_VALUE)
return _statics[_lookupTable[staticID]];
if (_lut[staticID] != NO_VALUE)
return _statics[_lut[staticID]];
_statics.emplace_back();
_lookupTable[staticID] = (int)_statics.size() - 1;
_lut[staticID] = (int)_statics.size() - 1;
return _statics.back();
}

View file

@ -135,10 +135,10 @@ struct StaticInfo
class StaticHandler
{
private:
static const int _defaultLUTSize = 256;
static constexpr auto LUT_SIZE = 256;
std::vector<StaticInfo> _statics = {};
std::vector<int> _lookupTable = {};
std::vector<int> _lut = {};
public:
void Initialize();
@ -147,6 +147,7 @@ public:
StaticInfo& operator [](int staticID);
// Iterators
auto begin() { return _statics.begin(); } // Non-const begin
auto end() { return _statics.end(); } // Non-const end
auto begin() const { return _statics.cbegin(); } // Const begin

View file

@ -509,7 +509,7 @@ namespace TEN::Renderer
const Vector4& color0, const Vector4& color1, const Vector4& color2, const Vector4& color3,
BlendMode blendMode, RenderView& view, SpriteRenderType renderType = SpriteRenderType::Default);
Matrix GetWorldMatrixForSprite(RendererSpriteToDraw* spr, RenderView& view);
Matrix GetWorldMatrixForSprite(const RendererSpriteToDraw& sprite, RenderView& view);
RendererObject& GetRendererObject(GAME_OBJECT_ID id);
RendererMesh* GetMesh(int meshIndex);
Texture2D CreateDefaultNormalTexture();

View file

@ -2925,7 +2925,7 @@ namespace TEN::Renderer
rDrawSprite.Width = STAR_SIZE * star.Scale;
rDrawSprite.Height = STAR_SIZE * star.Scale;
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(&rDrawSprite, renderView);
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(rDrawSprite, renderView);
_stInstancedSpriteBuffer.Sprites[i].Color = Vector4(
star.Color.x,
star.Color.y,
@ -2988,7 +2988,7 @@ namespace TEN::Renderer
rDrawSprite.Height = 192;
rDrawSprite.ConstrainAxis = meteor.Direction;
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(&rDrawSprite, renderView);
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(rDrawSprite, renderView);
_stInstancedSpriteBuffer.Sprites[i].Color = Vector4(
meteor.Color.x,
meteor.Color.y,
@ -3092,7 +3092,7 @@ namespace TEN::Renderer
rDrawSprite.Height = SUN_SIZE;
rDrawSprite.color = renderView.LensFlaresToDraw[0].Color;
_stInstancedSpriteBuffer.Sprites[0].World = GetWorldMatrixForSprite(&rDrawSprite, renderView);
_stInstancedSpriteBuffer.Sprites[0].World = GetWorldMatrixForSprite(rDrawSprite, renderView);
_stInstancedSpriteBuffer.Sprites[0].Color = renderView.LensFlaresToDraw[0].Color;
_stInstancedSpriteBuffer.Sprites[0].IsBillboard = 1;
_stInstancedSpriteBuffer.Sprites[0].IsSoftParticle = 0;
@ -3442,7 +3442,7 @@ namespace TEN::Renderer
uv2 = spr->Sprite->UV[2];
uv3 = spr->Sprite->UV[3];
auto world = GetWorldMatrixForSprite(currentObject->Sprite, view);
auto world = GetWorldMatrixForSprite(*currentObject->Sprite, view);
Vertex v0;
v0.Position = Vector3::Transform(p0t, world);

View file

@ -1291,14 +1291,14 @@ namespace TEN::Renderer
}
}
Matrix Renderer::GetWorldMatrixForSprite(RendererSpriteToDraw* sprite, RenderView& view)
Matrix Renderer::GetWorldMatrixForSprite(const RendererSpriteToDraw& sprite, RenderView& view)
{
auto spriteMatrix = Matrix::Identity;
auto scaleMatrix = Matrix::CreateScale(sprite->Width * sprite->Scale, sprite->Height * sprite->Scale, sprite->Scale);
auto scaleMatrix = Matrix::CreateScale(sprite.Width * sprite.Scale, sprite.Height * sprite.Scale, sprite.Scale);
auto spritePos = sprite->pos;
auto spritePos = sprite.pos;
if (sprite->Type == SpriteType::ThreeD)
if (sprite.Type == SpriteType::ThreeD)
{
ReflectMatrixOptionally(spriteMatrix);
}
@ -1307,23 +1307,23 @@ namespace TEN::Renderer
ReflectVectorOptionally(spritePos);
}
switch (sprite->Type)
switch (sprite.Type)
{
case SpriteType::Billboard:
{
auto cameraUp = Vector3(view.Camera.View._12, view.Camera.View._22, view.Camera.View._32);
spriteMatrix = scaleMatrix * Matrix::CreateRotationZ(sprite->Rotation) * Matrix::CreateBillboard(spritePos, Camera.pos.ToVector3(), cameraUp);
spriteMatrix = scaleMatrix * Matrix::CreateRotationZ(sprite.Rotation) * Matrix::CreateBillboard(spritePos, Camera.pos.ToVector3(), cameraUp);
}
break;
case SpriteType::CustomBillboard:
{
auto rotMatrix = Matrix::CreateRotationY(sprite->Rotation);
auto rotMatrix = Matrix::CreateRotationY(sprite.Rotation);
auto quadForward = Vector3(0.0f, 0.0f, 1.0f);
spriteMatrix = scaleMatrix * Matrix::CreateConstrainedBillboard(
spritePos,
Camera.pos.ToVector3(),
sprite->ConstrainAxis,
sprite.ConstrainAxis,
nullptr,
&quadForward);
}
@ -1332,7 +1332,7 @@ namespace TEN::Renderer
case SpriteType::LookAtBillboard:
{
auto translationMatrix = Matrix::CreateTranslation(spritePos);
auto rotMatrix = Matrix::CreateRotationZ(sprite->Rotation) * Matrix::CreateLookAt(Vector3::Zero, sprite->LookAtAxis, Vector3::UnitZ);
auto rotMatrix = Matrix::CreateRotationZ(sprite.Rotation) * Matrix::CreateLookAt(Vector3::Zero, sprite.LookAtAxis, Vector3::UnitZ);
spriteMatrix = scaleMatrix * rotMatrix * translationMatrix;
}
break;

View file

@ -1,12 +1,14 @@
#include "framework.h"
#include "Renderer/Structures/RendererSprite.h"
#include "Renderer/Structures/RendererSpriteBucket.h"
#include "Renderer/Renderer.h"
#include "Specific/Parallel.h"
using namespace TEN::Renderer::Structures;
namespace TEN::Renderer
{
using namespace TEN::Renderer::Structures;
void Renderer::AddSpriteBillboard(RendererSprite* sprite, const Vector3& pos, const Vector4& color, float orient2D, float scale,
Vector2 size, BlendMode blendMode, bool isSoftParticle, RenderView& view, SpriteRenderType renderType)
{
@ -261,16 +263,16 @@ namespace TEN::Renderer
return;
// Draw instanced sprites.
bool wasGPUSet = false;
for (auto& spriteBucket : _spriteBuckets)
bool wasGpuSet = false;
for (const auto& spriteBucket : _spriteBuckets)
{
if (spriteBucket.SpritesToDraw.size() == 0 || !spriteBucket.IsBillboard)
if (spriteBucket.SpritesToDraw.empty() || !spriteBucket.IsBillboard)
continue;
if (!SetupBlendModeAndAlphaTest(spriteBucket.BlendMode, rendererPass, 0))
continue;
if (!wasGPUSet)
if (!wasGpuSet)
{
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
@ -286,42 +288,45 @@ namespace TEN::Renderer
unsigned int offset = 0;
_context->IASetVertexBuffers(0, 1, _quadVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
wasGPUSet = true;
wasGpuSet = true;
}
// Prepare constant buffer for instanced sprites.
for (int i = 0; i < spriteBucket.SpritesToDraw.size(); i++)
// Define sprite preparation logic.
auto prepareSprites = [&](int start, int end)
{
auto& rDrawSprite = spriteBucket.SpritesToDraw[i];
for (int i = start; i < end; i++)
{
const auto& spriteToDraw = spriteBucket.SpritesToDraw[i];
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(&rDrawSprite, view);
_stInstancedSpriteBuffer.Sprites[i].Color = rDrawSprite.color;
_stInstancedSpriteBuffer.Sprites[i].IsBillboard = 1;
_stInstancedSpriteBuffer.Sprites[i].IsSoftParticle = rDrawSprite.SoftParticle ? 1 : 0;
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(spriteToDraw, view);
_stInstancedSpriteBuffer.Sprites[i].Color = spriteToDraw.color;
_stInstancedSpriteBuffer.Sprites[i].IsBillboard = 1.0f;
_stInstancedSpriteBuffer.Sprites[i].IsSoftParticle = spriteToDraw.SoftParticle ? 1.0f : 0.0f;
// NOTE: Strange packing due to particular HLSL 16 byte alignment requirements.
_stInstancedSpriteBuffer.Sprites[i].UV[0].x = rDrawSprite.Sprite->UV[0].x;
_stInstancedSpriteBuffer.Sprites[i].UV[0].y = rDrawSprite.Sprite->UV[1].x;
_stInstancedSpriteBuffer.Sprites[i].UV[0].z = rDrawSprite.Sprite->UV[2].x;
_stInstancedSpriteBuffer.Sprites[i].UV[0].w = rDrawSprite.Sprite->UV[3].x;
_stInstancedSpriteBuffer.Sprites[i].UV[1].x = rDrawSprite.Sprite->UV[0].y;
_stInstancedSpriteBuffer.Sprites[i].UV[1].y = rDrawSprite.Sprite->UV[1].y;
_stInstancedSpriteBuffer.Sprites[i].UV[1].z = rDrawSprite.Sprite->UV[2].y;
_stInstancedSpriteBuffer.Sprites[i].UV[1].w = rDrawSprite.Sprite->UV[3].y;
_stInstancedSpriteBuffer.Sprites[i].UV[0].x = spriteToDraw.Sprite->UV[0].x;
_stInstancedSpriteBuffer.Sprites[i].UV[0].y = spriteToDraw.Sprite->UV[1].x;
_stInstancedSpriteBuffer.Sprites[i].UV[0].z = spriteToDraw.Sprite->UV[2].x;
_stInstancedSpriteBuffer.Sprites[i].UV[0].w = spriteToDraw.Sprite->UV[3].x;
_stInstancedSpriteBuffer.Sprites[i].UV[1].x = spriteToDraw.Sprite->UV[0].y;
_stInstancedSpriteBuffer.Sprites[i].UV[1].y = spriteToDraw.Sprite->UV[1].y;
_stInstancedSpriteBuffer.Sprites[i].UV[1].z = spriteToDraw.Sprite->UV[2].y;
_stInstancedSpriteBuffer.Sprites[i].UV[1].w = spriteToDraw.Sprite->UV[3].y;
}
};
g_Parallel.AddTasks((int)spriteBucket.SpritesToDraw.size(), prepareSprites).wait();
BindTexture(TextureRegister::ColorMap, spriteBucket.Sprite->Texture, SamplerStateRegister::LinearClamp);
_cbInstancedSpriteBuffer.UpdateData(_stInstancedSpriteBuffer, _context.Get());
// Draw sprites with instancing.
DrawInstancedTriangles(4, (unsigned int)spriteBucket.SpritesToDraw.size(), 0);
DrawInstancedTriangles(4, (int)spriteBucket.SpritesToDraw.size(), 0);
_numInstancedSpritesDrawCalls++;
}
// Draw 3D non-instanced sprites.
wasGPUSet = false;
wasGpuSet = false;
for (auto& spriteBucket : _spriteBuckets)
{
@ -331,7 +336,7 @@ namespace TEN::Renderer
if (!SetupBlendModeAndAlphaTest(spriteBucket.BlendMode, rendererPass, 0))
continue;
if (!wasGPUSet)
if (!wasGpuSet)
{
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
@ -342,10 +347,10 @@ namespace TEN::Renderer
_shaders.Bind(Shader::Sprites);
wasGPUSet = true;
wasGpuSet = true;
}
_stSprite.IsSoftParticle = spriteBucket.IsSoftParticle ? 1 : 0;
_stSprite.IsSoftParticle = spriteBucket.IsSoftParticle ? 1.0f : 0.0f;
_stSprite.RenderType = (int)spriteBucket.RenderType;
_cbSprite.UpdateData(_stSprite, _context.Get());
@ -414,11 +419,11 @@ namespace TEN::Renderer
_shaders.Bind(Shader::InstancedSprites);
// Set up vertex buffer and parameters.
UINT stride = sizeof(Vertex);
UINT offset = 0;
unsigned int stride = sizeof(Vertex);
unsigned int offset = 0;
_context->IASetVertexBuffers(0, 1, _quadVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
_stInstancedSpriteBuffer.Sprites[0].World = GetWorldMatrixForSprite(object->Sprite, view);
_stInstancedSpriteBuffer.Sprites[0].World = GetWorldMatrixForSprite(*object->Sprite, view);
_stInstancedSpriteBuffer.Sprites[0].Color = object->Sprite->color;
_stInstancedSpriteBuffer.Sprites[0].IsBillboard = 1;
_stInstancedSpriteBuffer.Sprites[0].IsSoftParticle = object->Sprite->SoftParticle ? 1 : 0;

View file

@ -328,6 +328,12 @@ namespace TEN::Scripting
// @tfield Flow.ErrorMode errorMode error mode to use. */
"errorMode", &SystemSettings::ErrorMode,
/// Use multithreading in certain calculations. <br>
// When set to `true`, some performance-critical calculations will be performed in parallel, which can give
// a significant performance boost. Don't disable unless you have problems with launching or using TombEngine.
// @tfield bool multithreaded determines whether to use multithreading or not. */
"multithreaded", &SystemSettings::Multithreaded,
/// Can the game utilize the fast reload feature? <br>
// When set to `true`, the game will attempt to perform fast savegame reloading if current level is the same as
// the level loaded from the savegame. It will not work if the level timestamp or checksum has changed

View file

@ -80,6 +80,7 @@ namespace TEN::Scripting
{
ErrorMode ErrorMode = ErrorMode::Warn;
bool FastReload = true;
bool Multithreaded = true;
static void Register(sol::table& parent);
};

View file

@ -0,0 +1,173 @@
#include "framework.h"
#include "Specific/Parallel.h"
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
namespace TEN::Utils
{
ParallelTaskManager& g_Parallel = ParallelTaskManager::Get();
ParallelTaskManager::ParallelTaskManager()
{
_deinitialize = false;
}
ParallelTaskManager::~ParallelTaskManager()
{
// LOCK: Restrict shutdown flag access.
{
auto taskLock = std::lock_guard(_taskMutex);
_deinitialize = true;
}
// Notify all threads they should stop.
_taskCond.notify_all();
// Join all threads.
for (auto& thread : _threads)
{
if (!thread.joinable())
continue;
try
{
thread.join();
}
catch (const std::exception& ex)
{
TENLog("Error joining thread: " + std::string(ex.what()), LogLevel::Error);
}
}
}
ParallelTaskManager& ParallelTaskManager::Get()
{
static auto instance = ParallelTaskManager();
return instance;
}
unsigned int ParallelTaskManager::GetThreadCount() const
{
return (unsigned int)_threads.size();
}
unsigned int ParallelTaskManager::GetCoreCount() const
{
return std::max(std::thread::hardware_concurrency(), 1u);
}
void ParallelTaskManager::Initialize()
{
// Reserve threads.
unsigned int threadCount = g_GameFlow->GetSettings()->System.Multithreaded ? (GetCoreCount() * 2) : 1;
_threads.reserve(threadCount);
// Create threads.
for (int i = 0; i < threadCount; i++)
_threads.push_back(std::thread(&ParallelTaskManager::Worker, this));
}
std::future<void> ParallelTaskManager::AddTask(const ParallelTask& task)
{
return AddTasks(ParallelTaskGroup{ task });
}
std::future<void> ParallelTaskManager::AddTasks(const ParallelTaskGroup& tasks)
{
// HEAP ALLOC: Create counter and promise.
auto counter = std::make_shared<std::atomic<int>>();
auto promise = std::make_shared<std::promise<void>>();
counter->store((int)tasks.size(), std::memory_order_release);
// Add group tasks.
for (const auto& task : tasks)
AddTask(task, counter, promise);
// Notify available threads to handle tasks.
_taskCond.notify_all();
// Return future to wait on task group completion if needed.
return promise->get_future();
}
std::future<void> ParallelTaskManager::AddTasks(int elementCount, const std::function<void(int, int)>& splitTask)
{
// TODO: Make this a configuration option?
constexpr auto SERIAL_UNIT_COUNT_MAX = 32;
auto tasks = ParallelTaskGroup{};
// Process in parallel.
if (g_GameFlow->GetSettings()->System.Multithreaded &&
elementCount > SERIAL_UNIT_COUNT_MAX)
{
int threadCount = GetCoreCount();
int chunkSize = ((elementCount + threadCount) - 1) / threadCount;
// Collect group tasks.
tasks.reserve(threadCount);
for (int i = 0; i < threadCount; i++)
{
int start = i * chunkSize;
int end = std::min(start + chunkSize, elementCount);
tasks.push_back([&splitTask, start, end]() { splitTask(start, end); });
}
}
// Process linearly.
else
{
tasks.push_back([&splitTask, elementCount]() { splitTask(0, elementCount); });
}
// Add task group and return future to wait on completion if needed.
return AddTasks(tasks);
}
void ParallelTaskManager::Worker()
{
while (true)
{
auto task = ParallelTask();
// LOCK: Restrict task queue access.
{
auto taskLock = std::unique_lock(_taskMutex);
_taskCond.wait(taskLock, [this] { return (_deinitialize || !_tasks.empty()); });
// Shutting down and no pending tasks; return early.
if (_deinitialize && _tasks.empty())
return;
// Get task.
task = _tasks.front();
_tasks.pop();
}
// Execute task.
if (task)
task();
}
}
void ParallelTaskManager::AddTask(const ParallelTask& task, std::shared_ptr<std::atomic<int>> counter, std::shared_ptr<std::promise<void>> promise)
{
// Increment counter for task group.
counter->fetch_add(1, std::memory_order_relaxed);
// Add task with promise and counter handling.
_tasks.push([this, task, counter, promise]() { HandleTask(task, *counter, *promise); });
}
void ParallelTaskManager::HandleTask(const ParallelTask& task, std::atomic<int>& counter, std::promise<void>& promise)
{
// Execute task.
if (task)
task();
// Check for task group completion.
if (counter.fetch_sub(1, std::memory_order_acq_rel) == 1)
promise.set_value();
}
}

View file

@ -0,0 +1,54 @@
#pragma once
namespace TEN::Utils
{
using ParallelTask = std::function<void()>;
using ParallelTaskGroup = std::vector<ParallelTask>;
class ParallelTaskManager
{
private:
// Fields
std::vector<std::thread> _threads = {};
std::queue<ParallelTask> _tasks = {};
std::mutex _taskMutex = {};
std::condition_variable _taskCond = {};
bool _deinitialize = false;
// Constructors, destructors
ParallelTaskManager();
ParallelTaskManager(const ParallelTaskManager& manager) = delete;
~ParallelTaskManager();
public:
// Getters
static ParallelTaskManager& Get();
unsigned int GetThreadCount() const;
unsigned int GetCoreCount() const;
// Utilities
void Initialize();
std::future<void> AddTask(const ParallelTask& task);
std::future<void> AddTasks(const ParallelTaskGroup& tasks);
std::future<void> AddTasks(int elementCount, const std::function<void(int, int)>& splitTask);
private:
// Helpers
void Worker();
void AddTask(const ParallelTask& task, std::shared_ptr<std::atomic<int>> counter, std::shared_ptr<std::promise<void>> promise);
void HandleTask(const ParallelTask& task, std::atomic<int>& counter, std::promise<void>& promise);
// Operators
ParallelTaskManager& operator =(const ParallelTaskManager& manager) = delete;
};
extern ParallelTaskManager& g_Parallel;
}

View file

@ -14,6 +14,7 @@
#include "Sound/sound.h"
#include "Specific/level.h"
#include "Specific/configuration.h"
#include "Specific/Parallel.h"
#include "Specific/trutils.h"
#include "Scripting/Internal/LanguageScript.h"
#include "Scripting/Include/ScriptInterfaceState.h"
@ -592,6 +593,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
DoTheGame = true;
g_Parallel.Initialize();
ThreadEnded = false;
ThreadHandle = BeginThread(GameMain, ThreadID);

View file

@ -860,6 +860,7 @@ xcopy /Y "$(SolutionDir)Libs\zlib\x64\*.dll" "$(TargetDir)"</Command>
<ClInclude Include="Specific\Input\Input.h" />
<ClInclude Include="Specific\Input\InputAction.h" />
<ClInclude Include="Specific\LevelCameraInfo.h" />
<ClInclude Include="Specific\Parallel.h" />
<ClInclude Include="Specific\RGBAColor8Byte.h" />
<ClInclude Include="Specific\clock.h" />
<ClInclude Include="Specific\configuration.h" />
@ -1290,6 +1291,7 @@ xcopy /Y "$(SolutionDir)Libs\zlib\x64\*.dll" "$(TargetDir)"</Command>
<ClCompile Include="Specific\IO\ChunkReader.cpp" />
<ClCompile Include="Specific\IO\Streams.cpp" />
<ClCompile Include="Specific\level.cpp" />
<ClCompile Include="Specific\Parallel.cpp" />
<ClCompile Include="Specific\RGBAColor8Byte.cpp" />
<ClCompile Include="Specific\Structures\BoundingVolumeHierarchy.cpp" />
<ClCompile Include="Specific\trutils.cpp" />

View file

@ -2,6 +2,7 @@
#include <algorithm>
#include <array>
#include <atomic>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <deque>
@ -20,6 +21,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <queue>
#include <vector>
using namespace DirectX;