The way it is done in the CR version is a very good hack, but even better would be an internal solution, I think.
If it is possible and does not drop performance, would the following be achievable in a sane amount of time?
Layered landscape/solid mask
We have object layers (GetObjectLayer(), SetObjectLayer()) in CR, I have not checked what happened to them so far. Anyway, multiple landscape layers would be extremely useful. Unfortunately, this is probably just a feature for special scenarios, since it could make the original game very confusing.
The solution would have to meet a few requirements though:
- for buildings that can be entered we need solid masks to be in a separate layer. The best solution imo: collide with solid mask only if the objects are in the same object layer
- there should be an option to draw the other landscape layers behind the first layer, or display them only if your view is in that layer. I see a lot of problems with managing the viewport and selections already.
- getting to another landscape layer should be possible only via script.
Random thoughts:
- parallax drawing or zoom for landscape layers would be really cool eye candy ;)
- one additional layer would be enough probably
- different layers could be realized without too much changes with this method: Make a parameter in Scenario.txt that limits the landscape height to Y pixels. Every multiple of Y in the Map.bmp creates another layer. Objects stop falling at y-coordinate Y-1. If you want them in the next layer you have to SetPosition(X, y + layer * Y) only, and the object can only travel in the boundaries of that layer again.
I think there was another thread with similar ideas already, also don't take this too seriously yet.
Since I don't know much about the engine details at the moment, how much easier is "much easier" when it comes to making solidmasks specific to object layers?
Maybe it is an idea to overload FindObject in general to NOT get different layered objects per default?
Similarly, for per-layer SolidMasks: It's possible to implement and I could think of some ideas to make it not affect performance on non-layer-objects (mainly, having a second GetPix that checks SolidMask manually, but which is used by objects in layers only).
However, big problems arise when a lot of things happen in these layers. Pretty much every script has to be "layer-aware" in that it has to check for layers when looking for objects, create objects in the correct layer, etc. In the past, this has never been done and it's unlikely that people remember to do this in the future.
Also, there's a few things that would need a much larger rewrite like e.g. building loam bridge inside a building wouldn't work.
If you insist on doing this in engine code, what about a mode where a secondary "background position" can be attached to a viewport? When you're in a house, the background position contains the outside world (with the house front made invisible for the player) and is drawn instead of the sky. The house itself is in a part of the landscape where there is no materials and just transparent sky background. The special view mode could even be defined to draw objects and landscape in a constrained area only so you don't need to reserve large areas of the landscape.
And with that solution, you could build loam bridges in houses!
Regarding layers:
What about an approach where the searching object can ask passages (doors, teleports, whatever, objects in general) if it is allowed to search in that object layer?
So a Clonk could find objects outside of the building, if the building allows it. Similarly, objects inside the building are "hidden" to FindObject() unless the building allows it.
Regarding the viewport: A viewport material would be cool in general. Imagine actual security cameras and monitors displaying the picture in a Hazard-like scenario
>. Pretty much every script has to be "layer-aware" in that it has to check for layers when looking for objects, create objects in the correct layer, etc. In the past, this has never been done and it's unlikely that people remember to do this in the future.
I thought that too, but just now I was thinking. Maybe it would be just rules, goals and relaunches scripts?
Think about it, all calls from an object for creating or interacting (FindObject) with another object should be intended for the same layer as the source only.
I cannot think of any Monster, Clonk-fight, spell effect, switch interaction - that would behave wrong with this idea. The only stuff not working would be global scripts which count objects or clonks - so stuff like goals and rules. This shouldn't be too much hassle to adjust.
Also OC is "pretty fresh", and we don't have much content to begin with. If it would fail on the work of just adjusting those few goal and relaunch scripts. I think we can find someone who is willing to do this pretty quickly.
> I cannot think of any Monster, Clonk-fight, spell effect, switch interaction - that would behave wrong with this idea.
The "point out the next Clonk" spell.
Additionally, I think CreateObject would default to the creators layer.
There were still tons of cases where this was not enough.
* FindObjectOnAllLayers();
I imagine the execution like this:
Create a "viewport" object. Execute:
viewport->SetShape(300, 100); // area
viewport->SetNoIdeaWhatToCallIt(100,50); // upper left corner of the area in the landscape
Then the object draws everything in the rectangle with upper left corner (100,50) and lower right corner (400,150) on its face. You can create it behind an interior view of the house (which is still in a separate part of the landscape) and it shows what is going on around it.
Still expanding that idea, there could be a ViewportMask.png so that the viewport is not all over the face of the object, but only in certain areas. This way you could have the exterior of the house display what is going on inside (through the windows) without having to do a lot of tricks with SetGraphics().
We would only need a way to deal with viewport objects drawing what is visible in viewport objects so that there is no infinite loop.
This is probably the most practical version, because you can use loam bridges in the house without blocking the way outside of the house, as you said.
Is it used for anything else? Is it used for anything in CR? If not, perhaps we could remove it altogether then?
>It requires the scripter of every object (that uses FindObject and similar) to keep this in mind.
..which could be easily changed by making
FindObject
default to only finding objects on the same layer as the calling object.>Is it used for anything in CR? If not, perhaps we could remove it altogether then?
Over the time it prove to have some niche usages in CR (Terajumpers, Ghosts in ClonkParty).
> Over the time it prove to have some niche usages in CR (Terajumpers, Ghosts in ClonkParty).
Right, Ghosts in Clonk Fest use it as well.
>which could be easily changed by making FindObject default to only finding objects on the same layer as the calling object.
Apparently this is exactly what has been done already. I dug up this thread again, because I will start working on that viewport object now.
For decoration, it also doesn't matter as much if scripters forgot about layer checks. In the worst case e.g. a spell may hit a background building.
How do you tell the camera to stay at the ViewportArea, instead of moving to where your clonk is right now? What if more than one Viewport object displays the same landscape area; how do you tell camera which of the viewport areas is the one to look at?
You'd even have to save this...Camera Offset(?) for the individual clonks so nothing breaks when changing to another crew member.
Also, you'd get real problems with parallax objects.
I think you're trying to use viewport objects to project the interior onto the main building while the clonk is inside. That is needlessly complicated. Instead, you just have one big "exterior viewport object" that projects the exterior around the interior and several small "interior viewport objects (i.e. windows)" that project stuff you see from outside while you are outside.
The viewport object solution could also work of course and provides some cool extra features that could be used in other objects (like a TV screen that shows other parts of the game). But I believe it would also be quite a bit of coding work and might also incur quite a performance hit when you have multiple of such viewport objects open. The feature of a single background view per viewport would be relatively simple to implement.
The idea with having only one background viewport sounds ok for one building, but with the viewport object version, we could do a lot of other cool stuff. E.g. realizing different landscape layers, mirrors, ...
Also I am not sure how the single background solution could be used to implement overlapping buildings like I used them in Harkon.
The only downside would be that it probably is hard to implement in the engine and could eat a lot of performance. But I am not familiar with the engine code, so I don't know how easy or difficult it is.
But it is possible of course and and would allow some cool applications. If someone wants to dedicate the time to build it I'm sure it would be a great feature. If I had to do it, I would still favor the "secondary background view" concept for its simplicity.
GetCursor().BackgroundView = { OffX=0, OffY=-1000 }
You would set/reset that in the same script that transfers through doors. The viewport drawing procedure would evaluate this variable and if it is set, it would draw the background position (including sky) instead of drawing the regular sky. The engine evaluates it only once per frame per viewport so it shouldn't be a big deal.
An alternative would be a script functions (SetBackgroundView()) and storing it e.g. in the player. But then we would have to handle e.g. savegame storage and cursor switching manually.
Another alternative would be to bind it directly to the Y coordinate of the landscape and just have a global scenario property that indicates that the bottom half of the landscape is buildings and all viewports below a certain Y coordinate should be drawn accordingly. The advantage is that it also works on cursor-less viewports, but then we'd also need special viewports (or a script function) for editing :)
>One option would be to define a second view as a proplist to the cursor object. That way it is automatically removed when you switch clonks and >you don't need to handle callbacks to that. For example:
>GetCursor().BackgroundView = { OffX=0, OffY=-1000 }
Ok, after I got used to proplists I actually understand what you want to do there. I have no idea where to start actually, so at the moment I am collecting ideas.
I imagine that the implementation for this would need a modification to C4Viewport.cpp, or wherever the sky background is rendered, so that the property "BackgroundView" is evaluated. Looking into the code my first point was C4ViewportList::RecalculateViewports(), where the viewport area is set. It seems that this function does not actually do much, though.
How can I access properties in the engine code?
Regarding the other idea:
This would require a lot more modifications, trying to get the gist of it:
- either A) a new overlay mode for SetGraphics() that can draw viewport information instead of graphics information, or B) a new category (?) so that an object with this category is draws viewport information on it's shape.
- in case of A) a mechanism that loads a ViewportMask.png or whatever that defines which parts of the shape get the viewport information (if you want viewport stuff only on certain parts of the object)
- in case of B) possibly use the existing Overlay*.png mechanism to define where viewport stuff is drawn. This could also work with solution A) actually
- a proplist such as "BackgroundView" {X, Y, Wdt, Hgt} (width and height could be taken from the object shape actually) or a (parallely existing, if it makes sense) {X_Off, Y_Off} that defines the area where object takes viewport information from. This would work with both options, although I strongly favor version A) at the moment
- a solution for viewport objects drawing viewport objects? Apparently the easiest strategy would be, as suggested somewhere else "render everything, including the objects, as usual. Then render just the viewport objects again".
- the solution should be dynamic enough so that you can set up a rectangular viewport object just by creating the object "Dummy" and modifying it with SetShape and other script functions. The new scripting possibilities with proplists are really cool, because the need to create a very specific object that is used only once in the scenario for deco reasons or effects has been removed.
Where would I start with the actual rendering stuff? Also, if I remember correctly, objects do not get rendered if their center/offset is not on the screen. This could become very annoying if you use a large object to draw another part of the landscape and it suddenly goes off-screen.
> How can I access properties in the engine code?
Add the name of the property to the predefined strings in C4StringTable.h and .cpp. Call GetProperty, GetPropertyStr, GetPropertyArray, GetPropertyInt or GetPropertyPropList on the PropList you want to access. For example, a C4Object.
> Where would I start with the actual rendering stuff?
Well, the primary routine is C4Viewport::Draw, but you probably found that already. Maybe you can just create a custom C4Viewport instance for each viewport object, and call its Draw method to render it (and somehow making sure you don't do endless recursion in doing so).
New plans for the routine:
- (as is) the viewport draws sky, objects behind landscape, objects in front of landscape, particles, and some other stuff in that order.
- the viewport object would need that information when it is drawn, but that happens during the viewport rendering.
- in order to avoid recursions I would just have the viewport object lag one frame behind all other objects? So when the viewport object is drawn it either displays black color or the information that is currently in its own viewport (from last frame). This way one does not have to think too much about what is drawn when, and that one frame should not be noticeable too much.
- objects that qualify as viewport object save a reference to their custom viewport somehow and insert it into the viewport list
(Also, just having the background offset sounds much easier to implement)
The background offset should indeed be easier to implement, but it also is not as powerful, e.g. you cannot have a mirrored lake or TV screens with that solution. I still do not get how, instead of regular sky, I would draw the contents of a viewport.
Probably the information could be taken from the C4TargetFacet that is passed to the rendering routines in C4Viewport::Draw. However, this would mean drawing things twice (once, so that everything is in the C4TargetFacet, then another time with the modified background?) or save the information somewhere instead of clearing the facet every time.
You still would have a lot of C4Viewport members and functionality you do not need. So it may be a good idea to have derived classes, like C4Viewport base class and C4PlayerViewport and C4ObjectViewport/C4ScriptViewport/C4ExtraViewport (or however you want to name it) derived from that, which would implement custom behavior. E.g. per-frame exection of the player viewport does the zooming and scrolling by inputs and cursor, while the C4ObjectViewport may be locked to an offset from the player viewport.
You would also have to make sure C4ViewportList ignores these extra viewports when recalculating its main viewport window positions.
I also wouldn't necesserily let the object "own" the extra viewport. Why not just have a script function CreateViewport, which returns a viewport proplist (or a viewport ID if proplists are too complicated), which you can then set in the graphics? E.g. in script:
var viewport=CreateViewport(VIEWPORTTYPE_Offset, player_number, offset_x, offset_y, [max_size], [zoom_multiplier], etc.);
window1->SetGraphics("Viewport", [...], GFXOV_MODE_Viewport, viewport);
window2->SetGraphics("Viewport", [...], GFXOV_MODE_Viewport, viewport);
The viewport would not have the size of the object shape, but either a custom size of the size of the largest player viewport and then portions of it could be drawn onto each object that sets this viewport.
If you ensure that your new viewports are drawn before the player viewports, you are also not lagging a frame behind. Only if the viewport object is repeated in itself (or is contained in another viewport), there will be lag by one frame per drawing. Also you can have a priority to be set by script, so scripters can ensure multiple viewports are drawn in the correct order (e.g. when you have windows and a mirroring lake, you may want to draw the window viewports before the lake).
I would still be strongly opposed to re-implementing splitscreen on a script level. That would pull a ton of engine internals into script.
Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill