SDK how return struct to maxscript?


#2

Guess you can pass variables by reference and assign them directly without the need to return anything.

def_visible_primitive(AssignByRef, "AssignByRef");
Value* AssignByRef_cf(Value** arg_list, int count)
{
	check_arg_count_with_keys(AssignByRef, 3, count);

	Thunk *var1 = arg_list[0]->to_thunk();
	Thunk *var2 = arg_list[1]->to_thunk();
	Thunk *var3 = arg_list[2]->to_thunk();

	var1->assign(new String(_T("Victory!!!")));
	var2->assign(new Point3Value(1, 2, 3));
	var3->assign(new BitArrayValue(666));


	return &true_value;

}

Then in mxs

AssignByRef &a &b &c
a
b
c.count

– true
– “Victory!!!”
– [1,2,3]
– 666


#3

you could probably create and return one but I don’t think mxs would “understand” what it was getting. the generally accepted method is to return an array of values which in turn can be arrays or point3values etc here my implementation of polyop.intersectrayex as an example

def_struct_primitive(polyop_intersectRayEx, polyop, "intersectRayEx");

Value* polyop_intersectRayEx_cf(Value** arg_list, int count)
{
	check_arg_count(intersectRayEx, 2, count);
	MNMesh* pmesh = get_polyForValue(arg_list[0], MESH_READ_ACCESS, NULL, intersectRayEx);
	Ray ray = arg_list[1]->to_ray();

// get the node transforms

	INode* node = get_valid_node(arg_list[0], intersectRayEx);
	Matrix3 tm  = node->GetObjectTM(MAXScript_time());
	Matrix3 itm = Inverse(tm);
	
// convert the ray in to local space

	Ray os_ray;
	os_ray.p   = itm * ray.p;
	os_ray.dir = VectorTransform(itm, ray.dir);
	
	float t;
	Point3 normal;
	int fi;
	Tab<float> bary; 
	if(!pmesh->IntersectRay(os_ray,t,normal,fi,bary))
		return &undefined;

// convert bary table into array value
	
	Array* barray = new Array(bary.Count());
	for(int i = 0; i < bary.Count(); ++i)
		barray->append(Float::intern(bary[i]));

// Return the array containing the intersection details 

	one_typed_value_local(Array* result);
	vl.result = new Array(3);
	vl.result->append(new RayValue((os_ray.p + os_ray.dir * t) * tm, Normalize(VectorTransform(tm, normal))));
	vl.result->append(Integer::intern (fi+1));
	vl.result->append(barray);
	return_value (vl.result); 
}

with the result looking something like in mxs
#((ray [-22.0326,43.1826,28.3888] [-0.617795,0.767651,-0.170415]), 6, #(0.212238, 0.0, 0.593346, 0.194416))


#4

Thanks to both of you.

I end up doing something similar to what you just said, I created arrays inside arrays with all the information then in maxscript i convert them back to a struct. I just wasn’t sure it was the correct way. I was just doing it that way because i saw some samples written that way.

Another couple of questions about one_typed_value_local.

how does it work :slight_smile: When i first saw that there were different defines one_typed_value_local, two_typed_value_local , three_typed_value_local, etc. I thought i could put all the values in there and then by returning the result it would bring everything into maxscript. but of course it didn’t work. Then what are they for?

Also whats the difference between one_typed_value_local and one_typed_value_local_tls that i see in lots of the sample code.

Thanks,
Guillermo


#5

have a look at

Value*
get_max_file_asset_metadata_cf(Value** arg_list, int count)

in avg_DLX.cpp

looks like it returns an array of structs


#6

Thanks Klvnk,

I still haven’t figure it out how to use that code, but i will keep looking into.


#7

yep mxs at the source can be extremely arcane and obscure :slight_smile:


#8

Klvnk,

I have another completely different question.

in maxscript i have the following code to draw some viewport lines and points while the code is generating the positions.

fn DisplayDrawing =
	(

	local nodePoint

	gw.setTransform(Matrix3 1)
	
	for r = 1 to points.count do 
		(	
			if points[r].nodes.count  > 2 then 
				(
					thePoints = #()
					for n = 1 to points[r].nodes.count do
						(
							
							nodePoint = points[r].nodes[n].pos
							
							append thePoints nodePoint
								
							gw.setColor #line green
							gw.Polyline thePoints false  													

							gw.updateScreen()
							redrawViews() 		
						)

				)			
		)			
	)
	
	
fn doStuff =
(
	--- for loop generating the points
	for t = 1 to lots.count do
	(
		-- stuff here
		
		-- call the gw draw function
		DisplayDrawing()
	)
)

on chk_displayDrawing changed state do
(	
  unRegisterRedrawViewsCallback DisplayDrawing
  if state then
   registerRedrawViewsCallback DisplayDrawing
  completeRedraw()
)

I’m trying to do that on the sdk but i can’t find a solution.

The complete plugin is based on the FunctionPublishing sample in the howto solution.

I already have the function correctly generating a std::vector with all the positions i want to draw on the viewport.

but i can’t find how to implement it. I saw the post [SDK] issues drawing polyline in viewport
but i still can’t figure it out. I tried to use the code in the polycnt.cpp to register a callback but it didn’t work and crashed the plugin.
can you give me some guidelines? or perhaps you have done something similar in the past?

Thanks
Guillermo


#9

have a look at the source for the function you are using in the script in the file

C:\Program Files\Autodesk\3ds Max **** SDK\maxsdk\samples\maxscript\mxsagni\GraphicsWindow.cpp

and go from there.


#10

I will thanks,


#11

let’s clarify your question …

1 Do you want to define a new type of structure, create and return its instance with the parameters set?

2 Do you want to pass an existing definition of structure, create and return its instance with the parameters set?

3 Do you want to pass a structure instance and return it with the parameters set?

PS … everything above is possible to do with c++ SDK


#12

but in practice it’s easier to return an array of values


#13

in maxscript i have a struct which has a boolean, an int and another struct which in has several arrays of Points, a few floats and a boolean. On the c++ code i have classes of the same.
I ported a few functions to c++ so when they are done i wanted to return a struct similar to the one in maxscript so the script can continue with out having to do the conversion.
Right now i’m returning in C++ a multi dimensional array and once in maxscript i recreate the struct from it. It works but was wondering if i could just return the struct.

I have looked at the sample code Lkvnk suggested but was not able to get it working. so i just kept the multidimensional array :grinning:


#14

in your case it should be the situation #3, where you can pass mxs struct instance and get it back with set properties.

inside c++ method you can predefine property names to set, or pass a list of properties to the method.
the mxs struct instance is a Value, so it has _get_property and _set_property methods.

it might be different implementation but can be as:

void SetStructData(Value* my_struct)
{
	BOOL bit = TRUE;
	float tab[4] = { 0, 1, 2, 3 };  

	if (my_struct && is_struct(my_struct)) 
	{
		my_struct->_set_property(n_value, bit ? &true_value : &false_value);
		Array* arr = new Array(0);
		for (int k=0; k < 4; k++) arr->append(Float::intern(tab[k]));
		my_struct->_set_property(n_dataArray, arr);
	}
}

for more information see _set_property declaration

in mxs your struct should be at least as:

struct MyStruct (value, dataArray)

this is the basic.
but there are several tricks to safe memory, avoid unnecessary value or array creation, check property existence or property value type, etc.


#15

Thanks Denist for the explanation,

I quickly try it but didn’t work, i will look into later.

whats the difference in using one_typed_value_local ? That’s what i’m using now. but it returns a multi array and then i have to convert it into a struct in maxscript, which is fine if there is no other way.

std::vector<nodesClass> theNodes;
std::vector<anotherClass> theArr;


one_typed_value_local(Array* result);

vl.result = new Array((int)theArr.size());

for (std::vector<anotherClass>::iterator currItem = theArr.begin(); currItem!= theArr.end(); ++currItem)
{
	Array* tmp = new Array((int)currItem->nodes.size() + 3);

	// add a boolean and an int
	tmp->append(new Point2Value(currItem->doContinue, currItem->parents));

	// add 2 floats and a boolean
	tmp->append(new Point3Value(currItem->nodes.at(0).length, currItem->nodes.at(0).size, currItem->nodes.at(0).canContinue));

	for (std::vector<nodesClass>::iterator node = currItem->nodes.begin(); node != currItem->nodes.end(); ++node)
	{
		tmp->append(new Point3Value(node->pos));
	}

	vl.result->append(tmp);
}

