Virtual monitor

Blitz3D Forums/Blitz3D Programming/Virtual monitor

Kiyoshi(Posted 2013) [#1]
Hello all! Its been a while since I've posted...then again thats usually the case with me. I try my best to figure things out on my own, but I need a little help now and then :)

What I've done:
I've programmed a small online multiplayer game for me and my friends, a pretty simple first-person shooter. In this game, players can send out a flying rader plane that can spy on your enemies. For example, a player can walk up to a computer in-game, and activate the drone. This switches the camera view to a top down view of the map from the flying drone, until the player exits the computer interface. When this happens, the camera switches back to the player. Pretty simple, no?

What I'd like to do:
I want the computer model to display the view from the drone, using a second camera, as opposed to switching from the player's camera to the drone's camera. This way the player can turn and look at the computer to see what the drone can see WITHOUT activating the computer. I figured I'd use a 3D cube as the computer screen, and just position it with the computer mesh. I assume a texture would work fine.

Ultimately, my question is this:
How do I display what a camera sees onto a mesh/texture?

I suppose it would have been easier if I had just asked my question beforehand, but I feel that you could get a better understanding of what I'm trying to do if I explained it. If it helps any, I can recall reading something similar being done in either the code archives or the B3D samples, but I've failed to find it.

Thanks much,
Kii

p.s.
If there are multiple ways to go about this, I'd appreciate it if all methods were mentioned. Thank you for your time! :)


Yasha(Posted 2013) [#2]
Pretty simple, no?


Simple? Maybe I'm just easily impressed but I think that's really, really cool actually.


How do I display what a camera sees onto a mesh/texture?


Easily enough:

-- turn off your player camera (hide the HUD, any effects, etc. if necessary - you know this part)
-- turn on your drone camera
-- set your drone camera's viewport to an appropriate texture size, e.g. 0,0,256,256
-- render the view from your drone
-- now the important part: CopyRect the rect corresponding to that viewport from the back buffer to the texture of your monitor's screen
-- turn off the drone camera, turn on the player camera and go back to the rest of your rendering process

That's all it is: CopyRect after rendering the view in an appropriate scale, then render over with your normal view without Flipping and nobody will need to know you used the back buffer for two passes.

Important notes:

-- CopyRect is usually much faster if you copy to a texture with flag 256 (if copying between two textures, they should either both have it or both not have it; in general copy operations are practically free within the same area of memory, and expensive otherwise).
-- since the texture is being updated, every object using it will change immediately. Therefore every working monitor will need a unique screen texture, unless you want them all to show the same thing
-- (remember to have an "off" texture in storage to copy a static image from once or to retexture with when the monitor turns off)
-- making the screen a separate quad surface from the rest of the monitor model will give it sharper, well-defined edges, and also make it easier to dynamically retexture (the rest of the model can be instanced)
-- you only need to bother with a render pass for monitors that are actually in the player's view; if you can avoid as many passes as possible the game will perform significantly better
-- (this means you can save memory and only have a texture for all monitors in view at once; with good level design you can therefore cut it down to just two, for on and off)
-- you should not set the monitor viewport to be larger than the full screen resolution, so in vanilla B3D that limits your options for the virtual monitor resolution a bit (powers of two only!). If you try to render e.g. a 1024x1024 texture viewport in a 1366x768 game, it will behave unpredictably (it probably won't crash, you'll just get a garbage texture)

If you use FastExt, this process is streamlined a bit by its RenderToTexture and NonPowerOfTwoTexture options. However, in practice a basic render and CopyRect work very well.


Kiyoshi(Posted 2013) [#3]
Alright, seems simple enough. I'll get to work on this right away, thanks Yasha!


Yasha(Posted 2013) [#4]
Oh, and another thing (probably obvious but I forgot about it while writing the above):

Remember that since texture coordinates can be whatever you want, the monitor quad doesn't need to be textured with the whole square texture assigned to it; it can use e.g. the full width and only 75% of the height, to simulate a 3:4 monitor. Therefore your virtual monitors don't need to be square and don't in themselves need to have power-of-two resolutions - they can have whatever resolution you like that fits within that of the texture. So you could give them virtual resolutions as high as the full res of your player view if you wanted (going back to that 1366x768 game: there's no harm in giving the monitors 2048x2048 pixels of texture space as long as you only render and copy 1366x768 pixels of it at a time - although you will waste a lot of texture memory, and the player will never actually perceive every pixel thanks to downsampling in the final render).


Kiyoshi(Posted 2013) [#5]
Sorry for responding late-ish, I figured this out about halfway through my coding last night, lol. I wish I would have seen this post sooner; after getting off work and coding all night, I wanted nothing more than to sleep, but I kept messing around with the code. So far, it seems to be working nicely though! Below is a watered-down example of how I'm handling the textures:

Type RADAR_DRONE
	Field drone_mesh ;The plane mesh for the drone, the drone's camera is parented to this
	Field drone_camera ;The camera used for viewing what the drone sees
	Field drone_monitor ;The computer model on the ground, players interact with this
	Field drone_monitorscreen ;The computer screen, the texture is displayed on this
	Field drone_monitortexture ;The texture used for the computer's screen, the camera's view is displayed on this
End Type


Is this the most efficient way to work this? I'm storing an ON texture for each drone, and a default OFF texture used by all drones when the monitor is not being viewed. I would assume that this is fine, seeing as I'm not rendering to the texture when it's not needed, but would it be faster if I removed the ON drone texture from memory completely, when not being used? I would still keep the label for future use, but a new texture would be created when the monitor is activated.

Thanks, Kii


RemiD(Posted 2013) [#6]
To avoid to render the others scenes each loop, what i would do is code a routine to check if a screen is near enough a player (Distance check), and if a player can see it (DeltaYaw check).


Rroff(Posted 2013) [#7]
Depending on your useage you may not need to render the monitor viewpoint as often as the main camera - tho you'd have to be careful to make sure it updated correctly if the camera teleports and its not lagging too far behind and noticeably out of sync unless you make the slower update rate an effect i.e. scan lines.


Yasha(Posted 2013) [#8]
would it be faster if I removed the ON drone texture from memory completely, when not being used?


Probably not. Objects waiting to be used in memory don't cause slowdown unless there are too many of them to fit.

Creating objects, on the other hand, is usually considered a slow operation and something to avoid during user interaction.

In this case it would probably make no perceivable difference, but unless you're having problems fitting everything into memory, there's not a whole lot of reason to bother. Wait until you actually need to improve the system: it'll already be very close to optimal so focus your main efforts on the things that aren't yet done at all.


Kiyoshi(Posted 2013) [#9]
@RemiD This is actually exactly what I've done, but I've also added EntityVisible so the monitor doesn't update when the view is blocked by a wall.

@Rroff Right now I'm just rendering the monitor in real-time every frame when the player can see it/is close enough. Its working okay now, but if I need to update it slower, I'll implement something like scan lines.

@Yasha Okay. I'm not having any memory problems at the moment, so I'm just continuing the coding process for the rest of the program.

However...
Creating objects, on the other hand, is usually considered a slow operation and something to avoid during user interaction.

This specifically catches my attention for a different reason. When you say creating objects, are you merely referring to CPU heavy or texture-based commands? A bit off topic here, but I'm using projectiles for the weapons in my game. When a gun is fired, the program will create a brand-new bullet projectile in front of the weapon. I haven't noticed any slowdown using this method, so I'm hoping this is irrelevant. Either way, should I be doing this differently?

Thank you for all your input, everyone!


Yasha(Posted 2013) [#10]
When you say creating objects, are you merely referring to CPU heavy or texture-based commands?


I meant just allocating memory in general, although a fairly large and complicated multi-tier thing like a texture will certainly be on the slower end within that.

Anyway, it's not as big an issue as I made it sound. Anything you're doing in direct response to user interaction will be happening in the main loop, and therefore are happening in a part of the program that's mostly spent sitting around doing nothing waiting to flip the drawing buffers, so the fact that creating an object is 10-100 times slower than some unspecified static alternative really won't weigh in visibly at all (still far too fast to show up on a timer). The thing to worry about is if you start allocating objects within e.g. a pixel-processing loop.