├── bin └── win32 │ ├── blob.exe │ ├── glut.dll │ ├── bridge.exe │ ├── fracture.exe │ ├── glut32.dll │ ├── platform.exe │ ├── ragdoll.exe │ ├── sailboat.exe │ ├── ballistic.exe │ ├── fireworks.exe │ └── bigballistic.exe ├── lib └── win32 │ ├── glut.lib │ ├── cyclone.lib │ └── glut32.lib ├── src ├── demos │ ├── ogl_headers.h │ ├── timing.h │ ├── main.cpp │ ├── timing.cpp │ ├── sailboat │ │ └── sailboat.cpp │ ├── platform │ │ └── platform.cpp │ ├── app.h │ ├── bridge │ │ └── bridge.cpp │ ├── ballistic │ │ └── ballistic.cpp │ ├── app.cpp │ ├── flightsim │ │ └── flightsim.cpp │ └── ragdoll │ │ └── ragdoll.cpp ├── joints.cpp ├── collide_coarse.cpp ├── world.cpp ├── core.cpp ├── pworld.cpp ├── particle.cpp ├── plinks.cpp ├── random.cpp ├── fgen.cpp ├── pcontacts.cpp └── pfgen.cpp ├── doc ├── src │ ├── footer.html │ ├── header.html │ └── stylesheet.css └── build │ └── doxygen ├── .gitignore ├── include └── cyclone │ ├── cyclone.h │ ├── joints.h │ ├── precision.h │ ├── world.h │ ├── random.h │ ├── pworld.h │ ├── plinks.h │ ├── pcontacts.h │ ├── pfgen.h │ ├── collide_fine.h │ └── particle.h ├── linuxmake.mk ├── LICENSE └── Makefile /bin/win32/blob.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/bin/win32/blob.exe -------------------------------------------------------------------------------- /bin/win32/glut.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/bin/win32/glut.dll -------------------------------------------------------------------------------- /lib/win32/glut.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/lib/win32/glut.lib -------------------------------------------------------------------------------- /bin/win32/bridge.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/bin/win32/bridge.exe -------------------------------------------------------------------------------- /bin/win32/fracture.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/bin/win32/fracture.exe -------------------------------------------------------------------------------- /bin/win32/glut32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/bin/win32/glut32.dll -------------------------------------------------------------------------------- /bin/win32/platform.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/bin/win32/platform.exe -------------------------------------------------------------------------------- /bin/win32/ragdoll.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/bin/win32/ragdoll.exe -------------------------------------------------------------------------------- /bin/win32/sailboat.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/bin/win32/sailboat.exe -------------------------------------------------------------------------------- /lib/win32/cyclone.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/lib/win32/cyclone.lib -------------------------------------------------------------------------------- /lib/win32/glut32.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/lib/win32/glut32.lib -------------------------------------------------------------------------------- /bin/win32/ballistic.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/bin/win32/ballistic.exe -------------------------------------------------------------------------------- /bin/win32/fireworks.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/bin/win32/fireworks.exe -------------------------------------------------------------------------------- /bin/win32/bigballistic.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idmillington/cyclone-physics/HEAD/bin/win32/bigballistic.exe -------------------------------------------------------------------------------- /src/demos/ogl_headers.h: -------------------------------------------------------------------------------- 1 | #ifndef _OPENGL_HEADERS_ 2 | #define _OPENGL_HEADERS_ 3 | 4 | #ifdef __gnu_linux__ 5 | #include 6 | #else 7 | #include 8 | #endif 9 | 10 | #endif // _OPENGL_HEADERS_ -------------------------------------------------------------------------------- /doc/src/footer.html: -------------------------------------------------------------------------------- 1 | 2 |

3 | 9 |

