Move source to src.
This commit is contained in:
parent
6f246722f9
commit
2442efc1a6
82 changed files with 13 additions and 236 deletions
11
src/SDLMain.h
Normal file
11
src/SDLMain.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
|
||||
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
|
||||
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
|
||||
|
||||
Feel free to customize this file to suit your needs
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface SDLMain : NSObject
|
||||
@end
|
||||
382
src/SDLMain.m
Normal file
382
src/SDLMain.m
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
|
||||
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
|
||||
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
|
||||
|
||||
Feel free to customize this file to suit your needs
|
||||
*/
|
||||
|
||||
#import <SDL/SDL.h>
|
||||
#import "SDLMain.h"
|
||||
#import <sys/param.h> /* for MAXPATHLEN */
|
||||
#import <unistd.h>
|
||||
|
||||
/* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
|
||||
but the method still is there and works. To avoid warnings, we declare
|
||||
it ourselves here. */
|
||||
@interface NSApplication(SDL_Missing_Methods)
|
||||
- (void)setAppleMenu:(NSMenu *)menu;
|
||||
@end
|
||||
|
||||
/* Use this flag to determine whether we use SDLMain.nib or not */
|
||||
#define SDL_USE_NIB_FILE 0
|
||||
|
||||
/* Use this flag to determine whether we use CPS (docking) or not */
|
||||
#define SDL_USE_CPS 1
|
||||
#ifdef SDL_USE_CPS
|
||||
/* Portions of CPS.h */
|
||||
typedef struct CPSProcessSerNum
|
||||
{
|
||||
UInt32 lo;
|
||||
UInt32 hi;
|
||||
} CPSProcessSerNum;
|
||||
|
||||
extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
|
||||
extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
|
||||
extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
|
||||
|
||||
#endif /* SDL_USE_CPS */
|
||||
|
||||
static int gArgc;
|
||||
static char **gArgv;
|
||||
static BOOL gFinderLaunch;
|
||||
static BOOL gCalledAppMainline = FALSE;
|
||||
|
||||
static NSString *getApplicationName(void)
|
||||
{
|
||||
NSDictionary *dict;
|
||||
NSString *appName = 0;
|
||||
|
||||
/* Determine the application name */
|
||||
dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
|
||||
if (dict)
|
||||
appName = [dict objectForKey: @"CFBundleName"];
|
||||
|
||||
if (![appName length])
|
||||
appName = [[NSProcessInfo processInfo] processName];
|
||||
|
||||
return appName;
|
||||
}
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
/* A helper category for NSString */
|
||||
@interface NSString (ReplaceSubString)
|
||||
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
|
||||
@end
|
||||
#endif
|
||||
|
||||
@interface SDLApplication : NSApplication
|
||||
@end
|
||||
|
||||
@implementation SDLApplication
|
||||
/* Invoked from the Quit menu item */
|
||||
- (void)terminate:(id)sender
|
||||
{
|
||||
/* Post a SDL_QUIT event */
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
@end
|
||||
|
||||
/* The main class of the application, the application's delegate */
|
||||
@implementation SDLMain
|
||||
|
||||
/* Set the working directory to the .app's parent directory */
|
||||
- (void) setupWorkingDirectory:(BOOL)shouldChdir
|
||||
{
|
||||
if (shouldChdir)
|
||||
{
|
||||
char appdir[MAXPATHLEN];
|
||||
CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
||||
if (CFURLGetFileSystemRepresentation(url, true, (UInt8 *)appdir, MAXPATHLEN)) {
|
||||
assert ( chdir (appdir) == 0 ); /* chdir to the binary app */
|
||||
}
|
||||
CFRelease(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
|
||||
/* Fix menu to contain the real app name instead of "SDL App" */
|
||||
- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
|
||||
{
|
||||
NSRange aRange;
|
||||
NSEnumerator *enumerator;
|
||||
NSMenuItem *menuItem;
|
||||
|
||||
aRange = [[aMenu title] rangeOfString:@"SDL App"];
|
||||
if (aRange.length != 0)
|
||||
[aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
|
||||
|
||||
enumerator = [[aMenu itemArray] objectEnumerator];
|
||||
while ((menuItem = [enumerator nextObject]))
|
||||
{
|
||||
aRange = [[menuItem title] rangeOfString:@"SDL App"];
|
||||
if (aRange.length != 0)
|
||||
[menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
|
||||
if ([menuItem hasSubmenu])
|
||||
[self fixMenu:[menuItem submenu] withAppName:appName];
|
||||
}
|
||||
[ aMenu sizeToFit ];
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void setApplicationMenu(void)
|
||||
{
|
||||
/* warning: this code is very odd */
|
||||
NSMenu *appleMenu;
|
||||
NSMenuItem *menuItem;
|
||||
NSString *title;
|
||||
NSString *appName;
|
||||
|
||||
appName = getApplicationName();
|
||||
appleMenu = [[NSMenu alloc] initWithTitle:@""];
|
||||
|
||||
/* Add menu items */
|
||||
title = [@"About " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Hide " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
|
||||
|
||||
menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
|
||||
[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
|
||||
|
||||
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
|
||||
|
||||
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
title = [@"Quit " stringByAppendingString:appName];
|
||||
[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
|
||||
|
||||
|
||||
/* Put menu into the menubar */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
|
||||
[menuItem setSubmenu:appleMenu];
|
||||
[[NSApp mainMenu] addItem:menuItem];
|
||||
|
||||
/* Tell the application object that this is now the application menu */
|
||||
[NSApp setAppleMenu:appleMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[appleMenu release];
|
||||
[menuItem release];
|
||||
}
|
||||
|
||||
/* Create a window menu */
|
||||
static void setupWindowMenu(void)
|
||||
{
|
||||
NSMenu *windowMenu;
|
||||
NSMenuItem *windowMenuItem;
|
||||
NSMenuItem *menuItem;
|
||||
|
||||
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
|
||||
/* "Minimize" item */
|
||||
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
|
||||
[windowMenu addItem:menuItem];
|
||||
[menuItem release];
|
||||
|
||||
/* Put menu into the menubar */
|
||||
windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
|
||||
[windowMenuItem setSubmenu:windowMenu];
|
||||
[[NSApp mainMenu] addItem:windowMenuItem];
|
||||
|
||||
/* Tell the application object that this is now the window menu */
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
|
||||
/* Finally give up our references to the objects */
|
||||
[windowMenu release];
|
||||
[windowMenuItem release];
|
||||
}
|
||||
|
||||
/* Replacement for NSApplicationMain */
|
||||
static void CustomApplicationMain (int argc, char **argv)
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
SDLMain *sdlMain;
|
||||
|
||||
/* Ensure the application object is initialised */
|
||||
[SDLApplication sharedApplication];
|
||||
|
||||
#ifdef SDL_USE_CPS
|
||||
{
|
||||
CPSProcessSerNum PSN;
|
||||
/* Tell the dock about us */
|
||||
if (!CPSGetCurrentProcess(&PSN))
|
||||
if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
|
||||
if (!CPSSetFrontProcess(&PSN))
|
||||
[SDLApplication sharedApplication];
|
||||
}
|
||||
#endif /* SDL_USE_CPS */
|
||||
|
||||
/* Set up the menubar */
|
||||
[NSApp setMainMenu:[[NSMenu alloc] init]];
|
||||
setApplicationMenu();
|
||||
setupWindowMenu();
|
||||
|
||||
/* Create SDLMain and make it the app delegate */
|
||||
sdlMain = [[SDLMain alloc] init];
|
||||
[NSApp setDelegate:sdlMain];
|
||||
|
||||
/* Start the main event loop */
|
||||
[NSApp run];
|
||||
|
||||
[sdlMain release];
|
||||
[pool release];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Catch document open requests...this lets us notice files when the app
|
||||
* was launched by double-clicking a document, or when a document was
|
||||
* dragged/dropped on the app's icon. You need to have a
|
||||
* CFBundleDocumentsType section in your Info.plist to get this message,
|
||||
* apparently.
|
||||
*
|
||||
* Files are added to gArgv, so to the app, they'll look like command line
|
||||
* arguments. Previously, apps launched from the finder had nothing but
|
||||
* an argv[0].
|
||||
*
|
||||
* This message may be received multiple times to open several docs on launch.
|
||||
*
|
||||
* This message is ignored once the app's mainline has been called.
|
||||
*/
|
||||
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
|
||||
{
|
||||
const char *temparg;
|
||||
size_t arglen;
|
||||
char *arg;
|
||||
char **newargv;
|
||||
|
||||
if (!gFinderLaunch) /* MacOS is passing command line args. */
|
||||
return FALSE;
|
||||
|
||||
if (gCalledAppMainline) /* app has started, ignore this document. */
|
||||
return FALSE;
|
||||
|
||||
temparg = [filename UTF8String];
|
||||
arglen = SDL_strlen(temparg) + 1;
|
||||
arg = (char *) SDL_malloc(arglen);
|
||||
if (arg == NULL)
|
||||
return FALSE;
|
||||
|
||||
newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
|
||||
if (newargv == NULL)
|
||||
{
|
||||
SDL_free(arg);
|
||||
return FALSE;
|
||||
}
|
||||
gArgv = newargv;
|
||||
|
||||
SDL_strlcpy(arg, temparg, arglen);
|
||||
gArgv[gArgc++] = arg;
|
||||
gArgv[gArgc] = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* Called when the internal event loop has just started running */
|
||||
- (void) applicationDidFinishLaunching: (NSNotification *) note
|
||||
{
|
||||
int status;
|
||||
|
||||
/* Set the working directory to the .app's parent directory */
|
||||
[self setupWorkingDirectory:gFinderLaunch];
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
/* Set the main menu to contain the real app name instead of "SDL App" */
|
||||
[self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
|
||||
#endif
|
||||
|
||||
/* Hand off to main application code */
|
||||
gCalledAppMainline = TRUE;
|
||||
status = SDL_main (gArgc, gArgv);
|
||||
|
||||
/* We're done, thank you for playing */
|
||||
exit(status);
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSString (ReplaceSubString)
|
||||
|
||||
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
|
||||
{
|
||||
unsigned int bufferSize;
|
||||
unsigned int selfLen = [self length];
|
||||
unsigned int aStringLen = [aString length];
|
||||
unichar *buffer;
|
||||
NSRange localRange;
|
||||
NSString *result;
|
||||
|
||||
bufferSize = selfLen + aStringLen - aRange.length;
|
||||
buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar));
|
||||
|
||||
/* Get first part into buffer */
|
||||
localRange.location = 0;
|
||||
localRange.length = aRange.location;
|
||||
[self getCharacters:buffer range:localRange];
|
||||
|
||||
/* Get middle part into buffer */
|
||||
localRange.location = 0;
|
||||
localRange.length = aStringLen;
|
||||
[aString getCharacters:(buffer+aRange.location) range:localRange];
|
||||
|
||||
/* Get last part into buffer */
|
||||
localRange.location = aRange.location + aRange.length;
|
||||
localRange.length = selfLen - localRange.location;
|
||||
[self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
|
||||
|
||||
/* Build output string */
|
||||
result = [NSString stringWithCharacters:buffer length:bufferSize];
|
||||
|
||||
NSDeallocateMemoryPages(buffer, bufferSize);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
#ifdef main
|
||||
# undef main
|
||||
#endif
|
||||
|
||||
|
||||
/* Main entry point to executable - should *not* be SDL_main! */
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
/* Copy the arguments into a global variable */
|
||||
/* This is passed if we are launched by double-clicking */
|
||||
if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
|
||||
gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
|
||||
gArgv[0] = argv[0];
|
||||
gArgv[1] = NULL;
|
||||
gArgc = 1;
|
||||
gFinderLaunch = YES;
|
||||
} else {
|
||||
int i;
|
||||
gArgc = argc;
|
||||
gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
|
||||
for (i = 0; i <= argc; i++)
|
||||
gArgv[i] = argv[i];
|
||||
gFinderLaunch = NO;
|
||||
}
|
||||
|
||||
#if SDL_USE_NIB_FILE
|
||||
[SDLApplication poseAsClass:[NSApplication class]];
|
||||
NSApplicationMain (argc, argv);
|
||||
#else
|
||||
CustomApplicationMain (argc, argv);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
26
src/ball.cpp
Normal file
26
src/ball.cpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "ball.h"
|
||||
#include "material.h"
|
||||
#include "model.h"
|
||||
#include "physics/shape.h"
|
||||
#include "physics/vector.h"
|
||||
#include "room.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Ball::Ball(const Vector& x, float radius)
|
||||
: sphere_(x, radius), model_(sphere_) {
|
||||
}
|
||||
|
||||
Model& Ball::model() {
|
||||
return model_;
|
||||
}
|
||||
|
||||
const Shape& Ball::shape() const {
|
||||
return sphere_;
|
||||
}
|
||||
|
||||
float Ball::slip() const {
|
||||
return model_.material().slip();
|
||||
}
|
||||
30
src/ball.h
Normal file
30
src/ball.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_BALL_H
|
||||
#define MBOSTOCK_BALL_H
|
||||
|
||||
#include "model.h"
|
||||
#include "physics/shape.h"
|
||||
#include "room_object.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Ball : public RoomObject {
|
||||
public:
|
||||
Ball(const Vector& x, float radius);
|
||||
|
||||
virtual Model& model();
|
||||
virtual const Shape& shape() const;
|
||||
virtual float slip() const;
|
||||
|
||||
inline void setMaterial(const Material& m) { model_.setMaterial(m); }
|
||||
|
||||
private:
|
||||
const Sphere sphere_;
|
||||
SphereModel model_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
58
src/block.cpp
Normal file
58
src/block.cpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "block.h"
|
||||
#include "material.h"
|
||||
#include "model.h"
|
||||
#include "physics/shape.h"
|
||||
#include "physics/vector.h"
|
||||
#include "room.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
AxisAlignedBlock::AxisAlignedBlock(const Vector& min, const Vector& max)
|
||||
: box_(min, max), model_(box_) {
|
||||
}
|
||||
|
||||
Model& AxisAlignedBlock::model() {
|
||||
return model_;
|
||||
}
|
||||
|
||||
const Shape& AxisAlignedBlock::shape() const {
|
||||
return box_;
|
||||
}
|
||||
|
||||
void AxisAlignedBlock::setMaterial(const Material& m) {
|
||||
model_.setMaterial(m);
|
||||
}
|
||||
|
||||
void AxisAlignedBlock::setTopMaterial(const Material& m) {
|
||||
model_.setTopMaterial(m);
|
||||
}
|
||||
|
||||
float AxisAlignedBlock::slip() const {
|
||||
return model_.material().slip();
|
||||
}
|
||||
|
||||
Block::Block(const Vector& c, const Vector& x, const Vector& y, const Vector& z)
|
||||
: box_(c, x, y, z), model_(box_) {
|
||||
}
|
||||
|
||||
Model& Block::model() {
|
||||
return model_;
|
||||
}
|
||||
|
||||
const Shape& Block::shape() const {
|
||||
return box_;
|
||||
}
|
||||
|
||||
void Block::setMaterial(const Material& m) {
|
||||
model_.setMaterial(m);
|
||||
}
|
||||
|
||||
void Block::setTopMaterial(const Material& m) {
|
||||
model_.setTopMaterial(m);
|
||||
}
|
||||
|
||||
float Block::slip() const {
|
||||
return model_.material().slip();
|
||||
}
|
||||
47
src/block.h
Normal file
47
src/block.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_BLOCK_H
|
||||
#define MBOSTOCK_BLOCK_H
|
||||
|
||||
#include "model.h"
|
||||
#include "physics/shape.h"
|
||||
#include "room_object.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class AxisAlignedBlock : public RoomObject {
|
||||
public:
|
||||
AxisAlignedBlock(const Vector& min, const Vector& max);
|
||||
|
||||
virtual Model& model();
|
||||
virtual const Shape& shape() const;
|
||||
virtual float slip() const;
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
void setTopMaterial(const Material& m);
|
||||
|
||||
protected:
|
||||
const AxisAlignedBox box_;
|
||||
AxisAlignedBoxModel model_;
|
||||
};
|
||||
|
||||
class Block : public RoomObject {
|
||||
public:
|
||||
Block(const Vector& c, const Vector& x, const Vector& y, const Vector& z);
|
||||
|
||||
virtual Model& model();
|
||||
virtual const Shape& shape() const;
|
||||
virtual float slip() const;
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
void setTopMaterial(const Material& m);
|
||||
|
||||
private:
|
||||
const Box box_;
|
||||
BoxModel model_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
50
src/escalator.cpp
Normal file
50
src/escalator.cpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "escalator.h"
|
||||
#include "material.h"
|
||||
#include "model.h"
|
||||
#include "physics/particle.h"
|
||||
#include "physics/shape.h"
|
||||
#include "physics/vector.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Escalator::Escalator(const Vector& min, const Vector& max, const Vector& v)
|
||||
: box_(min, max), velocity_(v * ParticleSimulator::timeStep()),
|
||||
model_(box_) {
|
||||
model_.setTexOrientation((fabsf(v.x) > fabsf(v.z))
|
||||
? ((v.x > 0) ? AxisAlignedBoxModel::POSITIVE_X
|
||||
: AxisAlignedBoxModel::NEGATIVE_X)
|
||||
: ((v.z > 0) ? AxisAlignedBoxModel::POSITIVE_Z
|
||||
: AxisAlignedBoxModel::NEGATIVE_Z));
|
||||
}
|
||||
|
||||
Model& Escalator::model() {
|
||||
return model_;
|
||||
}
|
||||
|
||||
const Shape& Escalator::shape() const {
|
||||
return box_;
|
||||
}
|
||||
|
||||
void Escalator::step(const ParticleSimulator& s) {
|
||||
offset_.x = fmodf(offset_.x + velocity_.x, 1.f);
|
||||
offset_.z = fmodf(offset_.z + velocity_.z, 1.f);
|
||||
model_.setTexOffset(-offset_.x, -offset_.z);
|
||||
}
|
||||
|
||||
Vector Escalator::velocity(const Vector& x) const {
|
||||
return velocity_;
|
||||
}
|
||||
|
||||
void Escalator::setMaterial(const Material& m) {
|
||||
model_.setMaterial(m);
|
||||
}
|
||||
|
||||
void Escalator::setTopMaterial(const Material& m) {
|
||||
model_.setTopMaterial(m);
|
||||
}
|
||||
|
||||
float Escalator::slip() const {
|
||||
return model_.material().slip();
|
||||
}
|
||||
39
src/escalator.h
Normal file
39
src/escalator.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_ESCALATOR_H
|
||||
#define MBOSTOCK_ESCALATOR_H
|
||||
|
||||
#include "model.h"
|
||||
#include "physics/shape.h"
|
||||
#include "physics/vector.h"
|
||||
#include "room_object.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Material;
|
||||
class ParticleSimulator;
|
||||
|
||||
class Escalator : public DynamicRoomObject {
|
||||
public:
|
||||
Escalator(const Vector& min, const Vector& max, const Vector& v);
|
||||
|
||||
virtual Model& model();
|
||||
virtual const Shape& shape() const;
|
||||
virtual void step(const ParticleSimulator& s);
|
||||
virtual Vector velocity(const Vector& x) const;
|
||||
virtual float slip() const;
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
void setTopMaterial(const Material& m);
|
||||
|
||||
private:
|
||||
const AxisAlignedBox box_;
|
||||
const Vector velocity_;
|
||||
Vector offset_;
|
||||
|
||||
AxisAlignedBoxModel model_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
151
src/fan.cpp
Normal file
151
src/fan.cpp
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fan.h"
|
||||
#include "material.h"
|
||||
#include "physics/particle.h"
|
||||
#include "physics/shape.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class StaticFanModel : public Model {
|
||||
public:
|
||||
StaticFanModel(float r);
|
||||
virtual ~StaticFanModel();
|
||||
|
||||
virtual void initialize();
|
||||
virtual void display();
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
|
||||
private:
|
||||
void displayBlades();
|
||||
|
||||
const Material* material_;
|
||||
AxisAlignedBox bladeBox_;
|
||||
AxisAlignedBoxModel bladeModel_;
|
||||
GLUquadric* quadric_;
|
||||
float r_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
StaticFanModel::StaticFanModel(float r)
|
||||
: material_(&Materials::blank()),
|
||||
bladeBox_(Vector(0.f, 0.f, 0.f),
|
||||
Vector(r, r / 10.f, r / 20.f)),
|
||||
bladeModel_(bladeBox_), quadric_(NULL), r_(r) {
|
||||
}
|
||||
|
||||
StaticFanModel::~StaticFanModel() {
|
||||
if (quadric_ != NULL) {
|
||||
gluDeleteQuadric(quadric_);
|
||||
}
|
||||
}
|
||||
|
||||
void StaticFanModel::setMaterial(const Material& m) {
|
||||
material_ = &m;
|
||||
bladeModel_.setMaterial(m);
|
||||
}
|
||||
|
||||
void StaticFanModel::initialize() {
|
||||
if (quadric_ != NULL) {
|
||||
gluDeleteQuadric(quadric_);
|
||||
}
|
||||
quadric_ = gluNewQuadric();
|
||||
bladeModel_.initialize();
|
||||
}
|
||||
|
||||
void StaticFanModel::display() {
|
||||
float axleLength = r_ / 10.f * 1.5;
|
||||
float axleRadius = r_ / 20.f;
|
||||
|
||||
material_->bind();
|
||||
glPushMatrix();
|
||||
glTranslatef(0.f, 0.f, axleLength);
|
||||
gluSphere(quadric_, axleRadius, 16, 16);
|
||||
glPopMatrix();
|
||||
gluCylinder(quadric_, axleRadius, axleRadius, axleLength, 16, 4);
|
||||
gluQuadricOrientation(quadric_, GLU_INSIDE);
|
||||
gluDisk(quadric_, 0.f, axleRadius, 16, 4);
|
||||
gluQuadricOrientation(quadric_, GLU_OUTSIDE);
|
||||
displayBlades();
|
||||
}
|
||||
|
||||
void StaticFanModel::displayBlades() {
|
||||
int blades = 12;
|
||||
for (int i = 0, n = blades; i < n; i++) {
|
||||
glPushMatrix();
|
||||
glRotatef(i * 360.f / n, 0.f, 0.f, 1.f);
|
||||
glRotatef(60.f, 1.f, 0.f, 0.f);
|
||||
bladeModel_.display();
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
FanModel::FanModel(const Fan& fan)
|
||||
: fan_(fan), staticModel_(new StaticFanModel(fan.cylinder_.radius())),
|
||||
compiledModel_(Models::compile(staticModel_)) {
|
||||
for (int i = 0; i < 15; i++) {
|
||||
orientation_[i] = 0.f;
|
||||
}
|
||||
orientation_[15] = 1.f;
|
||||
}
|
||||
|
||||
FanModel::~FanModel() {
|
||||
delete compiledModel_;
|
||||
}
|
||||
|
||||
float* FanModel::orientation() {
|
||||
const Vector& z = fan_.cylinder_.z();
|
||||
const Vector& n = (fabsf(z.z) > .5f) ? Vector::Y() : Vector::Z();
|
||||
const Vector& x = n.cross(z);
|
||||
const Vector& y = -x.cross(z);
|
||||
orientation_[0] = x.x; orientation_[1] = x.y; orientation_[2] = x.z;
|
||||
orientation_[4] = y.x; orientation_[5] = y.y; orientation_[6] = y.z;
|
||||
orientation_[8] = z.x; orientation_[9] = z.y; orientation_[10] = z.z;
|
||||
return orientation_;
|
||||
}
|
||||
|
||||
void FanModel::setMaterial(const Material& m) {
|
||||
staticModel_->setMaterial(m);
|
||||
}
|
||||
|
||||
void FanModel::initialize() {
|
||||
compiledModel_->initialize();
|
||||
}
|
||||
|
||||
void FanModel::display() {
|
||||
glPushMatrix();
|
||||
glTranslatev(fan_.cylinder_.x0());
|
||||
glMultMatrixf(orientation());
|
||||
glRotatef(fan_.a_, 0.f, 0.f, 1.f);
|
||||
compiledModel_->display();
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
Fan::Fan(const Vector& x, const Vector& v, float r, float s)
|
||||
: cylinder_(x, x + v * (r / 10.f), r),
|
||||
s_(s * ParticleSimulator::timeStep()),
|
||||
a_(0.f), model_(*this) {
|
||||
}
|
||||
|
||||
Model& Fan::model() {
|
||||
return model_;
|
||||
}
|
||||
|
||||
const Shape& Fan::shape() const {
|
||||
return cylinder_;
|
||||
}
|
||||
|
||||
void Fan::step(const ParticleSimulator& s) {
|
||||
a_ += s_;
|
||||
}
|
||||
|
||||
void Fan::reset() {
|
||||
a_ = 0.f;
|
||||
}
|
||||
55
src/fan.h
Normal file
55
src/fan.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_FAN_H
|
||||
#define MBOSTOCK_FAN_H
|
||||
|
||||
#include "model.h"
|
||||
#include "physics/shape.h"
|
||||
#include "room_object.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Fan;
|
||||
class StaticFanModel;
|
||||
|
||||
class FanModel : public Model {
|
||||
public:
|
||||
FanModel(const Fan& fan);
|
||||
virtual ~FanModel();
|
||||
|
||||
virtual void initialize();
|
||||
virtual void display();
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
|
||||
private:
|
||||
float* orientation();
|
||||
|
||||
const Fan& fan_;
|
||||
StaticFanModel* staticModel_;
|
||||
Model* compiledModel_;
|
||||
float orientation_[16];
|
||||
};
|
||||
|
||||
class Fan : public DynamicRoomObject {
|
||||
public:
|
||||
Fan(const Vector& x, const Vector& v, float r, float s);
|
||||
|
||||
virtual Model& model();
|
||||
virtual const Shape& shape() const;
|
||||
virtual void step(const ParticleSimulator& s);
|
||||
virtual void reset();
|
||||
|
||||
inline void setMaterial(const Material& m) { model_.setMaterial(m); }
|
||||
|
||||
private:
|
||||
const Cylinder cylinder_;
|
||||
const float s_;
|
||||
float a_;
|
||||
FanModel model_;
|
||||
|
||||
friend class FanModel;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
144
src/lighting.cpp
Normal file
144
src/lighting.cpp
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lighting.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Light::Light()
|
||||
: id_(-1), enabled_(false) {
|
||||
setAmbient(0.f, 0.f, 0.f, 1.f);
|
||||
setDiffuse(0.f, 0.f, 0.f, 1.f);
|
||||
setSpecular(0.f, 0.f, 0.f, 1.f);
|
||||
setPosition(0.f, 0.f, 1.f, 0.f);
|
||||
setSpotDirection(0.f, 0.f, -1.f);
|
||||
spotExponent_ = 0.f;
|
||||
spotCutoff_ = 180.f;
|
||||
constantAttenuation_ = 1.f;
|
||||
linearAttenuation_ = 0.f;
|
||||
quadraticAttenuation_ = 0.f;
|
||||
}
|
||||
|
||||
void Light::setAmbient(float r, float g, float b, float a) {
|
||||
ambient_[0] = r;
|
||||
ambient_[1] = g;
|
||||
ambient_[2] = b;
|
||||
ambient_[3] = a;
|
||||
}
|
||||
|
||||
void Light::setDiffuse(float r, float g, float b, float a) {
|
||||
diffuse_[0] = r;
|
||||
diffuse_[1] = g;
|
||||
diffuse_[2] = b;
|
||||
diffuse_[3] = a;
|
||||
}
|
||||
|
||||
void Light::setSpecular(float r, float g, float b, float a) {
|
||||
specular_[0] = r;
|
||||
specular_[1] = g;
|
||||
specular_[2] = b;
|
||||
specular_[3] = a;
|
||||
}
|
||||
|
||||
void Light::setPosition(float x, float y, float z, float w) {
|
||||
position_[0] = x;
|
||||
position_[1] = y;
|
||||
position_[2] = z;
|
||||
position_[3] = w;
|
||||
}
|
||||
|
||||
void Light::setSpotDirection(float x, float y, float z) {
|
||||
spotDirection_[0] = x;
|
||||
spotDirection_[1] = y;
|
||||
spotDirection_[2] = z;
|
||||
}
|
||||
|
||||
void Light::setSpotExponent(float e) {
|
||||
spotExponent_ = e;
|
||||
}
|
||||
|
||||
void Light::setConstantAttenuation(float a) {
|
||||
constantAttenuation_ = a;
|
||||
}
|
||||
|
||||
void Light::setLinearAttenuation(float a) {
|
||||
linearAttenuation_ = a;
|
||||
}
|
||||
|
||||
void Light::setQuadraticAttenuation(float a) {
|
||||
quadraticAttenuation_ = a;
|
||||
}
|
||||
|
||||
void Light::initialize() const {
|
||||
if (enabled_) {
|
||||
glEnable(id_);
|
||||
glLightfv(id_, GL_AMBIENT, ambient_);
|
||||
glLightfv(id_, GL_DIFFUSE, diffuse_);
|
||||
glLightfv(id_, GL_SPECULAR, specular_);
|
||||
glLightf(id_, GL_SPOT_EXPONENT, spotExponent_);
|
||||
glLightf(id_, GL_SPOT_CUTOFF, spotCutoff_);
|
||||
glLightf(id_, GL_CONSTANT_ATTENUATION, constantAttenuation_);
|
||||
glLightf(id_, GL_LINEAR_ATTENUATION, linearAttenuation_);
|
||||
glLightf(id_, GL_QUADRATIC_ATTENUATION, quadraticAttenuation_);
|
||||
} else {
|
||||
glDisable(id_);
|
||||
}
|
||||
}
|
||||
|
||||
void Light::display() const {
|
||||
if (enabled_) {
|
||||
glLightfv(id_, GL_POSITION, position_);
|
||||
glLightfv(id_, GL_SPOT_DIRECTION, spotDirection_);
|
||||
}
|
||||
}
|
||||
|
||||
void Light::enable() {
|
||||
enabled_ = true;
|
||||
}
|
||||
|
||||
void Light::disable() {
|
||||
enabled_ = false;
|
||||
}
|
||||
|
||||
Lighting::Lighting() {
|
||||
for (int i = 0; i < lights(); i++) {
|
||||
lights_[i].id_ = GL_LIGHT0 + i;
|
||||
}
|
||||
setGlobalAmbient(.2f, .2f, .2f, 1.f);
|
||||
lights_[0].setDiffuse(1.f, 1.f, 1.f, 1.f);
|
||||
lights_[0].setSpecular(1.f, 1.f, 1.f, 1.f);
|
||||
lights_[0].enable();
|
||||
}
|
||||
|
||||
void Lighting::setGlobalAmbient(float r, float g, float b, float a) {
|
||||
globalAmbient_[0] = r;
|
||||
globalAmbient_[1] = g;
|
||||
globalAmbient_[2] = b;
|
||||
globalAmbient_[3] = a;
|
||||
}
|
||||
|
||||
static const Lighting* lastLighting_ = NULL;
|
||||
|
||||
void Lighting::initialize() const {
|
||||
lastLighting_ = this;
|
||||
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, globalAmbient_);
|
||||
for (int i = 0; i < lights(); i++) {
|
||||
lights_[i].initialize();
|
||||
}
|
||||
}
|
||||
|
||||
void Lighting::display() const {
|
||||
if (lastLighting_ != this) {
|
||||
lastLighting_ = this;
|
||||
initialize();
|
||||
}
|
||||
for (int i = 0; i < lights(); i++) {
|
||||
lights_[i].display();
|
||||
}
|
||||
}
|
||||
|
||||
const Lighting& Lightings::standard() {
|
||||
static const Lighting l;
|
||||
return l;
|
||||
}
|
||||
74
src/lighting.h
Normal file
74
src/lighting.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_LIGHTING_H
|
||||
#define MBOSTOCK_LIGHTING_H
|
||||
|
||||
#include <OpenGL/gl.h>
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Light {
|
||||
public:
|
||||
Light();
|
||||
|
||||
void initialize() const;
|
||||
void display() const;
|
||||
|
||||
void enable();
|
||||
void disable();
|
||||
inline bool enabled() const { return enabled_; }
|
||||
|
||||
void setAmbient(float r, float g, float b, float a);
|
||||
void setDiffuse(float r, float g, float b, float a);
|
||||
void setSpecular(float r, float g, float b, float a);
|
||||
void setPosition(float x, float y, float z, float w);
|
||||
void setSpotDirection(float x, float y, float z);
|
||||
void setSpotExponent(float e);
|
||||
void setConstantAttenuation(float a);
|
||||
void setLinearAttenuation(float a);
|
||||
void setQuadraticAttenuation(float a);
|
||||
|
||||
private:
|
||||
GLenum id_;
|
||||
bool enabled_;
|
||||
float ambient_[4];
|
||||
float diffuse_[4];
|
||||
float specular_[4];
|
||||
float position_[4];
|
||||
float spotDirection_[3];
|
||||
float spotExponent_;
|
||||
float spotCutoff_;
|
||||
float constantAttenuation_;
|
||||
float linearAttenuation_;
|
||||
float quadraticAttenuation_;
|
||||
|
||||
friend class Lighting;
|
||||
};
|
||||
|
||||
class Lighting {
|
||||
public:
|
||||
Lighting();
|
||||
|
||||
void initialize() const;
|
||||
void display() const;
|
||||
|
||||
void setGlobalAmbient(float r, float g, float b, float a);
|
||||
inline Light& light(int i) { return lights_[i]; }
|
||||
inline int lights() const { return 8; }
|
||||
|
||||
private:
|
||||
float globalAmbient_[4];
|
||||
Light lights_[8];
|
||||
};
|
||||
|
||||
class Lightings {
|
||||
public:
|
||||
static const Lighting& standard();
|
||||
|
||||
private:
|
||||
Lightings();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
215
src/main.cpp
Normal file
215
src/main.cpp
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <OpenGL/gl.h>
|
||||
#include <OpenGL/glu.h>
|
||||
#include <SDL/SDL.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <TinyXML/tinyxml.h>
|
||||
|
||||
#include "room.h"
|
||||
#include "shader.h"
|
||||
#include "sound.h"
|
||||
#include "texture.h"
|
||||
#include "world.h"
|
||||
#include "worlds.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
static const int defaultWidth = 640;
|
||||
static const int defaultHeight = 480;
|
||||
static int screenWidth = 0;
|
||||
static int screenHeight = 0;
|
||||
static const int defaultX = 50;
|
||||
static const int defaultY = 50;
|
||||
static const float kd = .060f; // frame-rate dependent
|
||||
|
||||
static bool run = true;
|
||||
static bool fullScreen = false;
|
||||
|
||||
static World* world = NULL;
|
||||
static bool wireframe = false;
|
||||
|
||||
static Shader* shaders[] = {
|
||||
Shaders::defaultShader(),
|
||||
Shaders::wireframeShader(),
|
||||
Shaders::normalShader()
|
||||
};
|
||||
|
||||
static int shaderi = 0;
|
||||
static const int shadern = 3;
|
||||
|
||||
static Shader* shader() {
|
||||
return shaders[shaderi];
|
||||
}
|
||||
|
||||
static void resizeSurface(int width, int height) {
|
||||
uint32_t flags = SDL_OPENGL | SDL_RESIZABLE;
|
||||
if (fullScreen) {
|
||||
flags |= SDL_FULLSCREEN;
|
||||
}
|
||||
SDL_SetVideoMode(width, height, 24, flags);
|
||||
|
||||
/* Store the screen resolution when going into full screen. */
|
||||
if (width == 0) {
|
||||
const SDL_VideoInfo* info = SDL_GetVideoInfo();
|
||||
width = screenWidth = info->current_w;
|
||||
height = screenHeight = info->current_h;
|
||||
}
|
||||
|
||||
glViewport(0, 0, width, height);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
gluPerspective(45.f, width / (float) height, 1.0f, 100.f);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||
|
||||
shader()->initialize();
|
||||
Textures::initialize();
|
||||
world->model().initialize();
|
||||
}
|
||||
|
||||
static void handleDisplay() {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glLoadIdentity();
|
||||
|
||||
world->simulate();
|
||||
|
||||
const Vector& p = world->player().origin();
|
||||
const Vector& min = world->room().cameraBounds().min();
|
||||
const Vector& max = world->room().cameraBounds().max();
|
||||
|
||||
/* Interpolate the eye location. */
|
||||
Vector ee(p.x, p.y + 4.f, p.z + 6.f);
|
||||
ee = Vector::min(Vector::max(min, ee), max);
|
||||
static Vector e = ee;
|
||||
e = e * (1.f - kd) + ee * kd;
|
||||
|
||||
/* Interpolate the camera direction towards the player. */
|
||||
static Vector c = p;
|
||||
c = c * (1.f - kd) + p * kd;
|
||||
|
||||
gluLookAt(e.x, e.y, e.z,
|
||||
c.x, c.y, c.z,
|
||||
0.f, 1.f, 0.f);
|
||||
|
||||
shader()->display(world->model());
|
||||
SDL_GL_SwapBuffers();
|
||||
}
|
||||
|
||||
static void toggleShader() {
|
||||
shaderi = (shaderi + 1) % shadern;
|
||||
shader()->initialize();
|
||||
}
|
||||
|
||||
static void toggleFullScreen() {
|
||||
fullScreen = !fullScreen;
|
||||
if (fullScreen) {
|
||||
resizeSurface(screenWidth, screenHeight);
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
} else {
|
||||
resizeSurface(defaultWidth, defaultHeight);
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
}
|
||||
}
|
||||
|
||||
static void handleKeyDown(SDL_Event* event) {
|
||||
switch (event->key.keysym.sym) {
|
||||
case SDLK_LEFT: {
|
||||
if (event->key.keysym.mod & KMOD_META) {
|
||||
world->previousRoom();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDLK_DOWN: {
|
||||
if (event->key.keysym.mod & KMOD_META) {
|
||||
world->resetPlayer();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDLK_RIGHT: {
|
||||
if (event->key.keysym.mod & KMOD_META) {
|
||||
world->nextRoom();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDLK_a: world->player().move(Player::LEFT); break;
|
||||
case SDLK_s: world->player().move(Player::BACKWARD); break;
|
||||
case SDLK_d: world->player().move(Player::RIGHT); break;
|
||||
case SDLK_w: world->player().move(Player::FORWARD); break;
|
||||
}
|
||||
}
|
||||
|
||||
static void handleKeyUp(SDL_Event* event) {
|
||||
switch (event->key.keysym.sym) {
|
||||
case SDLK_a: world->player().stop(Player::LEFT); break;
|
||||
case SDLK_s: world->player().stop(Player::BACKWARD); break;
|
||||
case SDLK_d: world->player().stop(Player::RIGHT); break;
|
||||
case SDLK_w: world->player().stop(Player::FORWARD); break;
|
||||
case SDLK_SPACE: world->togglePaused(); break;
|
||||
case SDLK_q: if (!(event->key.keysym.mod & KMOD_META)) break;
|
||||
case SDLK_ESCAPE: run = false; break;
|
||||
case SDLK_F9: toggleShader(); break;
|
||||
case SDLK_F10: world->toggleDebug(); break;
|
||||
case SDLK_F11: toggleFullScreen(); break;
|
||||
}
|
||||
}
|
||||
|
||||
static void handleQuit() {
|
||||
Sounds::dispose();
|
||||
delete world;
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
static void eventLoop() {
|
||||
SDL_Event event;
|
||||
while (run) {
|
||||
handleDisplay();
|
||||
if (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_VIDEORESIZE: {
|
||||
resizeSurface(event.resize.w, event.resize.h);
|
||||
break;
|
||||
}
|
||||
case SDL_KEYDOWN: {
|
||||
handleKeyDown(&event);
|
||||
break;
|
||||
}
|
||||
case SDL_KEYUP: {
|
||||
handleKeyUp(&event);
|
||||
break;
|
||||
}
|
||||
case SDL_QUIT: {
|
||||
run = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
SDL_Delay(10);
|
||||
}
|
||||
handleQuit();
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
|
||||
SDL_WM_SetCaption("POLLY-B-GONE", "POLLY-B-GONE");
|
||||
|
||||
Sounds::initialize();
|
||||
world = Worlds::fromFile("world.xml");
|
||||
// resizeSurface(defaultWidth, defaultHeight);
|
||||
toggleFullScreen();
|
||||
eventLoop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
88
src/material.cpp
Normal file
88
src/material.cpp
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <OpenGL/gl.h>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "material.h"
|
||||
#include "texture.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Material::Material()
|
||||
: shininess_(0.f), texture_(NULL), slip_(0.f) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ambient_[i] = 0.f;
|
||||
diffuse_[i] = 0.f;
|
||||
specular_[i] = 0.f;
|
||||
emission_[i] = 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
void Material::setAmbient(float r, float g, float b, float a) {
|
||||
ambient_[0] = r;
|
||||
ambient_[1] = g;
|
||||
ambient_[2] = b;
|
||||
ambient_[3] = a;
|
||||
}
|
||||
|
||||
void Material::setDiffuse(float r, float g, float b, float a) {
|
||||
diffuse_[0] = r;
|
||||
diffuse_[1] = g;
|
||||
diffuse_[2] = b;
|
||||
diffuse_[3] = a;
|
||||
}
|
||||
|
||||
void Material::setSpecular(float r, float g, float b, float a) {
|
||||
specular_[0] = r;
|
||||
specular_[1] = g;
|
||||
specular_[2] = b;
|
||||
specular_[3] = a;
|
||||
}
|
||||
|
||||
void Material::setEmission(float r, float g, float b, float a) {
|
||||
emission_[0] = r;
|
||||
emission_[1] = g;
|
||||
emission_[2] = b;
|
||||
emission_[3] = a;
|
||||
}
|
||||
|
||||
void Material::setShininess(float s) {
|
||||
shininess_ = s;
|
||||
}
|
||||
|
||||
void Material::setTexture(const char* path) {
|
||||
texture_ = (path == NULL) ? NULL : &(Textures::fromFile(path));
|
||||
}
|
||||
|
||||
void Material::setSlipAngle(float angle) {
|
||||
slip_ = cosf(angle * (2.f * M_PI / 360.f));
|
||||
}
|
||||
|
||||
void Material::bind() const {
|
||||
glMaterialfv(GL_FRONT, GL_AMBIENT, ambient_);
|
||||
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_);
|
||||
glMaterialfv(GL_FRONT, GL_SPECULAR, specular_);
|
||||
glMaterialfv(GL_FRONT, GL_EMISSION, emission_);
|
||||
glMaterialf(GL_FRONT, GL_SHININESS, shininess_);
|
||||
if (texture_ == NULL) {
|
||||
glDisable(GL_BLEND);
|
||||
glBindTexture(GL_TEXTURE_2D, GL_NONE);
|
||||
} else {
|
||||
texture_->bind();
|
||||
}
|
||||
}
|
||||
|
||||
const Material& Materials::blank() {
|
||||
static Material m;
|
||||
static bool init = false;
|
||||
if (!init) {
|
||||
init = true;
|
||||
m.setAmbient(.2f, .2f, .1f, .1f);
|
||||
m.setDiffuse(.4f, .1f, .1f, 1.f);
|
||||
m.setSpecular(.05f, .05f, .05f, .1f);
|
||||
m.setShininess(10.f);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
47
src/material.h
Normal file
47
src/material.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_MATERIAL_H
|
||||
#define MBOSTOCK_MATERIAL_H
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Texture;
|
||||
|
||||
class Material {
|
||||
public:
|
||||
Material();
|
||||
|
||||
void setAmbient(float r, float g, float b, float a);
|
||||
void setDiffuse(float r, float g, float b, float a);
|
||||
void setSpecular(float r, float g, float b, float a);
|
||||
void setEmission(float r, float g, float b, float a);
|
||||
void setShininess(float s);
|
||||
void setTexture(const char* path);
|
||||
|
||||
void setSlipAngle(float angle);
|
||||
inline float slip() const { return slip_; }
|
||||
|
||||
void bind() const;
|
||||
|
||||
private:
|
||||
float ambient_[4];
|
||||
float diffuse_[4];
|
||||
float specular_[4];
|
||||
float emission_[4];
|
||||
float shininess_;
|
||||
const Texture* texture_;
|
||||
|
||||
float slip_;
|
||||
};
|
||||
|
||||
class Materials {
|
||||
public:
|
||||
static const Material& blank();
|
||||
|
||||
private:
|
||||
Materials();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
607
src/model.cpp
Normal file
607
src/model.cpp
Normal file
|
|
@ -0,0 +1,607 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <GLUT/glut.h>
|
||||
#include <OpenGL/glu.h>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "material.h"
|
||||
#include "model.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
WedgeModel::WedgeModel(const Wedge& wedge)
|
||||
: wedge_(wedge), material_(&Materials::blank()), topMaterial_(NULL) {
|
||||
}
|
||||
|
||||
void WedgeModel::setMaterial(const Material& m) {
|
||||
material_ = &m;
|
||||
}
|
||||
|
||||
void WedgeModel::setTopMaterial(const Material& m) {
|
||||
topMaterial_ = &m;
|
||||
}
|
||||
|
||||
const Material& WedgeModel::topMaterial() const {
|
||||
return (topMaterial_ == NULL) ? *material_ : *topMaterial_;
|
||||
}
|
||||
|
||||
void WedgeModel::display() {
|
||||
Vector size = wedge_.x2() - wedge_.x0();
|
||||
|
||||
/* Top. */
|
||||
((topMaterial_ != NULL) ? topMaterial_ : material_)->bind();
|
||||
glBegin(GL_QUADS);
|
||||
glNormalv(wedge_.top().normal());
|
||||
glTexCoord2f(0, 0);
|
||||
glVertexv(wedge_.x0());
|
||||
glTexCoord2f(sqrtf(size.x * size.x + size.y * size.y), 0);
|
||||
glVertexv(wedge_.x1());
|
||||
glTexCoord2f(sqrtf(size.x * size.x + size.y * size.y), size.z);
|
||||
glVertexv(wedge_.x2());
|
||||
glTexCoord2f(0, size.z);
|
||||
glVertexv(wedge_.x3());
|
||||
glEnd();
|
||||
|
||||
material_->bind();
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
/* Right. */
|
||||
glNormalv(wedge_.right().normal());
|
||||
glTexCoord2f(size.z, size.y);
|
||||
glVertexv(wedge_.x2());
|
||||
glTexCoord2f(0, size.y);
|
||||
glVertexv(wedge_.x1());
|
||||
glTexCoord2f(0, 0);
|
||||
glVertexv(wedge_.x4());
|
||||
glTexCoord2f(size.z, 0);
|
||||
glVertexv(wedge_.x5());
|
||||
|
||||
/* Bottom. */
|
||||
glNormalv(wedge_.bottom().normal());
|
||||
glTexCoord2f(0, 0);
|
||||
glVertexv(wedge_.x0());
|
||||
glTexCoord2f(0, size.z);
|
||||
glVertexv(wedge_.x3());
|
||||
glTexCoord2f(size.x, size.z);
|
||||
glVertexv(wedge_.x5());
|
||||
glTexCoord2f(size.x, 0);
|
||||
glVertexv(wedge_.x4());
|
||||
|
||||
glEnd();
|
||||
glBegin(GL_TRIANGLES);
|
||||
|
||||
/* Front. */
|
||||
glNormalv(wedge_.front().normal());
|
||||
glTexCoord2f(size.x, size.y);
|
||||
glVertexv(wedge_.x1());
|
||||
glTexCoord2f(0, 0);
|
||||
glVertexv(wedge_.x0());
|
||||
glTexCoord2f(size.x, 0);
|
||||
glVertexv(wedge_.x4());
|
||||
|
||||
/* Back. */
|
||||
glNormalv(wedge_.back().normal());
|
||||
glTexCoord2f(0, 0);
|
||||
glVertexv(wedge_.x3());
|
||||
glTexCoord2f(size.x, size.y);
|
||||
glVertexv(wedge_.x2());
|
||||
glTexCoord2f(size.x, 0);
|
||||
glVertexv(wedge_.x5());
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
AxisAlignedBoxModel::AxisAlignedBoxModel(const AxisAlignedBox& box)
|
||||
: box_(box), material_(&Materials::blank()), topMaterial_(NULL),
|
||||
orientation_(POSITIVE_X), u_(0.f), v_(0.f) {
|
||||
}
|
||||
|
||||
void AxisAlignedBoxModel::setMaterial(const Material& m) {
|
||||
material_ = &m;
|
||||
}
|
||||
|
||||
void AxisAlignedBoxModel::setTopMaterial(const Material& m) {
|
||||
topMaterial_ = &m;
|
||||
}
|
||||
|
||||
const Material& AxisAlignedBoxModel::topMaterial() const {
|
||||
return (topMaterial_ == NULL) ? *material_ : *topMaterial_;
|
||||
}
|
||||
|
||||
void AxisAlignedBoxModel::display() {
|
||||
Vector size = box_.x7() - box_.x0();
|
||||
Vector t4, t5, t6, t7;
|
||||
switch (orientation_) {
|
||||
case POSITIVE_X: {
|
||||
t4 = Vector(size.x + u_, 0, v_);
|
||||
t5 = Vector(u_, 0, v_);
|
||||
t6 = Vector(u_, 0, size.z + v_);
|
||||
t7 = Vector(size.x + u_, 0, size.z + v_);
|
||||
break;
|
||||
}
|
||||
case POSITIVE_Z: {
|
||||
t5 = Vector(v_, 0, u_);
|
||||
t6 = Vector(size.z + v_, 0, u_);
|
||||
t7 = Vector(size.z + v_, 0, size.x + u_);
|
||||
t4 = Vector(v_, 0, size.x + u_);
|
||||
break;
|
||||
}
|
||||
case NEGATIVE_X: {
|
||||
t4 = Vector(-u_, 0, v_);
|
||||
t5 = Vector(size.x - u_, 0, v_);
|
||||
t6 = Vector(size.x - u_, 0, size.z + v_);
|
||||
t7 = Vector(-u_, 0, size.z + v_);
|
||||
break;
|
||||
}
|
||||
case NEGATIVE_Z: {
|
||||
t5 = Vector(size.z - v_, 0, -u_);
|
||||
t6 = Vector(-v_, 0, -u_);
|
||||
t7 = Vector(-v_, 0, size.x - u_);
|
||||
t4 = Vector(size.z - v_, 0, size.x - u_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Vector y(0, size.y, 0);
|
||||
Vector t0 = t5 + y;
|
||||
Vector t1 = t4 + y;
|
||||
Vector t2 = t7 + y;
|
||||
Vector t3 = t6 + y;
|
||||
|
||||
/* Top. */
|
||||
((topMaterial_ != NULL) ? topMaterial_ : material_)->bind();
|
||||
glBegin(GL_QUADS);
|
||||
glNormal3f(0.f, 1.f, 0.f);
|
||||
glTexCoord2f(t4.x, t4.z);
|
||||
glVertexv(box_.x4());
|
||||
glTexCoord2f(t5.x, t5.z);
|
||||
glVertexv(box_.x5());
|
||||
glTexCoord2f(t6.x, t6.z);
|
||||
glVertexv(box_.x6());
|
||||
glTexCoord2f(t7.x, t7.z);
|
||||
glVertexv(box_.x7());
|
||||
glEnd();
|
||||
|
||||
material_->bind();
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
/* Bottom. */
|
||||
glNormal3f(0.f, -1.f, 0.f);
|
||||
glTexCoord2f(t0.x, t0.z);
|
||||
glVertexv(box_.x0());
|
||||
glTexCoord2f(t1.x, t1.z);
|
||||
glVertexv(box_.x1());
|
||||
glTexCoord2f(t2.x, t2.z);
|
||||
glVertexv(box_.x2());
|
||||
glTexCoord2f(t3.x, t3.z);
|
||||
glVertexv(box_.x3());
|
||||
|
||||
/* Front. */
|
||||
glNormal3f(0.f, 0.f, 1.f);
|
||||
glTexCoord2f(t7.x, t7.y);
|
||||
glVertexv(box_.x7());
|
||||
glTexCoord2f(t6.x, t6.y);
|
||||
glVertexv(box_.x6());
|
||||
glTexCoord2f(t3.x, t3.y);
|
||||
glVertexv(box_.x3());
|
||||
glTexCoord2f(t2.x, t2.y);
|
||||
glVertexv(box_.x2());
|
||||
|
||||
/* Back. */
|
||||
glNormal3f(0.f, 0.f, -1.f);
|
||||
glTexCoord2f(t5.x, t5.y);
|
||||
glVertexv(box_.x5());
|
||||
glTexCoord2f(t4.x, t4.y);
|
||||
glVertexv(box_.x4());
|
||||
glTexCoord2f(t1.x, t1.y);
|
||||
glVertexv(box_.x1());
|
||||
glTexCoord2f(t0.x, t0.y);
|
||||
glVertexv(box_.x0());
|
||||
|
||||
/* Left. */
|
||||
glNormal3f(-1.f, 0.f, 0.f);
|
||||
glTexCoord2f(t6.z, t6.y);
|
||||
glVertexv(box_.x6());
|
||||
glTexCoord2f(t5.z, t5.y);
|
||||
glVertexv(box_.x5());
|
||||
glTexCoord2f(t0.z, t0.y);
|
||||
glVertexv(box_.x0());
|
||||
glTexCoord2f(t3.z, t3.y);
|
||||
glVertexv(box_.x3());
|
||||
|
||||
/* Right. */
|
||||
glNormal3f(1.f, 0.f, 0.f);
|
||||
glTexCoord2f(t4.z, t4.y);
|
||||
glVertexv(box_.x4());
|
||||
glTexCoord2f(t7.z, t7.y);
|
||||
glVertexv(box_.x7());
|
||||
glTexCoord2f(t2.z, t2.y);
|
||||
glVertexv(box_.x2());
|
||||
glTexCoord2f(t1.z, t1.y);
|
||||
glVertexv(box_.x1());
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void AxisAlignedBoxModel::setTexOffset(float u, float v) {
|
||||
u_ = u;
|
||||
v_ = v;
|
||||
}
|
||||
|
||||
void AxisAlignedBoxModel::setTexOrientation(Orientation orientation) {
|
||||
orientation_ = orientation;
|
||||
}
|
||||
|
||||
BoxModel::BoxModel(const Box& box)
|
||||
: box_(box), material_(&Materials::blank()), topMaterial_(NULL) {
|
||||
}
|
||||
|
||||
void BoxModel::setMaterial(const Material& m) {
|
||||
material_ = &m;
|
||||
}
|
||||
|
||||
void BoxModel::setTopMaterial(const Material& m) {
|
||||
topMaterial_ = &m;
|
||||
}
|
||||
|
||||
const Material& BoxModel::topMaterial() const {
|
||||
return (topMaterial_ == NULL) ? *material_ : *topMaterial_;
|
||||
}
|
||||
|
||||
void BoxModel::display() {
|
||||
Vector size(
|
||||
(box_.x1() - box_.x0()).length(),
|
||||
(box_.x5() - box_.x0()).length(),
|
||||
(box_.x3() - box_.x0()).length());
|
||||
|
||||
Vector t4(size.x, 0, 0);
|
||||
Vector t5(0, 0, 0);
|
||||
Vector t6(0, 0, size.z);
|
||||
Vector t7(size.x, 0, size.z);
|
||||
|
||||
Vector y(0, size.y, 0);
|
||||
Vector t0 = t5 + y;
|
||||
Vector t1 = t4 + y;
|
||||
Vector t2 = t7 + y;
|
||||
Vector t3 = t6 + y;
|
||||
|
||||
/* Top. */
|
||||
((topMaterial_ != NULL) ? topMaterial_ : material_)->bind();
|
||||
glBegin(GL_QUADS);
|
||||
glNormalv(box_.top().normal());
|
||||
glTexCoord2f(t4.x, t4.z);
|
||||
glVertexv(box_.x4());
|
||||
glTexCoord2f(t5.x, t5.z);
|
||||
glVertexv(box_.x5());
|
||||
glTexCoord2f(t6.x, t6.z);
|
||||
glVertexv(box_.x6());
|
||||
glTexCoord2f(t7.x, t7.z);
|
||||
glVertexv(box_.x7());
|
||||
glEnd();
|
||||
|
||||
material_->bind();
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
/* Bottom. */
|
||||
glNormalv(box_.bottom().normal());
|
||||
glTexCoord2f(t0.x, t0.z);
|
||||
glVertexv(box_.x0());
|
||||
glTexCoord2f(t1.x, t1.z);
|
||||
glVertexv(box_.x1());
|
||||
glTexCoord2f(t2.x, t2.z);
|
||||
glVertexv(box_.x2());
|
||||
glTexCoord2f(t3.x, t3.z);
|
||||
glVertexv(box_.x3());
|
||||
|
||||
/* Front. */
|
||||
glNormalv(box_.front().normal());
|
||||
glTexCoord2f(t7.x, t7.y);
|
||||
glVertexv(box_.x7());
|
||||
glTexCoord2f(t6.x, t6.y);
|
||||
glVertexv(box_.x6());
|
||||
glTexCoord2f(t3.x, t3.y);
|
||||
glVertexv(box_.x3());
|
||||
glTexCoord2f(t2.x, t2.y);
|
||||
glVertexv(box_.x2());
|
||||
|
||||
/* Back. */
|
||||
glNormalv(box_.back().normal());
|
||||
glTexCoord2f(t5.x, t5.y);
|
||||
glVertexv(box_.x5());
|
||||
glTexCoord2f(t4.x, t4.y);
|
||||
glVertexv(box_.x4());
|
||||
glTexCoord2f(t1.x, t1.y);
|
||||
glVertexv(box_.x1());
|
||||
glTexCoord2f(t0.x, t0.y);
|
||||
glVertexv(box_.x0());
|
||||
|
||||
/* Left. */
|
||||
glNormalv(box_.left().normal());
|
||||
glTexCoord2f(t6.z, t6.y);
|
||||
glVertexv(box_.x6());
|
||||
glTexCoord2f(t5.z, t5.y);
|
||||
glVertexv(box_.x5());
|
||||
glTexCoord2f(t0.z, t0.y);
|
||||
glVertexv(box_.x0());
|
||||
glTexCoord2f(t3.z, t3.y);
|
||||
glVertexv(box_.x3());
|
||||
|
||||
/* Right. */
|
||||
glNormalv(box_.right().normal());
|
||||
glTexCoord2f(t4.z, t4.y);
|
||||
glVertexv(box_.x4());
|
||||
glTexCoord2f(t7.z, t7.y);
|
||||
glVertexv(box_.x7());
|
||||
glTexCoord2f(t2.z, t2.y);
|
||||
glVertexv(box_.x2());
|
||||
glTexCoord2f(t1.z, t1.y);
|
||||
glVertexv(box_.x1());
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
QuadModel::QuadModel(const Quad& quad)
|
||||
: quad_(quad), material_(&Materials::blank()) {
|
||||
float x = (quad_.x1() - quad_.x0()).length();
|
||||
float y = (quad_.x3() - quad_.x0()).length();
|
||||
if (x < y) {
|
||||
texCoords_[0] = Vector(0, 0);
|
||||
texCoords_[1] = Vector(0, x);
|
||||
texCoords_[2] = Vector(y, x);
|
||||
texCoords_[3] = Vector(y, 0);
|
||||
} else {
|
||||
texCoords_[0] = Vector(0, 0);
|
||||
texCoords_[1] = Vector(x, 0);
|
||||
texCoords_[2] = Vector(x, y);
|
||||
texCoords_[3] = Vector(0, y);
|
||||
}
|
||||
}
|
||||
|
||||
void QuadModel::setMaterial(const Material& m) {
|
||||
material_ = &m;
|
||||
}
|
||||
|
||||
void QuadModel::setTexCoords(const Vector& t0, const Vector& t1,
|
||||
const Vector& t2, const Vector& t3) {
|
||||
texCoords_[0] = t0;
|
||||
texCoords_[1] = t1;
|
||||
texCoords_[2] = t2;
|
||||
texCoords_[3] = t3;
|
||||
}
|
||||
|
||||
void QuadModel::display() {
|
||||
material_->bind();
|
||||
displaySide(true);
|
||||
glFrontFace(GL_CW);
|
||||
displaySide(false);
|
||||
glFrontFace(GL_CCW);
|
||||
}
|
||||
|
||||
void QuadModel::displaySide(bool front) {
|
||||
glBegin(GL_QUADS);
|
||||
glNormalv(front ? quad_.normal() : -quad_.normal());
|
||||
glTexCoordv(texCoords_[0]);
|
||||
glVertexv(quad_.x0());
|
||||
glTexCoordv(texCoords_[1]);
|
||||
glVertexv(quad_.x1());
|
||||
glTexCoordv(texCoords_[2]);
|
||||
glVertexv(quad_.x2());
|
||||
glTexCoordv(texCoords_[3]);
|
||||
glVertexv(quad_.x3());
|
||||
glEnd();
|
||||
}
|
||||
|
||||
TriangleModel::TriangleModel(const Triangle& triangle)
|
||||
: triangle_(triangle), material_(&Materials::blank()) {
|
||||
float x = (triangle_.x1() - triangle_.x0()).length();
|
||||
float y = (triangle_.x2() - triangle_.x0()).length();
|
||||
texCoords_[0] = Vector(0, 0);
|
||||
texCoords_[1] = Vector(x, 0);
|
||||
texCoords_[2] = Vector(x, y);
|
||||
}
|
||||
|
||||
void TriangleModel::setMaterial(const Material& m) {
|
||||
material_ = &m;
|
||||
}
|
||||
|
||||
void TriangleModel::setTexCoords(const Vector& t0, const Vector& t1,
|
||||
const Vector& t2) {
|
||||
texCoords_[0] = t0;
|
||||
texCoords_[1] = t1;
|
||||
texCoords_[2] = t2;
|
||||
}
|
||||
|
||||
void TriangleModel::display() {
|
||||
material_->bind();
|
||||
displaySide(true);
|
||||
glFrontFace(GL_CW);
|
||||
displaySide(false);
|
||||
glFrontFace(GL_CCW);
|
||||
}
|
||||
|
||||
void TriangleModel::displaySide(bool front) {
|
||||
glBegin(GL_TRIANGLES);
|
||||
glNormalv(front ? triangle_.normal() : -triangle_.normal());
|
||||
glTexCoordv(texCoords_[0]);
|
||||
glVertexv(triangle_.x0());
|
||||
glTexCoordv(texCoords_[1]);
|
||||
glVertexv(triangle_.x1());
|
||||
glTexCoordv(texCoords_[2]);
|
||||
glVertexv(triangle_.x2());
|
||||
glEnd();
|
||||
}
|
||||
|
||||
CylinderModel::CylinderModel(const Cylinder& cylinder, const Vector& y)
|
||||
: cylinder_(cylinder), y_(y),
|
||||
material_(&Materials::blank()), capMaterial_(NULL),
|
||||
quadric_(NULL) {
|
||||
for (int i = 0; i < 15; i++) {
|
||||
orientation_[i] = 0.f;
|
||||
}
|
||||
orientation_[15] = 1.f;
|
||||
}
|
||||
|
||||
CylinderModel::~CylinderModel() {
|
||||
if (quadric_ != NULL) {
|
||||
gluDeleteQuadric(quadric_);
|
||||
}
|
||||
}
|
||||
|
||||
void CylinderModel::initialize() {
|
||||
if (quadric_ != NULL) {
|
||||
gluDeleteQuadric(quadric_);
|
||||
}
|
||||
quadric_ = gluNewQuadric();
|
||||
}
|
||||
|
||||
float* CylinderModel::orientation() {
|
||||
const Vector& z = cylinder_.z();
|
||||
const Vector& y = y_;
|
||||
const Vector& x = y.cross(z);
|
||||
orientation_[0] = x.x; orientation_[1] = x.y; orientation_[2] = x.z;
|
||||
orientation_[4] = y.x; orientation_[5] = y.y; orientation_[6] = y.z;
|
||||
orientation_[8] = z.x; orientation_[9] = z.y; orientation_[10] = z.z;
|
||||
return orientation_;
|
||||
}
|
||||
|
||||
void CylinderModel::setMaterial(const Material& m) {
|
||||
material_ = &m;
|
||||
}
|
||||
|
||||
void CylinderModel::setCapMaterial(const Material& m) {
|
||||
capMaterial_ = &m;
|
||||
}
|
||||
|
||||
const Material& CylinderModel::capMaterial() const {
|
||||
return (capMaterial_ == NULL) ? *material_ : *capMaterial_;
|
||||
}
|
||||
|
||||
void CylinderModel::display() {
|
||||
float r = cylinder_.radius();
|
||||
float l = cylinder_.length();
|
||||
int slices = std::max(16, (int) roundf(64 * r * r));
|
||||
int loops = 1;
|
||||
int stacks = 1;
|
||||
glPushMatrix();
|
||||
glTranslatev(cylinder_.x0());
|
||||
glMultMatrixf(orientation());
|
||||
gluQuadricTexture(quadric_, GL_TRUE);
|
||||
material_->bind();
|
||||
gluCylinder(quadric_, r, r, l, slices, stacks);
|
||||
glPushMatrix();
|
||||
glRotatef(180.f, 0.f, 1.f, 0.f);
|
||||
((capMaterial_ != NULL) ? capMaterial_ : material_)->bind();
|
||||
gluDisk(quadric_, 0.f, r, slices, loops);
|
||||
glPopMatrix();
|
||||
glTranslatef(0.f, 0.f, l);
|
||||
gluDisk(quadric_, 0.f, r, slices, loops);
|
||||
gluQuadricTexture(quadric_, GL_FALSE);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
SphereModel::SphereModel(const Sphere& sphere)
|
||||
: sphere_(sphere), material_(&Materials::blank()), quadric_(NULL) {
|
||||
}
|
||||
|
||||
SphereModel::~SphereModel() {
|
||||
if (quadric_ != NULL) {
|
||||
gluDeleteQuadric(quadric_);
|
||||
}
|
||||
}
|
||||
|
||||
void SphereModel::initialize() {
|
||||
if (quadric_ != NULL) {
|
||||
gluDeleteQuadric(quadric_);
|
||||
}
|
||||
quadric_ = gluNewQuadric();
|
||||
}
|
||||
|
||||
void SphereModel::setMaterial(const Material& m) {
|
||||
material_ = &m;
|
||||
}
|
||||
|
||||
void SphereModel::display() {
|
||||
float r2 = sphere_.radius() * sphere_.radius();
|
||||
int slices = std::max(16, (int) roundf(32 * r2));
|
||||
int stacks = std::max(4, (int) roundf(8 * r2));
|
||||
material_->bind();
|
||||
glPushMatrix();
|
||||
glTranslatev(sphere_.x());
|
||||
gluQuadricTexture(quadric_, GL_TRUE);
|
||||
gluSphere(quadric_, sphere_.radius(), slices, stacks);
|
||||
gluQuadricTexture(quadric_, GL_FALSE);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
static Model* teapotModel_ = NULL;
|
||||
static Model* icosahedronModel_ = NULL;
|
||||
|
||||
class TeapotModel : public Model {
|
||||
public:
|
||||
virtual void display() {
|
||||
glFrontFace(GL_CW);
|
||||
glutSolidTeapot(1.f);
|
||||
glFrontFace(GL_CCW);
|
||||
}
|
||||
};
|
||||
|
||||
class IcosahedronModel : public Model {
|
||||
public:
|
||||
virtual void display() {
|
||||
glutSolidIcosahedron();
|
||||
}
|
||||
};
|
||||
|
||||
class DisplayListModel : public Model {
|
||||
public:
|
||||
DisplayListModel(Model* model)
|
||||
: model_(model), displayList_(GL_NONE) {
|
||||
}
|
||||
|
||||
virtual ~DisplayListModel() {
|
||||
if (displayList_ != GL_NONE) {
|
||||
glDeleteLists(displayList_, 1);
|
||||
}
|
||||
delete model_;
|
||||
}
|
||||
|
||||
virtual void initialize() {
|
||||
if (displayList_ != GL_NONE) {
|
||||
glDeleteLists(displayList_, 1);
|
||||
}
|
||||
displayList_ = glGenLists(1);
|
||||
glNewList(displayList_, GL_COMPILE);
|
||||
model_->initialize();
|
||||
model_->display();
|
||||
glEndList();
|
||||
}
|
||||
|
||||
virtual void display() {
|
||||
glCallList(displayList_);
|
||||
}
|
||||
|
||||
private:
|
||||
Model* model_;
|
||||
GLuint displayList_;
|
||||
};
|
||||
|
||||
Model* Models::compile(Model* model) {
|
||||
return new DisplayListModel(model);
|
||||
}
|
||||
|
||||
Model* Models::teapot() {
|
||||
if (teapotModel_ == NULL) {
|
||||
teapotModel_ = new TeapotModel();
|
||||
}
|
||||
return teapotModel_;
|
||||
}
|
||||
|
||||
Model* Models::icosahedron() {
|
||||
if (icosahedronModel_ == NULL) {
|
||||
icosahedronModel_ = new IcosahedronModel();
|
||||
}
|
||||
return icosahedronModel_;
|
||||
}
|
||||
257
src/model.h
Normal file
257
src/model.h
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_MODEL_H
|
||||
#define MBOSTOCK_MODEL_H
|
||||
|
||||
#include <OpenGL/glu.h>
|
||||
|
||||
#include "physics/shape.h"
|
||||
#include "physics/vector.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Material;
|
||||
|
||||
/**
|
||||
* A generic base class for OpenGL models. Provides a set of convenience
|
||||
* methods based on the Vector class. The constructor should not assume that
|
||||
* OpenGL is initialized; instead, use the initialize method. Note that a
|
||||
* model may be initialized multiple times if the OpenGL context is destroyed
|
||||
* and recreated, as is the case with resizing the window or toggling
|
||||
* fullscreen mode.
|
||||
*/
|
||||
class Model {
|
||||
public:
|
||||
virtual ~Model() {}
|
||||
|
||||
/** Initializes any OpenGL state used by the model. */
|
||||
virtual void initialize() {}
|
||||
|
||||
/** Displays the model. */
|
||||
virtual void display() = 0;
|
||||
|
||||
protected:
|
||||
|
||||
/** A convenience wrapper for glTranslatef. */
|
||||
inline void glTranslatev(const Vector& v) const {
|
||||
glTranslatef(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
/** A convenience wrapper for glRotatef. */
|
||||
inline void glRotatev(float angle, const Vector& axis) const {
|
||||
glRotatef(angle, axis.x, axis.y, axis.z);
|
||||
}
|
||||
|
||||
/** A convenience wrapper for glVertex3f. */
|
||||
inline void glVertexv(const Vector& v) const {
|
||||
glVertex3f(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
/** A convenience wrapper for glTexCoord2f. Uses the x and y coordinate. */
|
||||
inline void glTexCoordv(const Vector& v) const {
|
||||
glTexCoord2f(v.x, v.y);
|
||||
}
|
||||
|
||||
/** A convenience wrapper for glNormal3f. */
|
||||
inline void glNormalv(const Vector& v) const {
|
||||
glNormal3f(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
/** A convenience wrapper for glMaterialfv. */
|
||||
inline void glMaterialv(GLenum face, GLenum pname, const Vector& v) const {
|
||||
GLfloat params[] = { v.x, v.y, v.z, 1.f };
|
||||
glMaterialfv(face, pname, params);
|
||||
}
|
||||
|
||||
/** A convenience wrapper for glColor3f. */
|
||||
inline void glColorv(const Vector& v) const {
|
||||
glColor3f(v.x, v.y, v.z);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A model for Wedge. A separate material may be specified for the top face of
|
||||
* the wedge.
|
||||
*/
|
||||
class WedgeModel : public Model {
|
||||
public:
|
||||
WedgeModel(const Wedge& wedge);
|
||||
|
||||
virtual void display();
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
void setTopMaterial(const Material& m);
|
||||
|
||||
inline const Material& material() const { return *material_; }
|
||||
const Material& topMaterial() const;
|
||||
|
||||
private:
|
||||
const Wedge& wedge_;
|
||||
const Material* material_;
|
||||
const Material* topMaterial_;
|
||||
};
|
||||
|
||||
/**
|
||||
* A model for AxisAlignedBox. A separate material may be specified for the
|
||||
* top face of the box. In addition, the texture may be oriented along either
|
||||
* the X or Z axis, and the texture may be offset (e.g., for escalatars).
|
||||
*/
|
||||
class AxisAlignedBoxModel : public Model {
|
||||
public:
|
||||
AxisAlignedBoxModel(const AxisAlignedBox& box);
|
||||
|
||||
virtual void display();
|
||||
|
||||
enum Orientation { POSITIVE_X, NEGATIVE_X, POSITIVE_Z, NEGATIVE_Z };
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
void setTopMaterial(const Material& m);
|
||||
void setTexOffset(float u, float v);
|
||||
void setTexOrientation(Orientation orientation);
|
||||
|
||||
inline const Material& material() const { return *material_; }
|
||||
const Material& topMaterial() const;
|
||||
|
||||
private:
|
||||
const AxisAlignedBox& box_;
|
||||
const Material* material_;
|
||||
const Material* topMaterial_;
|
||||
Orientation orientation_;
|
||||
float u_, v_;
|
||||
};
|
||||
|
||||
/**
|
||||
* A model for Box. A separate material may be specified for the top face of
|
||||
* the box.
|
||||
*/
|
||||
class BoxModel : public Model {
|
||||
public:
|
||||
BoxModel(const Box& box);
|
||||
|
||||
virtual void display();
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
void setTopMaterial(const Material& m);
|
||||
|
||||
inline const Material& material() const { return *material_; }
|
||||
const Material& topMaterial() const;
|
||||
|
||||
private:
|
||||
const Box& box_;
|
||||
const Material* material_;
|
||||
const Material* topMaterial_;
|
||||
};
|
||||
|
||||
/**
|
||||
* A model for Quad. The texture coordinates may be specified explicitly, or
|
||||
* they will be inferred from the vertex coordinates.
|
||||
*/
|
||||
class QuadModel : public Model {
|
||||
public:
|
||||
QuadModel(const Quad& quad);
|
||||
|
||||
virtual void display();
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
void setTexCoords(const Vector& t0, const Vector& t1,
|
||||
const Vector& t2, const Vector& t3);
|
||||
|
||||
inline const Material& material() const { return *material_; }
|
||||
|
||||
private:
|
||||
void displaySide(bool front);
|
||||
|
||||
const Quad& quad_;
|
||||
const Material* material_;
|
||||
Vector texCoords_[4];
|
||||
};
|
||||
|
||||
/**
|
||||
* A model for Triangle. The texture coordinates may be specified explicitly,
|
||||
* or they will be inferred from the vertex coordinates.
|
||||
*/
|
||||
class TriangleModel : public Model {
|
||||
public:
|
||||
TriangleModel(const Triangle& triangle);
|
||||
|
||||
virtual void display();
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
void setTexCoords(const Vector& t0, const Vector& t1, const Vector& t2);
|
||||
|
||||
inline const Material& material() const { return *material_; }
|
||||
|
||||
private:
|
||||
void displaySide(bool front);
|
||||
|
||||
const Triangle& triangle_;
|
||||
const Material* material_;
|
||||
Vector texCoords_[3];
|
||||
};
|
||||
|
||||
/**
|
||||
* A cylinder model. A separate material may be specified for the end caps. An
|
||||
* additional Y-axis is required to orient the texture on the cylinder's
|
||||
* surface; this axis must be orthogonal to the main axis of the cylinder. A
|
||||
* reference to the Y-axis vector is retained so that changes to the Y-axis
|
||||
* will be propagated to the displayed model.
|
||||
*/
|
||||
class CylinderModel : public Model {
|
||||
public:
|
||||
CylinderModel(const Cylinder& cylinder, const Vector& y);
|
||||
virtual ~CylinderModel();
|
||||
|
||||
virtual void initialize();
|
||||
virtual void display();
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
void setCapMaterial(const Material& m);
|
||||
|
||||
inline const Material& material() const { return *material_; }
|
||||
const Material& capMaterial() const;
|
||||
|
||||
private:
|
||||
float* orientation();
|
||||
|
||||
const Cylinder& cylinder_;
|
||||
const Vector& y_;
|
||||
const Material* material_;
|
||||
const Material* capMaterial_;
|
||||
GLUquadric* quadric_;
|
||||
float orientation_[16];
|
||||
};
|
||||
|
||||
/** A model for Sphere. */
|
||||
class SphereModel : public Model {
|
||||
public:
|
||||
SphereModel(const Sphere& sphere);
|
||||
virtual ~SphereModel();
|
||||
|
||||
virtual void initialize();
|
||||
virtual void display();
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
inline const Material& material() const { return *material_; }
|
||||
|
||||
private:
|
||||
float* orientation();
|
||||
|
||||
const Sphere& sphere_;
|
||||
const Material* material_;
|
||||
GLUquadric* quadric_;
|
||||
float orientation_[16];
|
||||
};
|
||||
|
||||
class Models {
|
||||
public:
|
||||
static Model* teapot();
|
||||
static Model* icosahedron();
|
||||
static Model* compile(Model* model);
|
||||
|
||||
private:
|
||||
Models();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
143
src/physics/constraint.cpp
Normal file
143
src/physics/constraint.cpp
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "constraint.h"
|
||||
#include "particle.h"
|
||||
#include "shape.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
static Projection p;
|
||||
|
||||
bool Constraints::minDistance(Particle& a, Vector p, float d) {
|
||||
Vector v = p - a.position;
|
||||
float l = v.length();
|
||||
if (l < d) {
|
||||
a.position += v * ((l - d) / l);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Constraints::minDistance(Particle& a, Particle& b, float d) {
|
||||
Vector v = b.position - a.position;
|
||||
float l = v.length();
|
||||
if (l < d) {
|
||||
v *= (l - d) / ((a.inverseMass + b.inverseMass) * l);
|
||||
a.position += v * a.inverseMass;
|
||||
b.position -= v * b.inverseMass;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Constraints::maxDistance(Particle& a, Vector p, float d) {
|
||||
Vector v = p - a.position;
|
||||
float l = v.length();
|
||||
if (l > d) {
|
||||
a.position += v * ((l - d) / l);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Constraints::maxDistance(Particle& a, Particle& b, float d) {
|
||||
Vector v = b.position - a.position;
|
||||
float l = v.length();
|
||||
if (l > d) {
|
||||
v *= (l - d) / ((a.inverseMass + b.inverseMass) * l);
|
||||
a.position += v * a.inverseMass;
|
||||
b.position -= v * b.inverseMass;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Constraints::distance(Particle& a, Vector p, float d) {
|
||||
Vector v = p - a.position;
|
||||
float l = v.length();
|
||||
a.position += v * ((l - d) / l);
|
||||
return l > 0.f;
|
||||
}
|
||||
|
||||
bool Constraints::distance(Particle& a, Particle& b, float d) {
|
||||
Vector v = b.position - a.position;
|
||||
float l = v.length();
|
||||
v *= (l - d) / ((a.inverseMass + b.inverseMass) * l);
|
||||
a.position += v * a.inverseMass;
|
||||
b.position -= v * b.inverseMass;
|
||||
return l > 0.f;
|
||||
}
|
||||
|
||||
bool Constraints::plane(Particle& a, Vector p, Vector n) {
|
||||
float d = n.dot(a.position - p);
|
||||
if (d < 0) {
|
||||
a.position -= n * d;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Constraints::plane(Particle& a, Vector p, Vector n, float kr) {
|
||||
float d = n.dot(a.position - p);
|
||||
if (d < 0) {
|
||||
Vector v = a.position - a.previousPosition;
|
||||
Vector vNormal = n * n.dot(v);
|
||||
Vector vTangent = v - vNormal;
|
||||
a.position -= n * d;
|
||||
a.previousPosition = a.position - vTangent + vNormal * kr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* XXX What if p.length is 0? Use the normal? */
|
||||
|
||||
bool Constraints::inside(Particle& a, const Shape& s, float r) {
|
||||
p = s.project(a.position);
|
||||
if (p.length > -r) {
|
||||
a.position = p.x + (p.x - a.position) * (r / p.length);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Constraints::inside(Particle& a, const Shape& s, float r, float kr) {
|
||||
p = s.project(a.position);
|
||||
if (p.length > -r) {
|
||||
Vector v = a.position - a.previousPosition;
|
||||
Vector vNormal = p.normal * p.normal.dot(v);
|
||||
Vector vTangent = v - vNormal;
|
||||
a.position = p.x + (p.x - a.position) * (r / p.length);
|
||||
a.previousPosition = a.position - vTangent + vNormal * kr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Constraints::outside(Particle& a, const Shape& s, float r) {
|
||||
p = s.project(a.position);
|
||||
if (p.length < r) {
|
||||
a.position = p.x - (p.x - a.position) * (r / p.length);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Constraints::outside(Particle& a, const Shape& s, float r, float kr) {
|
||||
p = s.project(a.position);
|
||||
if (p.length < r) {
|
||||
Vector v = a.position - a.previousPosition;
|
||||
Vector vNormal = p.normal * p.normal.dot(v);
|
||||
Vector vTangent = v - vNormal;
|
||||
a.position = p.x - (p.x - a.position) * (r / p.length);
|
||||
a.previousPosition = a.position - vTangent + vNormal * kr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const Projection& Constraints::projection() {
|
||||
return p;
|
||||
}
|
||||
125
src/physics/constraint.h
Normal file
125
src/physics/constraint.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_CONSTRAINT_H
|
||||
#define MBOSTOCK_CONSTRAINT_H
|
||||
|
||||
#include "shape.h"
|
||||
#include "vector.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Particle;
|
||||
class Shape;
|
||||
|
||||
class Constraints {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constrains a particle a to be distance d from position p, independent of
|
||||
* mass.
|
||||
*/
|
||||
static bool distance(Particle& a, Vector p, float d);
|
||||
|
||||
/**
|
||||
* Constrains two particles a and b to be distance d apart. The particles
|
||||
* are moved in opposite directions according to their relative mass, either
|
||||
* towards or away from each other, depending on whether their actual
|
||||
* distance is greater or less than the specified distance d.
|
||||
*/
|
||||
static bool distance(Particle& a, Particle& b, float d);
|
||||
|
||||
/**
|
||||
* Constrains a particle a to be at least distance d from position p,
|
||||
* independent of mass.
|
||||
*/
|
||||
static bool minDistance(Particle& a, Vector p, float d);
|
||||
|
||||
/**
|
||||
* Constrains two particles a and b to be at least distance d apart. The
|
||||
* particles are moved in opposite directions according to their relative
|
||||
* mass, away from each other, if their actual distance is less than the
|
||||
* specified distance d.
|
||||
*/
|
||||
static bool minDistance(Particle& a, Particle& b, float d);
|
||||
|
||||
/**
|
||||
* Constrains a particle a to be at most distance d from position p,
|
||||
* independent of mass.
|
||||
*/
|
||||
static bool maxDistance(Particle& a, Vector p, float d);
|
||||
|
||||
/**
|
||||
* Constrains two particles a and b to be at most distance d apart. The
|
||||
* particles are moved in opposite directions according to their relative
|
||||
* mass, towards each other, if their actual distance is greater than the
|
||||
* specified distance d.
|
||||
*/
|
||||
static bool maxDistance(Particle& a, Particle& b, float d);
|
||||
|
||||
/**
|
||||
* Constrains the particle so that its y-value is at least min. This
|
||||
* implicitly uses a coefficient of restitution of zero.
|
||||
*/
|
||||
static bool minY(Particle& a, float min) {
|
||||
return plane(a, Vector(0, min, 0), Vector(0, 1, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constrains the particle so that its y-value is at least min, simulating a
|
||||
* collision with a plane of coefficient of restitution kr. Note that the
|
||||
* bounce can only be simulated if the particle was previously above the
|
||||
* plane.
|
||||
*/
|
||||
static bool minY(Particle& a, float min, float kr) {
|
||||
return plane(a, Vector(0, min, 0), Vector(0, 1, 0), kr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constrains the particle to be above the plane defined by the point p and
|
||||
* the normal n. This implicitly uses a coefficient of restitution of zero.
|
||||
*/
|
||||
static bool plane(Particle& a, Vector p, Vector n);
|
||||
|
||||
/**
|
||||
* Constrains the particle to be above the plane defined by the point p and
|
||||
* the normal n, using a coefficient of restitution kr.
|
||||
*/
|
||||
static bool plane(Particle& a, Vector p, Vector n, float kr);
|
||||
|
||||
/**
|
||||
* Constrains the particle to be inside the specified shape by at least
|
||||
* radius r. This implicitly uses a coefficient of restitution of zero.
|
||||
*/
|
||||
static bool inside(Particle& a, const Shape& s, float r);
|
||||
|
||||
/**
|
||||
* Constrains the particle to be inside the specified shape by at least
|
||||
* radius r, using a coefficient of restitution kr.
|
||||
*/
|
||||
static bool inside(Particle& a, const Shape& s, float r, float kr);
|
||||
|
||||
/**
|
||||
* Constrains the particle to be outside the specified shape by at least
|
||||
* radius r. This implicitly uses a coefficient of restitution of zero.
|
||||
*/
|
||||
static bool outside(Particle& a, const Shape& s, float r);
|
||||
|
||||
/**
|
||||
* Constrains the particle to be outside the specified shape by at least
|
||||
* radius r, using a coefficient of restitution kr.
|
||||
*/
|
||||
static bool outside(Particle& a, const Shape& s, float r, float kr);
|
||||
|
||||
/**
|
||||
* Returns the projection information for the last call to a shape-based
|
||||
* constraint.
|
||||
*/
|
||||
static const Projection& projection();
|
||||
|
||||
private:
|
||||
Constraints();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
94
src/physics/force.cpp
Normal file
94
src/physics/force.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "force.h"
|
||||
#include "particle.h"
|
||||
#include "vector.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
static const float epsilon = 1E-20;
|
||||
|
||||
void UnaryForce::apply(Particle& p) {
|
||||
p.force += force(p);
|
||||
}
|
||||
|
||||
void BinaryForce::apply(Particle& a, Particle& b) {
|
||||
Vector f = force(a, b);
|
||||
a.force += f;
|
||||
b.force -= f;
|
||||
}
|
||||
|
||||
RandomForce::RandomForce(float k)
|
||||
: k_(k) {
|
||||
}
|
||||
|
||||
Vector RandomForce::force(Particle& p) {
|
||||
return Vector::randomVector(k_);
|
||||
}
|
||||
|
||||
GravitationalForce::GravitationalForce(float g)
|
||||
: g_(g) {
|
||||
}
|
||||
|
||||
Vector GravitationalForce::force(const Particle& p) {
|
||||
return Vector(0, -g_ / p.inverseMass, 0);
|
||||
}
|
||||
|
||||
LinearDragForce::LinearDragForce(float kd)
|
||||
: kd_(kd) {
|
||||
}
|
||||
|
||||
Vector LinearDragForce::force(const Particle& p) {
|
||||
return p.velocity() * -kd_;
|
||||
}
|
||||
|
||||
QuadraticDragForce::QuadraticDragForce(float kd)
|
||||
: kd_(kd) {
|
||||
}
|
||||
|
||||
Vector QuadraticDragForce::force(const Particle& p) {
|
||||
const Vector& v = p.velocity();
|
||||
return v * v.length() * -kd_;
|
||||
}
|
||||
|
||||
HookeForce::HookeForce(float r, float ks)
|
||||
: r_(r), ks_(ks) {
|
||||
}
|
||||
|
||||
Vector HookeForce::force(const Particle& a, const Particle& b) {
|
||||
Vector l = a.position - b.position;
|
||||
float ll = l.length();
|
||||
if (ll == 0.f) {
|
||||
l = Vector::randomVector(epsilon);
|
||||
ll = epsilon;
|
||||
}
|
||||
return l * -ks_ * (ll - r_) / ll;
|
||||
}
|
||||
|
||||
DampenedHookeForce::DampenedHookeForce(float r, float ks, float kd)
|
||||
: r_(r), ks_(ks), kd_(kd) {
|
||||
}
|
||||
|
||||
Vector DampenedHookeForce::force(const Particle& a, const Particle& b) {
|
||||
Vector l = a.position - b.position;
|
||||
Vector dl = a.velocity() - b.velocity();
|
||||
float ll = l.length();
|
||||
if (ll == 0.f) {
|
||||
l = Vector::randomVector(epsilon);
|
||||
ll = epsilon;
|
||||
}
|
||||
return l * -(ks_ * (ll - r_) + kd_ * dl.dot(l) / ll) / ll;
|
||||
}
|
||||
|
||||
CoulombForce::CoulombForce(float e)
|
||||
: e_(e) {
|
||||
}
|
||||
|
||||
Vector CoulombForce::force(const Particle& a, const Particle& b) {
|
||||
Vector l = a.position - b.position;
|
||||
float ll = l.length();
|
||||
return -l * e_ / (ll * ll * ll);
|
||||
}
|
||||
105
src/physics/force.h
Normal file
105
src/physics/force.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_FORCE_H
|
||||
#define MBOSTOCK_FORCE_H
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Particle;
|
||||
|
||||
class UnaryForce {
|
||||
public:
|
||||
virtual ~UnaryForce() {}
|
||||
|
||||
void apply(Particle& p);
|
||||
|
||||
virtual Vector force(const Particle& p) = 0;
|
||||
};
|
||||
|
||||
class BinaryForce {
|
||||
public:
|
||||
virtual ~BinaryForce() {}
|
||||
|
||||
void apply(Particle& a, Particle& b);
|
||||
|
||||
virtual Vector force(const Particle& a, const Particle& b) = 0;
|
||||
};
|
||||
|
||||
class RandomForce : public UnaryForce {
|
||||
public:
|
||||
RandomForce(float k);
|
||||
|
||||
virtual Vector force(Particle& p);
|
||||
|
||||
private:
|
||||
float k_;
|
||||
};
|
||||
|
||||
class GravitationalForce : public UnaryForce {
|
||||
public:
|
||||
GravitationalForce(float g);
|
||||
|
||||
virtual Vector force(const Particle& p);
|
||||
|
||||
private:
|
||||
float g_;
|
||||
};
|
||||
|
||||
class LinearDragForce : public UnaryForce {
|
||||
public:
|
||||
LinearDragForce(float kd);
|
||||
|
||||
virtual Vector force(const Particle& p);
|
||||
|
||||
private:
|
||||
float kd_;
|
||||
};
|
||||
|
||||
class QuadraticDragForce : public UnaryForce {
|
||||
public:
|
||||
QuadraticDragForce(float kd);
|
||||
|
||||
virtual Vector force(const Particle& p);
|
||||
|
||||
private:
|
||||
float kd_;
|
||||
};
|
||||
|
||||
class HookeForce : public BinaryForce {
|
||||
public:
|
||||
HookeForce(float r, float ks);
|
||||
|
||||
virtual Vector force(const Particle& a, const Particle& b);
|
||||
|
||||
private:
|
||||
float r_;
|
||||
float ks_;
|
||||
};
|
||||
|
||||
class DampenedHookeForce : public BinaryForce {
|
||||
public:
|
||||
DampenedHookeForce(float r, float ks, float kd);
|
||||
|
||||
virtual Vector force(const Particle& a, const Particle& b);
|
||||
|
||||
private:
|
||||
float r_;
|
||||
float ks_;
|
||||
float kd_;
|
||||
};
|
||||
|
||||
class CoulombForce : public BinaryForce {
|
||||
public:
|
||||
CoulombForce(float e);
|
||||
|
||||
virtual Vector force(const Particle& a, const Particle& b);
|
||||
|
||||
private:
|
||||
float e_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
37
src/physics/particle.cpp
Normal file
37
src/physics/particle.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "particle.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Particle::Particle() : inverseMass(1.f) {
|
||||
}
|
||||
|
||||
Vector Particle::velocity() const {
|
||||
/* Note: ignores force * (inverseMass * timeStep / 2). */
|
||||
return (position - previousPosition) / ParticleSimulator::timeStep();
|
||||
}
|
||||
|
||||
ParticleSimulator::ParticleSimulator()
|
||||
: drag_(1.f) {
|
||||
}
|
||||
|
||||
ParticleSimulator::ParticleSimulator(float drag)
|
||||
: drag_(drag) {
|
||||
}
|
||||
|
||||
float ParticleSimulator::timeStep() {
|
||||
return .003f; // TODO .005 and interpolate
|
||||
}
|
||||
|
||||
void ParticleSimulator::step(Particle& p) const {
|
||||
static const float timeStepSquared = timeStep() * timeStep();
|
||||
|
||||
Vector p0 = p.previousPosition;
|
||||
p.previousPosition = p.position;
|
||||
p.position += (p.position - p0) * drag_
|
||||
+ p.force * (p.inverseMass * timeStepSquared);
|
||||
}
|
||||
39
src/physics/particle.h
Normal file
39
src/physics/particle.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_PARTICLE_H
|
||||
#define MBOSTOCK_PARTICLE_H
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Particle {
|
||||
public:
|
||||
Particle();
|
||||
|
||||
float inverseMass;
|
||||
Vector position;
|
||||
Vector previousPosition;
|
||||
Vector force;
|
||||
|
||||
Vector velocity() const;
|
||||
};
|
||||
|
||||
class ParticleSimulator {
|
||||
public:
|
||||
ParticleSimulator();
|
||||
ParticleSimulator(float drag);
|
||||
|
||||
void step(Particle& p) const;
|
||||
|
||||
static float timeStep();
|
||||
|
||||
private:
|
||||
static float timeStep_;
|
||||
static float timeStepSquared_;
|
||||
float drag_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
89
src/physics/rotation.cpp
Normal file
89
src/physics/rotation.cpp
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "particle.h"
|
||||
#include "rotation.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Rotation::Rotation(const Vector& origin, const Vector& axis,
|
||||
float speed, float angle)
|
||||
: origin_(origin), axis_(axis), startAngle_(angle),
|
||||
speed_(speed * ParticleSimulator::timeStep()), angle_(angle) {
|
||||
update();
|
||||
}
|
||||
|
||||
void Rotation::reset() {
|
||||
angle_ = startAngle_;
|
||||
}
|
||||
|
||||
void Rotation::step() {
|
||||
if (!enabled()) {
|
||||
return;
|
||||
}
|
||||
angle_ += speed_;
|
||||
angle_ = fmodf(angle_, 360.f);
|
||||
update();
|
||||
}
|
||||
|
||||
void Rotation::update() {
|
||||
float r = angle_ * (2.f * M_PI / 360.f);
|
||||
float c = cosf(r);
|
||||
float s = sinf(r);
|
||||
|
||||
/* This look familiar to anyone? /wink */
|
||||
matrix_[0] = axis_.x * axis_.x * (1.f - c) + c;
|
||||
matrix_[1] = axis_.x * axis_.y * (1.f - c) - axis_.z * s;
|
||||
matrix_[2] = axis_.x * axis_.z * (1.f - c) + axis_.y * s;
|
||||
matrix_[3] = axis_.y * axis_.x * (1.f - c) + axis_.z * s;
|
||||
matrix_[4] = axis_.y * axis_.y * (1.f - c) + c;
|
||||
matrix_[5] = axis_.y * axis_.z * (1.f - c) - axis_.x * s;
|
||||
matrix_[6] = axis_.x * axis_.z * (1.f - c) - axis_.y * s;
|
||||
matrix_[7] = axis_.y * axis_.z * (1.f - c) + axis_.x * s;
|
||||
matrix_[8] = axis_.z * axis_.z * (1.f - c) + c;
|
||||
}
|
||||
|
||||
Vector Rotation::point(const Vector& x) const {
|
||||
return origin_ + vector(x - origin_);
|
||||
}
|
||||
|
||||
Vector Rotation::pointInverse(const Vector& x) const {
|
||||
return origin_ + vectorInverse(x - origin_);
|
||||
}
|
||||
|
||||
Vector Rotation::vector(const Vector& x) const {
|
||||
return Vector(
|
||||
matrix_[0] * x.x + matrix_[1] * x.y + matrix_[2] * x.z,
|
||||
matrix_[3] * x.x + matrix_[4] * x.y + matrix_[5] * x.z,
|
||||
matrix_[6] * x.x + matrix_[7] * x.y + matrix_[8] * x.z);
|
||||
}
|
||||
|
||||
Vector Rotation::vectorInverse(const Vector& x) const {
|
||||
return Vector(
|
||||
matrix_[0] * x.x + matrix_[3] * x.y + matrix_[6] * x.z,
|
||||
matrix_[1] * x.x + matrix_[4] * x.y + matrix_[7] * x.z,
|
||||
matrix_[2] * x.x + matrix_[5] * x.y + matrix_[8] * x.z);
|
||||
}
|
||||
|
||||
Vector Rotation::velocity(const Vector& x) const {
|
||||
return enabled()
|
||||
? (origin_ - x).cross(axis_) * speed_ * (2.f * M_PI / 360.f)
|
||||
: Vector::ZERO();
|
||||
}
|
||||
|
||||
RotatingShape::RotatingShape(const Shape& s, const Rotation& r)
|
||||
: shape_(s), rotation_(r) {
|
||||
}
|
||||
|
||||
bool RotatingShape::intersects(const Sphere& s) const {
|
||||
Sphere rs(rotation_.pointInverse(s.x()), s.radius());
|
||||
return shape_.intersects(rs);
|
||||
}
|
||||
|
||||
Projection RotatingShape::project(const Vector& x) const {
|
||||
Projection p = shape_.project(rotation_.pointInverse(x));
|
||||
p.x = rotation_.point(p.x);
|
||||
p.normal = rotation_.vector(p.normal);
|
||||
return p;
|
||||
}
|
||||
79
src/physics/rotation.h
Normal file
79
src/physics/rotation.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_ROTATION_H
|
||||
#define MBOSTOCK_ROTATION_H
|
||||
|
||||
#include "shape.h"
|
||||
#include "vector.h"
|
||||
#include "transform.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
/**
|
||||
* Computes a rotation matrix for a given origin, axis and angle. This is
|
||||
* roughly the equivalent to a glTranslatef and glRotatef call in OpenGL.
|
||||
*/
|
||||
class Rotation : public Transform {
|
||||
public:
|
||||
Rotation(const Vector& origin, const Vector& axis,
|
||||
float speed, float angle);
|
||||
|
||||
/** Rotates the specified point. */
|
||||
Vector point(const Vector& x) const;
|
||||
|
||||
/** Inverse-rotates the specified point. */
|
||||
Vector pointInverse(const Vector& x) const;
|
||||
|
||||
/** Rotates the specified vector. */
|
||||
Vector vector(const Vector& x) const;
|
||||
|
||||
/** Inverse-rotates the specified vector. */
|
||||
Vector vectorInverse(const Vector& x) const;
|
||||
|
||||
/** Returns the rotation's origin. */
|
||||
inline const Vector& origin() const { return origin_; }
|
||||
|
||||
/** Returns the rotation's axis. */
|
||||
inline const Vector& axis() const { return axis_; }
|
||||
|
||||
/** Returns the rotation's angle (in degrees, in the range [0, 360]). */
|
||||
inline float angle() const { return angle_; }
|
||||
|
||||
/** Returns the speed of the rotation (in degrees per time step). */
|
||||
inline float speed() const { return speed_; }
|
||||
|
||||
/** Advances the rotation by one time step. */
|
||||
virtual void step();
|
||||
|
||||
/** Resets the rotation. */
|
||||
virtual void reset();
|
||||
|
||||
/** Returns the velocity of the given point. */
|
||||
Vector velocity(const Vector& x) const;
|
||||
|
||||
private:
|
||||
void update();
|
||||
|
||||
Vector origin_;
|
||||
Vector axis_;
|
||||
float startAngle_;
|
||||
float angle_;
|
||||
float speed_;
|
||||
float matrix_[9];
|
||||
};
|
||||
|
||||
class RotatingShape : public Shape {
|
||||
public:
|
||||
RotatingShape(const Shape& s, const Rotation& r);
|
||||
|
||||
virtual bool intersects(const Sphere& s) const;
|
||||
virtual Projection project(const Vector& x) const;
|
||||
|
||||
private:
|
||||
const Shape& shape_;
|
||||
const Rotation& rotation_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
514
src/physics/shape.cpp
Normal file
514
src/physics/shape.cpp
Normal file
|
|
@ -0,0 +1,514 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Projection::Projection() : length(0) {
|
||||
}
|
||||
|
||||
Projection::Projection(const Vector& x, float length)
|
||||
: x(x), length(length) {
|
||||
}
|
||||
|
||||
Projection::Projection(const Vector& x, float length, const Vector& normal)
|
||||
: x(x), length(length), normal(normal) {
|
||||
}
|
||||
|
||||
bool Projection::operator<(const Projection& p) const {
|
||||
return fabsf(length) < fabsf(p.length);
|
||||
}
|
||||
|
||||
bool Projection::operator<=(const Projection& p) const {
|
||||
return fabsf(length) <= fabsf(p.length);
|
||||
}
|
||||
|
||||
bool Projection::operator>(const Projection& p) const {
|
||||
return fabsf(length) > fabsf(p.length);
|
||||
}
|
||||
|
||||
bool Projection::operator>=(const Projection& p) const {
|
||||
return fabsf(length) >= fabsf(p.length);
|
||||
}
|
||||
|
||||
Sphere::Sphere()
|
||||
: r_(0.f), r2_(0.f) {
|
||||
}
|
||||
|
||||
Sphere::Sphere(const Vector& x, float radius)
|
||||
: x_(x), r_(radius), r2_(radius * radius) {
|
||||
}
|
||||
|
||||
bool Sphere::above(const Plane& p) const {
|
||||
/* Derived from Point-Plane Distance on MathWorld. */
|
||||
return p.n_.dot(x_ - p.x_) > r_;
|
||||
}
|
||||
|
||||
bool Sphere::below(const Plane& p) const {
|
||||
/* Derived from Point-Plane Distance on MathWorld. */
|
||||
return p.n_.dot(x_ - p.x_) < -r_;
|
||||
}
|
||||
|
||||
bool Sphere::intersects(const Sphere& s) const {
|
||||
/* Derived from Moller-Haines section 16.13.1. */
|
||||
float r = r_ + s.r_;
|
||||
return (s.x_ - x_).squared() < (r * r);
|
||||
}
|
||||
|
||||
Projection Sphere::project(const Vector& x) const {
|
||||
Vector v = x - x_;
|
||||
float l = v.length();
|
||||
float d = l - r_;
|
||||
Vector n = v / l;
|
||||
return Projection(x - n * d, fabsf(d), (d < 0.f) ? -n : n);
|
||||
}
|
||||
|
||||
LineSegment::LineSegment() {
|
||||
}
|
||||
|
||||
LineSegment::LineSegment(const Vector& x0, const Vector& x1)
|
||||
: x0_(x0), x1_(x1), x01_(x1 - x0), l2_(x01_.dot(x01_)) {
|
||||
}
|
||||
|
||||
bool LineSegment::intersects(const Sphere& s) const {
|
||||
/* Derived from Point-Line Distance--3-Dimensional on MathWorld. */
|
||||
return (x01_.cross(x0_ - s.x_).squared() / l2_) < s.r2_;
|
||||
}
|
||||
|
||||
Projection LineSegment::project(const Vector& p) const {
|
||||
/* Derived from Point-Line Distance--3-Dimensional on MathWorld. */
|
||||
float t = (p - x0_).dot(x01_) / l2_;
|
||||
Vector x = (t <= 0.f) ? x0_ : ((t >= 1.f) ? x1_ : (x0_ + x01_ * t));
|
||||
return Projection(x, (p - x).length());
|
||||
}
|
||||
|
||||
Plane::Plane() {
|
||||
}
|
||||
|
||||
Plane::Plane(const Vector& x, const Vector& normal)
|
||||
: x_(x), n_(normal) {
|
||||
}
|
||||
|
||||
bool Plane::intersects(const Sphere& s) const {
|
||||
/* Derived from Point-Plane Distance on MathWorld. */
|
||||
return fabsf(n_.dot(s.x_ - x_)) < s.r_;
|
||||
}
|
||||
|
||||
Projection Plane::project(const Vector& p) const {
|
||||
/* Derived from Point-Plane Distance on MathWorld. */
|
||||
float l = n_.dot(p - x_);
|
||||
return Projection(p - n_ * l, fabsf(l), (l < 0.f) ? -n_ : n_);
|
||||
}
|
||||
|
||||
Triangle::Triangle() {
|
||||
}
|
||||
|
||||
Triangle::Triangle(const Vector& x0, const Vector& x1, const Vector& x2)
|
||||
: x01_(x0, x1), x12_(x1, x2), x20_(x2, x0),
|
||||
p_(x0, (x1 - x0).cross(x2 - x1).normalize()) {
|
||||
}
|
||||
|
||||
bool Triangle::intersects(const Sphere& s) const {
|
||||
/* Derived from ERIT section 2.3 (reordered). */
|
||||
Projection p0 = p_.project(s.x_);
|
||||
if (p0.length > s.r_) {
|
||||
return false;
|
||||
}
|
||||
if (x01_.intersects(s) || x12_.intersects(s) || x20_.intersects(s)) {
|
||||
return true;
|
||||
}
|
||||
return contains(p0.x);
|
||||
}
|
||||
|
||||
Projection Triangle::project(const Vector& p) const {
|
||||
Projection pp = p_.project(p);
|
||||
if (contains(pp.x)) {
|
||||
return pp;
|
||||
}
|
||||
Projection p01 = x01_.project(pp.x);
|
||||
Projection p12 = x12_.project(pp.x);
|
||||
Projection p20 = x20_.project(pp.x);
|
||||
Projection pb = std::min(std::min(p01, p12), p20);
|
||||
pb.length = sqrtf(pb.length * pb.length + pp.length * pp.length);
|
||||
pb.normal = pp.normal;
|
||||
return pb;
|
||||
}
|
||||
|
||||
bool Triangle::contains(const Vector& p) const {
|
||||
return ((x0() - x1()).cross(p - x0()).dot(p_.normal()) < 0.f)
|
||||
&& ((x1() - x2()).cross(p - x1()).dot(p_.normal()) < 0.f)
|
||||
&& ((x2() - x0()).cross(p - x2()).dot(p_.normal()) < 0.f);
|
||||
}
|
||||
|
||||
Quad::Quad() {
|
||||
}
|
||||
|
||||
Quad::Quad(const Vector& x0, const Vector& x1,
|
||||
const Vector& x2, const Vector& x3)
|
||||
: x01_(x0, x1), x12_(x1, x2), x23_(x2, x3), x30_(x3, x0),
|
||||
p_(x0, (x1 - x0).cross(x2 - x1).normalize()) {
|
||||
}
|
||||
|
||||
bool Quad::intersects(const Sphere& s) const {
|
||||
/* Derived from Triangle-Sphere test above. */
|
||||
Projection p0 = p_.project(s.x());
|
||||
if (p0.length > s.radius()) {
|
||||
return false;
|
||||
}
|
||||
if (x01_.intersects(s)
|
||||
|| x12_.intersects(s)
|
||||
|| x23_.intersects(s)
|
||||
|| x30_.intersects(s)) {
|
||||
return true;
|
||||
}
|
||||
return contains(p0.x);
|
||||
}
|
||||
|
||||
Projection Quad::project(const Vector& p) const {
|
||||
Projection pp = p_.project(p);
|
||||
if (contains(pp.x)) {
|
||||
return pp;
|
||||
}
|
||||
Projection p01 = x01_.project(pp.x);
|
||||
Projection p12 = x12_.project(pp.x);
|
||||
Projection p23 = x23_.project(pp.x);
|
||||
Projection p30 = x30_.project(pp.x);
|
||||
Projection pb = std::min(std::min(std::min(p01, p12), p23), p30);
|
||||
pb.length = sqrtf(pb.length * pb.length + pp.length * pp.length);
|
||||
pb.normal = pp.normal;
|
||||
return pb;
|
||||
}
|
||||
|
||||
bool Quad::contains(const Vector& p) const {
|
||||
return ((x0() - x1()).cross(p - x0()).dot(p_.normal()) < 0.f)
|
||||
&& ((x1() - x2()).cross(p - x1()).dot(p_.normal()) < 0.f)
|
||||
&& ((x2() - x3()).cross(p - x2()).dot(p_.normal()) < 0.f)
|
||||
&& ((x3() - x0()).cross(p - x3()).dot(p_.normal()) < 0.f);
|
||||
}
|
||||
|
||||
Wedge::Wedge() {
|
||||
}
|
||||
|
||||
Wedge::Wedge(const Vector& x0, const Vector& x1,
|
||||
const Vector& x2, const Vector& x3)
|
||||
: top_(x0, x1, x2, x3),
|
||||
right_(x2, x1, Vector(x1.x, x0.y, x1.z), Vector(x2.x, x0.y, x2.z)),
|
||||
bottom_(x0, x3, right_.x3(), right_.x2()),
|
||||
front_(x1, x0, right_.x2()),
|
||||
back_(x3, x2, right_.x3()) {
|
||||
}
|
||||
|
||||
bool Wedge::intersects(const Sphere& s) const {
|
||||
/* Derived (very approximately) from Moller-Haines section 16.14.2. */
|
||||
if (s.above(top_.p_)
|
||||
|| s.above(right_.p_)
|
||||
|| s.above(front_.p_)
|
||||
|| s.above(back_.p_)
|
||||
|| s.above(bottom_.p_)) {
|
||||
return false;
|
||||
}
|
||||
if (s.below(top_.p_)
|
||||
&& s.below(right_.p_)
|
||||
&& s.below(front_.p_)
|
||||
&& s.below(back_.p_)
|
||||
&& s.below(bottom_.p_)) {
|
||||
return true;
|
||||
}
|
||||
return top_.intersects(s)
|
||||
|| right_.intersects(s)
|
||||
|| front_.intersects(s)
|
||||
|| back_.intersects(s)
|
||||
|| bottom_.intersects(s);
|
||||
}
|
||||
|
||||
Projection Wedge::project(const Vector& p) const {
|
||||
Projection pt = top_.project(p);
|
||||
Projection pr = right_.project(p);
|
||||
Projection pf = front_.project(p);
|
||||
Projection pb = back_.project(p);
|
||||
Projection pd = bottom_.project(p);
|
||||
return std::min(std::min(std::min(std::min(pt, pr), pf), pb), pd);
|
||||
}
|
||||
|
||||
AxisAlignedBox::AxisAlignedBox() {
|
||||
}
|
||||
|
||||
AxisAlignedBox::AxisAlignedBox(const Vector& min, const Vector& max)
|
||||
: min_(min), max_(max) {
|
||||
}
|
||||
|
||||
bool AxisAlignedBox::intersects(const Sphere& s) const {
|
||||
/* Derived from Moller-Haines section 16.13.2. */
|
||||
Vector e = Vector::max(min_ - s.x_, Vector::ZERO())
|
||||
+ Vector::max(s.x_ - max_, Vector::ZERO());
|
||||
return e.squared() < s.r2_;
|
||||
}
|
||||
|
||||
bool AxisAlignedBox::contains(const Vector& p) const {
|
||||
return ((p.x >= min_.x) && (p.x < max_.x)
|
||||
&& (p.y >= min_.y) && (p.y < max_.y)
|
||||
&& (p.z >= min_.z) && (p.z < max_.z));
|
||||
}
|
||||
|
||||
Projection AxisAlignedBox::project(const Vector& p) const {
|
||||
return contains(p) ? projectOut(p) : projectIn(p);
|
||||
}
|
||||
|
||||
Projection AxisAlignedBox::projectOut(const Vector& p) const {
|
||||
Vector x = p;
|
||||
Vector n;
|
||||
float l;
|
||||
|
||||
float dx1 = p.x - min_.x;
|
||||
float dx2 = max_.x - p.x;
|
||||
float dx = std::min(dx1, dx2);
|
||||
|
||||
float dy1 = p.y - min_.y;
|
||||
float dy2 = max_.y - p.y;
|
||||
float dy = std::min(dy1, dy2);
|
||||
|
||||
float dz1 = p.z - min_.z;
|
||||
float dz2 = max_.z - p.z;
|
||||
float dz = std::min(dz1, dz2);
|
||||
|
||||
if ((dx <= dy) && (dx <= dz)) {
|
||||
if (dx1 <= dx2) {
|
||||
l = dx1;
|
||||
x.x = min_.x;
|
||||
n.x = 1.f;
|
||||
} else {
|
||||
l = dx2;
|
||||
x.x = max_.x;
|
||||
n.x = -1.f;
|
||||
}
|
||||
} else if (dy <= dz) {
|
||||
if (dy1 <= dy2) {
|
||||
l = dy1;
|
||||
x.y = min_.y;
|
||||
n.y = 1.f;
|
||||
} else {
|
||||
l = dy2;
|
||||
x.y = max_.y;
|
||||
n.y = -1.f;
|
||||
}
|
||||
} else {
|
||||
if (dz1 <= dz2) {
|
||||
l = dz1;
|
||||
x.z = min_.z;
|
||||
n.z = 1.f;
|
||||
} else {
|
||||
l = dz2;
|
||||
x.z = max_.z;
|
||||
n.z = -1.f;
|
||||
}
|
||||
}
|
||||
|
||||
return Projection(x, -l, n);
|
||||
}
|
||||
|
||||
Projection AxisAlignedBox::projectIn(const Vector& p) const {
|
||||
Vector x = p;
|
||||
Vector n;
|
||||
|
||||
if (p.x < min_.x) {
|
||||
x.x = min_.x;
|
||||
n = -Vector::X();
|
||||
} else if (p.x >= max_.x) {
|
||||
x.x = max_.x;
|
||||
n = Vector::X();
|
||||
}
|
||||
|
||||
if (p.z < min_.z) {
|
||||
x.z = min_.z;
|
||||
n = -Vector::Z();
|
||||
} else if (p.z >= max_.z) {
|
||||
x.z = max_.z;
|
||||
n = Vector::Z();
|
||||
}
|
||||
|
||||
if (p.y < min_.y) {
|
||||
x.y = min_.y;
|
||||
n = -Vector::Y();
|
||||
} else if (p.y >= max_.y) {
|
||||
x.y = max_.y;
|
||||
n = Vector::Y();
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: If the particle does not project directly onto the triangle (and
|
||||
* similarly for quads), we should interpolate the normal of the projection so
|
||||
* that particles appear to bounce off the corner.
|
||||
*
|
||||
* On the other hand, this leads to some weird behavior when applying
|
||||
* directional friction when Polly is on the edge of a platform. So for now,
|
||||
* just use axis normals.
|
||||
*/
|
||||
|
||||
return Projection(x, (p - x).length(), n);
|
||||
}
|
||||
|
||||
Box::Box() {
|
||||
}
|
||||
|
||||
Box::Box(const AxisAlignedBox& box)
|
||||
: top_(box.x4(), box.x5(), box.x6(), box.x7()),
|
||||
bottom_(box.x0(), box.x1(), box.x2(), box.x3()),
|
||||
left_(box.x0(), box.x3(), box.x6(), box.x5()),
|
||||
right_(box.x1(), box.x4(), box.x7(), box.x2()),
|
||||
front_(box.x2(), box.x7(), box.x6(), box.x3()),
|
||||
back_(box.x0(), box.x5(), box.x4(), box.x1()) {
|
||||
}
|
||||
|
||||
Box::Box(const Vector& c, const Vector& x, const Vector& y, const Vector& z) {
|
||||
Vector x0 = c - x - y - z;
|
||||
Vector x1 = c + x - y - z;
|
||||
Vector x2 = c + x - y + z;
|
||||
Vector x3 = c - x - y + z;
|
||||
Vector x4 = c + x + y - z;
|
||||
Vector x5 = c - x + y - z;
|
||||
Vector x6 = c - x + y + z;
|
||||
Vector x7 = c + x + y + z;
|
||||
top_ = Quad(x4, x5, x6, x7);
|
||||
bottom_ = Quad(x0, x1, x2, x3);
|
||||
left_ = Quad(x0, x3, x6, x5);
|
||||
right_ = Quad(x1, x4, x7, x2);
|
||||
front_ = Quad(x2, x7, x6, x3);
|
||||
back_ = Quad(x0, x5, x4, x1);
|
||||
}
|
||||
|
||||
Box::Box(const Vector& x0, const Vector& x1, const Vector& x2, const Vector& x3,
|
||||
const Vector& x4, const Vector& x5, const Vector& x6, const Vector& x7)
|
||||
: top_(x4, x5, x6, x7),
|
||||
bottom_(x0, x1, x2, x3),
|
||||
left_(x0, x3, x6, x5),
|
||||
right_(x1, x4, x7, x2),
|
||||
front_(x2, x7, x6, x3),
|
||||
back_(x0, x5, x4, x1) {
|
||||
}
|
||||
|
||||
bool Box::intersects(const Sphere& s) const {
|
||||
/* Derived (very approximately) from Moller-Haines section 16.14.2. */
|
||||
if (s.above(top_.plane())
|
||||
|| s.above(left_.plane())
|
||||
|| s.above(right_.plane())
|
||||
|| s.above(front_.plane())
|
||||
|| s.above(back_.plane())
|
||||
|| s.above(bottom_.plane())) {
|
||||
return false;
|
||||
}
|
||||
if (s.below(top_.plane())
|
||||
&& s.below(left_.plane())
|
||||
&& s.below(right_.plane())
|
||||
&& s.below(front_.plane())
|
||||
&& s.below(back_.plane())
|
||||
&& s.below(bottom_.plane())) {
|
||||
return true;
|
||||
}
|
||||
return top_.intersects(s)
|
||||
|| left_.intersects(s)
|
||||
|| right_.intersects(s)
|
||||
|| front_.intersects(s)
|
||||
|| back_.intersects(s)
|
||||
|| bottom_.intersects(s);
|
||||
}
|
||||
|
||||
Projection Box::project(const Vector& v) const {
|
||||
Projection p = top_.project(v);
|
||||
p = std::min(p, bottom_.project(v));
|
||||
p = std::min(p, left_.project(v));
|
||||
p = std::min(p, right_.project(v));
|
||||
p = std::min(p, front_.project(v));
|
||||
p = std::min(p, back_.project(v));
|
||||
return p;
|
||||
}
|
||||
|
||||
Cylinder::Cylinder() {
|
||||
}
|
||||
|
||||
Cylinder::Cylinder(const Vector& x0, const Vector& x1, float radius)
|
||||
: axis_(x0, x1), l_(sqrtf(axis_.l2_)), r_(radius), v_(axis_.x01_ / l_) {
|
||||
}
|
||||
|
||||
bool Cylinder::intersects(const Sphere& s) const {
|
||||
/* Derived from ERIT section 2.5. */
|
||||
Vector pa = s.x_ - axis_.x0_;
|
||||
float pqdotpa = axis_.x01_.dot(pa);
|
||||
float rs1;
|
||||
|
||||
/* P -> Q -> B */
|
||||
if (pqdotpa > axis_.l2_) {
|
||||
float f = pqdotpa - axis_.l2_;
|
||||
float d2 = (f * f) / axis_.l2_;
|
||||
if (d2 > s.r2_) {
|
||||
return false;
|
||||
}
|
||||
rs1 = sqrtf(s.r2_ - d2);
|
||||
}
|
||||
|
||||
/* B -> P -> Q */
|
||||
else if (pqdotpa < 0.f) {
|
||||
float qpdotqa = -axis_.x01_.dot(s.x_ - axis_.x1_);
|
||||
float f = qpdotqa - axis_.l2_;
|
||||
float d2 = (f * f) / axis_.l2_;
|
||||
if (d2 > s.r2_) {
|
||||
return false;
|
||||
}
|
||||
rs1 = sqrtf(s.r2_ - d2);
|
||||
}
|
||||
|
||||
/* P -> B -> Q */
|
||||
else {
|
||||
rs1 = s.r_;
|
||||
}
|
||||
|
||||
float ld2 = axis_.x01_.cross(pa).squared() / axis_.l2_;
|
||||
float r = r_ + rs1;
|
||||
return ld2 <= (r * r);
|
||||
}
|
||||
|
||||
Projection Cylinder::project(const Vector& p) const {
|
||||
/* Derived from ERIT section 2.5. */
|
||||
Vector pa = p - axis_.x0_;
|
||||
float pqdotpa = axis_.x01_.dot(pa);
|
||||
|
||||
/* P -> Q -> B */
|
||||
if (pqdotpa > axis_.l2_) {
|
||||
Vector x = Plane(axis_.x1_, v_).project(p).x;
|
||||
|
||||
Vector v = x - axis_.x1_;
|
||||
float l = v.length();
|
||||
if (l > r_) {
|
||||
float d = l - r_;
|
||||
Vector n = v / l;
|
||||
x -= n * d;
|
||||
}
|
||||
|
||||
return Projection(x, (p - x).length(), v_);
|
||||
}
|
||||
|
||||
/* B -> P -> Q */
|
||||
else if (pqdotpa < 0.f) {
|
||||
Vector x = Plane(axis_.x0_, v_).project(p).x;
|
||||
|
||||
Vector v = x - axis_.x0_;
|
||||
float l = v.length();
|
||||
if (l > r_) {
|
||||
float d = l - r_;
|
||||
Vector n = v / l;
|
||||
x -= n * d;
|
||||
}
|
||||
|
||||
return Projection(x, (p - x).length(), -v_);
|
||||
}
|
||||
|
||||
/* P -> B -> Q */
|
||||
Vector b = axis_.x0_ + axis_.x01_ * pqdotpa / axis_.l2_;
|
||||
Vector v = p - b;
|
||||
float l = v.length();
|
||||
float d = l - r_;
|
||||
Vector n = v / l;
|
||||
return Projection(p - n * d, fabsf(d), (d < 0.f) ? -n : n);
|
||||
}
|
||||
431
src/physics/shape.h
Normal file
431
src/physics/shape.h
Normal file
|
|
@ -0,0 +1,431 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_SHAPE_H
|
||||
#define MBOSTOCK_SHAPE_H
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Plane;
|
||||
class Sphere;
|
||||
|
||||
/**
|
||||
* Represents the projection of a point onto the closest corresponding point
|
||||
* on the surface of a shape. A projection is used when the bounding sphere of
|
||||
* a particle interpenetrates a shape and, as this constitutes a collision,
|
||||
* the sphere center is projected onto the surface of the shape to determine
|
||||
* how to move the particle out in response to the collision.
|
||||
*
|
||||
* When a point is projected onto a surface, the projection includes the
|
||||
* surface normal. However, if the point is projected onto a line, the
|
||||
* corresponding normal is undefined.
|
||||
*
|
||||
* Projections define a natural order based on the length of the projection,
|
||||
* so that it is convenient to find the shortest projection given a number of
|
||||
* candidate projections.
|
||||
*/
|
||||
class Projection {
|
||||
public:
|
||||
Projection();
|
||||
Projection(const Vector& x, float length);
|
||||
Projection(const Vector& x, float length, const Vector& normal);
|
||||
|
||||
/** The location of the projected point on the shape's surface. */
|
||||
Vector x;
|
||||
|
||||
/** The distance from the original point to the projected point. */
|
||||
float length;
|
||||
|
||||
/** The surface normal at the projected point. */
|
||||
Vector normal;
|
||||
|
||||
bool operator<(const Projection& p) const;
|
||||
bool operator<=(const Projection& p) const;
|
||||
bool operator>(const Projection& p) const;
|
||||
bool operator>=(const Projection& p) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a shape in three dimensions. Since particles are represented
|
||||
* solely as spheres, shapes need only define intersection tests with spheres,
|
||||
* and how to project points onto the closest point on the surface of the
|
||||
* shape.
|
||||
*/
|
||||
class Shape {
|
||||
public:
|
||||
virtual ~Shape() {}
|
||||
|
||||
/** Returns true if the specified sphere intersects with this shape. */
|
||||
virtual bool intersects(const Sphere& s) const = 0;
|
||||
|
||||
/**
|
||||
* Projects the specified point onto the surface of the shape. The
|
||||
* projection specifies the closest point on the surface of the shape to the
|
||||
* specified point, the pre-computed length of the projection (the distance
|
||||
* between two points), and the surface normal at the projected point, if
|
||||
* defined.
|
||||
*/
|
||||
virtual Projection project(const Vector& x) const = 0;
|
||||
};
|
||||
|
||||
/** Represents a sphere using a center point and a radius. */
|
||||
class Sphere : public Shape {
|
||||
public:
|
||||
Sphere();
|
||||
Sphere(const Vector& x, float radius);
|
||||
|
||||
/** The center point of the sphere. */
|
||||
inline Vector& x() { return x_; }
|
||||
inline const Vector& x() const { return x_; }
|
||||
|
||||
/** The radius of the sphere. */
|
||||
inline float radius() const { return r_; }
|
||||
|
||||
/** Returns true if the sphere is entirely above the specified plane. */
|
||||
bool above(const Plane& p) const;
|
||||
|
||||
/** Returns true if the sphere is entirely below the specified plane. */
|
||||
bool below(const Plane& p) const;
|
||||
|
||||
virtual bool intersects(const Sphere& s) const;
|
||||
virtual Projection project(const Vector& p) const;
|
||||
|
||||
private:
|
||||
Vector x_;
|
||||
float r_;
|
||||
float r2_;
|
||||
|
||||
friend class AxisAlignedBox;
|
||||
friend class Cylinder;
|
||||
friend class LineSegment;
|
||||
friend class Plane;
|
||||
friend class Triangle;
|
||||
};
|
||||
|
||||
/** Represents a line segment using a start point and an end point. */
|
||||
class LineSegment : public Shape {
|
||||
public:
|
||||
LineSegment();
|
||||
LineSegment(const Vector& x0, const Vector& x1);
|
||||
|
||||
/** The start point of the line segment. */
|
||||
inline const Vector& x0() const { return x0_; }
|
||||
|
||||
/** The end point of the line segment. */
|
||||
inline const Vector& x1() const { return x1_; }
|
||||
|
||||
virtual bool intersects(const Sphere& s) const;
|
||||
virtual Projection project(const Vector& p) const;
|
||||
|
||||
private:
|
||||
Vector x0_;
|
||||
Vector x1_;
|
||||
Vector x01_;
|
||||
float l2_;
|
||||
|
||||
friend class Cylinder;
|
||||
};
|
||||
|
||||
/** Represents a plane using a point on the plane and a normal. */
|
||||
class Plane : public Shape {
|
||||
public:
|
||||
Plane();
|
||||
Plane(const Vector& x, const Vector& normal);
|
||||
|
||||
/** A point on the plane. */
|
||||
inline const Vector& x() const { return x_; }
|
||||
|
||||
/** The normal. */
|
||||
inline const Vector& normal() const { return n_; }
|
||||
|
||||
virtual bool intersects(const Sphere& s) const;
|
||||
virtual Projection project(const Vector& p) const;
|
||||
|
||||
private:
|
||||
Vector x_;
|
||||
Vector n_;
|
||||
|
||||
friend class Sphere;
|
||||
};
|
||||
|
||||
/** Represents a triangle using three coplanar points. */
|
||||
class Triangle : public Shape {
|
||||
public:
|
||||
Triangle();
|
||||
Triangle(const Vector& x0, const Vector& x1, const Vector& x2);
|
||||
|
||||
/** The first point of the triangle. */
|
||||
inline const Vector& x0() const { return x01_.x0(); }
|
||||
|
||||
/** The second point of the triangle. */
|
||||
inline const Vector& x1() const { return x12_.x0(); }
|
||||
|
||||
/** The third point of the triangle. */
|
||||
inline const Vector& x2() const { return x20_.x0(); }
|
||||
|
||||
/** The normal of the triangle, using counterclockwise orientation. */
|
||||
inline const Vector& normal() const { return p_.normal(); }
|
||||
|
||||
virtual bool intersects(const Sphere& s) const;
|
||||
virtual Projection project(const Vector& p) const;
|
||||
|
||||
private:
|
||||
bool contains(const Vector& p) const;
|
||||
|
||||
LineSegment x01_;
|
||||
LineSegment x12_;
|
||||
LineSegment x20_;
|
||||
Plane p_;
|
||||
|
||||
friend class Wedge;
|
||||
};
|
||||
|
||||
/** Represents a quad using four coplanar points. */
|
||||
class Quad : public Shape {
|
||||
public:
|
||||
Quad();
|
||||
Quad(const Vector& x0, const Vector& x1,
|
||||
const Vector& x2, const Vector& x3);
|
||||
|
||||
/** The first point of the quad. */
|
||||
inline const Vector& x0() const { return x01_.x0(); }
|
||||
|
||||
/** The second point of the quad. */
|
||||
inline const Vector& x1() const { return x12_.x0(); }
|
||||
|
||||
/** The third point of the quad. */
|
||||
inline const Vector& x2() const { return x23_.x0(); }
|
||||
|
||||
/** The fourth point of the quad. */
|
||||
inline const Vector& x3() const { return x30_.x0(); }
|
||||
|
||||
/** The normal of the quad, using counterclockwise orientation. */
|
||||
inline const Vector& normal() const { return p_.normal(); }
|
||||
|
||||
/** The plane of the quad. */
|
||||
inline const Plane& plane() const { return p_; }
|
||||
|
||||
virtual bool intersects(const Sphere& s) const;
|
||||
virtual Projection project(const Vector& p) const;
|
||||
|
||||
private:
|
||||
bool contains(const Vector& p) const;
|
||||
|
||||
LineSegment x01_;
|
||||
LineSegment x12_;
|
||||
LineSegment x23_;
|
||||
LineSegment x30_;
|
||||
Plane p_;
|
||||
|
||||
friend class Wedge;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a wedge using the four coplanar points of the top surface.
|
||||
* Points x1 and x2 define the upper edge of the wedge; points x4 and x5 lie
|
||||
* directly beneath them to define the fifth side.
|
||||
*/
|
||||
class Wedge : public Shape {
|
||||
public:
|
||||
Wedge();
|
||||
Wedge(const Vector& x0, const Vector& x1,
|
||||
const Vector& x2, const Vector& x3);
|
||||
|
||||
/** Returns the first (lower) point of the wedge. */
|
||||
inline const Vector& x0() const { return top_.x0(); }
|
||||
|
||||
/** Returns the second (upper) point of the wedge. */
|
||||
inline const Vector& x1() const { return top_.x1(); }
|
||||
|
||||
/** Returns the third (upper) point of the wedge. */
|
||||
inline const Vector& x2() const { return top_.x2(); }
|
||||
|
||||
/** Returns the forth (upper) point of the wedge. */
|
||||
inline const Vector& x3() const { return top_.x3(); }
|
||||
|
||||
/** Returns the fifth point of the wedge, below x1. */
|
||||
inline const Vector& x4() const { return right_.x2(); }
|
||||
|
||||
/** Returns the six point of the wedge, below x2. */
|
||||
inline const Vector& x5() const { return right_.x3(); }
|
||||
|
||||
/** Returns the top quad. */
|
||||
inline const Quad& top() const { return top_; }
|
||||
|
||||
/** Returns the right quad. */
|
||||
inline const Quad& right() const { return right_; }
|
||||
|
||||
/** Returns the bottom quad. */
|
||||
inline const Quad& bottom() const { return bottom_; }
|
||||
|
||||
/** Returns the front triangle. */
|
||||
inline const Triangle& front() const { return front_; }
|
||||
|
||||
/** Returns the back triangle. */
|
||||
inline const Triangle& back() const { return back_; }
|
||||
|
||||
virtual bool intersects(const Sphere& s) const;
|
||||
virtual Projection project(const Vector& p) const;
|
||||
|
||||
private:
|
||||
Quad top_;
|
||||
Quad right_;
|
||||
Quad bottom_;
|
||||
Triangle front_;
|
||||
Triangle back_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an axis-aligned bounding box (AABB) using two points. The min
|
||||
* point is the bottom back left point, and the max point is the top front
|
||||
* right point.
|
||||
*/
|
||||
class AxisAlignedBox : public Shape {
|
||||
public:
|
||||
AxisAlignedBox();
|
||||
AxisAlignedBox(const Vector& min, const Vector& max);
|
||||
|
||||
/** Returns the min point, x0. */
|
||||
inline const Vector& min() const { return min_; }
|
||||
|
||||
/** Returns the max point, x7. */
|
||||
inline const Vector& max() const { return max_; }
|
||||
|
||||
/** Returns the left bottom back point. */
|
||||
inline const Vector& x0() const { return min_; }
|
||||
|
||||
/** Returns the right bottom back point. */
|
||||
inline Vector x1() const { return Vector(max_.x, min_.y, min_.z); }
|
||||
|
||||
/** Returns the right bottom front point. */
|
||||
inline Vector x2() const { return Vector(max_.x, min_.y, max_.z); }
|
||||
|
||||
/** Returns the left bottom front point. */
|
||||
inline Vector x3() const { return Vector(min_.x, min_.y, max_.z); }
|
||||
|
||||
/** Returns the right top back point. */
|
||||
inline Vector x4() const { return Vector(max_.x, max_.y, min_.z); }
|
||||
|
||||
/** Returns the left top back point. */
|
||||
inline Vector x5() const { return Vector(min_.x, max_.y, min_.z); }
|
||||
|
||||
/** Returns the left top front point. */
|
||||
inline Vector x6() const { return Vector(min_.x, max_.y, max_.z); }
|
||||
|
||||
/** Returns the right top front point. */
|
||||
inline const Vector& x7() const { return max_; }
|
||||
|
||||
/** Returns true if this box contains the specified point. */
|
||||
bool contains(const Vector& p) const;
|
||||
|
||||
virtual bool intersects(const Sphere& s) const;
|
||||
virtual Projection project(const Vector& p) const;
|
||||
|
||||
private:
|
||||
Projection projectOut(const Vector& p) const;
|
||||
Projection projectIn(const Vector& p) const;
|
||||
|
||||
Vector min_;
|
||||
Vector max_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an oriented bounding box (OBB) using eight points.
|
||||
*/
|
||||
class Box : public Shape {
|
||||
public:
|
||||
Box();
|
||||
Box(const AxisAlignedBox& aab);
|
||||
Box(const Vector& c, const Vector& x, const Vector& y, const Vector& z);
|
||||
Box(const Vector& x0, const Vector& x1, const Vector& x2, const Vector& x3,
|
||||
const Vector& x4, const Vector& x5, const Vector& x6, const Vector& x7);
|
||||
|
||||
/** Returns the left bottom back point. */
|
||||
inline const Vector& x0() const { return bottom_.x0(); }
|
||||
|
||||
/** Returns the right bottom back point. */
|
||||
inline const Vector& x1() const { return bottom_.x1(); }
|
||||
|
||||
/** Returns the right bottom front point. */
|
||||
inline const Vector& x2() const { return bottom_.x2(); }
|
||||
|
||||
/** Returns the left bottom front point. */
|
||||
inline const Vector& x3() const { return bottom_.x3(); }
|
||||
|
||||
/** Returns the right top back point. */
|
||||
inline const Vector& x4() const { return top_.x0(); }
|
||||
|
||||
/** Returns the left top back point. */
|
||||
inline const Vector& x5() const { return top_.x1(); }
|
||||
|
||||
/** Returns the left top front point. */
|
||||
inline const Vector& x6() const { return top_.x2(); }
|
||||
|
||||
/** Returns the right top front point. */
|
||||
inline const Vector& x7() const { return top_.x3(); }
|
||||
|
||||
/** Returns the left quad. */
|
||||
inline const Quad& left() const { return back_; }
|
||||
|
||||
/** Returns the right quad. */
|
||||
inline const Quad& right() const { return right_; }
|
||||
|
||||
/** Returns the bottom quad. */
|
||||
inline const Quad& bottom() const { return bottom_; }
|
||||
|
||||
/** Returns the top quad. */
|
||||
inline const Quad& top() const { return top_; }
|
||||
|
||||
/** Returns the back quad. */
|
||||
inline const Quad& back() const { return back_; }
|
||||
|
||||
/** Returns the front quad. */
|
||||
inline const Quad& front() const { return front_; }
|
||||
|
||||
virtual bool intersects(const Sphere& s) const;
|
||||
virtual Projection project(const Vector& p) const;
|
||||
|
||||
private:
|
||||
Quad top_;
|
||||
Quad bottom_;
|
||||
Quad left_;
|
||||
Quad right_;
|
||||
Quad front_;
|
||||
Quad back_;
|
||||
};
|
||||
|
||||
/** Represents a cylinder as two points and a radius. */
|
||||
class Cylinder : public Shape {
|
||||
public:
|
||||
Cylinder();
|
||||
Cylinder(const Vector& x0, const Vector& x1, float radius);
|
||||
|
||||
/** Returns the start point of the cylinder's main axis. */
|
||||
inline const Vector& x0() const { return axis_.x0(); }
|
||||
|
||||
/** Returns the end point of the cylinder's main axis. */
|
||||
inline const Vector& x1() const { return axis_.x1(); }
|
||||
|
||||
/** Returns the radius. */
|
||||
inline float radius() const { return r_; }
|
||||
|
||||
/** Returns the length of the cylinder. */
|
||||
inline float length() const { return l_; }
|
||||
|
||||
/** Returns the unit direction vector of the cylinder. */
|
||||
inline const Vector& z() const { return v_; }
|
||||
|
||||
virtual bool intersects(const Sphere& s) const;
|
||||
virtual Projection project(const Vector& p) const;
|
||||
|
||||
private:
|
||||
LineSegment axis_;
|
||||
float l_;
|
||||
float r_;
|
||||
Vector v_;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
301
src/physics/shape_test.cpp
Normal file
301
src/physics/shape_test.cpp
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
static int returnCode = 0;
|
||||
|
||||
void assertTrue(bool condition, char* message, ...) {
|
||||
if (!condition) {
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
printf("assertion failed: ");
|
||||
vprintf(message, args);
|
||||
printf("\n");
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
static void testLineXYZ() {
|
||||
printf("testLineXYZ...\n");
|
||||
LineSegment x(Vector(0, 0, 0), Vector(2, 0, 0));
|
||||
Projection px = x.project(Vector(1, 1, 0));
|
||||
assertTrue(px.length == 1, "px.length");
|
||||
assertTrue(px.x == Vector(1, 0, 0), "px.x");
|
||||
|
||||
LineSegment y(Vector(1, 1, 1), Vector(1, 4, 1));
|
||||
Projection py = y.project(Vector(2, 1, 1));
|
||||
assertTrue(py.length == 1, "py.length");
|
||||
assertTrue(py.x == Vector(1, 1, 1), "py.x");
|
||||
|
||||
LineSegment z(Vector(-2, -2, -2), Vector(-2, -2, -4));
|
||||
Projection pz = z.project(Vector(-2, -2, -2));
|
||||
assertTrue(pz.length == 0, "pz.length");
|
||||
assertTrue(pz.x == Vector(-2, -2, -2), "pz.x");
|
||||
}
|
||||
|
||||
static void testLineX() {
|
||||
printf("testLineX...\n");
|
||||
LineSegment x(Vector(0, 0, 0), Vector(2, 0, 0));
|
||||
|
||||
/* Colinear, before x0. */
|
||||
Projection p0 = x.project(Vector(-1, 0, 0));
|
||||
assertTrue(p0.length == 1, "p0.length");
|
||||
assertTrue(p0.x == Vector(0, 0, 0), "p0.x");
|
||||
|
||||
/* Colinear, after x1. */
|
||||
Projection p1 = x.project(Vector(3, 0, 0));
|
||||
assertTrue(p1.length == 1, "p1.length");
|
||||
assertTrue(p1.x == Vector(2, 0, 0), "p1.x");
|
||||
|
||||
/* Colinear, at x0. */
|
||||
Projection p2 = x.project(Vector(0, 0, 0));
|
||||
assertTrue(p2.length == 0, "p2.length");
|
||||
assertTrue(p2.x == Vector(0, 0, 0), "p2.x");
|
||||
|
||||
/* Colinear, at x1. */
|
||||
Projection p3 = x.project(Vector(2, 0, 0));
|
||||
assertTrue(p3.length == 0, "p3.length");
|
||||
assertTrue(p3.x == Vector(2, 0, 0), "p3.x");
|
||||
|
||||
/* Colinear, between x0 and x1. */
|
||||
for (float f = 0; f <= 2; f += .1f) {
|
||||
Projection p4 = x.project(Vector(f, 0, 0));
|
||||
assertTrue(p4.length == 0, "p4.length");
|
||||
assertTrue(p4.x == Vector(f, 0, 0), "p4.x");
|
||||
}
|
||||
|
||||
/* Noncolinear, before x0. */
|
||||
Projection p5 = x.project(Vector(-1, -1, -1));
|
||||
assertTrue(p5.length == sqrtf(3), "p5.length");
|
||||
assertTrue(p5.x == Vector(0, 0, 0), "p5.x");
|
||||
|
||||
/* Noncolinear, after x1. */
|
||||
Projection p6 = x.project(Vector(3, 1, 1));
|
||||
assertTrue(p6.length == sqrtf(3), "p6.length");
|
||||
assertTrue(p6.x == Vector(2, 0, 0), "p6.x");
|
||||
|
||||
/* Noncolinear, between x0 and x1. */
|
||||
Projection p7 = x.project(Vector(1, 1, 1));
|
||||
assertTrue(p7.length == sqrtf(2), "p7.length");
|
||||
assertTrue(p7.x == Vector(1, 0, 0), "p7.x");
|
||||
}
|
||||
|
||||
static void testPlaneXYZ() {
|
||||
printf("testPlaneXYZ...\n");
|
||||
Plane xz(Vector(0, 0, 0), Vector(0, 1, 0));
|
||||
Projection pxz = xz.project(Vector(1, 2, 3));
|
||||
assertTrue(pxz.length == 2, "pxz.length");
|
||||
assertTrue(pxz.x == Vector(1, 0, 3), "pxz.x");
|
||||
|
||||
Plane xy(Vector(0, 0, 0), Vector(0, 0, 1));
|
||||
Projection pxy = xy.project(Vector(1, 2, 3));
|
||||
assertTrue(pxy.length == 3, "pxy.length");
|
||||
assertTrue(pxy.x == Vector(1, 2, 0), "pxy.x");
|
||||
|
||||
Plane yz(Vector(0, 0, 0), Vector(1, 0, 0));
|
||||
Projection pyz = yz.project(Vector(1, 2, 3));
|
||||
assertTrue(pyz.length == 1, "pyz.length");
|
||||
assertTrue(pyz.x == Vector(0, 2, 3), "pyz.x");
|
||||
|
||||
Projection pyz2 = yz.project(Vector(-1, 2, 3));
|
||||
assertTrue(pyz2.length == 1, "pyz2.length");
|
||||
assertTrue(pyz2.x == Vector(0, 2, 3), "pyz2.x");
|
||||
}
|
||||
|
||||
static void testTriangle() {
|
||||
printf("testTriangle...\n");
|
||||
|
||||
Triangle xy(Vector(1, 0, 0), Vector(0, 1, 0), Vector(0, 0, 0));
|
||||
assertTrue(xy.normal() == Vector(0, 0, 1), "xy.normal");
|
||||
|
||||
/* At x0. */
|
||||
Projection p0 = xy.project(Vector(0, 0, 0));
|
||||
assertTrue(p0.length == 0, "p0.length");
|
||||
assertTrue(p0.x == Vector(0, 0, 0), "p0.x");
|
||||
|
||||
/* At x1. */
|
||||
Projection p1 = xy.project(Vector(0, 1, 0));
|
||||
assertTrue(p1.length == 0, "p1.length");
|
||||
assertTrue(p1.x == Vector(0, 1, 0), "p1.x");
|
||||
|
||||
/* At x2. */
|
||||
Projection p2 = xy.project(Vector(1, 0, 0));
|
||||
assertTrue(p2.length == 0, "p2.length");
|
||||
assertTrue(p2.x == Vector(1, 0, 0), "p2.x");
|
||||
|
||||
/* In the plane of the triangle, x < x0. */
|
||||
Projection p3 = xy.project(Vector(-1, 0, 0));
|
||||
assertTrue(p3.length == 1, "p3.length");
|
||||
assertTrue(p3.x == Vector(0, 0, 0), "p3.x");
|
||||
|
||||
/* In the plane of the triangle, x > x2. */
|
||||
Projection p4 = xy.project(Vector(2, 0, 0));
|
||||
assertTrue(p4.length == 1, "p4.length");
|
||||
assertTrue(p4.x == Vector(1, 0, 0), "p4.x");
|
||||
|
||||
/* In the plane of the triangle, x = x2, y = x1. */
|
||||
Projection p5 = xy.project(Vector(1, 1, 0));
|
||||
assertTrue(p5.length == sqrtf(2) / 2, "p5.length");
|
||||
assertTrue(p5.x == Vector(.5, .5, 0), "p5.x");
|
||||
|
||||
/* In front of the triangle (+z). */
|
||||
Projection p6 = xy.project(Vector(.3, .3, 1));
|
||||
assertTrue(p6.length == 1, "p6.length");
|
||||
assertTrue(p6.x == Vector(.3, .3, 0), "p6.x");
|
||||
}
|
||||
|
||||
static void testQuad() {
|
||||
printf("testQuad...\n");
|
||||
|
||||
Quad xy(Vector(0, 0, 0), Vector(1, 0, 0), Vector(1, 1, 0), Vector(0, 1, 0));
|
||||
assertTrue(xy.normal() == Vector(0, 0, 1), "xy.normal");
|
||||
|
||||
/* At x0. */
|
||||
Projection p0 = xy.project(Vector(0, 0, 0));
|
||||
assertTrue(p0.length == 0, "p0.length");
|
||||
assertTrue(p0.x == Vector(0, 0, 0), "p0.x");
|
||||
|
||||
/* At x1. */
|
||||
Projection p1 = xy.project(Vector(0, 1, 0));
|
||||
assertTrue(p1.length == 0, "p1.length");
|
||||
assertTrue(p1.x == Vector(0, 1, 0), "p1.x");
|
||||
|
||||
/* At x2. */
|
||||
Projection p2 = xy.project(Vector(1, 0, 0));
|
||||
assertTrue(p2.length == 0, "p2.length");
|
||||
assertTrue(p2.x == Vector(1, 0, 0), "p2.x");
|
||||
|
||||
/* In the plane of the quad, x < x0. */
|
||||
Projection p3 = xy.project(Vector(-1, 0, 0));
|
||||
assertTrue(p3.length == 1, "p3.length");
|
||||
assertTrue(p3.x == Vector(0, 0, 0), "p3.x");
|
||||
|
||||
/* In the plane of the quad, x > x2. */
|
||||
Projection p4 = xy.project(Vector(2, 0, 0));
|
||||
assertTrue(p4.length == 1, "p4.length");
|
||||
assertTrue(p4.x == Vector(1, 0, 0), "p4.x");
|
||||
|
||||
/* In the plane of the quad, x = x2, y = x1. */
|
||||
Projection p5 = xy.project(Vector(2, 2, 0));
|
||||
assertTrue(p5.length == sqrtf(2), "p5.length");
|
||||
assertTrue(p5.x == Vector(1, 1, 0), "p5.x");
|
||||
|
||||
/* In the plane of the quad. */
|
||||
Projection p6 = xy.project(Vector(.5, .5, 0));
|
||||
assertTrue(p6.length == 0, "p6.length");
|
||||
assertTrue(p6.x == Vector(.5, .5, 0), "p6.x");
|
||||
|
||||
/* In front of the quad (+z). */
|
||||
Projection p7 = xy.project(Vector(.3, .3, 1));
|
||||
assertTrue(p7.length == 1, "p7.length");
|
||||
assertTrue(p7.x == Vector(.3, .3, 0), "p6.x");
|
||||
}
|
||||
|
||||
static void testRamp() {
|
||||
printf("testRamp...\n");
|
||||
|
||||
Wedge r(Vector(-1, 0, .5),
|
||||
Vector(-.5, .25, .5),
|
||||
Vector(-.5, .25, -.5),
|
||||
Vector(-1, 0, -.5));
|
||||
|
||||
Projection p0 = r.project(Vector(-0.694026, 0.05, 0.35));
|
||||
assertTrue(p0.length > 0, "p0.length");
|
||||
}
|
||||
|
||||
static void testCylinderIntersects() {
|
||||
std::cout << "testCylinderIntersects...\n";
|
||||
Cylinder c(Vector(0, 0, 0), Vector(0, 2, 0), 1);
|
||||
|
||||
Sphere s1(Vector(0, 3.1, 0), 1);
|
||||
assertTrue(!c.intersects(s1), "c.intersects(s1)");
|
||||
|
||||
Sphere s2(Vector(0, 2.9, 0), 1);
|
||||
assertTrue(c.intersects(s2), "c.intersects(s2)");
|
||||
|
||||
Sphere s3(Vector(0, 3, 0), .9);
|
||||
assertTrue(!c.intersects(s3), "c.intersects(s3)");
|
||||
|
||||
Sphere s4(Vector(0, 3, 0), 1.1);
|
||||
assertTrue(c.intersects(s4), "c.intersects(s4)");
|
||||
|
||||
Sphere s5(Vector(0, -1.1, 0), 1);
|
||||
assertTrue(!c.intersects(s5), "c.intersects(s5)");
|
||||
|
||||
Sphere s6(Vector(0, -.9, 0), 1);
|
||||
assertTrue(c.intersects(s6), "c.intersects(s6)");
|
||||
|
||||
Sphere s7(Vector(0, -1, 0), .9);
|
||||
assertTrue(!c.intersects(s7), "c.intersects(s7)");
|
||||
|
||||
Sphere s8(Vector(0, -1, 0), 1.1);
|
||||
assertTrue(c.intersects(s8), "c.intersects(s8)");
|
||||
|
||||
Sphere s9(Vector(2, 1, 0), .9);
|
||||
assertTrue(!c.intersects(s9), "c.intersects(s9)");
|
||||
|
||||
Sphere s10(Vector(2, 1, 0), 1.1);
|
||||
assertTrue(c.intersects(s10), "c.intersects(s10)");
|
||||
|
||||
Sphere s11(Vector(-2, 1, 0), .9);
|
||||
assertTrue(!c.intersects(s11), "c.intersects(s11)");
|
||||
|
||||
Sphere s12(Vector(-2, 1, 0), 1.1);
|
||||
assertTrue(c.intersects(s12), "c.intersects(s12)");
|
||||
|
||||
Sphere s13(Vector(0, 1, 2), .9);
|
||||
assertTrue(!c.intersects(s13), "c.intersects(s13)");
|
||||
|
||||
Sphere s14(Vector(0, 1, 2), 1.1);
|
||||
assertTrue(c.intersects(s14), "c.intersects(s14)");
|
||||
|
||||
Sphere s15(Vector(0, 1, -2), .9);
|
||||
assertTrue(!c.intersects(s15), "c.intersects(s15)");
|
||||
|
||||
Sphere s16(Vector(0, 1, -2), 1.1);
|
||||
assertTrue(c.intersects(s16), "c.intersects(s16)");
|
||||
}
|
||||
|
||||
static void testCylinderProject() {
|
||||
std::cout << "testCylinderProject...\n";
|
||||
Cylinder c(Vector(0, 0, 0), Vector(0, 2, 0), 1);
|
||||
|
||||
Projection j1 = c.project(Vector(0, 3.1, 0));
|
||||
assertTrue(Vector(0, 2, 0) == j1.x, "c.project(p1).x");
|
||||
assertTrue(j1.length > 1.f && j1.length < 1.2f, "c.project(p1).length");
|
||||
assertTrue(Vector(0, 1, 0) == j1.normal, "c.project(p1).normal");
|
||||
|
||||
Projection j2 = c.project(Vector(0, -1.1, 0));
|
||||
assertTrue(Vector(0, 0, 0) == j2.x, "c.project(p2).x");
|
||||
assertTrue(j2.length > 1.f && j2.length < 1.2f, "c.project(p2).length");
|
||||
assertTrue(Vector(0, -1, 0) == j2.normal, "c.project(p2).normal");
|
||||
|
||||
Projection j3 = c.project(Vector(2.1, 1, 0));
|
||||
assertTrue(Vector(1, 1, 0) == j3.x, "c.project(p3).x");
|
||||
assertTrue(j3.length > 1.f && j3.length < 1.2f, "c.project(p3).length");
|
||||
assertTrue(Vector(1, 0, 0) == j3.normal, "c.project(p3).normal");
|
||||
|
||||
Projection j4 = c.project(Vector(-2.1, 1, 0));
|
||||
assertTrue(Vector(-1, 1, 0) == j4.x, "c.project(p4).x");
|
||||
assertTrue(j4.length > 1.f && j4.length < 1.2f, "c.project(p4).length");
|
||||
assertTrue(Vector(-1, 0, 0) == j4.normal, "c.project(p4).normal");
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testLineXYZ();
|
||||
testLineX();
|
||||
testPlaneXYZ();
|
||||
testTriangle();
|
||||
testQuad();
|
||||
testRamp();
|
||||
testCylinderIntersects();
|
||||
testCylinderProject();
|
||||
return returnCode;
|
||||
}
|
||||
13
src/physics/transform.cpp
Normal file
13
src/physics/transform.cpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "transform.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Transform::Transform()
|
||||
: enabled_(true) {
|
||||
}
|
||||
|
||||
void Transform::enable(bool enabled) {
|
||||
enabled_ = enabled;
|
||||
}
|
||||
25
src/physics/transform.h
Normal file
25
src/physics/transform.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_TRANSFORM_H
|
||||
#define MBOSTOCK_TRANSFORM_H
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Transform {
|
||||
public:
|
||||
Transform();
|
||||
virtual ~Transform() {}
|
||||
|
||||
void enable(bool enabled = true);
|
||||
inline bool enabled() const { return enabled_; }
|
||||
|
||||
virtual void reset() = 0;
|
||||
virtual void step() = 0;
|
||||
|
||||
private:
|
||||
bool enabled_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
87
src/physics/translation.cpp
Normal file
87
src/physics/translation.cpp
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "particle.h"
|
||||
#include "translation.h"
|
||||
#include "vector.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Translation::Translation(const Vector& x0, const Vector& x1,
|
||||
float speed, float start, float dampen)
|
||||
: x0_(x0), x1_(x1), u_(start), s_(speed * ParticleSimulator::timeStep()),
|
||||
kd_(1.f - dampen), v_((x1 - x0) * s_),
|
||||
x_(x0 * (1.f - u_) + x1 * u_), origin_(x_),
|
||||
mode_(REVERSE), reversed_(false) {
|
||||
}
|
||||
|
||||
void Translation::reset() {
|
||||
origin_ = x_ = x0_ * (1.f - u_) + x1_ * u_;
|
||||
v_ = (x1_ - x0_) * s_;
|
||||
reversed_ = false;
|
||||
}
|
||||
|
||||
void Translation::step() {
|
||||
if (!enabled()) {
|
||||
return;
|
||||
}
|
||||
Vector x = origin_ + v_;
|
||||
if (reversed_) {
|
||||
if (v_.dot(x0_ - x) < 0.f) {
|
||||
reversed_ = false;
|
||||
v_ *= -1.f;
|
||||
}
|
||||
} else {
|
||||
if (v_.dot(x1_ - x) < 0.f) {
|
||||
switch (mode_) {
|
||||
case REVERSE: {
|
||||
reversed_ = true;
|
||||
v_ *= -1.f;
|
||||
break;
|
||||
}
|
||||
case RESET: {
|
||||
x_ = x0_;
|
||||
origin_ = x0_;
|
||||
break;
|
||||
}
|
||||
case ONE_WAY: {
|
||||
v_ = Vector::ZERO();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
x_ += v_;
|
||||
dv_ = (x_ - origin_) * kd_;
|
||||
origin_ += dv_;
|
||||
}
|
||||
|
||||
void Translation::setMode(Mode m) {
|
||||
mode_ = m;
|
||||
}
|
||||
|
||||
const Vector& Translation::velocity() const {
|
||||
return enabled() ? dv_ : Vector::ZERO();
|
||||
}
|
||||
|
||||
Vector Translation::point(const Vector& x) const {
|
||||
return x + origin_;
|
||||
}
|
||||
|
||||
Vector Translation::pointInverse(const Vector& x) const {
|
||||
return x - origin_;
|
||||
}
|
||||
|
||||
TranslatingShape::TranslatingShape(const Shape& s, const Translation& t)
|
||||
: shape_(s), translation_(t) {
|
||||
}
|
||||
|
||||
bool TranslatingShape::intersects(const Sphere& s) const {
|
||||
Sphere ts(translation_.pointInverse(s.x()), s.radius());
|
||||
return shape_.intersects(ts);
|
||||
}
|
||||
|
||||
Projection TranslatingShape::project(const Vector& x) const {
|
||||
Projection p = shape_.project(translation_.pointInverse(x));
|
||||
p.x = translation_.point(p.x);
|
||||
return p;
|
||||
}
|
||||
70
src/physics/translation.h
Normal file
70
src/physics/translation.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_TRANSLATION_H
|
||||
#define MBOSTOCK_TRANSLATION_H
|
||||
|
||||
#include "shape.h"
|
||||
#include "transform.h"
|
||||
#include "vector.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Translation : public Transform {
|
||||
public:
|
||||
Translation(const Vector& x0, const Vector& x1,
|
||||
float s, float u, float kd);
|
||||
|
||||
enum Mode { REVERSE, RESET, ONE_WAY };
|
||||
|
||||
/* Sets the translation mode. The default is REVERSE. */
|
||||
void setMode(Mode m);
|
||||
|
||||
/** Translates the specified point. */
|
||||
Vector point(const Vector& x) const;
|
||||
|
||||
/** Inverse-translates the specified point. */
|
||||
Vector pointInverse(const Vector& x) const;
|
||||
|
||||
/** Advances the translation by one time step. */
|
||||
virtual void step();
|
||||
|
||||
/** Resets the translation. */
|
||||
virtual void reset();
|
||||
|
||||
/** Returns the current velocity. */
|
||||
const Vector& velocity() const;
|
||||
|
||||
/** Returns the current origin. */
|
||||
inline const Vector& origin() const { return origin_; }
|
||||
|
||||
private:
|
||||
void update();
|
||||
|
||||
Vector x0_;
|
||||
Vector x1_;
|
||||
float s_;
|
||||
float u_;
|
||||
float kd_;
|
||||
Vector v_;
|
||||
Vector dv_;
|
||||
Vector x_;
|
||||
Vector origin_;
|
||||
Mode mode_;
|
||||
bool reversed_;
|
||||
};
|
||||
|
||||
class TranslatingShape : public Shape {
|
||||
public:
|
||||
TranslatingShape(const Shape& s, const Translation& t);
|
||||
|
||||
virtual bool intersects(const Sphere& s) const;
|
||||
virtual Projection project(const Vector& x) const;
|
||||
|
||||
private:
|
||||
const Shape& shape_;
|
||||
const Translation& translation_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
52
src/physics/vector.cpp
Normal file
52
src/physics/vector.cpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
const Vector& Vector::ZERO() {
|
||||
static const Vector v;
|
||||
return v;
|
||||
}
|
||||
|
||||
const Vector& Vector::X() {
|
||||
static const Vector v(1.f, 0.f, 0.f);
|
||||
return v;
|
||||
}
|
||||
|
||||
const Vector& Vector::Y() {
|
||||
static const Vector v(0.f, 1.f, 0.f);
|
||||
return v;
|
||||
}
|
||||
|
||||
const Vector& Vector::Z() {
|
||||
static const Vector v(0.f, 0.f, 1.f);
|
||||
return v;
|
||||
}
|
||||
|
||||
const Vector& Vector::INF() {
|
||||
static const Vector v(INFINITY, INFINITY, INFINITY);
|
||||
return v;
|
||||
}
|
||||
|
||||
static float randomf() {
|
||||
return random() / (float) RAND_MAX;
|
||||
}
|
||||
|
||||
Vector Vector::min(const Vector& a, const Vector& b) {
|
||||
return Vector(std::min(a.x, b.x), std::min(a.y, b.y), std::min(a.z, b.z));
|
||||
}
|
||||
|
||||
Vector Vector::max(const Vector& a, const Vector& b) {
|
||||
return Vector(std::max(a.x, b.x), std::max(a.y, b.y), std::max(a.z, b.z));
|
||||
}
|
||||
|
||||
Vector Vector::randomVector(float k) {
|
||||
Vector v(randomf() - .5f, randomf() - .5f, randomf() - .5f);
|
||||
return ((v.x == 0.f) && (v.y == 0.f) && (v.z == 0.f))
|
||||
? Vector(k, 0.f, 0.f) // very unlikely, but still...
|
||||
: v.normalize() * k;
|
||||
}
|
||||
134
src/physics/vector.h
Normal file
134
src/physics/vector.h
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_VECTOR_H
|
||||
#define MBOSTOCK_VECTOR_H
|
||||
|
||||
#include "math.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Vector {
|
||||
public:
|
||||
inline Vector() : x(0.f), y(0.f), z(0.f) {}
|
||||
inline Vector(float x, float y) : x(x), y(y), z(0.f) {}
|
||||
inline Vector(float x, float y, float z) : x(x), y(y), z(z) {}
|
||||
|
||||
inline Vector operator*(float k) const {
|
||||
return Vector(x * k, y * k, z * k);
|
||||
}
|
||||
|
||||
inline Vector operator/(float k) const {
|
||||
return Vector(x / k, y / k, z / k);
|
||||
}
|
||||
|
||||
inline Vector operator-(float k) const {
|
||||
return Vector(x - k, y - k, z - k);
|
||||
}
|
||||
|
||||
inline Vector operator+(float k) const {
|
||||
return Vector(x + k, y + k, z + k);
|
||||
}
|
||||
|
||||
inline Vector operator-() const {
|
||||
return Vector(-x, -y, -z);
|
||||
}
|
||||
|
||||
inline Vector& operator*=(float k) {
|
||||
x *= k; y *= k; z *= k;
|
||||
return *this;
|
||||
}
|
||||
inline Vector& operator/=(float k) {
|
||||
x /= k; y /= k; z /= k;
|
||||
return *this;
|
||||
}
|
||||
inline Vector& operator-=(float k) {
|
||||
x -= k; y -= k; z -= k;
|
||||
return *this;
|
||||
}
|
||||
inline Vector& operator+=(float k) {
|
||||
x += k; y += k; z += k;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Vector operator-(const Vector& v) const {
|
||||
return Vector(x - v.x, y - v.y, z - v.z);
|
||||
}
|
||||
|
||||
inline Vector operator+(const Vector& v) const {
|
||||
return Vector(x + v.x, y + v.y, z + v.z);
|
||||
}
|
||||
|
||||
inline Vector operator*(const Vector& v) const {
|
||||
return Vector(x * v.x, y * v.y, z * v.z);
|
||||
}
|
||||
|
||||
inline Vector operator/(const Vector& v) const {
|
||||
return Vector(x / v.x, y / v.y, z / v.z);
|
||||
}
|
||||
|
||||
inline Vector& operator-=(const Vector& v) {
|
||||
x -= v.x; y -= v.y; z -= v.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Vector& operator+=(const Vector& v) {
|
||||
x += v.x; y += v.y; z += v.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Vector& operator*=(const Vector& v) {
|
||||
x *= v.x; y *= v.y; z *= v.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Vector& operator/=(const Vector& v) {
|
||||
x /= v.x; y /= v.y; z /= v.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool operator==(const Vector& v) const {
|
||||
return (x == v.x) && (y == v.y) && (z == v.z);
|
||||
}
|
||||
|
||||
inline bool operator!=(const Vector& v) const {
|
||||
return (x != v.x) || (y != v.y) || (z != v.z);
|
||||
}
|
||||
|
||||
inline float dot(const Vector& v) const {
|
||||
return x * v.x + y * v.y + z * v.z;
|
||||
}
|
||||
|
||||
inline Vector cross(const Vector& v) const {
|
||||
return Vector(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);
|
||||
}
|
||||
|
||||
inline float length() const {
|
||||
return sqrtf(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
inline float squared() const {
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
inline Vector normalize() const {
|
||||
return *this / length();
|
||||
}
|
||||
|
||||
static Vector min(const Vector& a, const Vector& b);
|
||||
static Vector max(const Vector& a, const Vector& b);
|
||||
static Vector randomVector(float k);
|
||||
|
||||
static const Vector& ZERO();
|
||||
static const Vector& X();
|
||||
static const Vector& Y();
|
||||
static const Vector& Z();
|
||||
static const Vector& INF();
|
||||
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
240
src/physics/vector_test.cpp
Normal file
240
src/physics/vector_test.cpp
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
static int returnCode = 0;
|
||||
|
||||
void assertTrue(bool condition, char* message, ...) {
|
||||
if (!condition) {
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
printf("assertion failed: ");
|
||||
vprintf(message, args);
|
||||
printf("\n");
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
static void testDefaultConstructor() {
|
||||
printf("testDefaultConstructor...\n");
|
||||
Vector v1;
|
||||
Vector v2;
|
||||
assertTrue(v1 == v2, "v1 != v2");
|
||||
assertTrue(v1.x == 0.f, "v1.x != 0.f");
|
||||
assertTrue(v1.y == 0.f, "v1.y != 0.f");
|
||||
assertTrue(v1.z == 0.f, "v1.z != 0.f");
|
||||
}
|
||||
|
||||
static void testExplicitConstructor() {
|
||||
printf("testExplicitConstructor...\n");
|
||||
Vector v(1.f, 2.f, 3.f);
|
||||
assertTrue(v.x == 1.f, "v.x != 1.f");
|
||||
assertTrue(v.y == 2.f, "v.y != 2.f");
|
||||
assertTrue(v.z == 3.f, "v.z != 3.f");
|
||||
}
|
||||
|
||||
static void testAssignment() {
|
||||
printf("testAssignment...\n");
|
||||
Vector v1(1.f, 2.f, 3.f);
|
||||
Vector v2(4.f, 5.f, 6.f);
|
||||
Vector v3(4.f, 5.f, 6.f);
|
||||
v1 = v2;
|
||||
assertTrue(v1 == v2, "v1 != v2");
|
||||
assertTrue(v1 == v3, "v1 != v3");
|
||||
assertTrue(v2 == v3, "v2 != v3");
|
||||
}
|
||||
|
||||
static void testCopyConstructor() {
|
||||
printf("testCopyConstructor...\n");
|
||||
Vector v1(1.f, 2.f, 3.f);
|
||||
Vector v2(v1);
|
||||
assertTrue(v1 == v2, "v1 != v2");
|
||||
assertTrue(v2 == v1, "v2 != v1");
|
||||
}
|
||||
|
||||
static void testEqualVector() {
|
||||
printf("testEqualVector...\n");
|
||||
Vector v1a(1.f, 2.f, 3.f);
|
||||
Vector v1b(1.f, 2.f, 3.f);
|
||||
Vector v2(1.f, 2.f, 4.f);
|
||||
Vector v3(1.f, 3.f, 3.f);
|
||||
Vector v4(2.f, 3.f, 3.f);
|
||||
assertTrue(v1a == v1b, "v1a != v1b");
|
||||
assertTrue(v1b == v1a, "v1b != v1a");
|
||||
assertTrue(!(v1a == v2), "v1a == v2");
|
||||
assertTrue(!(v1a == v3), "v1a == v3");
|
||||
assertTrue(!(v1a == v4), "v1a == v4");
|
||||
}
|
||||
|
||||
static void testNotEqualVector() {
|
||||
printf("testNotEqualVector...\n");
|
||||
Vector v1a(1.f, 2.f, 3.f);
|
||||
Vector v1b(1.f, 2.f, 3.f);
|
||||
Vector v2(1.f, 2.f, 4.f);
|
||||
Vector v3(1.f, 3.f, 3.f);
|
||||
Vector v4(2.f, 3.f, 3.f);
|
||||
assertTrue(!(v1a != v1b), "v1a != v1b");
|
||||
assertTrue(!(v1b != v1a), "v1b != v1a");
|
||||
assertTrue(v1a != v2, "v1a == v2");
|
||||
assertTrue(v1a != v3, "v1a == v3");
|
||||
assertTrue(v1a != v4, "v1a == v4");
|
||||
}
|
||||
|
||||
static void testMultiplyScalar() {
|
||||
printf("testMultiplyScalar...\n");
|
||||
Vector v1(1.f, 2.f, 3.f);
|
||||
Vector v2(2.f, 4.f, 6.f);
|
||||
assertTrue(v2 == (v1 * 2), "v2 != v1 * 2");
|
||||
assertTrue(v1 == (v2 * .5), "v1 != v2 * .5");
|
||||
}
|
||||
|
||||
static void testDivideScalar() {
|
||||
printf("testDivideScalar...\n");
|
||||
Vector v1(1.f, 2.f, 3.f);
|
||||
Vector v2(2.f, 4.f, 6.f);
|
||||
assertTrue(v2 == (v1 / .5), "v2 != v1 / .5");
|
||||
assertTrue(v1 == (v2 / 2), "v1 != v2 / 2");
|
||||
}
|
||||
|
||||
static void testAddScalar() {
|
||||
printf("testAddScalar...\n");
|
||||
Vector v1(1, 2, 3);
|
||||
assertTrue(Vector(2, 3, 4) == (v1 + 1), "v1 + 1");
|
||||
assertTrue(Vector(0, 1, 2) == (v1 + -1), "v1 + -1");
|
||||
}
|
||||
|
||||
static void testSubtractScalar() {
|
||||
printf("testSubtractScalar...\n");
|
||||
Vector v1(1, 2, 3);
|
||||
assertTrue(Vector(0, 1, 2) == (v1 - 1), "v1 - 1");
|
||||
assertTrue(Vector(2, 3, 4) == (v1 - -1), "v1 - -1");
|
||||
}
|
||||
|
||||
static void testMultiplyAssignmentScalar() {
|
||||
printf("testMultiplyAssignmentScalar...\n");
|
||||
Vector v1(1, 2, 3);
|
||||
v1 *= 2;
|
||||
assertTrue(Vector(2, 4, 6) == v1, "v1 * 2");
|
||||
v1 *= .5;
|
||||
assertTrue(Vector(1, 2, 3) == v1, "v1 * .5");
|
||||
}
|
||||
|
||||
static void testDivideAssignmentScalar() {
|
||||
printf("testDivideAssignmentScalar...\n");
|
||||
Vector v1(1, 2, 3);
|
||||
v1 /= .5;
|
||||
assertTrue(Vector(2, 4, 6) == v1, "v1 / .5");
|
||||
v1 /= 2;
|
||||
assertTrue(Vector(1, 2, 3) == v1, "v1 / 2");
|
||||
}
|
||||
|
||||
static void testAddAssignmentScalar() {
|
||||
printf("testAddAssignmentScalar...\n");
|
||||
Vector v1(1, 2, 3);
|
||||
v1 += 1;
|
||||
assertTrue(Vector(2, 3, 4) == v1, "v1 + 1");
|
||||
v1 += -1;
|
||||
assertTrue(Vector(1, 2, 3) == v1, "v1 + -1");
|
||||
}
|
||||
|
||||
static void testSubtractAssignmentScalar() {
|
||||
printf("testSubtractAssignmentScalar...\n");
|
||||
Vector v1(1, 2, 3);
|
||||
v1 -= -1;
|
||||
assertTrue(Vector(2, 3, 4) == v1, "v1 - -1");
|
||||
v1 -= 1;
|
||||
assertTrue(Vector(1, 2, 3) == v1, "v1 - 1");
|
||||
}
|
||||
|
||||
static void testAddVector() {
|
||||
printf("testAddVector...\n");
|
||||
Vector v1(1, 2, 3);
|
||||
Vector v2 = v1 * 2;
|
||||
Vector v3 = v1 + v2;
|
||||
assertTrue(Vector(3, 6, 9) == v3, "v1 + v1 * 2");
|
||||
}
|
||||
|
||||
static void testSubtractVector() {
|
||||
printf("testSubtractVector...\n");
|
||||
Vector v1(1, 2, 3);
|
||||
Vector v2 = v1 * 2;
|
||||
Vector v3 = v1 - v2;
|
||||
assertTrue(Vector(-1, -2, -3) == v3, "v1 - v1 * 2");
|
||||
}
|
||||
|
||||
static void testAddAssignmentVector() {
|
||||
printf("testAddAssignmentVector...\n");
|
||||
Vector v1(1, 2, 3);
|
||||
v1 += v1 * 2;
|
||||
assertTrue(Vector(3, 6, 9) == v1, "v1 + v1 * 2");
|
||||
}
|
||||
|
||||
static void testSubtractAssignmentVector() {
|
||||
printf("testSubtractAssignmentVector...\n");
|
||||
Vector v1(1, 2, 3);
|
||||
v1 -= v1 * 2;
|
||||
assertTrue(Vector(-1, -2, -3) == v1, "v1 - v1 * 2");
|
||||
}
|
||||
|
||||
static void testNegative() {
|
||||
printf("testNegative...\n");
|
||||
Vector v1(1, 2, 3);
|
||||
Vector v2(-1, -2, -3);
|
||||
assertTrue(v2 == -v1, "v2 != -v1");
|
||||
}
|
||||
|
||||
static void testDotVector() {
|
||||
printf("testDotVector...\n");
|
||||
Vector v1(1, 2, 3);
|
||||
Vector v2(-2, -3, -4);
|
||||
float d = 1 * -2 + 2 * -3 + 3 * -4;
|
||||
assertTrue(v1.dot(v2) == d, "v1 dot v2");
|
||||
}
|
||||
|
||||
static void testCrossVector() {
|
||||
printf("testCrossVector...\n");
|
||||
Vector x(1, 0, 0);
|
||||
Vector y(0, 1, 0);
|
||||
Vector z(0, 0, 1);
|
||||
assertTrue(x.cross(y) == z, "x cross y");
|
||||
}
|
||||
|
||||
static void testLength() {
|
||||
printf("testLength...\n");
|
||||
}
|
||||
|
||||
static void testNormalize() {
|
||||
printf("testNormalize...\n");
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testDefaultConstructor();
|
||||
testExplicitConstructor();
|
||||
testAssignment();
|
||||
testCopyConstructor();
|
||||
testEqualVector();
|
||||
testNotEqualVector();
|
||||
testMultiplyScalar();
|
||||
testDivideScalar();
|
||||
testAddScalar();
|
||||
testSubtractScalar();
|
||||
testMultiplyAssignmentScalar();
|
||||
testDivideAssignmentScalar();
|
||||
testAddAssignmentScalar();
|
||||
testSubtractAssignmentScalar();
|
||||
testAddVector();
|
||||
testSubtractVector();
|
||||
testAddAssignmentVector();
|
||||
testSubtractAssignmentVector();
|
||||
testNegative();
|
||||
testDotVector();
|
||||
testCrossVector();
|
||||
testLength();
|
||||
testNormalize();
|
||||
return returnCode;
|
||||
}
|
||||
626
src/player.cpp
Normal file
626
src/player.cpp
Normal file
|
|
@ -0,0 +1,626 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <GLUT/glut.h>
|
||||
#include <OpenGL/gl.h>
|
||||
#include <OpenGL/glu.h>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
#include "material.h"
|
||||
#include "model.h"
|
||||
#include "physics/constraint.h"
|
||||
#include "physics/force.h"
|
||||
#include "physics/particle.h"
|
||||
#include "physics/shape.h"
|
||||
#include "physics/vector.h"
|
||||
#include "player.h"
|
||||
#include "room.h"
|
||||
#include "room_object.h"
|
||||
#include "world.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
static const float axleRadius = .02f;
|
||||
static const float axleLength = .14f;
|
||||
static const float bodySize = .15f;
|
||||
static const float eyeRadius = .02f;
|
||||
static const float wheelRadius = .1f;
|
||||
static const float wheelWeight = 1.f;
|
||||
static const float counterWeightRadius = .07f;
|
||||
static const float counterWeightOffset = .07f;
|
||||
static const float counterWeight = .1f;
|
||||
|
||||
static const float forwardForce = 12.f;
|
||||
static const float backwardForce = 5.f;
|
||||
static const float turnForce = 1.f;
|
||||
static const float motorFriction = 1.f;
|
||||
static const float brakeFriction = 3.f;
|
||||
|
||||
static const Vector spokeColor(.5f, .5f, .5f);
|
||||
static const Vector eyeColor(.5f, .5f, .5f);
|
||||
static const Vector axleMaterialDiffuse(.9f, .8f, .8f);
|
||||
static const Vector tireMaterialDiffuse(.1f, .1f, .1f);
|
||||
static const Vector bodyMaterialDiffuse(.6f, .2f, .3f);
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
/**
|
||||
* A small model which displays just the player's wheel, so that this model
|
||||
* can be compiled into a display list for faster display. This model uses the
|
||||
* parent model's quadric.
|
||||
*/
|
||||
class PlayerWheelModel : public Model {
|
||||
public:
|
||||
PlayerWheelModel(const PlayerModel& model);
|
||||
|
||||
virtual void display();
|
||||
|
||||
private:
|
||||
const PlayerModel& model_;
|
||||
};
|
||||
|
||||
/**
|
||||
* A small model which displays just the player's body (and axle, and eyes),
|
||||
* so that this model can be compiled into a display list for faster display.
|
||||
* This model uses the parent model's quadric.
|
||||
*/
|
||||
class PlayerBodyModel : public Model {
|
||||
public:
|
||||
PlayerBodyModel(const PlayerModel& model);
|
||||
|
||||
virtual void display();
|
||||
|
||||
private:
|
||||
const PlayerModel& model_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
PlayerWheelModel::PlayerWheelModel(const PlayerModel& model)
|
||||
: model_(model) {
|
||||
}
|
||||
|
||||
void PlayerWheelModel::display() {
|
||||
/* Spokes. */
|
||||
glDisable(GL_LIGHTING);
|
||||
glColorv(spokeColor);
|
||||
glLineWidth(2.f);
|
||||
gluQuadricDrawStyle(model_.quadric_, GLU_LINE);
|
||||
gluDisk(model_.quadric_, 0, wheelRadius - wheelRadius / 8.f, 8, 1);
|
||||
gluQuadricDrawStyle(model_.quadric_, GLU_FILL);
|
||||
glEnable(GL_LIGHTING);
|
||||
|
||||
/* Tire. */
|
||||
glMaterialv(GL_FRONT_AND_BACK, GL_DIFFUSE, tireMaterialDiffuse);
|
||||
glutSolidTorus(wheelRadius / 4.f, 3 * wheelRadius / 4.f, 16, 32);
|
||||
}
|
||||
|
||||
PlayerBodyModel::PlayerBodyModel(const PlayerModel& model)
|
||||
: model_(model) {
|
||||
}
|
||||
|
||||
void PlayerBodyModel::display() {
|
||||
float l = axleLength + wheelRadius / 4.f + wheelRadius;
|
||||
|
||||
/* Axle endcap. */
|
||||
glMaterialv(GL_FRONT_AND_BACK, GL_DIFFUSE, axleMaterialDiffuse);
|
||||
glPushMatrix();
|
||||
glTranslatef(0.f, 0.f, -l / 2.f);
|
||||
glRotatef(180.f, 0.f, 1.f, 0.f);
|
||||
gluDisk(model_.quadric_, 0.f, axleRadius, 16, 8);
|
||||
glPopMatrix();
|
||||
|
||||
/* Axle. */
|
||||
glPushMatrix();
|
||||
glTranslatef(0.f, 0.f, -l / 2.f);
|
||||
gluCylinder(model_.quadric_, axleRadius, axleRadius, l, 16, 1);
|
||||
glPopMatrix();
|
||||
|
||||
/* Axle endcap. */
|
||||
glPushMatrix();
|
||||
glTranslatef(0.f, 0.f, l / 2.f);
|
||||
gluDisk(model_.quadric_, 0.f, axleRadius, 16, 8);
|
||||
glPopMatrix();
|
||||
|
||||
/* Body. */
|
||||
glMaterialv(GL_FRONT_AND_BACK, GL_DIFFUSE, bodyMaterialDiffuse);
|
||||
glutSolidCube(bodySize);
|
||||
|
||||
/* Eyes. */
|
||||
static const float eyeDepth = .01f;
|
||||
glMaterialv(GL_FRONT_AND_BACK, GL_DIFFUSE, axleMaterialDiffuse);
|
||||
glColorv(eyeColor);
|
||||
glTranslatef(bodySize / 2.f, bodySize / 6.f, bodySize / 4.f);
|
||||
glPushMatrix();
|
||||
glRotatef(90.f, 0.f, 1.f, 0.f);
|
||||
gluCylinder(model_.quadric_, eyeRadius, eyeRadius, eyeDepth, 16, 1);
|
||||
glTranslatef(0.f, 0.f, eyeDepth);
|
||||
gluDisk(model_.quadric_, 0, eyeRadius, 8, 1);
|
||||
glTranslatef(0.f, -.004f, .001f);
|
||||
glMaterialv(GL_FRONT_AND_BACK, GL_DIFFUSE, tireMaterialDiffuse);
|
||||
gluDisk(model_.quadric_, 0, eyeRadius / 2.f, 8, 1);
|
||||
glMaterialv(GL_FRONT_AND_BACK, GL_DIFFUSE, axleMaterialDiffuse);
|
||||
glPopMatrix();
|
||||
glTranslatef(0.f, 0.f, -bodySize / 2.f);
|
||||
glPushMatrix();
|
||||
glRotatef(90.f, 0.f, 1.f, 0.f);
|
||||
gluCylinder(model_.quadric_, eyeRadius, eyeRadius, eyeDepth, 16, 1);
|
||||
glTranslatef(0.f, 0.f, eyeDepth);
|
||||
gluDisk(model_.quadric_, 0, eyeRadius, 8, 1);
|
||||
glTranslatef(0.f, -.004f, .001f);
|
||||
glMaterialv(GL_FRONT_AND_BACK, GL_DIFFUSE, tireMaterialDiffuse);
|
||||
gluDisk(model_.quadric_, 0, eyeRadius / 2.f, 8, 1);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
PlayerModel::PlayerModel(const Player& player)
|
||||
: player_(player), quadric_(NULL),
|
||||
wheelModel_(Models::compile(new PlayerWheelModel(*this))),
|
||||
bodyModel_(Models::compile(new PlayerBodyModel(*this))) {
|
||||
for (int i = 0; i < 15; i++) {
|
||||
orientation_[i] = 0.f;
|
||||
}
|
||||
orientation_[15] = 1.f;
|
||||
}
|
||||
|
||||
PlayerModel::~PlayerModel() {
|
||||
delete wheelModel_;
|
||||
delete bodyModel_;
|
||||
if (quadric_ != NULL) {
|
||||
gluDeleteQuadric(quadric_);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerModel::initialize() {
|
||||
if (quadric_ != NULL) {
|
||||
gluDeleteQuadric(quadric_);
|
||||
}
|
||||
quadric_ = gluNewQuadric();
|
||||
wheelModel_->initialize();
|
||||
bodyModel_->initialize();
|
||||
}
|
||||
|
||||
float* PlayerModel::orientation() {
|
||||
const Vector& x = player_.x();
|
||||
const Vector& y = player_.y();
|
||||
const Vector& z = player_.z();
|
||||
orientation_[0] = x.x; orientation_[1] = x.y; orientation_[2] = x.z;
|
||||
orientation_[4] = y.x; orientation_[5] = y.y; orientation_[6] = y.z;
|
||||
orientation_[8] = z.x; orientation_[9] = z.y; orientation_[10] = z.z;
|
||||
return orientation_;
|
||||
}
|
||||
|
||||
void PlayerModel::display() {
|
||||
Materials::blank().bind();
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatev(player_.origin());
|
||||
glMultMatrixf(orientation());
|
||||
|
||||
/* Left wheel. */
|
||||
if (!World::world()->debug() || player_.leftWheelFriction()) {
|
||||
glPushMatrix();
|
||||
glTranslatef(0.f, 0.f, -axleLength / 2.f - wheelRadius / 2.f);
|
||||
glRotatef(player_.leftWheelAngle(), 0.f, 0.f, 1.f);
|
||||
glRotatef(180.f, 0.f, 1.f, 0.f);
|
||||
wheelModel_->display();
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
/* Right wheel. */
|
||||
if (!World::world()->debug() || player_.rightWheelFriction()) {
|
||||
glPushMatrix();
|
||||
glTranslatef(0.f, 0.f, axleLength / 2.f + wheelRadius / 2.f);
|
||||
glRotatef(player_.rightWheelAngle(), 0.f, 0.f, 1.f);
|
||||
wheelModel_->display();
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
/* Axes. */
|
||||
if (World::world()->debug()) {
|
||||
displayAxes();
|
||||
}
|
||||
|
||||
/* Body. */
|
||||
bodyModel_->display();
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void PlayerModel::displayAxes() {
|
||||
glDisable(GL_LIGHTING);
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(1.f, 0.f, 0.f);
|
||||
glVertex3f(0.f, 0.f, 0.f);
|
||||
glVertex3f(2 * bodySize, 0.f, 0.f);
|
||||
glColor3f(0.f, 1.f, 0.f);
|
||||
glVertex3f(0.f, 0.f, 0.f);
|
||||
glVertex3f(0.f, 2 * bodySize, 0.f);
|
||||
glColor3f(0.f, 0.f, 1.f);
|
||||
glVertex3f(0.f, 0.f, 0.f);
|
||||
glVertex3f(0.f, 0.f, 2 * bodySize);
|
||||
glEnd();
|
||||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
Player::Player()
|
||||
: turnState_(NONE), moveState_(NONE),
|
||||
sphere_(Vector::ZERO(), wheelRadius * 2.f), model_(*this) {
|
||||
counterWeight_.inverseMass = 1.f / counterWeight;
|
||||
}
|
||||
|
||||
float Player::mass() const {
|
||||
return counterWeight + 3.f;
|
||||
}
|
||||
|
||||
Player::Wheel::Wheel()
|
||||
: angle(0.f), angleStep(0.f) {
|
||||
inverseMass = 1.f / wheelWeight;
|
||||
}
|
||||
|
||||
void Player::setOrigin(const Vector& origin) {
|
||||
static const float a2 = axleLength / 2.f;
|
||||
|
||||
leftWheel_.position = origin + Vector(0.f, 0.f, -a2);
|
||||
rightWheel_.position = origin + Vector(0.f, 0.f, a2);
|
||||
body_.position = origin;
|
||||
counterWeight_.position = origin + Vector(-counterWeightOffset, 0.f, 0.f);
|
||||
|
||||
leftWheel_.previousPosition = leftWheel_.position;
|
||||
rightWheel_.previousPosition = rightWheel_.position;
|
||||
body_.previousPosition = body_.position;
|
||||
counterWeight_.previousPosition = counterWeight_.position;
|
||||
|
||||
origin_ = origin;
|
||||
x_ = Vector::X();
|
||||
y_ = Vector::Y();
|
||||
z_ = Vector::Z();
|
||||
}
|
||||
|
||||
void Player::setVelocity(const Vector& velocity) {
|
||||
|
||||
/* TODO: Rotate the player to face the velocity. But this works for now. */
|
||||
if (velocity.x < 0) {
|
||||
static const float a2 = axleLength / 2.f;
|
||||
|
||||
leftWheel_.position = origin_ + Vector(0.f, 0.f, a2);
|
||||
rightWheel_.position = origin_ + Vector(0.f, 0.f, -a2);
|
||||
counterWeight_.position = origin_ + Vector(counterWeightOffset, 0.f, 0.f);
|
||||
|
||||
leftWheel_.previousPosition = leftWheel_.position;
|
||||
rightWheel_.previousPosition = rightWheel_.position;
|
||||
counterWeight_.previousPosition = counterWeight_.position;
|
||||
}
|
||||
|
||||
Vector v = velocity * ParticleSimulator::timeStep();
|
||||
leftWheel_.previousPosition = leftWheel_.position - v;
|
||||
rightWheel_.previousPosition = rightWheel_.position - v;
|
||||
body_.previousPosition = body_.position - v;
|
||||
counterWeight_.previousPosition = counterWeight_.position - v;
|
||||
}
|
||||
|
||||
void Player::resetForces() {
|
||||
leftWheel_.force = Vector::ZERO();
|
||||
rightWheel_.force = Vector::ZERO();
|
||||
body_.force = Vector::ZERO();
|
||||
counterWeight_.force = Vector::ZERO();
|
||||
|
||||
/* Moving forces. */
|
||||
switch (moveState_) {
|
||||
case FORWARD: {
|
||||
leftWheel_.applyForce(z_, forwardForce);
|
||||
rightWheel_.applyForce(z_, forwardForce);
|
||||
break;
|
||||
}
|
||||
case BACKWARD: {
|
||||
leftWheel_.applyForce(z_, -backwardForce);
|
||||
rightWheel_.applyForce(z_, -backwardForce);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Turning forces. */
|
||||
switch (turnState_) {
|
||||
case RIGHT: {
|
||||
leftWheel_.applyForce(z_, turnForce);
|
||||
rightWheel_.applyForce(z_, -turnForce);
|
||||
break;
|
||||
}
|
||||
case LEFT: {
|
||||
leftWheel_.applyForce(z_, -turnForce);
|
||||
rightWheel_.applyForce(z_, turnForce);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Uniform drag forces. */
|
||||
if ((turnState_ != NONE) || (moveState_ == FORWARD)) {
|
||||
leftWheel_.applyQuadraticDrag(motorFriction);
|
||||
rightWheel_.applyQuadraticDrag(motorFriction);
|
||||
} else {
|
||||
leftWheel_.applyLinearDrag(brakeFriction);
|
||||
rightWheel_.applyLinearDrag(brakeFriction);
|
||||
}
|
||||
|
||||
/* Flipping force if upside-down. */
|
||||
if ((leftWheel_.contact && rightWheel_.contact)
|
||||
&& (y_.y < (.5 * leftWheel_.contactNormal.y))) {
|
||||
counterWeight_.force -= y_;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::Wheel::applyContact(const RoomObject& o) {
|
||||
const Vector& normal = Constraints::projection().normal;
|
||||
if ((normal.y > o.slip()) && (!contact || (contactNormal.y < normal.y))) {
|
||||
contactNormal = normal;
|
||||
contactVelocity = o.velocity(position);
|
||||
contact = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::Wheel::applyForce(const Vector& z, float f) {
|
||||
if (friction()) {
|
||||
force += z.cross(contactNormal) * -f;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::Wheel::applyLinearDrag(float kd) {
|
||||
if (friction()) {
|
||||
Vector v = (position - previousPosition - contactVelocity)
|
||||
/ ParticleSimulator::timeStep();
|
||||
float vs = v.squared();
|
||||
if (vs < 1E-3) {
|
||||
kd *= 10.f;
|
||||
} else if (vs < 1E-2) {
|
||||
kd *= 2.f;
|
||||
}
|
||||
force -= v * kd;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::Wheel::applyQuadraticDrag(float kd) {
|
||||
if (friction()) {
|
||||
Vector v = (position - previousPosition - contactVelocity)
|
||||
/ ParticleSimulator::timeStep();
|
||||
force -= v * v.length() * kd;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::applyForce(UnaryForce& force) {
|
||||
force.apply(leftWheel_);
|
||||
force.apply(rightWheel_);
|
||||
force.apply(body_);
|
||||
force.apply(counterWeight_);
|
||||
}
|
||||
|
||||
void Player::step(const ParticleSimulator& s) {
|
||||
leftWheel_.contact = false;
|
||||
rightWheel_.contact = false;
|
||||
s.step(leftWheel_);
|
||||
s.step(rightWheel_);
|
||||
s.step(body_);
|
||||
s.step(counterWeight_);
|
||||
sphere_.x() = body_.position;
|
||||
}
|
||||
|
||||
bool Player::intersects(const Shape& s) {
|
||||
return s.intersects(sphere_);
|
||||
}
|
||||
|
||||
bool Player::constrainOutside(const RoomObject& o) {
|
||||
const Shape& s = o.shape();
|
||||
bool contact = false;
|
||||
if (s.intersects(sphere_)) {
|
||||
if (Constraints::outside(leftWheel_, s, wheelRadius)) {
|
||||
constrainGlancing(o);
|
||||
leftWheel_.applyContact(o);
|
||||
contact = true;
|
||||
}
|
||||
if (Constraints::outside(rightWheel_, s, wheelRadius)) {
|
||||
constrainGlancing(o);
|
||||
rightWheel_.applyContact(o);
|
||||
contact = true;
|
||||
}
|
||||
if (Constraints::outside(body_, s, wheelRadius)) {
|
||||
sphere_.x() = body_.position;
|
||||
contact = true;
|
||||
}
|
||||
if (Constraints::outside(counterWeight_, s, counterWeightRadius)) {
|
||||
contact = true;
|
||||
}
|
||||
}
|
||||
return contact;
|
||||
}
|
||||
|
||||
void Player::constrainGlancing(const RoomObject& o) {
|
||||
const Projection& j = Constraints::projection();
|
||||
|
||||
/*
|
||||
* This constraint only applies if we are moving parallel to the wall, so if
|
||||
* the body particle isn't moving, don't do anything.
|
||||
*/
|
||||
if ((body_.position - body_.previousPosition).squared() < 1E-5) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* For glancing collisions, align parallel to the wall. */
|
||||
float n = j.normal.dot(z_);
|
||||
if (fabsf(n) > .8f) {
|
||||
Plane p = Plane(j.x, j.normal);
|
||||
Vector x = p.project((leftWheel_.position + rightWheel_.position) / 2.f).x;
|
||||
|
||||
Vector wp1 = x + j.normal * wheelRadius;
|
||||
Vector wp2 = x + j.normal * (wheelRadius + axleLength);
|
||||
Vector bp = x + j.normal * (wheelRadius + axleLength / 2.f);
|
||||
const Vector& lp = (n > 0.f) ? wp1 : wp2;
|
||||
const Vector& rp = (n > 0.f) ? wp2 : wp1;
|
||||
|
||||
/*
|
||||
* Only allow this constraint to move the player a small amount, as we don't
|
||||
* want this constraint to apply in weird cases like hitting a corner.
|
||||
* Otherwise, this constraint might throw the player large distances.
|
||||
*/
|
||||
if (((bp - body_.position).squared()
|
||||
+ (lp - leftWheel_.position).squared()
|
||||
+ (rp - rightWheel_.position).squared()) > 1E-3) {
|
||||
return;
|
||||
}
|
||||
|
||||
body_.position = bp;
|
||||
leftWheel_.position = lp;
|
||||
rightWheel_.position = rp;
|
||||
|
||||
/*
|
||||
* Include a small fudge factor to push the player away from the wall, so
|
||||
* that the player can turn. Otherwise, the player seemingly gets locked to
|
||||
* the wall surface!
|
||||
*/
|
||||
body_.position += j.normal * 1E-4;
|
||||
leftWheel_.position += j.normal * 1E-4;
|
||||
rightWheel_.position += j.normal * 1E-4;
|
||||
body_.previousPosition += j.normal * 1E-4;
|
||||
leftWheel_.previousPosition += j.normal * 1E-4;
|
||||
rightWheel_.previousPosition += j.normal * 1E-4;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::constrainBody() {
|
||||
Constraints::distance(leftWheel_, rightWheel_, axleLength);
|
||||
Constraints::distance(leftWheel_, body_, axleLength / 2.f);
|
||||
Constraints::distance(rightWheel_, body_, axleLength / 2.f);
|
||||
|
||||
static const float d
|
||||
= sqrtf(axleLength * axleLength / 4.f
|
||||
+ counterWeightOffset * counterWeightOffset);
|
||||
Constraints::distance(leftWheel_, counterWeight_, d);
|
||||
Constraints::distance(rightWheel_, counterWeight_, d);
|
||||
Constraints::distance(body_, counterWeight_, counterWeightOffset);
|
||||
}
|
||||
|
||||
void Player::constrainInternal() {
|
||||
leftWheel_.constrainDirection(z_);
|
||||
rightWheel_.constrainDirection(z_);
|
||||
constrainBody();
|
||||
|
||||
origin_ = body_.position;
|
||||
z_ = (rightWheel_.position - leftWheel_.position) / axleLength;
|
||||
x_ = (body_.position - counterWeight_.position) / counterWeightOffset;
|
||||
y_ = -x_.cross(z_);
|
||||
}
|
||||
|
||||
void Player::Wheel::constrainDirection(const Vector& z) {
|
||||
if (!friction()) {
|
||||
angle += angleStep;
|
||||
return; // no normal force, no friction
|
||||
}
|
||||
|
||||
/* Don't let upwards-moving platforms launch us into the air. */
|
||||
Vector cv = contactVelocity;
|
||||
if (cv.y > 0) {
|
||||
cv.y = 0.f;
|
||||
}
|
||||
|
||||
/* Cancel the sideways component of the velocity. */
|
||||
Vector v = position - previousPosition - cv;
|
||||
v -= z * z.dot(v);
|
||||
previousPosition = position - v - cv;
|
||||
|
||||
/* Rotate the wheel. */
|
||||
Vector forward = z.cross(contactNormal);
|
||||
Vector vForward = forward * forward.dot(v);
|
||||
float l = vForward.length();
|
||||
if (vForward.cross(contactNormal).dot(z) > 0) {
|
||||
l *= -1;
|
||||
}
|
||||
angleStep = 360.f * l / (M_PI * 2 * wheelRadius);
|
||||
angle += angleStep;
|
||||
}
|
||||
|
||||
void Player::jiggle() {
|
||||
Vector v = Vector::randomVector(ParticleSimulator::timeStep());
|
||||
v.y = ParticleSimulator::timeStep();
|
||||
leftWheel_.position += v;
|
||||
rightWheel_.position += v;
|
||||
body_.position += v;
|
||||
counterWeight_.position += v;
|
||||
}
|
||||
|
||||
void Player::move(Direction d) {
|
||||
switch (d) {
|
||||
case LEFT: {
|
||||
if (turnState_ == NONE) {
|
||||
turnState_ = LEFT;
|
||||
} else {
|
||||
turnState_ = NONE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RIGHT: {
|
||||
if (turnState_ == NONE) {
|
||||
turnState_ = RIGHT;
|
||||
} else {
|
||||
turnState_ = NONE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FORWARD: {
|
||||
if (moveState_ == NONE) {
|
||||
moveState_ = FORWARD;
|
||||
} else {
|
||||
moveState_ = NONE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BACKWARD: {
|
||||
if (moveState_ == NONE) {
|
||||
moveState_ = BACKWARD;
|
||||
} else {
|
||||
moveState_ = NONE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Player::stop() {
|
||||
turnState_ = NONE;
|
||||
moveState_ = NONE;
|
||||
}
|
||||
|
||||
void Player::stop(Direction d) {
|
||||
switch (d) {
|
||||
case LEFT: {
|
||||
if (turnState_ != NONE) {
|
||||
turnState_ = NONE;
|
||||
} else {
|
||||
turnState_ = RIGHT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RIGHT: {
|
||||
if (turnState_ != NONE) {
|
||||
turnState_ = NONE;
|
||||
} else {
|
||||
turnState_ = LEFT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FORWARD: {
|
||||
if (moveState_ != NONE) {
|
||||
moveState_ = NONE;
|
||||
} else {
|
||||
moveState_ = BACKWARD;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BACKWARD: {
|
||||
if (moveState_ != NONE) {
|
||||
moveState_ = NONE;
|
||||
} else {
|
||||
moveState_ = FORWARD;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
117
src/player.h
Normal file
117
src/player.h
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_PLAYER_H
|
||||
#define MBOSTOCK_PLAYER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "model.h"
|
||||
#include "physics/particle.h"
|
||||
#include "physics/shape.h"
|
||||
#include "physics/vector.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Player;
|
||||
class RoomObject;
|
||||
class UnaryForce;
|
||||
|
||||
class PlayerModel : public Model {
|
||||
public:
|
||||
PlayerModel(const Player& player);
|
||||
virtual ~PlayerModel();
|
||||
|
||||
virtual void initialize();
|
||||
virtual void display();
|
||||
|
||||
private:
|
||||
float* orientation();
|
||||
void displayAxes();
|
||||
|
||||
const Player& player_;
|
||||
GLUquadric* quadric_;
|
||||
float orientation_[16];
|
||||
Model* wheelModel_;
|
||||
Model* bodyModel_;
|
||||
|
||||
friend class PlayerWheelModel;
|
||||
friend class PlayerBodyModel;
|
||||
};
|
||||
|
||||
class Player {
|
||||
public:
|
||||
Player();
|
||||
|
||||
enum Direction { NONE, LEFT, RIGHT, FORWARD, BACKWARD };
|
||||
|
||||
void move(Direction d);
|
||||
void stop(Direction d);
|
||||
void stop();
|
||||
void jiggle();
|
||||
void setOrigin(const Vector& origin);
|
||||
void setVelocity(const Vector& velocity);
|
||||
|
||||
void resetForces();
|
||||
void applyForce(UnaryForce& force);
|
||||
void step(const ParticleSimulator& s);
|
||||
bool intersects(const Shape& s);
|
||||
bool constrainOutside(const RoomObject& o);
|
||||
void constrainInternal();
|
||||
|
||||
float mass() const;
|
||||
|
||||
inline const Vector& x() const { return x_; }
|
||||
inline const Vector& y() const { return y_; }
|
||||
inline const Vector& z() const { return z_; }
|
||||
inline const Vector& origin() const { return origin_; }
|
||||
inline float leftWheelAngle() const { return leftWheel_.angle; }
|
||||
inline float rightWheelAngle() const { return rightWheel_.angle; }
|
||||
|
||||
bool leftWheelFriction() const { return leftWheel_.friction(); }
|
||||
bool rightWheelFriction() const { return rightWheel_.friction(); }
|
||||
|
||||
inline Model& model() { return model_; }
|
||||
|
||||
private:
|
||||
class Wheel : public Particle {
|
||||
public:
|
||||
Wheel();
|
||||
|
||||
inline bool friction() const { return contact; }
|
||||
void applyContact(const RoomObject& o);
|
||||
void applyForce(const Vector& z, float f);
|
||||
void applyLinearDrag(float kd);
|
||||
void applyQuadraticDrag(float kd);
|
||||
void constrainDirection(const Vector& z);
|
||||
|
||||
bool contact;
|
||||
Vector contactNormal;
|
||||
Vector contactVelocity;
|
||||
|
||||
float angle;
|
||||
float angleStep;
|
||||
};
|
||||
|
||||
void constrainGlancing(const RoomObject& o);
|
||||
void constrainBody();
|
||||
|
||||
Wheel leftWheel_;
|
||||
Wheel rightWheel_;
|
||||
Particle body_;
|
||||
Particle counterWeight_;
|
||||
|
||||
Direction turnState_;
|
||||
Direction moveState_;
|
||||
|
||||
Sphere sphere_;
|
||||
Vector origin_;
|
||||
Vector x_;
|
||||
Vector y_;
|
||||
Vector z_;
|
||||
|
||||
PlayerModel model_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
11
src/portal.cpp
Normal file
11
src/portal.cpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "physics/vector.h"
|
||||
#include "portal.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Portal::Portal(const Vector& min, const Vector& max,
|
||||
int room, int origin, bool reset)
|
||||
: box_(min, max), room_(room), origin_(origin), reset_(reset) {
|
||||
}
|
||||
33
src/portal.h
Normal file
33
src/portal.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_PORTAL_H
|
||||
#define MBOSTOCK_PORTAL_H
|
||||
|
||||
#include "physics/shape.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Vector;
|
||||
|
||||
class Portal {
|
||||
public:
|
||||
Portal(const Vector& min, const Vector& max,
|
||||
int room, int origin, bool reset);
|
||||
|
||||
inline bool contains(const Vector& p) const { return box_.contains(p); }
|
||||
inline const AxisAlignedBox& bounds() const { return box_; }
|
||||
inline int room() const { return room_; }
|
||||
inline int origin() const { return origin_; }
|
||||
inline bool reset() const { return reset_; }
|
||||
|
||||
private:
|
||||
AxisAlignedBox box_;
|
||||
int room_;
|
||||
int origin_;
|
||||
bool reset_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
35
src/ramp.cpp
Normal file
35
src/ramp.cpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "material.h"
|
||||
#include "model.h"
|
||||
#include "physics/shape.h"
|
||||
#include "physics/vector.h"
|
||||
#include "ramp.h"
|
||||
#include "room.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Ramp::Ramp(const Vector& x0, const Vector& x1,
|
||||
const Vector& x2, const Vector& x3)
|
||||
: wedge_(x0, x1, x2, x3), model_(wedge_) {
|
||||
}
|
||||
|
||||
Model& Ramp::model() {
|
||||
return model_;
|
||||
}
|
||||
|
||||
const Shape& Ramp::shape() const {
|
||||
return wedge_;
|
||||
}
|
||||
|
||||
void Ramp::setMaterial(const Material& m) {
|
||||
model_.setMaterial(m);
|
||||
}
|
||||
|
||||
void Ramp::setTopMaterial(const Material& m) {
|
||||
model_.setTopMaterial(m);
|
||||
}
|
||||
|
||||
float Ramp::slip() const {
|
||||
return model_.material().slip();
|
||||
}
|
||||
32
src/ramp.h
Normal file
32
src/ramp.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_RAMP_H
|
||||
#define MBOSTOCK_RAMP_H
|
||||
|
||||
#include "model.h"
|
||||
#include "physics/shape.h"
|
||||
#include "room_object.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Ramp : public RoomObject {
|
||||
public:
|
||||
Ramp(const Vector& x0, const Vector& x1,
|
||||
const Vector& x2, const Vector& x3);
|
||||
|
||||
virtual Model& model();
|
||||
virtual const Shape& shape() const;
|
||||
virtual float slip() const;
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
void setTopMaterial(const Material& m);
|
||||
|
||||
private:
|
||||
const Wedge wedge_;
|
||||
WedgeModel model_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
27
src/resource.cpp
Normal file
27
src/resource.cpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <fstream>
|
||||
#include <ios>
|
||||
#include <iostream>
|
||||
|
||||
#include "resource.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
const char* Resources::path() {
|
||||
return "Contents/Resources/";
|
||||
}
|
||||
|
||||
const char* Resources::readFile(const char* p) {
|
||||
std::string fullPath(path());
|
||||
fullPath.append(p);
|
||||
std::ifstream file(fullPath.c_str());
|
||||
file.seekg(0, std::ios::end);
|
||||
std::ifstream::pos_type size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
char* buffer = new char[1 + size];
|
||||
file.read(buffer, size);
|
||||
buffer[size] = '\0';
|
||||
file.close();
|
||||
return buffer;
|
||||
}
|
||||
19
src/resource.h
Normal file
19
src/resource.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_RESOURCE_H
|
||||
#define MBOSTOCK_RESOURCE_H
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Resources {
|
||||
public:
|
||||
static const char* path();
|
||||
static const char* readFile(const char* path);
|
||||
|
||||
private:
|
||||
Resources();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
175
src/room.cpp
Normal file
175
src/room.cpp
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include "lighting.h"
|
||||
#include "physics/transform.h"
|
||||
#include "physics/vector.h"
|
||||
#include "portal.h"
|
||||
#include "room.h"
|
||||
#include "room_force.h"
|
||||
#include "room_object.h"
|
||||
#include "sound.h"
|
||||
#include "trail.h"
|
||||
#include "world.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
class RoomStaticModel : public Model {
|
||||
public:
|
||||
RoomStaticModel(const Room& room) : room_(room) {}
|
||||
|
||||
virtual void initialize() {
|
||||
staticObjects_.clear();
|
||||
std::vector<RoomObject*>::const_iterator i;
|
||||
for (i = room_.objects().begin(); i != room_.objects().end(); i++) {
|
||||
RoomObject* o = *i;
|
||||
if (!o->dynamic()) {
|
||||
o->model().initialize();
|
||||
staticObjects_.push_back(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void display() {
|
||||
std::vector<RoomObject*>::const_iterator i;
|
||||
for (i = staticObjects_.begin(); i != staticObjects_.end(); i++) {
|
||||
(*i)->model().display();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const Room& room_;
|
||||
std::vector<RoomObject*> staticObjects_;
|
||||
};
|
||||
|
||||
RoomModel::RoomModel(const Room& room)
|
||||
: room_(room), staticModel_(Models::compile(new RoomStaticModel(room))) {
|
||||
}
|
||||
|
||||
RoomModel::~RoomModel() {
|
||||
delete staticModel_;
|
||||
}
|
||||
|
||||
void RoomModel::initialize() {
|
||||
room_.lighting().initialize();
|
||||
staticModel_->initialize();
|
||||
dynamicObjects_.clear();
|
||||
std::vector<RoomObject*>::const_iterator i;
|
||||
for (i = room_.objects().begin(); i != room_.objects().end(); i++) {
|
||||
RoomObject* o = *i;
|
||||
if (o->dynamic()) {
|
||||
o->model().initialize();
|
||||
dynamicObjects_.push_back(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RoomModel::display() {
|
||||
if (World::world()->paused()) {
|
||||
World::world()->pauseLighting().display();
|
||||
} else {
|
||||
room_.lighting().display();
|
||||
}
|
||||
staticModel_->display();
|
||||
std::vector<RoomObject*>::const_iterator i;
|
||||
for (i = dynamicObjects_.begin(); i != dynamicObjects_.end(); i++) {
|
||||
(*i)->model().display();
|
||||
}
|
||||
}
|
||||
|
||||
Room::Room()
|
||||
: lighting_(&(Lightings::standard())), music_(NULL),
|
||||
cameraBounds_(-Vector::INF(), Vector::INF()),
|
||||
model_(*this), trail_(NULL) {
|
||||
}
|
||||
|
||||
Room::~Room() {
|
||||
std::vector<RoomObject*>::const_iterator io;
|
||||
for (io = objects_.begin(); io != objects_.end(); io++) {
|
||||
delete (*io);
|
||||
}
|
||||
std::vector<RoomOrigin*>::const_iterator ir;
|
||||
for (ir = origins_.begin(); ir != origins_.end(); ir++) {
|
||||
delete (*ir);
|
||||
}
|
||||
std::vector<Portal*>::const_iterator ip;
|
||||
for (ip = portals_.begin(); ip != portals_.end(); ip++) {
|
||||
delete (*ip);
|
||||
}
|
||||
std::vector<RoomForce*>::const_iterator ii;
|
||||
for (ii = forces_.begin(); ii != forces_.end(); ii++) {
|
||||
delete (*ii);
|
||||
}
|
||||
std::vector<Transform*>::const_iterator iz;
|
||||
for (iz = transforms_.begin(); iz != transforms_.end(); iz++) {
|
||||
delete (*iz);
|
||||
}
|
||||
std::vector<Trail*>::const_iterator it;
|
||||
for (it = trails_.begin(); it != trails_.end(); it++) {
|
||||
delete (*it);
|
||||
}
|
||||
if (trail_ != NULL) {
|
||||
delete trail_;
|
||||
}
|
||||
}
|
||||
|
||||
void Room::nextTrail(const Vector& origin) {
|
||||
if (trail_ != NULL) {
|
||||
trails_.push_back(trail_);
|
||||
}
|
||||
trail_ = new Trail(origin);
|
||||
}
|
||||
|
||||
void Room::setMusic(const Sound& music) {
|
||||
music_ = &music;
|
||||
}
|
||||
|
||||
void Room::setLighting(const Lighting& lighting) {
|
||||
lighting_ = &lighting;
|
||||
}
|
||||
|
||||
void Room::setCameraBounds(const Vector& min, const Vector& max) {
|
||||
cameraBounds_ = AxisAlignedBox(min, max);
|
||||
}
|
||||
|
||||
void Room::resetForces() {
|
||||
std::vector<RoomObject*>::const_iterator i;
|
||||
for (i = objects_.begin(); i != objects_.end(); i++) {
|
||||
(*i)->resetForces();
|
||||
}
|
||||
}
|
||||
|
||||
void Room::applyForce(UnaryForce& force) {
|
||||
std::vector<RoomObject*>::const_iterator i;
|
||||
for (i = objects_.begin(); i != objects_.end(); i++) {
|
||||
(*i)->applyForce(force);
|
||||
}
|
||||
}
|
||||
|
||||
void Room::step(const ParticleSimulator& s) {
|
||||
std::vector<Transform*>::const_iterator ir;
|
||||
for (ir = transforms_.begin(); ir != transforms_.end(); ir++) {
|
||||
(*ir)->step();
|
||||
}
|
||||
std::vector<RoomObject*>::const_iterator i;
|
||||
for (i = objects_.begin(); i != objects_.end(); i++) {
|
||||
(*i)->step(s);
|
||||
}
|
||||
}
|
||||
|
||||
void Room::reset() {
|
||||
std::vector<RoomObject*>::const_iterator i;
|
||||
for (i = objects_.begin(); i != objects_.end(); i++) {
|
||||
(*i)->reset();
|
||||
}
|
||||
std::vector<Transform*>::const_iterator iz;
|
||||
for (iz = transforms_.begin(); iz != transforms_.end(); iz++) {
|
||||
(*iz)->reset();
|
||||
}
|
||||
}
|
||||
|
||||
RoomOrigin::RoomOrigin(const Vector& position, const Vector& velocity)
|
||||
: position_(position), velocity_(velocity) {
|
||||
}
|
||||
99
src/room.h
Normal file
99
src/room.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_ROOM_H
|
||||
#define MBOSTOCK_ROOM_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "model.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Lighting;
|
||||
class ParticleSimulator;
|
||||
class Portal;
|
||||
class Room;
|
||||
class RoomForce;
|
||||
class RoomObject;
|
||||
class RoomOrigin;
|
||||
class Shape;
|
||||
class Sound;
|
||||
class Trail;
|
||||
class Transform;
|
||||
class UnaryForce;
|
||||
|
||||
class RoomModel : public Model {
|
||||
public:
|
||||
RoomModel(const Room& room);
|
||||
virtual ~RoomModel();
|
||||
|
||||
virtual void initialize();
|
||||
virtual void display();
|
||||
|
||||
private:
|
||||
const Room& room_;
|
||||
std::vector<RoomObject*> dynamicObjects_;
|
||||
Model* staticModel_;
|
||||
};
|
||||
|
||||
class Room {
|
||||
public:
|
||||
Room();
|
||||
~Room();
|
||||
|
||||
inline void addForce(RoomForce* o) { forces_.push_back(o); }
|
||||
inline void addOrigin(RoomOrigin* o) { origins_.push_back(o); }
|
||||
inline void addObject(RoomObject* o) { objects_.push_back(o); }
|
||||
inline void addPortal(Portal* p) { portals_.push_back(p); }
|
||||
inline void addTransform(Transform* r) { transforms_.push_back(r); }
|
||||
|
||||
inline const std::vector<RoomForce*>& forces() const { return forces_; }
|
||||
inline const std::vector<RoomOrigin*>& origins() const { return origins_; }
|
||||
inline const std::vector<RoomObject*>& objects() const { return objects_; }
|
||||
inline const std::vector<Portal*>& portals() const { return portals_; }
|
||||
inline const std::vector<Trail*>& trails() const { return trails_; }
|
||||
|
||||
inline Model& model() { return model_; }
|
||||
inline Trail& trail() { return *trail_; }
|
||||
inline const Lighting& lighting() const { return *lighting_; }
|
||||
inline const AxisAlignedBox& cameraBounds() const { return cameraBounds_; }
|
||||
inline const Sound* music() const { return music_; }
|
||||
|
||||
void setMusic(const Sound& music);
|
||||
void setLighting(const Lighting& lighting);
|
||||
void setCameraBounds(const Vector& min, const Vector& max);
|
||||
void resetForces();
|
||||
void applyForce(UnaryForce& force);
|
||||
void step(const ParticleSimulator& s);
|
||||
void reset();
|
||||
void nextTrail(const Vector& origin);
|
||||
|
||||
private:
|
||||
std::vector<RoomForce*> forces_;
|
||||
std::vector<RoomOrigin*> origins_;
|
||||
std::vector<RoomObject*> objects_;
|
||||
std::vector<Portal*> portals_;
|
||||
std::vector<Trail*> trails_;
|
||||
std::vector<Transform*> transforms_;
|
||||
const Lighting* lighting_;
|
||||
const Sound* music_;
|
||||
AxisAlignedBox cameraBounds_;
|
||||
RoomModel model_;
|
||||
Trail* trail_;
|
||||
};
|
||||
|
||||
class RoomOrigin {
|
||||
public:
|
||||
RoomOrigin(const Vector& position, const Vector& velocity);
|
||||
|
||||
inline const Vector& position() const { return position_; }
|
||||
inline const Vector& velocity() const { return velocity_; }
|
||||
|
||||
private:
|
||||
Vector position_;
|
||||
Vector velocity_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
20
src/room_force.cpp
Normal file
20
src/room_force.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "room_force.h"
|
||||
#include "physics/particle.h"
|
||||
#include "physics/vector.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
ConstantRoomForce::ConstantRoomForce(const Vector& min, const Vector& max,
|
||||
const Vector& f)
|
||||
: box_(min, max), force_(f) {
|
||||
}
|
||||
|
||||
const Shape& ConstantRoomForce::shape() const {
|
||||
return box_;
|
||||
}
|
||||
|
||||
Vector ConstantRoomForce::force(const Particle& p) {
|
||||
return box_.contains(p.position) ? force_ : Vector::ZERO();
|
||||
}
|
||||
45
src/room_force.h
Normal file
45
src/room_force.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_ROOM_FORCE_H
|
||||
#define MBOSTOCK_ROOM_FORCE_H
|
||||
|
||||
#include "physics/force.h"
|
||||
#include "physics/shape.h"
|
||||
#include "physics/vector.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Particle;
|
||||
class Shape;
|
||||
|
||||
/**
|
||||
* Defines a force that applies to the player when the player intersects a
|
||||
* given shape. For example, this can be used to represent a fan blowing on
|
||||
* the player, which the force of the fan stronger near the fan blades,
|
||||
* falling off to zero outside of the fan's stream.
|
||||
*/
|
||||
class RoomForce : public UnaryForce {
|
||||
public:
|
||||
virtual const Shape& shape() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A simple room force that applies a constant force within an axis-aligned
|
||||
* box. More elaborate forces can be approximated by composing multiple (even
|
||||
* overlapping) constant room forces.
|
||||
*/
|
||||
class ConstantRoomForce : public RoomForce {
|
||||
public:
|
||||
ConstantRoomForce(const Vector& min, const Vector& max, const Vector& f);
|
||||
|
||||
virtual const Shape& shape() const;
|
||||
virtual Vector force(const Particle& p);
|
||||
|
||||
private:
|
||||
AxisAlignedBox box_;
|
||||
Vector force_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
40
src/room_object.cpp
Normal file
40
src/room_object.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "physics/vector.h"
|
||||
#include "room_object.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
bool RoomObject::dynamic() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector RoomObject::velocity(const Vector& x) const {
|
||||
return Vector::ZERO();
|
||||
}
|
||||
|
||||
float RoomObject::slip() const {
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
void RoomObject::resetForces() {
|
||||
}
|
||||
|
||||
void RoomObject::applyForce(UnaryForce& force) {
|
||||
}
|
||||
|
||||
void RoomObject::applyWeight(float w, const Vector& x) {
|
||||
}
|
||||
|
||||
void RoomObject::step(const ParticleSimulator& s) {
|
||||
}
|
||||
|
||||
void RoomObject::constrainInternal() {
|
||||
}
|
||||
|
||||
void RoomObject::reset() {
|
||||
}
|
||||
|
||||
bool DynamicRoomObject::dynamic() const {
|
||||
return true;
|
||||
}
|
||||
36
src/room_object.h
Normal file
36
src/room_object.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_ROOM_OBJECT_H
|
||||
#define MBOSTOCK_ROOM_OBJECT_H
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Model;
|
||||
class ParticleSimulator;
|
||||
class Shape;
|
||||
class UnaryForce;
|
||||
class Vector;
|
||||
|
||||
class RoomObject {
|
||||
public:
|
||||
virtual Model& model() = 0;
|
||||
virtual const Shape& shape() const = 0;
|
||||
virtual bool dynamic() const;
|
||||
virtual Vector velocity(const Vector& x) const;
|
||||
virtual float slip() const;
|
||||
virtual void resetForces();
|
||||
virtual void applyForce(UnaryForce& force);
|
||||
virtual void applyWeight(float w, const Vector& x);
|
||||
virtual void step(const ParticleSimulator& s);
|
||||
virtual void constrainInternal();
|
||||
virtual void reset();
|
||||
};
|
||||
|
||||
class DynamicRoomObject : public RoomObject {
|
||||
public:
|
||||
virtual bool dynamic() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
46
src/rotating.cpp
Normal file
46
src/rotating.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "physics/particle.h"
|
||||
#include "rotating.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
RotatingModel::RotatingModel(Model& m, const Rotation& r)
|
||||
: model_(m), rotation_(r) {
|
||||
}
|
||||
|
||||
void RotatingModel::initialize() {
|
||||
model_.initialize();
|
||||
}
|
||||
|
||||
void RotatingModel::display() {
|
||||
glPushMatrix();
|
||||
glTranslatev(rotation_.origin());
|
||||
glRotatev(rotation_.angle(), rotation_.axis());
|
||||
glTranslatev(-rotation_.origin());
|
||||
model_.display();
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
RotatingRoomObject::RotatingRoomObject(RoomObject* o, const Rotation& r)
|
||||
: TransformingRoomObject(o), rotation_(r),
|
||||
shape_(o->shape(), r), model_(o->model(), r) {
|
||||
}
|
||||
|
||||
Model& RotatingRoomObject::model() {
|
||||
return model_;
|
||||
}
|
||||
|
||||
const Shape& RotatingRoomObject::shape() const {
|
||||
return shape_;
|
||||
}
|
||||
|
||||
Vector RotatingRoomObject::velocity(const Vector& x) const {
|
||||
Vector v = rotation_.velocity(x);
|
||||
if (object_->dynamic()) {
|
||||
v += rotation_.vector(object_->velocity(rotation_.pointInverse(x)));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
57
src/rotating.h
Normal file
57
src/rotating.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_ROTATING_H
|
||||
#define MBOSTOCK_ROTATING_H
|
||||
|
||||
#include "model.h"
|
||||
#include "physics/rotation.h"
|
||||
#include "physics/vector.h"
|
||||
#include "room_force.h"
|
||||
#include "room_object.h"
|
||||
#include "transforming.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class RotatingModel : public Model {
|
||||
public:
|
||||
RotatingModel(Model& m, const Rotation& r);
|
||||
|
||||
virtual void initialize();
|
||||
virtual void display();
|
||||
|
||||
private:
|
||||
Model& model_;
|
||||
const Rotation& rotation_;
|
||||
};
|
||||
|
||||
class RotatingRoomObject : public TransformingRoomObject {
|
||||
public:
|
||||
RotatingRoomObject(RoomObject* o, const Rotation& r);
|
||||
|
||||
virtual Model& model();
|
||||
virtual const Shape& shape() const;
|
||||
virtual Vector velocity(const Vector& x) const;
|
||||
|
||||
private:
|
||||
const Rotation& rotation_;
|
||||
RotatingShape shape_;
|
||||
RotatingModel model_;
|
||||
};
|
||||
|
||||
class RotatingRoomForce : public RoomForce {
|
||||
public:
|
||||
RotatingRoomForce(RoomForce* f, const Rotation& r);
|
||||
virtual ~RotatingRoomForce();
|
||||
|
||||
virtual const Shape& shape() const;
|
||||
virtual Vector force(const Particle& p);
|
||||
|
||||
private:
|
||||
RoomForce* force_;
|
||||
const Rotation& rotation_;
|
||||
RotatingShape shape_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
117
src/seesaw.cpp
Normal file
117
src/seesaw.cpp
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <OpenGL/gl.h>
|
||||
|
||||
#include "material.h"
|
||||
#include "physics/constraint.h"
|
||||
#include "physics/force.h"
|
||||
#include "physics/particle.h"
|
||||
#include "physics/shape.h"
|
||||
#include "physics/vector.h"
|
||||
#include "seesaw.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
static const float centerOffset = .1f;
|
||||
|
||||
Seesaw::Seesaw(const Vector& min, const Vector& max, float mass)
|
||||
: origin_((min + max) / 2.f), size_(max - min), drag_(1.f),
|
||||
model_(box_) {
|
||||
left_.inverseMass = 1.f / (mass * .1f);
|
||||
right_.inverseMass = 1.f / (mass * .1f);
|
||||
center_.inverseMass = 1.f / (mass * .8f);
|
||||
reset();
|
||||
}
|
||||
|
||||
const Shape& Seesaw::shape() const {
|
||||
return box_;
|
||||
}
|
||||
|
||||
Model& Seesaw::model() {
|
||||
return model_;
|
||||
}
|
||||
|
||||
void Seesaw::resetForces() {
|
||||
left_.force = Vector::ZERO();
|
||||
right_.force = Vector::ZERO();
|
||||
center_.force = Vector::ZERO();
|
||||
drag_.apply(left_);
|
||||
drag_.apply(right_);
|
||||
drag_.apply(center_);
|
||||
}
|
||||
|
||||
void Seesaw::applyForce(UnaryForce& force) {
|
||||
force.apply(left_);
|
||||
force.apply(right_);
|
||||
force.apply(center_);
|
||||
}
|
||||
|
||||
void Seesaw::applyWeight(float w, const Vector& x) {
|
||||
if (x.x < origin_.x) {
|
||||
left_.force.y -= w * (2 * (origin_.x - x.x) / size_.x);
|
||||
} else {
|
||||
right_.force.y += w * (2 * (origin_.x - x.x) / size_.x);
|
||||
}
|
||||
}
|
||||
|
||||
void Seesaw::step(const ParticleSimulator& s) {
|
||||
s.step(left_);
|
||||
s.step(right_);
|
||||
s.step(center_);
|
||||
updateBox();
|
||||
}
|
||||
|
||||
void Seesaw::updateBox() {
|
||||
Vector x = (right_.position - left_.position) / size_.x;
|
||||
Vector y = -x.cross(Vector::Z()) * size_.y / 2.f;
|
||||
Vector z = Vector::Z() * size_.z / 2.f;
|
||||
box_ = Box(
|
||||
left_.position - z - y,
|
||||
right_.position - z - y,
|
||||
right_.position + z - y,
|
||||
left_.position + z - y,
|
||||
right_.position - z + y,
|
||||
left_.position - z + y,
|
||||
left_.position + z + y,
|
||||
right_.position + z + y);
|
||||
}
|
||||
|
||||
void Seesaw::constrainInternal() {
|
||||
Vector v = origin_ - (left_.position + right_.position) / 2.f;
|
||||
right_.position += v;
|
||||
left_.position += v;
|
||||
|
||||
Constraints::distance(right_, origin_, size_.x / 2.f);
|
||||
Constraints::distance(left_, origin_, size_.x / 2.f);
|
||||
Constraints::distance(right_, left_, size_.x);
|
||||
Constraints::distance(center_, origin_, centerOffset);
|
||||
|
||||
float d = sqrtf(size_.x * size_.x / 4.f + centerOffset * centerOffset);
|
||||
Constraints::distance(left_, center_, d);
|
||||
Constraints::distance(right_, center_, d);
|
||||
}
|
||||
|
||||
void Seesaw::reset() {
|
||||
left_.position = origin_;
|
||||
left_.position.x -= size_.x / 2.f;
|
||||
right_.position = origin_;
|
||||
right_.position.x += size_.x / 2.f;
|
||||
center_.position = origin_;
|
||||
center_.position.y -= centerOffset;
|
||||
left_.previousPosition = left_.position;
|
||||
right_.previousPosition = right_.position;
|
||||
center_.previousPosition = center_.position;
|
||||
updateBox();
|
||||
}
|
||||
|
||||
void Seesaw::setMaterial(const Material& m) {
|
||||
model_.setMaterial(m);
|
||||
}
|
||||
|
||||
void Seesaw::setTopMaterial(const Material& m) {
|
||||
model_.setTopMaterial(m);
|
||||
}
|
||||
|
||||
float Seesaw::slip() const {
|
||||
return model_.material().slip();
|
||||
}
|
||||
51
src/seesaw.h
Normal file
51
src/seesaw.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_SEESAW_H
|
||||
#define MBOSTOCK_SEESAW_H
|
||||
|
||||
#include "model.h"
|
||||
#include "physics/force.h"
|
||||
#include "physics/particle.h"
|
||||
#include "physics/shape.h"
|
||||
#include "physics/vector.h"
|
||||
#include "room_object.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Material;
|
||||
|
||||
class Seesaw : public DynamicRoomObject {
|
||||
public:
|
||||
Seesaw(const Vector& min, const Vector& max, float mass);
|
||||
|
||||
virtual Model& model();
|
||||
virtual const Shape& shape() const;
|
||||
virtual void resetForces();
|
||||
virtual void applyForce(UnaryForce& force);
|
||||
virtual void applyWeight(float w, const Vector& x);
|
||||
virtual void step(const ParticleSimulator& s);
|
||||
virtual void constrainInternal();
|
||||
virtual void reset();
|
||||
virtual float slip() const;
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
void setTopMaterial(const Material& m);
|
||||
|
||||
private:
|
||||
void updateBox();
|
||||
|
||||
const Vector origin_;
|
||||
const Vector size_;
|
||||
LinearDragForce drag_;
|
||||
|
||||
Box box_;
|
||||
Particle left_;
|
||||
Particle right_;
|
||||
Particle center_;
|
||||
|
||||
BoxModel model_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
92
src/shader.cpp
Normal file
92
src/shader.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "model.h"
|
||||
#include "resource.h"
|
||||
#include "shader.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
static Shader* defaultShader_ = NULL;
|
||||
static Shader* normalShader_ = NULL;
|
||||
static Shader* wireframeShader_ = NULL;
|
||||
|
||||
class DefaultShader : public Shader {
|
||||
public:
|
||||
virtual void display(Model& model) {
|
||||
model.display();
|
||||
}
|
||||
};
|
||||
|
||||
class WireframeShader : public Shader {
|
||||
public:
|
||||
virtual void display(Model& model) {
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
model.display();
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
}
|
||||
};
|
||||
|
||||
Shader* Shaders::defaultShader() {
|
||||
if (defaultShader_ == NULL) {
|
||||
defaultShader_ = new DefaultShader();
|
||||
}
|
||||
return defaultShader_;
|
||||
}
|
||||
|
||||
Shader* Shaders::wireframeShader() {
|
||||
if (wireframeShader_ == NULL) {
|
||||
wireframeShader_ = new WireframeShader();
|
||||
}
|
||||
return wireframeShader_;
|
||||
}
|
||||
|
||||
Shader* Shaders::normalShader() {
|
||||
if (normalShader_ == NULL) {
|
||||
normalShader_ = new GlslShader("normal.vert", "normal.frag");
|
||||
}
|
||||
return normalShader_;
|
||||
}
|
||||
|
||||
GlslShader::GlslShader(const char* vertexPath, const char* fragmentPath)
|
||||
: vertexPath_(vertexPath), fragmentPath_(fragmentPath),
|
||||
program_(GL_NONE) {
|
||||
}
|
||||
|
||||
GlslShader::~GlslShader() {
|
||||
if (program_ != GL_NONE) {
|
||||
glDeleteProgram(program_);
|
||||
}
|
||||
}
|
||||
|
||||
void GlslShader::initialize() {
|
||||
if (program_ != GL_NONE) {
|
||||
glDeleteProgram(program_);
|
||||
}
|
||||
program_ = glCreateProgram();
|
||||
attach(vertexPath_, GL_VERTEX_SHADER);
|
||||
attach(fragmentPath_, GL_FRAGMENT_SHADER);
|
||||
link();
|
||||
}
|
||||
|
||||
void GlslShader::attach(const char* path, GLenum shaderType) {
|
||||
GLuint shader = glCreateShader(shaderType);
|
||||
const char* source = Resources::readFile(path);
|
||||
glShaderSource(shader, 1, &source, NULL);
|
||||
delete[] source;
|
||||
glCompileShader(shader);
|
||||
glAttachShader(program_, shader);
|
||||
}
|
||||
|
||||
void GlslShader::link() {
|
||||
glLinkProgram(program_);
|
||||
}
|
||||
|
||||
void GlslShader::display(Model& model) {
|
||||
glDisable(GL_LIGHTING);
|
||||
glUseProgram(program_);
|
||||
model.display();
|
||||
glUseProgram(GL_NONE);
|
||||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
49
src/shader.h
Normal file
49
src/shader.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef _SHADER_H
|
||||
#define _SHADER_H
|
||||
|
||||
#include <OpenGL/gl.h>
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Model;
|
||||
|
||||
class Shader {
|
||||
public:
|
||||
virtual ~Shader() {};
|
||||
|
||||
virtual void initialize() {}
|
||||
virtual void display(Model& model) = 0;
|
||||
};
|
||||
|
||||
class GlslShader : public Shader {
|
||||
public:
|
||||
GlslShader(const char* vertexPath, const char* fragmentPath);
|
||||
virtual ~GlslShader();
|
||||
|
||||
virtual void initialize();
|
||||
virtual void display(Model& model);
|
||||
|
||||
private:
|
||||
void attach(const char* path, GLenum shaderType);
|
||||
void link();
|
||||
|
||||
const char* vertexPath_;
|
||||
const char* fragmentPath_;
|
||||
GLuint program_;
|
||||
};
|
||||
|
||||
class Shaders {
|
||||
public:
|
||||
static Shader* defaultShader();
|
||||
static Shader* normalShader();
|
||||
static Shader* wireframeShader();
|
||||
|
||||
private:
|
||||
Shaders();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
45
src/simulation.cpp
Normal file
45
src/simulation.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <SDL/sdl.h>
|
||||
|
||||
#include "simulation.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
/*
|
||||
* If for some reason we haven't been able to run the simulation in a long time
|
||||
* (e.g., the computer went to sleep for two hours!), then we don't want to run
|
||||
* the simulation a zillion times to catch up. Instead we just drop some frames
|
||||
* and hope it gets better.
|
||||
*/
|
||||
static const uint32_t maxSkippedMs = 500;
|
||||
|
||||
Simulation::Simulation(uint32_t timeStepMs)
|
||||
: timeStepMs_(timeStepMs), skippedMs_(0), lastTimeMs_(0), paused_(false) {
|
||||
}
|
||||
|
||||
uint32_t Simulation::elapsedMillis() {
|
||||
uint32_t currentTimeMs = SDL_GetTicks();
|
||||
uint32_t differenceMs = currentTimeMs - lastTimeMs_;
|
||||
lastTimeMs_ = currentTimeMs;
|
||||
return differenceMs;
|
||||
}
|
||||
|
||||
void Simulation::togglePaused() {
|
||||
paused_ = !paused_;
|
||||
}
|
||||
|
||||
void Simulation::simulate() {
|
||||
if (paused_ || (lastTimeMs_ == 0)) {
|
||||
lastTimeMs_ = SDL_GetTicks();
|
||||
return;
|
||||
}
|
||||
skippedMs_ += elapsedMillis();
|
||||
if (skippedMs_ > maxSkippedMs) {
|
||||
skippedMs_ = timeStepMs_;
|
||||
}
|
||||
while (skippedMs_ >= timeStepMs_) {
|
||||
step();
|
||||
skippedMs_ -= timeStepMs_;
|
||||
}
|
||||
}
|
||||
34
src/simulation.h
Normal file
34
src/simulation.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_SIMULATION_H
|
||||
#define MBOSTOCK_SIMULATION_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Simulation {
|
||||
public:
|
||||
Simulation(uint32_t timeStepMs);
|
||||
virtual ~Simulation() {}
|
||||
|
||||
virtual void togglePaused();
|
||||
inline bool paused() const { return paused_; }
|
||||
|
||||
void simulate();
|
||||
|
||||
protected:
|
||||
virtual void step() = 0;
|
||||
|
||||
private:
|
||||
uint32_t elapsedMillis();
|
||||
|
||||
uint32_t timeStepMs_;
|
||||
uint32_t skippedMs_;
|
||||
uint32_t lastTimeMs_;
|
||||
bool paused_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
165
src/sound.cpp
Normal file
165
src/sound.cpp
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <SDL/SDL_error.h>
|
||||
#include <SDL_mixer/SDL_mixer.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "resource.h"
|
||||
#include "sound.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class SoundImpl : public Sound {
|
||||
public:
|
||||
SoundImpl(const char* path);
|
||||
|
||||
inline const char* path() const { return path_.c_str(); }
|
||||
|
||||
private:
|
||||
std::string path_;
|
||||
};
|
||||
|
||||
class MusicImpl : public SoundImpl {
|
||||
public:
|
||||
MusicImpl(const char* path);
|
||||
virtual ~MusicImpl();
|
||||
|
||||
virtual void play(int loops) const;
|
||||
virtual void stop() const;
|
||||
|
||||
private:
|
||||
Mix_Music* music_;
|
||||
};
|
||||
|
||||
class ChunkImpl : public SoundImpl {
|
||||
public:
|
||||
ChunkImpl(const char* path);
|
||||
virtual ~ChunkImpl();
|
||||
|
||||
virtual void play(int loops) const;
|
||||
virtual void stop() const;
|
||||
|
||||
private:
|
||||
Mix_Chunk* chunk_;
|
||||
mutable int channel_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
SoundImpl::SoundImpl(const char* path)
|
||||
: path_(path) {
|
||||
}
|
||||
|
||||
MusicImpl::MusicImpl(const char* path)
|
||||
: SoundImpl(path) {
|
||||
std::string fullPath(Resources::path());
|
||||
fullPath.append(path);
|
||||
music_ = Mix_LoadMUS(fullPath.c_str());
|
||||
if (music_ == NULL) {
|
||||
std::cerr << SDL_GetError() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
MusicImpl::~MusicImpl() {
|
||||
if (music_ != NULL) {
|
||||
Mix_FreeMusic(music_);
|
||||
}
|
||||
}
|
||||
|
||||
void MusicImpl::play(int loops) const {
|
||||
stop();
|
||||
if (music_ != NULL) {
|
||||
Mix_PlayMusic(music_, loops);
|
||||
}
|
||||
}
|
||||
|
||||
void MusicImpl::stop() const {
|
||||
Mix_HaltMusic();
|
||||
}
|
||||
|
||||
ChunkImpl::ChunkImpl(const char* path)
|
||||
: SoundImpl(path), channel_(-1) {
|
||||
std::string fullPath(Resources::path());
|
||||
fullPath.append(path);
|
||||
chunk_ = Mix_LoadWAV(fullPath.c_str());
|
||||
if (chunk_ == NULL) {
|
||||
std::cerr << SDL_GetError() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
ChunkImpl::~ChunkImpl() {
|
||||
if (chunk_ != NULL) {
|
||||
Mix_FreeChunk(chunk_);
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkImpl::play(int loops) const {
|
||||
stop();
|
||||
if (chunk_ != NULL) {
|
||||
channel_ = Mix_PlayChannel(-1, chunk_, loops);
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkImpl::stop() const {
|
||||
if (channel_ != -1) {
|
||||
Mix_HaltChannel(channel_);
|
||||
channel_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<SoundImpl*>& sounds() {
|
||||
static std::vector<SoundImpl*> v;
|
||||
return v;
|
||||
}
|
||||
|
||||
void Sounds::initialize() {
|
||||
Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 1024);
|
||||
}
|
||||
|
||||
void Sounds::dispose() {
|
||||
Mix_HaltMusic();
|
||||
Mix_HaltChannel(-1);
|
||||
std::vector<SoundImpl*>::const_iterator i;
|
||||
for (i = sounds().begin(); i != sounds().end(); i++) {
|
||||
delete *i;
|
||||
}
|
||||
Mix_CloseAudio();
|
||||
}
|
||||
|
||||
Sound& Sounds::fromFile(const char* path) {
|
||||
/* First check to see if we've loaded this sound already. */
|
||||
std::vector<SoundImpl*>::const_iterator i;
|
||||
for (i = sounds().begin(); i != sounds().end(); i++) {
|
||||
SoundImpl& s = **i;
|
||||
if (strcmp(s.path(), path) == 0) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/* If not, load the new sound. */
|
||||
std::string spath(path);
|
||||
SoundImpl* sound =
|
||||
((spath.rfind(".mid", std::string::npos, 4) != -1)
|
||||
|| (spath.rfind(".ogg", std::string::npos, 4) != -1)
|
||||
|| (spath.rfind(".mp3", std::string::npos, 4) != -1))
|
||||
? (SoundImpl*) new MusicImpl(path)
|
||||
: (SoundImpl*) new ChunkImpl(path);
|
||||
sounds().push_back(sound);
|
||||
return *sound;
|
||||
}
|
||||
|
||||
/* It appears that Mix_PauseMusic is unsupported for MIDI. :\ */
|
||||
|
||||
void Sounds::pause() {
|
||||
Mix_PauseMusic();
|
||||
Mix_Pause(-1);
|
||||
}
|
||||
|
||||
void Sounds::resume() {
|
||||
Mix_ResumeMusic();
|
||||
Mix_Resume(-1);
|
||||
}
|
||||
29
src/sound.h
Normal file
29
src/sound.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_SOUND_H
|
||||
#define MBOSTOCK_SOUND_H
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Sound {
|
||||
public:
|
||||
virtual ~Sound() {}
|
||||
virtual void play(int loops = 0) const = 0;
|
||||
virtual void stop() const = 0;
|
||||
};
|
||||
|
||||
class Sounds {
|
||||
public:
|
||||
static void initialize();
|
||||
static void pause();
|
||||
static void resume();
|
||||
static void dispose();
|
||||
static Sound& fromFile(const char* path);
|
||||
|
||||
private:
|
||||
Sounds();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
49
src/switch.cpp
Normal file
49
src/switch.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "material.h"
|
||||
#include "physics/transform.h"
|
||||
#include "physics/vector.h"
|
||||
#include "switch.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Switch::Switch(const Vector& min, const Vector& max)
|
||||
: AxisAlignedBlock(min, max), activeMaterial_(NULL) {
|
||||
}
|
||||
|
||||
bool Switch::dynamic() const {
|
||||
return (activeMaterial_ != NULL);
|
||||
}
|
||||
|
||||
void Switch::addTarget(Transform& t) {
|
||||
t.enable(false);
|
||||
targets_.push_back(&t);
|
||||
}
|
||||
|
||||
void Switch::setActiveMaterial(const Material& m) {
|
||||
activeMaterial_ = &m;
|
||||
inactiveMaterial_ = &model_.material();
|
||||
inactiveTopMaterial_ = &model_.topMaterial();
|
||||
}
|
||||
|
||||
void Switch::reset() {
|
||||
if (activeMaterial_ != NULL) {
|
||||
setMaterial(*inactiveMaterial_);
|
||||
setTopMaterial(*inactiveTopMaterial_);
|
||||
}
|
||||
std::vector<Transform*>::const_iterator i;
|
||||
for (i = targets_.begin(); i != targets_.end(); i++) {
|
||||
(*i)->enable(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Switch::applyWeight(float w, const Vector& x) {
|
||||
if (activeMaterial_ != NULL) {
|
||||
setMaterial(*activeMaterial_);
|
||||
setTopMaterial(*activeMaterial_);
|
||||
}
|
||||
std::vector<Transform*>::const_iterator i;
|
||||
for (i = targets_.begin(); i != targets_.end(); i++) {
|
||||
(*i)->enable();
|
||||
}
|
||||
}
|
||||
34
src/switch.h
Normal file
34
src/switch.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_SWITCH_H
|
||||
#define MBOSTOCK_SWITCH_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "block.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Transform;
|
||||
|
||||
class Switch : public AxisAlignedBlock {
|
||||
public:
|
||||
Switch(const Vector& min, const Vector& max);
|
||||
|
||||
virtual bool dynamic() const;
|
||||
virtual void applyWeight(float w, const Vector& x);
|
||||
virtual void reset();
|
||||
|
||||
void addTarget(Transform& t);
|
||||
void setActiveMaterial(const Material& m);
|
||||
|
||||
private:
|
||||
std::vector<Transform*> targets_;
|
||||
const Material* inactiveMaterial_;
|
||||
const Material* inactiveTopMaterial_;
|
||||
const Material* activeMaterial_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
99
src/texture.cpp
Normal file
99
src/texture.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <OpenGL/gl.h>
|
||||
#include <OpenGL/glu.h>
|
||||
#include <SDL/sdl.h>
|
||||
#include <SDL_image/SDL_image.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "resource.h"
|
||||
#include "texture.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
class TextureImpl : public Texture {
|
||||
public:
|
||||
TextureImpl(const char* path);
|
||||
virtual ~TextureImpl();
|
||||
|
||||
void initialize();
|
||||
virtual void bind() const;
|
||||
|
||||
private:
|
||||
GLuint id_;
|
||||
bool alpha_;
|
||||
const std::string path_;
|
||||
};
|
||||
|
||||
TextureImpl::TextureImpl(const char* path)
|
||||
: id_(GL_NONE), alpha_(false), path_(path) {
|
||||
}
|
||||
|
||||
TextureImpl::~TextureImpl() {
|
||||
if (id_ != GL_NONE) {
|
||||
glDeleteTextures(1, &id_);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureImpl::initialize() {
|
||||
if (id_ != GL_NONE) {
|
||||
glDeleteTextures(1, &id_);
|
||||
}
|
||||
|
||||
std::string path(Resources::path());
|
||||
path.append(path_);
|
||||
SDL_Surface* image = IMG_Load(path.c_str());
|
||||
if (image == NULL) {
|
||||
std::cerr << "Couldn't load " << path << ": " << SDL_GetError() << "\n";
|
||||
id_ = GL_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
glGenTextures(1, &id_);
|
||||
glBindTexture(GL_TEXTURE_2D, id_);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
alpha_ = (image->format->BytesPerPixel == 4);
|
||||
GLenum byteOrder = alpha_
|
||||
? ((image->format->Rmask == 0x000000FF) ? GL_RGBA : GL_BGRA)
|
||||
: ((image->format->Rmask == 0x000000FF) ? GL_RGB : GL_BGR);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0,
|
||||
image->format->BytesPerPixel, image->w, image->h, 0,
|
||||
byteOrder, GL_UNSIGNED_BYTE, image->pixels);
|
||||
|
||||
gluBuild2DMipmaps(GL_TEXTURE_2D,
|
||||
image->format->BytesPerPixel, image->w, image->h,
|
||||
byteOrder, GL_UNSIGNED_BYTE, image->pixels);
|
||||
|
||||
SDL_FreeSurface(image);
|
||||
}
|
||||
|
||||
void TextureImpl::bind() const {
|
||||
if (alpha_) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, id_);
|
||||
}
|
||||
|
||||
static std::vector<TextureImpl*>& textures() {
|
||||
static std::vector<TextureImpl*> v;
|
||||
return v;
|
||||
}
|
||||
|
||||
const Texture& Textures::fromFile(const char* path) {
|
||||
TextureImpl* texture = new TextureImpl(path);
|
||||
textures().push_back(texture);
|
||||
return *texture;
|
||||
}
|
||||
|
||||
void Textures::initialize() {
|
||||
std::vector<TextureImpl*>::const_iterator i;
|
||||
for (i = textures().begin(); i != textures().end(); i++) {
|
||||
(*i)->initialize();
|
||||
}
|
||||
}
|
||||
28
src/texture.h
Normal file
28
src/texture.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_TEXTURE_H
|
||||
#define MBOSTOCK_TEXTURE_H
|
||||
|
||||
#include <OpenGL/gl.h>
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Texture {
|
||||
public:
|
||||
virtual ~Texture() {}
|
||||
|
||||
virtual void bind() const = 0;
|
||||
};
|
||||
|
||||
class Textures {
|
||||
public:
|
||||
static const Texture& fromFile(const char* path);
|
||||
static void initialize();
|
||||
|
||||
private:
|
||||
Textures();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
38
src/trail.cpp
Normal file
38
src/trail.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "trail.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
static const float minsq = .1f * .1f;
|
||||
|
||||
Trail::Trail(const Vector& origin) {
|
||||
points_.push_back(origin);
|
||||
}
|
||||
|
||||
bool Trail::add(const Vector& p) {
|
||||
if ((points_.back() - p).squared() >= minsq) {
|
||||
points_.push_back(p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TrailModel::TrailModel(const Trail& trail)
|
||||
: trail_(trail) {
|
||||
}
|
||||
|
||||
void TrailModel::display() {
|
||||
glDisable(GL_LIGHTING);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(.6f, .2f, .3f, .5f);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
std::vector<Vector>::const_iterator i;
|
||||
for (i = trail_.points().begin(); i != trail_.points().end(); i++) {
|
||||
glVertexv(*i);
|
||||
}
|
||||
glEnd();
|
||||
glDisable(GL_BLEND);
|
||||
glEnable(GL_LIGHTING);
|
||||
}
|
||||
36
src/trail.h
Normal file
36
src/trail.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_TRAIL_H
|
||||
#define MBOSTOCK_TRAIL_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "model.h"
|
||||
#include "physics/vector.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Trail {
|
||||
public:
|
||||
Trail(const Vector& origin);
|
||||
|
||||
bool add(const Vector& p);
|
||||
inline const std::vector<Vector>& points() const { return points_; }
|
||||
|
||||
private:
|
||||
std::vector<Vector> points_;
|
||||
};
|
||||
|
||||
class TrailModel : public Model {
|
||||
public:
|
||||
TrailModel(const Trail& trail);
|
||||
|
||||
virtual void display();
|
||||
|
||||
private:
|
||||
const Trail& trail_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
41
src/transforming.cpp
Normal file
41
src/transforming.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "transforming.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
TransformingRoomObject::TransformingRoomObject(RoomObject* o)
|
||||
: object_(o) {
|
||||
}
|
||||
|
||||
TransformingRoomObject::~TransformingRoomObject() {
|
||||
delete object_;
|
||||
}
|
||||
|
||||
void TransformingRoomObject::step(const ParticleSimulator& s) {
|
||||
object_->step(s);
|
||||
}
|
||||
|
||||
float TransformingRoomObject::slip() const {
|
||||
return object_->slip();
|
||||
}
|
||||
|
||||
void TransformingRoomObject::resetForces() {
|
||||
object_->resetForces();
|
||||
}
|
||||
|
||||
void TransformingRoomObject::applyForce(UnaryForce& force) {
|
||||
object_->applyForce(force); // TODO transform the force
|
||||
}
|
||||
|
||||
void TransformingRoomObject::applyWeight(float w, const Vector& x) {
|
||||
object_->applyWeight(w, x); // TODO transform the point
|
||||
}
|
||||
|
||||
void TransformingRoomObject::constrainInternal() {
|
||||
object_->constrainInternal();
|
||||
}
|
||||
|
||||
void TransformingRoomObject::reset() {
|
||||
object_->reset();
|
||||
}
|
||||
29
src/transforming.h
Normal file
29
src/transforming.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_TRANSFORMING_H
|
||||
#define MBOSTOCK_TRANSFORMING_H
|
||||
|
||||
#include "room_object.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class TransformingRoomObject : public DynamicRoomObject {
|
||||
public:
|
||||
TransformingRoomObject(RoomObject* o);
|
||||
virtual ~TransformingRoomObject();
|
||||
|
||||
virtual float slip() const;
|
||||
virtual void resetForces();
|
||||
virtual void applyForce(UnaryForce& force);
|
||||
virtual void applyWeight(float w, const Vector& x);
|
||||
virtual void step(const ParticleSimulator& s);
|
||||
virtual void constrainInternal();
|
||||
virtual void reset();
|
||||
|
||||
protected:
|
||||
RoomObject* object_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
43
src/translating.cpp
Normal file
43
src/translating.cpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "physics/particle.h"
|
||||
#include "physics/vector.h"
|
||||
#include "translating.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
TranslatingModel::TranslatingModel(Model& m, const Translation& t)
|
||||
: model_(m), translation_(t) {
|
||||
}
|
||||
|
||||
void TranslatingModel::initialize() {
|
||||
model_.initialize();
|
||||
}
|
||||
|
||||
void TranslatingModel::display() {
|
||||
glPushMatrix();
|
||||
glTranslatev(translation_.origin());
|
||||
model_.display();
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
TranslatingRoomObject::TranslatingRoomObject(RoomObject* o, const Translation& t)
|
||||
: TransformingRoomObject(o), translation_(t),
|
||||
shape_(o->shape(), t), model_(o->model(), t) {
|
||||
}
|
||||
|
||||
Model& TranslatingRoomObject::model() {
|
||||
return model_;
|
||||
}
|
||||
|
||||
const Shape& TranslatingRoomObject::shape() const {
|
||||
return shape_;
|
||||
}
|
||||
|
||||
Vector TranslatingRoomObject::velocity(const Vector& x) const {
|
||||
Vector v = translation_.velocity();
|
||||
if (object_->dynamic()) {
|
||||
v += object_->velocity(translation_.pointInverse(x));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
43
src/translating.h
Normal file
43
src/translating.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_TRANSLATING_H
|
||||
#define MBOSTOCK_TRANSLATING_H
|
||||
|
||||
#include "model.h"
|
||||
#include "physics/shape.h"
|
||||
#include "physics/translation.h"
|
||||
#include "physics/vector.h"
|
||||
#include "room_object.h"
|
||||
#include "transforming.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class TranslatingModel : public Model {
|
||||
public:
|
||||
TranslatingModel(Model& m, const Translation& t);
|
||||
|
||||
virtual void initialize();
|
||||
virtual void display();
|
||||
|
||||
private:
|
||||
Model& model_;
|
||||
const Translation& translation_;
|
||||
};
|
||||
|
||||
class TranslatingRoomObject : public TransformingRoomObject {
|
||||
public:
|
||||
TranslatingRoomObject(RoomObject* o, const Translation& t);
|
||||
|
||||
virtual Model& model();
|
||||
virtual const Shape& shape() const;
|
||||
virtual Vector velocity(const Vector& x) const;
|
||||
|
||||
private:
|
||||
const Translation& translation_;
|
||||
TranslatingShape shape_;
|
||||
TranslatingModel model_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
35
src/tube.cpp
Normal file
35
src/tube.cpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "material.h"
|
||||
#include "model.h"
|
||||
#include "physics/particle.h"
|
||||
#include "physics/shape.h"
|
||||
#include "physics/vector.h"
|
||||
#include "room.h"
|
||||
#include "tube.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Tube::Tube(const Vector& x0, const Vector& x1, const Vector& y, float radius)
|
||||
: cylinder_(x0, x1, radius), y_(y), model_(cylinder_, y_) {
|
||||
}
|
||||
|
||||
Model& Tube::model() {
|
||||
return model_;
|
||||
}
|
||||
|
||||
const Shape& Tube::shape() const {
|
||||
return cylinder_;
|
||||
}
|
||||
|
||||
void Tube::setMaterial(const Material& m) {
|
||||
model_.setMaterial(m);
|
||||
}
|
||||
|
||||
void Tube::setCapMaterial(const Material& m) {
|
||||
model_.setCapMaterial(m);
|
||||
}
|
||||
|
||||
float Tube::slip() const {
|
||||
return model_.material().slip();
|
||||
}
|
||||
31
src/tube.h
Normal file
31
src/tube.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_TUBE_H
|
||||
#define MBOSTOCK_TUBE_H
|
||||
|
||||
#include "model.h"
|
||||
#include "physics/shape.h"
|
||||
#include "room_object.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Tube : public RoomObject {
|
||||
public:
|
||||
Tube(const Vector& x0, const Vector& x1, const Vector& y, float radius);
|
||||
|
||||
virtual Model& model();
|
||||
virtual const Shape& shape() const;
|
||||
virtual float slip() const;
|
||||
|
||||
void setMaterial(const Material& m);
|
||||
void setCapMaterial(const Material& m);
|
||||
|
||||
private:
|
||||
const Cylinder cylinder_;
|
||||
Vector y_;
|
||||
CylinderModel model_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
60
src/wall.cpp
Normal file
60
src/wall.cpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include "material.h"
|
||||
#include "model.h"
|
||||
#include "physics/shape.h"
|
||||
#include "room.h"
|
||||
#include "wall.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
Wall::Wall(const Vector& x0, const Vector& x1,
|
||||
const Vector& x2, const Vector& x3)
|
||||
: quad_(x0, x1, x2, x3), model_(quad_) {
|
||||
}
|
||||
|
||||
Model& Wall::model() {
|
||||
return model_;
|
||||
}
|
||||
|
||||
const Shape& Wall::shape() const {
|
||||
return quad_;
|
||||
}
|
||||
|
||||
void Wall::setMaterial(const Material& m) {
|
||||
model_.setMaterial(m);
|
||||
}
|
||||
|
||||
float Wall::slip() const {
|
||||
return model_.material().slip();
|
||||
}
|
||||
|
||||
void Wall::setTexCoords(const Vector& t0, const Vector& t1,
|
||||
const Vector& t2, const Vector& t3) {
|
||||
model_.setTexCoords(t0, t1, t2, t3);
|
||||
}
|
||||
|
||||
TriWall::TriWall(const Vector& x0, const Vector& x1, const Vector& x2)
|
||||
: triangle_(x0, x1, x2), model_(triangle_) {
|
||||
}
|
||||
|
||||
Model& TriWall::model() {
|
||||
return model_;
|
||||
}
|
||||
|
||||
const Shape& TriWall::shape() const {
|
||||
return triangle_;
|
||||
}
|
||||
|
||||
void TriWall::setTexCoords(const Vector& t0, const Vector& t1,
|
||||
const Vector& t2) {
|
||||
model_.setTexCoords(t0, t1, t2);
|
||||
}
|
||||
|
||||
void TriWall::setMaterial(const Material& m) {
|
||||
model_.setMaterial(m);
|
||||
}
|
||||
|
||||
float TriWall::slip() const {
|
||||
return model_.material().slip();
|
||||
}
|
||||
51
src/wall.h
Normal file
51
src/wall.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_WALL_H
|
||||
#define MBOSTOCK_WALL_H
|
||||
|
||||
#include "model.h"
|
||||
#include "physics/shape.h"
|
||||
#include "room_object.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Material;
|
||||
|
||||
class Wall : public RoomObject {
|
||||
public:
|
||||
Wall(const Vector& x0, const Vector& x1,
|
||||
const Vector& x2, const Vector& x3);
|
||||
|
||||
virtual Model& model();
|
||||
virtual const Shape& shape() const;
|
||||
virtual float slip() const;
|
||||
|
||||
void setTexCoords(const Vector& t0, const Vector& t1,
|
||||
const Vector& t2, const Vector& t3);
|
||||
void setMaterial(const Material& m);
|
||||
|
||||
private:
|
||||
const Quad quad_;
|
||||
QuadModel model_;
|
||||
};
|
||||
|
||||
class TriWall : public RoomObject {
|
||||
public:
|
||||
TriWall(const Vector& x0, const Vector& x1, const Vector& x2);
|
||||
|
||||
virtual Model& model();
|
||||
virtual const Shape& shape() const;
|
||||
virtual float slip() const;
|
||||
|
||||
void setTexCoords(const Vector& t0, const Vector& t1, const Vector& t2);
|
||||
void setMaterial(const Material& m);
|
||||
|
||||
private:
|
||||
const Triangle triangle_;
|
||||
TriangleModel model_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
236
src/world.cpp
Normal file
236
src/world.cpp
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#include <OpenGL/gl.h>
|
||||
|
||||
#include "material.h"
|
||||
#include "portal.h"
|
||||
#include "room.h"
|
||||
#include "room_force.h"
|
||||
#include "room_object.h"
|
||||
#include "sound.h"
|
||||
#include "trail.h"
|
||||
#include "world.h"
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
/* Fog. */
|
||||
static const float fogDensity = 0.02f;
|
||||
static const float fogColor[] = { 0.f, 0.f, 0.f, 1.f };
|
||||
|
||||
static const float gravity = 10.f;
|
||||
static const float minY = -50.f;
|
||||
|
||||
static World* world_ = NULL;
|
||||
|
||||
WorldModel::WorldModel(World& world)
|
||||
: world_(world) {
|
||||
}
|
||||
|
||||
void WorldModel::initialize() {
|
||||
glFogi(GL_FOG_MODE, GL_EXP2);
|
||||
glFogfv(GL_FOG_COLOR, fogColor);
|
||||
glFogf(GL_FOG_DENSITY, fogDensity);
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_FOG);
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
std::vector<Room*>::const_iterator i;
|
||||
for (i = world_.rooms().begin(); i != world_.rooms().end(); i++) {
|
||||
(*i)->model().initialize();
|
||||
}
|
||||
world_.player().model().initialize();
|
||||
world_.pauseLighting().initialize();
|
||||
}
|
||||
|
||||
void WorldModel::display() {
|
||||
world_.room().model().display();
|
||||
|
||||
if (world_.debug()) {
|
||||
std::vector<Trail*>::const_iterator i;
|
||||
for (i = world_.room().trails().begin(); i != world_.room().trails().end();
|
||||
i++) {
|
||||
TrailModel m(**i);
|
||||
m.initialize();
|
||||
m.display();
|
||||
}
|
||||
TrailModel m(world_.room().trail());
|
||||
m.initialize();
|
||||
m.display();
|
||||
}
|
||||
|
||||
world_.player().model().display();
|
||||
}
|
||||
|
||||
World::World()
|
||||
: Simulation(roundf(ParticleSimulator::timeStep() * 1000.f)),
|
||||
simulator_(1.f), gravity_(gravity), room_(NULL), debug_(false),
|
||||
model_(*this) {
|
||||
world_ = this;
|
||||
pauseLighting_.light(0).setDiffuse(.1f, .1f, .1f, 1.f);
|
||||
pauseLighting_.light(0).setSpecular(.1f, .1f, .1f, 1.f);
|
||||
}
|
||||
|
||||
World::~World() {
|
||||
std::vector<Room*>::const_iterator ir;
|
||||
for (ir = rooms_.begin(); ir != rooms_.end(); ir++) {
|
||||
delete (*ir);
|
||||
}
|
||||
std::vector<Material*>::const_iterator im;
|
||||
for (im = materials_.begin(); im != materials_.end(); im++) {
|
||||
delete (*im);
|
||||
}
|
||||
}
|
||||
|
||||
World* World::world() {
|
||||
return world_;
|
||||
}
|
||||
|
||||
void World::addRoom(Room* r) {
|
||||
if (room_ == NULL) {
|
||||
setRoom(r, r->origins()[0]);
|
||||
}
|
||||
rooms_.push_back(r);
|
||||
}
|
||||
|
||||
void World::addMaterial(Material* m) {
|
||||
materials_.push_back(m);
|
||||
}
|
||||
|
||||
void World::addLighting(Lighting* l) {
|
||||
lightings_.push_back(l);
|
||||
}
|
||||
|
||||
void World::togglePaused() {
|
||||
Simulation::togglePaused();
|
||||
if (paused()) {
|
||||
Sounds::pause();
|
||||
} else {
|
||||
Sounds::resume();
|
||||
}
|
||||
}
|
||||
|
||||
void World::toggleDebug() {
|
||||
debug_ = !debug_;
|
||||
}
|
||||
|
||||
void World::setRoom(Room* r, RoomOrigin* origin) {
|
||||
bool sameMusic = false;
|
||||
if ((room_ != NULL) && (room_->music() != NULL)) {
|
||||
if (room_->music() == r->music()) {
|
||||
sameMusic = true;
|
||||
} else {
|
||||
room_->music()->stop();
|
||||
}
|
||||
}
|
||||
|
||||
room_ = r;
|
||||
room_->nextTrail(origin->position());
|
||||
player_.setOrigin(origin->position());
|
||||
player_.setVelocity(origin->velocity());
|
||||
|
||||
if (!sameMusic && (room_->music() != NULL)) {
|
||||
room_->music()->play(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void World::step() {
|
||||
std::vector<RoomObject*>::const_iterator i;
|
||||
|
||||
/* Reset forces. */
|
||||
player_.resetForces();
|
||||
room_->resetForces();
|
||||
|
||||
/* Apply gravity and contact forces. */
|
||||
player_.applyForce(gravity_);
|
||||
room_->applyForce(gravity_);
|
||||
for (i = contactObjects_.begin(); i != contactObjects_.end(); i++) {
|
||||
(*i)->applyWeight(gravity * player_.mass(), player_.origin());
|
||||
}
|
||||
|
||||
/* Apply localized forces. */
|
||||
std::vector<RoomForce*>::const_iterator f;
|
||||
for (f = room_->forces().begin(); f != room_->forces().end(); f++) {
|
||||
RoomForce& force = **f;
|
||||
if (player_.intersects(force.shape())) {
|
||||
player_.applyForce(force);
|
||||
}
|
||||
}
|
||||
|
||||
/* Run the simulation. */
|
||||
player_.step(simulator_);
|
||||
room_->step(simulator_);
|
||||
|
||||
/* If the player landed on a portal, move to the associated room. */
|
||||
std::vector<Portal*>::const_iterator p;
|
||||
for (p = room_->portals().begin(); p != room_->portals().end(); p++) {
|
||||
Portal& portal = **p;
|
||||
if (portal.contains(player_.origin())) {
|
||||
if (portal.reset()) {
|
||||
room_->reset();
|
||||
}
|
||||
Room* room = rooms_[portal.room()];
|
||||
setRoom(room, room->origins()[portal.origin()]);
|
||||
contactObjects_.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply constraints, detect contacts. */
|
||||
contactObjects_.clear();
|
||||
for (i = room_->objects().begin(); i != room_->objects().end(); i++) {
|
||||
RoomObject& object = **i;
|
||||
object.constrainInternal();
|
||||
if (player_.constrainOutside(object)) {
|
||||
contactObjects_.push_back(&object);
|
||||
}
|
||||
}
|
||||
player_.constrainInternal();
|
||||
room_->trail().add(player_.origin());
|
||||
|
||||
/* Reset if the player falls into a chasm. */
|
||||
if (player_.origin().y < minY) {
|
||||
resetPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
void World::resetPlayer() {
|
||||
RoomOrigin* origin = room_->origins()[0];
|
||||
player_.setOrigin(origin->position());
|
||||
player_.setVelocity(origin->velocity());
|
||||
room_->reset();
|
||||
room_->nextTrail(origin->position());
|
||||
}
|
||||
|
||||
void World::nextRoom() {
|
||||
std::vector<Room*>::const_iterator i;
|
||||
Room* nextRoom = rooms_[0];
|
||||
for (i = rooms_.begin(); i != rooms_.end(); i++) {
|
||||
if ((*i) == room_) {
|
||||
i++;
|
||||
if (i != rooms_.end()) {
|
||||
nextRoom = *i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
setRoom(nextRoom, nextRoom->origins()[0]);
|
||||
}
|
||||
|
||||
void World::previousRoom() {
|
||||
std::vector<Room*>::reverse_iterator i;
|
||||
Room* nextRoom = rooms_[rooms_.size() - 1];
|
||||
for (i = rooms_.rbegin(); i != rooms_.rend(); i++) {
|
||||
if ((*i) == room_) {
|
||||
i++;
|
||||
if (i != rooms_.rend()) {
|
||||
nextRoom = *i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
setRoom(nextRoom, nextRoom->origins()[0]);
|
||||
}
|
||||
80
src/world.h
Normal file
80
src/world.h
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_WORLD_H
|
||||
#define MBOSTOCK_WORLD_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "lighting.h"
|
||||
#include "model.h"
|
||||
#include "physics/force.h"
|
||||
#include "player.h"
|
||||
#include "simulation.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Material;
|
||||
class Room;
|
||||
class RoomObject;
|
||||
class RoomOrigin;
|
||||
class World;
|
||||
|
||||
class WorldModel : public Model {
|
||||
public:
|
||||
WorldModel(World& world);
|
||||
|
||||
virtual void initialize();
|
||||
virtual void display();
|
||||
|
||||
private:
|
||||
World& world_;
|
||||
};
|
||||
|
||||
class World : public Simulation {
|
||||
public:
|
||||
World();
|
||||
virtual ~World();
|
||||
|
||||
static World* world();
|
||||
|
||||
void addRoom(Room* r);
|
||||
void addMaterial(Material* m);
|
||||
void addLighting(Lighting* l);
|
||||
|
||||
inline Player& player() { return player_; }
|
||||
inline const std::vector<Room*>& rooms() const { return rooms_; }
|
||||
inline Room& room() const { return *room_; }
|
||||
inline Model& model() { return model_; }
|
||||
inline const Lighting& pauseLighting() const { return pauseLighting_; }
|
||||
|
||||
void resetPlayer();
|
||||
void nextRoom();
|
||||
void previousRoom();
|
||||
|
||||
virtual void togglePaused();
|
||||
|
||||
void toggleDebug();
|
||||
inline bool debug() const { return debug_; }
|
||||
|
||||
protected:
|
||||
virtual void step();
|
||||
|
||||
private:
|
||||
void setRoom(Room* room, RoomOrigin* origin);
|
||||
|
||||
ParticleSimulator simulator_;
|
||||
GravitationalForce gravity_;
|
||||
Player player_;
|
||||
Lighting pauseLighting_;
|
||||
std::vector<Lighting*> lightings_;
|
||||
std::vector<Material*> materials_;
|
||||
std::vector<Room*> rooms_;
|
||||
std::vector<RoomObject*> contactObjects_;
|
||||
Room* room_;
|
||||
bool debug_;
|
||||
WorldModel model_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
798
src/worlds.cpp
Normal file
798
src/worlds.cpp
Normal file
|
|
@ -0,0 +1,798 @@
|
|||
#include <TinyXML/tinyxml.h>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ball.h"
|
||||
#include "block.h"
|
||||
#include "escalator.h"
|
||||
#include "fan.h"
|
||||
#include "lighting.h"
|
||||
#include "material.h"
|
||||
#include "portal.h"
|
||||
#include "ramp.h"
|
||||
#include "resource.h"
|
||||
#include "room.h"
|
||||
#include "room_force.h"
|
||||
#include "rotating.h"
|
||||
#include "seesaw.h"
|
||||
#include "sound.h"
|
||||
#include "switch.h"
|
||||
#include "translating.h"
|
||||
#include "tube.h"
|
||||
#include "wall.h"
|
||||
#include "world.h"
|
||||
#include "worlds.h"
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class Portal;
|
||||
class RoomForce;
|
||||
class RoomObject;
|
||||
class RoomOrigin;
|
||||
class World;
|
||||
|
||||
class SwitchTargets {
|
||||
public:
|
||||
SwitchTargets(Switch* s);
|
||||
|
||||
inline Switch* svitch() const { return switch_; }
|
||||
|
||||
void addTarget(const char* targetName);
|
||||
inline const std::vector<std::string>& targets() const { return targets_; }
|
||||
|
||||
private:
|
||||
Switch* switch_;
|
||||
std::vector<std::string> targets_;
|
||||
};
|
||||
|
||||
class XmlWorldBuilder {
|
||||
public:
|
||||
XmlWorldBuilder();
|
||||
|
||||
World* parseWorld(const char* path);
|
||||
|
||||
private:
|
||||
static Vector parseVector(TiXmlElement* e, const Vector& d);
|
||||
static Vector parseVector(TiXmlElement* e);
|
||||
static bool parseBool(TiXmlElement* e, const char* name, bool d);
|
||||
static bool parseBool(TiXmlElement* e, const char* name);
|
||||
|
||||
void parseLightings(TiXmlElement* e);
|
||||
void parseLighting(TiXmlElement* e);
|
||||
void parseLightGlobalAmbient(Lighting* l, TiXmlElement* e);
|
||||
void parseLights(Lighting* l, TiXmlElement* e);
|
||||
void parseLight(Light& l, TiXmlElement* e);
|
||||
void parseLightAmbient(Light& l, TiXmlElement* e);
|
||||
void parseLightDiffuse(Light& l, TiXmlElement* e);
|
||||
void parseLightSpecular(Light& l, TiXmlElement* e);
|
||||
void parseLightPosition(Light& l, TiXmlElement* e);
|
||||
void parseLightSpotDirection(Light& l, TiXmlElement* e);
|
||||
void parseLightAttributes(Light& l, TiXmlElement* e);
|
||||
|
||||
void parseMaterials(TiXmlElement* e);
|
||||
void parseMaterial(TiXmlElement* e);
|
||||
void parseMaterialParameter(Material* m, TiXmlElement* e);
|
||||
|
||||
void parseRooms(TiXmlElement* e);
|
||||
void parseRoom(TiXmlElement* e);
|
||||
void parseRoomLighting(Room* r, TiXmlElement* e);
|
||||
void parseRoomMusic(Room* r, TiXmlElement* e);
|
||||
void parseRoomCameraBounds(Room* r, TiXmlElement* e);
|
||||
void parseRoomTopLevelObjects(Room* r, TiXmlElement* e);
|
||||
void parseRoomObjects(Room* r, TiXmlElement* e);
|
||||
|
||||
RoomOrigin* parseRoomOrigin(TiXmlElement* e);
|
||||
Portal* parseRoomPortal(TiXmlElement* e);
|
||||
|
||||
RoomObject* parseRoomObject(TiXmlElement* e);
|
||||
RoomObject* parseRoomBlock(TiXmlElement* e);
|
||||
RoomObject* parseRoomAxisAlignedBlock(TiXmlElement* e);
|
||||
RoomObject* parseRoomOrientedBlock(TiXmlElement* e);
|
||||
RoomObject* parseRoomWall(TiXmlElement* e);
|
||||
RoomObject* parseRoomQuadWall(TiXmlElement* e);
|
||||
RoomObject* parseRoomTriWall(TiXmlElement* e);
|
||||
RoomObject* parseRoomEscalator(TiXmlElement* e);
|
||||
RoomObject* parseRoomSeesaw(TiXmlElement* e);
|
||||
RoomObject* parseRoomRamp(TiXmlElement* e);
|
||||
RoomObject* parseRoomTube(TiXmlElement* e);
|
||||
RoomObject* parseRoomBall(TiXmlElement* e);
|
||||
RoomObject* parseRoomFan(TiXmlElement* e);
|
||||
RoomObject* parseRoomSwitch(TiXmlElement* e);
|
||||
|
||||
RoomForce* parseRoomConstantForce(TiXmlElement* e);
|
||||
|
||||
void parseRotation(Room* r, TiXmlElement* e);
|
||||
void parseTranslation(Room* r, TiXmlElement* e);
|
||||
RoomObject* applyTransforms(RoomObject* o);
|
||||
|
||||
void parseRoomSwitchTargets(Switch* s, TiXmlElement* e);
|
||||
void resolveSwitchTargets();
|
||||
|
||||
int findRoom(const char* name);
|
||||
int findRoomOrigin(const char* name);
|
||||
|
||||
TiXmlDocument document_;
|
||||
World* world_;
|
||||
std::map<std::string, Lighting*> lightings_;
|
||||
std::map<std::string, Material*> materials_;
|
||||
std::map<std::string, Transform*> transforms_;
|
||||
std::list<Transform*> activeTransforms_;
|
||||
std::vector<SwitchTargets*> switchTargets_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using namespace mbostock;
|
||||
|
||||
SwitchTargets::SwitchTargets(Switch* s)
|
||||
: switch_(s) {
|
||||
}
|
||||
|
||||
void SwitchTargets::addTarget(const char* targetName) {
|
||||
targets_.push_back(targetName);
|
||||
}
|
||||
|
||||
XmlWorldBuilder::XmlWorldBuilder()
|
||||
: world_(NULL) {
|
||||
}
|
||||
|
||||
World* XmlWorldBuilder::parseWorld(const char* path) {
|
||||
std::string fullPath(Resources::path());
|
||||
fullPath.append(path);
|
||||
if (!document_.LoadFile(fullPath.c_str())) {
|
||||
std::cerr << "Error loading world \"" << path << "\": ";
|
||||
std::cerr << document_.ErrorDesc() << "\n";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
world_ = new World();
|
||||
TiXmlElement* e = document_.FirstChildElement("world");
|
||||
parseLightings(e);
|
||||
parseMaterials(e);
|
||||
parseRooms(e);
|
||||
resolveSwitchTargets();
|
||||
return world_;
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseLightings(TiXmlElement* e) {
|
||||
for (TiXmlElement* l = e->FirstChildElement("lighting"); l != NULL;
|
||||
l = l->NextSiblingElement("lighting")) {
|
||||
parseLighting(l);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseLighting(TiXmlElement* e) {
|
||||
Lighting* l = new Lighting();
|
||||
lightings_[e->Attribute("name")] = l;
|
||||
parseLightGlobalAmbient(l, e->FirstChildElement("ambient"));
|
||||
parseLights(l, e);
|
||||
world_->addLighting(l);
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseLightGlobalAmbient(Lighting* l, TiXmlElement* e) {
|
||||
if (e != NULL) {
|
||||
float r = 0.f, g = 0.f, b = 0.f, a = 1.f;
|
||||
e->QueryFloatAttribute("r", &r);
|
||||
e->QueryFloatAttribute("g", &g);
|
||||
e->QueryFloatAttribute("b", &b);
|
||||
e->QueryFloatAttribute("a", &a);
|
||||
l->setGlobalAmbient(r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseLights(Lighting* l, TiXmlElement* e) {
|
||||
int i = 0;
|
||||
for (TiXmlElement* le = e->FirstChildElement("light");
|
||||
le != NULL; le = le->NextSiblingElement("light")) {
|
||||
parseLight(l->light(i++), le);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseLight(Light& l, TiXmlElement* e) {
|
||||
parseLightAmbient(l, e->FirstChildElement("ambient"));
|
||||
parseLightSpecular(l, e->FirstChildElement("specular"));
|
||||
parseLightDiffuse(l, e->FirstChildElement("diffuse"));
|
||||
parseLightPosition(l, e->FirstChildElement("position"));
|
||||
parseLightSpotDirection(l, e->FirstChildElement("spot-direction"));
|
||||
parseLightAttributes(l, e);
|
||||
l.enable();
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseLightAmbient(Light& l, TiXmlElement* e) {
|
||||
if (e != NULL) {
|
||||
float r = 0.f, g = 0.f, b = 0.f, a = 1.f;
|
||||
e->QueryFloatAttribute("r", &r);
|
||||
e->QueryFloatAttribute("g", &g);
|
||||
e->QueryFloatAttribute("b", &b);
|
||||
e->QueryFloatAttribute("a", &a);
|
||||
l.setAmbient(r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseLightSpecular(Light& l, TiXmlElement* e) {
|
||||
if (e != NULL) {
|
||||
float r = 0.f, g = 0.f, b = 0.f, a = 1.f;
|
||||
e->QueryFloatAttribute("r", &r);
|
||||
e->QueryFloatAttribute("g", &g);
|
||||
e->QueryFloatAttribute("b", &b);
|
||||
e->QueryFloatAttribute("a", &a);
|
||||
l.setSpecular(r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseLightDiffuse(Light& l, TiXmlElement* e) {
|
||||
if (e != NULL) {
|
||||
float r = 0.f, g = 0.f, b = 0.f, a = 1.f;
|
||||
e->QueryFloatAttribute("r", &r);
|
||||
e->QueryFloatAttribute("g", &g);
|
||||
e->QueryFloatAttribute("b", &b);
|
||||
e->QueryFloatAttribute("a", &a);
|
||||
l.setDiffuse(r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseLightPosition(Light& l, TiXmlElement* e) {
|
||||
if (e != NULL) {
|
||||
float x, y, z, w;
|
||||
e->QueryFloatAttribute("x", &x);
|
||||
e->QueryFloatAttribute("y", &y);
|
||||
e->QueryFloatAttribute("z", &z);
|
||||
e->QueryFloatAttribute("w", &w);
|
||||
l.setPosition(x, y, z, w);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseLightSpotDirection(Light& l, TiXmlElement* e) {
|
||||
if (e != NULL) {
|
||||
float x, y, z;
|
||||
e->QueryFloatAttribute("x", &x);
|
||||
e->QueryFloatAttribute("y", &y);
|
||||
e->QueryFloatAttribute("z", &z);
|
||||
l.setSpotDirection(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseLightAttributes(Light& l, TiXmlElement* e) {
|
||||
if (e->Attribute("spot-exponent")) {
|
||||
float f;
|
||||
e->QueryFloatAttribute("spot-exponent", &f);
|
||||
l.setSpotExponent(f);
|
||||
}
|
||||
if (e->Attribute("constant-attenuation")) {
|
||||
float f;
|
||||
e->QueryFloatAttribute("constant-attenuation", &f);
|
||||
l.setConstantAttenuation(f);
|
||||
}
|
||||
if (e->Attribute("linear-attenuation")) {
|
||||
float f;
|
||||
e->QueryFloatAttribute("linear-attenuation", &f);
|
||||
l.setLinearAttenuation(f);
|
||||
}
|
||||
if (e->Attribute("quadratic-attenuation")) {
|
||||
float f;
|
||||
e->QueryFloatAttribute("quadratic-attenuation", &f);
|
||||
l.setQuadraticAttenuation(f);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseMaterials(TiXmlElement* e) {
|
||||
for (TiXmlElement* m = e->FirstChildElement("material"); m != NULL;
|
||||
m = m->NextSiblingElement("material")) {
|
||||
parseMaterial(m);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseMaterial(TiXmlElement* e) {
|
||||
Material* m = new Material();
|
||||
materials_[e->Attribute("name")] = m;
|
||||
float s = 90.f;
|
||||
e->QueryFloatAttribute("slip-angle", &s);
|
||||
m->setSlipAngle(s);
|
||||
for (TiXmlElement* p = e->FirstChildElement(); p != NULL;
|
||||
p = p->NextSiblingElement()) {
|
||||
parseMaterialParameter(m, p);
|
||||
}
|
||||
world_->addMaterial(m);
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseMaterialParameter(Material* m, TiXmlElement* e) {
|
||||
const std::string& name = e->ValueStr();
|
||||
if (name == "texture") {
|
||||
m->setTexture(e->Attribute("path"));
|
||||
return;
|
||||
}
|
||||
|
||||
float r = 0.f, g = 0.f, b = 0.f, a = 1.f;
|
||||
e->QueryFloatAttribute("r", &r);
|
||||
e->QueryFloatAttribute("g", &g);
|
||||
e->QueryFloatAttribute("b", &b);
|
||||
e->QueryFloatAttribute("a", &a);
|
||||
|
||||
if (name == "ambient") {
|
||||
m->setAmbient(r, g, b, a);
|
||||
} else if (name == "diffuse") {
|
||||
m->setDiffuse(r, g, b, a);
|
||||
} else if (name == "emission") {
|
||||
m->setEmission(r, g, b, a);
|
||||
} else if (name == "specular") {
|
||||
m->setSpecular(r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseRooms(TiXmlElement* e) {
|
||||
for (TiXmlElement* r = e->FirstChildElement("room"); r != NULL;
|
||||
r = r->NextSiblingElement("room")) {
|
||||
parseRoom(r);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseRoom(TiXmlElement* e) {
|
||||
Room* r = new Room();
|
||||
parseRoomLighting(r, e);
|
||||
parseRoomMusic(r, e);
|
||||
parseRoomCameraBounds(r, e);
|
||||
parseRoomTopLevelObjects(r, e);
|
||||
world_->addRoom(r);
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseRoomLighting(Room* r, TiXmlElement* e) {
|
||||
const char* lighting = e->Attribute("lighting");
|
||||
if (lighting != NULL) {
|
||||
r->setLighting(*lightings_[lighting]);
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseRoomMusic(Room* r, TiXmlElement* e) {
|
||||
const char* music = e->Attribute("music");
|
||||
if (music != NULL) {
|
||||
r->setMusic(Sounds::fromFile(music));
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseRoomCameraBounds(Room* r, TiXmlElement* e) {
|
||||
Vector min = parseVector(e->FirstChildElement("camera-min"), -Vector::INF());
|
||||
Vector max = parseVector(e->FirstChildElement("camera-max"), Vector::INF());
|
||||
r->setCameraBounds(min, max);
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseRoomTopLevelObjects(Room* r, TiXmlElement* e) {
|
||||
for (TiXmlElement* o = e->FirstChildElement(); o != NULL;
|
||||
o = o->NextSiblingElement()) {
|
||||
const std::string& name = o->ValueStr();
|
||||
if (name == "origin") {
|
||||
r->addOrigin(parseRoomOrigin(o));
|
||||
} else if (name == "portal") {
|
||||
r->addPortal(parseRoomPortal(o));
|
||||
}
|
||||
}
|
||||
parseRoomObjects(r, e);
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseRoomObjects(Room* r, TiXmlElement* e) {
|
||||
for (TiXmlElement* o = e->FirstChildElement(); o != NULL;
|
||||
o = o->NextSiblingElement()) {
|
||||
const std::string& name = o->ValueStr();
|
||||
if (name == "constant-force") {
|
||||
r->addForce(parseRoomConstantForce(o));
|
||||
} else if (name == "rotation") {
|
||||
parseRotation(r, o);
|
||||
} else if (name == "translation") {
|
||||
parseTranslation(r, o);
|
||||
} else {
|
||||
RoomObject* ro = parseRoomObject(o);
|
||||
if (ro != NULL) { // ignore unknown objects
|
||||
r->addObject(applyTransforms(ro));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseRotation(Room* r, TiXmlElement* e) {
|
||||
float s = 10.f, a = 0.f;
|
||||
e->QueryFloatAttribute("speed", &s);
|
||||
e->QueryFloatAttribute("angle", &a);
|
||||
Rotation* z = new Rotation(
|
||||
parseVector(e->FirstChildElement("origin")),
|
||||
parseVector(e->FirstChildElement("axis")),
|
||||
s, a);
|
||||
const char* name = e->Attribute("name");
|
||||
if (name != NULL) {
|
||||
transforms_[name] = z;
|
||||
}
|
||||
activeTransforms_.push_back(z);
|
||||
parseRoomObjects(r, e);
|
||||
activeTransforms_.pop_back();
|
||||
r->addTransform(z);
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseTranslation(Room* r, TiXmlElement* e) {
|
||||
float s = 10.f, u = 0.f, kd = 0.f;
|
||||
e->QueryFloatAttribute("speed", &s);
|
||||
e->QueryFloatAttribute("start", &u);
|
||||
e->QueryFloatAttribute("dampen", &kd);
|
||||
Translation* z = new Translation(
|
||||
parseVector(e->FirstChildElement("x0")),
|
||||
parseVector(e->FirstChildElement("x1")),
|
||||
s, u, kd);
|
||||
const char* mode = e->Attribute("mode");
|
||||
if (mode != NULL) {
|
||||
std::string modeString = mode;
|
||||
if (modeString == "one-way") {
|
||||
z->setMode(Translation::ONE_WAY);
|
||||
} else if (modeString == "reset") {
|
||||
z->setMode(Translation::RESET);
|
||||
}
|
||||
}
|
||||
const char* name = e->Attribute("name");
|
||||
if (name != NULL) {
|
||||
transforms_[name] = z;
|
||||
}
|
||||
activeTransforms_.push_back(z);
|
||||
parseRoomObjects(r, e);
|
||||
activeTransforms_.pop_back();
|
||||
r->addTransform(z);
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::applyTransforms(RoomObject* o) {
|
||||
std::list<Transform*>::reverse_iterator i;
|
||||
for (i = activeTransforms_.rbegin(); i != activeTransforms_.rend(); i++) {
|
||||
Transform* z = *i;
|
||||
Translation* t = dynamic_cast<Translation*>(z);
|
||||
if (t != NULL) {
|
||||
o = new TranslatingRoomObject(o, *t);
|
||||
continue;
|
||||
}
|
||||
Rotation* r = dynamic_cast<Rotation*>(z);
|
||||
if (r != NULL) {
|
||||
o = new RotatingRoomObject(o, *r);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
RoomOrigin* XmlWorldBuilder::parseRoomOrigin(TiXmlElement* e) {
|
||||
return new RoomOrigin(
|
||||
parseVector(e->FirstChildElement("position")),
|
||||
parseVector(e->FirstChildElement("velocity")));
|
||||
}
|
||||
|
||||
Portal* XmlWorldBuilder::parseRoomPortal(TiXmlElement* e) {
|
||||
return new Portal(
|
||||
parseVector(e->FirstChildElement("min")),
|
||||
parseVector(e->FirstChildElement("max")),
|
||||
findRoom(e->Attribute("origin")),
|
||||
findRoomOrigin(e->Attribute("origin")),
|
||||
parseBool(e, "reset", false));
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomObject(TiXmlElement* e) {
|
||||
const std::string& name = e->ValueStr();
|
||||
if (name == "block") {
|
||||
return parseRoomBlock(e);
|
||||
} else if (name == "wall") {
|
||||
return parseRoomWall(e);
|
||||
} else if (name == "escalator") {
|
||||
return parseRoomEscalator(e);
|
||||
} else if (name == "seesaw") {
|
||||
return parseRoomSeesaw(e);
|
||||
} else if (name == "ramp") {
|
||||
return parseRoomRamp(e);
|
||||
} else if (name == "tube") {
|
||||
return parseRoomTube(e);
|
||||
} else if (name == "ball") {
|
||||
return parseRoomBall(e);
|
||||
} else if (name == "fan") {
|
||||
return parseRoomFan(e);
|
||||
} else if (name == "switch") {
|
||||
return parseRoomSwitch(e);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomBlock(TiXmlElement* e) {
|
||||
return (e->FirstChildElement("min") != NULL)
|
||||
? parseRoomAxisAlignedBlock(e)
|
||||
: parseRoomOrientedBlock(e);
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomAxisAlignedBlock(TiXmlElement* e) {
|
||||
AxisAlignedBlock* b = new AxisAlignedBlock(
|
||||
parseVector(e->FirstChildElement("min")),
|
||||
parseVector(e->FirstChildElement("max")));
|
||||
const char* material = e->Attribute("material");
|
||||
if (material != NULL) {
|
||||
b->setMaterial(*materials_[material]);
|
||||
}
|
||||
const char* topMaterial = e->Attribute("top-material");
|
||||
if (topMaterial != NULL) {
|
||||
b->setTopMaterial(*materials_[topMaterial]);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomOrientedBlock(TiXmlElement* e) {
|
||||
Block* b = new Block(
|
||||
parseVector(e->FirstChildElement("c")),
|
||||
parseVector(e->FirstChildElement("x")),
|
||||
parseVector(e->FirstChildElement("y")),
|
||||
parseVector(e->FirstChildElement("z")));
|
||||
const char* material = e->Attribute("material");
|
||||
if (material != NULL) {
|
||||
b->setMaterial(*materials_[material]);
|
||||
}
|
||||
const char* topMaterial = e->Attribute("top-material");
|
||||
if (topMaterial != NULL) {
|
||||
b->setTopMaterial(*materials_[topMaterial]);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomWall(TiXmlElement* e) {
|
||||
return (e->FirstChildElement("x3") == NULL)
|
||||
? parseRoomTriWall(e)
|
||||
: parseRoomQuadWall(e);
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomQuadWall(TiXmlElement* e) {
|
||||
Wall* w = new Wall(
|
||||
parseVector(e->FirstChildElement("x0")),
|
||||
parseVector(e->FirstChildElement("x1")),
|
||||
parseVector(e->FirstChildElement("x2")),
|
||||
parseVector(e->FirstChildElement("x3")));
|
||||
TiXmlElement* t = e->FirstChildElement("tex-coords");
|
||||
if (t != NULL) {
|
||||
w->setTexCoords(
|
||||
parseVector(t->FirstChildElement("t0")),
|
||||
parseVector(t->FirstChildElement("t1")),
|
||||
parseVector(t->FirstChildElement("t2")),
|
||||
parseVector(t->FirstChildElement("t3")));
|
||||
}
|
||||
const char* material = e->Attribute("material");
|
||||
if (material != NULL) {
|
||||
w->setMaterial(*materials_[material]);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomTriWall(TiXmlElement* e) {
|
||||
TriWall* w = new TriWall(
|
||||
parseVector(e->FirstChildElement("x0")),
|
||||
parseVector(e->FirstChildElement("x1")),
|
||||
parseVector(e->FirstChildElement("x2")));
|
||||
TiXmlElement* t = e->FirstChildElement("tex-coords");
|
||||
if (t != NULL) {
|
||||
w->setTexCoords(
|
||||
parseVector(t->FirstChildElement("t0")),
|
||||
parseVector(t->FirstChildElement("t1")),
|
||||
parseVector(t->FirstChildElement("t2")));
|
||||
}
|
||||
const char* material = e->Attribute("material");
|
||||
if (material != NULL) {
|
||||
w->setMaterial(*materials_[material]);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomEscalator(TiXmlElement* e) {
|
||||
Escalator* o = new Escalator(
|
||||
parseVector(e->FirstChildElement("min")),
|
||||
parseVector(e->FirstChildElement("max")),
|
||||
parseVector(e->FirstChildElement("v")));
|
||||
const char* material = e->Attribute("material");
|
||||
if (material != NULL) {
|
||||
o->setMaterial(*materials_[material]);
|
||||
}
|
||||
const char* topMaterial = e->Attribute("top-material");
|
||||
if (topMaterial != NULL) {
|
||||
o->setTopMaterial(*materials_[topMaterial]);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomSeesaw(TiXmlElement* e) {
|
||||
float mass = 1.f;
|
||||
e->QueryFloatAttribute("mass", &mass);
|
||||
Seesaw* s = new Seesaw(
|
||||
parseVector(e->FirstChildElement("min")),
|
||||
parseVector(e->FirstChildElement("max")),
|
||||
mass);
|
||||
const char* material = e->Attribute("material");
|
||||
if (material != NULL) {
|
||||
s->setMaterial(*materials_[material]);
|
||||
}
|
||||
const char* topMaterial = e->Attribute("top-material");
|
||||
if (topMaterial != NULL) {
|
||||
s->setTopMaterial(*materials_[topMaterial]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomRamp(TiXmlElement* e) {
|
||||
Ramp* r = new Ramp(
|
||||
parseVector(e->FirstChildElement("x0")),
|
||||
parseVector(e->FirstChildElement("x1")),
|
||||
parseVector(e->FirstChildElement("x2")),
|
||||
parseVector(e->FirstChildElement("x3")));
|
||||
const char* material = e->Attribute("material");
|
||||
if (material != NULL) {
|
||||
r->setMaterial(*materials_[material]);
|
||||
}
|
||||
const char* topMaterial = e->Attribute("top-material");
|
||||
if (topMaterial != NULL) {
|
||||
r->setTopMaterial(*materials_[topMaterial]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomTube(TiXmlElement* e) {
|
||||
float r = 1.f;
|
||||
e->QueryFloatAttribute("radius", &r);
|
||||
Tube* t = new Tube(
|
||||
parseVector(e->FirstChildElement("x0")),
|
||||
parseVector(e->FirstChildElement("x1")),
|
||||
parseVector(e->FirstChildElement("y"), Vector::Y()),
|
||||
r);
|
||||
const char* material = e->Attribute("material");
|
||||
if (material != NULL) {
|
||||
t->setMaterial(*materials_[material]);
|
||||
}
|
||||
const char* capMaterial = e->Attribute("cap-material");
|
||||
if (capMaterial != NULL) {
|
||||
t->setCapMaterial(*materials_[capMaterial]);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomBall(TiXmlElement* e) {
|
||||
float r = 1.f;
|
||||
e->QueryFloatAttribute("radius", &r);
|
||||
Ball* b = new Ball(parseVector(e->FirstChildElement("x")), r);
|
||||
const char* material = e->Attribute("material");
|
||||
if (material != NULL) {
|
||||
b->setMaterial(*materials_[material]);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomFan(TiXmlElement* e) {
|
||||
float r, s;
|
||||
e->QueryFloatAttribute("radius", &r);
|
||||
e->QueryFloatAttribute("speed", &s);
|
||||
Fan* f = new Fan(
|
||||
parseVector(e->FirstChildElement("x")),
|
||||
parseVector(e->FirstChildElement("v")),
|
||||
r, s);
|
||||
const char* material = e->Attribute("material");
|
||||
if (material != NULL) {
|
||||
f->setMaterial(*materials_[material]);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
RoomObject* XmlWorldBuilder::parseRoomSwitch(TiXmlElement* e) {
|
||||
Switch* b = new Switch(
|
||||
parseVector(e->FirstChildElement("min")),
|
||||
parseVector(e->FirstChildElement("max")));
|
||||
const char* material = e->Attribute("material");
|
||||
if (material != NULL) {
|
||||
b->setMaterial(*materials_[material]);
|
||||
}
|
||||
const char* topMaterial = e->Attribute("top-material");
|
||||
if (topMaterial != NULL) {
|
||||
b->setTopMaterial(*materials_[topMaterial]);
|
||||
}
|
||||
const char* activeMaterial = e->Attribute("active-material");
|
||||
if (activeMaterial != NULL) {
|
||||
b->setActiveMaterial(*materials_[activeMaterial]);
|
||||
}
|
||||
parseRoomSwitchTargets(b, e);
|
||||
return b;
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::parseRoomSwitchTargets(Switch* s, TiXmlElement* e) {
|
||||
SwitchTargets* q = new SwitchTargets(s);
|
||||
for (TiXmlElement* t = e->FirstChildElement("target"); t != NULL;
|
||||
t = t->NextSiblingElement("target")) {
|
||||
q->addTarget(t->Attribute("name"));
|
||||
}
|
||||
switchTargets_.push_back(q);
|
||||
}
|
||||
|
||||
void XmlWorldBuilder::resolveSwitchTargets() {
|
||||
std::vector<SwitchTargets*>::const_iterator i;
|
||||
for (i = switchTargets_.begin(); i != switchTargets_.end(); i++) {
|
||||
SwitchTargets* t = *i;
|
||||
Switch* s = t->svitch();
|
||||
std::vector<std::string>::const_iterator is;
|
||||
for (is = t->targets().begin(); is != t->targets().end(); is++) {
|
||||
s->addTarget(*transforms_[*is]);
|
||||
}
|
||||
}
|
||||
switchTargets_.clear();
|
||||
}
|
||||
|
||||
RoomForce* XmlWorldBuilder::parseRoomConstantForce(TiXmlElement* e) {
|
||||
return new ConstantRoomForce(
|
||||
parseVector(e->FirstChildElement("min")),
|
||||
parseVector(e->FirstChildElement("max")),
|
||||
parseVector(e->FirstChildElement("force")));
|
||||
}
|
||||
|
||||
int XmlWorldBuilder::findRoom(const char* name) {
|
||||
int i = 0;
|
||||
std::string s = name;
|
||||
std::string roomName = s.substr(0, s.find('.'));
|
||||
TiXmlElement* e = document_.FirstChildElement("world");
|
||||
for (TiXmlElement* r = e->FirstChildElement("room"); r != NULL;
|
||||
r = r->NextSiblingElement("room"), i++) {
|
||||
const char* t = r->Attribute("name");
|
||||
if ((t != NULL) && (roomName == t)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
std::cerr << "Error: could not find room " << name << "\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
int XmlWorldBuilder::findRoomOrigin(const char* name) {
|
||||
std::string s = name;
|
||||
int i = s.find('.');
|
||||
std::string roomName = s.substr(0, i);
|
||||
std::string originName = s.substr(i + 1);
|
||||
TiXmlElement* e = document_.FirstChildElement("world");
|
||||
for (TiXmlElement* r = e->FirstChildElement("room"); r != NULL;
|
||||
r = r->NextSiblingElement("room")) {
|
||||
const char* t = r->Attribute("name");
|
||||
if ((t != NULL) && (roomName == t)) {
|
||||
int j = 0;
|
||||
for (TiXmlElement* o = r->FirstChildElement("origin"); o != NULL;
|
||||
o = o->NextSiblingElement("origin"), j++) {
|
||||
const char* u = o->Attribute("name");
|
||||
if ((u != NULL) && (originName == u)) {
|
||||
return j;
|
||||
}
|
||||
}
|
||||
std::cerr << "Error: could not find origin " << roomName << "."
|
||||
<< originName << "\n";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
std::cerr << "Error: could not find room " << roomName << "\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
Vector XmlWorldBuilder::parseVector(TiXmlElement* e, const Vector& d) {
|
||||
return (e == NULL) ? d : parseVector(e);
|
||||
}
|
||||
|
||||
Vector XmlWorldBuilder::parseVector(TiXmlElement* e) {
|
||||
Vector v;
|
||||
e->QueryFloatAttribute("x", &v.x);
|
||||
e->QueryFloatAttribute("y", &v.y);
|
||||
e->QueryFloatAttribute("z", &v.z);
|
||||
return v;
|
||||
}
|
||||
|
||||
bool XmlWorldBuilder::parseBool(TiXmlElement* e, const char* name) {
|
||||
static const std::string TRUE = "true";
|
||||
return TRUE == e->Attribute(name);
|
||||
}
|
||||
|
||||
bool XmlWorldBuilder::parseBool(TiXmlElement* e, const char* name, bool d) {
|
||||
const char* value = e->Attribute(name);
|
||||
if (value == NULL) {
|
||||
return d;
|
||||
}
|
||||
static const std::string TRUE = "true";
|
||||
return TRUE == value;
|
||||
}
|
||||
|
||||
World* Worlds::fromFile(const char* path) {
|
||||
XmlWorldBuilder builder;
|
||||
return builder.parseWorld(path);
|
||||
}
|
||||
20
src/worlds.h
Normal file
20
src/worlds.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// -*- C++ -*-
|
||||
|
||||
#ifndef MBOSTOCK_WORLDS_H
|
||||
#define MBOSTOCK_WORLDS_H
|
||||
|
||||
namespace mbostock {
|
||||
|
||||
class World;
|
||||
|
||||
class Worlds {
|
||||
public:
|
||||
static World* fromFile(const char* path);
|
||||
|
||||
private:
|
||||
Worlds();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue