Not logged inOpenClonk Forum
Up Topic Development / Scenario & Object Development / Enterable buildings
- - Date 2015-01-09 19:40
Parent - - By Marky [de] Date 2014-11-30 23:45
With my recent activity in OC development some thoughts occurred to me. For the second chapter of Harkon, and for some of my own ideas, we would need enterable buildings and cave systems.
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.
Parent - - By Sven2 [de] Date 2014-12-01 00:08
Layers were originally implemented exactly for this purpose. However, it turned out that it's much easier to just create the interior of buildings in a separate area of the landscape.
Parent - - By Marky [de] Date 2014-12-01 17:18
It is "easier", yes, but that depends on the way you want to use the buildings. Teleporting to that separate location, with the building surrounded by black background for example is very easy. Doing something as in Harkon where you seem to switch inside the building without teleporting is more difficult, and it is necessary to overload some engine functions because you have to adjust things after the teleport. This can become messy if other people overload engine functions as well.

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?
Parent - By ala [de] Date 2014-12-01 18:20 Edited 2014-12-01 18:30
Good point, talking from my "spawn protection" scripts for the league scenarios - where I put new generated clonks into new layers for a couple of frames - I can tell you there were a tons of things ignoring layers in Clonk Rage. Basically all effects / spells - and also in general most timer based objects using FindObject.

Maybe it is an idea to overload FindObject in general to NOT get different layered objects per default?
Parent - - By Sven2 Date 2014-12-01 18:31
The problem is similar to that of creating a "wrap-mode" in which the landscape wraps around: It's easy to implement the basic stuff like graphics wrapping and objects teleporting (In fact I built that once for CR), but it's hard to adjust a thousand scripts and places in the engine that aren't aware of wrapping.

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!
Parent - - By Sven2 Date 2014-12-01 18:34
The "background" position could also have a Z property so only stuff up to a certain plane is drawn. I.e., you would draw stuff behind the house but not stuff in front of the house.
Parent - By Marky [de] Date 2014-12-01 18:51
That's also a cool idea, and probably the better way to do it. Also, in that version one could have a multi-layer landscape with the "map split in two"-version of the implementation that I proposed earlier. Pretty neat actually :D
Parent - By Marky [de] Date 2014-12-04 21:51
So, no new ideas? I actually liked the viewport idea. It has the same issues as finding objects on different layers though - you have to look at different coordinates instead of different layers.

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
Parent - - By ala [de] Date 2014-12-01 18:42 Edited 2014-12-01 18:47

>. 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.
Parent - By Clonkonaut [de] Date 2014-12-01 21:46

> 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.
Reply
Parent - - By Sven2 [de] Date 2014-12-01 22:27
Iirc, FindObject in CR does behave like that (because I had exactly the same reasoning). The functionality was lost with the introduction of FindObject2 (which is now FindObject in OC).

Additionally, I think CreateObject would default to the creators layer.

There were still tons of cases where this was not enough.
Parent - - By Clonkonaut [de] Date 2014-12-01 23:54
That'd mean every Acquire command would fail if the target object is in a house.
Reply
Parent - - By Sven2 [de] Date 2014-12-02 01:00
Would need some TransferZone adjustments. Though the current Harkon solution would need that, too.
Parent - By Clonkonaut [de] Date 2014-12-02 10:12
Wouldn't it need more than that, would it? The clonk first needs a method* to even find the object before it could set such a command. Then it would hopefully enter the TransferZone and be safely guarded into the house.

* FindObjectOnAllLayers();
Reply
Parent - - By Marky [de] Date 2015-01-09 14:18
In the last discussion Randrian brought up an improvement of the viewport idea: Create an object that draws a certain part of the map on its shape.

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.
Parent - - By Newton [de] Date 2015-01-09 15:25
Why don't you use object layers to implement building interiors? When anything enters the building, its object layer is changed so objects from the outer world have no impact on them. Everything within the building is only visible to those that are inside the building. Solid masks probably don't work with layers, but as long as the buildings consist of rectangular shapes, it shouldn't be a big problem to implement walls via script.
Parent - By Marky [de] Date 2015-01-09 17:20
That was the original idea: A solid mask that counts for the object layer. A scripted wall is similar. Now, what if someone builds a loam wall while he is inside the building? The players outside the building bump into that, too.
Parent - - By Sven2 [de] Date 2015-01-09 19:09
Building interiors were actually exactly why I originally implemented object layers. I then abandoned the concept because I realized that it would always be buggy and require lots of extra code and special handling fro all kinds of objects. I would not recommend using them this way any more.
Parent - - By Newton [de] Date 2015-01-09 22:42
Yeah, it is quite bug prone. It requires the scripter of every object (that uses FindObject and similar) to keep this in mind.

Is it used for anything else? Is it used for anything in CR? If not, perhaps we could remove it altogether then?
Parent - - By Zapper [de] Date 2015-01-09 22:54

>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).
Parent - By Sven2 [de] Date 2015-01-09 23:29

> 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.
Parent - By Marky [de] Date 2016-01-04 22:42

