void RenderingDevice::SetMaterial(const Material &material) { SetRasterizerState(material.GetRasterizerState()); SetBlendState(material.GetBlendState()); SetDepthStencilState(material.GetDepthStencilState()); for (size_t i = 0; i < material.GetSamplers().size(); ++i) { auto& sampler = material.GetSamplers()[i]; if (sampler.GetTexture()) { D3DLOG(mDevice->SetTexture(i, sampler.GetTexture()->GetDeviceTexture())); } else { D3DLOG(mDevice->SetTexture(i, nullptr)); } SetSamplerState(i, sampler.GetState()); } // Free up the texture bindings of the samplers currently being used for (size_t i = material.GetSamplers().size(); i < mUsedSamplers; ++i) { D3DLOG(mDevice->SetTexture(i, nullptr)); } mUsedSamplers = material.GetSamplers().size(); material.GetVertexShader()->Bind(); material.GetPixelShader()->Bind(); }
static bool binkRenderFrame(BinkMovie* movie, IDirect3DTexture9* texture) { if (!binkFuncs.BinkWait(movie)) { binkFuncs.BinkDoFrame(movie); D3DLOCKED_RECT locked; HRESULT result; result = D3DLOG(texture->LockRect(0, &locked, nullptr, D3DLOCK_DISCARD)); if (result != D3D_OK) { logger->error("Unable to lock texture for movie frame!"); return false; } binkFuncs.BinkCopyToBuffer( movie, locked.pBits, locked.Pitch, movie->height, 0, 0, 0xF0000000 | 3); D3DLOG(texture->UnlockRect(0)); if (movie->currentFrame >= movie->frameCount) return false; binkFuncs.BinkNextFrame(movie); } return true; }
RenderingDevice::RenderingDevice(IDirect3DDevice9Ex *device, int renderWidth, int renderHeight) : mDevice(device), mRenderWidth(renderWidth), mRenderHeight(renderHeight), mShaders(*this), mTextures(*this, 128 * 1024 * 1024) { Expects(!renderingDevice); renderingDevice = this; mCamera.SetScreenWidth((float)renderWidth, (float)renderHeight); // TODO: color bullshit is not yet done (tig_d3d_init_handleformat et al) // Get the device caps for real this time. ReadCaps(); // Get the currently attached backbuffer if (D3DLOG(mDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer)) != D3D_OK) { throw TempleException("Unable to retrieve the back buffer"); } if (D3DLOG(mDevice->GetDepthStencilSurface(&mBackBufferDepth)) != D3D_OK) { throw TempleException("Unable to retrieve depth/stencil surface from device"); } memset(&mBackBufferDesc, 0, sizeof(mBackBufferDesc)); if (D3DLOG(mBackBuffer->GetDesc(&mBackBufferDesc)) != D3D_OK) { throw TempleException("Unable to retrieve back buffer description"); } // Create surfaces for the scene D3DLOG(mDevice->CreateRenderTarget( mRenderWidth, mRenderHeight, mBackBufferDesc.Format, D3DMULTISAMPLE_16_SAMPLES, 0, FALSE, &mSceneSurface, nullptr)); D3DLOG(mDevice->CreateDepthStencilSurface( mRenderWidth, mRenderHeight, D3DFMT_D16, D3DMULTISAMPLE_16_SAMPLES, 0, TRUE, &mSceneDepthSurface, nullptr)); for (auto &listener : mResourcesListeners) { listener->CreateResources(*this); } mResourcesCreated = true; }
void Graphics::CreateResources() { if (!renderStates) { renderStates = CreateLegacyRenderStates(*this); } renderStates->Reset(); // Get the currently attached backbuffer if (D3DLOG(mDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer)) != D3D_OK) { throw TempleException("Unable to retrieve the back buffer"); } if (D3DLOG(mDevice->GetDepthStencilSurface(&mBackBufferDepth)) != D3D_OK) { throw TempleException("Unable to retrieve depth/stencil surface from device"); } memset(&mBackBufferDesc, 0, sizeof(mBackBufferDesc)); if (D3DLOG(mBackBuffer->GetDesc(&mBackBufferDesc)) != D3D_OK) { throw TempleException("Unable to retrieve back buffer description"); } // Create surfaces for the scene D3DLOG(mDevice->CreateRenderTarget( config.renderWidth, config.renderHeight, mBackBufferDesc.Format, mBackBufferDesc.MultiSampleType, mBackBufferDesc.MultiSampleQuality, FALSE, &mSceneSurface, nullptr)); D3DLOG(mDevice->CreateDepthStencilSurface( config.renderWidth, config.renderHeight, D3DFMT_D16, mBackBufferDesc.MultiSampleType, mBackBufferDesc.MultiSampleQuality, TRUE, &mSceneDepthSurface, nullptr)); for (auto listener : mResourcesListeners) { listener->CreateResources(*this); } mResourcesCreated = true; // After a reset, the D3D cursor is hidden mouseFuncs.RefreshCursor(); }
VertexBufferPtr RenderingDevice::CreateVertexBufferRaw(gsl::span<const uint8_t> data) { CComPtr<IDirect3DVertexBuffer9> result; D3DLOG(mDevice->CreateVertexBuffer(data.size(), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &result, nullptr)); void* dataOut; D3DLOG(result->Lock(0, data.size(), &dataOut, D3DLOCK_DISCARD)); memcpy(dataOut, &data[0], data.size()); D3DLOG(result->Unlock()); return std::make_shared<VertexBuffer>(result, data.size()); }
void RenderingDevice::SetRenderSize(int w, int h) { mRenderWidth = w; mRenderHeight = h; mCamera.SetScreenWidth((float) mRenderWidth, (float) mRenderHeight); auto widthFactor = GetScreenWidthF() / (float)mRenderWidth; auto heightFactor = GetScreenHeightF() / (float)mRenderHeight; mSceneScale = std::min<float>(widthFactor, heightFactor); // Calculate the rectangle on the back buffer where the scene will // be stretched to auto drawWidth = mSceneScale * mRenderWidth; auto drawHeight = mSceneScale * mRenderHeight; auto drawX = (GetScreenWidthF() - drawWidth) / 2; auto drawY = (GetScreenHeightF() - drawHeight) / 2; mSceneRect = XMFLOAT4(drawX, drawY, drawWidth, drawHeight); mSceneSurface.Release(); mSceneDepthSurface.Release(); auto aaType = D3DMULTISAMPLE_NONE; if (mAntiAliasing && !mSupportedAaSamples.empty()) { aaType = mSupportedAaSamples.back(); } // Create surfaces for the scene D3DLOG(mDevice->CreateRenderTarget( mRenderWidth, mRenderHeight, mBackBufferDesc.Format, aaType, 0, FALSE, &mSceneSurface, nullptr)); D3DLOG(mDevice->CreateDepthStencilSurface( mRenderWidth, mRenderHeight, D3DFMT_D16, aaType, 0, TRUE, &mSceneDepthSurface, nullptr)); }
void AasRenderer::RenderGeometryShadow(gfx::AnimatedModel * model, const gfx::AnimatedModelParams & params, const gfx::Light3d & globalLight, float alpha) { // Find or create render caching data for the submesh auto &renderData = mRenderDataCache[model->GetHandle()]; if (!renderData) { renderData = std::make_unique<AasRenderData>(); } mDevice.SetMaterial(mGeometryShadowMaterial); auto d3d = mDevice.GetDevice(); d3d->SetVertexShaderConstantF(0, &mDevice.GetCamera().GetViewProj()._11, 4); d3d->SetVertexShaderConstantF(4, &globalLight.dir.x, 1); XMFLOAT4 floats{ params.offsetZ, 0, 0, 0 }; d3d->SetVertexShaderConstantF(5, &floats.x, 1); floats.x = alpha; d3d->SetVertexShaderConstantF(6, &floats.x, 1); auto materialIds(model->GetSubmeshes()); for (size_t i = 0; i < materialIds.size(); ++i) { auto submesh(model->GetSubmesh(params, i)); auto &submeshData = GetSubmeshData(*renderData, i, *submesh); submeshData.binding.Bind(); d3d->SetIndices(submeshData.idxBuffer->GetBuffer()); D3DLOG(d3d->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, submesh->GetVertexCount(), 0, submesh->GetPrimitiveCount())); } }
IndexBufferPtr RenderingDevice::CreateIndexBuffer(gsl::span<const uint16_t> data) { CComPtr<IDirect3DIndexBuffer9> result; D3DLOG(mDevice->CreateIndexBuffer(data.size_bytes(), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &result, nullptr)); void* dataOut; D3DLOG(result->Lock(0, data.size(), &dataOut, D3DLOCK_DISCARD)); memcpy(dataOut, &data[0], data.size() * sizeof(uint16_t)); D3DLOG(result->Unlock()); return std::make_shared<IndexBuffer>(result, data.size()); }
IndexBufferPtr RenderingDevice::CreateEmptyIndexBuffer(size_t count) { CComPtr<IDirect3DIndexBuffer9> buffer; auto length = sizeof(uint16_t) * count; if (D3DLOG(mDevice->CreateIndexBuffer(length, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &buffer, nullptr)) != D3D_OK) { throw TempleException("Unable to create index buffer."); } return std::make_shared<IndexBuffer>(buffer, count); }
bool RenderingDevice::BeginFrame() { if (mBeginSceneDepth++ > 0) { return true; } auto clearColor = D3DCOLOR_ARGB(0, 0, 0, 0); auto result = D3DLOG(mDevice->Clear(0, nullptr, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, clearColor, 1.0f, 0)); if (result != D3D_OK) { return false; } result = D3DLOG(mDevice->BeginScene()); if (result != D3D_OK) { return false; } mLastFrameStart = Clock::now(); return true; }
HRESULT Direct3DSurface8Adapter::GetDesc(THIS_ d3d8::D3DSURFACE_DESC* pDesc) { D3DSURFACE_DESC desc; ZeroMemory(&desc, sizeof(D3DSURFACE_DESC)); auto result = delegate->GetDesc(&desc); pDesc->Format = convert(desc.Format); pDesc->Type = (d3d8::D3DRESOURCETYPE) desc.Type; pDesc->Usage = desc.Usage; pDesc->Pool = (d3d8::D3DPOOL) desc.Pool; pDesc->MultiSampleType = (d3d8::D3DMULTISAMPLE_TYPE) desc.MultiSampleType; pDesc->Width = desc.Width; pDesc->Height = desc.Height; return D3DLOG(result); }
RenderTargetTexturePtr RenderingDevice::CreateRenderTargetTexture(D3DFORMAT format, int width, int height) { CComPtr<IDirect3DTexture9> texture; if (D3DLOG(mDevice->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, nullptr)) != D3D_OK) { return nullptr; } Size size{ width, height }; return std::make_shared<RenderTargetTexture>(texture, size); }
void AasRenderer::RenderWithoutMaterial(gfx::AnimatedModel *model, const gfx::AnimatedModelParams& params) { // Find or create render caching data for the model auto &renderData = mRenderDataCache[model->GetHandle()]; if (!renderData) { renderData = std::make_unique<AasRenderData>(); } auto materialIds(model->GetSubmeshes()); for (size_t i = 0; i < materialIds.size(); ++i) { auto submesh(model->GetSubmesh(params, i)); auto &submeshData = GetSubmeshData(*renderData, i, *submesh); submeshData.binding.Bind(); auto d3d = mDevice.GetDevice(); d3d->SetIndices(submeshData.idxBuffer->GetBuffer()); D3DLOG(d3d->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, submesh->GetVertexCount(), 0, submesh->GetPrimitiveCount())); } }
bool RenderingDevice::Present() { if (--mBeginSceneDepth > 0) { return true; } mTextures.FreeUnusedTextures(); if (D3DLOG(mDevice->EndScene()) != D3D_OK) { return false; } auto result = mDevice->Present(nullptr, nullptr, nullptr, nullptr); if (result != S_OK && result != S_PRESENT_OCCLUDED) { LogD3dError("Present()", result); if (result == D3DERR_DEVICELOST || result == S_PRESENT_MODE_CHANGED) { ResetDevice(); } return false; } return true; }
void RenderingDevice::ReadCaps() { mVideoMemory = mDevice->GetAvailableTextureMem(); if (D3DLOG(mDevice->GetDeviceCaps(&mCaps)) != D3D_OK) { throw TempleException("Unable to retrieve Direct3D device mCaps"); } /* Several sanity checks follow */ if (!(mCaps.SrcBlendCaps & D3DPBLENDCAPS_SRCALPHA)) { logger->error("source D3DPBLENDCAPS_SRCALPHA is missing"); } if (!(mCaps.SrcBlendCaps & D3DPBLENDCAPS_ONE)) { logger->error("source D3DPBLENDCAPS_ONE is missing"); } if (!(mCaps.SrcBlendCaps & D3DPBLENDCAPS_ZERO)) { logger->error("source D3DPBLENDCAPS_ZERO is missing"); } if (!(mCaps.DestBlendCaps & D3DPBLENDCAPS_INVSRCALPHA)) { logger->error("destination D3DPBLENDCAPS_INVSRCALPHA is missing"); } if (!(mCaps.DestBlendCaps & D3DPBLENDCAPS_ONE)) { logger->error("destination D3DPBLENDCAPS_ONE is missing"); } if (!(mCaps.DestBlendCaps & D3DPBLENDCAPS_ZERO)) { logger->error("destination D3DPBLENDCAPS_ZERO is missing"); } if (mCaps.MaxSimultaneousTextures < 4) { logger->error("less than 4 active textures possible: {}", mCaps.MaxSimultaneousTextures); } if (mCaps.MaxTextureBlendStages < 4) { logger->error("less than 4 texture blend stages possible: {}", mCaps.MaxTextureBlendStages); } if (!(mCaps.TextureOpCaps & D3DTOP_DISABLE)) { logger->error("texture op D3DTOP_DISABLE is missing"); } if (!(mCaps.TextureOpCaps & D3DTOP_SELECTARG1)) { logger->error("texture op D3DTOP_SELECTARG1 is missing"); } if (!(mCaps.TextureOpCaps & D3DTOP_SELECTARG2)) { logger->error("texture op D3DTOP_SELECTARG2 is missing"); } if (!(mCaps.TextureOpCaps & D3DTOP_BLENDTEXTUREALPHA)) { logger->error("texture op D3DTOP_BLENDTEXTUREALPHA is missing"); } if (!(mCaps.TextureOpCaps & D3DTOP_BLENDCURRENTALPHA)) { logger->error("texture op D3DTOP_BLENDCURRENTALPHA is missing"); } if (!(mCaps.TextureOpCaps & D3DTOP_MODULATE)) { logger->error("texture op D3DTOP_MODULATE is missing"); } if (!(mCaps.TextureOpCaps & D3DTOP_ADD)) { logger->error("texture op D3DTOP_ADD is missing"); } if (!(mCaps.TextureOpCaps & D3DTOP_MODULATEALPHA_ADDCOLOR)) { logger->error("texture op D3DTOP_MODULATEALPHA_ADDCOLOR is missing"); } if (mCaps.MaxTextureWidth < minTexWidth || mCaps.MaxTextureHeight < minTexHeight) { auto msg = fmt::format("minimum texture resolution of {}x{} is not supported. Supported: {}x{}", minTexWidth, minTexHeight, mCaps.MaxTextureWidth, mCaps.MaxTextureHeight); throw TempleException(msg); } if ((mCaps.TextureCaps & D3DPTEXTURECAPS_POW2) != 0) { logger->error("Textures must be power of two"); } if ((mCaps.TextureCaps & D3DPTEXTURECAPS_SQUAREONLY) != 0) { logger->error("Textures must be square"); } }
HRESULT Direct3DSurface8Adapter::UnlockRect(THIS) { return D3DLOG(delegate->UnlockRect()); }
void Graphics::InitializeDirect3d() { HRESULT d3dresult; /* Set some global flags. */ if (config.useDirect3d9Ex) { logger->info("Using Direct3D9Ex mode"); d3dresult = D3DLOG(Direct3DCreate9Ex(D3D_SDK_VERSION, &mDirect3d9)); if (d3dresult != D3D_OK) { throw TempleException("Unable to create Direct3D9 interface."); } } else { logger->info("Using standard Direct3D9 mode"); mDirect3d9 = static_cast<IDirect3D9Ex*>(Direct3DCreate9(D3D_SDK_VERSION)); if (!mDirect3d9) { throw TempleException("Unable to create Direct3D9 interface."); } } /** START OF OLD WINDOWED INIT */ // At this point we only do a GetDisplayMode to check the resolution. We could also do this elsewhere D3DDISPLAYMODE displayMode; d3dresult = D3DLOG(mDirect3d9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)); if (d3dresult != D3D_OK) { throw TempleException("Unable to query display mode for primary adapter."); } // We need at least 1024x768 if (displayMode.Width < 1024 || displayMode.Height < 768) { throw TempleException("You need at least a display resolution of 1024x768."); } auto presentParams = CreatePresentParams(); // presentParams.MultiSampleType = D3DMULTISAMPLE_4_SAMPLES; // presentParams.MultiSampleQuality = 0; // Nvidia drivers seriously barf on D3d9ex if we use software vertex processing here, as ToEE specifies. // I think we are safe with hardware vertex processing, since HW T&L has been standard for more than 10 years. if (config.useDirect3d9Ex) { logger->info("Creating Direct3D9Ex device."); d3dresult = D3DLOG(mDirect3d9->CreateDeviceEx( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mMainWindow.GetHwnd(), D3DCREATE_HARDWARE_VERTEXPROCESSING, &presentParams, nullptr, &mDevice)); } else { logger->info("Creating Direct3D9 device."); d3dresult = D3DLOG(mDirect3d9->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mMainWindow.GetHwnd(), D3DCREATE_HARDWARE_VERTEXPROCESSING, &presentParams, reinterpret_cast<IDirect3DDevice9**>(&mDevice))); } if (d3dresult != D3D_OK) { throw TempleException("Unable to create Direct3D9 device!"); } // TODO: color bullshit is not yet done (tig_d3d_init_handleformat et al) // Get the device caps for real this time. ReadCaps(); SetDefaultRenderStates(); CreateResources(); }
void AasRenderer::Render(gfx::AnimatedModel *model, const gfx::AnimatedModelParams& params, gsl::span<Light3d> lights, const MdfRenderOverrides *materialOverrides) { // Find or create render caching data for the model auto &renderData = mRenderDataCache[model->GetHandle()]; if (!renderData) { renderData = std::make_unique<AasRenderData>(); } auto materialIds(model->GetSubmeshes()); for (size_t i = 0; i < materialIds.size(); ++i) { auto materialId = materialIds[i]; auto submesh(model->GetSubmesh(params, i)); // Remove special material marker in the upper byte and only // use the actual shader registration id materialId &= 0x00FFFFFF; // Usually this should not happen, since it means there's // an unbound replacement material if (materialId == 0) { continue; } // if material was not found if (materialId == 0x00FFFFFF) { continue; } auto material = mMdfFactory.GetById(materialId); if (!material) { logger->error("Legacy shader with id {} wasn't found.", materialId); continue; } material->Bind(mDevice, lights, materialOverrides); // Do we have to recalculate the normals? if (material->GetSpec()->recalculateNormals) { RecalcNormals( submesh->GetVertexCount(), submesh->GetPositions().data(), submesh->GetNormals().data(), submesh->GetPrimitiveCount(), submesh->GetIndices().data() ); } auto &submeshData = GetSubmeshData(*renderData, i, *submesh); submeshData.binding.Bind(); auto d3d = mDevice.GetDevice(); d3d->SetIndices(submeshData.idxBuffer->GetBuffer()); D3DLOG(d3d->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, submesh->GetVertexCount(), 0, submesh->GetPrimitiveCount())); } }
void Graphics::TakeScaledScreenshot(const std::string& filename, int width, int height) { logger->debug("Creating screenshot with size {}x{} in {}", width, height, filename); auto device = mDevice; auto sceneSurface = mSceneSurface; D3DSURFACE_DESC desc; sceneSurface->GetDesc(&desc); // Support taking unscaled screenshots auto stretch = true; if (width == 0 || height == 0) { width = desc.Width; height = desc.Height; stretch = false; } // Create system memory surface to copy the screenshot to CComPtr<IDirect3DSurface9> sysMemSurface; if (D3DLOG(device->CreateOffscreenPlainSurface(width, height, desc.Format, D3DPOOL_SYSTEMMEM, &sysMemSurface, nullptr)) != D3D_OK) { logger->error("Unable to create offscreen surface for copying the screenshot"); return; } if (stretch) { CComPtr<IDirect3DSurface9> stretchedScene; if (D3DLOG(device->CreateRenderTarget(width, height, desc.Format, desc.MultiSampleType, desc.MultiSampleQuality, false, &stretchedScene, NULL)) != D3D_OK) { return; } if (D3DLOG(device->StretchRect(sceneSurface, nullptr, stretchedScene, nullptr, D3DTEXF_LINEAR)) != D3D_OK) { logger->error("Unable to copy front buffer to target surface for screenshot"); return; } if (D3DLOG(device->GetRenderTargetData(stretchedScene, sysMemSurface))) { logger->error("Unable to copy stretched render target to system memory."); return; } } else { if (D3DLOG(device->GetRenderTargetData(sceneSurface, sysMemSurface))) { logger->error("Unable to copy scene render target to system memory."); return; } } /* Get access to the pixel data for the surface and encode it to a JPEG. */ D3DLOCKED_RECT locked; if (D3DLOG(sysMemSurface->LockRect(&locked, nullptr, 0))) { logger->error("Unable to lock screenshot surface."); return; } // Quality is between 1 and 100 auto quality = std::min(100, std::max(1, config.screenshotQuality)); auto jpegData(gfx::EncodeJpeg(reinterpret_cast<uint8_t*>(locked.pBits), gfx::JpegPixelFormat::BGRX, width, height, quality, locked.Pitch)); if (D3DLOG(sysMemSurface->UnlockRect())) { logger->error("Unable to unlock screenshot surface."); return; } // We have to write using tio or else it goes god knows where auto fh = tio_fopen(filename.c_str(), "w+b"); if (tio_fwrite(jpegData.data(), 1, jpegData.size(), fh) != jpegData.size()) { logger->error("Unable to write screenshot to disk due to an IO error."); tio_fclose(fh); tio_remove(filename.c_str()); } else { tio_fclose(fh); } }
RenderingDevice::RenderingDevice(HWND windowHandle, int renderWidth, int renderHeight, bool antiAliasing) : mWindowHandle(windowHandle), mRenderWidth(renderWidth), mRenderHeight(renderHeight), mShaders(*this), mTextures(*this, 128 * 1024 * 1024), mAntiAliasing(antiAliasing) { Expects(!renderingDevice); renderingDevice = this; HRESULT status; status = D3DLOG(Direct3DCreate9Ex(D3D_SDK_VERSION, &mDirect3d9)); if (status != D3D_OK) { throw TempleException("Unable to create Direct3D9Ex interface."); } // At this point we only do a GetDisplayMode to check the resolution. We could also do this elsewhere D3DDISPLAYMODE displayMode; status = D3DLOG(mDirect3d9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)); if (status != D3D_OK) { throw TempleException("Unable to query display mode for primary adapter."); } D3DMULTISAMPLE_TYPE aaTypes[] = { D3DMULTISAMPLE_2_SAMPLES, D3DMULTISAMPLE_3_SAMPLES, D3DMULTISAMPLE_4_SAMPLES, D3DMULTISAMPLE_5_SAMPLES, D3DMULTISAMPLE_6_SAMPLES, D3DMULTISAMPLE_7_SAMPLES, D3DMULTISAMPLE_8_SAMPLES, D3DMULTISAMPLE_9_SAMPLES, D3DMULTISAMPLE_10_SAMPLES, D3DMULTISAMPLE_11_SAMPLES, D3DMULTISAMPLE_12_SAMPLES, D3DMULTISAMPLE_13_SAMPLES, D3DMULTISAMPLE_14_SAMPLES, D3DMULTISAMPLE_15_SAMPLES, D3DMULTISAMPLE_16_SAMPLES }; for (auto type : aaTypes) { status = mDirect3d9->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, TRUE, type, nullptr); if (status == D3D_OK) { logger->trace("AA method {} is available", type); mSupportedAaSamples.push_back(type); } } // We need at least 1024x768 if (displayMode.Width < 1024 || displayMode.Height < 768) { throw TempleException("You need at least a display resolution of 1024x768."); } CreatePresentParams(); status = D3DLOG(mDirect3d9->CreateDeviceEx( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mWindowHandle, D3DCREATE_HARDWARE_VERTEXPROCESSING, &mPresentParams, nullptr, &mDevice)); if (status != D3D_OK) { throw TempleException("Unable to create Direct3D9 device!"); } // TODO: color bullshit is not yet done (tig_d3d_init_handleformat et al) // Get the device caps for real this time. ReadCaps(); // Get the currently attached backbuffer if (D3DLOG(mDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer)) != D3D_OK) { throw TempleException("Unable to retrieve the back buffer"); } if (D3DLOG(mDevice->GetDepthStencilSurface(&mBackBufferDepth)) != D3D_OK) { throw TempleException("Unable to retrieve depth/stencil surface from device"); } memset(&mBackBufferDesc, 0, sizeof(mBackBufferDesc)); if (D3DLOG(mBackBuffer->GetDesc(&mBackBufferDesc)) != D3D_OK) { throw TempleException("Unable to retrieve back buffer description"); } SetRenderSize(renderWidth, renderHeight); for (auto &listener : mResourcesListeners) { listener->CreateResources(*this); } mResourcesCreated = true; }
HRESULT Direct3DSurface8Adapter::LockRect(THIS_ d3d8::D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) { return D3DLOG(delegate->LockRect((D3DLOCKED_RECT*)pLockedRect, pRect, Flags)); }
void Graphics::SetDefaultRenderStates() { /* SET DEFAULT RENDER STATES */ mDevice->SetRenderState(D3DRS_ZENABLE, TRUE); mDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); mDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); mDevice->SetRenderState(D3DRS_LIGHTING, TRUE); D3DLIGHT9 light; memset(&light, 0, sizeof(light)); light.Type = D3DLIGHT_DIRECTIONAL; light.Diffuse.r = 1.5f; light.Diffuse.g = 1.5f; light.Diffuse.b = 1.5f; light.Specular.r = 1.0f; light.Specular.g = 1.0f; light.Specular.b = 1.0f; light.Direction.x = -0.70700002f; light.Direction.y = -0.866f; light.Attenuation0 = 1; light.Range = 800; mDevice->SetLight(0, &light); mDevice->SetRenderState(D3DRS_AMBIENT, 0); mDevice->SetRenderState(D3DRS_SPECULARENABLE, 0); mDevice->SetRenderState(D3DRS_LOCALVIEWER, 0); D3DMATERIAL9 material; memset(&material, 0, sizeof(material)); material.Diffuse.r = 1.0f; material.Diffuse.g = 1.0f; material.Diffuse.b = 1.0f; material.Diffuse.a = 1.0f; material.Ambient.r = 1.0f; material.Ambient.g = 1.0f; material.Ambient.b = 1.0f; material.Ambient.a = 1.0f; material.Power = 50.0f; mDevice->SetMaterial(&material); D3DLOG(mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE)); D3DLOG(mDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA)); D3DLOG(mDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA)); D3DLOG(mDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1)); D3DLOG(mDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTOP_SELECTARG1)); D3DLOG(mDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTOP_DISABLE)); for (DWORD i = 0; i < 4; ++i) { D3DLOG(mDevice->SetSamplerState(i, D3DSAMP_MINFILTER, 1)); D3DLOG(mDevice->SetSamplerState(i, D3DSAMP_MAGFILTER, 2)); D3DLOG(mDevice->SetSamplerState(i, D3DSAMP_MIPFILTER, 1)); D3DLOG(mDevice->SetSamplerState(i, D3DSAMP_MIPMAPLODBIAS, 0)); D3DLOG(mDevice->SetSamplerState(i, D3DSAMP_MAXMIPLEVEL, 01)); D3DLOG(mDevice->SetSamplerState(i, D3DSAMP_MINFILTER, 1)); D3DLOG(mDevice->SetSamplerState(i, D3DSAMP_MINFILTER, 1)); D3DLOG(mDevice->SetSamplerState(i, D3DSAMP_ADDRESSU, 3)); D3DLOG(mDevice->SetSamplerState(i, D3DSAMP_ADDRESSV, 3)); D3DLOG(mDevice->SetTextureStageState(i, D3DTSS_TEXTURETRANSFORMFLAGS, 0)); D3DLOG(mDevice->SetTextureStageState(i, D3DTSS_TEXCOORDINDEX, 0)); } D3DXMATRIX identity; D3DXMatrixIdentity(&identity); D3DLOG(mDevice->SetTransform(D3DTS_TEXTURE0, &identity)); mDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); mDevice->SetRenderState(D3DRS_ALPHAREF, 1); mDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); }
int __cdecl HookedPlayMovieSlide(const char* imageFile, const char* soundFile, const SubtitleLine* subtitles, int flags, int soundtrackId) { logger->info("Play Movie Slide {} {} {} {}", imageFile, soundFile, flags, soundtrackId); // Load img into memory using TIO unique_ptr<vector<uint8_t>> imgData(TioReadBinaryFile(imageFile)); if (!imgData) { logger->error("Unable to load the image file {}", imageFile); return 1; // Can't play because we cant load the file } auto device = graphics->device(); gfx::ImageFileInfo info; auto surface(gfx::LoadImageToSurface(graphics->device(), *imgData.get(), info)); movieFuncs.MovieIsPlaying = true; device->ShowCursor(FALSE); // Clear screen with black color and present immediately device->Clear(0, nullptr, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 0, 0); device->Present(nullptr, nullptr, nullptr, nullptr); SubtitleRenderer subtitleRenderer(subtitles); TigRect bbRect(0, 0, graphics->backBufferDesc().Width, graphics->backBufferDesc().Height); TigRect destRect(0, 0, info.width, info.height); destRect.FitInto(bbRect); RECT fitDestRect = destRect.ToRect(); Stopwatch sw; TigSoundStreamWrapper stream; if (soundFile) { if (!stream.Play(soundFile, TigSoundType::Voice)) { logger->error("Unable to play sound {} during slideshow.", soundFile); } else { stream.SetVolume(*tigSoundAddresses.movieVolume); } } bool keyPressed = false; while (!keyPressed && (!stream.IsValid() || stream.IsPlaying() || sw.GetElapsedMs() < 3000)) { D3DLOG(device->Clear(0, nullptr, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 0, 0)); D3DLOG(device->BeginScene()); D3DLOG(device->StretchRect(surface, NULL, graphics->backBuffer(), &fitDestRect, D3DTEXF_LINEAR)); subtitleRenderer.Render(); D3DLOG(device->EndScene()); D3DLOG(device->Present(NULL, NULL, NULL, NULL)); templeFuncs.ProcessSystemEvents(); TigMsg msg; while (!msgFuncs.Process(&msg)) { // Flags 1 seems to disable skip via keyboard. Also seems unused. if (!(flags & 1) && msg.type == TigMsgType::KEYSTATECHANGE && LOBYTE(msg.arg2) == 1) { // TODO Wait for the key to be unpressed again keyPressed = true; break; } } } movieFuncs.MovieIsPlaying = false; device->ShowCursor(TRUE); return 0; }
void RenderingDevice::TakeScaledScreenshot(const std::string &filename, int width, int height, int quality) { logger->debug("Creating screenshot with size {}x{} in {}", width, height, filename); D3DSURFACE_DESC desc; mSceneSurface->GetDesc(&desc); // Support taking unscaled screenshots auto stretch = true; if (width == 0 || height == 0) { width = desc.Width; height = desc.Height; stretch = false; } // Create system memory surface to copy the screenshot to CComPtr<IDirect3DSurface9> sysMemSurface; if (D3DLOG(mDevice->CreateOffscreenPlainSurface(width, height, desc.Format, D3DPOOL_SYSTEMMEM, &sysMemSurface, nullptr)) != D3D_OK) { logger->error("Unable to create offscreen surface for copying the screenshot"); return; } if (stretch || desc.MultiSampleType != D3DMULTISAMPLE_NONE) { /* Create the secondary render target without multi sampling. */ CComPtr<IDirect3DSurface9> stretchedScene; if (D3DLOG(mDevice->CreateRenderTarget(width, height, desc.Format, D3DMULTISAMPLE_NONE, 0, false, &stretchedScene, NULL)) != D3D_OK) { return; } if (D3DLOG(mDevice->StretchRect(mSceneSurface, nullptr, stretchedScene, nullptr, D3DTEXF_LINEAR)) != D3D_OK) { logger->error("Unable to copy front buffer to target surface for screenshot"); return; } if (D3DLOG(mDevice->GetRenderTargetData(stretchedScene, sysMemSurface))) { logger->error("Unable to copy stretched render target to system memory."); return; } } else { if (D3DLOG(mDevice->GetRenderTargetData(mSceneSurface, sysMemSurface))) { logger->error("Unable to copy scene render target to system memory."); return; } } /* Get access to the pixel data for the surface and encode it to a JPEG. */ D3DLOCKED_RECT locked; if (D3DLOG(sysMemSurface->LockRect(&locked, nullptr, 0))) { logger->error("Unable to lock screenshot surface."); return; } // Clamp quality to [1, 100] quality = std::min(100, std::max(1, quality)); auto jpegData(gfx::EncodeJpeg(reinterpret_cast<uint8_t*>(locked.pBits), gfx::JpegPixelFormat::BGRX, width, height, quality, locked.Pitch)); if (D3DLOG(sysMemSurface->UnlockRect())) { logger->error("Unable to unlock screenshot surface."); return; } // We have to write using tio or else it goes god knows where try { vfs->WriteBinaryFile(filename, jpegData); } catch (std::exception &e) { logger->error("Unable to save screenshot due to an IO error: {}", e.what()); } }
void AasRenderer::RenderShadowMapShadow(gsl::span<gfx::AnimatedModel*> models, gsl::span<const gfx::AnimatedModelParams*> modelParams, const XMFLOAT3 ¢er, float radius, float height, const XMFLOAT4 &lightDir, float alpha, bool softShadows) { Expects(models.size() == modelParams.size()); float shadowMapWorldX, shadowMapWorldWidth, shadowMapWorldZ, shadowMapWorldHeight; if (lightDir.x < 0.0) { shadowMapWorldX = center.x - 2 * radius + lightDir.x * height; shadowMapWorldWidth = 4 * radius - lightDir.x * height; } else { shadowMapWorldX = center.x - 2 * radius; shadowMapWorldWidth = lightDir.x * height + 4 * radius; } if (lightDir.z < 0.0) { shadowMapWorldZ = center.z - 2 * radius + lightDir.z * height; shadowMapWorldHeight = 4 * radius - lightDir.z * height; } else { shadowMapWorldZ = center.z - 2 * radius; shadowMapWorldHeight = lightDir.z + height + 4 * radius; } CComPtr<IDirect3DSurface9> currentTarget; D3DLOG(mDevice.GetDevice()->GetRenderTarget(0, ¤tTarget)); D3DLOG(mDevice.GetDevice()->SetRenderTarget(0, mShadowTarget->GetSurface())); CComPtr<IDirect3DSurface9> depthStencil; mDevice.GetDevice()->GetDepthStencilSurface(&depthStencil); mDevice.GetDevice()->SetDepthStencilSurface(nullptr); mDevice.SetMaterial(mShadowMapMaterial); // Set shader params XMFLOAT4 floats{ shadowMapWorldX, shadowMapWorldZ, shadowMapWorldWidth, shadowMapWorldHeight }; mDevice.GetDevice()->SetVertexShaderConstantF(0, &floats.x, 1); mDevice.GetDevice()->SetVertexShaderConstantF(1, &lightDir.x, 1); floats.x = center.y; mDevice.GetDevice()->SetVertexShaderConstantF(2, &floats.x, 1); XMCOLOR color(0, 0, 0, 0.5f); XMStoreFloat4(&floats, PackedVector::XMLoadColor(&color)); mDevice.GetDevice()->SetVertexShaderConstantF(4, &floats.x, 1); D3DLOG(mDevice.GetDevice()->Clear(0, nullptr, D3DCLEAR_TARGET, 0, 0, 0)); for (size_t i = 0; i < models.size(); ++i) { RenderWithoutMaterial(models[i], *modelParams[i]); } if (softShadows) { mDevice.SetMaterial(mGaussBlurHor); D3DLOG(mDevice.GetDevice()->SetRenderTarget(0, mShadowTargetTmp->GetSurface())); mShapeRenderer2d.DrawFullScreenQuad(); mDevice.SetMaterial(mGaussBlurVer); D3DLOG(mDevice.GetDevice()->SetRenderTarget(0, mShadowTarget->GetSurface())); mShapeRenderer2d.DrawFullScreenQuad(); } D3DLOG(mDevice.GetDevice()->SetRenderTarget(0, currentTarget)); D3DLOG(mDevice.GetDevice()->SetDepthStencilSurface(depthStencil)); auto shadowMapWorldBottom = shadowMapWorldZ + shadowMapWorldHeight; auto shadowMapWorldRight = shadowMapWorldX + shadowMapWorldWidth; std::array<gfx::ShapeVertex3d, 4> corners; corners[0].pos = { shadowMapWorldX, center.y, shadowMapWorldZ }; corners[1].pos = { shadowMapWorldX, center.y, shadowMapWorldBottom }; corners[2].pos = { shadowMapWorldRight, center.y, shadowMapWorldBottom }; corners[3].pos = { shadowMapWorldRight, center.y, shadowMapWorldZ }; corners[0].uv = { 0, 0 }; corners[1].uv = { 0, 1 }; corners[2].uv = { 1, 1 }; corners[3].uv = { 1, 0 }; mShapeRenderer3d.DrawQuad(corners, 0xFFFFFFFF, mShadowTarget); }
int HookedPlayMovieBink(const char* filename, const SubtitleLine* subtitles, int flags, uint32_t soundtrackId) { if (!binkInitialized) { return 0; } movieFuncs.MovieIsPlaying = true; binkFuncs.initialize(); uint32_t openFlags = 0; if (soundtrackId) { binkFuncs.BinkSetSoundTrack(1, &soundtrackId); openFlags |= 0x4000; // Apparently tells BinkOpen a soundtrack was used } auto movie = binkFuncs.BinkOpen(filename, openFlags); if (!movie) { logger->error("Unable to load BINK movie {} with flags {}", filename, openFlags); return 13; } // The disasm apparently goes crazy for the conversion here int binkVolume = (*tigSoundAddresses.movieVolume) * 258; binkFuncs.BinkSetVolume(movie, 0, binkVolume); auto d3dDevice = graphics->device(); d3dDevice->ShowCursor(FALSE); // Create the movie texture we write to IDirect3DTexture9* texture; if (D3DLOG(d3dDevice->CreateTexture(movie->width, movie->height, 1, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &texture, nullptr)) != D3D_OK) { logger->error("Unable to create texture for bink video"); return 0; } // Clear screen with black color and present immediately d3dDevice->Clear(0, nullptr, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 0, 0); d3dDevice->Present(nullptr, nullptr, nullptr, nullptr); processTigMessages(); MovieRect movieRect = getMovieRect(movie); // TODO UV should be manipulated for certain vignettes since they have been letterboxed in the bink file!!! // Set vertex shader MovieVertex vertices[4] = { {movieRect.left, movieRect.top, 0, 0}, {movieRect.right, movieRect.top, 1, 0}, {movieRect.right, movieRect.bottom, 1, 1}, {movieRect.left, movieRect.bottom, 0, 1} }; IDirect3DVertexBuffer9* vertexBuffer; D3DLOG(d3dDevice->CreateVertexBuffer(sizeof(vertices), 0, D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1, D3DPOOL_DEFAULT, &vertexBuffer, nullptr)); void* data; D3DLOG(vertexBuffer->Lock(0, 0, &data, 0)); memcpy(data, vertices, sizeof(vertices)); vertexBuffer->Unlock(); SubtitleRenderer subtitleRenderer(subtitles); bool keyPressed = false; while (!keyPressed && binkRenderFrame(movie, texture)) { D3DLOG(d3dDevice->Clear(0, nullptr, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 0, 0)); D3DLOG(d3dDevice->BeginScene()); renderStates->SetTexture(0, texture); renderStates->SetTextureMinFilter(0, D3DTEXF_LINEAR); renderStates->SetTextureMagFilter(0, D3DTEXF_LINEAR); renderStates->SetTextureMipFilter(0, D3DTEXF_LINEAR); renderStates->SetLighting(false); renderStates->SetZEnable(false); renderStates->SetCullMode(D3DCULL_NONE); renderStates->SetTextureTransformFlags(0, D3DTTFF_DISABLE); renderStates->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1); renderStates->SetStreamSource(0, vertexBuffer, sizeof(MovieVertex)); renderStates->SetTextureColorOp(0, D3DTOP_SELECTARG1); renderStates->SetTextureColorArg1(0, D3DTA_TEXTURE); renderStates->SetTextureAlphaOp(0, D3DTOP_SELECTARG1); renderStates->SetTextureAlphaArg1(0, D3DTA_TEXTURE); renderStates->Commit(); D3DLOG(d3dDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2)); subtitleRenderer.Render(); D3DLOG(d3dDevice->EndScene()); D3DLOG(d3dDevice->Present(NULL, NULL, NULL, NULL)); templeFuncs.ProcessSystemEvents(); TigMsg msg; while (!msgFuncs.Process(&msg)) { // Flags 1 seems to disable skip via keyboard. Also seems unused. if (!(flags & 1) && msg.type == TigMsgType::KEYSTATECHANGE && LOBYTE(msg.arg2) == 1) { // TODO Wait for the key to be unpressed again keyPressed = true; break; } } } binkFuncs.BinkClose(movie); texture->Release(); vertexBuffer->Release(); // Unclear what this did (black out screen after last frame?) /* Haven't found a single occasion where it's used. if (!(flags & 8)) { sub_101D8790(0); play_movie_present(); } */ movieFuncs.MovieIsPlaying = false; d3dDevice->ShowCursor(TRUE); return 0; }