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
    • Recent
    • Tags
    • Users
    • Login

    Ray reflection inaccuracy [SOLVED]

    Scheduled Pinned Locked Moved PYTHON Development
    14 Posts 0 Posters 1.4k Views
    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.
    • H Offline
      Helper
      last edited by

      On 28/01/2015 at 04:44, xxxxxxxx wrote:

      Hi
      I'm in the middle of writing some code to calculate the ray reflections around tight corners inside a geometric object.  Sometimes the reflected ray bounces outside the object because it misses a close adjoining corner.  Anyway, do do this I need to manually test along the path of the reflected ray to see if it meets a new collision.  The code I'm using below is working toward this - but there's a general inaccuracy of the final position that ReflectRay calculates and the same position I calculate by taking the collision point and extrapolating along it.

      I'm pretty sure my math and code are correct, but my value (mypos)  and ReflectRay's value (pos) are very slightly different and it's bugging me - for example,

      Vector(-95.939, -75.666, -19.324) Vector(-97.445, -75.666, -19.324)

      global pos, vel

      predictedPos = pos + vel

      sourceobj = doc.SearchObject("Null") # The object the ray will start from 
          targetobj = doc.SearchObject("Cube") # The object the ray collides with    
          ray = GeRayCollider()                # Create a new GeRayCollider object
          ray.Init(targetobj, True)            # Assign the object to a variable

      start = pos
          direction = vel.GetNormalized()    #The direction the ray points.

      raylength = 1000

      CollisionState = ray.Intersect(start, direction, raylength)

      if CollisionState:
              hitpos = ray.GetIntersection(0)["hitpos"]
              collDist = c4d.Vector(hitpos - pos).GetLength()
              norm = ray.GetIntersection(0)["f_normal"]
              distance = ray.GetIntersection(0)["distance"]

      predDist = c4d.Vector(predictedPos - pos).GetLength()

      if collDist < predDist:
                  norm.Normalize()
                  reflect = ReflectRay(vel,norm) 
                  mypos = hitpos + ( reflect.GetNormalized() * (vel.GetLength() - distance) )

      vel = reflect
                  pos = pos + vel

      print pos, mypos

      else:
                  pos = pos + vel
          else:
              pos = pos + vel

      1 Reply Last reply Reply Quote 0
      • H Offline
        Helper
        last edited by

        On 28/01/2015 at 06:31, xxxxxxxx wrote:

        Hi Glenn,

        can you perhaps give me a little more information. The two vectors you provide upfront are to be used as input data for your code example? In which way? And what is the expected result?
        Without having done any testing, I have a feeling in my guts, that you might have run into problems with floating point arithmetic. So while answering my questions, you may also want to have a look at the article about Floating-Point Weirdness[URL-REMOVED] on our PluginCafe blog.


        [URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.

        1 Reply Last reply Reply Quote 0
        • H Offline
          Helper
          last edited by

          On 28/01/2015 at 06:51, xxxxxxxx wrote:

          Hi Andreas,
          The two vectors I listed are the results of 'print pos, mypos' in the code.  They should be exactly the same but either one of x,y,z is always slightly off.  I suspected a floating point issue myself. If that's the case that's fine. I was just worried my calculations were wrong.

          1 Reply Last reply Reply Quote 0
          • H Offline
            Helper
            last edited by

            On 28/01/2015 at 17:22, xxxxxxxx wrote:

            Hi Glenn,

            checking your code it seems not to be a floating point issue.
            I can´t see exactly why you are doing this, but only adding the position to the whole reflected ray can´t give you the same result as there is no information of where the ray hits the surface.
            With utils.ReflectRay you´ll get the direction and the intensity of the ray just reflected.
            Your mypos calculation is right, a normalized reflection vector multiplied by the distance difference and added to the hitposition.

            The problems you have with the ray collider might come from just checking the first hit, which also can be a point or an edge and won´t have the normal you might need for your calculation. Just a guess...

            Best wishes
            Martin

            1 Reply Last reply Reply Quote 0
            • H Offline
              Helper
              last edited by

              On 29/01/2015 at 00:46, xxxxxxxx wrote:

              Thanks Martin,
              The thing is even when my object hits a perfectly flat surface - it still shows a slight discrepancy - it shows every collision no matter where in fact.

              Anyway, I cobbled this code together - it's a recursive call which takes an incoming position and velocity, and tries to work out all collisions.  From looking at it it seems to work - catching small tight corners etc.  It also doesn't make use of my manual calculations, but uses ReflectRay entirely.  (I've left in the debug code which compares my mypos error though)

              If you spot any errors in this - I'd be grateful to learn, thank you.

              def getAllCollisions(pos,vel2,count) :
                  # print vel
                  predictedPos = pos + vel2

              # sourceobj = doc.SearchObject("Null") # The object the ray will start from 
                  targetobj = doc.SearchObject("Cube") # The object the ray collides with    
                  ray = GeRayCollider()                # Create a new GeRayCollider object
                  ray.Init(targetobj, True)            # Assign the object to a variable  
                  start = pos
                  direction = vel2.GetNormalized()    #The direction the ray points.

              raylength = 1000

              CollisionState = ray.Intersect(start, direction, raylength)

              if CollisionState:
                      print '........................'
                      hitpos = ray.GetIntersection(0)["hitpos"]
                      norm = ray.GetIntersection(0)["f_normal"]
                      distance = ray.GetIntersection(0)["distance"]

              if distance < vel2.GetLength() :
                          norm.Normalize()
                          reflect = ReflectRay(vel2,norm)

              mypos = hitpos + ( reflect.GetNormalized() * (vel2.GetLength() - distance) )
                          print mypos 
                          print pos + reflect # correct value ?

              vel2 = reflect

              count += 1
                          if count < 10:
                              vel2 = getAllCollisions(pos,vel2,count)
                  return vel2

              1 Reply Last reply Reply Quote 0
              • H Offline
                Helper
                last edited by

                On 29/01/2015 at 06:42, xxxxxxxx wrote:

                Hi Glenn,

                you again assume that ReflectRay will give you the right position without feeding the necessary hitposition into the equation.
                I attached a small snippet which deals with all collisions you can get from a given velocity, position and a bounce limit.
                The precision value is needed otherwise you will end up not to know if your are inside or outside the volume and therefore heading in the wrong direction, as you "stick on the surface".

                The code is heavily commented I hope this helps?
                Best wishes
                Martin

                  
                import c4d  
                from c4d import utils  
                  
                  
                def getAllCollisions(pos,vel,count,bounceList) :  
                    
                  print pos, "the start position"  
                  print vel, "the velocity vector"  
                  print count, "the bounce count limit to ten"  
                  precision = 0.000001 #distance tolerance to the surface avoiding unwanted catch at the surface      
                        
                  # initialize the ray collider with the target  
                  targetobj = doc.SearchObject("Cube") # The object the ray collides with      
                  ray = utils.GeRayCollider()          # Create a new GeRayCollider object  
                  ray.Init(targetobj, True)            # Assign the object to a variable    
                    
                    
                  # velocity values  
                  direction = vel.GetNormalized()    #The direction the ray points.   
                  intensity = vel.GetLength()        #The constant speed(intensitiy) the ray traverses  
                  print direction, "direction"     
                  print intensity, "the new raylength"  
                    
                    
                  # raylength is the velocity intensity and will decrease the longer the ray is on his way when reflecting.  
                  raylength = intensity  
                  
                  #check collisions  
                  CollisionState = ray.Intersect(pos, direction, raylength)  
                  
                  
                  if CollisionState and count < 10:  
                      print '........................'  
                        
                      #take the nearest collision  
                      hitpos = ray.GetNearestIntersection()["hitpos"]  
                        
                      norm = ray.GetNearestIntersection()["f_normal"]  
                      norm.Normalize()   
                  
                      distance = ray.GetNearestIntersection()["distance"]          
                        
                      #the normalized reflection vector  
                      reflect = utils.ReflectRay(direction,norm)  
                  
                      #the collision point refined by the precision value to avoid catch at surface given in targetobj local space  
                      hitpos = (hitpos + precision*reflect)  
                      print hitpos,"the collision point"  
                         
                      mypos = hitpos + ( reflect * (intensity - distance) )  
                      print mypos ,"the new calculated endposition------------"  
                  
                      vel2 =  mypos-hitpos   
                      print vel2 ,"the new velocity vector"  
                        
                      #the new calculated position and velocity for the next possible bounce          
                      bounceList.append([hitpos,vel2])  
                      count += 1  
                  
                      #recursivly searching for more bounces  
                      getAllCollisions(hitpos,vel2,count,bounceList)  
                                
                      return bounceList  
                  
                  
                  else:   
                        
                      return []  
                       
                  
                def main() :  
                    
                  sourceobj = doc.SearchObject("Null")  
                  
                  pos = sourceobj.GetAbsPos()  
                  vel = c4d.Vector(0,0,800)  
                  count = 0  
                    
                    
                  bounceList = [] #store all collision points with their velocities  
                  print getAllCollisions(pos,vel,count,bounceList)  
                    
                    
                    
                    
                  
                if __name__=='__main__':  
                  main()  
                  
                  
                
                1 Reply Last reply Reply Quote 0
                • H Offline
                  Helper
                  last edited by

                  On 29/01/2015 at 09:03, xxxxxxxx wrote:

                  Hi Martin,
                  Thanks so much for this - the code works perfectly and is well explained.
                  I see what you mean about ReflectRay and my assumption of a hitpoint.

                  Anyway - in my main loop (running every frame), if there has been any collisions then I update my new pos and vel like so

                  global pos, vel
                      bounceList = [] #store all collision points with their velocities
                      getAllCollisions(pos,vel,0,bounceList)

                  if len(bounceList) > 0:
                          newpos = bounceList[-1][0]
                          bounceVel = bounceList[-1][1]
                          pos = newpos + bounceVel
                          vel = vel.GetLength() * bounceVel.GetNormalized()

                  else:
                          pos = pos + vel

                  Incidentally, the reason I shied away from using raylength for testing is because I saw this
                  https://developers.maxon.net/forum/topic/5785/5839_geraycollider

                  there seems to be problems if it is less than 100, but I haven't noticed anything so far.

                  Thanks again!
                  Glenn.

                  1 Reply Last reply Reply Quote 0
                  • H Offline
                    Helper
                    last edited by

                    On 29/01/2015 at 11:18, xxxxxxxx wrote:

                    Hi Glenn,

                    I´m glad this helped.

                    For your task there should´nt be any problem with the raylength.
                    Another approach for other tasks could be to compare every collision in one "shot" and delete the double ones.

                    It´s important to know that ray collider works in local space of the collision object.

                    regarding your main loop:
                    I assume that you have precalculated your velocity vector in the dimension of time.
                    your vel is the way the ray travese in one time unit.
                    In this case the next position for let´s say the next frame, will be the last mypos calculated in the getAll collisions function multiplied by the object matrix.
                    The new vel will be, assuming we have a constant speed with no acceleration, the last reflect vector multiplied by the intensity we calculated at the beginning, as the speed doesn´t change.

                    Best wishes
                    Martin

                    1 Reply Last reply Reply Quote 0
                    • H Offline
                      Helper
                      last edited by

                      On 29/01/2015 at 11:43, xxxxxxxx wrote:

                      in other words

                        
                      def main() :  
                          
                        sourceobj = doc.SearchObject("Null")  
                        
                        pos = sourceobj.GetAbsPos()  
                        vel = c4d.Vector(0,0,800) #initial vel  
                        count = 0  
                              
                        bounceList = [] #store all collision points with their velocities  
                        print getAllCollisions(pos,vel,count,bounceList)  
                          
                        targetobj = doc.SearchObject("Cube")  
                        matr = targetobj.GetMg()  
                          
                        lastHitPos = bounceList[-1][0]  
                        lastVel = bounceList[-1][1]  
                        intensity = vel.GetLength()     
                          
                        newVel = lastVel.GetNormalized()*intensity      
                        newPos = (lastHitPos + lastVel)*matr  
                          
                        print newPos,newVel  
                        
                        
                      if __name__=='__main__':  
                        main()  
                      
                      1 Reply Last reply Reply Quote 0
                      • H Offline
                        Helper
                        last edited by

                        On 30/01/2015 at 06:47, xxxxxxxx wrote:

                        Hi Martin
                        In my test scene I just have a null with a small object as its child - the null bounces around inside the cube at a constant velocity.  And everything looks to be working perfectly.  It's useful to know about multiplying the object matrix though.  I'll probably need to do that in the future.
                        Thanks again,
                        Glenn.

                        1 Reply Last reply Reply Quote 0
                        • H Offline
                          Helper
                          last edited by

                          On 01/02/2015 at 04:59, xxxxxxxx wrote:

                          Hi Glenn,

                          great!
                          I can almost imagine your small objects bouncing around(funny sentence while typing it but wasn´t meant to be offensive in any way😉)

                          Referring to Andreas' feeling in his guts (nice article by the way) and for the sake of completeness:

                          1.There is a problem with the floating point precision while "sticking" at the surface, but this will be the case even if we have much more precision in computer technology.

                          2.The starting point needs to be within the object's space, too, if you move the object from world origin.

                          3.If you use multiple rays in your code but with only one static object, the ray init function must be called only once, as it seems just preparing the object for collisions.
                          This becomes obvious once your collision object has a looooot polygons.

                          Curious about what you´ll end up with!
                          Martin

                          1 Reply Last reply Reply Quote 0
                          • H Offline
                            Helper
                            last edited by

                            On 02/02/2015 at 09:49, xxxxxxxx wrote:

                            Hi Martin,
                            My bouncy objects I'm using to create traced lines inside geometry to create interesting abstract forms.  Below is a simple cube.
                            One little thing you might know about - The lines don't update in realtime inside the editor view when I play the timeline.  The code is inside a generator and updates every frame.  It's a bunch of growing loft objects basically.  When I move the camera around I get a brief glimpse of what's happening, but that's all.  Ive tried playing with the following..

                            c4d.EventAdd()
                            c4d.DrawViews( c4d.DRAWFLAGS_FORCEFULLREDRAW)

                            but no joy.. not the end of the world, I'm just glad the end result is working.

                            1 Reply Last reply Reply Quote 0
                            • H Offline
                              Helper
                              last edited by

                              On 03/02/2015 at 01:12, xxxxxxxx wrote:

                              Hi Glenn,

                              nice your tracer kind of thing!
                              If you want to do some further development I´ll sent you a pm.
                              Best wishes
                              Martin

                              1 Reply Last reply Reply Quote 0
                              • H Offline
                                Helper
                                last edited by

                                On 03/02/2015 at 04:52, xxxxxxxx wrote:

                                Thanks for the message Martin, will drop you a line soon!

                                1 Reply Last reply Reply Quote 0
                                • First post
                                  Last post