Jump to content
The Dark Mod Forums

Recommended Posts

I think it is now time to start looking at a better filter system, since the Model Preview is almost useless when it displays the collision hull instead of the model geometry. The user.xml file would seem a good place to put filter criteria, and would provide better functionality than the current hard-coded method.


There are two types of filter at the moment - entity class ("hide everything that is a light") and texture ("hide everything textured with caulk"). This could be expressed in the XML as a series of rules:


 <filter name="World">
<filterCriterion type="eclass" match="worldspawn" action="hide" />
 <filter name="Clips">
<filterCriterion type="texture" match="textures/common/collision" action="hide" />
<filterCriterion type="texture" match="textures/common/clip" action="hide" />


Each filterCriterion is applied in top to bottom order, and may either hide or show matching objects. In this case there is one entity class filter, which hides every entity class matching "worldspawn" (of which there is only one entity), and a single texture filter which hides both the collision and clip textures. Filter criteria that "show" their matches might be useful in some cases:


  <filter name="Entities">
<filterCriterion type="eclass" match="*" action="hide" />
<filterCriterion type="eclass" match="worldspawn" action="show" />


In this example the first criterion hides everything, and the second re-shows the worldspawn. The result is a filter which hides all entities but leaves map geometry visible.


Once that filter criteria can be specified, the GlobalFilterSystem API would be modified to allow objects to query whether a given entity class or texture should be displayed, based on the currently-selected filters. For example, each surface in a Model would query its own texture against the FilterSystem to check whether it should be displayed or not. If the "Clips" filter was active, the surface in the model with "textures/common/collision" would get a negative response from the filter system and would therefore not be rendered.

Link to post
Share on other sites

Well a clean-sweep of the FilterSystem turns out to be too complex, unsurprisingly since the interface is used all over the application. This will have to be an extend-migrate-remove job, with two filter systems in use for a while.


I think I will proceed as follows:


1. Extend the FilterSystem interface to provide the required new methods.

2. Add a top-level "Filter" menu, which is populated by the FilterSystem implementation.

3. Get the FilterSystem using its rules, and add functionality to the ModelSelector preview to query for texture filters.

4. Migrate Brush, Patch and Entity code to use the new FilterSystem.

5. Remove old FilterSystem


I wonder if it would also be a good opportunity to move the FilterSystem into its own DLL - theoretically anything that uses a Module interface can be in a separate binary, which might reduce build times.

Link to post
Share on other sites

OK, the first part of the new filtersystem is implemented. Texture filters can be specified in the game file, are available in the Filter menu and are used by the Model Selector to hide surfaces (this enables models with collision hulls to be viewed properly).


Still to implement are entity and object-based filters, and the conversion of brush, patch and entity render code to use the new filtersystem (which is expected to be a large task).

Link to post
Share on other sites

Entity class based filtering is now available. An example filter, "Hide lights" has been added to the doom3.game. This is implemented at the scene graph traversal stage, so does not require specific support from each entity type (as the previous system did).

Link to post
Share on other sites



Unfortunately Scons doesn't detect when you remove an include file, only when you modify it. Hence, removing this file did not trigger a recompilation and so I didn't realise there were still files including it.

Link to post
Share on other sites
  • 3 weeks later...

The FilterSystem is now moved to a separate module (DLL). Since this is quite a simple module and actually includes comments, it might be helpful to anyone who wants to understand how modules work (i.e. the Interface and Dependencies classes, static SingletonModule instance which registers itself with the ModuleServer and so on).


I was thinking that it might be worth trying to move the XML Registry into its own module which would be instantiated BEFORE the GlobalRadiant module. This means that code within the Radiant module's scope (such as MainFrame_Construct() and similar methods) would be able to access the XML registry via a module interface rather than using the registry() shortcut.

Link to post
Share on other sites

I've turned the XMLRegistry into a module now, but I found this problem:


In main.cpp there is the g_GamesDialog.Init(); call that loads the appropriate game file. The problem is that this function already expects the XMLRegistry to be setup correctly, but it isn't as all the modules are initialised in Radiant_Initialise() in mainframe.cpp which is quite a few lines below the former call.


What is the plan? I don't know how deadly it is to change the order of these calls as the shader module for example expects the game file to be already located.


Should I try to implement some kind of load() method to the game file dialog, so that the actual loading is done when the modules are initialised?

Link to post
Share on other sites

I'll have a look. I am aware that these problems exist because of the order in which modules are initialised, for instance most of the GUI construction is done in the Radiant module constructor, meaning that it can't access any of the GlobalRadiant() stuff because the module hasn't finished initialising yet.


I think the general strategy will be to move as much as possible into modules and out of the functions that are called before the modules are initialised, but the precise solution to this will need some investigation.

Link to post
Share on other sites

Hm, would it be too radical to kick the support for multiple game files? Would there be disadvantages for us?


(It seems that the .game files are needed before the modules to load the game-specific shortcuts.ini and local.pref from e.g. ~/.darkradiant/doom3.game folder.)


EDIT: woah, I tried to move the game file loading process after the DLL ModuleLoad and I get even more crashes now. But it's possible that there is something screwed with my .dll, as soon as I try to call a test method (e.g. Globalregistry().dump(), which does exactly nothing, I commented everything out) the program bails out. I checked if the module is loaded correctly (it is).


EDIT^2: Ok, I probably missed something: I just tried to implement a module named "testmodule" to test if I'm capable of installing any module (it has just one method called echo() that writes to std::cout) and it compiles, loads but still crashes on the echo() call. Any directions or common mistakes I could check?

Link to post
Share on other sites

Gah, I will let this rest until I have some better ideas how to solve this. I just copied over your filters.cpp file and changed the absolute minimum and it still crashes the second I call the echo() method. The point where I call the echo() method is located at the bottom of Radiant_Initialise() in mainframe.cpp where everything should be loaded.

Link to post
Share on other sites

Yes sure, hang on.


EDIT: sent to your gmail account. The testmodule is located under plugins/testmodule/. The patch creator warned me about not creating these files, so I hope you can apply this patch.


Basically, these are the changed files:

- includes/itest.h

- radiant/mainframe.cpp

- plugins/testmodule/ (folder added)

- plugins/testmodule/test.cpp

- Sconscript


Thanks for looking into this!

Link to post
Share on other sites

The problem is that nothing depends on your ModuleRef, meaning that it never gets instantiated.


By adding GlobalTestModuleRef as an additional parent in RadiantDependencies [radiant/plugin.cpp] the module gets instantiated and the test message is printed out.


Another way is to do this


GlobalTestModuleRef ref; // force instantiation
GlobalTest().echo("test"); // use the module


which is more obvious and doesn't require tracking down dependencies classes. In fact, you could put the module instantiation code in the GlobalTestModule() function, which is more consistent with the traditional Singleton pattern and would guarantee that the module is created any time you try to use it.

Link to post
Share on other sites



I obviously didn't look deep enough into the module code where the dependencies are handled. I thought every module is instantiated as soon as the dll is loaded...


Back to work then!


which is more obvious and doesn't require tracking down dependencies classes. In fact, you could put the module instantiation code in the GlobalTestModule() function, which is more consistent with the traditional Singleton pattern and would guarantee that the module is created any time you try to use it.

You mean like this:

inline TestModule& GlobalTest()
 static GlobalTestModuleRef ref;
 return GlobalTestModule::getTable();

or is the static not necessary? Without the static it crashes again, so I guess this is necessary. Or did you mean something different?

Link to post
Share on other sites

Join the conversation

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

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.

  • Create New...