Skarma
October 3rd, 2010, 08:15 PM
I quit working on Forge for the last week or so due to boredom -- I get distracted easily with side projects. I will get back to it soon enough. :ohdear:
While browsing through my favorite Direct3D resource website (http://www.toymaker.info/Games/html/direct3d_faq.html#pip), there was a small section about Picture-in-Picture(PiP) and a basic description of how it works. I instantly was intrigued to do something like it for Halo and sure enough in a few long days time I got something going with OpenSauce.
I was looking through the rasterizer system and saw Kornman had hooked the end of the window rendering function if DX was not defined. I am still curious to what he was planning to do with this, since there was some unfinished rendering code there. Anyway this window rendering function was what I needed to accomplish PiP. I thought it would be too hacky to just hook portions of the code that needed modified, so of course I went all the way and reversed the entire function into OS compatible C++ code and rerouted the original function to my function. This means that Halo is being rendered through code inside OS and it makes it a hell of a lot easier to tinker with.
To do PiP, the render code is run twice. The first time is normal and the second time with a modified viewport and vertex buffer(adjusted width & height). I did not really notice any performance drop with video options on max settings and my system being a laptop. The minimal choppiness you do see is normal for my specs without running OS. I was actually quite surprised, I really thought it would be a huge hit in performance.
Well here is my PiP example video, the map is an alpha version of Precipice (I show the phantom bsp errors in this):
LFOcWObEwlM
I am posting this to get ideas on things we could make this useful for. My main idea is to make a rear view mirror for the warthog. To accomplish this, I would have to update the view matrix to change the camera angle as well as not render the HUD elements. Other idea for reversing this function was to easily modify how Halo is rendered or easily add post-processing effects.
There were additions to multiple files so I don't really feel the need to post all of it. Here is just the window rendering code. If you want the full source just let me know and I'll send it right over.
bool SoftwareVertexProcessing() PTR_IMP_GET(software_vertex_processing);
IDirect3DSurface9* RenderTarget() DPTR_IMP_GET(render_target);
IDirect3DBaseTexture9* Texture() DPTR_IMP_GET(texture);
IDirect3DIndexBuffer9* IndexData() DPTR_IMP_GET(index_data);
IDirect3DVertexBuffer9* StreamData() DPTR_IMP_GET(stream_data);
bool render_pip = false;
bool adjust_pip = false;
void RenderWindow()
{
if(GET_PTR(unknown_boolean_3) == 1 && !GET_PTR(unknown_boolean_4))
{
DX9::Direct3DDevice()->SetRenderTarget(0, RenderTarget());
D3DSURFACE_DESC desc;
RenderTarget()->GetDesc(&desc);
D3DVIEWPORT9 viewport = {
0, // x
0, // y
adjust_pip ? 200 : desc.Width, // width
adjust_pip ? 150 : desc.Height, // height
0.0f, // minz
1.0f // maxz
};
DX9::Direct3DDevice()->SetViewport(&viewport);
if(DebugOptions()->wireframe == false)
DX9::Direct3DDevice()->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
DX9::Direct3DDevice()->SetTexture(0, Texture());
DX9::Direct3DDevice()->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
DX9::Direct3DDevice()->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
DX9::Direct3DDevice()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
DX9::Direct3DDevice()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
DX9::Direct3DDevice()->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
DX9::Direct3DDevice()->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
DX9::Direct3DDevice()->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED|D3DCOLORWRITEENABLE_GREEN| D3DCOLORWRITEENABLE_BLUE);
DX9::Direct3DDevice()->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
DX9::Direct3DDevice()->SetRenderState(D3DRS_ALPHATESTENABLE, false);
DX9::Direct3DDevice()->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
DX9::Direct3DDevice()->SetRenderState(D3DRS_FOGENABLE, false);
DX9::Direct3DDevice()->SetPixelShader(NULL);
DX9::Direct3DDevice()->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
DX9::Direct3DDevice()->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
DX9::Direct3DDevice()->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
DX9::Direct3DDevice()->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
DX9::Direct3DDevice()->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
DX9::Direct3DDevice()->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
if(StreamData())
{
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1)
uint32 fvf_size = D3DXGetFVFVertexSize(D3DFVF_CUSTOMVERTEX);
byte* vertex_data;
StreamData()->Lock(0, fvf_size*4, CAST_PTR(void**, &vertex_data), D3DLOCK_DISCARD);
uint32 height = adjust_pip ? 150 : GET_PTR(unknown_uint16_2) - GET_PTR(unknown_uint16_1);
uint32 width = adjust_pip ? 200 : GET_PTR(unknown_uint16_4) - GET_PTR(unknown_uint16_3);
// black
//argb_color vertex_color = {
// 255, // a
// 255, // r
// 255, // g
// 255 // b
//};
// ^^^ operator override was not working, so I did it this way instead
// it was telling me "cannot convert "Yelo::uint32" to "Yelo::uint32""
DWORD vert_color = 0xFFFFFFFF;
struct CUSTOMVERTEX {
real x, y, z;
real rhw;
uint32 color;
real tu, tv;
} vertices[4] = {
{ -0.5f, -0.5f, 0.0f, 1.0f, vert_color, 0.0f, 0.0f },
{ CAST(float, width) - 0.5f, -0.5f, 0.0f, 1.0f, vert_color, 1.0f, 0.0f },
{ CAST(float, width) - 0.5f, CAST(float, height) - 0.5f, 0.0f, 1.0f, vert_color, 1.0f, 1.0f },
{ -0.5f, CAST(float, height) - 0.5f, 0.0f, 1.0f, vert_color, 0.0f, 1.0f }
};
if(GET_PTR(unknown_uint32_1))
{
vertices[1].tu *= CAST(float, height);
vertices[2].tu *= CAST(float, height);
vertices[2].tv *= CAST(float, width);
vertices[3].tv *= CAST(float, width);
}
memcpy(vertex_data, vertices, fvf_size*4);
DX9::Direct3DDevice()->SetVertexShader(NULL);
DX9::Direct3DDevice()->SetFVF(D3DFVF_CUSTOMVERTEX);
StreamData()->Unlock();
DX9::Direct3DDevice()->SetSoftwareVertexProcessing(SoftwareVertexProcessi ng());
// Stride has to be equivalent to FVF size
DX9::Direct3DDevice()->SetStreamSource(0, StreamData(), 0, fvf_size);
DX9::Direct3DDevice()->SetIndices(IndexData());
DX9::Direct3DDevice()->DrawIndexedPrimitive(D3DPT_TRIANGLEFAN, 0, 0, 4, 0, 2);
DX9::Direct3DDevice()->SetFVF(NULL);
}
if(DebugOptions()->wireframe == true)
DX9::Direct3DDevice()->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
}
if(GameUI::HudChatGlobals()->active)
{
// Set chatbox background color
argb_color box_color = {
176, // a
32, // r
32, // g
32 // b
};
// Set chatbox rect coordinates
rectangle2d box_rect = {
0, // left
460, // top
480, // bottom
640 // right
};
Engine::DX9::RenderChatbox(box_rect, box_color);
}
bool b = true;
if(FAILED(DX9::Direct3DDevice()->SetSoftwareVertexProcessing(SoftwareVertexProcessi ng())))
{
b = false;
}
DX9::Direct3DDevice()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
DX9::Direct3DDevice()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
DX9::Direct3DDevice()->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
if(Keystone::MainWindow() && !GET_PTR(unknown_boolean_1))
{
if(FAILED(Keystone::Update(Keystone::MainWindow()) ))
{
GET_PTR(unknown_boolean_1) = true;
}
}
if(FAILED(DX9::Direct3DDevice()->SetSoftwareVertexProcessing(SoftwareVertexProcessi ng())))
{
b = false;
}
if(render_pip)
{
if(SUCCEEDED(DX9::Direct3DDevice()->EndScene()))
{
if(b)
{
GET_PTR(unknown_boolean_2) = false;
}
}
}
render_pip = !render_pip;
if(render_pip)
{
adjust_pip = true;
RenderWindow();
}
else
{
adjust_pip = false;
}
}
While browsing through my favorite Direct3D resource website (http://www.toymaker.info/Games/html/direct3d_faq.html#pip), there was a small section about Picture-in-Picture(PiP) and a basic description of how it works. I instantly was intrigued to do something like it for Halo and sure enough in a few long days time I got something going with OpenSauce.
I was looking through the rasterizer system and saw Kornman had hooked the end of the window rendering function if DX was not defined. I am still curious to what he was planning to do with this, since there was some unfinished rendering code there. Anyway this window rendering function was what I needed to accomplish PiP. I thought it would be too hacky to just hook portions of the code that needed modified, so of course I went all the way and reversed the entire function into OS compatible C++ code and rerouted the original function to my function. This means that Halo is being rendered through code inside OS and it makes it a hell of a lot easier to tinker with.
To do PiP, the render code is run twice. The first time is normal and the second time with a modified viewport and vertex buffer(adjusted width & height). I did not really notice any performance drop with video options on max settings and my system being a laptop. The minimal choppiness you do see is normal for my specs without running OS. I was actually quite surprised, I really thought it would be a huge hit in performance.
Well here is my PiP example video, the map is an alpha version of Precipice (I show the phantom bsp errors in this):
LFOcWObEwlM
I am posting this to get ideas on things we could make this useful for. My main idea is to make a rear view mirror for the warthog. To accomplish this, I would have to update the view matrix to change the camera angle as well as not render the HUD elements. Other idea for reversing this function was to easily modify how Halo is rendered or easily add post-processing effects.
There were additions to multiple files so I don't really feel the need to post all of it. Here is just the window rendering code. If you want the full source just let me know and I'll send it right over.
bool SoftwareVertexProcessing() PTR_IMP_GET(software_vertex_processing);
IDirect3DSurface9* RenderTarget() DPTR_IMP_GET(render_target);
IDirect3DBaseTexture9* Texture() DPTR_IMP_GET(texture);
IDirect3DIndexBuffer9* IndexData() DPTR_IMP_GET(index_data);
IDirect3DVertexBuffer9* StreamData() DPTR_IMP_GET(stream_data);
bool render_pip = false;
bool adjust_pip = false;
void RenderWindow()
{
if(GET_PTR(unknown_boolean_3) == 1 && !GET_PTR(unknown_boolean_4))
{
DX9::Direct3DDevice()->SetRenderTarget(0, RenderTarget());
D3DSURFACE_DESC desc;
RenderTarget()->GetDesc(&desc);
D3DVIEWPORT9 viewport = {
0, // x
0, // y
adjust_pip ? 200 : desc.Width, // width
adjust_pip ? 150 : desc.Height, // height
0.0f, // minz
1.0f // maxz
};
DX9::Direct3DDevice()->SetViewport(&viewport);
if(DebugOptions()->wireframe == false)
DX9::Direct3DDevice()->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
DX9::Direct3DDevice()->SetTexture(0, Texture());
DX9::Direct3DDevice()->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
DX9::Direct3DDevice()->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
DX9::Direct3DDevice()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
DX9::Direct3DDevice()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
DX9::Direct3DDevice()->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
DX9::Direct3DDevice()->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
DX9::Direct3DDevice()->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED|D3DCOLORWRITEENABLE_GREEN| D3DCOLORWRITEENABLE_BLUE);
DX9::Direct3DDevice()->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
DX9::Direct3DDevice()->SetRenderState(D3DRS_ALPHATESTENABLE, false);
DX9::Direct3DDevice()->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
DX9::Direct3DDevice()->SetRenderState(D3DRS_FOGENABLE, false);
DX9::Direct3DDevice()->SetPixelShader(NULL);
DX9::Direct3DDevice()->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
DX9::Direct3DDevice()->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
DX9::Direct3DDevice()->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
DX9::Direct3DDevice()->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
DX9::Direct3DDevice()->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
DX9::Direct3DDevice()->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
if(StreamData())
{
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1)
uint32 fvf_size = D3DXGetFVFVertexSize(D3DFVF_CUSTOMVERTEX);
byte* vertex_data;
StreamData()->Lock(0, fvf_size*4, CAST_PTR(void**, &vertex_data), D3DLOCK_DISCARD);
uint32 height = adjust_pip ? 150 : GET_PTR(unknown_uint16_2) - GET_PTR(unknown_uint16_1);
uint32 width = adjust_pip ? 200 : GET_PTR(unknown_uint16_4) - GET_PTR(unknown_uint16_3);
// black
//argb_color vertex_color = {
// 255, // a
// 255, // r
// 255, // g
// 255 // b
//};
// ^^^ operator override was not working, so I did it this way instead
// it was telling me "cannot convert "Yelo::uint32" to "Yelo::uint32""
DWORD vert_color = 0xFFFFFFFF;
struct CUSTOMVERTEX {
real x, y, z;
real rhw;
uint32 color;
real tu, tv;
} vertices[4] = {
{ -0.5f, -0.5f, 0.0f, 1.0f, vert_color, 0.0f, 0.0f },
{ CAST(float, width) - 0.5f, -0.5f, 0.0f, 1.0f, vert_color, 1.0f, 0.0f },
{ CAST(float, width) - 0.5f, CAST(float, height) - 0.5f, 0.0f, 1.0f, vert_color, 1.0f, 1.0f },
{ -0.5f, CAST(float, height) - 0.5f, 0.0f, 1.0f, vert_color, 0.0f, 1.0f }
};
if(GET_PTR(unknown_uint32_1))
{
vertices[1].tu *= CAST(float, height);
vertices[2].tu *= CAST(float, height);
vertices[2].tv *= CAST(float, width);
vertices[3].tv *= CAST(float, width);
}
memcpy(vertex_data, vertices, fvf_size*4);
DX9::Direct3DDevice()->SetVertexShader(NULL);
DX9::Direct3DDevice()->SetFVF(D3DFVF_CUSTOMVERTEX);
StreamData()->Unlock();
DX9::Direct3DDevice()->SetSoftwareVertexProcessing(SoftwareVertexProcessi ng());
// Stride has to be equivalent to FVF size
DX9::Direct3DDevice()->SetStreamSource(0, StreamData(), 0, fvf_size);
DX9::Direct3DDevice()->SetIndices(IndexData());
DX9::Direct3DDevice()->DrawIndexedPrimitive(D3DPT_TRIANGLEFAN, 0, 0, 4, 0, 2);
DX9::Direct3DDevice()->SetFVF(NULL);
}
if(DebugOptions()->wireframe == true)
DX9::Direct3DDevice()->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
}
if(GameUI::HudChatGlobals()->active)
{
// Set chatbox background color
argb_color box_color = {
176, // a
32, // r
32, // g
32 // b
};
// Set chatbox rect coordinates
rectangle2d box_rect = {
0, // left
460, // top
480, // bottom
640 // right
};
Engine::DX9::RenderChatbox(box_rect, box_color);
}
bool b = true;
if(FAILED(DX9::Direct3DDevice()->SetSoftwareVertexProcessing(SoftwareVertexProcessi ng())))
{
b = false;
}
DX9::Direct3DDevice()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
DX9::Direct3DDevice()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
DX9::Direct3DDevice()->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
if(Keystone::MainWindow() && !GET_PTR(unknown_boolean_1))
{
if(FAILED(Keystone::Update(Keystone::MainWindow()) ))
{
GET_PTR(unknown_boolean_1) = true;
}
}
if(FAILED(DX9::Direct3DDevice()->SetSoftwareVertexProcessing(SoftwareVertexProcessi ng())))
{
b = false;
}
if(render_pip)
{
if(SUCCEEDED(DX9::Direct3DDevice()->EndScene()))
{
if(b)
{
GET_PTR(unknown_boolean_2) = false;
}
}
}
render_pip = !render_pip;
if(render_pip)
{
adjust_pip = true;
RenderWindow();
}
else
{
adjust_pip = false;
}
}