Copy UVs between meshes with same geometry

About Truespace Archives

These pages are a copy of the official truespace forums prior to their removal somewhere around 2011.

They are retained here for archive purposes only.

Copy UVs between meshes with same geometry // Scriptorium

1  |  

Post by Jack Edwards // Apr 6, 2007, 6:06pm

Jack Edwards
Total Posts: 4062
pic
I've been bugging the developers for this as a UV tool for a while, but I'm beginning to think it would be fairly simple to implement as a script.

Can anyone point me in the right directions towards implementing this?

1.) First problem would be selecting the two objects.

2.) Then how to read the UV array from the one and set it on the other.

Haven't really touched the scripting since I finished up with Norm's course so I'm a fair bit rusty at the moment. ;-)

-Jack.

Post by ProfessorKhaos // Apr 7, 2007, 8:05am

ProfessorKhaos
Total Posts: 622
pic
By same geometry do you mean an object with the same number of vertices, triangles, and faces? Each of which would have to have the same order in their respective lists to do a direct UV copy.


If that's what you mean, then it's actually a fairly easy task.


One could always check the number of vertices, the number of triangles, and the vertex order in the triangles as a safeguard. If those match, then you could copy the respective UV streams to the new object.


Glen

Post by ProfessorKhaos // Apr 7, 2007, 9:51am

ProfessorKhaos
Total Posts: 622
pic
Not well polished, but functional... relies on the link editor to provide a geometry source mesh and a UV source mesh.


Probably could be automated a bit more by someone who's a little bit smarter at selecting objects and creating tool buttons. This one just compares 2 object meshes linked to it and if they have the same number of vertices and triangles, and the triangle list is in the same order then it assumes a UV map can be transferred.


Threw in a "Matrix Matcher with XYZ transform" to make sure the copied object has the same dimensions as the original geometry object. XYZ transform allows you to position the copied object relative to the geometry object so that they're not overlapping.


Hope this helps.

Post by ProfessorKhaos // Apr 7, 2007, 10:16am

ProfessorKhaos
Total Posts: 622
pic
Guts of code is as follows for quick reference



// OnComputeOutputs

// Called to compute values of all output connectors

function OnComputeOutputs(params)

{

UVSourceMesh = params.conValue('UVSourceMesh');

GMSourceMesh = params.conValue('GMSourceMesh');


// TODO: put your computation here


params.ConValue('Mesh') = GMSourceMesh;


// get some info from input meshes


UVSourceNumTris = UVSourceMesh.GetNumTriangles();

GMSourceNumTris = GMSourceMesh.GetNumTriangles();


// check to make sure there are input meshes present


if ((!UVSourceNumTris) || (!GMSourceNumTris)) return;


// we have input meshes, get other pertinent data for comparison


UVSourceNumVertices = UVSourceMesh.GetNumVertices();

GMSourceNumVertices = GMSourceMesh.GetNumVertices();


//UVSourceTriList = System.CreateDO('Space 3D Package/Triangle Vertices Stream Data');

//GMSourceTriList = System.CreateDO('Space 3D Package/Triangle Vertices Stream Data');


UVSourceTriList = UVSourceMesh.GetTrianglesStreamByName('Triangle Vertices Stream Data');

GMSourceTriList = GMSourceMesh.GetTrianglesStreamByName('Triangle Vertices Stream Data');


// are there equal number of vertices and triangles in each mesh?


if ((UVSourceNumVertices != GMSourceNumVertices) || (UVSourceNumTris != GMSourceNumTris)) return;


// are triangle vertex indices identical in both meshes?


bad_tri_flag = 0;


for (tri_index = 0; tri_index < UVSourceNumTris; tri_index++) {

if (UVSourceTriList.i(tri_index) != GMSourceTriList.i(tri_index)) bad_tri_flag = 1;

if (UVSourceTriList.j(tri_index) != GMSourceTriList.j(tri_index)) bad_tri_flag = 1;

if (UVSourceTriList.k(tri_index) != GMSourceTriList.k(tri_index)) bad_tri_flag = 1;

}


if (bad_tri_flag == 1) return;


// ok, mesh is close enough to allow UV direct copying


UVSourceUVCoord = System.CreateDO('Space 3D Package/UV Coordinate Stream Data');

UVSourceUVCoord = UVSourceMesh.GetCustStreamByName('UV Coordinate Stream Data');


UVSourceUVTris = System.CreateDO('Space 3D Package/UV Triangle Stream Data');

UVSourceUVTris = UVSourceMesh.GetTrianglesStreamByName('UV Triangle Stream Data');


// copy geometry input mesh


Mesh = GMSourceMesh;


// modify output mesh by UV input mesh UV coords and triangles lists


Mesh.AttachCustStream(UVSourceUVCoord);

Mesh.AttachTrianglesStream(UVSourceUVTris);


// output should have UV map from 1st mesh applied to geometry of 2nd mesh


params.conValue("Mesh") = Mesh

}

