PDA

View Full Version : Maxscript, SDK and .NET


Bercon
04-26-2008, 03:17 PM
Okey lets say I want to make a maxscript function with max sdk that adds .NET Button to form user gives to the maxscript function.

Looking at the source code of maxsdotNet plugin it seems that the argument given to the max sdk function is DotNetObjectWrapper, however max sdk doesn't really have any information on how to handdle these. How would I get the .NET form (or actually MaxCustomControls.MaxForm) out of the wrapper so I can use it as standard .NET stuff?

Do I have to take all classes from maxsdotNet sources and compile them along with my plugin or what? Would that even work?

ypuech
04-27-2008, 11:51 AM
MaxCustomControls is a standard .NET assembly so you can add it as a .NET dependency in the Project settings.

But I don't really understand what you're trying to do. I think to speed up your script, you can just use a .NET assembly without coding a MAXScript extension that will be really hard to implement.

In fact, you can use the MaxForm in a .NET assembly (C# or VB.NET), build your form with the designer and add methods to be able to change controls values and communicate with them in MAXScript code.

Bercon
04-27-2008, 04:56 PM
I'm trying to recreate scene explorer, so basicly I have to deal large amount of data (>10 000 objects) from 3ds max. I need solid connection to 3ds max scene, taking name of 10 000 objects and listing them into DataGridView. This takes MINUTES with maxscript dotnet connection. There is no way to speed it up with .NET assemblies since I need to take name of each object from 3ds max, .NET doesn't have any access to 3ds max. Or does it? There is zero documentation about it if it can do it.

So I need 3ds max SDK to connect object data to .NET form. This is still fairly simple. The problem rises when I want to combine maxscript with SDK and .NET since Autodesk decided not to document maxscript .NET connection at all.

In a nutshell. I want to make my own custom .NET maxscript functions. Like:

myAddButtonFunction maxscriptDotNetForm "Hello World button"

which would add .NET button to given .NET form.

ypuech
04-28-2008, 10:41 AM
What's slow ?
The processing in MAXScript ?
Sending string to DataGridView with MAXScript ?
The building of the .NET form ?

Maybe your example: myAddButtonFunction maxscriptDotNetForm "Hello World button"
isn't meaningful for what you're trying to do, but there's no advantage to code such method in C++ as in C# it would be easier and faster.

For example, SceneExplorer was designed with 2 .NET assemblies : a C# assembly (with core SceneExplorer Framework) and a C++/CLI, mix of managed and unmanaged plugin code. I think that if we want to use MAXScript a lot, we can avoid the C++/CLI stage and use MAXScript extension but without .NET. Why ?:
If the use of a DataGridView is slow, do that work in C# or VB.NET.
If the MAXScript processing of objects is too slow, code a MAXScript extension but without .NET. So, you can communicate between your MAXScript code and the managed code with basic data types:
MAXScript -> MAXScript Extension -> MAXScript -> C# / VB.NET -> MAXScript etc.

C++ SDK + C++/CLI + MAXScript SDK == real pain :D.
As you can see it, the code of the mxsDotNet DLX is really hard to read because MAXScript SDK isn't very easy to use and not well designed, C++/CLI is just glue code; very hard to read and use.

Bercon
04-28-2008, 02:37 PM
for i in 1 to objects.count do DataGridView.Rows.Item[i-1].Cells.Item[0].Value = objects[i].name

That is slow.

And that I want to speed up by creating custom Maxscript function which does it for me. Unfortunately it I just can't figure out how to get Maxscript .NET Wrapper classes to my project. I tried compiling the mxsdotNet, using the library from that. However I can't seem to figure out how to get namespaces over.

I get this when I use "using namespace MXS_dotNet;"
error C2871: 'MXS_dotNet' : a namespace with this name does not exist

Bercon
04-28-2008, 03:03 PM
I guess its time to give up on this. Obviously Autodesk never meant anybody to be able to expand their Maxscript .NET connection.

ypuech
04-28-2008, 04:03 PM
for i in 1 to objects.count do DataGridView.Rows.Item[i-1].Cells.Item[0].Value = objects[i].name
What you can do in this situation is trying to send to a custom .NET assembly method an array of objects names and the DataGridView control. So this heavy loop will be completely done in managed code and should be (for sure!) very much faster than a loop in MAXScript.

I can code this small assembly if you want.

Bercon
04-28-2008, 04:12 PM
How would you handle maxscript array? I guess you could try converting the whole array into single string but I'm not sure if maxscript string object likes strings with 100 000 chars.

ypuech
04-28-2008, 04:24 PM
My first try would be to convert the maxscript array to a .NET array. With 10000 objects this can be slow but it's worse the try!

EDIT:
For example, here is how to convert a MAXScript string array to a .NET string array:

fn mxsArrayToDotNetArray mxsArray =
(
local mxsArrayCount = mxsArray.Count
netArray = dotNetObject "System.String[]" mxsArrayCount

for i = 1 to mxsArrayCount do
(
local netString = dotNetObject "System.String" mxsArray[i]
local netIndex = dotNetObject "System.Int32" (i-1)
netArray.SetValue netString netIndex
)

netArray
)

mxsStrArray = #("Hello", "GoodBye", "DotNet", "ADO")
netStrArray = mxsArrayToDotNetArray mxsStrArray

for i = 0 to (netStrArray.Length-1) do
(
local netIndex = dotNetObject "System.Int32" i
print (netStrArray.GetValue netIndex)
)

dotNetUtils.AssignColumnFromArray (DataGridView dataGridView) (String[] names) (int column)

Here is how to use the dotNetUtils assembly:

dotNet.loadAssembly "dotNetUtils.dll"
dataGridViewUtils = dotNetClass "dotNetUtils.DataGridViewUtils"
dataGridViewUtils.AssignColumnFromArray dataGridView netStrArray 0

I'm not sure the assembly code is good 'cause I've never used a DataGridView.

Bercon
04-28-2008, 05:56 PM
You are right, its slightly faster than working directly with DataGridView cell values. However, even with 1 000 objects it crashes max. I'm not sure where the problem is, even increasing the heapsize doesn't help. Maxs dotnet connection just can't handle large amount of data. I wish there would be a way to send sdk pointters to actual dotnet objects with maxscript. It would make everything so easy. Without doing this, there is just no way to get this thing working.

Too bad too sad, thanks for your help anyway.

ypuech
04-28-2008, 06:13 PM
It crashes max without any error or message ?

ZeBoxx2
04-28-2008, 06:46 PM
Out of curiosity... have you considered not loading 10,000 or 1,000 or even 100 objects into the list?
It would be nice if you could, but let's face it - the user can't display 100 lines on screen as it is (okay, those running at 2560x1600 could).

So perhaps you can just chunk it up into N items maximum at a time, and let any unloading/loading happen dynamically (based on the scrollbar position or... who knows what).

Bercon
04-28-2008, 08:05 PM
ypuech, it uses 100% of one core and does nothing.

ZeBoxx2, yeah I thought about that. I guess I'll give it a try, it might give annoying delay when scrolling since its all running on the same thread (I think).

One option is to do everything with SDK and .NET but it would mean a lot more work not to mention making changes and customizing the thing would be a pain.

Bercon
04-29-2008, 06:22 PM
Okey I finally found a way to do it. Maxscript -> SDK -> .NET. However its still pretty slow, and the time to update single cell seems like O(n^2). It also hangs when there are >2000 cells or something like that. Anyways, here are the functions:

#pragma managed
#using <System.dll>
#using <System.Windows.Forms.dll>
using namespace System::Windows::Forms;
namespace BerconExplorer {
void managedUpdateColumn(int handle, int index, char** arr, int length) {
System::Windows::Forms::Control ^ cont = gcnew System::Windows::Forms::Control();
DataGridView ^ dGW = (DataGridView ^)cont->FromHandle(System::IntPtr(handle));
for (int i = 0; i<length; i++)
dGW->Rows[i]->Cells[index]->Value = gcnew System::String(arr[i]) ;
}
}
#pragma unmanaged
#include "definsfn.h"
def_visible_primitive(BerconExplorer, "BerconExplorer");
using namespace BerconExplorer;
Value* BerconExplorer_cf(Value **arg_list, int count) {
check_arg_count(BerconExplorer, 3, count);
int handle = arg_list[0]->to_int();
int index = arg_list[2]->to_int();
Array* nameArr = (Array *)arg_list[1];
int length = nameArr->size;
char** stringArr = new char*[length];
for (int i = 0; i<length; i++)
stringArr[i] = nameArr->data[i]->to_string();
BerconExplorer::managedUpdateColumn(handle, index, stringArr, length);
return &ok;
}

ypuech
04-30-2008, 09:13 AM
That's a good try Jerry.

But, is DataGridView cells number limited ?
Have you tried to use dGW->Rows->Add() instead of dGW->Rows[i] ?

Bercon
04-30-2008, 02:36 PM
I create the rows before calling that update. Its kinda weird that the code I posted is actually slower than doing it maxscript, or doing it using the assembly you made. I'm not really sure why. I do have two conversions, first from maxscript string to char* and then to .NET String. But I think thats unavoidable and it shouldn't really be THAT slow.

Anyway, the correct way to work here would probably be using DataGridView in virtual mode, so it'd only update the shown values. I'll have to check out how well that works out.

Bercon
04-30-2008, 04:14 PM
Okey, I tried the virtual mode and all I can say is WHOA. Its very fast, without any lag even with 10 000 objects.

I also studied what was actually slow in the SDK function. It turns out that reserving memory for new string takes ages! Meaning the gcnew System::String("Hello").

Anyway I wont be needing that any more since using the Virtualmode (only updating the displayed cells) is so much faster.

Thanks for everybody for support.

grabjacket
08-26-2008, 09:50 PM
Hi Bercon,

i'm struggling with a listview. I want it to display a nodeselection (name and wirecolor). It's rather slow when selecting large numbers of nodes. I also stumbled across the virtualmode for the .net listview. But i'm having trouble understanding it.
I'm able to display my testItems in the listview using the virtualmode, like so:
lvPreview = dotNetObject "listview"
lvPreview.virtualmode = true
--the virtualListSize gets set through a callback
--so i'm always getting the correct amount of listviewItems
--in this example i'll just use an arbitrary number
lvPreview.virtualListSize = 26

--the event handler function
function fn_createVirtualItem control arg =
(
newItem = dotNetObject "listViewItem" "myTestItem"
newItem.subItems.add "theSub"
arg.item = newItem
)
dotNet.addEventHandler lvPreview "RetrieveVirtualItem" fn_createVirtualItem

So i'm just creating a certain amount of listviewItems which are the same. Could you help me with creating each listviewItem to display the name and wirecolor of each selected object? Somehow i need to get into that eventHandler function and change the properties of the listviewItems, but i don't see how.

Klaas

CGTalk Moderation
08-26-2008, 09:50 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.