Leaderboard
Popular Content
Showing content with the highest reputation on 04/07/2021 in all areas
-
I have no idea why I avoided this so long for a little inconvenience of setting it up and using it. The results are insane. I am not even colorgrading my renders and they look like they just went to an entire higher level. Especially the colors. I urge everyone to have a look at it and integrate it into their workflows when possible. As I said, I haven't colorgraded with it yet but it doesn't seem to be that much of a pain for way, way better results. Here's some comparison shots. Top is always sRGB, bottom is ACES. Look at these juicy colors! Here's a video on how to set it up with Redshift:2 points
-
I mean yeah, I don't use Reflectance or Sculpting, but Takes were a gamechanger and made many, many things sooo much easier.2 points
-
April 13th 3D & Motion Design Show "...Some exciting news and announcements from MAXON" Looks like you guys won't have long to wait. As a perpetual customer I'm happy to have saved last year's typical upgrade cost. It'll be interesting to see if the pace of development has increased under DMcG. I imagine it has, but whether they make progress fast enough remains to be seen. In 2021 there are lots of alternatives.1 point
-
noedle.com is an awesome place, where EVERYTHING you can find is FOR FREE. Hundreds of maps and more to come! This is still a beta btw, if you have any suggestion, please suggest!1 point
-
A bit petty but I hope MAXON stop that useless "S" mark. Just call it R23.5 if you want to release midyear.1 point
-
Hi @bjlotus, This was not me be pedantic about terminology and you are computing the mean and the normals. I was just pointing out that you were just computing the normal for one of the planes in a polygon - unless I am overlooking something here. But for quads there are two, one for each triangle in the quad. Below is an illustration of a quad which I did manually triangulate for illustration purposes. You can see the vertex normals in white (which are each equal to the normal of the triangle/plane the lie in). And two plane normals in black, one for each triangle. The actual polygon normal is then the arithmetic mean of both plane normals. So when you just pick one vertex in a quad to calculate the polygon normal, i.e., simply say one of the white normals is the polygon normal, then you assume that both tris of the quad lie in the same plane, i.e., that the quad is coplanar. Which of course can happen, but there is no guarantee. Quads are today usually quite close to being coplanar (and not that comically deformed as my example), but you will still introduce a little bit of imprecision by assuming all quads to be coplanar. Cheers, Ferdinand1 point
-
Hi @bjlotus, hi @Cairyn, there is one problem with your normals calculation. You calculate just a vertex normal in the polygon and then declare it to be the polygon normal 😉 (your variable "cross"), i.e., you assume all your polygons to be coplanar. With today's high density meshes you can probably get away with that to a certain degree, but it would not hurt to actually calculate the mean vertex normal of a polygon, a.k.a., the polygon normal to have a precise result. The problem with your "flattening" is that it is not one. Unless I have overread something here in the thread, the missing keyword would be a point-plane projection. You just translate all selected points by a fixed amount, which was if I understood that correctly only a work in progress, but obviously won't work. Things could also be written a bit more tidely and compact in a pythonic fashion, but that has very little impact on the performance and is mostly me being nitpicky 😉 I did attach a version of how I would do this at the end (there are of course many ways to do this, but maybe it will help you). Cheers, Ferdinand """'Flattens' the active polygon selection of a PolygonObject. Projects the points which are part of the active polygon selection into the mean plane of the polygon selection. """ import c4d def GetMean(collection): """Returns the mean value of collection. In Python 3.4+ we could also use statistics.mean() instead. Args: collection (iterable): An iterable of types that support addition, whose product supports multiplication. Returns: any: The mean value of collection. """ return sum(collection) * (1. / len(collection)) def GetPolygonNormal(cpoly, points): """Returns the mean of all vertex normals of a polygon. You could also use PolygonObject.CreatePhongNormals, in case you expect to always have a phong tag present and want to respect phong breaks. Args: cpoly (c4d.Cpolygon): A polygon. points (list[c4d.vector]): All the points of the object. Returns: c4d.Vector: The polygon normal. """ # The points in question. a, b, c, d = (points[cpoly.a], points[cpoly.b], points[cpoly.c], points[cpoly.d]) points = [a, b, c] if c == d else [a, b, c, d] step = len(points) - 1 # We now could do some mathematical gymnastics to figure out just two # vertices we want to use to compute the normal of the two triangles in # the quad. But this would not only be harder to read, but also most # likely slower. So we are going to be 'lazy' and just compute all vertex # normals in the polygon and then compute the mean value for them. normals = [] for i in range(step + 1): o = points[i - 1] if i > 0 else points[step] p = points[i] q = points[i + 1] if i < step else points[0] # The modulo operator is the cross product. normals.append(((p - q)) % (p - o)) # Return the normalized (with the inversion operator) mean of them. return ~GetMean(normals) def ProjectOnPlane(p, q, normal): """Projects p into the plane defined by q and normal. Args: p (c4d.Vector): The point to project. q (c4d.Vector): A point in the plane. normal (c4d.Vector): The normal of the plane (expected to be a unit vector). Returns: c4d.Vector: The projected point. """ # The distance from p to the plane. distance = (p - q) * normal # Return p minus that distance. return p - normal * distance def FlattenPolygonSelection(node): """'Flattens' the active polygon selection of a PolygonObject. Projects the points which are part of the active polygon selection into the mean plane of the polygon selection. Args: node (c4d.PolygonObject): The polygon node. Returns: bool: If the operation has been carried out or not. Raises: TypeError: When node is not a c4d.PolygonObject. """ if not isinstance(op, c4d.PolygonObject): raise TypeError("Expected a PolygonObject for 'node'.") # Get the point, polygons and polygon selection of the node. points = node.GetAllPoints() polygons = node.GetAllPolygons() polygonCount = len(polygons) baseSelect = node.GetPolygonS() # This is a list of booleans, e.g., for a PolygonObject with three # polygons and the first and third polygon being selected, it would be # [True, False, True]. polygonSelection = baseSelect.GetAll(polygonCount) # The selected polygons and the points which are part of these polygons. selectedPolygonIds = [i for i, v in enumerate(polygonSelection) if v] selectedPolygons = [polygons[i] for i in selectedPolygonIds] selectedPointIds = list({p for cpoly in selectedPolygons for p in [cpoly.a, cpoly.b, cpoly.c, cpoly.d]}) selectedPoints = [points[i] for i in selectedPointIds] # There is nothing to do for us here. if not polygonCount or not selectedPolygons: return False # The polygon normals, the mean normal and the mean point. The mean point # and the mean normal define the plane we have to project into. Your # image implied picking the bottom plane of the bounding box of the # selected vertices as the origin of the plane, you would have to do that # yourself. Not that hard to do, but I wanted to keep things short ;) polygonNormals = [GetPolygonNormal(polygons[pid], points) for pid in selectedPolygonIds] meanNormal = ~GetMean(polygonNormals) meanPoint = GetMean(selectedPoints) # Project all the selected points. for pid in selectedPointIds: points[pid] = ProjectOnPlane(points[pid], meanPoint, meanNormal) # Create an undo, write the points back into the polygon node and tell # it that we did modify it. doc.StartUndo() doc.AddUndo(c4d.UNDOTYPE_CHANGE, node) node.SetAllPoints(points) doc.EndUndo() node.Message(c4d.MSG_UPDATE) # Things went without any major hiccups :) return True def main(): """Entry point. """ if FlattenPolygonSelection(op): c4d.EventAdd() if __name__ == '__main__': main()1 point
-
Sorry Stephanie - I missed the Forum! At least I now know who to speak to when I need some Python help ; )1 point
-
Hi @israeldavid, hi @srek, I think that is a fun topic 😄 . But I also think you underestimate a bit the size of our solar system ^^ Neptune, the last planet in our system (sorry, Pluto 😞 ), has a radius of about 25,000 km but is about 4.5 billion kilometers away from the sun. So its not hard to imagine that when you work with real data, that you won't see much except the sun and maybee a planet when you are close enough. So you will need some sort of scaling when you want to show the whole solar system. Srek's approach of animating stuff more or less manually is probably much more production focused, but I thought I'd give it a go with a Python generator. It took me surprisingly only about 90 lines of code to do a somewhat working solar system. You can find the code and a scene file below. Please keep in mind that I mostly did this as a "fun thing", so it will likely not be exactly what you want, but maybe you can still borrow bits from it (for example to do the expressions Srek talked about). My approach is deliberately data driven, since this is what I thought to be fun 😉 A little animation done with the generator, the little blue pixel is earth. The solar system is at 1% distance scale of the real solar system, so 1km is 10m in this system. solar_system.mp4 Cheers, Ferdinand The code: """Summary Attributes: ORBITAL_SYSTEM (TYPE): Description """ import c4d import math INVERSE_EARTH_PERIOD = 1. / 365. # The solar system as a hierarchical data structure. Distances and radii are # measured in kilometers, orbital periods are measured in (earth) days. You # can extend the system easily by just adding satellites (technically you # could also let the sun orbit something). The code below is however setup in # such fashion that you can only have one root in the system, i.e., you cannot # have two primaries in the data structure at level zero. ORBITAL_SYSTEM = { "Sun": { # The name of the primary "radius": 696340, # The radius of the primary "distance": 0, # The distance to its primary "color": c4d.Vector(1, .75, 0), # The viewport color of the primary "period": 0, # The orbital period of the primary "satellites": { # The satellites of the primary "Mercury": { "radius": 2439.7, "distance": 57.91E6, # i.e., 57.91 million kilometers "color": c4d.Vector(.55, .55, .55), "period": 88, "satellites": {}, }, "Venus": { "radius": 6051.8, "distance": 108.2E6, "color": c4d.Vector(.9, .9, .9), "period": 225, "satellites": {}, }, "Earth": { "radius": 6371, "distance": 148.55E6, "color": c4d.Vector(.1, .5, .9), "period": 365, "satellites": { "Moon": { "radius": 1737.1, "distance": 384400, "color": c4d.Vector(.3, .3, .3), "period": 27, "satellites": {} }, }, }, "Mars": { "radius": 3389.5, "distance": 240.16E6, "color": c4d.Vector(.85, .35, .1), "period": 687, "satellites": {}, }, "Jupiter": { "radius": 69911, "distance": 778.5E6, "color": c4d.Vector(.85, .7, .1), "period": 4380, "satellites": {}, }, "Saturn": { "radius": 58232, "distance": 1489.6E6, "color": c4d.Vector(.55, .4, .25), "period": 10585, "satellites": {}, }, "Uranus": { "radius": 25362, "distance": 2871E6, "color": c4d.Vector(.65, .85, .95), "period": 30660, "satellites": {}, }, "Neptune": { "radius": 24622, "distance": 4495E6, "color": c4d.Vector(.05, .45, .65), "period": 60225, "satellites": {}, }, }, }, } TWO_PI = math.pi * 2. def build_system(name, data, settings): """Builds a system from a fragment of an ORBITAL_SYSTEM data structure. This function builds recursively all the sub-systems of a system. Args: name (str): The name of the primary. data (dict): Its data dictionary taken from ORBITAL_SYSTEM. settings (dict): The user data parameters. Returns: c4d.BaseObject: The system fragment. """ # The primary as a sphere object. primary = c4d.BaseList2D(c4d.Osphere) primary.SetName("body") primary.MakeTag(c4d.Tphong) # Write its node data. combined_scale = settings["global_scale"] * settings["distance_scale"] xpos = data["distance"] * combined_scale primary[c4d.PRIM_SPHERE_SUB] = 32 primary[c4d.PRIM_SPHERE_RAD] = data["radius"] * settings["global_scale"] primary[c4d.ID_BASEOBJECT_REL_POSITION, c4d.VECTOR_X] = xpos primary[c4d.ID_BASEOBJECT_USECOLOR] = 2 primary[c4d.ID_BASEOBJECT_COLOR] = data["color"] # And build its satellites. for satellite_name, satellite_data in data["satellites"].items(): satellite = build_system(satellite_name, satellite_data, settings) satellite.InsertUnder(primary) # A null so that we can easily animate the orbit arround its primary. root = c4d.BaseList2D(c4d.Onull) root.SetName(name) primary.InsertUnder(root) # The current document time. t = doc.GetTime().Get() # Animation period of of that primary in relation to earth period. p = data["period"] * settings["orbit_duration"] * INVERSE_EARTH_PERIOD # Bails if the object has no period. if p == 0.0: return root # The rotational value as the fraction of period remainder of the # document time. We then have to multiply by 2π since Cinema handles # roatation in radians. theta = TWO_PI * (t % p / p) # Set the heading of the null. root[c4d.ID_BASEOBJECT_REL_ROTATION, c4d.VECTOR_X] = theta return root def main(): """Entry point for Cinema 4D to evaluate the Python generator object. """ settings = { # Out input data is in real world units, we have to scale this # a bit down. "global_scale": op[c4d.ID_USERDATA, 1], # Just our solar system alone is fricking huge, so we propbably want # to scale down distances a bit, so that we can actually see # something. "distance_scale": op[c4d.ID_USERDATA, 2], # The time in seconds on year takes, i.e., one full earth orbit. "orbit_duration": op[c4d.ID_USERDATA, 4], } # The root element. root_name = list(ORBITAL_SYSTEM.keys())[0] root_data = ORBITAL_SYSTEM[root_name] # Build the system. root = build_system(root_name, root_data, settings) # This is only needed for when the user collapses the Python generator via # CSTO, so that we get then a nice hierarchy. system = c4d.BaseList2D(c4d.Onull) system.SetName(op.GetName()) root.InsertUnder(system) return system solar_system.c4d1 point