Jump to content
The Dark Mod Forums

Recommended Posts

Posted

The MapExpressions are only implemented on a very basic level at the moment. They should be reimplemented in a better way.

 

At the moment I need to understand how they are used (or should be used) and how the new design should look.

 

OrbWeaver said something about a hierarchy of polymorphic objects and an IMapExpression interface.

The IMapExpression interface sounds like a module, I'm not sure whether a completely new module is appropriate for such a special thing as MapExpressions.

Polymorphic objects sound good, but I'm not sure how the hierarchy should be done, the current implementation seems a bit trival, perhaps there is a better solution.

 

Is the functionality of the MapExpressions (addnormals, hightmap, etc.) implemented somewhere already, or do I have to write those from scratch too?

 

So that's where I currently am, tell me what you think.

  • Replies 314
  • Created
  • Last Reply

Top Posters In This Topic

Posted

OrbWeaver knows best about the current MapExpressions hierarchy, I know more about the ShaderSystem itself, which is surrounding all that stuff.

 

As for the functionality: the algorithm for addnormals() is nowhere implemented so far (this will need some serious thinking and/or googling I guess), but I have saved a heightMapCreator code snippet in the shaders module, which looks functional (haven't tested it).

Posted
OrbWeaver said something about a hierarchy of polymorphic objects and an IMapExpression interface.

The IMapExpression interface sounds like a module, I'm not sure whether a completely new module is appropriate for such a special thing as MapExpressions.

Polymorphic objects sound good, but I'm not sure how the hierarchy should be done, the current implementation seems a bit trival, perhaps there is a better solution.

 

All modules have an interface, but not all interfaces are modules. I mean an interface similar to the following (within the shaders module):

 

class IMapExpression {
public :
virtual ImageConstructor& getImage()  = 0;
}

 

Where ImageConstructor is the lazy-evaluating image construction object that Greebo implemented in the shaders module.

 

Individual map expression types would then implement this interface, so that you would have a HeightMapExpression, an AddNormalsMapExpression and so on. When the getImage method was called, the map expression subclass would recursively evaluate any "child" IMapExpressions it contained, which in most cases would be a simple texture name but could also include any number of recursive calls to addnormals() etc, and then perform its own class-specific image processing on the results in order to return its own "flattened" image.

 

How best to construct the parse tree requires some consideration too, the process is based around a stream of tokens retrieved from a parser::DefTokeniser object. I am not immediately sure whether each MapExpression subclass should construct itself from a token stream, or whether there should be a separate service class which reads the token stream and constructs the relevant MapExpressions explicitly.

Posted

After thinking about a service class, I am not sure anymore whether it would be easier with an extra service class.

This extra service class would have to seperate between the different Tokens and work differently for different MapExpressions (as the different MapExpressions need different input).

So I think implementing a service class that differenciates between the different MapExpressions and parses them the input they need would end in one big service class that does all the work and very small MapExpression classes that could as well be only functions. (the polymorphism wouldn't get used).

 

MapExpressions that call each other are perhaps still better.

Posted

Well you're going to need some kind of factory class obviously, in order to create the correct MapExpression class from a given token, but you are correct that the MapExpressions will need to parse their own token stream, otherwise as you say the logic will all be in the service class and the MapExpressions won't really do anything.

 

You could have a static method in the base class, e.g.

 

class IMapExpression {
public:
boost::shared_ptr<IMapExpression> createForToken(const std::string& token);
}

 

which would look up the string token in a static std::map and return a new instance of the correct MapExpression. The parsing code in each MapExpression would then need to detect that there was a recursive expression inside it, and call this function to create an instance of the required class.

 

This means that for parsing the expression

 

map addnormals(textures/blah, heightmap(textures/bleh))

 

the sequence would be

 

1. Shaders code detects the "map" keyword which introduces a map expression, then parses the next token.

2. Token is passed to the static factory method to create the required MapExpression. It looks in its map and constructs an AddNormalsExpression which is returned to the shaders code.

3. Shaders code passes the tokeniser to AddNormalsExpression.

4. AddNormals expression reads the "(".

5. AddNormals expression reads the next token, and detects it is a texture. This is added as the first child for later processing (you might want an ImageMapExpression that is another subclass of IMapExpression representing a single texture name).

6. AddNormalsExpression reads the ",".

7. The next token is heightmap, which requires a new MapExpression. The factory method is called once again, and returns a HeightMapExpression.

8. AddNormalsExpression passes the current tokeniser to the new HeightMapExpression.

9. HeightMapExpression parses the bracket, texture name and bracket. It adds the texture as its single permitted child.

10. The construction of HeightMapExpression is complete. Control returns to the AddNormalsExpression which parses the final ")".

11. Construction of AddNormalsExpression is complete. Control returns to the shaders module, which now has a complete parse tree representing the map expression in its entirety.

12. Shaders module continues where it left off, reading tokens from the Tokeniser.

Posted

With such a createForToken function I wouldn't even need an extra factory class. The method would simply be called for the base class by the Shader code, that's cool.

And I think that this single function should already suffice as the public interface for the class.

I will try/start to implement it tomorrow, I guess the next big task will be the MapExpressions themselves.

Posted

Would a

boost::shared_ptr<IMapExpression> createForToken(parser::DefTokeniser& tok)

external friend function also be acceptable instead of another class? I got stuck while trying to use this function as a member of class IMapExpression, because you can't instanciate a class that is has vitual, but need to, because the createForToken is member of it. So simplest solution would be to make it external, but friend. Is that acceptable?

Posted

There is no need for a friend function, this is the wrong idiom. A public static function is equivalent to a C function at file scope, in that it can be called without any class instance, e.g.

 

IMapExpressionPtr expr = IMapExpression::createForToken("heightmap");

 

It doesn't have to be associated with a class as far as the compiler is concerned, but this is a stylistic device used for tidiness. There is going to need to be a static map associating IMapExpression subclasses with string tokens, and this should be a member of the same class.

 

Essentially, the IMapExpression interface is also functioning as its own factory class.

Posted

Hey, that is what I tried to do before, but then the compiler told me that it is plainly impossible to call functions from non instantiated classes, now I know better ;) . Cool, learned another thing, thanks. :rolleyes: This way it is of course nicer.

Posted

I, too, would have been unceremoniously thrown onto the street a long long time ago if we had strict deadlines to stick to. :) Get things done as quickly as you can by all means, but don't stress out if you don't have time.

