Bug D3D9Client IMS-compatibility fix

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,873
Reaction score
2,129
Points
203
Location
between the planets
As some might already have noticed, there were two compatibility problems between D3D9 client and IMS. The problems were not very IMS-specific and might crop up in connections with other projects as well.

I have tracked down the problems and made a fix. Since I'm not an adept user of version control systems and code repositories, the easiest way seems to post them here. They are not large fixes and easy to implement.

The first problem was D3D9 client writing out of bounds when animations were dinamically created during sim-time.

D3D9 client initialises animations and allocates memory to store the state of those both at loadup, and dinamically when a mesh is added.
This happens in the function InitAnimations(UINT) in vVessel.cpp.

The trouble is, this function only is called when a mesh is added. nanim receives the number of animations the vessel currently has, but this is before new animations are added.

IMS (and presumably any other add-on that creates animations on the fly) then goes on to add the new animations if neccessary. After doing so, vVessel::Update() is called in the client, which calls UpdateAnimations(UINT = -1).

The new animations will have been added in the meantime. Meaning, nanim will receive the new number of animations, while InitAnimations only got the old number of animations, without the newly added ones.

As a result of this, UpdateAnimations writes over the allocated length of animstate in the block at the end.

I fixed the problem by introducing a new block into UpdateAnimations that checks for newly added animations and calls InitAnimations for them. The block seems a bit overcomplicated. The problem is I need the meshindex, and at this point (called from Update()) The meshindex passed to UpdateAnimations is invalid, so I have to extract all meshindices first, and of course I had to check for duplicity, since I don't want to call InitAnimations over and over for the same mesh. I used a vector, which means I had to include vector.h in vVessel.cpp:

Code:
void vVessel::UpdateAnimations (UINT mshidx)
{
	double newstate;
	
	UINT oldnAnim = nanim;
	nanim = vessel->GetAnimPtr(&anim);

	if (nanim>0 && animstate==NULL) {
		LogErr("[ANOMALY] UpdateAnimations() called before the animations are initialized. Calling InitAnimations()...");
		InitAnimations();
	}

	if (nanim > oldnAnim)
	//animations have been added without being initialised
	{
		std::vector<UINT> meshesToInitialise;
		for (UINT i = oldnAnim; i < nanim; ++i)
		//extracting meshindices for uninitialised animations.
		//this might seem overly complicated, but it seems savest
		//it guarantees that all new animations are initialised, and are initialised only once
		{
			for (UINT j = 0; j < anim[i].ncomp; ++j)
			{
				bool alreadyNoted = false;
				for (UINT jj = 0;  jj < meshesToInitialise.size(); ++jj)
				{
					if (anim[i].comp[j]->trans->mesh == meshesToInitialise[jj])
					{
						alreadyNoted = true;
						break;
					}
				}
				if (!alreadyNoted)
				{
					meshesToInitialise.push_back(anim[i].comp[j]->trans->mesh);
				}
			}
		}
		for (UINT i = 0; i < meshesToInitialise.size(); ++i)
		{
			InitAnimations(meshesToInitialise[i]);
		}
	}

	for (UINT i = 0; i < nanim; i++) {
		if (!anim[i].ncomp) continue;
		if (animstate[i] != (newstate = anim[i].state)) {
			Animate(i, newstate, mshidx);
			animstate[i] = newstate;
		}
	}
}

I'm probably breaking a convention on every line I wrote here, and I don't know if the inclusion of vector is welcome, as there seems to be an avid evasion of both string and vector in the whole client. I could have made a fixed size array with a size that seems large enough, but I'd rather play it save... :shifty:


It is also possible that it would be sufficient to call InitAnimations just once, even if more than one mesh was added (doesn't happen in IMS, so I didn't test)... as far as I can see it wouldn't make much of a difference, but the function requiring a meshindex made me suspicious.


The other problem had to do with classnames longer than 62 characters causing a crash in ParseSkins() (also in vVessel.cpp). Since the path is included in the classname, this can happen faster than one might think...

The fix was to simply increase the buffer size to 256. This is still potentially unsave, but 256 seems a reasonable enough value and equals the allocated size in pSkins, so extending it further or making the allocation dynamic would require some more involved changes.
The crash is caused by sprintf_s invocing the default invalid parameter handler (which seems to crash the program on purpose to tell the programmer that there's something wrong) as soon as the the string (including terminator) is equal to the buffer size.

Code:
void vVessel::ParseSkins()
{
        char classname[256];

        D3D9Client *gc = scn->GetClient();
        DWORD start = 0;

        sprintf_s(classname, 256, "#%s", vessel->GetClassNameA());
 

kuddel

Donator
Donator
Joined
Apr 1, 2008
Messages
2,064
Reaction score
507
Points
113
Thank you very much for your work Jedidia!

I will add those changes to the repository as soon as I find some time (currently not at my working machine)
- The buffer-size increase to 256 characters seems to be fairly simple and save (and needed ;) ).
- The other one has a bit more changes, but also seems quite organized:thumbup:

As I am not the main maintainer of D3D9Client (that's Jarmonik) I can not promise any official release date, but it shouldn't be to long ;)

So, thanks a lot again for all the efford you've put into this.

/Kuddel

---------- Post added 23-09-13 at 20:41 ---------- Previous post was 22-09-13 at 23:50 ----------

Hello Jedidia,

as I am not good in creating vessels with animations (read: I am unable to ;) ), could you create a very simple test-case vessel that adds animations dynamically?

I am thinking of a basic vessel with no other dependencies, so it can be used to verify any further code-changes in the D3D9Client.

A (vessel-)DLL would be enough, although a complete source code might be even better.

Or is there a vessel in the default Orbiter installation that does add dynamic animations?

...By the way: If anyone else can provide this, his/her contribution is welcomed as well :thumbup:

Thanks in advance,
Kuddel
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,873
Reaction score
2,129
Points
203
Location
between the planets
I am thinking of a basic vessel with no other dependencies, so it can be used to verify any further code-changes in the D3D9Client.

Can do. Might take a few days until I get around to it, though.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,873
Reaction score
2,129
Points
203
Location
between the planets
Right, here we go. I took a little shortcut and adapted the default deltaglider to reproduce the problem.

At this opportunity I realised that I did not fully understand the problem: Animations can be added during simtime, since there is a saveguard against animstates being NULL. The problem only crops up when not all animations are defined at once.

The file attached contains dll, config, scenario and sourcecode for a DG that creates one animation during creation, and the others in the first clbkPreStep. This reliably reproduces the problem.

Just drop the contents of the zip into your orbiter root (don't worry, it won't overwrite anything), set a breakpoint in updateanimations and go for a ride with the dynamicDG scenario. You will see that at the first call to UpdateAnimations everything is ok (one animation defined, and space allocated for one). The next time UpdateAnimations is called (right before the first frame is rendered), you will see the problem, as animstates is still allocated for 1 element, while nanim now recieves the full 37 animations.

Source is in orbitersdk\samples\dynamicDeltaGlider
 

Attachments

  • dynamicDG.zip
    5.7 MB · Views: 8
Last edited:

BruceJohnJennerLawso

Dread Lord of the Idiots
Addon Developer
Joined
Apr 14, 2012
Messages
2,585
Reaction score
0
Points
36
I think this will fix the problem with animations added via Lua ;) like for example a VC with working analog instruments :)

Ahh. So lua scripts must be read as instructions for the program to add dynamic components by type of command. Nothing wrong with it, but I wouldnt be surprised if various things have trouble with it. Dynamic memory tends to be a tricky thing to get perfect across multiple code bases written by different people with different goals in mind.
 

Linguofreak

Well-known member
Joined
May 10, 2008
Messages
5,032
Reaction score
1,273
Points
188
Location
Dallas, TX
Might this be related to the docking port revealer not working properly with D3D9?
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,873
Reaction score
2,129
Points
203
Location
between the planets
I'm not aware of the docking port revealer adding animations, so I think not... I have not expierienced any trouble with addMesh if there were no additional animations defined in the meantime.
 

Linguofreak

Well-known member
Joined
May 10, 2008
Messages
5,032
Reaction score
1,273
Points
188
Location
Dallas, TX
I'm not aware of the docking port revealer adding animations, so I think not... I have not expierienced any trouble with addMesh if there were no additional animations defined in the meantime.

Now, keep in mind that I'm speaking from what I see without any actual knowledge of what the code looks like or how what I see would be achieved in code in Orbiter, so I may be talking out my rear, but this is the line of thought that led to the question:

With the built-in graphics client, the docking port revealer adds a blinking arrow to the selected port (I think, I don't use it often and I asked the question a lot closer to when I used it last). The blinking strikes me as the sort of thing that might plausibly be an animation (but I have no idea how it is actually implemented internally).

With D3D9, there's a big black rectangle sticking out of the spacecraft at a 90 degree angle to the plane of the arrow (parallel to the plane separating the two spacecraft if something were docked there).
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,873
Reaction score
2,129
Points
203
Location
between the planets
With D3D9, there's a big black rectangle sticking out of the spacecraft at a 90 degree angle to the plane of the arrow (parallel to the plane separating the two spacecraft if something were docked there).

Heh, didn't even see this until now...

[hypothesis] I think the blinking is caused by adjusting the mesh opacity. I can only speculate on why this does not show properly in d3d9 client. My theory would be that dockport revealer works with default MESHHANDLE instead of DEVMESHHANDLE and that this causes the problem somehow.[/hypothesis]

[fact] if the blinking would be an actual animation (which I consider unlikely already based on the fact that Artlav is a very efficient programmer and this would be a very inefficient method to produce the effect) there shouldn't be a black triangle, there should be a ctd. [/fact]

Anyways, since there seems to be movement again in D3D9 development, has anyone actually implemented a fix?
 

Bibi Uncle

50% Orbinaut, 50% Developer
Addon Developer
Joined
Aug 12, 2010
Messages
192
Reaction score
0
Points
0
Location
Québec, QC
Sorry if this issue is a bit old, but something in its implementation is catching my eye.

When I dynamically create animations, D3D9 client responds as it should. However, I get this annoying [ERROR] EVENT_VESSEL_NEWANIM Not Implemented message in D3D9Client log. I searched the source code of the client and found the cause of this error. In clbkEvent(), the message EVENT_VESSEL_NEWANIM is sent and ignored. However, due to Jedidia's patch, the animations is still added, but at runtime.

I suggest to rearrange the code to respond to EVENT_VESSEL_NEWANIM instead of doing it live in UpdateAnimation(). I don't have much time right now and I don't have a D3D9Client debug installation, but I guess doing

Code:
case EVENT_VESSEL_NEWANIM:
InitAnimations((DWORD)context);
break;

and removing Jedidia's patch in UpdateAnimations() should do the trick.

Edit:
While re-reading my post, I found myself being rude with Jedidia. It was not my intention. Don't take it personnaly, English is not my native language. Anyway, thanks for your contribution to the D3D9 client Jedidia ! :tiphat:
 
Last edited:

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,873
Reaction score
2,129
Points
203
Location
between the planets
While re-reading my post, I found myself being rude with Jedidia.

No worries, didn't see anything rude in it.

I did absolutely not bother with the event cue (simply didn't occur to me to search for the problem there), so if the real reason for the bug was an improperly handled event when new animations were added, then it's only logical to fix it there. And oh so much more elegant, it would seem :thumbup:
 

Bibi Uncle

50% Orbinaut, 50% Developer
Addon Developer
Joined
Aug 12, 2010
Messages
192
Reaction score
0
Points
0
Location
Québec, QC
It was a little more complicated than what I thought, but I reused some code from you and it finally works (at least, with my project...)

Here is the complete code for vVessel.cpp.
 

Attachments

  • VVessel.zip
    12.1 KB · Views: 5

kuddel

Donator
Donator
Joined
Apr 1, 2008
Messages
2,064
Reaction score
507
Points
113
...The file attached contains dll, config, scenario and sourcecode for a DG that creates one animation during creation, and the others in the first clbkPreStep. This reliably reproduces the problem.

Hi jeddia et al.

first: sorry for the long delay from my side...real life was hard to me this year so far.
Now back to the issue. Thank you for the scenario to reproduce the problem. It really helps (helped me) a lot!
I've done some more tweaks in preparation of the other callback-events and merged your input.
I did change some conceptual things though:

  1. I used std::set instead of std::vector to track newly added animations (faster & nicer) see vVessel::InitNewAnimations()
  2. Already initialized animation states are now just (mem-)copied and only newly added animations will be initialized (should also increase speed)

Could you[1] please verify that these changes work as you expect?

Thanks in advance,
Kuddel

[1] Bibi Uncle possibly too, maybe?
 

Attachments

  • vVessel_new.zip
    14.2 KB · Views: 3

Bibi Uncle

50% Orbinaut, 50% Developer
Addon Developer
Joined
Aug 12, 2010
Messages
192
Reaction score
0
Points
0
Location
Québec, QC
Works like a charm on my side. Very nice coding style BTW.

I hope your year gets better kuddel. You've done a great job with D3D9Client :thumbup:
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,873
Reaction score
2,129
Points
203
Location
between the planets
Could you[1] please verify that these changes work as you expect?

I'll try to get around to it in the next days. RL is pretty tough here too at the moment... :shifty:
 
Top