Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Particles from python SDK

    Cinema 4D SDK
    2
    5
    938
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Z
      zauhar
      last edited by

      I was looking for examples of creating particle systems using the python SDK. The articles I find are about accessing particle information after the system is created.

      I need to create the systems programmatically. and adjust properties (emitter rate and color) dynamically. Are there any examples available?

      Thanks!

      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @zauhar
        last edited by ferdinand

        Hello @zauhar,

        welcome to the Plugin Café and thank you for reaching out to us. Although you did well for your first posting, I would recommend that you read our Forum and Support Guidelines for future questions, as they line out some procedures.

        About your question: There are two particle systems in Cinema 4D; the standard particle system and the Thinking Particles system.

        Standard Particles

        Standard particles are exposed in the Python API with c4d.modules.particles and c4d.plugins.ObjectData.ModifyParticles. There can be multiple standard particle systems in a scene, which are represented by particle emitter objects. The system is inherently multi-threaded (i.e., relatively fast), but you cannot allocate particles for a system programmatically, you can only change the parameters of an emitter (either manually or programmatically). You can however write particle system modifiers, i.e., a force which acts upon particles with ObjectData.ModifyParticles. So, to interact with standard particles (in the intended way) you must implement an ObjectData plugin, and you cannot spawn any new particles. There is formally a way to circumvent these problems by reading the raw particle data of the hidden Tparticle tags attached to an emitter. But for that you will need either an intimate knowledge of our API or be comfortable with reverse engineering data structures in general. Find here an example for that approach.

        Thinking Particles

        Thinking Particles (TP form now on) are exposed in the Python API with c4d.modules.thinkingparticles. There is only one TP system per document, no matter how many Xpresso setups and TP emitter the document contains, the TP master system. In some scripting contexts this master system is already pre-exposed as a module attribute tp, check Manuals: Python Scripting Nodes to find out where it is pre-exposed. In all other cases you must retrieve it manually with BaseDocument.GetParticleSystem(). Other than for standard particles, you can allocate and modify particles from everywhere in code, but as a down-side, the TP system is single-threaded. You could for example write a Python Programming Tag which both spawns TP particles each frame as well as modifies the existing ones.

        We cannot go much further here, as we cannot provide full solutions in support, as lined out in our Forum Guidelines, you must produce a concrete problem case, a code-example, before we write code.

        And as a minor warning: Particles are in general a very computationally complex topic, and while I in my time as a user had a lot of fun with writing my own TP stuff in Python, you should be aware that Python is a language that is ill-suited for particle computations of high complexity. You will be better off using C++ when you want a high-throughput solution.

        Cheers,
        Ferdinand

        MAXON SDK Specialist
        developers.maxon.net

        Z 1 Reply Last reply Reply Quote 0
        • Z
          zauhar @ferdinand
          last edited by

          @ferdinand
          Thanks so much for the detailed reply. At this point I do not need the particles to interact with any forces, this is for display only. I will investigate your links when I want to do something more complex, which I am sure I will.

          I have gotten things working partly at this point, I don't know if it is appropriate owing to length, but I am posting demo code below. This just creates a particle emitter and programmatically moves it around.

          Unfortunately when I render the animation, by default only a fraction of the objects show up, because I don't know the corresponding property name for 'Birthrate Renderer' . I can manually set this in the dialog and all is well but cannot figure out what the correct symbol is.
          PARTICLEOBJECT_RENDERINSTANCES does not seen to do it, and if I try to set
          PARTICLEOBJECT_BIRTHRAYTRACER it seems to break things.

          Can you direct me to correct attribute name?
          Thanks!
          Randy

          import c4d
          import math
          import random
          
          mat = c4d.BaseMaterial(c4d.Mmaterial)
          mat.SetName('emitter sphere')
          mat[c4d.MATERIAL_COLOR_COLOR] = c4d.Vector(0.8, 0.0, 0.0)
          doc.InsertMaterial(mat)
          
          sph = c4d.BaseObject(5160)
          rad = sph.GetRad()
          
          particleRad = 2.0
          
          scale = particleRad/rad[0]
          
          sph.SetAbsScale((scale,scale,scale))
          
          ttag = c4d.TextureTag()
          ttag.SetMaterial(mat)
          sph.InsertTag(ttag)
          sph.SetBit(c4d.BIT_ACTIVE)
          
          emitter = c4d.BaseObject(5109)
          emitter.SetBit(c4d.BIT_ACTIVE)
          doc.InsertObject(emitter)
          sph.InsertUnder(emitter)
          
          emitter[c4d.PARTICLEOBJECT_BIRTHEDITOR] = 500
          emitter[c4d.PARTICLEOBJECT_RENDERINSTANCES] = 500
          emitter[c4d.PARTICLEOBJECT_SIZEX] = 0.2
          emitter[c4d.PARTICLEOBJECT_SIZEY] = 0.2
          emitter[c4d.PARTICLEOBJECT_TYPE] = c4d.PARTICLEOBJECT_TYPE_PYRAMID
          emitter[c4d.PARTICLEOBJECT_ANGLEH] = 2 * math.pi
          emitter[c4d.PARTICLEOBJECT_ANGLEV] = math.pi
          emitter[c4d.PARTICLEOBJECT_SHOWOBJECTS] = True
          
          
          fps = 24
          emitter[c4d.PARTICLEOBJECT_START] = c4d.BaseTime(0, fps)
          emitter[c4d.PARTICLEOBJECT_STOP] = c4d.BaseTime(500, fps)
          emitter[c4d.PARTICLEOBJECT_LIFETIME] = c4d.BaseTime(5, fps)
          
          
          ## Animate 500 frames, new position every ten frames
          
          frame = 0
          pos = c4d.Vector(0,0,0)
          emitter.SetAbsPos(pos)
          doc.SetTime(c4d.BaseTime(frame, fps))
          c4d.CallCommand(12410)
          
          
          
          for segment in range(50) :
          	frame = 10 * segment
          	for k in range(3) :
          		pos[k] = -30 + 60*random.random()
          	emitter.SetAbsPos(pos)
          	doc.SetTime(c4d.BaseTime(frame, fps))
          	c4d.CallCommand(12410) # record
          
          
          
          
          
          ferdinandF 1 Reply Last reply Reply Quote 0
          • ferdinandF
            ferdinand @zauhar
            last edited by ferdinand

            Hello @zauhar,

            @zauhar said in Particles from python SDK:

            I have gotten things working partly at this point, I don't know if it is appropriate owing to length, but I am posting demo code below.

            I am not 100% sure how this sentence was meant, but you can post here demo code of any length you want, and yours is relatively tame. When you are encountering a bug, or what you think is a bug, it is however of beneficial to shorten the code when possible. And to be crystal clear here: We love executable code, i.e., what you posted, as this makes it often easier to answer things.

            Can you direct me to correct attribute name?

            The id you are likely looking for is PARTICLEOBJECT_BIRTHRAYTRACER. But I can do you one better and show you how to find out these ids on your own (see screen grab below). There was also recently a posting on this topic if you want a more complete summary on how to find out type and DescId.

            123.gif

            I am not quite sure what you mean by PARTICLEOBJECT_BIRTHRAYTRACER 'breaks things'. Could you please elaborate since you do not use that parameter in your script (or show us a script where stuff breaks)?

            Cheers,
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 0
            • Z
              zauhar
              last edited by

              @ferdinand

              That is great! I did not imagine that working so simply, that's a wonderful feature.

              Regarding the 'broken' part, I did 'something' and spheres stopped appearing in the viewport, just the 'tracers'. I cannot reproduce that, I must have screwed up. All is right with the world now.

              In case it helps others, the demo code below now does everything I currently need. Programmatically changes position of the emitter, and following this discussion (https://forums.cgsociety.org/t/c4d-animation-via-python/1546556) I also change the color over the course of each 10-frame segment.

              Thanks so much for your help, I am really sold on 4D.

              import c4d
              import math
              import random
              
              mat = c4d.BaseMaterial(c4d.Mmaterial)
              mat.SetName('emitter sphere')
              mat[c4d.MATERIAL_COLOR_COLOR] = c4d.Vector(0.8, 0.0, 0.0)
              
              ## Get RGB tracks for continuous color update
              
              redtrack = c4d.CTrack(mat, c4d.DescID(c4d.DescLevel(c4d.MATERIAL_COLOR_COLOR, c4d.DTYPE_COLOR, 0, ), c4d.DescLevel(c4d.VECTOR_X, c4d.DTYPE_REAL, 0)))
              mat.InsertTrackSorted(redtrack)
              
              greentrack = c4d.CTrack(mat, c4d.DescID(c4d.DescLevel(c4d.MATERIAL_COLOR_COLOR, c4d.DTYPE_COLOR, 0, ), c4d.DescLevel(c4d.VECTOR_Y, c4d.DTYPE_REAL, 0)))
              mat.InsertTrackSorted(greentrack)
              
              bluetrack = c4d.CTrack(mat, c4d.DescID(c4d.DescLevel(c4d.MATERIAL_COLOR_COLOR, c4d.DTYPE_COLOR, 0, ), c4d.DescLevel(c4d.VECTOR_Z, c4d.DTYPE_REAL, 0)))
              mat.InsertTrackSorted(bluetrack)
              
              doc.InsertMaterial(mat)
              
              sph = c4d.BaseObject(5160)
              rad = sph.GetRad()
              
              particleRad = 2.0
              
              scale = particleRad/rad[0]
              
              sph.SetAbsScale((scale,scale,scale))
              
              ttag = c4d.TextureTag()
              ttag.SetMaterial(mat)
              sph.InsertTag(ttag)
              sph.SetBit(c4d.BIT_ACTIVE)
              
              emitter = c4d.BaseObject(5109)
              emitter.SetBit(c4d.BIT_ACTIVE)
              doc.InsertObject(emitter)
              sph.InsertUnder(emitter)
              
              # emit particles at rate 500 
              emitter[c4d.PARTICLEOBJECT_BIRTHEDITOR] = 500
              emitter[c4d.PARTICLEOBJECT_BIRTHRAYTRACER ] = 500
              emitter[c4d.PARTICLEOBJECT_RENDERINSTANCES] = 500
              emitter[c4d.PARTICLEOBJECT_SIZEX] = 0.2
              emitter[c4d.PARTICLEOBJECT_SIZEY] = 0.2
              emitter[c4d.PARTICLEOBJECT_TYPE] = c4d.PARTICLEOBJECT_TYPE_PYRAMID
              emitter[c4d.PARTICLEOBJECT_ANGLEH] = 2 * math.pi
              emitter[c4d.PARTICLEOBJECT_ANGLEV] = math.pi
              emitter[c4d.PARTICLEOBJECT_SHOWOBJECTS] = True
              
              
              fps = 24
              emitter[c4d.PARTICLEOBJECT_START] = c4d.BaseTime(0, fps)
              emitter[c4d.PARTICLEOBJECT_STOP] = c4d.BaseTime(500, fps)
              emitter[c4d.PARTICLEOBJECT_LIFETIME] = c4d.BaseTime(5, fps)
              
              
              ## Animate 500 frames, new position every ten frames,
              ## transition to next color (cycle red->green->blue)
              
              ## First set key frames for color change
              
              nextRGB = [1.,0.,0.]
              
              redcurve = redtrack.GetCurve()
              
              greencurve = greentrack.GetCurve()
              
              bluecurve = bluetrack.GetCurve()
              
              for segment in range(50) :
              	frame = 50*segment 
              	redkey = redcurve.AddKey(c4d.BaseTime(frame, fps))['key']
              	redkey.SetValue(redcurve, nextRGB[0])
              	greenkey = greencurve.AddKey(c4d.BaseTime(frame, fps))['key']
              	greenkey.SetValue(greencurve, nextRGB[1])
              	bluekey = bluecurve.AddKey(c4d.BaseTime(frame, fps))['key']
              	bluekey.SetValue(bluecurve, nextRGB[2])
              	#
              	# rotate RGB values
              	nextRGB.append(nextRGB.pop(0))
              
              ## run animation
              
              frame = 0
              pos = c4d.Vector(0,0,0)
              emitter.SetAbsPos(pos)
              doc.SetTime(c4d.BaseTime(frame, fps))
              c4d.CallCommand(12410)
              
              for segment in range(50) :
              	frame = 10 * segment
              	mat.Update(True, True)
              	sph.Message(c4d.MSG_UPDATE) 
              
              	for k in range(3) :
              		pos[k] = -30 + 60*random.random()
              
              	emitter.SetAbsPos(pos)
              	
              	doc.SetTime(c4d.BaseTime(frame, fps))
              	c4d.CallCommand(12410) # record
              
              
              
              1 Reply Last reply Reply Quote 0
              • First post
                Last post