Jump to content
The Dark Mod Forums
Sign in to follow this  
STiFU

Understanding the Code

Recommended Posts

Implementing the Readable Editor: Status and notes

 

Implemented:

  • Don't allow typing of forbidden characters in inventory- and xdataname.
  • Only allow typing of numbers in numPages-entry. Zero should be forbidden.
  • While editing numPages, if you press escape, the old value will be restored.
  • Changing the pagelayout will eventually hide the two right textviews and the headlines "Left" and "Right".
  • Switching pagelayouts should not discard text.
  • A newly created page will be initialized with the same gui as the page before.
  • After entering an XData name, it will be checked whether that name is unique. If it's not a popup will ask the user whether the existing definition shall be imported. If it's not imported, another name is suggested (numbers appended).
  • If a new XData definition is created an XData name should be suggested, like: readables/[MapName]/<enter name>
  • Provide method for inserting a page before the current page.
  • Further extension of the insert/delete feature for twosided readables. It should be possible to inser/delete single sides.
  • Provide method for deleting a page.
  • Comfort feature: If the current page is an end of the readable (beginning or end) and the user clicks the switch-page-button of the "end-direction", a little pullup-menu could be shown, asking whether a page shall be inserted.
  • Tools: "Show last Import summary"
  • Tools: "Show duplicated Definitions"
  • Tools: "Show gui definition summary"
  • Derive pagelayout from entity. Greedy approach: Check whether entity name contains "book".
  • Make sure XDataName is set properly before saving.
  • New XData is now stored in the [mod_path]/xdata/ directory.
  • The GUI-browser will only list definitions that match the page-layout.
  • Hand-typed gui-definitions should be checked, whether they match the pagelayout.
  • Changing page-layout in the gui-browser should change pagelayout on the ReadableEditorDialog and preview accordingly.
  • Choosing a different GUI will update the preview. Optimally while the gui-chooser dialog is still opened.
  • Limit gui manager filevisitor to the readables folder. It just takes too long to parse all definitions and we don't gain anything from it.
  • XData-Browser previews the selected XData definition.
  • Fix issue with crash upon previewing certain readables in the XData browser.
  • Add loading definitions screen.
  • Maybe add options to the DR preferences menu in which the user can specify in which folder xdata definitions are supposed to be stored.
  • Tweak the preview: Cropping and gray background.

 

Postponed to future releases:

  • Notify the user when he typed a character that is not available in the font.
  • Option to check syntax of a specified file.
  • Option to duplicate and freeze preview or a general translator's dialog.
  • If the end of a page is reached in the preview, the editor should automatically jump to the next textView field.
  • Reducing page count via the spinbutton, should not delete the content directly so that the user can revert, if necessary.

 

 

Original Post:

I want to create a readable editor editor for DR. Up until now, I have only coded in medium- to small-sized projects, so I will probably need some help along the road. I am really interested to do this currently, because I guess it will teach me a lot... Invaluable experience, you know? :)

 

For now, I have created a new project in the Visual Studio solution called "dm.readable" and pasted the include- and libraryfolders from one of the other dm-plugins. DR compiles fine, so I am basically good to go. Right now, the project is a console application, which should make the initial developing phase a little easier for me.

 

Decoding the .dat files already works fine, but I need a little help figuring out how to load the font textures. I read everything DR-coding-related in the wiki and so I decided to include iimage.h and use the inline function GlobalImageLoader to request an Imageloader for TGA:

ImageLoaderPtr LoaderTGA = GlobalImageLoader("TGA");

 

But when I build and run this, I get an error saying "Assertion failed: _registry, file c:\dark_radiant_svn\include\ imodule.h, line 271". So I'll probably need to figure out how to initialize the image module and how this whole module-thingy in general works. I am going to look into that later, because I'll have to start learning now, but if someone can point me into the right direction here, that'd be swell!! :)

 

I also tried including "ImageFileLoader.h", but I'd have to add another includefolder for this and I don't know if I am supposed to do that. I should only use the includefolder for Modules, right?

