コード例 #1
0
ファイル: device.cpp プロジェクト: pakoito/TemplePlus
	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();
	}
コード例 #2
0
ファイル: movies.cpp プロジェクト: ema29/TemplePlus
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;
}
コード例 #3
0
ファイル: device.cpp プロジェクト: pakoito/TemplePlus
	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;
	}
コード例 #4
0
ファイル: graphics.cpp プロジェクト: ema29/TemplePlus
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();
}
コード例 #5
0
ファイル: device.cpp プロジェクト: pakoito/TemplePlus
	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());
	}
コード例 #6
0
ファイル: device.cpp プロジェクト: pakoito/TemplePlus
	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));

	}
コード例 #7
0
ファイル: aasrenderer.cpp プロジェクト: pakoito/TemplePlus
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()));
	}

}
コード例 #8
0
ファイル: device.cpp プロジェクト: pakoito/TemplePlus
	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());
	}
コード例 #9
0
ファイル: device.cpp プロジェクト: pakoito/TemplePlus
	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);
	}
コード例 #10
0
ファイル: device.cpp プロジェクト: pakoito/TemplePlus
	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;
	}
コード例 #11
0
ファイル: d3d8to9_surface.cpp プロジェクト: ema29/TemplePlus
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);
}
コード例 #12
0
ファイル: device.cpp プロジェクト: pakoito/TemplePlus
	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);

	}
コード例 #13
0
ファイル: aasrenderer.cpp プロジェクト: pakoito/TemplePlus
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()));
	}
}
コード例 #14
0
ファイル: device.cpp プロジェクト: pakoito/TemplePlus
	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;
	}
コード例 #15
0
ファイル: device.cpp プロジェクト: pakoito/TemplePlus
	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");
		}
	}
コード例 #16
0
ファイル: d3d8to9_surface.cpp プロジェクト: ema29/TemplePlus
HRESULT Direct3DSurface8Adapter::UnlockRect(THIS)
{
    return D3DLOG(delegate->UnlockRect());
}
コード例 #17
0
ファイル: graphics.cpp プロジェクト: ema29/TemplePlus
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();
}
コード例 #18
0
ファイル: aasrenderer.cpp プロジェクト: pakoito/TemplePlus
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()));

	}

}
コード例 #19
0
ファイル: graphics.cpp プロジェクト: ema29/TemplePlus
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);
    }
}
コード例 #20
0
ファイル: device.cpp プロジェクト: pakoito/TemplePlus
	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;
	}
コード例 #21
0
ファイル: d3d8to9_surface.cpp プロジェクト: ema29/TemplePlus
HRESULT Direct3DSurface8Adapter::LockRect(THIS_ d3d8::D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags)
{
    return D3DLOG(delegate->LockRect((D3DLOCKED_RECT*)pLockedRect, pRect, Flags));
}
コード例 #22
0
ファイル: graphics.cpp プロジェクト: ema29/TemplePlus
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);
}
コード例 #23
0
ファイル: movies.cpp プロジェクト: ema29/TemplePlus
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;
}
コード例 #24
0
ファイル: device.cpp プロジェクト: pakoito/TemplePlus
	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());

		}
	}
コード例 #25
0
ファイル: aasrenderer.cpp プロジェクト: pakoito/TemplePlus
void AasRenderer::RenderShadowMapShadow(gsl::span<gfx::AnimatedModel*> models,
										gsl::span<const gfx::AnimatedModelParams*> modelParams,
										const XMFLOAT3 &center,
										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, &currentTarget));
	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);	

}
コード例 #26
0
ファイル: movies.cpp プロジェクト: ema29/TemplePlus
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;
}