PDA

View Full Version : Creating a vmap in Layout?



bamboy
06-23-2007, 09:59 PM
Is it possible to create a vmap (specifically a morph map) in layout and then save it back to the object?

I have been applying cloth dynamics to objects then using a plugin I've written to save out endomorphs at small intervals. The goal is to "sample" the dynamics using endomorphs and then remove the dynamics. A second plugin sets the endomorph envelopes.

All this works except for one thing. When the endomorphs are saved out they are "fully" translated points, that is the vertex locations include the delta from the object's key framed location. I want an endomorph that contains only the "local" displacement from the dynamics. I figure I can "probably" calculate this local displacement but I can't figure how to create a custom morph vmap in Layout so I can save it back to the object. Is it possible?
Thanks!

Sensei
06-24-2007, 06:20 AM
I don't think so.. Modeler tools are in half working in Layout..

You just have to write two plug-ins - one layout generic that will in loop call go to next frame, and save transformed object as.. after each is custom save object matrix file.. And second modeler tool that will take lwo & this additional file and pass all points through matrix to go back from world to object coords space and build new relative morph map..

SplineGod
06-24-2007, 10:05 AM
You can create endomorphs in layout. You would have to create an endomorph before dynamics then create another endomorph after dynamics and subtract out the predynamics endomorph using the apply morph tool which does work in layout.
Another option is to save out a series of transformed objects and then use the Multi Target Single Envelope morph. You could probably use it as is or mdd scan the results or use your plugin on it as well.

bamboy
06-25-2007, 07:48 AM
Thanks for the info. Here is the workflow:
1) Apply dynamics to an animated object in Layout and calculate.
2) Use my first plugin to create and save endomorph "slices." The dynamics can now be removed.
3) Use my second plugin to set keys for the endomorphs.
4) Create a new plugin to "correct" the exported endomorphs by subtracting out the "keyframe animation translation" to return the dynamics displacement back relative to the base position. I'm going to try and use my "fix" map as a reference since those point shouldn't move from a local perspective.

The first two plugins are done. I just need to make the third. Later I will probably combine the first two leaving me with one Layout and one Modeler plugin.
Thanks again!

SplineGod
06-25-2007, 11:50 AM
The tool I would normally use subtract out the base morph is apply morph in modeler. It also appears to work in layout. It would be nice if you had a single plugin that could do all of that in one go in layout.

Sensei
06-25-2007, 12:14 PM
Try making simple Modeler plug-in for f.e. making one point with vmap. Then make simple Master handler, and try executing this Modeler tool.. If it works, you can use master handlers even notifications and get mesh data after bones, after motions, after deformations.. You will have to store them in custom made effective database. LW will send you these notifications after GoToFrame and Redraw All commands. In the main loop you will have to execute this Modeler command that will be making morphs and reading previously made mesh database and subtract from 2nd mesh database (without deformations) and giving you final relative position.

bamboy
06-25-2007, 03:52 PM
Those are great ideas guys. Thanks, I'll definitely keep working on it. I haven't found any commands or functions in the LWSDK that seem to fit "apply morph" though. Where should I look in the HTML documentation?
Thanks!

Sensei
06-25-2007, 04:36 PM
AFAIK it's only user tool, not available to developer to execute in command sequence class plug-in.. You can try searching Modeler.exe in binary mode in some hex editor for "apply" and then use it in CmdSeq class plug-in, but I doubt it'll work to be honest..

Sensei
06-25-2007, 04:57 PM
BTW, Apply Morph IMHO is useless to you, because it just adds morph to currently selected morph or base.. You need something that'll create new morph with new unique name after reading original point positions and subtraction of final after dynamics point position (and perhaps after world to object conversion matrix).