Share this post


Link to post
Share on other sites

You won't have much luck in developing a console application - all the code is relying on the ModuleRegistry located in the core DarkRadiant binary, so getting this to work is a pretty chanceless adventure. The Global* methods request the module reference from the GlobalModuleRegistry, and this one is initialised by the code module at startup. All the modules more or less depend on each other so you can't just load a single DLL and use it - they need to be properly initialised, in the correct order, which is handled by the main module.

 

The approach of compiling a separate module is still the best, of course, so this is how I'd advance:

 

- Copy the existing folder dm.editing to dm.gui (the name is more general than readables). (Make sure you don't copy the .svn folders over or remove them after copying, otherwise Tortoise might get confused).

- Copy the existing dm.editing.vcproj to dm.gui.vcproj (it should still remain in the tools/vcprojects folder).

- Then add the project to the solution via "Add" > "Existing Project". Immediately rename the project as long as it is selected (F2), as it will still be called "dm.editing" right after import. Name it dm.gui.

- You'll end up with a project that is mostly set up with all the headers and libraries you need.

- Then remove all the files you don't need from the project.

- Then start to rename the classes and strings to fit your needs.

 

Try to compile and try to place a breakpoint in the initialiseModule() method. It should be loaded sometime during DarkRadiant's startup phase.

 

If you're stuck, I can provide you with such an empty module project.

Share this post


Link to post
Share on other sites

Ah, tanks for the info. Yeah, the project was going to be turned into a module eventually, but I thought development might be easier if I start with a console application. I mostly prefer coded outputs instead of checking variables at breakpoints, although it's basically slower. I guess I will have to get used to the latter now... :) I'll try to setup my own empty module later then. Do I have to tell DR somewhere that it's supposed to load my module as well?

Share this post


Link to post
Share on other sites

Do I have to tell DR somewhere that it's supposed to load my module as well?

No, DarkRadiant will try every DLL found in the plugins/ or modules/ folder, so it should happen automatically.

Share this post


Link to post
Share on other sites

Ok nice. I got it working. Good thing printf() is still linked to the console and globalOutputStream() is also useful. :)

 

One question about the VFS for later: Are all files loaded or are they filtered by file-extension? I am asking because I didn't find the readable-guis of TDM in the mediabrowser, nor the corresponding .dat-files for the fonts and I'll need those later.

Share this post


Link to post
Share on other sites

One question about the VFS for later: Are all files loaded or are they filtered by file-extension?

You can use a FileVisitor class to traverse certain types of files in all the VFS. Have a look at the sound module or the particles module, there are some examples of FileVisitors.

 

I am asking because I didn't find the readable-guis of TDM in the mediabrowser, nor the corresponding .dat-files for the fonts and I'll need those later.

The fonts are not read by DarkRadiant at all, neither are the GUI files. DarkRadiant cannot render any of the fonts yet - this is to be implemented (as GlobalFontManager module and in the GlobalRenderSystem too).

Share this post


Link to post
Share on other sites

I am currently writing the importer/exporter for xdata files and I need to know whether comments are supported and how they are declared. So far I didn't see any, but if I implement it without comment-support, my class would probably send a SyntaxErrorException.

Share this post


Link to post
Share on other sites

Yes, comments are supported. I assume you're using the existing DefTokeniser class to parse it?

 

On a general note, if you like I can give you SVN write access, so that you can commit your changes while you're working on the project (in a separate SVN branch of course). This way I could also keep track of your code and add some suggestions along the road.

Share this post


Link to post
Share on other sites

Yeah that'd be sweet. Then I won't need to backup the sourcefiles manually anymore... :)

 

I didn't know though you guys already had a parser compatible with xdata. I thought, if you don't have a readable editor, you won't have such thing either. I was gonna parse it manually, but I'll look into that class later then.

Share this post


Link to post
Share on other sites

Yeah that'd be sweet. Then I won't need to backup the sourcefiles manually anymore... :)

You'll need a sourceforge account for that, you need to tell me the username, then I can grant permissions to the repo.

 

I didn't know though you guys already had a parser for xdata. I thought, if you don't have a readable editor, you won't have such thing either. I was gonna parse it manually, but I'll look into that class later then.

Not for xdata specifically, but for tokenising declaration files like materials, defs, etc. They are very similar and support C++-valid comments.

 

Take a look at the DefTokeniser in libs/parser and the parsers in ParticlesManager::parseParticleDef or ShaderFileLoader::parseShaderFile. There is a corresponding exception named parser::ParseException which can be thrown on syntax errors.

 

The actual xdata parser in the game is in the TDM source, see declxdata.cpp.

Share this post


Link to post
Share on other sites

Ok thanks, I'll have a look at those later then. No need to reinvent the wheel!! :)

 

Here is my new sourceforge account: username schtifu, public name STiFU

(Damn it, username stifu already existed^^)

 

Which branch should I use then? Also, do you generally commit after every work-period or just when a source file has significant changes?

Share this post


Link to post
Share on other sites

Ok thanks, I'll have a look at those later then. No need to reinvent the wheel!! :)

Definitely not, but it's admittedly harder to understand existing code than writing your own. Just ask if you hit a wall.

 

Here is my new sourceforge account: username schtifu, public name STiFU

(Damn it, username stifu already existed^^)

I added your account, you should have subversion access now.

 

My username was taken already as well, back in 2006, but that's not surprising for such a name. In retrospect I'd have chosen a different one, but who cares anyway.

 

Which branch should I use then?

This one: https://darkradiant....nches/readables

 

Also, do you generally commit after every work-period or just when a source file has significant changes?

I've found it's best to commit small steps, but there are no hard rules. For the trunk the code each revision should at least compile, for branches this rule is not as important (nobody will check out branches unless they know what they're doing). Don't hold back commits just because it's not 6 PM yet, there's no reason not to commit multiple times per hour if it makes sense, code-wise.

 

Each commit should only contain a fix for one tracker issue at a time if possible, so it should at least have a specific context. There's no reason not to commit small changes, but I don't commit fixed line indentation, typos in documentation or code formatting changes just on their own (I save these for the next substantial commit). Keep it as reasonable as possible.

 

Just imagine having to go back in SVN history to test where a specific commit broke something (I had to do that more than once in the past) - with huge block commits this is really, really tedious, with small commits this is much easier to perform.

Share this post


Link to post
Share on other sites

Yeah I think so too, but I thought I'd ask what the commons around here are. Don't want anyone to get pissed because of anything I do... :)

 

Definitely not, but it's admittedly harder to understand existing code than writing your own. Just ask if you hit a wall.

Hey, really nice of you, to give me so much support. Thanks!

 

My username was taken already as well, back in 2006, but that's not surprising for such a name. In retrospect I'd have chosen a different one, but who cares anyway.

I always thought mine was pretty unique until I discovered that there is a character called Stifu in the french version of Dragon Ball and eventually more people started naming themselves like that, probably also because of "STFU". But well, I've been using this alias since 15 years now and I won't change it. I like it and many of my friends call me like that sometimes... :) Other than that also Stiff, Stiffson, Stiffmaster, Stiffzor, Stizzle... :D

Share this post


Link to post
Share on other sites

By the way, whenever I add the imageloader to the list of dependencies in plugin.cpp the program collapses eventually with an unhandled exception. Maybe you could have a look at that, now that the code is online?

 

I've also received an email after uploading to the svn saying

Your mail to 'Darkradiant-svn' with the subject

 

SF.net SVN: darkradiant:[5196] branches/readables

 

Is being held until the list moderator can review it for approval.

 

The reason it is being held:

 

Post by non-member to a members-only list

Is this configuration intentional?

Share this post


Link to post
Share on other sites

Don't know, I guess you need to subscribe to the darkradiant-svn mailing list?

Share this post


Link to post
Share on other sites

I'm still compiling the code from the branch, but I saw a few things while skimming the code:

 

- You're passing a lot of things by value. For built-in types this is ok (float, int, double, pointers), but classes should be passed by reference-to-const. Instead of func(std::string param) it should be func(const std::string& param) to avoid frequent calls to copy constructors. This includes shared_ptrs and boost::filesystem::path objects.

- This is minor: there are no newlines at the end of most files, which is something gcc will complain about, for whatever reason.

 

edit: One more: you're loading files directly from disk, using ImageLoaders. This is probably fine for now, but in the end we will want to incorporate this into the GlobalMaterialManager() class, which takes care of loading the correct file from the VFS, with proper DDS handling and so on.

Share this post


Link to post
Share on other sites

This will make your image loaders work:

 

_dependencies.insert(MODULE_IMAGELOADER + "TGA");

_dependencies.insert(MODULE_IMAGELOADER + "DDS");

 

Although we really need to have that font image loading functionality in the shaders module. I can look into that, if you can work on other things in the meantime.

Share this post


Link to post
Share on other sites

Sweet input, thanks. Especially the reference-to-const-thingy is the kind of valuable knowledge we weren't taught at the uni. This technique has also the advantage that no exceptions can be generated in a copy-constructor, I guess.

 

I haven't looked into the VFS yet, so for now I was just thinking to load them this way. One thing at a time. I planned the VFS to be dealt with as soon as I setup the GUI for my module, because I'll need it then for the filechoosers.

 

Including the font texture loading into the shaders module does make sense. Take your time, I am not in a hurry. I still have a lot of other tasks in this project and I am also working rather slow here, as you can probably tell by the complexity of my code... :)

Share this post


Link to post
Share on other sites

Sweet input, thanks. Especially the reference-to-const-thingy is the kind of valuable knowledge we weren't taught at the uni. This technique has also the advantage that no exceptions can be generated in a copy-constructor, I guess.

Constructors should never leak exceptions by design, but that's a different matter. :)

 

(If you're interested in a book recommendation, I'd strongle recommend Scott Meyers' Effective C++. It's not a syntax tutorial (you can find that amass on the net) but tips and advice including the reasoning behind it. It's a short book, 50 small chapters each with a specific objective.)

 

I haven't looked into the VFS yet, so for now I was just thinking to load them this way. One thing at a time. I planned the VFS to be dealt with as soon as I setup the GUI for my module, because I'll need it then for the filechoosers.

The VFS can only be used to access the files in the joint PK4-OS-Filesystem, but unfortunately it cannot be used in the GTK filechooser dialogs. I had a look at that in the past, but I failed because the corresponding GLIB code is not documented very well and it's too low-level for DarkRadiant to place a hook.

 

Anyway, it's always possible to populate our own treeview with available GUIs, if that's what you wanted.

 

Including the font texture loading into the shaders module does make sense. Take your time, I am not in a hurry. I still have a lot of other tasks in this project and I am also working rather slow here, as you can probably tell by the complexity of my code... :)

Ok, I will have a look at that.

Share this post


Link to post
Share on other sites

After looking at this for a while, I think I'll go ahead and create a FontManager module which is just taking care of the fonts themselves. This module is making use of the GlobalMaterialManager to load texture files and provides some methods to retrieve a RenderableFont structure, which in turn offers some methods to submit the correct commands to GL, taking position, blend type and scale as parameter. The GUI module can make use of that RenderableFont to draw stuff.

Share this post


Link to post
Share on other sites

Ah, ok. Well that was exactly the way I planed the class FontReader to work until you said, we should keep the font-texture loading in the shaders module... :D There's a method for returning the information of a requested glpyh and a method that returns the corresponding texture for use in GL. It's just not all fleshed out yet and also, I don't know yet how all that stuff in GL works. So far I only coded directdraw, when it comes to graphics.

 

If I can't use the VFS in Filechoosers, I'll just create lists allowing the user to choose files. One list each for .dat, .gui and .xd files.

 

I just talked to my dear friend namespace about that book you recommended and he's got it at home in german, so I am going to borrow it. :)

