C4D & 3DCoat: prevent vertex order shuffle?

Become a member of the CGSociety

Connect, Share, and Learn with our Large Growing CG Art Community. It's Free!

REPLY TO THREAD
 
Thread Tools Search this Thread Display Modes
  01 January 2018
C4D & 3DCoat: prevent vertex order shuffle?

Hi there,

I realize that I did things out of order by UVing after rigging, but I needed to make changes to the topology as I rigged. I don't have a ton of character build experience.

Now I'm having some issues with point order coming from the .OBJ and .FBX models in 3DCoat, heading to C4D. It normally wouldn't really matter to me, but I've already painted weights in my rig in C4D. I have tried transferring weights and the rig using VAMP, but that's getting jacked up too. Have tried Bret's CV weights scripts as well, no dice. It doesn't see all the joints for some reason.

Because of all of this, I feel like the easier thing to do is to just transfer the UVs.

Working in C4D R19 & 4.7.24
1) Modelled in zBrush, retopo in 3DCoat and C4D
2) UV'd in 3DCoat
3) export .OBJ or .FBX (tried both)
4) import into C4D
5) vertex order on imported model totally shuffled from the old one. Point count is the same, geo looks to be the same.

The only other thing I can think of is using the exchange plugin with 3DC, maybe that preserves vertex order?

Has anyone had this experience working between these apps? Shed some light. Worst case is that I'll bite the bullet and re-weight.


Thanks,
Mike
__________________
insta
 
  01 January 2018
yup, its a recuring problem, i report it, it gets fixed, then a couple of versions later its back.

send all this info along with files to support. i guess it got messed up again.
 
  01 January 2018
Thanks for the response Aleksey.

I'm assuming you mean it's a bug in the 3DCoat export. Or is it something that happens in C4D upon import?


Thanks,
Mike
__________________
insta
 
  01 January 2018
Had that issue too, wrote a script that transfers the UVs from one model to another based on the vertex position data. It's of limited use as it doesn't solve topological duplicates but it works for my cases. (Model in C4D, export to 3DCoat for UVing, reimport to C4D.)
 
  01 January 2018
oooooOOOo sounds good Cairyn, care to share?
__________________
insta
 
  01 January 2018
Well, no guarantees.
Select source first (the object re-imported from 3DCoat), then target (original C4D object).


import c4d
from c4d import gui

# Transfers the UVW coordinates from one object to another.
# The objects need to represent the same model and have the same
# general topology, however, point and polygon indices may be
# different. The script will use the local coordinates to find the
# fitting polys and then transfer the corresponding UVW.
#
# The reason for this script is: When a model is transferred to
# 3DCoat for UVing and painting, sometimes (not always) the point
# and polygon indices are different on re-import. The reason for
# this is unknown; it does not seem to result from the save/load
# format.
#
# VAMP is supposed to do this already and transfer UV mapping
# between objects (even if not topologically identical!) but it
# just doesn't work.


def getUVTag(obj):
 currenttag = obj.GetFirstTag()

 if currenttag == None :
 return None
 while currenttag != None :
 # print currenttag.GetTypeName()
 if currenttag.GetType() == c4d.Tuvw :
 return currenttag
 currenttag = currenttag.GetNext()
 return None


def ecomp(vec1, vec2):
 epsilon = 0.0001
 if vec1.x > vec2.x + epsilon : return False
 if vec1.x < vec2.x - epsilon : return False
 if vec1.y > vec2.y + epsilon : return False
 if vec1.y < vec2.y - epsilon : return False
 if vec1.z > vec2.z + epsilon : return False
 if vec1.z < vec2.z - epsilon : return False
 return True