Post by Jack Edwards // Apr 7, 2007, 2:34pm

Jack Edwards
Total Posts: 4062
pic
Sweet Prof! :)


LOL guess the only part you left for me to do is the picking mechanism.. ^-^


I'll go through the code later tonight. I think this may be exactly what I was looking for.


What I want it for is uv mapping pesky things like cables and horns and things that are generally extruded cylindrical shapes. Just make a copy of the object point edit it to a cylindrical shape then apply a cylindrical mapping. Copy the UVs back to the original object and bam UV mapping done. ;)


Should work as a good starting point for heads too. Copy the head, Spherize the verts, apply a spherical mapping, then UV copy back to the original.


-Jack.

Post by Jack Edwards // Apr 8, 2007, 2:33am

Jack Edwards
Total Posts: 4062
pic
Ok, I think I'm close to cracking this.


Looks like the Node class is what I want to use to get the selected objects. First one being the source, second (and additional) one(s) being the destination.


Where's I'm stuck is going from the name of the object to a pointer to the object that lets me modify it, instead of giving me a copy...


I've got some ideas left to try, so might be posting a solution later.


-Jack.

Post by Jack Edwards // Apr 8, 2007, 3:21am

Jack Edwards
Total Posts: 4062
pic
Ok I'm stuck.

Here's a section of the code (using a command script):


iSelCount = Node.SelectedCount();
if (iSelCount < 2) // check for more than one selected
{
System.Alert("Please select both a Source and Destination Object.");
}

sSrcObjName = Node.Selected(0);
sDestObjName = Node.Selected(1);
System.Alert(sSrcObjName + " " + sDestObjName);

srcMesh = Node.Value(sSrcObjName, "Mesh");
destMesh = Node.Value(sDestObjName, "Mesh");


It seems to work fine except for the fact that destMesh appears to be a local copy and not the original and doesn't seem to update any changes made to it... :-(

If I can get this part solved there's a lot of cool things I can get done with other ideas too.

-Jack.

Post by ProfessorKhaos // Apr 8, 2007, 4:03am

ProfessorKhaos
Total Posts: 622
pic
I wonder if the assignment operator can be done the other way too? Maybe you manipulate destMesh as desired, then assign it back to the connector as follows?


Node.Value(sDestObjName, "Mesh") = destMesh


Haven't tried it myself yet but maybe it will work.

Post by Jack Edwards // Apr 8, 2007, 4:41am

Jack Edwards
Total Posts: 4062
pic
Think I tried that and it complained. Mesh is output only I believe. I'll check again though.


I wonder if it's a limitation, because I've noticed that all the example scripts always create a whole new object. Which is what I may end up having to do. Create a third object then delete the second. Or possibly swap out the shape node for a new one...


Still that seems a crappy solution because without the ability to update the existing Mesh data we're really limited in what scripting can do to edit a mesh.


-Jack.

Post by Jack Edwards // Apr 8, 2007, 4:43am

Jack Edwards
Total Posts: 4062
pic
Yeah no go: "invalid proceedure call or argument"

Same result for Input Mesh, btw.

-Jack.

Post by ProfessorKhaos // Apr 8, 2007, 5:34am

ProfessorKhaos
Total Posts: 622
pic
There maybe a way to create a temporary input connector directly to the shape brick, pass the output mesh to the shape brick, then remove the temporary input connector. This is where it gets to be dicey territory.


BTW, I think I only did the easy part for you ;)

Post by Jack Edwards // Apr 8, 2007, 5:36am

Jack Edwards
Total Posts: 4062
pic
Crud unless someone's got any other ideas, I think the only solution is to use your cube mesh generator method, ProfK, and create a whole new object from scratch based on the data in the old object. :(

edit:
Looks like our posts crossed. I think we need Norm or one of the devs to help with this one. I'm really suprised that it never came up before...

edit:
Hmm.... just checked and the cube generator uses an script object instead of script command, so it's back to the same problem... :(

-Jack.

Post by Jack Edwards // Apr 8, 2007, 10:35pm

Jack Edwards
Total Posts: 4062
pic
Alright, I think I have a possible idea... what if the Lock() function is used to get the stream pointer directly...?



HRESULT IRdMesh::LockTrianglesStream ( [in] RtStreamId idStream,

[in] RtDWORD dwFlags,

[out, retval] void ** ppTrianglesStream

)




Locks given triangles stream and returns access pointer to that stream


Parameters:

idStream identification of stream to be locked

dwFlags flags for locking

ppTrianglesStream returned locked stream


Returns:

standard HRESULT processing can be applied to returned value


This is from the IRdMesh Struct Reference in the TrueSpace7 Data Objects document.


It would make sense for this to be the mechanism to get at the actual data...


-Jack.
Awportals.com is a privately held community resource website dedicated to Active Worlds.
Copyright (c) Mark Randall 2006 - 2024. All Rights Reserved.
Awportals.com   ·   ProLibraries Live   ·   Twitter   ·   LinkedIn