API Question Set Material to a group at runtime

fred18

Addon Developer
Addon Developer
Donator
Joined
Feb 2, 2012
Messages
1,667
Reaction score
104
Points
78
Hi guys,

you can see i'm developing in this days from the questions I keep asking here :lol:

Correct me if I'm wrong: you can not change the material index of a group in a mesh at runtime, right?

WHAT I AM TRYING TO DO:
highlight a meshgroup, pretty much similar to what the D3D9 debug mesh controls does. But I want multiple groups selection.

I already implemented an HighlightMesh function which highlights the entire mesh very well, but for single groups the story is a bit different.

I tried to use the same method (change the emissive set of the material) but when multiple groups share the same material also the groups that are not selected are highlighted.

So I thought of adding a material at the bottom of the list and then point the highlighted group to that, and I was in the middle of that when I realized that the GROUPEDITSPEC does not have any flag about materials... I can get the material index from the GROUPREQUESTSPEC but I cannot change it, and this is probably a show stopper for this option because I see no other way to do it.

Can anyone confirm this or point me on another path to get it?

As always thanks to anyone who will reply! :cheers:

Fred

EDIT: Also I was thinking that if this is not possible I don't understand the point of the oapiAddMaterial function. if then I can set nothing to use it why should I add a material to a mesh?
 
Last edited:

jarmonik

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 28, 2008
Messages
2,666
Reaction score
795
Points
128
Hi Fred,


There is a good propability that you can change the mesh group material index by using oapiMeshGroup() function. It should be possible to propagate the change to a graphics client by using VESSEL::MeshModified() but that will delete the old mesh and reconstruct a new one from scratch, so, it would be very slow operation and might have an impact in animations as well since the animation matrices are stored in a mesh.


We could add a gcAPI function to set the data directry to a DEVMESH but in that case a different codes would be required for inline and d3d9.
 

fred18

Addon Developer
Addon Developer
Donator
Joined
Feb 2, 2012
Messages
1,667
Reaction score
104
Points
78
You are right, I was just testing with D3D9 and it wasn't working.

So the issue is that with inline client works very well and easily, but with D3D9 no luck, MeshModified doesn't make any difference there, I don't know why.

I tried many things, including getting a group with oapiMeshGroupEx from a devmesh (I found on the forum that it works) but that is fine just to GET informations, if you try to apply something like changing the MtrlIdx it CTDs immediately...
 

jarmonik

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 28, 2008
Messages
2,666
Reaction score
795
Points
128
So the issue is that with inline client works very well and easily, but with D3D9 no luck, MeshModified doesn't make any difference there, I don't know why.


I found a potential bug from the client that could have effected MeshModified() method. I quess nobody's been using that before. Also, to get a mesh handle for oapiMeshGroup() function VESSEL::GetMeshTemplate() should be used. Could you check that "MeshModified Event = 0x%X" is written in the D3D9ClientLog in /Modules/D3D9Client


Are you developping on Beta or 2016 ?


Here is a new binary for Beta that "should" be working.
 

Attachments

  • D3D9Client.zip
    498.2 KB · Views: 6

jarmonik

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 28, 2008
Messages
2,666
Reaction score
795
Points
128
There is also this note:
PHP:
/**
     * \brief Make a copy of one of the vessel's mesh templates.
     * \param idx mesh index
     * \return handle of copied mesh
     * \note Meshes loaded with \ref oapiLoadMeshGlobal are templates shared
     *   between all vessel instances and should never be modified by individual
     *   vessels. If a vessel needs to modify its meshes, it should operate on
     *   a copy of the template.
     */
    MESHHANDLE CopyMeshFromTemplate (UINT idx) const;
 

fred18

Addon Developer
Addon Developer
Donator
Joined
Feb 2, 2012
Messages
1,667
Reaction score
104
Points
78
Also, to get a mesh handle for oapiMeshGroup() function VESSEL::GetMeshTemplate() should be used.

