rem This project demonstrates how to create a screen quad for use with post processing shaders.
rem The screen quad is entirely GPU-generated using a null mesh, and perfectly aligned to the 
rem window size. As a simple demonstrative example, a blur shader is used on the screen quad which
rem draws the render target of a secondary scene camera.

rem Screen size (change these as appropriate; note that they need to match a valid screen resolution on your system however)
#constant SCREEN_WIDTH      1600
#constant SCREEN_HEIGHT     900

rem Ensure that the DX11Constants.dba file is properly included (if it isn't we'll get unexpected
rem behaviour as non-set constants will default to zeroes with DBPro's dynamic typing)
INCLUDEGUARD DX11_CONSTANTS

rem Start the DX11 engine; we can use the DX10 feature level for backwards compatibility with this example since it doesn't
rem rely on any DX11-exclusive functionality
dx11 init SCREEN_WIDTH, SCREEN_HEIGHT, false, "DX11 Plugin Example: Screen quad", DXVERSION_10_0

rem Setup main camera
mainCam = dx11 get main camera()
dx11 set camera backdrop mainCam, 0xff60ffaa

rem Create a secondary camera that will render the scene to a render target image; this will be used as 
rem input to the screen quad, which will be the only thing the main camera renders
cam = dx11 create camera()
rem We can set the alpha channel for the back buffer clearing colour; this allows us to capture a scene render with a completely
rem transparent background that can be blended with something that the main camera renders in the background, behind the screen quad.
dx11 set camera backdrop cam, 0x00000000
renderTarget = dx11 set new camera render target(cam, SCREEN_WIDTH, SCREEN_HEIGHT)

rem In order to blur the render target image when drawing the screen quad our screen quad pixel shader 
rem needs to know how large a pixel is in texture coordinates. We'll supply this information in a constant buffer.
rem Since this only needs to be set essentially once (save for updates if the screen resolution changes during runtime, 
rem which it won't here) we don't set the makeDynamic flag of the cbuffer. This makes it slightly faster to access for 
rem reading by the GPU, while making updating it from the CPU slower.
rem     (*) This holds true for most graphics cards, however some may use the same memory access pattern regardless of this setting.
cbTexelData = dx11 create cbuffer(8, false)
dx11 set cbuffer float cbTexelData, 0, 1.0 / (1.0 * dx11 get image width(renderTarget))
dx11 set cbuffer float cbTexelData, 4, 1.0 / (1.0 * dx11 get image height(renderTarget))

rem Create a set of cubes
imgCrate = dx11 load image("media/crate.jpg")
dim Cube(3, 3, 3) as dword
for x = 0 to 3
    for y = 0 to 3
        for z = 0 to 3
            Cube(x, y, z) = dx11 create object cube(1)
            dx11 set object texture Cube(x, y, z), imgCrate
            dx11 set object position Cube(x, y, z), -2.5 + x * 1.5, -2.5 + y * 1.5, -2.5 + z * 1.5
            rem These cubes should only be drawn to our RT camera and not to the main one
            dx11 set object camera mask Cube(x, y, z), 1 << dx11 get camera id(cam)
        next z
    next y
next x

rem Create the screen quad object. This will use a "null mesh", which essentially means that no vertex 
rem or index data will be available to the GPU. Instead the vertex (and possibly geometry) shader will 
rem be responsible for creating the appropriate vertices from an id alone. The vertex shader will be 
rem invoked as many times as the "vertex count" of the null mesh implies, and the current invocation can 
rem be determined via the system value obtainable through the SV_VertexId semantic (see the code in 
rem "Screen Quad.fx" for details).
rem It is important to note that the Ziggurat engine cannot figure out the size of a null mesh object by 
rem itself since it lacks vertex position data. As such we oftentimes need to set an appropriately sized 
rem bounding box ourselves; if this box (which is moved in accordance with the null object's current transform)
rem falls outside of a camera's view frustum the object won't be rendered so it is important to ensure that the 
rem final geometry created by the GPU will fit inside this bounding box (which can be updated during runtime, 
rem should you need to).
rem As you can see here I chose to leave the bounding box arguments out from the call to DX11 CREATE NULL MESH.
rem In this case a default bounding box of (1, 1, 1) world units will be created. This will do for the screen 
rem quad since the main camera will never be moved and as such this bounding volume will always fall within its 
rem view. If you do want a moving camera to render a screen quad for whatever reason you should move the null object 
rem so that it falls in front of the camera at all times.
rem Also note that the topology of a null mesh defaults to TOPOLOGY_TRIANGLESTRIP unless set otherwise. This allows
rem drawing connected vertices such that a quad can be created from just 4 vertices instead of 6 (as in two triangles).
sqMesh      = dx11 create null mesh(4)
sqObject    = dx11 create object(sqMesh)
rem Since we won't need to update the mesh we can release our reference to it. If we do this, the actual mesh will be
rem deleted at the same time as the object using it. If not, the mesh will remain in existence after the object is 
rem deleted until such time that we call DX11 DELETE MESH for it.
dx11 delete mesh sqMesh
rem We also want the screen quad to only draw to the main camera and not the one that renders the scene that will later
rem be used with the render target. This is in fact a DirectX requirement; you cannot have a camera that writes to a 
rem resource (such as a render target image) that is at the same time read by an object / sprite being rendered by the 
rem same camera.
dx11 set object camera mask sqObject, 1 << dx11 get camera id(mainCam)
rem Set the texture of the screen quad object to the render target of our scene drawing camera
dx11 set object texture sqObject, renderTarget

rem Since the screen quad effect will blur the render target generated by our scene camera, out-of-bounds indexing into 
rem UV coordinates outside the (0..1) range should be clamped rather than wrapped around to the other side of the texture.
rem In order to facilitate this we need to create a separate sampler state for its shader technique.
ssScreenQuad = dx11 create sampler state()
dx11 set sampler state texture address mode ssScreenQuad, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP

rem Load shaders and set up a technique for rendering the screen quad
vsScreenQuad = dx11 load vertex shader("shader/Screen Quad.fx", "VS")
psScreenQuad = dx11 load pixel shader("shader/Screen Quad.fx", "PS")
rem We can use a null vertex layout with a null mesh since it doesn't feature any vertex data that the shader will need to adhere to
technique = dx11 create shader technique(nullptr, vsScreenQuad, psScreenQuad)
rem Bind needed resources
dx11 add constant buffer technique, cbTexelData
dx11 set sampler state technique, 0, ssScreenQuad
rem And finally set the shader technique to our screen quad object
dx11 set object shader technique sqObject, technique

rem We will have to draw the scene camera separatedly from drawing the main camera.
rem By setting the sync mask like this we can tell the engine to only render the main camera when calling DX11 SYNC.
rem Other cameras can be rendered at arbitrary times using the DX11 RENDER CAMERA function. The reason we do it like 
rem this is mainly to show that it can be done; if we set the sync mask such that it contains both cameras, «cam» will
rem be rendered before «mainCam» (which is always rendered last) anyway. You could also set the sync mask to 0 and render
rem all cameras, including the main one, using the DX11 RENDER CAMERA function, however DX11 SYNC updates other parts of
rem the engine as well (such as input, ticks, etc.) and should be called once per frame.
dx11 set sync mask 1 << dx11 get camera id(mainCam)

rem Main loop
lastTick    as double integer
currentTick as double integer
currentTick =  dx11 get high resolution tick() - 1
while not dx11 quit()
    rem Update timer
    lastTick    = currentTick
    currentTick = dx11 get high resolution tick()
    delta#      = dx11 get high resolution delta time(lastTick, currentTick)
    
    rem Rotate camera around the blocks (which are centered at (0, 0, 0))
    angle# = dx11 wrap(angle# + delta# * 90, 0.0, 360.0)
    dx11 set camera position cam, 20.0 * cos(angle#), 0.0, 20.0 * sin(angle#)
    dx11 set camera lookat cam, 0, 0, 0
    
    rem Render scene camera
    dx11 render camera cam
    
    rem Draw some informative text to the screen
    dx11 draw text 4, 4, "Camera position: (" + str$(dx11 get camera position x(cam), 2) + ", " + str$(dx11 get camera position y(cam), 2) + ", " + str$(dx11 get camera position z(cam), 2) + ")"
    dx11 draw text SCREEN_WIDTH / 2, SCREEN_HEIGHT - 20, "[c=0xffffff80]FPS: " + str$(dx11 get screen fps()), true
    
    dx11 sync
endwhile
end
