I threw something together that might be acceptable:
rem setup screen
sync on
sync rate 60
backdrop on
color backdrop 0
hide mouse
rem make some weird scenery stuff
for n = 1 to 200
make object sphere n, rnd(100)+50
position object n, rnd(500)-250, rnd(500)-250, rnd(500)-250
set object wireframe n, 1
color object n, rgb(rnd(255), rnd(255), rnd(255))
next n
rem The screen quad object. Needs to be textured with the two
rem render targets of the players for the shader to work.
screenQuad = 300
make object plain screenQuad, 1, 1
load effect "split-camera.fx", 1, 0
set object effect screenQuad, 1
rem set up players
type vec3
x# as float
y# as float
z# as float
endtype
type Player_t
pos as vec3
angle as vec3
speed# as float
targetSpeed# as float
maxSpeed# as float
renderTargetImage as integer
camera as integer
endtype
dim players(1) as Player_t
players(0).renderTargetImage = 1
players(1).renderTargetImage = 2
players(0).camera = 1
players(1).camera = 2
players(0).maxSpeed# = 2.0
players(1).maxSpeed# = 2.0
createPlayers(2, screenQuad)
rem Optimisation: Camera 0 only has to draw the screen quad and
rem nothing else. Therefore, we can disable all existing objects
rem from drawing to camera 0.
maskAllObjectsForCamera0Except(screenQuad)
rem main loop
do
rem help
set cursor 0,0
print "WASD for player 1, arrow keys for player 2"
print "press space to change angle of split"
rem angle of split
if spacekey()
splitAngle = wrapvalue(splitAngle + 5)
set effect constant float 1, "splitGradient", tan(splitAngle)
endif
rem control players
for n = 0 to 1
rem map player specific controls
if n = 0
up = keystate(17)
down = keystate(31)
left = keystate(30)
right = keystate(32)
endif
if n = 1
up = upkey()
down = downkey()
left = leftkey()
right = rightkey()
endif
rem control forward and backward movement
players(n).targetSpeed# = 0.0
if up then players(n).targetSpeed# = players(n).maxSpeed#
if down then players(n).targetSpeed# = 0.0 - players(n).maxSpeed#
players(n).speed# = curvevalue(players(n).targetSpeed#, players(n).speed#, 3.0)
rem rotate movement
if left then dec players(n).angle.y#, 5.0
if right then inc players(n).angle.y#, 5.0
rem update position
players(n).pos.x# = newxvalue(players(n).pos.x#, players(n).angle.y#, players(n).speed#)
players(n).pos.z# = newzvalue(players(n).pos.z#, players(n).angle.y#, players(n).speed#)
rem update camera
position camera players(n).camera, players(n).pos.x#, players(n).pos.y#, players(n).pos.z#
rotate camera players(n).camera, players(n).angle.x#, players(n).angle.y#, players(n).angle.z#
next n
rem refresh screen
rem will render all cameras except camera 0
sync mask 0xFFFFFFE
fastsync
rem will render only camera 0
sync mask 0x1
sync
rem end of main loop
loop
end
function createPlayers(playerCount, screenQuad)
for n = 0 to playerCount-1
make camera players(n).camera
set camera to image players(n).camera, players(n).renderTargetImage, screen width(), screen height()
texture object screenQuad, n, players(n).renderTargetImage
color backdrop players(n).camera, 0
next n
endfunction
function maskAllObjectsForCamera0Except(excludeObject)
for n = 1 to 65536
if object exist(n)
if excludeObject <> n
set object mask n, 0xFFFFFFFE
endif
endif
next n
endfunction
This is the shader, save it as "split-camera.fx" in the same folder:
// ------------------------------------------------------------------
// Split screen on an arbitrary straight line
// by TheComet
// ------------------------------------------------------------------
// ------------------------------------------------------------------
// tweaks
// ------------------------------------------------------------------
// gradient of the split. This is calculated with the formula:
// gradient = tan(angle_of_split)
// Note that an angle of 90° is impossible because that would require
// the gradient to be infinit.
// Default value is 45°.
float splitGradient = 1.0f;
// ------------------------------------------------------------------
// textures
// ------------------------------------------------------------------
texture texPlayer1
<
string ResourceName = "";
>;
texture texPlayer2
<
string ResourceName = "";
>;
// ------------------------------------------------------------------
// samplers
// ------------------------------------------------------------------
sampler2D sampPlayer1 = sampler_state
{
Texture = <texPlayer1>;
};
sampler2D sampPlayer2 = sampler_state
{
Texture = <texPlayer2>;
};
// ------------------------------------------------------------------
// input and output structs
// ------------------------------------------------------------------
struct VS_INPUT
{
float4 position : POSITION0;
};
struct VS_OUTPUT
{
float4 position : POSITION0;
float2 texCoord : TEXCOORD0;
};
struct PS_INPUT
{
float2 texCoord : TEXCOORD0;
};
struct PS_OUTPUT
{
float4 color : COLOR;
};
// ------------------------------------------------------------------
// vertex shader
// ------------------------------------------------------------------
VS_OUTPUT vs_main(VS_INPUT input)
{
VS_OUTPUT output;
// align quad to screen and calculate texture coordinates
// for pixel shader
output.position = float4(sign(input.position.xy), 0.0f, 1.0f);
output.texCoord.x = (output.position.x+1.0f) * 0.5f;
output.texCoord.y = 1.0 - ((output.position.y+1.0f) * 0.5f);
return output;
}
// ------------------------------------------------------------------
// pixel shader
// ------------------------------------------------------------------
PS_OUTPUT ps_main(PS_INPUT input)
{
PS_OUTPUT output;
// calculate y coordinate of line according to split gradient
float x = input.texCoord.x-0.5f;
float y = x*splitGradient;
// if we're beneath the line, draw player 1
if(y > input.texCoord.y-0.5f)
{
output.color = tex2D(sampPlayer1, input.texCoord);
}
// if we're above the line, draw player 2
else
{
output.color = tex2D(sampPlayer2, input.texCoord);
}
return output;
}
// ------------------------------------------------------------------
// techniques
// ------------------------------------------------------------------
technique Default
{
pass p0
{
VertexShader = compile vs_1_1 vs_main();
PixelShader = compile ps_2_0 ps_main();
}
}
@ GG (or anyone else experienced with shaders)
Three issues:
1) I was unable to do that thing where you shift the screen quad by half a pixel.
float2 ViewSize : ViewSize; was always returning {0, 0} for me for some reason.
2) Everything seems blurry, it's as if the quad's texture is being lerped. It may have something to do with 1) now that I think about it.
3) A few of the spheres are flickering. Not sure why.
I like offending people. People who get offended should be offended. --
Linus Torvalds