feat: better cmake support (#21)

* feat: CMakeSettings.json

* chore: remove visual studio stuff
chore: move .clang-format to main directory

* feat: new file structure, easier to implement as a submodule within existing projects

* chore: we actually include some code now...

* chore: remove prebuilt libraries (oops)

* feat: new shader building script using cmake

* chore: add debug postfix specifier

* fix: forgot that windows builds use 4J_Render_PC for the render library

* fix: didn't add the output directory (where shaders are saved to) to the include directories of 4J_Render
This commit is contained in:
gsds
2026-04-21 02:07:59 -04:00
committed by GitHub
parent 576f038dd0
commit e0ae7b1ec1
111 changed files with 1802 additions and 1458 deletions
+503
View File
@@ -0,0 +1,503 @@
/*
MIT License
Copyright (c) 2026 Patoke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "stdafx.h"
#include "4J_Render.h"
#include "Renderer.h"
C4JRender RenderManager;
void C4JRender::Tick()
{
InternalRenderManager.CBuffTick();
}
void C4JRender::UpdateGamma(unsigned short usGamma)
{
InternalRenderManager.UpdateGamma(usGamma);
}
void C4JRender::MatrixMode(int type)
{
InternalRenderManager.MatrixMode(type);
}
void C4JRender::MatrixSetIdentity()
{
InternalRenderManager.MatrixSetIdentity();
}
void C4JRender::MatrixTranslate(float x, float y, float z)
{
InternalRenderManager.MatrixTranslate(x, y, z);
}
void C4JRender::MatrixRotate(float angle, float x, float y, float z)
{
InternalRenderManager.MatrixRotate(angle, x, y, z);
}
void C4JRender::MatrixScale(float x, float y, float z)
{
InternalRenderManager.MatrixScale(x, y, z);
}
void C4JRender::MatrixPerspective(float fovy, float aspect, float zNear, float zFar)
{
InternalRenderManager.MatrixPerspective(fovy, aspect, zNear, zFar);
}
void C4JRender::MatrixOrthogonal(float left, float right, float bottom, float top, float zNear, float zFar)
{
InternalRenderManager.MatrixOrthogonal(left, right, bottom, top, zNear, zFar);
}
void C4JRender::MatrixPop()
{
InternalRenderManager.MatrixPop();
}
void C4JRender::MatrixPush()
{
InternalRenderManager.MatrixPush();
}
void C4JRender::MatrixMult(float* mat)
{
InternalRenderManager.MatrixMult(mat);
}
const float* C4JRender::MatrixGet(int type)
{
return InternalRenderManager.MatrixGet(type);
}
void C4JRender::Set_matrixDirty()
{
InternalRenderManager.Set_matrixDirty();
}
void C4JRender::Initialise(ID3D11Device* pDevice, IDXGISwapChain* pSwapChain)
{
InternalRenderManager.Initialise(pDevice, pSwapChain);
}
void C4JRender::InitialiseContext()
{
InternalRenderManager.InitialiseContext(false);
}
void C4JRender::StartFrame()
{
InternalRenderManager.StartFrame();
}
void C4JRender::DoScreenGrabOnNextPresent()
{
InternalRenderManager.DoScreenGrabOnNextPresent();
}
void C4JRender::Present()
{
InternalRenderManager.Present();
}
void C4JRender::Clear(int flags, D3D11_RECT* pRect)
{
InternalRenderManager.Clear(flags, pRect);
}
void C4JRender::SetClearColour(const float colourRGBA[4])
{
InternalRenderManager.SetClearColour(colourRGBA);
}
bool C4JRender::IsWidescreen()
{
return InternalRenderManager.IsWidescreen();
}
bool C4JRender::IsHiDef()
{
return InternalRenderManager.IsHiDef();
}
void C4JRender::CaptureThumbnail(ImageFileBuffer* pngOut)
{
InternalRenderManager.CaptureThumbnail(pngOut);
}
void C4JRender::CaptureScreen(ImageFileBuffer* jpgOut, XSOCIAL_PREVIEWIMAGE* previewOut)
{
InternalRenderManager.CaptureScreen(jpgOut, previewOut);
}
void C4JRender::BeginConditionalSurvey(int identifier)
{
InternalRenderManager.BeginConditionalSurvey(identifier);
}
void C4JRender::EndConditionalSurvey()
{
InternalRenderManager.EndConditionalSurvey();
}
void C4JRender::BeginConditionalRendering(int identifier)
{
InternalRenderManager.BeginConditionalRendering(identifier);
}
void C4JRender::EndConditionalRendering()
{
InternalRenderManager.EndConditionalRendering();
}
void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count, void* dataIn, eVertexType vType, ePixelShaderType psType)
{
InternalRenderManager.DrawVertices(PrimitiveType, count, dataIn, vType, psType);
}
void C4JRender::DrawVertexBuffer(ePrimitiveType PrimitiveType, int count, ID3D11Buffer* buffer, eVertexType vType, ePixelShaderType psType)
{
InternalRenderManager.DrawVertexBuffer(PrimitiveType, count, buffer, vType, psType);
}
void C4JRender::CBuffLockStaticCreations()
{
InternalRenderManager.CBuffLockStaticCreations();
}
int C4JRender::CBuffCreate(int count)
{
return InternalRenderManager.CBuffCreate(count);
}
void C4JRender::CBuffDelete(int first, int count)
{
InternalRenderManager.CBuffDelete(first, count);
}
void C4JRender::CBuffStart(int index, bool full)
{
InternalRenderManager.CBuffStart(index, full);
}
void C4JRender::CBuffClear(int index)
{
InternalRenderManager.CBuffClear(index);
}
int C4JRender::CBuffSize(int index)
{
return InternalRenderManager.CBuffSize(index);
}
void C4JRender::CBuffEnd()
{
InternalRenderManager.CBuffEnd();
}
bool C4JRender::CBuffCall(int index, bool full)
{
return InternalRenderManager.CBuffCall(index, full);
}
void C4JRender::CBuffTick()
{
InternalRenderManager.CBuffTick();
}
void C4JRender::CBuffDeferredModeStart()
{
InternalRenderManager.CBuffDeferredModeStart();
}
void C4JRender::CBuffDeferredModeEnd()
{
InternalRenderManager.CBuffDeferredModeEnd();
}
int C4JRender::TextureCreate()
{
return InternalRenderManager.TextureCreate();
}
void C4JRender::TextureFree(int idx)
{
InternalRenderManager.TextureFree(idx);
}
void C4JRender::TextureBind(int idx)
{
InternalRenderManager.TextureBind(idx);
}
void C4JRender::TextureBindVertex(int idx)
{
InternalRenderManager.TextureBindVertex(idx);
}
void C4JRender::TextureSetTextureLevels(int levels)
{
InternalRenderManager.TextureSetTextureLevels(levels);
}
int C4JRender::TextureGetTextureLevels()
{
return InternalRenderManager.TextureGetTextureLevels();
}
void C4JRender::TextureData(int width, int height, void* data, int level, eTextureFormat format)
{
InternalRenderManager.TextureData(width, height, data, level, format);
}
void C4JRender::TextureDataUpdate(int xoffset, int yoffset, int width, int height, void* data, int level)
{
InternalRenderManager.TextureDataUpdate(xoffset, yoffset, width, height, data, level);
}
void C4JRender::TextureSetParam(int param, int value)
{
InternalRenderManager.TextureSetParam(param, value);
}
void C4JRender::TextureDynamicUpdateStart()
{
InternalRenderManager.TextureDynamicUpdateStart();
}
void C4JRender::TextureDynamicUpdateEnd()
{
InternalRenderManager.TextureDynamicUpdateEnd();
}
HRESULT C4JRender::LoadTextureData(const char* szFilename, D3DXIMAGE_INFO* pSrcInfo, int** ppDataOut)
{
return InternalRenderManager.LoadTextureData(szFilename, pSrcInfo, ppDataOut);
}
HRESULT C4JRender::LoadTextureData(BYTE* pbData, DWORD dwBytes, D3DXIMAGE_INFO* pSrcInfo, int** ppDataOut)
{
return InternalRenderManager.LoadTextureData(pbData, dwBytes, pSrcInfo, ppDataOut);
}
HRESULT C4JRender::SaveTextureData(const char* szFilename, D3DXIMAGE_INFO* pSrcInfo, int* ppDataOut)
{
return InternalRenderManager.SaveTextureData(szFilename, pSrcInfo, ppDataOut);
}
HRESULT C4JRender::SaveTextureDataToMemory(void* pOutput, int outputCapacity, int* outputLength, int width, int height, int* ppDataIn)
{
return InternalRenderManager.SaveTextureDataToMemory(pOutput, outputCapacity, outputLength, width, height, ppDataIn);
}
void C4JRender::TextureGetStats()
{
}
ID3D11ShaderResourceView* C4JRender::TextureGetTexture(int idx)
{
return InternalRenderManager.TextureGetTexture(idx);
}
void C4JRender::StateSetColour(float r, float g, float b, float a)
{
InternalRenderManager.StateSetColour(r, g, b, a);
}
void C4JRender::StateSetDepthMask(bool enable)
{
InternalRenderManager.StateSetDepthMask(enable);
}
void C4JRender::StateSetBlendEnable(bool enable)
{
InternalRenderManager.StateSetBlendEnable(enable);
}
void C4JRender::StateSetBlendFunc(int src, int dst)
{
InternalRenderManager.StateSetBlendFunc(src, dst);
}
void C4JRender::StateSetBlendFactor(unsigned int colour)
{
InternalRenderManager.StateSetBlendFactor(colour);
}
void C4JRender::StateSetAlphaFunc(int func, float param)
{
InternalRenderManager.StateSetAlphaFunc(func, param);
}
void C4JRender::StateSetDepthFunc(int func)
{
InternalRenderManager.StateSetDepthFunc(func);
}
void C4JRender::StateSetFaceCull(bool enable)
{
InternalRenderManager.StateSetFaceCull(enable);
}
void C4JRender::StateSetFaceCullCW(bool enable)
{
InternalRenderManager.StateSetFaceCullCW(enable);
}
void C4JRender::StateSetLineWidth(float width)
{
InternalRenderManager.StateSetLineWidth(width);
}
void C4JRender::StateSetWriteEnable(bool red, bool green, bool blue, bool alpha)
{
InternalRenderManager.StateSetWriteEnable(red, green, blue, alpha);
}
void C4JRender::StateSetDepthTestEnable(bool enable)
{
InternalRenderManager.StateSetDepthTestEnable(enable);
}
void C4JRender::StateSetAlphaTestEnable(bool enable)
{
InternalRenderManager.StateSetAlphaTestEnable(enable);
}
void C4JRender::StateSetDepthSlopeAndBias(float slope, float bias)
{
InternalRenderManager.StateSetDepthSlopeAndBias(slope, bias);
}
void C4JRender::StateSetFogEnable(bool enable)
{
InternalRenderManager.StateSetFogEnable(enable);
}
void C4JRender::StateSetFogMode(int mode)
{
InternalRenderManager.StateSetFogMode(mode);
}
void C4JRender::StateSetFogNearDistance(float dist)
{
InternalRenderManager.StateSetFogNearDistance(dist);
}
void C4JRender::StateSetFogFarDistance(float dist)
{
InternalRenderManager.StateSetFogFarDistance(dist);
}
void C4JRender::StateSetFogDensity(float density)
{
InternalRenderManager.StateSetFogDensity(density);
}
void C4JRender::StateSetFogColour(float red, float green, float blue)
{
InternalRenderManager.StateSetFogColour(red, green, blue);
}
void C4JRender::StateSetLightingEnable(bool enable)
{
InternalRenderManager.StateSetLightingEnable(enable);
}
void C4JRender::StateSetVertexTextureUV(float u, float v)
{
InternalRenderManager.StateSetVertexTextureUV(u, v);
}
void C4JRender::StateSetLightColour(int light, float red, float green, float blue)
{
InternalRenderManager.StateSetLightColour(light, red, green, blue);
}
void C4JRender::StateSetLightAmbientColour(float red, float green, float blue)
{
InternalRenderManager.StateSetLightAmbientColour(red, green, blue);
}
void C4JRender::StateSetLightDirection(int light, float x, float y, float z)
{
InternalRenderManager.StateSetLightDirection(light, x, y, z);
}
void C4JRender::StateSetLightEnable(int light, bool enable)
{
InternalRenderManager.StateSetLightEnable(light, enable);
}
void C4JRender::StateSetViewport(eViewportType viewportType)
{
InternalRenderManager.StateSetViewport(viewportType);
}
void C4JRender::StateSetEnableViewportClipPlanes(bool enable)
{
InternalRenderManager.StateSetEnableViewportClipPlanes(enable);
}
void C4JRender::StateSetTexGenCol(int col, float x, float y, float z, float w, bool eyeSpace)
{
InternalRenderManager.StateSetTexGenCol(col, x, y, z, w, eyeSpace);
}
void C4JRender::StateSetStencil(int Function, uint8_t stencil_ref, uint8_t stencil_func_mask, uint8_t stencil_write_mask)
{
InternalRenderManager.StateSetStencil((D3D11_COMPARISON_FUNC)Function, stencil_ref, stencil_func_mask, stencil_write_mask);
}
void C4JRender::StateSetForceLOD(int LOD)
{
InternalRenderManager.StateSetForceLOD(LOD);
}
void C4JRender::BeginEvent(LPCWSTR eventName)
{
InternalRenderManager.BeginEvent(eventName);
}
void C4JRender::EndEvent()
{
InternalRenderManager.EndEvent();
}
void C4JRender::Suspend()
{
InternalRenderManager.Suspend();
}
bool C4JRender::Suspended()
{
return InternalRenderManager.Suspended();
}
void C4JRender::Resume()
{
InternalRenderManager.Resume();
}
@@ -0,0 +1,844 @@
/*
MIT License
Copyright (c) 2026 Patoke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "stdafx.h"
#include "Renderer.h"
#include <cstdint>
#include <cstring>
#include <new>
Renderer::CommandBuffer::CommandBuffer(bool full)
: m_vertexBuffer(NULL)
, m_vertexData(NULL)
, m_vertexDataLength(0)
, m_commands()
, m_allocated(0x1000)
, isActive(full ? 1 : 0)
{
m_vertexData = std::malloc(m_allocated);
EnterCriticalSection(&Renderer::totalAllocCS);
Renderer::totalAlloc += static_cast<int>(m_allocated);
LeaveCriticalSection(&Renderer::totalAllocCS);
}
Renderer::CommandBuffer::~CommandBuffer()
{
if (m_vertexBuffer)
m_vertexBuffer->Release();
std::free(m_vertexData);
EnterCriticalSection(&Renderer::totalAllocCS);
Renderer::totalAlloc -= static_cast<int>(m_allocated);
LeaveCriticalSection(&Renderer::totalAllocCS);
}
void Renderer::CommandBuffer::AddMatrix(const float *matrix)
{
Command command = {};
command.m_command_type = COMMAND_ADD_MATRIX;
std::memcpy(command.add_matrix.m_matrix, matrix, sizeof(command.add_matrix.m_matrix));
m_commands.push_back(command);
}
void Renderer::CommandBuffer::AddVertices(unsigned int stride, unsigned int count, void *dataIn, Renderer::Context &c)
{
PROFILER_SCOPE("Renderer::CommandBuffer::AddVertices", "AddVertices", MP_ORANGE);
if (c.matrixDirty[MATRIX_MODE_MODELVIEW_CBUFF])
{
AddMatrix(InternalRenderManager.MatrixGet(MATRIX_MODE_MODELVIEW_CBUFF));
c.matrixDirty[MATRIX_MODE_MODELVIEW_CBUFF] = false;
}
const std::uint64_t vertexOffset = m_vertexDataLength;
const std::uint64_t copySize = std::uint64_t(stride) * std::uint64_t(count);
Command command = {};
command.m_command_type = COMMAND_ADD_VERTICES;
command.add_vertices.m_vertex_index_start = static_cast<unsigned int>(vertexOffset);
command.add_vertices.m_vertex_count = count;
m_vertexDataLength = vertexOffset + copySize;
if (m_vertexDataLength > m_allocated)
{
EnterCriticalSection(&Renderer::totalAllocCS);
Renderer::totalAlloc -= static_cast<int>(m_allocated);
LeaveCriticalSection(&Renderer::totalAllocCS);
m_allocated = ((m_vertexDataLength + (0x1000 - 1)) & ~(0x1000 - 1));
m_vertexData = std::realloc(m_vertexData, m_allocated);
EnterCriticalSection(&Renderer::totalAllocCS);
Renderer::totalAlloc += static_cast<int>(m_allocated);
LeaveCriticalSection(&Renderer::totalAllocCS);
}
const std::size_t byteCount = std::size_t(stride) * std::size_t(count);
std::memcpy(static_cast<std::uint8_t*>(m_vertexData) + vertexOffset, dataIn, byteCount);
m_commands.push_back(command);
}
void Renderer::CommandBuffer::BindTexture(int idx)
{
Command command = {};
command.m_command_type = COMMAND_BIND_TEXTURE;
command.bind_texture.m_texture_index = idx;
m_commands.push_back(command);
}
bool Renderer::CBuffCall(int index, bool full)
{
EnterCriticalSection(&m_commandBufferCS);
bool result = false;
const int commandIndex = m_vertexIdxToBufferIdx[index];
if (commandIndex >= 0)
{
Renderer::Context &c = getContext();
const std::uint8_t vertexType = m_commandVertexTypes[commandIndex];
const std::uint8_t primitiveType = m_commandPrimitiveTypes[commandIndex];
if (full)
{
if (c.matrixDirty[MATRIX_MODE_MODELVIEW])
{
D3D11_MAPPED_SUBRESOURCE mappedMatrix0 = {};
c.m_pDeviceContext->Map(c.m_modelViewMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedMatrix0);
std::memcpy(mappedMatrix0.pData, MatrixGet(MATRIX_MODE_MODELVIEW), sizeof(DirectX::XMMATRIX));
c.m_pDeviceContext->Unmap(c.m_modelViewMatrix, 0);
c.matrixDirty[MATRIX_MODE_MODELVIEW] = false;
}
if (c.matrixDirty[MATRIX_MODE_MODELVIEW_PROJECTION])
{
D3D11_MAPPED_SUBRESOURCE mappedMatrix2 = {};
c.m_pDeviceContext->Map(c.m_projectionMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedMatrix2);
std::memcpy(mappedMatrix2.pData, MatrixGet(MATRIX_MODE_MODELVIEW_PROJECTION), sizeof(DirectX::XMMATRIX));
c.m_pDeviceContext->Unmap(c.m_projectionMatrix, 0);
c.matrixDirty[MATRIX_MODE_MODELVIEW_PROJECTION] = false;
}
if (c.matrixDirty[MATRIX_MODE_MODELVIEW_TEXTURE])
{
D3D11_MAPPED_SUBRESOURCE mappedMatrix3 = {};
c.m_pDeviceContext->Map(c.m_textureMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedMatrix3);
std::memcpy(mappedMatrix3.pData, MatrixGet(MATRIX_MODE_MODELVIEW_TEXTURE), sizeof(DirectX::XMMATRIX));
c.m_pDeviceContext->Unmap(c.m_textureMatrix, 0);
c.matrixDirty[MATRIX_MODE_MODELVIEW_TEXTURE] = false;
}
UpdateFogState();
UpdateLightingState();
UpdateViewportState();
UpdateTexGenState();
if (vertexType != activeVertexType)
{
c.m_pDeviceContext->VSSetShader(vertexShaderTable[vertexType], NULL, 0);
c.m_pDeviceContext->IASetInputLayout(inputLayoutTable[vertexType]);
activeVertexType = vertexType;
}
int pixelType = 0;
if (static_cast<int>(c.forcedLOD) > -1)
{
const float forcedLod[4] = {static_cast<float>(static_cast<int>(c.forcedLOD)), 0.0f, 0.0f, 0.0f};
D3D11_MAPPED_SUBRESOURCE mappedAux4 = {};
c.m_pDeviceContext->Map(c.m_forcedLODBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedAux4);
std::memcpy(mappedAux4.pData, forcedLod, sizeof(forcedLod));
c.m_pDeviceContext->Unmap(c.m_forcedLODBuffer, 0);
pixelType = C4JRender::PIXEL_SHADER_TYPE_FORCELOD;
}
if (static_cast<DWORD>(pixelType) != activePixelType)
{
c.m_pDeviceContext->PSSetShader(pixelShaderTable[pixelType], NULL, 0);
activePixelType = pixelType;
}
c.m_pDeviceContext->IASetPrimitiveTopology(g_topologies[primitiveType]);
ID3D11Buffer *indexBuffer = NULL;
if (primitiveType == C4JRender::PRIMITIVE_TYPE_QUAD_LIST)
indexBuffer = quadIndexBuffer;
else if (primitiveType == C4JRender::PRIMITIVE_TYPE_TRIANGLE_FAN)
indexBuffer = fanIndexBuffer;
c.m_pDeviceContext->IASetIndexBuffer(indexBuffer, DXGI_FORMAT_R16_UINT, 0);
}
m_commandBuffers[commandIndex]->Render(static_cast<C4JRender::eVertexType>(vertexType), c, primitiveType);
if (full)
{
MultWithStack(m_commandMatrices[commandIndex]);
c.matrixStacks[MATRIX_MODE_MODELVIEW_CBUFF][0] = DirectX::XMMatrixIdentity();
c.matrixDirty[MATRIX_MODE_MODELVIEW_CBUFF] = true;
}
result = true;
}
LeaveCriticalSection(&m_commandBufferCS);
return result;
}
void Renderer::CBuffClear(int index)
{
EnterCriticalSection(&m_commandBufferCS);
const int internalIndex = m_vertexIdxToBufferIdx[index];
if (internalIndex >= 0)
{
DeleteInternalBuffer(internalIndex);
m_vertexIdxToBufferIdx[index] = static_cast<std::int16_t>(-2);
}
LeaveCriticalSection(&m_commandBufferCS);
}
int Renderer::CBuffCreate(int count)
{
EnterCriticalSection(&m_commandBufferCS);
int first = reservedRendererDword1;
if (first < NUM_COMMAND_HANDLES)
{
int probe = first;
int end = first + count;
while (true)
{
assert(first < NUM_COMMAND_HANDLES);
int cursor = probe;
while (cursor < end && cursor < NUM_COMMAND_HANDLES && m_vertexIdxToBufferIdx[cursor] == static_cast<std::int16_t>(-1))
{
++cursor;
}
if (cursor >= end)
break;
++first;
++probe;
++end;
if (first >= NUM_COMMAND_HANDLES || end > NUM_COMMAND_HANDLES)
{
first = -1;
break;
}
}
if (first >= 0)
{
const int allocationEnd = first + count;
for (int i = first; i < allocationEnd; ++i)
m_vertexIdxToBufferIdx[i] = static_cast<std::int16_t>(-2);
if (reservedRendererByte1)
reservedRendererDword1 = allocationEnd;
}
}
else
{
first = -1;
}
LeaveCriticalSection(&m_commandBufferCS);
return first;
}
void Renderer::CBuffDeferredModeEnd()
{
Renderer::Context &c = getContext();
if (!c.deferredModeEnabled)
return;
EnterCriticalSection(&m_commandBufferCS);
c.deferredModeEnabled = false;
for (std::vector<Renderer::DeferredCBuff>::const_iterator it = c.deferredBuffers.begin(); it != c.deferredBuffers.end(); ++it)
{
const Renderer::DeferredCBuff &deferred = *it;
const int existingIndex = m_vertexIdxToBufferIdx[deferred.m_vertex_index];
if (existingIndex >= 0)
DeleteInternalBuffer(existingIndex);
if (static_cast<int>(m_currentCommandBuffer + m_numBuffersToDeallocate + 10) > MAX_COMMAND_BUFFERS)
DebugBreak();
const int internalSlot = m_currentCommandBuffer;
++m_currentCommandBuffer;
m_vertexIdxToBufferIdx[deferred.m_vertex_index] = static_cast<std::int16_t>(internalSlot);
m_bufferIdxToVertexIdx[internalSlot] = deferred.m_vertex_index;
m_commandVertexTypes[internalSlot] = static_cast<std::uint8_t>(deferred.m_vertex_type);
m_commandPrimitiveTypes[internalSlot] = static_cast<std::uint8_t>(deferred.m_primitive_type);
m_commandBuffers[internalSlot] = deferred.m_command_buf;
m_commandMatrices[internalSlot] = deferred.m_matrix;
}
c.deferredBuffers.clear();
LeaveCriticalSection(&m_commandBufferCS);
}
void Renderer::CBuffDeferredModeStart()
{
getContext().deferredModeEnabled = true;
}
void Renderer::CBuffDelete(int first, int count)
{
EnterCriticalSection(&m_commandBufferCS);
const int end = first + count;
for (int i = first; i < end; ++i)
{
const int internalIndex = m_vertexIdxToBufferIdx[i];
if (internalIndex >= 0)
DeleteInternalBuffer(internalIndex);
m_vertexIdxToBufferIdx[i] = static_cast<std::int16_t>(-1);
}
LeaveCriticalSection(&m_commandBufferCS);
}
void Renderer::CBuffEnd()
{
Renderer::Context &c = getContext();
assert(c.stackType == MATRIX_MODE_MODELVIEW_CBUFF);
assert(c.stackPos[MATRIX_MODE_MODELVIEW_CBUFF] == 0);
EnterCriticalSection(&m_commandBufferCS);
if (c.deferredModeEnabled)
{
Renderer::DeferredCBuff deferred;
deferred.m_command_buf = c.commandBuffer;
deferred.m_vertex_index = c.recordingBufferIndex;
deferred.m_vertex_type = c.recordingVertexType;
deferred.m_primitive_type = c.recordingPrimitiveType;
deferred.m_matrix = c.matrixStacks[MATRIX_MODE_MODELVIEW_CBUFF][0];
c.deferredBuffers.push_back(deferred);
}
else
{
const int existingIndex = m_vertexIdxToBufferIdx[c.recordingBufferIndex];
if (existingIndex >= 0)
DeleteInternalBuffer(existingIndex);
if (static_cast<int>(m_currentCommandBuffer + m_numBuffersToDeallocate + 10) > MAX_COMMAND_BUFFERS)
DebugBreak();
const int internalSlot = m_currentCommandBuffer;
++m_currentCommandBuffer;
m_vertexIdxToBufferIdx[c.recordingBufferIndex] = static_cast<std::int16_t>(internalSlot);
m_bufferIdxToVertexIdx[internalSlot] = c.recordingBufferIndex;
m_commandVertexTypes[internalSlot] = static_cast<std::uint8_t>(c.recordingVertexType);
m_commandPrimitiveTypes[internalSlot] = static_cast<std::uint8_t>(c.recordingPrimitiveType);
m_commandBuffers[internalSlot] = c.commandBuffer;
m_commandMatrices[internalSlot] = c.matrixStacks[MATRIX_MODE_MODELVIEW_CBUFF][0];
}
c.stackType = MATRIX_MODE_MODELVIEW;
c.commandBuffer->EndRecording(m_pDevice);
c.commandBuffer = NULL;
LeaveCriticalSection(&m_commandBufferCS);
}
void Renderer::CBuffLockStaticCreations()
{
reservedRendererByte1 = 0;
}
int Renderer::CBuffSize(int index)
{
if (index == -1)
return totalAlloc < 0 ? 0 : totalAlloc;
unsigned int size = 0;
EnterCriticalSection(&m_commandBufferCS);
const int commandIndex = m_vertexIdxToBufferIdx[index];
if (commandIndex >= 0)
size = static_cast<unsigned int>(m_commandBuffers[commandIndex]->GetAllocated());
LeaveCriticalSection(&m_commandBufferCS);
return size;
}
void Renderer::CBuffStart(int index, bool full)
{
Renderer::Context &c = getContext();
c.commandBuffer = new (std::nothrow) Renderer::CommandBuffer(full);
c.recordingBufferIndex = index;
assert(c.stackType == MATRIX_MODE_MODELVIEW);
c.stackType = MATRIX_MODE_MODELVIEW_CBUFF;
c.stackPos[MATRIX_MODE_MODELVIEW_CBUFF] = 0;
c.matrixStacks[MATRIX_MODE_MODELVIEW_CBUFF][0] = DirectX::XMMatrixIdentity();
c.matrixDirty[MATRIX_MODE_MODELVIEW_CBUFF] = true;
}
void Renderer::CBuffTick()
{
EnterCriticalSection(&m_commandBufferCS);
int completedDeletes = 0;
if (m_numBuffersToDeallocate > 0)
{
int deleteIdx = MAX_COMMAND_BUFFERS - 1;
do
{
Renderer::CommandBuffer *buffer = m_commandBuffers[deleteIdx];
if (buffer)
delete buffer;
m_commandBuffers[deleteIdx] = NULL;
if (--m_numBuffersToDeallocate == 0)
{
++completedDeletes;
--deleteIdx;
}
else
{
m_commandBuffers[deleteIdx] = m_commandBuffers[(MAX_COMMAND_BUFFERS - 1) - static_cast<int>(m_numBuffersToDeallocate)];
}
} while (completedDeletes < static_cast<int>(m_numBuffersToDeallocate));
}
LeaveCriticalSection(&m_commandBufferCS);
}
void Renderer::DeleteInternalBuffer(int index)
{
EnterCriticalSection(&m_commandBufferCS);
++m_numBuffersToDeallocate;
const int index_delete = MAX_COMMAND_BUFFERS - static_cast<int>(m_numBuffersToDeallocate);
m_commandBuffers[index_delete] = m_commandBuffers[index];
m_commandMatrices[index_delete] = m_commandMatrices[index];
if (m_currentCommandBuffer-- != 1)
{
const int mappedFrom = m_currentCommandBuffer;
m_commandBuffers[index] = m_commandBuffers[mappedFrom];
m_commandMatrices[index] = m_commandMatrices[mappedFrom];
m_commandVertexTypes[index] = m_commandVertexTypes[mappedFrom];
m_commandPrimitiveTypes[index] = m_commandPrimitiveTypes[mappedFrom];
const int commandIndex = m_bufferIdxToVertexIdx[mappedFrom];
m_vertexIdxToBufferIdx[commandIndex] = static_cast<std::int16_t>(index);
m_bufferIdxToVertexIdx[index] = commandIndex;
}
LeaveCriticalSection(&m_commandBufferCS);
}
void Renderer::CommandBuffer::EndRecording(ID3D11Device *device)
{
if (m_vertexDataLength != 0)
{
D3D11_BUFFER_DESC desc = {};
desc.ByteWidth = static_cast<UINT>(m_vertexDataLength);
desc.Usage = D3D11_USAGE_IMMUTABLE;
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA data = {};
data.pSysMem = m_vertexData;
device->CreateBuffer(&desc, &data, &m_vertexBuffer);
}
std::free(m_vertexData);
m_vertexData = NULL;
}
std::uint64_t Renderer::CommandBuffer::GetAllocated()
{
return m_allocated;
}
bool Renderer::CommandBuffer::IsBusy()
{
return false;
}
void Renderer::CommandBuffer::Render(C4JRender::eVertexType vType, Renderer::Context &c, int primitiveType)
{
PROFILER_SCOPE("Renderer::CommandBuffer::Render", "Render", MP_ORANGE);
if (!m_vertexBuffer)
return;
int drawVertexType = vType;
int shaderVertexType = drawVertexType;
bool matrixOverride = false;
for (const Command &command : m_commands)
{
PROFILER_SCOPE("Renderer::CommandBuffer::Render", "ProcessCommand", MP_ORANGE);
switch (command.m_command_type)
{
case COMMAND_ADD_MATRIX:
{
if (drawVertexType == C4JRender::VERTEX_TYPE_COMPRESSED)
{
const float row[4] = {
command.add_matrix.m_matrix[12], command.add_matrix.m_matrix[13],
command.add_matrix.m_matrix[14], command.add_matrix.m_matrix[15]
};
D3D11_MAPPED_SUBRESOURCE mappedAux0 = {};
c.m_pDeviceContext->Map(c.m_compressedTranslationBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedAux0);
std::memcpy(mappedAux0.pData, row, sizeof(row));
c.m_pDeviceContext->Unmap(c.m_compressedTranslationBuffer, 0);
}
else
{
D3D11_MAPPED_SUBRESOURCE mappedMatrix1 = {};
c.m_pDeviceContext->Map(c.m_localTransformMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedMatrix1);
std::memcpy(mappedMatrix1.pData, command.add_matrix.m_matrix, sizeof(command.add_matrix.m_matrix));
c.m_pDeviceContext->Unmap(c.m_localTransformMatrix, 0);
matrixOverride = true;
}
break;
}
case COMMAND_ADD_VERTICES:
{
if (isActive)
{
InternalRenderManager.UpdateLightingState();
if (drawVertexType == C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1)
{
if (c.lightingEnabled)
{
drawVertexType = C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT;
shaderVertexType = C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT;
}
}
else if (drawVertexType == C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT && !c.lightingEnabled)
{
drawVertexType = C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1;
shaderVertexType = C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1;
}
if (static_cast<DWORD>(drawVertexType) != InternalRenderManager.activeVertexType)
{
c.m_pDeviceContext->VSSetShader(InternalRenderManager.vertexShaderTable[shaderVertexType], NULL, 0);
c.m_pDeviceContext->IASetInputLayout(InternalRenderManager.inputLayoutTable[shaderVertexType]);
InternalRenderManager.activeVertexType = drawVertexType;
}
}
unsigned int drawCount = command.add_vertices.m_vertex_count;
bool drawIndexed = false;
if (primitiveType == C4JRender::PRIMITIVE_TYPE_QUAD_LIST)
{
drawCount = (drawCount * 6) / 4;
drawIndexed = true;
}
else if (primitiveType == C4JRender::PRIMITIVE_TYPE_TRIANGLE_FAN)
{
drawCount = (drawCount - 2) * 3;
drawIndexed = true;
}
ID3D11Buffer *buffer = m_vertexBuffer;
const UINT stride = InternalRenderManager.vertexStrideTable[drawVertexType];
const UINT offset = command.add_vertices.m_vertex_index_start;
c.m_pDeviceContext->IASetVertexBuffers(0, 1, &buffer, &stride, &offset);
if (drawIndexed)
c.m_pDeviceContext->DrawIndexed(drawCount, 0, 0);
else
c.m_pDeviceContext->Draw(drawCount, 0);
break;
}
case COMMAND_BIND_TEXTURE:
{
c.textureIdx = command.bind_texture.m_texture_index;
ID3D11ShaderResourceView *view = InternalRenderManager.m_textures[c.textureIdx].view;
c.m_pDeviceContext->PSSetShaderResources(0, 1, &view);
InternalRenderManager.UpdateTextureState(false);
break;
}
case COMMAND_SET_COLOR:
{
D3D11_MAPPED_SUBRESOURCE mappedColour = {};
c.m_pDeviceContext->Map(c.m_tintColorBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedColour);
std::memcpy(mappedColour.pData, command.set_color.m_color, sizeof(command.set_color.m_color));
c.m_pDeviceContext->Unmap(c.m_tintColorBuffer, 0);
break;
}
case COMMAND_SET_DEPTH_FUNC:
{
c.depthStencilDesc.DepthFunc = static_cast<D3D11_COMPARISON_FUNC>(command.set_depth_func.m_depth_func);
c.m_pDeviceContext->OMSetDepthStencilState(InternalRenderManager.GetManagedDepthStencilState(), 0);
break;
}
case COMMAND_SET_DEPTH_MASK:
{
c.depthWriteEnabled = command.set_depth_mask.m_enable;
c.depthStencilDesc.DepthWriteMask = command.set_depth_mask.m_enable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
c.m_pDeviceContext->OMSetDepthStencilState(InternalRenderManager.GetManagedDepthStencilState(), 0);
break;
}
case COMMAND_SET_DEPTH_TEST:
{
c.depthTestEnabled = command.set_depth_test.m_enable;
c.depthStencilDesc.DepthEnable = command.set_depth_test.m_enable;
c.m_pDeviceContext->OMSetDepthStencilState(InternalRenderManager.GetManagedDepthStencilState(), 0);
break;
}
case COMMAND_SET_LIGHTING_ENABLE:
{
c.lightingEnabled = command.set_lighting_enable.m_enable;
break;
}
case COMMAND_SET_LIGHT_ENABLE:
{
const int light = command.set_light_enable.m_light_index;
if (light >= 0 && light < 2)
{
c.lightEnabled[light] = command.set_light_enable.m_enable;
c.lightingDirty = true;
}
break;
}
case COMMAND_SET_LIGHT_DIRECTION:
{
const int light = command.set_light_direction.m_light_index;
if (light >= 0 && light < 2)
{
c.lightDirection[light].x = command.set_light_direction.m_direction[0];
c.lightDirection[light].y = command.set_light_direction.m_direction[1];
c.lightDirection[light].z = command.set_light_direction.m_direction[2];
c.lightDirection[light].w = command.set_light_direction.m_direction[3];
c.lightingDirty = true;
}
break;
}
case COMMAND_SET_LIGHT_COLOUR:
{
const int light = command.set_light_colour.m_light_index;
if (light >= 0 && light < 2)
{
c.lightColour[light].x = command.set_light_colour.m_color[0];
c.lightColour[light].y = command.set_light_colour.m_color[1];
c.lightColour[light].z = command.set_light_colour.m_color[2];
c.lightColour[light].w = 1.0f;
c.lightingDirty = true;
}
break;
}
case COMMAND_SET_LIGHT_AMBIENT_COLOUR:
{
c.lightAmbientColour.x = command.set_light_ambient_colour.m_color[0];
c.lightAmbientColour.y = command.set_light_ambient_colour.m_color[1];
c.lightAmbientColour.z = command.set_light_ambient_colour.m_color[2];
c.lightAmbientColour.w = 1.0f;
c.lightingDirty = true;
break;
}
case COMMAND_SET_BLEND_ENABLE:
{
c.blendDesc.RenderTarget[0].BlendEnable = command.set_blend_enable.m_enable;
break;
}
case COMMAND_SET_BLEND_FUNC:
{
c.blendDesc.RenderTarget[0].SrcBlend = static_cast<D3D11_BLEND>(command.set_blend_func.m_src);
c.blendDesc.RenderTarget[0].DestBlend = static_cast<D3D11_BLEND>(command.set_blend_func.m_dst);
c.m_pDeviceContext->OMSetBlendState(InternalRenderManager.GetManagedBlendState(), c.blendFactor, 0xFFFFFFFF);
break;
}
case COMMAND_SET_BLEND_FACTOR:
{
const unsigned int factor = command.set_blend_factor.m_blend_factor;
c.blendFactor[0] = float((factor >> 0) & 0xFF) / 255.0f;
c.blendFactor[1] = float((factor >> 8) & 0xFF) / 255.0f;
c.blendFactor[2] = float((factor >> 16) & 0xFF) / 255.0f;
c.blendFactor[3] = float((factor >> 24) & 0xFF) / 255.0f;
c.m_pDeviceContext->OMSetBlendState(InternalRenderManager.GetManagedBlendState(), c.blendFactor, 0xFFFFFFFF);
break;
}
case COMMAND_SET_FACE_CULL:
{
c.rasterizerDesc.CullMode = command.set_face_cull.m_enable ? D3D11_CULL_BACK : D3D11_CULL_NONE;
c.m_pDeviceContext->RSSetState(InternalRenderManager.GetManagedRasterizerState());
c.faceCullEnabled = command.set_face_cull.m_enable;
break;
}
default:
break;
}
}
if (matrixOverride)
{
const DirectX::XMMATRIX identity = DirectX::XMMatrixIdentity();
D3D11_MAPPED_SUBRESOURCE mappedIdentity = {};
c.m_pDeviceContext->Map(c.m_localTransformMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedIdentity);
std::memcpy(mappedIdentity.pData, &identity, sizeof(identity));
c.m_pDeviceContext->Unmap(c.m_localTransformMatrix, 0);
}
}
void Renderer::CommandBuffer::SetBlendEnable(bool enable)
{
Command command = {};
command.m_command_type = COMMAND_SET_BLEND_ENABLE;
command.set_blend_enable.m_enable = enable;
m_commands.push_back(command);
}
void Renderer::CommandBuffer::SetBlendFactor(unsigned int factor)
{
Command command = {};
command.m_command_type = COMMAND_SET_BLEND_FACTOR;
command.set_blend_factor.m_blend_factor = factor;
m_commands.push_back(command);
}
void Renderer::CommandBuffer::SetBlendFunc(int src, int dst)
{
Command command = {};
command.m_command_type = COMMAND_SET_BLEND_FUNC;
command.set_blend_func.m_src = src;
command.set_blend_func.m_dst = dst;
m_commands.push_back(command);
}
void Renderer::CommandBuffer::SetColor(float r, float g, float b, float a)
{
Command command = {};
command.m_command_type = COMMAND_SET_COLOR;
command.set_color.m_color[0] = r;
command.set_color.m_color[1] = g;
command.set_color.m_color[2] = b;
command.set_color.m_color[3] = a;
m_commands.push_back(command);
}
void Renderer::CommandBuffer::SetDepthFunc(int func)
{
Command command = {};
command.m_command_type = COMMAND_SET_DEPTH_FUNC;
command.set_depth_func.m_depth_func = func;
m_commands.push_back(command);
}
void Renderer::CommandBuffer::SetDepthMask(bool enable)
{
Command command = {};
command.m_command_type = COMMAND_SET_DEPTH_MASK;
command.set_depth_mask.m_enable = enable;
m_commands.push_back(command);
}
void Renderer::CommandBuffer::SetDepthTestEnable(bool enable)
{
Command command = {};
command.m_command_type = COMMAND_SET_DEPTH_TEST;
command.set_depth_test.m_enable = enable;
m_commands.push_back(command);
}
void Renderer::CommandBuffer::SetFaceCull(bool enable)
{
Command command = {};
command.m_command_type = COMMAND_SET_FACE_CULL;
command.set_face_cull.m_enable = enable;
m_commands.push_back(command);
}
void Renderer::CommandBuffer::SetLightAmbientColour(float r, float g, float b)
{
Command command = {};
command.m_command_type = COMMAND_SET_LIGHT_AMBIENT_COLOUR;
command.set_light_ambient_colour.m_color[0] = r;
command.set_light_ambient_colour.m_color[1] = g;
command.set_light_ambient_colour.m_color[2] = b;
m_commands.push_back(command);
}
void Renderer::CommandBuffer::SetLightColour(int light, float r, float g, float b)
{
Command command = {};
command.m_command_type = COMMAND_SET_LIGHT_COLOUR;
command.set_light_colour.m_light_index = light;
command.set_light_colour.m_color[0] = r;
command.set_light_colour.m_color[1] = g;
command.set_light_colour.m_color[2] = b;
m_commands.push_back(command);
}
void Renderer::CommandBuffer::SetLightDirection(int light, float x, float y, float z)
{
Renderer::Context &c = InternalRenderManager.getContext();
const std::uint32_t depth = c.stackPos[MATRIX_MODE_MODELVIEW_CBUFF];
const DirectX::XMMATRIX &matrix = c.matrixStacks[MATRIX_MODE_MODELVIEW_CBUFF][depth];
DirectX::XMVECTOR direction = DirectX::XMVectorSet(x, y, z, 0.0f);
direction = DirectX::XMVector3TransformNormal(direction, matrix);
direction = DirectX::XMVector3Normalize(direction);
Command command = {};
command.m_command_type = COMMAND_SET_LIGHT_DIRECTION;
command.set_light_direction.m_light_index = light;
DirectX::XMFLOAT4 outDirection;
DirectX::XMStoreFloat4(&outDirection, direction);
command.set_light_direction.m_direction[0] = outDirection.x;
command.set_light_direction.m_direction[1] = outDirection.y;
command.set_light_direction.m_direction[2] = outDirection.z;
command.set_light_direction.m_direction[3] = outDirection.w;
m_commands.push_back(command);
}
void Renderer::CommandBuffer::SetLightEnable(int light, bool enable)
{
Command command = {};
command.m_command_type = COMMAND_SET_LIGHT_ENABLE;
command.set_light_enable.m_light_index = light;
command.set_light_enable.m_enable = enable;
m_commands.push_back(command);
}
void Renderer::CommandBuffer::SetLightingEnable(bool enable)
{
Command command = {};
command.m_command_type = COMMAND_SET_LIGHTING_ENABLE;
command.set_lighting_enable.m_enable = enable;
m_commands.push_back(command);
}
void Renderer::CommandBuffer::StartRecording() {}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,144 @@
/*
MIT License
Copyright (c) 2026 Patoke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "stdafx.h"
#include "Renderer.h"
#include <cstring>
const float *Renderer::MatrixGet(int type)
{
Context &c = getContext();
const int depth = c.stackPos[type];
const DirectX::XMMATRIX &matrix = c.matrixStacks[type][depth];
return &matrix.r[0].m128_f32[0];
}
void Renderer::MatrixMode(int type)
{
Context &c = getContext();
assert(type >= 0);
assert(type < STACK_TYPES);
c.stackType = type;
}
void Renderer::MatrixMult(float *mat)
{
DirectX::XMMATRIX matrix;
std::memcpy(&matrix, mat, sizeof(matrix));
MultWithStack(matrix);
}
void Renderer::MatrixOrthogonal(float left, float right, float bottom, float top, float zNear, float zFar)
{
const DirectX::XMMATRIX matrix = DirectX::XMMatrixOrthographicOffCenterRH(left, right, bottom, top, zNear, zFar);
MultWithStack(matrix);
}
void Renderer::MatrixPerspective(float fovy, float aspect, float zNear, float zFar)
{
const float fovRadians = fovy * (PI / 180.0f);
const DirectX::XMMATRIX matrix = DirectX::XMMatrixPerspectiveFovRH(fovRadians, aspect, zNear, zFar);
MultWithStack(matrix);
}
void Renderer::MatrixPop()
{
Context &c = getContext();
assert(c.stackPos[c.stackType] > 0);
const int mode = c.stackType;
--c.stackPos[mode];
c.matrixDirty[mode] = true;
}
void Renderer::MatrixPush()
{
Context &c = getContext();
assert(c.stackPos[c.stackType] < (STACK_SIZE - 1));
const int mode = c.stackType;
const int depth = c.stackPos[mode];
c.matrixStacks[mode][depth + 1] = c.matrixStacks[mode][depth];
++c.stackPos[mode];
}
void Renderer::MatrixRotate(float angle, float x, float y, float z)
{
const DirectX::XMVECTOR axis = DirectX::XMVectorSet(x, y, z, 0.0f);
const DirectX::XMMATRIX matrix = DirectX::XMMatrixRotationAxis(axis, angle);
MultWithStack(matrix);
}
void Renderer::MatrixScale(float x, float y, float z)
{
const DirectX::XMMATRIX matrix = DirectX::XMMatrixScaling(x, y, z);
MultWithStack(matrix);
}
void Renderer::MatrixSetIdentity()
{
Context &c = getContext();
const int mode = c.stackType;
const int depth = c.stackPos[mode];
c.matrixStacks[mode][depth] = DirectX::XMMatrixIdentity();
c.matrixDirty[mode] = true;
}
void Renderer::MatrixTranslate(float x, float y, float z)
{
const DirectX::XMMATRIX matrix = DirectX::XMMatrixTranslation(x, y, z);
MultWithStack(matrix);
}
void Renderer::MultWithStack(DirectX::XMMATRIX matrix)
{
Context &c = getContext();
const int mode = c.stackType;
const int depth = c.stackPos[mode];
DirectX::XMMATRIX &current = c.matrixStacks[mode][depth];
current = DirectX::XMMatrixMultiply(matrix, current);
c.matrixDirty[mode] = true;
}
void Renderer::Set_matrixDirty()
{
Context &c = getContext();
const DirectX::XMMATRIX identity = DirectX::XMMatrixIdentity();
c.matrixStacks[MATRIX_MODE_MODELVIEW][0] = identity;
c.matrixStacks[MATRIX_MODE_MODELVIEW_PROJECTION][0] = identity;
c.matrixStacks[MATRIX_MODE_MODELVIEW_TEXTURE][0] = identity;
c.matrixStacks[MATRIX_MODE_MODELVIEW_CBUFF][0] = identity;
c.matrixDirty[MATRIX_MODE_MODELVIEW] = true;
c.matrixDirty[MATRIX_MODE_MODELVIEW_PROJECTION] = true;
c.matrixDirty[MATRIX_MODE_MODELVIEW_TEXTURE] = true;
c.matrixDirty[MATRIX_MODE_MODELVIEW_CBUFF] = true;
activeVertexType = -1;
activePixelType = -1;
}
@@ -0,0 +1,688 @@
/*
MIT License
Copyright (c) 2026 Patoke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "stdafx.h"
#include "Renderer.h"
#include <cstring>
#include <limits>
ID3D11BlendState *Renderer::GetManagedBlendState()
{
PROFILER_SCOPE("Renderer::GetManagedBlendState", "GetManagedBlendState", MP_ORCHID1);
Context &c = getContext();
const D3D11_RENDER_TARGET_BLEND_DESC &rtBlend = c.blendDesc.RenderTarget[0];
const int key = (rtBlend.BlendEnable ? 1 : 0) | ((static_cast<int>(rtBlend.SrcBlend) & 0x1F) << 1) |
((static_cast<int>(rtBlend.DestBlend) & 0x1F) << 6) | ((static_cast<int>(rtBlend.RenderTargetWriteMask) & 0x0F) << 11);
auto it = managedBlendStates.find(key);
if (it != managedBlendStates.end())
return it->second;
ID3D11BlendState *state = NULL;
m_pDevice->CreateBlendState(&c.blendDesc, &state);
managedBlendStates.emplace(key, state);
return state;
}
ID3D11DepthStencilState *Renderer::GetManagedDepthStencilState()
{
PROFILER_SCOPE("Renderer::GetManagedBlendState", "GetManagedDepthStencilState", MP_ORCHID1);
Context &c = getContext();
const int key = (c.depthStencilDesc.DepthEnable ? 2 : 0) | ((static_cast<int>(c.depthStencilDesc.DepthFunc) & 0x0F) << 2) |
(c.depthStencilDesc.DepthWriteMask == D3D11_DEPTH_WRITE_MASK_ALL ? 1 : 0);
auto it = managedDepthStencilStates.find(key);
if (it != managedDepthStencilStates.end())
return it->second;
ID3D11DepthStencilState *state = NULL;
m_pDevice->CreateDepthStencilState(&c.depthStencilDesc, &state);
managedDepthStencilStates.emplace(key, state);
return state;
}
ID3D11RasterizerState *Renderer::GetManagedRasterizerState()
{
PROFILER_SCOPE("Renderer::GetManagedRasterizerState", "GetManagedRasterizerState", MP_ORCHID1);
Context &c = getContext();
const int key = (static_cast<std::uint8_t>(c.rasterizerDesc.DepthBias)) |
(static_cast<std::uint8_t>(static_cast<int>(c.rasterizerDesc.SlopeScaledDepthBias)) << 8) |
((static_cast<int>(c.rasterizerDesc.CullMode) & 0x03) << 16);
auto it = managedRasterizerStates.find(key);
if (it != managedRasterizerStates.end())
return it->second;
ID3D11RasterizerState *state = NULL;
m_pDevice->CreateRasterizerState(&c.rasterizerDesc, &state);
managedRasterizerStates.emplace(key, state);
return state;
}
ID3D11SamplerState *Renderer::GetManagedSamplerState()
{
PROFILER_SCOPE("Renderer::GetManagedSamplerState", "GetManagedSamplerState", MP_ORCHID1);
Context &c = getContext();
const int key = m_textures[c.textureIdx].samplerParams;
auto it = managedSamplerStates.find(key);
if (it != managedSamplerStates.end())
return it->second;
const bool clampU = (key & SAMPLER_PARAM_CLAMP_U) != 0;
const bool clampV = (key & SAMPLER_PARAM_CLAMP_V) != 0;
const bool linearFilter = (key & SAMPLER_PARAM_LINEAR_FILTER) != 0;
const bool mipLinear = (key & SAMPLER_PARAM_LINEAR_MIPS) != 0;
const int filterBits = (mipLinear != 0 ? (linearFilter ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR)
: (linearFilter ? D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR : D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR));
D3D11_SAMPLER_DESC desc = {};
desc.Filter = static_cast<D3D11_FILTER>(filterBits);
desc.AddressU = clampU ? D3D11_TEXTURE_ADDRESS_CLAMP : D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressV = clampV ? D3D11_TEXTURE_ADDRESS_CLAMP : D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
desc.MipLODBias = 0.0f;
desc.MaxAnisotropy = 16;
desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
desc.BorderColor[0] = 0.0f;
desc.BorderColor[1] = 0.0f;
desc.BorderColor[2] = 0.0f;
desc.BorderColor[3] = 0.0f;
desc.MinLOD = -(std::numeric_limits<float>::max)();
desc.MaxLOD = (std::numeric_limits<float>::max)();
ID3D11SamplerState *state = NULL;
m_pDevice->CreateSamplerState(&desc, &state);
managedSamplerStates.emplace(key, state);
return state;
}
void Renderer::StateSetAlphaFunc(int, float param)
{
Context &c = getContext();
c.alphaReference = param;
const float alpha[4] = {0.0f, 0.0f, 0.0f, c.alphaTestEnabled ? c.alphaReference : 0.0f};
D3D11_MAPPED_SUBRESOURCE mapped = {};
c.m_pDeviceContext->Map(c.m_alphaTestBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
std::memcpy(mapped.pData, alpha, sizeof(alpha));
c.m_pDeviceContext->Unmap(c.m_alphaTestBuffer, 0);
}
void Renderer::StateSetAlphaTestEnable(bool enable)
{
Context &c = getContext();
c.alphaTestEnabled = enable;
const float alpha[4] = {0.0f, 0.0f, 0.0f, enable ? c.alphaReference : 0.0f};
D3D11_MAPPED_SUBRESOURCE mapped = {};
c.m_pDeviceContext->Map(c.m_alphaTestBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
std::memcpy(mapped.pData, alpha, sizeof(alpha));
c.m_pDeviceContext->Unmap(c.m_alphaTestBuffer, 0);
}
void Renderer::StateSetBlendEnable(bool enable)
{
Context &c = getContext();
if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0)
{
c.commandBuffer->SetBlendEnable(enable);
return;
}
c.blendDesc.RenderTarget[0].BlendEnable = enable;
c.m_pDeviceContext->OMSetBlendState(GetManagedBlendState(), c.blendFactor, 0xFFFFFFFF);
}
void Renderer::StateSetBlendFactor(unsigned int colour)
{
Context &c = getContext();
if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0)
{
c.commandBuffer->SetBlendFactor(colour);
return;
}
const float scale = 255.0f;
c.blendFactor[0] = static_cast<float>((colour >> 0) & 0xFF) / scale;
c.blendFactor[1] = static_cast<float>((colour >> 8) & 0xFF) / scale;
c.blendFactor[2] = static_cast<float>((colour >> 16) & 0xFF) / scale;
c.blendFactor[3] = static_cast<float>((colour >> 24) & 0xFF) / scale;
c.m_pDeviceContext->OMSetBlendState(GetManagedBlendState(), c.blendFactor, 0xFFFFFFFF);
}
void Renderer::StateSetBlendFunc(int src, int dst)
{
Context &c = getContext();
if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0)
{
c.commandBuffer->SetBlendFunc(src, dst);
return;
}
c.blendDesc.RenderTarget[0].SrcBlend = static_cast<D3D11_BLEND>(src);
c.blendDesc.RenderTarget[0].DestBlend = static_cast<D3D11_BLEND>(dst);
c.m_pDeviceContext->OMSetBlendState(GetManagedBlendState(), c.blendFactor, 0xFFFFFFFF);
}
void Renderer::StateSetColour(float r, float g, float b, float a)
{
Context &c = getContext();
if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0)
{
c.commandBuffer->SetColor(r, g, b, a);
return;
}
ID3D11DeviceContext *d3d11 = c.m_pDeviceContext;
const float colour[4] = {r, g, b, a};
D3D11_MAPPED_SUBRESOURCE mapped = {};
d3d11->Map(c.m_tintColorBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
std::memcpy(mapped.pData, colour, sizeof(colour));
d3d11->Unmap(c.m_tintColorBuffer, 0);
}
void Renderer::StateSetDepthFunc(int func)
{
Context &c = getContext();
if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0)
{
c.commandBuffer->SetDepthFunc(func);
return;
}
c.depthStencilDesc.DepthFunc = static_cast<D3D11_COMPARISON_FUNC>(func);
c.m_pDeviceContext->OMSetDepthStencilState(GetManagedDepthStencilState(), 0);
}
void Renderer::StateSetDepthMask(bool enable)
{
Context &c = getContext();
if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0)
{
c.commandBuffer->SetDepthMask(enable);
return;
}
c.depthStencilDesc.DepthWriteMask = enable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
c.m_pDeviceContext->OMSetDepthStencilState(GetManagedDepthStencilState(), 0);
c.depthWriteEnabled = enable;
}
void Renderer::StateSetDepthSlopeAndBias(float slope, float bias)
{
Context &c = getContext();
const float scale = 65536.0f;
c.rasterizerDesc.DepthBias = static_cast<int>(bias * scale);
c.rasterizerDesc.SlopeScaledDepthBias = slope * scale;
c.m_pDeviceContext->RSSetState(GetManagedRasterizerState());
}
void Renderer::StateSetDepthTestEnable(bool enable)
{
Context &c = getContext();
if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0)
{
c.commandBuffer->SetDepthTestEnable(enable);
return;
}
c.depthStencilDesc.DepthEnable = enable;
c.m_pDeviceContext->OMSetDepthStencilState(GetManagedDepthStencilState(), 0);
c.depthTestEnabled = enable;
}
void Renderer::StateSetEnableViewportClipPlanes(bool) {}
void Renderer::StateSetFaceCull(bool enable)
{
Context &c = getContext();
if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0)
{
c.commandBuffer->SetFaceCull(enable);
return;
}
c.rasterizerDesc.CullMode = enable ? D3D11_CULL_BACK : D3D11_CULL_NONE;
c.m_pDeviceContext->RSSetState(GetManagedRasterizerState());
c.faceCullEnabled = enable;
}
void Renderer::StateSetFaceCullCW(bool enable)
{
Context &c = getContext();
if (c.faceCullEnabled)
c.rasterizerDesc.CullMode = enable ? D3D11_CULL_BACK : D3D11_CULL_FRONT;
else
c.rasterizerDesc.CullMode = D3D11_CULL_NONE;
c.m_pDeviceContext->RSSetState(GetManagedRasterizerState());
}
void Renderer::StateSetFogColour(float red, float green, float blue)
{
Context &c = getContext();
c.fogColourRed = red;
c.fogColourBlue = blue;
c.fogColourGreen = green;
}
void Renderer::StateSetFogDensity(float density)
{
Context &c = getContext();
c.fogDensity = density;
}
void Renderer::StateSetFogEnable(bool enable)
{
Context &c = getContext();
c.fogEnabled = enable;
}
void Renderer::StateSetFogFarDistance(float dist)
{
Context &c = getContext();
c.fogFarDistance = dist;
}
void Renderer::StateSetFogMode(int mode)
{
Context &c = getContext();
c.fogMode = mode;
}
void Renderer::StateSetFogNearDistance(float dist)
{
Context &c = getContext();
c.fogNearDistance = dist;
}
void Renderer::StateSetForceLOD(int LOD)
{
Context &c = getContext();
c.forcedLOD = LOD;
}
void Renderer::StateSetLightAmbientColour(float red, float green, float blue)
{
Context &c = getContext();
if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0)
{
c.commandBuffer->SetLightAmbientColour(red, green, blue);
return;
}
c.lightAmbientColour.x = red;
c.lightAmbientColour.y = green;
c.lightAmbientColour.z = blue;
c.lightAmbientColour.w = 1.0f;
c.lightingDirty = true;
}
void Renderer::StateSetLightColour(int light, float red, float green, float blue)
{
if (light >= 2)
return;
Context &c = getContext();
if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0)
{
c.commandBuffer->SetLightColour(light, red, green, blue);
return;
}
c.lightColour[light].x = red;
c.lightColour[light].y = green;
c.lightColour[light].z = blue;
c.lightColour[light].w = 1.0f;
c.lightingDirty = true;
}
void Renderer::StateSetLightDirection(int light, float x, float y, float z)
{
if (light >= 2)
return;
Context &c = getContext();
if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0)
{
c.commandBuffer->SetLightDirection(light, x, y, z);
return;
}
const std::uint32_t stackIndex = c.stackPos[MATRIX_MODE_MODELVIEW];
const DirectX::XMMATRIX &modelView = c.matrixStacks[MATRIX_MODE_MODELVIEW][stackIndex];
const DirectX::XMVECTOR direction = DirectX::XMVectorSet(x, y, z, 0.0f);
const DirectX::XMVECTOR transformed = DirectX::XMVector3TransformNormal(direction, modelView);
const DirectX::XMVECTOR normalized = DirectX::XMVector3Normalize(transformed);
DirectX::XMStoreFloat4(&c.lightDirection[light], normalized);
c.lightingDirty = true;
}
void Renderer::StateSetLightEnable(int light, bool enable)
{
if (light >= 2)
return;
Context &c = getContext();
if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0)
{
c.commandBuffer->SetLightEnable(light, enable);
return;
}
c.lightEnabled[light] = enable;
c.lightingDirty = true;
}
void Renderer::StateSetLightingEnable(bool enable)
{
Context &c = getContext();
if (c.commandBuffer != NULL && c.commandBuffer->isActive != 0)
{
c.commandBuffer->SetLightingEnable(enable);
return;
}
c.lightingEnabled = enable;
}
void Renderer::StateSetLineWidth(float) {}
void Renderer::StateSetStencil(D3D11_COMPARISON_FUNC function, uint8_t stencil_ref, uint8_t stencil_func_mask, uint8_t stencil_write_mask)
{
Context &c = getContext();
D3D11_DEPTH_STENCIL_DESC desc = c.depthStencilDesc;
desc.StencilEnable = true;
desc.StencilReadMask = stencil_func_mask;
desc.StencilWriteMask = stencil_write_mask;
desc.FrontFace.StencilFunc = function;
desc.BackFace.StencilFunc = function;
ID3D11DepthStencilState *state = NULL;
m_pDevice->CreateDepthStencilState(&desc, &state);
m_pDeviceContext->OMSetDepthStencilState(state, stencil_ref);
if (state != NULL) state->Release();
}
void Renderer::StateSetTexGenCol(int col, float x, float y, float z, float w, bool eyeSpace)
{
Context &c = getContext();
DirectX::XMVECTOR plane = DirectX::XMVectorSet(x, y, z, w);
if (eyeSpace)
{
DirectX::XMFLOAT4X4 modelView;
std::memset(&modelView, 0, sizeof(modelView));
std::memcpy(&modelView, MatrixGet(MATRIX_MODE_MODELVIEW), sizeof(modelView));
DirectX::XMVECTOR determinant = DirectX::XMVectorZero();
const DirectX::XMMATRIX inverse = DirectX::XMMatrixInverse(&determinant, DirectX::XMLoadFloat4x4(&modelView));
plane = DirectX::XMVector4Transform(plane, inverse);
}
DirectX::XMFLOAT4 transformed;
DirectX::XMStoreFloat4(&transformed, plane);
const int activeSet = eyeSpace ? 0 : 1;
const int inactiveSet = eyeSpace ? 1 : 0;
float *active = reinterpret_cast<float *>(&c.texGenMatrices[activeSet]);
active[col + 0] = transformed.x;
active[col + 4] = transformed.y;
active[col + 8] = transformed.z;
active[col + 12] = transformed.w;
float *inactive = reinterpret_cast<float *>(&c.texGenMatrices[inactiveSet]);
inactive[col + 0] = 0.0f;
inactive[col + 4] = 0.0f;
inactive[col + 8] = 0.0f;
inactive[col + 12] = 0.0f;
}
void Renderer::StateSetVertexTextureUV(float u, float v)
{
Context &c = getContext();
const float texgen[4] = {u - 1.0f, v - 1.0f, 0.0f, 0.0f};
D3D11_MAPPED_SUBRESOURCE mapped = {};
c.m_pDeviceContext->Map(c.m_vertexTexcoordBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
std::memcpy(mapped.pData, texgen, sizeof(texgen));
c.m_pDeviceContext->Unmap(c.m_vertexTexcoordBuffer, 0);
}
void Renderer::StateSetViewport(C4JRender::eViewportType viewportType)
{
getContext();
m_ViewportType = viewportType;
const float fullWidth = static_cast<float>(backBufferWidth);
const float fullHeight = static_cast<float>(backBufferHeight);
float x = 0.0f;
float y = 0.0f;
float width = fullWidth;
float height = fullHeight;
switch (viewportType)
{
case C4JRender::VIEWPORT_TYPE_FULLSCREEN:
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
height = fullHeight * 0.5f;
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
y = fullHeight * 0.5f;
height = fullHeight * 0.5f;
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
width = fullWidth * 0.5f;
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
x = fullWidth * 0.5f;
width = fullWidth * 0.5f;
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
width = fullWidth * 0.5f;
height = fullHeight * 0.5f;
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
x = fullWidth * 0.5f;
width = fullWidth * 0.5f;
height = fullHeight * 0.5f;
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
y = fullHeight * 0.5f;
width = fullWidth * 0.5f;
height = fullHeight * 0.5f;
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
x = fullWidth * 0.5f;
y = fullHeight * 0.5f;
width = fullWidth * 0.5f;
height = fullHeight * 0.5f;
break;
default:
break;
}
D3D11_VIEWPORT viewport = {};
viewport.TopLeftX = x;
viewport.TopLeftY = y;
viewport.Width = width;
viewport.Height = height;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
m_pDeviceContext->RSSetViewports(1, &viewport);
m_pDeviceContext->OMSetRenderTargets(1, &mainRenderTargetView, depthStencilView);
}
void Renderer::StateSetWriteEnable(bool red, bool green, bool blue, bool alpha)
{
Context &c = getContext();
std::uint8_t mask = 0;
mask |= red ? 0x1 : 0;
mask |= green ? 0x2 : 0;
mask |= blue ? 0x4 : 0;
mask |= alpha ? 0x8 : 0;
c.blendDesc.RenderTarget[0].RenderTargetWriteMask = mask;
c.m_pDeviceContext->OMSetBlendState(GetManagedBlendState(), c.blendFactor, 0xFFFFFFFF);
}
void Renderer::StateUpdate()
{
PROFILER_SCOPE("Renderer::StateUpdate", "StateUpdate", MP_ORCHID1);
Context &c = getContext();
StateSetFaceCull(c.faceCullEnabled);
StateSetDepthMask(c.depthWriteEnabled);
StateSetDepthTestEnable(c.depthTestEnabled);
StateSetAlphaTestEnable(c.alphaTestEnabled);
}
void Renderer::UpdateFogState()
{
PROFILER_SCOPE("Renderer::UpdateFogState", "UpdateFogState", MP_ORCHID1);
Context &c = getContext();
ID3D11DeviceContext *d3d11 = c.m_pDeviceContext;
float fogParams[4] = {};
if (c.fogEnabled)
{
if (c.fogMode == 1)
{
fogParams[0] = c.fogFarDistance;
fogParams[1] = 1.0f / (c.fogFarDistance - c.fogNearDistance);
fogParams[2] = 1.0f;
}
else
{
fogParams[0] = c.fogDensity;
fogParams[2] = 2.0f;
}
}
const float fogColour[4] = {c.fogColourRed, c.fogColourGreen, c.fogColourBlue, 1.0f};
D3D11_MAPPED_SUBRESOURCE mapped = {};
d3d11->Map(c.m_fogParamsBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
std::memcpy(mapped.pData, fogParams, sizeof(fogParams));
d3d11->Unmap(c.m_fogParamsBuffer, 0);
d3d11->Map(c.m_fogColourBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
std::memcpy(mapped.pData, fogColour, sizeof(fogColour));
d3d11->Unmap(c.m_fogColourBuffer, 0);
}
void Renderer::UpdateLightingState()
{
PROFILER_SCOPE("Renderer::UpdateLightingState", "UpdateLightingState", MP_ORCHID1);
Context &c = getContext();
if (!c.lightingDirty || !c.lightingEnabled)
{
return;
}
if (!c.lightEnabled[0])
{
std::memset(&c.lightDirection[0], 0, sizeof(c.lightDirection[0]));
std::memset(&c.lightColour[0], 0, sizeof(c.lightColour[0]));
}
if (!c.lightEnabled[1])
{
std::memset(&c.lightDirection[1], 0, sizeof(c.lightDirection[1]));
std::memset(&c.lightColour[1], 0, sizeof(c.lightColour[1]));
}
const std::size_t lightingBytes = sizeof(c.lightDirection) + sizeof(c.lightColour) + sizeof(c.lightAmbientColour);
D3D11_MAPPED_SUBRESOURCE mapped = {};
c.m_pDeviceContext->Map(c.m_lightingStateBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
std::memmove(mapped.pData, c.lightDirection, lightingBytes);
c.m_pDeviceContext->Unmap(c.m_lightingStateBuffer, 0);
c.lightingDirty = false;
}
void Renderer::UpdateTexGenState()
{
PROFILER_SCOPE("Renderer::UpdateTexGenState", "UpdateTexGenState", MP_ORCHID1);
Context &c = getContext();
D3D11_MAPPED_SUBRESOURCE mapped = {};
c.m_pDeviceContext->Map(c.m_texGenMatricesBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
c.m_pDeviceContext->Unmap(c.m_texGenMatricesBuffer, 0);
std::memcpy(mapped.pData, c.texGenMatrices, sizeof(c.texGenMatrices));
}
void Renderer::UpdateViewportState() {}
@@ -0,0 +1,333 @@
/*
MIT License
Copyright (c) 2026 Patoke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "stdafx.h"
#include "Renderer.h"
#include "libpng/png.h"
unsigned char* dataStart;
unsigned char *dataCurr;
unsigned char *dataEnd;
DXGI_FORMAT Renderer::textureFormats[] = {
DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_UNKNOWN
};
// these are here because they are used before they are defined
// its fine like that and the order of everything matches ida so we have to put these here
void user_flush_data(png_struct_def *png_ptr);
void user_write_data(png_struct_def *png_ptr, unsigned char *src, size_t length);
HRESULT Renderer::LoadTextureData(BYTE* pbData, DWORD dwBytes, D3DXIMAGE_INFO* pSrcInfo, int** ppDataOut)
{
PROFILER_SCOPE("Renderer::LoadTextureData_Memory", "LoadTextureData_Memory", MP_PURPLE4);
png_image image;
memset(&image, 0, sizeof(image));
image.version = PNG_IMAGE_VERSION;
png_image_begin_read_from_memory(&image, pbData, dwBytes);
if (PNG_IMAGE_FAILED(image))
return E_FAIL;
image.format = PNG_FORMAT_BGRA;
*ppDataOut = new int[image.width * image.height];
if (!*ppDataOut || !png_image_finish_read(&image, NULL, *ppDataOut, NULL, NULL))
return E_FAIL;
pSrcInfo->Width = image.width;
pSrcInfo->Height = image.height;
return S_OK;
}
HRESULT Renderer::LoadTextureData(const char* szFilename, D3DXIMAGE_INFO* pSrcInfo, int** ppDataOut)
{
PROFILER_SCOPE("Renderer::LoadTextureData_File", "LoadTextureData_File", MP_PURPLE4);
png_image image;
memset(&image, 0, sizeof(image));
image.version = PNG_IMAGE_VERSION;
png_image_begin_read_from_file(&image, szFilename);
if (PNG_IMAGE_FAILED(image))
return E_FAIL;
image.format = PNG_FORMAT_BGRA;
*ppDataOut = new int[image.width * image.height];
if (!*ppDataOut || !png_image_finish_read(&image, NULL, *ppDataOut, NULL, NULL))
return E_FAIL;
pSrcInfo->Width = image.width;
pSrcInfo->Height = image.height;
return S_OK;
}
HRESULT Renderer::SaveTextureData(const char* szFilename, D3DXIMAGE_INFO* pSrcInfo, int* ppDataOut)
{
PROFILER_SCOPE("Renderer::SaveTextureData", "SaveTextureData", MP_PURPLE4);
png_image image;
memset(&image, 0, sizeof(image));
image.width = pSrcInfo->Width;
image.height = pSrcInfo->Height;
image.version = PNG_IMAGE_VERSION;
image.format = PNG_FORMAT_RGBA;
png_image_write_to_file(&image, szFilename, NULL, ppDataOut, NULL, NULL);
return S_OK;
}
HRESULT Renderer::SaveTextureDataToMemory(void* pOutput, int outputCapacity, int* outputLength, int width, int height, int* ppDataIn)
{
PROFILER_SCOPE("Renderer::SaveTextureDataToMemory", "SaveTextureDataToMemory", MP_PURPLE4);
png_image image;
memset(&image, 0, sizeof(image));
image.width = width;
image.height = height;
dataEnd = (BYTE *)pOutput + outputCapacity;
image.version = PNG_IMAGE_VERSION;
image.format = PNG_FORMAT_RGBA;
dataStart = (BYTE*)pOutput;
dataCurr = (BYTE*)pOutput;
png_image_write_to_stdio(&image, NULL, NULL, ppDataIn, NULL, NULL, user_write_data, user_flush_data);
*outputLength = (int)(dataCurr - dataStart);
return S_OK;
}
void Renderer::TextureBind(int idx)
{
PROFILER_SCOPE("Renderer::TextureBind", "TextureBind", MP_PURPLE4);
if (idx == -1)
idx = defaultTextureIndex;
Context& c = getContext();
if (c.commandBuffer && c.commandBuffer->isActive)
c.commandBuffer->BindTexture(idx);
c.textureIdx = idx;
c.m_pDeviceContext->PSSetShaderResources(0, 1, &m_textures[idx].view);
UpdateTextureState(false);
}
void Renderer::TextureBindVertex(int idx)
{
PROFILER_SCOPE("Renderer::TextureBindVertex", "TextureBindVertex", MP_PURPLE4);
if (idx == -1)
idx = defaultTextureIndex;
Context& c = getContext();
c.textureIdx = idx;
c.m_pDeviceContext->VSSetShaderResources(0, 1, &m_textures[idx].view);
UpdateTextureState(true);
}
int Renderer::TextureCreate()
{
PROFILER_SCOPE("Renderer::TextureCreate", "TextureCreate", MP_PURPLE4);
for (int i = 0; i < MAX_TEXTURES; i++)
{
if (!m_textures[i].allocated)
{
m_textures[i].texture = NULL;
m_textures[i].allocated = true;
m_textures[i].mipLevels = 1;
m_textures[i].samplerParams = 0;
return i;
}
}
return -1;
}
void Renderer::TextureData(int width, int height, void* data, int level, C4JRender::eTextureFormat format)
{
PROFILER_SCOPE("Renderer::TextureData", "TextureData", MP_PURPLE4);
Context& c = getContext();
int idx = c.textureIdx;
m_textures[idx].textureFormat = format;
if (level == 0)
{
D3D11_TEXTURE2D_DESC desc;
desc.Width = width;
desc.Height = height;
desc.MipLevels = m_textures[idx].mipLevels;
desc.ArraySize = 1;
desc.Format = textureFormats[format];
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
m_pDevice->CreateTexture2D(&desc, NULL, &m_textures[idx].texture);
m_pDevice->CreateShaderResourceView(m_textures[idx].texture, NULL, &m_textures[idx].view);
}
c.m_pDeviceContext->UpdateSubresource(
m_textures[idx].texture,
level,
NULL,
data,
static_cast<UINT>(width * 4),
static_cast<UINT>(width * height * 4)
);
}
void Renderer::TextureDataUpdate(int xoffset, int yoffset, int width, int height, void* data, int level)
{
PROFILER_SCOPE("Renderer::TextureDataUpdate", "TextureDataUpdate", MP_PURPLE4);
Context& c = getContext();
int idx = c.textureIdx;
D3D11_TEXTURE2D_DESC desc = {};
m_textures[idx].texture->GetDesc(&desc);
D3D11_BOX box = {};
box.left = xoffset;
box.top = yoffset;
box.right = xoffset + width;
box.bottom = yoffset + height;
box.front = 0;
box.back = 1;
c.m_pDeviceContext->UpdateSubresource(
m_textures[idx].texture,
level,
&box,
data,
static_cast<UINT>(width * 4),
static_cast<UINT>(width * height * 4)
);
}
void Renderer::TextureDynamicUpdateEnd() {}
void Renderer::TextureDynamicUpdateStart() {}
void Renderer::TextureFree(int idx)
{
PROFILER_SCOPE("Renderer::TextureFree", "TextureFree", MP_PURPLE4);
m_textures[idx].texture->Release();
m_textures[idx].view->Release();
m_textures[idx].view = NULL;
m_textures[idx].allocated = false;
m_textures[idx].texture = NULL;
}
void Renderer::TextureGetStats() {}
ID3D11ShaderResourceView* Renderer::TextureGetTexture(int idx)
{
if (idx < MAX_TEXTURES) {
if (m_textures[idx].allocated) return m_textures[idx].view;
}
return NULL;
}
int Renderer::TextureGetTextureLevels()
{
Context& c = getContext();
return m_textures[c.textureIdx].mipLevels;
}
void Renderer::TextureSetParam(int param, int value)
{
Context& c = getContext();
int idx = c.textureIdx;
switch (param)
{
case GL_TEXTURE_MIN_FILTER:
m_textures[idx].samplerParams &= ~4u;
if (value == GL_LINEAR)
m_textures[idx].samplerParams |= 4u;
break;
case GL_TEXTURE_MAG_FILTER:
m_textures[idx].samplerParams &= ~8u;
if (value == GL_LINEAR)
m_textures[idx].samplerParams |= 8u;
break;
case GL_TEXTURE_WRAP_S:
m_textures[idx].samplerParams &= ~1u;
if (value == GL_CLAMP)
m_textures[idx].samplerParams |= 1u;
break;
case GL_TEXTURE_WRAP_T:
m_textures[idx].samplerParams &= ~2u;
if (value == GL_CLAMP)
m_textures[idx].samplerParams |= 2u;
break;
}
}
void Renderer::TextureSetTextureLevels(int levels)
{
Context& c = getContext();
m_textures[c.textureIdx].mipLevels = levels;
}
void Renderer::UpdateTextureState(bool bVertex)
{
Context& c = getContext();
ID3D11SamplerState* pSampler = GetManagedSamplerState();
if (bVertex)
c.m_pDeviceContext->VSSetSamplers(0, 1, &pSampler);
else
c.m_pDeviceContext->PSSetSamplers(0, 1, &pSampler);
}
void user_flush_data(png_struct_def* png_ptr) {}
void user_write_data(png_struct_def* png_ptr, unsigned char* src, size_t length)
{
int bytesToWrite = static_cast<int>(dataEnd - dataCurr);
if (static_cast<int>(length) < bytesToWrite)
bytesToWrite = (int)length;
std::memcpy(dataCurr, src, bytesToWrite);
dataCurr += bytesToWrite;
}
int user_write_data_bytes_written()
{
return static_cast<int>(dataCurr - dataStart);
}
void user_write_data_init(unsigned char* pBuffer, int size)
{
dataStart = pBuffer;
dataCurr = pBuffer;
dataEnd = pBuffer + size;
}
@@ -0,0 +1,185 @@
/*
MIT License
Copyright (c) 2026 Patoke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "stdafx.h"
#include "Renderer.h"
void Renderer::DrawVertexBuffer(C4JRender::ePrimitiveType PrimitiveType, int count, ID3D11Buffer *buffer, C4JRender::eVertexType vType,
C4JRender::ePixelShaderType psType)
{
PROFILER_SCOPE("Renderer::DrawVertexBuffer", "DrawVertexBuffer", MP_RED2);
Renderer::Context &c = getContext();
ID3D11DeviceContext *d3d11 = c.m_pDeviceContext;
int drawCount = count;
bool indexed = false;
PROFILER_SCOPE("Renderer::DrawVertexBuffer", "DrawVertexSetup", MP_RED2);
DrawVertexSetup(vType, psType, PrimitiveType, &drawCount, &indexed);
StateUpdate();
const UINT stride = vertexStrideTable[vType];
const UINT offset = 0;
d3d11->IASetVertexBuffers(0, 1, &buffer, &stride, &offset);
if (indexed)
d3d11->DrawIndexed(drawCount, 0, 0);
else
d3d11->Draw(count, 0);
}
void Renderer::DrawVertexSetup(C4JRender::eVertexType vType, C4JRender::ePixelShaderType psType, C4JRender::ePrimitiveType PrimitiveType, int *count,
bool *indexed)
{
PROFILER_SCOPE("Renderer::DrawVertexSetup", "DrawVertexSetup", MP_RED2);
Renderer::Context &c = getContext();
ID3D11DeviceContext *d3d11 = c.m_pDeviceContext;
C4JRender::eVertexType effectiveVertexType = vType;
if (effectiveVertexType == C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1 && c.lightingEnabled)
effectiveVertexType = C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT;
if (effectiveVertexType != activeVertexType)
{
d3d11->VSSetShader(vertexShaderTable[effectiveVertexType], NULL, 0);
d3d11->IASetInputLayout(inputLayoutTable[effectiveVertexType]);
activeVertexType = effectiveVertexType;
}
if (psType != activePixelType)
{
d3d11->PSSetShader(pixelShaderTable[psType], NULL, 0);
activePixelType = psType;
}
D3D11_MAPPED_SUBRESOURCE mapped = {};
if (c.matrixDirty[MATRIX_MODE_MODELVIEW])
{
d3d11->Map(c.m_modelViewMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
memcpy(mapped.pData, MatrixGet(MATRIX_MODE_MODELVIEW), sizeof(DirectX::XMMATRIX));
d3d11->Unmap(c.m_modelViewMatrix, 0);
c.matrixDirty[MATRIX_MODE_MODELVIEW] = false;
}
if (c.matrixDirty[MATRIX_MODE_MODELVIEW_PROJECTION])
{
d3d11->Map(c.m_projectionMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
memcpy(mapped.pData, MatrixGet(MATRIX_MODE_MODELVIEW_PROJECTION), sizeof(DirectX::XMMATRIX));
d3d11->Unmap(c.m_projectionMatrix, 0);
c.matrixDirty[MATRIX_MODE_MODELVIEW_PROJECTION] = false;
}
if (c.matrixDirty[MATRIX_MODE_MODELVIEW_TEXTURE])
{
d3d11->Map(c.m_textureMatrix, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
memcpy(mapped.pData, MatrixGet(MATRIX_MODE_MODELVIEW_TEXTURE), sizeof(DirectX::XMMATRIX));
d3d11->Unmap(c.m_textureMatrix, 0);
c.matrixDirty[MATRIX_MODE_MODELVIEW_TEXTURE] = false;
}
UpdateFogState();
UpdateViewportState();
UpdateLightingState();
UpdateTexGenState();
d3d11->IASetPrimitiveTopology(g_topologies[PrimitiveType]);
if (PrimitiveType == C4JRender::PRIMITIVE_TYPE_QUAD_LIST)
{
d3d11->IASetIndexBuffer(quadIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
*count = (*count * 6) / 4;
*indexed = true;
return;
}
if (PrimitiveType == C4JRender::PRIMITIVE_TYPE_TRIANGLE_FAN)
{
d3d11->IASetIndexBuffer(fanIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
*count = (*count - 2) * 3;
*indexed = true;
return;
}
d3d11->IASetIndexBuffer(NULL, DXGI_FORMAT_R16_UINT, 0);
*indexed = false;
}
void Renderer::DrawVertices(C4JRender::ePrimitiveType PrimitiveType, int count, void *vertices, C4JRender::eVertexType vType,
C4JRender::ePixelShaderType psType)
{
PROFILER_SCOPE("Renderer::DrawVertices", "DrawVertices", MP_RED2);
Renderer::Context &c = getContext();
ID3D11DeviceContext *d3d11 = c.m_pDeviceContext;
Renderer::CommandBuffer *commandBuffer = c.commandBuffer;
if (commandBuffer != NULL)
{
C4JRender::eVertexType effectiveVertexType = vType;
if (effectiveVertexType == C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1 && c.lightingEnabled)
effectiveVertexType = C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT;
c.recordingPrimitiveType = PrimitiveType;
c.recordingVertexType = effectiveVertexType;
const UINT stride = vertexStrideTable[effectiveVertexType];
commandBuffer->AddVertices(stride, static_cast<UINT>(count), vertices, c);
return;
}
int drawCount = count;
bool indexed = false;
PROFILER_SCOPE("Renderer::DrawVertices", "DrawVertexSetup", MP_RED2);
DrawVertexSetup(vType, psType, PrimitiveType, &drawCount, &indexed);
const UINT stride = vertexStrideTable[vType];
const UINT vertexBytes = stride * static_cast<UINT>(count);
assert(vertexBytes <= Context::VERTEX_BUFFER_SIZE);
UINT vertexOffset = c.dynamicVertexOffset;
if (vertexOffset + vertexBytes > Context::VERTEX_BUFFER_SIZE)
vertexOffset = 0;
D3D11_MAPPED_SUBRESOURCE mapped = {};
const D3D11_MAP mapType = vertexOffset == 0 ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE;
const HRESULT hr = d3d11->Map(c.dynamicVertexBuffer, 0, mapType, 0, &mapped);
if (FAILED(hr))
printf("ERROR: 0x%x\n", static_cast<unsigned int>(hr));
memcpy(reinterpret_cast<std::uint8_t *>(mapped.pData) + vertexOffset, vertices, vertexBytes);
d3d11->Unmap(c.dynamicVertexBuffer, 0);
StateUpdate();
ID3D11Buffer *dynamicBuffer = c.dynamicVertexBuffer;
d3d11->IASetVertexBuffers(0, 1, &dynamicBuffer, &stride, &vertexOffset);
if (indexed)
d3d11->DrawIndexed(drawCount, 0, 0);
else
d3d11->Draw(count, 0);
c.dynamicVertexOffset = vertexOffset + vertexBytes;
}
+25
View File
@@ -0,0 +1,25 @@
/*
MIT License
Copyright (c) 2026 Patoke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "stdafx.h"