yes, I use that, in D3D9 the meshhandle returned by GetMesh is always NULL

Could you check that "MeshModified Event = 0x%X" is written in the D3D9ClientLog in /Modules/D3D9Client

Actually I cannot see any call to that even though its returned value is 0.

Are you developping on Beta or 2016 ?

Actually for 2016. I always do it first for 2016 and then I go for the Beta, in order not to rely on APIs that are not there yet in 2016.

Here is a new binary for Beta that "should" be working.

Thanks!!! it will take some time to pass the project to the Beta folder because I'm really bad with VC properties, but I will test it as soon as i get it done.

There is also this note:
PHP:
/**
     * \brief Make a copy of one of the vessel's mesh templates.
     * \param idx mesh index
     * \return handle of copied mesh
     * \note Meshes loaded with \ref oapiLoadMeshGlobal are templates shared
     *   between all vessel instances and should never be modified by individual
     *   vessels. If a vessel needs to modify its meshes, it should operate on
     *   a copy of the template.
     */
    MESHHANDLE CopyMeshFromTemplate (UINT idx) const;

I know this, I think it is because theoretically if you modify a meshtemplate and there are other instances of the mesh in the sim you will see also the other getting modified. The curious part is that it is not happening. I'm using a the loadmesh clbk in oapiloadmeshglobal to get the meshes rotated properly before they get passed to D3D9, but if I reload the mesh or if I edit it (materials, or vertex editing) nothing happens to the other instances... I also thought that I haven't seen any addon to use that function, even though we all should use it: the right procedure I think it would be to load a mesh from a mesh manager with oaploadmeshglobal, to make a copy with copymeshfromtemplate and then to add the copy to the vessel, but I never saw this in any addon or example
 

jarmonik

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 28, 2008
Messages
2,666
Reaction score
795
Points
128
Actually I cannot see any call to that even though its returned value is 0.

If you are not seeing "MeshModified Event" Logged to a log. Then the event wont fire. Based on my understanding calling MeshModified() should fire EVENT_VESSEL_MODMESHGROUP to notify client about it.

But anyway, here's the binary for 2016

I have some other matters to attend to, so, I won't be in the forums this evening.
.
 

Attachments

  • D3D9Client.zip
    511.3 KB · Views: 7

fred18

Addon Developer
Addon Developer
Donator
Joined
Feb 2, 2012
Messages
1,667
Reaction score
104
Points
78
Ok, so:
- MeshModified is now regularly call, I can see it in the log.
- There is no visual change in D3D9, looks like nothing is happening BUT if I use GetMeshTemplate also in the inline client nothing happens, even if I call MeshModified. If I use GetMesh it works in the inline, but of course the result is NULL in the D3D9.

It seems a dead end. The only way I know to edit the groups in DevMeshes is through GRPEDITSPEC but it's not possible to set the material there. so not much to do right now.

The only way I see at the moment maybe a bit crazy but is this:
in loadclbk of oapiloadmeshglobal create copies of all the materials so each group will have its own, then highlight through that. I don't know if that's going to have serious impact on the performance: having 400 materials instead of 20 changes a lot in terms of performance? I think not: it occupies more memory but the calculations that need to be done are always the same, right?
 

Donamy

Addon Developer
Addon Developer
Donator
Beta Tester
Joined
Oct 16, 2007
Messages
6,906
Reaction score
201
Points
138
Location
Cape
Are you trying to change materials, that have been set for multiple groups ? Like reflective parts that shouldn't be ? If they are the ISS A2Z original meshes. I could separate those in the mesh for you. Then it would be up to others, to do the same with their addons.
 

fred18

Addon Developer
Addon Developer
Donator
Joined
Feb 2, 2012
Messages
1,667
Reaction score
104
Points
78
Are you trying to change materials, that have been set for multiple groups ? Like reflective parts that shouldn't be ? If they are the ISS A2Z original meshes. I could separate those in the mesh for you. Then it would be up to others, to do the same with their addons.