def findPolyIndex(targetpoly, targetobj, sourcepolycnt, sourceobj):
 # targetpoly is a polygon. What polygon in the sourceobj
 # is the equivalent? Search through all polys and find
 # one with the same positions.
 pointa = targetobj.GetPoint(targetpoly.a)
 pointb = targetobj.GetPoint(targetpoly.b)
 pointc = targetobj.GetPoint(targetpoly.c)
 pointd = targetobj.GetPoint(targetpoly.d)
 targettri = targetpoly.IsTriangle()
 for i in xrange(sourcepolycnt):
 sourcepoly = sourceobj.GetPolygon(i)
 sourcetri = sourcepoly.IsTriangle()
 if sourcetri == targettri :
 # both polys must be tris or quads, otherwise we can stop 
 # the comparison already
 spointa = sourceobj.GetPoint(sourcepoly.a)
 spointb = sourceobj.GetPoint(sourcepoly.b)
 spointc = sourceobj.GetPoint(sourcepoly.c)
 # now the actual comparison.
 # This relies on the idea that the points have the same sequence,
 # which is not necessarily true.
 if sourcetri :
 if ecomp(pointa,spointa) and ecomp(pointb,spointb) and ecomp(pointc,spointc) :
 # the comparison is done as vectors.
 # Perhaps it is needed to introduce an epsilon as minimum distance
 return True, i, 0
 if ecomp(pointa,spointb) and ecomp(pointb,spointc) and ecomp(pointc,spointa) :
 return True, i, 1
 if ecomp(pointa,spointc) and ecomp(pointb,spointa) and ecomp(pointc,spointb) :
 return True, i, 2
 else :
 spointd = sourceobj.GetPoint(sourcepoly.d)
 if ecomp(pointa,spointa) and ecomp(pointb,spointb) and ecomp(pointc,spointc) and ecomp(pointd,spointd) :
 return True, i, 0
 if ecomp(pointa,spointb) and ecomp(pointb,spointc) and ecomp(pointc,spointd) and ecomp(pointd,spointa) :
 return True, i, 1
 if ecomp(pointa,spointc) and ecomp(pointb,spointd) and ecomp(pointc,spointa) and ecomp(pointd,spointb) :
 return True, i, 2
 if ecomp(pointa,spointd) and ecomp(pointb,spointa) and ecomp(pointc,spointb) and ecomp(pointd,spointc) :
 return True, i, 3
 
 return False, 0, 0
 

def main():
 print "---------------------"
 print "TransferUV"
 print "---------------------"
 selectlist = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_SELE  CTIONORDER)

 # we must have two objects here, one source and one target
 if len(selectlist) != 2 :
 print "Must select exactly 2 objects in the correct sequence"
 return
 sourceobj = selectlist[0]
 if sourceobj == None :
 print "No source object"
 return
 if sourceobj.GetType() != c4d.Opolygon :
 print "Source not polygonal"
 return
 targetobj = selectlist[1]
 if targetobj == None :
 print "No target object"
 return
 if targetobj.GetType() != c4d.Opolygon :
 print "Target not polygonal"
 return
 print "Source: " + sourceobj.GetName()
 print "Target: " + targetobj.GetName()
 sourcetag = getUVTag(sourceobj)
 if sourcetag == None :
 print "No source UV tag"
 return
 targettag = getUVTag(targetobj)
 if targettag == None :
 print "No target UV tag"
 return
 print "Source Tag: " + sourcetag.GetName()
 print "Target Tag: " + targettag.GetName()
 # At this point, the two tag variables are pointing at
 # UVW tags. Now we can fetch data from these. The object
 # variables will provide us with point and polygon data.
 
 sourcepointcnt = sourceobj.GetPointCount()
 targetpointcnt = targetobj.GetPointCount()
 print "Source points: ", sourcepointcnt
 print "Target points: ", targetpointcnt
 # Access a point with vector = GetPoint(index)
 # No point writing needed here!
 sourcepolycnt = sourceobj.GetPolygonCount()
 targetpolycnt = targetobj.GetPolygonCount()
 print "Source polygons: ", sourcepolycnt
 print "Target polygons: ", targetpolycnt 
 # Polygon counter counts also ngon subpolys, thus
 # we can ignore ngons here!

 # Test for the UVW tag 
 # for i in xrange(sourcetag.GetDataCount()):
 # uvwdict = sourcetag.GetSlow(i)
 # print i, uvwdict["a"], uvwdict["b"], uvwdict["c"], uvwdict["d"]

 # Now we know our data. What we need to do:
 # for each poly in the target, find the corresponding
 # poly in the source; if found, transfer that UVW value.
 notfoundcounter = 0
 foundcounter = 0
 for ti in xrange(targetpolycnt):
 targetpoly = targetobj.GetPolygon(ti)
 # print targetpoly
 found, si, offset = findPolyIndex(targetpoly, targetobj, sourcepolycnt, sourceobj)
 if not found :
 notfoundcounter += 1
 print "Not found: ", ti
 else :
 # here we will transfer the uv coordinates 
 foundcounter += 1
 print "Found: ", ti, si, offset
 # for now ignore the offset; this happens rarely anyway...
 # seems this is an issue of the ngons after all
 # for checking: 3906/4187 im Ziel entspricht 73/72 in der Source
 uvwdict = sourcetag.GetSlow(si)
 targettag.SetSlow(ti, uvwdict["a"], uvwdict["b"], uvwdict["c"], uvwdict["d"])

 print "Found: ", foundcounter, " Not found: ", notfoundcounter
 print "----- end transfer -----"
 targettag.Message (c4d.MSG_UPDATE)
 targetobj.Message (c4d.MSG_UPDATE)
 c4d.EventAdd()
 return

if __name__=='__main__':
 main()
 
  02 February 2018
Thanks for posting up the code Cairyn, but unfortunately I wasn't able to get it working.

1) selected source
2) selected target
3) executed script

...nothing seemed to happen. Tried in R18 and R19.

Will submit a bug report to 3DC and report back.
__________________
insta
 
  02 February 2018
Originally Posted by upskate: Thanks for posting up the code Cairyn, but unfortunately I wasn't able to get it working.

1) selected source
2) selected target
3) executed script


What is the error message in the console? Do you have UV tags on both poly objects?

You should get massive output in the console about any points that were found in the "other" model (or not).
 
  02 February 2018
Yes both poly objects have UV tags.

I noticed there was an error on line 93 (accidental tab or spaces), so I tweaked that, ran the script again.Now the script definitely is searching as it takes a little while to go through everything, but all the polys come up "Not found: XXXX"

My target was under a null, so brought it out and tried again but no luck. Also tried renaming both source and target polys but no dice.
__________________
insta
 
  02 February 2018
Originally Posted by upskate: Yes both poly objects have UV tags.

I noticed there was an error on line 93 (accidental tab or spaces), so I tweaked that, ran the script again.Now the script definitely is searching as it takes a little while to go through everything, but all the polys come up "Not found: XXXX"

My target was under a null, so brought it out and tried again but no luck. Also tried renaming both source and target polys but no dice.

If you put both objects into the same position, are the objects covering each other? Maybe the positions have been ruined too.

You can change the constant epsilon to something larger - this is the cube around each point that defines the maximum distance where the point in the other object is found.

Try it for two cubes and check what the output says... or send me your file and I'll have a look why the algorithm doesn't work for you. (As I not said, this is a very ad hoc approach and neither efficient nor totally smart.)
 
  02 February 2018
Riptide Pro (for C4D) preserves vertex/point ordering on both import/export of .obj files... of course that won't help if 3DCoat turns out to be the re-ordering culprit.

Cheers.
 
  02 February 2018
Why do these programs change the mesh point orders when doing symmetry?

This is my guess on how most people are writing the code for this task. And why this re-ordering problem happens:
- Get all of the points to the left or right side of the center points of the mesh
- Delete them ( At this time in the process. The point order of the remaining points is still unchanged)
- Copy all of the polygons that have not been deleted
- Flip their point positions by *-1 and paste them into the mesh's matrix <-------This is where the point order gets changed
- Weld the center points at the middle of the mesh
Result: A symmetrical mesh. But the point order is now different from the original

Is there another way to make a mesh symmetrical without deleting, copying, flipping, and pasting and thus killing the point orders?
On meshes where the sides are fairly close I know this can be done using a range option to sample the good side to the new side. And just moving the points. That will keep the current point order in tact.
But I don't see how you can do that when the two sides of the mesh are drastically different?
Is there a technique, or maybe even a code library, for doing this?
Or is it just an industry wide accepted rule that symming always kills the point order. And there's nothing we can do about it?

-ScottA
__________________
My Gallery
 
  02 February 2018
Hi Scott,

Keep in mind that I'm tootin' my own horn here - with some commercial plugins - but...

My Undertow plugin, in addition to a bunch of UV-mapping tools, was specifically designed for working with Symmetry Objects and/or other symmetrical mesh modeling. It has tools for selecting (or setting up selections to select) half the model, recovering the symmetrical uv-mapping once the mesh has been re-built and even mirroring Group selections across the mesh (if you need those, for Poser, lFoot -> rFoot, etc. for example).

While I think you'd find that useful, my Morph Mill plugin might help more with the topic at hand... it has a Remap Point Order command that will remap the vertices/points of one mesh to match another (just keep a copy of the original around to remap to), along with some other tools.

You can try either/both for a free 30-day trial (just enter DEMO when prompted for a license).

Cheers.
 
  02 February 2018
Hi Keith,
I was really wondering about how people are approaching this task industry wide. And how I might be able to do it myself without relying on anyone else.
It sounds like your Morph Mill is using that same delete, copy, flip, paste workflow. Which kills the original point order.
And your approach to dealing with that problem is to cache the original non-symmetrical mesh's point order (not the positions) in some sort of table. And in some manner edit the newly symmed mesh's point order to make it match the cached point orders.

That actually helps me out quite a bit.
I'll just need to go learn how to edit mesh point orders. I've never done that kind of thing.

Thanks,
-ScottA
__________________
My Gallery
 
  02 February 2018
Not exactly, but close... the plugin goes through a pretty elaborate process to correlate the new mesh (it's points) with the target/original mesh when activated. If interested, it's probably best explained by the existing plugin documentation (just click on the Remap Point Order link on the left).

While I'm at it, the "Background Info" link of the Undertow plugin documentation explains the "Symmetry Workflow" process that I use (but was written before and does not cover use of the Remap Point Order of the Morph Mill plugin).

Cheers.
 
reply share thread



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
CGSociety
Society of Digital Artists
www.cgsociety.org

Powered by vBulletin
Copyright 2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump
Miscellaneous

All times are GMT. The time now is 12:36 PM.


Powered by vBulletin
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.