Jump to content
The Dark Mod Forums

Texture Copy/paste


Recommended Posts

I started looking into the brushface texture code and this is what I was able to find out so far:

 

- From messing around I found that the brush face definition in the map file can be read like this:

( -1 0 0 96.4022 ) ( ( 0.0080353 0 -0.5 ) ( 0 0.0357143 1.14286 ) ) "textures/bleh" 0 0 0
(nx ny nz d) ( (scalex scalexy offsetx) (scaley scaleyx offsety) ) "shadername" don't know

with the plane normal vector and d its distance. The interesting part is the texture coordinate bit, with the offsetx,offsety being measured in multiples of the actual image width (e.g. if you apply a 256 pixel wide texture, an offsetx of 0.5 would mean that the texture is shifted by 256*0.5 = 128 pixels in the x-direction).

 

The other four values can be used to express the texture scale and rotation. If the texture is not rotated (0°), the scalexy/scaleyx values stay zero. Maybe these values are stored in multiples of the image width as well, I have yet to find out.

 

- It doesn't look like there is any documentation of how the texture coordinates are stored in the map files. Nor does anyone on Doom3World fully understand it (or at least no one is sharing his knowledge). I found some threads (http://www.doom3world.org/phpbb2/viewtopic.php?t=11101, http://www.doom3world.org/phpbb2/viewtopic.php?t=13639) with some links to old quake docs but that doesn't help a lot. At least I was confirmed with the values stored in image width/height multiples.

 

- Next step will be butchering around in the codebase to find out how the map file values are translated into the values the surface inspector is showing. Maybe this is easily done with some scaling by the image dimensions, maybe there is some transformation matrix involved. The code is - as usual - undocumented, apart from some parts in the surface inspector code that have some comments from Shamus.

 

I will post back here what I can find out, anyone with more knowledge in this matter feel free and speak up!

Link to comment
Share on other sites

I had some exposure to this when I was updating the map writing code (really all of this stuff should be in the mapdoom3 plugin rather than having hardcoded knowledge of the mapformat in the brushes and patches themselves). I too would like to know more about the map format, in particular what sort of precision the floating points should be and whether they can accept exponential notation -- currently I am just using the default options for writing floats as text which sometimes uses an exponent, and although Doom 3 hasn't crashed yet I am not 100% certain that this is acceptable.

 

I worked out the plane equation but never figured out what the texture coordinates were doing, so that is useful. It is unsurprising that they are expressed in U/V values rather than pixels, since (1) the brushes have no knowledge of the texture sizes involved, and (2) OpenGL deals entirely with U/V (or S/T as they are called) coordinates.

 

I am guessing that when each brush face is rendered, the UV-coordinates sent to OpenGL for the face must be looked up by sampling the "texture plane" (as specified by the offset/scaling factors) at specific points corresponding to the bounding vertices (called the "winding"). Copying textures across coplanar brush faces might be achieved simply by ensuring that both windings were sampling the same texture plane, which in turn MIGHT involve little more than copying the values from one face to another. I haven't looked deeply at the code for this.

 

I think a large part of the problem was related to patches -- when you originally raised this issue I think you mentioned being unable to copy a planar texture onto an adjacent deformed patch so as to create a seamless transition -- which I suspect might be considerably more complex in terms of texture coordinates.

Link to comment
Share on other sites

I found two conversion functions TexMatToFakeTexCoords and FakeTexCoordsToTexMat which convert a texture matrix into what's called "fake" texture coordinates (shift, scale and rotation). I assume that the fake texture coordinates are the ones that are presented in the surface inspectors. At least I know now where to look for the actual transformations.

 

Overall, the code organisation is a terrible mess, the classes are distributed somewhere in the brush.h / brush_primit.h / .cpp and brushmanip.cpp files. It seems to me like they were thrown into the next best place as they were created. Without Eclipse's Ctrl-H search function I would be completely lost... Maybe I will extract some of these classes into new files, if I get too sick of always having to search for the class names.

 

edit (just saw your post): yes, the problem is indeed related to patches. I lack some basic understanding of how textures are handled in the code, so I started at the map files. As soon as I will have understood the brush code (which I will need if I want to extract texture information from the faces) I will move on to the patch texture code.

Link to comment
Share on other sites

I found two conversion functions TexMatToFakeTexCoords and FakeTexCoordsToTexMat which convert a texture matrix into what's called "fake" texture coordinates (shift, scale and rotation). I assume that the fake texture coordinates are the ones that are presented in the surface inspectors. At least I know now where to look for the actual transformations.

 

That makes sense. What presumably is happening is that the values you enter for offset, scale and rotation are converted into a matrix which is applied directly as an OpenGL texture matrix (glMatrixMode(GL_TEXTURE)) during rendering. This almost certainly takes place in the game itself as well -- when you add a shift or scale parameter to a material stage it will use a texture matrix rather than generating a new image each time.

Link to comment
Share on other sites