yes, but not for appearence looking. I'm working on the insim animation definitions, so I want that when the user select a group to animate it will highlight itself. It is already a working option for the entire mesh, but I'm trying to implement this also for the single groups. BTW I'm making progress with animations: it seems to work quite well, I can create animations and modify them insim, so this could e a good option to add. Let's see if I manage to get there
 

jarmonik

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 28, 2008
Messages
2,666
Reaction score
795
Points
128
I don't know if that's going to have serious impact on the performance: having 400 materials instead of 20 changes a lot in terms of performance? I think not: it occupies more memory but the calculations that need to be done are always the same, right?


No, there shouldn't be any performance impact from that. However, the MeshModified() would be very heavy. There is something wrong in there but I don't know what. GetMesh() doesn't work since it gives NULL. So, does CopyMeshFromTemplate() create a new copy everytime it's called or does it give a HANDLE to the same local copy everytime ? Well, that probably doesn't matter since the MeshModified() approach is pretty bad anyways.
 

fred18

Addon Developer
Addon Developer
Donator
Joined
Feb 2, 2012
Messages
1,667
Reaction score
104
Points
78
:woohoo: :woohoo:

IT WORKS!!!!!!!!!!!!!!!!!!!!!

Group36 of unity mesh highlighted in red:
group36unity.png


Here's the brief description of how I made it, below the commented code:

whenever a mesh is loaded it passes through the loadmeshclbk function which manipulates the mesh handle before either pass it to the D3D9 or to the orbiter core.

In this function the following happens:
if we are with the inline client the function create a new material definition (actually three, one for each highlight color possible) and append it to the material list of the mesh.

when there is a call to highlight the group then the system simply change the material index of the live mesh (GetMesh) to the new one created. When the group shall not be highlighted anymore it just revert to the original index, getting it from the mesh template.

With D3D9 is a bit more complicated: when a mesh is loaded each group gets assigned to a new material with the identical definition of its own. Then the first original materials are deleted, so we now have a mesh with n groups and n materials.

Now, when a call to highlight the group arrives the system manipulates the material of the group and changes its emissive part. Now it's possible, since each material is assigned to just one group no more issues happens.

When the system asks to revert, the material is reverted to the original one getting it from the template... and that's it!

here's the code,
for the function called within oapiloadmeshglobal:
Code:
void MeshManager::LoadingRearrangeMaterials(MESHHANDLE msh) {
	if (UsingD3D9()) { //If D3D9
		DWORD groups_count = oapiMeshGroupCount(msh);
		DWORD initial_material_count = oapiMeshMaterialCount(msh);  //Get Initial Material Count to be used afterwards for cleanup

		for (DWORD i = 0; i < groups_count; i++) {		//for every group			
			MESHGROUP *mg = oapiMeshGroup(msh, i);		// get the group
			DWORD matidx = mg->MtrlIdx;					// get its original material index;
			MATERIAL *mat = oapiMeshMaterial(msh, matidx);		//get the group material
			DWORD newmatidx = oapiAddMaterial(msh, mat);		//add a copy of the material at the bootm
			mg->MtrlIdx = newmatidx;							//set the group material to the copy just added
		}
		for (DWORD i = 1; i < initial_material_count; i++) {	//delete all the initial materials since now all the groups point to the copies
			oapiDeleteMaterial(msh, i);
		}
	}
	else {	//if NOT D3D9
		COLOUR4 colred = { 1,0,0,1 };	//Red Color
		COLOUR4 colgreen = { 0,1,0,1 };	//Green Color
		COLOUR4 colblue = { 0,0,1,1 };	//Blue Color
		matred = new MATERIAL;			//Create the tree new materials 
		matred->ambient = colred;
		matred->diffuse = colred;
		matred->emissive = colred;
		matred->specular = colred;
		matred->power = 0;
		matgreen = new MATERIAL;
		matgreen->ambient = colgreen;
		matgreen->diffuse = colgreen;
		matgreen->emissive = colgreen;
		matgreen->specular = colgreen;
		matgreen->power = 0;
		matblue = new MATERIAL;
		matblue->ambient = colblue;
		matblue->diffuse = colblue;
		matblue->emissive = colblue;
		matblue->specular = colblue;
		matblue->power = 0;
		
		DWORD green_idx = oapiAddMaterial(msh, matgreen);	//Add the new materials to the mesh
		DWORD blue_idx = oapiAddMaterial(msh, matblue);
		DWORD red_idx = oapiAddMaterial(msh, matred);
		delete matred;									//cleanup
		delete matgreen;
		delete matblue;
	}
	
	return;
}

and for the highlight group function:
Code:
bool MeshManager::HighlightMeshGroup(UINT msh_idx, UINT grp_idx, bool Highlight) {

	DEVMESHHANDLE devmsh_h = SB1->GetDevMesh(SB1->visual, msh_idx);		//let's get the devmesh from the vessel
	MESHHANDLE temp_msh_h = SB1->GetMeshTemplate(msh_idx);				//let's get the template mesh from the vessel to have the original values
	if (temp_msh_h == NULL) { return false; }// you'll never know...

	MESHGROUP *mg = oapiMeshGroup(temp_msh_h, grp_idx);		//Get the original mesh group 
	if (UsingD3D9()) {											// if D3D9
		DWORD matidx = mg->MtrlIdx;								//Get The original material index
		MATERIAL *mat = oapiMeshMaterial(temp_msh_h, matidx);	//Get the original material definition
		MATERIAL *mat2 = new MATERIAL;							//Create the new material for the excahnge
		mat2->ambient = mat->ambient;							//Set all the values of the new material exactly as the original
		mat2->diffuse = mat->diffuse;
		mat2->emissive = mat->emissive;
		mat2->specular = mat->specular;
		mat2->power = mat->power;

		if (Highlight) {										//if Highlight
			if (HighLightColor == HIGHLIGHTCOLORRED) {				
				mat2->emissive.r = 1;							// Set the emissive color of the new material at maximum 
			}
			else if (HighLightColor == HIGHLIGHTCOLORBLUE) {
				mat2->emissive.b = 1;
			}
			else if (HighLightColor == HIGHLIGHTCOLORGREEN) {
				mat2->emissive.g = 1;
			}

		}
		oapiSetMaterial(devmsh_h, matidx, mat2);		//change the material to the new one. if not highlight the original will then be restored
		delete mat2;									//cleanup
	}
	else {												//if Inline client
		MESHHANDLE msh_h = SB1->GetMesh(SB1->visual, msh_idx);		//Get the Live mesh from the sim
		if (msh_h == NULL) { return false; }// you'll never know...
		DWORD mat_count = oapiMeshMaterialCount(msh_h);				//Get the material count to know the know the index
		MESHGROUP *mg_inline = oapiMeshGroup(msh_h, grp_idx);		//Get the meshgroup from the live sim
		
		if (Highlight) {											//if highlight
			if (HighLightColor == HIGHLIGHTCOLORRED) {
				mg_inline->MtrlIdx = mat_count -1;					//change the material index to the one created at the beginning
			}
			else if (HighLightColor == HIGHLIGHTCOLORBLUE) {
				mg_inline->MtrlIdx = mat_count - 2;
			}
			else if (HighLightColor == HIGHLIGHTCOLORGREEN) {
				mg_inline->MtrlIdx = mat_count - 3;
			}
		}
		else {
			mg_inline->MtrlIdx = mg->MtrlIdx;					//if not highlight just restore with the template index
		}
	}
	
	return true;
}

:cheers:
 
Last edited:
Top