|
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
Post by Jack Edwards // Apr 6, 2007, 6:06pm
Jack Edwards
Total Posts: 4062
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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. |
|