I have made some progress at last. After a short intermezzo with the brush_primit files, I added a method to the Patch class, which gets called on the PasteTexture command. In this method I read the scale of the texture definition and apply this very scale to the brush.

 

Before my change it was only possible to copy the shader name to the patch; right now the scale is copied over as well. For the next step, I will have to figure out how to retrieve the shift values / texture coordinates from the brush face and apply it to the patch accordingly, which will be a little tough, I'm afraid, but it should be feasible with some projection operations.

Link to comment
Share on other sites

Before my change it was only possible to copy the shader name to the patch; right now the scale is copied over as well. For the next step, I will have to figure out how to retrieve the shift values / texture coordinates from the brush face and apply it to the patch accordingly, which will be a little tough, I'm afraid, but it should be feasible with some projection operations.

 

It might be worth considering whether you want to define proper interfaces for brushes/patches, for example IBrush and IPatch which go in their respective include files, which could (amongst other things) return this sort of information.

 

At some stage I am planning to rewrite the import/export code such that the mapdoom3 plugin walks the scenegraph itself and reads/writes brush and patch data using such interfaces, rather than having brushes/patches export themselves into tokens (which breaks separation between the primitive data and the map text format).

Link to comment
Share on other sites

You mean something like this:

class IBrushFace
{
public:
 virtual const char* GetShader() const = 0;
 virtual void SetShader(const char* name) = 0;
 virtual const TextureProjection& GetTexdef() const = 0;
 virtual void GetTexdef(TextureProjection& projection) const = 0;
 virtual void SetTexdef(const TextureProjection& projection) = 0;
 virtual void GetFlags(ContentsFlagsValue& flags) const = 0;
 virtual void SetFlags(const ContentsFlagsValue& flags) = 0;
 virtual void ShiftTexdef(float s, float t) = 0;
 virtual void ScaleTexdef(float s, float t) = 0;
 virtual void RotateTexdef(float angle) = 0;
 virtual void FitTexture(float s_repeat, float t_repeat) = 0;
 virtual bool isDetail() const = 0;
 virtual void setDetail(bool detail) = 0;
};

This is from ibrush.h, but it's currently disabled by #if 0 compiler directives.

Link to comment
Share on other sites

Yeah, that's the sort of thing. You will no doubt have a much better idea than me regarding what functionality such an interface has to expose.

 

The downside is that in order to do this you will probably have to rewrite most of the primitive-handling code, since I suspect it sucks badly. Seriously, WTF is the point of ArrayReference in patch.h? How many stupid reimplementations of std::vector do we need?

Link to comment
Share on other sites

I really have no idea, this is most probably the result of some serious ego trip...

 

The deeper I'm digging into the brush code, the more I want to reorganise it. What the heck is the point in maintaining a file with fricking 4200 lines (see brush.h)?

 

Regarding that interface you mentioned: perhaps I will really do this, but I think I will need some assistance when that time comes, especially when it comes to implementation details (I'm still green regarding C++ design).

 

However, for now I'll try to get the feature completed without doing a whole rewrite, let's see how far I can get...

Link to comment
Share on other sites

The deeper I'm digging into the brush code, the more I want to reorganise it. What the heck is the point in maintaining a file with fricking 4200 lines (see brush.h)?

 

And it's a header file as well, which means that those 4200 lines of crap will be COPIED into every single object file that includes it.

 

Regarding that interface you mentioned: perhaps I will really do this, but I think I will need some assistance when that time comes, especially when it comes to implementation details (I'm still green regarding C++ design).

 

I'll be happy to help with that if/when the time comes. Interface design can be tricky to get right, especially with something complex like this, so it may well require several iterations and refinements.

Link to comment
Share on other sites

I guess the designs themselves aren't as catastrophical as the coding style. The codebase is one huge mess and it's hard to keep track of the things that are actually going on. This is not what makes life easier, as you can imagine. But finally, Salvation has come... :D

Link to comment
Share on other sites

I get a kick out of reading these. I'm guessing you guys don't like a lot of the original designer('s(')) methods. Am I guessing right? ^_^

 

Whatever gave you that impression? :laugh:

 

To be fair, parts of the codebase are very old -- dating back to the original Quake in some cases. It is very likely that at that time, much of C++ was not fully standardised or understood, and the best design practices were not so obvious as they are now.

Link to comment
Share on other sites

Some good news:

 

This is the original situation in my testmap:

texturingbeforehandca0.th.jpg

 

This is after four mouseclicks (Ctrl-Alt-MMB on the source brush, 3x Ctrl-Shift-MMB on the other three):

texturingafterwardsqk2.th.jpg

 

So I guess this feature is as good as implemented :). (I will have to do some clean-up before I can commit this.)

 

It took us (both angua and me) four to five hours to figure out how the world coordinates are transformed into texture space. The actual implementation took me approx. 20 minutes :D.

 

We also came up with some more ideas that should make the texturing of bevel patches really easy, so expect more to come!

Link to comment
Share on other sites

Wow, nice work!

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.
Link to comment
Share on other sites