My games | Public Service Announcement: TDM is not set in the Thief universe. The city in which it takes place is not the City from Thief. The player character is not called Garrett. Any person who contradicts these facts will be subjected to disapproving stares.
Posted

I'm afraid I'm even worse. I had to drop off the forums for three weeks due to finals and vacationing in Sydney. I've yet to do anything since I've gotten back (apart from playing Twilight Princess). Deadlines are my kryptonite. :)

Posted

Good to hear that. :)

 

I didn't follow the discussion above very closely, but maybe a small sketch of the system to be implemented might prove useful here? We did that back when we redesigned the ShaderSystem, and it was good to have some plans around - also for later reference and/or the wiki.

 

Not a must of course, just a suggestion.

Posted

Well, what I have since now is:

 

Base class:

 class IMapExpression {
   public:
	   static boost::shared_ptr<IMapExpression> createForToken(parser::DefTokeniser& tok);
	   virtual TextureConstructor& getImage() = 0;
   };

 

specific Map Expressions then look this way:

class HeightMapExpression : IMapExpression {
	   shared_ptr<IMapExpression> argOne;
	   float argTwo;
   public:
	   HeightMapExpression (parser::DefTokeniser& tok);
	   TextureConstructor& getImage();
   };

 

implementation for createForToken:

boost::shared_ptr<IMapExpression> IMapExpression::createForToken(parser::DefTokeniser& tok) {

	 // Switch on the first keyword, to determine what kind of expression this
	 // is.
	 std::string token = boost::algorithm::to_lower_copy(token.nextToken());

	 if (token == "heightmap") {
		 return shared_ptr<IMapExpression> p(new HeightMapExpression (tok));
	 }
	 else if (token == "addnormals") {
		 return shared_ptr<IMapExpression> p(new AddNormalsExpression (tok));
	 } ...

 

I'm not entirely sure where the images should be saved, inside the structures? Also not sure when they should be processed. Either when creating the class or when getImage is called. So, comments please :rolleyes:

Posted

Forgot, there is also an "ImageExpression" which just returns an image.

 

Edit: is there a way to upload files (.h and .cpp)? That would make things easier, as I could just paste the files I've written so far.

Posted

The DarkRadiant directory will be good enough for this, it's just the snapshot ZIP lying around there, so feel free to drop it there.

 

The standard Doom3 files can be enough I think, just do a grep for addnormals() or heightmap in the material files, I think they are in pak001.pk4 or pak002.pk4.

Posted

So here is my current progress:

MapExpressionnew.cpp

MapExpressionnew.h

 

I just looked at the surrounding files and I think I will have to change some other files too to make them work with the new interface, right? Then this should be done first, so I can test the code I write.

 

 

Should the processed images be saved at all (or just created every time the Shadersystem requests one) and if yes, where?

In the Constructor: all MapExpressions would be evaluated, independent of their usage

getImage could also be defined in the base class

in getImage: either the MapExpression is recreated everytime it is asked for, or saved (either in the class or pushed to the texture manager)

 

I think in getImage would be better. Different from the files above, I think it would be better to save the results instead of the arguments.

Edit: I changed MapExpressionnew.h to reflect this.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recent Status Updates

    • HEX_OX12

      ai have information and ai want to sell them
      · 0 replies
    • jivo

      I just uploaded a new version of the Visible Player Hands mod. It's been updated for TDM 2.13 and has new animations. Check out the post if you're interested!
      · 0 replies
    • datiswous

      I moved from Manjaro Linux (rolling release) to Linux Mint (LTS). One of the reasons was that I found the updates a bit too often and long. But now on Mint I get updates every day, although they're usually small updates.
      · 3 replies
    • JackFarmer

      "Hidden Hands: Vitalic Fever" - new update available including subtitles & compressed briefing video (thanks to @datiswous) and several fixes.
      · 0 replies
    • Wolfmond

      🇬🇧

      2025-04-20
      I'd like to track my level design progress a bit more often now, so I'm using the feed in my profile here.
      I've been working intensively on Springheel's YouTube course over the past few days. I'm currently up to lesson 8. There is so much information that needs to be processed and practiced. 
      I have started to create my own house. As I don't have the imagination to create a good floor plan, I grabbed a floor plan generator from Watabou and experimented with it. I chose a floor plan that I will modify slightly, but at least I now have an initial idea. 
      I used two guards as a measuring tape: The rooms are two guards high. It turned out that I can simply double the number of boxes in DarkRadiant in grid size 8 that are drawn in the floor plan. 
      I practiced the simplest things on the floor plan first. Drawing walls, cutting walls, inserting doors, cutting out frames, creating VisPortals, furnishing rooms.
      I have had my first success in creating a book. Creating a book was easier than I thought. I have a few ideas with books. The level I'm creating will be more or less a chill level, just for me, where I'll try out a few things. I don't have an idea for my own mission yet. I want to start small first.
      For the cellar, I wanted to have a second entrance, which should be on the outside. I'm fascinated by these basement doors from the USA, I think they're called Bilco basement doors. They are very unusual in Germany, but this type of access is sometimes used for deliveries to restaurants etc., where barrels can be rolled or lifted into the cellar. 
      I used two Hatch Doors, but they got completely disoriented after turning. I have since got them reasonably tamed. It's not perfect, but it's acceptable. 
      In the cellar today I experimented with a trap door that leads to a shaft system. The rooms aren't practically finished yet, but I want to continue working on the floor plan for now. I'll be starting on the upper floor very soon.

      __________________________________________________________________________________
      🇩🇪

      2025-04-20

      Ich möchte nun mal öfters ein bisschen meinen Werdegang beim Leveldesign tracken, dazu nutze ich hier den Feed in meinem Profil.
      Ich habe mich in den vergangenen Tagen intensiv mit dem Youtube-Kurs von Springheel beschäftigt. Aktuell bin ich bis zu Lektion 8 gekommen. Das sind so viele Informationen, die erstmal verarbeitet werden wollen und trainiert werden wollen. 

      Ich habe mich daran gemacht, ein eigenes Haus zu erstellen. Da mir die Fantasie fehlt, einen guten Raumplan zu erstellen, habe ich mir einen Grundrissgenerator von Watabou geschnappt und damit experimentiert. Ich habe mich für einen Grundriss entschieden, den ich noch leicht abwandeln werde, aber zumindest habe ich nun eine erste Idee. 

      Als Maßband habe ich zwei Wächter genommen: Die Räume sind zwei Wächter hoch. Es hat sich herausgestellt, dass ich in DarkRadiant in Gittergröße 8 einfach die doppelte Anzahl an Kästchen übernehmen kann, die im Grundriss eingezeichnet sind. 

      Ich habe bei dem Grundriss erstmal die einfachsten Sachen geübt. Wände ziehen, Wände zerschneiden, Türen einsetzen, Zargen herausschneiden, VisPortals erstellen, Räume einrichten.

      Ich habe erste Erfolge mit einem Buch gehabt. Das Erstellen eines Buchs ging leichter als gedacht. Ich habe ein paar Ideen mit Bücher. Das Level, das ich gerade erstelle, wird mehr oder weniger ein Chill-Level, einfach nur für mich, bei dem ich ein paar Sachen ausprobieren werde. Ich habe noch keine Idee für eine eigene Mission. Ich möchte erst einmal klein anfangen.

      Beim Keller wollte ich gerne einen zweiten Zugang haben, der sich außen befinden soll. Mich faszinieren diese Kellertüren aus den USA, Bilco basement doors heißen die, glaube ich. Diese sind in Deutschland sehr unüblich, diese Art von Zugängen gibt es aber manchmal zur Anlieferung bei Restaurants etc., wo Fässer dann in den Keller gerollt oder gehoben werden können. 
      Ich habe zwei Hatch Doors verwendet, die allerdings nach dem Drehen vollkommen aus dem Ruder liefen. Inzwischen habe ich sie einigermaßen gebändigt bekommen. Es ist nicht perfekt, aber annehmbar. 
      Im Keller habe ich heute mit einer Falltür experimentiert, die zu einem Schachtsystem führt. Die Räume sind noch quasi nicht eingerichtet, aber ich möchte erstmal am Grundriss weiterarbeiten. In Kürze fange ich das Obergeschoss an.



      · 2 replies
×
×
  • Create New...