新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 39-lesson  40 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 14774 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 39-lesson  40 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客楼主
    发贴心情 [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 39-lesson  40


    第三十九课 四十课 源码


    第三十九课

    按此在新窗口浏览图片物理模拟简介:

    还记得高中的物理吧,直线运动,自由落体运动,弹簧。在这一课里,我们将创造这一切。

      
       
       
    物理模拟介绍

    如果你很熟悉物理规律,并且想实现它,这篇文章很适合你。

    在这篇教程里,你会创建一个非常简单的物理引擎,我们将创建以下类:

    内容:

    位置类
    * class Vector3D --->  用来记录物体的三维坐标的类

    力和运动
    * class Mass --->  表示一个物体的物理属性

    模拟类
    * class Simulation --->  模拟物理规律

    模拟匀速运动
    * class ConstantVelocity : public Simulation --->  模拟匀速运动

    模拟在力的作用下运动
    * class MotionUnderGravitation : public Simulation --->  模拟在引力的作用下运动
    * class MassConnectedWithSpring : public Simulation --->  模拟在弹簧的作用下运动

      
       

    class Mass
    {
    public:
     float m;         // 质量
     Vector3D pos;        // 位置
     Vector3D vel;        // 速度
     Vector3D force;        // 力

     Mass(float m)        // 构造函数
     {
      this->m = m;
     }

     ...

       
    下面的代码给物体增加一个力,在初始时这个力为0
      
       

     void applyForce(Vector3D force)
     {
      this->force += force;      // 增加一个力
     }

     void init()        // 初始时设为0
     {
      force.x = 0;
      force.y = 0;
      force.z = 0;
     }

     ...

       
    下面的步骤完成一个模拟:

    1.设置力
    2.应用外力
    3.根据力的时间,计算物体的位置和速度
      
       

     void simulate(float dt)
     {
      vel += (force / m) * dt;     // 更新速度
            

      pos += vel * dt;      // 更新位置
              
     }

       
    模拟类怎样运作:

    在一个物理模拟中,我们按以下规律进行模拟,设置力,更新物体的位置和速度,按时间一次又一次的进行模拟。下面是它的实现代码:  
       

    class Simulation
    {
    public:
     int numOfMasses;        // 物体的个数
     Mass** masses;        // 指向物体结构的指针

     Simulation(int numOfMasses, float m)      // 构造函数
     {
      this->numOfMasses = numOfMasses;

      masses = new Mass*[numOfMasses];    

      for (int a = 0; a < numOfMasses; ++a)    
       masses[a] = new Mass(m);   
     }

     virtual void release()       // 释放所有的物体
     {
      for (int a = 0; a < numOfMasses; ++a)    
      {
       delete(masses[a]);
       masses[a] = NULL;
      }

      delete(masses);
      masses = NULL;
     }

     Mass* getMass(int index)
     {
      if (index < 0 || index >= numOfMasses)    // 返回第i个物体
       return NULL;      

      return masses[index];      
     }

    ...

     (class Simulation continued)

     virtual void init()       // 初始化所有的物体
     {
      for (int a = 0; a < numOfMasses; ++a)    
       masses[a]->init();     
     }

     virtual void solve()       //虚函数,在具体的应用中设置各个施加给各个物体的力
     {
             
     }

     virtual void simulate(float dt)      //让所有的物体模拟一步
     {
      for (int a = 0; a < numOfMasses; ++a)    
       masses[a]->simulate(dt);    
     }

     ...

       
    整个模拟的部分被封装到下面的函数中  
       

     (class Simulation continued)

     virtual void operate(float dt)      //  完整的模拟过程
     {
      init();        // 设置力为0
      solve();        // 应用力
      simulate(dt);       // 模拟
     }
    };

       
    现在我们已经有了一个简单的物理模拟引擎了,它包含有物体和模拟两个类,下面我们基于它们创建三个具体的模拟对象:

    1. 具有恒定速度的物体
    2. 具有恒定加速度的物体
    3. 具有与距离成反比的力的物体

    在程序中控制一个模拟对象:

    在我们写一个具体的模拟类之前,让我们看看如何在程序中模拟一个对象,在这个教程里,模拟引擎和操作模拟的程序在两个文件里,在程序中我们使用如下的函数,操作模拟:

      
       

    void Update (DWORD milliseconds)      // 执行模拟

       
    这个函数在每一帧的开始更新,参数为相隔的时间。  
       

    void Update (DWORD milliseconds)
    {
     ...
     ...
     ...

     float dt = milliseconds / 1000.0f;     // 转化为秒

     dt /= slowMotionRatio;      // 除以模拟系数

     timeElapsed += dt;       // 更新流失的时间

     ...

       
    在下面的代码中,我们定义一个处理间隔,没隔这么长时间,让物理引擎模拟一次。  
       

     ...

     float maxPossible_dt = 0.1f;     // 设置模拟间隔
             

       int numOfIterations = (int)(dt / maxPossible_dt) + 1;   //计算在流失的时间里模拟的次数
     if (numOfIterations != 0)     
      dt = dt / numOfIterations;     

     for (int a = 0; a < numOfIterations; ++a)    // 模拟它们 
     {
      constantVelocity->operate(dt);     
      motionUnderGravitation->operate(dt);    
      massConnectedWithSpring->operate(dt);    
     }
    }

       
    下面让我们来写着两个具体的模拟类:

    1. 具有恒定速度的物体
    * class ConstantVelocity : public Simulation ---> 模拟一个匀速运动的物体

      
       

    class ConstantVelocity : public Simulation
    {
    public:
     ConstantVelocity() : Simulation(1, 1.0f)    
     {
      masses[0]->pos = Vector3D(0.0f, 0.0f, 0.0f);   // 初始位置为0
      masses[0]->vel = Vector3D(1.0f, 0.0f, 0.0f);   // 向右运动
     }
    };

       
    下面我们来创建一个具有恒定加速的物体:

      
       

    class MotionUnderGravitation : public Simulation
    {
     Vector3D gravitation;       // 加速度

     MotionUnderGravitation(Vector3D gravitation) : Simulation(1, 1.0f)  //  构造函数
     {         
      this->gravitation = gravitation;     // 设置加速度
      masses[0]->pos = Vector3D(-10.0f, 0.0f, 0.0f);   // 设置位置为左边-10处
      masses[0]->vel = Vector3D(10.0f, 15.0f, 0.0f);   // 设置速度为右上
     }

     ...

       
    下面的函数设置施加给物体的力  
       

     virtual void solve()       // 设置当前的力
     {
      for (int a = 0; a < numOfMasses; ++a)    
       masses[a]->applyForce(gravitation * masses[a]->m); 
     }

       
    下面的类创建一个受到与距离成正比的力的物体:  
       

    class MassConnectedWithSpring : public Simulation
    {
    public:
     float springConstant;       // 弹性系数
     Vector3D connectionPos;       // 连接方向

     MassConnectedWithSpring(float springConstant) : Simulation(1, 1.0f)  // 构造函数
     {
      this->springConstant = springConstant;    

      connectionPos = Vector3D(0.0f, -5.0f, 0.0f);  

      masses[0]->pos = connectionPos + Vector3D(10.0f, 0.0f, 0.0f); 
      masses[0]->vel = Vector3D(0.0f, 0.0f, 0.0f);  
     }

     ...

       
    下面的函数设置当前物体所受到的力:  
       

    virtual void solve()        // 设置当前的力
    {
     for (int a = 0; a < numOfMasses; ++a)     
     {
      Vector3D springVector = masses[a]->pos - connectionPos;  
      masses[a]->applyForce(-springVector * springConstant);  
     }
    }

       
    好了上面就是一个简单的物理模拟,希望你能喜欢:)


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/31 19:39:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客2
    发贴心情 
    Lesson 39
       
    Introduction to Physical Simulations

    If you are familiar to physics and want to start implementing code of physical simulations, this tutorial could help you. In order to benefit from this tutorial you should be familiar to vectoral operations in 3D as well as physical concepts such as force and velocity.

    In this tutorial you will find a very simple physical simulation engine. Contents of the tutorial is as follows:

    Contents:

    The Design:
    * class Vector3D --->  An Object To Represent A 3D Vector Or A 3D Point In Space.

    Force and Motion:
    * class Mass --->  An Object To Represent A Mass.

    How A Simulation Should Operate:
    * class Simulation --->  A Container Object For Simulating Masses.

    Operating A Simulation By An Application:
    * class ConstantVelocity : public Simulation --->  A Simulation Object That Creates A Mass With A Constant Velocity.

    Applying Force:
    * class MotionUnderGravitation : public Simulation --->  A Simulation Object That Creates A Mass Which Moves Under Gravitation.
    * class MassConnectedWithSpring : public Simulation --->  A Simulation Object That Creates A Mass Connected To A Point By A Spring.

    The Design:

    Design of physical simulation engines is not always simple. But there is a simple order of dependency; application depends on simulation toolkit and the simulation toolkit depends on math libraries. Here, we will make use of this simple order. Our purpose is to obtain a container to simulate motion of masses. The simulation toolkit will include an object "class Mass" and an object "class Simulation". "class Simulation" will be our container. When we obtain the Simulation class, we will be able to develop applications. But before that, we need a math library. The library includes only one class "class Vector3D". Vector3D class will be used to represent points, vectors, position, velocity, and force in 3 dimensional space.

    * class Vector3D ---> An Object To Represent A 3D Vector Or A 3D Point In Space.

    Vector3D class is the only member of our modest math library. Vector3D holds x, y, and z values and it implements operators for vector arithmetics in 3D. Addition, subtraction, multiplication, and division operators are coded in Vector3D. Since this tutorial focuses on physics, I will not go into details of Vector3D. If you have a look at Physics1.h you will see how simple Vector3D is.

    Force and Motion:

    For implementing physical simulations, we should know what a mass is. A mass has a position and a velocity. A mass has weight on Earth, Moon, Mars, and at any place where gravitation exists. Weight is different on different gravitations of different places. But there is one common value for a mass, which is the same in all conditions. This value is also called mass. Mass value of a mass! Mass value represents "how much a mass exists in space"! For example a book is a mass with a weight of say 1 kg on The Earth and with a weight of 0.17 kg on The Moon and has a mass value of 1 kg everywhere. The mass value is designated to be equal to its mass on The Earth.

    After having understood the mass of a mass, we should go on with force and motion. A mass, with a non-zero velocity in space, moves in the direction of the velocity. Therefore, one reason of the change in position is the velocity. Passing of time is another reason. Change in position depends on how fast a mass moves and how much time has passed. You should have understood until here to go on to the next paragraph. If not, spend some time thinking on the relation between position, velocity and time.

    Velocity of a mass changes if there is force acting on it. Velocity tends to the direction of the force. This tending is proportional to the force and iversly proportional to mass. The change in velocity per unit time is called acceleration. More the force on a mass, more its acceleration. More the mass value of a mass, less its acceleration. When acceleration is formulated it is:

    acceleration = force / mass

    From here we obtain the famous equation:

    force = mass * acceleration

    (We will mostly use the acceleration formula)

    For preparing a physical medium to simulate, you should be aware of the environment that the simulation takes place. The environment in this tutorial is simply empty space waiting to be filled by masses we create. The units of the values to represent masses and time shall be decided firstly. I have decided to use the time unit as seconds and units of position values as meters. Accordingly, unit of velocity becomes meters per second (m/s). And the unit of acceleration becomes meters per second per second (m/s/s)! (this means velocity per second since velocity is meters per second) I have decided the unit of mass values as kilograms (kg).

    * class Mass ---> An Object To Represent A Mass.

    Now we are starting to use the theory! We have to write a class to represent a mass. It should hold the mass value, the position, the velocity, and the force applied at an instance.   
       

    class Mass
    {
    public:
     float m;        // The Mass Value.
     Vector3D pos;        // Position In Space.
     Vector3D vel;        // Velocity.
     Vector3D force;        // Force Applied On This Mass At An Instance.

     Mass(float m)        // Constructor.
     {
      this->m = m;
     }

     ...

       
    We want to apply force to this mass. At an instance in time, there might be several sources of external forces acting on the mass. The vector sum of these forces gives the net force on the mass at that instance. Before starting to apply forces, we should reset the force on the mass. Then we can add external forces on the mass.   
       

     (class Mass continued)

     void applyForce(Vector3D force)
     {
      this->force += force;      // The External Force Is Added To The Force On The Mass.
     }

     void init()        // This Method Sets The Force Values To Zero.
     {
      force.x = 0;
      force.y = 0;
      force.z = 0;
     }

     ...

       
    There is a simple order of things to do in a simulation:

    1. Reset the force (see the init() method())
    2. Apply external forces
    3. Iterate time by "the change in time"

    Here, iterating the time is implemented with "The Euler Method". The Euler Method is a simple simulation method. There are more sophisticated methods for simulations. But Euler is good enough for lots of applications. Most of computer and video games use The Euler Method. What this method does is that it calculates the next velocity and next position of a mass according to the force applied and time passed. The itteration is done in void simulate(float dt):   
       

     (class Mass continued)

     void simulate(float dt)
     {
      vel += (force / m) * dt;     // Change In Velocity Is Added To The Velocity.
              // The Change Is Proportinal With The Acceleration (force / m) And Change In Time.

      pos += vel * dt;      // Change In Position Is Added To The Position.
              // Change In Position Is Velocity Times The Change In Time.
     }

       
    How a Simulation Should Operate:

    In a physical simulation, at every iteration, the same process takes place. Forces are set to zero, forces are applied, new positions and new velocities are calculated. This process cycles as long as we want the time to pass. This process is implemented in "class Simulation".

    * class Simulation ---> A Container Object For Simulating Masses.

    Simulation class holds masses as its members. The role of the class is to create and delete masses, and maintain the simulation procedure.   
       

    class Simulation
    {
    public:
     int numOfMasses;       // Number Of Masses In This Container.
     Mass** masses;        // Masses Are Held By Array Of Pointers. (Here Mass** Represents A 1 Dimensional Array).

     Simulation(int numOfMasses, float m)     // Constructor Creates Some Masses With Mass Values m.
     {
      this->numOfMasses = numOfMasses;

      masses = new Mass*[numOfMasses];    // Create An Array Of Pointers.

      for (int a = 0; a < numOfMasses; ++a)    // We Will Step To Every Pointer In The Array.
       masses[a] = new Mass(m);    // Create A Mass As A Pointer And Put It In The Array.
     }

     virtual void release()       // Delete The Masses Created.
     {
      for (int a = 0; a < numOfMasses; ++a)    // We Will Delete All Of Them.
      {
       delete(masses[a]);
       masses[a] = NULL;
      }

      delete(masses);
      masses = NULL;
     }

     Mass* getMass(int index)
     {
      if (index < 0 || index >= numOfMasses)    // If The index Is Not In The Array.
       return NULL;      // Then Return NULL.

      return masses[index];      // Get The Mass At The index.
     }

    ...

       
    The simulation procedure has three steps:

    1. init() to set forces to zero
    2. solve() to apply forces
    3. simulate(float dt) to iterate masses by the change in time   
       

     (class Simulation continued)

     virtual void init()       // This Method Will Call The init() Method Of Every Mass.
     {
      for (int a = 0; a < numOfMasses; ++a)    // We Will init() Every Mass.
       masses[a]->init();     // Call init() Method Of The Mass.
     }

     virtual void solve()       // No Implementation Because No Forces Are Wanted In This Basic Container.
     {
              // In Advanced Containers, This Method Will Be Overridden And Some Forces Will Act On Masses.
     }

     virtual void simulate(float dt)      // Iterate The Masses By The Change In Time.
     {
      for (int a = 0; a < numOfMasses; ++a)    // We Will Iterate Every Mass.
       masses[a]->simulate(dt);    // Iterate The Mass And Obtain New Position And New Velocity.
     }

     ...

       
    The simulation procedure is packed into one method:   
       

     (class Simulation continued)

     virtual void operate(float dt)      // The Complete Procedure Of Simulation.
     {
      init();        // Step 1: Reset Forces To Zero.
      solve();       // Step 2: Apply Forces.
      simulate(dt);       // Step 3: Iterate The Masses By The Change In Time.
     }
    };

       
    By now, we have a simple physical simulation engine. It is based on a math library. It contains Mass and Simulation classes. It uses a very common pattern of simulation procedure and it uses The Euler Method. Now we are ready to develop applications. The applications that we will develop are:

    1. Mass with constant velocity
    2. Mass under gravitational force
    3. Mass connected to a still point by a spring

    Operating A Simulation By An Application:

    Before we write a specific simulation, we should know how to operate simulations by applications. In this tutorial, the simulation engine and the application to operate the simulations are seperated in two files. In the application file there is a function as:

      
       

    void Update (DWORD milliseconds)      // Perform Motion Updates Here.

       
    This function is called repeatedly at every frame update. The "DWORD milliseconds" is the time period from the previous frame to the current frame. From this, we can say that we should iterate simulations according to the "milliseconds". If the simulations follow this time period, they should go parallel with the real world's time. To iterate a simulation, we simply call the "void operate(float dt)" method. To call this method, we should know "dt". Since we take the time unit as seconds we firstly convert milliseconds to seconds (see below in the code). Then we use a value "slowMotionRatio" which means, how slow we want to run the simulation relative to the real world time. We divide dt by this value and we obtain a new dt. Now we can add dt to "timeElapsed". "timeElapsed" is the time of the simulation, not the time of the real world.   
       

    void Update (DWORD milliseconds)
    {
     ...
     ...
     ...

     float dt = milliseconds / 1000.0f;     // Let's Convert milliseconds To Seconds.

     dt /= slowMotionRatio;       // Divide dt By slowMotionRatio And Obtain The New dt.

     timeElapsed += dt;       // Iterate Elapsed Time.

     ...

       
    Now dt is almost ready for operating the simulation. But! there is one very important thing that we should know: dt is the detail of precision. If dt is not small enough, your simulation would show instability and the motion would not be calculated precisely. Stability analysis is used for physical simulations to find the maximum dt value that a simulation can handle. In this tutorial we will not go into the details and if you are just developing a game but not a scientific application, it is always valid to find the value of maximum dt by trial and error.

    As an example, in a car racing game, it is convenient to use dt as about 2 to 5 milliseconds for regular car, and 1 to 3 milliseconds for a formula car. In an arcade car simulation it is possible to use dt as about 10 to 200 milliseconds. Less the value of dt, more the CPU ticks we need to catch up the real world time. That is why physical simulations are rarely used in older games.

    In the code below we define the maximum possible dt as 0.1 seconds (100 milliseconds). With this value we will calculate the number iterations to be made at the current update. We write a formula:

    int numOfIterations = (int)(dt / maxPossible_dt) + 1;

    numOfIterations is the number of iterations to be made for a simulation. Say that the application is running with 20 frames per second, which gives dt = 0.05 seconds. Then numOfIterations becomes 1. The simulation will be iterated once by 0.05 seconds. Say dt was 0.12 seconds. Then numOfIterations is 2. Below, just after "int numOfIterations = (int)(dt / maxPossible_dt) + 1;", you should see that dt is calculated once again. There, dt is divided by numOfIterations and it becomes dt = 0.12 / 2 = 0.06. dt was originally more than the maximum possible value 0.1. Now we have dt as 0.06 but we will operate the simulation twice so that we catch up 0.12 seconds as a result. Examine the code below and be sure that you understand the paragraph above.   
       

     ...

     float maxPossible_dt = 0.1f;      // Say That The Maximum Possible dt Is 0.1 Seconds.
              // This Is Needed So We Do Not Pass Over A Non Precise dt Value.

       int numOfIterations = (int)(dt / maxPossible_dt) + 1;   // Calculate Number Of Iterations To Be Made At This Update Depending On maxPossible_dt And dt.
     if (numOfIterations != 0)      // Avoid Division By Zero.
      dt = dt / numOfIterations;     // dt Should Be Updated According To numOfIterations.

     for (int a = 0; a < numOfIterations; ++a)    // We Need To Iterate Simulations "numOfIterations" Times.
     {
      constantVelocity->operate(dt);     // Iterate constantVelocity Simulation By dt Seconds.
      motionUnderGravitation->operate(dt);    // Iterate motionUnderGravitation Simulation By dt Seconds.
      massConnectedWithSpring->operate(dt);    // Iterate massConnectedWithSpring Simulation By dt Seconds.
     }
    }

       
    Let's begin to write the applications:

    1. Mass with constant velocity
    * class ConstantVelocity : public Simulation ---> A Simulation Object That Creates A Mass With A Constant Velocity.

    Mass with constant velocity does not need any external force. We just have to create 1 mass and set its velocity to (1.0f, 0.0f, 0.0f) so that it moves in the x direction with a velocity of 1 m/s. We will derive a class from Simulation. This class is "class ConstantVelocity":   
       

    class ConstantVelocity : public Simulation
    {
    public:
     ConstantVelocity() : Simulation(1, 1.0f)    // Constructor Firstly Constructs Its Super Class With 1 Mass And 1 Kg.
     {
      masses[0]->pos = Vector3D(0.0f, 0.0f, 0.0f);   // A Mass Was Created And We Set Its Position To The Origin.
      masses[0]->vel = Vector3D(1.0f, 0.0f, 0.0f);   // We Set The Mass's Velocity To (1.0f, 0.0f, 0.0f) m/s.
     }
    };

       
    When the operate(float dt) method of ConstantVelocity class is called, it calculates the next state of the mass. This method is called by the main application, before every re-draw of the window. Say that your application is running with 10 frames per second. If the exact time change was passed to operate(float dt) at every frame, then dt would be 0.1 seconds. When the simulation calls simulate(float dt) of the mass, its new position will be incremented by velocity * dt which is

    Vector3D(1.0f, 0.0f, 0.0f) * 0.1 = Vector3D(0.1f, 0.0f, 0.0f)

    At every iteration, the mass moves 0.1 meters to the right. After 10 frames, it will have moved 1.0 meter to the right. The velocity was 1.0 m/s and it moves 1.0 meter in one second. Was that a coincidental or a logical result? If you can't answer this question spend some time thinking about the relation above.

    When you run the application, you see the mass with constant velocity moving in the x direction. The application provides two modes of motion flow. By pressing F2 you get time flow parallel to the real world. And by pressing F3 you get time flow 10 times slower than the real world time. On the screen you will see lines to represent the coordinate plane. The spacing between these lines is 1 meter. By the use of the lines, you can observe that the mass moves 1 meter in a second when it is set to the real world time mode. And in the slow mode it moves 1 meter in ten seconds. The techique described above is a common one to make the real-time simulations flow parallel to the real world time. To use this technique, you must have strictly decided on the units of your simulation.

    Applying Force:

    In the constant velocity simulation, we did not apply any force to the mass. Because we know that if a force acts on a body, it accelerates. When we want accelerating motion, we apply forces. At one operation of a simulation, we apply forces in the "solve" method. When the operation comes to the "simulate" phase, the net force results as the total of forces. The net force determines the motion.

    Say that you want to apply 1 N force on a mass in the x direction. Then you should write:

    mass->applyForce(Vector3D(1.0f, 0.0f, 0.0f));

    in the "solve" method. If you want to add another force say 2 N in the y direction you should write:

    mass->applyForce(Vector3D(1.0f, 0.0f, 0.0f));
    mass->applyForce(Vector3D(0.0f, 2.0f, 0.0f));

    in the "solve" method. You can add any force in any formulated way and you obtain the motion. In the next application you will see a single force in a formulated way.

    2. Mass under gravitational force
    * class MotionUnderGravitation : public Simulation ---> A Simulation Object That Creates A Mass Which Moves Under Gravitation.

    MotionUnderGravitation class creates a mass and it applies a force to it. This force is the force of gravitation. Force of gravitation is equal to the mass times the gravitational acceleration:

    F = m * g

    Gravitational acceleration is the acceleration of free body. On the earth, when you drop an object, it gains 9.81 m/s velocity every second, unless it experiences a force other than the gravitational force. Therefore the gravitational acceleration is constant for all masses on the earth and it is 9.81 m/s/s. (This is independent of the mass. All masses fall with the same acceleration.)

    MotionUnderGravitation class has such a constructor:

      
       

    class MotionUnderGravitation : public Simulation
    {
     Vector3D gravitation;       // The Gravitational Acceleration.

     MotionUnderGravitation(Vector3D gravitation) : Simulation(1, 1.0f) // Constructor Firstly Constructs Its Super Class With 1 Mass And 1 Kg.
     {         // Vector3D Gravitation, Is The Gravitational Acceleration.
      this->gravitation = gravitation;    // Set This Class's Gravitation.
      masses[0]->pos = Vector3D(-10.0f, 0.0f, 0.0f);   // Set The Position Of The Mass.
      masses[0]->vel = Vector3D(10.0f, 15.0f, 0.0f);   // Set The Velocity Of The Mass.
     }

     ...

       
    The constructor gets a Vector3D gravitation, which is the gravitational acceleration and the simulation uses that in the force to be applied.   
       

     virtual void solve()       // Gravitational Force Will Be Applied Therefore We Need A "Solve" Method.
     {
      for (int a = 0; a < numOfMasses; ++a)    // We Will Apply Force To All Masses (Actually We Have 1 Mass, But We Can Extend It In The Future).
       masses[a]->applyForce(gravitation * masses[a]->m); // Gravitational Force Is As F = m * g. (Mass Times The Gravitational Acceleration).
     }

       
    Above, in the code, you should see the force formula as F = m * g. The application creates MotionUnderGravitation with a Vector3D value as "Vector3D(0.0f, -9.81f, 0.0f)". -9.81 means an acceleration in the negative y direction so that the mass falls down when displayed on the window. Run the application and observe what's happening. Write 9.81 instead of -9.81 and observe the difference.

    3. Mass connected to a still point by a spring * class MassConnectedWithSpring : public Simulation ---> A Simulation Object That Creates A Mass Connected To A Point By A Spring.

    In this example, we want to connect a mass to a still point by a spring. The spring should pull the mass towards the connection position, so that it oscillates. In the constructor, MassConnectedWithSpring class sets the connection position and sets the position of the mass.   
       

    class MassConnectedWithSpring : public Simulation
    {
    public:
     float springConstant;       // The More springConstant, The Stiffer The Spring Force.
     Vector3D connectionPos;       // The Arbitrary Still Point That The Mass Is Connected.

     MassConnectedWithSpring(float springConstant) : Simulation(1, 1.0f) // Constructor Firstly Constructs Its Super Class With 1 Mass And 1 Kg.
     {
      this->springConstant = springConstant;    // Set The springConstant.

      connectionPos = Vector3D(0.0f, -5.0f, 0.0f);   // Set The connectionPos.

      masses[0]->pos = connectionPos + Vector3D(10.0f, 0.0f, 0.0f); // Set The Position Of The Mass 10 Meters To The Right Side Of The connectionPos.
      masses[0]->vel = Vector3D(0.0f, 0.0f, 0.0f);   // Set The Velocity Of The Mass To Zero.
     }

     ...

       
    Velocity of the mass is set to zero and its position is set to 10 meters to the right side of the connectionPos so that it can be pulled towards left at the beginning. Force of a spring is formulated as

    F = -k * x

    k is a value to represent how stiff the spring should be. And x is the distance from the mass to the connection position. The negative sign at the formula indicates that this is an attractive force. If it was positive, the spring would push the mass which is not something that we would expect to see.   
       

    virtual void solve()        // The Spring Force Will Be Applied.
    {
     for (int a = 0; a < numOfMasses; ++a)     // We Will Apply Force To All Masses (Actually We Have 1 Mass, But We Can Extend It In The Future).
     {
      Vector3D springVector = masses[a]->pos - connectionPos;  // Find A Vector From The Position Of The Mass To The connectionPos.
      masses[a]->applyForce(-springVector * springConstant);  // Apply The Force According To The Famous Spring Force Formulation.
     }
    }

       
    The spring force in the code above is the same as the spring formula (F = -k * x). Here instead of x we use a Vector3D because we want to use 3D space. "springVector" gives the difference of the mass position and the connectionPos and springConstant stands for k. More the value of springConstant, more the force, and faster the mass oscillates.

    In this tutorial, I tried to point to the key concepts of physical simulations. If you are interested in physics, you will not have hard time to create new simulations of your own. You can try complicated interactions and obtain very attracive demos and games. The next steps to take will be rigidbody simulations, simple mechanisms and advanced simulation methods.

    For any comments or questions please contact me:

    Erkin Tunca (erkintunca@icqmail.com)

    Jeff Molofee (NeHe)

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/31 19:44:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客3
    发贴心情 

    第四十课

    按此在新窗口浏览图片绳子的模拟:

    怎样模拟一根绳子呢,把它想象成一个个紧密排列的点,怎么样有了思路了吧,在这一课你你将学会怎样建模,简单吧,你能模拟更多。

      
       
       
    绳索模拟

    在这个教程里我们将模拟一段绳索,我们是在39课的基础上进行的。

    在物理模拟中,我们必须设置各个物理量,就像它们在自然界中的行为一样。模拟中的运动并不一定和自然界相同,我们使用的运动模型,必须和我们需要模拟的目的有关,目的决定了它的精确度。要知道我们的目标不是模拟原子和分子,也不是模拟成千上万的粒子系。首先我们需要确定我们模拟的目标,才能创建我们的物理模型。它和下面内容相关:

    1. 运动的数学表示
    2. 执行模拟的计算机的速度

    1. 运动的数学表示:

    这个问题决定了我们使用何种数学方程来模拟运动,使用经典力学还是量子力学。

    2. 执行模拟的计算机的速度:

    计算机的速度决定了我们可以模拟的精度。

    设计绳索的物理模型:

    我们在经典力学和高于500Mhz的计算机上模拟这个问题。首先我们需要设定需要的精度,我们使用一系列互相用弹簧连接的质点来模拟绳索,精度决定了我们用多少个点来模拟,当然越多越精确。在下面我决定用50或100个点来模拟绳子一段3或4m长的绳子,换句话说,我们的模拟精度就是3到8厘米。

    设计运动模型:

    在绳子中,施加给各个质点的力来自于自身的质量和相连的内力(参见大学里的普通力学)。如下我们用"O"表示质点,“—”表示连接质点的弹簧。
    O----O----O----O
    1    2    3    4

    弹簧的力学公式如下:

    力 = -k * x
    k: 弹性系数
    x: 相距平衡位置的位移

    上面的公式说明,如果相邻点的距离为平衡距离,那么它们不受到任何力的作用。如果我们设置平衡位置为5cm,那么100个点的绳子长5m。如果相连质点之间的位置小于5cm,它们受到排斥力。

    上面的公式只是一个基础,现在我们可以加上摩擦力,如果没有这项,那么绳子将永远动下去。

    弹簧类:

    这个类包含相连接的两个物体,它们之间具有作用力。
      
       

    class Spring         
    {          
    public:
     Mass* mass1;        // 质点1
     Mass* mass2;        // 质点2

     float springConstant;       // 弹性系数
     float springLength;       //弹簧长度
     float frictionConstant;       //摩擦系数

     Spring(Mass* mass1, Mass* mass2,
      // 构造函数
      float springConstant, float springLength, float frictionConstant)
     {
      this->springConstant = springConstant;   
      this->springLength = springLength;    
      this->frictionConstant = frictionConstant;   

      this->mass1 = mass1;      
      this->mass2 = mass2;      
     }

     void solve()        // 计算各个物体的受力
     {
      Vector3D springVector = mass1->pos - mass2->pos;  
      
      float r = springVector.length();     // 计算两个物体之间的距离

      Vector3D force;       
      
      if (r != 0)       // 计算力
       force += -(springVector / r) * (r - springLength) * springConstant;
      ...

      force += -(mass1->vel - mass2->vel) * frictionConstant;  // 加上摩擦力
      mass1->applyForce(force);      // 给物体1施加力
      mass2->applyForce(-force);      // 给物体2施加力
     }         
       
    下面我们把绳子钉在墙上,所以我们的模拟就多了一个万有引力,空气摩擦力。万有引力的公式如下:

    力 = (重力加速度) * 质量

    万有引力会作用在每一个质点上,地面也会给每个物体一个作用力。在我们的模型中将考虑绳子和地面之间的接触,地面给绳子向上的力,并提供摩擦力。

    设置模拟的初始值

    现在我们已经设置好模拟环境了,长度单位是m,时间单位是秒,质量单位是kg。

    为了设置初始值,我们必须提供供模拟开始的参数。我们定义一下参数:

    1. 重力加速度: 9.81 m/s/s 垂直向下
    2. 质点个数: 80
    3. 相连质点的距离: 5 cm (0.05 meters)
    4. 质量: 50 克(0.05 kg)
    5. 绳子开始处于垂直状态

    下面计算绳子受到的力

    f = (绳子质量) * (重力加速度) = (4 kg) * (9.81) ~= 40 N

    弹簧必须平衡这个力 40 N,它伸长1cm,计算弹性系数:

    合力= -k * x = -k * 0.01 m

    合力应该为0 :

    40 N + (-k * 0.01 meters) = 0

    弹性系数 k 为:

    k = 4000 N / m

    设置弹簧的摩擦系数:

    springFrictionConstant = 0.2 N/(m/s)

    下面我们看看这个绳索类:

    1. virtual void init() --->  重置力

    2. virtual void solve() --->  计算各个质点的力

    3. virtual void simulate(float dt) --->  模拟一次

    4. virtual void operate(float dt) --->  执行一次操作

    绳索类如下所示 :   
       

    class RopeSimulation : public Simulation     //绳索类
    {
    public:
     Spring** springs;       // 弹簧类结构的数组的指针

     Vector3D gravitation;      // 万有引力

     Vector3D ropeConnectionPos;      // 绳索的连接点
     
     Vector3D ropeConnectionVel;      //连接点的速度,我们使用这个移动绳子

     float groundRepulsionConstant;     //地面的反作用力
     
     float groundFrictionConstant;     //地面的摩擦系数
     
     float groundAbsorptionConstant;     //地面的缓冲力
     
     float groundHeight;      //地面高度

     float airFrictionConstant;      //空气的摩擦系数
       
    下面是它的构造函数  
       

     RopeSimulation(        
      int numOfMasses,     
      float m,       
      float springConstant,     
      float springLength,      
      float springFrictionConstant,     
      Vector3D gravitation,      
      float airFrictionConstant,    
      float groundRepulsionConstant,    
      float groundFrictionConstant,    
      float groundAbsorptionConstant,    
      float groundHeight      
      ) : Simulation(numOfMasses, m)   
     {
      this->gravitation = gravitation;
      
      this->airFrictionConstant = airFrictionConstant;

      this->groundFrictionConstant = groundFrictionConstant;
      this->groundRepulsionConstant = groundRepulsionConstant;
      this->groundAbsorptionConstant = groundAbsorptionConstant;
      this->groundHeight = groundHeight;

      for (int a = 0; a < numOfMasses; ++a)    // 设置质点位置
      {
       masses[a]->pos.x = a * springLength;   
       masses[a]->pos.y = 0;     
       masses[a]->pos.z = 0;     
      }

      springs = new Spring*[numOfMasses - 1];   
      
      for (a = 0; a < numOfMasses - 1; ++a)    //创建各个质点之间的模拟弹簧
      {
       springs[a] = new Spring(masses[a], masses[a + 1],
        springConstant, springLength, springFrictionConstant);
      }
     }

       
    计算施加给各个质点的力  
       

     void solve()        // 计算施加给各个质点的力
     {
      for (int a = 0; a < numOfMasses - 1; ++a)   // 弹簧施加给各个物体的力
      {
       springs[a]->solve();     
      }

      for (a = 0; a < numOfMasses; ++a)    // 计算各个物体受到的其它的力
      {
       masses[a]->applyForce(gravitation * masses[a]->m); // 万有引力
       // 空气的摩擦力
       masses[a]->applyForce(-masses[a]->vel * airFrictionConstant);

       if (masses[a]->pos.y < groundHeight)   // 计算地面对质点的作用
       {
        Vector3D v;     

        v = masses[a]->vel;    // 返回速度
        v.y = 0;     // y方向的速度为0

      // 计算地面给质点的力
        masses[a]->applyForce(-v * groundFrictionConstant);

        v = masses[a]->vel;    
        v.x = 0;     
        v.z = 0;     

        if (v.y < 0)     // 计算地面的缓冲力

         masses[a]->applyForce(-v * groundAbsorptionConstant);
        
        // 计算地面的反作用力
        Vector3D force = Vector3D(0, groundRepulsionConstant, 0) *
         (groundHeight - masses[a]->pos.y);

        masses[a]->applyForce(force);   // 施加地面对质点的力
       }
      }
     }

       
    下面的代码完成整个模拟过程  
       

     void simulate(float dt)      // 模拟一次
     {
      Simulation::simulate(dt);     // 调用基类的模拟函数

      ropeConnectionPos += ropeConnectionVel * dt;   // 计算绳子的连接点

      if (ropeConnectionPos.y < groundHeight)    
      {
       ropeConnectionPos.y = groundHeight;
       ropeConnectionVel.y = 0;
      }

      masses[0]->pos = ropeConnectionPos;    // 更新绳子的连接点和速度
      masses[0]->vel = ropeConnectionVel;    
     }

     void setRopeConnectionVel(Vector3D ropeConnectionVel)   
     {
      this->ropeConnectionVel = ropeConnectionVel;
     }

       
    有了上面的类,我们可以很方便的模拟绳子,代码如下:  
       

    RopeSimulation* ropeSimulation =
     new RopeSimulation(
      80,        // 80 质点
      0.05f,        // 每个质点50g
      10000.0f,        // 弹性系数
      0.05f,        // 质点之间的距离
      0.2f,        // 弹簧的内摩擦力
      Vector3D(0, -9.81f, 0),      // 万有引力
      0.02f,        // 空气摩擦力
      100.0f,        // 地面反作用系数
      0.2f,        // 地面摩擦系数
      2.0f,        // 地面缓冲系数
      -1.5f);        // 地面高度

       
    下面的代码在程序中执行绳子的模拟  
       

    float dt = milliseconds / 1000.0f;      // 经过的秒数

    float maxPossible_dt = 0.002f;      // 模拟间隔

    int numOfIterations = (int)(dt / maxPossible_dt) + 1;    // 模拟次数
    if (numOfIterations != 0)       
     dt = dt / numOfIterations;      

    for (int a = 0; a < numOfIterations; ++a)     // 执行模拟
     ropeSimulation->operate(dt);

       
     

    我相信这一个教会了你很多,从最开始的模型的建立,到完成最后的代码。有了这个基础,相信你会创造出很多更有意思的代码!

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/31 20:08:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客4
    发贴心情 
    Lesson 40
       
    Rope Simulation

    In this tutorial, you will find a simulation of a rope. This simulation is based on the simple physical simulation engine in Lesson 39. In order to benefit from this tutorial, you should know how forces are applied to masses in simulations, how position and velocity of a mass is iterated while the simulation runs, and how 3D vectoral operations are used in physics. If you hesitate on any one of those subjects, read about them from Lesson 39 and other sources, and develop several applications.

    In physical simulations, the purpose is to form a physical setting, which acts the same as in the natural environment. Motion in simulations cannot always be exactly the same as in the nature. A model to cover the motion, that we aim to simulate, must be put forward to form the physical setting. The model that we create must state, how precise and detailed the motion should be observed from the simulation. Are we aiming to observe the atoms, the electrons or the photons, or are we aiming to observe the approximate motion of a cluster of particles? What is the scale that we want to see? What is the scale of space and time?

    The scale of space and time to observe is related to:

    1. Mathematics of motion
    2. Performance of the computer we use for the simulation

    1. Mathematics Of Motion:

    Here, the mathematics of motion is called "classical mechanics", which is simply representing masses as particles in space and accelerating these masses by forces as the time passes. In the scale that we can observe by naked eye, classical mechanics is valid to use. Therefore, we can use classical mechanics for simulating objects and mechanisms from our daily lives. In Lesson 39, force of gravitation and spring were applied to masses of 1 kg by the use of classical mechanics. In this tutorial, we will use classical mechanics in a rope simulation.

    2. Performance Of The Computer We Use For The Simulation:

    Performance of the computer to run the simulation, determines how detailed we could observe. For example, when simulating a walking man on a slow computer, we would think of eliminating the simulation of fingers. Fingers of the feet surely have an important role. Eventhough, without using the fingers in the simulation, we could obtain a walking human. Maybe the quality of motion would be low, but the calculations would cost less. In the walking human example, performance of the computer forces us to choose the scale as feet and leg and forces us to omit the fingers.

    Designing The Physical Setting For The Rope:

    Having classical mechanics (as the mathematics of motion) and a computer with 500 mhz CPU speed (let's choose this as a minimum requirement), we will design the physical setting of a rope simulation. Firstly we have to determine how much detail we want to observe. While implementing the code, we will use Physics1.h from Lesson 39. From Physics1.h, we have a "class Mass" which represents a mass as a point particle. We can make use of that Mass class. If we bind these point-like masses by springs to each other, we can form a physical model to represent a rope. From the model, we can examine how detailed the observed motion of the rope would be. We can deduce that the rope will show the swinging and waving motion, but it would not show the swirling motion. (Imagine swirling like this; say you have a thin rope in between two fingers and you are rubbing your fingers so that the rope gets curly.) We cannot observe swirling because we use point-like particles in the model. Point-like particles cannot turn around an axis, therefore the rope does not get curly. Let's decide to use the model described above and state that our detail is limited as the swinging and waving motion. Let's also state that we want to observe the rope's waving motion as detailed as about 10 cm. This means the rope will show discontinuity under 10 cm. I have chosen these constraints because I want use about 50 or 100 particles (due to performance) in the rope and I want this rope to be about 3 to 4 meters long. Which means there is about 3 to 8 cm between the particles of the rope which is under the discontinuity level we chose (10 cm).

    Determining The Motion Of Equation:

    Motion of equation mathematically means a second order differential equation and conceptually means the forces acting in a physical setting. Let's use the conceptual meaning because it sounds better. Determining the motion of equation means determining the forces. In the rope model, the forces will act on the particles which make up the rope. The first force will be the spring tension between these particles. Below, each particle is shown by "O" and the springs are shown as "----":
    O----O----O----O
    1    2    3    4

    Particle 1 is bounded to 2, 2 to 3, and 3 to 4. We have 4 particles in this rope and 3 springs. The springs are the sources of the force between two particles. Remember that the spring force is formulated as:

    force = -k * x
    k: a constant to represent the stiffness of the spring
    x: distance of the mass from the point it is bound to

    The spring formula we will use will be very similar to the one above. If we use the above formula as it is, it would cause the rope to wrinkle! Because unless x is zero (x is the distance between two bound masses in our rope model), there is force. Therefore all the particles of the rope would be pulled to each other until x was zero. This is not what we want. Imagine a rope put on a table. We want our rope to stay steady like the rope on the table. Somehow we have to maintain a constant length. To do, the force shall be zero when x was a positive value. Let's write the formula as:

    force = -k * (x - d)
    k: a constant to represent the stiffness of the spring
    x: distance of the mass from the point it is bound to
    d: a constant positive distance value that a spring stays steady

    With this formula, it is clear that if the distance between two masses is equal to d, no force will be applied. Let's say that we have 100 particles. If we choose d as 5 cm (0.05 meters), we would have a steady rope of 5 meters when put on a table. When x is more than d, the spring would stretch and when it was less, it would shrink.

    Now, the formula gives a proper motion, but it needs more. It needs some friction. Unless there is friction, a physical system conserves its energy. If we don't use a friction factor, the rope would never stop swinging. Before going into details of the friction factor let's have a look at the code.

    Class Spring:

    The spring class binds two masses and exerts force to each of these masses.   
       

    class Spring         // An Object To Represent A Spring With Inner Friction Binding Two Masses. The Spring
    {          // Has A Normal Length (The Length That The Spring Does Not Exert Any Force)
    public:
     Mass* mass1;        // The First Mass At One Tip Of The Spring
     Mass* mass2;        // The Second Mass At The Other Tip Of The Spring

     float springConstant;       // A Constant To Represent The Stiffness Of The Spring
     float springLength;       // The Length That The spring Does Not Exert Any Force
     float frictionConstant;       // A Constant To be Used For The Inner Friction Of The Spring

     Spring(Mass* mass1, Mass* mass2,
      // Constructor
      float springConstant, float springLength, float frictionConstant)
     {
      this->springConstant = springConstant;    // Set The springConstant
      this->springLength = springLength;    // Set The springLength
      this->frictionConstant = frictionConstant;   // Set The frictionConstant

      this->mass1 = mass1;      // Set mass1
      this->mass2 = mass2;      // Set mass2
     }

     void solve()        // solve() Method: The Method Where Forces Can Be Applied
     {
      Vector3D springVector = mass1->pos - mass2->pos;  // Vector Between The Two Masses
      
      float r = springVector.length();    // Distance Between The Two Masses

      Vector3D force;       // Force Initially Has A Zero Value
      
      if (r != 0)       // To Avoid A Division By Zero... Check If r Is Zero
       // The Spring Force Is Added To The Force  
       force += -(springVector / r) * (r - springLength) * springConstant;
      ...

       
    In the constructor, mass1, mass2, and the constants are set. The thrilling part is the solve() method. In this method forces are applied. To apply force we have to write the spring formula that we obtained:

    force = -k * (x - d)

    A vector to represent the distance between the masses in 3D;

    Vector3D springVector = mass1->pos - mass2->pos; (Vector Between The Two Masses)

    is found. Then a zero force is created:

    Vector3D force;

    Then, the spring force is added to that:

    force += (springVector / r) * (r - springLength) * (-springConstant);

    To reach the formula above, we firstly obtain a unit vector for representing just the directional vector between the masses:

    (springVector / r)

    Then, with the use of this unit vector we obtain (x - d) part of the formula in 3D by:

    (springVector / r) * (r - springLength)

    And we multiply the above 3D vector by;

    (-springConstant)

    which stands for -k in the original formula (the negative sign means pull rather than repel). We already have finished the spring tension part of the force. Let's go on to the friction part. This friction is in the spring. The spring tends to loose energy by this force. If you apply force to a mass in the opposite direction that the mass moves, you make the mass get slower. Therefore, the friction force can be stated in terms of the velocity of a mass:

    friction force = -k * velocity
    k: a constant to represent how much friction there is
    velocity: velocity of the mass that is under the friction force

    A friction formula could be written differently but this one works fine for our rope model. In this formula only one mass is considered. In the spring we consider two. We can take the difference of the velocities of the two masses and obtain a relative velocity. This will provide an inner friction.   
       

      (void solve() continued)

      force += -(mass1->vel - mass2->vel) * frictionConstant;  // The Friction Force Is Added To The force
              // With This Addition We Obtain The Net Force Of The Spring
      mass1->applyForce(force);     // Force Is Applied To mass1
      mass2->applyForce(-force);     // The Opposite Of Force Is Applied To mass2
     }         // Void Solve() Ends Here

       
    force += -(mass1->vel - mass2->vel) * frictionConstant;

    Above, the friction force obtained by the relative velocities of the masses is added to the force of the spring. The force is applied to mass1 as it is:

    mass1->applyForce(force);

    and the opposite of the force is applied to mass2:

    mass2->applyForce(-force);

    In physics, all interactions occur between two particles. A force always acts on two masses in opposite directions. In simulations, if one mass is negligible when compared with the other, force acting on the larger mass can be neglected since the larger mass's acceleration will be small. For example while a gravitational force pulls a small mass down, the mass pulls the earth up, but we neglect the force on the earth.

    By now, we have written an equation of motion, which is actually the spring forces in the rope. To complete the simulation, we should create an environment, which contains the rope, and consider the external forces acting on the rope. Let's have a gravitational field in this environment. When there is gravitation, masses experience the gravitational force. I would also like to have air friction which is as simple as:

    friction force = -k * velocity
    k: a constant to represent how much friction there is
    velocity: velocity of the mass that is under the friction force

    Let's also have a planer surface that we can drag the rope on. So, our equation of motion extends. Gravitation, air friction and the forces from the ground (planer surface) must be added. Gravitational force is simply:

    force = (gravitational acceleration) * mass

    Gravitation and air friction will act on every particle on the rope. What about the force from the ground? Force from the ground will act on every mass as well. We should think of a model to represent the ground - rope interaction. My model is an easy one: the ground pushes a mass upwards and exerts a friction force. The force should act on a mass whenever that mass touches the ground. So we will check for that.

    Setting The Initial Values Of The Simulation

    By now, our environment is ready for simulating. The units will be meters (for position), seconds (for time), and kg (for weight).

    To set the initial values, we should define the orientation of the rope before the simulation starts and define the constants. Let's define that the gravity acts in negative y direction by 9.81 m/s/s. Let's place a rope of 4 meters long with 80 particles. Let's have this rope stand horizontal just before the simulation starts. To do, we should put each particle with a 5 cm distance to its neighbor (4 meters / 80 = 0.05 meters = 5 cm). Let's define that the normal spring length (the length that a spring does not exert any force) is 5 cm so that that rope is left without tension at the begining of the simulation. Let's define the total mass of the rope as 4 kg (this is a heavy rope). This gives 0.05 kg (50 grams) for each of the masses. Before going further let's see what we have in hand:

    1. gravitational acceleration: 9.81 m/s/s in negative y direction
    2. number of masses: 80
    3. normal distance between two neighbor masses: 5 cm (0.05 meters)
    4. weight of a mass: 50 grams (0.05 kg)
    5. orientation of the rope: horizontally placed without tension

    Next, we could find the spring constant. When we hang the rope from one tip, it would surely stretch. The spring at the top of the rope would stretch the most. I wouldn't like this spring to stretch more than 1 cm (0.01cm). The weight that this spring carries is almost all the rope (the particle at the tip is exclusive). The force is:

    f = (mass of the rope) * (gravitational acceleration) = (4 kg) * (9.81) ~= 40 N

    Spring force should balance 40 N:

    spring force = -k * x = -k * 0.01 meters

    Total of these forces should be zero:

    40 N + (-k * 0.01 meters) = 0

    From here we obtain k as:

    k = 4000 N / m

    To remember more easily, let's assume k as 10000 N / m, which gives a stiffer rope with about 4 mm stretch at the top spring.

    To find the friction constant in the springs, we should do calculations more complicated than the above. Therefore, I will use the value that I found by trial and error. Which is:

    springFrictionConstant = 0.2 N/(m/s)

    0.2 N/(m/s) springFrictionConstant is fine for our rope to look realistic (this was my opinion after I watched the simulation).

    Before going on to the air friction and forces from the ground, let's have a look at the RopeSimulation class. This class is derived from the "class Simulation" from Physics1.h which was explained in Lesson 39. class Simulation has four methods to run a simulation. These are:

    1. virtual void init() --->  Resets The Forces.

    2. virtual void solve() --->  Intended Forces Are Applied.

    3. virtual void simulate(float dt) --->  Position And Velocity Are Iterated.

    4. virtual void operate(float dt) --->  Method 1., 2., And 3. Are Packed So That They Are Called In A Series.

    In the RopeSimulation class, we will override solve() and simulate(float dt) because we have a special implementation for the rope. We will apply forces in solve() method, and stabilize one tip of the rope in simulate(float dt) method.

    class RopeSimulation is derived from class Simulation (from Physics1.h). It simulates a rope with point-like particles bound with springs. The springs have inner friction and normal length. One tip of the rope is stabilized at a point in space called "Vector3D ropeConnectionPos". This point can be moved externally by a method "void setRopeConnectionVel(Vector3D ropeConnectionVel)". RopeSimulation creates air friction and a planer surface (or ground) with a normal in +y direction. RopeSimulation implements the force applied by this surface. In the code, the surface is refered as "ground".

    The RopeSimulation class starts as follows:   
       

    class RopeSimulation : public Simulation     // An Object To Simulate A Rope Interacting With A Planer Surface And Air
    {
    public:
     Spring** springs;       // Springs Binding The Masses (There Shall Be [numOfMasses - 1] Of Them)

     Vector3D gravitation;       // Gravitational Acceleration (Gravity Will Be Applied To All Masses)

     Vector3D ropeConnectionPos;      // A Point In Space That Is Used To Set The Position Of The
              // First Mass In The System (Mass With Index 0)
     
     Vector3D ropeConnectionVel;      // A Variable To Move The ropeConnectionPos (By This, We Ccan Swing The Rope)

     float groundRepulsionConstant;      // A Constant To Represent How Much The Ground Shall Repel The Masses
     
     float groundFrictionConstant;      // A Constant Of Friction Applied To Masses By The Ground
              // (Used For Sliding Of Rope On The Ground)
     
     float groundAbsorptionConstant;      // A Constant Of Absorption Friction Applied To Masses By The Ground
              // (Used For Vertical Collisions Of The Rope With The Ground)
     
     float groundHeight;       // A Value To Represent The Y Value Of The Ground
              // (The Ground Is A Planer Surface Facing +Y Direction)

     float airFrictionConstant;      // A Constant Of Air Friction Applied To Masses

       
    And the class has a constructor with 11 parameters to take:   
       

     RopeSimulation(        // A Long Long Constructor With 11 Parameters Starts Here
      int numOfMasses,      // 1. The Number Of Masses
      float m,       // 2. Weight Of Each Mass
      float springConstant,      // 3. How Stiff The Springs Are
      float springLength,      // 4. The Length That A Spring Does Not Exert Any Force
      float springFrictionConstant,     // 5. Inner Friction Constant Of Spring
      Vector3D gravitation,      // 6. Gravitational Acceleration
      float airFrictionConstant,     // 7. Air Friction Constant
      float groundRepulsionConstant,     // 8. Ground Repulsion Constant
      float groundFrictionConstant,     // 9. Ground Friction Constant
      float groundAbsorptionConstant,     // 10. Ground Absorption Constant
      float groundHeight      // 11. Height Of The Ground (Y Position)
      ) : Simulation(numOfMasses, m)     // The Super Class Creates Masses With Weights m Of Each
     {
      this->gravitation = gravitation;
      
      this->airFrictionConstant = airFrictionConstant;

      this->groundFrictionConstant = groundFrictionConstant;
      this->groundRepulsionConstant = groundRepulsionConstant;
      this->groundAbsorptionConstant = groundAbsorptionConstant;
      this->groundHeight = groundHeight;

      for (int a = 0; a < numOfMasses; ++a)    // To Set The Initial Positions Of Masses Loop With For(;;)
      {
       masses[a]->pos.x = a * springLength;   // Set X-Position Of masses[a] With springLength Distance To Its Neighbor
       masses[a]->pos.y = 0;     // Set Y-Position As 0 So That It Stand Horizontal With Respect To The Ground
       masses[a]->pos.z = 0;     // Set Z-Position As 0 So That It Looks Simple
      }

      springs = new Spring*[numOfMasses - 1];    // Create [numOfMasses - 1] Pointers For springs
              // ([numOfMasses - 1] Springs Are Necessary For numOfMasses)
      
      for (a = 0; a < numOfMasses - 1; ++a)    // To Create Everyone Of Each Start A Loop
      {
       // Create The Spring With Index "a" By The Mass With Index "a" And Another Mass With Index "a + 1".
       springs[a] = new Spring(masses[a], masses[a + 1],
        springConstant, springLength, springFrictionConstant);
      }
     }

       
    [numOfMasses - 1] springs are created (remember the figure: O----O----O----O). The masses are initially placed in a horizontal orientation. When the forces applied are implemented in the solve method, the equation of motion will be solved while the simulation runs. The solve method looks like this:   
       

     void solve()        // solve() Is Overriden Because We Have Forces To Be Applied
     {
      for (int a = 0; a < numOfMasses - 1; ++a)   // Apply Force Of All Springs
      {
       springs[a]->solve();     // Spring With Index "a" Should Apply Its Force
      }

      for (a = 0; a < numOfMasses; ++a)    // Start A Loop To Apply Forces Which Are Common For All Masses
      {
       masses[a]->applyForce(gravitation * masses[a]->m); // The Gravitational Force
       // The air friction
       masses[a]->applyForce(-masses[a]->vel * airFrictionConstant);

       if (masses[a]->pos.y < groundHeight)   // Forces From The Ground Are Applied If A Mass Collides With The Ground
       {
        Vector3D v;     // A Temporary Vector3D

        v = masses[a]->vel;    // Get The Velocity
        v.y = 0;     // Omit The Velocity Component In Y-Direction

        // The Velocity In Y-Direction Is Omited Because We Will Apply A Friction Force To Create
        // A Sliding Effect. Sliding Is Parallel To The Ground. Velocity In Y-Direction Will Be Used
        // In The Absorption Effect.

        // Ground Friction Force Is Applied    
        masses[a]->applyForce(-v * groundFrictionConstant);

        v = masses[a]->vel;    // Get The Velocity
        v.x = 0;     // Omit The x And z Components Of The Velocity
        v.z = 0;     // We Will Use v In The Absorption Effect
        
        // Above, We Obtained A Velocity Which Is Vertical To The Ground And It Will Be Used In
        // The Absorption Force

        if (v.y < 0)     // Let's Absorb Energy Only When A Mass Collides Towards The Ground

         // The Absorption Force Is Applied    
         masses[a]->applyForce(-v * groundAbsorptionConstant);
        
        // The Ground Shall Repel A Mass Like A Spring.
        // By "Vector3D(0, groundRepulsionConstant, 0)" We Create A Vector In The Plane Normal Direction
        // With A Magnitude Of groundRepulsionConstant.
        // By (groundHeight - masses[a]->pos.y) We Repel A Mass As Much As It Crashes Into The Ground.
        Vector3D force = Vector3D(0, groundRepulsionConstant, 0) *
         (groundHeight - masses[a]->pos.y);

        masses[a]->applyForce(force);   // The Ground Repulsion Force Is Applied
       }
      }
     }

       
    In the code above, firstly springs are solved (the order has no importance). Then the forces that are common for all masses are solved in a for(;;) loop. These forces are the gravity, air friction and forces from the ground. Forces from the ground looks a bit complicated, but it actually is as simple as the others. The rope's sliding effect on the ground is povided by a friction force which omits the velocity in y direction. y is the direction that the ground faces up. A sliding effect shall not be in the direction of the face. That's why y direction is omited. This is just the opposite for the absoption effect. The absorption force is applied only in the direction of the face of the ground. There is an exceptional case for the absorption effect: it does not exert force when a mass is moving away from the ground. Otherwise the rope would tend to stick to the ground while we pull it upwards. We implement this exceptional case with "if (v.y < 0)". Lastly there is the repelling force from the ground. The ground repels masses just like there was a spring pushing the mass upwards.

    RopeSimulation class simulates the particle at the begining index of the rope. The purpose is to create a medium to swing the rope from one tip. The mass with index "0" is simulated seperately with ropeConnectionPos and ropeConnectionVel values.   
       

     void simulate(float dt)       // simulate(float dt) Is Overriden Because We Want To Simulate
              // The Motion Of The ropeConnectionPos
     {
      Simulation::simulate(dt);     // The Super Class Shall Simulate The Masses

      ropeConnectionPos += ropeConnectionVel * dt;   // Iterate The Positon Of ropeConnectionPos

      if (ropeConnectionPos.y < groundHeight)    // ropeConnectionPos Shall Not Go Under The Ground
      {
       ropeConnectionPos.y = groundHeight;
       ropeConnectionVel.y = 0;
      }

      masses[0]->pos = ropeConnectionPos;    // Mass With Index "0" Shall Position At ropeConnectionPos
      masses[0]->vel = ropeConnectionVel;    // The Mass's Velocity Is Set To Be Equal To ropeConnectionVel
     }

       
    We set the value of ropeConnectionVel by a method:   
       

     void setRopeConnectionVel(Vector3D ropeConnectionVel)   // The Method To Set ropeConnectionVel
     {
      this->ropeConnectionVel = ropeConnectionVel;
     }

       
    ropeConnectionVel is used in the simulation. By using the keys we set ropeConnectionVel and we can move the rope like we were holding from one tip.

    There are some constants which are not easy to estimate before we run the simulation. The constants I found appropriate are below (taken from Physics2Application.cpp):   
       

    RopeSimulation* ropeSimulation =
     new RopeSimulation(
      80,        // 80 Particles (Masses)
      0.05f,        // Each Particle Has A Weight Of 50 Grams
      10000.0f,       // springConstant In The Rope
      0.05f,        // Normal Length Of Springs In The Rope
      0.2f,        // Spring Inner Friction Constant
      Vector3D(0, -9.81f, 0),      // Gravitational Acceleration
      0.02f,        // Air Friction Constant
      100.0f,        // Ground Repel Constant
      0.2f,        // Ground Slide Friction Constant
      2.0f,        // Ground Absoption Constant
      -1.5f);        // Height Of Ground

       
    By changing the values above, you can obtain different motions for the rope. Notice that "height of ground" is -1.5 meters. The rope was initialized at y = 0. This gives us a rope swinging down towards the ground and then colliding, which looks cool. From Lesson 39, remember that there is a maximum possible dt value for a simulation. With the parameters above, I found that this maximum dt was about 0.002 seconds. If your changes in the parameters decreases the maximum dt, your simulation would show instability and the rope would not work. To make it work you have to find the new maximum possible dt. Greater forces and/or smaller masses means more instability because acceleration is more in that case (remember "acceleration = force / mass").

    Same as Lesson 39, the simulation is operated from the application file (Physics2Application.cpp):   
       

    float dt = milliseconds / 1000.0f;      // Let's Convert Milliseconds To Seconds

    float maxPossible_dt = 0.002f;       // Maximum Possible dt Is 0.002 Seconds
              // This Is Needed To Prevent Passing Over A Non-Precise dt Value

    int numOfIterations = (int)(dt / maxPossible_dt) + 1;    // Calculate Number Of Iterations To Be Made At This Update Depending On maxPossible_dt And dt
    if (numOfIterations != 0)       // Avoid Division By Zero
     dt = dt / numOfIterations;      // dt Should Be Updated According To numOfIterations

    for (int a = 0; a < numOfIterations; ++a)     // We Need To Iterate Simulations "numOfIterations" Times
     ropeSimulation->operate(dt);

       
    When you run the application, use the arrow keys, and the HOME and END keys to move the rope around. Try playing with the rope. Observe the waving and the swinging motion.

    Simulation procedure loads onto the CPU. Therefore, it is recommended to optimize your compiler. In default Visual C++ Release settings, the rope simulation runs more than 10 times faster than Debug. In Debug, the minimum requirement is 500 mhz of CPU speed. In Release, the minimum requirement is much less than that.

    In this tutorial, a complete simulation is presented. Its physical setting, theory, design, and implementation are mentioned. More advanced simulations look like the one above. The most frequently used concepts are covered with the rope example. This is true for physical simulations in game development as well. Try using physics in your applications and create demos and games of your own.

    For any comments or questions please contact me:

    Erkin Tunca (erkintunca@icqmail.com)

    Jeff Molofee (NeHe)

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/31 20:14:00
     
     hwfchina 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:4
      积分:68
      门派:XML.ORG.CN
      注册:2008/3/17

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给hwfchina发送一个短消息 把hwfchina加入好友 查看hwfchina的个人资料 搜索hwfchina在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看hwfchina的博客5
    发贴心情 
    顶~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2008/3/18 22:30:00
     
     hwfchina 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:4
      积分:68
      门派:XML.ORG.CN
      注册:2008/3/17

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给hwfchina发送一个短消息 把hwfchina加入好友 查看hwfchina的个人资料 搜索hwfchina在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看hwfchina的博客6
    发贴心情 
    顶~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2008/3/18 22:32:00
     
     skflip 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:2
      积分:58
      门派:XML.ORG.CN
      注册:2009/2/13

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给skflip发送一个短消息 把skflip加入好友 查看skflip的个人资料 搜索skflip在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看skflip的博客7
    发贴心情 
    非常好,网站太棒了,加油,这个好东西能找到,真是不容易,nehe的网站上都下不下来啊
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2009/2/13 11:58:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/4/19 22:03:53

    本主题贴数7,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    476.563ms