There is more to come: at the moment, I'm trying to handle such a sitation (note the distorted texturing of some patch parts):

 

texturingbevelsbefore3uj6.th.jpg

 

If things go well, I will be able to solve this during this day, but this will be a lot tougher than the other task!

Link to comment
Share on other sites

Perfect.

 

If it is not too hard, it would probably be worth switching around the controls so that your middle-click operation grabs the clicked texture and puts it on the selected brush, rather than copying the selected brush's texture and pasting it on the clicked brush. This is more consistent with DoomEdit and might avoid inadvertently changing the texture on the wrong brush.

Link to comment
Share on other sites

I realise it's a little different, but I'm naively assuming the code base would be a similar area, but would it be possible to develop any kind of analogue control over texture placement? Have a certain brush selected and, with a key held down, use the mouse to position the texture? I know we have specific placement controls in the texture editor but I can think of several intances where this analogue type of control would be beneficial.

Link to comment
Share on other sites

This thought occurred to me as well, and it's definitely feasible, but I don't know if it will make it into any of the next few releases. But be sure to post it into the Wishlist thread so that the idea doesn't get lost!

Link to comment
Share on other sites

IMHO, texturing is one of D3ed's biggest weaknesses, and if DarkRadiant had good texturing capabilities that'd probably be enough to get me to (mostly) stop using D3ed... There's a wishlist of things I've wanted...

 

1. The ability to set the "origin" of transformations. In D3ed, when you scale or rotate, I think it's always around the world origin. This is very inconvenient.

 

It'd be nice if when trying to rotate a texture to fit a wood beam, I could set the origin at a corner vertex (or some other spot) and just rotate it until it fit, instead of having to iteratively rotate it a little, then shift it to see if it lines up with the beam, then rotate it some more, then shift some more, ad infinitum.

 

Similarly, for scaling textures, it'd be nice if I could put the origin at a corner vertex, and then scale the texture until its size matches the beam. (instead of scaling, then shifting to see if the size matches, then scaling again, etc)

 

Preferably, I'd like to be able to set the origin at any location.

 

2. The ability to undo transformations. At least I should be able to undo them to the state that existed before I opened up the texture window or before copying a texture's orientation. In D3ed, sometimes I'll work for a half-hour, iteratively rotating, scaling and shifting a texture until it matches a brush... then I'll accidentally middle click another brush or some such, and lose all that work.

 

3. The ability to project a brush-face's texture onto another brush-face that's not co-planar to it. (D3ed lacks this as far as I can tell)

Link to comment
Share on other sites

There is more to come: at the moment, I'm trying to handle such a sitation (note the distorted texturing of some patch parts):

 

texturingbevelsbefore3uj6.th.jpg

 

If things go well, I will be able to solve this during this day, but this will be a lot tougher than the other task!

:blink: If you figure out a way to handle that, you will have solved one of the biggest and most persistent texturing problems known to man and woman alike.

 

Edit: Oh also, I think a foundation for manual texture manipulation is in there somewhere... I mentioned a key combo in one of my posts around here where it almost seemed like you could do it, but the feature wasn't completely implemented.

Link to comment
Share on other sites

There is actually a "textool" plugin, currently disabled, which supposedly provides UV-like texture editing capabilities (where you view the brush face as a polygon and drag its points over the texture background). It would be worth seeing if I can get this working and possibly integrate it with the Surface Inspector in some way.

Link to comment
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.

  • Recent Status Updates

    • OrbWeaver

      Does anyone actually use the Normalise button in the Surface inspector? Even after looking at the code I'm not quite sure what it's for.
      · 1 reply
    • Ansome

      Turns out my 15th anniversary mission idea has already been done once or twice before! I've been beaten to the punch once again, but I suppose that's to be expected when there's over 170 FMs out there, eh? I'm not complaining though, I love learning new tricks and taking inspiration from past FMs. Best of luck on your own fan missions!
      · 4 replies
    • The Black Arrow

      I wanna play Doom 3, but fhDoom has much better features than dhewm3, yet fhDoom is old, outdated and probably not supported. Damn!
      Makes me think that TDM engine for Doom 3 itself would actually be perfect.
      · 6 replies
    • Petike the Taffer

      Maybe a bit of advice ? In the FM series I'm preparing, the two main characters have the given names Toby and Agnes (it's the protagonist and deuteragonist, respectively), I've been toying with the idea of giving them family names as well, since many of the FM series have named protagonists who have surnames. Toby's from a family who were usually farriers, though he eventually wound up working as a cobbler (this serves as a daylight "front" for his night time thieving). Would it make sense if the man's popularly accepted family name was Farrier ? It's an existing, though less common English surname, and it directly refers to the profession practiced by his relatives. Your suggestions ?
      · 9 replies
    • nbohr1more

      Looks like the "Reverse April Fools" releases were too well hidden. Darkfate still hasn't acknowledge all the new releases. Did you play any of the new April Fools missions?
      · 5 replies
×
×
  • Create New...