Hi Ferdinand,
sadly i have to resurect this topic hence I still have problem with the normals when the AXIS changes.
I boiled down a script which moves the axis of selected polygon object to the parent (which is a common task in deply nested cad data )
anyway the axis is not a problem but I still have wrong shading because the normals are not transformed corectly ...
I attached a simple exaple file where a nut is orientaded off axis and results in wrong normals when executed the script.
thanks in advance
import c4d, array
#from c4d import Vector as V
#https://plugincafe.maxon.net/topic/13004/cad-normal-tag-flipped-after-polyon-merge-join-and-axis-repositioning-how-realign/6
#new Task:
#Corrects points and normals on a point object after placing the polygon axis to the same as the parent
def adjust_point_node(node, transform):
"""Corrects points and normals on a point object after running the join
command in the scenario specific to PC13004.
Args:
node (c4d.PointObject): The PointObject to transform.
transform (c4d.Matrix): The transform to apply.
Raises:
TypeError: When either `node` or `transform` are not of expected type.
"""
def read_normal_tag(tag):
"""`Reads a `c4d.NormalTag` to a list of c4d.Vector.
Args:
tag (c4d.NormalTag): The tag to read the normals from.
Returns:
list[c4d.Vector]: The red in normals. There are polygon_count * 4
normals, i.e. each vertex has a normal for each polygon it is
attached to. The normals are stored in polygon groups.
Raises:
RuntimeError: When the memory of the tag cannot be red.
TypeError: When `tag` is not a `c4d.NormalTag`.
"""
if not (isinstance(tag, c4d.BaseTag) and tag.CheckType(c4d.Tnormal)):
msg = f"Expected normal tag, received: {tag}."
raise TypeError(tag)
buffer = tag.GetLowlevelDataAddressR()
if buffer is None:
msg = "Failed to retrieve memory buffer for VariableTag."
raise RuntimeError(msg)
data = array.array('h')
data.frombytes(buffer)
# Convert the int16 representation of the normals to c4d.Vector.
return [c4d.Vector(data[i-3] / 32000.0,
data[i-2] / 32000.0,
data[i-1] / 32000.0)
for i in range(3, len(data) + 3, 3)]
def write_normal_tag(tag, normals, do_normalize=True):
"""`Writes a list of c4d.Vector to a `c4d.NormalTag`.
Args:
tag (c4d.NormalTag): The tag to write the normals to.
normals (list[c4d.Vector]): The normals to write.
do_normalize (bool, optional): If to normalize the normals.
Default to `True`.
Note:
Does not ensure that `normals` is only composed of c4d.Vector.
Raises:
IndexError: When `normals` does not match the size of `tag`.
RuntimeError: When the memory of the tag cannot be red.
TypeError: When `tag` is not a `c4d.NormalTag`.
"""
if not (isinstance(tag, c4d.BaseTag) and tag.CheckType(c4d.Tnormal)):
msg = f"Expected normal tag, received: {tag}."
raise TypeError(tag)
buffer = tag.GetLowlevelDataAddressW()
if buffer is None:
msg = "Failed to retrieve memory buffer for VariableTag."
raise RuntimeError(msg)
if do_normalize:
normals = [n.GetNormalized() for n in normals]
# Convert c4d.Vector normals to integer representation.
raw_normals = [int(component * 32000.0)
for n in normals for component in (n.x, n.y, n.z)]
# Catch input data of invalid length.
count = tag.GetDataCount()
if count * 12 != len(raw_normals):
msg = (f"Invalid data size. Expected length of {count}. "
f"Received: {len(raw_normals)}")
raise IndexError(msg)
# Write the data back.
data = array.array('h')
data.fromlist(raw_normals)
data = data.tobytes()
buffer[:len(data)] = data
# --- Start of outer function --------------------------------------------
if (not isinstance(node, c4d.PointObject) or
not isinstance(transform, c4d.Matrix)):
msg = f"Illegal argument types: {type(node)}{type(transform)}"
raise TypeError(msg)
# Transform the points of the node.
#points = [p * ~transform for p in node.GetAllPoints()]
# This works the polygon object stays in place now
mat = op.GetMg()
points = op.GetAllPoints()
for i,p in enumerate(points):
points[i] = ~transform * mat * p #Note that the multiplication sequence needs to be read from the right to the left.
node.SetAllPoints(points)
node.Message(c4d.MSG_UPDATE)
# these where my desperate efforts to fix it
#------------------------------------------------------------------------
node.SetMg(transform)
nodelocalmatrix = node.GetMl()
nodematrix = node.GetMg()
# Transform its baked normals.
tag = node.GetTag(c4d.Tnormal)
if isinstance(tag, c4d.NormalTag):
# We just have to apply the same transform here (without the
# translation).
# original Code
#normals = [~transform.MulV(n) for n in read_normal_tag(tag)]
# my tests
normals = [~transform.MulV(n) for n in read_normal_tag(tag)]
write_normal_tag(tag, normals)
else:
msg = f"Could not find normal data to adjust for: {node}."
print(f"Warning - {msg}")
def main():
c4d.CallCommand(13957) # Konsole löschen
if op is None or not op.CheckType(c4d.Opoint):
return False
doc.StartUndo()
doc.AddUndo(c4d.UNDOTYPE_CHANGE, op)
targetMat = op.GetUp().GetMg()
adjust_point_node(op, targetMat)
op.Message(c4d.MSG_UPDATE)
c4d.EventAdd()
doc.EndUndo()
if __name__=='__main__':
main()