Share this post


Link to post
Share on other sites

I've just coded a first version of the XData exporter. Support for merging and overwriting files is still missing, because I didn't have a look at the DefTokeniser yet, which might be applicable here for checking...

  • ...if multiple definitions exist in the target file, so that a warning can be displayed if the user tries to overwrite such file.
  • ...if an old definition of the new passed one already exists in the target file, which should be replaced with the new definition in that case.

Doing this by hand wouldn't be a problem of course, but we want clean code... :) Both problems could also be dealt with by using the importer with the specified file, but that method does a lot more work than necessary in these cases.

 

I've also got a couple of questions:

  1. I am currently using plugin.cpp for a testbench, because it is a very quick way to test things. Is that ok?
  2. How do we deal with Quotes (") in the sourcestrings, that are supposed to go into the XData-definition? Is there an escape character for XData-definitions or will I have to replace those by apostrophes (')?
  3. I call an inline-method (XDataManager::generateXDataDef(..))with a reference-to-const parameter. Inside that method I use a dynamic_cast on that const-parameter to retrieve a non-shared_ptr. In this case it is ok to use a normal pointer, because the passed const-object only exists as long as the method is active anyway, right? The calling functions will deal with deallocating.

 

EDIT: I just saw that '\n' does exist in xdata definitions, so I tried '\"', but it's discarded. So I guess I should have the exporter remove quotes or prevent adding those in the GUI.

Share this post


Link to post
Share on other sites

You might get away by replacing " with '' (a double with two single quotes). Or does D3 support unicode? (probably not, otherwise you could use the fancy quotes that are in it somewhere.)


"The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man." -- George Bernard Shaw (1856 - 1950)

 

"Remember: If the game lets you do it, it's not cheating." -- Xarax

Share this post


Link to post
Share on other sites

I am currently using plugin.cpp for a testbench, because it is a very quick way to test things. Is that ok?

Whatever does the trick. As long as the test code doesn't end up in a release, I'm perfectly fine with having it in there, especially during this early phase in development.

 

How do we deal with Quotes (") in the sourcestrings, that are supposed to go into the XData-definition? Is there an escape character for XData-definitions or will I have to replace those by apostrophes (')?

I can't say how double-quotes are handled in the xdata, never tried that.

 

I call an inline-method (XDataManager::generateXDataDef(..))with a reference-to-const parameter. Inside that method I use a dynamic_cast on that const-parameter to retrieve a non-shared_ptr. In this case it is ok to use a normal pointer, because the passed const-object only exists as long as the method is active anyway, right? The calling functions will deal with deallocating.

You can dynamic_cast shared_ptrs just as fine, using boost::dynamic_pointer_cast. I'd have to look at the actual function though, to see whether using the raw pointer is necessary or not.

Share this post


Link to post
Share on other sites

Ok, but it was more of a general question. Although I didn't look into the dynamic cast of boost yet.

 

Do you think Gildoran would know how quotes are created in xdata definitions? He wrote the wiki article about the fileformat...

 

The importer uses the DefTokeniser now and it works really good, despite the support for the import-directive. I'll have to do some investigation first to understand how exactly this import-directive works. Does it operate with multiple files or does it just use definitions of the same file? These questions will be answered later in little experiments. I've just included the import (static) and export methods into the XData-container class in a similar fashion like you did with GlyphSet, because before that, I used a static class called XDataManager, which was not so nice. This solution is much more comfortable and cleaner.

 

Anyway, since you're working on the FontLoader now, I've got a feature request for you: A boolean method with a char/int parameter, telling the caller whether the requested glyph is available or not. The q3-font-array is an ASCII-table as far as I know, so requesting a certain character will be very comfortable. I'd like to use this method for informing the user when he typed a not-available glyph in the readable-Editor-Gui.

 

I guess we can remove my FontLoader from the SVN then, right? I think all features have been integrated into your improved one...

Share this post


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.

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.

Sign in to follow this  

×
×
  • Create New...