10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Linux Object Files 3 | src/*.o 4 | lib/linux/* 5 | bin/linux/* 6 | 7 | 8 | # Prebuilt OSX Executables 9 | lib/osx64/* 10 | bin/osx64/* 11 | 12 | 13 | # Xcode Specific Files 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | *.xcworkspace 23 | !default.xcworkspace 24 | xcuserdata 25 | *.moved-aside 26 | -------------------------------------------------------------------------------- /doc/src/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | $projectname > $title 7 | 8 | 9 | 10 | 11 | 12 |
$projectname$title
13 | 14 | -------------------------------------------------------------------------------- /include/cyclone/cyclone.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Global import header. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | #include "precision.h" 13 | #include "core.h" 14 | #include "random.h" 15 | #include "particle.h" 16 | #include "body.h" 17 | #include "pcontacts.h" 18 | #include "pworld.h" 19 | #include "collide_fine.h" 20 | #include "contacts.h" 21 | #include "fgen.h" 22 | #include "joints.h" -------------------------------------------------------------------------------- /linuxmake.mk: -------------------------------------------------------------------------------- 1 | # New makefile to build the Cyclone physics engine for Linux. 2 | 3 | # Determine architecture (Linux or Mac OS X). 4 | PLATFORM = $(shell uname) 5 | 6 | ifeq ($(PLATFORM), Linux) 7 | LDFLAGS = -lGL -lGLU -lglut 8 | else 9 | $(error This OS is not Ubuntu Linux. Aborting) 10 | endif 11 | 12 | # Demo files path. 13 | DEMOPATH = ./src/demos/ 14 | 15 | # Demo core files. 16 | DEMOCOREFILES = $(DEMOPATH)main.cpp $(DEMOPATH)app.cpp $(DEMOPATH)timing.cpp 17 | 18 | # Demo files. 19 | DEMOLIST = ballistic bigballistic blob bridge explosion fireworks flightsim fracture platform ragdoll sailboat 20 | 21 | # Cyclone core files. 22 | CYCLONEFILES = ./src/body.cpp ./src/collide_coarse.cpp ./src/collide_fine.cpp ./src/contacts.cpp ./src/core.cpp ./src/fgen.cpp ./src/joints.cpp ./src/particle.cpp ./src/pcontacts.cpp ./src/pfgen.cpp ./src/plinks.cpp ./src/pworld.cpp ./src/random.cpp ./src/world.cpp 23 | 24 | .PHONY: clean 25 | 26 | all: $(DEMOLIST) 27 | 28 | $(DEMOLIST): 29 | g++ -O2 -Iinclude $(DEMOCOREFILES) $(CYCLONEFILES) $(DEMOPATH)$@/$@.cpp -o $@ $(LDFLAGS) 30 | 31 | clean: 32 | rm $(DEMOLIST) 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2003-2009 Ian Millington 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # OS X 2 | ARCH = $(shell uname) 3 | ifeq ($(ARCH),Darwin) 4 | LDFLAGS = -framework GLUT -framework OpenGL -framework Cocoa 5 | else 6 | $(error This OS is not Mac OSX. Aborting. Please run linuxmake.mk) 7 | endif 8 | 9 | mkdir=mkdir -p 10 | rm=rm -f 11 | AR=ar cq 12 | RANLIB=ranlib 13 | 14 | 15 | # CYCLONEPHYSICS LIB 16 | CXXFLAGS=-O2 -Iinclude -fPIC 17 | CYCLONEOBJS=src/body.o src/collide_coarse.o src/collide_fine.o src/contacts.o src/core.o src/fgen.o src/joints.o src/particle.o src/pcontacts.o src/pfgen.o src/plinks.o src/pworld.o src/random.o src/world.o 18 | 19 | 20 | # DEMO FILES 21 | 22 | LIBNAME=libcyclone.a 23 | CYCLONELIB=./lib/linux/$(LIBNAME) 24 | 25 | DEMO_CPP=./src/demos/app.cpp ./src/demos/timing.cpp ./src/demos/main.cpp 26 | 27 | DEMOS=ballistic bigballistic blob bridge explosion fireworks flightsim fracture platform ragdoll sailboat 28 | 29 | 30 | # OUTPUT DIRECTORIES 31 | 32 | OUTDIRS=./lib/linux ./bin/linux 33 | 34 | 35 | 36 | # BUILD COMMANDS 37 | 38 | all: out_dirs $(CYCLONELIB) $(DEMOS) 39 | 40 | 41 | out_dirs: 42 | $(mkdir) $(OUTDIRS) 43 | 44 | 45 | $(CYCLONELIB): $(CYCLONEOBJS) 46 | $(rm) $@ 47 | $(AR) $@ $(CYCLONEOBJS) 48 | $(RANLIB) $@ 49 | 50 | 51 | $(DEMOS): 52 | $(CXX) $(CXXFLAGS) -o ./bin/linux/$@ $(DEMO_CPP) $(CYCLONELIB) ./src/demos/$@/$@.cpp $(LDFLAGS) 53 | 54 | 55 | clean: 56 | $(rm) src/*.o lib/linux/libcyclone.a 57 | $(rm) \ 58 | ./bin/linux/fireworks \ 59 | ./bin/linux/fracture \ 60 | ./bin/linux/flightsim \ 61 | ./bin/linux/bridge \ 62 | ./bin/linux/sailboat \ 63 | ./bin/linux/explosion \ 64 | ./bin/linux/ballistic \ 65 | ./bin/linux/platform \ 66 | ./bin/linux/bigballistic \ 67 | ./bin/linux/blob \ 68 | ./bin/linux/ragdoll 69 | -------------------------------------------------------------------------------- /src/joints.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Implementation file for joints. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | 15 | using namespace cyclone; 16 | 17 | unsigned Joint::addContact(Contact *contact, unsigned limit) const 18 | { 19 | // Calculate the position of each connection point in world coordinates 20 | Vector3 a_pos_world = body[0]->getPointInWorldSpace(position[0]); 21 | Vector3 b_pos_world = body[1]->getPointInWorldSpace(position[1]); 22 | 23 | // Calculate the length of the joint 24 | Vector3 a_to_b = b_pos_world - a_pos_world; 25 | Vector3 normal = a_to_b; 26 | normal.normalise(); 27 | real length = a_to_b.magnitude(); 28 | 29 | // Check if it is violated 30 | if (real_abs(length) > error) 31 | { 32 | contact->body[0] = body[0]; 33 | contact->body[1] = body[1]; 34 | contact->contactNormal = normal; 35 | contact->contactPoint = (a_pos_world + b_pos_world) * 0.5f; 36 | contact->penetration = length-error; 37 | contact->friction = 1.0f; 38 | contact->restitution = 0; 39 | return 1; 40 | } 41 | 42 | return 0; 43 | } 44 | 45 | void Joint::set(RigidBody *a, const Vector3& a_pos, 46 | RigidBody *b, const Vector3& b_pos, 47 | real error) 48 | { 49 | body[0] = a; 50 | body[1] = b; 51 | 52 | position[0] = a_pos; 53 | position[1] = b_pos; 54 | 55 | Joint::error = error; 56 | } -------------------------------------------------------------------------------- /src/collide_coarse.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Implementation file for the coarse collision detector. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | 15 | using namespace cyclone; 16 | 17 | BoundingSphere::BoundingSphere(const Vector3 ¢re, real radius) 18 | { 19 | BoundingSphere::centre = centre; 20 | BoundingSphere::radius = radius; 21 | } 22 | 23 | BoundingSphere::BoundingSphere(const BoundingSphere &one, 24 | const BoundingSphere &two) 25 | { 26 | Vector3 centreOffset = two.centre - one.centre; 27 | real distance = centreOffset.squareMagnitude(); 28 | real radiusDiff = two.radius - one.radius; 29 | 30 | // Check if the larger sphere encloses the small one 31 | if (radiusDiff*radiusDiff >= distance) 32 | { 33 | if (one.radius > two.radius) 34 | { 35 | centre = one.centre; 36 | radius = one.radius; 37 | } 38 | else 39 | { 40 | centre = two.centre; 41 | radius = two.radius; 42 | } 43 | } 44 | 45 | // Otherwise we need to work with partially 46 | // overlapping spheres 47 | else 48 | { 49 | distance = real_sqrt(distance); 50 | radius = (distance + one.radius + two.radius) * ((real)0.5); 51 | 52 | // The new centre is based on one's centre, moved towards 53 | // two's centre by an ammount proportional to the spheres' 54 | // radii. 55 | centre = one.centre; 56 | if (distance > 0) 57 | { 58 | centre += centreOffset * ((radius - one.radius)/distance); 59 | } 60 | } 61 | 62 | } 63 | 64 | bool BoundingSphere::overlaps(const BoundingSphere *other) const 65 | { 66 | real distanceSquared = (centre - other->centre).squareMagnitude(); 67 | return distanceSquared < (radius+other->radius)*(radius+other->radius); 68 | } 69 | 70 | real BoundingSphere::getGrowth(const BoundingSphere &other) const 71 | { 72 | BoundingSphere newSphere(*this, other); 73 | 74 | // We return a value proportional to the change in surface 75 | // area of the sphere. 76 | return newSphere.radius*newSphere.radius - radius*radius; 77 | } -------------------------------------------------------------------------------- /include/cyclone/joints.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface file for joints between rigid bodies. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | /** 14 | * @file 15 | * 16 | * This file contains the definitions for joints that link together 17 | * different rigid bodies. 18 | */ 19 | #ifndef CYCLONE_JOINTS_H 20 | #define CYCLONE_JOINTS_H 21 | 22 | #include "contacts.h" 23 | 24 | namespace cyclone { 25 | 26 | /** 27 | * Joints link together two rigid bodies and make sure they do not 28 | * separate. In a general phyiscs engine there may be many 29 | * different types of joint, that reduce the number of relative 30 | * degrees of freedom between two objects. This joint is a common 31 | * position joint: each object has a location (given in 32 | * body-coordinates) that will be kept at the same point in the 33 | * simulation. 34 | */ 35 | class Joint : public ContactGenerator 36 | { 37 | public: 38 | /** 39 | * Holds the two rigid bodies that are connected by this joint. 40 | */ 41 | RigidBody* body[2]; 42 | 43 | /** 44 | * Holds the relative location of the connection for each 45 | * body, given in local coordinates. 46 | */ 47 | Vector3 position[2]; 48 | 49 | /** 50 | * Holds the maximum displacement at the joint before the 51 | * joint is considered to be violated. This is normally a 52 | * small, epsilon value. It can be larger, however, in which 53 | * case the joint will behave as if an inelastic cable joined 54 | * the bodies at their joint locations. 55 | */ 56 | real error; 57 | 58 | /** 59 | * Configures the joint in one go. 60 | */ 61 | void set( 62 | RigidBody *a, const Vector3& a_pos, 63 | RigidBody *b, const Vector3& b_pos, 64 | real error 65 | ); 66 | 67 | /** 68 | * Generates the contacts required to restore the joint if it 69 | * has been violated. 70 | */ 71 | unsigned addContact(Contact *contact, unsigned limit) const; 72 | }; 73 | 74 | } // namespace cyclone 75 | 76 | #endif // CYCLONE_JOINTS_H -------------------------------------------------------------------------------- /src/world.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Implementation file for random number generation. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | using namespace cyclone; 17 | 18 | World::World(unsigned maxContacts, unsigned iterations) 19 | : 20 | firstBody(NULL), 21 | resolver(iterations), 22 | firstContactGen(NULL), 23 | maxContacts(maxContacts) 24 | { 25 | contacts = new Contact[maxContacts]; 26 | calculateIterations = (iterations == 0); 27 | } 28 | 29 | World::~World() 30 | { 31 | delete[] contacts; 32 | } 33 | 34 | void World::startFrame() 35 | { 36 | BodyRegistration *reg = firstBody; 37 | while (reg) 38 | { 39 | // Remove all forces from the accumulator 40 | reg->body->clearAccumulators(); 41 | reg->body->calculateDerivedData(); 42 | 43 | // Get the next registration 44 | reg = reg->next; 45 | } 46 | } 47 | 48 | unsigned World::generateContacts() 49 | { 50 | unsigned limit = maxContacts; 51 | Contact *nextContact = contacts; 52 | 53 | ContactGenRegistration * reg = firstContactGen; 54 | while (reg) 55 | { 56 | unsigned used = reg->gen->addContact(nextContact, limit); 57 | limit -= used; 58 | nextContact += used; 59 | 60 | // We've run out of contacts to fill. This means we're missing 61 | // contacts. 62 | if (limit <= 0) break; 63 | 64 | reg = reg->next; 65 | } 66 | 67 | // Return the number of contacts used. 68 | return maxContacts - limit; 69 | } 70 | 71 | void World::runPhysics(real duration) 72 | { 73 | // First apply the force generators 74 | //registry.updateForces(duration); 75 | 76 | // Then integrate the objects 77 | BodyRegistration *reg = firstBody; 78 | while (reg) 79 | { 80 | // Remove all forces from the accumulator 81 | reg->body->integrate(duration); 82 | 83 | // Get the next registration 84 | reg = reg->next; 85 | } 86 | 87 | // Generate contacts 88 | unsigned usedContacts = generateContacts(); 89 | 90 | // And process them 91 | if (calculateIterations) resolver.setIterations(usedContacts * 4); 92 | resolver.resolveContacts(contacts, usedContacts, duration); 93 | } 94 | -------------------------------------------------------------------------------- /include/cyclone/precision.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface file for code that changes when the core's precision is 3 | * altered. 4 | * 5 | * Part of the Cyclone physics system. 6 | * 7 | * Copyright (c) Icosagon 2003. All Rights Reserved. 8 | * 9 | * This software is distributed under licence. Use of this software 10 | * implies agreement with all terms and conditions of the accompanying 11 | * software licence. 12 | */ 13 | 14 | /** 15 | * @file 16 | * 17 | * Because Cyclone is designed to work at either single or double 18 | * precision, mathematical functions such as sqrt cannot be used 19 | * in the source code or headers. This file provides defines for 20 | * the real number type and mathematical formulae that work on it. 21 | * 22 | * @note All the contents of this file need to be changed to compile 23 | * Cyclone at a different precision. 24 | */ 25 | #ifndef CYCLONE_PRECISION_H 26 | #define CYCLONE_PRECISION_H 27 | 28 | #include 29 | 30 | namespace cyclone { 31 | 32 | #if 0 33 | /** 34 | * Defines we're in single precision mode, for any code 35 | * that needs to be conditionally compiled. 36 | */ 37 | #define SINGLE_PRECISION 38 | 39 | /** 40 | * Defines a real number precision. Cyclone can be compiled in 41 | * single or double precision versions. By default single precision is 42 | * provided. 43 | */ 44 | typedef float real; 45 | 46 | /** Defines the highest value for the real number. */ 47 | #define REAL_MAX FLT_MAX 48 | 49 | /** Defines the precision of the square root operator. */ 50 | #define real_sqrt sqrtf 51 | /** Defines the precision of the absolute magnitude operator. */ 52 | #define real_abs fabsf 53 | /** Defines the precision of the sine operator. */ 54 | #define real_sin sinf 55 | 56 | /** Defines the precision of the cosine operator. */ 57 | #define real_cos cosf 58 | 59 | /** Defines the precision of the exponent operator. */ 60 | #define real_exp expf 61 | /** Defines the precision of the power operator. */ 62 | #define real_pow powf 63 | 64 | /** Defines the precision of the floating point modulo operator. */ 65 | #define real_fmod fmodf 66 | 67 | /** Defines the number e on which 1+e == 1 **/ 68 | #define real_epsilon FLT_EPSILON 69 | 70 | #define R_PI 3.14159f 71 | #else 72 | #define DOUBLE_PRECISION 73 | typedef double real; 74 | #define REAL_MAX DBL_MAX 75 | #define real_sqrt sqrt 76 | #define real_abs fabs 77 | #define real_sin sin 78 | #define real_cos cos 79 | #define real_exp exp 80 | #define real_pow pow 81 | #define real_fmod fmod 82 | #define real_epsilon DBL_EPSILON 83 | #define R_PI 3.14159265358979 84 | #endif 85 | } 86 | 87 | #endif // CYCLONE_PRECISION_H 88 | -------------------------------------------------------------------------------- /doc/src/stylesheet.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: black; 3 | font-family: Arial, Helvetica, sans-serif; 4 | } 5 | 6 | h1 { 7 | color: #666666; 8 | text-align: center; 9 | } 10 | 11 | h2 { 12 | color: #336699; 13 | } 14 | 15 | h3 { 16 | color: #990000; 17 | } 18 | 19 | h4 { 20 | color: #336699; 21 | } 22 | 23 | h5 { 24 | color: #336699; 25 | } 26 | 27 | p { 28 | line-height: 130% 29 | } 30 | 31 | ul { 32 | line-height: 130% 33 | } 34 | 35 | dl { 36 | line-height: 130% 37 | } 38 | 39 | dt { 40 | font-weight: bold; 41 | margin-top: 8px 42 | } 43 | 44 | /* 45 | * Link styles. 46 | */ 47 | a:visited { 48 | color: #000099 49 | } 50 | 51 | a:link { 52 | color: #990000 53 | } 54 | 55 | a:active { 56 | color: #999999 57 | } 58 | 59 | /* 60 | * Divisions. 61 | * These tend to be generated by doxygen or docbook. 62 | */ 63 | div.navheader, div.navfooter { 64 | font-size: x-small; 65 | } 66 | 67 | div.note { 68 | border: 1px solid #336699; 69 | padding-right: 8px; 70 | border-left: 8px solid #336699; 71 | background-color: #fff9ee; 72 | } 73 | 74 | div.figure { 75 | font-family: small; 76 | color: #990000; 77 | margin-left: 48px; 78 | margin-right: 64px; 79 | border: 1px solid #336699; 80 | padding-right: 8px; 81 | border-left: 8px solid #336699; 82 | background-color: #fff9ee; 83 | } 84 | 85 | div.groupHeader { 86 | font-weight: bold; 87 | font-size: 110%; 88 | padding: 12px 0px 8px 0px; 89 | } 90 | 91 | /* 92 | * Navigation header and footer on all documentation pages. 93 | */ 94 | table.header { 95 | border: 1px solid #336699; 96 | border-left: 8px solid #336699; 97 | background-color: #cccccc; 98 | font-size: 80%; 99 | } 100 | 101 | table.footer { 102 | border-top: 22px solid #cccccc; 103 | background-color: white; 104 | font-size: 75%; 105 | } 106 | 107 | /* 108 | * Classes generated by doxygen and docbook. 109 | */ 110 | .small { 111 | font-size: x-small; 112 | } 113 | .example { 114 | margin-left: 48px; 115 | } 116 | 117 | .qindex { 118 | font-size: xx-small; 119 | } 120 | 121 | .md { 122 | font-weight: bold; 123 | } 124 | 125 | span.keyword { color: #008000 } 126 | span.keywordtype { color: #604020 } 127 | span.keywordflow { color: #e08000 } 128 | span.comment { color: #800000 } 129 | span.preprocessor { color: #806020 } 130 | span.stringliteral { color: #002080 } 131 | span.charliteral { color: #008080 } 132 | 133 | -------------------------------------------------------------------------------- /src/demos/timing.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Timing functions. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Ian Millington 2003-2006. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | /** 14 | * @file 15 | * 16 | * Holds the timing system for the physics demos. 17 | */ 18 | #ifndef CYCLONE_DEMO_TIMING_H 19 | #define CYCLONE_DEMO_TIMING_H 20 | 21 | /** 22 | * Represents all the information that the demo might need about the 23 | * timing of the game: current time, fps, frame number, and so on. 24 | */ 25 | struct TimingData 26 | { 27 | /** The current render frame. This simply increments. */ 28 | unsigned frameNumber; 29 | 30 | /** 31 | * The timestamp when the last frame ended. Times are 32 | * given in milliseconds since some undefined time. 33 | */ 34 | unsigned lastFrameTimestamp; 35 | 36 | /** 37 | * The duration of the last frame in milliseconds. 38 | */ 39 | unsigned lastFrameDuration; 40 | 41 | /** 42 | * The clockstamp of the end of the last frame. 43 | */ 44 | unsigned long lastFrameClockstamp; 45 | 46 | /** 47 | * The duration of the last frame in clock ticks. 48 | */ 49 | unsigned long lastFrameClockTicks; 50 | 51 | /** 52 | * Keeps track of whether the rendering is paused. 53 | */ 54 | bool isPaused; 55 | 56 | // Calculated data 57 | 58 | /** 59 | * This is a recency weighted average of the frame time, calculated 60 | * from frame durations. 61 | */ 62 | double averageFrameDuration; 63 | 64 | /** 65 | * The reciprocal of the average frame duration giving the mean 66 | * fps over a recency weighted average. 67 | */ 68 | float fps; 69 | 70 | /** 71 | * Gets the global timing data object. 72 | */ 73 | static TimingData& get(); 74 | 75 | /** 76 | * Updates the timing system, should be called once per frame. 77 | */ 78 | static void update(); 79 | 80 | /** 81 | * Initialises the frame information system. Use the overall 82 | * init function to set up all modules. 83 | */ 84 | static void init(); 85 | 86 | /** 87 | * Deinitialises the frame information system. 88 | */ 89 | static void deinit(); 90 | 91 | /** 92 | * Gets the global system time, in the best resolution possible. 93 | * Timing is in milliseconds. 94 | */ 95 | static unsigned getTime(); 96 | 97 | /** 98 | * Gets the clock ticks since process start. 99 | */ 100 | static unsigned long getClock(); 101 | 102 | 103 | private: 104 | // These are private to stop instances being created: use get(). 105 | TimingData() {} 106 | TimingData(const TimingData &) {} 107 | TimingData& operator=(const TimingData &); 108 | }; 109 | 110 | 111 | #endif // CYCLONE_DEMO_TIMING_H 112 | 113 | 114 | -------------------------------------------------------------------------------- /src/demos/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The main entry point for all demos. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | // Include appropriate OpenGL headers. 14 | #include "ogl_headers.h" 15 | 16 | // Include the general application structure. 17 | #include "app.h" 18 | 19 | // Include the timing functions 20 | #include "timing.h" 21 | 22 | // Forward declaration of the function that will return the 23 | // application object for this particular demo. This should be 24 | // implemented in the demo's .cpp file. 25 | extern Application* getApplication(); 26 | 27 | // Store the global application object. 28 | Application* app; 29 | 30 | /** 31 | * Creates a window in which to display the scene. 32 | */ 33 | void createWindow(const char* title) 34 | { 35 | glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); 36 | glutInitWindowSize(640,320); 37 | glutInitWindowPosition(0,0); 38 | glutCreateWindow(title); 39 | } 40 | 41 | /** 42 | * Called each frame to update the 3D scene. Delegates to 43 | * the application. 44 | */ 45 | void update() 46 | { 47 | // Update the timing. 48 | TimingData::get().update(); 49 | 50 | // Delegate to the application. 51 | app->update(); 52 | } 53 | 54 | /** 55 | * Called each frame to display the 3D scene. Delegates to 56 | * the application. 57 | */ 58 | void display() 59 | { 60 | app->display(); 61 | 62 | // Update the displayed content. 63 | glFlush(); 64 | glutSwapBuffers(); 65 | } 66 | 67 | /** 68 | * Called when a mouse button is pressed. Delegates to the 69 | * application. 70 | */ 71 | void mouse(int button, int state, int x, int y) 72 | { 73 | app->mouse(button, state, x, y); 74 | } 75 | 76 | /** 77 | * Called when the display window changes size. 78 | */ 79 | void reshape(int width, int height) 80 | { 81 | app->resize(width, height); 82 | } 83 | 84 | /** 85 | * Called when a key is pressed. 86 | */ 87 | void keyboard(unsigned char key, int x, int y) 88 | { 89 | // Note we omit passing on the x and y: they are rarely needed. 90 | app->key(key); 91 | } 92 | 93 | /** 94 | * Called when the mouse is dragged. 95 | */ 96 | void motion(int x, int y) 97 | { 98 | app->mouseDrag(x, y); 99 | } 100 | 101 | /** 102 | * The main entry point. We pass arguments onto GLUT. 103 | */ 104 | int main(int argc, char** argv) 105 | { 106 | // Set up GLUT and the timers 107 | glutInit(&argc, argv); 108 | TimingData::init(); 109 | 110 | // Create the application and its window 111 | app = getApplication(); 112 | createWindow(app->getTitle()); 113 | 114 | // Set up the appropriate handler functions 115 | glutReshapeFunc(reshape); 116 | glutKeyboardFunc(keyboard); 117 | glutDisplayFunc(display); 118 | glutIdleFunc(update); 119 | glutMouseFunc(mouse); 120 | glutMotionFunc(motion); 121 | 122 | // Run the application 123 | app->initGraphics(); 124 | glutMainLoop(); 125 | 126 | // Clean up the application 127 | app->deinit(); 128 | delete app; 129 | TimingData::deinit(); 130 | } 131 | -------------------------------------------------------------------------------- /src/core.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Implementation file for core functions in the library. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | 15 | using namespace cyclone; 16 | 17 | const Vector3 Vector3::GRAVITY = Vector3(0, -9.81, 0); 18 | const Vector3 Vector3::HIGH_GRAVITY = Vector3(0, -19.62, 0); 19 | const Vector3 Vector3::UP = Vector3(0, 1, 0); 20 | const Vector3 Vector3::RIGHT = Vector3(1, 0, 0); 21 | const Vector3 Vector3::OUT_OF_SCREEN = Vector3(0, 0, 1); 22 | const Vector3 Vector3::X = Vector3(0, 1, 0); 23 | const Vector3 Vector3::Y = Vector3(1, 0, 0); 24 | const Vector3 Vector3::Z = Vector3(0, 0, 1); 25 | 26 | /* 27 | * Definition of the sleep epsilon extern. 28 | */ 29 | real cyclone::sleepEpsilon = ((real)0.3); 30 | 31 | /* 32 | * Functions to change sleepEpsilon. 33 | */ 34 | void cyclone::setSleepEpsilon(real value) 35 | { 36 | cyclone::sleepEpsilon = value; 37 | } 38 | 39 | real cyclone::getSleepEpsilon() 40 | { 41 | return cyclone::sleepEpsilon; 42 | } 43 | 44 | real Matrix4::getDeterminant() const 45 | { 46 | return -data[8]*data[5]*data[2]+ 47 | data[4]*data[9]*data[2]+ 48 | data[8]*data[1]*data[6]- 49 | data[0]*data[9]*data[6]- 50 | data[4]*data[1]*data[10]+ 51 | data[0]*data[5]*data[10]; 52 | } 53 | 54 | void Matrix4::setInverse(const Matrix4 &m) 55 | { 56 | // Make sure the determinant is non-zero. 57 | real det = getDeterminant(); 58 | if (det == 0) return; 59 | det = ((real)1.0)/det; 60 | 61 | data[0] = (-m.data[9]*m.data[6]+m.data[5]*m.data[10])*det; 62 | data[4] = (m.data[8]*m.data[6]-m.data[4]*m.data[10])*det; 63 | data[8] = (-m.data[8]*m.data[5]+m.data[4]*m.data[9])*det; 64 | 65 | data[1] = (m.data[9]*m.data[2]-m.data[1]*m.data[10])*det; 66 | data[5] = (-m.data[8]*m.data[2]+m.data[0]*m.data[10])*det; 67 | data[9] = (m.data[8]*m.data[1]-m.data[0]*m.data[9])*det; 68 | 69 | data[2] = (-m.data[5]*m.data[2]+m.data[1]*m.data[6])*det; 70 | data[6] = (+m.data[4]*m.data[2]-m.data[0]*m.data[6])*det; 71 | data[10] = (-m.data[4]*m.data[1]+m.data[0]*m.data[5])*det; 72 | 73 | data[3] = (m.data[9]*m.data[6]*m.data[3] 74 | -m.data[5]*m.data[10]*m.data[3] 75 | -m.data[9]*m.data[2]*m.data[7] 76 | +m.data[1]*m.data[10]*m.data[7] 77 | +m.data[5]*m.data[2]*m.data[11] 78 | -m.data[1]*m.data[6]*m.data[11])*det; 79 | data[7] = (-m.data[8]*m.data[6]*m.data[3] 80 | +m.data[4]*m.data[10]*m.data[3] 81 | +m.data[8]*m.data[2]*m.data[7] 82 | -m.data[0]*m.data[10]*m.data[7] 83 | -m.data[4]*m.data[2]*m.data[11] 84 | +m.data[0]*m.data[6]*m.data[11])*det; 85 | data[11] =(m.data[8]*m.data[5]*m.data[3] 86 | -m.data[4]*m.data[9]*m.data[3] 87 | -m.data[8]*m.data[1]*m.data[7] 88 | +m.data[0]*m.data[9]*m.data[7] 89 | +m.data[4]*m.data[1]*m.data[11] 90 | -m.data[0]*m.data[5]*m.data[11])*det; 91 | } 92 | 93 | Matrix3 Matrix3::linearInterpolate(const Matrix3& a, const Matrix3& b, real prop) 94 | { 95 | Matrix3 result; 96 | for (unsigned i = 0; i < 9; i++) { 97 | result.data[i] = a.data[i] * (1-prop) + b.data[i] * prop; 98 | } 99 | return result; 100 | } 101 | -------------------------------------------------------------------------------- /include/cyclone/world.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface file for the rigid body world structure. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | /** 14 | * @file 15 | * 16 | * This file contains the definitions for a structure to hold any 17 | * number of rigid bodies, and to manage their simulation. 18 | */ 19 | #ifndef CYCLONE_WORLD_H 20 | #define CYCLONE_WORLD_H 21 | 22 | #include "body.h" 23 | #include "contacts.h" 24 | 25 | namespace cyclone { 26 | /** 27 | * The world represents an independent simulation of physics. It 28 | * keeps track of a set of rigid bodies, and provides the means to 29 | * update them all. 30 | */ 31 | class World 32 | { 33 | // ... other World data as before ... 34 | /** 35 | * True if the world should calculate the number of iterations 36 | * to give the contact resolver at each frame. 37 | */ 38 | bool calculateIterations; 39 | 40 | /** 41 | * Holds a single rigid body in a linked list of bodies. 42 | */ 43 | struct BodyRegistration 44 | { 45 | RigidBody *body; 46 | BodyRegistration * next; 47 | }; 48 | 49 | /** 50 | * Holds the head of the list of registered bodies. 51 | */ 52 | BodyRegistration *firstBody; 53 | 54 | /** 55 | * Holds the resolver for sets of contacts. 56 | */ 57 | ContactResolver resolver; 58 | 59 | /** 60 | * Holds one contact generators in a linked list. 61 | */ 62 | struct ContactGenRegistration 63 | { 64 | ContactGenerator *gen; 65 | ContactGenRegistration *next; 66 | }; 67 | 68 | /** 69 | * Holds the head of the list of contact generators. 70 | */ 71 | ContactGenRegistration *firstContactGen; 72 | 73 | /** 74 | * Holds an array of contacts, for filling by the contact 75 | * generators. 76 | */ 77 | Contact *contacts; 78 | 79 | /** 80 | * Holds the maximum number of contacts allowed (i.e. the size 81 | * of the contacts array). 82 | */ 83 | unsigned maxContacts; 84 | 85 | public: 86 | /** 87 | * Creates a new simulator that can handle up to the given 88 | * number of contacts per frame. You can also optionally give 89 | * a number of contact-resolution iterations to use. If you 90 | * don't give a number of iterations, then four times the 91 | * number of detected contacts will be used for each frame. 92 | */ 93 | World(unsigned maxContacts, unsigned iterations=0); 94 | ~World(); 95 | 96 | /** 97 | * Calls each of the registered contact generators to report 98 | * their contacts. Returns the number of generated contacts. 99 | */ 100 | unsigned generateContacts(); 101 | 102 | /** 103 | * Processes all the physics for the world. 104 | */ 105 | void runPhysics(real duration); 106 | 107 | /** 108 | * Initialises the world for a simulation frame. This clears 109 | * the force and torque accumulators for bodies in the 110 | * world. After calling this, the bodies can have their forces 111 | * and torques for this frame added. 112 | */ 113 | void startFrame(); 114 | 115 | }; 116 | 117 | } // namespace cyclone 118 | 119 | #endif // CYCLONE_PWORLD_H 120 | -------------------------------------------------------------------------------- /src/pworld.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Implementation file for random number generation. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | using namespace cyclone; 17 | 18 | ParticleWorld::ParticleWorld(unsigned maxContacts, unsigned iterations) 19 | : 20 | resolver(iterations), 21 | maxContacts(maxContacts) 22 | { 23 | contacts = new ParticleContact[maxContacts]; 24 | calculateIterations = (iterations == 0); 25 | 26 | } 27 | 28 | ParticleWorld::~ParticleWorld() 29 | { 30 | delete[] contacts; 31 | } 32 | 33 | void ParticleWorld::startFrame() 34 | { 35 | for (Particles::iterator p = particles.begin(); 36 | p != particles.end(); 37 | p++) 38 | { 39 | // Remove all forces from the accumulator 40 | (*p)->clearAccumulator(); 41 | } 42 | } 43 | 44 | unsigned ParticleWorld::generateContacts() 45 | { 46 | unsigned limit = maxContacts; 47 | ParticleContact *nextContact = contacts; 48 | 49 | for (ContactGenerators::iterator g = contactGenerators.begin(); 50 | g != contactGenerators.end(); 51 | g++) 52 | { 53 | unsigned used =(*g)->addContact(nextContact, limit); 54 | limit -= used; 55 | nextContact += used; 56 | 57 | // We've run out of contacts to fill. This means we're missing 58 | // contacts. 59 | if (limit <= 0) break; 60 | } 61 | 62 | // Return the number of contacts used. 63 | return maxContacts - limit; 64 | } 65 | 66 | void ParticleWorld::integrate(real duration) 67 | { 68 | for (Particles::iterator p = particles.begin(); 69 | p != particles.end(); 70 | p++) 71 | { 72 | // Remove all forces from the accumulator 73 | (*p)->integrate(duration); 74 | } 75 | } 76 | 77 | void ParticleWorld::runPhysics(real duration) 78 | { 79 | // First apply the force generators 80 | registry.updateForces(duration); 81 | 82 | // Then integrate the objects 83 | integrate(duration); 84 | 85 | // Generate contacts 86 | unsigned usedContacts = generateContacts(); 87 | 88 | // And process them 89 | if (usedContacts) 90 | { 91 | if (calculateIterations) resolver.setIterations(usedContacts * 2); 92 | resolver.resolveContacts(contacts, usedContacts, duration); 93 | } 94 | } 95 | 96 | ParticleWorld::Particles& ParticleWorld::getParticles() 97 | { 98 | return particles; 99 | } 100 | 101 | ParticleWorld::ContactGenerators& ParticleWorld::getContactGenerators() 102 | { 103 | return contactGenerators; 104 | } 105 | 106 | ParticleForceRegistry& ParticleWorld::getForceRegistry() 107 | { 108 | return registry; 109 | } 110 | 111 | void GroundContacts::init(cyclone::ParticleWorld::Particles *particles) 112 | { 113 | GroundContacts::particles = particles; 114 | } 115 | 116 | unsigned GroundContacts::addContact(cyclone::ParticleContact *contact, 117 | unsigned limit) const 118 | { 119 | unsigned count = 0; 120 | for (cyclone::ParticleWorld::Particles::iterator p = particles->begin(); 121 | p != particles->end(); 122 | p++) 123 | { 124 | cyclone::real y = (*p)->getPosition().y; 125 | if (y < 0.0f) 126 | { 127 | contact->contactNormal = cyclone::Vector3::UP; 128 | contact->particle[0] = *p; 129 | contact->particle[1] = NULL; 130 | contact->penetration = -y; 131 | contact->restitution = 0.2f; 132 | contact++; 133 | count++; 134 | } 135 | 136 | if (count >= limit) return count; 137 | } 138 | return count; 139 | } -------------------------------------------------------------------------------- /src/particle.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Implementation file for the particle class. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | using namespace cyclone; 17 | 18 | 19 | /* 20 | * -------------------------------------------------------------------------- 21 | * FUNCTIONS DECLARED IN HEADER: 22 | * -------------------------------------------------------------------------- 23 | */ 24 | 25 | void Particle::integrate(real duration) 26 | { 27 | // We don't integrate things with zero mass. 28 | if (inverseMass <= 0.0f) return; 29 | 30 | assert(duration > 0.0); 31 | 32 | // Update linear position. 33 | position.addScaledVector(velocity, duration); 34 | 35 | // Work out the acceleration from the force 36 | Vector3 resultingAcc = acceleration; 37 | resultingAcc.addScaledVector(forceAccum, inverseMass); 38 | 39 | // Update linear velocity from the acceleration. 40 | velocity.addScaledVector(resultingAcc, duration); 41 | 42 | // Impose drag. 43 | velocity *= real_pow(damping, duration); 44 | 45 | // Clear the forces. 46 | clearAccumulator(); 47 | } 48 | 49 | 50 | 51 | void Particle::setMass(const real mass) 52 | { 53 | assert(mass != 0); 54 | Particle::inverseMass = ((real)1.0)/mass; 55 | } 56 | 57 | real Particle::getMass() const 58 | { 59 | if (inverseMass == 0) { 60 | return REAL_MAX; 61 | } else { 62 | return ((real)1.0)/inverseMass; 63 | } 64 | } 65 | 66 | void Particle::setInverseMass(const real inverseMass) 67 | { 68 | Particle::inverseMass = inverseMass; 69 | } 70 | 71 | real Particle::getInverseMass() const 72 | { 73 | return inverseMass; 74 | } 75 | 76 | bool Particle::hasFiniteMass() const 77 | { 78 | return inverseMass >= 0.0f; 79 | } 80 | 81 | void Particle::setDamping(const real damping) 82 | { 83 | Particle::damping = damping; 84 | } 85 | 86 | real Particle::getDamping() const 87 | { 88 | return damping; 89 | } 90 | 91 | void Particle::setPosition(const Vector3 &position) 92 | { 93 | Particle::position = position; 94 | } 95 | 96 | void Particle::setPosition(const real x, const real y, const real z) 97 | { 98 | position.x = x; 99 | position.y = y; 100 | position.z = z; 101 | } 102 | 103 | void Particle::getPosition(Vector3 *position) const 104 | { 105 | *position = Particle::position; 106 | } 107 | 108 | Vector3 Particle::getPosition() const 109 | { 110 | return position; 111 | } 112 | 113 | void Particle::setVelocity(const Vector3 &velocity) 114 | { 115 | Particle::velocity = velocity; 116 | } 117 | 118 | void Particle::setVelocity(const real x, const real y, const real z) 119 | { 120 | velocity.x = x; 121 | velocity.y = y; 122 | velocity.z = z; 123 | } 124 | 125 | void Particle::getVelocity(Vector3 *velocity) const 126 | { 127 | *velocity = Particle::velocity; 128 | } 129 | 130 | Vector3 Particle::getVelocity() const 131 | { 132 | return velocity; 133 | } 134 | 135 | void Particle::setAcceleration(const Vector3 &acceleration) 136 | { 137 | Particle::acceleration = acceleration; 138 | } 139 | 140 | void Particle::setAcceleration(const real x, const real y, const real z) 141 | { 142 | acceleration.x = x; 143 | acceleration.y = y; 144 | acceleration.z = z; 145 | } 146 | 147 | void Particle::getAcceleration(Vector3 *acceleration) const 148 | { 149 | *acceleration = Particle::acceleration; 150 | } 151 | 152 | Vector3 Particle::getAcceleration() const 153 | { 154 | return acceleration; 155 | } 156 | 157 | void Particle::clearAccumulator() 158 | { 159 | forceAccum.clear(); 160 | } 161 | 162 | void Particle::addForce(const Vector3 &force) 163 | { 164 | forceAccum += force; 165 | } 166 | -------------------------------------------------------------------------------- /include/cyclone/random.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface file for the random number generator. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | /** 14 | * @file 15 | * 16 | * This file contains the definitions for a random number generator. 17 | */ 18 | #ifndef CYCLONE_RANDOM_H 19 | #define CYCLONE_RANDOM_H 20 | 21 | #include "core.h" 22 | 23 | namespace cyclone { 24 | 25 | 26 | /** 27 | * Keeps track of one random stream: i.e. a seed and its output. 28 | * This is used to get random numbers. Rather than a funcion, this 29 | * allows there to be several streams of repeatable random numbers 30 | * at the same time. Uses the RandRotB algorithm. 31 | */ 32 | class Random 33 | { 34 | public: 35 | /** 36 | * left bitwise rotation 37 | */ 38 | 39 | unsigned rotl(unsigned n, unsigned r); 40 | /** 41 | * right bitwise rotation 42 | */ 43 | unsigned rotr(unsigned n, unsigned r); 44 | 45 | /** 46 | * Creates a new random number stream with a seed based on 47 | * timing data. 48 | */ 49 | Random(); 50 | 51 | /** 52 | * Creates a new random stream with the given seed. 53 | */ 54 | Random(unsigned seed); 55 | 56 | /** 57 | * Sets the seed value for the random stream. 58 | */ 59 | void seed(unsigned seed); 60 | 61 | /** 62 | * Returns the next random bitstring from the stream. This is 63 | * the fastest method. 64 | */ 65 | unsigned randomBits(); 66 | 67 | /** 68 | * Returns a random floating point number between 0 and 1. 69 | */ 70 | real randomReal(); 71 | 72 | /** 73 | * Returns a random floating point number between 0 and scale. 74 | */ 75 | real randomReal(real scale); 76 | 77 | /** 78 | * Returns a random floating point number between min and max. 79 | */ 80 | real randomReal(real min, real max); 81 | 82 | /** 83 | * Returns a random integer less than the given value. 84 | */ 85 | unsigned randomInt(unsigned max); 86 | 87 | /** 88 | * Returns a random binomially distributed number between -scale 89 | * and +scale. 90 | */ 91 | real randomBinomial(real scale); 92 | 93 | /** 94 | * Returns a random vector where each component is binomially 95 | * distributed in the range (-scale to scale) [mean = 0.0f]. 96 | */ 97 | Vector3 randomVector(real scale); 98 | 99 | /** 100 | * Returns a random vector where each component is binomially 101 | * distributed in the range (-scale to scale) [mean = 0.0f], 102 | * where scale is the corresponding component of the given 103 | * vector. 104 | */ 105 | Vector3 randomVector(const Vector3 &scale); 106 | 107 | /** 108 | * Returns a random vector in the cube defined by the given 109 | * minimum and maximum vectors. The probability is uniformly 110 | * distributed in this region. 111 | */ 112 | Vector3 randomVector(const Vector3 &min, const Vector3 &max); 113 | 114 | /** 115 | * Returns a random vector where each component is binomially 116 | * distributed in the range (-scale to scale) [mean = 0.0f], 117 | * except the y coordinate which is zero. 118 | */ 119 | Vector3 randomXZVector(real scale); 120 | 121 | /** 122 | * Returns a random orientation (i.e. normalized) quaternion. 123 | */ 124 | Quaternion randomQuaternion(); 125 | 126 | private: 127 | // Internal mechanics 128 | int p1, p2; 129 | unsigned buffer[17]; 130 | }; 131 | 132 | } // namespace cyclone 133 | 134 | #endif // CYCLONE_BODY_H 135 | -------------------------------------------------------------------------------- /include/cyclone/pworld.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface file for the particle / mass aggregate world structure. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | /** 14 | * @file 15 | * 16 | * This file contains the definitions for a structure to hold any number o 17 | * particle masses, and their connections. 18 | */ 19 | #ifndef CYCLONE_PWORLD_H 20 | #define CYCLONE_PWORLD_H 21 | 22 | #include "pfgen.h" 23 | #include "plinks.h" 24 | 25 | namespace cyclone { 26 | 27 | /** 28 | * Keeps track of a set of particles, and provides the means to 29 | * update them all. 30 | */ 31 | class ParticleWorld 32 | { 33 | public: 34 | typedef std::vector Particles; 35 | typedef std::vector ContactGenerators; 36 | 37 | protected: 38 | /** 39 | * Holds the particles 40 | */ 41 | Particles particles; 42 | 43 | /** 44 | * True if the world should calculate the number of iterations 45 | * to give the contact resolver at each frame. 46 | */ 47 | bool calculateIterations; 48 | 49 | /** 50 | * Holds the force generators for the particles in this world. 51 | */ 52 | ParticleForceRegistry registry; 53 | 54 | /** 55 | * Holds the resolver for contacts. 56 | */ 57 | ParticleContactResolver resolver; 58 | 59 | /** 60 | * Contact generators. 61 | */ 62 | ContactGenerators contactGenerators; 63 | 64 | /** 65 | * Holds the list of contacts. 66 | */ 67 | ParticleContact *contacts; 68 | 69 | /** 70 | * Holds the maximum number of contacts allowed (i.e. the 71 | * size of the contacts array). 72 | */ 73 | unsigned maxContacts; 74 | 75 | public: 76 | 77 | /** 78 | * Creates a new particle simulator that can handle up to the 79 | * given number of contacts per frame. You can also optionally 80 | * give a number of contact-resolution iterations to use. If you 81 | * don't give a number of iterations, then twice the number of 82 | * contacts will be used. 83 | */ 84 | ParticleWorld(unsigned maxContacts, unsigned iterations=0); 85 | 86 | /** 87 | * Deletes the simulator. 88 | */ 89 | ~ParticleWorld(); 90 | 91 | /** 92 | * Calls each of the registered contact generators to report 93 | * their contacts. Returns the number of generated contacts. 94 | */ 95 | unsigned generateContacts(); 96 | 97 | /** 98 | * Integrates all the particles in this world forward in time 99 | * by the given duration. 100 | */ 101 | void integrate(real duration); 102 | 103 | /** 104 | * Processes all the physics for the particle world. 105 | */ 106 | void runPhysics(real duration); 107 | 108 | /** 109 | * Initializes the world for a simulation frame. This clears 110 | * the force accumulators for particles in the world. After 111 | * calling this, the particles can have their forces for this 112 | * frame added. 113 | */ 114 | void startFrame(); 115 | 116 | /** 117 | * Returns the list of particles. 118 | */ 119 | Particles& getParticles(); 120 | 121 | /** 122 | * Returns the list of contact generators. 123 | */ 124 | ContactGenerators& getContactGenerators(); 125 | 126 | /** 127 | * Returns the force registry. 128 | */ 129 | ParticleForceRegistry& getForceRegistry(); 130 | }; 131 | 132 | /** 133 | * A contact generator that takes an STL vector of particle pointers and 134 | * collides them against the ground. 135 | */ 136 | class GroundContacts : public cyclone::ParticleContactGenerator 137 | { 138 | cyclone::ParticleWorld::Particles *particles; 139 | 140 | public: 141 | void init(cyclone::ParticleWorld::Particles *particles); 142 | 143 | virtual unsigned addContact(cyclone::ParticleContact *contact, 144 | unsigned limit) const; 145 | }; 146 | 147 | } // namespace cyclone 148 | 149 | #endif // CYCLONE_PWORLD_H 150 | -------------------------------------------------------------------------------- /src/plinks.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Implementation file for particle links. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | 15 | using namespace cyclone; 16 | 17 | real ParticleLink::currentLength() const 18 | { 19 | Vector3 relativePos = particle[0]->getPosition() - 20 | particle[1]->getPosition(); 21 | return relativePos.magnitude(); 22 | } 23 | 24 | unsigned ParticleCable::addContact(ParticleContact *contact, 25 | unsigned limit) const 26 | { 27 | // Find the length of the cable 28 | real length = currentLength(); 29 | 30 | // Check if we're over-extended 31 | if (length < maxLength) 32 | { 33 | return 0; 34 | } 35 | 36 | // Otherwise return the contact 37 | contact->particle[0] = particle[0]; 38 | contact->particle[1] = particle[1]; 39 | 40 | // Calculate the normal 41 | Vector3 normal = particle[1]->getPosition() - particle[0]->getPosition(); 42 | normal.normalise(); 43 | contact->contactNormal = normal; 44 | 45 | contact->penetration = length-maxLength; 46 | contact->restitution = restitution; 47 | 48 | return 1; 49 | } 50 | 51 | unsigned ParticleRod::addContact(ParticleContact *contact, 52 | unsigned limit) const 53 | { 54 | // Find the length of the rod 55 | real currentLen = currentLength(); 56 | 57 | // Check if we're over-extended 58 | if (currentLen == length) 59 | { 60 | return 0; 61 | } 62 | 63 | // Otherwise return the contact 64 | contact->particle[0] = particle[0]; 65 | contact->particle[1] = particle[1]; 66 | 67 | // Calculate the normal 68 | Vector3 normal = particle[1]->getPosition() - particle[0]->getPosition(); 69 | normal.normalise(); 70 | 71 | // The contact normal depends on whether we're extending or compressing 72 | if (currentLen > length) { 73 | contact->contactNormal = normal; 74 | contact->penetration = currentLen - length; 75 | } else { 76 | contact->contactNormal = normal * -1; 77 | contact->penetration = length - currentLen; 78 | } 79 | 80 | // Always use zero restitution (no bounciness) 81 | contact->restitution = 0; 82 | 83 | return 1; 84 | } 85 | 86 | real ParticleConstraint::currentLength() const 87 | { 88 | Vector3 relativePos = particle->getPosition() - anchor; 89 | return relativePos.magnitude(); 90 | } 91 | 92 | unsigned ParticleCableConstraint::addContact(ParticleContact *contact, 93 | unsigned limit) const 94 | { 95 | // Find the length of the cable 96 | real length = currentLength(); 97 | 98 | // Check if we're over-extended 99 | if (length < maxLength) 100 | { 101 | return 0; 102 | } 103 | 104 | // Otherwise return the contact 105 | contact->particle[0] = particle; 106 | contact->particle[1] = 0; 107 | 108 | // Calculate the normal 109 | Vector3 normal = anchor - particle->getPosition(); 110 | normal.normalise(); 111 | contact->contactNormal = normal; 112 | 113 | contact->penetration = length-maxLength; 114 | contact->restitution = restitution; 115 | 116 | return 1; 117 | } 118 | 119 | unsigned ParticleRodConstraint::addContact(ParticleContact *contact, 120 | unsigned limit) const 121 | { 122 | // Find the length of the rod 123 | real currentLen = currentLength(); 124 | 125 | // Check if we're over-extended 126 | if (currentLen == length) 127 | { 128 | return 0; 129 | } 130 | 131 | // Otherwise return the contact 132 | contact->particle[0] = particle; 133 | contact->particle[1] = 0; 134 | 135 | // Calculate the normal 136 | Vector3 normal = anchor - particle->getPosition(); 137 | normal.normalise(); 138 | 139 | // The contact normal depends on whether we're extending or compressing 140 | if (currentLen > length) { 141 | contact->contactNormal = normal; 142 | contact->penetration = currentLen - length; 143 | } else { 144 | contact->contactNormal = normal * -1; 145 | contact->penetration = length - currentLen; 146 | } 147 | 148 | // Always use zero restitution (no bounciness) 149 | contact->restitution = 0; 150 | 151 | return 1; 152 | } -------------------------------------------------------------------------------- /src/random.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Implementation file for random number generation. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace cyclone; 18 | 19 | Random::Random() 20 | { 21 | seed(0); 22 | } 23 | 24 | Random::Random(unsigned seed) 25 | { 26 | Random::seed(seed); 27 | } 28 | 29 | void Random::seed(unsigned s) 30 | { 31 | if (s == 0) { 32 | s = (unsigned)clock(); 33 | } 34 | 35 | // Fill the buffer with some basic random numbers 36 | for (unsigned i = 0; i < 17; i++) 37 | { 38 | // Simple linear congruential generator 39 | s = s * 2891336453 + 1; 40 | buffer[i] = s; 41 | } 42 | 43 | // Initialize pointers into the buffer 44 | p1 = 0; p2 = 10; 45 | } 46 | 47 | unsigned Random::rotl(unsigned n, unsigned r) 48 | { 49 | return (n << r) | 50 | (n >> (32 - r)); 51 | } 52 | 53 | unsigned Random::rotr(unsigned n, unsigned r) 54 | { 55 | return (n >> r) | 56 | (n << (32 - r)); 57 | } 58 | 59 | unsigned Random::randomBits() 60 | { 61 | unsigned result; 62 | 63 | // Rotate the buffer and store it back to itself 64 | result = buffer[p1] = rotl(buffer[p2], 13) + rotl(buffer[p1], 9); 65 | 66 | // Rotate pointers 67 | if (--p1 < 0) p1 = 16; 68 | if (--p2 < 0) p2 = 16; 69 | 70 | // Return result 71 | return result; 72 | } 73 | 74 | #ifdef SINGLE_PRECISION 75 | real Random::randomReal() 76 | { 77 | // Get the random number 78 | unsigned bits = randomBits(); 79 | 80 | // Set up a reinterpret structure for manipulation 81 | union { 82 | real value; 83 | unsigned word; 84 | } convert; 85 | 86 | // Now assign the bits to the word. This works by fixing the ieee 87 | // sign and exponent bits (so that the size of the result is 1-2) 88 | // and using the bits to create the fraction part of the float. 89 | convert.word = (bits >> 9) | 0x3f800000; 90 | 91 | // And return the value 92 | return convert.value - 1.0f; 93 | } 94 | #else 95 | real Random::randomReal() 96 | { 97 | // Get the random number 98 | unsigned bits = randomBits(); 99 | 100 | // Set up a reinterpret structure for manipulation 101 | union { 102 | real value; 103 | unsigned words[2]; 104 | } convert; 105 | 106 | // Now assign the bits to the words. This works by fixing the ieee 107 | // sign and exponent bits (so that the size of the result is 1-2) 108 | // and using the bits to create the fraction part of the float. Note 109 | // that bits are used more than once in this process. 110 | convert.words[0] = bits << 20; // Fill in the top 16 bits 111 | convert.words[1] = (bits >> 12) | 0x3FF00000; // And the bottom 20 112 | 113 | // And return the value 114 | return convert.value - 1.0; 115 | } 116 | #endif 117 | 118 | real Random::randomReal(real min, real max) 119 | { 120 | return randomReal() * (max-min) + min; 121 | } 122 | 123 | real Random::randomReal(real scale) 124 | { 125 | return randomReal() * scale; 126 | } 127 | 128 | unsigned Random::randomInt(unsigned max) 129 | { 130 | return randomBits() % max; 131 | } 132 | 133 | real Random::randomBinomial(real scale) 134 | { 135 | return (randomReal()-randomReal())*scale; 136 | } 137 | 138 | Quaternion Random::randomQuaternion() 139 | { 140 | Quaternion q( 141 | randomReal(), 142 | randomReal(), 143 | randomReal(), 144 | randomReal() 145 | ); 146 | q.normalise(); 147 | return q; 148 | } 149 | 150 | Vector3 Random::randomVector(real scale) 151 | { 152 | return Vector3( 153 | randomBinomial(scale), 154 | randomBinomial(scale), 155 | randomBinomial(scale) 156 | ); 157 | } 158 | 159 | Vector3 Random::randomXZVector(real scale) 160 | { 161 | return Vector3( 162 | randomBinomial(scale), 163 | 0, 164 | randomBinomial(scale) 165 | ); 166 | } 167 | 168 | Vector3 Random::randomVector(const Vector3 &scale) 169 | { 170 | return Vector3( 171 | randomBinomial(scale.x), 172 | randomBinomial(scale.y), 173 | randomBinomial(scale.z) 174 | ); 175 | } 176 | 177 | Vector3 Random::randomVector(const Vector3 &min, const Vector3 &max) 178 | { 179 | return Vector3( 180 | randomReal(min.x, max.x), 181 | randomReal(min.y, max.y), 182 | randomReal(min.z, max.z) 183 | ); 184 | } 185 | -------------------------------------------------------------------------------- /src/demos/timing.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Timing functions, frame management and profiling. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Ian Millington 2003-2006. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include "timing.h" 14 | 15 | 16 | // Hold internal timing data for the performance counter. 17 | static bool qpcFlag; 18 | 19 | #if (__APPLE__ || __unix) 20 | #define TIMING_UNIX 1 21 | 22 | #include 23 | #include 24 | 25 | // assume unix based OS 26 | typedef unsigned long long LONGLONG; 27 | #else 28 | #define TIMING_WINDOWS 1 29 | // assume windows 30 | 31 | // Import the high performance timer (c. 4ms). 32 | #include 33 | #include 34 | 35 | static double qpcFrequency; 36 | #endif 37 | 38 | 39 | 40 | // Internal time and clock access functions 41 | unsigned systemTime() 42 | { 43 | #if TIMING_UNIX 44 | struct timeval tv; 45 | gettimeofday(&tv, 0); 46 | 47 | return tv.tv_sec * 1000 + tv.tv_usec/1000; 48 | 49 | #else 50 | if(qpcFlag) 51 | { 52 | static LONGLONG qpcMillisPerTick; 53 | QueryPerformanceCounter((LARGE_INTEGER*)&qpcMillisPerTick); 54 | return (unsigned)(qpcMillisPerTick * qpcFrequency); 55 | } 56 | else 57 | { 58 | return unsigned(timeGetTime()); 59 | } 60 | #endif 61 | 62 | } 63 | 64 | unsigned TimingData::getTime() 65 | { 66 | return systemTime(); 67 | } 68 | 69 | #if TIMING_WINDOWS 70 | unsigned long systemClock() 71 | { 72 | __asm { 73 | rdtsc; 74 | } 75 | } 76 | #endif 77 | 78 | unsigned long TimingData::getClock() 79 | { 80 | 81 | #if TIMING_UNIX 82 | struct timeval tv; 83 | gettimeofday(&tv, 0); 84 | 85 | return tv.tv_sec * 1000 + tv.tv_usec/1000; 86 | #else 87 | return systemClock(); 88 | #endif 89 | } 90 | 91 | // Sets up the timing system and registers the performance timer. 92 | void initTime() 93 | { 94 | #if TIMING_UNIX 95 | qpcFlag = false; 96 | #else 97 | LONGLONG time; 98 | 99 | qpcFlag = (QueryPerformanceFrequency((LARGE_INTEGER*)&time) > 0); 100 | 101 | // Check if we have access to the performance counter at this 102 | // resolution. 103 | if (qpcFlag) qpcFrequency = 1000.0 / time; 104 | #endif 105 | } 106 | 107 | 108 | // Holds the global frame time that is passed around 109 | static TimingData *timingData = NULL; 110 | 111 | // Retrieves the global frame info instance 112 | TimingData& TimingData::get() 113 | { 114 | return (TimingData&)*timingData; 115 | } 116 | 117 | // Updates the global frame information. Should be called once per frame. 118 | void TimingData::update() 119 | { 120 | if (!timingData) return; 121 | 122 | // Advance the frame number. 123 | if (!timingData->isPaused) 124 | { 125 | timingData->frameNumber++; 126 | } 127 | 128 | // Update the timing information. 129 | unsigned thisTime = systemTime(); 130 | timingData->lastFrameDuration = thisTime - 131 | timingData->lastFrameTimestamp; 132 | timingData->lastFrameTimestamp = thisTime; 133 | 134 | // Update the tick information. 135 | unsigned long thisClock = getClock(); 136 | timingData->lastFrameClockTicks = 137 | thisClock - timingData->lastFrameClockstamp; 138 | timingData->lastFrameClockstamp = thisClock; 139 | 140 | // Update the RWA frame rate if we are able to. 141 | if (timingData->frameNumber > 1) { 142 | if (timingData->averageFrameDuration <= 0) 143 | { 144 | timingData->averageFrameDuration = 145 | (double)timingData->lastFrameDuration; 146 | } 147 | else 148 | { 149 | // RWA over 100 frames. 150 | timingData->averageFrameDuration *= 0.99; 151 | timingData->averageFrameDuration += 152 | 0.01 * (double)timingData->lastFrameDuration; 153 | 154 | // Invert to get FPS 155 | timingData->fps = 156 | (float)(1000.0/timingData->averageFrameDuration); 157 | } 158 | } 159 | } 160 | 161 | void TimingData::init() 162 | { 163 | // Set up the timing system. 164 | initTime(); 165 | 166 | // Create the frame info object 167 | if (!timingData) timingData = new TimingData(); 168 | 169 | // Set up the frame info structure. 170 | timingData->frameNumber = 0; 171 | 172 | timingData->lastFrameTimestamp = systemTime(); 173 | timingData->lastFrameDuration = 0; 174 | 175 | timingData->lastFrameClockstamp = getClock(); 176 | timingData->lastFrameClockTicks = 0; 177 | 178 | timingData->isPaused = false; 179 | 180 | timingData->averageFrameDuration = 0; 181 | timingData->fps = 0; 182 | } 183 | 184 | void TimingData::deinit() 185 | { 186 | delete timingData; 187 | timingData = NULL; 188 | } 189 | -------------------------------------------------------------------------------- /src/fgen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Implementation file for the rigid body force generators. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under license. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software license. 11 | */ 12 | 13 | #include 14 | 15 | using namespace cyclone; 16 | 17 | void ForceRegistry::updateForces(real duration) 18 | { 19 | Registry::iterator i = registrations.begin(); 20 | for (; i != registrations.end(); i++) 21 | { 22 | i->fg->updateForce(i->body, duration); 23 | } 24 | } 25 | 26 | void ForceRegistry::add(RigidBody *body, ForceGenerator *fg) 27 | { 28 | ForceRegistry::ForceRegistration registration; 29 | registration.body = body; 30 | registration.fg = fg; 31 | registrations.push_back(registration); 32 | } 33 | 34 | Buoyancy::Buoyancy(const Vector3 &cOfB, real maxDepth, real volume, 35 | real waterHeight, real liquidDensity /* = 1000.0f */) 36 | { 37 | centreOfBuoyancy = cOfB; 38 | Buoyancy::liquidDensity = liquidDensity; 39 | Buoyancy::maxDepth = maxDepth; 40 | Buoyancy::volume = volume; 41 | Buoyancy::waterHeight = waterHeight; 42 | } 43 | 44 | void Buoyancy::updateForce(RigidBody *body, real duration) 45 | { 46 | // Calculate the submersion depth 47 | Vector3 pointInWorld = body->getPointInWorldSpace(centreOfBuoyancy); 48 | real depth = pointInWorld.y; 49 | 50 | // Check if we're out of the water 51 | if (depth >= waterHeight + maxDepth) return; 52 | Vector3 force(0,0,0); 53 | 54 | // Check if we're at maximum depth 55 | if (depth <= waterHeight - maxDepth) 56 | { 57 | force.y = liquidDensity * volume; 58 | body->addForceAtBodyPoint(force, centreOfBuoyancy); 59 | return; 60 | } 61 | 62 | // Otherwise we are partly submerged 63 | force.y = liquidDensity * volume * 64 | (depth - maxDepth - waterHeight) / 2 * maxDepth; 65 | body->addForceAtBodyPoint(force, centreOfBuoyancy); 66 | } 67 | 68 | Gravity::Gravity(const Vector3& gravity) 69 | : gravity(gravity) 70 | { 71 | } 72 | 73 | void Gravity::updateForce(RigidBody* body, real duration) 74 | { 75 | // Check that we do not have infinite mass 76 | if (!body->hasFiniteMass()) return; 77 | 78 | // Apply the mass-scaled force to the body 79 | body->addForce(gravity * body->getMass()); 80 | } 81 | 82 | Spring::Spring(const Vector3 &localConnectionPt, 83 | RigidBody *other, 84 | const Vector3 &otherConnectionPt, 85 | real springConstant, 86 | real restLength) 87 | : connectionPoint(localConnectionPt), 88 | otherConnectionPoint(otherConnectionPt), 89 | other(other), 90 | springConstant(springConstant), 91 | restLength(restLength) 92 | { 93 | } 94 | 95 | void Spring::updateForce(RigidBody* body, real duration) 96 | { 97 | // Calculate the two ends in world space 98 | Vector3 lws = body->getPointInWorldSpace(connectionPoint); 99 | Vector3 ows = other->getPointInWorldSpace(otherConnectionPoint); 100 | 101 | // Calculate the vector of the spring 102 | Vector3 force = lws - ows; 103 | 104 | // Calculate the magnitude of the force 105 | real magnitude = force.magnitude(); 106 | magnitude = real_abs(magnitude - restLength); 107 | magnitude *= springConstant; 108 | 109 | // Calculate the final force and apply it 110 | force.normalise(); 111 | force *= -magnitude; 112 | body->addForceAtPoint(force, lws); 113 | } 114 | 115 | Aero::Aero(const Matrix3 &tensor, const Vector3 &position, const Vector3 *windspeed) 116 | { 117 | Aero::tensor = tensor; 118 | Aero::position = position; 119 | Aero::windspeed = windspeed; 120 | } 121 | 122 | void Aero::updateForce(RigidBody *body, real duration) 123 | { 124 | Aero::updateForceFromTensor(body, duration, tensor); 125 | } 126 | 127 | void Aero::updateForceFromTensor(RigidBody *body, real duration, 128 | const Matrix3 &tensor) 129 | { 130 | // Calculate total velocity (windspeed and body's velocity). 131 | Vector3 velocity = body->getVelocity(); 132 | velocity += *windspeed; 133 | 134 | // Calculate the velocity in body coordinates 135 | Vector3 bodyVel = body->getTransform().transformInverseDirection(velocity); 136 | 137 | // Calculate the force in body coordinates 138 | Vector3 bodyForce = tensor.transform(bodyVel); 139 | Vector3 force = body->getTransform().transformDirection(bodyForce); 140 | 141 | // Apply the force 142 | body->addForceAtBodyPoint(force, position); 143 | } 144 | 145 | AeroControl::AeroControl(const Matrix3 &base, const Matrix3 &min, const Matrix3 &max, 146 | const Vector3 &position, const Vector3 *windspeed) 147 | : 148 | Aero(base, position, windspeed) 149 | { 150 | AeroControl::minTensor = min; 151 | AeroControl::maxTensor = max; 152 | controlSetting = 0.0f; 153 | } 154 | 155 | Matrix3 AeroControl::getTensor() 156 | { 157 | if (controlSetting <= -1.0f) return minTensor; 158 | else if (controlSetting >= 1.0f) return maxTensor; 159 | else if (controlSetting < 0) 160 | { 161 | return Matrix3::linearInterpolate(minTensor, tensor, controlSetting+1.0f); 162 | } 163 | else if (controlSetting > 0) 164 | { 165 | return Matrix3::linearInterpolate(tensor, maxTensor, controlSetting); 166 | } 167 | else return tensor; 168 | } 169 | 170 | void AeroControl::setControl(real value) 171 | { 172 | controlSetting = value; 173 | } 174 | 175 | void AeroControl::updateForce(RigidBody *body, real duration) 176 | { 177 | Matrix3 tensor = getTensor(); 178 | Aero::updateForceFromTensor(body, duration, tensor); 179 | } 180 | 181 | void Explosion::updateForce(RigidBody* body, real duration) 182 | { 183 | 184 | } -------------------------------------------------------------------------------- /include/cyclone/plinks.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface file for the particle links. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | /** 14 | * @file 15 | * 16 | * This file contains classes representing the connections between 17 | * particles. 18 | */ 19 | #ifndef CYCLONE_PLINKS_H 20 | #define CYCLONE_PLINKS_H 21 | 22 | #include "pcontacts.h" 23 | 24 | namespace cyclone { 25 | 26 | /** 27 | * Links connect two particles together, generating a contact if 28 | * they violate the constraints of their link. It is used as a 29 | * base class for cables and rods, and could be used as a base 30 | * class for springs with a limit to their extension.. 31 | */ 32 | class ParticleLink : public ParticleContactGenerator 33 | { 34 | public: 35 | /** 36 | * Holds the pair of particles that are connected by this link. 37 | */ 38 | Particle* particle[2]; 39 | 40 | protected: 41 | /** 42 | * Returns the current length of the link. 43 | */ 44 | real currentLength() const; 45 | 46 | public: 47 | /** 48 | * Geneates the contacts to keep this link from being 49 | * violated. This class can only ever generate a single 50 | * contact, so the pointer can be a pointer to a single 51 | * element, the limit parameter is assumed to be at least one 52 | * (zero isn't valid) and the return value is either 0, if the 53 | * cable wasn't over-extended, or one if a contact was needed. 54 | * 55 | * NB: This method is declared in the same way (as pure 56 | * virtual) in the parent class, but is replicated here for 57 | * documentation purposes. 58 | */ 59 | virtual unsigned addContact(ParticleContact *contact, 60 | unsigned limit) const = 0; 61 | }; 62 | 63 | /** 64 | * Cables link a pair of particles, generating a contact if they 65 | * stray too far apart. 66 | */ 67 | class ParticleCable : public ParticleLink 68 | { 69 | public: 70 | /** 71 | * Holds the maximum length of the cable. 72 | */ 73 | real maxLength; 74 | 75 | /** 76 | * Holds the restitution (bounciness) of the cable. 77 | */ 78 | real restitution; 79 | 80 | public: 81 | /** 82 | * Fills the given contact structure with the contact needed 83 | * to keep the cable from over-extending. 84 | */ 85 | virtual unsigned addContact(ParticleContact *contact, 86 | unsigned limit) const; 87 | }; 88 | 89 | /** 90 | * Rods link a pair of particles, generating a contact if they 91 | * stray too far apart or too close. 92 | */ 93 | class ParticleRod : public ParticleLink 94 | { 95 | public: 96 | /** 97 | * Holds the length of the rod. 98 | */ 99 | real length; 100 | 101 | public: 102 | /** 103 | * Fills the given contact structure with the contact needed 104 | * to keep the rod from extending or compressing. 105 | */ 106 | virtual unsigned addContact(ParticleContact *contact, 107 | unsigned limit) const; 108 | }; 109 | 110 | /** 111 | * Constraints are just like links, except they connect a particle to 112 | * an immovable anchor point. 113 | */ 114 | class ParticleConstraint : public ParticleContactGenerator 115 | { 116 | public: 117 | /** 118 | * Holds the particles connected by this constraint. 119 | */ 120 | Particle* particle; 121 | 122 | /** 123 | * The point to which the particle is anchored. 124 | */ 125 | Vector3 anchor; 126 | 127 | protected: 128 | /** 129 | * Returns the current length of the link. 130 | */ 131 | real currentLength() const; 132 | 133 | public: 134 | /** 135 | * Geneates the contacts to keep this link from being 136 | * violated. This class can only ever generate a single 137 | * contact, so the pointer can be a pointer to a single 138 | * element, the limit parameter is assumed to be at least one 139 | * (zero isn't valid) and the return value is either 0, if the 140 | * cable wasn't over-extended, or one if a contact was needed. 141 | * 142 | * NB: This method is declared in the same way (as pure 143 | * virtual) in the parent class, but is replicated here for 144 | * documentation purposes. 145 | */ 146 | virtual unsigned addContact(ParticleContact *contact, 147 | unsigned limit) const = 0; 148 | }; 149 | 150 | /** 151 | * Cables link a particle to an anchor point, generating a contact if they 152 | * stray too far apart. 153 | */ 154 | class ParticleCableConstraint : public ParticleConstraint 155 | { 156 | public: 157 | /** 158 | * Holds the maximum length of the cable. 159 | */ 160 | real maxLength; 161 | 162 | /** 163 | * Holds the restitution (bounciness) of the cable. 164 | */ 165 | real restitution; 166 | 167 | public: 168 | /** 169 | * Fills the given contact structure with the contact needed 170 | * to keep the cable from over-extending. 171 | */ 172 | virtual unsigned addContact(ParticleContact *contact, 173 | unsigned limit) const; 174 | }; 175 | 176 | /** 177 | * Rods link a particle to an anchor point, generating a contact if they 178 | * stray too far apart or too close. 179 | */ 180 | class ParticleRodConstraint : public ParticleConstraint 181 | { 182 | public: 183 | /** 184 | * Holds the length of the rod. 185 | */ 186 | real length; 187 | 188 | public: 189 | /** 190 | * Fills the given contact structure with the contact needed 191 | * to keep the rod from extending or compressing. 192 | */ 193 | virtual unsigned addContact(ParticleContact *contact, 194 | unsigned limit) const; 195 | }; 196 | } // namespace cyclone 197 | 198 | #endif // CYCLONE_CONTACTS_H -------------------------------------------------------------------------------- /src/demos/sailboat/sailboat.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The sailboat demo. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | #include "../ogl_headers.h" 15 | #include "../app.h" 16 | #include "../timing.h" 17 | 18 | #include 19 | #include 20 | 21 | 22 | /** 23 | * The main demo class definition. 24 | */ 25 | class SailboatDemo : public Application 26 | { 27 | cyclone::Buoyancy buoyancy; 28 | 29 | cyclone::Aero sail; 30 | cyclone::RigidBody sailboat; 31 | cyclone::ForceRegistry registry; 32 | 33 | cyclone::Random r; 34 | cyclone::Vector3 windspeed; 35 | 36 | float sail_control; 37 | 38 | public: 39 | /** Creates a new demo object. */ 40 | SailboatDemo(); 41 | virtual ~SailboatDemo(); 42 | 43 | /** Returns the window title for the demo. */ 44 | virtual const char* getTitle(); 45 | 46 | /** Display the particles. */ 47 | virtual void display(); 48 | 49 | /** Update the particle positions. */ 50 | virtual void update(); 51 | 52 | /** Handle a key press. */ 53 | virtual void key(unsigned char key); 54 | }; 55 | 56 | // Method definitions 57 | SailboatDemo::SailboatDemo() 58 | : 59 | Application(), 60 | 61 | sail(cyclone::Matrix3(0,0,0, 0,0,0, 0,0,-1.0f), 62 | cyclone::Vector3(2.0f, 0, 0), &windspeed), 63 | 64 | buoyancy(cyclone::Vector3(0.0f, 0.5f, 0.0f), 1.0f, 3.0f, 1.6f), 65 | 66 | sail_control(0), 67 | 68 | windspeed(0,0,0) 69 | { 70 | // Set up the boat's rigid body. 71 | sailboat.setPosition(0, 1.6f, 0); 72 | sailboat.setOrientation(1,0,0,0); 73 | 74 | sailboat.setVelocity(0,0,0); 75 | sailboat.setRotation(0,0,0); 76 | 77 | sailboat.setMass(200.0f); 78 | cyclone::Matrix3 it; 79 | it.setBlockInertiaTensor(cyclone::Vector3(2,1,1), 100.0f); 80 | sailboat.setInertiaTensor(it); 81 | 82 | sailboat.setDamping(0.8f, 0.8f); 83 | 84 | sailboat.setAcceleration(cyclone::Vector3::GRAVITY); 85 | sailboat.calculateDerivedData(); 86 | 87 | sailboat.setAwake(); 88 | sailboat.setCanSleep(false); 89 | 90 | registry.add(&sailboat, &sail); 91 | registry.add(&sailboat, &buoyancy); 92 | } 93 | 94 | SailboatDemo::~SailboatDemo() 95 | { 96 | } 97 | 98 | static void drawBoat() 99 | { 100 | // Left Hull 101 | glPushMatrix(); 102 | glTranslatef(0, 0, -1.0f); 103 | glScalef(2.0f, 0.4f, 0.4f); 104 | glutSolidCube(1.0f); 105 | glPopMatrix(); 106 | 107 | // Right Hull 108 | glPushMatrix(); 109 | glTranslatef(0, 0, 1.0f); 110 | glScalef(2.0f, 0.4f, 0.4f); 111 | glutSolidCube(1.0f); 112 | glPopMatrix(); 113 | 114 | // Deck 115 | glPushMatrix(); 116 | glTranslatef(0, 0.3f, 0); 117 | glScalef(1.0f, 0.1f, 2.0f); 118 | glutSolidCube(1.0f); 119 | glPopMatrix(); 120 | 121 | // Mast 122 | glPushMatrix(); 123 | glTranslatef(0, 1.8f, 0); 124 | glScalef(0.1f, 3.0f, 0.1f); 125 | glutSolidCube(1.0f); 126 | glPopMatrix(); 127 | 128 | } 129 | 130 | void SailboatDemo::display() 131 | { 132 | // Clear the view port and set the camera direction 133 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 134 | glLoadIdentity(); 135 | 136 | cyclone::Vector3 pos = sailboat.getPosition(); 137 | cyclone::Vector3 offset(4.0f, 0, 0); 138 | offset = sailboat.getTransform().transformDirection(offset); 139 | gluLookAt(pos.x+offset.x, pos.y+5.0f, pos.z+offset.z, 140 | pos.x, pos.y, pos.z, 141 | 0.0, 1.0, 0.0); 142 | 143 | glColor3f(0.6f,0.6f,0.6f); 144 | int bx = int(pos.x); 145 | int bz = int(pos.z); 146 | glBegin(GL_QUADS); 147 | for (int x = -20; x <= 20; x++) for (int z = -20; z <= 20; z++) 148 | { 149 | glVertex3f(bx+x-0.1f, 0, bz+z-0.1f); 150 | glVertex3f(bx+x-0.1f, 0, bz+z+0.1f); 151 | glVertex3f(bx+x+0.1f, 0, bz+z+0.1f); 152 | glVertex3f(bx+x+0.1f, 0, bz+z-0.1f); 153 | } 154 | glEnd(); 155 | 156 | // Set the transform matrix for the aircraft 157 | cyclone::Matrix4 transform = sailboat.getTransform(); 158 | GLfloat gl_transform[16]; 159 | transform.fillGLArray(gl_transform); 160 | glPushMatrix(); 161 | glMultMatrixf(gl_transform); 162 | 163 | // Draw the boat 164 | glColor3f(0,0,0); 165 | drawBoat(); 166 | glPopMatrix(); 167 | 168 | char buffer[256]; 169 | sprintf( 170 | buffer, 171 | "Speed %.1f", 172 | sailboat.getVelocity().magnitude() 173 | ); 174 | glColor3f(0,0,0); 175 | renderText(10.0f, 24.0f, buffer); 176 | 177 | sprintf( 178 | buffer, 179 | "Sail Control: %.1f", 180 | sail_control 181 | ); 182 | renderText(10.0f, 10.0f, buffer); 183 | } 184 | 185 | void SailboatDemo::update() 186 | { 187 | // Find the duration of the last frame in seconds 188 | float duration = (float)TimingData::get().lastFrameDuration * 0.001f; 189 | if (duration <= 0.0f) return; 190 | 191 | // Start with no forces or acceleration. 192 | sailboat.clearAccumulators(); 193 | 194 | // Add the forces acting on the boat. 195 | registry.updateForces(duration); 196 | 197 | // Update the boat's physics. 198 | sailboat.integrate(duration); 199 | 200 | // Change the wind speed. 201 | windspeed = windspeed * 0.9f + r.randomXZVector(1.0f); 202 | 203 | Application::update(); 204 | } 205 | 206 | const char* SailboatDemo::getTitle() 207 | { 208 | return "Cyclone > Sail Boat Demo"; 209 | } 210 | 211 | void SailboatDemo::key(unsigned char key) 212 | { 213 | switch(key) 214 | { 215 | case 'q': case 'Q': 216 | sail_control -= 0.1f; 217 | break; 218 | 219 | case 'e': case 'E': 220 | sail_control += 0.1f; 221 | break; 222 | 223 | case 'w': case 'W': 224 | sail_control = 0.0f; 225 | break; 226 | 227 | default: 228 | Application::key(key); 229 | } 230 | 231 | // Make sure the controls are in range 232 | if (sail_control < -1.0f) sail_control = -1.0f; 233 | else if (sail_control > 1.0f) sail_control = 1.0f; 234 | 235 | // Update the control surfaces 236 | //sail.setControl(sail_control); 237 | } 238 | 239 | /** 240 | * Called by the common demo framework to create an application 241 | * object (with new) and return a pointer. 242 | */ 243 | Application* getApplication() 244 | { 245 | return new SailboatDemo(); 246 | } -------------------------------------------------------------------------------- /include/cyclone/pcontacts.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface file for the contact resolution system for particles. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | /** 14 | * @file 15 | * 16 | * This file contains the contact resolution system for particles 17 | * cyclone. 18 | * 19 | * The resolver uses an iterative satisfaction algorithm; it loops 20 | * through each contact and tries to resolve it. This is a very fast 21 | * algorithm but can be unstable when the contacts are highly 22 | * inter-related. 23 | */ 24 | #ifndef CYCLONE_PCONTACTS_H 25 | #define CYCLONE_PCONTACTS_H 26 | 27 | #include "particle.h" 28 | 29 | namespace cyclone { 30 | 31 | /* 32 | * Forward declaration, see full declaration below for complete 33 | * documentation. 34 | */ 35 | class ParticleContactResolver; 36 | 37 | /** 38 | * A Contact represents two objects in contact (in this case 39 | * ParticleContact representing two Particles). Resolving a 40 | * contact removes their interpenetration, and applies sufficient 41 | * impulse to keep them apart. Colliding bodies may also rebound. 42 | * 43 | * The contact has no callable functions, it just holds the 44 | * contact details. To resolve a set of contacts, use the particle 45 | * contact resolver class. 46 | */ 47 | class ParticleContact 48 | { 49 | // ... Other ParticleContact code as before ... 50 | 51 | 52 | /** 53 | * The contact resolver object needs access into the contacts to 54 | * set and effect the contact. 55 | */ 56 | friend class ParticleContactResolver; 57 | 58 | 59 | public: 60 | /** 61 | * Holds the particles that are involved in the contact. The 62 | * second of these can be NULL, for contacts with the scenery. 63 | */ 64 | Particle* particle[2]; 65 | 66 | /** 67 | * Holds the normal restitution coefficient at the contact. 68 | */ 69 | real restitution; 70 | 71 | /** 72 | * Holds the direction of the contact in world coordinates. 73 | */ 74 | Vector3 contactNormal; 75 | 76 | /** 77 | * Holds the depth of penetration at the contact. 78 | */ 79 | real penetration; 80 | 81 | /** 82 | * Holds the amount each particle is moved by during interpenetration 83 | * resolution. 84 | */ 85 | Vector3 particleMovement[2]; 86 | 87 | protected: 88 | /** 89 | * Resolves this contact, for both velocity and interpenetration. 90 | */ 91 | void resolve(real duration); 92 | 93 | /** 94 | * Calculates the separating velocity at this contact. 95 | */ 96 | real calculateSeparatingVelocity() const; 97 | 98 | private: 99 | /** 100 | * Handles the impulse calculations for this collision. 101 | */ 102 | void resolveVelocity(real duration); 103 | 104 | /** 105 | * Handles the interpenetration resolution for this contact. 106 | */ 107 | void resolveInterpenetration(real duration); 108 | 109 | }; 110 | 111 | /** 112 | * The contact resolution routine for particle contacts. One 113 | * resolver instance can be shared for the whole simulation. 114 | */ 115 | class ParticleContactResolver 116 | { 117 | protected: 118 | /** 119 | * Holds the number of iterations allowed. 120 | */ 121 | unsigned iterations; 122 | 123 | /** 124 | * This is a performance tracking value - we keep a record 125 | * of the actual number of iterations used. 126 | */ 127 | unsigned iterationsUsed; 128 | 129 | public: 130 | /** 131 | * Creates a new contact resolver. 132 | */ 133 | ParticleContactResolver(unsigned iterations); 134 | 135 | /** 136 | * Sets the number of iterations that can be used. 137 | */ 138 | void setIterations(unsigned iterations); 139 | 140 | /** 141 | * Resolves a set of particle contacts for both penetration 142 | * and velocity. 143 | * 144 | * Contacts that cannot interact with each other should be 145 | * passed to separate calls to resolveContacts, as the 146 | * resolution algorithm takes much longer for lots of contacts 147 | * than it does for the same number of contacts in small sets. 148 | * 149 | * @param contactArray Pointer to an array of particle contact 150 | * objects. 151 | * 152 | * @param numContacts The number of contacts in the array to 153 | * resolve. 154 | * 155 | * @param numIterations The number of iterations through the 156 | * resolution algorithm. This should be at least the number of 157 | * contacts (otherwise some constraints will not be resolved - 158 | * although sometimes this is not noticable). If the 159 | * iterations are not needed they will not be used, so adding 160 | * more iterations may not make any difference. But in some 161 | * cases you would need millions of iterations. Think about 162 | * the number of iterations as a bound: if you specify a large 163 | * number, sometimes the algorithm WILL use it, and you may 164 | * drop frames. 165 | * 166 | * @param duration The duration of the previous integration step. 167 | * This is used to compensate for forces applied. 168 | */ 169 | void resolveContacts(ParticleContact *contactArray, 170 | unsigned numContacts, 171 | real duration); 172 | }; 173 | 174 | /** 175 | * This is the basic polymorphic interface for contact generators 176 | * applying to particles. 177 | */ 178 | class ParticleContactGenerator 179 | { 180 | public: 181 | /** 182 | * Fills the given contact structure with the generated 183 | * contact. The contact pointer should point to the first 184 | * available contact in a contact array, where limit is the 185 | * maximum number of contacts in the array that can be written 186 | * to. The method returns the number of contacts that have 187 | * been written. 188 | */ 189 | virtual unsigned addContact(ParticleContact *contact, 190 | unsigned limit) const = 0; 191 | }; 192 | 193 | 194 | 195 | } // namespace cyclone 196 | 197 | #endif // CYCLONE_CONTACTS_H 198 | -------------------------------------------------------------------------------- /src/pcontacts.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Implementation file for the particle contact resolution system. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | 15 | using namespace cyclone; 16 | 17 | // Contact implementation 18 | 19 | void ParticleContact::resolve(real duration) 20 | { 21 | resolveVelocity(duration); 22 | resolveInterpenetration(duration); 23 | } 24 | 25 | real ParticleContact::calculateSeparatingVelocity() const 26 | { 27 | Vector3 relativeVelocity = particle[0]->getVelocity(); 28 | if (particle[1]) relativeVelocity -= particle[1]->getVelocity(); 29 | return relativeVelocity * contactNormal; 30 | } 31 | 32 | void ParticleContact::resolveVelocity(real duration) 33 | { 34 | // Find the velocity in the direction of the contact 35 | real separatingVelocity = calculateSeparatingVelocity(); 36 | 37 | // Check if it needs to be resolved 38 | if (separatingVelocity > 0) 39 | { 40 | // The contact is either separating, or stationary - there's 41 | // no impulse required. 42 | return; 43 | } 44 | 45 | // Calculate the new separating velocity 46 | real newSepVelocity = -separatingVelocity * restitution; 47 | 48 | // Check the velocity build-up due to acceleration only 49 | Vector3 accCausedVelocity = particle[0]->getAcceleration(); 50 | if (particle[1]) accCausedVelocity -= particle[1]->getAcceleration(); 51 | real accCausedSepVelocity = accCausedVelocity * contactNormal * duration; 52 | 53 | // If we've got a closing velocity due to acceleration build-up, 54 | // remove it from the new separating velocity 55 | if (accCausedSepVelocity < 0) 56 | { 57 | newSepVelocity += restitution * accCausedSepVelocity; 58 | 59 | // Make sure we haven't removed more than was 60 | // there to remove. 61 | if (newSepVelocity < 0) newSepVelocity = 0; 62 | } 63 | 64 | real deltaVelocity = newSepVelocity - separatingVelocity; 65 | 66 | // We apply the change in velocity to each object in proportion to 67 | // their inverse mass (i.e. those with lower inverse mass [higher 68 | // actual mass] get less change in velocity).. 69 | real totalInverseMass = particle[0]->getInverseMass(); 70 | if (particle[1]) totalInverseMass += particle[1]->getInverseMass(); 71 | 72 | // If all particles have infinite mass, then impulses have no effect 73 | if (totalInverseMass <= 0) return; 74 | 75 | // Calculate the impulse to apply 76 | real impulse = deltaVelocity / totalInverseMass; 77 | 78 | // Find the amount of impulse per unit of inverse mass 79 | Vector3 impulsePerIMass = contactNormal * impulse; 80 | 81 | // Apply impulses: they are applied in the direction of the contact, 82 | // and are proportional to the inverse mass. 83 | particle[0]->setVelocity(particle[0]->getVelocity() + 84 | impulsePerIMass * particle[0]->getInverseMass() 85 | ); 86 | if (particle[1]) 87 | { 88 | // Particle 1 goes in the opposite direction 89 | particle[1]->setVelocity(particle[1]->getVelocity() + 90 | impulsePerIMass * -particle[1]->getInverseMass() 91 | ); 92 | } 93 | } 94 | 95 | void ParticleContact::resolveInterpenetration(real duration) 96 | { 97 | // If we don't have any penetration, skip this step. 98 | if (penetration <= 0) return; 99 | 100 | // The movement of each object is based on their inverse mass, so 101 | // total that. 102 | real totalInverseMass = particle[0]->getInverseMass(); 103 | if (particle[1]) totalInverseMass += particle[1]->getInverseMass(); 104 | 105 | // If all particles have infinite mass, then we do nothing 106 | if (totalInverseMass <= 0) return; 107 | 108 | // Find the amount of penetration resolution per unit of inverse mass 109 | Vector3 movePerIMass = contactNormal * (penetration / totalInverseMass); 110 | 111 | // Calculate the the movement amounts 112 | particleMovement[0] = movePerIMass * particle[0]->getInverseMass(); 113 | if (particle[1]) { 114 | particleMovement[1] = movePerIMass * -particle[1]->getInverseMass(); 115 | } else { 116 | particleMovement[1].clear(); 117 | } 118 | 119 | // Apply the penetration resolution 120 | particle[0]->setPosition(particle[0]->getPosition() + particleMovement[0]); 121 | if (particle[1]) { 122 | particle[1]->setPosition(particle[1]->getPosition() + particleMovement[1]); 123 | } 124 | } 125 | 126 | ParticleContactResolver::ParticleContactResolver(unsigned iterations) 127 | : 128 | iterations(iterations) 129 | { 130 | } 131 | 132 | void ParticleContactResolver::setIterations(unsigned iterations) 133 | { 134 | ParticleContactResolver::iterations = iterations; 135 | } 136 | 137 | void ParticleContactResolver::resolveContacts(ParticleContact *contactArray, 138 | unsigned numContacts, 139 | real duration) 140 | { 141 | unsigned i; 142 | 143 | iterationsUsed = 0; 144 | while(iterationsUsed < iterations) 145 | { 146 | // Find the contact with the largest closing velocity; 147 | real max = REAL_MAX; 148 | unsigned maxIndex = numContacts; 149 | for (i = 0; i < numContacts; i++) 150 | { 151 | real sepVel = contactArray[i].calculateSeparatingVelocity(); 152 | if (sepVel < max && 153 | (sepVel < 0 || contactArray[i].penetration > 0)) 154 | { 155 | max = sepVel; 156 | maxIndex = i; 157 | } 158 | } 159 | 160 | // Do we have anything worth resolving? 161 | if (maxIndex == numContacts) break; 162 | 163 | // Resolve this contact 164 | contactArray[maxIndex].resolve(duration); 165 | 166 | // Update the interpenetrations for all particles 167 | Vector3 *move = contactArray[maxIndex].particleMovement; 168 | for (i = 0; i < numContacts; i++) 169 | { 170 | if (contactArray[i].particle[0] == contactArray[maxIndex].particle[0]) 171 | { 172 | contactArray[i].penetration -= move[0] * contactArray[i].contactNormal; 173 | } 174 | else if (contactArray[i].particle[0] == contactArray[maxIndex].particle[1]) 175 | { 176 | contactArray[i].penetration -= move[1] * contactArray[i].contactNormal; 177 | } 178 | if (contactArray[i].particle[1]) 179 | { 180 | if (contactArray[i].particle[1] == contactArray[maxIndex].particle[0]) 181 | { 182 | contactArray[i].penetration += move[0] * contactArray[i].contactNormal; 183 | } 184 | else if (contactArray[i].particle[1] == contactArray[maxIndex].particle[1]) 185 | { 186 | contactArray[i].penetration += move[1] * contactArray[i].contactNormal; 187 | } 188 | } 189 | } 190 | 191 | iterationsUsed++; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/pfgen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Implementation file for the particle force generators. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | 15 | using namespace cyclone; 16 | 17 | 18 | void ParticleForceRegistry::updateForces(real duration) 19 | { 20 | Registry::iterator i = registrations.begin(); 21 | for (; i != registrations.end(); i++) 22 | { 23 | i->fg->updateForce(i->particle, duration); 24 | } 25 | } 26 | 27 | void ParticleForceRegistry::add(Particle* particle, ParticleForceGenerator *fg) 28 | { 29 | ParticleForceRegistry::ParticleForceRegistration registration; 30 | registration.particle = particle; 31 | registration.fg = fg; 32 | registrations.push_back(registration); 33 | } 34 | 35 | ParticleGravity::ParticleGravity(const Vector3& gravity) 36 | : gravity(gravity) 37 | { 38 | } 39 | 40 | void ParticleGravity::updateForce(Particle* particle, real duration) 41 | { 42 | // Check that we do not have infinite mass 43 | if (!particle->hasFiniteMass()) return; 44 | 45 | // Apply the mass-scaled force to the particle 46 | particle->addForce(gravity * particle->getMass()); 47 | } 48 | 49 | ParticleDrag::ParticleDrag(real k1, real k2) 50 | : k1(k1), k2(k2) 51 | { 52 | } 53 | 54 | void ParticleDrag::updateForce(Particle* particle, real duration) 55 | { 56 | Vector3 force; 57 | particle->getVelocity(&force); 58 | 59 | // Calculate the total drag coefficient 60 | real dragCoeff = force.magnitude(); 61 | dragCoeff = k1 * dragCoeff + k2 * dragCoeff * dragCoeff; 62 | 63 | // Calculate the final force and apply it 64 | force.normalise(); 65 | force *= -dragCoeff; 66 | particle->addForce(force); 67 | } 68 | 69 | ParticleSpring::ParticleSpring(Particle *other, real sc, real rl) 70 | : other(other), springConstant(sc), restLength(rl) 71 | { 72 | } 73 | 74 | void ParticleSpring::updateForce(Particle* particle, real duration) 75 | { 76 | // Calculate the vector of the spring 77 | Vector3 force; 78 | particle->getPosition(&force); 79 | force -= other->getPosition(); 80 | 81 | // Calculate the magnitude of the force 82 | real magnitude = force.magnitude(); 83 | magnitude = real_abs(magnitude - restLength); 84 | magnitude *= springConstant; 85 | 86 | // Calculate the final force and apply it 87 | force.normalise(); 88 | force *= -magnitude; 89 | particle->addForce(force); 90 | } 91 | 92 | ParticleBuoyancy::ParticleBuoyancy(real maxDepth, 93 | real volume, 94 | real waterHeight, 95 | real liquidDensity) 96 | : 97 | maxDepth(maxDepth), volume(volume), 98 | waterHeight(waterHeight), liquidDensity(liquidDensity) 99 | { 100 | } 101 | 102 | void ParticleBuoyancy::updateForce(Particle* particle, real duration) 103 | { 104 | // Calculate the submersion depth 105 | real depth = particle->getPosition().y; 106 | 107 | // Check if we're out of the water 108 | if (depth >= waterHeight + maxDepth) return; 109 | Vector3 force(0,0,0); 110 | 111 | // Check if we're at maximum depth 112 | if (depth <= waterHeight - maxDepth) 113 | { 114 | force.y = liquidDensity * volume; 115 | particle->addForce(force); 116 | return; 117 | } 118 | 119 | // Otherwise we are partly submerged 120 | force.y = liquidDensity * volume * 121 | (depth - maxDepth - waterHeight) / (2 * maxDepth); 122 | particle->addForce(force); 123 | } 124 | 125 | ParticleBungee::ParticleBungee(Particle *other, real sc, real rl) 126 | : other(other), springConstant(sc), restLength(rl) 127 | { 128 | } 129 | 130 | void ParticleBungee::updateForce(Particle* particle, real duration) 131 | { 132 | // Calculate the vector of the spring 133 | Vector3 force; 134 | particle->getPosition(&force); 135 | force -= other->getPosition(); 136 | 137 | // Check if the bungee is compressed 138 | real magnitude = force.magnitude(); 139 | if (magnitude <= restLength) return; 140 | 141 | // Calculate the magnitude of the force 142 | magnitude = springConstant * (restLength - magnitude); 143 | 144 | // Calculate the final force and apply it 145 | force.normalise(); 146 | force *= -magnitude; 147 | particle->addForce(force); 148 | } 149 | 150 | ParticleFakeSpring::ParticleFakeSpring(Vector3 *anchor, real sc, real d) 151 | : anchor(anchor), springConstant(sc), damping(d) 152 | { 153 | } 154 | 155 | void ParticleFakeSpring::updateForce(Particle* particle, real duration) 156 | { 157 | // Check that we do not have infinite mass 158 | if (!particle->hasFiniteMass()) return; 159 | 160 | // Calculate the relative position of the particle to the anchor 161 | Vector3 position; 162 | particle->getPosition(&position); 163 | position -= *anchor; 164 | 165 | // Calculate the constants and check they are in bounds. 166 | real gamma = 0.5f * real_sqrt(4 * springConstant - damping*damping); 167 | if (gamma == 0.0f) return; 168 | Vector3 c = position * (damping / (2.0f * gamma)) + 169 | particle->getVelocity() * (1.0f / gamma); 170 | 171 | // Calculate the target position 172 | Vector3 target = position * real_cos(gamma * duration) + 173 | c * real_sin(gamma * duration); 174 | target *= real_exp(-0.5f * duration * damping); 175 | 176 | // Calculate the resulting acceleration and therefore the force 177 | Vector3 accel = (target - position) * ((real)1.0 / (duration*duration)) - 178 | particle->getVelocity() * ((real)1.0/duration); 179 | particle->addForce(accel * particle->getMass()); 180 | } 181 | 182 | ParticleAnchoredSpring::ParticleAnchoredSpring() 183 | { 184 | } 185 | 186 | ParticleAnchoredSpring::ParticleAnchoredSpring(Vector3 *anchor, 187 | real sc, real rl) 188 | : anchor(anchor), springConstant(sc), restLength(rl) 189 | { 190 | } 191 | 192 | void ParticleAnchoredSpring::init(Vector3 *anchor, real springConstant, 193 | real restLength) 194 | { 195 | ParticleAnchoredSpring::anchor = anchor; 196 | ParticleAnchoredSpring::springConstant = springConstant; 197 | ParticleAnchoredSpring::restLength = restLength; 198 | } 199 | 200 | void ParticleAnchoredBungee::updateForce(Particle* particle, real duration) 201 | { 202 | // Calculate the vector of the spring 203 | Vector3 force; 204 | particle->getPosition(&force); 205 | force -= *anchor; 206 | 207 | // Calculate the magnitude of the force 208 | real magnitude = force.magnitude(); 209 | if (magnitude < restLength) return; 210 | 211 | magnitude = magnitude - restLength; 212 | magnitude *= springConstant; 213 | 214 | // Calculate the final force and apply it 215 | force.normalise(); 216 | force *= -magnitude; 217 | particle->addForce(force); 218 | } 219 | 220 | void ParticleAnchoredSpring::updateForce(Particle* particle, real duration) 221 | { 222 | // Calculate the vector of the spring 223 | Vector3 force; 224 | particle->getPosition(&force); 225 | force -= *anchor; 226 | 227 | // Calculate the magnitude of the force 228 | real magnitude = force.magnitude(); 229 | magnitude = (restLength - magnitude) * springConstant; 230 | 231 | // Calculate the final force and apply it 232 | force.normalise(); 233 | force *= magnitude; 234 | particle->addForce(force); 235 | } 236 | -------------------------------------------------------------------------------- /doc/build/doxygen: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.3-rc3 2 | 3 | #--------------------------------------------------------------------------- 4 | # General configuration options 5 | #--------------------------------------------------------------------------- 6 | PROJECT_NAME = Cyclone 7 | PROJECT_NUMBER = "Version 2.0.5" 8 | OUTPUT_DIRECTORY = c:/data/writing/physeng/software/ 9 | OUTPUT_LANGUAGE = English 10 | EXTRACT_ALL = YES 11 | EXTRACT_PRIVATE = NO 12 | EXTRACT_STATIC = YES 13 | EXTRACT_LOCAL_CLASSES = NO 14 | HIDE_UNDOC_MEMBERS = NO 15 | HIDE_UNDOC_CLASSES = NO 16 | HIDE_FRIEND_COMPOUNDS = NO 17 | HIDE_IN_BODY_DOCS = NO 18 | BRIEF_MEMBER_DESC = YES 19 | REPEAT_BRIEF = YES 20 | ALWAYS_DETAILED_SEC = YES 21 | INLINE_INHERITED_MEMB = NO 22 | FULL_PATH_NAMES = YES 23 | STRIP_FROM_PATH = c:/data/writing/physeng/software/include/ 24 | INTERNAL_DOCS = NO 25 | CASE_SENSE_NAMES = YES 26 | SHORT_NAMES = NO 27 | HIDE_SCOPE_NAMES = NO 28 | VERBATIM_HEADERS = YES 29 | SHOW_INCLUDE_FILES = YES 30 | JAVADOC_AUTOBRIEF = YES 31 | MULTILINE_CPP_IS_BRIEF = NO 32 | DETAILS_AT_TOP = NO 33 | INHERIT_DOCS = YES 34 | INLINE_INFO = YES 35 | SORT_MEMBER_DOCS = YES 36 | DISTRIBUTE_GROUP_DOC = NO 37 | TAB_SIZE = 8 38 | GENERATE_TODOLIST = YES 39 | GENERATE_TESTLIST = YES 40 | GENERATE_BUGLIST = YES 41 | GENERATE_DEPRECATEDLIST= YES 42 | ALIASES = 43 | ENABLED_SECTIONS = 44 | MAX_INITIALIZER_LINES = 30 45 | OPTIMIZE_OUTPUT_FOR_C = NO 46 | OPTIMIZE_OUTPUT_JAVA = NO 47 | SHOW_USED_FILES = YES 48 | #--------------------------------------------------------------------------- 49 | # configuration options related to warning and progress messages 50 | #--------------------------------------------------------------------------- 51 | QUIET = NO 52 | WARNINGS = YES 53 | WARN_IF_UNDOCUMENTED = YES 54 | WARN_IF_DOC_ERROR = YES 55 | WARN_FORMAT = "$file:$line: $text" 56 | WARN_LOGFILE = warn.log 57 | #--------------------------------------------------------------------------- 58 | # configuration options related to the input files 59 | #--------------------------------------------------------------------------- 60 | INPUT = ../../include 61 | FILE_PATTERNS = *.h 62 | RECURSIVE = YES 63 | EXCLUDE = 64 | EXCLUDE_SYMLINKS = NO 65 | EXCLUDE_PATTERNS = 66 | EXAMPLE_PATH = 67 | EXAMPLE_PATTERNS = 68 | EXAMPLE_RECURSIVE = NO 69 | IMAGE_PATH = 70 | INPUT_FILTER = 71 | FILTER_SOURCE_FILES = NO 72 | #--------------------------------------------------------------------------- 73 | # configuration options related to source browsing 74 | #--------------------------------------------------------------------------- 75 | SOURCE_BROWSER = NO 76 | INLINE_SOURCES = NO 77 | STRIP_CODE_COMMENTS = YES 78 | REFERENCED_BY_RELATION = YES 79 | REFERENCES_RELATION = YES 80 | #--------------------------------------------------------------------------- 81 | # configuration options related to the alphabetical class index 82 | #--------------------------------------------------------------------------- 83 | ALPHABETICAL_INDEX = YES 84 | COLS_IN_ALPHA_INDEX = 5 85 | IGNORE_PREFIX = 86 | #--------------------------------------------------------------------------- 87 | # configuration options related to the HTML output 88 | #--------------------------------------------------------------------------- 89 | GENERATE_HTML = YES 90 | HTML_OUTPUT = c:/data/writing/physeng/software/doc/doc/ 91 | HTML_FILE_EXTENSION = .html 92 | HTML_HEADER = ../src/header.html 93 | HTML_FOOTER = ../src/footer.html 94 | HTML_STYLESHEET = ../src/stylesheet.css 95 | HTML_ALIGN_MEMBERS = YES 96 | GENERATE_HTMLHELP = NO 97 | CHM_FILE = cyclone.chm 98 | HHC_LOCATION = 99 | GENERATE_CHI = YES 100 | BINARY_TOC = NO 101 | TOC_EXPAND = YES 102 | DISABLE_INDEX = NO 103 | ENUM_VALUES_PER_LINE = 4 104 | GENERATE_TREEVIEW = YES 105 | TREEVIEW_WIDTH = 250 106 | #--------------------------------------------------------------------------- 107 | # configuration options related to the LaTeX output 108 | #--------------------------------------------------------------------------- 109 | GENERATE_LATEX = NO 110 | LATEX_OUTPUT = latex 111 | LATEX_CMD_NAME = latex 112 | MAKEINDEX_CMD_NAME = makeindex 113 | COMPACT_LATEX = NO 114 | PAPER_TYPE = a4wide 115 | EXTRA_PACKAGES = 116 | LATEX_HEADER = 117 | PDF_HYPERLINKS = NO 118 | USE_PDFLATEX = NO 119 | LATEX_BATCHMODE = NO 120 | #--------------------------------------------------------------------------- 121 | # configuration options related to the RTF output 122 | #--------------------------------------------------------------------------- 123 | GENERATE_RTF = NO 124 | RTF_OUTPUT = rtf 125 | COMPACT_RTF = NO 126 | RTF_HYPERLINKS = NO 127 | RTF_STYLESHEET_FILE = 128 | RTF_EXTENSIONS_FILE = 129 | #--------------------------------------------------------------------------- 130 | # configuration options related to the man page output 131 | #--------------------------------------------------------------------------- 132 | GENERATE_MAN = NO 133 | MAN_OUTPUT = man 134 | MAN_EXTENSION = .3 135 | MAN_LINKS = NO 136 | #--------------------------------------------------------------------------- 137 | # configuration options related to the XML output 138 | #--------------------------------------------------------------------------- 139 | GENERATE_XML = NO 140 | XML_SCHEMA = 141 | XML_DTD = 142 | #--------------------------------------------------------------------------- 143 | # configuration options for the AutoGen Definitions output 144 | #--------------------------------------------------------------------------- 145 | GENERATE_AUTOGEN_DEF = NO 146 | #--------------------------------------------------------------------------- 147 | # configuration options related to the Perl module output 148 | #--------------------------------------------------------------------------- 149 | GENERATE_PERLMOD = NO 150 | PERLMOD_LATEX = NO 151 | PERLMOD_PRETTY = YES 152 | PERLMOD_MAKEVAR_PREFIX = 153 | #--------------------------------------------------------------------------- 154 | # Configuration options related to the preprocessor 155 | #--------------------------------------------------------------------------- 156 | ENABLE_PREPROCESSING = YES 157 | MACRO_EXPANSION = NO 158 | EXPAND_ONLY_PREDEF = NO 159 | SEARCH_INCLUDES = YES 160 | INCLUDE_PATH = 161 | INCLUDE_FILE_PATTERNS = 162 | PREDEFINED = 163 | EXPAND_AS_DEFINED = 164 | SKIP_FUNCTION_MACROS = YES 165 | #--------------------------------------------------------------------------- 166 | # Configuration::addtions related to external references 167 | #--------------------------------------------------------------------------- 168 | TAGFILES = 169 | GENERATE_TAGFILE = 170 | ALLEXTERNALS = NO 171 | EXTERNAL_GROUPS = YES 172 | PERL_PATH = /usr/bin/perl 173 | #--------------------------------------------------------------------------- 174 | # Configuration options related to the dot tool 175 | #--------------------------------------------------------------------------- 176 | CLASS_DIAGRAMS = YES 177 | HIDE_UNDOC_RELATIONS = YES 178 | HAVE_DOT = YES 179 | CLASS_GRAPH = YES 180 | COLLABORATION_GRAPH = YES 181 | TEMPLATE_RELATIONS = YES 182 | INCLUDE_GRAPH = YES 183 | INCLUDED_BY_GRAPH = YES 184 | GRAPHICAL_HIERARCHY = YES 185 | DOT_IMAGE_FORMAT = png 186 | DOT_PATH = C:\Program Files\ATT\Graphviz\bin 187 | DOTFILE_DIRS = 188 | MAX_DOT_GRAPH_WIDTH = 700 189 | MAX_DOT_GRAPH_HEIGHT = 1024 190 | GENERATE_LEGEND = YES 191 | DOT_CLEANUP = YES 192 | -------------------------------------------------------------------------------- /src/demos/platform/platform.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The platform demo. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | #include "../ogl_headers.h" 15 | #include "../app.h" 16 | #include "../timing.h" 17 | 18 | #include 19 | #include 20 | 21 | #define ROD_COUNT 15 22 | 23 | #define BASE_MASS 1 24 | #define EXTRA_MASS 10 25 | 26 | /** 27 | * The main demo class definition. 28 | */ 29 | class PlatformDemo : public MassAggregateApplication 30 | { 31 | cyclone::ParticleRod *rods; 32 | 33 | cyclone::Vector3 massPos; 34 | cyclone::Vector3 massDisplayPos; 35 | 36 | /** 37 | * Updates particle masses to take into account the mass 38 | * that's on the platform. 39 | */ 40 | void updateAdditionalMass(); 41 | 42 | public: 43 | /** Creates a new demo object. */ 44 | PlatformDemo(); 45 | virtual ~PlatformDemo(); 46 | 47 | /** Returns the window title for the demo. */ 48 | virtual const char* getTitle(); 49 | 50 | /** Display the particles. */ 51 | virtual void display(); 52 | 53 | /** Update the particle positions. */ 54 | virtual void update(); 55 | 56 | /** Handle a key press. */ 57 | virtual void key(unsigned char key); 58 | }; 59 | 60 | // Method definitions 61 | PlatformDemo::PlatformDemo() 62 | : 63 | MassAggregateApplication(6), rods(0), 64 | massPos(0,0,0.5f) 65 | { 66 | // Create the masses and connections. 67 | particleArray[0].setPosition(0,0,1); 68 | particleArray[1].setPosition(0,0,-1); 69 | particleArray[2].setPosition(-3,2,1); 70 | particleArray[3].setPosition(-3,2,-1); 71 | particleArray[4].setPosition(4,2,1); 72 | particleArray[5].setPosition(4,2,-1); 73 | for (unsigned i = 0; i < 6; i++) 74 | { 75 | particleArray[i].setMass(BASE_MASS); 76 | particleArray[i].setVelocity(0,0,0); 77 | particleArray[i].setDamping(0.9f); 78 | particleArray[i].setAcceleration(cyclone::Vector3::GRAVITY); 79 | particleArray[i].clearAccumulator(); 80 | } 81 | 82 | rods = new cyclone::ParticleRod[ROD_COUNT]; 83 | 84 | rods[0].particle[0] = &particleArray[0]; 85 | rods[0].particle[1] = &particleArray[1]; 86 | rods[0].length = 2; 87 | rods[1].particle[0] = &particleArray[2]; 88 | rods[1].particle[1] = &particleArray[3]; 89 | rods[1].length = 2; 90 | rods[2].particle[0] = &particleArray[4]; 91 | rods[2].particle[1] = &particleArray[5]; 92 | rods[2].length = 2; 93 | 94 | rods[3].particle[0] = &particleArray[2]; 95 | rods[3].particle[1] = &particleArray[4]; 96 | rods[3].length = 7; 97 | rods[4].particle[0] = &particleArray[3]; 98 | rods[4].particle[1] = &particleArray[5]; 99 | rods[4].length = 7; 100 | 101 | rods[5].particle[0] = &particleArray[0]; 102 | rods[5].particle[1] = &particleArray[2]; 103 | rods[5].length = 3.606; 104 | rods[6].particle[0] = &particleArray[1]; 105 | rods[6].particle[1] = &particleArray[3]; 106 | rods[6].length = 3.606; 107 | 108 | rods[7].particle[0] = &particleArray[0]; 109 | rods[7].particle[1] = &particleArray[4]; 110 | rods[7].length = 4.472; 111 | rods[8].particle[0] = &particleArray[1]; 112 | rods[8].particle[1] = &particleArray[5]; 113 | rods[8].length = 4.472; 114 | 115 | rods[9].particle[0] = &particleArray[0]; 116 | rods[9].particle[1] = &particleArray[3]; 117 | rods[9].length = 4.123; 118 | rods[10].particle[0] = &particleArray[2]; 119 | rods[10].particle[1] = &particleArray[5]; 120 | rods[10].length = 7.28; 121 | rods[11].particle[0] = &particleArray[4]; 122 | rods[11].particle[1] = &particleArray[1]; 123 | rods[11].length = 4.899; 124 | rods[12].particle[0] = &particleArray[1]; 125 | rods[12].particle[1] = &particleArray[2]; 126 | rods[12].length = 4.123; 127 | rods[13].particle[0] = &particleArray[3]; 128 | rods[13].particle[1] = &particleArray[4]; 129 | rods[13].length = 7.28; 130 | rods[14].particle[0] = &particleArray[5]; 131 | rods[14].particle[1] = &particleArray[0]; 132 | rods[14].length = 4.899; 133 | 134 | for (unsigned i = 0; i < ROD_COUNT; i++) 135 | { 136 | world.getContactGenerators().push_back(&rods[i]); 137 | } 138 | 139 | updateAdditionalMass(); 140 | } 141 | 142 | PlatformDemo::~PlatformDemo() 143 | { 144 | if (rods) delete[] rods; 145 | } 146 | 147 | void PlatformDemo::updateAdditionalMass() 148 | { 149 | for (unsigned i = 2; i < 6; i++) 150 | { 151 | particleArray[i].setMass(BASE_MASS); 152 | } 153 | 154 | // Find the coordinates of the mass as an index and proportion 155 | cyclone::real xp = massPos.x; 156 | if (xp < 0) xp = 0; 157 | if (xp > 1) xp = 1; 158 | 159 | cyclone::real zp = massPos.z; 160 | if (zp < 0) zp = 0; 161 | if (zp > 1) zp = 1; 162 | 163 | // Calculate where to draw the mass 164 | massDisplayPos.clear(); 165 | 166 | // Add the proportion to the correct masses 167 | particleArray[2].setMass(BASE_MASS + EXTRA_MASS*(1-xp)*(1-zp)); 168 | massDisplayPos.addScaledVector( 169 | particleArray[2].getPosition(), (1-xp)*(1-zp) 170 | ); 171 | 172 | if (xp > 0) 173 | { 174 | particleArray[4].setMass(BASE_MASS + EXTRA_MASS*xp*(1-zp)); 175 | massDisplayPos.addScaledVector( 176 | particleArray[4].getPosition(), xp*(1-zp) 177 | ); 178 | 179 | if (zp > 0) 180 | { 181 | particleArray[5].setMass(BASE_MASS + EXTRA_MASS*xp*zp); 182 | massDisplayPos.addScaledVector( 183 | particleArray[5].getPosition(), xp*zp 184 | ); 185 | } 186 | } 187 | if (zp > 0) 188 | { 189 | particleArray[3].setMass(BASE_MASS + EXTRA_MASS*(1-xp)*zp); 190 | massDisplayPos.addScaledVector( 191 | particleArray[3].getPosition(), (1-xp)*zp 192 | ); 193 | } 194 | } 195 | 196 | void PlatformDemo::display() 197 | { 198 | MassAggregateApplication::display(); 199 | 200 | glBegin(GL_LINES); 201 | glColor3f(0,0,1); 202 | for (unsigned i = 0; i < ROD_COUNT; i++) 203 | { 204 | cyclone::Particle **particles = rods[i].particle; 205 | const cyclone::Vector3 &p0 = particles[0]->getPosition(); 206 | const cyclone::Vector3 &p1 = particles[1]->getPosition(); 207 | glVertex3f(p0.x, p0.y, p0.z); 208 | glVertex3f(p1.x, p1.y, p1.z); 209 | } 210 | glEnd(); 211 | 212 | glColor3f(1,0,0); 213 | glPushMatrix(); 214 | glTranslatef(massDisplayPos.x, massDisplayPos.y+0.25f, massDisplayPos.z); 215 | glutSolidSphere(0.25f, 20, 10); 216 | glPopMatrix(); 217 | } 218 | 219 | void PlatformDemo::update() 220 | { 221 | MassAggregateApplication::update(); 222 | 223 | updateAdditionalMass(); 224 | } 225 | 226 | const char* PlatformDemo::getTitle() 227 | { 228 | return "Cyclone > Platform Demo"; 229 | } 230 | 231 | void PlatformDemo::key(unsigned char key) 232 | { 233 | switch(key) 234 | { 235 | case 'w': case 'W': 236 | massPos.z += 0.1f; 237 | if (massPos.z > 1.0f) massPos.z = 1.0f; 238 | break; 239 | case 's': case 'S': 240 | massPos.z -= 0.1f; 241 | if (massPos.z < 0.0f) massPos.z = 0.0f; 242 | break; 243 | case 'a': case 'A': 244 | massPos.x -= 0.1f; 245 | if (massPos.x < 0.0f) massPos.x = 0.0f; 246 | break; 247 | case 'd': case 'D': 248 | massPos.x += 0.1f; 249 | if (massPos.x > 1.0f) massPos.x = 1.0f; 250 | break; 251 | 252 | default: 253 | MassAggregateApplication::key(key); 254 | } 255 | } 256 | 257 | /** 258 | * Called by the common demo framework to create an application 259 | * object (with new) and return a pointer. 260 | */ 261 | Application* getApplication() 262 | { 263 | return new PlatformDemo(); 264 | } -------------------------------------------------------------------------------- /src/demos/app.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The base application class for all demos. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | #include 13 | 14 | #include 15 | 16 | /** 17 | * An application is the base class for all demonstration progams. 18 | * GLUT is a c-style API, which calls bare functions. This makes 19 | * it more difficult to provide default services for all demos and 20 | * only override them when needed. 21 | * 22 | * To solve this, the GLUT API is translated into calls on a 23 | * generic application object. Each demonstration will create a 24 | * concrete subclass of Application, providing the behaviours it 25 | * needs. The common code for all demos manages dispatch of 26 | * requests to the appropriate application object. 27 | * 28 | * To provide a correct application object of the right type without 29 | * the core code needing to know which subclass is being used, each 30 | * demonstration will supply a getApplication function which creates 31 | * (with new) and returns a pointer to a new Application instance. 32 | * 33 | * Even though subclasses will have to implement most of the methods 34 | * in this class, I have not made them pure virtual. This saves the 35 | * annoying need to implement an empty function that isn't needed. 36 | */ 37 | class Application 38 | { 39 | protected: 40 | /** 41 | * Holds the height of the application window. 42 | */ 43 | int height; 44 | 45 | /** 46 | * Holds the current width of the application window. 47 | */ 48 | int width; 49 | 50 | public: 51 | /** 52 | * Gets the title of the demo for the title bar of the window. 53 | * 54 | * The default implementation returns a generic title. 55 | */ 56 | virtual const char* getTitle(); 57 | 58 | /** 59 | * Sets up the graphics, and allows the application to acquire 60 | * graphical resources. Guaranteed to be called after OpenGL is 61 | * set up. 62 | * 63 | * The default implementation sets up a basic view, and calls 64 | * setView to set up the camera projection. 65 | */ 66 | virtual void initGraphics(); 67 | 68 | /** 69 | * Called to set the projection characteristics of the camera. 70 | * 71 | * The default implementation uses a 60 degree field of view camera 72 | * with a range from 1-500 units. 73 | */ 74 | virtual void setView(); 75 | 76 | /** 77 | * Called just before the application is destroyed. Clear up can 78 | * be performed here or in the application destructor. 79 | * 80 | * The default implementation does nothing. 81 | */ 82 | virtual void deinit(); 83 | 84 | /** 85 | * Called each frame to display the current scene. The common code 86 | * will automatically flush the graphics pipe and swap the render 87 | * buffers after calling this so glFlush doesn't need to be called. 88 | * 89 | * The default 90 | * implementation draws a simple diagonal line across the surface 91 | * (as a sanity check to make sure GL is working). 92 | */ 93 | virtual void display(); 94 | 95 | /** 96 | * Called each frame to update the current state of the scene. 97 | * 98 | * The default implementation requests that the display be refreshed. 99 | * It should probably be called from any subclass update as the last 100 | * command. 101 | */ 102 | virtual void update(); 103 | 104 | /** 105 | * Called when a keypress is detected. 106 | * 107 | * The default implementation does nothing. 108 | * 109 | * @param key The ascii code of the key that has been pressed. 110 | */ 111 | virtual void key(unsigned char key); 112 | 113 | 114 | /** 115 | * Notifies the application that the window has changed size. 116 | * The new size is given. 117 | * 118 | * The default implementation sets the internal height and width 119 | * parameters and changes the gl viewport. These are steps you'll 120 | * almost always need, so its worth calling the base class version 121 | * of this method even if you override it in a demo class. 122 | */ 123 | virtual void resize(int width, int height); 124 | 125 | /** 126 | * Called when GLUT detects a mouse button press. 127 | * 128 | * The default implementation does nothing. 129 | */ 130 | virtual void mouse(int button, int state, int x, int y); 131 | 132 | /** 133 | * Called when GLUT detects a mouse drag. 134 | * 135 | * The default implementation does nothing. 136 | */ 137 | virtual void mouseDrag(int x, int y); 138 | 139 | // These are helper functions that can be used by an application 140 | // to render things. 141 | 142 | /** 143 | * Renders the given text to the given x,y location (in screen space) 144 | * on the window. This is used to pass status information to the 145 | * application. 146 | */ 147 | void renderText(float x, float y, const char *text, void* font=NULL); 148 | }; 149 | 150 | /** 151 | * This application adds additional functionality used in the mass-aggregate demos. 152 | */ 153 | class MassAggregateApplication : public Application 154 | { 155 | protected: 156 | cyclone::ParticleWorld world; 157 | cyclone::Particle *particleArray; 158 | cyclone::GroundContacts groundContactGenerator; 159 | 160 | public: 161 | MassAggregateApplication(unsigned int particleCount); 162 | virtual ~MassAggregateApplication(); 163 | 164 | /** Update the particle positions. */ 165 | virtual void update(); 166 | 167 | /** Sets up the graphic rendering. */ 168 | virtual void initGraphics(); 169 | 170 | /** Display the particles. */ 171 | virtual void display(); 172 | }; 173 | 174 | /** 175 | * This application adds additional functionality used in many of the 176 | * demos. This includes the ability to track contacts (for rigid bodies) 177 | * and move the camera around. 178 | */ 179 | class RigidBodyApplication : public Application 180 | { 181 | protected: 182 | /** Holds the maximum number of contacts. */ 183 | const static unsigned maxContacts = 256; 184 | 185 | /** Holds the array of contacts. */ 186 | cyclone::Contact contacts[maxContacts]; 187 | 188 | /** Holds the collision data structure for collision detection. */ 189 | cyclone::CollisionData cData; 190 | 191 | /** Holds the contact resolver. */ 192 | cyclone::ContactResolver resolver; 193 | 194 | /** Holds the camera angle. */ 195 | float theta; 196 | 197 | /** Holds the camera elevation. */ 198 | float phi; 199 | 200 | /** Holds the position of the mouse at the last frame of a drag. */ 201 | int last_x, last_y; 202 | 203 | /** True if the contacts should be rendered. */ 204 | bool renderDebugInfo; 205 | 206 | /** True if the simulation is paused. */ 207 | bool pauseSimulation; 208 | 209 | /** Pauses the simulation after the next frame automatically */ 210 | bool autoPauseSimulation; 211 | 212 | /** Processes the contact generation code. */ 213 | virtual void generateContacts() = 0; 214 | 215 | /** Processes the objects in the simulation forward in time. */ 216 | virtual void updateObjects(cyclone::real duration) = 0; 217 | 218 | /** 219 | * Finishes drawing the frame, adding debugging information 220 | * as needed. 221 | */ 222 | void drawDebug(); 223 | 224 | /** Resets the simulation. */ 225 | virtual void reset() = 0; 226 | 227 | public: 228 | /** 229 | * Creates a new application object. 230 | */ 231 | RigidBodyApplication(); 232 | 233 | /** Display the application. */ 234 | virtual void display(); 235 | 236 | /** Update the objects. */ 237 | virtual void update(); 238 | 239 | /** Handle a mouse click. */ 240 | virtual void mouse(int button, int state, int x, int y); 241 | 242 | /** Handle a mouse drag */ 243 | virtual void mouseDrag(int x, int y); 244 | 245 | /** Handles a key press. */ 246 | virtual void key(unsigned char key); 247 | }; 248 | -------------------------------------------------------------------------------- /src/demos/bridge/bridge.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The bridge demo. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | #include "../ogl_headers.h" 15 | #include "../app.h" 16 | #include "../timing.h" 17 | 18 | #include 19 | #include 20 | 21 | #define ROD_COUNT 6 22 | #define CABLE_COUNT 10 23 | #define SUPPORT_COUNT 12 24 | 25 | #define BASE_MASS 1 26 | #define EXTRA_MASS 10 27 | 28 | /** 29 | * The main demo class definition. 30 | */ 31 | class BridgeDemo : public MassAggregateApplication 32 | { 33 | cyclone::ParticleCableConstraint *supports; 34 | cyclone::ParticleCable *cables; 35 | cyclone::ParticleRod *rods; 36 | 37 | cyclone::Vector3 massPos; 38 | cyclone::Vector3 massDisplayPos; 39 | 40 | /** 41 | * Updates particle masses to take into account the mass 42 | * that's crossing the bridge. 43 | */ 44 | void updateAdditionalMass(); 45 | 46 | public: 47 | /** Creates a new demo object. */ 48 | BridgeDemo(); 49 | virtual ~BridgeDemo(); 50 | 51 | /** Returns the window title for the demo. */ 52 | virtual const char* getTitle(); 53 | 54 | /** Display the particles. */ 55 | virtual void display(); 56 | 57 | /** Update the particle positions. */ 58 | virtual void update(); 59 | 60 | /** Handle a key press. */ 61 | virtual void key(unsigned char key); 62 | }; 63 | 64 | // Method definitions 65 | BridgeDemo::BridgeDemo() 66 | : 67 | MassAggregateApplication(12), cables(0), supports(0), rods(0), 68 | massPos(0,0,0.5f) 69 | { 70 | // Create the masses and connections. 71 | for (unsigned i = 0; i < 12; i++) 72 | { 73 | unsigned x = (i%12)/2; 74 | particleArray[i].setPosition( 75 | cyclone::real(i/2)*2.0f-5.0f, 76 | 4, 77 | cyclone::real(i%2)*2.0f-1.0f 78 | ); 79 | particleArray[i].setVelocity(0,0,0); 80 | particleArray[i].setDamping(0.9f); 81 | particleArray[i].setAcceleration(cyclone::Vector3::GRAVITY); 82 | particleArray[i].clearAccumulator(); 83 | } 84 | 85 | // Add the links 86 | cables = new cyclone::ParticleCable[CABLE_COUNT]; 87 | for (unsigned i = 0; i < 10; i++) 88 | { 89 | cables[i].particle[0] = &particleArray[i]; 90 | cables[i].particle[1] = &particleArray[i+2]; 91 | cables[i].maxLength = 1.9f; 92 | cables[i].restitution = 0.3f; 93 | world.getContactGenerators().push_back(&cables[i]); 94 | } 95 | 96 | supports = new cyclone::ParticleCableConstraint[SUPPORT_COUNT]; 97 | for (unsigned i = 0; i < SUPPORT_COUNT; i++) 98 | { 99 | supports[i].particle = particleArray+i; 100 | supports[i].anchor = cyclone::Vector3( 101 | cyclone::real(i/2)*2.2f-5.5f, 102 | 6, 103 | cyclone::real(i%2)*1.6f-0.8f 104 | ); 105 | if (i < 6) supports[i].maxLength = cyclone::real(i/2)*0.5f + 3.0f; 106 | else supports[i].maxLength = 5.5f - cyclone::real(i/2)*0.5f; 107 | supports[i].restitution = 0.5f; 108 | world.getContactGenerators().push_back(&supports[i]); 109 | } 110 | 111 | rods = new cyclone::ParticleRod[ROD_COUNT]; 112 | for (unsigned i = 0; i < 6; i++) 113 | { 114 | rods[i].particle[0] = &particleArray[i*2]; 115 | rods[i].particle[1] = &particleArray[i*2+1]; 116 | rods[i].length = 2; 117 | world.getContactGenerators().push_back(&rods[i]); 118 | } 119 | 120 | updateAdditionalMass(); 121 | } 122 | 123 | BridgeDemo::~BridgeDemo() 124 | { 125 | if (cables) delete[] cables; 126 | if (rods) delete[] rods; 127 | if (supports) delete[] supports; 128 | } 129 | 130 | void BridgeDemo::updateAdditionalMass() 131 | { 132 | for (unsigned i = 0; i < 12; i++) 133 | { 134 | particleArray[i].setMass(BASE_MASS); 135 | } 136 | 137 | // Find the coordinates of the mass as an index and proportion 138 | int x = int(massPos.x); 139 | cyclone::real xp = real_fmod(massPos.x, cyclone::real(1.0f)); 140 | if (x < 0) 141 | { 142 | x = 0; 143 | xp = 0; 144 | } 145 | if (x >= 5) 146 | { 147 | x = 5; 148 | xp = 0; 149 | } 150 | 151 | int z = int(massPos.z); 152 | cyclone::real zp = real_fmod(massPos.z, cyclone::real(1.0f)); 153 | if (z < 0) 154 | { 155 | z = 0; 156 | zp = 0; 157 | } 158 | if (z >= 1) 159 | { 160 | z = 1; 161 | zp = 0; 162 | } 163 | 164 | // Calculate where to draw the mass 165 | massDisplayPos.clear(); 166 | 167 | // Add the proportion to the correct masses 168 | particleArray[x*2+z].setMass(BASE_MASS + EXTRA_MASS*(1-xp)*(1-zp)); 169 | massDisplayPos.addScaledVector( 170 | particleArray[x*2+z].getPosition(), (1-xp)*(1-zp) 171 | ); 172 | 173 | if (xp > 0) 174 | { 175 | particleArray[x*2+z+2].setMass(BASE_MASS + EXTRA_MASS*xp*(1-zp)); 176 | massDisplayPos.addScaledVector( 177 | particleArray[x*2+z+2].getPosition(), xp*(1-zp) 178 | ); 179 | 180 | if (zp > 0) 181 | { 182 | particleArray[x*2+z+3].setMass(BASE_MASS + EXTRA_MASS*xp*zp); 183 | massDisplayPos.addScaledVector( 184 | particleArray[x*2+z+3].getPosition(), xp*zp 185 | ); 186 | } 187 | } 188 | if (zp > 0) 189 | { 190 | particleArray[x*2+z+1].setMass(BASE_MASS + EXTRA_MASS*(1-xp)*zp); 191 | massDisplayPos.addScaledVector( 192 | particleArray[x*2+z+1].getPosition(), (1-xp)*zp 193 | ); 194 | } 195 | } 196 | 197 | void BridgeDemo::display() 198 | { 199 | MassAggregateApplication::display(); 200 | 201 | glBegin(GL_LINES); 202 | glColor3f(0,0,1); 203 | for (unsigned i = 0; i < ROD_COUNT; i++) 204 | { 205 | cyclone::Particle **particles = rods[i].particle; 206 | const cyclone::Vector3 &p0 = particles[0]->getPosition(); 207 | const cyclone::Vector3 &p1 = particles[1]->getPosition(); 208 | glVertex3f(p0.x, p0.y, p0.z); 209 | glVertex3f(p1.x, p1.y, p1.z); 210 | } 211 | 212 | glColor3f(0,1,0); 213 | for (unsigned i = 0; i < CABLE_COUNT; i++) 214 | { 215 | cyclone::Particle **particles = cables[i].particle; 216 | const cyclone::Vector3 &p0 = particles[0]->getPosition(); 217 | const cyclone::Vector3 &p1 = particles[1]->getPosition(); 218 | glVertex3f(p0.x, p0.y, p0.z); 219 | glVertex3f(p1.x, p1.y, p1.z); 220 | } 221 | 222 | glColor3f(0.7f, 0.7f, 0.7f); 223 | for (unsigned i = 0; i < SUPPORT_COUNT; i++) 224 | { 225 | const cyclone::Vector3 &p0 = supports[i].particle->getPosition(); 226 | const cyclone::Vector3 &p1 = supports[i].anchor; 227 | glVertex3f(p0.x, p0.y, p0.z); 228 | glVertex3f(p1.x, p1.y, p1.z); 229 | } 230 | glEnd(); 231 | 232 | glColor3f(1,0,0); 233 | glPushMatrix(); 234 | glTranslatef(massDisplayPos.x, massDisplayPos.y+0.25f, massDisplayPos.z); 235 | glutSolidSphere(0.25f, 20, 10); 236 | glPopMatrix(); 237 | } 238 | 239 | void BridgeDemo::update() 240 | { 241 | MassAggregateApplication::update(); 242 | 243 | updateAdditionalMass(); 244 | } 245 | 246 | const char* BridgeDemo::getTitle() 247 | { 248 | return "Cyclone > Bridge Demo"; 249 | } 250 | 251 | void BridgeDemo::key(unsigned char key) 252 | { 253 | switch(key) 254 | { 255 | case 's': case 'S': 256 | massPos.z += 0.1f; 257 | if (massPos.z > 1.0f) massPos.z = 1.0f; 258 | break; 259 | case 'w': case 'W': 260 | massPos.z -= 0.1f; 261 | if (massPos.z < 0.0f) massPos.z = 0.0f; 262 | break; 263 | case 'a': case 'A': 264 | massPos.x -= 0.1f; 265 | if (massPos.x < 0.0f) massPos.x = 0.0f; 266 | break; 267 | case 'd': case 'D': 268 | massPos.x += 0.1f; 269 | if (massPos.x > 5.0f) massPos.x = 5.0f; 270 | break; 271 | 272 | default: 273 | MassAggregateApplication::key(key); 274 | } 275 | } 276 | 277 | /** 278 | * Called by the common demo framework to create an application 279 | * object (with new) and return a pointer. 280 | */ 281 | Application* getApplication() 282 | { 283 | return new BridgeDemo(); 284 | } -------------------------------------------------------------------------------- /src/demos/ballistic/ballistic.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The ballistic demo. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | #include "../ogl_headers.h" 15 | #include "../app.h" 16 | #include "../timing.h" 17 | 18 | #include 19 | 20 | /** 21 | * The main demo class definition. 22 | */ 23 | class BallisticDemo : public Application 24 | { 25 | enum ShotType 26 | { 27 | UNUSED = 0, 28 | PISTOL, 29 | ARTILLERY, 30 | FIREBALL, 31 | LASER 32 | }; 33 | 34 | /** 35 | * Holds a single ammunition round record. 36 | */ 37 | struct AmmoRound 38 | { 39 | cyclone::Particle particle; 40 | ShotType type; 41 | unsigned startTime; 42 | 43 | /** Draws the round. */ 44 | void render() 45 | { 46 | cyclone::Vector3 position; 47 | particle.getPosition(&position); 48 | 49 | glColor3f(0, 0, 0); 50 | glPushMatrix(); 51 | glTranslatef(position.x, position.y, position.z); 52 | glutSolidSphere(0.3f, 5, 4); 53 | glPopMatrix(); 54 | 55 | glColor3f(0.75, 0.75, 0.75); 56 | glPushMatrix(); 57 | glTranslatef(position.x, 0, position.z); 58 | glScalef(1.0f, 0.1f, 1.0f); 59 | glutSolidSphere(0.6f, 5, 4); 60 | glPopMatrix(); 61 | } 62 | }; 63 | 64 | /** 65 | * Holds the maximum number of rounds that can be 66 | * fired. 67 | */ 68 | const static unsigned ammoRounds = 16; 69 | 70 | /** Holds the particle data. */ 71 | AmmoRound ammo[ammoRounds]; 72 | 73 | /** Holds the current shot type. */ 74 | ShotType currentShotType; 75 | 76 | /** Dispatches a round. */ 77 | void fire(); 78 | 79 | public: 80 | /** Creates a new demo object. */ 81 | BallisticDemo(); 82 | 83 | /** Returns the window title for the demo. */ 84 | virtual const char* getTitle(); 85 | 86 | /** Update the particle positions. */ 87 | virtual void update(); 88 | 89 | /** Display the particle positions. */ 90 | virtual void display(); 91 | 92 | /** Handle a mouse click. */ 93 | virtual void mouse(int button, int state, int x, int y); 94 | 95 | /** Handle a keypress. */ 96 | virtual void key(unsigned char key); 97 | }; 98 | 99 | // Method definitions 100 | BallisticDemo::BallisticDemo() 101 | : currentShotType(LASER) 102 | { 103 | // Make all shots unused 104 | for (AmmoRound *shot = ammo; shot < ammo+ammoRounds; shot++) 105 | { 106 | shot->type = UNUSED; 107 | } 108 | } 109 | 110 | const char* BallisticDemo::getTitle() 111 | { 112 | return "Cyclone > Ballistic Demo"; 113 | } 114 | 115 | void BallisticDemo::fire() 116 | { 117 | // Find the first available round. 118 | AmmoRound *shot; 119 | for (shot = ammo; shot < ammo+ammoRounds; shot++) 120 | { 121 | if (shot->type == UNUSED) break; 122 | } 123 | 124 | // If we didn't find a round, then exit - we can't fire. 125 | if (shot >= ammo+ammoRounds) return; 126 | 127 | // Set the properties of the particle 128 | switch(currentShotType) 129 | { 130 | case PISTOL: 131 | shot->particle.setMass(2.0f); // 2.0kg 132 | shot->particle.setVelocity(0.0f, 0.0f, 35.0f); // 35m/s 133 | shot->particle.setAcceleration(0.0f, -1.0f, 0.0f); 134 | shot->particle.setDamping(0.99f); 135 | break; 136 | 137 | case ARTILLERY: 138 | shot->particle.setMass(200.0f); // 200.0kg 139 | shot->particle.setVelocity(0.0f, 30.0f, 40.0f); // 50m/s 140 | shot->particle.setAcceleration(0.0f, -20.0f, 0.0f); 141 | shot->particle.setDamping(0.99f); 142 | break; 143 | 144 | case FIREBALL: 145 | shot->particle.setMass(1.0f); // 1.0kg - mostly blast damage 146 | shot->particle.setVelocity(0.0f, 0.0f, 10.0f); // 5m/s 147 | shot->particle.setAcceleration(0.0f, 0.6f, 0.0f); // Floats up 148 | shot->particle.setDamping(0.9f); 149 | break; 150 | 151 | case LASER: 152 | // Note that this is the kind of laser bolt seen in films, 153 | // not a realistic laser beam! 154 | shot->particle.setMass(0.1f); // 0.1kg - almost no weight 155 | shot->particle.setVelocity(0.0f, 0.0f, 100.0f); // 100m/s 156 | shot->particle.setAcceleration(0.0f, 0.0f, 0.0f); // No gravity 157 | shot->particle.setDamping(0.99f); 158 | break; 159 | } 160 | 161 | // Set the data common to all particle types 162 | shot->particle.setPosition(0.0f, 1.5f, 0.0f); 163 | shot->startTime = TimingData::get().lastFrameTimestamp; 164 | shot->type = currentShotType; 165 | 166 | // Clear the force accumulators 167 | shot->particle.clearAccumulator(); 168 | } 169 | 170 | void BallisticDemo::update() 171 | { 172 | // Find the duration of the last frame in seconds 173 | float duration = (float)TimingData::get().lastFrameDuration * 0.001f; 174 | if (duration <= 0.0f) return; 175 | 176 | // Update the physics of each particle in turn 177 | for (AmmoRound *shot = ammo; shot < ammo+ammoRounds; shot++) 178 | { 179 | if (shot->type != UNUSED) 180 | { 181 | // Run the physics 182 | shot->particle.integrate(duration); 183 | 184 | // Check if the particle is now invalid 185 | if (shot->particle.getPosition().y < 0.0f || 186 | shot->startTime+5000 < TimingData::get().lastFrameTimestamp || 187 | shot->particle.getPosition().z > 200.0f) 188 | { 189 | // We simply set the shot type to be unused, so the 190 | // memory it occupies can be reused by another shot. 191 | shot->type = UNUSED; 192 | } 193 | } 194 | } 195 | 196 | Application::update(); 197 | } 198 | 199 | void BallisticDemo::display() 200 | { 201 | // Clear the viewport and set the camera direction 202 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 203 | glLoadIdentity(); 204 | gluLookAt(-25.0, 8.0, 5.0, 0.0, 5.0, 22.0, 0.0, 1.0, 0.0); 205 | 206 | // Draw a sphere at the firing point, and add a shadow projected 207 | // onto the ground plane. 208 | glColor3f(0.0f, 0.0f, 0.0f); 209 | glPushMatrix(); 210 | glTranslatef(0.0f, 1.5f, 0.0f); 211 | glutSolidSphere(0.1f, 5, 5); 212 | glTranslatef(0.0f, -1.5f, 0.0f); 213 | glColor3f(0.75f, 0.75f, 0.75f); 214 | glScalef(1.0f, 0.1f, 1.0f); 215 | glutSolidSphere(0.1f, 5, 5); 216 | glPopMatrix(); 217 | 218 | // Draw some scale lines 219 | glColor3f(0.75f, 0.75f, 0.75f); 220 | glBegin(GL_LINES); 221 | for (unsigned i = 0; i < 200; i += 10) 222 | { 223 | glVertex3f(-5.0f, 0.0f, i); 224 | glVertex3f(5.0f, 0.0f, i); 225 | } 226 | glEnd(); 227 | 228 | // Render each particle in turn 229 | for (AmmoRound *shot = ammo; shot < ammo+ammoRounds; shot++) 230 | { 231 | if (shot->type != UNUSED) 232 | { 233 | shot->render(); 234 | } 235 | } 236 | 237 | // Render the description 238 | glColor3f(0.0f, 0.0f, 0.0f); 239 | renderText(10.0f, 34.0f, "Click: Fire\n1-4: Select Ammo"); 240 | 241 | // Render the name of the current shot type 242 | switch(currentShotType) 243 | { 244 | case PISTOL: renderText(10.0f, 10.0f, "Current Ammo: Pistol"); break; 245 | case ARTILLERY: renderText(10.0f, 10.0f, "Current Ammo: Artillery"); break; 246 | case FIREBALL: renderText(10.0f, 10.0f, "Current Ammo: Fireball"); break; 247 | case LASER: renderText(10.0f, 10.0f, "Current Ammo: Laser"); break; 248 | } 249 | } 250 | 251 | void BallisticDemo::mouse(int button, int state, int x, int y) 252 | { 253 | // Fire the current weapon. 254 | if (state == GLUT_DOWN) fire(); 255 | } 256 | 257 | void BallisticDemo::key(unsigned char key) 258 | { 259 | switch(key) 260 | { 261 | case '1': currentShotType = PISTOL; break; 262 | case '2': currentShotType = ARTILLERY; break; 263 | case '3': currentShotType = FIREBALL; break; 264 | case '4': currentShotType = LASER; break; 265 | } 266 | } 267 | 268 | /** 269 | * Called by the common demo framework to create an application 270 | * object (with new) and return a pointer. 271 | */ 272 | Application* getApplication() 273 | { 274 | return new BallisticDemo(); 275 | } -------------------------------------------------------------------------------- /src/demos/app.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The definition file for the default application object. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | #include 13 | #include "ogl_headers.h" 14 | #include "app.h" 15 | #include "timing.h" 16 | 17 | void Application::initGraphics() 18 | { 19 | glClearColor(0.9f, 0.95f, 1.0f, 1.0f); 20 | glEnable(GL_DEPTH_TEST); 21 | glShadeModel(GL_SMOOTH); 22 | 23 | setView(); 24 | } 25 | 26 | void Application::setView() 27 | { 28 | glMatrixMode(GL_PROJECTION); 29 | glLoadIdentity(); 30 | gluPerspective(60.0, (double)width/(double)height, 1.0, 500.0); 31 | glMatrixMode(GL_MODELVIEW); 32 | } 33 | 34 | void Application::display() 35 | { 36 | glClear(GL_COLOR_BUFFER_BIT); 37 | 38 | glBegin(GL_LINES); 39 | glVertex2i(1, 1); 40 | glVertex2i(639, 319); 41 | glEnd(); 42 | } 43 | 44 | const char* Application::getTitle() 45 | { 46 | return "Cyclone Demo"; 47 | } 48 | 49 | void Application::deinit() 50 | { 51 | } 52 | 53 | void Application::update() 54 | { 55 | glutPostRedisplay(); 56 | } 57 | 58 | void Application::key(unsigned char key) 59 | { 60 | } 61 | 62 | 63 | void Application::resize(int width, int height) 64 | { 65 | // Avoid the divide by zero. 66 | if (height <= 0) height = 1; 67 | 68 | // Set the internal variables and update the view 69 | Application::width = width; 70 | Application::height = height; 71 | glViewport(0, 0, width, height); 72 | setView(); 73 | } 74 | 75 | void Application::mouse(int button, int state, int x, int y) 76 | { 77 | } 78 | 79 | void Application::mouseDrag(int x, int y) 80 | { 81 | } 82 | 83 | // The following methods aren't intended to be overloaded 84 | void Application::renderText(float x, float y, const char *text, void *font) 85 | { 86 | glDisable(GL_DEPTH_TEST); 87 | 88 | // Temporarily set up the view in orthographic projection. 89 | glMatrixMode(GL_PROJECTION); 90 | glPushMatrix(); 91 | glLoadIdentity(); 92 | glOrtho(0.0, (double)width, 0.0, (double)height, -1.0, 1.0); 93 | 94 | // Move to modelview mode. 95 | glMatrixMode(GL_MODELVIEW); 96 | glPushMatrix(); 97 | glLoadIdentity(); 98 | 99 | // Ensure we have a font 100 | if (font == NULL) { 101 | font = GLUT_BITMAP_HELVETICA_10; 102 | } 103 | 104 | // Loop through characters displaying them. 105 | size_t len = strlen(text); 106 | 107 | glRasterPos2f(x, y); 108 | for (const char *letter = text; letter < text+len; letter++) { 109 | 110 | // If we meet a newline, then move down by the line-height 111 | // TODO: Make the line-height a function of the font 112 | if (*letter == '\n') { 113 | y -= 12.0f; 114 | glRasterPos2f(x, y); 115 | } 116 | glutBitmapCharacter(font, *letter); 117 | } 118 | 119 | // Pop the matrices to return to how we were before. 120 | glPopMatrix(); 121 | glMatrixMode(GL_PROJECTION); 122 | glPopMatrix(); 123 | glMatrixMode(GL_MODELVIEW); 124 | 125 | glEnable(GL_DEPTH_TEST); 126 | } 127 | 128 | 129 | MassAggregateApplication::MassAggregateApplication(unsigned int particleCount) 130 | : 131 | world(particleCount*10) 132 | { 133 | particleArray = new cyclone::Particle[particleCount]; 134 | for (unsigned i = 0; i < particleCount; i++) 135 | { 136 | world.getParticles().push_back(particleArray + i); 137 | } 138 | 139 | groundContactGenerator.init(&world.getParticles()); 140 | world.getContactGenerators().push_back(&groundContactGenerator); 141 | } 142 | 143 | MassAggregateApplication::~MassAggregateApplication() 144 | { 145 | delete[] particleArray; 146 | } 147 | 148 | void MassAggregateApplication::initGraphics() 149 | { 150 | // Call the superclass 151 | Application::initGraphics(); 152 | } 153 | 154 | void MassAggregateApplication::display() 155 | { 156 | // Clear the view port and set the camera direction 157 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 158 | glLoadIdentity(); 159 | gluLookAt(0.0, 3.5, 8.0, 0.0, 3.5, 0.0, 0.0, 1.0, 0.0); 160 | 161 | glColor3f(0,0,0); 162 | 163 | cyclone::ParticleWorld::Particles &particles = world.getParticles(); 164 | for (cyclone::ParticleWorld::Particles::iterator p = particles.begin(); 165 | p != particles.end(); 166 | p++) 167 | { 168 | cyclone::Particle *particle = *p; 169 | const cyclone::Vector3 &pos = particle->getPosition(); 170 | glPushMatrix(); 171 | glTranslatef(pos.x, pos.y, pos.z); 172 | glutSolidSphere(0.1f, 20, 10); 173 | glPopMatrix(); 174 | } 175 | } 176 | 177 | void MassAggregateApplication::update() 178 | { 179 | // Clear accumulators 180 | world.startFrame(); 181 | 182 | // Find the duration of the last frame in seconds 183 | float duration = (float)TimingData::get().lastFrameDuration * 0.001f; 184 | if (duration <= 0.0f) return; 185 | 186 | // Run the simulation 187 | world.runPhysics(duration); 188 | 189 | Application::update(); 190 | } 191 | 192 | RigidBodyApplication::RigidBodyApplication() 193 | : 194 | theta(0.0f), 195 | phi(15.0f), 196 | resolver(maxContacts*8), 197 | 198 | renderDebugInfo(false), 199 | pauseSimulation(true), 200 | autoPauseSimulation(false) 201 | { 202 | cData.contactArray = contacts; 203 | } 204 | 205 | void RigidBodyApplication::update() 206 | { 207 | // Find the duration of the last frame in seconds 208 | float duration = (float)TimingData::get().lastFrameDuration * 0.001f; 209 | if (duration <= 0.0f) return; 210 | else if (duration > 0.05f) duration = 0.05f; 211 | 212 | // Exit immediately if we aren't running the simulation 213 | if (pauseSimulation) 214 | { 215 | Application::update(); 216 | return; 217 | } 218 | else if (autoPauseSimulation) 219 | { 220 | pauseSimulation = true; 221 | autoPauseSimulation = false; 222 | } 223 | 224 | // Update the objects 225 | updateObjects(duration); 226 | 227 | // Perform the contact generation 228 | generateContacts(); 229 | 230 | // Resolve detected contacts 231 | resolver.resolveContacts( 232 | cData.contactArray, 233 | cData.contactCount, 234 | duration 235 | ); 236 | 237 | Application::update(); 238 | } 239 | 240 | void RigidBodyApplication::display() 241 | { 242 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 243 | glLoadIdentity(); 244 | gluLookAt(18.0f, 0, 0, 0, 0, 0, 0, 1.0f, 0); 245 | glRotatef(-phi, 0, 0, 1); 246 | glRotatef(theta, 0, 1, 0); 247 | glTranslatef(0, -5.0f, 0); 248 | } 249 | 250 | void RigidBodyApplication::drawDebug() 251 | { 252 | if (!renderDebugInfo) return; 253 | 254 | // Recalculate the contacts, so they are current (in case we're 255 | // paused, for example). 256 | generateContacts(); 257 | 258 | // Render the contacts, if required 259 | glBegin(GL_LINES); 260 | for (unsigned i = 0; i < cData.contactCount; i++) 261 | { 262 | // Interbody contacts are in green, floor contacts are red. 263 | if (contacts[i].body[1]) { 264 | glColor3f(0,1,0); 265 | } else { 266 | glColor3f(1,0,0); 267 | } 268 | 269 | cyclone::Vector3 vec = contacts[i].contactPoint; 270 | glVertex3f(vec.x, vec.y, vec.z); 271 | 272 | vec += contacts[i].contactNormal; 273 | glVertex3f(vec.x, vec.y, vec.z); 274 | } 275 | 276 | glEnd(); 277 | } 278 | 279 | void RigidBodyApplication::mouse(int button, int state, int x, int y) 280 | { 281 | // Set the position 282 | last_x = x; 283 | last_y = y; 284 | } 285 | 286 | void RigidBodyApplication::mouseDrag(int x, int y) 287 | { 288 | // Update the camera 289 | theta += (x - last_x)*0.25f; 290 | phi += (y - last_y)*0.25f; 291 | 292 | // Keep it in bounds 293 | if (phi < -20.0f) phi = -20.0f; 294 | else if (phi > 80.0f) phi = 80.0f; 295 | 296 | // Remember the position 297 | last_x = x; 298 | last_y = y; 299 | } 300 | 301 | void RigidBodyApplication::key(unsigned char key) 302 | { 303 | switch(key) 304 | { 305 | case 'R': case 'r': 306 | // Reset the simulation 307 | reset(); 308 | return; 309 | 310 | case 'C': case 'c': 311 | // Toggle rendering of contacts 312 | renderDebugInfo = !renderDebugInfo; 313 | return; 314 | 315 | case 'P': case 'p': 316 | // Toggle running the simulation 317 | pauseSimulation = !pauseSimulation; 318 | return; 319 | 320 | case ' ': 321 | // Advance one frame 322 | autoPauseSimulation = true; 323 | pauseSimulation = false; 324 | } 325 | 326 | Application::key(key); 327 | } 328 | -------------------------------------------------------------------------------- /include/cyclone/pfgen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface file for the force generators. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | /** 14 | * @file 15 | * 16 | * This file contains the interface and sample force generators. 17 | */ 18 | #ifndef CYCLONE_PFGEN_H 19 | #define CYCLONE_PFGEN_H 20 | 21 | #include "core.h" 22 | #include "particle.h" 23 | #include 24 | 25 | namespace cyclone { 26 | 27 | /** 28 | * A force generator can be asked to add a force to one or more 29 | * particles. 30 | */ 31 | class ParticleForceGenerator 32 | { 33 | public: 34 | 35 | /** 36 | * Overload this in implementations of the interface to calculate 37 | * and update the force applied to the given particle. 38 | */ 39 | virtual void updateForce(Particle *particle, real duration) = 0; 40 | }; 41 | 42 | /** 43 | * A force generator that applies a gravitational force. One instance 44 | * can be used for multiple particles. 45 | */ 46 | class ParticleGravity : public ParticleForceGenerator 47 | { 48 | /** Holds the acceleration due to gravity. */ 49 | Vector3 gravity; 50 | 51 | public: 52 | 53 | /** Creates the generator with the given acceleration. */ 54 | ParticleGravity(const Vector3 &gravity); 55 | 56 | /** Applies the gravitational force to the given particle. */ 57 | virtual void updateForce(Particle *particle, real duration); 58 | }; 59 | 60 | /** 61 | * A force generator that applies a drag force. One instance 62 | * can be used for multiple particles. 63 | */ 64 | class ParticleDrag : public ParticleForceGenerator 65 | { 66 | /** Holds the velocity drag coeffificent. */ 67 | real k1; 68 | 69 | /** Holds the velocity squared drag coeffificent. */ 70 | real k2; 71 | 72 | public: 73 | 74 | /** Creates the generator with the given coefficients. */ 75 | ParticleDrag(real k1, real k2); 76 | 77 | /** Applies the drag force to the given particle. */ 78 | virtual void updateForce(Particle *particle, real duration); 79 | }; 80 | 81 | /** 82 | * A force generator that applies a Spring force, where 83 | * one end is attached to a fixed point in space. 84 | */ 85 | class ParticleAnchoredSpring : public ParticleForceGenerator 86 | { 87 | protected: 88 | /** The location of the anchored end of the spring. */ 89 | Vector3 *anchor; 90 | 91 | /** Holds the sprint constant. */ 92 | real springConstant; 93 | 94 | /** Holds the rest length of the spring. */ 95 | real restLength; 96 | 97 | public: 98 | ParticleAnchoredSpring(); 99 | 100 | /** Creates a new spring with the given parameters. */ 101 | ParticleAnchoredSpring(Vector3 *anchor, 102 | real springConstant, 103 | real restLength); 104 | 105 | /** Retrieve the anchor point. */ 106 | const Vector3* getAnchor() const { return anchor; } 107 | 108 | /** Set the spring's properties. */ 109 | void init(Vector3 *anchor, 110 | real springConstant, 111 | real restLength); 112 | 113 | /** Applies the spring force to the given particle. */ 114 | virtual void updateForce(Particle *particle, real duration); 115 | }; 116 | 117 | /** 118 | * A force generator that applies a bungee force, where 119 | * one end is attached to a fixed point in space. 120 | */ 121 | class ParticleAnchoredBungee : public ParticleAnchoredSpring 122 | { 123 | public: 124 | /** Applies the spring force to the given particle. */ 125 | virtual void updateForce(Particle *particle, real duration); 126 | }; 127 | 128 | /** 129 | * A force generator that fakes a stiff spring force, and where 130 | * one end is attached to a fixed point in space. 131 | */ 132 | class ParticleFakeSpring : public ParticleForceGenerator 133 | { 134 | /** The location of the anchored end of the spring. */ 135 | Vector3 *anchor; 136 | 137 | /** Holds the sprint constant. */ 138 | real springConstant; 139 | 140 | /** Holds the damping on the oscillation of the spring. */ 141 | real damping; 142 | 143 | public: 144 | 145 | /** Creates a new spring with the given parameters. */ 146 | ParticleFakeSpring(Vector3 *anchor, real springConstant, 147 | real damping); 148 | 149 | /** Applies the spring force to the given particle. */ 150 | virtual void updateForce(Particle *particle, real duration); 151 | }; 152 | 153 | /** 154 | * A force generator that applies a Spring force. 155 | */ 156 | class ParticleSpring : public ParticleForceGenerator 157 | { 158 | /** The particle at the other end of the spring. */ 159 | Particle *other; 160 | 161 | /** Holds the sprint constant. */ 162 | real springConstant; 163 | 164 | /** Holds the rest length of the spring. */ 165 | real restLength; 166 | 167 | public: 168 | 169 | /** Creates a new spring with the given parameters. */ 170 | ParticleSpring(Particle *other, 171 | real springConstant, real restLength); 172 | 173 | /** Applies the spring force to the given particle. */ 174 | virtual void updateForce(Particle *particle, real duration); 175 | }; 176 | 177 | /** 178 | * A force generator that applies a spring force only 179 | * when extended. 180 | */ 181 | class ParticleBungee : public ParticleForceGenerator 182 | { 183 | /** The particle at the other end of the spring. */ 184 | Particle *other; 185 | 186 | /** Holds the sprint constant. */ 187 | real springConstant; 188 | 189 | /** 190 | * Holds the length of the bungee at the point it begins to 191 | * generator a force. 192 | */ 193 | real restLength; 194 | 195 | public: 196 | 197 | /** Creates a new bungee with the given parameters. */ 198 | ParticleBungee(Particle *other, 199 | real springConstant, real restLength); 200 | 201 | /** Applies the spring force to the given particle. */ 202 | virtual void updateForce(Particle *particle, real duration); 203 | }; 204 | 205 | /** 206 | * A force generator that applies a buoyancy force for a plane of 207 | * liquid parrallel to XZ plane. 208 | */ 209 | class ParticleBuoyancy : public ParticleForceGenerator 210 | { 211 | /** 212 | * The maximum submersion depth of the object before 213 | * it generates its maximum boyancy force. 214 | */ 215 | real maxDepth; 216 | 217 | /** 218 | * The volume of the object. 219 | */ 220 | real volume; 221 | 222 | /** 223 | * The height of the water plane above y=0. The plane will be 224 | * parrallel to the XZ plane. 225 | */ 226 | real waterHeight; 227 | 228 | /** 229 | * The density of the liquid. Pure water has a density of 230 | * 1000kg per cubic meter. 231 | */ 232 | real liquidDensity; 233 | 234 | public: 235 | 236 | /** Creates a new buoyancy force with the given parameters. */ 237 | ParticleBuoyancy(real maxDepth, real volume, real waterHeight, 238 | real liquidDensity = 1000.0f); 239 | 240 | /** Applies the buoyancy force to the given particle. */ 241 | virtual void updateForce(Particle *particle, real duration); 242 | }; 243 | 244 | /** 245 | * Holds all the force generators and the particles they apply to. 246 | */ 247 | class ParticleForceRegistry 248 | { 249 | protected: 250 | 251 | /** 252 | * Keeps track of one force generator and the particle it 253 | * applies to. 254 | */ 255 | struct ParticleForceRegistration 256 | { 257 | Particle *particle; 258 | ParticleForceGenerator *fg; 259 | }; 260 | 261 | /** 262 | * Holds the list of registrations. 263 | */ 264 | typedef std::vector Registry; 265 | Registry registrations; 266 | 267 | public: 268 | /** 269 | * Registers the given force generator to apply to the 270 | * given particle. 271 | */ 272 | void add(Particle* particle, ParticleForceGenerator *fg); 273 | 274 | /** 275 | * Removes the given registered pair from the registry. 276 | * If the pair is not registered, this method will have 277 | * no effect. 278 | */ 279 | void remove(Particle* particle, ParticleForceGenerator *fg); 280 | 281 | /** 282 | * Clears all registrations from the registry. This will 283 | * not delete the particles or the force generators 284 | * themselves, just the records of their connection. 285 | */ 286 | void clear(); 287 | 288 | /** 289 | * Calls all the force generators to update the forces of 290 | * their corresponding particles. 291 | */ 292 | void updateForces(real duration); 293 | }; 294 | } 295 | 296 | #endif // CYCLONE_PFGEN_H -------------------------------------------------------------------------------- /src/demos/flightsim/flightsim.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The flightsim demo. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | #include "../ogl_headers.h" 15 | #include "../app.h" 16 | #include "../timing.h" 17 | 18 | #include 19 | #include 20 | 21 | /** 22 | * The main demo class definition. 23 | */ 24 | class FlightSimDemo : public Application 25 | { 26 | cyclone::AeroControl left_wing; 27 | cyclone::AeroControl right_wing; 28 | cyclone::AeroControl rudder; 29 | cyclone::Aero tail; 30 | cyclone::RigidBody aircraft; 31 | cyclone::ForceRegistry registry; 32 | 33 | cyclone::Vector3 windspeed; 34 | 35 | float left_wing_control; 36 | float right_wing_control; 37 | float rudder_control; 38 | 39 | void resetPlane(); 40 | 41 | public: 42 | /** Creates a new demo object. */ 43 | FlightSimDemo(); 44 | virtual ~FlightSimDemo(); 45 | 46 | /** Returns the window title for the demo. */ 47 | virtual const char* getTitle(); 48 | 49 | /** Display the particles. */ 50 | virtual void display(); 51 | 52 | /** Update the particle positions. */ 53 | virtual void update(); 54 | 55 | /** Handle a key press. */ 56 | virtual void key(unsigned char key); 57 | }; 58 | 59 | // Method definitions 60 | FlightSimDemo::FlightSimDemo() 61 | : 62 | Application(), 63 | 64 | right_wing(cyclone::Matrix3(0,0,0, -1,-0.5f,0, 0,0,0), 65 | cyclone::Matrix3(0,0,0, -0.995f,-0.5f,0, 0,0,0), 66 | cyclone::Matrix3(0,0,0, -1.005f,-0.5f,0, 0,0,0), 67 | cyclone::Vector3(-1.0f, 0.0f, 2.0f), &windspeed), 68 | 69 | left_wing(cyclone::Matrix3(0,0,0, -1,-0.5f,0, 0,0,0), 70 | cyclone::Matrix3(0,0,0, -0.995f,-0.5f,0, 0,0,0), 71 | cyclone::Matrix3(0,0,0, -1.005f,-0.5f,0, 0,0,0), 72 | cyclone::Vector3(-1.0f, 0.0f, -2.0f), &windspeed), 73 | 74 | rudder(cyclone::Matrix3(0,0,0, 0,0,0, 0,0,0), 75 | cyclone::Matrix3(0,0,0, 0,0,0, 0.01f,0,0), 76 | cyclone::Matrix3(0,0,0, 0,0,0, -0.01f,0,0), 77 | cyclone::Vector3(2.0f, 0.5f, 0), &windspeed), 78 | 79 | tail(cyclone::Matrix3(0,0,0, -1,-0.5f,0, 0,0,-0.1f), 80 | cyclone::Vector3(2.0f, 0, 0), &windspeed), 81 | 82 | left_wing_control(0), right_wing_control(0), rudder_control(0), 83 | 84 | windspeed(0,0,0) 85 | { 86 | // Set up the aircraft rigid body. 87 | resetPlane(); 88 | 89 | aircraft.setMass(2.5f); 90 | cyclone::Matrix3 it; 91 | it.setBlockInertiaTensor(cyclone::Vector3(2,1,1), 1); 92 | aircraft.setInertiaTensor(it); 93 | 94 | aircraft.setDamping(0.8f, 0.8f); 95 | 96 | aircraft.setAcceleration(cyclone::Vector3::GRAVITY); 97 | aircraft.calculateDerivedData(); 98 | 99 | aircraft.setAwake(); 100 | aircraft.setCanSleep(false); 101 | 102 | registry.add(&aircraft, &left_wing); 103 | registry.add(&aircraft, &right_wing); 104 | registry.add(&aircraft, &rudder); 105 | registry.add(&aircraft, &tail); 106 | } 107 | 108 | FlightSimDemo::~FlightSimDemo() 109 | { 110 | } 111 | 112 | void FlightSimDemo::resetPlane() 113 | { 114 | aircraft.setPosition(0, 0, 0); 115 | aircraft.setOrientation(1,0,0,0); 116 | 117 | aircraft.setVelocity(0,0,0); 118 | aircraft.setRotation(0,0,0); 119 | } 120 | 121 | static void drawAircraft() 122 | { 123 | // Fuselage 124 | glPushMatrix(); 125 | glTranslatef(-0.5f, 0, 0); 126 | glScalef(2.0f, 0.8f, 1.0f); 127 | glutSolidCube(1.0f); 128 | glPopMatrix(); 129 | 130 | // Rear Fuselage 131 | glPushMatrix(); 132 | glTranslatef(1.0f, 0.15f, 0); 133 | glScalef(2.75f, 0.5f, 0.5f); 134 | glutSolidCube(1.0f); 135 | glPopMatrix(); 136 | 137 | // Wings 138 | glPushMatrix(); 139 | glTranslatef(0, 0.3f, 0); 140 | glScalef(0.8f, 0.1f, 6.0f); 141 | glutSolidCube(1.0f); 142 | glPopMatrix(); 143 | 144 | // Rudder 145 | glPushMatrix(); 146 | glTranslatef(2.0f, 0.775f, 0); 147 | glScalef(0.75f, 1.15f, 0.1f); 148 | glutSolidCube(1.0f); 149 | glPopMatrix(); 150 | 151 | // Tail-plane 152 | glPushMatrix(); 153 | glTranslatef(1.9f, 0, 0); 154 | glScalef(0.85f, 0.1f, 2.0f); 155 | glutSolidCube(1.0f); 156 | glPopMatrix(); 157 | } 158 | 159 | void FlightSimDemo::display() 160 | { 161 | // Clear the view port and set the camera direction 162 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 163 | glLoadIdentity(); 164 | 165 | cyclone::Vector3 pos = aircraft.getPosition(); 166 | cyclone::Vector3 offset(4.0f+aircraft.getVelocity().magnitude(), 0, 0); 167 | offset = aircraft.getTransform().transformDirection(offset); 168 | gluLookAt(pos.x+offset.x, pos.y+5.0f, pos.z+offset.z, 169 | pos.x, pos.y, pos.z, 170 | 0.0, 1.0, 0.0); 171 | 172 | glColor3f(0.6f,0.6f,0.6f); 173 | int bx = int(pos.x); 174 | int bz = int(pos.z); 175 | glBegin(GL_QUADS); 176 | for (int x = -20; x <= 20; x++) for (int z = -20; z <= 20; z++) 177 | { 178 | glVertex3f(bx+x-0.1f, 0, bz+z-0.1f); 179 | glVertex3f(bx+x-0.1f, 0, bz+z+0.1f); 180 | glVertex3f(bx+x+0.1f, 0, bz+z+0.1f); 181 | glVertex3f(bx+x+0.1f, 0, bz+z-0.1f); 182 | } 183 | glEnd(); 184 | 185 | // Set the transform matrix for the aircraft 186 | cyclone::Matrix4 transform = aircraft.getTransform(); 187 | GLfloat gl_transform[16]; 188 | transform.fillGLArray(gl_transform); 189 | glPushMatrix(); 190 | glMultMatrixf(gl_transform); 191 | 192 | // Draw the aircraft 193 | glColor3f(0,0,0); 194 | drawAircraft(); 195 | glPopMatrix(); 196 | 197 | glColor3f(0.8f, 0.8f, 0.8f); 198 | glPushMatrix(); 199 | glTranslatef(0, -1.0f - pos.y, 0); 200 | glScalef(1.0f, 0.001f, 1.0f); 201 | glMultMatrixf(gl_transform); 202 | drawAircraft(); 203 | glPopMatrix(); 204 | 205 | char buffer[256]; 206 | sprintf( 207 | buffer, 208 | "Altitude: %.1f | Speed %.1f", 209 | aircraft.getPosition().y, 210 | aircraft.getVelocity().magnitude() 211 | ); 212 | glColor3f(0,0,0); 213 | renderText(10.0f, 24.0f, buffer); 214 | 215 | sprintf( 216 | buffer, 217 | "Left Wing: %.1f | Right Wing: %.1f | Rudder %.1f", 218 | left_wing_control, right_wing_control, rudder_control 219 | ); 220 | renderText(10.0f, 10.0f, buffer); 221 | } 222 | 223 | void FlightSimDemo::update() 224 | { 225 | // Find the duration of the last frame in seconds 226 | float duration = (float)TimingData::get().lastFrameDuration * 0.001f; 227 | if (duration <= 0.0f) return; 228 | 229 | // Start with no forces or acceleration. 230 | aircraft.clearAccumulators(); 231 | 232 | // Add the propeller force 233 | cyclone::Vector3 propulsion(-10.0f, 0, 0); 234 | propulsion = aircraft.getTransform().transformDirection(propulsion); 235 | aircraft.addForce(propulsion); 236 | 237 | // Add the forces acting on the aircraft. 238 | registry.updateForces(duration); 239 | 240 | // Update the aircraft's physics. 241 | aircraft.integrate(duration); 242 | 243 | // Do a very basic collision detection and response with the ground. 244 | cyclone::Vector3 pos = aircraft.getPosition(); 245 | if (pos.y < 0.0f) 246 | { 247 | pos.y = 0.0f; 248 | aircraft.setPosition(pos); 249 | 250 | if (aircraft.getVelocity().y < -10.0f) 251 | { 252 | resetPlane(); 253 | } 254 | } 255 | 256 | Application::update(); 257 | } 258 | 259 | const char* FlightSimDemo::getTitle() 260 | { 261 | return "Cyclone > Flight Sim Demo"; 262 | } 263 | 264 | void FlightSimDemo::key(unsigned char key) 265 | { 266 | switch(key) 267 | { 268 | case 'q': case 'Q': 269 | rudder_control += 0.1f; 270 | break; 271 | 272 | case 'e': case 'E': 273 | rudder_control -= 0.1f; 274 | break; 275 | 276 | case 'w': case 'W': 277 | left_wing_control -= 0.1f; 278 | right_wing_control -= 0.1f; 279 | break; 280 | 281 | case 's': case 'S': 282 | left_wing_control += 0.1f; 283 | right_wing_control += 0.1f; 284 | break; 285 | 286 | case 'd': case 'D': 287 | left_wing_control -= 0.1f; 288 | right_wing_control += 0.1f; 289 | break; 290 | 291 | case 'a': case 'A': 292 | left_wing_control += 0.1f; 293 | right_wing_control -= 0.1f; 294 | break; 295 | 296 | case 'x': case 'X': 297 | left_wing_control = 0.0f; 298 | right_wing_control = 0.0f; 299 | rudder_control = 0.0f; 300 | break; 301 | 302 | case 'r': case 'R': 303 | resetPlane(); 304 | break; 305 | 306 | default: 307 | Application::key(key); 308 | } 309 | 310 | // Make sure the controls are in range 311 | if (left_wing_control < -1.0f) left_wing_control = -1.0f; 312 | else if (left_wing_control > 1.0f) left_wing_control = 1.0f; 313 | if (right_wing_control < -1.0f) right_wing_control = -1.0f; 314 | else if (right_wing_control > 1.0f) right_wing_control = 1.0f; 315 | if (rudder_control < -1.0f) rudder_control = -1.0f; 316 | else if (rudder_control > 1.0f) rudder_control = 1.0f; 317 | 318 | // Update the control surfaces 319 | left_wing.setControl(left_wing_control); 320 | right_wing.setControl(right_wing_control); 321 | rudder.setControl(rudder_control); 322 | } 323 | 324 | /** 325 | * Called by the common demo framework to create an application 326 | * object (with new) and return a pointer. 327 | */ 328 | Application* getApplication() 329 | { 330 | return new FlightSimDemo(); 331 | } -------------------------------------------------------------------------------- /include/cyclone/collide_fine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface file for the fine grained collision detection system. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | /** 14 | * @file 15 | * 16 | * This file contains the fine grained collision detection system. 17 | * It is used to return contacts between pairs of primitives. 18 | * 19 | * There are two groups of tests in this file. Intersection tests 20 | * use the fastest separating axis method to check if two objects 21 | * intersect, and the collision tests generate the contacts. The 22 | * collision tests typically use the intersection tests as an early 23 | * out. 24 | */ 25 | #ifndef CYCLONE_COLLISION_FINE_H 26 | #define CYCLONE_COLLISION_FINE_H 27 | 28 | #include "contacts.h" 29 | 30 | namespace cyclone { 31 | 32 | // Forward declarations of primitive friends 33 | class IntersectionTests; 34 | class CollisionDetector; 35 | 36 | /** 37 | * Represents a primitive to detect collisions against. 38 | */ 39 | class CollisionPrimitive 40 | { 41 | public: 42 | /** 43 | * This class exists to help the collision detector 44 | * and intersection routines, so they should have 45 | * access to its data. 46 | */ 47 | friend class IntersectionTests; 48 | friend class CollisionDetector; 49 | 50 | /** 51 | * The rigid body that is represented by this primitive. 52 | */ 53 | RigidBody * body; 54 | 55 | /** 56 | * The offset of this primitive from the given rigid body. 57 | */ 58 | Matrix4 offset; 59 | 60 | /** 61 | * Calculates the internals for the primitive. 62 | */ 63 | void calculateInternals(); 64 | 65 | /** 66 | * This is a convenience function to allow access to the 67 | * axis vectors in the transform for this primitive. 68 | */ 69 | Vector3 getAxis(unsigned index) const 70 | { 71 | return transform.getAxisVector(index); 72 | } 73 | 74 | /** 75 | * Returns the resultant transform of the primitive, calculated from 76 | * the combined offset of the primitive and the transform 77 | * (orientation + position) of the rigid body to which it is 78 | * attached. 79 | */ 80 | const Matrix4& getTransform() const 81 | { 82 | return transform; 83 | } 84 | 85 | 86 | protected: 87 | /** 88 | * The resultant transform of the primitive. This is 89 | * calculated by combining the offset of the primitive 90 | * with the transform of the rigid body. 91 | */ 92 | Matrix4 transform; 93 | }; 94 | 95 | /** 96 | * Represents a rigid body that can be treated as a sphere 97 | * for collision detection. 98 | */ 99 | class CollisionSphere : public CollisionPrimitive 100 | { 101 | public: 102 | /** 103 | * The radius of the sphere. 104 | */ 105 | real radius; 106 | }; 107 | 108 | /** 109 | * The plane is not a primitive: it doesn't represent another 110 | * rigid body. It is used for contacts with the immovable 111 | * world geometry. 112 | */ 113 | class CollisionPlane 114 | { 115 | public: 116 | /** 117 | * The plane normal 118 | */ 119 | Vector3 direction; 120 | 121 | /** 122 | * The distance of the plane from the origin. 123 | */ 124 | real offset; 125 | }; 126 | 127 | /** 128 | * Represents a rigid body that can be treated as an aligned bounding 129 | * box for collision detection. 130 | */ 131 | class CollisionBox : public CollisionPrimitive 132 | { 133 | public: 134 | /** 135 | * Holds the half-sizes of the box along each of its local axes. 136 | */ 137 | Vector3 halfSize; 138 | }; 139 | 140 | /** 141 | * A wrapper class that holds fast intersection tests. These 142 | * can be used to drive the coarse collision detection system or 143 | * as an early out in the full collision tests below. 144 | */ 145 | class IntersectionTests 146 | { 147 | public: 148 | 149 | static bool sphereAndHalfSpace( 150 | const CollisionSphere &sphere, 151 | const CollisionPlane &plane); 152 | 153 | static bool sphereAndSphere( 154 | const CollisionSphere &one, 155 | const CollisionSphere &two); 156 | 157 | static bool boxAndBox( 158 | const CollisionBox &one, 159 | const CollisionBox &two); 160 | 161 | /** 162 | * Does an intersection test on an arbitrarily aligned box and a 163 | * half-space. 164 | * 165 | * The box is given as a transform matrix, including 166 | * position, and a vector of half-sizes for the extend of the 167 | * box along each local axis. 168 | * 169 | * The half-space is given as a direction (i.e. unit) vector and the 170 | * offset of the limiting plane from the origin, along the given 171 | * direction. 172 | */ 173 | static bool boxAndHalfSpace( 174 | const CollisionBox &box, 175 | const CollisionPlane &plane); 176 | }; 177 | 178 | 179 | /** 180 | * A helper structure that contains information for the detector to use 181 | * in building its contact data. 182 | */ 183 | struct CollisionData 184 | { 185 | /** 186 | * Holds the base of the collision data: the first contact 187 | * in the array. This is used so that the contact pointer (below) 188 | * can be incremented each time a contact is detected, while 189 | * this pointer points to the first contact found. 190 | */ 191 | Contact *contactArray; 192 | 193 | /** Holds the contact array to write into. */ 194 | Contact *contacts; 195 | 196 | /** Holds the maximum number of contacts the array can take. */ 197 | int contactsLeft; 198 | 199 | /** Holds the number of contacts found so far. */ 200 | unsigned contactCount; 201 | 202 | /** Holds the friction value to write into any collisions. */ 203 | real friction; 204 | 205 | /** Holds the restitution value to write into any collisions. */ 206 | real restitution; 207 | 208 | /** 209 | * Holds the collision tolerance, even uncolliding objects this 210 | * close should have collisions generated. 211 | */ 212 | real tolerance; 213 | 214 | /** 215 | * Checks if there are more contacts available in the contact 216 | * data. 217 | */ 218 | bool hasMoreContacts() 219 | { 220 | return contactsLeft > 0; 221 | } 222 | 223 | /** 224 | * Resets the data so that it has no used contacts recorded. 225 | */ 226 | void reset(unsigned maxContacts) 227 | { 228 | contactsLeft = maxContacts; 229 | contactCount = 0; 230 | contacts = contactArray; 231 | } 232 | 233 | /** 234 | * Notifies the data that the given number of contacts have 235 | * been added. 236 | */ 237 | void addContacts(unsigned count) 238 | { 239 | // Reduce the number of contacts remaining, add number used 240 | contactsLeft -= count; 241 | contactCount += count; 242 | 243 | // Move the array forward 244 | contacts += count; 245 | } 246 | }; 247 | 248 | /** 249 | * A wrapper class that holds the fine grained collision detection 250 | * routines. 251 | * 252 | * Each of the functions has the same format: it takes the details 253 | * of two objects, and a pointer to a contact array to fill. It 254 | * returns the number of contacts it wrote into the array. 255 | */ 256 | class CollisionDetector 257 | { 258 | public: 259 | 260 | static unsigned sphereAndHalfSpace( 261 | const CollisionSphere &sphere, 262 | const CollisionPlane &plane, 263 | CollisionData *data 264 | ); 265 | 266 | static unsigned sphereAndTruePlane( 267 | const CollisionSphere &sphere, 268 | const CollisionPlane &plane, 269 | CollisionData *data 270 | ); 271 | 272 | static unsigned sphereAndSphere( 273 | const CollisionSphere &one, 274 | const CollisionSphere &two, 275 | CollisionData *data 276 | ); 277 | 278 | /** 279 | * Does a collision test on a collision box and a plane representing 280 | * a half-space (i.e. the normal of the plane 281 | * points out of the half-space). 282 | */ 283 | static unsigned boxAndHalfSpace( 284 | const CollisionBox &box, 285 | const CollisionPlane &plane, 286 | CollisionData *data 287 | ); 288 | 289 | static unsigned boxAndBox( 290 | const CollisionBox &one, 291 | const CollisionBox &two, 292 | CollisionData *data 293 | ); 294 | 295 | static unsigned boxAndPoint( 296 | const CollisionBox &box, 297 | const Vector3 &point, 298 | CollisionData *data 299 | ); 300 | 301 | static unsigned boxAndSphere( 302 | const CollisionBox &box, 303 | const CollisionSphere &sphere, 304 | CollisionData *data 305 | ); 306 | }; 307 | 308 | 309 | 310 | } // namespace cyclone 311 | 312 | #endif // CYCLONE_COLLISION_FINE_H 313 | -------------------------------------------------------------------------------- /include/cyclone/particle.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface file for the particle class. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | /** 14 | * @file 15 | * 16 | * This file contains the definitions for the paticle class, which can 17 | * be used in place of rigid bodies for simpler simulations or 18 | * assemblies. 19 | */ 20 | #ifndef CYCLONE_PARTICLE_H 21 | #define CYCLONE_PARTICLE_H 22 | 23 | #include "core.h" 24 | 25 | namespace cyclone { 26 | 27 | /** 28 | * A particle is the simplest object that can be simulated in the 29 | * physics system. 30 | * 31 | * It has position data (no orientation data), along with 32 | * velocity. It can be integrated forward through time, and have 33 | * linear forces, and impulses applied to it. The particle manages 34 | * its state and allows access through a set of methods. 35 | */ 36 | class Particle 37 | { 38 | public: 39 | 40 | // ... Other Particle code as before ... 41 | 42 | 43 | protected: 44 | /** 45 | * @name Characteristic Data and State 46 | * 47 | * This data holds the state of the particle. There are two 48 | * sets of data: characteristics and state. 49 | * 50 | * Characteristics are properties of the particle 51 | * independent of its current kinematic situation. This 52 | * includes mass, moment of inertia and damping 53 | * properties. Two identical particles will have the same 54 | * values for their characteristics. 55 | * 56 | * State includes all the characteristics and also includes 57 | * the kinematic situation of the particle in the current 58 | * simulation. By setting the whole state data, a particle's 59 | * exact game state can be replicated. Note that state does 60 | * not include any forces applied to the body. Two identical 61 | * rigid bodies in the same simulation will not share the same 62 | * state values. 63 | * 64 | * The state values make up the smallest set of independent 65 | * data for the particle. Other state data is calculated 66 | * from their current values. When state data is changed the 67 | * dependent values need to be updated: this can be achieved 68 | * either by integrating the simulation, or by calling the 69 | * calculateInternals function. This two stage process is used 70 | * because recalculating internals can be a costly process: 71 | * all state changes should be carried out at the same time, 72 | * allowing for a single call. 73 | * 74 | * @see calculateInternals 75 | */ 76 | /*@{*/ 77 | 78 | /** 79 | * Holds the inverse of the mass of the particle. It 80 | * is more useful to hold the inverse mass because 81 | * integration is simpler, and because in real time 82 | * simulation it is more useful to have objects with 83 | * infinite mass (immovable) than zero mass 84 | * (completely unstable in numerical simulation). 85 | */ 86 | real inverseMass; 87 | 88 | /** 89 | * Holds the amount of damping applied to linear 90 | * motion. Damping is required to remove energy added 91 | * through numerical instability in the integrator. 92 | */ 93 | real damping; 94 | 95 | /** 96 | * Holds the linear position of the particle in 97 | * world space. 98 | */ 99 | Vector3 position; 100 | 101 | /** 102 | * Holds the linear velocity of the particle in 103 | * world space. 104 | */ 105 | Vector3 velocity; 106 | 107 | /*@}*/ 108 | 109 | /** 110 | * @name Force Accumulators 111 | * 112 | * These data members store the current force and 113 | * global linear acceleration of the particle. 114 | */ 115 | 116 | /*@{*/ 117 | 118 | /** 119 | * Holds the accumulated force to be applied at the next 120 | * simulation iteration only. This value is zeroed at each 121 | * integration step. 122 | */ 123 | Vector3 forceAccum; 124 | 125 | /** 126 | * Holds the acceleration of the particle. This value 127 | * can be used to set acceleration due to gravity (its primary 128 | * use), or any other constant acceleration. 129 | */ 130 | Vector3 acceleration; 131 | 132 | /*@}*/ 133 | 134 | public: 135 | /** 136 | * @name Constructor and Destructor 137 | * 138 | * There are no data members in the particle class that are 139 | * created on the heap. So all data storage is handled 140 | * automatically. 141 | */ 142 | /*@{*/ 143 | /*@}*/ 144 | 145 | /** 146 | * @name Integration and Simulation Functions 147 | * 148 | * These functions are used to simulate the particle's 149 | * motion over time. A normal application sets up one or more 150 | * rigid bodies, applies permanent forces (i.e. gravity), then 151 | * adds transient forces each frame, and integrates, prior to 152 | * rendering. 153 | * 154 | * Currently the only integration function provided is the 155 | * first order Newton Euler method. 156 | */ 157 | /*@{*/ 158 | 159 | /** 160 | * Integrates the particle forward in time by the given amount. 161 | * This function uses a Newton-Euler integration method, which is a 162 | * linear approximation to the correct integral. For this reason it 163 | * may be inaccurate in some cases. 164 | */ 165 | void integrate(real duration); 166 | 167 | /*@}*/ 168 | 169 | 170 | /** 171 | * @name Accessor Functions for the Particle's State 172 | * 173 | * These functions provide access to the particle's 174 | * characteristics or state. 175 | */ 176 | /*@{*/ 177 | 178 | /** 179 | * Sets the mass of the particle. 180 | * 181 | * @param mass The new mass of the body. This may not be zero. 182 | * Small masses can produce unstable rigid bodies under 183 | * simulation. 184 | * 185 | * @warning This invalidates internal data for the particle. 186 | * Either an integration function, or the calculateInternals 187 | * function should be called before trying to get any settings 188 | * from the particle. 189 | */ 190 | void setMass(const real mass); 191 | 192 | /** 193 | * Gets the mass of the particle. 194 | * 195 | * @return The current mass of the particle. 196 | */ 197 | real getMass() const; 198 | 199 | /** 200 | * Sets the inverse mass of the particle. 201 | * 202 | * @param inverseMass The new inverse mass of the body. This 203 | * may be zero, for a body with infinite mass 204 | * (i.e. unmovable). 205 | * 206 | * @warning This invalidates internal data for the particle. 207 | * Either an integration function, or the calculateInternals 208 | * function should be called before trying to get any settings 209 | * from the particle. 210 | */ 211 | void setInverseMass(const real inverseMass); 212 | 213 | /** 214 | * Gets the inverse mass of the particle. 215 | * 216 | * @return The current inverse mass of the particle. 217 | */ 218 | real getInverseMass() const; 219 | 220 | /** 221 | * Returns true if the mass of the particle is not-infinite. 222 | */ 223 | bool hasFiniteMass() const; 224 | 225 | /** 226 | * Sets both the damping of the particle. 227 | */ 228 | void setDamping(const real damping); 229 | 230 | /** 231 | * Gets the current damping value. 232 | */ 233 | real getDamping() const; 234 | 235 | /** 236 | * Sets the position of the particle. 237 | * 238 | * @param position The new position of the particle. 239 | */ 240 | void setPosition(const Vector3 &position); 241 | 242 | /** 243 | * Sets the position of the particle by component. 244 | * 245 | * @param x The x coordinate of the new position of the rigid 246 | * body. 247 | * 248 | * @param y The y coordinate of the new position of the rigid 249 | * body. 250 | * 251 | * @param z The z coordinate of the new position of the rigid 252 | * body. 253 | */ 254 | void setPosition(const real x, const real y, const real z); 255 | 256 | /** 257 | * Fills the given vector with the position of the particle. 258 | * 259 | * @param position A pointer to a vector into which to write 260 | * the position. 261 | */ 262 | void getPosition(Vector3 *position) const; 263 | 264 | /** 265 | * Gets the position of the particle. 266 | * 267 | * @return The position of the particle. 268 | */ 269 | Vector3 getPosition() const; 270 | 271 | /** 272 | * Sets the velocity of the particle. 273 | * 274 | * @param velocity The new velocity of the particle. 275 | */ 276 | void setVelocity(const Vector3 &velocity); 277 | 278 | /** 279 | * Sets the velocity of the particle by component. 280 | * 281 | * @param x The x coordinate of the new velocity of the rigid 282 | * body. 283 | * 284 | * @param y The y coordinate of the new velocity of the rigid 285 | * body. 286 | * 287 | * @param z The z coordinate of the new velocity of the rigid 288 | * body. 289 | */ 290 | void setVelocity(const real x, const real y, const real z); 291 | 292 | /** 293 | * Fills the given vector with the velocity of the particle. 294 | * 295 | * @param velocity A pointer to a vector into which to write 296 | * the velocity. The velocity is given in world local space. 297 | */ 298 | void getVelocity(Vector3 *velocity) const; 299 | 300 | /** 301 | * Gets the velocity of the particle. 302 | * 303 | * @return The velocity of the particle. The velocity is 304 | * given in world local space. 305 | */ 306 | Vector3 getVelocity() const; 307 | 308 | /** 309 | * Sets the constant acceleration of the particle. 310 | * 311 | * @param acceleration The new acceleration of the particle. 312 | */ 313 | void setAcceleration(const Vector3 &acceleration); 314 | 315 | /** 316 | * Sets the constant acceleration of the particle by component. 317 | * 318 | * @param x The x coordinate of the new acceleration of the rigid 319 | * body. 320 | * 321 | * @param y The y coordinate of the new acceleration of the rigid 322 | * body. 323 | * 324 | * @param z The z coordinate of the new acceleration of the rigid 325 | * body. 326 | */ 327 | void setAcceleration(const real x, const real y, const real z); 328 | 329 | /** 330 | * Fills the given vector with the acceleration of the particle. 331 | * 332 | * @param acceleration A pointer to a vector into which to write 333 | * the acceleration. The acceleration is given in world local space. 334 | */ 335 | void getAcceleration(Vector3 *acceleration) const; 336 | 337 | /** 338 | * Gets the acceleration of the particle. 339 | * 340 | * @return The acceleration of the particle. The acceleration is 341 | * given in world local space. 342 | */ 343 | Vector3 getAcceleration() const; 344 | 345 | /*@}*/ 346 | 347 | /** 348 | * @name Force Set-up Functions 349 | * 350 | * These functions set up forces to apply to the 351 | * particle. 352 | */ 353 | /*@{*/ 354 | 355 | /** 356 | * Clears the forces applied to the particle. This will be 357 | * called automatically after each integration step. 358 | */ 359 | void clearAccumulator(); 360 | 361 | /** 362 | * Adds the given force to the particle, to be applied at the 363 | * next iteration only. 364 | * 365 | * @param force The force to apply. 366 | */ 367 | void addForce(const Vector3 &force); 368 | 369 | 370 | }; 371 | } 372 | 373 | #endif // CYCLONE_BODY_H -------------------------------------------------------------------------------- /src/demos/ragdoll/ragdoll.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The ragdoll demo. 3 | * 4 | * Part of the Cyclone physics system. 5 | * 6 | * Copyright (c) Icosagon 2003. All Rights Reserved. 7 | * 8 | * This software is distributed under licence. Use of this software 9 | * implies agreement with all terms and conditions of the accompanying 10 | * software licence. 11 | */ 12 | 13 | #include 14 | #include "../ogl_headers.h" 15 | #include "../app.h" 16 | #include "../timing.h" 17 | 18 | #include 19 | 20 | #define NUM_BONES 12 21 | #define NUM_JOINTS 11 22 | 23 | class Bone : public cyclone::CollisionBox 24 | { 25 | public: 26 | Bone() 27 | { 28 | body = new cyclone::RigidBody(); 29 | } 30 | 31 | ~Bone() 32 | { 33 | delete body; 34 | } 35 | 36 | /** 37 | * We use a sphere to collide bone on bone to allow some limited 38 | * interpenetration. 39 | */ 40 | cyclone::CollisionSphere getCollisionSphere() const 41 | { 42 | cyclone::CollisionSphere sphere; 43 | sphere.body = body; 44 | sphere.radius = halfSize.x; 45 | sphere.offset = cyclone::Matrix4(); 46 | if (halfSize.y < sphere.radius) sphere.radius = halfSize.y; 47 | if (halfSize.z < sphere.radius) sphere.radius = halfSize.z; 48 | sphere.calculateInternals(); 49 | return sphere; 50 | } 51 | 52 | /** Draws the bone. */ 53 | void render() 54 | { 55 | // Get the OpenGL transformation 56 | GLfloat mat[16]; 57 | body->getGLTransform(mat); 58 | 59 | if (body->getAwake()) glColor3f(0.5f, 0.3f, 0.3f); 60 | else glColor3f(0.3f, 0.3f, 0.5f); 61 | 62 | glPushMatrix(); 63 | glMultMatrixf(mat); 64 | glScalef(halfSize.x*2, halfSize.y*2, halfSize.z*2); 65 | glutSolidCube(1.0f); 66 | glPopMatrix(); 67 | } 68 | 69 | /** Sets the bone to a specific location. */ 70 | void setState(const cyclone::Vector3 &position, 71 | const cyclone::Vector3 &extents) 72 | { 73 | body->setPosition(position); 74 | body->setOrientation(cyclone::Quaternion()); 75 | body->setVelocity(cyclone::Vector3()); 76 | body->setRotation(cyclone::Vector3()); 77 | halfSize = extents; 78 | 79 | cyclone::real mass = halfSize.x * halfSize.y * halfSize.z * 8.0f; 80 | body->setMass(mass); 81 | 82 | cyclone::Matrix3 tensor; 83 | tensor.setBlockInertiaTensor(halfSize, mass); 84 | body->setInertiaTensor(tensor); 85 | 86 | body->setLinearDamping(0.95f); 87 | body->setAngularDamping(0.8f); 88 | body->clearAccumulators(); 89 | body->setAcceleration(cyclone::Vector3::GRAVITY); 90 | 91 | body->setCanSleep(false); 92 | body->setAwake(); 93 | 94 | body->calculateDerivedData(); 95 | calculateInternals(); 96 | } 97 | 98 | }; 99 | 100 | /** 101 | * The main demo class definition. 102 | */ 103 | class RagdollDemo : public RigidBodyApplication 104 | { 105 | cyclone::Random random; 106 | 107 | /** Holds the bone bodies. */ 108 | Bone bones[NUM_BONES]; 109 | 110 | /** Holds the joints. */ 111 | cyclone::Joint joints[NUM_JOINTS]; 112 | 113 | /** Processes the contact generation code. */ 114 | virtual void generateContacts(); 115 | 116 | /** Processes the objects in the simulation forward in time. */ 117 | virtual void updateObjects(cyclone::real duration); 118 | 119 | /** Resets the position of all the bones. */ 120 | virtual void reset(); 121 | 122 | public: 123 | /** Creates a new demo object. */ 124 | RagdollDemo(); 125 | 126 | /** Sets up the rendering. */ 127 | virtual void initGraphics(); 128 | 129 | /** Returns the window title for the demo. */ 130 | virtual const char* getTitle(); 131 | 132 | /** Display the particle positions. */ 133 | virtual void display(); 134 | }; 135 | 136 | // Method definitions 137 | RagdollDemo::RagdollDemo() 138 | : 139 | RigidBodyApplication() 140 | { 141 | // Set up the bone hierarchy. 142 | 143 | // Right Knee 144 | joints[0].set( 145 | bones[0].body, cyclone::Vector3(0, 1.07f, 0), 146 | bones[1].body, cyclone::Vector3(0, -1.07f, 0), 147 | 0.15f 148 | ); 149 | 150 | // Left Knee 151 | joints[1].set( 152 | bones[2].body, cyclone::Vector3(0, 1.07f, 0), 153 | bones[3].body, cyclone::Vector3(0, -1.07f, 0), 154 | 0.15f 155 | ); 156 | 157 | // Right elbow 158 | joints[2].set( 159 | bones[9].body, cyclone::Vector3(0, 0.96f, 0), 160 | bones[8].body, cyclone::Vector3(0, -0.96f, 0), 161 | 0.15f 162 | ); 163 | 164 | // Left elbow 165 | joints[3].set( 166 | bones[11].body, cyclone::Vector3(0, 0.96f, 0), 167 | bones[10].body, cyclone::Vector3(0, -0.96f, 0), 168 | 0.15f 169 | ); 170 | 171 | // Stomach to Waist 172 | joints[4].set( 173 | bones[4].body, cyclone::Vector3(0.054f, 0.50f, 0), 174 | bones[5].body, cyclone::Vector3(-0.043f, -0.45f, 0), 175 | 0.15f 176 | ); 177 | 178 | joints[5].set( 179 | bones[5].body, cyclone::Vector3(-0.043f, 0.411f, 0), 180 | bones[6].body, cyclone::Vector3(0, -0.411f, 0), 181 | 0.15f 182 | ); 183 | 184 | joints[6].set( 185 | bones[6].body, cyclone::Vector3(0, 0.521f, 0), 186 | bones[7].body, cyclone::Vector3(0, -0.752f, 0), 187 | 0.15f 188 | ); 189 | 190 | // Right hip 191 | joints[7].set( 192 | bones[1].body, cyclone::Vector3(0, 1.066f, 0), 193 | bones[4].body, cyclone::Vector3(0, -0.458f, -0.5f), 194 | 0.15f 195 | ); 196 | 197 | // Left Hip 198 | joints[8].set( 199 | bones[3].body, cyclone::Vector3(0, 1.066f, 0), 200 | bones[4].body, cyclone::Vector3(0, -0.458f, 0.5f), 201 | 0.105f 202 | ); 203 | 204 | // Right shoulder 205 | joints[9].set( 206 | bones[6].body, cyclone::Vector3(0, 0.367f, -0.8f), 207 | bones[8].body, cyclone::Vector3(0, 0.888f, 0.32f), 208 | 0.15f 209 | ); 210 | 211 | // Left shoulder 212 | joints[10].set( 213 | bones[6].body, cyclone::Vector3(0, 0.367f, 0.8f), 214 | bones[10].body, cyclone::Vector3(0, 0.888f, -0.32f), 215 | 0.15f 216 | ); 217 | 218 | // Set up the initial positions 219 | reset(); 220 | } 221 | 222 | const char* RagdollDemo::getTitle() 223 | { 224 | return "Cyclone > Ragdoll Demo"; 225 | } 226 | 227 | void RagdollDemo::generateContacts() 228 | { 229 | // Create the ground plane data 230 | cyclone::CollisionPlane plane; 231 | plane.direction = cyclone::Vector3(0,1,0); 232 | plane.offset = 0; 233 | 234 | // Set up the collision data structure 235 | cData.reset(maxContacts); 236 | cData.friction = (cyclone::real)0.9; 237 | cData.restitution = (cyclone::real)0.6; 238 | cData.tolerance = (cyclone::real)0.1; 239 | 240 | // Perform exhaustive collision detection on the ground plane 241 | cyclone::Matrix4 transform, otherTransform; 242 | cyclone::Vector3 position, otherPosition; 243 | for (Bone *bone = bones; bone < bones+NUM_BONES; bone++) 244 | { 245 | // Check for collisions with the ground plane 246 | if (!cData.hasMoreContacts()) return; 247 | cyclone::CollisionDetector::boxAndHalfSpace(*bone, plane, &cData); 248 | 249 | cyclone::CollisionSphere boneSphere = bone->getCollisionSphere(); 250 | 251 | // Check for collisions with each other box 252 | for (Bone *other = bone+1; other < bones+NUM_BONES; other++) 253 | { 254 | if (!cData.hasMoreContacts()) return; 255 | 256 | cyclone::CollisionSphere otherSphere = other->getCollisionSphere(); 257 | 258 | cyclone::CollisionDetector::sphereAndSphere( 259 | boneSphere, 260 | otherSphere, 261 | &cData 262 | ); 263 | } 264 | } 265 | 266 | // Check for joint violation 267 | for (cyclone::Joint *joint = joints; joint < joints+NUM_JOINTS; joint++) 268 | { 269 | if (!cData.hasMoreContacts()) return; 270 | unsigned added = joint->addContact(cData.contacts, cData.contactsLeft); 271 | cData.addContacts(added); 272 | } 273 | } 274 | 275 | void RagdollDemo::reset() 276 | { 277 | bones[0].setState( 278 | cyclone::Vector3(0, 0.993, -0.5), 279 | cyclone::Vector3(0.301, 1.0, 0.234)); 280 | bones[1].setState( 281 | cyclone::Vector3(0, 3.159, -0.56), 282 | cyclone::Vector3(0.301, 1.0, 0.234)); 283 | bones[2].setState( 284 | cyclone::Vector3(0, 0.993, 0.5), 285 | cyclone::Vector3(0.301, 1.0, 0.234)); 286 | bones[3].setState( 287 | cyclone::Vector3(0, 3.15, 0.56), 288 | cyclone::Vector3(0.301, 1.0, 0.234)); 289 | bones[4].setState( 290 | cyclone::Vector3(-0.054, 4.683, 0.013), 291 | cyclone::Vector3(0.415, 0.392, 0.690)); 292 | bones[5].setState( 293 | cyclone::Vector3(0.043, 5.603, 0.013), 294 | cyclone::Vector3(0.301, 0.367, 0.693)); 295 | bones[6].setState( 296 | cyclone::Vector3(0, 6.485, 0.013), 297 | cyclone::Vector3(0.435, 0.367, 0.786)); 298 | bones[7].setState( 299 | cyclone::Vector3(0, 7.759, 0.013), 300 | cyclone::Vector3(0.45, 0.598, 0.421)); 301 | bones[8].setState( 302 | cyclone::Vector3(0, 5.946, -1.066), 303 | cyclone::Vector3(0.267, 0.888, 0.207)); 304 | bones[9].setState( 305 | cyclone::Vector3(0, 4.024, -1.066), 306 | cyclone::Vector3(0.267, 0.888, 0.207)); 307 | bones[10].setState( 308 | cyclone::Vector3(0, 5.946, 1.066), 309 | cyclone::Vector3(0.267, 0.888, 0.207)); 310 | bones[11].setState( 311 | cyclone::Vector3(0, 4.024, 1.066), 312 | cyclone::Vector3(0.267, 0.888, 0.207)); 313 | 314 | cyclone::real strength = -random.randomReal(500.0f, 1000.0f); 315 | for (unsigned i = 0; i < NUM_BONES; i++) 316 | { 317 | bones[i].body->addForceAtBodyPoint( 318 | cyclone::Vector3(strength, 0, 0), cyclone::Vector3() 319 | ); 320 | } 321 | bones[6].body->addForceAtBodyPoint( 322 | cyclone::Vector3(strength, 0, random.randomBinomial(1000.0f)), 323 | cyclone::Vector3(random.randomBinomial(4.0f), random.randomBinomial(3.0f), 0) 324 | ); 325 | 326 | // Reset the contacts 327 | cData.contactCount = 0; 328 | } 329 | 330 | void RagdollDemo::updateObjects(cyclone::real duration) 331 | { 332 | for (Bone *bone = bones; bone < bones+NUM_BONES; bone++) 333 | { 334 | bone->body->integrate(duration); 335 | bone->calculateInternals(); 336 | } 337 | } 338 | 339 | void RagdollDemo::initGraphics() 340 | { 341 | GLfloat lightAmbient[] = {0.8f,0.8f,0.8f,1.0f}; 342 | GLfloat lightDiffuse[] = {0.9f,0.95f,1.0f,1.0f}; 343 | 344 | glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient); 345 | glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse); 346 | 347 | glEnable(GL_LIGHT0); 348 | 349 | Application::initGraphics(); 350 | } 351 | 352 | void RagdollDemo::display() 353 | { 354 | const static GLfloat lightPosition[] = {0.7f,-1,0.4f,0}; 355 | const static GLfloat lightPositionMirror[] = {0.7f,1,0.4f,0}; 356 | 357 | RigidBodyApplication::display(); 358 | 359 | // Render the bones 360 | glEnable(GL_DEPTH_TEST); 361 | glEnable(GL_LIGHTING); 362 | glEnable(GL_LIGHT0); 363 | 364 | glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); 365 | glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); 366 | glEnable(GL_COLOR_MATERIAL); 367 | 368 | glEnable(GL_NORMALIZE); 369 | glColor3f(1,0,0); 370 | for (unsigned i = 0; i < NUM_BONES; i++) 371 | { 372 | bones[i].render(); 373 | } 374 | glDisable(GL_NORMALIZE); 375 | 376 | glDisable(GL_LIGHTING); 377 | glDisable(GL_COLOR_MATERIAL); 378 | 379 | glDisable(GL_DEPTH_TEST); 380 | glBegin(GL_LINES); 381 | for (unsigned i = 0; i < NUM_JOINTS; i++) 382 | { 383 | cyclone::Joint *joint = joints + i; 384 | cyclone::Vector3 a_pos = joint->body[0]->getPointInWorldSpace(joint->position[0]); 385 | cyclone::Vector3 b_pos = joint->body[1]->getPointInWorldSpace(joint->position[1]); 386 | cyclone::real length = (b_pos - a_pos).magnitude(); 387 | 388 | if (length > joint->error) glColor3f(1,0,0); 389 | else glColor3f(0,1,0); 390 | 391 | glVertex3f(a_pos.x, a_pos.y, a_pos.z); 392 | glVertex3f(b_pos.x, b_pos.y, b_pos.z); 393 | } 394 | glEnd(); 395 | glEnable(GL_DEPTH_TEST); 396 | 397 | // Draw some scale circles 398 | glColor3f(0.75, 0.75, 0.75); 399 | for (unsigned i = 1; i < 20; i++) 400 | { 401 | glBegin(GL_LINE_LOOP); 402 | for (unsigned j = 0; j < 32; j++) 403 | { 404 | float theta = 3.1415926f * j / 16.0f; 405 | glVertex3f(i*cosf(theta),0.0f,i*sinf(theta)); 406 | } 407 | glEnd(); 408 | } 409 | glBegin(GL_LINES); 410 | glVertex3f(-20,0,0); 411 | glVertex3f(20,0,0); 412 | glVertex3f(0,0,-20); 413 | glVertex3f(0,0,20); 414 | glEnd(); 415 | 416 | RigidBodyApplication::drawDebug(); 417 | } 418 | 419 | /** 420 | * Called by the common demo framework to create an application 421 | * object (with new) and return a pointer. 422 | */ 423 | Application* getApplication() 424 | { 425 | return new RagdollDemo(); 426 | } --------------------------------------------------------------------------------