how to get object's position and material color on all time line?
-
Dear. MAXON's SDK Team
I use R21 on microsoft windows with python.
I have threads that how to get object's position and material color on all time line.
below my simple source code.import c4d # import Cinema 4D module doc = c4d.documents.GetActiveDocument() # Cinema 4D active document number_of_sphere = 10 # [Set] sphere number frames_count = 2600 # [Set] frames counter for i in range(0, number_of_sphere): for f in range(0, frames_count): doc.SetTime(c4d.BaseTime(f, doc[c4d.DOCUMENT_FPS])) c4d.EventAdd() obj = doc.SearchObject('Sphere_' + str(i)) # get object position x = int(obj.GetMg().off.x) y = int(obj.GetMg().off.y) z = int(obj.GetMg().off.z) # get object color r = int(obj[c4d.ID_BASEOBJECT_COLOR].x * 255) g = int(obj[c4d.ID_BASEOBJECT_COLOR].y * 255) b = int(obj[c4d.ID_BASEOBJECT_COLOR].z * 255) print(obj.GetName() + ", Frame = " + str(f) + ", (X,Y,Z) = " + str(x) + "," + str(y) + "," + str(z) + ", RGB = " + str(r) + "," + str(g) + "," + str(b))
Please, guide to me for method of this.
Cheers,
MAXON's SDK Team -
Hi @jhpark!
I don't work for the SDK team, but I believe the script below will do what you want.Some quick notes about posting:
-
When entering your code into a post on this forum, make sure you hit this button first
It creates code tags in your post. Put your code in between those and then it will format your code automatically. -
Also, after submitting, hit the button Topic Tools at the bottom right of your post to Ask as Question.
-
When someone has answered your question correctly, click this button at the bottom of their post.
This makes it clear to the moderators when the question has been correctly answered.
Here's the code. Because you were using
ID_BASEOBJECT_COLOR
, I was unsure if you wanted the object's display color or the material color (they are two different things), but I wrote this for the sphere's texture tags' material's color. Also, the code is for the spheres' relative position. More work would need to be done to get the animating position track values into global space.import c4d from c4d import gui def GetNextObject(op): #function for navigating the hierarchy if op==None: return None if op.GetDown(): return op.GetDown() while not op.GetNext() and op.GetUp(): op = op.GetUp() return op.GetNext() c4d.EventAdd() def getPreviewRange(doc,fps): #returns the active preview range fps = doc.GetFps() fromTime = doc.GetLoopMinTime().GetFrame(fps) toTime = doc.GetLoopMaxTime().GetFrame(fps)+1 return [fromTime,toTime] def convertVecToRgb(vector): #converts vector to rgb list return [vector[0]*255,vector[1]*255,vector[2]*255] def main(doc): fps = doc.GetFps() previewRange = getPreviewRange(doc,fps) #rather than needing to set frames manually, you can simply resize your preview range. frame_count = previewRange[1]-previewRange[0] # this section navigates the hierarchy and saves all of the spheres to a list called 'output' # it's better to do this than to use doc.SearchObject in the case you have multiple spheres with the same name obj = doc.GetFirstObject() if obj==None: gui.MessageDialog('There are no objects in the scene.') return output = [] while obj and obj!=None: if obj.GetType() == c4d.Osphere: output.append(obj) obj = GetNextObject(obj) if len(output) == 0: gui.MessageDialog('There are no spheres in the scene.') # loops through spheres in the scene for sphere in output: #prints a separating line to the console print '#' * 80 for f in range(previewRange[0], previewRange[1]): doc.SetTime(c4d.BaseTime(0, doc[c4d.DOCUMENT_FPS])) keyTime = c4d.BaseTime(f,fps) #get the current frame # POSITION pTracks = sphere.GetCTracks() #get the sphere's animating tracks pos = [sphere.GetMl().off.x,sphere.GetMl().off.y,sphere.GetMl().off.z] #get the sphere's default relative position #replace those values with the animating ones. for t in pTracks: descid = t.GetDescriptionID() #get the track's id if descid[0].id == c4d.ID_BASEOBJECT_REL_POSITION: #see if it matches the object's position track curve = t.GetCurve() #get the track's animation curve keyvalue = curve.GetValue(keyTime, fps) #get the animation curve's value at the current frame if descid[1].id == c4d.VECTOR_X: pos[0] = keyvalue #add to x elif descid[1].id == c4d.VECTOR_Y: pos[1] = keyvalue #add to x elif descid[1].id == c4d.VECTOR_Z: pos[2] = keyvalue #add to z # MATERIAL COLOR tags = sphere.GetTags() #get sphere's tags matColor = [] #create material color list for tag in tags: #loop through sphere's tags if tag.GetType() == c4d.Ttexture: #check if tag is a texture tag mat = tag.GetMaterial() #if yes, get the tag's material tracks = mat.GetCTracks() #get the material's animating tracks for t in tracks: descid = t.GetDescriptionID() #get the track's id if descid[0].id == c4d.MATERIAL_COLOR_COLOR: #see if it matches the material color track curve = t.GetCurve() #get the track's animation curve keyvalue = curve.GetValue(keyTime, fps) #get the animation curve's value at the current frame matColor.append(keyvalue*255) #add r,g,b to matColor if len(tracks) == 0: #in case it's not animating, use general Color matColor = convertVecToRgb(mat[c4d.MATERIAL_COLOR_COLOR]) # I prefer using string formatting with the placeholder %s for strings, %d for numbers, # and the % as the replacement operator print("Name: %s, Frame: %d, Position (x,y,z): %d,%d,%d, Material Color (r,g,b): %d,%d,%d"%( sphere.GetName(),f,pos[0],pos[1],pos[2], matColor[0],matColor[1],matColor[2])) if __name__=='__main__': # rather than using documents.GetActiveDocument, I found that you can pass a reference to the document using this method main(doc)
Here's a scene file where the object's display colors and material colors are different. The display colors are visible in the viewport, but you will see the material color if you render.
Spheres.c4d -
-
Hi jhpark, thanks for reaching out us.
Aside from the notes left by @blastframe - thanks dude for the remarks - I think it's worthy, thinking of a more generic scene, to mention the need BaseDocument::ExecutePasses() to be sure that everything is actually evaluated before querying the scene rather than the EventAdd() which serves a different scope.
This function is responsible to execute the scene evaluation and, consequently to be sure that, moving from a frame to another, all the items in the scene reflect the changes imposed by the frame switch.The approach used by @blastframe actually operates on CTracks and key but, although this approach works fine for your specific case, when more evaluation dependencies are created in the scene you could easily end up in unexpected results.
The code could then look like
frames_count = 10 # [Set] frames counter for f in range(0, frames_count): doc.SetTime(c4d.BaseTime(f, doc.GetFps())) # evaluate the scene doc.ExecutePasses(None, True, True, True, c4d.BUILDFLAGS_NONE) obj = doc.SearchObject('Cube') # get object position x = int(obj.GetMg().off.x) y = int(obj.GetMg().off.y) z = int(obj.GetMg().off.z) # get object color r = int(obj[c4d.ID_BASEOBJECT_COLOR].x * 255) g = int(obj[c4d.ID_BASEOBJECT_COLOR].y * 255) b = int(obj[c4d.ID_BASEOBJECT_COLOR].z * 255) print("Frame = " + str(f) + ", (X,Y,Z) = " + str(x) + "," + str(y) + "," + str(z) + ", RGB = " + str(r) + "," + str(g) + "," + str(b))