bamboy
06-29-2007, 01:04 PM
Question:
The MeshInfo function pntVGet assigns an array of floats to the array pointer passed as a parameter. Am I correct in assuming that this pointer now points to a data structure that holds the vertex map point data and that the values in that array can now be set... or is function just loading up the array with copies of the float values (passing by value) and therefore changing the values will not change the value in the vertex map. If the first is true then I know how to proceed but if the second is true I'm lost because I don't see a pntVSet function!
Thanks!

Sensei
06-29-2007, 01:24 PM
LWMeshInfo has "Info" suffix - it's real-only (there is also second type of globals with "Funcs" suffix which actually do something).. It just read vmap that has been set or loaded from LWO.. You must use plug-in class MeshDataEdit, MeshToolEdit or CommandSequence to make or modify vmap.. The all other plug-in classes are not able to modify and make new geometry..

bamboy
06-29-2007, 09:39 PM
Argh. I hate posting such a seemingly simple problem but can you see any reason this wouldn't work?

XCALL_( static int ) MorphCalc( int version, GlobalFunc *global, LWModCommand *local, void *serverData )
{
MeshEditOp *edit = local->editBegin(0, 0, OPSEL_DIRECT | OPSEL_MODIFY);
if(!edit) return AFUNC_BADLOCAL; // I'm returning with an error here
return AFUNC_OK;
}

ServerRecord ServerDesc[] = {
{ LWMODCOMMAND_CLASS, "MorphCalc", (ActivateFunc*)MorphCalc },
{ NULL }
};

I can't get a MeshDataOp pointer.
local seems valid. I can local->evaluate a command just fine.

FYI, I'm developing on a Powerbook using XCode 2 running against the Universal Binary build 1188 of Modeler. The model has just one three-point polygon.
Thanks!

Sensei
06-30-2007, 03:47 AM
Try OPSEL_GLOBAL, OPSEL_USER instead.. And try everything first in Modeler.. If it works in Modeler and does not work in Layout, then you are using something that has not been yet implemented in Layout.. Put MessageBox() Mac equivalent to open requester in function or quotes that you're not sure whether it's executed.. And of course add edit->done() before final return.. Ahh.. I see another problem, you don't have if( version != LWMODCOMMAND_VERSION ) return( AFUNC_BADVERSION ); that could confuse LW a lot!!!

bamboy
06-30-2007, 06:35 AM
1. I tried OPSEL_GLOBAL with and without OPSEL_MODIFY with no luck.
2. In this case I AM working in Modeler.
3. I can't even get an "edit" to call "edit->done" on.
4. I have the LWMODCOMMAND_VERSION check but removed it from my post for simplicity sake.
5. I know that it is local->editBegin failing because I tried:
if(!edit) msgf->Info("couldn't create MeshEditOp", NULL);
and it popped the error message.

This should be such a straight forward call that I'm wondering if it is a bug specific to the PPC portion of the UB 1188 build. I think I might try and rebuild and run this on my Intel Mac tower, and then if that doesn't work, I'll build it on my PC using VSExpress C++ and run it on the release PC build. If you can't see a reason why this simple call would fail like this I should try and rule out the possibility of an API or a "beta" bug.
Thanks!

Sensei
06-30-2007, 06:53 AM
I am afraid that I don't see anything else than what I mentioned earlier..

bamboy
06-30-2007, 08:28 PM
Sensei,
I figured out my mistake.

At some point early on I probably successfully called editBegin(...), but then exited with an error before the code could reach done(...).