>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.
Parent - - By Marky [de] Date 2015-01-09 22:55
In CR I liked the fact that hit detection and interaction such as grabbing was possible only with objects in the same layer. This made deco buildings with no function possible without a lot of work (create hut, sawmill, whatever, set to own layer, done). This still can be achieved with SetGraphics().
Parent - By Sven2 [de] Date 2015-01-09 23:15
Yes, that's where I see its main use. SetGraphics is not a full replacement as it doesn't really allow to have active buildings (or e.g. NPCs interacting with buildings in background).

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.
Parent - - By Clonkonaut [de] Date 2015-01-09 16:08
Clonk enters building -> SetPosition(150, 140);
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.
Reply
Parent - - By Sven2 Date 2015-01-09 16:34 Edited 2015-01-09 16:38
The camera always follows the clonk (although a parameter is needed to make the camera move instantaneously when a clonk enters the building). There is one view offset per (real) viewport and "viewport objects" just keep their landscape offset and add the viewport offset to that. I don't see a problem.

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.
Parent - By Randrian [de] Date 2015-01-09 18:08
Even if you have the outside as a background around the inside, you have issues with camera movement when switching between different clonks.

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.
Reply
Parent - By Clonkonaut [de] Date 2015-01-09 18:36
Ah, I get it. You always use a viewport object for stuff that's "outside the clonk's plane". Yeah, that could work.
Reply
Parent - By Marky [de] Date 2015-01-09 19:10
How much of a performance hit do you estimate? I imagine it like opening another viewport window for every player in editor mode, which does not do much. Of course this does not draw unto a viewport again.
Parent - By Marky [de] Date 2015-01-09 17:49
I made a short explanation what I wanted to do. The landscape is split in half. The lower half has a giant viewport object that renders everything which happens on the upper half (pink). In the lower half there is also the interior view of the building. In the upper half there is another viewport that renders what is inside the building (orange).
Parent - - By Randrian [de] Date 2015-01-09 18:12
I think the system as you describe it would be very flexible and cool to use. Perhaps the view could even be transformed by SetObjTransform, which could make smaller background landscape layers possible or a mirroring lake,...
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.
Reply
Parent - - By Sven2 [de] Date 2015-01-09 19:14
Well, calling the rendering routine a second time is obviously a lot less error-prone than calling it recursively. For the latter, care must be taken that code does not rely on static variables it modifies, etc. in current and future code.

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.
Parent - - By Marky [de] Date 2015-01-09 19:40
How would you define where to draw the "secondary background view"? Implement this could also be the first step, imo. The interior of buildings displayed through windows can be done with multiple graphics layers as Randrian already did.
Parent - - By Sven2 [de] Date 2015-01-09 23:26
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 }

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 :)
Parent - - By Marky [de] Date 2016-01-04 23:36

>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.
Parent - By Günther [de] Date 2016-01-05 02:11

> 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.
Reply
Parent - - By Clonk-Karl [us] Date 2016-01-05 05:53

> 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).
Reply
Parent - By Marky [de] Date 2016-01-05 13:53
Yes, that would make sense. At first I'll start with testing and possibly fixing #1458, because it sort of goes in the same direction.

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
Parent - - By Marky [de] Date 2016-01-07 22:02
I'd change bool C4ViewportList::CreateViewport to return the created viewport object, because I think the viewport object needs a reference to the viewport that it creates and the boolean return value of the original function is never used.
Parent - - By Sven2 [us] Date 2016-01-07 23:33
Why do you need to care for viewport creation? Do you actually want to keep an extra C4Viewport around for every viewport object times every open viewport? That sounds like a complex and potentially very error-prone solution. You could just call add parameters to the drawing function of the viewport to run on different state. If they are members, just subclass the relevant viewport members and pass that modified class to Draw.

(Also, just having the background offset sounds much easier to implement)
Parent - - By Marky [de] Date 2016-01-08 08:44
I wanted a C4Viewport for every viewport object, so that it can take the information from that viewport and draw it on it's overlay graphics, instead of color information. If I can take that information from an existing viewport without owner somehow that would be great, because I would not need any new viewports. You may have noticed me using "somehow" very often - this is because I rarely modify engine features and the engine code is quite confusing if you want to understand more than the function you are currently looking at.
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.
Parent - By Sven2 [us] Date 2016-01-08 15:13 Edited 2016-01-08 15:19
Okay, now I understand the idea. I thought you just wanted the C4Viewport as a proxy to route the drawing through. Instead, you basically want extra C4Viewports in the list that do not actually have a window open, but store their drawn surface so it can be used as a source by objects. That's actually neat because you could reuse these viewports in multiple objects (for example, multiple windows of a house) and apply transformations. In this case, extra C4Viewport without window may actually be a good solution.

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.
Parent - By Clonk-Karl [us] Date 2014-12-01 18:39
For solidmasks, we could consider whether instead of drawing them into the landscape, the GetPix() function could check the solidmask of all objects before checking the landscape. The big question would be whether this would degrade the performance significantly, but it would probably easily allow layered solidmasks as well as solidmasks that can only be transferred in only one direction.
Reply
Up Topic Development / Scenario & Object Development / Enterable buildings

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill