outdated: 40.Introduction to Physical Simulations
这一节为物理模拟绳子。在上一节中,三种物理模拟的基本运动作为这一节的基础。(直线运动, 弹性运动,万有引力下运动)
让绳子拥有弹性感,可以在每个质量节点中加入很小长度的弹簧。弹簧上力的变化可参考上节的弹性运动。
基本上所谓的重点就是绳子模拟类了,RopeSimulation类继承Simulation类(上节中)。
类中定义了,每个弹簧,万有引力, 每节绳子连接处位置和速率,还有地面对绳子的抵抗力系数,地面摩擦力系数, 地面吸引力系数,地面高度,空气摩擦力系数。先来看个构造函数,
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 i = 0; i < numOfMasses; ++i) { masses[i]->pos.x = i * springLength; masses[i]->pos.y = 0; masses[i]->pos.z = 0; } springs = new Spring*[numOfMasses - 1]; for (int i = 0; i < numOfMasses - 1; ++i) { springs[i] = new Spring(masses[i], masses[i + 1], springConstant, springLength, springFrictionConstant); } }
下面为RopeSimulation类代码,
class RopeSimulation : public Simulation { public: Spring** springs; Vector3D gravitation; Vector3D ropeConnectionPos; Vector3D ropeConnectionVel; // A constant to represent how much the ground shall repel the masses float groundRepulsionConstant; // A constant of friction applied to masses by the ground (sliding of rope) float groundFrictionConstant; // A constant of absorption friction alppiled to masses by the ground (vertical collisions of rope) float groundAbsorptionConstant; float groundHeight; float airFrictionConstant; // A constant of air friction-applied to masses 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 i = 0; i < numOfMasses; ++i) { masses[i]->pos.x = i * springLength; masses[i]->pos.y = 0; masses[i]->pos.z = 0; } springs = new Spring*[numOfMasses - 1]; for (int i = 0; i < numOfMasses - 1; ++i) { springs[i] = new Spring(masses[i], masses[i + 1], springConstant, springLength, springFrictionConstant); } } void release() { Simulation::release(); for (int i = 0; i < numOfMasses - 1; ++i) { delete(springs[i]); springs[i] = NULL; } delete(springs); springs = NULL; } void solve() { for (int i = 0; i < numOfMasses - 1; ++i) { springs[i]->solve(); } for (int i = 0; i < numOfMasses; ++i) { masses[i]->applyForce(gravitation * masses[i]->m); // Gravitation masses[i]->applyForce(-masses[i]->vel * airFrictionConstant); // Air Friction if (masses[i]->pos.y < groundHeight) { Vector3D v = masses[i]->vel; // 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. masses[i]->applyForce(-v * groundFrictionConstant); v = masses[i]->vel; v.x = 0; // Omit v.z = 0; if (v.y < 0) { // Absorb energy only when a mass collides towards the ground masses[i]->applyForce(-v * groundAbsorptionConstant); } // The ground shall repel a mass like a spring. Vector3D force = Vector3D(0, groundRepulsionConstant, 0) * (groundHeight - masses[i]->pos.y); masses[i]->applyForce(force); } } } void simulate(float dt) // Overriden { Simulation::simulate(dt); ropeConnectionPos += ropeConnectionVel * dt; // Iterate the positon of ropeConnectionPos 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; } };
下面为代码,
#ifndef GL_FRAMEWORK_INCLUDED #define GL_FRAMEWORK_INCLUDED #include <windows.h> typedef struct { // Structure for keyboard stuff BOOL keyDown[256]; } Keys; typedef struct { // Contains information vital to applications HMODULE hInstance; // Application Instance const char* className; } Application; typedef struct { // Window creation info Application* application; char* title; int width; int height; int bitsPerPixel; BOOL isFullScreen; } GL_WindowInit; typedef struct { // Contains information vital to a window Keys* keys; HWND hWnd; // Windows handle HDC hDC; // Device context HGLRC hRC; // Rendering context GL_WindowInit init; BOOL isVisible; // Window visiable? DWORD lastTickCount; // Tick counter } GL_Window; void TerminateApplication(GL_Window* window); // Terminate the application void ToggleFullscreen(GL_Window* window); // Toggle fullscreen / Windowed mode BOOL Initialize(GL_Window* window, Keys* keys); void Deinitialize(void); void Update(DWORD milliseconds); void Draw(void); #endif
#include <Windows.h> #include <GL\glew.h> #include <GL\glut.h> #include "Previous.h" #define WM_TOGGLEFULLSCREEN (WM_USER+1) // Application define message for toggling // between fulscreen / windowed mode static BOOL g_isProgramLooping; // Window creation loop, for fullscreen / windowed mode static BOOL g_createFullScreen; // If true, then create window void TerminateApplication(GL_Window* window) // Terminate the application { PostMessage(window->hWnd, WM_QUIT, 0, 0); // Send a WM_QUIT message g_isProgramLooping = FALSE; // Stop looping of the program } void ToggleFullscreen(GL_Window* window) // Toggle fullscreen /windowed mode { PostMessage(window->hWnd, WM_TOGGLEFULLSCREEN, 0, 0); // Send a WM_TOGGLEFULLSCREEN message } void ReshapeGL(int width, int height) // Reshape the window when it's moved or resized { glViewport(0, 0, (GLsizei)(width), (GLsizei)(height)); // Reset the current viewport glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Calcutate the aspect ratio of the window gluPerspective(45.0f, (GLfloat)(width) / (GLfloat)(height), 1.0, 100.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } BOOL ChangeScreenResolution(int width, int height, int bitsPerPixel) // Change the screen resolution { DEVMODE dmScreenSettings; // Device mode ZeroMemory(&dmScreenSettings, sizeof(DEVMODE)); // Make sure memory is cleared dmScreenSettings.dmSize = sizeof(DEVMODE); // Size of the devmode structure dmScreenSettings.dmPelsWidth = width; dmScreenSettings.dmPelsHeight = height; dmScreenSettings.dmBitsPerPel = bitsPerPixel; dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { return FALSE; // Display change failed } return TRUE; } BOOL CreateWindowGL(GL_Window* window) { DWORD windowStyle = WS_OVERLAPPEDWINDOW; // Define window style DWORD windowExtendedStyle = WS_EX_APPWINDOW; // Define the window's extended style PIXELFORMATDESCRIPTOR pdf = { sizeof(PIXELFORMATDESCRIPTOR), // Size of this pixel format descriptor 1, // Version Number PFD_DRAW_TO_WINDOW | // Format must support window PFD_SUPPORT_OPENGL | // Format must support openGL PFD_DOUBLEBUFFER, // Must support double buffering PFD_TYPE_RGBA, // Request an RGBA format window->init.bitsPerPixel, // Select color depth 0, 0, 0, 0, 0, 0, // Color bits ignored 0, // No alpha buffer 0, // Shift bit ignored 0, // No accumulation buffer 0, 0, 0, 0, // Accumulation bits ignored 16, // 16bits Z-buffer (depth buffer) 0, // No stencil buffer 0, // No auxiliary buffer PFD_MAIN_PLANE, // Main drawing layer 0, // Reserved 0, 0, 0 // Layer masks ignored }; RECT windowRect = { 0, 0, window->init.width, window->init.height }; // Window coordiantes GLuint PixelFormat; if (window->init.isFullScreen == TRUE) { if (ChangeScreenResolution(window->init.width, window->init.height, window->init.bitsPerPixel) == FALSE) { // Fullscreen mode failed, run in windowed mode instead MessageBox(HWND_DESKTOP, "Mode Switch Failed.\nRuning In Windowed Mode.", "Error", MB_OK | MB_ICONEXCLAMATION); window->init.isFullScreen = FALSE; } else { ShowCursor(FALSE); windowStyle = WS_POPUP; // Popup window windowExtendedStyle |= WS_EX_TOPMOST; } } else { // Adjust window, account for window borders AdjustWindowRectEx(&windowRect, windowStyle, 0, windowExtendedStyle); } // Create Opengl window window->hWnd = CreateWindowEx(windowExtendedStyle, // Extended style window->init.application->className, // Class name window->init.title, // Window title windowStyle, // Window style 0, 0, // Window X,Y position windowRect.right - windowRect.left, // Window width windowRect.bottom - windowRect.top, // Window height HWND_DESKTOP, // Desktop is window's parent 0, // No menu window->init.application->hInstance, // Pass the window instance window); if (window->hWnd == 0) { // Was window creation a success? return FALSE; } window->hDC = GetDC(window->hWnd); if (window->hDC == 0) { DestroyWindow(window->hWnd); window->hWnd = 0; return FALSE; } PixelFormat = ChoosePixelFormat(window->hDC, &pdf); // Find a compatible pixel format if (PixelFormat == 0) { ReleaseDC(window->hWnd, window->hDC); // Release device context window->hDC = 0; DestroyWindow(window->hWnd); window->hWnd = 0; return FALSE; } if (SetPixelFormat(window->hDC, PixelFormat, &pdf) == FALSE) { // Try to set the pixel format ReleaseDC(window->hWnd, window->hDC); window->hDC = 0; DestroyWindow(window->hWnd); window->hWnd = 0; return FALSE; } window->hRC = wglCreateContext(window->hDC); // Try to get a rendering context if (window->hRC == 0) { ReleaseDC(window->hWnd, window->hDC); window->hDC = 0; DestroyWindow(window->hWnd); window->hWnd = 0; return FALSE; } // Make the rendering context our current rendering context if (wglMakeCurrent(window->hDC, window->hRC) == FALSE) { wglDeleteContext(window->hRC); // Delete the rendering context window->hRC = 0; ReleaseDC(window->hWnd, window->hDC); window->hDC = 0; DestroyWindow(window->hWnd); window->hWnd = 0; return FALSE; } ShowWindow(window->hWnd, SW_NORMAL); // Make the window visiable window->isVisible = TRUE; ReshapeGL(window->init.width, window->init.height); // Reshape our GL window ZeroMemory(window->keys, sizeof(Keys)); // Clear all keys window->lastTickCount = GetTickCount(); return TRUE; } BOOL DestoryWindowGL(GL_Window* window) { if (window->hWnd != 0) { if (window->hDC != 0) { wglMakeCurrent(window->hDC, 0); // Setting current active rendering context to zero if (window->hRC != 0) { wglDeleteContext(window->hRC); window->hRC = 0; } ReleaseDC(window->hWnd, window->hDC); window->hDC = 0; } DestroyWindow(window->hWnd); window->hWnd = 0; } if (window->init.isFullScreen) { ChangeDisplaySettings(NULL, 0); // Switch back to desktop resolution ShowCursor(TRUE); } return TRUE; } // Process window message callback LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { // Get the window context GL_Window* window = (GL_Window*)(GetWindowLong(hWnd, GWL_USERDATA)); switch (uMsg) { // Evaluate window message case WM_SYSCOMMAND: // Intercept system commands { switch (wParam) { // Check system calls case SC_SCREENSAVE: // Screensaver trying to start? case SC_MONITORPOWER: // Mointer trying to enter powersave? return 0; // Prevent form happening } break; } return 0; case WM_CREATE: { CREATESTRUCT* creation = (CREATESTRUCT*)(lParam); // Store window structure pointer window = (GL_Window*)(creation->lpCreateParams); SetWindowLong(hWnd, GWL_USERDATA, (LONG)(window)); } return 0; case WM_CLOSE: TerminateApplication(window); return 0; case WM_SIZE: switch (wParam) { case SIZE_MINIMIZED: // Was window minimized? window->isVisible = FALSE; return 0; case SIZE_MAXIMIZED: window->isVisible = TRUE; ReshapeGL(LOWORD(lParam), HIWORD(lParam)); return 0; case SIZE_RESTORED: window->isVisible = TRUE; ReshapeGL(LOWORD(lParam), HIWORD(lParam)); return 0; } break; case WM_KEYDOWN: if ((wParam >= 0) && (wParam <= 255)) { window->keys->keyDown[wParam] = TRUE; // Set the selected key(wParam) to true return 0; } break; case WM_KEYUP: if ((wParam >= 0) && (wParam <= 255)) { window->keys->keyDown[wParam] = FALSE; return 0; } break; case WM_TOGGLEFULLSCREEN: g_createFullScreen = (g_createFullScreen == TRUE) ? FALSE : TRUE; PostMessage(hWnd, WM_QUIT, 0, 0); break; } return DefWindowProc(hWnd, uMsg, wParam, lParam); // Pass unhandle message to DefWindowProc } BOOL RegisterWindowClass(Application* application) { WNDCLASSEX windowClass; ZeroMemory(&windowClass, sizeof(WNDCLASSEX)); // Make sure memory is cleared windowClass.cbSize = sizeof(WNDCLASSEX); // Size of the windowClass structure windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraws the window for any movement / resizing windowClass.lpfnWndProc = (WNDPROC)(WindowProc); // WindowProc handles message windowClass.hInstance = application->hInstance; // Set the instance windowClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE);// Class background brush color windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); // Load the arrow pointer windowClass.lpszClassName = application->className; // Sets the application className if (RegisterClassEx(&windowClass) == 0) { MessageBox(HWND_DESKTOP, "RegisterClassEx Failed!", "Error", MB_OK | MB_ICONEXCLAMATION); return FALSE; } return TRUE; } int WINAPI WinMain(HINSTANCE hIstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { Application application; GL_Window window; Keys keys; BOOL isMessagePumpActive; MSG msg; DWORD tickCount; application.className = "OpenGL"; application.hInstance = hIstance; ZeroMemory(&window, sizeof(GL_Window)); window.keys = &keys; // Window key structure window.init.application = &application; // Window application window.init.title = "Resource File"; // Window title window.init.width = 640; // Window width window.init.height = 480; // Window height window.init.bitsPerPixel = 16; // Bits per pixel window.init.isFullScreen = TRUE; // Fullscreen? (set to TRUE) ZeroMemory(&keys, sizeof(Keys)); if (MessageBox(HWND_DESKTOP, "Would You Like To Run In Fullscreen Mode?", "Start FullScreen?", MB_YESNO | MB_ICONQUESTION) == IDNO) { window.init.isFullScreen = FALSE; } if (RegisterWindowClass(&application) == FALSE) { MessageBox(HWND_DESKTOP, "Error Registering Window Class!", "Error", MB_OK | MB_ICONEXCLAMATION); return -1; } g_isProgramLooping = TRUE; g_createFullScreen = window.init.isFullScreen; while (g_isProgramLooping) { // Loop until WM_QUIT is received window.init.isFullScreen = g_createFullScreen; // Set init param of window creation to fullscreen? if (CreateWindowGL(&window) == TRUE) { // Was window creation successful? // At this point we should have a window that is setup to render OpenGL if (Initialize(&window, &keys) == FALSE) { TerminateApplication(&window); // Close window, this will handle the shutdown } else { isMessagePumpActive = TRUE; while (isMessagePumpActive == TRUE) { // Success creating window. Check for window messages if (PeekMessage(&msg, window.hWnd, 0, 0, PM_REMOVE) != 0) { if (msg.message != WM_QUIT) { DispatchMessage(&msg); } else { isMessagePumpActive = FALSE; // Terminate the message pump } } else { if (window.isVisible == FALSE) { WaitMessage(); // Application is minimized wait for a message } else { // Process application loop tickCount = GetTickCount(); // Get the tick count Update(tickCount - window.lastTickCount); // Update the counter window.lastTickCount = tickCount;// Set last count to current count Draw(); // Draw screen SwapBuffers(window.hDC); } } } } // Application is finished Deinitialize(); DestoryWindowGL(&window); } else { MessageBox(HWND_DESKTOP, "Error Creating OpenGL Window", "Error", MB_OK | MB_ICONEXCLAMATION); g_isProgramLooping = FALSE; } } UnregisterClass(application.className, application.hInstance); // UnRegister window class return 0; }
#include <math.h> class Vector3D { public: float x, y, z; Vector3D(): x(0), y(0), z(0) {} Vector3D(float x, float y, float z) { this->x = x; this->y = y; this->z = z; } Vector3D& operator= (Vector3D v) { x = v.x; y = v.y; z = v.z; return *this; } Vector3D operator+ (Vector3D v) { return Vector3D(x + v.x, y + v.y, z + v.z); } Vector3D operator- (Vector3D v) { return Vector3D(x - v.x, y - v.y, z - v.z); } Vector3D operator* (float value) { return Vector3D(x * value, y * value, z * value); } Vector3D operator/ (float value) { return Vector3D(x / value, y / value, z / value); } Vector3D& operator+= (Vector3D v) { x += v.x; y += v.y; z += v.z; return *this; } Vector3D& operator-= (Vector3D v) { x -= v.x; y -= v.y; z -= v.z; return *this; } Vector3D& operator*= (Vector3D v) { x *= v.x; y *= v.y; z *= v.z; return *this; } Vector3D& operator/= (Vector3D v) { x /= v.x; y /= v.y; z /= v.z; return *this; } Vector3D operator- () { return Vector3D(-x, -y, -z); } float length() { return sqrtf(x*x + y*y + z*z); } void unitize() // Normalizes { float length = this->length(); if (length == 0) return; x /= length; y /= length; z /= length; } Vector3D unit() // Normalizes return a new Vector3D { float length = this->length(); if (length == 0) return *this; return Vector3D(x / length, y / length, z / length); } }; class Mass { public: float m; // The mass value Vector3D pos; // Position Vector3D vel; // Velocity Vector3D force; // Force Mass(float m): m(m) {} void applyForce(Vector3D force) { this->force += force; } void init() { force.x = 0; force.y = 0; force.z = 0; } void simulate(float dt) // New velocity and position { vel += (force / m) * dt; pos += vel * dt; } }; class Simulation { public: int numOfMasses; Mass** masses; Simulation(int numOfMasses, float m) // Constructor { this->numOfMasses = numOfMasses; masses = new Mass*[numOfMasses]; for (int i = 0; i < numOfMasses; ++i) { masses[i] = new Mass(m); } } virtual void release() // Delete { for (int i = 0; i < numOfMasses; ++i) { delete(masses[i]); masses[i] = NULL; } delete(masses); masses = NULL; } Mass* getMass(int index) { if (index < 0 || index >= numOfMasses) { return NULL; } return masses[index]; } virtual void init() { for (int i = 0; i < numOfMasses; ++i) { masses[i]->init(); } } // No implementation because no forces are wanted in this basic container // in advanced containers, this method will be overrided and some forces will act on masses virtual void solve() {} virtual void simulate(float dt) { for (int i = 0; i < numOfMasses; ++i) { masses[i]->simulate(dt); } } virtual void operate(float dt) // The complete produce of simulation { init(); solve(); simulate(dt); } }; class ConstantVelocity : public Simulation { public: ConstantVelocity() : Simulation(1, 1.0f) { masses[0]->pos = Vector3D(0.0f, 0.0f, 0.0f); masses[0]->vel = Vector3D(1.0f, 0.0f, 0.0f); } }; class MotionUnderGravitation : public Simulation { public: Vector3D gravitation; MotionUnderGravitation(Vector3D gravitation) : Simulation(1, 1.0f) { this->gravitation = gravitation; masses[0]->pos = Vector3D(-10.0f, 0.0f, 0.0f); masses[0]->vel = Vector3D(10.0f, 15.0f, 0.0f); } virtual void solve() { for (int i = 0; i < numOfMasses; ++i) { masses[i]->applyForce(gravitation * masses[i]->m); } } }; class MassConnectedWithSpring : public Simulation { public: float springConstant; // More the springConstant, stiffer the spring force Vector3D connectionPos; // The arbitrary constant point that the mass is connected 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 i = 0; i < numOfMasses; ++i) { Vector3D springVector = masses[i]->pos - connectionPos; masses[i]->applyForce(-springVector * springConstant); } } };
#include "Physics1.h" // 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) class Spring { public: Mass* mass1; Mass* mass2; float springConstant; // A constant to represent the stiffness of the spring float springLength; float frictionConstant; // Constructor Spring(Mass* mass1, Mass* mass2, float springConstant, float springLength, float frictionConstant) : mass1(mass1), mass2(mass2), springConstant(springConstant), springLength(springLength), frictionConstant(frictionConstant) {} 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); mass2->applyForce(-force); } }; class RopeSimulation : public Simulation { public: Spring** springs; Vector3D gravitation; Vector3D ropeConnectionPos; Vector3D ropeConnectionVel; // A constant to represent how much the ground shall repel the masses float groundRepulsionConstant; // A constant of friction applied to masses by the ground (sliding of rope) float groundFrictionConstant; // A constant of absorption friction alppiled to masses by the ground (vertical collisions of rope) float groundAbsorptionConstant; float groundHeight; float airFrictionConstant; // A constant of air friction-applied to masses 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 i = 0; i < numOfMasses; ++i) { masses[i]->pos.x = i * springLength; masses[i]->pos.y = 0; masses[i]->pos.z = 0; } springs = new Spring*[numOfMasses - 1]; for (int i = 0; i < numOfMasses - 1; ++i) { springs[i] = new Spring(masses[i], masses[i + 1], springConstant, springLength, springFrictionConstant); } } void release() { Simulation::release(); for (int i = 0; i < numOfMasses - 1; ++i) { delete(springs[i]); springs[i] = NULL; } delete(springs); springs = NULL; } void solve() { for (int i = 0; i < numOfMasses - 1; ++i) { springs[i]->solve(); } for (int i = 0; i < numOfMasses; ++i) { masses[i]->applyForce(gravitation * masses[i]->m); // Gravitation masses[i]->applyForce(-masses[i]->vel * airFrictionConstant); // Air Friction if (masses[i]->pos.y < groundHeight) { Vector3D v = masses[i]->vel; // 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. masses[i]->applyForce(-v * groundFrictionConstant); v = masses[i]->vel; v.x = 0; // Omit v.z = 0; if (v.y < 0) { // Absorb energy only when a mass collides towards the ground masses[i]->applyForce(-v * groundAbsorptionConstant); } // The ground shall repel a mass like a spring. Vector3D force = Vector3D(0, groundRepulsionConstant, 0) * (groundHeight - masses[i]->pos.y); masses[i]->applyForce(force); } } } void simulate(float dt) // Overriden { Simulation::simulate(dt); ropeConnectionPos += ropeConnectionVel * dt; // Iterate the positon of ropeConnectionPos 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; } };
#include <Windows.h> #include <GL/glew.h> #include <GL/glut.h> #include <GL/GLUAX.H> #include <math.h> #include <stdio.h> #include "Previous.h" #include "Physics2.h" #pragma comment(lib, "legacy_stdio_definitions.lib") #ifndef CDS_FULLSCREEN #define CDS_FULLSCREEN 4 #endif GL_Window* g_window; Keys* g_keys; RopeSimulation* ropeSimulation = new RopeSimulation( 80, // Particles 0.05f, // Each particle has a weight of 50 grams 1000.0f, // SpringConstant 0.05f, // Normal length of string 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 2.0f, // Ground absoption constant -1.5f); // Height of ground BOOL Initialize(GL_Window* window, Keys* keys) { g_window = window; g_keys = keys; ropeSimulation->getMass(ropeSimulation->numOfMasses - 1)->vel.z = 10.0f; glClearColor(0.0f, 0.0f, 0.0f, 0.5f); glClearDepth(1.0f); glShadeModel(GL_SMOOTH); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); return TRUE; } void Deinitialize(void) { ropeSimulation->release(); delete(ropeSimulation); ropeSimulation = NULL; } void Update(DWORD milliseconds) { if (g_keys->keyDown[VK_ESCAPE] == TRUE) TerminateApplication(g_window); if (g_keys->keyDown[VK_F1] == TRUE) ToggleFullscreen(g_window); Vector3D ropeConnectionVel; if (g_keys->keyDown[VK_RIGHT] == TRUE) ropeConnectionVel.x += 3.0f; if (g_keys->keyDown[VK_LEFT] == TRUE) ropeConnectionVel.x -= 3.0f; if (g_keys->keyDown[VK_UP] == TRUE) ropeConnectionVel.z -= 3.0f; if (g_keys->keyDown[VK_DOWN] == TRUE) ropeConnectionVel.z += 3.0f; if (g_keys->keyDown[VK_HOME] == TRUE) ropeConnectionVel.y += 3.0f; if (g_keys->keyDown[VK_END] == TRUE) ropeConnectionVel.y -= 3.0f; ropeSimulation->setRopeConnectionVel(ropeConnectionVel); float dt = milliseconds / 1000.0f; float maxPossible_dt = 0.002f; // Maximum possible dt is 0.002 seconds int numOfIteration = (int)(dt / maxPossible_dt) + 1; if (numOfIteration != 0) { dt /= numOfIteration; } for (int i = 0; i < numOfIteration; ++i) { ropeSimulation->operate(dt); } } void Draw(void) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, 0, 4, 0, 0, 0, 0, 1, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBegin(GL_QUADS); glColor3ub(0, 0, 255); glVertex3f(20, ropeSimulation->groundHeight, 20); glVertex3f(-20, ropeSimulation->groundHeight, 20); glColor3ub(0, 0, 0); glVertex3f(-20, ropeSimulation->groundHeight, -20); glVertex3f(20, ropeSimulation->groundHeight, -20); glEnd(); // Shadow of rope glColor3ub(0, 0, 0); for (int i = 0; i < ropeSimulation->numOfMasses - 1; ++i) { Mass* mass1 = ropeSimulation->getMass(i); Vector3D* pos1 = &mass1->pos; Mass* mass2 = ropeSimulation->getMass(i + 1); Vector3D* pos2 = &mass2->pos; glLineWidth(2); glBegin(GL_LINES); glVertex3f(pos1->x, ropeSimulation->groundHeight, pos1->z); glVertex3f(pos2->x, ropeSimulation->groundHeight, pos2->z); glEnd(); } glColor3ub(255, 255, 0); for (int i = 0; i < ropeSimulation->numOfMasses - 1; ++i) { Mass* mass1 = ropeSimulation->getMass(i); Vector3D* pos1 = &mass1->pos; Mass* mass2 = ropeSimulation->getMass(i + 1); Vector3D* pos2 = &mass2->pos; glLineWidth(4); glBegin(GL_LINES); glVertex3f(pos1->x, pos1->y, pos1->z); glVertex3f(pos2->x, pos2->y, pos2->z); glEnd(); } glFlush(); }
Thanks for Nehe's tutorials, this is his home.