Once you create a MeshEditOp without releasing it with done(..), that instance of Modeler is fried and will never again be able to call editBegin(...) without throwing an error, even if you have corrected your code. You need to quit and restart Modeler to clear the error (at least on Macs. I haven't tried it on PCs yet).

It didn't occur to me to restart Modeler. Duh!
I'll need to make sure all error conditions channel to done(...)
Thanks!

Sensei
07-01-2007, 10:26 AM
I have no idea how you made it, but on Windows I can't build project when application is still running.. Executables are locked by running app (still in use)..
Additionally still running Modeler is always using old code, not the newly one compiled..

bamboy
07-01-2007, 05:38 PM
I primarily work on a Mac PPC Powerbook although I have an Intel Mac and two PCs as well.

I develop the plugins in XCode and add them to Lightwave or Modeler using Add Plugin or Edit Plugin. Each time I recompile I just re-add the plugin and it works right away without restarting Lightwave or Modeler.

bamboy
07-04-2007, 08:45 AM
So close!

My CommandSequence Modeler plugin (part 2 of my Dynamics->Endomorph process) is working... almost.

I am looping through each morph map in the model adjusting the points in each map. My problem is that only the "pntMove" for the last vmap in the list seems to stick. The others vmaps remain untransformed.

First I tried:
enter MeshEdit
pointScan
--pointScan callback
----begin loop through vmaps
------set vmap for this pass
------move point in map
----end loop
exit meshEdit

Then I tried:
enter MeshEdit
begin loop through vmaps
--set vmap for this pass
--pointScan
----pointScan callback
------move point in map
end loop
exit meshEdit

And most recently:
begin loop through vmaps
--set vmap for this pass
--enter MeshEdit
--pointScan
------pointScan callback
------move point in map
----exit meshEdit
end loop

Same result in all approaches.

It's odd but the only thing I can think of is that maybe the CommandSequence holds only one point transformation buffer for each point so no matter how many maps I work on, it will only store the last transformation "in." Then, the buffer is only "committed" when the plugin is exited. Grasping at straws here.

Does anyone have any experience with how this works? Maybe I have the wrong approach altogether. I don't know. Any ideas? I'd appreciate any feedback. I'm so close! (I think)

FYI, here is the callback where the map points are moved. Like I said, it works, but only sticks for the last map processed in the loop:

static EDError morpPointCallback(void* data, const EDPointInfo *pi){
edit->pointVSet( edit->state, NULL, ((morphAttributes*)data)->map_type, ((morphAttributes*)data)->vmap_name ); // Sets this vmap for further actions
LWPntID pointID = pi->pnt;
float val[3];
if(edit->pointVGet( edit->state, pointID, val )){ // returns the rel offset (morph) of the current pt
float refVal[3]; // holds the position of the "fixed" point in the vmap
if(edit->pointVGet( edit->state, ((morphAttributes*)data)->refFixPnt, refVal )){ // returns the rel offset (morph) of the ref pt
double baseVal[3];
edit->pointPos( edit->state, pointID, baseVal);
static double newPos[3];
newPos[0] = baseVal[0] - (double)refVal[0];
newPos[1] = baseVal[1] - (double)refVal[1];
newPos[2] = baseVal[2] - (double)refVal[2];
edit->pntMove(edit->state, pointID, newPos);
}
}
return EDERR_NONE;
}


Thanks!

bamboy
07-04-2007, 09:30 AM
P.S - Here is the outer vmap loop that contains the meshEdit/pointScan calls:

// Get a count of VMaps of requested type
int numAllMapType = objfunc->numVMaps( userData.map_type );
for(int i=0; i<numAllMapType; i++){ // Loop through each VMap of requested type
// Retrieve VMap name for testing
userData.vmap_name = objfunc->vmapName( userData.map_type, i );
// Check for match
if(strstr(userData.vmap_name, userData.morph_group_str) != NULL){
edit = local->editBegin(0, 0, OPSEL_GLOBAL);
if(!edit){
return 102;
}else{
msgf->info("Created MeshEditOp!", NULL);
}
edit->pointScan(edit->state, morpPointCallback, &userData, OPLYR_ALL);
if (edit) { edit->done(edit->state, EDERR_NONE, EDSELM_CLEARCURRENT); edit=NULL; msgf->info("Released MeshEditOp.", NULL); }
}
}

Sensei
07-04-2007, 10:38 AM
I am little lost of what you're tring to achive...

You want to build series of morph maps?

pntMove() always works with currently selected in M button on bar morph.. Usually [base]

If you want to build series of morph maps, use pointVSet() or other vmap making one.. Alternatively to change active vmap that's selected in user interface (which probably also works with pntMove()) you can use command:

SELECTVMAP ktype sname
Set the current vertex map of a given type. The type can be MORF (relative morph), SPOT (absolute morph), WGHT (weight), MNVW (subpatch weight) or TXUV (texture UV).

BTW, vmap looking up (which is slow) can be optimized:

change:


edit->pointVSet( edit->state, NULL, ((morphAttributes*)data)->map_type, ((morphAttributes*)data)->vmap_name ); // Sets this vmap for further actions


to:


((morphAttributes*)data)->vmap = edit->pointVSet( edit->state, ((morphAttributes*)data)->vmap, ((morphAttributes*)data)->map_type, ((morphAttributes*)data)->vmap_name ); // Sets this vmap for further actions

Remember to clear up ((morphAttributes*)data)->vmap right before calling pointScan() or other scanning routines.. You can lookup as many vmaps as you wish (it saves LightWave searching by vmap name (strcmp() in loop each point) in database)

bamboy
07-04-2007, 04:58 PM
Success!


I am little lost of what you're tring to achive...
You want to build series of morph maps?
Actually, the morph maps are already built. They have been exported from layout by my first plugin. But, when they are saved out of Layout they are positioned relative to the animated objects position. I am just moving the points so the morph is back to being relative to the base position.


pntMove() always works with currently selected in M button on bar morph.. Usually [base]
This is where I went wrong. I thought that using pointVSet to select the vmap was enought... but it wasn't. You were right about the command SELECTVMAP.


If you want to build series of morph maps, use pointVSet() or other vmap making one.. Alternatively to change active vmap that's selected in user interface (which probably also works with pntMove()) you can use command:

SELECTVMAP ktype sname
Set the current vertex map of a given type. The type can be MORF (relative morph), SPOT (absolute morph), WGHT (weight), MNVW (subpatch weight) or TXUV (texture UV).
Bingo! I keep forgetting that the LWSDK API, like LScript is still in many ways a scripting API, and so it depends on you physically setting Lightwave's UI state.


BTW, vmap looking up (which is slow) can be optimized:

change:


edit->pointVSet( edit->state, NULL, ((morphAttributes*)data)->map_type, ((morphAttributes*)data)->vmap_name ); // Sets this vmap for further actions


to:


((morphAttributes*)data)->vmap = edit->pointVSet( edit->state, ((morphAttributes*)data)->vmap, ((morphAttributes*)data)->map_type, ((morphAttributes*)data)->vmap_name ); // Sets this vmap for further actions

Remember to clear up ((morphAttributes*)data)->vmap right before calling pointScan() or other scanning routines.. You can lookup as many vmaps as you wish (it saves LightWave searching by vmap name (strcmp() in loop each point) in database)
Ah, I see. The first time through I need to loop up the value but once I have it I can stuff a reference to it into my userData (aka (morphAttributes*)data) for the remainder of that pointScan activity. Great tip. Thanks!

Now I need to add a ton of error checking, warnings, inline instructions, etc. so it will be bulletproof enough to share. Then I will port it to Windows.

Thanks for your help Sensei!

SplineGod
07-04-2007, 09:53 PM
Congrats and thanks! :)

bamboy
07-05-2007, 11:28 AM
Thank you Spline God.
I have been very impressed that such high-level users such as yourself and Sensei are willing to help out beginners like myself. Plugin development is so arcane as it is and what with the SDK documentation being so spare I doubt I would have been able to code these plugins without the help of a community like this. Hopefully I can continue to increase my knowledge and contribute as well.
Thank you everyone!

SplineGod
07-05-2007, 07:46 PM
Youre welcome though I dont know how much help I actually was. Sensei definately knows more about this then I do. :)