return_value(vl.result);

Klvnk suggested to look into the code in avg_DLX.ccp at line 7869
it seems to return a struct but could not get it to work either yet.


#16

instead of this line you can create an instance of your structure, and finally return an array of struct instances.
so it’s a scenario #2. (or #1). I would prefer to pass mxs struct definition to the function in case in this structure has not only data members but also some methods which you expect to use in MXS. So it’s #2.

if you have a mxs struct definition value in c++ you can create its instance as:

//StructDef* def ;
Value* structInstance = def->apply(NULL, 0);  // the instance with default parameters and initial settings

after that you can set the values as you need using _set_property, and append every new instance to returning array.


#17

I can’t get it to work.

To clarify in C++ i don’t have and maxscript struct’s, the only requirements when the plugin runs is a node( a max mesh) and a position. but i want the c++ code to return a maxscript struct.

looking at the code in avg_DLX.cpp i came up with the following code but it does not return anything just an OK. And i can’t find any information on how that works in on the help.

em i on the right track or i’m completely lost? :confused:

MAXScript_TLS* _tls = (MAXScript_TLS*)TlsGetValue(thread_locals_index);
six_typed_value_locals_tls(Name* struct_name, StructDef* sd, Array* result, Value* struct_instance, Value* doContinue, Value* parents);


vl.struct_name = (Name*)Name::intern(_T("test"));

int member_count = 0;
HashTable* tmpHash = new HashTable();
tmpHash->put_new(n_value, Integer::heap_intern(member_count++));
tmpHash->put_new(n_value, Integer::heap_intern(member_count++));

vl.sd = new StructDef(vl.struct_name, member_count, NULL, tmpHash, NULL, NULL);

vl.result = new Array(0);
vl.doContinue = (n_value, theArr.begin()->doContinue ? &true_value : &false_value);
vl.parents = (n_value, Integer::intern(theArr.begin()->parents));

Value* args[2] = { vl.doContinue, vl.parents };
vl.struct_instance = vl.sd->apply(args, 2);

vl.result->append(vl.struct_instance);

return_value_tls(vl.result);

I have to create a struct in sdk then add all the results to it then return that new struct.


#18

Actually my last code works!

While testing something else i had change the function return from Value to void… so of course wasn’t returning anything…

Thanks Denist


#19

nope it doesn’t not work.

it return the struct test, but its empty. the int and bool does not show up. can anyone tell me whats wrong?

Thanks,
Guillermo


#20
-- MXS call
struct my_struct_def (pt2, boo, arr)
data = createStructInstances my_struct_def
// Publishing MXS function:

def_visible_primitive(createStructInstances, "createStructInstances");
Value* createStructInstances_cf(Value **arg_list, int count)
{
	check_arg_count_with_keys(createStructInstances, 1, count);
	if is_structdef(arg_list[0])
	{
		StructDef* def = (StructDef*)arg_list[0];

		Value* prop1 = Name::intern(L"pt2");
		Value* prop2 = Name::intern(L"boo");
		Value* prop3 = Name::intern(L"arr");

		Array* result = new Array(0);
		for (int k=0; k < 10; k++)
		{
			Value* str = def->apply(NULL, 0);

			str->_set_property(prop1, new Point2Value((float)k, 0.0f));
			str->_set_property(prop2, mod(k,2) ? &true_value : &false_value);

			Array* arr = new Array(0);
			for (int i=0; i < 10; i++) arr->append(Integer::intern(k*1000 + i)); 
			str->_set_property(prop3, arr);

			result->append(str);
		}
		return result;
	}
	return &undefined;
}

that’s how i would do it… also i would check prop names and their existence, and create Values on TLS


#21

This makes sense, but is providing a struct to c++ in my case i want:

– mxs
result = calculateThePoints $box $point.pos

The method calculateThePoints has to return a mxs struct, so in c++ i need to create the struct from scratch, add all the values and return it to maxscript.