#include "Renderer11.h" #include "..\Specific\input.h" #include "..\Specific\winmain.h" #include "..\Specific\roomload.h" #include "..\Specific\game.h" #include "..\Specific\configuration.h" #include "..\Game\draw.h" #include "..\Game\healt.h" #include "..\Game\pickup.h" #include "..\Game\inventory.h" #include "..\Game\gameflow.h" #include "..\Game\Lara.h" #include "..\Game\effect2.h" #include "..\Game\rope.h" #include "..\Game\items.h" #include "..\Game\Camera.h" #include "..\Game\healt.h" #include "../Game/tomb4fx.h" #include "math.h" #include #include #include #include "../Game/misc.h" #include "../Game/footprint.h" using ns = chrono::nanoseconds; using get_time = chrono::steady_clock; extern std::deque footprints; int SortLightsFunction(RendererLight* a, RendererLight* b) { if (a->Dynamic > b->Dynamic) return -1; return 0; } bool SortRoomsFunction(RendererRoom* a, RendererRoom* b) { return (a->Distance < b->Distance); } int SortRoomsFunctionNonStd(RendererRoom* a, RendererRoom* b) { return (a->Distance - b->Distance); } Renderer11::Renderer11() { initialiseHairRemaps(); m_blinkColorDirection = 1; } Renderer11::~Renderer11() { FreeRendererData(); DX11_RELEASE(m_backBufferRTV); DX11_RELEASE(m_backBufferTexture); DX11_RELEASE(m_depthStencilState); DX11_RELEASE(m_depthStencilTexture); DX11_RELEASE(m_depthStencilView); DX11_DELETE(m_primitiveBatch); DX11_DELETE(m_spriteBatch); DX11_DELETE(m_gameFont); DX11_DELETE(m_states); for (int i = 0; i < NUM_CAUSTICS_TEXTURES; i++) DX11_DELETE(m_caustics[i]); DX11_DELETE(m_titleScreen); DX11_DELETE(m_binocularsTexture); DX11_DELETE(m_whiteTexture); DX11_DELETE(m_logo); DX11_RELEASE(m_vsRooms); DX11_RELEASE(m_psRooms); DX11_RELEASE(m_vsItems); DX11_RELEASE(m_psItems); DX11_RELEASE(m_vsStatics); DX11_RELEASE(m_psStatics); DX11_RELEASE(m_vsHairs); DX11_RELEASE(m_psHairs); DX11_RELEASE(m_vsSky); DX11_RELEASE(m_psSky); DX11_RELEASE(m_vsSprites); DX11_RELEASE(m_psSprites); DX11_RELEASE(m_vsSolid); DX11_RELEASE(m_psSolid); DX11_RELEASE(m_vsInventory); DX11_RELEASE(m_psInventory); DX11_RELEASE(m_vsFullScreenQuad); DX11_RELEASE(m_psFullScreenQuad); DX11_RELEASE(m_cbCameraMatrices); DX11_RELEASE(m_cbItem); DX11_RELEASE(m_cbStatic); DX11_RELEASE(m_cbLights); DX11_RELEASE(m_cbMisc); DX11_DELETE(m_renderTarget); DX11_DELETE(m_dumpScreenRenderTarget); DX11_DELETE(m_shadowMap); DX11_RELEASE(m_swapChain); DX11_RELEASE(m_context); DX11_RELEASE(m_device); } void Renderer11::FreeRendererData() { m_meshPointersToMesh.clear(); for (int i = 0; i < ID_NUMBER_OBJECTS; i++) DX11_DELETE(m_moveableObjects[i]); free(m_moveableObjects); for (int i = 0; i < g_NumSprites; i++) DX11_DELETE(m_sprites[i]); free(m_sprites); for (int i = 0; i < ID_NUMBER_OBJECTS; i++) DX11_DELETE(m_spriteSequences[i]); free(m_spriteSequences); for (int i = 0; i < NUM_STATICS; i++) DX11_DELETE(m_staticObjects[i]); free(m_staticObjects); for (int i = 0; i < NUM_ROOMS; i++) DX11_DELETE(m_rooms[i]); free(m_rooms); for (int i = 0; i < m_numAnimatedTextureSets; i++) DX11_DELETE(m_animatedTextureSets[i]); free(m_animatedTextureSets); DX11_DELETE(m_textureAtlas); DX11_DELETE(m_skyTexture); DX11_DELETE(m_roomsVertexBuffer); DX11_DELETE(m_roomsIndexBuffer); DX11_DELETE(m_moveablesVertexBuffer); DX11_DELETE(m_moveablesIndexBuffer); DX11_DELETE(m_staticsVertexBuffer); DX11_DELETE(m_staticsIndexBuffer); } bool Renderer11::Create() { D3D_FEATURE_LEVEL levels[1] = { D3D_FEATURE_LEVEL_10_0 }; D3D_FEATURE_LEVEL featureLevel; HRESULT res; #ifdef _RELEASE res = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, levels, 1, D3D11_SDK_VERSION, &m_device, &featureLevel, &m_context); #else res = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, levels, 1, D3D11_SDK_VERSION, &m_device, &featureLevel, &m_context); // D3D11_CREATE_DEVICE_DEBUG #endif if (FAILED(res)) return false; return true; } bool Renderer11::EnumerateVideoModes() { HRESULT res; IDXGIFactory* dxgiFactory = NULL; res = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&dxgiFactory); if (FAILED(res)) return false; IDXGIAdapter* dxgiAdapter = NULL; for (int i = 0; dxgiFactory->EnumAdapters(i, &dxgiAdapter) != DXGI_ERROR_NOT_FOUND; i++) { DXGI_ADAPTER_DESC adapterDesc; UINT stringLength; char videoCardDescription[128]; dxgiAdapter->GetDesc(&adapterDesc); int error = wcstombs_s(&stringLength, videoCardDescription, 128, adapterDesc.Description, 128); RendererVideoAdapter adapter; adapter.Index = i; adapter.Name = videoCardDescription; printf("Adapter %d\n", i); printf("\t Device Name: %s\n", videoCardDescription); IDXGIOutput* output = NULL; res = dxgiAdapter->EnumOutputs(0, &output); if (FAILED(res)) return false; UINT numModes = 0; DXGI_MODE_DESC* displayModes = NULL; DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM; // Get the number of elements res = output->GetDisplayModeList(format, 0, &numModes, NULL); if (FAILED(res)) return false; // Get the list displayModes = new DXGI_MODE_DESC[numModes]; res = output->GetDisplayModeList(format, 0, &numModes, displayModes); if (FAILED(res)) { delete displayModes; return false; } for (int j = 0; j < numModes; j++) { DXGI_MODE_DESC* mode = &displayModes[j]; RendererDisplayMode newMode; // discard lower resolutions if (mode->Width < 1024 || mode->Height < 768) continue; newMode.Width = mode->Width; newMode.Height = mode->Height; newMode.RefreshRate = mode->RefreshRate.Numerator / mode->RefreshRate.Denominator; bool found = false; for (int k = 0; k < adapter.DisplayModes.size(); k++) { RendererDisplayMode* currentMode = &adapter.DisplayModes[k]; if (currentMode->Width == newMode.Width && currentMode->Height == newMode.Height && currentMode->RefreshRate == newMode.RefreshRate) { found = true; break; } } if (found) continue; adapter.DisplayModes.push_back(newMode); printf("\t\t %d x %d %d Hz\n", newMode.Width, newMode.Height, newMode.RefreshRate); } m_adapters.push_back(adapter); delete displayModes; } dxgiFactory->Release(); return true; } bool Renderer11::initialiseScreen(int w, int h, int refreshRate, bool windowed, HWND handle, bool reset) { HRESULT res; DXGI_SWAP_CHAIN_DESC sd; sd.BufferDesc.Width = w; sd.BufferDesc.Height = h; sd.BufferDesc.RefreshRate.Numerator = refreshRate; sd.BufferDesc.RefreshRate.Denominator = 1; sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE; sd.BufferDesc.Scaling = DXGI_MODE_SCALING_STRETCHED; sd.Windowed = windowed; sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; sd.OutputWindow = handle; sd.SampleDesc.Count = 1; sd.SampleDesc.Quality = 0; sd.BufferCount = 1; sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; IDXGIDevice* dxgiDevice = NULL; res = m_device->QueryInterface(__uuidof(IDXGIDevice), (void**)& dxgiDevice); if (FAILED(res)) return false; IDXGIAdapter* dxgiAdapter = NULL; res = dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)& dxgiAdapter); if (FAILED(res)) return false; IDXGIFactory* dxgiFactory = NULL; res = dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void**)& dxgiFactory); if (FAILED(res)) return false; if (reset) { // Always return to windowed mode otherwise crash will happen m_swapChain->SetFullscreenState(false, NULL); m_swapChain->Release(); } m_swapChain = NULL; res = dxgiFactory->CreateSwapChain(m_device, &sd, &m_swapChain); if (FAILED(res)) return false; dxgiFactory->MakeWindowAssociation(handle, 0); res = m_swapChain->SetFullscreenState(!windowed, NULL); dxgiDevice->Release(); dxgiAdapter->Release(); dxgiFactory->Release(); // Initialise the back buffer m_backBufferTexture = NULL; res = m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast (&m_backBufferTexture)); if (FAILED(res)) return false; m_backBufferRTV = NULL; res = m_device->CreateRenderTargetView(m_backBufferTexture, NULL, &m_backBufferRTV); if (FAILED(res)) return false; D3D11_TEXTURE2D_DESC depthStencilDesc; depthStencilDesc.Width = w; depthStencilDesc.Height = h; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthStencilDesc.SampleDesc.Count = 1; depthStencilDesc.SampleDesc.Quality = 0; depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; m_depthStencilTexture = NULL; res = m_device->CreateTexture2D(&depthStencilDesc, NULL, &m_depthStencilTexture); if (FAILED(res)) return false; m_depthStencilView = NULL; res = m_device->CreateDepthStencilView(m_depthStencilTexture, NULL, &m_depthStencilView); if (FAILED(res)) return false; // Bind the back buffer and the depth stencil m_context->OMSetRenderTargets(1, &m_backBufferRTV, m_depthStencilView); // Initialise sprites and font m_spriteBatch = new SpriteBatch(m_context); m_gameFont = new SpriteFont(m_device, L"Font.spritefont"); m_primitiveBatch = new PrimitiveBatch(m_context); // Initialise buffers m_renderTarget = RenderTarget2D::Create(m_device, w, h, DXGI_FORMAT_R8G8B8A8_UNORM); m_dumpScreenRenderTarget = RenderTarget2D::Create(m_device, w, h, DXGI_FORMAT_R8G8B8A8_UNORM); m_shadowMap = RenderTarget2D::Create(m_device, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, DXGI_FORMAT_R32_FLOAT); // Shadow map /*D3D11_TEXTURE2D_DESC depthTexDesc; ZeroMemory(&depthTexDesc, sizeof(D3D11_TEXTURE2D_DESC)); depthTexDesc.Width = SHADOW_MAP_SIZE; depthTexDesc.Height = SHADOW_MAP_SIZE; depthTexDesc.MipLevels = 1; depthTexDesc.ArraySize = 1; depthTexDesc.SampleDesc.Count = 1; depthTexDesc.SampleDesc.Quality = 0; depthTexDesc.Format = DXGI_FORMAT_R32_TYPELESS; depthTexDesc.Usage = D3D11_USAGE_DEFAULT; depthTexDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE; depthTexDesc.CPUAccessFlags = 0; depthTexDesc.MiscFlags = 0; res = m_device->CreateTexture2D(&depthTexDesc, NULL, &m_shadowMapTexture); if (FAILED(res)) return false; D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc; ZeroMemory(&dsvDesc, sizeof(D3D11_DEPTH_STENCIL_VIEW_DESC)); dsvDesc.Format = depthTexDesc.Format; dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; dsvDesc.Texture2D.MipSlice = 0; m_shadowMapDSV = NULL; res = m_device->CreateDepthStencilView(m_shadowMapTexture, &dsvDesc, &m_shadowMapDSV); if (FAILED(res)) return false; D3D11_SHADER_RESOURCE_VIEW_DESC shaderDesc; shaderDesc.Format = DXGI_FORMAT_R32_FLOAT; shaderDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; shaderDesc.Texture2D.MostDetailedMip = 0; shaderDesc.Texture2D.MipLevels = 1; res = m_device->CreateShaderResourceView(m_shadowMapTexture, &shaderDesc, &m_shadowMapRV); if (FAILED(res)) return false;*/ // Initialise viewport m_viewport.TopLeftX = 0; m_viewport.TopLeftY = 0; m_viewport.Width = w; m_viewport.Height = h; m_viewport.MinDepth = 0.0f; m_viewport.MaxDepth = 1.0f; m_shadowMapViewport.TopLeftX = 0; m_shadowMapViewport.TopLeftY = 0; m_shadowMapViewport.Width = SHADOW_MAP_SIZE; m_shadowMapViewport.Height = SHADOW_MAP_SIZE; m_shadowMapViewport.MinDepth = 0.0f; m_shadowMapViewport.MaxDepth = 1.0f; m_viewportToolkit = new Viewport(m_viewport.TopLeftX, m_viewport.TopLeftY, m_viewport.Width, m_viewport.Height, m_viewport.MinDepth, m_viewport.MaxDepth); if (windowed) { SetWindowLong(WindowsHandle, GWL_STYLE, WS_OVERLAPPEDWINDOW); SetWindowPos(WindowsHandle, HWND_TOP, 0, 0, g_Configuration.Width, g_Configuration.Height, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } else { SetWindowLong(WindowsHandle, GWL_STYLE, WS_POPUPWINDOW); SetWindowPos(WindowsHandle, HWND_TOP, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } UpdateWindow(handle); return true; } bool Renderer11::Initialise(int w, int h, int refreshRate, bool windowed, HWND handle) { HRESULT res; DB_Log(2, "Renderer::Initialise - DLL"); printf("Initialising DX11\n"); CoInitialize(NULL); ScreenWidth = w; ScreenHeight = h; Windowed = windowed; if (!initialiseScreen(w, h, refreshRate, windowed, handle, false)) return false; // Initialise render states m_states = new CommonStates(m_device); // Load caustics const char* causticsNames[NUM_CAUSTICS_TEXTURES] = { "CausticsRender_001.bmp", "CausticsRender_002.bmp", "CausticsRender_003.bmp", "CausticsRender_004.bmp", "CausticsRender_005.bmp", "CausticsRender_006.bmp", "CausticsRender_007.bmp", "CausticsRender_008.bmp", "CausticsRender_009.bmp", "CausticsRender_010.bmp", "CausticsRender_011.bmp", "CausticsRender_012.bmp", "CausticsRender_013.bmp", "CausticsRender_014.bmp", "CausticsRender_015.bmp", "CausticsRender_016.bmp" }; for (int i = 0; i < NUM_CAUSTICS_TEXTURES; i++) { m_caustics[i] = Texture2D::LoadFromFile(m_device, causticsNames[i]); if (m_caustics[i] == NULL) return false; } m_titleScreen = Texture2D::LoadFromFile(m_device, (char*)g_GameFlow->GetLevel(0)->Background.c_str()); if (m_titleScreen == NULL) return false; m_binocularsTexture = Texture2D::LoadFromFile(m_device, "Binoculars.png"); if (m_binocularsTexture == NULL) return false; m_whiteTexture = Texture2D::LoadFromFile(m_device, "WhiteSprite.png"); if (m_whiteTexture == NULL) return false; m_logo = Texture2D::LoadFromFile(m_device, "Logo.png"); if (m_logo == NULL) return false; // Load shaders ID3D10Blob* blob; m_vsRooms = compileVertexShader("Shaders\\DX11_Rooms.fx", "VS", "vs_4_0", &blob); if (m_vsRooms == NULL) return false; // Initialise input layout using the first vertex shader D3D11_INPUT_ELEMENT_DESC inputLayout[] = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0}, {"BLENDINDICES", 0, DXGI_FORMAT_R32_FLOAT, 0, 48, D3D11_INPUT_PER_VERTEX_DATA, 0}, }; m_inputLayout = NULL; res = m_device->CreateInputLayout(inputLayout, 5, blob->GetBufferPointer(), blob->GetBufferSize(), &m_inputLayout); if (FAILED(res)) return false; m_psRooms = compilePixelShader("Shaders\\DX11_Rooms.fx", "PS", "ps_4_0", &blob); if (m_psRooms == NULL) return false; m_vsItems = compileVertexShader("Shaders\\DX11_Items.fx", "VS", "vs_4_0", &blob); if (m_vsItems == NULL) return false; m_psItems = compilePixelShader("Shaders\\DX11_Items.fx", "PS", "ps_4_0", &blob); if (m_psItems == NULL) return false; m_vsStatics = compileVertexShader("Shaders\\DX11_Statics.fx", "VS", "vs_4_0", &blob); if (m_vsStatics == NULL) return false; m_psStatics = compilePixelShader("Shaders\\DX11_Statics.fx", "PS", "ps_4_0", &blob); if (m_psStatics == NULL) return false; m_vsHairs = compileVertexShader("Shaders\\DX11_Hairs.fx", "VS", "vs_4_0", &blob); if (m_vsHairs == NULL) return false; m_psHairs = compilePixelShader("Shaders\\DX11_Hairs.fx", "PS", "ps_4_0", &blob); if (m_psHairs == NULL) return false; m_vsSky = compileVertexShader("Shaders\\DX11_Sky.fx", "VS", "vs_4_0", &blob); if (m_vsSky == NULL) return false; m_psSky = compilePixelShader("Shaders\\DX11_Sky.fx", "PS", "ps_4_0", &blob); if (m_psSky == NULL) return false; m_vsSprites = compileVertexShader("Shaders\\DX11_Sprites.fx", "VS", "vs_4_0", &blob); if (m_vsSprites == NULL) return false; m_psSprites = compilePixelShader("Shaders\\DX11_Sprites.fx", "PS", "ps_4_0", &blob); if (m_psSprites == NULL) return false; m_vsSolid = compileVertexShader("Shaders\\DX11_Solid.fx", "VS", "vs_4_0", &blob); if (m_vsSolid == NULL) return false; m_psSolid = compilePixelShader("Shaders\\DX11_Solid.fx", "PS", "ps_4_0", &blob); if (m_psSolid == NULL) return false; m_vsInventory = compileVertexShader("Shaders\\DX11_Inventory.fx", "VS", "vs_4_0", &blob); if (m_vsInventory == NULL) return false; m_psInventory = compilePixelShader("Shaders\\DX11_Inventory.fx", "PS", "ps_4_0", &blob); if (m_psInventory == NULL) return false; m_vsFullScreenQuad = compileVertexShader("Shaders\\DX11_FullScreenQuad.fx", "VS", "vs_4_0", &blob); if (m_vsFullScreenQuad == NULL) return false; m_psFullScreenQuad = compilePixelShader("Shaders\\DX11_FullScreenQuad.fx", "PS", "ps_4_0", &blob); if (m_psFullScreenQuad == NULL) return false; m_vsShadowMap = compileVertexShader("Shaders\\DX11_ShadowMap.fx", "VS", "vs_4_0", &blob); if (m_vsShadowMap == NULL) return false; m_psShadowMap = compilePixelShader("Shaders\\DX11_ShadowMap.fx", "PS", "ps_4_0", &blob); if (m_psShadowMap == NULL) return false; // Initialise constant buffers m_cbCameraMatrices = createConstantBuffer(sizeof(CCameraMatrixBuffer)); m_cbItem = createConstantBuffer(sizeof(CItemBuffer)); m_cbStatic = createConstantBuffer(sizeof(CStaticBuffer)); m_cbLights = createConstantBuffer(sizeof(CLightBuffer)); m_cbMisc = createConstantBuffer(sizeof(CMiscBuffer)); m_cbShadowMap = createConstantBuffer(sizeof(CShadowLightBuffer)); m_cbRoom = createConstantBuffer(sizeof(CRoomBuffer)); m_currentCausticsFrame = 0; m_firstWeather = true; // Preallocate lists m_roomsToDraw.Reserve(NUM_ROOMS); m_itemsToDraw.Reserve(NUM_ITEMS); m_effectsToDraw.Reserve(NUM_ITEMS); m_lightsToDraw.Reserve(MAX_LIGHTS_DRAW); m_dynamicLights.Reserve(MAX_DYNAMIC_LIGHTS); m_staticsToDraw.Reserve(MAX_DRAW_STATICS); m_spritesToDraw.Reserve(MAX_SPRITES); m_lines3DToDraw.Reserve(MAX_LINES_3D); m_lines2DToDraw.Reserve(MAX_LINES_2D); m_tempItemLights.Reserve(MAX_LIGHTS); m_spritesBuffer = (RendererSpriteToDraw*)malloc(sizeof(RendererSpriteToDraw) * MAX_SPRITES); m_lines3DBuffer = (RendererLine3D*)malloc(sizeof(RendererLine3D) * MAX_LINES_3D); m_lines2DBuffer = (RendererLine2D*)malloc(sizeof(RendererLine2D) * MAX_LINES_2D); m_pickupRotation = 0; for (int i = 0; i < NUM_ITEMS; i++) { m_items[i].Lights.Reserve(MAX_LIGHTS_PER_ITEM); m_effects[i].Lights.Reserve(MAX_LIGHTS_PER_ITEM); } m_textureAtlas = NULL; m_skyTexture = NULL; D3D11_BLEND_DESC desc; desc.RenderTarget[0].BlendEnable = TRUE; desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA; desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_DEST_COLOR; desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE; m_device->CreateBlendState(&desc,&m_subtractiveBlendState); return true; } int Renderer11::Draw() { drawScene(false); drawFinalPass(); return 0; } void Renderer11::UpdateCameraMatrices(float posX, float posY, float posZ, float targetX, float targetY, float targetZ, float roll, float fov) { g_Configuration.MaxDrawDistance = 200; Vector3 up = -Vector3::UnitY; Matrix upRotation = Matrix::CreateFromYawPitchRoll(0.0f, 0.0f, roll); up = Vector3::Transform(up, upRotation); FieldOfView = fov; View = Matrix::CreateLookAt(Vector3(posX, posY, posZ), Vector3(targetX, targetY, targetZ), up); Projection = Matrix::CreatePerspectiveFieldOfView(fov, ScreenWidth / (float)ScreenHeight, 20.0f, g_Configuration.MaxDrawDistance * 1024.0f); m_stCameraMatrices.View = View; m_stCameraMatrices.Projection = Projection; } bool Renderer11::drawAmbientCubeMap(short roomNumber) { return true; } void Renderer11::clearSceneItems() { m_roomsToDraw.Clear(); m_itemsToDraw.Clear(); m_effectsToDraw.Clear(); m_lightsToDraw.Clear(); m_staticsToDraw.Clear(); m_spritesToDraw.Clear(); m_lines3DToDraw.Clear(); m_lines2DToDraw.Clear(); } bool Renderer11::drawHorizonAndSky() { // Update the sky GameScriptLevel* level = g_GameFlow->GetLevel(CurrentLevel); Vector4 color = Vector4(SkyColor1.r / 255.0f, SkyColor1.g / 255.0f, SkyColor1.b / 255.0f, 1.0f); if (!level->Horizon) return true; if (BinocularRange) AlterFOV(14560 - BinocularRange); // Storm if (level->Storm) { if (Unk_00E6D74C || Unk_00E6D73C) { UpdateStorm(); if (StormTimer > -1) StormTimer--; if (!StormTimer) SoundEffect(SFX_THUNDER_RUMBLE, NULL, 0); } else if (!(rand() & 0x7F)) { Unk_00E6D74C = (rand() & 0x1F) + 16; Unk_00E6E4DC = rand() + 256; StormTimer = (rand() & 3) + 12; } color = Vector4((SkyStormColor[0]) / 255.0f, SkyStormColor[1] / 255.0f, SkyStormColor[2] / 255.0f, 1.0f); } ID3D11SamplerState* sampler; UINT stride = sizeof(RendererVertex); UINT offset = 0; // Draw the sky Matrix rotation = Matrix::CreateRotationX(PI); RendererVertex vertices[4]; float size = 9728.0f; vertices[0].Position.x = -size / 2.0f; vertices[0].Position.y = 0.0f; vertices[0].Position.z = size / 2.0f; vertices[0].UV.x = 0.0f; vertices[0].UV.y = 0.0f; vertices[0].Color.x = 1.0f; vertices[0].Color.y = 1.0f; vertices[0].Color.z = 1.0f; vertices[0].Color.w = 1.0f; vertices[1].Position.x = size / 2.0f; vertices[1].Position.y = 0.0f; vertices[1].Position.z = size / 2.0f; vertices[1].UV.x = 1.0f; vertices[1].UV.y = 0.0f; vertices[1].Color.x = 1.0f; vertices[1].Color.y = 1.0f; vertices[1].Color.z = 1.0f; vertices[1].Color.w = 1.0f; vertices[2].Position.x = size / 2.0f; vertices[2].Position.y = 0.0f; vertices[2].Position.z = -size / 2.0f; vertices[2].UV.x = 1.0f; vertices[2].UV.y = 1.0f; vertices[2].Color.x = 1.0f; vertices[2].Color.y = 1.0f; vertices[2].Color.z = 1.0f; vertices[2].Color.w = 1.0f; vertices[3].Position.x = -size / 2.0f; vertices[3].Position.y = 0.0f; vertices[3].Position.z = -size / 2.0f; vertices[3].UV.x = 0.0f; vertices[3].UV.y = 1.0f; vertices[3].Color.x = 1.0f; vertices[3].Color.y = 1.0f; vertices[3].Color.z = 1.0f; vertices[3].Color.w = 1.0f; m_context->VSSetShader(m_vsSky, NULL, 0); m_context->PSSetShader(m_psSky, NULL, 0); m_stCameraMatrices.View = View.Transpose(); m_stCameraMatrices.Projection = Projection.Transpose(); updateConstantBuffer(m_cbCameraMatrices, &m_stCameraMatrices, sizeof(CCameraMatrixBuffer)); m_context->VSSetConstantBuffers(0, 1, &m_cbCameraMatrices); m_stMisc.AlphaTest = true; updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); m_context->PSSetShaderResources(0, 1, &m_skyTexture->ShaderResourceView); sampler = m_states->AnisotropicClamp(); m_context->PSSetSamplers(0, 1, &sampler); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); for (int i = 0; i < 2; i++) { Matrix translation = Matrix::CreateTranslation(Camera.pos.x + SkyPos1 - i * 9728.0f, Camera.pos.y - 1536.0f, Camera.pos.z); Matrix world = rotation * translation; m_stStatic.World = (rotation * translation).Transpose(); m_stStatic.Color = color; updateConstantBuffer(m_cbStatic, &m_stStatic, sizeof(CStaticBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbStatic); m_context->PSSetConstantBuffers(1, 1, &m_cbStatic); m_primitiveBatch->Begin(); m_primitiveBatch->DrawQuad(vertices[0], vertices[1], vertices[2], vertices[3]); m_primitiveBatch->End(); } // Draw horizon if (m_moveableObjects[ID_HORIZON] != NULL) { m_context->IASetVertexBuffers(0, 1, &m_moveablesVertexBuffer->Buffer, &stride, &offset); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); m_context->IASetIndexBuffer(m_moveablesIndexBuffer->Buffer, DXGI_FORMAT_R32_UINT, 0); m_context->PSSetShaderResources(0, 1, &m_textureAtlas->ShaderResourceView); sampler = m_states->AnisotropicClamp(); m_context->PSSetSamplers(0, 1, &sampler); RendererObject* moveableObj = m_moveableObjects[ID_HORIZON]; m_stStatic.World = Matrix::CreateTranslation(Camera.pos.x, Camera.pos.y, Camera.pos.z).Transpose(); m_stStatic.Position = Vector4::Zero; m_stStatic.Color = Vector4::One; updateConstantBuffer(m_cbStatic, &m_stStatic, sizeof(CStaticBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbStatic); m_context->PSSetConstantBuffers(1, 1, &m_cbStatic); m_stMisc.AlphaTest = true; updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); for (int k = 0; k < moveableObj->ObjectMeshes.size(); k++) { RendererMesh* mesh = moveableObj->ObjectMeshes[k]; for (int j = 0; j < NUM_BUCKETS; j++) { RendererBucket* bucket = &mesh->Buckets[j]; if (bucket->Vertices.size() == 0) continue; if (j == RENDERER_BUCKET_SOLID_DS || j == RENDERER_BUCKET_TRANSPARENT_DS) m_context->RSSetState(m_states->CullNone()); else m_context->RSSetState(m_states->CullCounterClockwise()); if (j == RENDERER_BUCKET_TRANSPARENT || j == RENDERER_BUCKET_TRANSPARENT_DS) m_context->OMSetBlendState(m_states->Additive(), NULL, 0xFFFFFFFF); else m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); // Draw vertices m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } } } // Clear just the Z-buffer so we can start drawing on top of the horizon m_context->ClearDepthStencilView(m_currentRenderTarget->DepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); return true; } bool Renderer11::drawRooms(bool transparent, bool animated) { UINT stride = sizeof(RendererVertex); UINT offset = 0; int firstBucket = (transparent ? 2 : 0); int lastBucket = (transparent ? 4 : 2); if (!animated) { // Set vertex buffer m_context->IASetVertexBuffers(0, 1, &m_roomsVertexBuffer->Buffer, &stride, &offset); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); m_context->IASetIndexBuffer(m_roomsIndexBuffer->Buffer, DXGI_FORMAT_R32_UINT, 0); } // Set shaders m_context->VSSetShader(m_vsRooms, NULL, 0); m_context->PSSetShader(m_psRooms, NULL, 0); // Set texture m_context->PSSetShaderResources(0, 1, &m_textureAtlas->ShaderResourceView); ID3D11SamplerState* sampler = m_states->AnisotropicWrap(); ID3D11SamplerState* shadowSampler = m_states->PointClamp(); m_context->PSSetSamplers(0, 1, &sampler); m_context->PSSetShaderResources(1, 1, &m_caustics[m_currentCausticsFrame / 2]->ShaderResourceView); m_context->PSSetSamplers(1, 1, &shadowSampler); m_context->PSSetShaderResources(2, 1, &m_shadowMap->ShaderResourceView); // Set camera matrices m_stCameraMatrices.View = View.Transpose(); m_stCameraMatrices.Projection = Projection.Transpose(); updateConstantBuffer(m_cbCameraMatrices, &m_stCameraMatrices, sizeof(CCameraMatrixBuffer)); m_context->VSSetConstantBuffers(0, 1, &m_cbCameraMatrices); // Set shadow map data if (m_shadowLight != NULL) { memcpy(&m_stShadowMap.Light, m_shadowLight, sizeof(ShaderLight)); m_stShadowMap.CastShadows = true; //m_stShadowMap.ViewProjectionInverse = ViewProjection.Invert().Transpose(); } else { m_stShadowMap.CastShadows = false; } updateConstantBuffer(m_cbShadowMap, &m_stShadowMap, sizeof(CShadowLightBuffer)); m_context->VSSetConstantBuffers(4, 1, &m_cbShadowMap); m_context->PSSetConstantBuffers(4, 1, &m_cbShadowMap); if (animated) m_primitiveBatch->Begin(); for (int i = 0; i < m_roomsToDraw.Size(); i++) { RendererRoom* room = m_roomsToDraw[i]; m_stLights.NumLights = room->LightsToDraw.Size(); for (int j = 0; j < room->LightsToDraw.Size(); j++) memcpy(&m_stLights.Lights[j], room->LightsToDraw[j], sizeof(ShaderLight)); updateConstantBuffer(m_cbLights, &m_stLights, sizeof(CLightBuffer)); m_context->PSSetConstantBuffers(1, 1, &m_cbLights); m_stMisc.Caustics = (room->Room->flags & ENV_FLAG_WATER); m_stMisc.AlphaTest = !transparent; updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); m_stRoom.AmbientColor = room->AmbientLight; updateConstantBuffer(m_cbRoom, &m_stRoom, sizeof(CRoomBuffer)); m_context->PSSetConstantBuffers(5, 1, &m_cbRoom); for (int j = firstBucket; j < lastBucket; j++) { RendererBucket* bucket; if (!animated) bucket = &room->Buckets[j]; else bucket = &room->AnimatedBuckets[j]; if (bucket->Vertices.size() == 0) continue; if (!animated) if (j == RENDERER_BUCKET_SOLID_DS || j == RENDERER_BUCKET_TRANSPARENT_DS) m_context->RSSetState(m_states->CullNone()); else m_context->RSSetState(m_states->CullCounterClockwise()); if (!animated) { m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } else { for (int k = 0; k < bucket->Polygons.size(); k++) { RendererPolygon* poly = &bucket->Polygons[k]; if (poly->Shape == SHAPE_RECTANGLE) { m_primitiveBatch->DrawQuad(bucket->Vertices[poly->Indices[0]], bucket->Vertices[poly->Indices[1]], bucket->Vertices[poly->Indices[2]], bucket->Vertices[poly->Indices[3]]); } else { m_primitiveBatch->DrawTriangle(bucket->Vertices[poly->Indices[0]], bucket->Vertices[poly->Indices[1]], bucket->Vertices[poly->Indices[2]]); } } } } } if (animated) m_primitiveBatch->End(); return true; } bool Renderer11::drawStatics(bool transparent) { //return true; UINT stride = sizeof(RendererVertex); UINT offset = 0; int firstBucket = (transparent ? 2 : 0); int lastBucket = (transparent ? 4 : 2); m_context->IASetVertexBuffers(0, 1, &m_staticsVertexBuffer->Buffer, &stride, &offset); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); m_context->IASetIndexBuffer(m_staticsIndexBuffer->Buffer, DXGI_FORMAT_R32_UINT, 0); // Set shaders m_context->VSSetShader(m_vsStatics, NULL, 0); m_context->PSSetShader(m_psStatics, NULL, 0); // Set texture m_context->PSSetShaderResources(0, 1, &m_textureAtlas->ShaderResourceView); ID3D11SamplerState* sampler = m_states->AnisotropicClamp(); m_context->PSSetSamplers(0, 1, &sampler); // Set camera matrices m_stCameraMatrices.View = View.Transpose(); m_stCameraMatrices.Projection = Projection.Transpose(); updateConstantBuffer(m_cbCameraMatrices, &m_stCameraMatrices, sizeof(CCameraMatrixBuffer)); m_context->VSSetConstantBuffers(0, 1, &m_cbCameraMatrices); for (int i = 0; i < m_staticsToDraw.Size(); i++) { MESH_INFO* msh = m_staticsToDraw[i]->Mesh; if (!(msh->Flags & 1)) continue; RendererRoom* room = m_rooms[m_staticsToDraw[i]->RoomIndex]; RendererObject* staticObj = m_staticObjects[msh->staticNumber]; RendererMesh* mesh = staticObj->ObjectMeshes[0]; m_stStatic.World = (Matrix::CreateRotationY(TR_ANGLE_TO_RAD(msh->yRot)) * Matrix::CreateTranslation(msh->x, msh->y, msh->z)).Transpose(); m_stStatic.Color = Vector4(((msh->shade >> 10) & 0xFF) / 255.0f, ((msh->shade >> 5) & 0xFF) / 255.0f, ((msh->shade >> 0) & 0xFF) / 255.0f, 1.0f); updateConstantBuffer(m_cbStatic, &m_stStatic, sizeof(CStaticBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbStatic); for (int j = firstBucket; j < lastBucket; j++) { if (j == RENDERER_BUCKET_SOLID_DS || j == RENDERER_BUCKET_TRANSPARENT_DS) m_context->RSSetState(m_states->CullNone()); else m_context->RSSetState(m_states->CullCounterClockwise()); RendererBucket* bucket = &mesh->Buckets[j]; if (bucket->Vertices.size() == 0) continue; // Draw vertices m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } } return true; } bool Renderer11::drawAnimatingItem(RendererItem* item, bool transparent, bool animated) { UINT stride = sizeof(RendererVertex); UINT offset = 0; int firstBucket = (transparent ? 2 : 0); int lastBucket = (transparent ? 4 : 2); RendererRoom* room = m_rooms[item->Item->roomNumber]; if (room == NULL) return true; RendererObject* moveableObj = m_moveableObjects[item->Item->objectNumber]; m_stItem.World = item->World.Transpose(); m_stItem.Position = Vector4(item->Item->pos.xPos, item->Item->pos.yPos, item->Item->pos.zPos, 1.0f); m_stItem.AmbientLight = room->AmbientLight; memcpy(m_stItem.BonesMatrices, item->AnimationTransforms, sizeof(Matrix) * 32); updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbItem); m_stLights.NumLights = item->Lights.Size(); for (int j = 0; j < item->Lights.Size(); j++) memcpy(&m_stLights.Lights[j], item->Lights[j], sizeof(ShaderLight)); updateConstantBuffer(m_cbLights, &m_stLights, sizeof(CLightBuffer)); m_context->PSSetConstantBuffers(2, 1, &m_cbLights); m_stMisc.AlphaTest = !transparent; updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); for (int k = 0; k < moveableObj->ObjectMeshes.size(); k++) { RendererMesh* mesh = moveableObj->ObjectMeshes[k]; for (int j = firstBucket; j < lastBucket; j++) { RendererBucket* bucket = &mesh->Buckets[j]; if (bucket->Vertices.size() == 0) continue; if (j == RENDERER_BUCKET_SOLID_DS || j == RENDERER_BUCKET_TRANSPARENT_DS) m_context->RSSetState(m_states->CullNone()); else m_context->RSSetState(m_states->CullCounterClockwise()); // Draw vertices m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } } return true; } bool Renderer11::drawWaterfalls() { UINT stride = sizeof(RendererVertex); UINT offset = 0; // Draw waterfalls m_context->RSSetState(m_states->CullCounterClockwise()); for (int i = 0; i < m_itemsToDraw.Size(); i++) { RendererItem* item = m_itemsToDraw[i]; RendererRoom* room = m_rooms[item->Item->roomNumber]; RendererObject* moveableObj = m_moveableObjects[item->Item->objectNumber]; short objectNumber = item->Item->objectNumber; if (objectNumber >= ID_WATERFALL1 && objectNumber <= ID_WATERFALLSS2) { RendererRoom* room = m_rooms[item->Item->roomNumber]; RendererObject* moveableObj = m_moveableObjects[item->Item->objectNumber]; m_stItem.World = item->World.Transpose(); m_stItem.Position = Vector4(item->Item->pos.xPos, item->Item->pos.yPos, item->Item->pos.zPos, 1.0f); m_stItem.AmbientLight = room->AmbientLight; //Vector4::One * 0.1f; // room->AmbientLight; memcpy(m_stItem.BonesMatrices, item->AnimationTransforms, sizeof(Matrix) * 32); updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbItem); m_stLights.NumLights = item->Lights.Size(); for (int j = 0; j < item->Lights.Size(); j++) memcpy(&m_stLights.Lights[j], item->Lights[j], sizeof(ShaderLight)); updateConstantBuffer(m_cbLights, &m_stLights, sizeof(CLightBuffer)); m_context->PSSetConstantBuffers(2, 1, &m_cbLights); m_stMisc.AlphaTest = false; updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); m_primitiveBatch->Begin(); for (int k = 0; k < moveableObj->ObjectMeshes.size(); k++) { RendererMesh* mesh = moveableObj->ObjectMeshes[k]; for (int b = 0; b < NUM_BUCKETS; b++) { RendererBucket* bucket = &mesh->Buckets[b]; if (bucket->Vertices.size() == 0) continue; for (int p = 0; p < bucket->Polygons.size(); p++) { RendererPolygon* poly = &bucket->Polygons[p]; OBJECT_TEXTURE* texture = &ObjectTextures[poly->TextureId]; int tile = texture->tileAndFlag & 0x7FFF; if (poly->Shape == SHAPE_RECTANGLE) { bucket->Vertices[poly->Indices[0]].UV.y = (texture->vertices[0].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; bucket->Vertices[poly->Indices[1]].UV.y = (texture->vertices[1].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; bucket->Vertices[poly->Indices[2]].UV.y = (texture->vertices[2].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; bucket->Vertices[poly->Indices[3]].UV.y = (texture->vertices[3].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; m_primitiveBatch->DrawQuad(bucket->Vertices[poly->Indices[0]], bucket->Vertices[poly->Indices[1]], bucket->Vertices[poly->Indices[2]], bucket->Vertices[poly->Indices[3]]); } else { bucket->Vertices[poly->Indices[0]].UV.y = (texture->vertices[0].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; bucket->Vertices[poly->Indices[1]].UV.y = (texture->vertices[1].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; bucket->Vertices[poly->Indices[2]].UV.y = (texture->vertices[2].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; m_primitiveBatch->DrawTriangle(bucket->Vertices[poly->Indices[0]], bucket->Vertices[poly->Indices[1]], bucket->Vertices[poly->Indices[2]]); } } } } m_primitiveBatch->End(); } else { continue; } } return true; } bool Renderer11::drawItems(bool transparent, bool animated) { UINT stride = sizeof(RendererVertex); UINT offset = 0; int firstBucket = (transparent ? 2 : 0); int lastBucket = (transparent ? 4 : 2); m_context->IASetVertexBuffers(0, 1, &m_moveablesVertexBuffer->Buffer, &stride, &offset); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); m_context->IASetIndexBuffer(m_moveablesIndexBuffer->Buffer, DXGI_FORMAT_R32_UINT, 0); RendererItem* item = &m_items[Lara.itemNumber]; // Set shaders m_context->VSSetShader(m_vsItems, NULL, 0); m_context->PSSetShader(m_psItems, NULL, 0); // Set texture m_context->PSSetShaderResources(0, 1, &m_textureAtlas->ShaderResourceView); ID3D11SamplerState* sampler = m_states->AnisotropicClamp(); m_context->PSSetSamplers(0, 1, &sampler); // Set camera matrices m_stCameraMatrices.View = View.Transpose(); m_stCameraMatrices.Projection = Projection.Transpose(); updateConstantBuffer(m_cbCameraMatrices, &m_stCameraMatrices, sizeof(CCameraMatrixBuffer)); m_context->VSSetConstantBuffers(0, 1, &m_cbCameraMatrices); m_stMisc.AlphaTest = !transparent; updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); for (int i = 0; i < m_itemsToDraw.Size(); i++) { RendererItem* item = m_itemsToDraw[i]; RendererRoom* room = m_rooms[item->Item->roomNumber]; RendererObject* moveableObj = m_moveableObjects[item->Item->objectNumber]; short objectNumber = item->Item->objectNumber; if (moveableObj->DoNotDraw) { continue; } else if (objectNumber >= ID_WATERFALL1 && objectNumber <= ID_WATERFALLSS2) { // We'll draw waterfalls later continue; } else { drawAnimatingItem(item, transparent, animated); } } return true; } bool Renderer11::drawEffects(bool transparent) { UINT stride = sizeof(RendererVertex); UINT offset = 0; int firstBucket = (transparent ? 2 : 0); int lastBucket = (transparent ? 4 : 2); m_context->IASetVertexBuffers(0, 1, &m_moveablesVertexBuffer->Buffer, &stride, &offset); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); m_context->IASetIndexBuffer(m_moveablesIndexBuffer->Buffer, DXGI_FORMAT_R32_UINT, 0); for (int i = 0; i < m_effectsToDraw.Size(); i++) { RendererEffect* effect = m_effectsToDraw[i]; RendererRoom* room = m_rooms[effect->Effect->roomNumber]; OBJECT_INFO* obj = &Objects[effect->Effect->objectNumber]; drawEffect(effect, transparent); if (obj->nmeshes > 0) { } else { } } return true; } bool Renderer11::drawEffect(RendererEffect* effect, bool transparent) { UINT stride = sizeof(RendererVertex); UINT offset = 0; int firstBucket = (transparent ? 2 : 0); int lastBucket = (transparent ? 4 : 2); RendererRoom * room = m_rooms[effect->Effect->roomNumber]; RendererObject * moveableObj = m_moveableObjects[effect->Effect->objectNumber]; m_stItem.World = effect->World.Transpose(); m_stItem.Position = Vector4(effect->Effect->pos.xPos, effect->Effect->pos.yPos, effect->Effect->pos.zPos, 1.0f); m_stItem.AmbientLight = room->AmbientLight; Matrix matrices[1] = { Matrix::Identity }; memcpy(m_stItem.BonesMatrices, matrices, sizeof(Matrix)); updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbItem); m_stLights.NumLights = effect->Lights.Size(); for (int j = 0; j < effect->Lights.Size(); j++) memcpy(&m_stLights.Lights[j], effect->Lights[j], sizeof(ShaderLight)); updateConstantBuffer(m_cbLights, &m_stLights, sizeof(CLightBuffer)); m_context->PSSetConstantBuffers(2, 1, &m_cbLights); m_stMisc.AlphaTest = !transparent; updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); RendererMesh* mesh = effect->Mesh; for (int j = firstBucket; j < lastBucket; j++) { RendererBucket* bucket = &mesh->Buckets[j]; if (bucket->Vertices.size() == 0) continue; if (j == RENDERER_BUCKET_SOLID_DS || j == RENDERER_BUCKET_TRANSPARENT_DS) m_context->RSSetState(m_states->CullNone()); else m_context->RSSetState(m_states->CullCounterClockwise()); // Draw vertices m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } return true; } bool Renderer11::drawLara(bool transparent, bool shadowMap) { // Don't draw Lara if binoculars or sniper if (BinocularRange || SpotcamOverlay || SpotcamDontDrawLara || CurrentLevel == 0) return true; UINT stride = sizeof(RendererVertex); UINT offset = 0; int firstBucket = (transparent ? 2 : 0); int lastBucket = (transparent ? 4 : 2); m_context->IASetVertexBuffers(0, 1, &m_moveablesVertexBuffer->Buffer, &stride, &offset); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); m_context->IASetIndexBuffer(m_moveablesIndexBuffer->Buffer, DXGI_FORMAT_R32_UINT, 0); RendererItem* item = &m_items[Lara.itemNumber]; // Set shaders if (shadowMap) { m_context->VSSetShader(m_vsShadowMap, NULL, 0); m_context->PSSetShader(m_psShadowMap, NULL, 0); } else { m_context->VSSetShader(m_vsItems, NULL, 0); m_context->PSSetShader(m_psItems, NULL, 0); } // Set texture m_context->PSSetShaderResources(0, 1, &m_textureAtlas->ShaderResourceView); ID3D11SamplerState* sampler = m_states->AnisotropicClamp(); m_context->PSSetSamplers(0, 1, &sampler); // Set camera matrices m_stCameraMatrices.View = View.Transpose(); m_stCameraMatrices.Projection = Projection.Transpose(); updateConstantBuffer(m_cbCameraMatrices, &m_stCameraMatrices, sizeof(CCameraMatrixBuffer)); m_context->VSSetConstantBuffers(0, 1, &m_cbCameraMatrices); m_stMisc.AlphaTest = !transparent; updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); RendererObject* laraObj = m_moveableObjects[ID_LARA]; RendererObject* laraSkin = m_moveableObjects[ID_LARA_SKIN]; RendererRoom* room = m_rooms[LaraItem->roomNumber]; m_stItem.World = m_LaraWorldMatrix.Transpose(); m_stItem.Position = Vector4(LaraItem->pos.xPos, LaraItem->pos.yPos, LaraItem->pos.zPos, 1.0f); m_stItem.AmbientLight = room->AmbientLight; memcpy(m_stItem.BonesMatrices, laraObj->AnimationTransforms.data(), sizeof(Matrix) * 32); updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbItem); m_context->PSSetConstantBuffers(1, 1, &m_cbItem); if (!shadowMap) { m_stLights.NumLights = item->Lights.Size(); for (int j = 0; j < item->Lights.Size(); j++) memcpy(&m_stLights.Lights[j], item->Lights[j], sizeof(ShaderLight)); updateConstantBuffer(m_cbLights, &m_stLights, sizeof(CLightBuffer)); m_context->PSSetConstantBuffers(2, 1, &m_cbLights); } for (int k = 0; k < laraSkin->ObjectMeshes.size(); k++) { RendererMesh* mesh = m_meshPointersToMesh[reinterpret_cast(Lara.meshPtrs[k])]; for (int j = firstBucket; j < lastBucket; j++) { RendererBucket* bucket = &mesh->Buckets[j]; if (bucket->Vertices.size() == 0) continue; if (j == RENDERER_BUCKET_SOLID_DS || j == RENDERER_BUCKET_TRANSPARENT_DS) m_context->RSSetState(m_states->CullNone()); else m_context->RSSetState(m_states->CullCounterClockwise()); // Draw vertices m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } } if (m_moveableObjects[ID_LARA_SKIN_JOINTS] != NULL) { RendererObject* laraSkinJoints = m_moveableObjects[ID_LARA_SKIN_JOINTS]; for (int k = 0; k < laraSkinJoints->ObjectMeshes.size(); k++) { RendererMesh* mesh = laraSkinJoints->ObjectMeshes[k]; for (int j = firstBucket; j < lastBucket; j++) { RendererBucket* bucket = &mesh->Buckets[j]; if (bucket->Vertices.size() == 0) continue; // Draw vertices m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } } } if (!transparent) { for (int k = 0; k < laraSkin->ObjectMeshes.size(); k++) { RendererMesh* mesh = laraSkin->ObjectMeshes[k]; for (int j = 0; j < NUM_BUCKETS; j++) { RendererBucket* bucket = &mesh->Buckets[j]; if (bucket->Vertices.size() == 0) continue; // Draw vertices m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } } // Hairs are pre-transformed Matrix matrices[8] = { Matrix::Identity, Matrix::Identity, Matrix::Identity, Matrix::Identity, Matrix::Identity, Matrix::Identity, Matrix::Identity, Matrix::Identity }; memcpy(m_stItem.BonesMatrices, matrices, sizeof(Matrix) * 8); m_stItem.World = Matrix::Identity; updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); if (m_moveableObjects[ID_LARA_HAIR] != NULL) { m_primitiveBatch->Begin(); m_primitiveBatch->DrawIndexed(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, (const uint16_t*)m_hairIndices.data(), m_numHairIndices, m_hairVertices.data(), m_numHairVertices); m_primitiveBatch->End(); } } return true; } bool Renderer11::drawScene(bool dump) { m_timeUpdate = 0; m_timeDraw = 0; m_timeFrame = 0; m_numDrawCalls = 0; m_nextLight = 0; m_nextSprite = 0; m_nextLine3D = 0; m_nextLine2D = 0; m_currentCausticsFrame++; m_currentCausticsFrame %= 32; m_strings.clear(); GameScriptLevel* level = g_GameFlow->GetLevel(CurrentLevel); ViewProjection = View * Projection; m_stLights.CameraPosition = Vector3(Camera.pos.x, Camera.pos.y, Camera.pos.z); // Prepare the scene to draw auto time1 = chrono::high_resolution_clock::now(); clearSceneItems(); collectRooms(); updateLaraAnimations(); updateItemsAnimations(); updateEffects(); m_items[Lara.itemNumber].Item = LaraItem; collectLightsForItem(LaraItem->roomNumber, &m_items[Lara.itemNumber]); // Update animated textures every 2 frames if (GnFrameCounter % 2 == 0) updateAnimatedTextures(); auto time2 = chrono::high_resolution_clock::now(); m_timeUpdate = (chrono::duration_cast(time2 - time1)).count() / 1000000; time1 = time2; // Draw shadow map if (g_Configuration.EnableShadows) drawShadowMap(); // Reset GPU state m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); m_context->RSSetState(m_states->CullCounterClockwise()); m_context->OMSetDepthStencilState(m_states->DepthDefault(), 0); // Bind and clear render target m_currentRenderTarget = (dump ? m_dumpScreenRenderTarget : m_renderTarget); m_context->ClearRenderTargetView(m_currentRenderTarget->RenderTargetView, Colors::Black); m_context->ClearDepthStencilView(m_currentRenderTarget->DepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); m_context->OMSetRenderTargets(1, &m_currentRenderTarget->RenderTargetView, m_currentRenderTarget->DepthStencilView); m_context->RSSetViewports(1, &m_viewport); drawHorizonAndSky(); // Opaque geometry m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); drawRooms(false, false); drawRooms(false, true); drawStatics(false); drawLara(false, false); drawItems(false, false); drawItems(false, true); drawEffects(false); drawGunFlashes(); drawGunShells(); drawDebris(false); drawBats(); drawRats(); drawSpiders(); drawFootprints(); // Transparent geometry m_context->OMSetBlendState(m_states->Additive(), NULL, 0xFFFFFFFF); m_context->OMSetDepthStencilState(m_states->DepthRead(), 0); drawRooms(true, false); drawRooms(true, true); drawStatics(true); drawLara(true, false); drawItems(true, false); drawItems(true, true); drawEffects(true); drawWaterfalls(); drawDebris(true); m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); m_context->OMSetDepthStencilState(m_states->DepthDefault(), 0); // Do special effects and weather drawFires(); drawSmokes(); drawBlood(); drawSparks(); drawBubbles(); drawDrips(); drawRipples(); drawUnderwaterDust(); drawSplahes(); drawShockwaves(); switch (level->Weather) { case WEATHER_NORMAL: // no weather in normal break; case WEATHER_RAIN: doRain(); break; case WEATHER_SNOW: doSnow(); break; } drawRopes(); drawSprites(); drawLines3D(); time2 = chrono::high_resolution_clock::now(); m_timeFrame = (chrono::duration_cast(time2 - time1)).count() / 1000000; time1 = time2; // Bars int flash = FlashIt(); if (DashTimer < 120) DrawBar(630, 32, 150, 12, 100 * (unsigned short)DashTimer / 120, 0xA0A000, 0xA000); UpdateHealtBar(flash); UpdateAirBar(flash); DrawAllPickups(); drawLines2D(); if (CurrentLevel != 0) { // Draw binoculars or lasersight drawOverlays(); m_currentY = 60; #ifdef _DEBUG ROOM_INFO* r = &Rooms[LaraItem->roomNumber]; printDebugMessage("Update time: %d", m_timeUpdate); printDebugMessage("Frame time: %d", m_timeFrame); printDebugMessage("Draw calls: %d", m_numDrawCalls); printDebugMessage("Rooms: %d", m_roomsToDraw.Size()); printDebugMessage("Items: %d", m_itemsToDraw.Size()); printDebugMessage("Statics: %d", m_staticsToDraw.Size()); printDebugMessage("Lights: %d", m_lightsToDraw.Size()); printDebugMessage("Lara.roomNumber: %d", LaraItem->roomNumber); printDebugMessage("Lara.pos: %d %d %d", LaraItem->pos.xPos, LaraItem->pos.yPos, LaraItem->pos.zPos); printDebugMessage("Lara.rot: %d %d %d", LaraItem->pos.xRot, LaraItem->pos.yRot, LaraItem->pos.zRot); printDebugMessage("Lara.animNumber: %d", LaraItem->animNumber); printDebugMessage("Lara.frameNumber: %d", LaraItem->frameNumber); printDebugMessage("Lara.currentAnimState: %d", LaraItem->currentAnimState); printDebugMessage("Lara.requiredAnimState: %d", LaraItem->requiredAnimState); printDebugMessage("Lara.goalAnimState: %d", LaraItem->goalAnimState); printDebugMessage("Room: %d %d %d %d", r->x, r->z, r->x + r->xSize * WALL_SIZE, r->z + r->ySize * WALL_SIZE); printDebugMessage("Camera.pos: %d %d %d", Camera.pos.x, Camera.pos.y, Camera.pos.z); printDebugMessage("Camera.target: %d %d %d", Camera.target.x, Camera.target.y, Camera.target.z); #endif } drawAllStrings(); /*m_spriteBatch->Begin(); RECT rect; rect.top = rect.left = 0; rect.right = rect.bottom = 128; m_spriteBatch->Draw(m_shadowMap->ShaderResourceView, rect, Colors::White); m_spriteBatch->End();*/ if (!dump) m_swapChain->Present(0, 0); return true; } int Renderer11::DumpGameScene() { drawScene(true); return 0; } int Renderer11::DrawInventory() { if (CurrentLevel == 0 && g_GameFlow->TitleType == TITLE_FLYBY) drawScene(true); drawInventoryScene(); drawFinalPass(); return 0; } int Renderer11::SyncRenderer() { // Sync the renderer int nf = Sync(); if (nf < 2) { int i = 2 - nf; nf = 2; do { while (!Sync()); i--; } while (i); } GnFrameCounter++; return nf; } bool Renderer11::PrintString(int x, int y, char* string, D3DCOLOR color, int flags) { int realX = x; int realY = y; float factorX = ScreenWidth / 800.0f; float factorY = ScreenHeight / 600.0f; RECT rect = { 0, 0, 0, 0 }; // Convert the string to wstring int sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, string, strlen(string), NULL, 0); std::wstring wstr(sizeNeeded, 0); MultiByteToWideChar(CP_UTF8, 0, string, strlen(string), &wstr[0], sizeNeeded); // Prepare the structure for the renderer RendererStringToDraw str; str.String = wstr; str.Flags = flags; str.X = 0; str.Y = 0; str.Color = Vector3((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF); // Measure the string Vector2 size = m_gameFont->MeasureString(wstr.c_str()); if (flags & PRINTSTRING_CENTER) { int width = size.x; rect.left = x * factorX - width / 2; rect.right = x * factorX + width / 2; rect.top += y * factorY; rect.bottom += y * factorY; } else { rect.left = x * factorX; rect.right += x * factorX; rect.top = y * factorY; rect.bottom += y * factorY; } str.X = rect.left; str.Y = rect.top; if (flags & PRINTSTRING_BLINK) { str.Color = Vector3(m_blinkColorValue, m_blinkColorValue, m_blinkColorValue); if (!(flags & PRINTSTRING_DONT_UPDATE_BLINK)) { m_blinkColorValue += m_blinkColorDirection * 16; if (m_blinkColorValue < 0) { m_blinkColorValue = 0; m_blinkColorDirection = 1; } if (m_blinkColorValue > 255) { m_blinkColorValue = 255; m_blinkColorDirection = -1; } } } m_strings.push_back(str); return true; } int Renderer11::drawFinalPass() { // Update fade status if (m_fadeStatus == RENDERER_FADE_STATUS::FADE_IN && m_fadeFactor > 0.99f) m_fadeStatus = RENDERER_FADE_STATUS::NO_FADE; if (m_fadeStatus == RENDERER_FADE_STATUS::FADE_OUT && m_fadeFactor <= 0.01f) m_fadeStatus = RENDERER_FADE_STATUS::NO_FADE; // Reset GPU state m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); m_context->RSSetState(m_states->CullCounterClockwise()); m_context->OMSetDepthStencilState(m_states->DepthDefault(), 0); m_context->ClearRenderTargetView(m_backBufferRTV, Colors::Black); m_context->ClearDepthStencilView(m_depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); m_context->OMSetRenderTargets(1, &m_backBufferRTV, m_depthStencilView); drawFullScreenQuad(m_renderTarget->ShaderResourceView, Vector3(m_fadeFactor, m_fadeFactor, m_fadeFactor), m_enableCinematicBars); m_swapChain->Present(0, 0); // Update fade status if (m_fadeStatus == RENDERER_FADE_STATUS::FADE_IN) m_fadeFactor += FADE_FACTOR; if (m_fadeStatus == RENDERER_FADE_STATUS::FADE_OUT) m_fadeFactor -= FADE_FACTOR; return 0; } bool Renderer11::drawFullScreenImage(ID3D11ShaderResourceView* texture, float fade) { // Reset GPU state m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); m_context->RSSetState(m_states->CullCounterClockwise()); m_context->OMSetDepthStencilState(m_states->DepthDefault(), 0); m_context->ClearRenderTargetView(m_backBufferRTV, Colors::White); m_context->ClearDepthStencilView(m_depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); m_context->OMSetRenderTargets(1, &m_backBufferRTV, m_depthStencilView); m_context->RSSetViewports(1, &m_viewport); drawFullScreenQuad(texture, Vector3(fade, fade, fade), false); m_swapChain->Present(0, 0); return true; } bool Renderer11::drawAllStrings() { m_spriteBatch->Begin(); for (int i = 0; i < m_strings.size(); i++) { RendererStringToDraw* str = &m_strings[i]; // Draw shadow if needed if (str->Flags & PRINTSTRING_OUTLINE) m_gameFont->DrawString(m_spriteBatch, str->String.c_str(), Vector2(str->X + 1, str->Y + 1), Vector4(0.0f, 0.0f, 0.0f, 1.0f)); // Draw string m_gameFont->DrawString(m_spriteBatch, str->String.c_str(), Vector2(str->X, str->Y), Vector4(str->Color.x / 255.0f, str->Color.y / 255.0f, str->Color.z / 255.0f, 1.0f)); } m_spriteBatch->End(); return true; } bool Renderer11::PrepareDataForTheRenderer() { m_moveableObjects = (RendererObject**)malloc(sizeof(RendererObject*) * ID_NUMBER_OBJECTS); ZeroMemory(m_moveableObjects, sizeof(RendererObject*) * ID_NUMBER_OBJECTS); m_spriteSequences = (RendererSpriteSequence**)malloc(sizeof(RendererSpriteSequence*) * ID_NUMBER_OBJECTS); ZeroMemory(m_spriteSequences, sizeof(RendererSpriteSequence*) * ID_NUMBER_OBJECTS); m_staticObjects = (RendererObject**)malloc(sizeof(RendererObject*) * NUM_STATICS); ZeroMemory(m_staticObjects, sizeof(RendererObject*) * NUM_STATICS); m_rooms = (RendererRoom**)malloc(sizeof(RendererRoom*) * NUM_ROOMS); ZeroMemory(m_rooms, sizeof(RendererRoom*) * NUM_ROOMS); m_meshes.clear(); // Step 0: prepare animated textures short numSets = *AnimatedTextureRanges; short* animatedPtr = AnimatedTextureRanges; animatedPtr++; m_animatedTextureSets = (RendererAnimatedTextureSet**)malloc(sizeof(RendererAnimatedTextureSet*) * NUM_ANIMATED_SETS); m_numAnimatedTextureSets = numSets; for (int i = 0; i < numSets; i++) { RendererAnimatedTextureSet* set = new RendererAnimatedTextureSet(); short numTextures = *animatedPtr + 1; animatedPtr++; set->Textures = (RendererAnimatedTexture**)malloc(sizeof(RendererAnimatedTexture) * numTextures); set->NumTextures = numTextures; for (int j = 0; j < numTextures; j++) { short textureId = *animatedPtr; animatedPtr++; OBJECT_TEXTURE* texture = &ObjectTextures[textureId]; int tile = texture->tileAndFlag & 0x7FFF; RendererAnimatedTexture* newTexture = new RendererAnimatedTexture(); newTexture->Id = textureId; for (int k = 0; k < 4; k++) { float x = (texture->vertices[k].x * 256.0f + 0.5f + GET_ATLAS_PAGE_X(tile)) / (float)TEXTURE_ATLAS_SIZE; float y = (texture->vertices[k].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; newTexture->UV[k] = Vector2(x, y); } set->Textures[j] = newTexture; } m_animatedTextureSets[i] = set; } // Step 1: create the texture atlas byte* buffer = (byte*)malloc(TEXTURE_ATLAS_SIZE * TEXTURE_ATLAS_SIZE * 4); ZeroMemory(buffer, TEXTURE_ATLAS_SIZE * TEXTURE_ATLAS_SIZE * 4); int blockX = 0; int blockY = 0; if (g_GameFlow->GetLevel(CurrentLevel)->LaraType == LARA_YOUNG) { memcpy(m_laraSkinJointRemap, m_youngLaraSkinJointRemap, 15 * 32 * 2); } else { memcpy(m_laraSkinJointRemap, m_normalLaraSkinJointRemap, 15 * 32 * 2); } for (int p = 0; p < NumTexturePages; p++) { for (int y = 0; y < 256; y++) { for (int x = 0; x < 256; x++) { int pixelIndex = blockY * TEXTURE_PAGE_SIZE * NUM_TEXTURE_PAGES_PER_ROW + y * 256 * NUM_TEXTURE_PAGES_PER_ROW * 4 + blockX * 256 * 4 + x * 4; int oldPixelIndex = p * TEXTURE_PAGE_SIZE + y * 256 * 4 + x * 4; byte r = Texture32[oldPixelIndex]; byte g = Texture32[oldPixelIndex + 1]; byte b = Texture32[oldPixelIndex + 2]; byte a = Texture32[oldPixelIndex + 3]; buffer[pixelIndex + 2] = r; buffer[pixelIndex + 1] = g; buffer[pixelIndex + 0] = b; buffer[pixelIndex + 3] = a; } } blockX++; if (blockX == NUM_TEXTURE_PAGES_PER_ROW) { blockX = 0; blockY++; } } if (m_textureAtlas != NULL) delete m_textureAtlas; m_textureAtlas = Texture2D::LoadFromByteArray(m_device, TEXTURE_ATLAS_SIZE, TEXTURE_ATLAS_SIZE, &buffer[0]); if (m_textureAtlas == NULL) return false; free(buffer); buffer = (byte*)malloc(256 * 256 * 4); memcpy(buffer, MiscTextures + 256 * 512 * 4, 256 * 256 * 4); m_skyTexture = Texture2D::LoadFromByteArray(m_device, 256, 256, &buffer[0]); if (m_skyTexture == NULL) return false; //D3DX11SaveTextureToFileA(m_context, m_skyTexture->Texture, D3DX11_IFF_PNG, "H:\\sky.png"); free(buffer); // Step 2: prepare rooms vector roomVertices; vector roomIndices; int baseRoomVertex = 0; int baseRoomIndex = 0; for (int i = 0; i < NumberRooms; i++) { ROOM_INFO* room = &Rooms[i]; RendererRoom* r = new RendererRoom(); r->RoomNumber = i; r->Room = room; r->AmbientLight = Vector4(room->ambient.b / 255.0f, room->ambient.g / 255.0f, room->ambient.r / 255.0f, 1.0f); r->LightsToDraw.Reserve(32); m_rooms[i] = r; if (room->NumVertices == 0) continue; int lastRectangle = 0; int lastTriangle = 0; tr5_room_layer* layers = (tr5_room_layer*)room->LayerOffset; for (int l = 0; l < room->NumLayers; l++) { tr5_room_layer* layer = &layers[l]; if (layer->NumLayerVertices == 0) continue; byte* polygons = (byte*)layer->PolyOffset; tr5_room_vertex* vertices = (tr5_room_vertex*)layer->VerticesOffset; if (layer->NumLayerRectangles > 0) { for (int n = 0; n < layer->NumLayerRectangles; n++) { tr4_mesh_face4* poly = (tr4_mesh_face4*)polygons; // Get the real texture index and if double sided short textureIndex = poly->Texture & 0x3FFF; bool doubleSided = (poly->Texture & 0x8000) >> 15; // Get the object texture OBJECT_TEXTURE* texture = &ObjectTextures[textureIndex]; int tile = texture->tileAndFlag & 0x7FFF; // Create vertices RendererBucket* bucket; int animatedSetIndex = getAnimatedTextureInfo(textureIndex); int bucketIndex = RENDERER_BUCKET_SOLID; if (!doubleSided) { if (texture->attribute == 2) bucketIndex = RENDERER_BUCKET_TRANSPARENT; else bucketIndex = RENDERER_BUCKET_SOLID; } else { if (texture->attribute == 2) bucketIndex = RENDERER_BUCKET_TRANSPARENT_DS; else bucketIndex = RENDERER_BUCKET_SOLID_DS; } if (animatedSetIndex == -1) { bucket = &r->Buckets[bucketIndex]; } else { bucket = &r->AnimatedBuckets[bucketIndex]; } // Calculate face normal Vector3 p0 = Vector3(vertices[poly->Vertices[0]].Vertex.x, vertices[poly->Vertices[0]].Vertex.y, vertices[poly->Vertices[0]].Vertex.z); Vector3 p1 = Vector3(vertices[poly->Vertices[1]].Vertex.x, vertices[poly->Vertices[1]].Vertex.y, vertices[poly->Vertices[1]].Vertex.z); Vector3 p2 = Vector3(vertices[poly->Vertices[2]].Vertex.x, vertices[poly->Vertices[2]].Vertex.y, vertices[poly->Vertices[2]].Vertex.z); Vector3 e1 = p1 - p0; Vector3 e2 = p1 - p2; Vector3 normal = e1.Cross(e2); normal.Normalize(); int baseVertices = bucket->NumVertices; for (int v = 0; v < 4; v++) { RendererVertex vertex; vertex.Position.x = room->x + vertices[poly->Vertices[v]].Vertex.x; vertex.Position.y = room->y + vertices[poly->Vertices[v]].Vertex.y; vertex.Position.z = room->z + vertices[poly->Vertices[v]].Vertex.z; vertex.Normal.x = vertices[poly->Vertices[v]].Normal.x; vertex.Normal.y = vertices[poly->Vertices[v]].Normal.y; vertex.Normal.z = vertices[poly->Vertices[v]].Normal.z; vertex.UV.x = (texture->vertices[v].x * 256.0f + 0.5f + GET_ATLAS_PAGE_X(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.UV.y = (texture->vertices[v].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.Color.x = ((vertices[poly->Vertices[v]].Colour >> 16) & 0xFF) / 255.0f; vertex.Color.y = ((vertices[poly->Vertices[v]].Colour >> 8) & 0xFF) / 255.0f; vertex.Color.z = ((vertices[poly->Vertices[v]].Colour >> 0) & 0xFF) / 255.0f; vertex.Color.w = 1.0f; vertex.Bone = 0; bucket->NumVertices++; bucket->Vertices.push_back(vertex); } bucket->Indices.push_back(baseVertices); bucket->Indices.push_back(baseVertices + 1); bucket->Indices.push_back(baseVertices + 3); bucket->Indices.push_back(baseVertices + 2); bucket->Indices.push_back(baseVertices + 3); bucket->Indices.push_back(baseVertices + 1); bucket->NumIndices += 6; RendererPolygon newPolygon; newPolygon.Shape = SHAPE_RECTANGLE; newPolygon.AnimatedSet = animatedSetIndex; newPolygon.TextureId = textureIndex; newPolygon.Indices[0] = baseVertices; newPolygon.Indices[1] = baseVertices + 1; newPolygon.Indices[2] = baseVertices + 2; newPolygon.Indices[3] = baseVertices + 3; bucket->Polygons.push_back(newPolygon); polygons += sizeof(tr4_mesh_face4); } } if (layer->NumLayerTriangles > 0) { for (int n = 0; n < layer->NumLayerTriangles; n++) { tr4_mesh_face3* poly = (tr4_mesh_face3*)polygons; // Get the real texture index and if double sided short textureIndex = poly->Texture & 0x3FFF; bool doubleSided = (poly->Texture & 0x8000) >> 15; // Get the object texture OBJECT_TEXTURE* texture = &ObjectTextures[textureIndex]; int tile = texture->tileAndFlag & 0x7FFF; // Create vertices RendererBucket* bucket; int animatedSetIndex = getAnimatedTextureInfo(textureIndex); int bucketIndex = RENDERER_BUCKET_SOLID; if (!doubleSided) { if (texture->attribute == 2) bucketIndex = RENDERER_BUCKET_TRANSPARENT; else bucketIndex = RENDERER_BUCKET_SOLID; } else { if (texture->attribute == 2) bucketIndex = RENDERER_BUCKET_TRANSPARENT_DS; else bucketIndex = RENDERER_BUCKET_SOLID_DS; } if (animatedSetIndex == -1) { bucket = &r->Buckets[bucketIndex]; } else { bucket = &r->AnimatedBuckets[bucketIndex]; } // Calculate face normal Vector3 p0 = Vector3(vertices[poly->Vertices[0]].Vertex.x, vertices[poly->Vertices[0]].Vertex.y, vertices[poly->Vertices[0]].Vertex.z); Vector3 p1 = Vector3(vertices[poly->Vertices[1]].Vertex.x, vertices[poly->Vertices[1]].Vertex.y, vertices[poly->Vertices[1]].Vertex.z); Vector3 p2 = Vector3(vertices[poly->Vertices[2]].Vertex.x, vertices[poly->Vertices[2]].Vertex.y, vertices[poly->Vertices[2]].Vertex.z); Vector3 e1 = p1 - p0; Vector3 e2 = p1 - p2; Vector3 normal = e1.Cross(e2); normal.Normalize(); int baseVertices = bucket->NumVertices; for (int v = 0; v < 3; v++) { RendererVertex vertex; vertex.Position.x = room->x + vertices[poly->Vertices[v]].Vertex.x; vertex.Position.y = room->y + vertices[poly->Vertices[v]].Vertex.y; vertex.Position.z = room->z + vertices[poly->Vertices[v]].Vertex.z; vertex.Normal.x = vertices[poly->Vertices[v]].Normal.x; vertex.Normal.y = vertices[poly->Vertices[v]].Normal.y; vertex.Normal.z = vertices[poly->Vertices[v]].Normal.z; vertex.UV.x = (texture->vertices[v].x * 256.0f + 0.5f + GET_ATLAS_PAGE_X(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.UV.y = (texture->vertices[v].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.Color.x = ((vertices[poly->Vertices[v]].Colour >> 16) & 0xFF) / 255.0f; vertex.Color.y = ((vertices[poly->Vertices[v]].Colour >> 8) & 0xFF) / 255.0f; vertex.Color.z = ((vertices[poly->Vertices[v]].Colour >> 0) & 0xFF) / 255.0f; vertex.Color.w = 1.0f; vertex.Bone = 0; bucket->NumVertices++; bucket->Vertices.push_back(vertex); } bucket->Indices.push_back(baseVertices); bucket->Indices.push_back(baseVertices + 1); bucket->Indices.push_back(baseVertices + 2); bucket->NumIndices += 3; RendererPolygon newPolygon; newPolygon.Shape = SHAPE_TRIANGLE; newPolygon.AnimatedSet = animatedSetIndex; newPolygon.TextureId = textureIndex; newPolygon.Indices[0] = baseVertices; newPolygon.Indices[1] = baseVertices + 1; newPolygon.Indices[2] = baseVertices + 2; bucket->Polygons.push_back(newPolygon); polygons += sizeof(tr4_mesh_face3); } } } if (room->numLights != 0) { tr5_room_light* oldLight = room->light; for (int l = 0; l < room->numLights; l++) { RendererLight light; if (oldLight->LightType == LIGHT_TYPES::LIGHT_TYPE_SUN) { light.Color = Vector3(oldLight->r, oldLight->g, oldLight->b); light.Direction = Vector4(oldLight->dx, oldLight->dy, oldLight->dz, 1.0f); light.Type = LIGHT_TYPES::LIGHT_TYPE_SUN; light.Intensity = 1.0f; r->Lights.push_back(light); } else if (oldLight->LightType == LIGHT_TYPE_POINT) { light.Position = Vector3(oldLight->x, oldLight->y, oldLight->z); light.Color = Vector3(oldLight->r, oldLight->g, oldLight->b); light.Direction = Vector4(oldLight->dx, oldLight->dy, oldLight->dz, 1.0f); light.Intensity = 1.0f; light.In = oldLight->In; light.Out = oldLight->Out; light.Type = LIGHT_TYPE_POINT; r->Lights.push_back(light); } else if (oldLight->LightType == LIGHT_TYPE_SHADOW) { light.Position = Vector3(oldLight->x, oldLight->y, oldLight->z); light.Color = Vector3(oldLight->r, oldLight->g, oldLight->b); light.In = oldLight->In; light.Out = oldLight->Out; light.Type = LIGHT_TYPE_SHADOW; light.Intensity = 1.0f; r->Lights.push_back(light); } else if (oldLight->LightType == LIGHT_TYPE_SPOT) { light.Position = Vector3(oldLight->x, oldLight->y, oldLight->z); light.Color = Vector3(oldLight->r, oldLight->g, oldLight->b); light.Direction = Vector4(oldLight->dx, oldLight->dy, oldLight->dz, 1.0f); light.Intensity = 1.0f; light.In = oldLight->In; light.Out = oldLight->Out; light.Range = oldLight->Range; light.Type = LIGHT_TYPE_SPOT; r->Lights.push_back(light); } oldLight++; } } MESH_INFO* mesh = room->mesh; for (int j = 0; j < room->numMeshes; j++) { RendererStatic obj; obj.Mesh = mesh; obj.RoomIndex = i; r->Statics.push_back(obj); } // Merge vertices and indices in a single list for (int j = 0; j < NUM_BUCKETS; j++) { RendererBucket* bucket = &r->Buckets[j]; bucket->StartVertex = baseRoomVertex; bucket->StartIndex = baseRoomIndex; for (int k = 0; k < bucket->Vertices.size(); k++) roomVertices.push_back(bucket->Vertices[k]); for (int k = 0; k < bucket->Indices.size(); k++) roomIndices.push_back(baseRoomVertex + bucket->Indices[k]); baseRoomVertex += bucket->Vertices.size(); baseRoomIndex += bucket->Indices.size(); } } // Create a single vertex buffer and a single index buffer for all rooms // NOTICE: in theory, a 1,000,000 vertices scene should have a VB of 52 MB and an IB of 4 MB m_roomsVertexBuffer = VertexBuffer::Create(m_device, roomVertices.size(), roomVertices.data()); m_roomsIndexBuffer = IndexBuffer::Create(m_device, roomIndices.size(), roomIndices.data()); m_numHairVertices = 0; m_numHairIndices = 0; vector moveablesVertices; vector moveablesIndices; int baseMoveablesVertex = 0; int baseMoveablesIndex = 0; // Step 3: prepare moveables for (int i = 0; i < MoveablesIds.size(); i++) { int objNum = MoveablesIds[i]; OBJECT_INFO* obj = &Objects[objNum]; if (obj->nmeshes > 0) { RendererObject* moveable = new RendererObject(); moveable->Id = MoveablesIds[i]; // Assign the draw routine if (objNum == ID_FLAME || objNum == ID_FLAME_EMITTER || objNum == ID_FLAME_EMITTER2 || objNum == ID_FLAME_EMITTER3 || objNum == ID_TRIGGER_TRIGGERER || objNum == ID_TIGHT_ROPE || objNum == ID_AI_AMBUSH || objNum == ID_AI_FOLLOW || objNum == ID_AI_GUARD || objNum == ID_AI_MODIFY || objNum == ID_AI_PATROL1 || objNum == ID_AI_PATROL2 || objNum == ID_AI_X1 || objNum == ID_AI_X2 || objNum == ID_DART_EMITTER || objNum == ID_HOMING_DART_EMITTER || objNum == ID_ROPE || objNum == ID_KILL_ALL_TRIGGERS || objNum == ID_EARTHQUAKE || objNum == ID_CAMERA_TARGET || objNum == ID_WATERFALLMIST || objNum == ID_SMOKE_EMITTER_BLACK || objNum == ID_SMOKE_EMITTER_WHITE) { moveable->DoNotDraw = true; } else { moveable->DoNotDraw = false; } for (int j = 0; j < obj->nmeshes; j++) { // HACK: mesh pointer 0 is the placeholder for Lara's body parts and is right hand with pistols // We need to override the bone index because the engine will take mesh 0 while drawing pistols anim, // and vertices have bone index 0 and not 10 int meshPtrIndex = RawMeshPointers[obj->meshIndex / 2 + j] / 2; int boneIndex = (meshPtrIndex == 0 ? LM_RHAND : j); short* meshPtr = &RawMeshData[meshPtrIndex]; RendererMesh* mesh = getRendererMeshFromTrMesh(moveable, meshPtr, Meshes[obj->meshIndex + 2 * j], boneIndex, MoveablesIds[i] == ID_LARA_SKIN_JOINTS, MoveablesIds[i] == ID_LARA_HAIR); moveable->ObjectMeshes.push_back(mesh); } int* bone = &Bones[obj->boneIndex]; stack stack; for (int j = 0; j < obj->nmeshes; j++) { moveable->LinearizedBones.push_back(new RendererBone(j)); moveable->AnimationTransforms.push_back(Matrix::Identity); moveable->BindPoseTransforms.push_back(Matrix::Identity); } RendererBone* currentBone = moveable->LinearizedBones[0]; RendererBone* stackBone = moveable->LinearizedBones[0]; for (int mi = 0; mi < obj->nmeshes - 1; mi++) { int j = mi + 1; int opcode = *(bone++); int linkX = *(bone++); int linkY = *(bone++); int linkZ = *(bone++); byte flags = opcode & 0x1C; moveable->LinearizedBones[j]->ExtraRotationFlags = flags; switch (opcode & 0x03) { case 0: moveable->LinearizedBones[j]->Parent = currentBone; moveable->LinearizedBones[j]->Translation = Vector3(linkX, linkY, linkZ); currentBone->Children.push_back(moveable->LinearizedBones[j]); currentBone = moveable->LinearizedBones[j]; break; case 1: if (stack.empty()) continue; currentBone = stack.top(); stack.pop(); moveable->LinearizedBones[j]->Parent = currentBone; moveable->LinearizedBones[j]->Translation = Vector3(linkX, linkY, linkZ); currentBone->Children.push_back(moveable->LinearizedBones[j]); currentBone = moveable->LinearizedBones[j]; break; case 2: stack.push(currentBone); moveable->LinearizedBones[j]->Translation = Vector3(linkX, linkY, linkZ); moveable->LinearizedBones[j]->Parent = currentBone; currentBone->Children.push_back(moveable->LinearizedBones[j]); currentBone = moveable->LinearizedBones[j]; break; case 3: if (stack.empty()) continue; RendererBone* theBone = stack.top(); stack.pop(); moveable->LinearizedBones[j]->Translation = Vector3(linkX, linkY, linkZ); moveable->LinearizedBones[j]->Parent = theBone; theBone->Children.push_back(moveable->LinearizedBones[j]); currentBone = moveable->LinearizedBones[j]; stack.push(theBone); break; } } for (int n = 0; n < obj->nmeshes; n++) moveable->LinearizedBones[n]->Transform = Matrix::CreateTranslation( moveable->LinearizedBones[n]->Translation.x, moveable->LinearizedBones[n]->Translation.y, moveable->LinearizedBones[n]->Translation.z); moveable->Skeleton = moveable->LinearizedBones[0]; buildHierarchy(moveable); // Fix Lara skin joints and hairs if (MoveablesIds[i] == ID_LARA_SKIN_JOINTS) { RendererObject* objSkin = m_moveableObjects[ID_LARA_SKIN]; for (int j = 1; j < obj->nmeshes; j++) { RendererMesh* jointMesh = moveable->ObjectMeshes[j]; RendererBone* jointBone = moveable->LinearizedBones[j]; for (int b1 = 0; b1 < NUM_BUCKETS; b1++) { RendererBucket* jointBucket = &jointMesh->Buckets[b1]; for (int v1 = 0; v1 < jointBucket->Vertices.size(); v1++) { RendererVertex* jointVertex = &jointBucket->Vertices[v1]; if (jointVertex->Bone != j) { RendererMesh* skinMesh = objSkin->ObjectMeshes[jointVertex->Bone]; RendererBone* skinBone = objSkin->LinearizedBones[jointVertex->Bone]; for (int b2 = 0; b2 < NUM_BUCKETS; b2++) { RendererBucket* skinBucket = &skinMesh->Buckets[b2]; for (int v2 = 0; v2 < skinBucket->Vertices.size(); v2++) { RendererVertex* skinVertex = &skinBucket->Vertices[v2]; int x1 = jointBucket->Vertices[v1].Position.x + jointBone->GlobalTranslation.x; int y1 = jointBucket->Vertices[v1].Position.y + jointBone->GlobalTranslation.y; int z1 = jointBucket->Vertices[v1].Position.z + jointBone->GlobalTranslation.z; int x2 = skinBucket->Vertices[v2].Position.x + skinBone->GlobalTranslation.x; int y2 = skinBucket->Vertices[v2].Position.y + skinBone->GlobalTranslation.y; int z2 = skinBucket->Vertices[v2].Position.z + skinBone->GlobalTranslation.z; if (abs(x1 - x2) < 2 && abs(y1 - y2) < 2 && abs(z1 - z2) < 2) { jointVertex->Position.x = skinVertex->Position.x; jointVertex->Position.y = skinVertex->Position.y; jointVertex->Position.z = skinVertex->Position.z; } } } } } } } } if (MoveablesIds[i] == ID_LARA_HAIR) { for (int j = 0; j < moveable->ObjectMeshes.size(); j++) { RendererMesh* mesh = moveable->ObjectMeshes[j]; for (int n = 0; n < NUM_BUCKETS; n++) { m_numHairVertices += mesh->Buckets[n].NumVertices; m_numHairIndices += mesh->Buckets[n].NumIndices; } } m_hairVertices.clear(); m_hairIndices.clear(); RendererVertex vertex; for (int m = 0; m < m_numHairVertices * 2; m++) m_hairVertices.push_back(vertex); for (int m = 0; m < m_numHairIndices * 2; m++) m_hairIndices.push_back(0); } m_moveableObjects[MoveablesIds[i]] = moveable; // Merge vertices and indices in a single list for (int m = 0; m < moveable->ObjectMeshes.size(); m++) { RendererMesh* msh = moveable->ObjectMeshes[m]; for (int j = 0; j < NUM_BUCKETS; j++) { RendererBucket* bucket = &msh->Buckets[j]; bucket->StartVertex = baseMoveablesVertex; bucket->StartIndex = baseMoveablesIndex; for (int k = 0; k < bucket->Vertices.size(); k++) moveablesVertices.push_back(bucket->Vertices[k]); for (int k = 0; k < bucket->Indices.size(); k++) moveablesIndices.push_back(baseMoveablesVertex + bucket->Indices[k]); baseMoveablesVertex += bucket->Vertices.size(); baseMoveablesIndex += bucket->Indices.size(); } } } } // Create a single vertex buffer and a single index buffer for all moveables m_moveablesVertexBuffer = VertexBuffer::Create(m_device, moveablesVertices.size(), moveablesVertices.data()); m_moveablesIndexBuffer = IndexBuffer::Create(m_device, moveablesIndices.size(), moveablesIndices.data()); // Step 4: prepare static meshes vector staticsVertices; vector staticsIndices; int baseStaticsVertex = 0; int baseStaticsIndex = 0; for (int i = 0; i < StaticObjectsIds.size(); i++) { STATIC_INFO* obj = &StaticObjects[StaticObjectsIds[i]]; RendererObject* staticObject = new RendererObject(); staticObject->Id = StaticObjectsIds[i]; short* meshPtr = &RawMeshData[RawMeshPointers[obj->meshNumber / 2] / 2]; RendererMesh* mesh = getRendererMeshFromTrMesh(staticObject, meshPtr, Meshes[obj->meshNumber], 0, false, false); staticObject->ObjectMeshes.push_back(mesh); m_staticObjects[StaticObjectsIds[i]] = staticObject; // Merge vertices and indices in a single list RendererMesh* msh = staticObject->ObjectMeshes[0]; for (int j = 0; j < NUM_BUCKETS; j++) { RendererBucket* bucket = &msh->Buckets[j]; bucket->StartVertex = baseStaticsVertex; bucket->StartIndex = baseStaticsIndex; for (int k = 0; k < bucket->Vertices.size(); k++) staticsVertices.push_back(bucket->Vertices[k]); for (int k = 0; k < bucket->Indices.size(); k++) staticsIndices.push_back(baseStaticsVertex + bucket->Indices[k]); baseStaticsVertex += bucket->Vertices.size(); baseStaticsIndex += bucket->Indices.size(); } } // Create missing meshes (effect objects like ID_BODY_PART have nmeshes = 0 and they are "lost" with current procedures) for (int i = 0; i < NumMeshPointers; i++) { unsigned int mp = reinterpret_cast(Meshes[i * 2]); RendererMesh* mesh = m_meshPointersToMesh[mp]; if (mesh == NULL) { int meshPtrIndex = RawMeshPointers[i] / 2; short* meshPtr = &RawMeshData[meshPtrIndex]; RendererMesh* mesh = getRendererMeshFromTrMesh(NULL, meshPtr, Meshes[i * 2], 0, false, false); } } // Create a single vertex buffer and a single index buffer for all statics m_staticsVertexBuffer = VertexBuffer::Create(m_device, staticsVertices.size(), staticsVertices.data()); m_staticsIndexBuffer = IndexBuffer::Create(m_device, staticsIndices.size(), staticsIndices.data()); // Step 5: prepare sprites m_sprites = (RendererSprite**)malloc(sizeof(RendererSprite*) * g_NumSprites); ZeroMemory(m_sprites, sizeof(RendererSprite*) * g_NumSprites); for (int i = 0; i < g_NumSprites; i++) { SPRITE* oldSprite = &Sprites[i]; RendererSprite* sprite = new RendererSprite(); sprite->Width = (oldSprite->right - oldSprite->left)*256.0f; sprite->Height = (oldSprite->bottom - oldSprite->top)*256.0f; float left = (oldSprite->left * 256.0f + GET_ATLAS_PAGE_X(oldSprite->tile - 1)); float top = (oldSprite->top * 256.0f + GET_ATLAS_PAGE_Y(oldSprite->tile - 1)); float right = (oldSprite->right * 256.0f + GET_ATLAS_PAGE_X(oldSprite->tile - 1)); float bottom = (oldSprite->bottom * 256.0f + GET_ATLAS_PAGE_Y(oldSprite->tile - 1)); sprite->UV[0] = Vector2(left / (float)TEXTURE_ATLAS_SIZE, top / (float)TEXTURE_ATLAS_SIZE); sprite->UV[1] = Vector2(right / (float)TEXTURE_ATLAS_SIZE, top / (float)TEXTURE_ATLAS_SIZE); sprite->UV[2] = Vector2(right / (float)TEXTURE_ATLAS_SIZE, bottom / (float)TEXTURE_ATLAS_SIZE); sprite->UV[3] = Vector2(left / (float)TEXTURE_ATLAS_SIZE, bottom / (float)TEXTURE_ATLAS_SIZE); m_sprites[i] = sprite; } for (int i = 0; i < MoveablesIds.size(); i++) { OBJECT_INFO* obj = &Objects[MoveablesIds[i]]; if (obj->nmeshes < 0) { short numSprites = abs(obj->nmeshes); short baseSprite = obj->meshIndex; RendererSpriteSequence* sequence = new RendererSpriteSequence(MoveablesIds[i], numSprites); for (int j = baseSprite; j < baseSprite + numSprites; j++) { sequence->SpritesList[j - baseSprite] = m_sprites[j]; } m_spriteSequences[MoveablesIds[i]] = sequence; } } for (int i = 0; i < 6; i++) { if (Objects[ID_WATERFALL1 + i].loaded) { // Get the first textured bucket RendererBucket* bucket = NULL; for (int j = 0; j < NUM_BUCKETS; j++) if (m_moveableObjects[ID_WATERFALL1 + i]->ObjectMeshes[0]->Buckets[j].Polygons.size() > 0) bucket = &m_moveableObjects[ID_WATERFALL1 + i]->ObjectMeshes[0]->Buckets[j]; if (bucket == NULL) continue; OBJECT_TEXTURE* texture = &ObjectTextures[bucket->Polygons[0].TextureId]; WaterfallTextures[i] = texture; WaterfallY[i] = texture->vertices[0].y; } } return true; } ID3D11VertexShader* Renderer11::compileVertexShader(const char* fileName, const char* function, const char* model, ID3D10Blob** bytecode) { HRESULT res; *bytecode = NULL; ID3DBlob* errors = NULL; printf("Compiling vertex shader: %s\n", fileName); res = D3DX11CompileFromFileA(fileName, NULL, NULL, function, model, D3D10_SHADER_OPTIMIZATION_LEVEL3, 0, NULL, bytecode, &errors, NULL); if (FAILED(res)) { printf("Compilation failed: %s\n", errors->GetBufferPointer()); return NULL; } ID3D11VertexShader* shader = NULL; res = m_device->CreateVertexShader((*bytecode)->GetBufferPointer(), (*bytecode)->GetBufferSize(), NULL, &shader); if (FAILED(res)) return NULL; return shader; } ID3D11PixelShader* Renderer11::compilePixelShader(const char* fileName, const char* function, const char* model, ID3D10Blob** bytecode) { HRESULT res; *bytecode = NULL; ID3DBlob* errors = NULL; printf("Compiling pixel shader: %s\n", fileName); res = D3DX11CompileFromFileA(fileName, NULL, NULL, function, model, D3D10_SHADER_OPTIMIZATION_LEVEL3, 0, NULL, bytecode, &errors, NULL); if (FAILED(res)) { printf("Compilation failed: %s\n", errors->GetBufferPointer()); return NULL; } ID3D11PixelShader* shader = NULL; res = m_device->CreatePixelShader((*bytecode)->GetBufferPointer(), (*bytecode)->GetBufferSize(), NULL, &shader); if (FAILED(res)) return NULL; return shader; } ID3D11GeometryShader* Renderer11::compileGeometryShader(const char* fileName) { HRESULT res; ID3DBlob* bytecode = NULL; ID3DBlob* errors = NULL; res = D3DX11CompileFromFileA(fileName, NULL, NULL, NULL, "gs_4_0", D3D10_SHADER_OPTIMIZATION_LEVEL3, 0, NULL, &bytecode, &errors, NULL); if (FAILED(res)) return NULL; ID3D11GeometryShader* shader = NULL; res = m_device->CreateGeometryShader(bytecode->GetBufferPointer(), bytecode->GetBufferSize(), NULL, &shader); if (FAILED(res)) return NULL; return shader; } ID3D11ComputeShader* Renderer11::compileComputeShader(const char* fileName) { HRESULT res; ID3DBlob* bytecode = NULL; ID3DBlob* errors = NULL; res = D3DX11CompileFromFileA(fileName, NULL, NULL, NULL, "gs_4_0", D3D10_SHADER_OPTIMIZATION_LEVEL3, 0, NULL, &bytecode, &errors, NULL); if (FAILED(res)) return NULL; ID3D11ComputeShader* shader = NULL; res = m_device->CreateComputeShader(bytecode->GetBufferPointer(), bytecode->GetBufferSize(), NULL, &shader); if (FAILED(res)) return NULL; return shader; } void Renderer11::ClearDynamicLights() { m_dynamicLights.Clear(); } void Renderer11::AddDynamicLight(int x, int y, int z, short falloff, byte r, byte g, byte b) { if (m_nextLight >= MAX_LIGHTS) return; RendererLight* dynamicLight = &m_lights[m_nextLight++]; dynamicLight->Position = Vector3(float(x), float(y), float(z)); dynamicLight->Color = Vector3(r / 255.0f, g / 255.0f, b / 255.0f); dynamicLight->Out = falloff * 256.0f; dynamicLight->Type = LIGHT_TYPES::LIGHT_TYPE_POINT; dynamicLight->Dynamic = 1; dynamicLight->Intensity = 2.0f; m_dynamicLights.Add(dynamicLight); //NumDynamics++; } void Renderer11::EnableCinematicBars(bool value) { m_enableCinematicBars = value; } void Renderer11::FadeIn() { m_fadeStatus = RENDERER_FADE_STATUS::FADE_IN; m_fadeFactor = 0.0f; } void Renderer11::FadeOut() { m_fadeStatus = RENDERER_FADE_STATUS::FADE_OUT; m_fadeFactor = 1.0f; } void Renderer11::DrawLoadingScreen(char* fileName) { return; Texture2D* texture = Texture2D::LoadFromFile(m_device, fileName); if (texture == NULL) return; m_fadeStatus = RENDERER_FADE_STATUS::FADE_IN; m_fadeFactor = 0.0f; while (true) { if (m_fadeStatus == RENDERER_FADE_STATUS::FADE_IN && m_fadeFactor < 1.0f) m_fadeFactor += FADE_FACTOR; if (m_fadeStatus == RENDERER_FADE_STATUS::FADE_OUT && m_fadeFactor > 0.0f) m_fadeFactor -= FADE_FACTOR; // Set basic render states m_context->OMSetDepthStencilState(m_states->DepthDefault(), 0); m_context->RSSetState(m_states->CullCounterClockwise()); m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); // Clear screen m_context->ClearRenderTargetView(m_backBufferRTV, Colors::Black); m_context->ClearDepthStencilView(m_depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); // Bind the back buffer m_context->OMSetRenderTargets(1, &m_backBufferRTV, m_depthStencilView); m_context->RSSetViewports(1, &m_viewport); // Draw the full screen background drawFullScreenQuad(texture->ShaderResourceView, Vector3(m_fadeFactor, m_fadeFactor, m_fadeFactor), false); m_context->ClearDepthStencilView(m_depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); m_swapChain->Present(0, 0); if (m_fadeStatus == RENDERER_FADE_STATUS::FADE_IN && m_fadeFactor >= 1.0f) { m_fadeStatus = RENDERER_FADE_STATUS::NO_FADE; m_fadeFactor = 1.0f; } if (m_fadeStatus == RENDERER_FADE_STATUS::NO_FADE && m_progress == 100) { m_fadeStatus = RENDERER_FADE_STATUS::FADE_OUT; m_fadeFactor = 1.0f; } if (m_fadeStatus == RENDERER_FADE_STATUS::FADE_OUT && m_fadeFactor <= 0.0f) { break; } } delete texture; } void Renderer11::UpdateProgress(float value) { m_progress = value; } bool Renderer11::IsFading() { return (m_fadeStatus != FADEMODE_NONE); } void Renderer11::GetLaraBonePosition(Vector3* pos, int bone) { } bool Renderer11::ToggleFullScreen() { return true; } bool Renderer11::IsFullsScreen() { return (!Windowed); } bool Renderer11::ChangeScreenResolution(int width, int height, int frequency, bool windowed) { HRESULT res; /*if (windowed && !Windowed) { res = m_swapChain->SetFullscreenState(false, NULL); if (FAILED(res)) return false; } else if (!windowed && Windowed) { res = m_swapChain->SetFullscreenState(true, NULL); if(FAILED(res)) return false; } IDXGIOutput* output; res = m_swapChain->GetContainingOutput(&output); if(FAILED(res)) return false; DXGI_SWAP_CHAIN_DESC scd; res = m_swapChain->GetDesc(&scd); if (FAILED(res)) return false; UINT numModes = 1024; DXGI_MODE_DESC modes[1024]; res = output->GetDisplayModeList(scd.BufferDesc.Format, 0, &numModes, modes); if (FAILED(res)) return false; DXGI_MODE_DESC* mode = &modes[0]; for (int i = 0; i < numModes; i++) { mode = &modes[i]; if (mode->Width == width && mode->Height == height) break; } ID3D11RenderTargetView* nullViews[] = { nullptr }; m_context->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); m_backBufferRTV->Release(); // Microsoft::WRL::ComPtr here does a Release(); m_depthStencilView->Release(); m_context->Flush(); res = m_swapChain->ResizeBuffers(2, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0); res = m_swapChain->ResizeTarget(mode); if (FAILED(res)) return false; RECT rect; GetClientRect(WindowsHandle, &rect); UINT w = static_cast(rect.right); UINT h = static_cast(rect.bottom); m_context->ClearState(); m_backBufferRTV->Release(); m_depthStencilView->Release(); m_depthStencilTexture->Release(); res = m_swapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH); if (FAILED(res)) return false; // Recreate render target res = m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&m_backBufferTexture); if (FAILED(res)) return false; m_device->CreateRenderTargetView(m_backBufferTexture, NULL, &m_backBufferRTV); D3D11_TEXTURE2D_DESC backBufferDesc; m_backBufferTexture->GetDesc(&backBufferDesc); m_backBufferTexture->Release(); D3D11_TEXTURE2D_DESC depthStencilDesc; depthStencilDesc.Width = width; depthStencilDesc.Height = height; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthStencilDesc.SampleDesc.Count = 1; depthStencilDesc.SampleDesc.Quality = 0; depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; m_depthStencilTexture = NULL; res = m_device->CreateTexture2D(&depthStencilDesc, NULL, &m_depthStencilTexture); if (FAILED(res)) return false; m_depthStencilView = NULL; res = m_device->CreateDepthStencilView(m_depthStencilTexture, NULL, &m_depthStencilView); if (FAILED(res)) return false; DX11_DELETE(m_renderTarget); DX11_DELETE(m_dumpScreenRenderTarget); DX11_DELETE(m_shadowMap); m_renderTarget = RenderTarget2D::Create(m_device, width, height, DXGI_FORMAT_R8G8B8A8_UNORM); m_dumpScreenRenderTarget = RenderTarget2D::Create(m_device, width, height, DXGI_FORMAT_R8G8B8A8_UNORM); m_shadowMap = RenderTarget2D::Create(m_device, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, DXGI_FORMAT_R32_FLOAT); DX11_DELETE(m_gameFont); DX11_DELETE(m_spriteBatch); DX11_DELETE(m_primitiveBatch); m_spriteBatch = new SpriteBatch(m_context); m_gameFont = new SpriteFont(m_device, L"Font.spritefont"); m_primitiveBatch = new PrimitiveBatch(m_context); ScreenWidth = width; ScreenHeight = height; Windowed = windowed;*/ ID3D11RenderTargetView* nullViews[] = { nullptr }; m_context->OMSetRenderTargets(0, nullViews, NULL); DX11_DELETE(m_renderTarget); DX11_DELETE(m_dumpScreenRenderTarget); DX11_DELETE(m_shadowMap); DX11_DELETE(m_gameFont); DX11_DELETE(m_spriteBatch); DX11_DELETE(m_primitiveBatch); m_backBufferTexture->Release(); m_backBufferRTV->Release(); m_depthStencilView->Release(); m_depthStencilTexture->Release(); m_context->Flush(); m_context->ClearState(); //res = m_swapChain->ResizeBuffers(2, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0); /*res = m_swapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH); if (FAILED(res)) return false;*/ IDXGIOutput* output; res = m_swapChain->GetContainingOutput(&output); if (FAILED(res)) return false; DXGI_SWAP_CHAIN_DESC scd; res = m_swapChain->GetDesc(&scd); if (FAILED(res)) return false; UINT numModes = 1024; DXGI_MODE_DESC modes[1024]; res = output->GetDisplayModeList(scd.BufferDesc.Format, 0, &numModes, modes); if (FAILED(res)) return false; DXGI_MODE_DESC* mode = &modes[0]; for (int i = 0; i < numModes; i++) { mode = &modes[i]; if (mode->Width == width && mode->Height == height) break; } res = m_swapChain->ResizeTarget(mode); if (FAILED(res)) return false; if (!initialiseScreen(width, height, frequency, windowed, WindowsHandle, true)) return false; ScreenWidth = width; ScreenHeight = height; Windowed = windowed; return true; } ID3D11Buffer* Renderer11::createConstantBuffer(int size) { ID3D11Buffer* buffer; D3D11_BUFFER_DESC desc; ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC)); desc.ByteWidth = ceil(size / 16) * 16; // Constant buffer must have a size multiple of 16 bytes desc.Usage = D3D11_USAGE_DYNAMIC; desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; HRESULT res = m_device->CreateBuffer(&desc, NULL, &buffer); if (FAILED(res)) return NULL; return buffer; } int Renderer11::getAnimatedTextureInfo(short textureId) { for (int i = 0; i < m_numAnimatedTextureSets; i++) { RendererAnimatedTextureSet* set = m_animatedTextureSets[i]; for (int j = 0; j < set->NumTextures; j++) { if (set->Textures[j]->Id == textureId) return i; } } return -1; } void Renderer11::initialiseHairRemaps() { memset(m_normalLaraSkinJointRemap, -1, 15 * 32 * 2); memset(m_youngLaraSkinJointRemap, -1, 15 * 32 * 2); // Normal Lara m_normalLaraSkinJointRemap[1][0] = 0; m_normalLaraSkinJointRemap[1][1] = 0; m_normalLaraSkinJointRemap[1][2] = 0; m_normalLaraSkinJointRemap[1][3] = 0; m_normalLaraSkinJointRemap[1][4] = 0; m_normalLaraSkinJointRemap[1][5] = 0; m_normalLaraSkinJointRemap[2][0] = 1; m_normalLaraSkinJointRemap[2][1] = 1; m_normalLaraSkinJointRemap[2][2] = 1; m_normalLaraSkinJointRemap[2][3] = 1; m_normalLaraSkinJointRemap[2][4] = 1; m_normalLaraSkinJointRemap[3][4] = 2; m_normalLaraSkinJointRemap[3][5] = 2; m_normalLaraSkinJointRemap[3][6] = 2; m_normalLaraSkinJointRemap[3][7] = 2; m_normalLaraSkinJointRemap[4][0] = 0; m_normalLaraSkinJointRemap[4][1] = 0; m_normalLaraSkinJointRemap[4][2] = 0; m_normalLaraSkinJointRemap[4][3] = 0; m_normalLaraSkinJointRemap[4][4] = 0; m_normalLaraSkinJointRemap[4][5] = 0; m_normalLaraSkinJointRemap[5][0] = 4; m_normalLaraSkinJointRemap[5][1] = 4; m_normalLaraSkinJointRemap[5][2] = 4; m_normalLaraSkinJointRemap[5][3] = 4; m_normalLaraSkinJointRemap[5][4] = 4; m_normalLaraSkinJointRemap[6][4] = 5; m_normalLaraSkinJointRemap[6][5] = 5; m_normalLaraSkinJointRemap[6][6] = 5; m_normalLaraSkinJointRemap[6][7] = 5; m_normalLaraSkinJointRemap[7][0] = 0; m_normalLaraSkinJointRemap[7][1] = 0; m_normalLaraSkinJointRemap[7][2] = 0; m_normalLaraSkinJointRemap[7][3] = 0; m_normalLaraSkinJointRemap[7][4] = 0; m_normalLaraSkinJointRemap[7][5] = 0; m_normalLaraSkinJointRemap[8][6] = 7; m_normalLaraSkinJointRemap[8][7] = 7; m_normalLaraSkinJointRemap[8][8] = 7; m_normalLaraSkinJointRemap[8][9] = 7; m_normalLaraSkinJointRemap[8][10] = 7; m_normalLaraSkinJointRemap[8][11] = 7; m_normalLaraSkinJointRemap[9][5] = 8; m_normalLaraSkinJointRemap[9][6] = 8; m_normalLaraSkinJointRemap[9][7] = 8; m_normalLaraSkinJointRemap[9][8] = 8; m_normalLaraSkinJointRemap[9][9] = 8; m_normalLaraSkinJointRemap[10][0] = 9; m_normalLaraSkinJointRemap[10][1] = 9; m_normalLaraSkinJointRemap[10][2] = 9; m_normalLaraSkinJointRemap[10][3] = 9; m_normalLaraSkinJointRemap[10][4] = 9; m_normalLaraSkinJointRemap[11][6] = 7; m_normalLaraSkinJointRemap[11][7] = 7; m_normalLaraSkinJointRemap[11][8] = 7; m_normalLaraSkinJointRemap[11][9] = 7; m_normalLaraSkinJointRemap[11][10] = 7; m_normalLaraSkinJointRemap[11][11] = 7; m_normalLaraSkinJointRemap[12][5] = 11; m_normalLaraSkinJointRemap[12][6] = 11; m_normalLaraSkinJointRemap[12][7] = 11; m_normalLaraSkinJointRemap[12][8] = 11; m_normalLaraSkinJointRemap[12][9] = 11; m_normalLaraSkinJointRemap[13][0] = 12; m_normalLaraSkinJointRemap[13][1] = 12; m_normalLaraSkinJointRemap[13][2] = 12; m_normalLaraSkinJointRemap[13][3] = 12; m_normalLaraSkinJointRemap[13][4] = 12; m_normalLaraSkinJointRemap[14][6] = 7; m_normalLaraSkinJointRemap[14][7] = 7; m_normalLaraSkinJointRemap[14][8] = 7; m_normalLaraSkinJointRemap[14][9] = 7; m_normalLaraSkinJointRemap[14][10] = 7; m_normalLaraSkinJointRemap[14][11] = 7; // Young Lara m_youngLaraSkinJointRemap[1][0] = 0; // Left up leg m_youngLaraSkinJointRemap[1][1] = 0; m_youngLaraSkinJointRemap[1][2] = 0; m_youngLaraSkinJointRemap[1][3] = 0; m_youngLaraSkinJointRemap[1][4] = 0; m_youngLaraSkinJointRemap[1][5] = 0; m_youngLaraSkinJointRemap[2][0] = 1; // Bottom left leg m_youngLaraSkinJointRemap[2][1] = 1; m_youngLaraSkinJointRemap[2][2] = 1; m_youngLaraSkinJointRemap[2][3] = 1; m_youngLaraSkinJointRemap[2][4] = 1; m_youngLaraSkinJointRemap[3][0] = 2; // Left foot m_youngLaraSkinJointRemap[3][1] = 2; m_youngLaraSkinJointRemap[3][2] = 2; m_youngLaraSkinJointRemap[3][3] = 2; m_youngLaraSkinJointRemap[4][6] = 0; // Right upper leg m_youngLaraSkinJointRemap[4][7] = 0; m_youngLaraSkinJointRemap[4][8] = 0; m_youngLaraSkinJointRemap[4][9] = 0; m_youngLaraSkinJointRemap[4][10] = 0; m_youngLaraSkinJointRemap[4][11] = 0; m_youngLaraSkinJointRemap[5][0] = 4; // Right bottom leg m_youngLaraSkinJointRemap[5][1] = 4; m_youngLaraSkinJointRemap[5][2] = 4; m_youngLaraSkinJointRemap[5][3] = 4; m_youngLaraSkinJointRemap[5][4] = 4; m_youngLaraSkinJointRemap[6][0] = 5; // Right foot m_youngLaraSkinJointRemap[6][1] = 5; m_youngLaraSkinJointRemap[6][2] = 5; m_youngLaraSkinJointRemap[6][3] = 5; m_youngLaraSkinJointRemap[7][0] = 0; // Torso m_youngLaraSkinJointRemap[7][1] = 0; m_youngLaraSkinJointRemap[7][2] = 0; m_youngLaraSkinJointRemap[7][3] = 0; m_youngLaraSkinJointRemap[7][4] = 0; m_youngLaraSkinJointRemap[7][5] = 0; m_youngLaraSkinJointRemap[8][0] = 7; // Left upper arm m_youngLaraSkinJointRemap[8][1] = 7; m_youngLaraSkinJointRemap[8][2] = 7; m_youngLaraSkinJointRemap[8][3] = 7; m_youngLaraSkinJointRemap[8][4] = 7; m_youngLaraSkinJointRemap[8][5] = 7; m_youngLaraSkinJointRemap[9][5] = 8; // Left bottom arm m_youngLaraSkinJointRemap[9][6] = 8; m_youngLaraSkinJointRemap[9][7] = 8; m_youngLaraSkinJointRemap[9][8] = 8; m_youngLaraSkinJointRemap[9][9] = 8; m_youngLaraSkinJointRemap[10][0] = 9; // Left hand m_youngLaraSkinJointRemap[10][1] = 9; m_youngLaraSkinJointRemap[10][2] = 9; m_youngLaraSkinJointRemap[10][3] = 9; m_youngLaraSkinJointRemap[10][4] = 9; m_youngLaraSkinJointRemap[11][0] = 7; // Right upper arm m_youngLaraSkinJointRemap[11][1] = 7; m_youngLaraSkinJointRemap[11][2] = 7; m_youngLaraSkinJointRemap[11][3] = 7; m_youngLaraSkinJointRemap[11][4] = 7; m_youngLaraSkinJointRemap[11][5] = 7; m_youngLaraSkinJointRemap[12][5] = 11; // Right low arm m_youngLaraSkinJointRemap[12][6] = 11; m_youngLaraSkinJointRemap[12][7] = 11; m_youngLaraSkinJointRemap[12][8] = 11; m_youngLaraSkinJointRemap[12][9] = 11; m_youngLaraSkinJointRemap[13][0] = 12; // Right arm m_youngLaraSkinJointRemap[13][1] = 12; m_youngLaraSkinJointRemap[13][2] = 12; m_youngLaraSkinJointRemap[13][3] = 12; m_youngLaraSkinJointRemap[13][4] = 12; m_youngLaraSkinJointRemap[14][0] = 7; // Head m_youngLaraSkinJointRemap[14][1] = 7; m_youngLaraSkinJointRemap[14][2] = 7; m_youngLaraSkinJointRemap[14][3] = 7; m_youngLaraSkinJointRemap[14][4] = 7; m_youngLaraSkinJointRemap[14][5] = 7; } void Renderer11::getVisibleRooms(int from, int to, Vector4* viewPort, bool water, int count) { // Avoid allocations, 1024 should be fine RendererRoomNode nodes[1024]; int nextNode = 0; // Avoid reallocations, 1024 should be fine RendererRoomNode* stack[1024]; int stackDepth = 0; RendererRoomNode* node = &nodes[nextNode++]; node->To = to; node->From = -1; // Push stack[stackDepth++] = node; while (stackDepth > 0) { // Pop node = stack[--stackDepth]; if (m_rooms[node->To]->Visited) continue; ROOM_INFO* room = &Rooms[node->To]; Vector3 roomCentre = Vector3(room->x + room->xSize * WALL_SIZE / 2.0f, (room->minfloor + room->maxceiling) / 2.0f, room->z + room->ySize * WALL_SIZE / 2.0f); Vector3 laraPosition = Vector3(Camera.pos.x, Camera.pos.y, Camera.pos.z); m_rooms[node->To]->Distance = (roomCentre - laraPosition).Length(); m_rooms[node->To]->Visited = true; m_roomsToDraw.Add(m_rooms[node->To]); collectLightsForRoom(node->To); collectItems(node->To); collectStatics(node->To); collectEffects(node->To); Vector4 clipPort; short numDoors = *(room->door); if (numDoors) { short* door = room->door + 1; for (int i = 0; i < numDoors; i++) { short adjoiningRoom = *(door); if (node->From != adjoiningRoom && checkPortal(node->To, door, viewPort, &node->ClipPort)) { RendererRoomNode* childNode = &nodes[nextNode++]; childNode->From = node->To; childNode->To = adjoiningRoom; // Push stack[stackDepth++] = childNode; } door += 16; } } } } bool Renderer11::checkPortal(short roomIndex, short* portal, Vector4* viewPort, Vector4* clipPort) { //return true; ROOM_INFO* room = &Rooms[roomIndex]; Vector3 n = Vector3(*(portal + 1), *(portal + 2), *(portal + 3)); Vector3 v = Vector3(Camera.pos.x - (room->x + *(portal + 4)), Camera.pos.y - (room->y + *(portal + 5)), Camera.pos.z - (room->z + *(portal + 6))); if (n.Dot(v) <= 0.0f) return false; int zClip = 0; Vector4 p[4]; clipPort->x = FLT_MAX; clipPort->y = FLT_MAX; clipPort->z = FLT_MIN; clipPort->w = FLT_MIN; for (int i = 0; i < 4; i++) { Vector4 tmp = Vector4(*(portal + 4 + 3 * i) + room->x, *(portal + 4 + 3 * i + 1) + room->y, *(portal + 4 + 3 * i + 2) + room->z, 1.0f); Vector4::Transform(tmp, ViewProjection, p[i]); if (p[i].w > 0.0f) { p[i].x *= (1.0f / p[i].w); p[i].y *= (1.0f / p[i].w); p[i].z *= (1.0f / p[i].w); clipPort->x = min(clipPort->x, p[i].x); clipPort->y = min(clipPort->y, p[i].y); clipPort->z = max(clipPort->z, p[i].x); clipPort->w = max(clipPort->w, p[i].y); } else zClip++; } if (zClip == 4) return false; if (zClip > 0) { for (int i = 0; i < 4; i++) { Vector4 a = p[i]; Vector4 b = p[(i + 1) % 4]; if ((a.w > 0.0f) ^ (b.w > 0.0f)) { if (a.x < 0.0f && b.x < 0.0f) clipPort->x = -1.0f; else if (a.x > 0.0f && b.x > 0.0f) clipPort->z = 1.0f; else { clipPort->x = -1.0f; clipPort->z = 1.0f; } if (a.y < 0.0f && b.y < 0.0f) clipPort->y = -1.0f; else if (a.y > 0.0f && b.y > 0.0f) clipPort->w = 1.0f; else { clipPort->y = -1.0f; clipPort->w = 1.0f; } } } } if (clipPort->x > viewPort->z || clipPort->y > viewPort->w || clipPort->z < viewPort->x || clipPort->w < viewPort->y) return false; clipPort->x = max(clipPort->x, viewPort->x); clipPort->y = max(clipPort->y, viewPort->y); clipPort->z = min(clipPort->z, viewPort->z); clipPort->w = min(clipPort->w, viewPort->w); return true; } void Renderer11::collectRooms() { short baseRoomIndex = Camera.pos.roomNumber; for (int i = 0; i < NumberRooms; i++) { m_rooms[i]->Visited = false; m_rooms[i]->LightsToDraw.Clear(); } Vector4 vp = Vector4(-1.0f, -1.0f, 1.0f, 1.0f); getVisibleRooms(-1, baseRoomIndex, &vp, false, 0); } inline void Renderer11::collectItems(short roomNumber) { RendererRoom* room = m_rooms[roomNumber]; if (room == NULL) return; ROOM_INFO* r = room->Room; short itemNum = NO_ITEM; for (itemNum = r->itemNumber; itemNum != NO_ITEM; itemNum = Items[itemNum].nextItem) { //printf("ItemNum: %d, NextItem: %d\n", itemNum, Items[itemNum].nextItem); ITEM_INFO* item = &Items[itemNum]; if (item->objectNumber == ID_LARA && itemNum == Items[itemNum].nextItem) break; if (item->objectNumber == ID_LARA) continue; if (item->status == ITEM_INVISIBLE) continue; if (m_moveableObjects[item->objectNumber] == NULL) continue; RendererItem* newItem = &m_items[itemNum]; newItem->Item = item; newItem->Id = itemNum; newItem->NumMeshes = Objects[item->objectNumber].nmeshes; newItem->World = Matrix::CreateFromYawPitchRoll(TR_ANGLE_TO_RAD(item->pos.yRot), TR_ANGLE_TO_RAD(item->pos.xRot), TR_ANGLE_TO_RAD(item->pos.zRot)) * Matrix::CreateTranslation(item->pos.xPos, item->pos.yPos, item->pos.zPos); collectLightsForItem(item->roomNumber, newItem); m_itemsToDraw.Add(newItem); } } inline void Renderer11::collectStatics(short roomNumber) { RendererRoom* room = m_rooms[roomNumber]; if (room == NULL) return; ROOM_INFO* r = room->Room; if (r->numMeshes <= 0) return; MESH_INFO* mesh = r->mesh; int numStatics = room->Statics.size(); for (int i = 0; i < numStatics; i++) { RendererStatic* newStatic = &room->Statics[i]; newStatic->Mesh = mesh; newStatic->RoomIndex = roomNumber; newStatic->World = Matrix::CreateRotationY(TR_ANGLE_TO_RAD(mesh->yRot)) * Matrix::CreateTranslation(mesh->x, mesh->y, mesh->z); m_staticsToDraw.Add(newStatic); mesh++; } } inline void Renderer11::collectLightsForEffect(short roomNumber, RendererEffect* effect) { effect->Lights.Clear(); RendererRoom* room = m_rooms[roomNumber]; if (room == NULL) return; ROOM_INFO * r = room->Room; if (r->numLights <= 0) return; m_tempItemLights.Clear(); Vector3 itemPosition = Vector3(effect->Effect->pos.xPos, effect->Effect->pos.yPos, effect->Effect->pos.zPos); // Dynamic lights have the priority for (int i = 0; i < m_dynamicLights.Size(); i++) { RendererLight* light = m_dynamicLights[i]; Vector3 lightPosition = Vector3(light->Position.x, light->Position.y, light->Position.z); float distance = (itemPosition - lightPosition).Length(); if (distance > light->Out) continue; m_tempItemLights.Add(light); } int numLights = room->Lights.size(); m_shadowLight = NULL; RendererLight* brightestLight = NULL; float brightest = 0.0f; for (int j = 0; j < numLights; j++) { RendererLight* light = &room->Lights[j]; // Check only lights different from sun if (light->Type == LIGHT_TYPE_SUN) { // Sun is added without checks } else if (light->Type == LIGHT_TYPE_POINT || light->Type == LIGHT_TYPE_SHADOW) { Vector3 lightPosition = Vector3(light->Position.x, light->Position.y, light->Position.z); float distance = (itemPosition - lightPosition).Length(); // Collect only lights nearer than 20 sectors if (distance >= 20 * WALL_SIZE) continue; // Check the out radius if (distance > light->Out) continue; // If Lara, try to collect shadow casting light if (effect->Effect->objectNumber == ID_LARA) { float attenuation = 1.0f - distance / light->Out; float intensity = max(0.0f, attenuation * (light->Color.x + light->Color.y + light->Color.z) / 3.0f); if (intensity >= brightest) { brightest = intensity; brightestLight = light; } } } else if (light->Type == LIGHT_TYPE_SPOT) { Vector3 lightPosition = Vector3(light->Position.x, light->Position.y, light->Position.z); float distance = (itemPosition - lightPosition).Length(); // Collect only lights nearer than 20 sectors if (distance >= 20 * WALL_SIZE) continue; // Check the range if (distance > light->Range) continue; } else { // Invalid light type continue; } m_tempItemLights.Add(light); } for (int i = 0; i < min(MAX_LIGHTS_PER_ITEM, m_tempItemLights.Size()); i++) { effect->Lights.Add(m_tempItemLights[i]); } } inline void Renderer11::collectLightsForItem(short roomNumber, RendererItem* item) { item->Lights.Clear(); RendererRoom* room = m_rooms[roomNumber]; if (room == NULL) return; ROOM_INFO* r = room->Room; if (r->numLights <= 0) return; m_tempItemLights.Clear(); Vector3 itemPosition = Vector3(item->Item->pos.xPos, item->Item->pos.yPos, item->Item->pos.zPos); // Dynamic lights have the priority for (int i = 0; i < m_dynamicLights.Size(); i++) { RendererLight* light = m_dynamicLights[i]; Vector3 lightPosition = Vector3(light->Position.x, light->Position.y, light->Position.z); float distance = (itemPosition - lightPosition).Length(); if (distance > light->Out) continue; m_tempItemLights.Add(light); } int numLights = room->Lights.size(); m_shadowLight = NULL; RendererLight* brightestLight = NULL; float brightest = 0.0f; for (int j = 0; j < numLights; j++) { RendererLight* light = &room->Lights[j]; // Check only lights different from sun if (light->Type == LIGHT_TYPE_SUN) { // Sun is added without checks } else if (light->Type == LIGHT_TYPE_POINT || light->Type == LIGHT_TYPE_SHADOW) { Vector3 lightPosition = Vector3(light->Position.x, light->Position.y, light->Position.z); float distance = (itemPosition - lightPosition).Length(); // Collect only lights nearer than 20 sectors if (distance >= 20 * WALL_SIZE) continue; // Check the out radius if (distance > light->Out) continue; // If Lara, try to collect shadow casting light if (item->Item->objectNumber == ID_LARA) { float attenuation = 1.0f - distance / light->Out; float intensity = max(0.0f, attenuation * (light->Color.x + light->Color.y + light->Color.z) / 3.0f); if (intensity >= brightest) { brightest = intensity; brightestLight = light; } } } else if (light->Type == LIGHT_TYPE_SPOT) { Vector3 lightPosition = Vector3(light->Position.x, light->Position.y, light->Position.z); float distance = (itemPosition - lightPosition).Length(); // Collect only lights nearer than 20 sectors if (distance >= 20 * WALL_SIZE) continue; // Check the range if (distance > light->Range) continue; // If Lara, try to collect shadow casting light if (item->Item->objectNumber == ID_LARA) { float attenuation = 1.0f - distance / light->Range; float intensity = max(0.0f, attenuation * (light->Color.x + light->Color.y + light->Color.z) / 3.0f); if (intensity >= brightest) { brightest = intensity; brightestLight = light; } } } else { // Invalid light type continue; } m_tempItemLights.Add(light); } for (int i = 0; i < min(MAX_LIGHTS_PER_ITEM, m_tempItemLights.Size()); i++) { item->Lights.Add(m_tempItemLights[i]); } if (item->Item->objectNumber == ID_LARA) { m_shadowLight = brightestLight; } } inline void Renderer11::collectLightsForRoom(short roomNumber) { RendererRoom* room = m_rooms[roomNumber]; if (room == NULL) return; ROOM_INFO* r = room->Room; if (r->numLights <= 0) return; int numLights = room->Lights.size(); // Collect dynamic lights for rooms for (int i = 0; i < m_dynamicLights.Size(); i++) { RendererLight* light = m_dynamicLights[i]; float left = r->x + WALL_SIZE; float bottom = r->z + WALL_SIZE; float right = r->x + (r->xSize - 1) * WALL_SIZE; float top = r->z + (r->ySize - 1) * WALL_SIZE; float closestX = light->Position.x; if (closestX < left) closestX = left; else if (closestX > right) closestX = right; float closestZ = light->Position.z; if (closestZ < bottom) closestZ = bottom; else if (closestZ > top) closestZ = top; // Calculate the distance between the circle's center and this closest point float distanceX = light->Position.x - closestX; float distanceY = light->Position.z - closestZ; // If the distance is less than the circle's radius, an intersection occurs float distanceSquared = (distanceX * distanceX) + (distanceY * distanceY); if (distanceSquared < SQUARE(light->Out)) room->LightsToDraw.Add(light); } } bool Renderer11::sphereBoxIntersection(Vector3 boxMin, Vector3 boxMax, Vector3 sphereCentre, float sphereRadius) { //Vector3 closestPointInAabb = Vector3::Min(Vector3::Max(sphereCentre, boxMin), boxMax); //double distanceSquared = (closestPointInAabb - sphereCentre).LengthSquared(); //return (distanceSquared < (sphereRadius * sphereRadius)); /*float x = max(boxMin.x, min(sphereCentre.x, boxMax.x)); float y = max(boxMin.y, min(sphereCentre.y, boxMax.y)); float z = max(boxMin.z, min(sphereCentre.z, boxMax.z)); float distance = sqrt((x - sphereCentre.x) * (x - sphereCentre.x) + (y - sphereCentre.y) * (y - sphereCentre.y) + (z - sphereCentre.z) * (z - sphereCentre.z)); return (distance < sphereRadius);*/ return 0; } void Renderer11::prepareLights() { // Add dynamic lights for (int i = 0; i < m_dynamicLights.Size(); i++) m_lightsToDraw.Add(m_dynamicLights[i]); // Now I have a list full of draw. Let's sort them. //std::sort(m_lightsToDraw.begin(), m_lightsToDraw.end(), SortLightsFunction); //m_lightsToDraw.Sort(SortLightsFunction); // Let's draw first 32 lights //if (m_lightsToDraw.size() > 32) // m_lightsToDraw.resize(32); // Now try to search for a shadow caster, using Lara as reference RendererRoom* room = m_rooms[LaraItem->roomNumber]; // Search for the brightest light. We do a simple version of the classic calculation done in pixel shader. RendererLight* brightestLight = NULL; float brightest = 0.0f; // Try room lights if (room->Lights.size() != 0) { for (int j = 0; j < room->Lights.size(); j++) { RendererLight* light = &room->Lights[j]; Vector4 itemPos = Vector4(LaraItem->pos.xPos, LaraItem->pos.yPos, LaraItem->pos.zPos, 1.0f); Vector4 lightVector = itemPos - light->Position; float distance = lightVector.Length(); lightVector.Normalize(); float intensity; float attenuation; float angle; float d; float attenuationRange; float attenuationAngle; switch ((int)light->Type) { case LIGHT_TYPES::LIGHT_TYPE_POINT: if (distance > light->Out || light->Out < 2048.0f) continue; attenuation = 1.0f - distance / light->Out; intensity = max(0.0f, attenuation * (light->Color.x + light->Color.y + light->Color.z) / 3.0f); if (intensity >= brightest) { brightest = intensity; brightestLight = light; } break; case LIGHT_TYPES::LIGHT_TYPE_SPOT: if (distance > light->Range) continue; attenuation = 1.0f - distance / light->Range; intensity = max(0.0f, attenuation * (light->Color.x + light->Color.y + light->Color.z) / 3.0f); if (intensity >= brightest) { brightest = intensity; brightestLight = light; } break; } } } // If the brightest light is found, then fill the data structure. We ignore for now dynamic lights for shadows. m_shadowLight = brightestLight; } inline void Renderer11::collectEffects(short roomNumber) { RendererRoom* room = m_rooms[roomNumber]; if (room == NULL) return; ROOM_INFO* r = room->Room; short fxNum = NO_ITEM; for (fxNum = r->fxNumber; fxNum != NO_ITEM; fxNum = Effects[fxNum].nextFx) { FX_INFO* fx = &Effects[fxNum]; if (fx->objectNumber < 0) continue; OBJECT_INFO* obj = &Objects[fx->objectNumber]; RendererEffect* newEffect = &m_effects[fxNum]; newEffect->Effect = fx; newEffect->Id = fxNum; newEffect->World = Matrix::CreateTranslation(fx->pos.xPos, fx->pos.yPos, fx->pos.zPos); newEffect->Mesh = m_meshPointersToMesh[reinterpret_cast(Meshes[0 (obj->nmeshes ? obj->meshIndex : fx->frameNumber * 2)])]; collectLightsForEffect(fx->roomNumber, newEffect); m_effectsToDraw.Add(newEffect); short* mp = Meshes[(obj->nmeshes ? obj->meshIndex : fx->frameNumber)]; short hhh = 0; } } RendererMesh* Renderer11::getRendererMeshFromTrMesh(RendererObject* obj, short* meshPtr, short* refMeshPtr, short boneIndex, int isJoints, int isHairs) { RendererMesh* mesh = new RendererMesh(); short* basePtr = meshPtr; short cx = *meshPtr++; short cy = *meshPtr++; short cz = *meshPtr++; short r1 = *meshPtr++; short r2 = *meshPtr++; short numVertices = *meshPtr++; VECTOR* vertices = (VECTOR*)malloc(sizeof(VECTOR) * numVertices); for (int v = 0; v < numVertices; v++) { short x = *meshPtr++; short y = *meshPtr++; short z = *meshPtr++; vertices[v].vx = x; vertices[v].vy = y; vertices[v].vz = z; mesh->Positions.push_back(Vector3(x, y, z)); } short numNormals = *meshPtr++; VECTOR* normals = NULL; short* colors = NULL; if (numNormals > 0) { normals = (VECTOR*)malloc(sizeof(VECTOR) * numNormals); for (int v = 0; v < numNormals; v++) { short x = *meshPtr++; short y = *meshPtr++; short z = *meshPtr++; normals[v].vx = x; normals[v].vy = y; normals[v].vz = z; } } else { short numLights = -numNormals; colors = (short*)malloc(sizeof(short) * numLights); for (int v = 0; v < numLights; v++) { colors[v] = *meshPtr++; } } short numRectangles = *meshPtr++; for (int r = 0; r < numRectangles; r++) { short v1 = *meshPtr++; short v2 = *meshPtr++; short v3 = *meshPtr++; short v4 = *meshPtr++; short textureId = *meshPtr++; short effects = *meshPtr++; short indices[4] = { v1,v2,v3,v4 }; short textureIndex = textureId & 0x7FFF; bool doubleSided = (textureId & 0x8000) >> 15; // Get the object texture OBJECT_TEXTURE* texture = &ObjectTextures[textureIndex]; int tile = texture->tileAndFlag & 0x7FFF; // Create vertices RendererBucket* bucket; int bucketIndex = RENDERER_BUCKET_SOLID; if (!doubleSided) { if (texture->attribute == 2 || (effects & 1)) bucketIndex = RENDERER_BUCKET_TRANSPARENT; else bucketIndex = RENDERER_BUCKET_SOLID; } else { if (texture->attribute == 2 || (effects & 1)) bucketIndex = RENDERER_BUCKET_TRANSPARENT_DS; else bucketIndex = RENDERER_BUCKET_SOLID_DS; } // ColAddHorizon special handling if (obj != NULL && obj->Id == ID_HORIZON && g_GameFlow->GetLevel(CurrentLevel)->ColAddHorizon) { if (texture->attribute == 2 || (effects & 1)) bucketIndex = RENDERER_BUCKET_TRANSPARENT; else bucketIndex = RENDERER_BUCKET_SOLID; } bucket = &mesh->Buckets[bucketIndex]; if (obj != NULL) obj->HasDataInBucket[bucketIndex] = true; int baseVertices = bucket->NumVertices; for (int v = 0; v < 4; v++) { RendererVertex vertex; vertex.Position.x = vertices[indices[v]].vx; vertex.Position.y = vertices[indices[v]].vy; vertex.Position.z = vertices[indices[v]].vz; if (numNormals > 0) { vertex.Normal.x = normals[indices[v]].vx / 16300.0f; vertex.Normal.y = normals[indices[v]].vy / 16300.0f; vertex.Normal.z = normals[indices[v]].vz / 16300.0f; } vertex.UV.x = (texture->vertices[v].x * 256.0f + 0.5f + GET_ATLAS_PAGE_X(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.UV.y = (texture->vertices[v].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.Bone = boneIndex; if (isJoints && boneIndex != 0 && m_laraSkinJointRemap[boneIndex][indices[v]] != -1) vertex.Bone = m_laraSkinJointRemap[boneIndex][indices[v]]; if (isHairs) vertex.Bone = indices[v]; if (colors == NULL) { vertex.Color = Vector4::One * 0.5f; } else { short shade = colors[indices[v]]; shade = (255 - shade * 255 / 8191) & 0xFF; vertex.Color = Vector4(shade / 255.0f, shade / 255.0f, shade / 255.0f, 1.0f); } bucket->NumVertices++; bucket->Vertices.push_back(vertex); } bucket->Indices.push_back(baseVertices); bucket->Indices.push_back(baseVertices + 1); bucket->Indices.push_back(baseVertices + 3); bucket->Indices.push_back(baseVertices + 2); bucket->Indices.push_back(baseVertices + 3); bucket->Indices.push_back(baseVertices + 1); bucket->NumIndices += 6; RendererPolygon newPolygon; newPolygon.Shape = SHAPE_RECTANGLE; newPolygon.TextureId = textureId; newPolygon.Indices[0] = baseVertices; newPolygon.Indices[1] = baseVertices + 1; newPolygon.Indices[2] = baseVertices + 2; newPolygon.Indices[3] = baseVertices + 3; bucket->Polygons.push_back(newPolygon); } short numTriangles = *meshPtr++; for (int r = 0; r < numTriangles; r++) { short v1 = *meshPtr++; short v2 = *meshPtr++; short v3 = *meshPtr++; short textureId = *meshPtr++; short effects = *meshPtr++; short indices[3] = { v1,v2,v3 }; short textureIndex = textureId & 0x7FFF; bool doubleSided = (textureId & 0x8000) >> 15; // Get the object texture OBJECT_TEXTURE* texture = &ObjectTextures[textureIndex]; int tile = texture->tileAndFlag & 0x7FFF; // Create vertices RendererBucket* bucket; int bucketIndex = RENDERER_BUCKET_SOLID; if (!doubleSided) { if (texture->attribute == 2 || (effects & 1)) bucketIndex = RENDERER_BUCKET_TRANSPARENT; else bucketIndex = RENDERER_BUCKET_SOLID; } else { if (texture->attribute == 2 || (effects & 1)) bucketIndex = RENDERER_BUCKET_TRANSPARENT_DS; else bucketIndex = RENDERER_BUCKET_SOLID_DS; } bucket = &mesh->Buckets[bucketIndex]; if (obj != NULL) obj->HasDataInBucket[bucketIndex] = true; int baseVertices = bucket->NumVertices; for (int v = 0; v < 3; v++) { RendererVertex vertex; vertex.Position.x = vertices[indices[v]].vx; vertex.Position.y = vertices[indices[v]].vy; vertex.Position.z = vertices[indices[v]].vz; if (numNormals > 0) { vertex.Normal.x = normals[indices[v]].vx / 16300.0f; vertex.Normal.y = normals[indices[v]].vy / 16300.0f; vertex.Normal.z = normals[indices[v]].vz / 16300.0f; } vertex.UV.x = (texture->vertices[v].x * 256.0f + 0.5f + GET_ATLAS_PAGE_X(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.UV.y = (texture->vertices[v].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.Bone = boneIndex; if (isJoints && boneIndex != 0 && m_laraSkinJointRemap[boneIndex][indices[v]] != -1) vertex.Bone = m_laraSkinJointRemap[boneIndex][indices[v]]; if (isHairs) vertex.Bone = indices[v]; if (colors == NULL) { vertex.Color = Vector4::One * 0.5f; } else { short shade = colors[indices[v]]; shade = (255 - shade * 255 / 8191) & 0xFF; vertex.Color = Vector4(shade / 255.0f, shade / 255.0f, shade / 255.0f, 1.0f); } bucket->NumVertices++; bucket->Vertices.push_back(vertex); } bucket->Indices.push_back(baseVertices); bucket->Indices.push_back(baseVertices + 1); bucket->Indices.push_back(baseVertices + 2); bucket->NumIndices += 3; RendererPolygon newPolygon; newPolygon.Shape = SHAPE_TRIANGLE; newPolygon.TextureId = textureId; newPolygon.Indices[0] = baseVertices; newPolygon.Indices[1] = baseVertices + 1; newPolygon.Indices[2] = baseVertices + 2; bucket->Polygons.push_back(newPolygon); } free(vertices); if (normals != NULL) free(normals); if (colors != NULL) free(colors); unsigned int castedMeshPtr = reinterpret_cast(refMeshPtr); if (m_meshPointersToMesh.find(castedMeshPtr) == m_meshPointersToMesh.end()) m_meshPointersToMesh.insert(pair(castedMeshPtr, mesh)); m_meshes.push_back(mesh); return mesh; } void Renderer11::buildHierarchyRecursive(RendererObject* obj, RendererBone* node, RendererBone* parentNode) { node->GlobalTransform = node->Transform * parentNode->GlobalTransform; obj->BindPoseTransforms[node->Index] = node->GlobalTransform; obj->Skeleton->GlobalTranslation = Vector3(0.0f, 0.0f, 0.0f); node->GlobalTranslation = node->Translation + parentNode->GlobalTranslation; for (int j = 0; j < node->Children.size(); j++) { buildHierarchyRecursive(obj, node->Children[j], node); } } void Renderer11::buildHierarchy(RendererObject* obj) { obj->Skeleton->GlobalTransform = obj->Skeleton->Transform; obj->BindPoseTransforms[obj->Skeleton->Index] = obj->Skeleton->GlobalTransform; obj->Skeleton->GlobalTranslation = Vector3(0.0f, 0.0f, 0.0f); for (int j = 0; j < obj->Skeleton->Children.size(); j++) { buildHierarchyRecursive(obj, obj->Skeleton->Children[j], obj->Skeleton); } } void Renderer11::fromTrAngle(Matrix* matrix, short* frameptr, int index) { short* ptr = &frameptr[0]; ptr += 9; for (int i = 0; i < index; i++) { ptr += ((*ptr & 0xc000) == 0 ? 2 : 1); } int rot0 = *ptr++; int frameMode = (rot0 & 0xc000); int rot1; int rotX; int rotY; int rotZ; switch (frameMode) { case 0: rot1 = *ptr++; rotX = ((rot0 & 0x3ff0) >> 4); rotY = (((rot1 & 0xfc00) >> 10) | ((rot0 & 0xf) << 6) & 0x3ff); rotZ = ((rot1) & 0x3ff); *matrix = Matrix::CreateFromYawPitchRoll(rotY* (360.0f / 1024.0f) * RADIAN, rotX* (360.0f / 1024.0f) * RADIAN, rotZ* (360.0f / 1024.0f) * RADIAN); break; case 0x4000: *matrix = Matrix::CreateRotationX((rot0 & 0xfff)* (360.0f / 4096.0f) * RADIAN); break; case 0x8000: *matrix = Matrix::CreateRotationY((rot0 & 0xfff)* (360.0f / 4096.0f) * RADIAN); break; case 0xc000: *matrix = Matrix::CreateRotationZ((rot0 & 0xfff)* (360.0f / 4096.0f) * RADIAN); break; } } bool Renderer11::updateConstantBuffer(ID3D11Buffer* buffer, void* data, int size) { HRESULT res; D3D11_MAPPED_SUBRESOURCE mappedResource; // Lock the constant buffer so it can be written to. res = m_context->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); if (FAILED(res)) return false; // Get a pointer to the data in the constant buffer. char* dataPtr = reinterpret_cast(mappedResource.pData); memcpy(dataPtr, data, size); // Unlock the constant buffer. m_context->Unmap(buffer, 0); return true; } void Renderer11::updateItemsAnimations() { Matrix translation; Matrix rotation; int numItems = m_itemsToDraw.Size(); for (int i = 0; i < numItems; i++) { RendererItem* itemToDraw = m_itemsToDraw[i]; ITEM_INFO* item = itemToDraw->Item; CREATURE_INFO* creature = (CREATURE_INFO*)item->data; // Lara has her own routine if (item->objectNumber == ID_LARA) continue; OBJECT_INFO* obj = &Objects[item->objectNumber]; RendererObject* moveableObj = m_moveableObjects[item->objectNumber]; // Update animation matrices if (obj->animIndex != -1 /*&& item->objectNumber != ID_HARPOON*/) { // Apply extra rotations int lastJoint = 0; for (int j = 0; j < moveableObj->LinearizedBones.size(); j++) { RendererBone* currentBone = moveableObj->LinearizedBones[j]; currentBone->ExtraRotation = Vector3(0.0f, 0.0f, 0.0f); if (creature) { if (currentBone->ExtraRotationFlags & ROT_Y) { currentBone->ExtraRotation.y = TR_ANGLE_TO_RAD(creature->jointRotation[lastJoint]); lastJoint++; } if (currentBone->ExtraRotationFlags & ROT_X) { currentBone->ExtraRotation.x = TR_ANGLE_TO_RAD(creature->jointRotation[lastJoint]); lastJoint++; } if (currentBone->ExtraRotationFlags & ROT_Z) { currentBone->ExtraRotation.z = TR_ANGLE_TO_RAD(creature->jointRotation[lastJoint]); lastJoint++; } } } short *framePtr[2]; int rate; int frac = GetFrame_D2(item, framePtr, &rate); updateAnimation(itemToDraw, moveableObj, framePtr, frac, rate, 0xFFFFFFFF); for (int m = 0; m < itemToDraw->NumMeshes; m++) itemToDraw->AnimationTransforms[m] = itemToDraw->AnimationTransforms[m].Transpose(); } // Update world matrix //translation = Matrix::CreateTranslation(item->pos.xPos, item->pos.yPos, item->pos.zPos); //rotation = Matrix::CreateFromYawPitchRoll(TR_ANGLE_TO_RAD(item->pos.yRot), TR_ANGLE_TO_RAD(item->pos.xRot), TR_ANGLE_TO_RAD(item->pos.zRot)); //itemToDraw->World = rotation * translation; int test = 0; } } void Renderer11::updateLaraAnimations() { Matrix translation; Matrix rotation; Matrix lastMatrix; Matrix hairMatrix; Matrix identity; Matrix world; RendererObject* laraObj = m_moveableObjects[ID_LARA]; // Clear extra rotations for (int i = 0; i < laraObj->LinearizedBones.size(); i++) laraObj->LinearizedBones[i]->ExtraRotation = Vector3(0.0f, 0.0f, 0.0f); // Lara world matrix translation = Matrix::CreateTranslation(LaraItem->pos.xPos, LaraItem->pos.yPos, LaraItem->pos.zPos); rotation = Matrix::CreateFromYawPitchRoll(TR_ANGLE_TO_RAD(LaraItem->pos.yRot), TR_ANGLE_TO_RAD(LaraItem->pos.xRot), TR_ANGLE_TO_RAD(LaraItem->pos.zRot)); m_LaraWorldMatrix = rotation * translation; // Update first Lara's animations laraObj->LinearizedBones[LM_TORSO]->ExtraRotation = Vector3(TR_ANGLE_TO_RAD(Lara.torsoXrot), TR_ANGLE_TO_RAD(Lara.torsoYrot), TR_ANGLE_TO_RAD(Lara.torsoZrot)); laraObj->LinearizedBones[LM_HEAD]->ExtraRotation = Vector3(TR_ANGLE_TO_RAD(Lara.headXrot), TR_ANGLE_TO_RAD(Lara.headYrot), TR_ANGLE_TO_RAD(Lara.headZrot)); // First calculate matrices for legs, hips, head and torso int mask = MESH_BITS(LM_HIPS) | MESH_BITS(LM_LTHIGH) | MESH_BITS(LM_LSHIN) | MESH_BITS(LM_LFOOT) | MESH_BITS(LM_RTHIGH) | MESH_BITS(LM_RSHIN) | MESH_BITS(LM_RFOOT) | MESH_BITS(LM_TORSO) | MESH_BITS(LM_HEAD); short *framePtr[2]; int rate, frac; frac = GetFrame_D2(LaraItem, framePtr, &rate); updateAnimation(NULL, laraObj, framePtr, frac, rate, mask); // Then the arms, based on current weapon status if (Lara.gunStatus == LG_NO_ARMS || Lara.gunStatus == LG_HANDS_BUSY && Lara.gunType != WEAPON_FLARE) { // Both arms mask = MESH_BITS(LM_LINARM) | MESH_BITS(LM_LOUTARM) | MESH_BITS(LM_LHAND) | MESH_BITS(LM_RINARM) | MESH_BITS(LM_ROUTARM) | MESH_BITS(LM_RHAND); frac = GetFrame_D2(LaraItem, framePtr, &rate); updateAnimation(NULL, laraObj, framePtr, frac, rate, mask); } else { // While handling weapon some extra rotation could be applied to arms laraObj->LinearizedBones[LM_LINARM]->ExtraRotation += Vector3(TR_ANGLE_TO_RAD(Lara.leftArm.xRot), TR_ANGLE_TO_RAD(0), TR_ANGLE_TO_RAD(-Lara.leftArm.yRot)); laraObj->LinearizedBones[LM_RINARM]->ExtraRotation += Vector3(TR_ANGLE_TO_RAD(Lara.rightArm.xRot), TR_ANGLE_TO_RAD(0), TR_ANGLE_TO_RAD(-Lara.rightArm.yRot)); LARA_ARM* leftArm = &Lara.leftArm; LARA_ARM* rightArm = &Lara.rightArm; // HACK: backguns handles differently // TokyoSU: not really a hack since it's the original way to do that. switch (Lara.gunType) { case WEAPON_SHOTGUN: case WEAPON_HK: case WEAPON_CROSSBOW: case WEAPON_GRENADE_LAUNCHER: case WEAPON_HARPOON_GUN: short* shotgunFramePtr; // Left arm mask = MESH_BITS(LM_LINARM) | MESH_BITS(LM_LOUTARM) | MESH_BITS(LM_LHAND); shotgunFramePtr = Lara.leftArm.frameBase + (Lara.leftArm.frameNumber) * (Anims[Lara.leftArm.animNumber].interpolation >> 8); updateAnimation(NULL, laraObj, &shotgunFramePtr, 0, 1, mask); // Right arm mask = MESH_BITS(LM_RINARM) | MESH_BITS(LM_ROUTARM) | MESH_BITS(LM_RHAND); shotgunFramePtr = Lara.rightArm.frameBase + (Lara.rightArm.frameNumber) * (Anims[Lara.rightArm.animNumber].interpolation >> 8); updateAnimation(NULL, laraObj, &shotgunFramePtr, 0, 1, mask); break; case WEAPON_PISTOLS: case WEAPON_UZI: case WEAPON_REVOLVER: default: { short* pistolFramePtr; // Left arm int upperArmMask = MESH_BITS(LM_LINARM); mask = MESH_BITS(LM_LOUTARM) | MESH_BITS(LM_LHAND); pistolFramePtr = Lara.leftArm.frameBase + (Lara.leftArm.frameNumber - Anims[Lara.leftArm.animNumber].frameBase) * (Anims[Lara.leftArm.animNumber].interpolation >> 8); updateAnimation(NULL, laraObj, &pistolFramePtr, 0, 1, upperArmMask, true); updateAnimation(NULL, laraObj, &pistolFramePtr, 0, 1, mask); // Right arm upperArmMask = MESH_BITS(LM_RINARM); mask = MESH_BITS(LM_ROUTARM) | MESH_BITS(LM_RHAND); pistolFramePtr = Lara.rightArm.frameBase + (Lara.rightArm.frameNumber - Anims[Lara.rightArm.animNumber].frameBase) * (Anims[Lara.rightArm.animNumber].interpolation >> 8); updateAnimation(NULL, laraObj, &pistolFramePtr, 0, 1, upperArmMask, true); updateAnimation(NULL, laraObj, &pistolFramePtr, 0, 1, mask); } break; case WEAPON_FLARE: // Left arm mask = MESH_BITS(LM_LINARM) | MESH_BITS(LM_LOUTARM) | MESH_BITS(LM_LHAND); frac = getFrame(Lara.leftArm.animNumber, Lara.leftArm.frameNumber, framePtr, &rate); updateAnimation(NULL, laraObj, framePtr, frac, rate, mask); // Right arm mask = MESH_BITS(LM_RINARM) | MESH_BITS(LM_ROUTARM) | MESH_BITS(LM_RHAND); frac = GetFrame_D2(LaraItem, framePtr, &rate); updateAnimation(NULL, laraObj, framePtr, frac, rate, mask); break; } } // At this point, Lara's matrices are ready. Now let's do ponytails... if (m_moveableObjects[ID_LARA_HAIR] != NULL) { RendererObject* hairsObj = m_moveableObjects[ID_LARA_HAIR]; lastMatrix = Matrix::Identity; identity = Matrix::Identity; Vector3 parentVertices[6][4]; Matrix headMatrix; RendererObject* objSkin = m_moveableObjects[ID_LARA_SKIN]; RendererObject* objLara = m_moveableObjects[ID_LARA]; RendererMesh* parentMesh = objSkin->ObjectMeshes[LM_HEAD]; RendererBone* parentBone = objSkin->LinearizedBones[LM_HEAD]; world = objLara->AnimationTransforms[LM_HEAD] * m_LaraWorldMatrix; int lastVertex = 0; int lastIndex = 0; GameScriptLevel* level = g_GameFlow->GetLevel(CurrentLevel); for (int p = 0; p < ((level->LaraType == LARA_YOUNG) ? 2 : 1); p++) { // We can't use hardware skinning here, however hairs have just a few vertices so // it's not so bad doing skinning in software if (level->LaraType == LARA_YOUNG) { if (p == 1) { parentVertices[0][0] = Vector3::Transform(parentMesh->Positions[68], world); parentVertices[0][1] = Vector3::Transform(parentMesh->Positions[69], world); parentVertices[0][2] = Vector3::Transform(parentMesh->Positions[70], world); parentVertices[0][3] = Vector3::Transform(parentMesh->Positions[71], world); } else { parentVertices[0][0] = Vector3::Transform(parentMesh->Positions[78], world); parentVertices[0][1] = Vector3::Transform(parentMesh->Positions[78], world); parentVertices[0][2] = Vector3::Transform(parentMesh->Positions[77], world); parentVertices[0][3] = Vector3::Transform(parentMesh->Positions[76], world); } } else { parentVertices[0][0] = Vector3::Transform(parentMesh->Positions[37], world); parentVertices[0][1] = Vector3::Transform(parentMesh->Positions[39], world); parentVertices[0][2] = Vector3::Transform(parentMesh->Positions[40], world); parentVertices[0][3] = Vector3::Transform(parentMesh->Positions[38], world); } for (int i = 0; i < 6; i++) { RendererMesh* mesh = hairsObj->ObjectMeshes[i]; RendererBucket* bucket = &mesh->Buckets[RENDERER_BUCKET_SOLID]; translation = Matrix::CreateTranslation(Hairs[7 * p + i].pos.xPos, Hairs[7 * p + i].pos.yPos, Hairs[7 * p + i].pos.zPos); rotation = Matrix::CreateFromYawPitchRoll(TR_ANGLE_TO_RAD(Hairs[7 * p + i].pos.yRot), TR_ANGLE_TO_RAD(Hairs[7 * p + i].pos.xRot), TR_ANGLE_TO_RAD(Hairs[7 * p + i].pos.zRot)); m_hairsMatrices[6 * p + i] = rotation * translation; int baseVertex = lastVertex; for (int j = 0; j < bucket->Vertices.size(); j++) { int oldVertexIndex = (int)bucket->Vertices[j].Bone; if (oldVertexIndex < 4) { m_hairVertices[lastVertex].Position.x = parentVertices[i][oldVertexIndex].x; m_hairVertices[lastVertex].Position.y = parentVertices[i][oldVertexIndex].y; m_hairVertices[lastVertex].Position.z = parentVertices[i][oldVertexIndex].z; m_hairVertices[lastVertex].UV.x = bucket->Vertices[j].UV.x; m_hairVertices[lastVertex].UV.y = bucket->Vertices[j].UV.y; Vector3 n = Vector3(bucket->Vertices[j].Normal.x, bucket->Vertices[j].Normal.y, bucket->Vertices[j].Normal.z); n.Normalize(); n = Vector3::TransformNormal(n, m_hairsMatrices[6 * p + i]); n.Normalize(); m_hairVertices[lastVertex].Normal.x = n.x; m_hairVertices[lastVertex].Normal.y = n.y; m_hairVertices[lastVertex].Normal.z = n.z; m_hairVertices[lastVertex].Color = Vector4::One * 0.5f; lastVertex++; } else { Vector3 in = Vector3(bucket->Vertices[j].Position.x, bucket->Vertices[j].Position.y, bucket->Vertices[j].Position.z); Vector3 out = Vector3::Transform(in, m_hairsMatrices[6 * p + i]); if (i < 5) { parentVertices[i + 1][oldVertexIndex - 4].x = out.x; parentVertices[i + 1][oldVertexIndex - 4].y = out.y; parentVertices[i + 1][oldVertexIndex - 4].z = out.z; } m_hairVertices[lastVertex].Position.x = out.x; m_hairVertices[lastVertex].Position.y = out.y; m_hairVertices[lastVertex].Position.z = out.z; m_hairVertices[lastVertex].UV.x = bucket->Vertices[j].UV.x; m_hairVertices[lastVertex].UV.y = bucket->Vertices[j].UV.y; Vector3 n = Vector3(bucket->Vertices[j].Normal.x, bucket->Vertices[j].Normal.y, bucket->Vertices[j].Normal.z); n.Normalize(); n = Vector3::TransformNormal(n, m_hairsMatrices[6 * p + i]); n.Normalize(); m_hairVertices[lastVertex].Normal.x = n.x; m_hairVertices[lastVertex].Normal.y = n.y; m_hairVertices[lastVertex].Normal.z = n.z; m_hairVertices[lastVertex].Color = Vector4::One * 0.5f; lastVertex++; } } for (int j = 0; j < bucket->Indices.size(); j++) { m_hairIndices[lastIndex] = baseVertex + bucket->Indices[j]; lastIndex++; } } } } // Transpose matrices for shaders for (int m = 0; m < 15; m++) laraObj->AnimationTransforms[m] = laraObj->AnimationTransforms[m].Transpose(); } int Renderer11::getFrame(short animation, short frame, short** framePtr, int* rate) { ITEM_INFO item; item.animNumber = animation; item.frameNumber = frame; return GetFrame_D2(&item, framePtr, rate); } void Renderer11::updateEffects() { for (int i = 0; i < m_effectsToDraw.Size(); i++) { RendererEffect* fx = m_effectsToDraw[i]; Matrix translation = Matrix::CreateTranslation(fx->Effect->pos.xPos, fx->Effect->pos.yPos, fx->Effect->pos.zPos); Matrix rotation = Matrix::CreateFromYawPitchRoll(TR_ANGLE_TO_RAD(fx->Effect->pos.yRot), TR_ANGLE_TO_RAD(fx->Effect->pos.xRot), TR_ANGLE_TO_RAD(fx->Effect->pos.zRot)); m_effectsToDraw[i]->World = rotation * translation; } } void Renderer11::updateAnimation(RendererItem* item, RendererObject* obj, short** frmptr, short frac, short rate, int mask, bool useObjectWorldRotation) { RendererBone* bones[32]; int nextBone = 0; Matrix rotation; Matrix* transforms = (item == NULL ? obj->AnimationTransforms.data() : &item->AnimationTransforms[0]); // Push bones[nextBone++] = obj->Skeleton; while (nextBone != 0) { // Pop the last bone in the stack RendererBone* bone = bones[--nextBone]; bool calculateMatrix = (mask >> bone->Index) & 1; if (calculateMatrix) { Vector3 p = Vector3((int)*(frmptr[0] + 6), (int)*(frmptr[0] + 7), (int)*(frmptr[0] + 8)); fromTrAngle(&rotation, frmptr[0], bone->Index); if (frac) { Vector3 p2 = Vector3((int)*(frmptr[1] + 6), (int)*(frmptr[1] + 7), (int)*(frmptr[1] + 8)); p = Vector3::Lerp(p, p2, frac / ((float)rate)); Matrix rotation2; fromTrAngle(&rotation2, frmptr[1], bone->Index); Quaternion q1, q2, q3; q1 = Quaternion::CreateFromRotationMatrix(rotation); q2 = Quaternion::CreateFromRotationMatrix(rotation2); q3 = Quaternion::Slerp(q1, q2, frac / ((float)rate)); rotation = Matrix::CreateFromQuaternion(q3); } Matrix translation; if (bone == obj->Skeleton) translation = Matrix::CreateTranslation(p.x, p.y, p.z); Matrix extraRotation; extraRotation = Matrix::CreateFromYawPitchRoll(bone->ExtraRotation.y, bone->ExtraRotation.x, bone->ExtraRotation.z); if (useObjectWorldRotation) { Quaternion invertedQuat; transforms[bone->Parent->Index].Invert().Decompose(Vector3(), invertedQuat, Vector3()); rotation = extraRotation * rotation * Matrix::CreateFromQuaternion(invertedQuat); } else { rotation = extraRotation * rotation; } if (bone != obj->Skeleton) transforms[bone->Index] = rotation * bone->Transform; else transforms[bone->Index] = rotation * translation; if (bone != obj->Skeleton) transforms[bone->Index] = transforms[bone->Index] * transforms[bone->Parent->Index]; } for (int i = 0; i < bone->Children.size(); i++) { // Push bones[nextBone++] = bone->Children[i]; } } } bool Renderer11::printDebugMessage(int x, int y, int alpha, byte r, byte g, byte b, LPCSTR Message) { return true; } void Renderer11::printDebugMessage(LPCSTR message, ...) { char buffer[255]; ZeroMemory(buffer, 255); va_list args; va_start(args, message); _vsprintf_l(buffer, message, NULL, args); va_end(args); PrintString(10, m_currentY, buffer, 0xFFFFFFFF, PRINTSTRING_OUTLINE); m_currentY += 20; } void Renderer11::drawBlood() { for (int i = 0; i < 32; i++) { BLOOD_STRUCT* blood = &Blood[i]; if (blood->on) { AddSpriteBillboard(m_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + 15], Vector3(blood->x, blood->y, blood->z), Vector4(blood->shade * 244/255.0f, blood->shade * 0, blood->shade * 0,1.0f), TR_ANGLE_TO_RAD(blood->rotAng), 1.0f, blood->size * 8.0f, blood->size * 8.0f, BLENDMODE_ALPHABLEND); } } } void Renderer11::drawSparks() { for (int i = 0; i < MAX_SPARKS; i++) { SPARKS* spark = &Sparks[i]; if (spark->on) { if (spark->flags & SP_DEF) { FX_INFO* fx = &Effects[spark->fxObj]; AddSpriteBillboard(m_sprites[spark->def], Vector3(fx->pos.xPos + spark->x,fx->pos.yPos + spark->y, fx->pos.zPos + spark->z), Vector4(spark->r/255.0f, spark->g / 255.0f, spark->b / 255.0f,1.0f), TR_ANGLE_TO_RAD(spark->rotAng), spark->scalar, spark->size * 12.0f, spark->size * 12.0f, BLENDMODE_ALPHABLEND); } else { Vector3 v = Vector3(spark->xVel, spark->yVel, spark->zVel); v.Normalize(); AddLine3D(Vector3(spark->x, spark->y, spark->z),Vector3( spark->x + v.x * 24.0f, spark->y + v.y * 24.0f, spark->z + v.z * 24.0f), Vector4(spark->r / 255.0f, spark->g / 255.0f, spark->b / 255.0f,1.0f)); } } } } void Renderer11::drawFires() { for (int k = 0; k < MAX_FIRE_LIST; k++) { FIRE_LIST* fire = &Fires[k]; if (fire->on) { for (int i = 0; i < MAX_SPARKS_FIRE; i++) { FIRE_SPARKS* spark = &FireSparks[i]; if (spark->on) AddSpriteBillboard(m_sprites[spark->def], Vector3(fire->x + spark->x, fire->y + spark->y, fire->z + spark->z), Vector4(spark->r / 255.0f, spark->g / 255.0f, spark->b / 255.0f,1.0f), TR_ANGLE_TO_RAD(spark->rotAng), spark->scalar, spark->size * 4.0f, spark->size * 4.0f, BLENDMODE_ALPHABLEND); } } } } void Renderer11::AddSpriteBillboard(RendererSprite* sprite,Vector3 pos, Vector4 color, float rotation, float scale, float width, float height, BLEND_MODES blendMode) { if (m_nextSprite >= MAX_SPRITES) return; scale = 1.0f; width *= scale; height *= scale; RendererSpriteToDraw* spr = &m_spritesBuffer[m_nextSprite++]; spr->Type = RENDERER_SPRITE_TYPE::SPRITE_TYPE_BILLBOARD; spr->Sprite = sprite; spr->pos = pos; spr->color = color; spr->Rotation = rotation; spr->Scale = scale; spr->Width = width; spr->Height = height; spr->BlendMode = blendMode; m_spritesToDraw.Add(spr); } void Renderer11::drawSmokes() { for (int i = 0; i < 32; i++) { SMOKE_SPARKS* spark = &SmokeSparks[i]; if (spark->on) { AddSpriteBillboard(m_sprites[spark->def], Vector3(spark->x, spark->y, spark->z), Vector4(spark->shade / 255.0f, spark->shade / 255.0f, spark->shade / 255.0f,1.0f), TR_ANGLE_TO_RAD(spark->rotAng), spark->scalar, spark->size * 4.0f, spark->size * 4.0f, BLENDMODE_ALPHABLEND); } } } void Renderer11::AddLine3D(Vector3 start, Vector3 end, Vector4 color) { if (m_nextLine3D >= MAX_LINES_3D) return; RendererLine3D* line = &m_lines3DBuffer[m_nextLine3D++]; line->start = start; line->end = end; line->color = color; m_lines3DToDraw.Add(line); } void Renderer11::AddSprite3D(RendererSprite* sprite, Vector3 vtx1,Vector3 vtx2, Vector3 vtx3, Vector3 vtx4, Vector4 color, float rotation, float scale, float width, float height, BLEND_MODES blendMode) { if (m_nextSprite >= MAX_SPRITES) return; scale = 1.0f; width *= scale; height *= scale; RendererSpriteToDraw* spr = &m_spritesBuffer[m_nextSprite++]; spr->Type = RENDERER_SPRITE_TYPE::SPRITE_TYPE_3D; spr->Sprite = sprite; spr->vtx1 = vtx1; spr->vtx2 = vtx2; spr->vtx3 = vtx3; spr->vtx4 = vtx4; spr->color = color; spr->Rotation = rotation; spr->Scale = scale; spr->Width = width; spr->Height = height; spr->BlendMode = blendMode; m_spritesToDraw.Add(spr); } void Renderer11::drawShockwaves() { for (int i = 0; i < MAX_SHOCKWAVE; i++) { SHOCKWAVE_STRUCT* shockwave = &ShockWaves[i]; if (shockwave->life) { byte color = shockwave->life * 8; // Inner circle float angle = PI / 32.0f; float c = cos(angle); float s = sin(angle); float x1 = shockwave->x + (shockwave->innerRad * c); float z1 = shockwave->z + (shockwave->innerRad * s); float x4 = shockwave->x + (shockwave->outerRad * c); float z4 = shockwave->z + (shockwave->outerRad * s); angle -= PI / 8.0f; for (int j = 0; j < 16; j++) { c = cos(angle); s = sin(angle); float x2 = shockwave->x + (shockwave->innerRad * c); float z2 = shockwave->z + (shockwave->innerRad * s); float x3 = shockwave->x + (shockwave->outerRad * c); float z3 = shockwave->z + (shockwave->outerRad * s); angle -= PI / 8.0f; AddSprite3D(m_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_SPLASH], Vector3(x1, shockwave->y, z1), Vector3(x2, shockwave->y, z2), Vector3(x3, shockwave->y, z3), Vector3(x4, shockwave->y, z4), Vector4(color / 255.0f, color / 255.0f, color/255.0f,1.0f), 0, 1, 0, 0, BLENDMODE_ALPHABLEND); x1 = x2; z1 = z2; x4 = x3; z4 = z3; } } } } void Renderer11::drawRipples() { for (int i = 0; i < MAX_RIPPLES; i++) { RIPPLE_STRUCT* ripple = &Ripples[i]; if (ripple->flags & 1) { float x1 = ripple->x - ripple->size; float z1 = ripple->z - ripple->size; float x2 = ripple->x + ripple->size; float z2 = ripple->z + ripple->size; float y = ripple->y; byte color = (ripple->init ? ripple->init << 1 : ripple->life << 1); AddSprite3D(m_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_RIPPLES], Vector3(x1, y, z2),Vector3(x2, y, z2),Vector3( x2, y, z1),Vector3( x1, y, z1),Vector4( color / 255.0f, color / 255.0f, color/255.0f,1.0f), 0.0f, 1.0f, ripple->size, ripple->size, BLENDMODE_ALPHABLEND); } } } void Renderer11::drawDrips() { for (int i = 0; i < MAX_DRIPS; i++) { DRIP_STRUCT* drip = &Drips[i]; if (drip->on) { AddLine3D(Vector3(drip->x, drip->y, drip->z),Vector3( drip->x, drip->y + 24.0f, drip->z), Vector4(drip->r/255.0f, drip->g/255.0f, drip->b/255.0f,1.0f)); } } } void Renderer11::drawBubbles() { for (int i = 0; i < MAX_BUBBLES; i++) { BUBBLE_STRUCT* bubble = &Bubbles[i]; if (bubble->size) AddSpriteBillboard(m_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_BUBBLES], Vector3(bubble->pos.x, bubble->pos.y, bubble->pos.z),Vector4( bubble->shade, bubble->shade, bubble->shade,1.0f), 0.0f, 1.0f, bubble->size * 0.5f, bubble->size * 0.5f, BLENDMODE_ALPHABLEND); } } void Renderer11::drawSplahes() { constexpr size_t NUM_POINTS = 12; for (int i = 0; i < MAX_SPLASH; i++) { SPLASH_STRUCT& splash = Splashes[i]; if (splash.isActive) { constexpr float alpha = 360 / NUM_POINTS; byte color = (splash.life >= 32 ? 255 : (byte)((splash.life / 32.0f) * 255)); if (!splash.isRipple) { if (splash.heightSpeed < 0 && splash.height < 1024) { float multiplier = splash.height / 1024.0f; color = (float)color*multiplier; } } float innerRadius = splash.innerRad; float outerRadius = splash.outerRad; float xInner; float zInner; float xOuter; float zOuter; float x2Inner; float z2Inner; float x2Outer; float z2Outer; float yInner = splash.y; float yOuter = splash.y - splash.height; for (int i = 0; i < NUM_POINTS; i++) { xInner = innerRadius * sin(alpha * i * PI / 180); zInner = innerRadius * cos(alpha * i * PI / 180); xOuter = outerRadius * sin(alpha * i * PI / 180); zOuter = outerRadius * cos(alpha * i * PI / 180); xInner += splash.x; zInner += splash.z; xOuter += splash.x; zOuter += splash.z; int j = (i + 1) % NUM_POINTS; x2Inner = innerRadius * sin(alpha * j * PI / 180); x2Inner += splash.x; z2Inner = innerRadius * cos(alpha * j * PI / 180); z2Inner += splash.z; x2Outer = outerRadius * sin(alpha * j * PI / 180); x2Outer += splash.x; z2Outer = outerRadius * cos(alpha * j * PI / 180); z2Outer += splash.z; AddSprite3D(m_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + splash.spriteSequenceStart + (int)splash.animationPhase],Vector3( xOuter, yOuter, zOuter), Vector3(x2Outer, yOuter, z2Outer),Vector3( x2Inner, yInner, z2Inner), Vector3(xInner, yInner, zInner),Vector4( color / 255.0f, color / 255.0f, color / 255.0f,1.0f), 0, 1, 0, 0, BLENDMODE_ALPHABLEND); } } } } bool Renderer11::drawSprites() { m_context->RSSetState(m_states->CullNone()); m_context->OMSetDepthStencilState(m_states->DepthRead(), 0); m_context->VSSetShader(m_vsSprites, NULL, 0); m_context->PSSetShader(m_psSprites, NULL, 0); m_stCameraMatrices.View = View.Transpose(); m_stCameraMatrices.Projection = Projection.Transpose(); updateConstantBuffer(m_cbCameraMatrices, &m_stCameraMatrices, sizeof(CCameraMatrixBuffer)); m_context->VSSetConstantBuffers(0, 1, &m_cbCameraMatrices); m_stMisc.AlphaTest = true; updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); m_context->PSSetShaderResources(0, 1, &m_textureAtlas->ShaderResourceView); ID3D11SamplerState* sampler = m_states->AnisotropicClamp(); m_context->PSSetSamplers(0, 1, &sampler); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); for (int b = 0; b < 4; b++) { BLEND_MODES currentBlendMode = (BLEND_MODES)b; int numSpritesToDraw = m_spritesToDraw.Size(); int lastSprite = 0; m_primitiveBatch->Begin(); for (int i = 0; i < numSpritesToDraw; i++) { RendererSpriteToDraw* spr = m_spritesToDraw[i]; if (spr->BlendMode != currentBlendMode) continue; switch (currentBlendMode) { case BLENDMODE_ALPHABLEND: m_context->OMSetBlendState(m_states->Additive(), NULL, 0xFFFFFFFF); break; case BLENDMODE_ALPHATEST: m_context->OMSetBlendState(m_states->Additive(), NULL, 0xFFFFFFFF); break; case BLENDMODE_OPAQUE: m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); break; case BLENDMODE_SUBTRACTIVE: m_context->OMSetBlendState(m_subtractiveBlendState, NULL, 0xFFFFFFFF); break; } if (spr->Type == RENDERER_SPRITE_TYPE::SPRITE_TYPE_BILLBOARD) { float halfWidth = spr->Width / 2.0f; float halfHeight = spr->Height / 2.0f; Matrix billboardMatrix; createBillboardMatrix(&billboardMatrix, &spr->pos, &Vector3(Camera.pos.x, Camera.pos.y, Camera.pos.z), spr->Rotation); Vector3 p0 = Vector3(-halfWidth, -halfHeight, 0); Vector3 p1 = Vector3(halfWidth, -halfHeight, 0); Vector3 p2 = Vector3(halfWidth, halfHeight, 0); Vector3 p3 = Vector3(-halfWidth, halfHeight, 0); Vector3 p0t = Vector3::Transform(p0, billboardMatrix); Vector3 p1t = Vector3::Transform(p1, billboardMatrix); Vector3 p2t = Vector3::Transform(p2, billboardMatrix); Vector3 p3t = Vector3::Transform(p3, billboardMatrix); RendererVertex v0; v0.Position.x = p0t.x; v0.Position.y = p0t.y; v0.Position.z = p0t.z; v0.UV.x = spr->Sprite->UV[0].x; v0.UV.y = spr->Sprite->UV[0].y; v0.Color = spr->color; RendererVertex v1; v1.Position.x = p1t.x; v1.Position.y = p1t.y; v1.Position.z = p1t.z; v1.UV.x = spr->Sprite->UV[1].x; v1.UV.y = spr->Sprite->UV[1].y; v1.Color = spr->color; RendererVertex v2; v2.Position.x = p2t.x; v2.Position.y = p2t.y; v2.Position.z = p2t.z; v2.UV.x = spr->Sprite->UV[2].x; v2.UV.y = spr->Sprite->UV[2].y; v2.Color = spr->color; RendererVertex v3; v3.Position.x = p3t.x; v3.Position.y = p3t.y; v3.Position.z = p3t.z; v3.UV.x = spr->Sprite->UV[3].x; v3.UV.y = spr->Sprite->UV[3].y; v3.Color = spr->color; m_primitiveBatch->DrawQuad(v0, v1, v2, v3); } else if (spr->Type == RENDERER_SPRITE_TYPE::SPRITE_TYPE_3D) { Vector3 p0t = spr->vtx1; Vector3 p1t = spr->vtx2; Vector3 p2t = spr->vtx3; Vector3 p3t = spr->vtx4; RendererVertex v0; v0.Position.x = p0t.x; v0.Position.y = p0t.y; v0.Position.z = p0t.z; v0.UV.x = spr->Sprite->UV[0].x; v0.UV.y = spr->Sprite->UV[0].y; v0.Color = spr->color; RendererVertex v1; v1.Position.x = p1t.x; v1.Position.y = p1t.y; v1.Position.z = p1t.z; v1.UV.x = spr->Sprite->UV[1].x; v1.UV.y = spr->Sprite->UV[1].y; v1.Color = spr->color; RendererVertex v2; v2.Position.x = p2t.x; v2.Position.y = p2t.y; v2.Position.z = p2t.z; v2.UV.x = spr->Sprite->UV[2].x; v2.UV.y = spr->Sprite->UV[2].y; v2.Color = spr->color; RendererVertex v3; v3.Position.x = p3t.x; v3.Position.y = p3t.y; v3.Position.z = p3t.z; v3.UV.x = spr->Sprite->UV[3].x; v3.UV.y = spr->Sprite->UV[3].y; v3.Color = spr->color; m_primitiveBatch->DrawQuad(v0, v1, v2, v3); } } m_primitiveBatch->End(); } m_context->RSSetState(m_states->CullCounterClockwise()); m_context->OMSetDepthStencilState(m_states->DepthDefault(), 0); return true; } void Renderer11::createBillboardMatrix(Matrix* out, Vector3* particlePos, Vector3* cameraPos, float rotation) { Vector3 look = *particlePos; look = look - *cameraPos; look.Normalize(); Vector3 cameraUp = Vector3(0.0f, -1.0f, 0.0f); Vector3 right; right = cameraUp.Cross(look); right.Normalize(); // Rotate right vector Matrix rightTransform = Matrix::CreateFromAxisAngle(look, rotation); right = Vector3::Transform(right, rightTransform); Vector3 up; up = look.Cross(right); up.Normalize(); *out = Matrix::Identity; out->_11 = right.x; out->_12 = right.y; out->_13 = right.z; out->_21 = up.x; out->_22 = up.y; out->_23 = up.z; out->_31 = look.x; out->_32 = look.y; out->_33 = look.z; out->_41 = particlePos->x; out->_42 = particlePos->y; out->_43 = particlePos->z; } void Renderer11::updateAnimatedTextures() { // Update room's animated textures for (int i = 0; i < NumberRooms; i++) { RendererRoom* room = m_rooms[i]; if (room == NULL) continue; for (int bucketIndex = 0; bucketIndex < NUM_BUCKETS; bucketIndex++) { RendererBucket* bucket = &room->AnimatedBuckets[bucketIndex]; if (bucket->Vertices.size() == 0) continue; for (int p = 0; p < bucket->Polygons.size(); p++) { RendererPolygon* polygon = &bucket->Polygons[p]; RendererAnimatedTextureSet* set = m_animatedTextureSets[polygon->AnimatedSet]; int textureIndex = -1; for (int j = 0; j < set->NumTextures; j++) { if (set->Textures[j]->Id == polygon->TextureId) { textureIndex = j; break; } } if (textureIndex == -1) continue; if (textureIndex == set->NumTextures - 1) textureIndex = 0; else textureIndex++; polygon->TextureId = set->Textures[textureIndex]->Id; for (int v = 0; v < (polygon->Shape == SHAPE_RECTANGLE ? 4 : 3); v++) { bucket->Vertices[polygon->Indices[v]].UV.x = set->Textures[textureIndex]->UV[v].x; bucket->Vertices[polygon->Indices[v]].UV.y = set->Textures[textureIndex]->UV[v].y; } } } } // Update waterfalls textures for (int i = ID_WATERFALL1; i <= ID_WATERFALLSS2; i++) { OBJECT_INFO* obj = &Objects[i]; if (obj->loaded) { RendererObject* waterfall = m_moveableObjects[i]; for (int m = 0; m < waterfall->ObjectMeshes.size(); m++) { RendererMesh* mesh = waterfall->ObjectMeshes[m]; RendererBucket* bucket = &mesh->Buckets[RENDERER_BUCKET_TRANSPARENT_DS]; for (int v = 0; v < bucket->Vertices.size(); v++) { RendererVertex* vertex = &bucket->Vertices[v]; int y = vertex->UV.y * TEXTURE_ATLAS_SIZE + 64; y %= 128; vertex->UV.y = (float)y / TEXTURE_ATLAS_SIZE; } } } } } bool Renderer11::drawLines3D() { m_context->RSSetState(m_states->CullNone()); m_context->OMSetBlendState(m_states->Additive(), NULL, 0xFFFFFFFF); m_context->OMSetDepthStencilState(m_states->DepthRead(), 0); m_context->VSSetShader(m_vsSolid, NULL, 0); m_context->PSSetShader(m_psSolid, NULL, 0); m_stCameraMatrices.View = View.Transpose(); m_stCameraMatrices.Projection = Projection.Transpose(); updateConstantBuffer(m_cbCameraMatrices, &m_stCameraMatrices, sizeof(CCameraMatrixBuffer)); m_context->VSSetConstantBuffers(0, 1, &m_cbCameraMatrices); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST); m_context->IASetInputLayout(m_inputLayout); m_primitiveBatch->Begin(); for (int i = 0; i < m_lines3DToDraw.Size(); i++) { RendererLine3D* line = m_lines3DToDraw[i]; RendererVertex v1; v1.Position = line->start; v1.Color = line->color; RendererVertex v2; v2.Position = line->end; v2.Color = line->color; m_primitiveBatch->DrawLine(v1, v2); } m_primitiveBatch->End(); m_context->RSSetState(m_states->CullCounterClockwise()); m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); m_context->OMSetDepthStencilState(m_states->DepthDefault(), 0); return true; } bool Renderer11::doRain() { if (m_firstWeather) { for (int i = 0; i < NUM_RAIN_DROPS; i++) { m_rain[i].Reset = true; m_rain[i].Draw = true; } } for (int i = 0; i < NUM_RAIN_DROPS; i++) { RendererWeatherParticle* drop = &m_rain[i]; if (drop->Reset) { drop->Draw = true; drop->X = LaraItem->pos.xPos + rand() % WEATHER_RADIUS - WEATHER_RADIUS / 2.0f; drop->Y = LaraItem->pos.yPos - (m_firstWeather ? rand() % WEATHER_HEIGHT : WEATHER_HEIGHT); drop->Z = LaraItem->pos.zPos + rand() % WEATHER_RADIUS - WEATHER_RADIUS / 2.0f; // Check if in inside room short roomNumber = Camera.pos.roomNumber; FLOOR_INFO* floor = GetFloor(drop->X, drop->Y, drop->Z, &roomNumber); ROOM_INFO* room = &Rooms[roomNumber]; if (!(room->flags & ENV_FLAG_OUTSIDE)) { drop->Reset = true; continue; } drop->Size = RAIN_SIZE + (rand() % 64); drop->AngleH = (rand() % RAIN_MAX_ANGLE_H) * RADIAN; drop->AngleV = (rand() % RAIN_MAX_ANGLE_V) * RADIAN; drop->Reset = false; } float x1 = drop->X; float y1 = drop->Y; float z1 = drop->Z; float radius = drop->Size * sin(drop->AngleV); float dx = sin(drop->AngleH) * radius; float dy = drop->Size * cos(drop->AngleV); float dz = cos(drop->AngleH) * radius; drop->X += dx; drop->Y += RAIN_DELTA_Y; drop->Z += dz; if (drop->Draw) AddLine3D(Vector3(x1, y1, z1),Vector3( drop->X, drop->Y, drop->Z), Vector4(RAIN_COLOR, RAIN_COLOR, RAIN_COLOR,1.0f)); // If rain drop has hit the ground, then reset it and add a little drip short roomNumber = Camera.pos.roomNumber; FLOOR_INFO* floor = GetFloor(drop->X, drop->Y, drop->Z, &roomNumber); ROOM_INFO* room = &Rooms[roomNumber]; if (drop->Y >= room->y + room->minfloor) { drop->Reset = true; AddWaterSparks(drop->X, room->y + room->minfloor, drop->Z, 1); } } m_firstWeather = false; return true; } bool Renderer11::doSnow() { if (m_firstWeather) { for (int i = 0; i < NUM_SNOW_PARTICLES; i++) m_snow[i].Reset = true; } for (int i = 0; i < NUM_SNOW_PARTICLES; i++) { RendererWeatherParticle* snow = &m_snow[i]; if (snow->Reset) { snow->X = LaraItem->pos.xPos + rand() % WEATHER_RADIUS - WEATHER_RADIUS / 2.0f; snow->Y = LaraItem->pos.yPos - (m_firstWeather ? rand() % WEATHER_HEIGHT : WEATHER_HEIGHT) + (rand() % 512); snow->Z = LaraItem->pos.zPos + rand() % WEATHER_RADIUS - WEATHER_RADIUS / 2.0f; // Check if in inside room short roomNumber = Camera.pos.roomNumber; FLOOR_INFO* floor = GetFloor(snow->X, snow->Y, snow->Z, &roomNumber); ROOM_INFO* room = &Rooms[roomNumber]; if (!(room->flags & ENV_FLAG_OUTSIDE)) continue; snow->Size = SNOW_DELTA_Y + (rand() % 64); snow->AngleH = (rand() % SNOW_MAX_ANGLE_H) * RADIAN; snow->AngleV = (rand() % SNOW_MAX_ANGLE_V) * RADIAN; snow->Reset = false; } float radius = snow->Size * sin(snow->AngleV); float dx = sin(snow->AngleH) * radius; float dz = cos(snow->AngleH) * radius; snow->X += dx; snow->Y += SNOW_DELTA_Y; snow->Z += dz; if (snow->X <= 0 || snow->Z <= 0 || snow->X >= 100 * 1024.0f || snow->Z >= 100 * 1024.0f) { snow->Reset = true; continue; } AddSpriteBillboard(m_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_UNDERWATERDUST], Vector3(snow->X, snow->Y, snow->Z), Vector4(1, 1, 1,1), 0.0f, 1.0f, SNOW_SIZE, SNOW_SIZE, BLENDMODE_ALPHABLEND); short roomNumber = Camera.pos.roomNumber; FLOOR_INFO* floor = GetFloor(snow->X, snow->Y, snow->Z, &roomNumber); ROOM_INFO* room = &Rooms[roomNumber]; if (snow->Y >= room->y + room->minfloor) snow->Reset = true; } m_firstWeather = false; return true; } bool Renderer11::drawDebris(bool transparent) { UINT cPasses = 1; // First collect debrises vector vertices; for (int i = 0; i < NUM_DEBRIS; i++) { DEBRIS_STRUCT* debris = &Debris[i]; if (debris->on) { Matrix translation = Matrix::CreateTranslation(debris->x, debris->y, debris->z); Matrix rotation = Matrix::CreateFromYawPitchRoll(TR_ANGLE_TO_RAD(debris->yRot), TR_ANGLE_TO_RAD(debris->xRot), 0); Matrix world = rotation * translation; OBJECT_TEXTURE* texture = &ObjectTextures[(int)(debris->textInfo) & 0x7FFF]; int tile = texture->tileAndFlag & 0x7FFF; // Draw only debris of the current bucket if (texture->attribute == 0 && transparent || texture->attribute == 1 && transparent || texture->attribute == 2 && !transparent) continue; RendererVertex vertex; // Prepare the triangle Vector3 p = Vector3(debris->xyzOffsets1[0], debris->xyzOffsets1[1], debris->xyzOffsets1[2]); p = Vector3::Transform(p, world); vertex.Position.x = p.x; vertex.Position.y = p.y; vertex.Position.z = p.z; vertex.UV.x = (texture->vertices[0].x * 256.0f + 0.5f + GET_ATLAS_PAGE_X(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.UV.y = (texture->vertices[0].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.Color.x = debris->pad[2] / 255.0f; vertex.Color.y = debris->pad[3] / 255.0f; vertex.Color.z = debris->pad[4] / 255.0f; vertices.push_back(vertex); p = Vector3(debris->xyzOffsets2[0], debris->xyzOffsets2[1], debris->xyzOffsets2[2]); p = Vector3::Transform(p, world); vertex.Position.x = p.x; vertex.Position.y = p.y; vertex.Position.z = p.z; vertex.UV.x = (texture->vertices[1].x * 256.0f + 0.5f + GET_ATLAS_PAGE_X(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.UV.y = (texture->vertices[1].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.Color.x = debris->pad[6] / 255.0f; vertex.Color.y = debris->pad[7] / 255.0f; vertex.Color.z = debris->pad[8] / 255.0f; vertices.push_back(vertex); p = Vector3(debris->xyzOffsets3[0], debris->xyzOffsets3[1], debris->xyzOffsets3[2]); p = Vector3::Transform(p, world); vertex.Position.x = p.x; vertex.Position.y = p.y; vertex.Position.z = p.z; vertex.UV.x = (texture->vertices[2].x * 256.0f + 0.5f + GET_ATLAS_PAGE_X(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.UV.y = (texture->vertices[2].y * 256.0f + 0.5f + GET_ATLAS_PAGE_Y(tile)) / (float)TEXTURE_ATLAS_SIZE; vertex.Color.x = debris->pad[10] / 255.0f; vertex.Color.y = debris->pad[11] / 255.0f; vertex.Color.z = debris->pad[12] / 255.0f; vertices.push_back(vertex); } } // Check if no debris have to be drawn if (vertices.size() == 0) return true; m_primitiveBatch->Begin(); // Set shaders m_context->VSSetShader(m_vsStatics, NULL, 0); m_context->PSSetShader(m_psStatics, NULL, 0); // Set texture m_context->PSSetShaderResources(0, 1, &m_textureAtlas->ShaderResourceView); ID3D11SamplerState* sampler = m_states->AnisotropicClamp(); m_context->PSSetSamplers(0, 1, &sampler); // Set camera matrices m_stCameraMatrices.View = View.Transpose(); m_stCameraMatrices.Projection = Projection.Transpose(); updateConstantBuffer(m_cbCameraMatrices, &m_stCameraMatrices, sizeof(CCameraMatrixBuffer)); m_context->VSSetConstantBuffers(0, 1, &m_cbCameraMatrices); m_stMisc.AlphaTest = !transparent; updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); m_stStatic.World = Matrix::Identity; m_stStatic.Color = Vector4::One; updateConstantBuffer(m_cbStatic, &m_stStatic, sizeof(CStaticBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbStatic); // Draw vertices m_primitiveBatch->Draw(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST, vertices.data(), vertices.size()); m_numDrawCalls++; m_primitiveBatch->End(); return true; } bool Renderer11::drawBats() { UINT stride = sizeof(RendererVertex); UINT offset = 0; m_context->IASetVertexBuffers(0, 1, &m_moveablesVertexBuffer->Buffer, &stride, &offset); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); m_context->IASetIndexBuffer(m_moveablesIndexBuffer->Buffer, DXGI_FORMAT_R32_UINT, 0); if (Objects[ID_BATS].loaded) { OBJECT_INFO* obj = &Objects[ID_BATS]; RendererObject* moveableObj = m_moveableObjects[ID_BATS]; short* meshPtr = Meshes[Objects[ID_BATS].meshIndex + 2 * (-GlobalCounter & 3)]; RendererMesh* mesh = m_meshPointersToMesh[reinterpret_cast(meshPtr)]; for (int m = 0; m < 32; m++) memcpy(&m_stItem.BonesMatrices[m], &Matrix::Identity, sizeof(Matrix)); for (int b = 0; b < 2; b++) { RendererBucket* bucket = &mesh->Buckets[b]; if (bucket->NumVertices == 0) continue; for (int i = 0; i < NUM_BATS; i++) { BAT_STRUCT* bat = &Bats[i]; if (bat->on) { Matrix translation = Matrix::CreateTranslation(bat->pos.xPos, bat->pos.yPos, bat->pos.zPos); Matrix rotation = Matrix::CreateFromYawPitchRoll(bat->pos.yRot, bat->pos.xRot, bat->pos.zRot); Matrix world = rotation * translation; m_stItem.World = world.Transpose(); m_stItem.Position = Vector4(bat->pos.xPos, bat->pos.yPos, bat->pos.zPos, 1.0f); m_stItem.AmbientLight = m_rooms[bat->roomNumber]->AmbientLight; updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } } } } return true; } bool Renderer11::drawRats() { UINT stride = sizeof(RendererVertex); UINT offset = 0; m_context->IASetVertexBuffers(0, 1, &m_moveablesVertexBuffer->Buffer, &stride, &offset); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); m_context->IASetIndexBuffer(m_moveablesIndexBuffer->Buffer, DXGI_FORMAT_R32_UINT, 0); if (Objects[ID_RATS].loaded) { OBJECT_INFO* obj = &Objects[ID_BATS]; RendererObject* moveableObj = m_moveableObjects[ID_BATS]; for (int m = 0; m < 32; m++) memcpy(&m_stItem.BonesMatrices[m], &Matrix::Identity, sizeof(Matrix)); for (int i = 0; i < NUM_RATS; i += 4) { RAT_STRUCT* rat = &Rats[i]; if (rat->on) { short* meshPtr = Meshes[Objects[ID_BATS].meshIndex + (((i + Wibble) >> 2) & 0xE)]; RendererMesh* mesh = m_meshPointersToMesh[reinterpret_cast(meshPtr)]; Matrix translation = Matrix::CreateTranslation(rat->pos.xPos, rat->pos.yPos, rat->pos.zPos); Matrix rotation = Matrix::CreateFromYawPitchRoll(rat->pos.yRot, rat->pos.xRot, rat->pos.zRot); Matrix world = rotation * translation; m_stItem.World = world.Transpose(); m_stItem.Position = Vector4(rat->pos.xPos, rat->pos.yPos, rat->pos.zPos, 1.0f); m_stItem.AmbientLight = m_rooms[rat->roomNumber]->AmbientLight; updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); for (int b = 0; b < 2; b++) { RendererBucket* bucket = &mesh->Buckets[b]; if (bucket->NumVertices == 0) continue; m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } } } } return true; } bool Renderer11::drawSpiders() { /*XMMATRIX world; UINT cPasses = 1; if (Objects[ID_SPIDER].loaded) { OBJECT_INFO* obj = &Objects[ID_SPIDER]; RendererObject* moveableObj = m_moveableObjects[ID_SPIDER].get(); short* meshPtr = Meshes[Objects[ID_SPIDER].meshIndex + ((Wibble >> 2) & 2)]; RendererMesh* mesh = m_meshPointersToMesh[meshPtr]; RendererBucket* bucket = mesh->GetBucket(bucketIndex); if (bucket->NumVertices == 0) return true; setGpuStateForBucket(bucketIndex); m_device->SetStreamSource(0, bucket->GetVertexBuffer(), 0, sizeof(RendererVertex)); m_device->SetIndices(bucket->GetIndexBuffer()); LPD3DXEFFECT effect; if (pass == RENDERER_PASS_SHADOW_MAP) effect = m_shaderDepth->GetEffect(); else if (pass == RENDERER_PASS_RECONSTRUCT_DEPTH) effect = m_shaderReconstructZBuffer->GetEffect(); else if (pass == RENDERER_PASS_GBUFFER) effect = m_shaderFillGBuffer->GetEffect(); else effect = m_shaderTransparent->GetEffect(); effect->SetBool(effect->GetParameterByName(NULL, "UseSkinning"), false); effect->SetInt(effect->GetParameterByName(NULL, "ModelType"), MODEL_TYPE_MOVEABLE); if (bucketIndex == RENDERER_BUCKET_SOLID || bucketIndex == RENDERER_BUCKET_SOLID_DS) effect->SetInt(effect->GetParameterByName(NULL, "BlendMode"), BLENDMODE_OPAQUE); else effect->SetInt(effect->GetParameterByName(NULL, "BlendMode"), BLENDMODE_ALPHATEST); for (int i = 0; i < NUM_SPIDERS; i++) { SPIDER_STRUCT* spider = &Spiders[i]; if (spider->on) { XMMATRIXTranslation(&m_tempTranslation, spider->pos.xPos, spider->pos.yPos, spider->pos.zPos); XMMATRIXRotationYawPitchRoll(&m_tempRotation, spider->pos.yRot, spider->pos.xRot, spider->pos.zRot); XMMATRIXMultiply(&m_tempWorld, &m_tempRotation, &m_tempTranslation); effect->SetMatrix(effect->GetParameterByName(NULL, "World"), &m_tempWorld); effect->SetVector(effect->GetParameterByName(NULL, "AmbientLight"), &m_rooms[spider->roomNumber]->AmbientLight); for (int iPass = 0; iPass < cPasses; iPass++) { effect->BeginPass(iPass); effect->CommitChanges(); drawPrimitives(D3DPT_TRIANGLELIST, 0, 0, bucket->NumVertices, 0, bucket->NumIndices / 3); effect->EndPass(); } } } }*/ return true; } int Renderer11::drawInventoryScene() { char stringBuffer[255]; bool drawLogo = true; RECT guiRect; Vector4 guiColor = Vector4(0.0f, 0.0f, 0.25f, 0.5f); bool drawGuiRect = false; RECT rect; rect.left = 0; rect.top = 0; rect.right = ScreenWidth; rect.bottom = ScreenHeight; m_lines2DToDraw.Clear(); m_strings.clear(); m_nextLine2D = 0; // Set basic render states m_context->OMSetDepthStencilState(m_states->DepthDefault(), 0); m_context->RSSetState(m_states->CullCounterClockwise()); m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); // Bind and clear render target m_context->ClearRenderTargetView(m_renderTarget->RenderTargetView, Colors::Black); m_context->ClearDepthStencilView(m_renderTarget->DepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); m_context->OMSetRenderTargets(1, &m_renderTarget->RenderTargetView, m_renderTarget->DepthStencilView); m_context->RSSetViewports(1, &m_viewport); // Clear the Z-Buffer after drawing the background if (g_Inventory->GetType() == INV_TYPE_TITLE) { if (g_GameFlow->TitleType == TITLE_BACKGROUND) drawFullScreenQuad(m_titleScreen->ShaderResourceView, Vector3(m_fadeFactor, m_fadeFactor, m_fadeFactor), false); else drawFullScreenQuad(m_dumpScreenRenderTarget->ShaderResourceView, Vector3(1.0f, 1.0f, 1.0f), false); } else { drawFullScreenQuad(m_dumpScreenRenderTarget->ShaderResourceView, Vector3(0.2f, 0.2f, 0.2f), false); } m_context->ClearDepthStencilView(m_renderTarget->DepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); UINT stride = sizeof(RendererVertex); UINT offset = 0; // Set vertex buffer m_context->IASetVertexBuffers(0, 1, &m_moveablesVertexBuffer->Buffer, &stride, &offset); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); m_context->IASetIndexBuffer(m_moveablesIndexBuffer->Buffer, DXGI_FORMAT_R32_UINT, 0); // Set shaders m_context->VSSetShader(m_vsInventory, NULL, 0); m_context->PSSetShader(m_psInventory, NULL, 0); // Set texture m_context->PSSetShaderResources(0, 1, &m_textureAtlas->ShaderResourceView); ID3D11SamplerState* sampler = m_states->AnisotropicClamp(); m_context->PSSetSamplers(0, 1, &sampler); InventoryRing* activeRing = g_Inventory->GetRing(g_Inventory->GetActiveRing()); int lastRing = 0; float cameraX = INV_CAMERA_DISTANCE * cos(g_Inventory->GetCameraTilt() * RADIAN); float cameraY = g_Inventory->GetCameraY() - INV_CAMERA_DISTANCE * sin(g_Inventory->GetCameraTilt() * RADIAN); float cameraZ = 0.0f; m_stCameraMatrices.View = Matrix::CreateLookAt(Vector3(cameraX, cameraY, cameraZ), Vector3(0.0f, g_Inventory->GetCameraY() - 512.0f, 0.0f), Vector3(0.0f, -1.0f, 0.0f)).Transpose(); m_stCameraMatrices.Projection = Matrix::CreatePerspectiveFieldOfView(80.0f * RADIAN, g_Renderer->ScreenWidth / (float)g_Renderer->ScreenHeight, 1.0f, 200000.0f).Transpose(); updateConstantBuffer(m_cbCameraMatrices, &m_stCameraMatrices, sizeof(CCameraMatrixBuffer)); m_context->VSSetConstantBuffers(0, 1, &m_cbCameraMatrices); for (int k = 0; k < NUM_INVENTORY_RINGS; k++) { InventoryRing* ring = g_Inventory->GetRing(k); if (ring->draw == false || ring->numObjects == 0) continue; short numObjects = ring->numObjects; float deltaAngle = 360.0f / numObjects; int objectIndex = 0; objectIndex = ring->currentObject; // Yellow title if (ring->focusState == INV_FOCUS_STATE_NONE && g_Inventory->GetType() != INV_TYPE_TITLE) PrintString(400, 20, g_GameFlow->GetString(activeRing->titleStringIndex), PRINTSTRING_COLOR_YELLOW, PRINTSTRING_CENTER); for (int i = 0; i < numObjects; i++) { short inventoryObject = ring->objects[objectIndex].inventoryObject; short objectNumber = g_Inventory->GetInventoryObject(ring->objects[objectIndex].inventoryObject)->objectNumber; //if (ring->focusState != INV_FOCUS_STATE_NONE && (k != g_Inventory->GetActiveRing() || inventoryObject != ring->objects[i].inventoryObject)) // continue; // Calculate the inventory object position and rotation float currentAngle = 0.0f; short steps = -objectIndex + ring->currentObject; if (steps < 0) steps += numObjects; currentAngle = steps * deltaAngle; currentAngle += ring->rotation; if (ring->focusState == INV_FOCUS_STATE_NONE && k == g_Inventory->GetActiveRing()) { if (objectIndex == ring->currentObject) ring->objects[objectIndex].rotation += 45 * 360 / 30; else if (ring->objects[objectIndex].rotation != 0) ring->objects[objectIndex].rotation += 45 * 360 / 30; } else if (ring->focusState != INV_FOCUS_STATE_POPUP && ring->focusState != INV_FOCUS_STATE_POPOVER) g_Inventory->GetRing(k)->objects[objectIndex].rotation = 0; if (ring->objects[objectIndex].rotation > 65536.0f) ring->objects[objectIndex].rotation = 0; int x = ring->distance * cos(currentAngle * RADIAN); int y = g_Inventory->GetRing(k)->y; int z = ring->distance * sin(currentAngle * RADIAN); // Prepare the object transform Matrix scale = Matrix::CreateScale(ring->objects[objectIndex].scale, ring->objects[objectIndex].scale, ring->objects[objectIndex].scale); Matrix translation = Matrix::CreateTranslation(x, y, z); Matrix rotation = Matrix::CreateRotationY(TR_ANGLE_TO_RAD(ring->objects[objectIndex].rotation + 16384 + g_Inventory->GetInventoryObject(inventoryObject)->rotY)); Matrix transform = (scale * rotation) * translation; OBJECT_INFO* obj = &Objects[objectNumber]; RendererObject* moveableObj = m_moveableObjects[objectNumber]; if (moveableObj == NULL) continue; // Build the object animation matrices if (ring->focusState == INV_FOCUS_STATE_FOCUSED && obj->animIndex != -1 && objectIndex == ring->currentObject && k == g_Inventory->GetActiveRing()) { short* framePtr[2]; int rate = 0; getFrame(obj->animIndex, ring->frameIndex, framePtr, &rate); updateAnimation(NULL, moveableObj, framePtr, 0, 1, 0xFFFFFFFF); } else { if (obj->animIndex != -1) updateAnimation(NULL, moveableObj, &Anims[obj->animIndex].framePtr, 0, 1, 0xFFFFFFFF); } for (int n = 0; n < moveableObj->ObjectMeshes.size(); n++) { RendererMesh* mesh = moveableObj->ObjectMeshes[n]; // HACK: revolver and crossbow + lasersight if (moveableObj->Id == ID_REVOLVER_ITEM && !g_LaraExtra.Weapons[WEAPON_REVOLVER].HasLasersight && n > 0) break; if (moveableObj->Id == ID_CROSSBOW_ITEM && !g_LaraExtra.Weapons[WEAPON_CROSSBOW].HasLasersight && n > 0) break; // Finish the world matrix if (obj->animIndex != -1) m_stItem.World = (moveableObj->AnimationTransforms[n] * transform).Transpose(); else m_stItem.World = (moveableObj->BindPoseTransforms[n].Transpose() * transform).Transpose(); m_stItem.AmbientLight = Vector4(0.5f, 0.5f, 0.5f, 1.0f); updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbItem); m_context->PSSetConstantBuffers(1, 1, &m_cbItem); for (int m = 0; m < NUM_BUCKETS; m++) { RendererBucket* bucket = &mesh->Buckets[m]; if (bucket->NumVertices == 0) continue; if (m < 2) m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); else m_context->OMSetBlendState(m_states->Additive(), NULL, 0xFFFFFFFF); m_stMisc.AlphaTest = (m < 2); updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); } } short inventoryItem = ring->objects[objectIndex].inventoryObject; // Draw special stuff if needed if (objectIndex == ring->currentObject && k == g_Inventory->GetActiveRing()) { if (g_Inventory->GetActiveRing() == INV_RING_OPTIONS) { /* **************** PASSAPORT ************* */ if (inventoryItem == INV_OBJECT_PASSPORT && ring->focusState == INV_FOCUS_STATE_FOCUSED) { /* **************** LOAD AND SAVE MENU ************* */ if (ring->passportAction == INV_WHAT_PASSPORT_LOAD_GAME || ring->passportAction == INV_WHAT_PASSPORT_SAVE_GAME) { y = 44; for (int n = 0; n < MAX_SAVEGAMES; n++) { if (!g_NewSavegameInfos[n].Present) PrintString(400, y, g_GameFlow->GetString(45), D3DCOLOR_ARGB(255, 255, 255, 255), PRINTSTRING_CENTER | PRINTSTRING_OUTLINE | (ring->selectedIndex == n ? PRINTSTRING_BLINK : 0)); else { sprintf(stringBuffer, "%05d", g_NewSavegameInfos[n].Count); PrintString(200, y, stringBuffer, D3DCOLOR_ARGB(255, 255, 255, 255), PRINTSTRING_OUTLINE | (ring->selectedIndex == n ? PRINTSTRING_BLINK | PRINTSTRING_DONT_UPDATE_BLINK : 0)); PrintString(250, y, (char*)g_NewSavegameInfos[n].LevelName.c_str(), D3DCOLOR_ARGB(255, 255, 255, 255), PRINTSTRING_OUTLINE | (ring->selectedIndex == n ? PRINTSTRING_BLINK | PRINTSTRING_DONT_UPDATE_BLINK : 0)); sprintf(stringBuffer, g_GameFlow->GetString(44), g_NewSavegameInfos[n].Days, g_NewSavegameInfos[n].Hours, g_NewSavegameInfos[n].Minutes, g_NewSavegameInfos[n].Seconds); PrintString(475, y, stringBuffer, D3DCOLOR_ARGB(255, 255, 255, 255), PRINTSTRING_OUTLINE | (ring->selectedIndex == n ? PRINTSTRING_BLINK : 0)); } y += 24; } drawLogo = false; drawGuiRect = true; guiRect.left = 180; guiRect.right = 440; guiRect.top = 24; guiRect.bottom = y + 20 - 24; //drawColoredQuad(180, 24, 440, y + 20 - 24, Vector4(0.0f, 0.0f, 0.25f, 0.5f)); } /* **************** SELECT LEVEL ************* */ else if (ring->passportAction == INV_WHAT_PASSPORT_SELECT_LEVEL) { drawLogo = false; drawGuiRect = true; guiRect.left = 200; guiRect.right = 400; guiRect.top = 24; guiRect.bottom = 24 * (g_GameFlow->GetNumLevels() - 1) + 40; //drawColoredQuad(200, 24, 400, 24 * (g_GameFlow->GetNumLevels() - 1) + 40, Vector4(0.0f, 0.0f, 0.25f, 0.5f)); short lastY = 50; for (int n = 1; n < g_GameFlow->GetNumLevels(); n++) { GameScriptLevel* levelScript = g_GameFlow->GetLevel(n); PrintString(400, lastY, g_GameFlow->GetString(levelScript->NameStringIndex), D3DCOLOR_ARGB(255, 255, 255, 255), PRINTSTRING_CENTER | PRINTSTRING_OUTLINE | (ring->selectedIndex == n - 1 ? PRINTSTRING_BLINK : 0)); lastY += 24; } } char* string = (char*)""; switch (ring->passportAction) { case INV_WHAT_PASSPORT_NEW_GAME: string = g_GameFlow->GetString(STRING_NEW_GAME); break; case INV_WHAT_PASSPORT_SELECT_LEVEL: string = g_GameFlow->GetString(STRING_SELECT_LEVEL); break; case INV_WHAT_PASSPORT_LOAD_GAME: string = g_GameFlow->GetString(STRING_LOAD_GAME); break; case INV_WHAT_PASSPORT_SAVE_GAME: string = g_GameFlow->GetString(STRING_SAVE_GAME); break; case INV_WHAT_PASSPORT_EXIT_GAME: string = g_GameFlow->GetString(STRING_EXIT_GAME); break; case INV_WHAT_PASSPORT_EXIT_TO_TITLE: string = g_GameFlow->GetString(STRING_EXIT_TO_TITLE); break; } PrintString(400, 550, string, PRINTSTRING_COLOR_ORANGE, PRINTSTRING_CENTER | PRINTSTRING_OUTLINE); } /* **************** GRAPHICS SETTINGS ************* */ else if (inventoryItem == INV_OBJECT_SUNGLASSES && ring->focusState == INV_FOCUS_STATE_FOCUSED) { // Draw settings menu RendererVideoAdapter* adapter = &m_adapters[g_Configuration.Adapter]; int y = 200; PrintString(400, y, g_GameFlow->GetString(STRING_DISPLAY), PRINTSTRING_COLOR_YELLOW, PRINTSTRING_OUTLINE | PRINTSTRING_CENTER); y += 25; // Screen resolution PrintString(200, y, g_GameFlow->GetString(STRING_SCREEN_RESOLUTION), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_DONT_UPDATE_BLINK | PRINTSTRING_OUTLINE | (ring->selectedIndex == 0 ? PRINTSTRING_BLINK : 0)); RendererDisplayMode* mode = &adapter->DisplayModes[ring->SelectedVideoMode]; char buffer[255]; ZeroMemory(buffer, 255); sprintf(buffer, "%d x %d (%d Hz)", mode->Width, mode->Height, mode->RefreshRate); PrintString(400, y, buffer, PRINTSTRING_COLOR_WHITE, PRINTSTRING_OUTLINE | (ring->selectedIndex == 0 ? PRINTSTRING_BLINK : 0)); y += 25; // Windowed mode PrintString(200, y, g_GameFlow->GetString(STRING_WINDOWED), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_DONT_UPDATE_BLINK | PRINTSTRING_OUTLINE | (ring->selectedIndex == 1 ? PRINTSTRING_BLINK : 0)); PrintString(400, y, g_GameFlow->GetString(ring->Configuration.Windowed ? STRING_ENABLED : STRING_DISABLED), PRINTSTRING_COLOR_WHITE, PRINTSTRING_OUTLINE | (ring->selectedIndex == 1 ? PRINTSTRING_BLINK : 0)); y += 25; // Enable dynamic shadows PrintString(200, y, g_GameFlow->GetString(STRING_SHADOWS), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_DONT_UPDATE_BLINK | PRINTSTRING_OUTLINE | (ring->selectedIndex == 2 ? PRINTSTRING_BLINK : 0)); PrintString(400, y, g_GameFlow->GetString(ring->Configuration.EnableShadows ? STRING_ENABLED : STRING_DISABLED), PRINTSTRING_COLOR_WHITE, PRINTSTRING_OUTLINE | (ring->selectedIndex == 2 ? PRINTSTRING_BLINK : 0)); y += 25; // Enable caustics PrintString(200, y, g_GameFlow->GetString(STRING_CAUSTICS), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_DONT_UPDATE_BLINK | PRINTSTRING_OUTLINE | (ring->selectedIndex == 3 ? PRINTSTRING_BLINK : 0)); PrintString(400, y, g_GameFlow->GetString(ring->Configuration.EnableCaustics ? STRING_ENABLED : STRING_DISABLED), PRINTSTRING_COLOR_WHITE, PRINTSTRING_OUTLINE | (ring->selectedIndex == 3 ? PRINTSTRING_BLINK : 0)); y += 25; // Enable volumetric fog PrintString(200, y, g_GameFlow->GetString(STRING_VOLUMETRIC_FOG), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_DONT_UPDATE_BLINK | PRINTSTRING_OUTLINE | (ring->selectedIndex == 4 ? PRINTSTRING_BLINK : 0)); PrintString(400, y, g_GameFlow->GetString(ring->Configuration.EnableVolumetricFog ? STRING_ENABLED : STRING_DISABLED), PRINTSTRING_COLOR_WHITE, PRINTSTRING_OUTLINE | (ring->selectedIndex == 4 ? PRINTSTRING_BLINK : 0)); y += 25; // Apply and cancel PrintString(400, y, g_GameFlow->GetString(STRING_APPLY), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_CENTER | PRINTSTRING_OUTLINE | (ring->selectedIndex == 5 ? PRINTSTRING_BLINK : 0)); y += 25; PrintString(400, y, g_GameFlow->GetString(STRING_CANCEL), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_CENTER | PRINTSTRING_OUTLINE | (ring->selectedIndex == 6 ? PRINTSTRING_BLINK : 0)); y += 25; drawLogo = false; drawGuiRect = true; guiRect.left = 180; guiRect.right = 440; guiRect.top = 180; guiRect.bottom = y + 20 - 180; //drawColoredQuad(180, 180, 440, y + 20 - 180, Vector4(0.0f, 0.0f, 0.25f, 0.5f)); } /* **************** AUDIO SETTINGS ************* */ else if (inventoryItem == INV_OBJECT_HEADPHONES && ring->focusState == INV_FOCUS_STATE_FOCUSED) { // Draw sound menu y = 200; PrintString(400, y, g_GameFlow->GetString(STRING_SOUND), PRINTSTRING_COLOR_YELLOW, PRINTSTRING_OUTLINE | PRINTSTRING_CENTER); y += 25; // Enable sound PrintString(200, y, g_GameFlow->GetString(STRING_ENABLE_SOUND), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_DONT_UPDATE_BLINK | PRINTSTRING_OUTLINE | (ring->selectedIndex == 0 ? PRINTSTRING_BLINK : 0)); PrintString(400, y, g_GameFlow->GetString(ring->Configuration.EnableSound ? STRING_ENABLED : STRING_DISABLED), PRINTSTRING_COLOR_WHITE, PRINTSTRING_OUTLINE | (ring->selectedIndex == 0 ? PRINTSTRING_BLINK : 0)); y += 25; // Enable sound special effects PrintString(200, y, g_GameFlow->GetString(STRING_SPECIAL_SOUND_FX), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_DONT_UPDATE_BLINK | PRINTSTRING_OUTLINE | (ring->selectedIndex == 1 ? PRINTSTRING_BLINK : 0)); PrintString(400, y, g_GameFlow->GetString(ring->Configuration.EnableAudioSpecialEffects ? STRING_ENABLED : STRING_DISABLED), PRINTSTRING_COLOR_WHITE, PRINTSTRING_OUTLINE | (ring->selectedIndex == 1 ? PRINTSTRING_BLINK : 0)); y += 25; // Music volume PrintString(200, y, g_GameFlow->GetString(STRING_MUSIC_VOLUME), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_OUTLINE | (ring->selectedIndex == 2 ? PRINTSTRING_BLINK : 0)); DrawBar(400, y + 4, 150, 18, ring->Configuration.MusicVolume, 0x0000FF, 0x0000FF); y += 25; // Sound FX volume PrintString(200, y, g_GameFlow->GetString(STRING_SFX_VOLUME), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_OUTLINE | (ring->selectedIndex == 3 ? PRINTSTRING_BLINK : 0)); DrawBar(400, y + 4, 150, 18, ring->Configuration.SfxVolume, 0x0000FF, 0x0000FF); y += 25; // Apply and cancel PrintString(400, y, g_GameFlow->GetString(STRING_APPLY), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_CENTER | PRINTSTRING_OUTLINE | (ring->selectedIndex == 4 ? PRINTSTRING_BLINK : 0)); y += 25; PrintString(400, y, g_GameFlow->GetString(STRING_CANCEL), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_CENTER | PRINTSTRING_OUTLINE | (ring->selectedIndex == 5 ? PRINTSTRING_BLINK : 0)); y += 25; drawLogo = false; drawGuiRect = true; guiRect.left = 180; guiRect.right = 440; guiRect.top = 180; guiRect.bottom = y + 20 - 180; //drawColoredQuad(180, 180, 440, y + 20 - 180, Vector4(0.0f, 0.0f, 0.25f, 0.5f)); } /* **************** CONTROLS SETTINGS ************* */ else if (inventoryItem == INV_OBJECT_KEYS && ring->focusState == INV_FOCUS_STATE_FOCUSED) { // Draw sound menu y = 40; PrintString(400, y, g_GameFlow->GetString(STRING_CONTROLS), PRINTSTRING_COLOR_YELLOW, PRINTSTRING_OUTLINE | PRINTSTRING_CENTER); y += 25; for (int k = 0; k < 18; k++) { PrintString(200, y, g_GameFlow->GetString(STRING_CONTROLS_MOVE_FORWARD + k), PRINTSTRING_COLOR_WHITE, PRINTSTRING_OUTLINE | (ring->selectedIndex == k ? PRINTSTRING_BLINK : 0) | (ring->waitingForKey ? PRINTSTRING_DONT_UPDATE_BLINK : 0)); if (ring->waitingForKey && k == ring->selectedIndex) { PrintString(400, y, g_GameFlow->GetString(STRING_WAITING_FOR_KEY), PRINTSTRING_COLOR_YELLOW, PRINTSTRING_OUTLINE | PRINTSTRING_BLINK); } else { PrintString(400, y, (char*)g_KeyNames[KeyboardLayout1[k]], PRINTSTRING_COLOR_ORANGE, PRINTSTRING_OUTLINE); } y += 25; } // Apply and cancel PrintString(400, y, g_GameFlow->GetString(STRING_APPLY), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_CENTER | PRINTSTRING_OUTLINE | (ring->selectedIndex == NUM_CONTROLS + 0 ? PRINTSTRING_BLINK : 0)); y += 25; PrintString(400, y, g_GameFlow->GetString(STRING_CANCEL), PRINTSTRING_COLOR_ORANGE, PRINTSTRING_CENTER | PRINTSTRING_OUTLINE | (ring->selectedIndex == NUM_CONTROLS + 1 ? PRINTSTRING_BLINK : 0)); y += 25; drawLogo = false; drawGuiRect = true; guiRect.left = 180; guiRect.right = 440; guiRect.top = 20; guiRect.bottom = y + 20 - 20; //drawColoredQuad(180, 20, 440, y + 20 - 20, Vector4(0.0f, 0.0f, 0.25f, 0.5f)); } else { // Draw the description below the object char* string = g_GameFlow->GetString(g_Inventory->GetInventoryObject(inventoryItem)->objectName); // (char*)g_NewStrings[g_Inventory->GetInventoryObject(inventoryItem)->objectName].c_str(); // &AllStrings[AllStringsOffsets[g_Inventory->GetInventoryObject(inventoryItem)->objectName]]; PrintString(400, 550, string, PRINTSTRING_COLOR_ORANGE, PRINTSTRING_CENTER | PRINTSTRING_OUTLINE); } } else { short inventoryItem = g_Inventory->GetRing(k)->objects[objectIndex].inventoryObject; char* string = g_GameFlow->GetString(g_Inventory->GetInventoryObject(inventoryItem)->objectName); // &AllStrings[AllStringsOffsets[InventoryObjectsList[inventoryItem].objectName]]; if (g_Inventory->IsCurrentObjectWeapon() && ring->focusState == INV_FOCUS_STATE_FOCUSED) { y = 100; for (int a = 0; a < ring->numActions; a++) { int stringIndex = 0; if (ring->actions[a] == INV_ACTION_USE) stringIndex = STRING_USE; if (ring->actions[a] == INV_ACTION_COMBINE) stringIndex = STRING_COMBINE; if (ring->actions[a] == INV_ACTION_SEPARE) stringIndex = STRING_SEPARE; if (ring->actions[a] == INV_ACTION_SELECT_AMMO) stringIndex = STRING_CHOOSE_AMMO; // Apply and cancel PrintString(400, y, g_GameFlow->GetString(stringIndex), PRINTSTRING_COLOR_WHITE, PRINTSTRING_CENTER | PRINTSTRING_OUTLINE | (ring->selectedIndex == a ? PRINTSTRING_BLINK : 0)); y += 25; } drawLogo = false; drawGuiRect = true; guiRect.left = 300; guiRect.right = 200; guiRect.top = 80; guiRect.bottom = y + 20 - 80; //drawColoredQuad(300, 80, 200, y + 20 - 80, Vector4(0.0f, 0.0f, 0.25f, 0.5f)); } int quantity = -1; switch (objectNumber) { case ID_BIGMEDI_ITEM: quantity = Lara.numLargeMedipack; break; case ID_SMALLMEDI_ITEM: quantity = Lara.numSmallMedipack; break; case ID_FLARE_INV_ITEM: quantity = Lara.numFlares; break; case ID_SHOTGUN_AMMO1_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_SHOTGUN].Ammo[0]; if (quantity != -1) quantity /= 6; break; case ID_SHOTGUN_AMMO2_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_SHOTGUN].Ammo[1]; if (quantity != -1) quantity /= 6; break; case ID_HK_AMMO_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_HK].Ammo[0]; break; case ID_CROSSBOW_AMMO1_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_CROSSBOW].Ammo[0]; break; case ID_CROSSBOW_AMMO2_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_CROSSBOW].Ammo[1]; break; case ID_CROSSBOW_AMMO3_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_CROSSBOW].Ammo[2]; break; case ID_REVOLVER_AMMO_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_REVOLVER].Ammo[0]; break; case ID_UZI_AMMO_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_UZI].Ammo[0]; break; case ID_PISTOLS_AMMO_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_PISTOLS].Ammo[0]; break; case ID_GRENADE_AMMO1_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_GRENADE_LAUNCHER].Ammo[0]; break; case ID_GRENADE_AMMO2_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_GRENADE_LAUNCHER].Ammo[1]; break; case ID_GRENADE_AMMO3_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_GRENADE_LAUNCHER].Ammo[2]; break; case ID_HARPOON_AMMO_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_HARPOON_GUN].Ammo[0]; break; case ID_ROCKET_LAUNCHER_AMMO_ITEM: quantity = g_LaraExtra.Weapons[WEAPON_ROCKET_LAUNCHER].Ammo[0]; break; case ID_PICKUP_ITEM4: quantity = Savegame.Level.Secrets; break; default: if (objectNumber >= ID_PUZZLE_ITEM1 && objectNumber <= ID_PUZZLE_ITEM8) quantity = Lara.puzzleItems[objectNumber - ID_PUZZLE_ITEM1]; else if (objectNumber >= ID_PUZZLE_ITEM1_COMBO1 && objectNumber <= ID_PUZZLE_ITEM8_COMBO2) quantity = (Lara.puzzleItemsCombo >> (objectNumber - ID_PUZZLE_ITEM1_COMBO1)) & 1; else if (objectNumber >= ID_KEY_ITEM1 && objectNumber <= ID_KEY_ITEM8) quantity = (Lara.keyItems >> (objectNumber - ID_KEY_ITEM1)) & 1; else if (objectNumber >= ID_KEY_ITEM1_COMBO1 && objectNumber <= ID_KEY_ITEM8_COMBO2) quantity = (Lara.keyItemsCombo >> (objectNumber - ID_KEY_ITEM1_COMBO1)) & 1; else if (objectNumber >= ID_PICKUP_ITEM1 && objectNumber <= ID_PICKUP_ITEM3) quantity = (Lara.pickupItems >> (objectNumber - ID_PICKUP_ITEM1)) & 1; else if (objectNumber >= ID_PICKUP_ITEM1_COMBO1 && objectNumber <= ID_PICKUP_ITEM3_COMBO2) quantity = (Lara.pickupItemsCombo >> (objectNumber - ID_PICKUP_ITEM1_COMBO1)) & 1; else if (objectNumber == ID_EXAMINE1) quantity = Lara.examine1; else if (objectNumber == ID_EXAMINE2) quantity = Lara.examine2; else if (objectNumber == ID_EXAMINE3) quantity = Lara.examine3; } if (quantity < 1) PrintString(400, 550, string, D3DCOLOR_ARGB(255, 216, 117, 49), PRINTSTRING_CENTER); else { sprintf(stringBuffer, "%d x %s", quantity, string); PrintString(400, 550, stringBuffer, D3DCOLOR_ARGB(255, 216, 117, 49), PRINTSTRING_CENTER); } } } objectIndex++; if (objectIndex == numObjects) objectIndex = 0; } lastRing++; } if (drawGuiRect) { // Draw blu box drawColoredQuad(guiRect.left, guiRect.top, guiRect.right, guiRect.bottom, guiColor); } drawLines2D(); drawAllStrings(); if (g_Inventory->GetType() == INV_TYPE_TITLE && g_GameFlow->TitleType == TITLE_FLYBY && drawLogo) { // Draw main logo float factorX = ScreenWidth / 800.0f; float factorY = ScreenHeight / 600.0f; RECT rect; rect.left = 250 * factorX; rect.right = 550 * factorX; rect.top = 50 * factorY; rect.bottom = 200 * factorY; m_spriteBatch->Begin(SpriteSortMode_BackToFront, m_states->Additive()); m_spriteBatch->Draw(m_logo->ShaderResourceView, rect, Vector4::One); m_spriteBatch->End(); } return 0; } bool Renderer11::drawColoredQuad(int x, int y, int w, int h, Vector4 color) { float factorW = ScreenWidth / 800.0f; float factorH = ScreenHeight / 600.0f; RECT rect; rect.top = y * factorH; rect.left = x * factorW; rect.bottom = (y + h) * factorH; rect.right = (x + w) * factorW; m_spriteBatch->Begin(SpriteSortMode_BackToFront, m_states->AlphaBlend(), NULL, m_states->DepthRead()); m_spriteBatch->Draw(m_whiteTexture->ShaderResourceView, rect, color); m_spriteBatch->End(); int shiftW = 4 * factorW; int shiftH = 4 * factorH; AddLine2D(rect.left + shiftW, rect.top + shiftH, rect.right - shiftW, rect.top + shiftH, 128, 128, 128, 128); AddLine2D(rect.right - shiftW, rect.top + shiftH, rect.right - shiftW, rect.bottom - shiftH, 128, 128, 128, 128); AddLine2D(rect.left + shiftW, rect.bottom - shiftH, rect.right - shiftW, rect.bottom - shiftH, 128, 128, 128, 128); AddLine2D(rect.left + shiftW, rect.top + shiftH, rect.left + shiftW, rect.bottom - shiftH, 128, 128, 128, 128); m_context->OMSetDepthStencilState(m_states->DepthDefault(), 0); return true; } bool Renderer11::drawFullScreenQuad(ID3D11ShaderResourceView* texture, Vector3 color, bool cinematicBars) { RendererVertex vertices[4]; if (!cinematicBars) { vertices[0].Position.x = -1.0f; vertices[0].Position.y = 1.0f; vertices[0].Position.z = 0.0f; vertices[0].UV.x = 0.0f; vertices[0].UV.y = 0.0f; vertices[0].Color = Vector4(color.x, color.y, color.z, 1.0f); vertices[1].Position.x = 1.0f; vertices[1].Position.y = 1.0f; vertices[1].Position.z = 0.0f; vertices[1].UV.x = 1.0f; vertices[1].UV.y = 0.0f; vertices[1].Color = Vector4(color.x, color.y, color.z, 1.0f); vertices[2].Position.x = 1.0f; vertices[2].Position.y = -1.0f; vertices[2].Position.z = 0.0f; vertices[2].UV.x = 1.0f; vertices[2].UV.y = 1.0f; vertices[2].Color = Vector4(color.x, color.y, color.z, 1.0f); vertices[3].Position.x = -1.0f; vertices[3].Position.y = -1.0f; vertices[3].Position.z = 0.0f; vertices[3].UV.x = 0.0f; vertices[3].UV.y = 1.0f; vertices[3].Color = Vector4(color.x, color.y, color.z, 1.0f); } else { float cinematicFactor = 0.12f; vertices[0].Position.x = -1.0f; vertices[0].Position.y = 1.0f - cinematicFactor * 2; vertices[0].Position.z = 0.0f; vertices[0].UV.x = 0.0f; vertices[0].UV.y = cinematicFactor; vertices[0].Color = Vector4(color.x, color.y, color.z, 1.0f); vertices[1].Position.x = 1.0f; vertices[1].Position.y = 1.0f - cinematicFactor * 2; vertices[1].Position.z = 0.0f; vertices[1].UV.x = 1.0f; vertices[1].UV.y = cinematicFactor; vertices[1].Color = Vector4(color.x, color.y, color.z, 1.0f); vertices[2].Position.x = 1.0f; vertices[2].Position.y = -(1.0f - cinematicFactor * 2); vertices[2].Position.z = 0.0f; vertices[2].UV.x = 1.0f; vertices[2].UV.y = 1.0f - cinematicFactor; vertices[2].Color = Vector4(color.x, color.y, color.z, 1.0f); vertices[3].Position.x = -1.0f; vertices[3].Position.y = -(1.0f - cinematicFactor * 2); vertices[3].Position.z = 0.0f; vertices[3].UV.x = 0.0f; vertices[3].UV.y = 1.0f - cinematicFactor; vertices[3].Color = Vector4(color.x, color.y, color.z, 1.0f); } m_context->VSSetShader(m_vsFullScreenQuad, NULL, 0); m_context->PSSetShader(m_psFullScreenQuad, NULL, 0); m_context->PSSetShaderResources(0, 1, &texture); ID3D11SamplerState* sampler = m_states->AnisotropicClamp(); m_context->PSSetSamplers(0, 1, &sampler); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); m_primitiveBatch->Begin(); m_primitiveBatch->DrawQuad(vertices[0], vertices[1], vertices[2], vertices[3]); m_primitiveBatch->End(); return true; } bool Renderer11::drawRopes() { for (int n = 0; n < NumRopes; n++) { ROPE_STRUCT* rope = &Ropes[n]; if (rope->active) { // Original algorithm: // 1) Transform segment coordinates from 3D to 2D + depth // 2) Get dx, dy and the segment length // 3) Get sine and cosine from dx / length and dy / length // 4) Calculate a scale factor // 5) Get the coordinates of the 4 corners of each sprite iteratively // 6) Last step only for us, unproject back to 3D coordinates // Tranform rope points Vector3 projected[24]; Matrix world = Matrix::Identity; for (int i = 0; i < 24; i++) { Vector3 absolutePosition = Vector3(rope->position.x + rope->segment[i].x / 65536.0f, rope->position.y + rope->segment[i].y / 65536.0f, rope->position.z + rope->segment[i].z / 65536.0f); projected[i] = m_viewportToolkit->Project(absolutePosition, Projection, View, world); } // Now each rope point is transformed in screen X, Y and Z depth // Let's calculate dx, dy corrections and scaling float dx = projected[1].x - projected[0].x; float dy = projected[1].y - projected[0].y; float length = sqrt(dx * dx + dy * dy); float s = 0; float c = 0; if (length != 0) { s = -dy / length; c = dx / length; } float w = 6.0f; if (projected[0].z) { w = 6.0f * PhdPerspective / projected[0].z / 65536.0f; if (w < 3) w = 3; } float sdx = s * w; float sdy = c * w; float x1 = projected[0].x - sdx; float y1 = projected[0].y - sdy; float x2 = projected[0].x + sdx; float y2 = projected[0].y + sdy; float depth = projected[0].z; for (int j = 0; j < 24; j++) { Vector3 p1 = m_viewportToolkit->Unproject(Vector3(x1, y1, depth), Projection, View, world); Vector3 p2 = m_viewportToolkit->Unproject(Vector3(x2, y2, depth), Projection, View, world); dx = projected[j].x - projected[j - 1].x; dy = projected[j].y - projected[j - 1].y; length = sqrt(dx * dx + dy * dy); s = 0; c = 0; if (length != 0) { s = -dy / length; c = dx / length; } w = 6.0f; if (projected[j].z) { w = 6.0f * PhdPerspective / projected[j].z / 65536.0f; if (w < 3) w = 3; } float sdx = s * w; float sdy = c * w; float x3 = projected[j].x - sdx; float y3 = projected[j].y - sdy; float x4 = projected[j].x + sdx; float y4 = projected[j].y + sdy; depth = projected[j].z; Vector3 p3 = m_viewportToolkit->Unproject(Vector3(x3, y3, depth), Projection, View, world); Vector3 p4 = m_viewportToolkit->Unproject(Vector3(x4, y4, depth), Projection, View, world); AddSprite3D(m_sprites[20], Vector3(p1.x, p1.y, p1.z), Vector3(p2.x, p2.y, p2.z), Vector3(p3.x, p3.y, p3.z), Vector3(p4.x, p4.y, p4.z), Vector4(0.5f, 0.5f, 0.5f,1.0f), 0, 1, 0, 0, BLENDMODE_OPAQUE); x1 = x4; y1 = y4; x2 = x3; y2 = y3; } } } return true; } bool Renderer11::drawLines2D() { m_context->RSSetState(m_states->CullNone()); m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); m_context->OMSetDepthStencilState(m_states->DepthRead(), 0); m_context->VSSetShader(m_vsSolid, NULL, 0); m_context->PSSetShader(m_psSolid, NULL, 0); Matrix world = Matrix::CreateOrthographicOffCenter(0, ScreenWidth, ScreenHeight, 0, m_viewport.MinDepth, m_viewport.MaxDepth); m_stCameraMatrices.View = Matrix::Identity; m_stCameraMatrices.Projection = Matrix::Identity; updateConstantBuffer(m_cbCameraMatrices, &m_stCameraMatrices, sizeof(CCameraMatrixBuffer)); m_context->VSSetConstantBuffers(0, 1, &m_cbCameraMatrices); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST); m_context->IASetInputLayout(m_inputLayout); m_primitiveBatch->Begin(); for (int i = 0; i < m_lines2DToDraw.Size(); i++) { RendererLine2D* line = m_lines2DToDraw[i]; RendererVertex v1; v1.Position.x = line->Vertices[0].x; v1.Position.y = line->Vertices[0].y; v1.Position.z = 1.0f; v1.Color.x = line->Color.x / 255.0f; v1.Color.y = line->Color.y / 255.0f; v1.Color.z = line->Color.z / 255.0f; v1.Color.w = line->Color.w / 255.0f; RendererVertex v2; v2.Position.x = line->Vertices[1].x; v2.Position.y = line->Vertices[1].y; v2.Position.z = 1.0f; v2.Color.x = line->Color.x / 255.0f; v2.Color.y = line->Color.y / 255.0f; v2.Color.z = line->Color.z / 255.0f; v2.Color.w = line->Color.w / 255.0f; v1.Position = Vector3::Transform(v1.Position, world); v2.Position = Vector3::Transform(v2.Position, world); v1.Position.z = 0.5f; v2.Position.z = 0.5f; m_primitiveBatch->DrawLine(v1, v2); } m_primitiveBatch->End(); m_context->RSSetState(m_states->CullCounterClockwise()); m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); m_context->OMSetDepthStencilState(m_states->DepthDefault(), 0); return true; } bool Renderer11::drawOverlays() { if (!BinocularRange && !SpotcamOverlay) return true; m_context->OMSetBlendState(m_states->AlphaBlend(), NULL, 0xFFFFFFFF); drawFullScreenQuad(m_binocularsTexture->ShaderResourceView, Vector3::One, false); m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); return true; } bool Renderer11::DrawBar(int x, int y, int w, int h, int percent, int color1, int color2) { byte r1 = (color1 >> 16) & 0xFF; byte g1 = (color1 >> 8) & 0xFF; byte b1 = (color1 >> 0) & 0xFF; byte r2 = (color2 >> 16) & 0xFF; byte g2 = (color2 >> 8) & 0xFF; byte b2 = (color2 >> 0) & 0xFF; float factorX = ScreenWidth / 800.0f; float factorY = ScreenHeight / 600.0f; int realX = x * factorX; int realY = y * factorY; int realW = w * factorX; int realH = h * factorY; int realPercent = percent / 100.0f * realW; for (int i = 0; i < realH; i++) AddLine2D(realX, realY + i, realX + realW, realY + i, 0, 0, 0, 255); for (int i = 0; i < realH; i++) AddLine2D(realX, realY + i, realX + realPercent, realY + i, r1, g1, b1, 255); AddLine2D(realX, realY, realX + realW, realY, 255, 255, 255, 255); AddLine2D(realX, realY + realH, realX + realW, realY + realH, 255, 255, 255, 255); AddLine2D(realX, realY, realX, realY + realH, 255, 255, 255, 255); AddLine2D(realX + realW, realY, realX + realW, realY + realH + 1, 255, 255, 255, 255); return true; } void Renderer11::drawFootprints() { for (auto i = footprints.begin(); i != footprints.end();i++) { FOOTPRINT_STRUCT& footprint = *i; if (footprint.active) { float y = footprint.pos.yPos; Matrix rot = Matrix::CreateRotationY(TR_ANGLE_TO_RAD(footprint.pos.yRot) + PI); Vector3 p1 = Vector3(-64, 0, -64); Vector3 p2 = Vector3(64, 0, -64); Vector3 p3 = Vector3(64, 0, 64); Vector3 p4 = Vector3(-64, 0, 64); p1 = XMVector3Transform(p1, rot); p2 = XMVector3Transform(p2, rot); p3 = XMVector3Transform(p3, rot); p4 = XMVector3Transform(p4, rot); p1 += Vector3(footprint.pos.xPos, footprint.pos.yPos, footprint.pos.zPos); p2 += Vector3(footprint.pos.xPos, footprint.pos.yPos, footprint.pos.zPos); p3 += Vector3(footprint.pos.xPos, footprint.pos.yPos, footprint.pos.zPos); p4 += Vector3(footprint.pos.xPos, footprint.pos.yPos, footprint.pos.zPos); AddSprite3D(m_sprites[Objects[ID_MISC_SPRITES].meshIndex+1], p1,p2,p3,p4, Vector4(1,1,1,footprint.opacity), 0, 1, 1, 1, BLENDMODE_SUBTRACTIVE); } } } void Renderer11::AddLine2D(int x1, int y1, int x2, int y2, byte r, byte g, byte b, byte a) { RendererLine2D* line = &m_lines2DBuffer[m_nextLine2D++]; line->Vertices[0] = Vector2(x1, y1); line->Vertices[1] = Vector2(x2, y2); line->Color = Vector4(r, g, b, a); m_lines2DToDraw.Add(line); } bool Renderer11::drawGunFlashes() { if (!Lara.rightArm.flash_gun && !Lara.leftArm.flash_gun) return true; Matrix world; Matrix translation; Matrix rotation; RendererObject* laraObj = m_moveableObjects[ID_LARA]; RendererObject* laraSkin = m_moveableObjects[ID_LARA_SKIN]; OBJECT_INFO* obj = &Objects[0]; RendererRoom* room = m_rooms[LaraItem->roomNumber]; RendererItem* item = &m_items[Lara.itemNumber]; m_stItem.AmbientLight = room->AmbientLight; memcpy(m_stItem.BonesMatrices, &Matrix::Identity, sizeof(Matrix)); m_stLights.NumLights = item->Lights.Size(); for (int j = 0; j < item->Lights.Size(); j++) memcpy(&m_stLights.Lights[j], item->Lights[j], sizeof(ShaderLight)); updateConstantBuffer(m_cbLights, &m_stLights, sizeof(CLightBuffer)); m_context->PSSetConstantBuffers(2, 1, &m_cbLights); m_stMisc.AlphaTest = true; updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); short length = 0; short zOffset = 0; short rotationX = 0; m_context->OMSetBlendState(m_states->Additive(), NULL, 0xFFFFFFFF); m_context->OMSetDepthStencilState(m_states->DepthNone(), 0); if (Lara.weaponItem != WEAPON_FLARE && Lara.weaponItem != WEAPON_SHOTGUN && Lara.weaponItem != WEAPON_CROSSBOW) { switch (Lara.weaponItem) { case WEAPON_REVOLVER: length = 192; zOffset = 68; rotationX = -14560; break; case WEAPON_UZI: length = 190; zOffset = 50; break; case WEAPON_HK: length = 300; zOffset = 92; rotationX = -14560; break; default: case WEAPON_PISTOLS: length = 180; zOffset = 40; rotationX = -16830; break; } OBJECT_INFO* flashObj = &Objects[ID_GUN_FLASH]; RendererObject* flashMoveable = m_moveableObjects[ID_GUN_FLASH]; RendererMesh* flashMesh = flashMoveable->ObjectMeshes[0]; for (int b = 0; b < NUM_BUCKETS; b++) { RendererBucket* flashBucket = &flashMesh->Buckets[b]; if (flashBucket->NumVertices != 0) { Matrix offset = Matrix::CreateTranslation(0, length, zOffset); Matrix rotation2 = Matrix::CreateRotationX(TR_ANGLE_TO_RAD(rotationX)); if (Lara.leftArm.flash_gun) { world = laraObj->AnimationTransforms[LM_LHAND].Transpose() * m_LaraWorldMatrix; world = offset * world; world = rotation2 * world; m_stItem.World = world.Transpose(); updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbItem); m_context->DrawIndexed(flashBucket->NumIndices, flashBucket->StartIndex, 0); m_numDrawCalls++; } if (Lara.rightArm.flash_gun) { world = laraObj->AnimationTransforms[LM_RHAND].Transpose() * m_LaraWorldMatrix; world = offset * world; world = rotation2 * world; m_stItem.World = world.Transpose(); updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbItem); m_context->DrawIndexed(flashBucket->NumIndices, flashBucket->StartIndex, 0); m_numDrawCalls++; } } } } m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); m_context->OMSetDepthStencilState(m_states->DepthDefault(), 0); return true; } bool Renderer11::drawGunShells() { RendererRoom* room = m_rooms[LaraItem->roomNumber]; RendererItem* item = &m_items[Lara.itemNumber]; m_stItem.AmbientLight = room->AmbientLight; memcpy(m_stItem.BonesMatrices, &Matrix::Identity, sizeof(Matrix)); m_stLights.NumLights = item->Lights.Size(); for (int j = 0; j < item->Lights.Size(); j++) memcpy(&m_stLights.Lights[j], item->Lights[j], sizeof(ShaderLight)); updateConstantBuffer(m_cbLights, &m_stLights, sizeof(CLightBuffer)); m_context->PSSetConstantBuffers(2, 1, &m_cbLights); m_stMisc.AlphaTest = true; updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); for (int i = 0; i < 24; i++) { GUNSHELL_STRUCT* gunshell = &Gunshells[i]; if (gunshell->counter > 0) { OBJECT_INFO* obj = &Objects[gunshell->objectNumber]; RendererObject* moveableObj = m_moveableObjects[gunshell->objectNumber]; Matrix translation = Matrix::CreateTranslation(gunshell->pos.xPos, gunshell->pos.yPos, gunshell->pos.zPos); Matrix rotation = Matrix::CreateFromYawPitchRoll(TR_ANGLE_TO_RAD(gunshell->pos.yRot), TR_ANGLE_TO_RAD(gunshell->pos.xRot), TR_ANGLE_TO_RAD(gunshell->pos.zRot)); Matrix world = rotation * translation; m_stItem.World = world.Transpose(); updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbItem); RendererMesh* mesh = moveableObj->ObjectMeshes[0]; for (int b = 0; b < NUM_BUCKETS; b++) { RendererBucket* bucket = &mesh->Buckets[b]; if (bucket->NumVertices == 0) continue; m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } } } return true; } void Renderer11::drawUnderwaterDust() { if (m_firstUnderwaterDustParticles) { for (int i = 0; i < NUM_UNDERWATER_DUST_PARTICLES; i++) m_underwaterDustParticles[i].Reset = true; } for (int i = 0; i < NUM_UNDERWATER_DUST_PARTICLES; i++) { RendererUnderwaterDustParticle* dust = &m_underwaterDustParticles[i]; if (dust->Reset) { dust->X = LaraItem->pos.xPos + rand() % UNDERWATER_DUST_PARTICLES_RADIUS - UNDERWATER_DUST_PARTICLES_RADIUS / 2.0f; dust->Y = LaraItem->pos.yPos + rand() % UNDERWATER_DUST_PARTICLES_RADIUS - UNDERWATER_DUST_PARTICLES_RADIUS / 2.0f; dust->Z = LaraItem->pos.zPos + rand() % UNDERWATER_DUST_PARTICLES_RADIUS - UNDERWATER_DUST_PARTICLES_RADIUS / 2.0f; // Check if water room short roomNumber = Camera.pos.roomNumber; FLOOR_INFO* floor = GetFloor(dust->X, dust->Y, dust->Z, &roomNumber); if (!isRoomUnderwater(roomNumber)) continue; if (!isInRoom(dust->X, dust->Y, dust->Z, roomNumber)) { dust->Reset = true; continue; } dust->Life = 0; dust->Reset = false; } dust->Life++; byte color = (dust->Life > 16 ? 32 - dust->Life : dust->Life) * 4; AddSpriteBillboard(m_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_UNDERWATERDUST], Vector3(dust->X, dust->Y, dust->Z),Vector4( color/255.0f, color/255.0f, color/255.0f,1.0f), 0.0f, 1.0f, 12, 12, BLENDMODE_ALPHABLEND); if (dust->Life >= 32) dust->Reset = true; } m_firstUnderwaterDustParticles = false; return; } bool Renderer11::isRoomUnderwater(short roomNumber) { return (m_rooms[roomNumber]->Room->flags & ENV_FLAG_WATER); } bool Renderer11::isInRoom(int x, int y, int z, short roomNumber) { RendererRoom* room = m_rooms[roomNumber]; ROOM_INFO* r = room->Room; return (x >= r->x && x <= r->x + r->xSize * 1024.0f && y >= r->maxceiling && y <= r->minfloor && z >= r->z && z <= r->z + r->ySize * 1024.0f); } vector* Renderer11::GetAdapters() { return &m_adapters; } int Renderer11::DrawPickup(short objectNum) { drawObjectOn2DPosition(700 + PickupX, 450, objectNum, 0, m_pickupRotation, 0); // TODO: + PickupY m_pickupRotation += 45 * 360 / 30; return 0; } bool Renderer11::drawObjectOn2DPosition(short x, short y, short objectNum, short rotX, short rotY, short rotZ) { Matrix translation; Matrix rotation; Matrix world; Matrix view; Matrix projection; Matrix scale; UINT stride = sizeof(RendererVertex); UINT offset = 0; x *= (ScreenWidth / 800.0f); y *= (ScreenHeight / 600.0f); view = Matrix::CreateLookAt(Vector3(0.0f, 0.0f, 2048.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, -1.0f, 0.0f)); projection = Matrix::CreateOrthographic(ScreenWidth, ScreenHeight, -1024.0f, 1024.0f); OBJECT_INFO* obj = &Objects[objectNum]; RendererObject* moveableObj = m_moveableObjects[objectNum]; if (obj->animIndex != -1) { updateAnimation(NULL, moveableObj, &Anims[obj->animIndex].framePtr, 0, 0, 0xFFFFFFFF); } Vector3 pos = m_viewportToolkit->Unproject(Vector3(x, y, 1), projection, view, Matrix::Identity); // Clear just the Z-buffer so we can start drawing on top of the scene m_context->ClearDepthStencilView(m_currentRenderTarget->DepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); // Set vertex buffer m_context->IASetVertexBuffers(0, 1, &m_moveablesVertexBuffer->Buffer, &stride, &offset); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); m_context->IASetIndexBuffer(m_moveablesIndexBuffer->Buffer, DXGI_FORMAT_R32_UINT, 0); // Set shaders m_context->VSSetShader(m_vsInventory, NULL, 0); m_context->PSSetShader(m_psInventory, NULL, 0); // Set texture m_context->PSSetShaderResources(0, 1, &m_textureAtlas->ShaderResourceView); ID3D11SamplerState* sampler = m_states->AnisotropicClamp(); m_context->PSSetSamplers(0, 1, &sampler); // Set matrices m_stCameraMatrices.View = view.Transpose(); m_stCameraMatrices.Projection = projection.Transpose(); updateConstantBuffer(m_cbCameraMatrices, &m_stCameraMatrices, sizeof(CCameraMatrixBuffer)); m_context->VSSetConstantBuffers(0, 1, &m_cbCameraMatrices); for (int n = 0; n < moveableObj->ObjectMeshes.size(); n++) { RendererMesh* mesh = moveableObj->ObjectMeshes[n]; // Finish the world matrix translation = Matrix::CreateTranslation(pos.x, pos.y, pos.z + 1024.0f); rotation = Matrix::CreateFromYawPitchRoll(TR_ANGLE_TO_RAD(rotY), TR_ANGLE_TO_RAD(rotX), TR_ANGLE_TO_RAD(rotZ)); scale = Matrix::CreateScale(0.5f); world = scale * rotation; world = world * translation; if (obj->animIndex != -1) m_stItem.World = (moveableObj->AnimationTransforms[n] * world).Transpose(); else m_stItem.World = (moveableObj->BindPoseTransforms[n].Transpose() * world).Transpose(); m_stItem.AmbientLight = Vector4(0.5f, 0.5f, 0.5f, 1.0f); updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbItem); m_context->PSSetConstantBuffers(1, 1, &m_cbItem); for (int m = 0; m < NUM_BUCKETS; m++) { RendererBucket* bucket = &mesh->Buckets[m]; if (bucket->NumVertices == 0) continue; if (m < 2) m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); else m_context->OMSetBlendState(m_states->Additive(), NULL, 0xFFFFFFFF); m_stMisc.AlphaTest = (m < 2); updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); } } return true; } bool Renderer11::drawShadowMap() { m_shadowLight = NULL; RendererLight* brightestLight = NULL; float brightest = 0.0f; Vector3 itemPosition = Vector3(LaraItem->pos.xPos, LaraItem->pos.yPos, LaraItem->pos.zPos); for (int k = 0; k < m_roomsToDraw.Size(); k++) { RendererRoom* room = m_roomsToDraw[k]; int numLights = room->Lights.size(); for (int j = 0; j < numLights; j++) { RendererLight* light = &room->Lights[j]; // Check only lights different from sun if (light->Type == LIGHT_TYPE_SUN) { // Sun is added without checks } else if (light->Type == LIGHT_TYPE_POINT) { Vector3 lightPosition = Vector3(light->Position.x, light->Position.y, light->Position.z); float distance = (itemPosition - lightPosition).Length(); // Collect only lights nearer than 20 sectors if (distance >= 20 * WALL_SIZE) continue; // Check the out radius if (distance > light->Out) continue; float attenuation = 1.0f - distance / light->Out; float intensity = max(0.0f, attenuation * (light->Color.x + light->Color.y + light->Color.z) / 3.0f); if (intensity >= brightest) { brightest = intensity; brightestLight = light; } } else if (light->Type == LIGHT_TYPE_SPOT) { Vector3 lightPosition = Vector3(light->Position.x, light->Position.y, light->Position.z); float distance = (itemPosition - lightPosition).Length(); // Collect only lights nearer than 20 sectors if (distance >= 20 * WALL_SIZE) continue; // Check the range if (distance > light->Range) continue; // If Lara, try to collect shadow casting light float attenuation = 1.0f - distance / light->Range; float intensity = max(0.0f, attenuation * (light->Color.x + light->Color.y + light->Color.z) / 3.0f); if (intensity >= brightest) { brightest = intensity; brightestLight = light; } } else { // Invalid light type continue; } } } m_shadowLight = brightestLight; if (m_shadowLight == NULL) return true; // Reset GPU state m_context->OMSetBlendState(m_states->Opaque(), NULL, 0xFFFFFFFF); m_context->RSSetState(m_states->CullCounterClockwise()); m_context->OMSetDepthStencilState(m_states->DepthDefault(), 0); // Bind and clear render target m_context->ClearRenderTargetView(m_shadowMap->RenderTargetView, Colors::White); m_context->ClearDepthStencilView(m_shadowMap->DepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0); m_context->OMSetRenderTargets(1, &m_shadowMap->RenderTargetView, m_shadowMap->DepthStencilView); m_context->RSSetViewports(1, &m_shadowMapViewport); //drawLara(false, true); Vector3 lightPos = Vector3(m_shadowLight->Position.x, m_shadowLight->Position.y, m_shadowLight->Position.z); Vector3 itemPos = Vector3(LaraItem->pos.xPos, LaraItem->pos.yPos, LaraItem->pos.zPos); if (lightPos == itemPos) return true; UINT stride = sizeof(RendererVertex); UINT offset = 0; // Set shaders m_context->VSSetShader(m_vsShadowMap, NULL, 0); m_context->PSSetShader(m_psShadowMap, NULL, 0); m_context->IASetVertexBuffers(0, 1, &m_moveablesVertexBuffer->Buffer, &stride, &offset); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout); m_context->IASetIndexBuffer(m_moveablesIndexBuffer->Buffer, DXGI_FORMAT_R32_UINT, 0); // Set texture m_context->PSSetShaderResources(0, 1, &m_textureAtlas->ShaderResourceView); ID3D11SamplerState* sampler = m_states->AnisotropicClamp(); m_context->PSSetSamplers(0, 1, &sampler); // Set camera matrices Matrix view = Matrix::CreateLookAt(lightPos, itemPos, Vector3(0.0f, -1.0f, 0.0f)); Matrix projection = Matrix::CreatePerspectiveFieldOfView(90.0f * RADIAN, 1.0f, 64.0f, (m_shadowLight->Type == LIGHT_TYPE_POINT ? m_shadowLight->Out : m_shadowLight->Range) * 1.2f); m_stCameraMatrices.View = view.Transpose(); m_stCameraMatrices.Projection = projection.Transpose(); updateConstantBuffer(m_cbCameraMatrices, &m_stCameraMatrices, sizeof(CCameraMatrixBuffer)); m_context->VSSetConstantBuffers(0, 1, &m_cbCameraMatrices); m_stShadowMap.LightViewProjection = (view * projection).Transpose(); m_stMisc.AlphaTest = true; updateConstantBuffer(m_cbMisc, &m_stMisc, sizeof(CMiscBuffer)); m_context->PSSetConstantBuffers(3, 1, &m_cbMisc); RendererObject* laraObj = m_moveableObjects[ID_LARA]; RendererObject* laraSkin = m_moveableObjects[ID_LARA_SKIN]; RendererRoom* room = m_rooms[LaraItem->roomNumber]; m_stItem.World = m_LaraWorldMatrix.Transpose(); m_stItem.Position = Vector4(LaraItem->pos.xPos, LaraItem->pos.yPos, LaraItem->pos.zPos, 1.0f); m_stItem.AmbientLight = room->AmbientLight; memcpy(m_stItem.BonesMatrices, laraObj->AnimationTransforms.data(), sizeof(Matrix) * 32); updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); m_context->VSSetConstantBuffers(1, 1, &m_cbItem); m_context->PSSetConstantBuffers(1, 1, &m_cbItem); for (int k = 0; k < laraSkin->ObjectMeshes.size(); k++) { RendererMesh* mesh = m_meshPointersToMesh[reinterpret_cast(Lara.meshPtrs[k])]; for (int j = 0; j < 2; j++) { RendererBucket* bucket = &mesh->Buckets[j]; if (bucket->Vertices.size() == 0) continue; if (j == RENDERER_BUCKET_SOLID_DS || j == RENDERER_BUCKET_TRANSPARENT_DS) m_context->RSSetState(m_states->CullNone()); else m_context->RSSetState(m_states->CullCounterClockwise()); // Draw vertices m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } } if (m_moveableObjects[ID_LARA_SKIN_JOINTS] != NULL) { RendererObject* laraSkinJoints = m_moveableObjects[ID_LARA_SKIN_JOINTS]; for (int k = 0; k < laraSkinJoints->ObjectMeshes.size(); k++) { RendererMesh* mesh = laraSkinJoints->ObjectMeshes[k]; for (int j = 0; j < 2; j++) { RendererBucket* bucket = &mesh->Buckets[j]; if (bucket->Vertices.size() == 0) continue; // Draw vertices m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } } } for (int k = 0; k < laraSkin->ObjectMeshes.size(); k++) { RendererMesh* mesh = laraSkin->ObjectMeshes[k]; for (int j = 0; j < NUM_BUCKETS; j++) { RendererBucket* bucket = &mesh->Buckets[j]; if (bucket->Vertices.size() == 0) continue; // Draw vertices m_context->DrawIndexed(bucket->NumIndices, bucket->StartIndex, 0); m_numDrawCalls++; } } // Hairs are pre-transformed Matrix matrices[8] = { Matrix::Identity, Matrix::Identity, Matrix::Identity, Matrix::Identity, Matrix::Identity, Matrix::Identity, Matrix::Identity, Matrix::Identity }; memcpy(m_stItem.BonesMatrices, matrices, sizeof(Matrix) * 8); m_stItem.World = Matrix::Identity; updateConstantBuffer(m_cbItem, &m_stItem, sizeof(CItemBuffer)); if (m_moveableObjects[ID_LARA_HAIR] != NULL) { m_primitiveBatch->Begin(); m_primitiveBatch->DrawIndexed(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, (const unsigned short*)m_hairIndices.data(), m_numHairIndices, m_hairVertices.data(), m_numHairVertices); m_primitiveBatch->End(); } return true; } bool Renderer11::DoTitleImage() { Texture2D* texture = Texture2D::LoadFromFile(m_device, (char*)g_GameFlow->Intro); if (!texture) return false; float currentFade = 0; while (currentFade <= 1.0f) { drawFullScreenImage(texture->ShaderResourceView, currentFade); SyncRenderer(); currentFade += FADE_FACTOR; } for (int i = 0; i < 30 * 1.5f; i++) { drawFullScreenImage(texture->ShaderResourceView, 1.0f); SyncRenderer(); } currentFade = 1.0f; while (currentFade >= 0.0f) { drawFullScreenImage(texture->ShaderResourceView, currentFade); SyncRenderer(); currentFade -= FADE_FACTOR; } delete texture; return true; }