Created
October 21, 2016 20:22
-
-
Save donovankeith/6ff1d342bc09ca1aacb1c0c549cdd7a7 to your computer and use it in GitHub Desktop.
Code for a Cinema 4D Python Expression Tag that automatically selects edges based on the angle between adjoining faces.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Select Phong Edges Expression | |
Selects any edges w/ broken phong shading. | |
Last Modified: 2016-10-21 | |
v0.0.1 | |
By Donovan Keith for MAXON USA | |
Usage Instructions | |
------------------- | |
1. Add a Python Expression tag to a polygon object. | |
2. Paste in this code. | |
Known Limitations | |
----------------- | |
- Doesn't support custom point normals. | |
- Calculates based on the first triangle in a given polygon, results may be unpredictable with non-coplanar NGons. | |
License | |
-------- | |
Feel free to re-use this code in any personal or commercial projects. Credit would be nice, but isn't mandatory. | |
""" | |
import c4d | |
import math | |
def YieldEdges(op, nbr): | |
"""Generates a list of a,b points in all edges. | |
""" | |
vadr = op.GetAllPolygons() | |
for i in xrange(op.GetPolygonCount()): | |
pli = nbr.GetPolyInfo(i) | |
for side in xrange(4): # # test all 4 sides of a polygon | |
# Only proceed if edge has not already been processed | |
# and edge really exists (for triangles side 2 from c..d does not exist as c==d) | |
if pli["mark"][side] or side==2 and vadr[i].c==vadr[i].d: continue | |
# One can also skip the side==2 && vadr[i].c==vadr[i].d test as pli["mark"][2] is always True for triangles | |
if side==0: | |
a=vadr[i].a; b=vadr[i].b | |
elif side==1: | |
a=vadr[i].b; b=vadr[i].c | |
elif side==2: | |
a=vadr[i].c; b=vadr[i].d | |
elif side==3: | |
a=vadr[i].d; b=vadr[i].a | |
yield a, b | |
def GetEdgeAngle(poly1, poly2, points): | |
"""Returns the angle between two polygons. | |
Limitation: only takes the first triangle into account. | |
poly1, poly2: CPolygons | |
Return: angle in Radians | |
Source Reference: | |
http://www.plugincafe.com/forum/forum_posts.asp?TID=12869&KW=polygon+normal&PID=50916#50916 | |
""" | |
a1,b1,c1 = points[poly1.a],points[poly1.b],points[poly1.c] | |
n1 = (b1 - a1).Cross(c1 - a1).GetNormalized() | |
a2,b2,c2 = points[poly2.a],points[poly2.b],points[poly2.c] | |
n2 = (b2 - a2).Cross(c2 - a2).GetNormalized() | |
angle_rad = c4d.utils.GetAngle(n1,n2) | |
return angle_rad | |
def GetPhongTag(obj): | |
"""Returns the first PhongTag on object.""" | |
if not obj: | |
return | |
tags = obj.GetTags() | |
for tag in tags: | |
if tag.GetType() == c4d.Tphong: | |
return tag | |
def GetPhongAngle(obj): | |
"""Returns phong limit angle of obj""" | |
if not obj: | |
return | |
phong_tag = GetPhongTag(obj) | |
if not phong_tag: | |
return 0.0 | |
angle_limit = phong_tag[c4d.PHONGTAG_PHONG_ANGLELIMIT] | |
if angle_limit: | |
return phong_tag[c4d.PHONGTAG_PHONG_ANGLE] | |
else: | |
return c4d.utils.Rad(180.0) | |
def PhongSelectEdges(obj, phong_deg=None): | |
"""Selects the edges whose adjoining polys have a > phong_deg angle | |
between them. Also selects broken phong edges. If phong_deg is not | |
specified, the function will calculate based on the Phong tag on the | |
object. | |
Note: no Undo support or Update calls are made. | |
""" | |
points = obj.GetAllPoints() | |
polys = obj.GetAllPolygons() | |
# Calculate the phong threshold using the specified angle, or if None the Phong tag on the object | |
phong_angle = None | |
if phong_deg is not None: | |
phong_angle = c4d.utils.Rad(phong_deg) | |
else: | |
phong_angle = GetPhongAngle(obj) | |
if phong_angle is None: | |
phong_angle = 0 | |
# Select Manually Broken Edges | |
neighbor = c4d.utils.Neighbor() | |
neighbor.Init(obj) | |
edge_count = neighbor.GetEdgeCount() | |
phong_break_edges = obj.GetSelectedEdges(neighbor, c4d.EDGESELECTIONTYPE_PHONG) | |
# Go through every edge | |
for edge_index, edge in enumerate(YieldEdges(obj, neighbor)): | |
point_a, point_b = edge | |
# Get the neighbor polygons | |
poly1_index, poly2_index = neighbor.GetEdgePolys(point_a, point_b) | |
poly1 = polys[poly1_index] | |
poly2 = polys[poly2_index] | |
# Calculate the difference between them | |
angle = GetEdgeAngle(poly1, poly2, points) | |
# If difference is over a threshold, select the edge | |
if angle >= phong_angle: | |
phong_break_edges.Select(edge_index) | |
# Select the Broken Phong Edges + Auto-Broken Edges | |
obj.SetSelectedEdges(neighbor, phong_break_edges, c4d.EDGESELECTIONTYPE_SELECTION) | |
def main(): | |
if not op: | |
return | |
# Retrieve the polygon object | |
obj = op.GetObject() | |
if not obj or not obj.IsInstanceOf(c4d.Opolygon): | |
return | |
PhongSelectEdges(obj) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment