migrating to the latest JUCE version

This commit is contained in:
2022-11-04 23:11:33 +01:00
committed by Nikolai Rodionov
parent 4257a0f8ba
commit faf8f18333
2796 changed files with 888518 additions and 784244 deletions

View File

@@ -1,72 +1,72 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
/* This component scrolls a continuous waveform showing the audio that's
coming into whatever audio inputs this object is connected to.
*/
class LiveScrollingAudioDisplay : public AudioVisualiserComponent,
public AudioIODeviceCallback
{
public:
LiveScrollingAudioDisplay() : AudioVisualiserComponent (1)
{
setSamplesPerBlock (256);
setBufferSize (1024);
}
//==============================================================================
void audioDeviceAboutToStart (AudioIODevice*) override
{
clear();
}
void audioDeviceStopped() override
{
clear();
}
void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
float** outputChannelData, int numOutputChannels,
int numberOfSamples) override
{
for (int i = 0; i < numberOfSamples; ++i)
{
float inputSample = 0;
for (int chan = 0; chan < numInputChannels; ++chan)
if (const float* inputChannel = inputChannelData[chan])
inputSample += inputChannel[i]; // find the sum of all the channels
inputSample *= 10.0f; // boost the level to make it more easily visible.
pushSample (&inputSample, 1);
}
// We need to clear the output buffers before returning, in case they're full of junk..
for (int j = 0; j < numOutputChannels; ++j)
if (float* outputChannel = outputChannelData[j])
zeromem (outputChannel, (size_t) numberOfSamples * sizeof (float));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LiveScrollingAudioDisplay)
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
/* This component scrolls a continuous waveform showing the audio that's
coming into whatever audio inputs this object is connected to.
*/
class LiveScrollingAudioDisplay : public AudioVisualiserComponent,
public AudioIODeviceCallback
{
public:
LiveScrollingAudioDisplay() : AudioVisualiserComponent (1)
{
setSamplesPerBlock (256);
setBufferSize (1024);
}
//==============================================================================
void audioDeviceAboutToStart (AudioIODevice*) override
{
clear();
}
void audioDeviceStopped() override
{
clear();
}
void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
float** outputChannelData, int numOutputChannels,
int numberOfSamples) override
{
for (int i = 0; i < numberOfSamples; ++i)
{
float inputSample = 0;
for (int chan = 0; chan < numInputChannels; ++chan)
if (const float* inputChannel = inputChannelData[chan])
inputSample += inputChannel[i]; // find the sum of all the channels
inputSample *= 10.0f; // boost the level to make it more easily visible.
pushSample (&inputSample, 1);
}
// We need to clear the output buffers before returning, in case they're full of junk..
for (int j = 0; j < numOutputChannels; ++j)
if (float* outputChannel = outputChannelData[j])
zeromem (outputChannel, (size_t) numberOfSamples * sizeof (float));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LiveScrollingAudioDisplay)
};

View File

@@ -1,51 +1,51 @@
#ifndef AddPair_H
#define AddPair_H
class AddPair : public Test
{
public:
AddPair()
{
m_world->SetGravity(b2Vec2(0.0f,0.0f));
{
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = 0.1f;
float minX = -6.0f;
float maxX = 0.0f;
float minY = 4.0f;
float maxY = 6.0f;
for (int i = 0; i < 400; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = b2Vec2(RandomFloat(minX,maxX),RandomFloat(minY,maxY));
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 0.01f);
}
}
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-40.0f,5.0f);
bd.bullet = true;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
body->SetLinearVelocity(b2Vec2(150.0f, 0.0f));
}
}
static Test* Create()
{
return new AddPair;
}
};
#endif
#ifndef AddPair_H
#define AddPair_H
class AddPair : public Test
{
public:
AddPair()
{
m_world->SetGravity(b2Vec2(0.0f,0.0f));
{
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = 0.1f;
float minX = -6.0f;
float maxX = 0.0f;
float minY = 4.0f;
float maxY = 6.0f;
for (int i = 0; i < 400; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = b2Vec2(RandomFloat(minX,maxX),RandomFloat(minY,maxY));
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 0.01f);
}
}
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-40.0f,5.0f);
bd.bullet = true;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
body->SetLinearVelocity(b2Vec2(150.0f, 0.0f));
}
}
static Test* Create()
{
return new AddPair;
}
};
#endif

View File

@@ -1,183 +1,183 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef APPLY_FORCE_H
#define APPLY_FORCE_H
class ApplyForce : public Test
{
public:
ApplyForce()
{
m_world->SetGravity(b2Vec2(0.0f, 0.0f));
const float32 k_restitution = 0.4f;
b2Body* ground;
{
b2BodyDef bd;
bd.position.Set(0.0f, 20.0f);
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
b2FixtureDef sd;
sd.shape = &shape;
sd.density = 0.0f;
sd.restitution = k_restitution;
// Left vertical
shape.Set(b2Vec2(-20.0f, -20.0f), b2Vec2(-20.0f, 20.0f));
ground->CreateFixture(&sd);
// Right vertical
shape.Set(b2Vec2(20.0f, -20.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Top horizontal
shape.Set(b2Vec2(-20.0f, 20.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Bottom horizontal
shape.Set(b2Vec2(-20.0f, -20.0f), b2Vec2(20.0f, -20.0f));
ground->CreateFixture(&sd);
}
{
b2Transform xf1;
xf1.q.Set(0.3524f * b2_pi);
xf1.p = xf1.q.GetXAxis();
b2Vec2 vertices[3];
vertices[0] = b2Mul(xf1, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf1, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf1, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly1;
poly1.Set(vertices, 3);
b2FixtureDef sd1;
sd1.shape = &poly1;
sd1.density = 4.0f;
b2Transform xf2;
xf2.q.Set(-0.3524f * b2_pi);
xf2.p = -xf2.q.GetXAxis();
vertices[0] = b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf2, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf2, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly2;
poly2.Set(vertices, 3);
b2FixtureDef sd2;
sd2.shape = &poly2;
sd2.density = 2.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.angularDamping = 5.0f;
bd.linearDamping = 0.1f;
bd.position.Set(0.0f, 2.0f);
bd.angle = b2_pi;
bd.allowSleep = false;
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&sd1);
m_body->CreateFixture(&sd2);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.3f;
for (int i = 0; i < 10; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 5.0f + 1.54f * i);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
float32 gravity = 10.0f;
float32 I = body->GetInertia();
float32 mass = body->GetMass();
// For a circle: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m)
float32 radius = b2Sqrt(2.0f * I / mass);
b2FrictionJointDef jd;
jd.localAnchorA.SetZero();
jd.localAnchorB.SetZero();
jd.bodyA = ground;
jd.bodyB = body;
jd.collideConnected = true;
jd.maxForce = mass * gravity;
jd.maxTorque = mass * radius * gravity;
m_world->CreateJoint(&jd);
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'w':
{
b2Vec2 f = m_body->GetWorldVector(b2Vec2(0.0f, -200.0f));
b2Vec2 p = m_body->GetWorldPoint(b2Vec2(0.0f, 2.0f));
m_body->ApplyForce(f, p);
}
break;
case 'a':
{
m_body->ApplyTorque(50.0f);
}
break;
case 'd':
{
m_body->ApplyTorque(-50.0f);
}
break;
default:
break;
}
}
static Test* Create()
{
return new ApplyForce;
}
b2Body* m_body;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef APPLY_FORCE_H
#define APPLY_FORCE_H
class ApplyForce : public Test
{
public:
ApplyForce()
{
m_world->SetGravity(b2Vec2(0.0f, 0.0f));
const float32 k_restitution = 0.4f;
b2Body* ground;
{
b2BodyDef bd;
bd.position.Set(0.0f, 20.0f);
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
b2FixtureDef sd;
sd.shape = &shape;
sd.density = 0.0f;
sd.restitution = k_restitution;
// Left vertical
shape.Set(b2Vec2(-20.0f, -20.0f), b2Vec2(-20.0f, 20.0f));
ground->CreateFixture(&sd);
// Right vertical
shape.Set(b2Vec2(20.0f, -20.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Top horizontal
shape.Set(b2Vec2(-20.0f, 20.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Bottom horizontal
shape.Set(b2Vec2(-20.0f, -20.0f), b2Vec2(20.0f, -20.0f));
ground->CreateFixture(&sd);
}
{
b2Transform xf1;
xf1.q.Set(0.3524f * b2_pi);
xf1.p = xf1.q.GetXAxis();
b2Vec2 vertices[3];
vertices[0] = b2Mul(xf1, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf1, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf1, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly1;
poly1.Set(vertices, 3);
b2FixtureDef sd1;
sd1.shape = &poly1;
sd1.density = 4.0f;
b2Transform xf2;
xf2.q.Set(-0.3524f * b2_pi);
xf2.p = -xf2.q.GetXAxis();
vertices[0] = b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf2, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf2, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly2;
poly2.Set(vertices, 3);
b2FixtureDef sd2;
sd2.shape = &poly2;
sd2.density = 2.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.angularDamping = 5.0f;
bd.linearDamping = 0.1f;
bd.position.Set(0.0f, 2.0f);
bd.angle = b2_pi;
bd.allowSleep = false;
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&sd1);
m_body->CreateFixture(&sd2);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.3f;
for (int i = 0; i < 10; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 5.0f + 1.54f * i);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
float32 gravity = 10.0f;
float32 I = body->GetInertia();
float32 mass = body->GetMass();
// For a circle: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m)
float32 radius = b2Sqrt(2.0f * I / mass);
b2FrictionJointDef jd;
jd.localAnchorA.SetZero();
jd.localAnchorB.SetZero();
jd.bodyA = ground;
jd.bodyB = body;
jd.collideConnected = true;
jd.maxForce = mass * gravity;
jd.maxTorque = mass * radius * gravity;
m_world->CreateJoint(&jd);
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'w':
{
b2Vec2 f = m_body->GetWorldVector(b2Vec2(0.0f, -200.0f));
b2Vec2 p = m_body->GetWorldPoint(b2Vec2(0.0f, 2.0f));
m_body->ApplyForce(f, p);
}
break;
case 'a':
{
m_body->ApplyTorque(50.0f);
}
break;
case 'd':
{
m_body->ApplyTorque(-50.0f);
}
break;
default:
break;
}
}
static Test* Create()
{
return new ApplyForce;
}
b2Body* m_body;
};
#endif

View File

@@ -1,159 +1,159 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BODY_TYPES_H
#define BODY_TYPES_H
class BodyTypes : public Test
{
public:
BodyTypes()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
b2FixtureDef fd;
fd.shape = &shape;
ground->CreateFixture(&fd);
}
// Define attachment
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 3.0f);
m_attachment = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 2.0f);
m_attachment->CreateFixture(&shape, 2.0f);
}
// Define platform
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-4.0f, 5.0f);
m_platform = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 4.0f, b2Vec2(4.0f, 0.0f), 0.5f * b2_pi);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.6f;
fd.density = 2.0f;
m_platform->CreateFixture(&fd);
b2RevoluteJointDef rjd;
rjd.Initialize(m_attachment, m_platform, b2Vec2(0.0f, 5.0f));
rjd.maxMotorTorque = 50.0f;
rjd.enableMotor = true;
m_world->CreateJoint(&rjd);
b2PrismaticJointDef pjd;
pjd.Initialize(ground, m_platform, b2Vec2(0.0f, 5.0f), b2Vec2(1.0f, 0.0f));
pjd.maxMotorForce = 1000.0f;
pjd.enableMotor = true;
pjd.lowerTranslation = -10.0f;
pjd.upperTranslation = 10.0f;
pjd.enableLimit = true;
m_world->CreateJoint(&pjd);
m_speed = 3.0f;
}
// Create a payload
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 8.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.75f, 0.75f);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.6f;
fd.density = 2.0f;
body->CreateFixture(&fd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'd':
m_platform->SetType(b2_dynamicBody);
break;
case 's':
m_platform->SetType(b2_staticBody);
break;
case 'k':
m_platform->SetType(b2_kinematicBody);
m_platform->SetLinearVelocity(b2Vec2(-m_speed, 0.0f));
m_platform->SetAngularVelocity(0.0f);
break;
}
}
void Step(Settings* settings)
{
// Drive the kinematic body.
if (m_platform->GetType() == b2_kinematicBody)
{
b2Vec2 p = m_platform->GetTransform().p;
b2Vec2 v = m_platform->GetLinearVelocity();
if ((p.x < -10.0f && v.x < 0.0f) ||
(p.x > 10.0f && v.x > 0.0f))
{
v.x = -v.x;
m_platform->SetLinearVelocity(v);
}
}
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (d) dynamic, (s) static, (k) kinematic");
m_textLine += 15;
}
static Test* Create()
{
return new BodyTypes;
}
b2Body* m_attachment;
b2Body* m_platform;
float32 m_speed;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BODY_TYPES_H
#define BODY_TYPES_H
class BodyTypes : public Test
{
public:
BodyTypes()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
b2FixtureDef fd;
fd.shape = &shape;
ground->CreateFixture(&fd);
}
// Define attachment
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 3.0f);
m_attachment = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 2.0f);
m_attachment->CreateFixture(&shape, 2.0f);
}
// Define platform
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-4.0f, 5.0f);
m_platform = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 4.0f, b2Vec2(4.0f, 0.0f), 0.5f * b2_pi);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.6f;
fd.density = 2.0f;
m_platform->CreateFixture(&fd);
b2RevoluteJointDef rjd;
rjd.Initialize(m_attachment, m_platform, b2Vec2(0.0f, 5.0f));
rjd.maxMotorTorque = 50.0f;
rjd.enableMotor = true;
m_world->CreateJoint(&rjd);
b2PrismaticJointDef pjd;
pjd.Initialize(ground, m_platform, b2Vec2(0.0f, 5.0f), b2Vec2(1.0f, 0.0f));
pjd.maxMotorForce = 1000.0f;
pjd.enableMotor = true;
pjd.lowerTranslation = -10.0f;
pjd.upperTranslation = 10.0f;
pjd.enableLimit = true;
m_world->CreateJoint(&pjd);
m_speed = 3.0f;
}
// Create a payload
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 8.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.75f, 0.75f);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.6f;
fd.density = 2.0f;
body->CreateFixture(&fd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'd':
m_platform->SetType(b2_dynamicBody);
break;
case 's':
m_platform->SetType(b2_staticBody);
break;
case 'k':
m_platform->SetType(b2_kinematicBody);
m_platform->SetLinearVelocity(b2Vec2(-m_speed, 0.0f));
m_platform->SetAngularVelocity(0.0f);
break;
}
}
void Step(Settings* settings)
{
// Drive the kinematic body.
if (m_platform->GetType() == b2_kinematicBody)
{
b2Vec2 p = m_platform->GetTransform().p;
b2Vec2 v = m_platform->GetLinearVelocity();
if ((p.x < -10.0f && v.x < 0.0f) ||
(p.x > 10.0f && v.x > 0.0f))
{
v.x = -v.x;
m_platform->SetLinearVelocity(v);
}
}
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (d) dynamic, (s) static, (k) kinematic");
m_textLine += 15;
}
static Test* Create()
{
return new BodyTypes;
}
b2Body* m_attachment;
b2Body* m_platform;
float32 m_speed;
};
#endif

View File

@@ -1,155 +1,155 @@
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BREAKABLE_TEST_H
#define BREAKABLE_TEST_H
// This is used to test sensor shapes.
class Breakable : public Test
{
public:
enum
{
e_count = 7
};
Breakable()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Breakable dynamic body
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 40.0f);
bd.angle = 0.25f * b2_pi;
m_body1 = m_world->CreateBody(&bd);
m_shape1.SetAsBox(0.5f, 0.5f, b2Vec2(-0.5f, 0.0f), 0.0f);
m_piece1 = m_body1->CreateFixture(&m_shape1, 1.0f);
m_shape2.SetAsBox(0.5f, 0.5f, b2Vec2(0.5f, 0.0f), 0.0f);
m_piece2 = m_body1->CreateFixture(&m_shape2, 1.0f);
}
m_break = false;
m_broke = false;
}
void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
{
if (m_broke)
{
// The body already broke.
return;
}
// Should the body break?
int32 count = contact->GetManifold()->pointCount;
float32 maxImpulse = 0.0f;
for (int32 i = 0; i < count; ++i)
{
maxImpulse = b2Max(maxImpulse, impulse->normalImpulses[i]);
}
if (maxImpulse > 40.0f)
{
// Flag the body for breaking.
m_break = true;
}
}
void Break()
{
// Create two bodies from one.
b2Body* body1 = m_piece1->GetBody();
b2Vec2 center = body1->GetWorldCenter();
body1->DestroyFixture(m_piece2);
m_piece2 = NULL;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = body1->GetPosition();
bd.angle = body1->GetAngle();
b2Body* body2 = m_world->CreateBody(&bd);
m_piece2 = body2->CreateFixture(&m_shape2, 1.0f);
// Compute consistent velocities for new bodies based on
// cached velocity.
b2Vec2 center1 = body1->GetWorldCenter();
b2Vec2 center2 = body2->GetWorldCenter();
b2Vec2 velocity1 = m_velocity + b2Cross(m_angularVelocity, center1 - center);
b2Vec2 velocity2 = m_velocity + b2Cross(m_angularVelocity, center2 - center);
body1->SetAngularVelocity(m_angularVelocity);
body1->SetLinearVelocity(velocity1);
body2->SetAngularVelocity(m_angularVelocity);
body2->SetLinearVelocity(velocity2);
}
void Step(Settings* settings)
{
if (m_break)
{
Break();
m_broke = true;
m_break = false;
}
// Cache velocities to improve movement on breakage.
if (m_broke == false)
{
m_velocity = m_body1->GetLinearVelocity();
m_angularVelocity = m_body1->GetAngularVelocity();
}
Test::Step(settings);
}
static Test* Create()
{
return new Breakable;
}
b2Body* m_body1;
b2Vec2 m_velocity;
float32 m_angularVelocity;
b2PolygonShape m_shape1;
b2PolygonShape m_shape2;
b2Fixture* m_piece1;
b2Fixture* m_piece2;
bool m_broke;
bool m_break;
};
#endif
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BREAKABLE_TEST_H
#define BREAKABLE_TEST_H
// This is used to test sensor shapes.
class Breakable : public Test
{
public:
enum
{
e_count = 7
};
Breakable()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Breakable dynamic body
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 40.0f);
bd.angle = 0.25f * b2_pi;
m_body1 = m_world->CreateBody(&bd);
m_shape1.SetAsBox(0.5f, 0.5f, b2Vec2(-0.5f, 0.0f), 0.0f);
m_piece1 = m_body1->CreateFixture(&m_shape1, 1.0f);
m_shape2.SetAsBox(0.5f, 0.5f, b2Vec2(0.5f, 0.0f), 0.0f);
m_piece2 = m_body1->CreateFixture(&m_shape2, 1.0f);
}
m_break = false;
m_broke = false;
}
void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
{
if (m_broke)
{
// The body already broke.
return;
}
// Should the body break?
int32 count = contact->GetManifold()->pointCount;
float32 maxImpulse = 0.0f;
for (int32 i = 0; i < count; ++i)
{
maxImpulse = b2Max(maxImpulse, impulse->normalImpulses[i]);
}
if (maxImpulse > 40.0f)
{
// Flag the body for breaking.
m_break = true;
}
}
void Break()
{
// Create two bodies from one.
b2Body* body1 = m_piece1->GetBody();
b2Vec2 center = body1->GetWorldCenter();
body1->DestroyFixture(m_piece2);
m_piece2 = NULL;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = body1->GetPosition();
bd.angle = body1->GetAngle();
b2Body* body2 = m_world->CreateBody(&bd);
m_piece2 = body2->CreateFixture(&m_shape2, 1.0f);
// Compute consistent velocities for new bodies based on
// cached velocity.
b2Vec2 center1 = body1->GetWorldCenter();
b2Vec2 center2 = body2->GetWorldCenter();
b2Vec2 velocity1 = m_velocity + b2Cross(m_angularVelocity, center1 - center);
b2Vec2 velocity2 = m_velocity + b2Cross(m_angularVelocity, center2 - center);
body1->SetAngularVelocity(m_angularVelocity);
body1->SetLinearVelocity(velocity1);
body2->SetAngularVelocity(m_angularVelocity);
body2->SetLinearVelocity(velocity2);
}
void Step(Settings* settings)
{
if (m_break)
{
Break();
m_broke = true;
m_break = false;
}
// Cache velocities to improve movement on breakage.
if (m_broke == false)
{
m_velocity = m_body1->GetLinearVelocity();
m_angularVelocity = m_body1->GetAngularVelocity();
}
Test::Step(settings);
}
static Test* Create()
{
return new Breakable;
}
b2Body* m_body1;
b2Vec2 m_velocity;
float32 m_angularVelocity;
b2PolygonShape m_shape1;
b2PolygonShape m_shape2;
b2Fixture* m_piece1;
b2Fixture* m_piece2;
bool m_broke;
bool m_break;
};
#endif

View File

@@ -1,125 +1,125 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BRIDGE_H
#define BRIDGE_H
class Bridge : public Test
{
public:
enum
{
e_count = 30
};
Bridge()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
b2RevoluteJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
if (i == (e_count >> 1))
{
m_middle = body;
}
prevBody = body;
}
b2Vec2 anchor(-15.0f + 1.0f * e_count, 5.0f);
jd.Initialize(prevBody, ground, anchor);
m_world->CreateJoint(&jd);
}
for (int32 i = 0; i < 2; ++i)
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
b2PolygonShape shape;
shape.Set(vertices, 3);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-8.0f + 8.0f * i, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
for (int32 i = 0; i < 3; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 6.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
static Test* Create()
{
return new Bridge;
}
b2Body* m_middle;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BRIDGE_H
#define BRIDGE_H
class Bridge : public Test
{
public:
enum
{
e_count = 30
};
Bridge()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
b2RevoluteJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
if (i == (e_count >> 1))
{
m_middle = body;
}
prevBody = body;
}
b2Vec2 anchor(-15.0f + 1.0f * e_count, 5.0f);
jd.Initialize(prevBody, ground, anchor);
m_world->CreateJoint(&jd);
}
for (int32 i = 0; i < 2; ++i)
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
b2PolygonShape shape;
shape.Set(vertices, 3);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-8.0f + 8.0f * i, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
for (int32 i = 0; i < 3; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 6.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
static Test* Create()
{
return new Bridge;
}
b2Body* m_middle;
};
#endif

View File

@@ -1,136 +1,136 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BULLET_TEST_H
#define BULLET_TEST_H
class BulletTest : public Test
{
public:
BulletTest()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
body->CreateFixture(&edge, 0.0f);
b2PolygonShape shape;
shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
body->CreateFixture(&shape, 0.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 4.0f);
b2PolygonShape box;
box.SetAsBox(2.0f, 0.1f);
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&box, 1.0f);
box.SetAsBox(0.25f, 0.25f);
//m_x = RandomFloat(-1.0f, 1.0f);
m_x = 0.20352793f;
bd.position.Set(m_x, 10.0f);
bd.bullet = true;
m_bullet = m_world->CreateBody(&bd);
m_bullet->CreateFixture(&box, 100.0f);
m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
}
}
void Launch()
{
m_body->SetTransform(b2Vec2(0.0f, 4.0f), 0.0f);
m_body->SetLinearVelocity(b2Vec2_zero);
m_body->SetAngularVelocity(0.0f);
m_x = RandomFloat(-1.0f, 1.0f);
m_bullet->SetTransform(b2Vec2(m_x, 10.0f), 0.0f);
m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
m_bullet->SetAngularVelocity(0.0f);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
b2_gjkCalls = 0;
b2_gjkIters = 0;
b2_gjkMaxIters = 0;
b2_toiCalls = 0;
b2_toiIters = 0;
b2_toiMaxIters = 0;
b2_toiRootIters = 0;
b2_toiMaxRootIters = 0;
}
void Step(Settings* settings)
{
Test::Step(settings);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern int32 b2_toiCalls, b2_toiIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
if (b2_gjkCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
b2_gjkCalls, b2_gjkIters / float32(b2_gjkCalls), b2_gjkMaxIters);
m_textLine += 15;
}
if (b2_toiCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave toi iters = %3.1f, max toi iters = %d",
b2_toiCalls, b2_toiIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "ave toi root iters = %3.1f, max toi root iters = %d",
b2_toiRootIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
}
if (m_stepCount % 60 == 0)
{
Launch();
}
}
static Test* Create()
{
return new BulletTest;
}
b2Body* m_body;
b2Body* m_bullet;
float32 m_x;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BULLET_TEST_H
#define BULLET_TEST_H
class BulletTest : public Test
{
public:
BulletTest()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
body->CreateFixture(&edge, 0.0f);
b2PolygonShape shape;
shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
body->CreateFixture(&shape, 0.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 4.0f);
b2PolygonShape box;
box.SetAsBox(2.0f, 0.1f);
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&box, 1.0f);
box.SetAsBox(0.25f, 0.25f);
//m_x = RandomFloat(-1.0f, 1.0f);
m_x = 0.20352793f;
bd.position.Set(m_x, 10.0f);
bd.bullet = true;
m_bullet = m_world->CreateBody(&bd);
m_bullet->CreateFixture(&box, 100.0f);
m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
}
}
void Launch()
{
m_body->SetTransform(b2Vec2(0.0f, 4.0f), 0.0f);
m_body->SetLinearVelocity(b2Vec2_zero);
m_body->SetAngularVelocity(0.0f);
m_x = RandomFloat(-1.0f, 1.0f);
m_bullet->SetTransform(b2Vec2(m_x, 10.0f), 0.0f);
m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
m_bullet->SetAngularVelocity(0.0f);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
b2_gjkCalls = 0;
b2_gjkIters = 0;
b2_gjkMaxIters = 0;
b2_toiCalls = 0;
b2_toiIters = 0;
b2_toiMaxIters = 0;
b2_toiRootIters = 0;
b2_toiMaxRootIters = 0;
}
void Step(Settings* settings)
{
Test::Step(settings);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern int32 b2_toiCalls, b2_toiIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
if (b2_gjkCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
b2_gjkCalls, b2_gjkIters / float32(b2_gjkCalls), b2_gjkMaxIters);
m_textLine += 15;
}
if (b2_toiCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave toi iters = %3.1f, max toi iters = %d",
b2_toiCalls, b2_toiIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "ave toi root iters = %3.1f, max toi root iters = %d",
b2_toiRootIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
}
if (m_stepCount % 60 == 0)
{
Launch();
}
}
static Test* Create()
{
return new BulletTest;
}
b2Body* m_body;
b2Body* m_bullet;
float32 m_x;
};
#endif

View File

@@ -1,211 +1,211 @@
/*
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CANTILEVER_H
#define CANTILEVER_H
// It is difficult to make a cantilever made of links completely rigid with weld joints.
// You will have to use a high number of iterations to make them stiff.
// So why not go ahead and use soft weld joints? They behave like a revolute
// joint with a rotational spring.
class Cantilever : public Test
{
public:
enum
{
e_count = 8
};
Cantilever()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
jd.frequencyHz = 5.0f;
jd.dampingRatio = 0.7f;
b2Body* prevBody = ground;
for (int32 i = 0; i < 3; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.0f + 2.0f * i, 15.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 2.0f * i, 15.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-4.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
if (i > 0)
{
b2Vec2 anchor(-5.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
}
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
jd.frequencyHz = 8.0f;
jd.dampingRatio = 0.7f;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(5.5f + 1.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
if (i > 0)
{
b2Vec2 anchor(5.0f + 1.0f * i, 10.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
}
prevBody = body;
}
}
for (int32 i = 0; i < 2; ++i)
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
b2PolygonShape shape;
shape.Set(vertices, 3);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-8.0f + 8.0f * i, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
for (int32 i = 0; i < 2; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 6.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
static Test* Create()
{
return new Cantilever;
}
b2Body* m_middle;
};
#endif
/*
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CANTILEVER_H
#define CANTILEVER_H
// It is difficult to make a cantilever made of links completely rigid with weld joints.
// You will have to use a high number of iterations to make them stiff.
// So why not go ahead and use soft weld joints? They behave like a revolute
// joint with a rotational spring.
class Cantilever : public Test
{
public:
enum
{
e_count = 8
};
Cantilever()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
jd.frequencyHz = 5.0f;
jd.dampingRatio = 0.7f;
b2Body* prevBody = ground;
for (int32 i = 0; i < 3; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.0f + 2.0f * i, 15.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 2.0f * i, 15.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-4.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
if (i > 0)
{
b2Vec2 anchor(-5.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
}
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
jd.frequencyHz = 8.0f;
jd.dampingRatio = 0.7f;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(5.5f + 1.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
if (i > 0)
{
b2Vec2 anchor(5.0f + 1.0f * i, 10.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
}
prevBody = body;
}
}
for (int32 i = 0; i < 2; ++i)
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
b2PolygonShape shape;
shape.Set(vertices, 3);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-8.0f + 8.0f * i, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
for (int32 i = 0; i < 2; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 6.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
static Test* Create()
{
return new Cantilever;
}
b2Body* m_middle;
};
#endif

View File

@@ -1,286 +1,286 @@
/*
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CAR_H
#define CAR_H
// This is a fun demo that shows off the wheel joint
class Car : public Test
{
public:
Car()
{
m_hz = 4.0f;
m_zeta = 0.7f;
m_speed = 50.0f;
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 0.0f;
fd.friction = 0.6f;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&fd);
float32 hs[10] = {0.25f, 1.0f, 4.0f, 0.0f, 0.0f, -1.0f, -2.0f, -2.0f, -1.25f, 0.0f};
float32 x = 20.0f, y1 = 0.0f, dx = 5.0f;
for (int32 i = 0; i < 10; ++i)
{
float32 y2 = hs[i];
shape.Set(b2Vec2(x, y1), b2Vec2(x + dx, y2));
ground->CreateFixture(&fd);
y1 = y2;
x += dx;
}
for (int32 i = 0; i < 10; ++i)
{
float32 y2 = hs[i];
shape.Set(b2Vec2(x, y1), b2Vec2(x + dx, y2));
ground->CreateFixture(&fd);
y1 = y2;
x += dx;
}
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 80.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 40.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 10.0f, 5.0f));
ground->CreateFixture(&fd);
x += 20.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 40.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x, 20.0f));
ground->CreateFixture(&fd);
}
// Teeter
{
b2BodyDef bd;
bd.position.Set(140.0f, 1.0f);
bd.type = b2_dynamicBody;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape box;
box.SetAsBox(10.0f, 0.25f);
body->CreateFixture(&box, 1.0f);
b2RevoluteJointDef jd;
jd.Initialize(ground, body, body->GetPosition());
jd.lowerAngle = -8.0f * b2_pi / 180.0f;
jd.upperAngle = 8.0f * b2_pi / 180.0f;
jd.enableLimit = true;
m_world->CreateJoint(&jd);
body->ApplyAngularImpulse(100.0f);
}
// Bridge
{
int32 N = 20;
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.6f;
b2RevoluteJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < N; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(161.0f + 2.0f * i, -0.125f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(160.0f + 2.0f * i, -0.125f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
b2Vec2 anchor(160.0f + 2.0f * N, -0.125f);
jd.Initialize(prevBody, ground, anchor);
m_world->CreateJoint(&jd);
}
// Boxes
{
b2PolygonShape box;
box.SetAsBox(0.5f, 0.5f);
b2Body* body = NULL;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(230.0f, 0.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 1.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 2.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 3.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 4.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
}
// Car
{
b2PolygonShape chassis;
b2Vec2 vertices[8];
vertices[0].Set(-1.5f, -0.5f);
vertices[1].Set(1.5f, -0.5f);
vertices[2].Set(1.5f, 0.0f);
vertices[3].Set(0.0f, 0.9f);
vertices[4].Set(-1.15f, 0.9f);
vertices[5].Set(-1.5f, 0.2f);
chassis.Set(vertices, 6);
b2CircleShape circle;
circle.m_radius = 0.4f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 1.0f);
m_car = m_world->CreateBody(&bd);
m_car->CreateFixture(&chassis, 1.0f);
b2FixtureDef fd;
fd.shape = &circle;
fd.density = 1.0f;
fd.friction = 0.9f;
bd.position.Set(-1.0f, 0.35f);
m_wheel1 = m_world->CreateBody(&bd);
m_wheel1->CreateFixture(&fd);
bd.position.Set(1.0f, 0.4f);
m_wheel2 = m_world->CreateBody(&bd);
m_wheel2->CreateFixture(&fd);
b2WheelJointDef jd;
b2Vec2 axis(0.0f, 1.0f);
jd.Initialize(m_car, m_wheel1, m_wheel1->GetPosition(), axis);
jd.motorSpeed = 0.0f;
jd.maxMotorTorque = 20.0f;
jd.enableMotor = true;
jd.frequencyHz = m_hz;
jd.dampingRatio = m_zeta;
m_spring1 = (b2WheelJoint*)m_world->CreateJoint(&jd);
jd.Initialize(m_car, m_wheel2, m_wheel2->GetPosition(), axis);
jd.motorSpeed = 0.0f;
jd.maxMotorTorque = 10.0f;
jd.enableMotor = false;
jd.frequencyHz = m_hz;
jd.dampingRatio = m_zeta;
m_spring2 = (b2WheelJoint*)m_world->CreateJoint(&jd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_spring1->SetMotorSpeed(m_speed);
break;
case 's':
m_spring1->SetMotorSpeed(0.0f);
break;
case 'd':
m_spring1->SetMotorSpeed(-m_speed);
break;
case 'q':
m_hz = b2Max(0.0f, m_hz - 1.0f);
m_spring1->SetSpringFrequencyHz(m_hz);
m_spring2->SetSpringFrequencyHz(m_hz);
break;
case 'e':
m_hz += 1.0f;
m_spring1->SetSpringFrequencyHz(m_hz);
m_spring2->SetSpringFrequencyHz(m_hz);
break;
}
}
void Step(Settings* settings)
{
m_debugDraw.DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, hz down = q, hz up = e");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "frequency = %g hz, damping ratio = %g", m_hz, m_zeta);
m_textLine += 15;
settings->viewCenter.x = m_car->GetPosition().x;
Test::Step(settings);
}
static Test* Create()
{
return new Car;
}
b2Body* m_car;
b2Body* m_wheel1;
b2Body* m_wheel2;
float32 m_hz;
float32 m_zeta;
float32 m_speed;
b2WheelJoint* m_spring1;
b2WheelJoint* m_spring2;
};
#endif
/*
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CAR_H
#define CAR_H
// This is a fun demo that shows off the wheel joint
class Car : public Test
{
public:
Car()
{
m_hz = 4.0f;
m_zeta = 0.7f;
m_speed = 50.0f;
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 0.0f;
fd.friction = 0.6f;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&fd);
float32 hs[10] = {0.25f, 1.0f, 4.0f, 0.0f, 0.0f, -1.0f, -2.0f, -2.0f, -1.25f, 0.0f};
float32 x = 20.0f, y1 = 0.0f, dx = 5.0f;
for (int32 i = 0; i < 10; ++i)
{
float32 y2 = hs[i];
shape.Set(b2Vec2(x, y1), b2Vec2(x + dx, y2));
ground->CreateFixture(&fd);
y1 = y2;
x += dx;
}
for (int32 i = 0; i < 10; ++i)
{
float32 y2 = hs[i];
shape.Set(b2Vec2(x, y1), b2Vec2(x + dx, y2));
ground->CreateFixture(&fd);
y1 = y2;
x += dx;
}
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 80.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 40.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 10.0f, 5.0f));
ground->CreateFixture(&fd);
x += 20.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 40.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x, 20.0f));
ground->CreateFixture(&fd);
}
// Teeter
{
b2BodyDef bd;
bd.position.Set(140.0f, 1.0f);
bd.type = b2_dynamicBody;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape box;
box.SetAsBox(10.0f, 0.25f);
body->CreateFixture(&box, 1.0f);
b2RevoluteJointDef jd;
jd.Initialize(ground, body, body->GetPosition());
jd.lowerAngle = -8.0f * b2_pi / 180.0f;
jd.upperAngle = 8.0f * b2_pi / 180.0f;
jd.enableLimit = true;
m_world->CreateJoint(&jd);
body->ApplyAngularImpulse(100.0f);
}
// Bridge
{
int32 N = 20;
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.6f;
b2RevoluteJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < N; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(161.0f + 2.0f * i, -0.125f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(160.0f + 2.0f * i, -0.125f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
b2Vec2 anchor(160.0f + 2.0f * N, -0.125f);
jd.Initialize(prevBody, ground, anchor);
m_world->CreateJoint(&jd);
}
// Boxes
{
b2PolygonShape box;
box.SetAsBox(0.5f, 0.5f);
b2Body* body = NULL;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(230.0f, 0.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 1.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 2.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 3.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 4.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
}
// Car
{
b2PolygonShape chassis;
b2Vec2 vertices[8];
vertices[0].Set(-1.5f, -0.5f);
vertices[1].Set(1.5f, -0.5f);
vertices[2].Set(1.5f, 0.0f);
vertices[3].Set(0.0f, 0.9f);
vertices[4].Set(-1.15f, 0.9f);
vertices[5].Set(-1.5f, 0.2f);
chassis.Set(vertices, 6);
b2CircleShape circle;
circle.m_radius = 0.4f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 1.0f);
m_car = m_world->CreateBody(&bd);
m_car->CreateFixture(&chassis, 1.0f);
b2FixtureDef fd;
fd.shape = &circle;
fd.density = 1.0f;
fd.friction = 0.9f;
bd.position.Set(-1.0f, 0.35f);
m_wheel1 = m_world->CreateBody(&bd);
m_wheel1->CreateFixture(&fd);
bd.position.Set(1.0f, 0.4f);
m_wheel2 = m_world->CreateBody(&bd);
m_wheel2->CreateFixture(&fd);
b2WheelJointDef jd;
b2Vec2 axis(0.0f, 1.0f);
jd.Initialize(m_car, m_wheel1, m_wheel1->GetPosition(), axis);
jd.motorSpeed = 0.0f;
jd.maxMotorTorque = 20.0f;
jd.enableMotor = true;
jd.frequencyHz = m_hz;
jd.dampingRatio = m_zeta;
m_spring1 = (b2WheelJoint*)m_world->CreateJoint(&jd);
jd.Initialize(m_car, m_wheel2, m_wheel2->GetPosition(), axis);
jd.motorSpeed = 0.0f;
jd.maxMotorTorque = 10.0f;
jd.enableMotor = false;
jd.frequencyHz = m_hz;
jd.dampingRatio = m_zeta;
m_spring2 = (b2WheelJoint*)m_world->CreateJoint(&jd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_spring1->SetMotorSpeed(m_speed);
break;
case 's':
m_spring1->SetMotorSpeed(0.0f);
break;
case 'd':
m_spring1->SetMotorSpeed(-m_speed);
break;
case 'q':
m_hz = b2Max(0.0f, m_hz - 1.0f);
m_spring1->SetSpringFrequencyHz(m_hz);
m_spring2->SetSpringFrequencyHz(m_hz);
break;
case 'e':
m_hz += 1.0f;
m_spring1->SetSpringFrequencyHz(m_hz);
m_spring2->SetSpringFrequencyHz(m_hz);
break;
}
}
void Step(Settings* settings)
{
m_debugDraw.DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, hz down = q, hz up = e");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "frequency = %g hz, damping ratio = %g", m_hz, m_zeta);
m_textLine += 15;
settings->viewCenter.x = m_car->GetPosition().x;
Test::Step(settings);
}
static Test* Create()
{
return new Car;
}
b2Body* m_car;
b2Body* m_wheel1;
b2Body* m_wheel2;
float32 m_hz;
float32 m_zeta;
float32 m_speed;
b2WheelJoint* m_spring1;
b2WheelJoint* m_spring2;
};
#endif

View File

@@ -1,74 +1,74 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CHAIN_H
#define CHAIN_H
class Chain : public Test
{
public:
Chain()
{
b2Body* ground = {};
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.6f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
b2RevoluteJointDef jd;
jd.collideConnected = false;
const float32 y = 25.0f;
b2Body* prevBody = ground;
for (int i = 0; i < 30; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.5f + i, y);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(float32(i), y);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
}
static Test* Create()
{
return new Chain;
}
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CHAIN_H
#define CHAIN_H
class Chain : public Test
{
public:
Chain()
{
b2Body* ground = {};
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.6f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
b2RevoluteJointDef jd;
jd.collideConnected = false;
const float32 y = 25.0f;
b2Body* prevBody = ground;
for (int i = 0; i < 30; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.5f + i, y);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(float32(i), y);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
}
static Test* Create()
{
return new Chain;
}
};
#endif

View File

@@ -1,253 +1,253 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CHARACTER_COLLISION_H
#define CHARACTER_COLLISION_H
/// This is a test of typical character collision scenarios. This does not
/// show how you should implement a character in your application.
/// Instead this is used to test smooth collision on edge chains.
class CharacterCollision : public Test
{
public:
CharacterCollision()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Collinear edges with no adjacency information.
// This shows the problematic case where a box shape can hit
// an internal vertex.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-8.0f, 1.0f), b2Vec2(-6.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(-6.0f, 1.0f), b2Vec2(-4.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(-4.0f, 1.0f), b2Vec2(-2.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Chain shape
{
b2BodyDef bd;
bd.angle = 0.25f * b2_pi;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[4];
vs[0].Set(5.0f, 7.0f);
vs[1].Set(6.0f, 8.0f);
vs[2].Set(7.0f, 8.0f);
vs[3].Set(8.0f, 7.0f);
b2ChainShape shape;
shape.CreateChain(vs, 4);
ground->CreateFixture(&shape, 0.0f);
}
// Square tiles. This shows that adjacency shapes may
// have non-smooth collision. There is no solution
// to this problem.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(1.0f, 1.0f, b2Vec2(4.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
shape.SetAsBox(1.0f, 1.0f, b2Vec2(6.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
shape.SetAsBox(1.0f, 1.0f, b2Vec2(8.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
}
// Square made from an edge loop. Collision should be smooth.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[4];
vs[0].Set(-1.0f, 3.0f);
vs[1].Set(1.0f, 3.0f);
vs[2].Set(1.0f, 5.0f);
vs[3].Set(-1.0f, 5.0f);
b2ChainShape shape;
shape.CreateLoop(vs, 4);
ground->CreateFixture(&shape, 0.0f);
}
// Edge loop. Collision should be smooth.
{
b2BodyDef bd;
bd.position.Set(-10.0f, 4.0f);
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[10];
vs[0].Set(0.0f, 0.0f);
vs[1].Set(6.0f, 0.0f);
vs[2].Set(6.0f, 2.0f);
vs[3].Set(4.0f, 1.0f);
vs[4].Set(2.0f, 2.0f);
vs[5].Set(0.0f, 2.0f);
vs[6].Set(-2.0f, 2.0f);
vs[7].Set(-4.0f, 3.0f);
vs[8].Set(-6.0f, 2.0f);
vs[9].Set(-6.0f, 0.0f);
b2ChainShape shape;
shape.CreateLoop(vs, 10);
ground->CreateFixture(&shape, 0.0f);
}
// Square character 1
{
b2BodyDef bd;
bd.position.Set(-3.0f, 8.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Square character 2
{
b2BodyDef bd;
bd.position.Set(-5.0f, 5.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.25f, 0.25f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Hexagon character
{
b2BodyDef bd;
bd.position.Set(-5.0f, 8.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
float32 angle = 0.0f;
float32 delta = b2_pi / 3.0f;
b2Vec2 vertices[6];
for (int32 i = 0; i < 6; ++i)
{
vertices[i].Set(0.5f * cosf(angle), 0.5f * sinf(angle));
angle += delta;
}
b2PolygonShape shape;
shape.Set(vertices, 6);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(3.0f, 5.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(-7.0f, 6.0f);
bd.type = b2_dynamicBody;
bd.allowSleep = false;
m_character = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.25f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 1.0f;
m_character->CreateFixture(&fd);
}
}
void Step(Settings* settings)
{
b2Vec2 v = m_character->GetLinearVelocity();
v.x = -5.0f;
m_character->SetLinearVelocity(v);
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "This tests various character collision shapes.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Limitation: square and hexagon can snag on aligned boxes.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Feature: edge chains have smooth collision inside and out.");
m_textLine += 15;
}
static Test* Create()
{
return new CharacterCollision;
}
b2Body* m_character;
};
#endif
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CHARACTER_COLLISION_H
#define CHARACTER_COLLISION_H
/// This is a test of typical character collision scenarios. This does not
/// show how you should implement a character in your application.
/// Instead this is used to test smooth collision on edge chains.
class CharacterCollision : public Test
{
public:
CharacterCollision()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Collinear edges with no adjacency information.
// This shows the problematic case where a box shape can hit
// an internal vertex.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-8.0f, 1.0f), b2Vec2(-6.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(-6.0f, 1.0f), b2Vec2(-4.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(-4.0f, 1.0f), b2Vec2(-2.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Chain shape
{
b2BodyDef bd;
bd.angle = 0.25f * b2_pi;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[4];
vs[0].Set(5.0f, 7.0f);
vs[1].Set(6.0f, 8.0f);
vs[2].Set(7.0f, 8.0f);
vs[3].Set(8.0f, 7.0f);
b2ChainShape shape;
shape.CreateChain(vs, 4);
ground->CreateFixture(&shape, 0.0f);
}
// Square tiles. This shows that adjacency shapes may
// have non-smooth collision. There is no solution
// to this problem.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(1.0f, 1.0f, b2Vec2(4.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
shape.SetAsBox(1.0f, 1.0f, b2Vec2(6.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
shape.SetAsBox(1.0f, 1.0f, b2Vec2(8.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
}
// Square made from an edge loop. Collision should be smooth.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[4];
vs[0].Set(-1.0f, 3.0f);
vs[1].Set(1.0f, 3.0f);
vs[2].Set(1.0f, 5.0f);
vs[3].Set(-1.0f, 5.0f);
b2ChainShape shape;
shape.CreateLoop(vs, 4);
ground->CreateFixture(&shape, 0.0f);
}
// Edge loop. Collision should be smooth.
{
b2BodyDef bd;
bd.position.Set(-10.0f, 4.0f);
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[10];
vs[0].Set(0.0f, 0.0f);
vs[1].Set(6.0f, 0.0f);
vs[2].Set(6.0f, 2.0f);
vs[3].Set(4.0f, 1.0f);
vs[4].Set(2.0f, 2.0f);
vs[5].Set(0.0f, 2.0f);
vs[6].Set(-2.0f, 2.0f);
vs[7].Set(-4.0f, 3.0f);
vs[8].Set(-6.0f, 2.0f);
vs[9].Set(-6.0f, 0.0f);
b2ChainShape shape;
shape.CreateLoop(vs, 10);
ground->CreateFixture(&shape, 0.0f);
}
// Square character 1
{
b2BodyDef bd;
bd.position.Set(-3.0f, 8.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Square character 2
{
b2BodyDef bd;
bd.position.Set(-5.0f, 5.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.25f, 0.25f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Hexagon character
{
b2BodyDef bd;
bd.position.Set(-5.0f, 8.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
float32 angle = 0.0f;
float32 delta = b2_pi / 3.0f;
b2Vec2 vertices[6];
for (int32 i = 0; i < 6; ++i)
{
vertices[i].Set(0.5f * cosf(angle), 0.5f * sinf(angle));
angle += delta;
}
b2PolygonShape shape;
shape.Set(vertices, 6);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(3.0f, 5.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(-7.0f, 6.0f);
bd.type = b2_dynamicBody;
bd.allowSleep = false;
m_character = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.25f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 1.0f;
m_character->CreateFixture(&fd);
}
}
void Step(Settings* settings)
{
b2Vec2 v = m_character->GetLinearVelocity();
v.x = -5.0f;
m_character->SetLinearVelocity(v);
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "This tests various character collision shapes.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Limitation: square and hexagon can snag on aligned boxes.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Feature: edge chains have smooth collision inside and out.");
m_textLine += 15;
}
static Test* Create()
{
return new CharacterCollision;
}
b2Body* m_character;
};
#endif

View File

@@ -1,176 +1,176 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COLLISION_FILTERING_H
#define COLLISION_FILTERING_H
// This is a test of collision filtering.
// There is a triangle, a box, and a circle.
// There are 6 shapes. 3 large and 3 small.
// The 3 small ones always collide.
// The 3 large ones never collide.
// The boxes don't collide with triangles (except if both are small).
const int16 k_smallGroup = 1;
const int16 k_largeGroup = -1;
const uint16 k_defaultCategory = 0x0001;
const uint16 k_triangleCategory = 0x0002;
const uint16 k_boxCategory = 0x0004;
const uint16 k_circleCategory = 0x0008;
const uint16 k_triangleMask = 0xFFFF;
const uint16 k_boxMask = 0xFFFF ^ k_triangleCategory;
const uint16 k_circleMask = 0xFFFF;
class CollisionFiltering : public Test
{
public:
CollisionFiltering()
{
// Ground body
{
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2FixtureDef sd;
sd.shape = &shape;
sd.friction = 0.3f;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&sd);
}
// Small triangle
b2Vec2 vertices[3];
vertices[0].Set(-1.0f, 0.0f);
vertices[1].Set(1.0f, 0.0f);
vertices[2].Set(0.0f, 2.0f);
b2PolygonShape polygon;
polygon.Set(vertices, 3);
b2FixtureDef triangleShapeDef;
triangleShapeDef.shape = &polygon;
triangleShapeDef.density = 1.0f;
triangleShapeDef.filter.groupIndex = k_smallGroup;
triangleShapeDef.filter.categoryBits = k_triangleCategory;
triangleShapeDef.filter.maskBits = k_triangleMask;
b2BodyDef triangleBodyDef;
triangleBodyDef.type = b2_dynamicBody;
triangleBodyDef.position.Set(-5.0f, 2.0f);
b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
body1->CreateFixture(&triangleShapeDef);
// Large triangle (recycle definitions)
vertices[0] *= 2.0f;
vertices[1] *= 2.0f;
vertices[2] *= 2.0f;
polygon.Set(vertices, 3);
triangleShapeDef.filter.groupIndex = k_largeGroup;
triangleBodyDef.position.Set(-5.0f, 6.0f);
triangleBodyDef.fixedRotation = true; // look at me!
b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
body2->CreateFixture(&triangleShapeDef);
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-5.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape p;
p.SetAsBox(0.5f, 1.0f);
body->CreateFixture(&p, 1.0f);
b2PrismaticJointDef jd;
jd.bodyA = body2;
jd.bodyB = body;
jd.enableLimit = true;
jd.localAnchorA.Set(0.0f, 4.0f);
jd.localAnchorB.SetZero();
jd.localAxisA.Set(0.0f, 1.0f);
jd.lowerTranslation = -1.0f;
jd.upperTranslation = 1.0f;
m_world->CreateJoint(&jd);
}
// Small box
polygon.SetAsBox(1.0f, 0.5f);
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &polygon;
boxShapeDef.density = 1.0f;
boxShapeDef.restitution = 0.1f;
boxShapeDef.filter.groupIndex = k_smallGroup;
boxShapeDef.filter.categoryBits = k_boxCategory;
boxShapeDef.filter.maskBits = k_boxMask;
b2BodyDef boxBodyDef;
boxBodyDef.type = b2_dynamicBody;
boxBodyDef.position.Set(0.0f, 2.0f);
b2Body* body3 = m_world->CreateBody(&boxBodyDef);
body3->CreateFixture(&boxShapeDef);
// Large box (recycle definitions)
polygon.SetAsBox(2.0f, 1.0f);
boxShapeDef.filter.groupIndex = k_largeGroup;
boxBodyDef.position.Set(0.0f, 6.0f);
b2Body* body4 = m_world->CreateBody(&boxBodyDef);
body4->CreateFixture(&boxShapeDef);
// Small circle
b2CircleShape circle;
circle.m_radius = 1.0f;
b2FixtureDef circleShapeDef;
circleShapeDef.shape = &circle;
circleShapeDef.density = 1.0f;
circleShapeDef.filter.groupIndex = k_smallGroup;
circleShapeDef.filter.categoryBits = k_circleCategory;
circleShapeDef.filter.maskBits = k_circleMask;
b2BodyDef circleBodyDef;
circleBodyDef.type = b2_dynamicBody;
circleBodyDef.position.Set(5.0f, 2.0f);
b2Body* body5 = m_world->CreateBody(&circleBodyDef);
body5->CreateFixture(&circleShapeDef);
// Large circle
circle.m_radius *= 2.0f;
circleShapeDef.filter.groupIndex = k_largeGroup;
circleBodyDef.position.Set(5.0f, 6.0f);
b2Body* body6 = m_world->CreateBody(&circleBodyDef);
body6->CreateFixture(&circleShapeDef);
}
static Test* Create()
{
return new CollisionFiltering;
}
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COLLISION_FILTERING_H
#define COLLISION_FILTERING_H
// This is a test of collision filtering.
// There is a triangle, a box, and a circle.
// There are 6 shapes. 3 large and 3 small.
// The 3 small ones always collide.
// The 3 large ones never collide.
// The boxes don't collide with triangles (except if both are small).
const int16 k_smallGroup = 1;
const int16 k_largeGroup = -1;
const uint16 k_defaultCategory = 0x0001;
const uint16 k_triangleCategory = 0x0002;
const uint16 k_boxCategory = 0x0004;
const uint16 k_circleCategory = 0x0008;
const uint16 k_triangleMask = 0xFFFF;
const uint16 k_boxMask = 0xFFFF ^ k_triangleCategory;
const uint16 k_circleMask = 0xFFFF;
class CollisionFiltering : public Test
{
public:
CollisionFiltering()
{
// Ground body
{
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2FixtureDef sd;
sd.shape = &shape;
sd.friction = 0.3f;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&sd);
}
// Small triangle
b2Vec2 vertices[3];
vertices[0].Set(-1.0f, 0.0f);
vertices[1].Set(1.0f, 0.0f);
vertices[2].Set(0.0f, 2.0f);
b2PolygonShape polygon;
polygon.Set(vertices, 3);
b2FixtureDef triangleShapeDef;
triangleShapeDef.shape = &polygon;
triangleShapeDef.density = 1.0f;
triangleShapeDef.filter.groupIndex = k_smallGroup;
triangleShapeDef.filter.categoryBits = k_triangleCategory;
triangleShapeDef.filter.maskBits = k_triangleMask;
b2BodyDef triangleBodyDef;
triangleBodyDef.type = b2_dynamicBody;
triangleBodyDef.position.Set(-5.0f, 2.0f);
b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
body1->CreateFixture(&triangleShapeDef);
// Large triangle (recycle definitions)
vertices[0] *= 2.0f;
vertices[1] *= 2.0f;
vertices[2] *= 2.0f;
polygon.Set(vertices, 3);
triangleShapeDef.filter.groupIndex = k_largeGroup;
triangleBodyDef.position.Set(-5.0f, 6.0f);
triangleBodyDef.fixedRotation = true; // look at me!
b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
body2->CreateFixture(&triangleShapeDef);
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-5.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape p;
p.SetAsBox(0.5f, 1.0f);
body->CreateFixture(&p, 1.0f);
b2PrismaticJointDef jd;
jd.bodyA = body2;
jd.bodyB = body;
jd.enableLimit = true;
jd.localAnchorA.Set(0.0f, 4.0f);
jd.localAnchorB.SetZero();
jd.localAxisA.Set(0.0f, 1.0f);
jd.lowerTranslation = -1.0f;
jd.upperTranslation = 1.0f;
m_world->CreateJoint(&jd);
}
// Small box
polygon.SetAsBox(1.0f, 0.5f);
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &polygon;
boxShapeDef.density = 1.0f;
boxShapeDef.restitution = 0.1f;
boxShapeDef.filter.groupIndex = k_smallGroup;
boxShapeDef.filter.categoryBits = k_boxCategory;
boxShapeDef.filter.maskBits = k_boxMask;
b2BodyDef boxBodyDef;
boxBodyDef.type = b2_dynamicBody;
boxBodyDef.position.Set(0.0f, 2.0f);
b2Body* body3 = m_world->CreateBody(&boxBodyDef);
body3->CreateFixture(&boxShapeDef);
// Large box (recycle definitions)
polygon.SetAsBox(2.0f, 1.0f);
boxShapeDef.filter.groupIndex = k_largeGroup;
boxBodyDef.position.Set(0.0f, 6.0f);
b2Body* body4 = m_world->CreateBody(&boxBodyDef);
body4->CreateFixture(&boxShapeDef);
// Small circle
b2CircleShape circle;
circle.m_radius = 1.0f;
b2FixtureDef circleShapeDef;
circleShapeDef.shape = &circle;
circleShapeDef.density = 1.0f;
circleShapeDef.filter.groupIndex = k_smallGroup;
circleShapeDef.filter.categoryBits = k_circleCategory;
circleShapeDef.filter.maskBits = k_circleMask;
b2BodyDef circleBodyDef;
circleBodyDef.type = b2_dynamicBody;
circleBodyDef.position.Set(5.0f, 2.0f);
b2Body* body5 = m_world->CreateBody(&circleBodyDef);
body5->CreateFixture(&circleShapeDef);
// Large circle
circle.m_radius *= 2.0f;
circleShapeDef.filter.groupIndex = k_largeGroup;
circleBodyDef.position.Set(5.0f, 6.0f);
b2Body* body6 = m_world->CreateBody(&circleBodyDef);
body6->CreateFixture(&circleShapeDef);
}
static Test* Create()
{
return new CollisionFiltering;
}
};
#endif

View File

@@ -1,188 +1,188 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COLLISION_PROCESSING_H
#define COLLISION_PROCESSING_H
#include <algorithm>
// This test shows collision processing and tests
// deferred body destruction.
class CollisionProcessing : public Test
{
public:
CollisionProcessing()
{
// Ground body
{
b2EdgeShape shape;
shape.Set(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f));
b2FixtureDef sd;
sd.shape = &shape;;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&sd);
}
float32 xLo = -5.0f, xHi = 5.0f;
float32 yLo = 2.0f, yHi = 35.0f;
// Small triangle
b2Vec2 vertices[3];
vertices[0].Set(-1.0f, 0.0f);
vertices[1].Set(1.0f, 0.0f);
vertices[2].Set(0.0f, 2.0f);
b2PolygonShape polygon;
polygon.Set(vertices, 3);
b2FixtureDef triangleShapeDef;
triangleShapeDef.shape = &polygon;
triangleShapeDef.density = 1.0f;
b2BodyDef triangleBodyDef;
triangleBodyDef.type = b2_dynamicBody;
triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
body1->CreateFixture(&triangleShapeDef);
// Large triangle (recycle definitions)
vertices[0] *= 2.0f;
vertices[1] *= 2.0f;
vertices[2] *= 2.0f;
polygon.Set(vertices, 3);
triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
body2->CreateFixture(&triangleShapeDef);
// Small box
polygon.SetAsBox(1.0f, 0.5f);
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &polygon;
boxShapeDef.density = 1.0f;
b2BodyDef boxBodyDef;
boxBodyDef.type = b2_dynamicBody;
boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body3 = m_world->CreateBody(&boxBodyDef);
body3->CreateFixture(&boxShapeDef);
// Large box (recycle definitions)
polygon.SetAsBox(2.0f, 1.0f);
boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body4 = m_world->CreateBody(&boxBodyDef);
body4->CreateFixture(&boxShapeDef);
// Small circle
b2CircleShape circle;
circle.m_radius = 1.0f;
b2FixtureDef circleShapeDef;
circleShapeDef.shape = &circle;
circleShapeDef.density = 1.0f;
b2BodyDef circleBodyDef;
circleBodyDef.type = b2_dynamicBody;
circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body5 = m_world->CreateBody(&circleBodyDef);
body5->CreateFixture(&circleShapeDef);
// Large circle
circle.m_radius *= 2.0f;
circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body6 = m_world->CreateBody(&circleBodyDef);
body6->CreateFixture(&circleShapeDef);
}
void Step(Settings* settings)
{
Test::Step(settings);
// We are going to destroy some bodies according to contact
// points. We must buffer the bodies that should be destroyed
// because they may belong to multiple contact points.
const int32 k_maxNuke = 6;
b2Body* nuke[k_maxNuke];
int32 nukeCount = 0;
// Traverse the contact results. Destroy bodies that
// are touching heavier bodies.
for (int32 i = 0; i < m_pointCount; ++i)
{
ContactPoint* point = m_points + i;
b2Body* body1 = point->fixtureA->GetBody();
b2Body* body2 = point->fixtureB->GetBody();
float32 mass1 = body1->GetMass();
float32 mass2 = body2->GetMass();
if (mass1 > 0.0f && mass2 > 0.0f)
{
if (mass2 > mass1)
{
nuke[nukeCount++] = body1;
}
else
{
nuke[nukeCount++] = body2;
}
if (nukeCount == k_maxNuke)
{
break;
}
}
}
// Sort the nuke array to group duplicates.
std::sort(nuke, nuke + nukeCount);
// Destroy the bodies, skipping duplicates.
int32 i = 0;
while (i < nukeCount)
{
b2Body* b = nuke[i++];
while (i < nukeCount && nuke[i] == b)
{
++i;
}
if (b != m_bomb)
{
m_world->DestroyBody(b);
}
}
}
static Test* Create()
{
return new CollisionProcessing;
}
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COLLISION_PROCESSING_H
#define COLLISION_PROCESSING_H
#include <algorithm>
// This test shows collision processing and tests
// deferred body destruction.
class CollisionProcessing : public Test
{
public:
CollisionProcessing()
{
// Ground body
{
b2EdgeShape shape;
shape.Set(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f));
b2FixtureDef sd;
sd.shape = &shape;;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&sd);
}
float32 xLo = -5.0f, xHi = 5.0f;
float32 yLo = 2.0f, yHi = 35.0f;
// Small triangle
b2Vec2 vertices[3];
vertices[0].Set(-1.0f, 0.0f);
vertices[1].Set(1.0f, 0.0f);
vertices[2].Set(0.0f, 2.0f);
b2PolygonShape polygon;
polygon.Set(vertices, 3);
b2FixtureDef triangleShapeDef;
triangleShapeDef.shape = &polygon;
triangleShapeDef.density = 1.0f;
b2BodyDef triangleBodyDef;
triangleBodyDef.type = b2_dynamicBody;
triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
body1->CreateFixture(&triangleShapeDef);
// Large triangle (recycle definitions)
vertices[0] *= 2.0f;
vertices[1] *= 2.0f;
vertices[2] *= 2.0f;
polygon.Set(vertices, 3);
triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
body2->CreateFixture(&triangleShapeDef);
// Small box
polygon.SetAsBox(1.0f, 0.5f);
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &polygon;
boxShapeDef.density = 1.0f;
b2BodyDef boxBodyDef;
boxBodyDef.type = b2_dynamicBody;
boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body3 = m_world->CreateBody(&boxBodyDef);
body3->CreateFixture(&boxShapeDef);
// Large box (recycle definitions)
polygon.SetAsBox(2.0f, 1.0f);
boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body4 = m_world->CreateBody(&boxBodyDef);
body4->CreateFixture(&boxShapeDef);
// Small circle
b2CircleShape circle;
circle.m_radius = 1.0f;
b2FixtureDef circleShapeDef;
circleShapeDef.shape = &circle;
circleShapeDef.density = 1.0f;
b2BodyDef circleBodyDef;
circleBodyDef.type = b2_dynamicBody;
circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body5 = m_world->CreateBody(&circleBodyDef);
body5->CreateFixture(&circleShapeDef);
// Large circle
circle.m_radius *= 2.0f;
circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body6 = m_world->CreateBody(&circleBodyDef);
body6->CreateFixture(&circleShapeDef);
}
void Step(Settings* settings)
{
Test::Step(settings);
// We are going to destroy some bodies according to contact
// points. We must buffer the bodies that should be destroyed
// because they may belong to multiple contact points.
const int32 k_maxNuke = 6;
b2Body* nuke[k_maxNuke];
int32 nukeCount = 0;
// Traverse the contact results. Destroy bodies that
// are touching heavier bodies.
for (int32 i = 0; i < m_pointCount; ++i)
{
ContactPoint* point = m_points + i;
b2Body* body1 = point->fixtureA->GetBody();
b2Body* body2 = point->fixtureB->GetBody();
float32 mass1 = body1->GetMass();
float32 mass2 = body2->GetMass();
if (mass1 > 0.0f && mass2 > 0.0f)
{
if (mass2 > mass1)
{
nuke[nukeCount++] = body1;
}
else
{
nuke[nukeCount++] = body2;
}
if (nukeCount == k_maxNuke)
{
break;
}
}
}
// Sort the nuke array to group duplicates.
std::sort(nuke, nuke + nukeCount);
// Destroy the bodies, skipping duplicates.
int32 i = 0;
while (i < nukeCount)
{
b2Body* b = nuke[i++];
while (i < nukeCount && nuke[i] == b)
{
++i;
}
if (b != m_bomb)
{
m_world->DestroyBody(b);
}
}
}
static Test* Create()
{
return new CollisionProcessing;
}
};
#endif

View File

@@ -1,143 +1,143 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COMPOUND_SHAPES_H
#define COMPOUND_SHAPES_H
// TODO_ERIN test joints on compounds.
class CompoundShapes : public Test
{
public:
CompoundShapes()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f));
body->CreateFixture(&shape, 0.0f);
}
{
b2CircleShape circle1;
circle1.m_radius = 0.5f;
circle1.m_p.Set(-0.5f, 0.5f);
b2CircleShape circle2;
circle2.m_radius = 0.5f;
circle2.m_p.Set(0.5f, 0.5f);
for (int i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x + 5.0f, 1.05f + 2.5f * i);
bd.angle = RandomFloat(-b2_pi, b2_pi);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&circle1, 2.0f);
body->CreateFixture(&circle2, 0.0f);
}
}
{
b2PolygonShape polygon1;
polygon1.SetAsBox(0.25f, 0.5f);
b2PolygonShape polygon2;
polygon2.SetAsBox(0.25f, 0.5f, b2Vec2(0.0f, -0.5f), 0.5f * b2_pi);
for (int i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x - 5.0f, 1.05f + 2.5f * i);
bd.angle = RandomFloat(-b2_pi, b2_pi);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&polygon1, 2.0f);
body->CreateFixture(&polygon2, 2.0f);
}
}
{
b2Transform xf1;
xf1.q.Set(0.3524f * b2_pi);
xf1.p = xf1.q.GetXAxis();
b2Vec2 vertices[3];
b2PolygonShape triangle1;
vertices[0] = b2Mul(xf1, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf1, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf1, b2Vec2(0.0f, 0.5f));
triangle1.Set(vertices, 3);
b2Transform xf2;
xf2.q.Set(-0.3524f * b2_pi);
xf2.p = -xf2.q.GetXAxis();
b2PolygonShape triangle2;
vertices[0] = b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf2, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf2, b2Vec2(0.0f, 0.5f));
triangle2.Set(vertices, 3);
for (int32 i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x, 2.05f + 2.5f * i);
bd.angle = 0.0f;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&triangle1, 2.0f);
body->CreateFixture(&triangle2, 2.0f);
}
}
{
b2PolygonShape bottom;
bottom.SetAsBox( 1.5f, 0.15f );
b2PolygonShape left;
left.SetAsBox(0.15f, 2.7f, b2Vec2(-1.45f, 2.35f), 0.2f);
b2PolygonShape right;
right.SetAsBox(0.15f, 2.7f, b2Vec2(1.45f, 2.35f), -0.2f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set( 0.0f, 2.0f );
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&bottom, 4.0f);
body->CreateFixture(&left, 4.0f);
body->CreateFixture(&right, 4.0f);
}
}
static Test* Create()
{
return new CompoundShapes;
}
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COMPOUND_SHAPES_H
#define COMPOUND_SHAPES_H
// TODO_ERIN test joints on compounds.
class CompoundShapes : public Test
{
public:
CompoundShapes()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f));
body->CreateFixture(&shape, 0.0f);
}
{
b2CircleShape circle1;
circle1.m_radius = 0.5f;
circle1.m_p.Set(-0.5f, 0.5f);
b2CircleShape circle2;
circle2.m_radius = 0.5f;
circle2.m_p.Set(0.5f, 0.5f);
for (int i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x + 5.0f, 1.05f + 2.5f * i);
bd.angle = RandomFloat(-b2_pi, b2_pi);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&circle1, 2.0f);
body->CreateFixture(&circle2, 0.0f);
}
}
{
b2PolygonShape polygon1;
polygon1.SetAsBox(0.25f, 0.5f);
b2PolygonShape polygon2;
polygon2.SetAsBox(0.25f, 0.5f, b2Vec2(0.0f, -0.5f), 0.5f * b2_pi);
for (int i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x - 5.0f, 1.05f + 2.5f * i);
bd.angle = RandomFloat(-b2_pi, b2_pi);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&polygon1, 2.0f);
body->CreateFixture(&polygon2, 2.0f);
}
}
{
b2Transform xf1;
xf1.q.Set(0.3524f * b2_pi);
xf1.p = xf1.q.GetXAxis();
b2Vec2 vertices[3];
b2PolygonShape triangle1;
vertices[0] = b2Mul(xf1, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf1, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf1, b2Vec2(0.0f, 0.5f));
triangle1.Set(vertices, 3);
b2Transform xf2;
xf2.q.Set(-0.3524f * b2_pi);
xf2.p = -xf2.q.GetXAxis();
b2PolygonShape triangle2;
vertices[0] = b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf2, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf2, b2Vec2(0.0f, 0.5f));
triangle2.Set(vertices, 3);
for (int32 i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x, 2.05f + 2.5f * i);
bd.angle = 0.0f;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&triangle1, 2.0f);
body->CreateFixture(&triangle2, 2.0f);
}
}
{
b2PolygonShape bottom;
bottom.SetAsBox( 1.5f, 0.15f );
b2PolygonShape left;
left.SetAsBox(0.15f, 2.7f, b2Vec2(-1.45f, 2.35f), 0.2f);
b2PolygonShape right;
right.SetAsBox(0.15f, 2.7f, b2Vec2(1.45f, 2.35f), -0.2f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set( 0.0f, 2.0f );
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&bottom, 4.0f);
body->CreateFixture(&left, 4.0f);
body->CreateFixture(&right, 4.0f);
}
}
static Test* Create()
{
return new CompoundShapes;
}
};
#endif

View File

@@ -1,167 +1,167 @@
/*
* Copyright (c) 2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CONFINED_H
#define CONFINED_H
class Confined : public Test
{
public:
enum
{
e_columnCount = 0,
e_rowCount = 0
};
Confined()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
// Floor
shape.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
// Left wall
shape.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(-10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
// Right wall
shape.Set(b2Vec2(10.0f, 0.0f), b2Vec2(10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
// Roof
shape.Set(b2Vec2(-10.0f, 20.0f), b2Vec2(10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
}
float32 radius = 0.5f;
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = radius;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.1f;
for (int32 j = 0; j < e_columnCount; ++j)
{
for (int i = 0; i < e_rowCount; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + (2.1f * j + 1.0f + 0.01f * i) * radius, (2.0f * i + 1.0f) * radius);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
m_world->SetGravity(b2Vec2(0.0f, 0.0f));
}
void CreateCircle()
{
float32 radius = 2.0f;
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = radius;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.0f;
b2Vec2 p(RandomFloat(), 3.0f + RandomFloat());
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = p;
//bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'c':
CreateCircle();
break;
}
}
void Step(Settings* settings)
{
bool sleeping = true;
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() != b2_dynamicBody)
{
continue;
}
if (b->IsAwake())
{
sleeping = false;
}
}
if (m_stepCount == 180)
{
m_stepCount += 0;
}
//if (sleeping)
//{
// CreateCircle();
//}
Test::Step(settings);
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() != b2_dynamicBody)
{
continue;
}
b2Vec2 p = b->GetPosition();
if (p.x <= -10.0f || 10.0f <= p.x || p.y <= 0.0f || 20.0f <= p.y)
{
p.x += 0.0;
}
}
m_debugDraw.DrawString(5, m_textLine, "Press 'c' to create a circle.");
m_textLine += 15;
}
static Test* Create()
{
return new Confined;
}
};
#endif
/*
* Copyright (c) 2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CONFINED_H
#define CONFINED_H
class Confined : public Test
{
public:
enum
{
e_columnCount = 0,
e_rowCount = 0
};
Confined()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
// Floor
shape.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
// Left wall
shape.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(-10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
// Right wall
shape.Set(b2Vec2(10.0f, 0.0f), b2Vec2(10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
// Roof
shape.Set(b2Vec2(-10.0f, 20.0f), b2Vec2(10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
}
float32 radius = 0.5f;
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = radius;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.1f;
for (int32 j = 0; j < e_columnCount; ++j)
{
for (int i = 0; i < e_rowCount; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + (2.1f * j + 1.0f + 0.01f * i) * radius, (2.0f * i + 1.0f) * radius);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
m_world->SetGravity(b2Vec2(0.0f, 0.0f));
}
void CreateCircle()
{
float32 radius = 2.0f;
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = radius;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.0f;
b2Vec2 p(RandomFloat(), 3.0f + RandomFloat());
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = p;
//bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'c':
CreateCircle();
break;
}
}
void Step(Settings* settings)
{
bool sleeping = true;
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() != b2_dynamicBody)
{
continue;
}
if (b->IsAwake())
{
sleeping = false;
}
}
if (m_stepCount == 180)
{
m_stepCount += 0;
}
//if (sleeping)
//{
// CreateCircle();
//}
Test::Step(settings);
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() != b2_dynamicBody)
{
continue;
}
b2Vec2 p = b->GetPosition();
if (p.x <= -10.0f || 10.0f <= p.x || p.y <= 0.0f || 20.0f <= p.y)
{
p.x += 0.0;
}
}
m_debugDraw.DrawString(5, m_textLine, "Press 'c' to create a circle.");
m_textLine += 15;
}
static Test* Create()
{
return new Confined;
}
};
#endif

View File

@@ -1,137 +1,137 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CONTINUOUS_TEST_H
#define CONTINUOUS_TEST_H
class ContinuousTest : public Test
{
public:
ContinuousTest()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
body->CreateFixture(&edge, 0.0f);
b2PolygonShape shape;
shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
body->CreateFixture(&shape, 0.0f);
}
#if 1
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 20.0f);
//bd.angle = 0.1f;
b2PolygonShape shape;
shape.SetAsBox(2.0f, 0.1f);
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&shape, 1.0f);
m_angularVelocity = RandomFloat(-50.0f, 50.0f);
//m_angularVelocity = 46.661274f;
m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
m_body->SetAngularVelocity(m_angularVelocity);
}
#else
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 2.0f);
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = 0.5f;
body->CreateFixture(&shape, 1.0f);
bd.bullet = true;
bd.position.Set(0.0f, 10.0f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
}
#endif
}
void Launch()
{
m_body->SetTransform(b2Vec2(0.0f, 20.0f), 0.0f);
m_angularVelocity = RandomFloat(-50.0f, 50.0f);
m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
m_body->SetAngularVelocity(m_angularVelocity);
}
void Step(Settings* settings)
{
if (m_stepCount == 12)
{
m_stepCount += 0;
}
Test::Step(settings);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
if (b2_gjkCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
b2_gjkCalls, b2_gjkIters / float32(b2_gjkCalls), b2_gjkMaxIters);
m_textLine += 15;
}
extern int32 b2_toiCalls, b2_toiIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
if (b2_toiCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave toi iters = %3.1f, max toi iters = %d",
b2_toiCalls, b2_toiIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "ave toi root iters = %3.1f, max toi root iters = %d",
b2_toiRootIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
}
if (m_stepCount % 60 == 0)
{
//Launch();
}
}
static Test* Create()
{
return new ContinuousTest;
}
b2Body* m_body;
float32 m_angularVelocity;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CONTINUOUS_TEST_H
#define CONTINUOUS_TEST_H
class ContinuousTest : public Test
{
public:
ContinuousTest()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
body->CreateFixture(&edge, 0.0f);
b2PolygonShape shape;
shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
body->CreateFixture(&shape, 0.0f);
}
#if 1
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 20.0f);
//bd.angle = 0.1f;
b2PolygonShape shape;
shape.SetAsBox(2.0f, 0.1f);
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&shape, 1.0f);
m_angularVelocity = RandomFloat(-50.0f, 50.0f);
//m_angularVelocity = 46.661274f;
m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
m_body->SetAngularVelocity(m_angularVelocity);
}
#else
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 2.0f);
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = 0.5f;
body->CreateFixture(&shape, 1.0f);
bd.bullet = true;
bd.position.Set(0.0f, 10.0f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
}
#endif
}
void Launch()
{
m_body->SetTransform(b2Vec2(0.0f, 20.0f), 0.0f);
m_angularVelocity = RandomFloat(-50.0f, 50.0f);
m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
m_body->SetAngularVelocity(m_angularVelocity);
}
void Step(Settings* settings)
{
if (m_stepCount == 12)
{
m_stepCount += 0;
}
Test::Step(settings);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
if (b2_gjkCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
b2_gjkCalls, b2_gjkIters / float32(b2_gjkCalls), b2_gjkMaxIters);
m_textLine += 15;
}
extern int32 b2_toiCalls, b2_toiIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
if (b2_toiCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave toi iters = %3.1f, max toi iters = %d",
b2_toiCalls, b2_toiIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "ave toi root iters = %3.1f, max toi root iters = %d",
b2_toiRootIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
}
if (m_stepCount % 60 == 0)
{
//Launch();
}
}
static Test* Create()
{
return new ContinuousTest;
}
b2Body* m_body;
float32 m_angularVelocity;
};
#endif

View File

@@ -1,135 +1,135 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DISTANCE_TEST_H
#define DISTANCE_TEST_H
class DistanceTest : public Test
{
public:
DistanceTest()
{
{
m_transformA.SetIdentity();
m_transformA.p.Set(0.0f, -0.2f);
m_polygonA.SetAsBox(10.0f, 0.2f);
}
{
m_positionB.Set(12.017401f, 0.13678508f);
m_angleB = -0.0109265f;
m_transformB.Set(m_positionB, m_angleB);
m_polygonB.SetAsBox(2.0f, 0.1f);
}
}
static Test* Create()
{
return new DistanceTest;
}
void Step(Settings* settings)
{
Test::Step(settings);
b2DistanceInput input;
input.proxyA.Set(&m_polygonA, 0);
input.proxyB.Set(&m_polygonB, 0);
input.transformA = m_transformA;
input.transformB = m_transformB;
input.useRadii = true;
b2SimplexCache cache;
cache.count = 0;
b2DistanceOutput output;
b2Distance(&output, &cache, &input);
m_debugDraw.DrawString(5, m_textLine, "distance = %g", output.distance);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "iterations = %d", output.iterations);
m_textLine += 15;
{
b2Color color(0.9f, 0.9f, 0.9f);
b2Vec2 v[b2_maxPolygonVertices];
for (int32 i = 0; i < m_polygonA.m_vertexCount; ++i)
{
v[i] = b2Mul(m_transformA, m_polygonA.m_vertices[i]);
}
m_debugDraw.DrawPolygon(v, m_polygonA.m_vertexCount, color);
for (int32 i = 0; i < m_polygonB.m_vertexCount; ++i)
{
v[i] = b2Mul(m_transformB, m_polygonB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(v, m_polygonB.m_vertexCount, color);
}
b2Vec2 x1 = output.pointA;
b2Vec2 x2 = output.pointB;
b2Color c1(1.0f, 0.0f, 0.0f);
m_debugDraw.DrawPoint(x1, 4.0f, c1);
b2Color c2(1.0f, 1.0f, 0.0f);
m_debugDraw.DrawPoint(x2, 4.0f, c2);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_positionB.x -= 0.1f;
break;
case 'd':
m_positionB.x += 0.1f;
break;
case 's':
m_positionB.y -= 0.1f;
break;
case 'w':
m_positionB.y += 0.1f;
break;
case 'q':
m_angleB += 0.1f * b2_pi;
break;
case 'e':
m_angleB -= 0.1f * b2_pi;
break;
}
m_transformB.Set(m_positionB, m_angleB);
}
b2Vec2 m_positionB;
float32 m_angleB;
b2Transform m_transformA;
b2Transform m_transformB;
b2PolygonShape m_polygonA;
b2PolygonShape m_polygonB;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DISTANCE_TEST_H
#define DISTANCE_TEST_H
class DistanceTest : public Test
{
public:
DistanceTest()
{
{
m_transformA.SetIdentity();
m_transformA.p.Set(0.0f, -0.2f);
m_polygonA.SetAsBox(10.0f, 0.2f);
}
{
m_positionB.Set(12.017401f, 0.13678508f);
m_angleB = -0.0109265f;
m_transformB.Set(m_positionB, m_angleB);
m_polygonB.SetAsBox(2.0f, 0.1f);
}
}
static Test* Create()
{
return new DistanceTest;
}
void Step(Settings* settings)
{
Test::Step(settings);
b2DistanceInput input;
input.proxyA.Set(&m_polygonA, 0);
input.proxyB.Set(&m_polygonB, 0);
input.transformA = m_transformA;
input.transformB = m_transformB;
input.useRadii = true;
b2SimplexCache cache;
cache.count = 0;
b2DistanceOutput output;
b2Distance(&output, &cache, &input);
m_debugDraw.DrawString(5, m_textLine, "distance = %g", output.distance);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "iterations = %d", output.iterations);
m_textLine += 15;
{
b2Color color(0.9f, 0.9f, 0.9f);
b2Vec2 v[b2_maxPolygonVertices];
for (int32 i = 0; i < m_polygonA.m_vertexCount; ++i)
{
v[i] = b2Mul(m_transformA, m_polygonA.m_vertices[i]);
}
m_debugDraw.DrawPolygon(v, m_polygonA.m_vertexCount, color);
for (int32 i = 0; i < m_polygonB.m_vertexCount; ++i)
{
v[i] = b2Mul(m_transformB, m_polygonB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(v, m_polygonB.m_vertexCount, color);
}
b2Vec2 x1 = output.pointA;
b2Vec2 x2 = output.pointB;
b2Color c1(1.0f, 0.0f, 0.0f);
m_debugDraw.DrawPoint(x1, 4.0f, c1);
b2Color c2(1.0f, 1.0f, 0.0f);
m_debugDraw.DrawPoint(x2, 4.0f, c2);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_positionB.x -= 0.1f;
break;
case 'd':
m_positionB.x += 0.1f;
break;
case 's':
m_positionB.y -= 0.1f;
break;
case 'w':
m_positionB.y += 0.1f;
break;
case 'q':
m_angleB += 0.1f * b2_pi;
break;
case 'e':
m_angleB -= 0.1f * b2_pi;
break;
}
m_transformB.Set(m_positionB, m_angleB);
}
b2Vec2 m_positionB;
float32 m_angleB;
b2Transform m_transformA;
b2Transform m_transformB;
b2PolygonShape m_polygonA;
b2PolygonShape m_polygonB;
};
#endif

View File

@@ -1,215 +1,215 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DOMINOS_H
#define DOMINOS_H
class Dominos : public Test
{
public:
Dominos()
{
b2Body* b1;
{
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2BodyDef bd;
b1 = m_world->CreateBody(&bd);
b1->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(6.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(-1.5f, 10.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.1f, 1.0f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.1f;
for (int i = 0; i < 10; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 1.0f * i, 11.25f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
{
b2PolygonShape shape;
shape.SetAsBox(7.0f, 0.25f, b2Vec2_zero, 0.3f);
b2BodyDef bd;
bd.position.Set(1.0f, 6.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
b2Body* b2;
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 1.5f);
b2BodyDef bd;
bd.position.Set(-7.0f, 4.0f);
b2 = m_world->CreateBody(&bd);
b2->CreateFixture(&shape, 0.0f);
}
b2Body* b3;
{
b2PolygonShape shape;
shape.SetAsBox(6.0f, 0.125f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-0.9f, 1.0f);
bd.angle = -0.15f;
b3 = m_world->CreateBody(&bd);
b3->CreateFixture(&shape, 10.0f);
}
b2RevoluteJointDef jd;
b2Vec2 anchor;
anchor.Set(-2.0f, 1.0f);
jd.Initialize(b1, b3, anchor);
jd.collideConnected = true;
m_world->CreateJoint(&jd);
b2Body* b4;
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 0.25f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f, 15.0f);
b4 = m_world->CreateBody(&bd);
b4->CreateFixture(&shape, 10.0f);
}
anchor.Set(-7.0f, 15.0f);
jd.Initialize(b2, b4, anchor);
m_world->CreateJoint(&jd);
b2Body* b5;
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(6.5f, 3.0f);
b5 = m_world->CreateBody(&bd);
b2PolygonShape shape;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 10.0f;
fd.friction = 0.1f;
shape.SetAsBox(1.0f, 0.1f, b2Vec2(0.0f, -0.9f), 0.0f);
b5->CreateFixture(&fd);
shape.SetAsBox(0.1f, 1.0f, b2Vec2(-0.9f, 0.0f), 0.0f);
b5->CreateFixture(&fd);
shape.SetAsBox(0.1f, 1.0f, b2Vec2(0.9f, 0.0f), 0.0f);
b5->CreateFixture(&fd);
}
anchor.Set(6.0f, 2.0f);
jd.Initialize(b1, b5, anchor);
m_world->CreateJoint(&jd);
b2Body* b6;
{
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(6.5f, 4.1f);
b6 = m_world->CreateBody(&bd);
b6->CreateFixture(&shape, 30.0f);
}
anchor.Set(7.5f, 4.0f);
jd.Initialize(b5, b6, anchor);
m_world->CreateJoint(&jd);
b2Body* b7;
{
b2PolygonShape shape;
shape.SetAsBox(0.1f, 1.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(7.4f, 1.0f);
b7 = m_world->CreateBody(&bd);
b7->CreateFixture(&shape, 10.0f);
}
b2DistanceJointDef djd;
djd.bodyA = b3;
djd.bodyB = b7;
djd.localAnchorA.Set(6.0f, 0.0f);
djd.localAnchorB.Set(0.0f, -1.0f);
b2Vec2 d = djd.bodyB->GetWorldPoint(djd.localAnchorB) - djd.bodyA->GetWorldPoint(djd.localAnchorA);
djd.length = d.Length();
m_world->CreateJoint(&djd);
{
float32 radius = 0.2f;
b2CircleShape shape;
shape.m_radius = radius;
for (int i = 0; i < 4; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(5.9f + 2.0f * radius * i, 2.4f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 10.0f);
}
}
}
static Test* Create()
{
return new Dominos;
}
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DOMINOS_H
#define DOMINOS_H
class Dominos : public Test
{
public:
Dominos()
{
b2Body* b1;
{
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2BodyDef bd;
b1 = m_world->CreateBody(&bd);
b1->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(6.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(-1.5f, 10.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.1f, 1.0f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.1f;
for (int i = 0; i < 10; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 1.0f * i, 11.25f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
{
b2PolygonShape shape;
shape.SetAsBox(7.0f, 0.25f, b2Vec2_zero, 0.3f);
b2BodyDef bd;
bd.position.Set(1.0f, 6.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
b2Body* b2;
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 1.5f);
b2BodyDef bd;
bd.position.Set(-7.0f, 4.0f);
b2 = m_world->CreateBody(&bd);
b2->CreateFixture(&shape, 0.0f);
}
b2Body* b3;
{
b2PolygonShape shape;
shape.SetAsBox(6.0f, 0.125f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-0.9f, 1.0f);
bd.angle = -0.15f;
b3 = m_world->CreateBody(&bd);
b3->CreateFixture(&shape, 10.0f);
}
b2RevoluteJointDef jd;
b2Vec2 anchor;
anchor.Set(-2.0f, 1.0f);
jd.Initialize(b1, b3, anchor);
jd.collideConnected = true;
m_world->CreateJoint(&jd);
b2Body* b4;
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 0.25f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f, 15.0f);
b4 = m_world->CreateBody(&bd);
b4->CreateFixture(&shape, 10.0f);
}
anchor.Set(-7.0f, 15.0f);
jd.Initialize(b2, b4, anchor);
m_world->CreateJoint(&jd);
b2Body* b5;
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(6.5f, 3.0f);
b5 = m_world->CreateBody(&bd);
b2PolygonShape shape;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 10.0f;
fd.friction = 0.1f;
shape.SetAsBox(1.0f, 0.1f, b2Vec2(0.0f, -0.9f), 0.0f);
b5->CreateFixture(&fd);
shape.SetAsBox(0.1f, 1.0f, b2Vec2(-0.9f, 0.0f), 0.0f);
b5->CreateFixture(&fd);
shape.SetAsBox(0.1f, 1.0f, b2Vec2(0.9f, 0.0f), 0.0f);
b5->CreateFixture(&fd);
}
anchor.Set(6.0f, 2.0f);
jd.Initialize(b1, b5, anchor);
m_world->CreateJoint(&jd);
b2Body* b6;
{
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(6.5f, 4.1f);
b6 = m_world->CreateBody(&bd);
b6->CreateFixture(&shape, 30.0f);
}
anchor.Set(7.5f, 4.0f);
jd.Initialize(b5, b6, anchor);
m_world->CreateJoint(&jd);
b2Body* b7;
{
b2PolygonShape shape;
shape.SetAsBox(0.1f, 1.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(7.4f, 1.0f);
b7 = m_world->CreateBody(&bd);
b7->CreateFixture(&shape, 10.0f);
}
b2DistanceJointDef djd;
djd.bodyA = b3;
djd.bodyB = b7;
djd.localAnchorA.Set(6.0f, 0.0f);
djd.localAnchorB.Set(0.0f, -1.0f);
b2Vec2 d = djd.bodyB->GetWorldPoint(djd.localAnchorB) - djd.bodyA->GetWorldPoint(djd.localAnchorA);
djd.length = d.Length();
m_world->CreateJoint(&djd);
{
float32 radius = 0.2f;
b2CircleShape shape;
shape.m_radius = radius;
for (int i = 0; i < 4; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(5.9f + 2.0f * radius * i, 2.4f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 10.0f);
}
}
}
static Test* Create()
{
return new Dominos;
}
};
#endif

View File

@@ -1,267 +1,267 @@
/*
* Copyright (c) 2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DUMP_SHELL_H
#define DUMP_SHELL_H
// This test holds worlds dumped using b2World::Dump.
class DumpShell : public Test
{
public:
DumpShell()
{
b2Vec2 g(0.000000000000000e+00f, 0.000000000000000e+00f);
m_world->SetGravity(g);
b2Body** bodies = (b2Body**)b2Alloc(3 * sizeof(b2Body*));
b2Joint** joints = (b2Joint**)b2Alloc(2 * sizeof(b2Joint*));
{
b2BodyDef bd;
bd.type = b2BodyType(2);
bd.position.Set(1.304347801208496e+01f, 2.500000000000000e+00f);
bd.angle = 0.000000000000000e+00f;
bd.linearVelocity.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
bd.angularVelocity = 0.000000000000000e+00f;
bd.linearDamping = 5.000000000000000e-01f;
bd.angularDamping = 5.000000000000000e-01f;
bd.allowSleep = bool(4);
bd.awake = bool(2);
bd.fixedRotation = bool(0);
bd.bullet = bool(0);
bd.active = bool(32);
bd.gravityScale = 1.000000000000000e+00f;
bodies[0] = m_world->CreateBody(&bd);
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+00f;
fd.restitution = 5.000000000000000e-01f;
fd.density = 1.000000000000000e+01f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2PolygonShape shape;
b2Vec2 vs[8];
vs[0].Set(-6.900000095367432e+00f, -3.000000119209290e-01f);
vs[1].Set(2.000000029802322e-01f, -3.000000119209290e-01f);
vs[2].Set(2.000000029802322e-01f, 2.000000029802322e-01f);
vs[3].Set(-6.900000095367432e+00f, 2.000000029802322e-01f);
shape.Set(vs, 4);
fd.shape = &shape;
bodies[0]->CreateFixture(&fd);
}
}
{
b2BodyDef bd;
bd.type = b2BodyType(2);
bd.position.Set(8.478260636329651e-01f, 2.500000000000000e+00f);
bd.angle = 0.000000000000000e+00f;
bd.linearVelocity.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
bd.angularVelocity = 0.000000000000000e+00f;
bd.linearDamping = 5.000000000000000e-01f;
bd.angularDamping = 5.000000000000000e-01f;
bd.allowSleep = bool(4);
bd.awake = bool(2);
bd.fixedRotation = bool(0);
bd.bullet = bool(0);
bd.active = bool(32);
bd.gravityScale = 1.000000000000000e+00f;
bodies[1] = m_world->CreateBody(&bd);
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+00f;
fd.restitution = 5.000000000000000e-01f;
fd.density = 1.000000000000000e+01f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2PolygonShape shape;
b2Vec2 vs[8];
vs[0].Set(-3.228000104427338e-01f, -2.957000136375427e-01f);
vs[1].Set(6.885900020599365e+00f, -3.641000092029572e-01f);
vs[2].Set(6.907599925994873e+00f, 3.271999955177307e-01f);
vs[3].Set(-3.228000104427338e-01f, 2.825999855995178e-01f);
shape.Set(vs, 4);
fd.shape = &shape;
bodies[1]->CreateFixture(&fd);
}
}
{
b2BodyDef bd;
bd.type = b2BodyType(0);
bd.position.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
bd.angle = 0.000000000000000e+00f;
bd.linearVelocity.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
bd.angularVelocity = 0.000000000000000e+00f;
bd.linearDamping = 0.000000000000000e+00f;
bd.angularDamping = 0.000000000000000e+00f;
bd.allowSleep = bool(4);
bd.awake = bool(2);
bd.fixedRotation = bool(0);
bd.bullet = bool(0);
bd.active = bool(32);
bd.gravityScale = 1.000000000000000e+00f;
bodies[2] = m_world->CreateBody(&bd);
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex1.Set(4.452173995971680e+01f, 1.669565200805664e+01f);
shape.m_vertex2.Set(4.452173995971680e+01f, 0.000000000000000e+00f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
bodies[2]->CreateFixture(&fd);
}
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex1.Set(0.000000000000000e+00f, 1.669565200805664e+01f);
shape.m_vertex2.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
bodies[2]->CreateFixture(&fd);
}
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex1.Set(0.000000000000000e+00f, 1.669565200805664e+01f);
shape.m_vertex2.Set(4.452173995971680e+01f, 1.669565200805664e+01f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
bodies[2]->CreateFixture(&fd);
}
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex1.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex2.Set(4.452173995971680e+01f, 0.000000000000000e+00f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
bodies[2]->CreateFixture(&fd);
}
}
{
b2PrismaticJointDef jd;
jd.bodyA = bodies[1];
jd.bodyB = bodies[0];
jd.collideConnected = bool(0);
jd.localAnchorA.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
jd.localAnchorB.Set(-1.219565200805664e+01f, 0.000000000000000e+00f);
jd.localAxisA.Set(-1.219565200805664e+01f, 0.000000000000000e+00f);
jd.referenceAngle = 0.000000000000000e+00f;
jd.enableLimit = bool(1);
jd.lowerTranslation = -2.000000000000000e+01f;
jd.upperTranslation = 0.000000000000000e+00f;
jd.enableMotor = bool(1);
jd.motorSpeed = 0.000000000000000e+00f;
jd.maxMotorForce = 1.000000000000000e+01f;
joints[0] = m_world->CreateJoint(&jd);
}
{
b2RevoluteJointDef jd;
jd.bodyA = bodies[1];
jd.bodyB = bodies[2];
jd.collideConnected = bool(0);
jd.localAnchorA.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
jd.localAnchorB.Set(8.478260636329651e-01f, 2.500000000000000e+00f);
jd.referenceAngle = 0.000000000000000e+00f;
jd.enableLimit = bool(0);
jd.lowerAngle = 0.000000000000000e+00f;
jd.upperAngle = 0.000000000000000e+00f;
jd.enableMotor = bool(0);
jd.motorSpeed = 0.000000000000000e+00f;
jd.maxMotorTorque = 0.000000000000000e+00f;
joints[1] = m_world->CreateJoint(&jd);
}
b2Free(joints);
b2Free(bodies);
joints = NULL;
bodies = NULL;
}
static Test* Create()
{
return new DumpShell;
}
};
#endif
/*
* Copyright (c) 2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DUMP_SHELL_H
#define DUMP_SHELL_H
// This test holds worlds dumped using b2World::Dump.
class DumpShell : public Test
{
public:
DumpShell()
{
b2Vec2 g(0.000000000000000e+00f, 0.000000000000000e+00f);
m_world->SetGravity(g);
b2Body** bodies = (b2Body**)b2Alloc(3 * sizeof(b2Body*));
b2Joint** joints = (b2Joint**)b2Alloc(2 * sizeof(b2Joint*));
{
b2BodyDef bd;
bd.type = b2BodyType(2);
bd.position.Set(1.304347801208496e+01f, 2.500000000000000e+00f);
bd.angle = 0.000000000000000e+00f;
bd.linearVelocity.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
bd.angularVelocity = 0.000000000000000e+00f;
bd.linearDamping = 5.000000000000000e-01f;
bd.angularDamping = 5.000000000000000e-01f;
bd.allowSleep = bool(4);
bd.awake = bool(2);
bd.fixedRotation = bool(0);
bd.bullet = bool(0);
bd.active = bool(32);
bd.gravityScale = 1.000000000000000e+00f;
bodies[0] = m_world->CreateBody(&bd);
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+00f;
fd.restitution = 5.000000000000000e-01f;
fd.density = 1.000000000000000e+01f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2PolygonShape shape;
b2Vec2 vs[8];
vs[0].Set(-6.900000095367432e+00f, -3.000000119209290e-01f);
vs[1].Set(2.000000029802322e-01f, -3.000000119209290e-01f);
vs[2].Set(2.000000029802322e-01f, 2.000000029802322e-01f);
vs[3].Set(-6.900000095367432e+00f, 2.000000029802322e-01f);
shape.Set(vs, 4);
fd.shape = &shape;
bodies[0]->CreateFixture(&fd);
}
}
{
b2BodyDef bd;
bd.type = b2BodyType(2);
bd.position.Set(8.478260636329651e-01f, 2.500000000000000e+00f);
bd.angle = 0.000000000000000e+00f;
bd.linearVelocity.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
bd.angularVelocity = 0.000000000000000e+00f;
bd.linearDamping = 5.000000000000000e-01f;
bd.angularDamping = 5.000000000000000e-01f;
bd.allowSleep = bool(4);
bd.awake = bool(2);
bd.fixedRotation = bool(0);
bd.bullet = bool(0);
bd.active = bool(32);
bd.gravityScale = 1.000000000000000e+00f;
bodies[1] = m_world->CreateBody(&bd);
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+00f;
fd.restitution = 5.000000000000000e-01f;
fd.density = 1.000000000000000e+01f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2PolygonShape shape;
b2Vec2 vs[8];
vs[0].Set(-3.228000104427338e-01f, -2.957000136375427e-01f);
vs[1].Set(6.885900020599365e+00f, -3.641000092029572e-01f);
vs[2].Set(6.907599925994873e+00f, 3.271999955177307e-01f);
vs[3].Set(-3.228000104427338e-01f, 2.825999855995178e-01f);
shape.Set(vs, 4);
fd.shape = &shape;
bodies[1]->CreateFixture(&fd);
}
}
{
b2BodyDef bd;
bd.type = b2BodyType(0);
bd.position.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
bd.angle = 0.000000000000000e+00f;
bd.linearVelocity.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
bd.angularVelocity = 0.000000000000000e+00f;
bd.linearDamping = 0.000000000000000e+00f;
bd.angularDamping = 0.000000000000000e+00f;
bd.allowSleep = bool(4);
bd.awake = bool(2);
bd.fixedRotation = bool(0);
bd.bullet = bool(0);
bd.active = bool(32);
bd.gravityScale = 1.000000000000000e+00f;
bodies[2] = m_world->CreateBody(&bd);
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex1.Set(4.452173995971680e+01f, 1.669565200805664e+01f);
shape.m_vertex2.Set(4.452173995971680e+01f, 0.000000000000000e+00f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
bodies[2]->CreateFixture(&fd);
}
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex1.Set(0.000000000000000e+00f, 1.669565200805664e+01f);
shape.m_vertex2.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
bodies[2]->CreateFixture(&fd);
}
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex1.Set(0.000000000000000e+00f, 1.669565200805664e+01f);
shape.m_vertex2.Set(4.452173995971680e+01f, 1.669565200805664e+01f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
bodies[2]->CreateFixture(&fd);
}
{
b2FixtureDef fd;
fd.friction = 1.000000000000000e+01f;
fd.restitution = 0.000000000000000e+00f;
fd.density = 0.000000000000000e+00f;
fd.isSensor = bool(0);
fd.filter.categoryBits = uint16(1);
fd.filter.maskBits = uint16(65535);
fd.filter.groupIndex = int16(0);
b2EdgeShape shape;
shape.m_radius = 9.999999776482582e-03f;
shape.m_vertex0.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex1.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_vertex2.Set(4.452173995971680e+01f, 0.000000000000000e+00f);
shape.m_vertex3.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
shape.m_hasVertex0 = bool(0);
shape.m_hasVertex3 = bool(0);
fd.shape = &shape;
bodies[2]->CreateFixture(&fd);
}
}
{
b2PrismaticJointDef jd;
jd.bodyA = bodies[1];
jd.bodyB = bodies[0];
jd.collideConnected = bool(0);
jd.localAnchorA.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
jd.localAnchorB.Set(-1.219565200805664e+01f, 0.000000000000000e+00f);
jd.localAxisA.Set(-1.219565200805664e+01f, 0.000000000000000e+00f);
jd.referenceAngle = 0.000000000000000e+00f;
jd.enableLimit = bool(1);
jd.lowerTranslation = -2.000000000000000e+01f;
jd.upperTranslation = 0.000000000000000e+00f;
jd.enableMotor = bool(1);
jd.motorSpeed = 0.000000000000000e+00f;
jd.maxMotorForce = 1.000000000000000e+01f;
joints[0] = m_world->CreateJoint(&jd);
}
{
b2RevoluteJointDef jd;
jd.bodyA = bodies[1];
jd.bodyB = bodies[2];
jd.collideConnected = bool(0);
jd.localAnchorA.Set(0.000000000000000e+00f, 0.000000000000000e+00f);
jd.localAnchorB.Set(8.478260636329651e-01f, 2.500000000000000e+00f);
jd.referenceAngle = 0.000000000000000e+00f;
jd.enableLimit = bool(0);
jd.lowerAngle = 0.000000000000000e+00f;
jd.upperAngle = 0.000000000000000e+00f;
jd.enableMotor = bool(0);
jd.motorSpeed = 0.000000000000000e+00f;
jd.maxMotorTorque = 0.000000000000000e+00f;
joints[1] = m_world->CreateJoint(&jd);
}
b2Free(joints);
b2Free(bodies);
joints = NULL;
bodies = NULL;
}
static Test* Create()
{
return new DumpShell;
}
};
#endif

View File

@@ -1,357 +1,357 @@
/*
* Copyright (c) 2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DYNAMIC_TREE_TEST_H
#define DYNAMIC_TREE_TEST_H
class DynamicTreeTest : public Test
{
public:
enum
{
e_actorCount = 128
};
DynamicTreeTest()
{
m_worldExtent = 15.0f;
m_proxyExtent = 0.5f;
srand(888);
for (int32 i = 0; i < e_actorCount; ++i)
{
Actor* actor = m_actors + i;
GetRandomAABB(&actor->aabb);
actor->proxyId = m_tree.CreateProxy(actor->aabb, actor);
}
m_stepCount = 0;
float32 h = m_worldExtent;
m_queryAABB.lowerBound.Set(-3.0f, -4.0f + h);
m_queryAABB.upperBound.Set(5.0f, 6.0f + h);
m_rayCastInput.p1.Set(-5.0, 5.0f + h);
m_rayCastInput.p2.Set(7.0f, -4.0f + h);
//m_rayCastInput.p1.Set(0.0f, 2.0f + h);
//m_rayCastInput.p2.Set(0.0f, -2.0f + h);
m_rayCastInput.maxFraction = 1.0f;
m_automated = false;
}
static Test* Create()
{
return new DynamicTreeTest;
}
void Step(Settings* settings)
{
B2_NOT_USED(settings);
m_rayActor = NULL;
for (int32 i = 0; i < e_actorCount; ++i)
{
m_actors[i].fraction = 1.0f;
m_actors[i].overlap = false;
}
if (m_automated == true)
{
int32 actionCount = b2Max(1, e_actorCount >> 2);
for (int32 i = 0; i < actionCount; ++i)
{
Action();
}
}
Query();
RayCast();
for (int32 i = 0; i < e_actorCount; ++i)
{
Actor* actor = m_actors + i;
if (actor->proxyId == b2_nullNode)
continue;
b2Color c(0.9f, 0.9f, 0.9f);
if (actor == m_rayActor && actor->overlap)
{
c.Set(0.9f, 0.6f, 0.6f);
}
else if (actor == m_rayActor)
{
c.Set(0.6f, 0.9f, 0.6f);
}
else if (actor->overlap)
{
c.Set(0.6f, 0.6f, 0.9f);
}
m_debugDraw.DrawAABB(&actor->aabb, c);
}
b2Color c(0.7f, 0.7f, 0.7f);
m_debugDraw.DrawAABB(&m_queryAABB, c);
m_debugDraw.DrawSegment(m_rayCastInput.p1, m_rayCastInput.p2, c);
b2Color c1(0.2f, 0.9f, 0.2f);
b2Color c2(0.9f, 0.2f, 0.2f);
m_debugDraw.DrawPoint(m_rayCastInput.p1, 6.0f, c1);
m_debugDraw.DrawPoint(m_rayCastInput.p2, 6.0f, c2);
if (m_rayActor)
{
b2Color cr(0.2f, 0.2f, 0.9f);
b2Vec2 p = m_rayCastInput.p1 + m_rayActor->fraction * (m_rayCastInput.p2 - m_rayCastInput.p1);
m_debugDraw.DrawPoint(p, 6.0f, cr);
}
{
int32 height = m_tree.GetHeight();
m_debugDraw.DrawString(5, m_textLine, "dynamic tree height = %d", height);
m_textLine += 15;
}
++m_stepCount;
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_automated = !m_automated;
break;
case 'c':
CreateProxy();
break;
case 'd':
DestroyProxy();
break;
case 'm':
MoveProxy();
break;
}
}
bool QueryCallback(int32 proxyId)
{
Actor* actor = (Actor*)m_tree.GetUserData(proxyId);
actor->overlap = b2TestOverlap(m_queryAABB, actor->aabb);
return true;
}
float32 RayCastCallback(const b2RayCastInput& input, int32 proxyId)
{
Actor* actor = (Actor*)m_tree.GetUserData(proxyId);
b2RayCastOutput output;
bool hit = actor->aabb.RayCast(&output, input);
if (hit)
{
m_rayCastOutput = output;
m_rayActor = actor;
m_rayActor->fraction = output.fraction;
return output.fraction;
}
return input.maxFraction;
}
private:
struct Actor
{
b2AABB aabb;
float32 fraction;
bool overlap;
int32 proxyId;
};
void GetRandomAABB(b2AABB* aabb)
{
b2Vec2 w; w.Set(2.0f * m_proxyExtent, 2.0f * m_proxyExtent);
//aabb->lowerBound.x = -m_proxyExtent;
//aabb->lowerBound.y = -m_proxyExtent + m_worldExtent;
aabb->lowerBound.x = RandomFloat(-m_worldExtent, m_worldExtent);
aabb->lowerBound.y = RandomFloat(0.0f, 2.0f * m_worldExtent);
aabb->upperBound = aabb->lowerBound + w;
}
void MoveAABB(b2AABB* aabb)
{
b2Vec2 d;
d.x = RandomFloat(-0.5f, 0.5f);
d.y = RandomFloat(-0.5f, 0.5f);
//d.x = 2.0f;
//d.y = 0.0f;
aabb->lowerBound += d;
aabb->upperBound += d;
b2Vec2 c0 = 0.5f * (aabb->lowerBound + aabb->upperBound);
b2Vec2 min; min.Set(-m_worldExtent, 0.0f);
b2Vec2 max; max.Set(m_worldExtent, 2.0f * m_worldExtent);
b2Vec2 c = b2Clamp(c0, min, max);
aabb->lowerBound += c - c0;
aabb->upperBound += c - c0;
}
void CreateProxy()
{
for (int32 i = 0; i < e_actorCount; ++i)
{
int32 j = rand() % e_actorCount;
Actor* actor = m_actors + j;
if (actor->proxyId == b2_nullNode)
{
GetRandomAABB(&actor->aabb);
actor->proxyId = m_tree.CreateProxy(actor->aabb, actor);
return;
}
}
}
void DestroyProxy()
{
for (int32 i = 0; i < e_actorCount; ++i)
{
int32 j = rand() % e_actorCount;
Actor* actor = m_actors + j;
if (actor->proxyId != b2_nullNode)
{
m_tree.DestroyProxy(actor->proxyId);
actor->proxyId = b2_nullNode;
return;
}
}
}
void MoveProxy()
{
for (int32 i = 0; i < e_actorCount; ++i)
{
int32 j = rand() % e_actorCount;
Actor* actor = m_actors + j;
if (actor->proxyId == b2_nullNode)
{
continue;
}
b2AABB aabb0 = actor->aabb;
MoveAABB(&actor->aabb);
b2Vec2 displacement = actor->aabb.GetCenter() - aabb0.GetCenter();
m_tree.MoveProxy(actor->proxyId, actor->aabb, displacement);
return;
}
}
void Action()
{
int32 choice = rand() % 20;
switch (choice)
{
case 0:
CreateProxy();
break;
case 1:
DestroyProxy();
break;
default:
MoveProxy();
}
}
void Query()
{
m_tree.Query(this, m_queryAABB);
for (int32 i = 0; i < e_actorCount; ++i)
{
if (m_actors[i].proxyId == b2_nullNode)
{
continue;
}
bool overlap = b2TestOverlap(m_queryAABB, m_actors[i].aabb);
B2_NOT_USED(overlap);
b2Assert(overlap == m_actors[i].overlap);
}
}
void RayCast()
{
m_rayActor = NULL;
b2RayCastInput input = m_rayCastInput;
// Ray cast against the dynamic tree.
m_tree.RayCast(this, input);
// Brute force ray cast.
Actor* bruteActor = NULL;
b2RayCastOutput bruteOutput;
for (int32 i = 0; i < e_actorCount; ++i)
{
if (m_actors[i].proxyId == b2_nullNode)
{
continue;
}
b2RayCastOutput output;
bool hit = m_actors[i].aabb.RayCast(&output, input);
if (hit)
{
bruteActor = m_actors + i;
bruteOutput = output;
input.maxFraction = output.fraction;
}
}
if (bruteActor != NULL)
{
b2Assert(bruteOutput.fraction == m_rayCastOutput.fraction);
}
}
float32 m_worldExtent;
float32 m_proxyExtent;
b2DynamicTree m_tree;
b2AABB m_queryAABB;
b2RayCastInput m_rayCastInput;
b2RayCastOutput m_rayCastOutput;
Actor* m_rayActor;
Actor m_actors[e_actorCount];
int32 m_stepCount;
bool m_automated;
};
#endif
/*
* Copyright (c) 2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef DYNAMIC_TREE_TEST_H
#define DYNAMIC_TREE_TEST_H
class DynamicTreeTest : public Test
{
public:
enum
{
e_actorCount = 128
};
DynamicTreeTest()
{
m_worldExtent = 15.0f;
m_proxyExtent = 0.5f;
srand(888);
for (int32 i = 0; i < e_actorCount; ++i)
{
Actor* actor = m_actors + i;
GetRandomAABB(&actor->aabb);
actor->proxyId = m_tree.CreateProxy(actor->aabb, actor);
}
m_stepCount = 0;
float32 h = m_worldExtent;
m_queryAABB.lowerBound.Set(-3.0f, -4.0f + h);
m_queryAABB.upperBound.Set(5.0f, 6.0f + h);
m_rayCastInput.p1.Set(-5.0, 5.0f + h);
m_rayCastInput.p2.Set(7.0f, -4.0f + h);
//m_rayCastInput.p1.Set(0.0f, 2.0f + h);
//m_rayCastInput.p2.Set(0.0f, -2.0f + h);
m_rayCastInput.maxFraction = 1.0f;
m_automated = false;
}
static Test* Create()
{
return new DynamicTreeTest;
}
void Step(Settings* settings)
{
B2_NOT_USED(settings);
m_rayActor = NULL;
for (int32 i = 0; i < e_actorCount; ++i)
{
m_actors[i].fraction = 1.0f;
m_actors[i].overlap = false;
}
if (m_automated == true)
{
int32 actionCount = b2Max(1, e_actorCount >> 2);
for (int32 i = 0; i < actionCount; ++i)
{
Action();
}
}
Query();
RayCast();
for (int32 i = 0; i < e_actorCount; ++i)
{
Actor* actor = m_actors + i;
if (actor->proxyId == b2_nullNode)
continue;
b2Color c(0.9f, 0.9f, 0.9f);
if (actor == m_rayActor && actor->overlap)
{
c.Set(0.9f, 0.6f, 0.6f);
}
else if (actor == m_rayActor)
{
c.Set(0.6f, 0.9f, 0.6f);
}
else if (actor->overlap)
{
c.Set(0.6f, 0.6f, 0.9f);
}
m_debugDraw.DrawAABB(&actor->aabb, c);
}
b2Color c(0.7f, 0.7f, 0.7f);
m_debugDraw.DrawAABB(&m_queryAABB, c);
m_debugDraw.DrawSegment(m_rayCastInput.p1, m_rayCastInput.p2, c);
b2Color c1(0.2f, 0.9f, 0.2f);
b2Color c2(0.9f, 0.2f, 0.2f);
m_debugDraw.DrawPoint(m_rayCastInput.p1, 6.0f, c1);
m_debugDraw.DrawPoint(m_rayCastInput.p2, 6.0f, c2);
if (m_rayActor)
{
b2Color cr(0.2f, 0.2f, 0.9f);
b2Vec2 p = m_rayCastInput.p1 + m_rayActor->fraction * (m_rayCastInput.p2 - m_rayCastInput.p1);
m_debugDraw.DrawPoint(p, 6.0f, cr);
}
{
int32 height = m_tree.GetHeight();
m_debugDraw.DrawString(5, m_textLine, "dynamic tree height = %d", height);
m_textLine += 15;
}
++m_stepCount;
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_automated = !m_automated;
break;
case 'c':
CreateProxy();
break;
case 'd':
DestroyProxy();
break;
case 'm':
MoveProxy();
break;
}
}
bool QueryCallback(int32 proxyId)
{
Actor* actor = (Actor*)m_tree.GetUserData(proxyId);
actor->overlap = b2TestOverlap(m_queryAABB, actor->aabb);
return true;
}
float32 RayCastCallback(const b2RayCastInput& input, int32 proxyId)
{
Actor* actor = (Actor*)m_tree.GetUserData(proxyId);
b2RayCastOutput output;
bool hit = actor->aabb.RayCast(&output, input);
if (hit)
{
m_rayCastOutput = output;
m_rayActor = actor;
m_rayActor->fraction = output.fraction;
return output.fraction;
}
return input.maxFraction;
}
private:
struct Actor
{
b2AABB aabb;
float32 fraction;
bool overlap;
int32 proxyId;
};
void GetRandomAABB(b2AABB* aabb)
{
b2Vec2 w; w.Set(2.0f * m_proxyExtent, 2.0f * m_proxyExtent);
//aabb->lowerBound.x = -m_proxyExtent;
//aabb->lowerBound.y = -m_proxyExtent + m_worldExtent;
aabb->lowerBound.x = RandomFloat(-m_worldExtent, m_worldExtent);
aabb->lowerBound.y = RandomFloat(0.0f, 2.0f * m_worldExtent);
aabb->upperBound = aabb->lowerBound + w;
}
void MoveAABB(b2AABB* aabb)
{
b2Vec2 d;
d.x = RandomFloat(-0.5f, 0.5f);
d.y = RandomFloat(-0.5f, 0.5f);
//d.x = 2.0f;
//d.y = 0.0f;
aabb->lowerBound += d;
aabb->upperBound += d;
b2Vec2 c0 = 0.5f * (aabb->lowerBound + aabb->upperBound);
b2Vec2 min; min.Set(-m_worldExtent, 0.0f);
b2Vec2 max; max.Set(m_worldExtent, 2.0f * m_worldExtent);
b2Vec2 c = b2Clamp(c0, min, max);
aabb->lowerBound += c - c0;
aabb->upperBound += c - c0;
}
void CreateProxy()
{
for (int32 i = 0; i < e_actorCount; ++i)
{
int32 j = rand() % e_actorCount;
Actor* actor = m_actors + j;
if (actor->proxyId == b2_nullNode)
{
GetRandomAABB(&actor->aabb);
actor->proxyId = m_tree.CreateProxy(actor->aabb, actor);
return;
}
}
}
void DestroyProxy()
{
for (int32 i = 0; i < e_actorCount; ++i)
{
int32 j = rand() % e_actorCount;
Actor* actor = m_actors + j;
if (actor->proxyId != b2_nullNode)
{
m_tree.DestroyProxy(actor->proxyId);
actor->proxyId = b2_nullNode;
return;
}
}
}
void MoveProxy()
{
for (int32 i = 0; i < e_actorCount; ++i)
{
int32 j = rand() % e_actorCount;
Actor* actor = m_actors + j;
if (actor->proxyId == b2_nullNode)
{
continue;
}
b2AABB aabb0 = actor->aabb;
MoveAABB(&actor->aabb);
b2Vec2 displacement = actor->aabb.GetCenter() - aabb0.GetCenter();
m_tree.MoveProxy(actor->proxyId, actor->aabb, displacement);
return;
}
}
void Action()
{
int32 choice = rand() % 20;
switch (choice)
{
case 0:
CreateProxy();
break;
case 1:
DestroyProxy();
break;
default:
MoveProxy();
}
}
void Query()
{
m_tree.Query(this, m_queryAABB);
for (int32 i = 0; i < e_actorCount; ++i)
{
if (m_actors[i].proxyId == b2_nullNode)
{
continue;
}
bool overlap = b2TestOverlap(m_queryAABB, m_actors[i].aabb);
B2_NOT_USED(overlap);
b2Assert(overlap == m_actors[i].overlap);
}
}
void RayCast()
{
m_rayActor = NULL;
b2RayCastInput input = m_rayCastInput;
// Ray cast against the dynamic tree.
m_tree.RayCast(this, input);
// Brute force ray cast.
Actor* bruteActor = NULL;
b2RayCastOutput bruteOutput;
for (int32 i = 0; i < e_actorCount; ++i)
{
if (m_actors[i].proxyId == b2_nullNode)
{
continue;
}
b2RayCastOutput output;
bool hit = m_actors[i].aabb.RayCast(&output, input);
if (hit)
{
bruteActor = m_actors + i;
bruteOutput = output;
input.maxFraction = output.fraction;
}
}
if (bruteActor != NULL)
{
b2Assert(bruteOutput.fraction == m_rayCastOutput.fraction);
}
}
float32 m_worldExtent;
float32 m_proxyExtent;
b2DynamicTree m_tree;
b2AABB m_queryAABB;
b2RayCastInput m_rayCastInput;
b2RayCastOutput m_rayCastOutput;
Actor* m_rayActor;
Actor m_actors[e_actorCount];
int32 m_stepCount;
bool m_automated;
};
#endif

View File

@@ -1,249 +1,249 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef EDGE_SHAPES_H
#define EDGE_SHAPES_H
class EdgeShapesCallback : public b2RayCastCallback
{
public:
EdgeShapesCallback()
{
m_fixture = NULL;
}
float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction)
{
m_fixture = fixture;
m_point = point;
m_normal = normal;
return fraction;
}
b2Fixture* m_fixture;
b2Vec2 m_point;
b2Vec2 m_normal;
};
class EdgeShapes : public Test
{
public:
enum
{
e_maxBodies = 256
};
EdgeShapes()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
float32 x1 = -20.0f;
float32 y1 = 2.0f * cosf(x1 / 10.0f * b2_pi);
for (int32 i = 0; i < 80; ++i)
{
float32 x2 = x1 + 0.5f;
float32 y2 = 2.0f * cosf(x2 / 10.0f * b2_pi);
b2EdgeShape shape;
shape.Set(b2Vec2(x1, y1), b2Vec2(x2, y2));
ground->CreateFixture(&shape, 0.0f);
x1 = x2;
y1 = y2;
}
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[0].Set(vertices, 3);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.1f, 0.0f);
vertices[1].Set(0.1f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[1].Set(vertices, 3);
}
{
float32 w = 1.0f;
float32 b = w / (2.0f + b2Sqrt(2.0f));
float32 s = b2Sqrt(2.0f) * b;
b2Vec2 vertices[8];
vertices[0].Set(0.5f * s, 0.0f);
vertices[1].Set(0.5f * w, b);
vertices[2].Set(0.5f * w, b + s);
vertices[3].Set(0.5f * s, w);
vertices[4].Set(-0.5f * s, w);
vertices[5].Set(-0.5f * w, b + s);
vertices[6].Set(-0.5f * w, b);
vertices[7].Set(-0.5f * s, 0.0f);
m_polygons[2].Set(vertices, 8);
}
{
m_polygons[3].SetAsBox(0.5f, 0.5f);
}
{
m_circle.m_radius = 0.5f;
}
m_bodyIndex = 0;
memset(m_bodies, 0, sizeof(m_bodies));
m_angle = 0.0f;
}
void Create(int32 index)
{
if (m_bodies[m_bodyIndex] != NULL)
{
m_world->DestroyBody(m_bodies[m_bodyIndex]);
m_bodies[m_bodyIndex] = NULL;
}
b2BodyDef bd;
float32 x = RandomFloat(-10.0f, 10.0f);
float32 y = RandomFloat(10.0f, 20.0f);
bd.position.Set(x, y);
bd.angle = RandomFloat(-b2_pi, b2_pi);
bd.type = b2_dynamicBody;
if (index == 4)
{
bd.angularDamping = 0.02f;
}
m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
if (index < 4)
{
b2FixtureDef fd;
fd.shape = m_polygons + index;
fd.friction = 0.3f;
fd.density = 20.0f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
else
{
b2FixtureDef fd;
fd.shape = &m_circle;
fd.friction = 0.3f;
fd.density = 20.0f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
}
void DestroyBody()
{
for (int32 i = 0; i < e_maxBodies; ++i)
{
if (m_bodies[i] != NULL)
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
return;
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case '1':
case '2':
case '3':
case '4':
case '5':
Create(key - '1');
break;
case 'd':
DestroyBody();
break;
}
}
void Step(Settings* settings)
{
bool advanceRay = settings->pause == 0 || settings->singleStep;
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff");
m_textLine += 15;
float32 L = 25.0f;
b2Vec2 point1(0.0f, 10.0f);
b2Vec2 d(L * cosf(m_angle), -L * b2Abs(sinf(m_angle)));
b2Vec2 point2 = point1 + d;
EdgeShapesCallback callback;
m_world->RayCast(&callback, point1, point2);
if (callback.m_fixture)
{
m_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
m_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
m_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
}
else
{
m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
}
if (advanceRay)
{
m_angle += 0.25f * b2_pi / 180.0f;
}
}
static Test* Create()
{
return new EdgeShapes;
}
int32 m_bodyIndex;
b2Body* m_bodies[e_maxBodies];
b2PolygonShape m_polygons[4];
b2CircleShape m_circle;
float32 m_angle;
};
#endif
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef EDGE_SHAPES_H
#define EDGE_SHAPES_H
class EdgeShapesCallback : public b2RayCastCallback
{
public:
EdgeShapesCallback()
{
m_fixture = NULL;
}
float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction)
{
m_fixture = fixture;
m_point = point;
m_normal = normal;
return fraction;
}
b2Fixture* m_fixture;
b2Vec2 m_point;
b2Vec2 m_normal;
};
class EdgeShapes : public Test
{
public:
enum
{
e_maxBodies = 256
};
EdgeShapes()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
float32 x1 = -20.0f;
float32 y1 = 2.0f * cosf(x1 / 10.0f * b2_pi);
for (int32 i = 0; i < 80; ++i)
{
float32 x2 = x1 + 0.5f;
float32 y2 = 2.0f * cosf(x2 / 10.0f * b2_pi);
b2EdgeShape shape;
shape.Set(b2Vec2(x1, y1), b2Vec2(x2, y2));
ground->CreateFixture(&shape, 0.0f);
x1 = x2;
y1 = y2;
}
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[0].Set(vertices, 3);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.1f, 0.0f);
vertices[1].Set(0.1f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[1].Set(vertices, 3);
}
{
float32 w = 1.0f;
float32 b = w / (2.0f + b2Sqrt(2.0f));
float32 s = b2Sqrt(2.0f) * b;
b2Vec2 vertices[8];
vertices[0].Set(0.5f * s, 0.0f);
vertices[1].Set(0.5f * w, b);
vertices[2].Set(0.5f * w, b + s);
vertices[3].Set(0.5f * s, w);
vertices[4].Set(-0.5f * s, w);
vertices[5].Set(-0.5f * w, b + s);
vertices[6].Set(-0.5f * w, b);
vertices[7].Set(-0.5f * s, 0.0f);
m_polygons[2].Set(vertices, 8);
}
{
m_polygons[3].SetAsBox(0.5f, 0.5f);
}
{
m_circle.m_radius = 0.5f;
}
m_bodyIndex = 0;
memset(m_bodies, 0, sizeof(m_bodies));
m_angle = 0.0f;
}
void Create(int32 index)
{
if (m_bodies[m_bodyIndex] != NULL)
{
m_world->DestroyBody(m_bodies[m_bodyIndex]);
m_bodies[m_bodyIndex] = NULL;
}
b2BodyDef bd;
float32 x = RandomFloat(-10.0f, 10.0f);
float32 y = RandomFloat(10.0f, 20.0f);
bd.position.Set(x, y);
bd.angle = RandomFloat(-b2_pi, b2_pi);
bd.type = b2_dynamicBody;
if (index == 4)
{
bd.angularDamping = 0.02f;
}
m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
if (index < 4)
{
b2FixtureDef fd;
fd.shape = m_polygons + index;
fd.friction = 0.3f;
fd.density = 20.0f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
else
{
b2FixtureDef fd;
fd.shape = &m_circle;
fd.friction = 0.3f;
fd.density = 20.0f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
}
void DestroyBody()
{
for (int32 i = 0; i < e_maxBodies; ++i)
{
if (m_bodies[i] != NULL)
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
return;
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case '1':
case '2':
case '3':
case '4':
case '5':
Create(key - '1');
break;
case 'd':
DestroyBody();
break;
}
}
void Step(Settings* settings)
{
bool advanceRay = settings->pause == 0 || settings->singleStep;
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff");
m_textLine += 15;
float32 L = 25.0f;
b2Vec2 point1(0.0f, 10.0f);
b2Vec2 d(L * cosf(m_angle), -L * b2Abs(sinf(m_angle)));
b2Vec2 point2 = point1 + d;
EdgeShapesCallback callback;
m_world->RayCast(&callback, point1, point2);
if (callback.m_fixture)
{
m_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
m_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
m_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
}
else
{
m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
}
if (advanceRay)
{
m_angle += 0.25f * b2_pi / 180.0f;
}
}
static Test* Create()
{
return new EdgeShapes;
}
int32 m_bodyIndex;
b2Body* m_bodies[e_maxBodies];
b2PolygonShape m_polygons[4];
b2CircleShape m_circle;
float32 m_angle;
};
#endif

View File

@@ -1,109 +1,109 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef EDGE_TEST_H
#define EDGE_TEST_H
class EdgeTest : public Test
{
public:
EdgeTest()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 v1(-10.0f, 0.0f), v2(-7.0f, -2.0f), v3(-4.0f, 0.0f);
b2Vec2 v4(0.0f, 0.0f), v5(4.0f, 0.0f), v6(7.0f, 2.0f), v7(10.0f, 0.0f);
b2EdgeShape shape;
shape.Set(v1, v2);
shape.m_hasVertex3 = true;
shape.m_vertex3 = v3;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v2, v3);
shape.m_hasVertex0 = true;
shape.m_hasVertex3 = true;
shape.m_vertex0 = v1;
shape.m_vertex3 = v4;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v3, v4);
shape.m_hasVertex0 = true;
shape.m_hasVertex3 = true;
shape.m_vertex0 = v2;
shape.m_vertex3 = v5;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v4, v5);
shape.m_hasVertex0 = true;
shape.m_hasVertex3 = true;
shape.m_vertex0 = v3;
shape.m_vertex3 = v6;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v5, v6);
shape.m_hasVertex0 = true;
shape.m_hasVertex3 = true;
shape.m_vertex0 = v4;
shape.m_vertex3 = v7;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v6, v7);
shape.m_hasVertex0 = true;
shape.m_vertex0 = v5;
ground->CreateFixture(&shape, 0.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-0.5f, 0.6f);
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.5f;
body->CreateFixture(&shape, 1.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(1.0f, 0.6f);
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
body->CreateFixture(&shape, 1.0f);
}
}
static Test* Create()
{
return new EdgeTest;
}
};
#endif
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef EDGE_TEST_H
#define EDGE_TEST_H
class EdgeTest : public Test
{
public:
EdgeTest()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 v1(-10.0f, 0.0f), v2(-7.0f, -2.0f), v3(-4.0f, 0.0f);
b2Vec2 v4(0.0f, 0.0f), v5(4.0f, 0.0f), v6(7.0f, 2.0f), v7(10.0f, 0.0f);
b2EdgeShape shape;
shape.Set(v1, v2);
shape.m_hasVertex3 = true;
shape.m_vertex3 = v3;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v2, v3);
shape.m_hasVertex0 = true;
shape.m_hasVertex3 = true;
shape.m_vertex0 = v1;
shape.m_vertex3 = v4;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v3, v4);
shape.m_hasVertex0 = true;
shape.m_hasVertex3 = true;
shape.m_vertex0 = v2;
shape.m_vertex3 = v5;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v4, v5);
shape.m_hasVertex0 = true;
shape.m_hasVertex3 = true;
shape.m_vertex0 = v3;
shape.m_vertex3 = v6;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v5, v6);
shape.m_hasVertex0 = true;
shape.m_hasVertex3 = true;
shape.m_vertex0 = v4;
shape.m_vertex3 = v7;
ground->CreateFixture(&shape, 0.0f);
shape.Set(v6, v7);
shape.m_hasVertex0 = true;
shape.m_vertex0 = v5;
ground->CreateFixture(&shape, 0.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-0.5f, 0.6f);
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.5f;
body->CreateFixture(&shape, 1.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(1.0f, 0.6f);
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
body->CreateFixture(&shape, 1.0f);
}
}
static Test* Create()
{
return new EdgeTest;
}
};
#endif

View File

@@ -1,187 +1,187 @@
/*
* Copyright (c) 2007-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef GEARS_H
#define GEARS_H
class Gears : public Test
{
public:
Gears()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Gears co
{
b2CircleShape circle1;
circle1.m_radius = 1.0f;
b2PolygonShape box;
box.SetAsBox(0.5f, 5.0f);
b2CircleShape circle2;
circle2.m_radius = 2.0f;
b2BodyDef bd1;
bd1.type = b2_staticBody;
bd1.position.Set(10.0f, 9.0f);
b2Body* body1 = m_world->CreateBody(&bd1);
body1->CreateFixture(&circle1, 0.0f);
b2BodyDef bd2;
bd2.type = b2_dynamicBody;
bd2.position.Set(10.0f, 8.0f);
b2Body* body2 = m_world->CreateBody(&bd2);
body2->CreateFixture(&box, 5.0f);
b2BodyDef bd3;
bd3.type = b2_dynamicBody;
bd3.position.Set(10.0f, 6.0f);
b2Body* body3 = m_world->CreateBody(&bd3);
body3->CreateFixture(&circle2, 5.0f);
b2RevoluteJointDef jd1;
jd1.Initialize(body2, body1, bd1.position);
b2Joint* joint1 = m_world->CreateJoint(&jd1);
b2RevoluteJointDef jd2;
jd2.Initialize(body2, body3, bd3.position);
b2Joint* joint2 = m_world->CreateJoint(&jd2);
b2GearJointDef jd4;
jd4.bodyA = body1;
jd4.bodyB = body3;
jd4.joint1 = joint1;
jd4.joint2 = joint2;
jd4.ratio = circle2.m_radius / circle1.m_radius;
m_world->CreateJoint(&jd4);
}
{
b2CircleShape circle1;
circle1.m_radius = 1.0f;
b2CircleShape circle2;
circle2.m_radius = 2.0f;
b2PolygonShape box;
box.SetAsBox(0.5f, 5.0f);
b2BodyDef bd1;
bd1.type = b2_dynamicBody;
bd1.position.Set(-3.0f, 12.0f);
b2Body* body1 = m_world->CreateBody(&bd1);
body1->CreateFixture(&circle1, 5.0f);
b2RevoluteJointDef jd1;
jd1.bodyA = ground;
jd1.bodyB = body1;
jd1.localAnchorA = ground->GetLocalPoint(bd1.position);
jd1.localAnchorB = body1->GetLocalPoint(bd1.position);
jd1.referenceAngle = body1->GetAngle() - ground->GetAngle();
m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&jd1);
b2BodyDef bd2;
bd2.type = b2_dynamicBody;
bd2.position.Set(0.0f, 12.0f);
b2Body* body2 = m_world->CreateBody(&bd2);
body2->CreateFixture(&circle2, 5.0f);
b2RevoluteJointDef jd2;
jd2.Initialize(ground, body2, bd2.position);
m_joint2 = (b2RevoluteJoint*)m_world->CreateJoint(&jd2);
b2BodyDef bd3;
bd3.type = b2_dynamicBody;
bd3.position.Set(2.5f, 12.0f);
b2Body* body3 = m_world->CreateBody(&bd3);
body3->CreateFixture(&box, 5.0f);
b2PrismaticJointDef jd3;
jd3.Initialize(ground, body3, bd3.position, b2Vec2(0.0f, 1.0f));
jd3.lowerTranslation = -5.0f;
jd3.upperTranslation = 5.0f;
jd3.enableLimit = true;
m_joint3 = (b2PrismaticJoint*)m_world->CreateJoint(&jd3);
b2GearJointDef jd4;
jd4.bodyA = body1;
jd4.bodyB = body2;
jd4.joint1 = m_joint1;
jd4.joint2 = m_joint2;
jd4.ratio = circle2.m_radius / circle1.m_radius;
m_joint4 = (b2GearJoint*)m_world->CreateJoint(&jd4);
b2GearJointDef jd5;
jd5.bodyA = body2;
jd5.bodyB = body3;
jd5.joint1 = m_joint2;
jd5.joint2 = m_joint3;
jd5.ratio = -1.0f / circle2.m_radius;
m_joint5 = (b2GearJoint*)m_world->CreateJoint(&jd5);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 0:
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
float32 ratio, value;
ratio = m_joint4->GetRatio();
value = m_joint1->GetJointAngle() + ratio * m_joint2->GetJointAngle();
m_debugDraw.DrawString(5, m_textLine, "theta1 + %4.2f * theta2 = %4.2f", (float) ratio, (float) value);
m_textLine += 15;
ratio = m_joint5->GetRatio();
value = m_joint2->GetJointAngle() + ratio * m_joint3->GetJointTranslation();
m_debugDraw.DrawString(5, m_textLine, "theta2 + %4.2f * delta = %4.2f", (float) ratio, (float) value);
m_textLine += 15;
}
static Test* Create()
{
return new Gears;
}
b2RevoluteJoint* m_joint1;
b2RevoluteJoint* m_joint2;
b2PrismaticJoint* m_joint3;
b2GearJoint* m_joint4;
b2GearJoint* m_joint5;
};
#endif
/*
* Copyright (c) 2007-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef GEARS_H
#define GEARS_H
class Gears : public Test
{
public:
Gears()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Gears co
{
b2CircleShape circle1;
circle1.m_radius = 1.0f;
b2PolygonShape box;
box.SetAsBox(0.5f, 5.0f);
b2CircleShape circle2;
circle2.m_radius = 2.0f;
b2BodyDef bd1;
bd1.type = b2_staticBody;
bd1.position.Set(10.0f, 9.0f);
b2Body* body1 = m_world->CreateBody(&bd1);
body1->CreateFixture(&circle1, 0.0f);
b2BodyDef bd2;
bd2.type = b2_dynamicBody;
bd2.position.Set(10.0f, 8.0f);
b2Body* body2 = m_world->CreateBody(&bd2);
body2->CreateFixture(&box, 5.0f);
b2BodyDef bd3;
bd3.type = b2_dynamicBody;
bd3.position.Set(10.0f, 6.0f);
b2Body* body3 = m_world->CreateBody(&bd3);
body3->CreateFixture(&circle2, 5.0f);
b2RevoluteJointDef jd1;
jd1.Initialize(body2, body1, bd1.position);
b2Joint* joint1 = m_world->CreateJoint(&jd1);
b2RevoluteJointDef jd2;
jd2.Initialize(body2, body3, bd3.position);
b2Joint* joint2 = m_world->CreateJoint(&jd2);
b2GearJointDef jd4;
jd4.bodyA = body1;
jd4.bodyB = body3;
jd4.joint1 = joint1;
jd4.joint2 = joint2;
jd4.ratio = circle2.m_radius / circle1.m_radius;
m_world->CreateJoint(&jd4);
}
{
b2CircleShape circle1;
circle1.m_radius = 1.0f;
b2CircleShape circle2;
circle2.m_radius = 2.0f;
b2PolygonShape box;
box.SetAsBox(0.5f, 5.0f);
b2BodyDef bd1;
bd1.type = b2_dynamicBody;
bd1.position.Set(-3.0f, 12.0f);
b2Body* body1 = m_world->CreateBody(&bd1);
body1->CreateFixture(&circle1, 5.0f);
b2RevoluteJointDef jd1;
jd1.bodyA = ground;
jd1.bodyB = body1;
jd1.localAnchorA = ground->GetLocalPoint(bd1.position);
jd1.localAnchorB = body1->GetLocalPoint(bd1.position);
jd1.referenceAngle = body1->GetAngle() - ground->GetAngle();
m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&jd1);
b2BodyDef bd2;
bd2.type = b2_dynamicBody;
bd2.position.Set(0.0f, 12.0f);
b2Body* body2 = m_world->CreateBody(&bd2);
body2->CreateFixture(&circle2, 5.0f);
b2RevoluteJointDef jd2;
jd2.Initialize(ground, body2, bd2.position);
m_joint2 = (b2RevoluteJoint*)m_world->CreateJoint(&jd2);
b2BodyDef bd3;
bd3.type = b2_dynamicBody;
bd3.position.Set(2.5f, 12.0f);
b2Body* body3 = m_world->CreateBody(&bd3);
body3->CreateFixture(&box, 5.0f);
b2PrismaticJointDef jd3;
jd3.Initialize(ground, body3, bd3.position, b2Vec2(0.0f, 1.0f));
jd3.lowerTranslation = -5.0f;
jd3.upperTranslation = 5.0f;
jd3.enableLimit = true;
m_joint3 = (b2PrismaticJoint*)m_world->CreateJoint(&jd3);
b2GearJointDef jd4;
jd4.bodyA = body1;
jd4.bodyB = body2;
jd4.joint1 = m_joint1;
jd4.joint2 = m_joint2;
jd4.ratio = circle2.m_radius / circle1.m_radius;
m_joint4 = (b2GearJoint*)m_world->CreateJoint(&jd4);
b2GearJointDef jd5;
jd5.bodyA = body2;
jd5.bodyB = body3;
jd5.joint1 = m_joint2;
jd5.joint2 = m_joint3;
jd5.ratio = -1.0f / circle2.m_radius;
m_joint5 = (b2GearJoint*)m_world->CreateJoint(&jd5);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 0:
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
float32 ratio, value;
ratio = m_joint4->GetRatio();
value = m_joint1->GetJointAngle() + ratio * m_joint2->GetJointAngle();
m_debugDraw.DrawString(5, m_textLine, "theta1 + %4.2f * theta2 = %4.2f", (float) ratio, (float) value);
m_textLine += 15;
ratio = m_joint5->GetRatio();
value = m_joint2->GetJointAngle() + ratio * m_joint3->GetJointTranslation();
m_debugDraw.DrawString(5, m_textLine, "theta2 + %4.2f * delta = %4.2f", (float) ratio, (float) value);
m_textLine += 15;
}
static Test* Create()
{
return new Gears;
}
b2RevoluteJoint* m_joint1;
b2RevoluteJoint* m_joint2;
b2PrismaticJoint* m_joint3;
b2GearJoint* m_joint4;
b2GearJoint* m_joint5;
};
#endif

View File

@@ -1,120 +1,120 @@
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef ONE_SIDED_PLATFORM_H
#define ONE_SIDED_PLATFORM_H
class OneSidedPlatform : public Test
{
public:
enum State
{
e_unknown,
e_above,
e_below
};
OneSidedPlatform()
{
// Ground
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Platform
{
b2BodyDef bd;
bd.position.Set(0.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(3.0f, 0.5f);
m_platform = body->CreateFixture(&shape, 0.0f);
m_bottom = 10.0f - 0.5f;
m_top = 10.0f + 0.5f;
}
// Actor
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
m_radius = 0.5f;
b2CircleShape shape;
shape.m_radius = m_radius;
m_character = body->CreateFixture(&shape, 20.0f);
body->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
m_state = e_unknown;
}
}
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
{
Test::PreSolve(contact, oldManifold);
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
if (fixtureA != m_platform && fixtureA != m_character)
{
return;
}
if (fixtureB != m_platform && fixtureB != m_character)
{
return;
}
b2Vec2 position = m_character->GetBody()->GetPosition();
if (position.y < m_top + m_radius - 3.0f * b2_linearSlop)
{
contact->SetEnabled(false);
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press: (c) create a shape, (d) destroy a shape.");
m_textLine += 15;
}
static Test* Create()
{
return new OneSidedPlatform;
}
float32 m_radius, m_top, m_bottom;
State m_state;
b2Fixture* m_platform;
b2Fixture* m_character;
};
#endif
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef ONE_SIDED_PLATFORM_H
#define ONE_SIDED_PLATFORM_H
class OneSidedPlatform : public Test
{
public:
enum State
{
e_unknown,
e_above,
e_below
};
OneSidedPlatform()
{
// Ground
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Platform
{
b2BodyDef bd;
bd.position.Set(0.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(3.0f, 0.5f);
m_platform = body->CreateFixture(&shape, 0.0f);
m_bottom = 10.0f - 0.5f;
m_top = 10.0f + 0.5f;
}
// Actor
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
m_radius = 0.5f;
b2CircleShape shape;
shape.m_radius = m_radius;
m_character = body->CreateFixture(&shape, 20.0f);
body->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
m_state = e_unknown;
}
}
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
{
Test::PreSolve(contact, oldManifold);
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
if (fixtureA != m_platform && fixtureA != m_character)
{
return;
}
if (fixtureB != m_platform && fixtureB != m_character)
{
return;
}
b2Vec2 position = m_character->GetBody()->GetPosition();
if (position.y < m_top + m_radius - 3.0f * b2_linearSlop)
{
contact->SetEnabled(false);
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press: (c) create a shape, (d) destroy a shape.");
m_textLine += 15;
}
static Test* Create()
{
return new OneSidedPlatform;
}
float32 m_radius, m_top, m_bottom;
State m_state;
b2Fixture* m_platform;
b2Fixture* m_character;
};
#endif

View File

@@ -1,169 +1,169 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef PINBALL_H
#define PINBALL_H
/// This tests bullet collision and provides an example of a gameplay scenario.
/// This also uses a loop shape.
class Pinball : public Test
{
public:
Pinball()
{
// Ground body
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2Vec2 vs[5];
vs[0].Set(0.0f, -2.0f);
vs[1].Set(8.0f, 6.0f);
vs[2].Set(8.0f, 20.0f);
vs[3].Set(-8.0f, 20.0f);
vs[4].Set(-8.0f, 6.0f);
b2ChainShape loop;
loop.CreateLoop(vs, 5);
b2FixtureDef fd;
fd.shape = &loop;
fd.density = 0.0f;
ground->CreateFixture(&fd);
}
// Flippers
{
b2Vec2 p1(-2.0f, 0.0f), p2(2.0f, 0.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = p1;
b2Body* leftFlipper = m_world->CreateBody(&bd);
bd.position = p2;
b2Body* rightFlipper = m_world->CreateBody(&bd);
b2PolygonShape box;
box.SetAsBox(1.75f, 0.1f);
b2FixtureDef fd;
fd.shape = &box;
fd.density = 1.0f;
leftFlipper->CreateFixture(&fd);
rightFlipper->CreateFixture(&fd);
b2RevoluteJointDef jd;
jd.bodyA = ground;
jd.localAnchorB.SetZero();
jd.enableMotor = true;
jd.maxMotorTorque = 1000.0f;
jd.enableLimit = true;
jd.motorSpeed = 0.0f;
jd.localAnchorA = p1;
jd.bodyB = leftFlipper;
jd.lowerAngle = -30.0f * b2_pi / 180.0f;
jd.upperAngle = 5.0f * b2_pi / 180.0f;
m_leftJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
jd.motorSpeed = 0.0f;
jd.localAnchorA = p2;
jd.bodyB = rightFlipper;
jd.lowerAngle = -5.0f * b2_pi / 180.0f;
jd.upperAngle = 30.0f * b2_pi / 180.0f;
m_rightJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(1.0f, 15.0f);
bd.type = b2_dynamicBody;
bd.bullet = true;
m_ball = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.2f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
m_ball->CreateFixture(&fd);
}
m_button = false;
}
void Step()
{
if (m_button)
{
m_leftJoint->SetMotorSpeed(20.0f);
m_rightJoint->SetMotorSpeed(-20.0f);
}
else
{
m_leftJoint->SetMotorSpeed(-10.0f);
m_rightJoint->SetMotorSpeed(10.0f);
}
// Test::Step(settings);
//
// m_debugDraw.DrawString(5, m_textLine, "Press 'a' to control the flippers");
// m_textLine += 15;
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
case 'A':
m_button = true;
break;
}
}
void KeyboardUp(unsigned char key)
{
switch (key)
{
case 'a':
case 'A':
m_button = false;
break;
}
}
static Test* Create()
{
return new Pinball;
}
b2RevoluteJoint* m_leftJoint;
b2RevoluteJoint* m_rightJoint;
b2Body* m_ball;
bool m_button;
};
#endif
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef PINBALL_H
#define PINBALL_H
/// This tests bullet collision and provides an example of a gameplay scenario.
/// This also uses a loop shape.
class Pinball : public Test
{
public:
Pinball()
{
// Ground body
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2Vec2 vs[5];
vs[0].Set(0.0f, -2.0f);
vs[1].Set(8.0f, 6.0f);
vs[2].Set(8.0f, 20.0f);
vs[3].Set(-8.0f, 20.0f);
vs[4].Set(-8.0f, 6.0f);
b2ChainShape loop;
loop.CreateLoop(vs, 5);
b2FixtureDef fd;
fd.shape = &loop;
fd.density = 0.0f;
ground->CreateFixture(&fd);
}
// Flippers
{
b2Vec2 p1(-2.0f, 0.0f), p2(2.0f, 0.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = p1;
b2Body* leftFlipper = m_world->CreateBody(&bd);
bd.position = p2;
b2Body* rightFlipper = m_world->CreateBody(&bd);
b2PolygonShape box;
box.SetAsBox(1.75f, 0.1f);
b2FixtureDef fd;
fd.shape = &box;
fd.density = 1.0f;
leftFlipper->CreateFixture(&fd);
rightFlipper->CreateFixture(&fd);
b2RevoluteJointDef jd;
jd.bodyA = ground;
jd.localAnchorB.SetZero();
jd.enableMotor = true;
jd.maxMotorTorque = 1000.0f;
jd.enableLimit = true;
jd.motorSpeed = 0.0f;
jd.localAnchorA = p1;
jd.bodyB = leftFlipper;
jd.lowerAngle = -30.0f * b2_pi / 180.0f;
jd.upperAngle = 5.0f * b2_pi / 180.0f;
m_leftJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
jd.motorSpeed = 0.0f;
jd.localAnchorA = p2;
jd.bodyB = rightFlipper;
jd.lowerAngle = -5.0f * b2_pi / 180.0f;
jd.upperAngle = 30.0f * b2_pi / 180.0f;
m_rightJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(1.0f, 15.0f);
bd.type = b2_dynamicBody;
bd.bullet = true;
m_ball = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.2f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
m_ball->CreateFixture(&fd);
}
m_button = false;
}
void Step()
{
if (m_button)
{
m_leftJoint->SetMotorSpeed(20.0f);
m_rightJoint->SetMotorSpeed(-20.0f);
}
else
{
m_leftJoint->SetMotorSpeed(-10.0f);
m_rightJoint->SetMotorSpeed(10.0f);
}
// Test::Step(settings);
//
// m_debugDraw.DrawString(5, m_textLine, "Press 'a' to control the flippers");
// m_textLine += 15;
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
case 'A':
m_button = true;
break;
}
}
void KeyboardUp(unsigned char key)
{
switch (key)
{
case 'a':
case 'A':
m_button = false;
break;
}
}
static Test* Create()
{
return new Pinball;
}
b2RevoluteJoint* m_leftJoint;
b2RevoluteJoint* m_rightJoint;
b2Body* m_ball;
bool m_button;
};
#endif

View File

@@ -1,122 +1,122 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef POLYCOLLISION_H
#define POLYCOLLISION_H
class PolyCollision : public Test
{
public:
PolyCollision()
{
{
m_polygonA.SetAsBox(0.2f, 0.4f);
m_transformA.Set(b2Vec2(0.0f, 0.0f), 0.0f);
}
{
m_polygonB.SetAsBox(0.5f, 0.5f);
m_positionB.Set(19.345284f, 1.5632932f);
m_angleB = 1.9160721f;
m_transformB.Set(m_positionB, m_angleB);
}
}
static Test* Create()
{
return new PolyCollision;
}
void Step(Settings* settings)
{
B2_NOT_USED(settings);
b2Manifold manifold;
b2CollidePolygons(&manifold, &m_polygonA, m_transformA, &m_polygonB, m_transformB);
b2WorldManifold worldManifold;
worldManifold.Initialize(&manifold, m_transformA, m_polygonA.m_radius, m_transformB, m_polygonB.m_radius);
m_debugDraw.DrawString(5, m_textLine, "point count = %d", manifold.pointCount);
m_textLine += 15;
{
b2Color color(0.9f, 0.9f, 0.9f);
b2Vec2 v[b2_maxPolygonVertices];
for (int32 i = 0; i < m_polygonA.m_vertexCount; ++i)
{
v[i] = b2Mul(m_transformA, m_polygonA.m_vertices[i]);
}
m_debugDraw.DrawPolygon(v, m_polygonA.m_vertexCount, color);
for (int32 i = 0; i < m_polygonB.m_vertexCount; ++i)
{
v[i] = b2Mul(m_transformB, m_polygonB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(v, m_polygonB.m_vertexCount, color);
}
for (int32 i = 0; i < manifold.pointCount; ++i)
{
m_debugDraw.DrawPoint(worldManifold.points[i], 4.0f, b2Color(0.9f, 0.3f, 0.3f));
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_positionB.x -= 0.1f;
break;
case 'd':
m_positionB.x += 0.1f;
break;
case 's':
m_positionB.y -= 0.1f;
break;
case 'w':
m_positionB.y += 0.1f;
break;
case 'q':
m_angleB += 0.1f * b2_pi;
break;
case 'e':
m_angleB -= 0.1f * b2_pi;
break;
}
m_transformB.Set(m_positionB, m_angleB);
}
b2PolygonShape m_polygonA;
b2PolygonShape m_polygonB;
b2Transform m_transformA;
b2Transform m_transformB;
b2Vec2 m_positionB;
float32 m_angleB;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef POLYCOLLISION_H
#define POLYCOLLISION_H
class PolyCollision : public Test
{
public:
PolyCollision()
{
{
m_polygonA.SetAsBox(0.2f, 0.4f);
m_transformA.Set(b2Vec2(0.0f, 0.0f), 0.0f);
}
{
m_polygonB.SetAsBox(0.5f, 0.5f);
m_positionB.Set(19.345284f, 1.5632932f);
m_angleB = 1.9160721f;
m_transformB.Set(m_positionB, m_angleB);
}
}
static Test* Create()
{
return new PolyCollision;
}
void Step(Settings* settings)
{
B2_NOT_USED(settings);
b2Manifold manifold;
b2CollidePolygons(&manifold, &m_polygonA, m_transformA, &m_polygonB, m_transformB);
b2WorldManifold worldManifold;
worldManifold.Initialize(&manifold, m_transformA, m_polygonA.m_radius, m_transformB, m_polygonB.m_radius);
m_debugDraw.DrawString(5, m_textLine, "point count = %d", manifold.pointCount);
m_textLine += 15;
{
b2Color color(0.9f, 0.9f, 0.9f);
b2Vec2 v[b2_maxPolygonVertices];
for (int32 i = 0; i < m_polygonA.m_vertexCount; ++i)
{
v[i] = b2Mul(m_transformA, m_polygonA.m_vertices[i]);
}
m_debugDraw.DrawPolygon(v, m_polygonA.m_vertexCount, color);
for (int32 i = 0; i < m_polygonB.m_vertexCount; ++i)
{
v[i] = b2Mul(m_transformB, m_polygonB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(v, m_polygonB.m_vertexCount, color);
}
for (int32 i = 0; i < manifold.pointCount; ++i)
{
m_debugDraw.DrawPoint(worldManifold.points[i], 4.0f, b2Color(0.9f, 0.3f, 0.3f));
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_positionB.x -= 0.1f;
break;
case 'd':
m_positionB.x += 0.1f;
break;
case 's':
m_positionB.y -= 0.1f;
break;
case 'w':
m_positionB.y += 0.1f;
break;
case 'q':
m_angleB += 0.1f * b2_pi;
break;
case 'e':
m_angleB -= 0.1f * b2_pi;
break;
}
m_transformB.Set(m_positionB, m_angleB);
}
b2PolygonShape m_polygonA;
b2PolygonShape m_polygonB;
b2Transform m_transformA;
b2Transform m_transformB;
b2Vec2 m_positionB;
float32 m_angleB;
};
#endif

View File

@@ -1,295 +1,295 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef POLY_SHAPES_H
#define POLY_SHAPES_H
/// This tests stacking. It also shows how to use b2World::Query
/// and b2TestOverlap.
const int32 k_maxBodies = 256;
/// This callback is called by b2World::QueryAABB. We find all the fixtures
/// that overlap an AABB. Of those, we use b2TestOverlap to determine which fixtures
/// overlap a circle. Up to 4 overlapped fixtures will be highlighted with a yellow border.
class PolyShapesCallback : public b2QueryCallback
{
public:
enum
{
e_maxCount = 4
};
PolyShapesCallback()
{
m_count = 0;
}
void DrawFixture(b2Fixture* fixture)
{
b2Color color(0.95f, 0.95f, 0.6f);
const b2Transform& xf = fixture->GetBody()->GetTransform();
switch (fixture->GetType())
{
case b2Shape::e_circle:
{
b2CircleShape* circle = (b2CircleShape*)fixture->GetShape();
b2Vec2 center = b2Mul(xf, circle->m_p);
float32 radius = circle->m_radius;
m_debugDraw->DrawCircle(center, radius, color);
}
break;
case b2Shape::e_polygon:
{
b2PolygonShape* poly = (b2PolygonShape*)fixture->GetShape();
int32 vertexCount = poly->m_vertexCount;
b2Assert(vertexCount <= b2_maxPolygonVertices);
b2Vec2 vertices[b2_maxPolygonVertices];
for (int32 i = 0; i < vertexCount; ++i)
{
vertices[i] = b2Mul(xf, poly->m_vertices[i]);
}
m_debugDraw->DrawPolygon(vertices, vertexCount, color);
}
break;
default:
break;
}
}
/// Called for each fixture found in the query AABB.
/// @return false to terminate the query.
bool ReportFixture(b2Fixture* fixture)
{
if (m_count == e_maxCount)
{
return false;
}
b2Body* body = fixture->GetBody();
b2Shape* shape = fixture->GetShape();
bool overlap = b2TestOverlap(shape, 0, &m_circle, 0, body->GetTransform(), m_transform);
if (overlap)
{
DrawFixture(fixture);
++m_count;
}
return true;
}
b2CircleShape m_circle;
b2Transform m_transform;
b2Draw* m_debugDraw;
int32 m_count;
};
class PolyShapes : public Test
{
public:
PolyShapes()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[0].Set(vertices, 3);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.1f, 0.0f);
vertices[1].Set(0.1f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[1].Set(vertices, 3);
}
{
float32 w = 1.0f;
float32 b = w / (2.0f + b2Sqrt(2.0f));
float32 s = b2Sqrt(2.0f) * b;
b2Vec2 vertices[8];
vertices[0].Set(0.5f * s, 0.0f);
vertices[1].Set(0.5f * w, b);
vertices[2].Set(0.5f * w, b + s);
vertices[3].Set(0.5f * s, w);
vertices[4].Set(-0.5f * s, w);
vertices[5].Set(-0.5f * w, b + s);
vertices[6].Set(-0.5f * w, b);
vertices[7].Set(-0.5f * s, 0.0f);
m_polygons[2].Set(vertices, 8);
}
{
m_polygons[3].SetAsBox(0.5f, 0.5f);
}
{
m_circle.m_radius = 0.5f;
}
m_bodyIndex = 0;
memset(m_bodies, 0, sizeof(m_bodies));
}
void Create(int32 index)
{
if (m_bodies[m_bodyIndex] != NULL)
{
m_world->DestroyBody(m_bodies[m_bodyIndex]);
m_bodies[m_bodyIndex] = NULL;
}
b2BodyDef bd;
bd.type = b2_dynamicBody;
float32 x = RandomFloat(-2.0f, 2.0f);
bd.position.Set(x, 10.0f);
bd.angle = RandomFloat(-b2_pi, b2_pi);
if (index == 4)
{
bd.angularDamping = 0.02f;
}
m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
if (index < 4)
{
b2FixtureDef fd;
fd.shape = m_polygons + index;
fd.density = 1.0f;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
else
{
b2FixtureDef fd;
fd.shape = &m_circle;
fd.density = 1.0f;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
m_bodyIndex = (m_bodyIndex + 1) % k_maxBodies;
}
void DestroyBody()
{
for (int32 i = 0; i < k_maxBodies; ++i)
{
if (m_bodies[i] != NULL)
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
return;
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case '1':
case '2':
case '3':
case '4':
case '5':
Create(key - '1');
break;
case 'a':
for (int32 i = 0; i < k_maxBodies; i += 2)
{
if (m_bodies[i])
{
bool active = m_bodies[i]->IsActive();
m_bodies[i]->SetActive(!active);
}
}
break;
case 'd':
DestroyBody();
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
PolyShapesCallback callback;
callback.m_circle.m_radius = 2.0f;
callback.m_circle.m_p.Set(0.0f, 1.1f);
callback.m_transform.SetIdentity();
callback.m_debugDraw = &m_debugDraw;
b2AABB aabb;
callback.m_circle.ComputeAABB(&aabb, callback.m_transform, 0);
m_world->QueryAABB(&callback, aabb);
b2Color color(0.4f, 0.7f, 0.8f);
m_debugDraw.DrawCircle(callback.m_circle.m_p, callback.m_circle.m_radius, color);
m_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Press 'a' to (de)activate some bodies");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Press 'd' to destroy a body");
m_textLine += 15;
}
static Test* Create()
{
return new PolyShapes;
}
int32 m_bodyIndex;
b2Body* m_bodies[k_maxBodies];
b2PolygonShape m_polygons[4];
b2CircleShape m_circle;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef POLY_SHAPES_H
#define POLY_SHAPES_H
/// This tests stacking. It also shows how to use b2World::Query
/// and b2TestOverlap.
const int32 k_maxBodies = 256;
/// This callback is called by b2World::QueryAABB. We find all the fixtures
/// that overlap an AABB. Of those, we use b2TestOverlap to determine which fixtures
/// overlap a circle. Up to 4 overlapped fixtures will be highlighted with a yellow border.
class PolyShapesCallback : public b2QueryCallback
{
public:
enum
{
e_maxCount = 4
};
PolyShapesCallback()
{
m_count = 0;
}
void DrawFixture(b2Fixture* fixture)
{
b2Color color(0.95f, 0.95f, 0.6f);
const b2Transform& xf = fixture->GetBody()->GetTransform();
switch (fixture->GetType())
{
case b2Shape::e_circle:
{
b2CircleShape* circle = (b2CircleShape*)fixture->GetShape();
b2Vec2 center = b2Mul(xf, circle->m_p);
float32 radius = circle->m_radius;
m_debugDraw->DrawCircle(center, radius, color);
}
break;
case b2Shape::e_polygon:
{
b2PolygonShape* poly = (b2PolygonShape*)fixture->GetShape();
int32 vertexCount = poly->m_vertexCount;
b2Assert(vertexCount <= b2_maxPolygonVertices);
b2Vec2 vertices[b2_maxPolygonVertices];
for (int32 i = 0; i < vertexCount; ++i)
{
vertices[i] = b2Mul(xf, poly->m_vertices[i]);
}
m_debugDraw->DrawPolygon(vertices, vertexCount, color);
}
break;
default:
break;
}
}
/// Called for each fixture found in the query AABB.
/// @return false to terminate the query.
bool ReportFixture(b2Fixture* fixture)
{
if (m_count == e_maxCount)
{
return false;
}
b2Body* body = fixture->GetBody();
b2Shape* shape = fixture->GetShape();
bool overlap = b2TestOverlap(shape, 0, &m_circle, 0, body->GetTransform(), m_transform);
if (overlap)
{
DrawFixture(fixture);
++m_count;
}
return true;
}
b2CircleShape m_circle;
b2Transform m_transform;
b2Draw* m_debugDraw;
int32 m_count;
};
class PolyShapes : public Test
{
public:
PolyShapes()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[0].Set(vertices, 3);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.1f, 0.0f);
vertices[1].Set(0.1f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[1].Set(vertices, 3);
}
{
float32 w = 1.0f;
float32 b = w / (2.0f + b2Sqrt(2.0f));
float32 s = b2Sqrt(2.0f) * b;
b2Vec2 vertices[8];
vertices[0].Set(0.5f * s, 0.0f);
vertices[1].Set(0.5f * w, b);
vertices[2].Set(0.5f * w, b + s);
vertices[3].Set(0.5f * s, w);
vertices[4].Set(-0.5f * s, w);
vertices[5].Set(-0.5f * w, b + s);
vertices[6].Set(-0.5f * w, b);
vertices[7].Set(-0.5f * s, 0.0f);
m_polygons[2].Set(vertices, 8);
}
{
m_polygons[3].SetAsBox(0.5f, 0.5f);
}
{
m_circle.m_radius = 0.5f;
}
m_bodyIndex = 0;
memset(m_bodies, 0, sizeof(m_bodies));
}
void Create(int32 index)
{
if (m_bodies[m_bodyIndex] != NULL)
{
m_world->DestroyBody(m_bodies[m_bodyIndex]);
m_bodies[m_bodyIndex] = NULL;
}
b2BodyDef bd;
bd.type = b2_dynamicBody;
float32 x = RandomFloat(-2.0f, 2.0f);
bd.position.Set(x, 10.0f);
bd.angle = RandomFloat(-b2_pi, b2_pi);
if (index == 4)
{
bd.angularDamping = 0.02f;
}
m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
if (index < 4)
{
b2FixtureDef fd;
fd.shape = m_polygons + index;
fd.density = 1.0f;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
else
{
b2FixtureDef fd;
fd.shape = &m_circle;
fd.density = 1.0f;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
m_bodyIndex = (m_bodyIndex + 1) % k_maxBodies;
}
void DestroyBody()
{
for (int32 i = 0; i < k_maxBodies; ++i)
{
if (m_bodies[i] != NULL)
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
return;
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case '1':
case '2':
case '3':
case '4':
case '5':
Create(key - '1');
break;
case 'a':
for (int32 i = 0; i < k_maxBodies; i += 2)
{
if (m_bodies[i])
{
bool active = m_bodies[i]->IsActive();
m_bodies[i]->SetActive(!active);
}
}
break;
case 'd':
DestroyBody();
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
PolyShapesCallback callback;
callback.m_circle.m_radius = 2.0f;
callback.m_circle.m_p.Set(0.0f, 1.1f);
callback.m_transform.SetIdentity();
callback.m_debugDraw = &m_debugDraw;
b2AABB aabb;
callback.m_circle.ComputeAABB(&aabb, callback.m_transform, 0);
m_world->QueryAABB(&callback, aabb);
b2Color color(0.4f, 0.7f, 0.8f);
m_debugDraw.DrawCircle(callback.m_circle.m_p, callback.m_circle.m_radius, color);
m_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Press 'a' to (de)activate some bodies");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Press 'd' to destroy a body");
m_textLine += 15;
}
static Test* Create()
{
return new PolyShapes;
}
int32 m_bodyIndex;
b2Body* m_bodies[k_maxBodies];
b2PolygonShape m_polygons[4];
b2CircleShape m_circle;
};
#endif

View File

@@ -1,107 +1,107 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef PRISMATIC_H
#define PRISMATIC_H
// The motor in this test gets smoother with higher velocity iterations.
class Prismatic : public Test
{
public:
Prismatic()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(2.0f, 0.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f, 10.0f);
bd.angle = 0.5f * b2_pi;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
b2PrismaticJointDef pjd;
// Bouncy limit
b2Vec2 axis(2.0f, 1.0f);
axis.Normalize();
pjd.Initialize(ground, body, b2Vec2(0.0f, 0.0f), axis);
// Non-bouncy limit
//pjd.Initialize(ground, body, b2Vec2(-10.0f, 10.0f), b2Vec2(1.0f, 0.0f));
pjd.motorSpeed = 10.0f;
pjd.maxMotorForce = 10000.0f;
pjd.enableMotor = true;
pjd.lowerTranslation = 0.0f;
pjd.upperTranslation = 20.0f;
pjd.enableLimit = true;
m_joint = (b2PrismaticJoint*)m_world->CreateJoint(&pjd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'l':
m_joint->EnableLimit(!m_joint->IsLimitEnabled());
break;
case 'm':
m_joint->EnableMotor(!m_joint->IsMotorEnabled());
break;
case 's':
m_joint->SetMotorSpeed(-m_joint->GetMotorSpeed());
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (l) limits, (m) motors, (s) speed");
m_textLine += 15;
float32 force = m_joint->GetMotorForce(settings->hz);
m_debugDraw.DrawString(5, m_textLine, "Motor Force = %4.0f", (float) force);
m_textLine += 15;
}
static Test* Create()
{
return new Prismatic;
}
b2PrismaticJoint* m_joint;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef PRISMATIC_H
#define PRISMATIC_H
// The motor in this test gets smoother with higher velocity iterations.
class Prismatic : public Test
{
public:
Prismatic()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(2.0f, 0.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f, 10.0f);
bd.angle = 0.5f * b2_pi;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
b2PrismaticJointDef pjd;
// Bouncy limit
b2Vec2 axis(2.0f, 1.0f);
axis.Normalize();
pjd.Initialize(ground, body, b2Vec2(0.0f, 0.0f), axis);
// Non-bouncy limit
//pjd.Initialize(ground, body, b2Vec2(-10.0f, 10.0f), b2Vec2(1.0f, 0.0f));
pjd.motorSpeed = 10.0f;
pjd.maxMotorForce = 10000.0f;
pjd.enableMotor = true;
pjd.lowerTranslation = 0.0f;
pjd.upperTranslation = 20.0f;
pjd.enableLimit = true;
m_joint = (b2PrismaticJoint*)m_world->CreateJoint(&pjd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'l':
m_joint->EnableLimit(!m_joint->IsLimitEnabled());
break;
case 'm':
m_joint->EnableMotor(!m_joint->IsMotorEnabled());
break;
case 's':
m_joint->SetMotorSpeed(-m_joint->GetMotorSpeed());
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (l) limits, (m) motors, (s) speed");
m_textLine += 15;
float32 force = m_joint->GetMotorForce(settings->hz);
m_debugDraw.DrawString(5, m_textLine, "Motor Force = %4.0f", (float) force);
m_textLine += 15;
}
static Test* Create()
{
return new Prismatic;
}
b2PrismaticJoint* m_joint;
};
#endif

View File

@@ -1,106 +1,106 @@
/*
* Copyright (c) 2007-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef PULLEYS_H
#define PULLEYS_H
class Pulleys : public Test
{
public:
Pulleys()
{
float32 y = 16.0f;
float32 L = 12.0f;
float32 a = 1.0f;
float32 b = 2.0f;
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
//ground->CreateFixture(&shape, 0.0f);
b2CircleShape circle;
circle.m_radius = 2.0f;
circle.m_p.Set(-10.0f, y + b + L);
ground->CreateFixture(&circle, 0.0f);
circle.m_p.Set(10.0f, y + b + L);
ground->CreateFixture(&circle, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(a, b);
b2BodyDef bd;
bd.type = b2_dynamicBody;
//bd.fixedRotation = true;
bd.position.Set(-10.0f, y);
b2Body* body1 = m_world->CreateBody(&bd);
body1->CreateFixture(&shape, 5.0f);
bd.position.Set(10.0f, y);
b2Body* body2 = m_world->CreateBody(&bd);
body2->CreateFixture(&shape, 5.0f);
b2PulleyJointDef pulleyDef;
b2Vec2 anchor1(-10.0f, y + b);
b2Vec2 anchor2(10.0f, y + b);
b2Vec2 groundAnchor1(-10.0f, y + b + L);
b2Vec2 groundAnchor2(10.0f, y + b + L);
pulleyDef.Initialize(body1, body2, groundAnchor1, groundAnchor2, anchor1, anchor2, 1.5f);
m_joint1 = (b2PulleyJoint*)m_world->CreateJoint(&pulleyDef);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 0:
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
float32 ratio = m_joint1->GetRatio();
float32 L = m_joint1->GetLengthA() + ratio * m_joint1->GetLengthB();
m_debugDraw.DrawString(5, m_textLine, "L1 + %4.2f * L2 = %4.2f", (float) ratio, (float) L);
m_textLine += 15;
}
static Test* Create()
{
return new Pulleys;
}
b2PulleyJoint* m_joint1;
};
#endif
/*
* Copyright (c) 2007-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef PULLEYS_H
#define PULLEYS_H
class Pulleys : public Test
{
public:
Pulleys()
{
float32 y = 16.0f;
float32 L = 12.0f;
float32 a = 1.0f;
float32 b = 2.0f;
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
//ground->CreateFixture(&shape, 0.0f);
b2CircleShape circle;
circle.m_radius = 2.0f;
circle.m_p.Set(-10.0f, y + b + L);
ground->CreateFixture(&circle, 0.0f);
circle.m_p.Set(10.0f, y + b + L);
ground->CreateFixture(&circle, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(a, b);
b2BodyDef bd;
bd.type = b2_dynamicBody;
//bd.fixedRotation = true;
bd.position.Set(-10.0f, y);
b2Body* body1 = m_world->CreateBody(&bd);
body1->CreateFixture(&shape, 5.0f);
bd.position.Set(10.0f, y);
b2Body* body2 = m_world->CreateBody(&bd);
body2->CreateFixture(&shape, 5.0f);
b2PulleyJointDef pulleyDef;
b2Vec2 anchor1(-10.0f, y + b);
b2Vec2 anchor2(10.0f, y + b);
b2Vec2 groundAnchor1(-10.0f, y + b + L);
b2Vec2 groundAnchor2(10.0f, y + b + L);
pulleyDef.Initialize(body1, body2, groundAnchor1, groundAnchor2, anchor1, anchor2, 1.5f);
m_joint1 = (b2PulleyJoint*)m_world->CreateJoint(&pulleyDef);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 0:
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
float32 ratio = m_joint1->GetRatio();
float32 L = m_joint1->GetLengthA() + ratio * m_joint1->GetLengthB();
m_debugDraw.DrawString(5, m_textLine, "L1 + %4.2f * L2 = %4.2f", (float) ratio, (float) L);
m_textLine += 15;
}
static Test* Create()
{
return new Pulleys;
}
b2PulleyJoint* m_joint1;
};
#endif

View File

@@ -1,89 +1,89 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef PYRAMID_H
#define PYRAMID_H
class Pyramid : public Test
{
public:
enum
{
e_count = 20
};
Pyramid()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
float32 a = 0.5f;
b2PolygonShape shape;
shape.SetAsBox(a, a);
b2Vec2 x(-7.0f, 0.75f);
b2Vec2 y;
b2Vec2 deltaX(0.5625f, 1.25f);
b2Vec2 deltaY(1.125f, 0.0f);
for (int32 i = 0; i < e_count; ++i)
{
y = x;
for (int32 j = i; j < e_count; ++j)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = y;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
y += deltaY;
}
x += deltaX;
}
}
}
void Step(Settings* settings)
{
Test::Step(settings);
//b2DynamicTree* tree = &m_world->m_contactManager.m_broadPhase.m_tree;
//if (m_stepCount == 400)
//{
// tree->RebuildBottomUp();
//}
}
static Test* Create()
{
return new Pyramid;
}
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef PYRAMID_H
#define PYRAMID_H
class Pyramid : public Test
{
public:
enum
{
e_count = 20
};
Pyramid()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
float32 a = 0.5f;
b2PolygonShape shape;
shape.SetAsBox(a, a);
b2Vec2 x(-7.0f, 0.75f);
b2Vec2 y;
b2Vec2 deltaX(0.5625f, 1.25f);
b2Vec2 deltaY(1.125f, 0.0f);
for (int32 i = 0; i < e_count; ++i)
{
y = x;
for (int32 j = i; j < e_count; ++j)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = y;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
y += deltaY;
}
x += deltaX;
}
}
}
void Step(Settings* settings)
{
Test::Step(settings);
//b2DynamicTree* tree = &m_world->m_contactManager.m_broadPhase.m_tree;
//if (m_stepCount == 400)
//{
// tree->RebuildBottomUp();
//}
}
static Test* Create()
{
return new Pyramid;
}
};
#endif

View File

@@ -1,440 +1,440 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef RAY_CAST_H
#define RAY_CAST_H
// This test demonstrates how to use the world ray-cast feature.
// NOTE: we are intentionally filtering one of the polygons, therefore
// the ray will always miss one type of polygon.
// This callback finds the closest hit. Polygon 0 is filtered.
class RayCastClosestCallback : public b2RayCastCallback
{
public:
RayCastClosestCallback()
{
m_hit = false;
}
float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction)
{
b2Body* body = fixture->GetBody();
void* userData = body->GetUserData();
if (userData)
{
int32 index = *(int32*)userData;
if (index == 0)
{
// filter
return -1.0f;
}
}
m_hit = true;
m_point = point;
m_normal = normal;
return fraction;
}
bool m_hit;
b2Vec2 m_point;
b2Vec2 m_normal;
};
// This callback finds any hit. Polygon 0 is filtered.
class RayCastAnyCallback : public b2RayCastCallback
{
public:
RayCastAnyCallback()
{
m_hit = false;
}
float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction)
{
b2Body* body = fixture->GetBody();
void* userData = body->GetUserData();
if (userData)
{
int32 index = *(int32*)userData;
if (index == 0)
{
// filter
return -1.0f;
}
}
m_hit = true;
m_point = point;
m_normal = normal;
return 0.0f;
}
bool m_hit;
b2Vec2 m_point;
b2Vec2 m_normal;
};
// This ray cast collects multiple hits along the ray. Polygon 0 is filtered.
class RayCastMultipleCallback : public b2RayCastCallback
{
public:
enum
{
e_maxCount = 3
};
RayCastMultipleCallback()
{
m_count = 0;
}
float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction)
{
b2Body* body = fixture->GetBody();
void* userData = body->GetUserData();
if (userData)
{
int32 index = *(int32*)userData;
if (index == 0)
{
// filter
return -1.0f;
}
}
b2Assert(m_count < e_maxCount);
m_points[m_count] = point;
m_normals[m_count] = normal;
++m_count;
if (m_count == e_maxCount)
{
return 0.0f;
}
return 1.0f;
}
b2Vec2 m_points[e_maxCount];
b2Vec2 m_normals[e_maxCount];
int32 m_count;
};
class RayCast : public Test
{
public:
enum
{
e_maxBodies = 256
};
enum Mode
{
e_closest,
e_any,
e_multiple
};
RayCast()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[0].Set(vertices, 3);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.1f, 0.0f);
vertices[1].Set(0.1f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[1].Set(vertices, 3);
}
{
float32 w = 1.0f;
float32 b = w / (2.0f + b2Sqrt(2.0f));
float32 s = b2Sqrt(2.0f) * b;
b2Vec2 vertices[8];
vertices[0].Set(0.5f * s, 0.0f);
vertices[1].Set(0.5f * w, b);
vertices[2].Set(0.5f * w, b + s);
vertices[3].Set(0.5f * s, w);
vertices[4].Set(-0.5f * s, w);
vertices[5].Set(-0.5f * w, b + s);
vertices[6].Set(-0.5f * w, b);
vertices[7].Set(-0.5f * s, 0.0f);
m_polygons[2].Set(vertices, 8);
}
{
m_polygons[3].SetAsBox(0.5f, 0.5f);
}
{
m_circle.m_radius = 0.5f;
}
m_bodyIndex = 0;
memset(m_bodies, 0, sizeof(m_bodies));
m_angle = 0.0f;
m_mode = e_closest;
}
void Create(int32 index)
{
if (m_bodies[m_bodyIndex] != NULL)
{
m_world->DestroyBody(m_bodies[m_bodyIndex]);
m_bodies[m_bodyIndex] = NULL;
}
b2BodyDef bd;
float32 x = RandomFloat(-10.0f, 10.0f);
float32 y = RandomFloat(0.0f, 20.0f);
bd.position.Set(x, y);
bd.angle = RandomFloat(-b2_pi, b2_pi);
m_userData[m_bodyIndex] = index;
bd.userData = m_userData + m_bodyIndex;
if (index == 4)
{
bd.angularDamping = 0.02f;
}
m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
if (index < 4)
{
b2FixtureDef fd;
fd.shape = m_polygons + index;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
else
{
b2FixtureDef fd;
fd.shape = &m_circle;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
}
void DestroyBody()
{
for (int32 i = 0; i < e_maxBodies; ++i)
{
if (m_bodies[i] != NULL)
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
return;
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case '1':
case '2':
case '3':
case '4':
case '5':
Create(key - '1');
break;
case 'd':
DestroyBody();
break;
case 'm':
if (m_mode == e_closest)
{
m_mode = e_any;
}
else if (m_mode == e_any)
{
m_mode = e_multiple;
}
else if (m_mode == e_multiple)
{
m_mode = e_closest;
}
}
}
void Step(Settings* settings)
{
bool advanceRay = settings->pause == 0 || settings->singleStep;
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff, m to change the mode");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Mode = %d", m_mode);
m_textLine += 15;
float32 L = 11.0f;
b2Vec2 point1(0.0f, 10.0f);
b2Vec2 d(L * cosf(m_angle), L * sinf(m_angle));
b2Vec2 point2 = point1 + d;
if (m_mode == e_closest)
{
RayCastClosestCallback callback;
m_world->RayCast(&callback, point1, point2);
if (callback.m_hit)
{
m_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
m_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
m_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
}
else
{
m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
}
}
else if (m_mode == e_any)
{
RayCastAnyCallback callback;
m_world->RayCast(&callback, point1, point2);
if (callback.m_hit)
{
m_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
m_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
m_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
}
else
{
m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
}
}
else if (m_mode == e_multiple)
{
RayCastMultipleCallback callback;
m_world->RayCast(&callback, point1, point2);
m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
for (int32 i = 0; i < callback.m_count; ++i)
{
b2Vec2 p = callback.m_points[i];
b2Vec2 n = callback.m_normals[i];
m_debugDraw.DrawPoint(p, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
m_debugDraw.DrawSegment(point1, p, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = p + 0.5f * n;
m_debugDraw.DrawSegment(p, head, b2Color(0.9f, 0.9f, 0.4f));
}
}
if (advanceRay)
{
m_angle += 0.25f * b2_pi / 180.0f;
}
#if 0
// This case was failing.
{
b2Vec2 vertices[4];
//vertices[0].Set(-22.875f, -3.0f);
//vertices[1].Set(22.875f, -3.0f);
//vertices[2].Set(22.875f, 3.0f);
//vertices[3].Set(-22.875f, 3.0f);
b2PolygonShape shape;
//shape.Set(vertices, 4);
shape.SetAsBox(22.875f, 3.0f);
b2RayCastInput input;
input.p1.Set(10.2725f,1.71372f);
input.p2.Set(10.2353f,2.21807f);
//input.maxFraction = 0.567623f;
input.maxFraction = 0.56762173f;
b2Transform xf;
xf.SetIdentity();
xf.position.Set(23.0f, 5.0f);
b2RayCastOutput output;
bool hit;
hit = shape.RayCast(&output, input, xf);
hit = false;
b2Color color(1.0f, 1.0f, 1.0f);
b2Vec2 vs[4];
for (int32 i = 0; i < 4; ++i)
{
vs[i] = b2Mul(xf, shape.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vs, 4, color);
m_debugDraw.DrawSegment(input.p1, input.p2, color);
}
#endif
}
static Test* Create()
{
return new RayCast;
}
int32 m_bodyIndex;
b2Body* m_bodies[e_maxBodies];
int32 m_userData[e_maxBodies];
b2PolygonShape m_polygons[4];
b2CircleShape m_circle;
float32 m_angle;
Mode m_mode;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef RAY_CAST_H
#define RAY_CAST_H
// This test demonstrates how to use the world ray-cast feature.
// NOTE: we are intentionally filtering one of the polygons, therefore
// the ray will always miss one type of polygon.
// This callback finds the closest hit. Polygon 0 is filtered.
class RayCastClosestCallback : public b2RayCastCallback
{
public:
RayCastClosestCallback()
{
m_hit = false;
}
float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction)
{
b2Body* body = fixture->GetBody();
void* userData = body->GetUserData();
if (userData)
{
int32 index = *(int32*)userData;
if (index == 0)
{
// filter
return -1.0f;
}
}
m_hit = true;
m_point = point;
m_normal = normal;
return fraction;
}
bool m_hit;
b2Vec2 m_point;
b2Vec2 m_normal;
};
// This callback finds any hit. Polygon 0 is filtered.
class RayCastAnyCallback : public b2RayCastCallback
{
public:
RayCastAnyCallback()
{
m_hit = false;
}
float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction)
{
b2Body* body = fixture->GetBody();
void* userData = body->GetUserData();
if (userData)
{
int32 index = *(int32*)userData;
if (index == 0)
{
// filter
return -1.0f;
}
}
m_hit = true;
m_point = point;
m_normal = normal;
return 0.0f;
}
bool m_hit;
b2Vec2 m_point;
b2Vec2 m_normal;
};
// This ray cast collects multiple hits along the ray. Polygon 0 is filtered.
class RayCastMultipleCallback : public b2RayCastCallback
{
public:
enum
{
e_maxCount = 3
};
RayCastMultipleCallback()
{
m_count = 0;
}
float32 ReportFixture( b2Fixture* fixture, const b2Vec2& point,
const b2Vec2& normal, float32 fraction)
{
b2Body* body = fixture->GetBody();
void* userData = body->GetUserData();
if (userData)
{
int32 index = *(int32*)userData;
if (index == 0)
{
// filter
return -1.0f;
}
}
b2Assert(m_count < e_maxCount);
m_points[m_count] = point;
m_normals[m_count] = normal;
++m_count;
if (m_count == e_maxCount)
{
return 0.0f;
}
return 1.0f;
}
b2Vec2 m_points[e_maxCount];
b2Vec2 m_normals[e_maxCount];
int32 m_count;
};
class RayCast : public Test
{
public:
enum
{
e_maxBodies = 256
};
enum Mode
{
e_closest,
e_any,
e_multiple
};
RayCast()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[0].Set(vertices, 3);
}
{
b2Vec2 vertices[3];
vertices[0].Set(-0.1f, 0.0f);
vertices[1].Set(0.1f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
m_polygons[1].Set(vertices, 3);
}
{
float32 w = 1.0f;
float32 b = w / (2.0f + b2Sqrt(2.0f));
float32 s = b2Sqrt(2.0f) * b;
b2Vec2 vertices[8];
vertices[0].Set(0.5f * s, 0.0f);
vertices[1].Set(0.5f * w, b);
vertices[2].Set(0.5f * w, b + s);
vertices[3].Set(0.5f * s, w);
vertices[4].Set(-0.5f * s, w);
vertices[5].Set(-0.5f * w, b + s);
vertices[6].Set(-0.5f * w, b);
vertices[7].Set(-0.5f * s, 0.0f);
m_polygons[2].Set(vertices, 8);
}
{
m_polygons[3].SetAsBox(0.5f, 0.5f);
}
{
m_circle.m_radius = 0.5f;
}
m_bodyIndex = 0;
memset(m_bodies, 0, sizeof(m_bodies));
m_angle = 0.0f;
m_mode = e_closest;
}
void Create(int32 index)
{
if (m_bodies[m_bodyIndex] != NULL)
{
m_world->DestroyBody(m_bodies[m_bodyIndex]);
m_bodies[m_bodyIndex] = NULL;
}
b2BodyDef bd;
float32 x = RandomFloat(-10.0f, 10.0f);
float32 y = RandomFloat(0.0f, 20.0f);
bd.position.Set(x, y);
bd.angle = RandomFloat(-b2_pi, b2_pi);
m_userData[m_bodyIndex] = index;
bd.userData = m_userData + m_bodyIndex;
if (index == 4)
{
bd.angularDamping = 0.02f;
}
m_bodies[m_bodyIndex] = m_world->CreateBody(&bd);
if (index < 4)
{
b2FixtureDef fd;
fd.shape = m_polygons + index;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
else
{
b2FixtureDef fd;
fd.shape = &m_circle;
fd.friction = 0.3f;
m_bodies[m_bodyIndex]->CreateFixture(&fd);
}
m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
}
void DestroyBody()
{
for (int32 i = 0; i < e_maxBodies; ++i)
{
if (m_bodies[i] != NULL)
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
return;
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case '1':
case '2':
case '3':
case '4':
case '5':
Create(key - '1');
break;
case 'd':
DestroyBody();
break;
case 'm':
if (m_mode == e_closest)
{
m_mode = e_any;
}
else if (m_mode == e_any)
{
m_mode = e_multiple;
}
else if (m_mode == e_multiple)
{
m_mode = e_closest;
}
}
}
void Step(Settings* settings)
{
bool advanceRay = settings->pause == 0 || settings->singleStep;
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press 1-5 to drop stuff, m to change the mode");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Mode = %d", m_mode);
m_textLine += 15;
float32 L = 11.0f;
b2Vec2 point1(0.0f, 10.0f);
b2Vec2 d(L * cosf(m_angle), L * sinf(m_angle));
b2Vec2 point2 = point1 + d;
if (m_mode == e_closest)
{
RayCastClosestCallback callback;
m_world->RayCast(&callback, point1, point2);
if (callback.m_hit)
{
m_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
m_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
m_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
}
else
{
m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
}
}
else if (m_mode == e_any)
{
RayCastAnyCallback callback;
m_world->RayCast(&callback, point1, point2);
if (callback.m_hit)
{
m_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
m_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = callback.m_point + 0.5f * callback.m_normal;
m_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f));
}
else
{
m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
}
}
else if (m_mode == e_multiple)
{
RayCastMultipleCallback callback;
m_world->RayCast(&callback, point1, point2);
m_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f));
for (int32 i = 0; i < callback.m_count; ++i)
{
b2Vec2 p = callback.m_points[i];
b2Vec2 n = callback.m_normals[i];
m_debugDraw.DrawPoint(p, 5.0f, b2Color(0.4f, 0.9f, 0.4f));
m_debugDraw.DrawSegment(point1, p, b2Color(0.8f, 0.8f, 0.8f));
b2Vec2 head = p + 0.5f * n;
m_debugDraw.DrawSegment(p, head, b2Color(0.9f, 0.9f, 0.4f));
}
}
if (advanceRay)
{
m_angle += 0.25f * b2_pi / 180.0f;
}
#if 0
// This case was failing.
{
b2Vec2 vertices[4];
//vertices[0].Set(-22.875f, -3.0f);
//vertices[1].Set(22.875f, -3.0f);
//vertices[2].Set(22.875f, 3.0f);
//vertices[3].Set(-22.875f, 3.0f);
b2PolygonShape shape;
//shape.Set(vertices, 4);
shape.SetAsBox(22.875f, 3.0f);
b2RayCastInput input;
input.p1.Set(10.2725f,1.71372f);
input.p2.Set(10.2353f,2.21807f);
//input.maxFraction = 0.567623f;
input.maxFraction = 0.56762173f;
b2Transform xf;
xf.SetIdentity();
xf.position.Set(23.0f, 5.0f);
b2RayCastOutput output;
bool hit;
hit = shape.RayCast(&output, input, xf);
hit = false;
b2Color color(1.0f, 1.0f, 1.0f);
b2Vec2 vs[4];
for (int32 i = 0; i < 4; ++i)
{
vs[i] = b2Mul(xf, shape.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vs, 4, color);
m_debugDraw.DrawSegment(input.p1, input.p2, color);
}
#endif
}
static Test* Create()
{
return new RayCast;
}
int32 m_bodyIndex;
b2Body* m_bodies[e_maxBodies];
int32 m_userData[e_maxBodies];
b2PolygonShape m_polygons[4];
b2CircleShape m_circle;
float32 m_angle;
Mode m_mode;
};
#endif

View File

@@ -1,166 +1,166 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef REVOLUTE_H
#define REVOLUTE_H
class Revolute : public Test
{
public:
Revolute()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2FixtureDef fd;
fd.shape = &shape;
//fd.filter.categoryBits = 2;
ground->CreateFixture(&fd);
}
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
b2RevoluteJointDef rjd;
bd.position.Set(-10.0f, 20.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
float32 w = 100.0f;
body->SetAngularVelocity(w);
body->SetLinearVelocity(b2Vec2(-8.0f * w, 0.0f));
rjd.Initialize(ground, body, b2Vec2(-10.0f, 12.0f));
rjd.motorSpeed = 1.0f * b2_pi;
rjd.maxMotorTorque = 10000.0f;
rjd.enableMotor = false;
rjd.lowerAngle = -0.25f * b2_pi;
rjd.upperAngle = 0.5f * b2_pi;
rjd.enableLimit = true;
rjd.collideConnected = true;
m_joint = (b2RevoluteJoint*)m_world->CreateJoint(&rjd);
}
{
b2CircleShape circle_shape;
circle_shape.m_radius = 3.0f;
b2BodyDef circle_bd;
circle_bd.type = b2_dynamicBody;
circle_bd.position.Set(5.0f, 30.0f);
b2FixtureDef fd;
fd.density = 5.0f;
fd.filter.maskBits = 1;
fd.shape = &circle_shape;
m_ball = m_world->CreateBody(&circle_bd);
m_ball->CreateFixture(&fd);
b2PolygonShape polygon_shape;
polygon_shape.SetAsBox(10.0f, 0.2f, b2Vec2 (-10.0f, 0.0f), 0.0f);
b2BodyDef polygon_bd;
polygon_bd.position.Set(20.0f, 10.0f);
polygon_bd.type = b2_dynamicBody;
polygon_bd.bullet = true;
b2Body* polygon_body = m_world->CreateBody(&polygon_bd);
polygon_body->CreateFixture(&polygon_shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(ground, polygon_body, b2Vec2(20.0f, 10.0f));
rjd.lowerAngle = -0.25f * b2_pi;
rjd.upperAngle = 0.0f * b2_pi;
rjd.enableLimit = true;
m_world->CreateJoint(&rjd);
}
// Tests mass computation of a small object far from the origin
{
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
b2Body* body = m_world->CreateBody(&bodyDef);
b2PolygonShape polyShape;
b2Vec2 verts[3];
verts[0].Set( 17.63f, 36.31f );
verts[1].Set( 17.52f, 36.69f );
verts[2].Set( 17.19f, 36.36f );
polyShape.Set(verts, 3);
b2FixtureDef polyFixtureDef;
polyFixtureDef.shape = &polyShape;
polyFixtureDef.density = 1;
body->CreateFixture(&polyFixtureDef); //assertion hits inside here
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'l':
m_joint->EnableLimit(!m_joint->IsLimitEnabled());
break;
case 'm':
m_joint->EnableMotor(!m_joint->IsMotorEnabled());
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (l) limits, (m) motor");
m_textLine += 15;
//if (m_stepCount == 360)
//{
// m_ball->SetTransform(b2Vec2(0.0f, 0.5f), 0.0f);
//}
//float32 torque1 = m_joint1->GetMotorTorque();
//m_debugDraw.DrawString(5, m_textLine, "Motor Torque = %4.0f, %4.0f : Motor Force = %4.0f", (float) torque1, (float) torque2, (float) force3);
//m_textLine += 15;
}
static Test* Create()
{
return new Revolute;
}
b2Body* m_ball;
b2RevoluteJoint* m_joint;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef REVOLUTE_H
#define REVOLUTE_H
class Revolute : public Test
{
public:
Revolute()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2FixtureDef fd;
fd.shape = &shape;
//fd.filter.categoryBits = 2;
ground->CreateFixture(&fd);
}
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
b2RevoluteJointDef rjd;
bd.position.Set(-10.0f, 20.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
float32 w = 100.0f;
body->SetAngularVelocity(w);
body->SetLinearVelocity(b2Vec2(-8.0f * w, 0.0f));
rjd.Initialize(ground, body, b2Vec2(-10.0f, 12.0f));
rjd.motorSpeed = 1.0f * b2_pi;
rjd.maxMotorTorque = 10000.0f;
rjd.enableMotor = false;
rjd.lowerAngle = -0.25f * b2_pi;
rjd.upperAngle = 0.5f * b2_pi;
rjd.enableLimit = true;
rjd.collideConnected = true;
m_joint = (b2RevoluteJoint*)m_world->CreateJoint(&rjd);
}
{
b2CircleShape circle_shape;
circle_shape.m_radius = 3.0f;
b2BodyDef circle_bd;
circle_bd.type = b2_dynamicBody;
circle_bd.position.Set(5.0f, 30.0f);
b2FixtureDef fd;
fd.density = 5.0f;
fd.filter.maskBits = 1;
fd.shape = &circle_shape;
m_ball = m_world->CreateBody(&circle_bd);
m_ball->CreateFixture(&fd);
b2PolygonShape polygon_shape;
polygon_shape.SetAsBox(10.0f, 0.2f, b2Vec2 (-10.0f, 0.0f), 0.0f);
b2BodyDef polygon_bd;
polygon_bd.position.Set(20.0f, 10.0f);
polygon_bd.type = b2_dynamicBody;
polygon_bd.bullet = true;
b2Body* polygon_body = m_world->CreateBody(&polygon_bd);
polygon_body->CreateFixture(&polygon_shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(ground, polygon_body, b2Vec2(20.0f, 10.0f));
rjd.lowerAngle = -0.25f * b2_pi;
rjd.upperAngle = 0.0f * b2_pi;
rjd.enableLimit = true;
m_world->CreateJoint(&rjd);
}
// Tests mass computation of a small object far from the origin
{
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
b2Body* body = m_world->CreateBody(&bodyDef);
b2PolygonShape polyShape;
b2Vec2 verts[3];
verts[0].Set( 17.63f, 36.31f );
verts[1].Set( 17.52f, 36.69f );
verts[2].Set( 17.19f, 36.36f );
polyShape.Set(verts, 3);
b2FixtureDef polyFixtureDef;
polyFixtureDef.shape = &polyShape;
polyFixtureDef.density = 1;
body->CreateFixture(&polyFixtureDef); //assertion hits inside here
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'l':
m_joint->EnableLimit(!m_joint->IsLimitEnabled());
break;
case 'm':
m_joint->EnableMotor(!m_joint->IsMotorEnabled());
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (l) limits, (m) motor");
m_textLine += 15;
//if (m_stepCount == 360)
//{
// m_ball->SetTransform(b2Vec2(0.0f, 0.5f), 0.0f);
//}
//float32 torque1 = m_joint1->GetMotorTorque();
//m_debugDraw.DrawString(5, m_textLine, "Motor Torque = %4.0f, %4.0f : Motor Force = %4.0f", (float) torque1, (float) torque2, (float) force3);
//m_textLine += 15;
}
static Test* Create()
{
return new Revolute;
}
b2Body* m_ball;
b2RevoluteJoint* m_joint;
};
#endif

View File

@@ -1,101 +1,101 @@
/*
* Copyright (c) 2011 Erin Catto http://box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef ROPE_H
#define ROPE_H
///
class Rope : public Test
{
public:
Rope()
{
const int32 N = 40;
b2Vec2 vertices[N];
float32 masses[N];
for (int32 i = 0; i < N; ++i)
{
vertices[i].Set(0.0f, 20.0f - 0.25f * i);
masses[i] = 1.0f;
}
masses[0] = 0.0f;
masses[1] = 0.0f;
b2RopeDef def;
def.vertices = vertices;
def.count = N;
def.gravity.Set(0.0f, -10.0f);
def.masses = masses;
def.damping = 0.1f;
def.k2 = 1.0f;
def.k3 = 0.5f;
m_rope.Initialize(&def);
m_angle = 0.0f;
m_rope.SetAngle(m_angle);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'q':
m_angle = b2Max(-b2_pi, m_angle - 0.05f * b2_pi);
m_rope.SetAngle(m_angle);
break;
case 'e':
m_angle = b2Min(b2_pi, m_angle + 0.05f * b2_pi);
m_rope.SetAngle(m_angle);
break;
}
}
void Step(Settings* settings)
{
float32 dt = settings->hz > 0.0f ? 1.0f / settings->hz : 0.0f;
if (settings->pause == 1 && settings->singleStep == 0)
{
dt = 0.0f;
}
m_rope.Step(dt, 1);
Test::Step(settings);
m_rope.Draw(&m_debugDraw);
m_debugDraw.DrawString(5, m_textLine, "Press (q,e) to adjust target angle");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Target angle = %g degrees", m_angle * 180.0f / b2_pi);
m_textLine += 15;
}
static Test* Create()
{
return new Rope;
}
b2Rope m_rope;
float32 m_angle;
};
#endif
/*
* Copyright (c) 2011 Erin Catto http://box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef ROPE_H
#define ROPE_H
///
class Rope : public Test
{
public:
Rope()
{
const int32 N = 40;
b2Vec2 vertices[N];
float32 masses[N];
for (int32 i = 0; i < N; ++i)
{
vertices[i].Set(0.0f, 20.0f - 0.25f * i);
masses[i] = 1.0f;
}
masses[0] = 0.0f;
masses[1] = 0.0f;
b2RopeDef def;
def.vertices = vertices;
def.count = N;
def.gravity.Set(0.0f, -10.0f);
def.masses = masses;
def.damping = 0.1f;
def.k2 = 1.0f;
def.k3 = 0.5f;
m_rope.Initialize(&def);
m_angle = 0.0f;
m_rope.SetAngle(m_angle);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'q':
m_angle = b2Max(-b2_pi, m_angle - 0.05f * b2_pi);
m_rope.SetAngle(m_angle);
break;
case 'e':
m_angle = b2Min(b2_pi, m_angle + 0.05f * b2_pi);
m_rope.SetAngle(m_angle);
break;
}
}
void Step(Settings* settings)
{
float32 dt = settings->hz > 0.0f ? 1.0f / settings->hz : 0.0f;
if (settings->pause == 1 && settings->singleStep == 0)
{
dt = 0.0f;
}
m_rope.Step(dt, 1);
Test::Step(settings);
m_rope.Draw(&m_debugDraw);
m_debugDraw.DrawString(5, m_textLine, "Press (q,e) to adjust target angle");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Target angle = %g degrees", m_angle * 180.0f / b2_pi);
m_textLine += 15;
}
static Test* Create()
{
return new Rope;
}
b2Rope m_rope;
float32 m_angle;
};
#endif

View File

@@ -1,145 +1,145 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef ROPE_JOINT_H
#define ROPE_JOINT_H
/// This test shows how a rope joint can be used to stabilize a chain of
/// bodies with a heavy payload. Notice that the rope joint just prevents
/// excessive stretching and has no other effect.
/// By disabling the rope joint you can see that the Box2D solver has trouble
/// supporting heavy bodies with light bodies. Try playing around with the
/// densities, time step, and iterations to see how they affect stability.
/// This test also shows how to use contact filtering. Filtering is configured
/// so that the payload does not collide with the chain.
class RopeJoint : public Test
{
public:
RopeJoint()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
fd.filter.categoryBits = 0x0001;
fd.filter.maskBits = 0xFFFF & ~0x0002;
b2RevoluteJointDef jd;
jd.collideConnected = false;
const int32 N = 10;
const float32 y = 15.0f;
m_ropeDef.localAnchorA.Set(0.0f, y);
b2Body* prevBody = ground;
for (int32 i = 0; i < N; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.5f + 1.0f * i, y);
if (i == N - 1)
{
shape.SetAsBox(1.5f, 1.5f);
fd.density = 100.0f;
fd.filter.categoryBits = 0x0002;
bd.position.Set(1.0f * i, y);
bd.angularDamping = 0.4f;
}
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(float32(i), y);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
m_ropeDef.localAnchorB.SetZero();
float32 extraLength = 0.01f;
m_ropeDef.maxLength = N - 1.0f + extraLength;
m_ropeDef.bodyB = prevBody;
}
{
m_ropeDef.bodyA = ground;
m_rope = m_world->CreateJoint(&m_ropeDef);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'j':
if (m_rope)
{
m_world->DestroyJoint(m_rope);
m_rope = NULL;
}
else
{
m_rope = m_world->CreateJoint(&m_ropeDef);
}
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press (j) to toggle the rope joint.");
m_textLine += 15;
if (m_rope)
{
m_debugDraw.DrawString(5, m_textLine, "Rope ON");
}
else
{
m_debugDraw.DrawString(5, m_textLine, "Rope OFF");
}
m_textLine += 15;
}
static Test* Create()
{
return new RopeJoint;
}
b2RopeJointDef m_ropeDef;
b2Joint* m_rope;
};
#endif
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef ROPE_JOINT_H
#define ROPE_JOINT_H
/// This test shows how a rope joint can be used to stabilize a chain of
/// bodies with a heavy payload. Notice that the rope joint just prevents
/// excessive stretching and has no other effect.
/// By disabling the rope joint you can see that the Box2D solver has trouble
/// supporting heavy bodies with light bodies. Try playing around with the
/// densities, time step, and iterations to see how they affect stability.
/// This test also shows how to use contact filtering. Filtering is configured
/// so that the payload does not collide with the chain.
class RopeJoint : public Test
{
public:
RopeJoint()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
fd.filter.categoryBits = 0x0001;
fd.filter.maskBits = 0xFFFF & ~0x0002;
b2RevoluteJointDef jd;
jd.collideConnected = false;
const int32 N = 10;
const float32 y = 15.0f;
m_ropeDef.localAnchorA.Set(0.0f, y);
b2Body* prevBody = ground;
for (int32 i = 0; i < N; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.5f + 1.0f * i, y);
if (i == N - 1)
{
shape.SetAsBox(1.5f, 1.5f);
fd.density = 100.0f;
fd.filter.categoryBits = 0x0002;
bd.position.Set(1.0f * i, y);
bd.angularDamping = 0.4f;
}
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(float32(i), y);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
m_ropeDef.localAnchorB.SetZero();
float32 extraLength = 0.01f;
m_ropeDef.maxLength = N - 1.0f + extraLength;
m_ropeDef.bodyB = prevBody;
}
{
m_ropeDef.bodyA = ground;
m_rope = m_world->CreateJoint(&m_ropeDef);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'j':
if (m_rope)
{
m_world->DestroyJoint(m_rope);
m_rope = NULL;
}
else
{
m_rope = m_world->CreateJoint(&m_ropeDef);
}
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press (j) to toggle the rope joint.");
m_textLine += 15;
if (m_rope)
{
m_debugDraw.DrawString(5, m_textLine, "Rope ON");
}
else
{
m_debugDraw.DrawString(5, m_textLine, "Rope OFF");
}
m_textLine += 15;
}
static Test* Create()
{
return new RopeJoint;
}
b2RopeJointDef m_ropeDef;
b2Joint* m_rope;
};
#endif

View File

@@ -1,181 +1,181 @@
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SENSOR_TEST_H
#define SENSOR_TEST_H
// This is used to test sensor shapes.
class SensorTest : public Test
{
public:
enum
{
e_count = 7
};
SensorTest()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
{
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
#if 0
{
b2FixtureDef sd;
sd.SetAsBox(10.0f, 2.0f, b2Vec2(0.0f, 20.0f), 0.0f);
sd.isSensor = true;
m_sensor = ground->CreateFixture(&sd);
}
#else
{
b2CircleShape shape;
shape.m_radius = 5.0f;
shape.m_p.Set(0.0f, 10.0f);
b2FixtureDef fd;
fd.shape = &shape;
fd.isSensor = true;
m_sensor = ground->CreateFixture(&fd);
}
#endif
}
{
b2CircleShape shape;
shape.m_radius = 1.0f;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + 3.0f * i, 20.0f);
bd.userData = m_touching + i;
m_touching[i] = false;
m_bodies[i] = m_world->CreateBody(&bd);
m_bodies[i]->CreateFixture(&shape, 1.0f);
}
}
}
// Implement contact listener.
void BeginContact(b2Contact* contact)
{
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
if (fixtureA == m_sensor)
{
void* userData = fixtureB->GetBody()->GetUserData();
if (userData)
{
bool* touching = (bool*)userData;
*touching = true;
}
}
if (fixtureB == m_sensor)
{
void* userData = fixtureA->GetBody()->GetUserData();
if (userData)
{
bool* touching = (bool*)userData;
*touching = true;
}
}
}
// Implement contact listener.
void EndContact(b2Contact* contact)
{
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
if (fixtureA == m_sensor)
{
void* userData = fixtureB->GetBody()->GetUserData();
if (userData)
{
bool* touching = (bool*)userData;
*touching = false;
}
}
if (fixtureB == m_sensor)
{
void* userData = fixtureA->GetBody()->GetUserData();
if (userData)
{
bool* touching = (bool*)userData;
*touching = false;
}
}
}
void Step(Settings* settings)
{
Test::Step(settings);
// Traverse the contact results. Apply a force on shapes
// that overlap the sensor.
for (int32 i = 0; i < e_count; ++i)
{
if (m_touching[i] == false)
{
continue;
}
b2Body* body = m_bodies[i];
b2Body* ground = m_sensor->GetBody();
b2CircleShape* circle = (b2CircleShape*)m_sensor->GetShape();
b2Vec2 center = ground->GetWorldPoint(circle->m_p);
b2Vec2 position = body->GetPosition();
b2Vec2 d = center - position;
if (d.LengthSquared() < FLT_EPSILON * FLT_EPSILON)
{
continue;
}
d.Normalize();
b2Vec2 F = 100.0f * d;
body->ApplyForce(F, position);
}
}
static Test* Create()
{
return new SensorTest;
}
b2Fixture* m_sensor;
b2Body* m_bodies[e_count];
bool m_touching[e_count];
};
#endif
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SENSOR_TEST_H
#define SENSOR_TEST_H
// This is used to test sensor shapes.
class SensorTest : public Test
{
public:
enum
{
e_count = 7
};
SensorTest()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
{
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
#if 0
{
b2FixtureDef sd;
sd.SetAsBox(10.0f, 2.0f, b2Vec2(0.0f, 20.0f), 0.0f);
sd.isSensor = true;
m_sensor = ground->CreateFixture(&sd);
}
#else
{
b2CircleShape shape;
shape.m_radius = 5.0f;
shape.m_p.Set(0.0f, 10.0f);
b2FixtureDef fd;
fd.shape = &shape;
fd.isSensor = true;
m_sensor = ground->CreateFixture(&fd);
}
#endif
}
{
b2CircleShape shape;
shape.m_radius = 1.0f;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + 3.0f * i, 20.0f);
bd.userData = m_touching + i;
m_touching[i] = false;
m_bodies[i] = m_world->CreateBody(&bd);
m_bodies[i]->CreateFixture(&shape, 1.0f);
}
}
}
// Implement contact listener.
void BeginContact(b2Contact* contact)
{
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
if (fixtureA == m_sensor)
{
void* userData = fixtureB->GetBody()->GetUserData();
if (userData)
{
bool* touching = (bool*)userData;
*touching = true;
}
}
if (fixtureB == m_sensor)
{
void* userData = fixtureA->GetBody()->GetUserData();
if (userData)
{
bool* touching = (bool*)userData;
*touching = true;
}
}
}
// Implement contact listener.
void EndContact(b2Contact* contact)
{
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
if (fixtureA == m_sensor)
{
void* userData = fixtureB->GetBody()->GetUserData();
if (userData)
{
bool* touching = (bool*)userData;
*touching = false;
}
}
if (fixtureB == m_sensor)
{
void* userData = fixtureA->GetBody()->GetUserData();
if (userData)
{
bool* touching = (bool*)userData;
*touching = false;
}
}
}
void Step(Settings* settings)
{
Test::Step(settings);
// Traverse the contact results. Apply a force on shapes
// that overlap the sensor.
for (int32 i = 0; i < e_count; ++i)
{
if (m_touching[i] == false)
{
continue;
}
b2Body* body = m_bodies[i];
b2Body* ground = m_sensor->GetBody();
b2CircleShape* circle = (b2CircleShape*)m_sensor->GetShape();
b2Vec2 center = ground->GetWorldPoint(circle->m_p);
b2Vec2 position = body->GetPosition();
b2Vec2 d = center - position;
if (d.LengthSquared() < FLT_EPSILON * FLT_EPSILON)
{
continue;
}
d.Normalize();
b2Vec2 F = 100.0f * d;
body->ApplyForce(F, position);
}
}
static Test* Create()
{
return new SensorTest;
}
b2Fixture* m_sensor;
b2Body* m_bodies[e_count];
bool m_touching[e_count];
};
#endif

View File

@@ -1,105 +1,105 @@
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SHAPE_EDITING_H
#define SHAPE_EDITING_H
class ShapeEditing : public Test
{
public:
ShapeEditing()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 10.0f);
m_body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(4.0f, 4.0f, b2Vec2(0.0f, 0.0f), 0.0f);
m_fixture1 = m_body->CreateFixture(&shape, 10.0f);
m_fixture2 = NULL;
m_sensor = false;
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'c':
if (m_fixture2 == NULL)
{
b2CircleShape shape;
shape.m_radius = 3.0f;
shape.m_p.Set(0.5f, -4.0f);
m_fixture2 = m_body->CreateFixture(&shape, 10.0f);
m_body->SetAwake(true);
}
break;
case 'd':
if (m_fixture2 != NULL)
{
m_body->DestroyFixture(m_fixture2);
m_fixture2 = NULL;
m_body->SetAwake(true);
}
break;
case 's':
if (m_fixture2 != NULL)
{
m_sensor = !m_sensor;
m_fixture2->SetSensor(m_sensor);
}
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press: (c) create a shape, (d) destroy a shape.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "sensor = %d", m_sensor);
m_textLine += 15;
}
static Test* Create()
{
return new ShapeEditing;
}
b2Body* m_body;
b2Fixture* m_fixture1;
b2Fixture* m_fixture2;
bool m_sensor;
};
#endif
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SHAPE_EDITING_H
#define SHAPE_EDITING_H
class ShapeEditing : public Test
{
public:
ShapeEditing()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 10.0f);
m_body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(4.0f, 4.0f, b2Vec2(0.0f, 0.0f), 0.0f);
m_fixture1 = m_body->CreateFixture(&shape, 10.0f);
m_fixture2 = NULL;
m_sensor = false;
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'c':
if (m_fixture2 == NULL)
{
b2CircleShape shape;
shape.m_radius = 3.0f;
shape.m_p.Set(0.5f, -4.0f);
m_fixture2 = m_body->CreateFixture(&shape, 10.0f);
m_body->SetAwake(true);
}
break;
case 'd':
if (m_fixture2 != NULL)
{
m_body->DestroyFixture(m_fixture2);
m_fixture2 = NULL;
m_body->SetAwake(true);
}
break;
case 's':
if (m_fixture2 != NULL)
{
m_sensor = !m_sensor;
m_fixture2->SetSensor(m_sensor);
}
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press: (c) create a shape, (d) destroy a shape.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "sensor = %d", m_sensor);
m_textLine += 15;
}
static Test* Create()
{
return new ShapeEditing;
}
b2Body* m_body;
b2Fixture* m_fixture1;
b2Fixture* m_fixture2;
bool m_sensor;
};
#endif

View File

@@ -1,156 +1,156 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SLIDER_CRANK_H
#define SLIDER_CRANK_H
// A motor driven slider crank with joint friction.
class SliderCrank : public Test
{
public:
SliderCrank()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2Body* prevBody = ground;
// Define crank.
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 2.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 7.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(0.0f, 5.0f));
rjd.motorSpeed = 1.0f * b2_pi;
rjd.maxMotorTorque = 10000.0f;
rjd.enableMotor = true;
m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&rjd);
prevBody = body;
}
// Define follower.
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 4.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 13.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(0.0f, 9.0f));
rjd.enableMotor = false;
m_world->CreateJoint(&rjd);
prevBody = body;
}
// Define piston
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.position.Set(0.0f, 17.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(0.0f, 17.0f));
m_world->CreateJoint(&rjd);
b2PrismaticJointDef pjd;
pjd.Initialize(ground, body, b2Vec2(0.0f, 17.0f), b2Vec2(0.0f, 1.0f));
pjd.maxMotorForce = 1000.0f;
pjd.enableMotor = true;
m_joint2 = (b2PrismaticJoint*)m_world->CreateJoint(&pjd);
}
// Create a payload
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 23.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'f':
m_joint2->EnableMotor(!m_joint2->IsMotorEnabled());
m_joint2->GetBodyB()->SetAwake(true);
break;
case 'm':
m_joint1->EnableMotor(!m_joint1->IsMotorEnabled());
m_joint1->GetBodyB()->SetAwake(true);
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (f) toggle friction, (m) toggle motor");
m_textLine += 15;
float32 torque = m_joint1->GetMotorTorque(settings->hz);
m_debugDraw.DrawString(5, m_textLine, "Motor Torque = %5.0f", (float) torque);
m_textLine += 15;
}
static Test* Create()
{
return new SliderCrank;
}
b2RevoluteJoint* m_joint1;
b2PrismaticJoint* m_joint2;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SLIDER_CRANK_H
#define SLIDER_CRANK_H
// A motor driven slider crank with joint friction.
class SliderCrank : public Test
{
public:
SliderCrank()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2Body* prevBody = ground;
// Define crank.
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 2.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 7.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(0.0f, 5.0f));
rjd.motorSpeed = 1.0f * b2_pi;
rjd.maxMotorTorque = 10000.0f;
rjd.enableMotor = true;
m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&rjd);
prevBody = body;
}
// Define follower.
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 4.0f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 13.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(0.0f, 9.0f));
rjd.enableMotor = false;
m_world->CreateJoint(&rjd);
prevBody = body;
}
// Define piston
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.position.Set(0.0f, 17.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
b2RevoluteJointDef rjd;
rjd.Initialize(prevBody, body, b2Vec2(0.0f, 17.0f));
m_world->CreateJoint(&rjd);
b2PrismaticJointDef pjd;
pjd.Initialize(ground, body, b2Vec2(0.0f, 17.0f), b2Vec2(0.0f, 1.0f));
pjd.maxMotorForce = 1000.0f;
pjd.enableMotor = true;
m_joint2 = (b2PrismaticJoint*)m_world->CreateJoint(&pjd);
}
// Create a payload
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 23.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 2.0f);
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'f':
m_joint2->EnableMotor(!m_joint2->IsMotorEnabled());
m_joint2->GetBodyB()->SetAwake(true);
break;
case 'm':
m_joint1->EnableMotor(!m_joint1->IsMotorEnabled());
m_joint1->GetBodyB()->SetAwake(true);
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (f) toggle friction, (m) toggle motor");
m_textLine += 15;
float32 torque = m_joint1->GetMotorTorque(settings->hz);
m_debugDraw.DrawString(5, m_textLine, "Motor Torque = %5.0f", (float) torque);
m_textLine += 15;
}
static Test* Create()
{
return new SliderCrank;
}
b2RevoluteJoint* m_joint1;
b2PrismaticJoint* m_joint2;
};
#endif

View File

@@ -1,86 +1,86 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SPHERE_STACK_H
#define SPHERE_STACK_H
class SphereStack : public Test
{
public:
enum
{
e_count = 10
};
SphereStack()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2CircleShape shape;
shape.m_radius = 1.0f;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0, 4.0f + 3.0f * i);
m_bodies[i] = m_world->CreateBody(&bd);
m_bodies[i]->CreateFixture(&shape, 1.0f);
m_bodies[i]->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
}
}
}
void Step(Settings* settings)
{
Test::Step(settings);
//for (int32 i = 0; i < e_count; ++i)
//{
// printf("%g ", m_bodies[i]->GetWorldCenter().y);
//}
//for (int32 i = 0; i < e_count; ++i)
//{
// printf("%g ", m_bodies[i]->GetLinearVelocity().y);
//}
//printf("\n");
}
static Test* Create()
{
return new SphereStack;
}
b2Body* m_bodies[e_count];
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SPHERE_STACK_H
#define SPHERE_STACK_H
class SphereStack : public Test
{
public:
enum
{
e_count = 10
};
SphereStack()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2CircleShape shape;
shape.m_radius = 1.0f;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0, 4.0f + 3.0f * i);
m_bodies[i] = m_world->CreateBody(&bd);
m_bodies[i]->CreateFixture(&shape, 1.0f);
m_bodies[i]->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
}
}
}
void Step(Settings* settings)
{
Test::Step(settings);
//for (int32 i = 0; i < e_count; ++i)
//{
// printf("%g ", m_bodies[i]->GetWorldCenter().y);
//}
//for (int32 i = 0; i < e_count; ++i)
//{
// printf("%g ", m_bodies[i]->GetLinearVelocity().y);
//}
//printf("\n");
}
static Test* Create()
{
return new SphereStack;
}
b2Body* m_bodies[e_count];
};
#endif

View File

@@ -1,125 +1,125 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "../Framework/Test.h"
#include "../Framework/Render.h"
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include "freeglut/freeglut.h"
#endif
#include <cstring>
using namespace std;
#include "AddPair.h"
#include "ApplyForce.h"
#include "BodyTypes.h"
#include "Breakable.h"
#include "Bridge.h"
#include "BulletTest.h"
#include "Cantilever.h"
#include "Car.h"
#include "ContinuousTest.h"
#include "Chain.h"
#include "CharacterCollision.h"
#include "CollisionFiltering.h"
#include "CollisionProcessing.h"
#include "CompoundShapes.h"
#include "Confined.h"
#include "DistanceTest.h"
#include "Dominos.h"
#include "DumpShell.h"
#include "DynamicTreeTest.h"
#include "EdgeShapes.h"
#include "EdgeTest.h"
#include "Gears.h"
#include "OneSidedPlatform.h"
#include "Pinball.h"
#include "PolyCollision.h"
#include "PolyShapes.h"
#include "Prismatic.h"
#include "Pulleys.h"
#include "Pyramid.h"
#include "RayCast.h"
#include "Revolute.h"
//#include "Rope.h"
#include "RopeJoint.h"
#include "SensorTest.h"
#include "ShapeEditing.h"
#include "SliderCrank.h"
#include "SphereStack.h"
#include "TheoJansen.h"
#include "Tiles.h"
#include "TimeOfImpact.h"
#include "Tumbler.h"
#include "VaryingFriction.h"
#include "VaryingRestitution.h"
#include "VerticalStack.h"
#include "Web.h"
TestEntry g_testEntries[] =
{
{"Tumbler", Tumbler::Create},
{"Tiles", Tiles::Create},
{"Dump Shell", DumpShell::Create},
{"Gears", Gears::Create},
{"Cantilever", Cantilever::Create},
{"Varying Restitution", VaryingRestitution::Create},
{"Character Collision", CharacterCollision::Create},
{"Edge Test", EdgeTest::Create},
{"Body Types", BodyTypes::Create},
{"Shape Editing", ShapeEditing::Create},
{"Car", Car::Create},
{"Apply Force", ApplyForce::Create},
{"Prismatic", Prismatic::Create},
{"Vertical Stack", VerticalStack::Create},
{"SphereStack", SphereStack::Create},
{"Revolute", Revolute::Create},
{"Pulleys", Pulleys::Create},
{"Polygon Shapes", PolyShapes::Create},
//{"Rope", Rope::Create},
{"Web", Web::Create},
{"RopeJoint", RopeJoint::Create},
{"One-Sided Platform", OneSidedPlatform::Create},
{"Pinball", Pinball::Create},
{"Bullet Test", BulletTest::Create},
{"Continuous Test", ContinuousTest::Create},
{"Time of Impact", TimeOfImpact::Create},
{"Ray-Cast", RayCast::Create},
{"Confined", Confined::Create},
{"Pyramid", Pyramid::Create},
{"Theo Jansen's Walker", TheoJansen::Create},
{"Edge Shapes", EdgeShapes::Create},
{"PolyCollision", PolyCollision::Create},
{"Bridge", Bridge::Create},
{"Breakable", Breakable::Create},
{"Chain", Chain::Create},
{"Collision Filtering", CollisionFiltering::Create},
{"Collision Processing", CollisionProcessing::Create},
{"Compound Shapes", CompoundShapes::Create},
{"Distance Test", DistanceTest::Create},
{"Dominos", Dominos::Create},
{"Dynamic Tree", DynamicTreeTest::Create},
{"Sensor Test", SensorTest::Create},
{"Slider Crank", SliderCrank::Create},
{"Varying Friction", VaryingFriction::Create},
{"Add Pair Stress Test", AddPair::Create},
{NULL, NULL}
};
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "../Framework/Test.h"
#include "../Framework/Render.h"
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include "freeglut/freeglut.h"
#endif
#include <cstring>
using namespace std;
#include "AddPair.h"
#include "ApplyForce.h"
#include "BodyTypes.h"
#include "Breakable.h"
#include "Bridge.h"
#include "BulletTest.h"
#include "Cantilever.h"
#include "Car.h"
#include "ContinuousTest.h"
#include "Chain.h"
#include "CharacterCollision.h"
#include "CollisionFiltering.h"
#include "CollisionProcessing.h"
#include "CompoundShapes.h"
#include "Confined.h"
#include "DistanceTest.h"
#include "Dominos.h"
#include "DumpShell.h"
#include "DynamicTreeTest.h"
#include "EdgeShapes.h"
#include "EdgeTest.h"
#include "Gears.h"
#include "OneSidedPlatform.h"
#include "Pinball.h"
#include "PolyCollision.h"
#include "PolyShapes.h"
#include "Prismatic.h"
#include "Pulleys.h"
#include "Pyramid.h"
#include "RayCast.h"
#include "Revolute.h"
//#include "Rope.h"
#include "RopeJoint.h"
#include "SensorTest.h"
#include "ShapeEditing.h"
#include "SliderCrank.h"
#include "SphereStack.h"
#include "TheoJansen.h"
#include "Tiles.h"
#include "TimeOfImpact.h"
#include "Tumbler.h"
#include "VaryingFriction.h"
#include "VaryingRestitution.h"
#include "VerticalStack.h"
#include "Web.h"
TestEntry g_testEntries[] =
{
{"Tumbler", Tumbler::Create},
{"Tiles", Tiles::Create},
{"Dump Shell", DumpShell::Create},
{"Gears", Gears::Create},
{"Cantilever", Cantilever::Create},
{"Varying Restitution", VaryingRestitution::Create},
{"Character Collision", CharacterCollision::Create},
{"Edge Test", EdgeTest::Create},
{"Body Types", BodyTypes::Create},
{"Shape Editing", ShapeEditing::Create},
{"Car", Car::Create},
{"Apply Force", ApplyForce::Create},
{"Prismatic", Prismatic::Create},
{"Vertical Stack", VerticalStack::Create},
{"SphereStack", SphereStack::Create},
{"Revolute", Revolute::Create},
{"Pulleys", Pulleys::Create},
{"Polygon Shapes", PolyShapes::Create},
//{"Rope", Rope::Create},
{"Web", Web::Create},
{"RopeJoint", RopeJoint::Create},
{"One-Sided Platform", OneSidedPlatform::Create},
{"Pinball", Pinball::Create},
{"Bullet Test", BulletTest::Create},
{"Continuous Test", ContinuousTest::Create},
{"Time of Impact", TimeOfImpact::Create},
{"Ray-Cast", RayCast::Create},
{"Confined", Confined::Create},
{"Pyramid", Pyramid::Create},
{"Theo Jansen's Walker", TheoJansen::Create},
{"Edge Shapes", EdgeShapes::Create},
{"PolyCollision", PolyCollision::Create},
{"Bridge", Bridge::Create},
{"Breakable", Breakable::Create},
{"Chain", Chain::Create},
{"Collision Filtering", CollisionFiltering::Create},
{"Collision Processing", CollisionProcessing::Create},
{"Compound Shapes", CompoundShapes::Create},
{"Distance Test", DistanceTest::Create},
{"Dominos", Dominos::Create},
{"Dynamic Tree", DynamicTreeTest::Create},
{"Sensor Test", SensorTest::Create},
{"Slider Crank", SliderCrank::Create},
{"Varying Friction", VaryingFriction::Create},
{"Add Pair Stress Test", AddPair::Create},
{NULL, NULL}
};

View File

@@ -1,256 +1,256 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
// Inspired by a contribution by roman_m
// Dimensions scooped from APE (http://www.cove.org/ape/index.htm)
#ifndef THEO_JANSEN_H
#define THEO_JANSEN_H
class TheoJansen : public Test
{
public:
void CreateLeg(float32 s, const b2Vec2& wheelAnchor)
{
b2Vec2 p1(5.4f * s, -6.1f);
b2Vec2 p2(7.2f * s, -1.2f);
b2Vec2 p3(4.3f * s, -1.9f);
b2Vec2 p4(3.1f * s, 0.8f);
b2Vec2 p5(6.0f * s, 1.5f);
b2Vec2 p6(2.5f * s, 3.7f);
b2FixtureDef fd1, fd2;
fd1.filter.groupIndex = -1;
fd2.filter.groupIndex = -1;
fd1.density = 1.0f;
fd2.density = 1.0f;
b2PolygonShape poly1, poly2;
if (s > 0.0f)
{
b2Vec2 vertices[3];
vertices[0] = p1;
vertices[1] = p2;
vertices[2] = p3;
poly1.Set(vertices, 3);
vertices[0] = b2Vec2_zero;
vertices[1] = p5 - p4;
vertices[2] = p6 - p4;
poly2.Set(vertices, 3);
}
else
{
b2Vec2 vertices[3];
vertices[0] = p1;
vertices[1] = p3;
vertices[2] = p2;
poly1.Set(vertices, 3);
vertices[0] = b2Vec2_zero;
vertices[1] = p6 - p4;
vertices[2] = p5 - p4;
poly2.Set(vertices, 3);
}
fd1.shape = &poly1;
fd2.shape = &poly2;
b2BodyDef bd1, bd2;
bd1.type = b2_dynamicBody;
bd2.type = b2_dynamicBody;
bd1.position = m_offset;
bd2.position = p4 + m_offset;
bd1.angularDamping = 10.0f;
bd2.angularDamping = 10.0f;
b2Body* body1 = m_world->CreateBody(&bd1);
b2Body* body2 = m_world->CreateBody(&bd2);
body1->CreateFixture(&fd1);
body2->CreateFixture(&fd2);
b2DistanceJointDef djd;
// Using a soft distance constraint can reduce some jitter.
// It also makes the structure seem a bit more fluid by
// acting like a suspension system.
djd.dampingRatio = 0.5f;
djd.frequencyHz = 10.0f;
djd.Initialize(body1, body2, p2 + m_offset, p5 + m_offset);
m_world->CreateJoint(&djd);
djd.Initialize(body1, body2, p3 + m_offset, p4 + m_offset);
m_world->CreateJoint(&djd);
djd.Initialize(body1, m_wheel, p3 + m_offset, wheelAnchor + m_offset);
m_world->CreateJoint(&djd);
djd.Initialize(body2, m_wheel, p6 + m_offset, wheelAnchor + m_offset);
m_world->CreateJoint(&djd);
b2RevoluteJointDef rjd;
rjd.Initialize(body2, m_chassis, p4 + m_offset);
m_world->CreateJoint(&rjd);
}
TheoJansen()
{
m_offset.Set(0.0f, 8.0f);
m_motorSpeed = 2.0f;
m_motorOn = true;
b2Vec2 pivot(0.0f, 0.8f);
// Ground
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(-50.0f, 0.0f), b2Vec2(-50.0f, 10.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(50.0f, 0.0f), b2Vec2(50.0f, 10.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Balls
for (int32 i = 0; i < 40; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.25f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-40.0f + 2.0f * i, 0.5f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
}
// Chassis
{
b2PolygonShape shape;
shape.SetAsBox(2.5f, 1.0f);
b2FixtureDef sd;
sd.density = 1.0f;
sd.shape = &shape;
sd.filter.groupIndex = -1;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = pivot + m_offset;
m_chassis = m_world->CreateBody(&bd);
m_chassis->CreateFixture(&sd);
}
{
b2CircleShape shape;
shape.m_radius = 1.6f;
b2FixtureDef sd;
sd.density = 1.0f;
sd.shape = &shape;
sd.filter.groupIndex = -1;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = pivot + m_offset;
m_wheel = m_world->CreateBody(&bd);
m_wheel->CreateFixture(&sd);
}
{
b2RevoluteJointDef jd;
jd.Initialize(m_wheel, m_chassis, pivot + m_offset);
jd.collideConnected = false;
jd.motorSpeed = m_motorSpeed;
jd.maxMotorTorque = 400.0f;
jd.enableMotor = m_motorOn;
m_motorJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
b2Vec2 wheelAnchor;
wheelAnchor = pivot + b2Vec2(0.0f, -0.8f);
CreateLeg(-1.0f, wheelAnchor);
CreateLeg(1.0f, wheelAnchor);
m_wheel->SetTransform(m_wheel->GetPosition(), 120.0f * b2_pi / 180.0f);
CreateLeg(-1.0f, wheelAnchor);
CreateLeg(1.0f, wheelAnchor);
m_wheel->SetTransform(m_wheel->GetPosition(), -120.0f * b2_pi / 180.0f);
CreateLeg(-1.0f, wheelAnchor);
CreateLeg(1.0f, wheelAnchor);
}
void Step(Settings* settings)
{
m_debugDraw.DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, toggle motor = m");
m_textLine += 15;
Test::Step(settings);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_motorJoint->SetMotorSpeed(-m_motorSpeed);
break;
case 's':
m_motorJoint->SetMotorSpeed(0.0f);
break;
case 'd':
m_motorJoint->SetMotorSpeed(m_motorSpeed);
break;
case 'm':
m_motorJoint->EnableMotor(!m_motorJoint->IsMotorEnabled());
break;
}
}
static Test* Create()
{
return new TheoJansen;
}
b2Vec2 m_offset;
b2Body* m_chassis;
b2Body* m_wheel;
b2RevoluteJoint* m_motorJoint;
bool m_motorOn;
float32 m_motorSpeed;
};
#endif // THEO_JANSEN_H
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
// Inspired by a contribution by roman_m
// Dimensions scooped from APE (http://www.cove.org/ape/index.htm)
#ifndef THEO_JANSEN_H
#define THEO_JANSEN_H
class TheoJansen : public Test
{
public:
void CreateLeg(float32 s, const b2Vec2& wheelAnchor)
{
b2Vec2 p1(5.4f * s, -6.1f);
b2Vec2 p2(7.2f * s, -1.2f);
b2Vec2 p3(4.3f * s, -1.9f);
b2Vec2 p4(3.1f * s, 0.8f);
b2Vec2 p5(6.0f * s, 1.5f);
b2Vec2 p6(2.5f * s, 3.7f);
b2FixtureDef fd1, fd2;
fd1.filter.groupIndex = -1;
fd2.filter.groupIndex = -1;
fd1.density = 1.0f;
fd2.density = 1.0f;
b2PolygonShape poly1, poly2;
if (s > 0.0f)
{
b2Vec2 vertices[3];
vertices[0] = p1;
vertices[1] = p2;
vertices[2] = p3;
poly1.Set(vertices, 3);
vertices[0] = b2Vec2_zero;
vertices[1] = p5 - p4;
vertices[2] = p6 - p4;
poly2.Set(vertices, 3);
}
else
{
b2Vec2 vertices[3];
vertices[0] = p1;
vertices[1] = p3;
vertices[2] = p2;
poly1.Set(vertices, 3);
vertices[0] = b2Vec2_zero;
vertices[1] = p6 - p4;
vertices[2] = p5 - p4;
poly2.Set(vertices, 3);
}
fd1.shape = &poly1;
fd2.shape = &poly2;
b2BodyDef bd1, bd2;
bd1.type = b2_dynamicBody;
bd2.type = b2_dynamicBody;
bd1.position = m_offset;
bd2.position = p4 + m_offset;
bd1.angularDamping = 10.0f;
bd2.angularDamping = 10.0f;
b2Body* body1 = m_world->CreateBody(&bd1);
b2Body* body2 = m_world->CreateBody(&bd2);
body1->CreateFixture(&fd1);
body2->CreateFixture(&fd2);
b2DistanceJointDef djd;
// Using a soft distance constraint can reduce some jitter.
// It also makes the structure seem a bit more fluid by
// acting like a suspension system.
djd.dampingRatio = 0.5f;
djd.frequencyHz = 10.0f;
djd.Initialize(body1, body2, p2 + m_offset, p5 + m_offset);
m_world->CreateJoint(&djd);
djd.Initialize(body1, body2, p3 + m_offset, p4 + m_offset);
m_world->CreateJoint(&djd);
djd.Initialize(body1, m_wheel, p3 + m_offset, wheelAnchor + m_offset);
m_world->CreateJoint(&djd);
djd.Initialize(body2, m_wheel, p6 + m_offset, wheelAnchor + m_offset);
m_world->CreateJoint(&djd);
b2RevoluteJointDef rjd;
rjd.Initialize(body2, m_chassis, p4 + m_offset);
m_world->CreateJoint(&rjd);
}
TheoJansen()
{
m_offset.Set(0.0f, 8.0f);
m_motorSpeed = 2.0f;
m_motorOn = true;
b2Vec2 pivot(0.0f, 0.8f);
// Ground
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(-50.0f, 0.0f), b2Vec2(-50.0f, 10.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(50.0f, 0.0f), b2Vec2(50.0f, 10.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Balls
for (int32 i = 0; i < 40; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.25f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-40.0f + 2.0f * i, 0.5f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
}
// Chassis
{
b2PolygonShape shape;
shape.SetAsBox(2.5f, 1.0f);
b2FixtureDef sd;
sd.density = 1.0f;
sd.shape = &shape;
sd.filter.groupIndex = -1;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = pivot + m_offset;
m_chassis = m_world->CreateBody(&bd);
m_chassis->CreateFixture(&sd);
}
{
b2CircleShape shape;
shape.m_radius = 1.6f;
b2FixtureDef sd;
sd.density = 1.0f;
sd.shape = &shape;
sd.filter.groupIndex = -1;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = pivot + m_offset;
m_wheel = m_world->CreateBody(&bd);
m_wheel->CreateFixture(&sd);
}
{
b2RevoluteJointDef jd;
jd.Initialize(m_wheel, m_chassis, pivot + m_offset);
jd.collideConnected = false;
jd.motorSpeed = m_motorSpeed;
jd.maxMotorTorque = 400.0f;
jd.enableMotor = m_motorOn;
m_motorJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
b2Vec2 wheelAnchor;
wheelAnchor = pivot + b2Vec2(0.0f, -0.8f);
CreateLeg(-1.0f, wheelAnchor);
CreateLeg(1.0f, wheelAnchor);
m_wheel->SetTransform(m_wheel->GetPosition(), 120.0f * b2_pi / 180.0f);
CreateLeg(-1.0f, wheelAnchor);
CreateLeg(1.0f, wheelAnchor);
m_wheel->SetTransform(m_wheel->GetPosition(), -120.0f * b2_pi / 180.0f);
CreateLeg(-1.0f, wheelAnchor);
CreateLeg(1.0f, wheelAnchor);
}
void Step(Settings* settings)
{
m_debugDraw.DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, toggle motor = m");
m_textLine += 15;
Test::Step(settings);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_motorJoint->SetMotorSpeed(-m_motorSpeed);
break;
case 's':
m_motorJoint->SetMotorSpeed(0.0f);
break;
case 'd':
m_motorJoint->SetMotorSpeed(m_motorSpeed);
break;
case 'm':
m_motorJoint->EnableMotor(!m_motorJoint->IsMotorEnabled());
break;
}
}
static Test* Create()
{
return new TheoJansen;
}
b2Vec2 m_offset;
b2Body* m_chassis;
b2Body* m_wheel;
b2RevoluteJoint* m_motorJoint;
bool m_motorOn;
float32 m_motorSpeed;
};
#endif // THEO_JANSEN_H

View File

@@ -1,156 +1,156 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef TILES_H
#define TILES_H
/// This stress tests the dynamic tree broad-phase. This also shows that tile
/// based collision is _not_ smooth due to Box2D not knowing about adjacency.
class Tiles : public Test
{
public:
enum
{
e_count = 20
};
Tiles()
{
m_fixtureCount = 0;
b2Timer timer;
{
float32 a = 0.5f;
b2BodyDef bd;
bd.position.y = -a;
b2Body* ground = m_world->CreateBody(&bd);
#if 1
int32 N = 200;
int32 M = 10;
b2Vec2 position;
position.y = 0.0f;
for (int32 j = 0; j < M; ++j)
{
position.x = -N * a;
for (int32 i = 0; i < N; ++i)
{
b2PolygonShape shape;
shape.SetAsBox(a, a, position, 0.0f);
ground->CreateFixture(&shape, 0.0f);
++m_fixtureCount;
position.x += 2.0f * a;
}
position.y -= 2.0f * a;
}
#else
int32 N = 200;
int32 M = 10;
b2Vec2 position;
position.x = -N * a;
for (int32 i = 0; i < N; ++i)
{
position.y = 0.0f;
for (int32 j = 0; j < M; ++j)
{
b2PolygonShape shape;
shape.SetAsBox(a, a, position, 0.0f);
ground->CreateFixture(&shape, 0.0f);
position.y -= 2.0f * a;
}
position.x += 2.0f * a;
}
#endif
}
{
float32 a = 0.5f;
b2PolygonShape shape;
shape.SetAsBox(a, a);
b2Vec2 x(-7.0f, 0.75f);
b2Vec2 y;
b2Vec2 deltaX(0.5625f, 1.25f);
b2Vec2 deltaY(1.125f, 0.0f);
for (int32 i = 0; i < e_count; ++i)
{
y = x;
for (int32 j = i; j < e_count; ++j)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = y;
//if (i == 0 && j == 0)
//{
// bd.allowSleep = false;
//}
//else
//{
// bd.allowSleep = true;
//}
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
++m_fixtureCount;
y += deltaY;
}
x += deltaX;
}
}
m_createTime = timer.GetMilliseconds();
}
void Step(Settings* settings)
{
const b2ContactManager& cm = m_world->GetContactManager();
int32 height = cm.m_broadPhase.GetTreeHeight();
int32 leafCount = cm.m_broadPhase.GetProxyCount();
int32 minimumNodeCount = 2 * leafCount - 1;
float32 minimumHeight = ceilf(logf(float32(minimumNodeCount)) / logf(2.0f));
m_debugDraw.DrawString(5, m_textLine, "dynamic tree height = %d, min = %d", height, int32(minimumHeight));
m_textLine += 15;
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "create time = %6.2f ms, fixture count = %d",
m_createTime, m_fixtureCount);
m_textLine += 15;
//b2DynamicTree* tree = &m_world->m_contactManager.m_broadPhase.m_tree;
//if (m_stepCount == 400)
//{
// tree->RebuildBottomUp();
//}
}
static Test* Create()
{
return new Tiles;
}
int32 m_fixtureCount;
float32 m_createTime;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef TILES_H
#define TILES_H
/// This stress tests the dynamic tree broad-phase. This also shows that tile
/// based collision is _not_ smooth due to Box2D not knowing about adjacency.
class Tiles : public Test
{
public:
enum
{
e_count = 20
};
Tiles()
{
m_fixtureCount = 0;
b2Timer timer;
{
float32 a = 0.5f;
b2BodyDef bd;
bd.position.y = -a;
b2Body* ground = m_world->CreateBody(&bd);
#if 1
int32 N = 200;
int32 M = 10;
b2Vec2 position;
position.y = 0.0f;
for (int32 j = 0; j < M; ++j)
{
position.x = -N * a;
for (int32 i = 0; i < N; ++i)
{
b2PolygonShape shape;
shape.SetAsBox(a, a, position, 0.0f);
ground->CreateFixture(&shape, 0.0f);
++m_fixtureCount;
position.x += 2.0f * a;
}
position.y -= 2.0f * a;
}
#else
int32 N = 200;
int32 M = 10;
b2Vec2 position;
position.x = -N * a;
for (int32 i = 0; i < N; ++i)
{
position.y = 0.0f;
for (int32 j = 0; j < M; ++j)
{
b2PolygonShape shape;
shape.SetAsBox(a, a, position, 0.0f);
ground->CreateFixture(&shape, 0.0f);
position.y -= 2.0f * a;
}
position.x += 2.0f * a;
}
#endif
}
{
float32 a = 0.5f;
b2PolygonShape shape;
shape.SetAsBox(a, a);
b2Vec2 x(-7.0f, 0.75f);
b2Vec2 y;
b2Vec2 deltaX(0.5625f, 1.25f);
b2Vec2 deltaY(1.125f, 0.0f);
for (int32 i = 0; i < e_count; ++i)
{
y = x;
for (int32 j = i; j < e_count; ++j)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = y;
//if (i == 0 && j == 0)
//{
// bd.allowSleep = false;
//}
//else
//{
// bd.allowSleep = true;
//}
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 5.0f);
++m_fixtureCount;
y += deltaY;
}
x += deltaX;
}
}
m_createTime = timer.GetMilliseconds();
}
void Step(Settings* settings)
{
const b2ContactManager& cm = m_world->GetContactManager();
int32 height = cm.m_broadPhase.GetTreeHeight();
int32 leafCount = cm.m_broadPhase.GetProxyCount();
int32 minimumNodeCount = 2 * leafCount - 1;
float32 minimumHeight = ceilf(logf(float32(minimumNodeCount)) / logf(2.0f));
m_debugDraw.DrawString(5, m_textLine, "dynamic tree height = %d, min = %d", height, int32(minimumHeight));
m_textLine += 15;
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "create time = %6.2f ms, fixture count = %d",
m_createTime, m_fixtureCount);
m_textLine += 15;
//b2DynamicTree* tree = &m_world->m_contactManager.m_broadPhase.m_tree;
//if (m_stepCount == 400)
//{
// tree->RebuildBottomUp();
//}
}
static Test* Create()
{
return new Tiles;
}
int32 m_fixtureCount;
float32 m_createTime;
};
#endif

View File

@@ -1,131 +1,131 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef TIME_OF_IMPACT_H
#define TIME_OF_IMPACT_H
class TimeOfImpact : public Test
{
public:
TimeOfImpact()
{
m_shapeA.SetAsBox(25.0f, 5.0f);
m_shapeB.SetAsBox(2.5f, 2.5f);
}
static Test* Create()
{
return new TimeOfImpact;
}
void Step(Settings* settings)
{
Test::Step(settings);
b2Sweep sweepA;
sweepA.c0.Set(24.0f, -60.0f);
sweepA.a0 = 2.95f;
sweepA.c = sweepA.c0;
sweepA.a = sweepA.a0;
sweepA.localCenter.SetZero();
b2Sweep sweepB;
sweepB.c0.Set(53.474274f, -50.252514f);
sweepB.a0 = 513.36676f; // - 162.0f * b2_pi;
sweepB.c.Set(54.595478f, -51.083473f);
sweepB.a = 513.62781f; // - 162.0f * b2_pi;
sweepB.localCenter.SetZero();
//sweepB.a0 -= 300.0f * b2_pi;
//sweepB.a -= 300.0f * b2_pi;
b2TOIInput input;
input.proxyA.Set(&m_shapeA, 0);
input.proxyB.Set(&m_shapeB, 0);
input.sweepA = sweepA;
input.sweepB = sweepB;
input.tMax = 1.0f;
b2TOIOutput output;
b2TimeOfImpact(&output, &input);
m_debugDraw.DrawString(5, m_textLine, "toi = %g", output.t);
m_textLine += 15;
extern int32 b2_toiMaxIters, b2_toiMaxRootIters;
m_debugDraw.DrawString(5, m_textLine, "max toi iters = %d, max root iters = %d", b2_toiMaxIters, b2_toiMaxRootIters);
m_textLine += 15;
b2Vec2 vertices[b2_maxPolygonVertices];
b2Transform transformA;
sweepA.GetTransform(&transformA, 0.0f);
for (int32 i = 0; i < m_shapeA.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformA, m_shapeA.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeA.m_vertexCount, b2Color(0.9f, 0.9f, 0.9f));
b2Transform transformB;
sweepB.GetTransform(&transformB, 0.0f);
b2Vec2 localPoint(2.0f, -0.1f);
b2Vec2 rB = b2Mul(transformB, localPoint) - sweepB.c0;
float32 wB = sweepB.a - sweepB.a0;
b2Vec2 vB = sweepB.c - sweepB.c0;
b2Vec2 v = vB + b2Cross(wB, rB);
for (int32 i = 0; i < m_shapeB.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeB.m_vertexCount, b2Color(0.5f, 0.9f, 0.5f));
sweepB.GetTransform(&transformB, output.t);
for (int32 i = 0; i < m_shapeB.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeB.m_vertexCount, b2Color(0.5f, 0.7f, 0.9f));
sweepB.GetTransform(&transformB, 1.0f);
for (int32 i = 0; i < m_shapeB.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeB.m_vertexCount, b2Color(0.9f, 0.5f, 0.5f));
#if 0
for (float32 t = 0.0f; t < 1.0f; t += 0.1f)
{
sweepB.GetTransform(&transformB, t);
for (int32 i = 0; i < m_shapeB.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeB.m_vertexCount, b2Color(0.9f, 0.5f, 0.5f));
}
#endif
}
b2PolygonShape m_shapeA;
b2PolygonShape m_shapeB;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef TIME_OF_IMPACT_H
#define TIME_OF_IMPACT_H
class TimeOfImpact : public Test
{
public:
TimeOfImpact()
{
m_shapeA.SetAsBox(25.0f, 5.0f);
m_shapeB.SetAsBox(2.5f, 2.5f);
}
static Test* Create()
{
return new TimeOfImpact;
}
void Step(Settings* settings)
{
Test::Step(settings);
b2Sweep sweepA;
sweepA.c0.Set(24.0f, -60.0f);
sweepA.a0 = 2.95f;
sweepA.c = sweepA.c0;
sweepA.a = sweepA.a0;
sweepA.localCenter.SetZero();
b2Sweep sweepB;
sweepB.c0.Set(53.474274f, -50.252514f);
sweepB.a0 = 513.36676f; // - 162.0f * b2_pi;
sweepB.c.Set(54.595478f, -51.083473f);
sweepB.a = 513.62781f; // - 162.0f * b2_pi;
sweepB.localCenter.SetZero();
//sweepB.a0 -= 300.0f * b2_pi;
//sweepB.a -= 300.0f * b2_pi;
b2TOIInput input;
input.proxyA.Set(&m_shapeA, 0);
input.proxyB.Set(&m_shapeB, 0);
input.sweepA = sweepA;
input.sweepB = sweepB;
input.tMax = 1.0f;
b2TOIOutput output;
b2TimeOfImpact(&output, &input);
m_debugDraw.DrawString(5, m_textLine, "toi = %g", output.t);
m_textLine += 15;
extern int32 b2_toiMaxIters, b2_toiMaxRootIters;
m_debugDraw.DrawString(5, m_textLine, "max toi iters = %d, max root iters = %d", b2_toiMaxIters, b2_toiMaxRootIters);
m_textLine += 15;
b2Vec2 vertices[b2_maxPolygonVertices];
b2Transform transformA;
sweepA.GetTransform(&transformA, 0.0f);
for (int32 i = 0; i < m_shapeA.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformA, m_shapeA.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeA.m_vertexCount, b2Color(0.9f, 0.9f, 0.9f));
b2Transform transformB;
sweepB.GetTransform(&transformB, 0.0f);
b2Vec2 localPoint(2.0f, -0.1f);
b2Vec2 rB = b2Mul(transformB, localPoint) - sweepB.c0;
float32 wB = sweepB.a - sweepB.a0;
b2Vec2 vB = sweepB.c - sweepB.c0;
b2Vec2 v = vB + b2Cross(wB, rB);
for (int32 i = 0; i < m_shapeB.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeB.m_vertexCount, b2Color(0.5f, 0.9f, 0.5f));
sweepB.GetTransform(&transformB, output.t);
for (int32 i = 0; i < m_shapeB.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeB.m_vertexCount, b2Color(0.5f, 0.7f, 0.9f));
sweepB.GetTransform(&transformB, 1.0f);
for (int32 i = 0; i < m_shapeB.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeB.m_vertexCount, b2Color(0.9f, 0.5f, 0.5f));
#if 0
for (float32 t = 0.0f; t < 1.0f; t += 0.1f)
{
sweepB.GetTransform(&transformB, t);
for (int32 i = 0; i < m_shapeB.m_vertexCount; ++i)
{
vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]);
}
m_debugDraw.DrawPolygon(vertices, m_shapeB.m_vertexCount, b2Color(0.9f, 0.5f, 0.5f));
}
#endif
}
b2PolygonShape m_shapeA;
b2PolygonShape m_shapeB;
};
#endif

View File

@@ -1,99 +1,99 @@
/*
* Copyright (c) 2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef TUMBLER_H
#define TUMBLER_H
class Tumbler : public Test
{
public:
enum
{
e_count = 800
};
Tumbler()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.allowSleep = false;
bd.position.Set(0.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 10.0f, b2Vec2( 10.0f, 0.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
shape.SetAsBox(0.5f, 10.0f, b2Vec2(-10.0f, 0.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
shape.SetAsBox(10.0f, 0.5f, b2Vec2(0.0f, 10.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
shape.SetAsBox(10.0f, 0.5f, b2Vec2(0.0f, -10.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
b2RevoluteJointDef jd;
jd.bodyA = ground;
jd.bodyB = body;
jd.localAnchorA.Set(0.0f, 10.0f);
jd.localAnchorB.Set(0.0f, 0.0f);
jd.referenceAngle = 0.0f;
jd.motorSpeed = 0.05f * b2_pi;
jd.maxMotorTorque = 1e8f;
jd.enableMotor = true;
m_joint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
m_count = 0;
}
void Step(Settings* settings)
{
Test::Step(settings);
if (m_count < e_count)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.125f, 0.125f);
body->CreateFixture(&shape, 1.0f);
++m_count;
}
}
static Test* Create()
{
return new Tumbler;
}
b2RevoluteJoint* m_joint;
int32 m_count;
};
#endif
/*
* Copyright (c) 2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef TUMBLER_H
#define TUMBLER_H
class Tumbler : public Test
{
public:
enum
{
e_count = 800
};
Tumbler()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.allowSleep = false;
bd.position.Set(0.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 10.0f, b2Vec2( 10.0f, 0.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
shape.SetAsBox(0.5f, 10.0f, b2Vec2(-10.0f, 0.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
shape.SetAsBox(10.0f, 0.5f, b2Vec2(0.0f, 10.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
shape.SetAsBox(10.0f, 0.5f, b2Vec2(0.0f, -10.0f), 0.0);
body->CreateFixture(&shape, 5.0f);
b2RevoluteJointDef jd;
jd.bodyA = ground;
jd.bodyB = body;
jd.localAnchorA.Set(0.0f, 10.0f);
jd.localAnchorB.Set(0.0f, 0.0f);
jd.referenceAngle = 0.0f;
jd.motorSpeed = 0.05f * b2_pi;
jd.maxMotorTorque = 1e8f;
jd.enableMotor = true;
m_joint = (b2RevoluteJoint*)m_world->CreateJoint(&jd);
}
m_count = 0;
}
void Step(Settings* settings)
{
Test::Step(settings);
if (m_count < e_count)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.125f, 0.125f);
body->CreateFixture(&shape, 1.0f);
++m_count;
}
}
static Test* Create()
{
return new Tumbler;
}
b2RevoluteJoint* m_joint;
int32 m_count;
};
#endif

View File

@@ -1,124 +1,124 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef VARYING_FRICTION_H
#define VARYING_FRICTION_H
class VaryingFriction : public Test
{
public:
VaryingFriction()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(13.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(-4.0f, 22.0f);
bd.angle = -0.25f;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 1.0f);
b2BodyDef bd;
bd.position.Set(10.5f, 19.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(13.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(4.0f, 14.0f);
bd.angle = 0.25f;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 1.0f);
b2BodyDef bd;
bd.position.Set(-10.5f, 11.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(13.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(-4.0f, 6.0f);
bd.angle = -0.25f;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 25.0f;
float friction[5] = {0.75f, 0.5f, 0.35f, 0.1f, 0.0f};
for (int i = 0; i < 5; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-15.0f + 4.0f * i, 28.0f);
b2Body* body = m_world->CreateBody(&bd);
fd.friction = friction[i];
body->CreateFixture(&fd);
}
}
}
static Test* Create()
{
return new VaryingFriction;
}
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef VARYING_FRICTION_H
#define VARYING_FRICTION_H
class VaryingFriction : public Test
{
public:
VaryingFriction()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(13.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(-4.0f, 22.0f);
bd.angle = -0.25f;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 1.0f);
b2BodyDef bd;
bd.position.Set(10.5f, 19.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(13.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(4.0f, 14.0f);
bd.angle = 0.25f;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.25f, 1.0f);
b2BodyDef bd;
bd.position.Set(-10.5f, 11.0f);
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(13.0f, 0.25f);
b2BodyDef bd;
bd.position.Set(-4.0f, 6.0f);
bd.angle = -0.25f;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 25.0f;
float friction[5] = {0.75f, 0.5f, 0.35f, 0.1f, 0.0f};
for (int i = 0; i < 5; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-15.0f + 4.0f * i, 28.0f);
b2Body* body = m_world->CreateBody(&bd);
fd.friction = friction[i];
body->CreateFixture(&fd);
}
}
}
static Test* Create()
{
return new VaryingFriction;
}
};
#endif

View File

@@ -1,69 +1,69 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef VARYING_RESTITUTION_H
#define VARYING_RESTITUTION_H
// Note: even with a restitution of 1.0, there is some energy change
// due to position correction.
class VaryingRestitution : public Test
{
public:
VaryingRestitution()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2CircleShape shape;
shape.m_radius = 1.0f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
float32 restitution[7] = {0.0f, 0.1f, 0.3f, 0.5f, 0.75f, 0.9f, 1.0f};
for (int32 i = 0; i < 7; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + 3.0f * i, 20.0f);
b2Body* body = m_world->CreateBody(&bd);
fd.restitution = restitution[i];
body->CreateFixture(&fd);
}
}
}
static Test* Create()
{
return new VaryingRestitution;
}
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef VARYING_RESTITUTION_H
#define VARYING_RESTITUTION_H
// Note: even with a restitution of 1.0, there is some energy change
// due to position correction.
class VaryingRestitution : public Test
{
public:
VaryingRestitution()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2CircleShape shape;
shape.m_radius = 1.0f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
float32 restitution[7] = {0.0f, 0.1f, 0.3f, 0.5f, 0.75f, 0.9f, 1.0f};
for (int32 i = 0; i < 7; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + 3.0f * i, 20.0f);
b2Body* body = m_world->CreateBody(&bd);
fd.restitution = restitution[i];
body->CreateFixture(&fd);
}
}
}
static Test* Create()
{
return new VaryingRestitution;
}
};
#endif

View File

@@ -1,165 +1,165 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef VERTICAL_STACK_H
#define VERTICAL_STACK_H
class VerticalStack : public Test
{
public:
enum
{
e_columnCount = 5,
e_rowCount = 16
//e_columnCount = 1,
//e_rowCount = 1
};
VerticalStack()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(20.0f, 0.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
}
float32 xs[5] = {0.0f, -10.0f, -5.0f, 5.0f, 10.0f};
for (int32 j = 0; j < e_columnCount; ++j)
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.3f;
for (int i = 0; i < e_rowCount; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
int32 n = j * e_rowCount + i;
b2Assert(n < e_rowCount * e_columnCount);
m_indices[n] = n;
bd.userData = m_indices + n;
float32 x = 0.0f;
//float32 x = RandomFloat(-0.02f, 0.02f);
//float32 x = i % 2 == 0 ? -0.025f : 0.025f;
bd.position.Set(xs[j] + x, 0.752f + 1.54f * i);
b2Body* body = m_world->CreateBody(&bd);
m_bodies[n] = body;
body->CreateFixture(&fd);
}
}
m_bullet = NULL;
}
void Keyboard(unsigned char key)
{
switch (key)
{
case ',':
if (m_bullet != NULL)
{
m_world->DestroyBody(m_bullet);
m_bullet = NULL;
}
{
b2CircleShape shape;
shape.m_radius = 0.25f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.restitution = 0.05f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.bullet = true;
bd.position.Set(-31.0f, 5.0f);
m_bullet = m_world->CreateBody(&bd);
m_bullet->CreateFixture(&fd);
m_bullet->SetLinearVelocity(b2Vec2(400.0f, 0.0f));
}
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press: (,) to launch a bullet.");
m_textLine += 15;
//if (m_stepCount == 300)
//{
// if (m_bullet != NULL)
// {
// m_world->DestroyBody(m_bullet);
// m_bullet = NULL;
// }
// {
// b2CircleShape shape;
// shape.m_radius = 0.25f;
// b2FixtureDef fd;
// fd.shape = &shape;
// fd.density = 20.0f;
// fd.restitution = 0.05f;
// b2BodyDef bd;
// bd.type = b2_dynamicBody;
// bd.bullet = true;
// bd.position.Set(-31.0f, 5.0f);
// m_bullet = m_world->CreateBody(&bd);
// m_bullet->CreateFixture(&fd);
// m_bullet->SetLinearVelocity(b2Vec2(400.0f, 0.0f));
// }
//}
}
static Test* Create()
{
return new VerticalStack;
}
b2Body* m_bullet;
b2Body* m_bodies[e_rowCount * e_columnCount];
int32 m_indices[e_rowCount * e_columnCount];
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef VERTICAL_STACK_H
#define VERTICAL_STACK_H
class VerticalStack : public Test
{
public:
enum
{
e_columnCount = 5,
e_rowCount = 16
//e_columnCount = 1,
//e_rowCount = 1
};
VerticalStack()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(20.0f, 0.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
}
float32 xs[5] = {0.0f, -10.0f, -5.0f, 5.0f, 10.0f};
for (int32 j = 0; j < e_columnCount; ++j)
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.3f;
for (int i = 0; i < e_rowCount; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
int32 n = j * e_rowCount + i;
b2Assert(n < e_rowCount * e_columnCount);
m_indices[n] = n;
bd.userData = m_indices + n;
float32 x = 0.0f;
//float32 x = RandomFloat(-0.02f, 0.02f);
//float32 x = i % 2 == 0 ? -0.025f : 0.025f;
bd.position.Set(xs[j] + x, 0.752f + 1.54f * i);
b2Body* body = m_world->CreateBody(&bd);
m_bodies[n] = body;
body->CreateFixture(&fd);
}
}
m_bullet = NULL;
}
void Keyboard(unsigned char key)
{
switch (key)
{
case ',':
if (m_bullet != NULL)
{
m_world->DestroyBody(m_bullet);
m_bullet = NULL;
}
{
b2CircleShape shape;
shape.m_radius = 0.25f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.restitution = 0.05f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.bullet = true;
bd.position.Set(-31.0f, 5.0f);
m_bullet = m_world->CreateBody(&bd);
m_bullet->CreateFixture(&fd);
m_bullet->SetLinearVelocity(b2Vec2(400.0f, 0.0f));
}
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Press: (,) to launch a bullet.");
m_textLine += 15;
//if (m_stepCount == 300)
//{
// if (m_bullet != NULL)
// {
// m_world->DestroyBody(m_bullet);
// m_bullet = NULL;
// }
// {
// b2CircleShape shape;
// shape.m_radius = 0.25f;
// b2FixtureDef fd;
// fd.shape = &shape;
// fd.density = 20.0f;
// fd.restitution = 0.05f;
// b2BodyDef bd;
// bd.type = b2_dynamicBody;
// bd.bullet = true;
// bd.position.Set(-31.0f, 5.0f);
// m_bullet = m_world->CreateBody(&bd);
// m_bullet->CreateFixture(&fd);
// m_bullet->SetLinearVelocity(b2Vec2(400.0f, 0.0f));
// }
//}
}
static Test* Create()
{
return new VerticalStack;
}
b2Body* m_bullet;
b2Body* m_bodies[e_rowCount * e_columnCount];
int32 m_indices[e_rowCount * e_columnCount];
};
#endif

View File

@@ -1,209 +1,209 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef WEB_H
#define WEB_H
// This tests distance joints, body destruction, and joint destruction.
class Web : public Test
{
public:
Web()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-5.0f, 5.0f);
m_bodies[0] = m_world->CreateBody(&bd);
m_bodies[0]->CreateFixture(&shape, 5.0f);
bd.position.Set(5.0f, 5.0f);
m_bodies[1] = m_world->CreateBody(&bd);
m_bodies[1]->CreateFixture(&shape, 5.0f);
bd.position.Set(5.0f, 15.0f);
m_bodies[2] = m_world->CreateBody(&bd);
m_bodies[2]->CreateFixture(&shape, 5.0f);
bd.position.Set(-5.0f, 15.0f);
m_bodies[3] = m_world->CreateBody(&bd);
m_bodies[3]->CreateFixture(&shape, 5.0f);
b2DistanceJointDef jd;
b2Vec2 p1, p2, d;
jd.frequencyHz = 2.0f;
jd.dampingRatio = 0.0f;
jd.bodyA = ground;
jd.bodyB = m_bodies[0];
jd.localAnchorA.Set(-10.0f, 0.0f);
jd.localAnchorB.Set(-0.5f, -0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[0] = m_world->CreateJoint(&jd);
jd.bodyA = ground;
jd.bodyB = m_bodies[1];
jd.localAnchorA.Set(10.0f, 0.0f);
jd.localAnchorB.Set(0.5f, -0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[1] = m_world->CreateJoint(&jd);
jd.bodyA = ground;
jd.bodyB = m_bodies[2];
jd.localAnchorA.Set(10.0f, 20.0f);
jd.localAnchorB.Set(0.5f, 0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[2] = m_world->CreateJoint(&jd);
jd.bodyA = ground;
jd.bodyB = m_bodies[3];
jd.localAnchorA.Set(-10.0f, 20.0f);
jd.localAnchorB.Set(-0.5f, 0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[3] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[0];
jd.bodyB = m_bodies[1];
jd.localAnchorA.Set(0.5f, 0.0f);
jd.localAnchorB.Set(-0.5f, 0.0f);;
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[4] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[1];
jd.bodyB = m_bodies[2];
jd.localAnchorA.Set(0.0f, 0.5f);
jd.localAnchorB.Set(0.0f, -0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[5] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[2];
jd.bodyB = m_bodies[3];
jd.localAnchorA.Set(-0.5f, 0.0f);
jd.localAnchorB.Set(0.5f, 0.0f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[6] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[3];
jd.bodyB = m_bodies[0];
jd.localAnchorA.Set(0.0f, -0.5f);
jd.localAnchorB.Set(0.0f, 0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[7] = m_world->CreateJoint(&jd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'b':
for (int32 i = 0; i < 4; ++i)
{
if (m_bodies[i])
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
break;
}
}
break;
case 'j':
for (int32 i = 0; i < 8; ++i)
{
if (m_joints[i])
{
m_world->DestroyJoint(m_joints[i]);
m_joints[i] = NULL;
break;
}
}
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "This demonstrates a soft distance joint.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Press: (b) to delete a body, (j) to delete a joint");
m_textLine += 15;
}
void JointDestroyed(b2Joint* joint)
{
for (int32 i = 0; i < 8; ++i)
{
if (m_joints[i] == joint)
{
m_joints[i] = NULL;
break;
}
}
}
static Test* Create()
{
return new Web;
}
b2Body* m_bodies[4];
b2Joint* m_joints[8];
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef WEB_H
#define WEB_H
// This tests distance joints, body destruction, and joint destruction.
class Web : public Test
{
public:
Web()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-5.0f, 5.0f);
m_bodies[0] = m_world->CreateBody(&bd);
m_bodies[0]->CreateFixture(&shape, 5.0f);
bd.position.Set(5.0f, 5.0f);
m_bodies[1] = m_world->CreateBody(&bd);
m_bodies[1]->CreateFixture(&shape, 5.0f);
bd.position.Set(5.0f, 15.0f);
m_bodies[2] = m_world->CreateBody(&bd);
m_bodies[2]->CreateFixture(&shape, 5.0f);
bd.position.Set(-5.0f, 15.0f);
m_bodies[3] = m_world->CreateBody(&bd);
m_bodies[3]->CreateFixture(&shape, 5.0f);
b2DistanceJointDef jd;
b2Vec2 p1, p2, d;
jd.frequencyHz = 2.0f;
jd.dampingRatio = 0.0f;
jd.bodyA = ground;
jd.bodyB = m_bodies[0];
jd.localAnchorA.Set(-10.0f, 0.0f);
jd.localAnchorB.Set(-0.5f, -0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[0] = m_world->CreateJoint(&jd);
jd.bodyA = ground;
jd.bodyB = m_bodies[1];
jd.localAnchorA.Set(10.0f, 0.0f);
jd.localAnchorB.Set(0.5f, -0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[1] = m_world->CreateJoint(&jd);
jd.bodyA = ground;
jd.bodyB = m_bodies[2];
jd.localAnchorA.Set(10.0f, 20.0f);
jd.localAnchorB.Set(0.5f, 0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[2] = m_world->CreateJoint(&jd);
jd.bodyA = ground;
jd.bodyB = m_bodies[3];
jd.localAnchorA.Set(-10.0f, 20.0f);
jd.localAnchorB.Set(-0.5f, 0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[3] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[0];
jd.bodyB = m_bodies[1];
jd.localAnchorA.Set(0.5f, 0.0f);
jd.localAnchorB.Set(-0.5f, 0.0f);;
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[4] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[1];
jd.bodyB = m_bodies[2];
jd.localAnchorA.Set(0.0f, 0.5f);
jd.localAnchorB.Set(0.0f, -0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[5] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[2];
jd.bodyB = m_bodies[3];
jd.localAnchorA.Set(-0.5f, 0.0f);
jd.localAnchorB.Set(0.5f, 0.0f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[6] = m_world->CreateJoint(&jd);
jd.bodyA = m_bodies[3];
jd.bodyB = m_bodies[0];
jd.localAnchorA.Set(0.0f, -0.5f);
jd.localAnchorB.Set(0.0f, 0.5f);
p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA);
p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB);
d = p2 - p1;
jd.length = d.Length();
m_joints[7] = m_world->CreateJoint(&jd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'b':
for (int32 i = 0; i < 4; ++i)
{
if (m_bodies[i])
{
m_world->DestroyBody(m_bodies[i]);
m_bodies[i] = NULL;
break;
}
}
break;
case 'j':
for (int32 i = 0; i < 8; ++i)
{
if (m_joints[i])
{
m_world->DestroyJoint(m_joints[i]);
m_joints[i] = NULL;
break;
}
}
break;
}
}
void Step(Settings* settings)
{
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "This demonstrates a soft distance joint.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Press: (b) to delete a body, (j) to delete a joint");
m_textLine += 15;
}
void JointDestroyed(b2Joint* joint)
{
for (int32 i = 0; i < 8; ++i)
{
if (m_joints[i] == joint)
{
m_joints[i] = NULL;
break;
}
}
}
static Test* Create()
{
return new Web;
}
b2Body* m_bodies[4];
b2Joint* m_joints[8];
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,246 +1,245 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
#ifndef PIP_DEMO_UTILITIES_INCLUDED
#define PIP_DEMO_UTILITIES_INCLUDED 1
#endif
//==============================================================================
/*
This file contains a bunch of miscellaneous utilities that are
used by the various demos.
*/
//==============================================================================
inline Colour getRandomColour (float brightness) noexcept
{
return Colour::fromHSV (Random::getSystemRandom().nextFloat(), 0.5f, brightness, 1.0f);
}
inline Colour getRandomBrightColour() noexcept { return getRandomColour (0.8f); }
inline Colour getRandomDarkColour() noexcept { return getRandomColour (0.3f); }
inline Colour getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour uiColour, Colour fallback = Colour (0xff4d4d4d)) noexcept
{
if (auto* v4 = dynamic_cast<LookAndFeel_V4*> (&LookAndFeel::getDefaultLookAndFeel()))
return v4->getCurrentColourScheme().getUIColour (uiColour);
return fallback;
}
inline File getExamplesDirectory() noexcept
{
#ifdef PIP_JUCE_EXAMPLES_DIRECTORY
MemoryOutputStream mo;
auto success = Base64::convertFromBase64 (mo, JUCE_STRINGIFY (PIP_JUCE_EXAMPLES_DIRECTORY));
ignoreUnused (success);
jassert (success);
return mo.toString();
#elif defined PIP_JUCE_EXAMPLES_DIRECTORY_STRING
return File { CharPointer_UTF8 { PIP_JUCE_EXAMPLES_DIRECTORY_STRING } };
#else
auto currentFile = File::getSpecialLocation (File::SpecialLocationType::currentApplicationFile);
auto exampleDir = currentFile.getParentDirectory().getChildFile ("examples");
if (exampleDir.exists())
return exampleDir;
// keep track of the number of parent directories so we don't go on endlessly
for (int numTries = 0; numTries < 15; ++numTries)
{
if (currentFile.getFileName() == "examples")
return currentFile;
const auto sibling = currentFile.getSiblingFile ("examples");
if (sibling.exists())
return sibling;
currentFile = currentFile.getParentDirectory();
}
return currentFile;
#endif
}
inline std::unique_ptr<InputStream> createAssetInputStream (const char* resourcePath)
{
#if JUCE_ANDROID
ZipFile apkZip (File::getSpecialLocation (File::invokedExecutableFile));
return std::unique_ptr<InputStream> (apkZip.createStreamForEntry (apkZip.getIndexOfFileName ("assets/" + String (resourcePath))));
#else
#if JUCE_IOS
auto assetsDir = File::getSpecialLocation (File::currentExecutableFile)
.getParentDirectory().getChildFile ("Assets");
#elif JUCE_MAC
auto assetsDir = File::getSpecialLocation (File::currentExecutableFile)
.getParentDirectory().getParentDirectory().getChildFile ("Resources").getChildFile ("Assets");
if (! assetsDir.exists())
assetsDir = getExamplesDirectory().getChildFile ("Assets");
#else
auto assetsDir = getExamplesDirectory().getChildFile ("Assets");
#endif
auto resourceFile = assetsDir.getChildFile (resourcePath);
jassert (resourceFile.existsAsFile());
return resourceFile.createInputStream();
#endif
}
inline Image getImageFromAssets (const char* assetName)
{
auto hashCode = (String (assetName) + "@juce_demo_assets").hashCode64();
auto img = ImageCache::getFromHashCode (hashCode);
if (img.isNull())
{
std::unique_ptr<InputStream> juceIconStream (createAssetInputStream (assetName));
if (juceIconStream == nullptr)
return {};
img = ImageFileFormat::loadFrom (*juceIconStream);
ImageCache::addImageToCache (img, hashCode);
}
return img;
}
inline String loadEntireAssetIntoString (const char* assetName)
{
std::unique_ptr<InputStream> input (createAssetInputStream (assetName));
if (input == nullptr)
return {};
return input->readString();
}
//==============================================================================
inline Path getJUCELogoPath()
{
return Drawable::parseSVGPath (
"M72.87 84.28A42.36 42.36 0 0130.4 42.14a42.48 42.48 0 0184.95 0 42.36 42.36 0 01-42.48 42.14zm0-78.67A36.74 36.74 0 0036 42.14a36.88 36.88 0 0073.75 0A36.75 36.75 0 0072.87 5.61z"
"M77.62 49.59a177.77 177.77 0 008.74 18.93A4.38 4.38 0 0092.69 70a34.5 34.5 0 008.84-9 4.3 4.3 0 00-2.38-6.49A176.73 176.73 0 0180 47.32a1.78 1.78 0 00-2.38 2.27zM81.05 44.27a169.68 169.68 0 0020.13 7.41 4.39 4.39 0 005.52-3.41 34.42 34.42 0 00.55-6.13 33.81 33.81 0 00-.67-6.72 4.37 4.37 0 00-6.31-3A192.32 192.32 0 0181.1 41a1.76 1.76 0 00-.05 3.27zM74.47 50.44a1.78 1.78 0 00-3.29 0 165.54 165.54 0 00-7.46 19.89 4.33 4.33 0 003.47 5.48 35.49 35.49 0 005.68.46 34.44 34.44 0 007.13-.79 4.32 4.32 0 003-6.25 187.83 187.83 0 01-8.53-18.79zM71.59 34.12a1.78 1.78 0 003.29.05 163.9 163.9 0 007.52-20.11A4.34 4.34 0 0079 8.59a35.15 35.15 0 00-13.06.17 4.32 4.32 0 00-3 6.26 188.41 188.41 0 018.65 19.1zM46.32 30.3a176.2 176.2 0 0120 7.48 1.78 1.78 0 002.37-2.28 180.72 180.72 0 00-9.13-19.84 4.38 4.38 0 00-6.33-1.47 34.27 34.27 0 00-9.32 9.65 4.31 4.31 0 002.41 6.46zM68.17 49.18a1.77 1.77 0 00-2.29-2.34 181.71 181.71 0 00-19.51 8.82A4.3 4.3 0 0044.91 62a34.36 34.36 0 009.42 8.88 4.36 4.36 0 006.5-2.38 175.11 175.11 0 017.34-19.32zM77.79 35.59a1.78 1.78 0 002.3 2.35 182.51 182.51 0 0019.6-8.88 4.3 4.3 0 001.5-6.25 34.4 34.4 0 00-9.41-9.14A4.36 4.36 0 0085.24 16a174.51 174.51 0 01-7.45 19.59zM64.69 40.6a167.72 167.72 0 00-20.22-7.44A4.36 4.36 0 0039 36.6a33.68 33.68 0 00-.45 5.54 34 34 0 00.81 7.4 4.36 4.36 0 006.28 2.84 189.19 189.19 0 0119-8.52 1.76 1.76 0 00.05-3.26zM20 129.315c0 5-2.72 8.16-7.11 8.16-2.37 0-4.17-1-6.2-3.56l-.69-.78-6 5 .57.76c3.25 4.36 7.16 6.39 12.31 6.39 9 0 15.34-6.57 15.34-16v-28.1H20zM61.69 126.505c0 6.66-3.76 11-9.57 11-5.81 0-9.56-4.31-9.56-11v-25.32h-8.23v25.69c0 10.66 7.4 18.4 17.6 18.4 10 0 17.61-7.72 18-18.4v-25.69h-8.24zM106.83 134.095c-3.58 2.43-6.18 3.38-9.25 3.38a14.53 14.53 0 010-29c3.24 0 5.66.88 9.25 3.38l.76.53 4.78-6-.75-.62a22.18 22.18 0 00-14.22-5.1 22.33 22.33 0 100 44.65 21.53 21.53 0 0014.39-5.08l.81-.64-5-6zM145.75 137.285h-19.06v-10.72h18.3v-7.61h-18.3v-10.16h19.06v-7.61h-27.28v43.53h27.28z"
"M68.015 83.917c-7.723-.902-15.472-4.123-21.566-8.966-8.475-6.736-14.172-16.823-15.574-27.575C29.303 35.31 33.538 22.7 42.21 13.631 49.154 6.368 58.07 1.902 68.042.695c2.15-.26 7.524-.26 9.675 0 12.488 1.512 23.464 8.25 30.437 18.686 8.332 12.471 9.318 28.123 2.605 41.368-2.28 4.5-4.337 7.359-7.85 10.909A42.273 42.273 0 0177.613 83.92c-2.027.227-7.644.225-9.598-.003zm7.823-5.596c8.435-.415 17.446-4.678 23.683-11.205 5.976-6.254 9.35-13.723 10.181-22.537.632-6.705-1.346-14.948-5.065-21.108C98.88 13.935 89.397 7.602 78.34 5.906c-2.541-.39-8.398-.386-10.96.006C53.54 8.034 42.185 17.542 37.81 30.67c-2.807 8.426-2.421 17.267 1.11 25.444 4.877 11.297 14.959 19.41 26.977 21.709 2.136.408 6.1.755 7.377.645.325-.028 1.48-.094 2.564-.147z"
);
}
//==============================================================================
#if JUCE_MODULE_AVAILABLE_juce_gui_extra
inline CodeEditorComponent::ColourScheme getDarkCodeEditorColourScheme()
{
struct Type
{
const char* name;
juce::uint32 colour;
};
const Type types[] =
{
{ "Error", 0xffe60000 },
{ "Comment", 0xff72d20c },
{ "Keyword", 0xffee6f6f },
{ "Operator", 0xffc4eb19 },
{ "Identifier", 0xffcfcfcf },
{ "Integer", 0xff42c8c4 },
{ "Float", 0xff885500 },
{ "String", 0xffbc45dd },
{ "Bracket", 0xff058202 },
{ "Punctuation", 0xffcfbeff },
{ "Preprocessor Text", 0xfff8f631 }
};
CodeEditorComponent::ColourScheme cs;
for (auto& t : types)
cs.set (t.name, Colour (t.colour));
return cs;
}
inline CodeEditorComponent::ColourScheme getLightCodeEditorColourScheme()
{
struct Type
{
const char* name;
juce::uint32 colour;
};
const Type types[] =
{
{ "Error", 0xffcc0000 },
{ "Comment", 0xff00aa00 },
{ "Keyword", 0xff0000cc },
{ "Operator", 0xff225500 },
{ "Identifier", 0xff000000 },
{ "Integer", 0xff880000 },
{ "Float", 0xff885500 },
{ "String", 0xff990099 },
{ "Bracket", 0xff000055 },
{ "Punctuation", 0xff004400 },
{ "Preprocessor Text", 0xff660000 }
};
CodeEditorComponent::ColourScheme cs;
for (auto& t : types)
cs.set (t.name, Colour (t.colour));
return cs;
}
#endif
//==============================================================================
// This is basically a sawtooth wave generator - maps a value that bounces between
// 0.0 and 1.0 at a random speed
struct BouncingNumber
{
BouncingNumber()
: speed (0.0004 + 0.0007 * Random::getSystemRandom().nextDouble()),
phase (Random::getSystemRandom().nextDouble())
{
}
float getValue() const
{
double v = fmod (phase + speed * Time::getMillisecondCounterHiRes(), 2.0);
return (float) (v >= 1.0 ? (2.0 - v) : v);
}
protected:
double speed, phase;
};
struct SlowerBouncingNumber : public BouncingNumber
{
SlowerBouncingNumber()
{
speed *= 0.3;
}
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#ifndef PIP_DEMO_UTILITIES_INCLUDED
#define PIP_DEMO_UTILITIES_INCLUDED 1
#include <JuceHeader.h>
//==============================================================================
/*
This file contains a bunch of miscellaneous utilities that are
used by the various demos.
*/
//==============================================================================
inline Colour getRandomColour (float brightness) noexcept
{
return Colour::fromHSV (Random::getSystemRandom().nextFloat(), 0.5f, brightness, 1.0f);
}
inline Colour getRandomBrightColour() noexcept { return getRandomColour (0.8f); }
inline Colour getRandomDarkColour() noexcept { return getRandomColour (0.3f); }
inline Colour getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour uiColour, Colour fallback = Colour (0xff4d4d4d)) noexcept
{
if (auto* v4 = dynamic_cast<LookAndFeel_V4*> (&LookAndFeel::getDefaultLookAndFeel()))
return v4->getCurrentColourScheme().getUIColour (uiColour);
return fallback;
}
inline File getExamplesDirectory() noexcept
{
#ifdef PIP_JUCE_EXAMPLES_DIRECTORY
MemoryOutputStream mo;
auto success = Base64::convertFromBase64 (mo, JUCE_STRINGIFY (PIP_JUCE_EXAMPLES_DIRECTORY));
ignoreUnused (success);
jassert (success);
return mo.toString();
#elif defined PIP_JUCE_EXAMPLES_DIRECTORY_STRING
return File { CharPointer_UTF8 { PIP_JUCE_EXAMPLES_DIRECTORY_STRING } };
#else
auto currentFile = File::getSpecialLocation (File::SpecialLocationType::currentApplicationFile);
auto exampleDir = currentFile.getParentDirectory().getChildFile ("examples");
if (exampleDir.exists())
return exampleDir;
// keep track of the number of parent directories so we don't go on endlessly
for (int numTries = 0; numTries < 15; ++numTries)
{
if (currentFile.getFileName() == "examples")
return currentFile;
const auto sibling = currentFile.getSiblingFile ("examples");
if (sibling.exists())
return sibling;
currentFile = currentFile.getParentDirectory();
}
return currentFile;
#endif
}
inline std::unique_ptr<InputStream> createAssetInputStream (const char* resourcePath)
{
#if JUCE_ANDROID
ZipFile apkZip (File::getSpecialLocation (File::invokedExecutableFile));
return std::unique_ptr<InputStream> (apkZip.createStreamForEntry (apkZip.getIndexOfFileName ("assets/" + String (resourcePath))));
#else
#if JUCE_IOS
auto assetsDir = File::getSpecialLocation (File::currentExecutableFile)
.getParentDirectory().getChildFile ("Assets");
#elif JUCE_MAC
auto assetsDir = File::getSpecialLocation (File::currentExecutableFile)
.getParentDirectory().getParentDirectory().getChildFile ("Resources").getChildFile ("Assets");
if (! assetsDir.exists())
assetsDir = getExamplesDirectory().getChildFile ("Assets");
#else
auto assetsDir = getExamplesDirectory().getChildFile ("Assets");
#endif
auto resourceFile = assetsDir.getChildFile (resourcePath);
jassert (resourceFile.existsAsFile());
return resourceFile.createInputStream();
#endif
}
inline Image getImageFromAssets (const char* assetName)
{
auto hashCode = (String (assetName) + "@juce_demo_assets").hashCode64();
auto img = ImageCache::getFromHashCode (hashCode);
if (img.isNull())
{
std::unique_ptr<InputStream> juceIconStream (createAssetInputStream (assetName));
if (juceIconStream == nullptr)
return {};
img = ImageFileFormat::loadFrom (*juceIconStream);
ImageCache::addImageToCache (img, hashCode);
}
return img;
}
inline String loadEntireAssetIntoString (const char* assetName)
{
std::unique_ptr<InputStream> input (createAssetInputStream (assetName));
if (input == nullptr)
return {};
return input->readString();
}
//==============================================================================
inline Path getJUCELogoPath()
{
return Drawable::parseSVGPath (
"M72.87 84.28A42.36 42.36 0 0130.4 42.14a42.48 42.48 0 0184.95 0 42.36 42.36 0 01-42.48 42.14zm0-78.67A36.74 36.74 0 0036 42.14a36.88 36.88 0 0073.75 0A36.75 36.75 0 0072.87 5.61z"
"M77.62 49.59a177.77 177.77 0 008.74 18.93A4.38 4.38 0 0092.69 70a34.5 34.5 0 008.84-9 4.3 4.3 0 00-2.38-6.49A176.73 176.73 0 0180 47.32a1.78 1.78 0 00-2.38 2.27zM81.05 44.27a169.68 169.68 0 0020.13 7.41 4.39 4.39 0 005.52-3.41 34.42 34.42 0 00.55-6.13 33.81 33.81 0 00-.67-6.72 4.37 4.37 0 00-6.31-3A192.32 192.32 0 0181.1 41a1.76 1.76 0 00-.05 3.27zM74.47 50.44a1.78 1.78 0 00-3.29 0 165.54 165.54 0 00-7.46 19.89 4.33 4.33 0 003.47 5.48 35.49 35.49 0 005.68.46 34.44 34.44 0 007.13-.79 4.32 4.32 0 003-6.25 187.83 187.83 0 01-8.53-18.79zM71.59 34.12a1.78 1.78 0 003.29.05 163.9 163.9 0 007.52-20.11A4.34 4.34 0 0079 8.59a35.15 35.15 0 00-13.06.17 4.32 4.32 0 00-3 6.26 188.41 188.41 0 018.65 19.1zM46.32 30.3a176.2 176.2 0 0120 7.48 1.78 1.78 0 002.37-2.28 180.72 180.72 0 00-9.13-19.84 4.38 4.38 0 00-6.33-1.47 34.27 34.27 0 00-9.32 9.65 4.31 4.31 0 002.41 6.46zM68.17 49.18a1.77 1.77 0 00-2.29-2.34 181.71 181.71 0 00-19.51 8.82A4.3 4.3 0 0044.91 62a34.36 34.36 0 009.42 8.88 4.36 4.36 0 006.5-2.38 175.11 175.11 0 017.34-19.32zM77.79 35.59a1.78 1.78 0 002.3 2.35 182.51 182.51 0 0019.6-8.88 4.3 4.3 0 001.5-6.25 34.4 34.4 0 00-9.41-9.14A4.36 4.36 0 0085.24 16a174.51 174.51 0 01-7.45 19.59zM64.69 40.6a167.72 167.72 0 00-20.22-7.44A4.36 4.36 0 0039 36.6a33.68 33.68 0 00-.45 5.54 34 34 0 00.81 7.4 4.36 4.36 0 006.28 2.84 189.19 189.19 0 0119-8.52 1.76 1.76 0 00.05-3.26zM20 129.315c0 5-2.72 8.16-7.11 8.16-2.37 0-4.17-1-6.2-3.56l-.69-.78-6 5 .57.76c3.25 4.36 7.16 6.39 12.31 6.39 9 0 15.34-6.57 15.34-16v-28.1H20zM61.69 126.505c0 6.66-3.76 11-9.57 11-5.81 0-9.56-4.31-9.56-11v-25.32h-8.23v25.69c0 10.66 7.4 18.4 17.6 18.4 10 0 17.61-7.72 18-18.4v-25.69h-8.24zM106.83 134.095c-3.58 2.43-6.18 3.38-9.25 3.38a14.53 14.53 0 010-29c3.24 0 5.66.88 9.25 3.38l.76.53 4.78-6-.75-.62a22.18 22.18 0 00-14.22-5.1 22.33 22.33 0 100 44.65 21.53 21.53 0 0014.39-5.08l.81-.64-5-6zM145.75 137.285h-19.06v-10.72h18.3v-7.61h-18.3v-10.16h19.06v-7.61h-27.28v43.53h27.28z"
"M68.015 83.917c-7.723-.902-15.472-4.123-21.566-8.966-8.475-6.736-14.172-16.823-15.574-27.575C29.303 35.31 33.538 22.7 42.21 13.631 49.154 6.368 58.07 1.902 68.042.695c2.15-.26 7.524-.26 9.675 0 12.488 1.512 23.464 8.25 30.437 18.686 8.332 12.471 9.318 28.123 2.605 41.368-2.28 4.5-4.337 7.359-7.85 10.909A42.273 42.273 0 0177.613 83.92c-2.027.227-7.644.225-9.598-.003zm7.823-5.596c8.435-.415 17.446-4.678 23.683-11.205 5.976-6.254 9.35-13.723 10.181-22.537.632-6.705-1.346-14.948-5.065-21.108C98.88 13.935 89.397 7.602 78.34 5.906c-2.541-.39-8.398-.386-10.96.006C53.54 8.034 42.185 17.542 37.81 30.67c-2.807 8.426-2.421 17.267 1.11 25.444 4.877 11.297 14.959 19.41 26.977 21.709 2.136.408 6.1.755 7.377.645.325-.028 1.48-.094 2.564-.147z"
);
}
//==============================================================================
#if JUCE_MODULE_AVAILABLE_juce_gui_extra
inline CodeEditorComponent::ColourScheme getDarkCodeEditorColourScheme()
{
struct Type
{
const char* name;
juce::uint32 colour;
};
const Type types[] =
{
{ "Error", 0xffe60000 },
{ "Comment", 0xff72d20c },
{ "Keyword", 0xffee6f6f },
{ "Operator", 0xffc4eb19 },
{ "Identifier", 0xffcfcfcf },
{ "Integer", 0xff42c8c4 },
{ "Float", 0xff885500 },
{ "String", 0xffbc45dd },
{ "Bracket", 0xff058202 },
{ "Punctuation", 0xffcfbeff },
{ "Preprocessor Text", 0xfff8f631 }
};
CodeEditorComponent::ColourScheme cs;
for (auto& t : types)
cs.set (t.name, Colour (t.colour));
return cs;
}
inline CodeEditorComponent::ColourScheme getLightCodeEditorColourScheme()
{
struct Type
{
const char* name;
juce::uint32 colour;
};
const Type types[] =
{
{ "Error", 0xffcc0000 },
{ "Comment", 0xff00aa00 },
{ "Keyword", 0xff0000cc },
{ "Operator", 0xff225500 },
{ "Identifier", 0xff000000 },
{ "Integer", 0xff880000 },
{ "Float", 0xff885500 },
{ "String", 0xff990099 },
{ "Bracket", 0xff000055 },
{ "Punctuation", 0xff004400 },
{ "Preprocessor Text", 0xff660000 }
};
CodeEditorComponent::ColourScheme cs;
for (auto& t : types)
cs.set (t.name, Colour (t.colour));
return cs;
}
#endif
//==============================================================================
// This is basically a sawtooth wave generator - maps a value that bounces between
// 0.0 and 1.0 at a random speed
struct BouncingNumber
{
BouncingNumber()
: speed (0.0004 + 0.0007 * Random::getSystemRandom().nextDouble()),
phase (Random::getSystemRandom().nextDouble())
{
}
float getValue() const
{
double v = fmod (phase + speed * Time::getMillisecondCounterHiRes(), 2.0);
return (float) (v >= 1.0 ? (2.0 - v) : v);
}
protected:
double speed, phase;
};
struct SlowerBouncingNumber : public BouncingNumber
{
SlowerBouncingNumber()
{
speed *= 0.3;
}
};
#endif // PIP_DEMO_UTILITIES_INCLUDED

View File

@@ -1,365 +1,365 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#pragma once
#include <map>
//==============================================================================
/**
This is a quick-and-dirty parser for the 3D OBJ file format.
Just call load() and if there aren't any errors, the 'shapes' array should
be filled with all the shape objects that were loaded from the file.
*/
class WavefrontObjFile
{
public:
WavefrontObjFile() {}
Result load (const String& objFileContent)
{
shapes.clear();
return parseObjFile (StringArray::fromLines (objFileContent));
}
Result load (const File& file)
{
sourceFile = file;
return load (file.loadFileAsString());
}
//==============================================================================
typedef juce::uint32 Index;
struct Vertex { float x, y, z; };
struct TextureCoord { float x, y; };
struct Mesh
{
Array<Vertex> vertices, normals;
Array<TextureCoord> textureCoords;
Array<Index> indices;
};
struct Material
{
Material() noexcept
{
zerostruct (ambient);
zerostruct (diffuse);
zerostruct (specular);
zerostruct (transmittance);
zerostruct (emission);
}
String name;
Vertex ambient, diffuse, specular, transmittance, emission;
float shininess = 1.0f, refractiveIndex = 0.0f;
String ambientTextureName, diffuseTextureName,
specularTextureName, normalTextureName;
StringPairArray parameters;
};
struct Shape
{
String name;
Mesh mesh;
Material material;
};
OwnedArray<Shape> shapes;
private:
//==============================================================================
File sourceFile;
struct TripleIndex
{
TripleIndex() noexcept {}
bool operator< (const TripleIndex& other) const noexcept
{
if (this == &other)
return false;
if (vertexIndex != other.vertexIndex)
return vertexIndex < other.vertexIndex;
if (textureIndex != other.textureIndex)
return textureIndex < other.textureIndex;
return normalIndex < other.normalIndex;
}
int vertexIndex = -1, textureIndex = -1, normalIndex = -1;
};
struct IndexMap
{
std::map<TripleIndex, Index> map;
Index getIndexFor (TripleIndex i, Mesh& newMesh, const Mesh& srcMesh)
{
const std::map<TripleIndex, Index>::iterator it (map.find (i));
if (it != map.end())
return it->second;
auto index = (Index) newMesh.vertices.size();
if (isPositiveAndBelow (i.vertexIndex, srcMesh.vertices.size()))
newMesh.vertices.add (srcMesh.vertices.getReference (i.vertexIndex));
if (isPositiveAndBelow (i.normalIndex, srcMesh.normals.size()))
newMesh.normals.add (srcMesh.normals.getReference (i.normalIndex));
if (isPositiveAndBelow (i.textureIndex, srcMesh.textureCoords.size()))
newMesh.textureCoords.add (srcMesh.textureCoords.getReference (i.textureIndex));
map[i] = index;
return index;
}
};
static float parseFloat (String::CharPointerType& t)
{
t.incrementToEndOfWhitespace();
return (float) CharacterFunctions::readDoubleValue (t);
}
static Vertex parseVertex (String::CharPointerType t)
{
Vertex v;
v.x = parseFloat (t);
v.y = parseFloat (t);
v.z = parseFloat (t);
return v;
}
static TextureCoord parseTextureCoord (String::CharPointerType t)
{
TextureCoord tc;
tc.x = parseFloat (t);
tc.y = parseFloat (t);
return tc;
}
static bool matchToken (String::CharPointerType& t, const char* token)
{
auto len = (int) strlen (token);
if (CharacterFunctions::compareUpTo (CharPointer_ASCII (token), t, len) == 0)
{
auto end = t + len;
if (end.isEmpty() || end.isWhitespace())
{
t = end.findEndOfWhitespace();
return true;
}
}
return false;
}
struct Face
{
Face (String::CharPointerType t)
{
while (! t.isEmpty())
triples.add (parseTriple (t));
}
Array<TripleIndex> triples;
void addIndices (Mesh& newMesh, const Mesh& srcMesh, IndexMap& indexMap)
{
TripleIndex i0 (triples[0]), i1, i2 (triples[1]);
for (auto i = 2; i < triples.size(); ++i)
{
i1 = i2;
i2 = triples.getReference (i);
newMesh.indices.add (indexMap.getIndexFor (i0, newMesh, srcMesh));
newMesh.indices.add (indexMap.getIndexFor (i1, newMesh, srcMesh));
newMesh.indices.add (indexMap.getIndexFor (i2, newMesh, srcMesh));
}
}
static TripleIndex parseTriple (String::CharPointerType& t)
{
TripleIndex i;
t.incrementToEndOfWhitespace();
i.vertexIndex = t.getIntValue32() - 1;
t = findEndOfFaceToken (t);
if (t.isEmpty() || t.getAndAdvance() != '/')
return i;
if (*t == '/')
{
++t;
}
else
{
i.textureIndex = t.getIntValue32() - 1;
t = findEndOfFaceToken (t);
if (t.isEmpty() || t.getAndAdvance() != '/')
return i;
}
i.normalIndex = t.getIntValue32() - 1;
t = findEndOfFaceToken (t);
return i;
}
static String::CharPointerType findEndOfFaceToken (String::CharPointerType t) noexcept
{
return CharacterFunctions::findEndOfToken (t, CharPointer_ASCII ("/ \t"), String().getCharPointer());
}
};
static Shape* parseFaceGroup (const Mesh& srcMesh,
Array<Face>& faceGroup,
const Material& material,
const String& name)
{
if (faceGroup.size() == 0)
return nullptr;
std::unique_ptr<Shape> shape (new Shape());
shape->name = name;
shape->material = material;
IndexMap indexMap;
for (auto& f : faceGroup)
f.addIndices (shape->mesh, srcMesh, indexMap);
return shape.release();
}
Result parseObjFile (const StringArray& lines)
{
Mesh mesh;
Array<Face> faceGroup;
Array<Material> knownMaterials;
Material lastMaterial;
String lastName;
for (auto lineNum = 0; lineNum < lines.size(); ++lineNum)
{
auto l = lines[lineNum].getCharPointer().findEndOfWhitespace();
if (matchToken (l, "v")) { mesh.vertices .add (parseVertex (l)); continue; }
if (matchToken (l, "vn")) { mesh.normals .add (parseVertex (l)); continue; }
if (matchToken (l, "vt")) { mesh.textureCoords.add (parseTextureCoord (l)); continue; }
if (matchToken (l, "f")) { faceGroup .add (Face (l)); continue; }
if (matchToken (l, "usemtl"))
{
auto name = String (l).trim();
for (auto i = knownMaterials.size(); --i >= 0;)
{
if (knownMaterials.getReference (i).name == name)
{
lastMaterial = knownMaterials.getReference (i);
break;
}
}
continue;
}
if (matchToken (l, "mtllib"))
{
auto r = parseMaterial (knownMaterials, String (l).trim());
continue;
}
if (matchToken (l, "g") || matchToken (l, "o"))
{
if (auto* shape = parseFaceGroup (mesh, faceGroup, lastMaterial, lastName))
shapes.add (shape);
faceGroup.clear();
lastName = StringArray::fromTokens (l, " \t", "")[0];
continue;
}
}
if (auto* shape = parseFaceGroup (mesh, faceGroup, lastMaterial, lastName))
shapes.add (shape);
return Result::ok();
}
Result parseMaterial (Array<Material>& materials, const String& filename)
{
jassert (sourceFile.exists());
auto f = sourceFile.getSiblingFile (filename);
if (! f.exists())
return Result::fail ("Cannot open file: " + filename);
auto lines = StringArray::fromLines (f.loadFileAsString());
materials.clear();
Material material;
for (auto line : lines)
{
auto l = line.getCharPointer().findEndOfWhitespace();
if (matchToken (l, "newmtl")) { materials.add (material); material.name = String (l).trim(); continue; }
if (matchToken (l, "Ka")) { material.ambient = parseVertex (l); continue; }
if (matchToken (l, "Kd")) { material.diffuse = parseVertex (l); continue; }
if (matchToken (l, "Ks")) { material.specular = parseVertex (l); continue; }
if (matchToken (l, "Kt")) { material.transmittance = parseVertex (l); continue; }
if (matchToken (l, "Ke")) { material.emission = parseVertex (l); continue; }
if (matchToken (l, "Ni")) { material.refractiveIndex = parseFloat (l); continue; }
if (matchToken (l, "Ns")) { material.shininess = parseFloat (l); continue; }
if (matchToken (l, "map_Ka")) { material.ambientTextureName = String (l).trim(); continue; }
if (matchToken (l, "map_Kd")) { material.diffuseTextureName = String (l).trim(); continue; }
if (matchToken (l, "map_Ks")) { material.specularTextureName = String (l).trim(); continue; }
if (matchToken (l, "map_Ns")) { material.normalTextureName = String (l).trim(); continue; }
auto tokens = StringArray::fromTokens (l, " \t", "");
if (tokens.size() >= 2)
material.parameters.set (tokens[0].trim(), tokens[1].trim());
}
materials.add (material);
return Result::ok();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavefrontObjFile)
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#pragma once
#include <map>
//==============================================================================
/**
This is a quick-and-dirty parser for the 3D OBJ file format.
Just call load() and if there aren't any errors, the 'shapes' array should
be filled with all the shape objects that were loaded from the file.
*/
class WavefrontObjFile
{
public:
WavefrontObjFile() {}
Result load (const String& objFileContent)
{
shapes.clear();
return parseObjFile (StringArray::fromLines (objFileContent));
}
Result load (const File& file)
{
sourceFile = file;
return load (file.loadFileAsString());
}
//==============================================================================
typedef juce::uint32 Index;
struct Vertex { float x, y, z; };
struct TextureCoord { float x, y; };
struct Mesh
{
Array<Vertex> vertices, normals;
Array<TextureCoord> textureCoords;
Array<Index> indices;
};
struct Material
{
Material() noexcept
{
zerostruct (ambient);
zerostruct (diffuse);
zerostruct (specular);
zerostruct (transmittance);
zerostruct (emission);
}
String name;
Vertex ambient, diffuse, specular, transmittance, emission;
float shininess = 1.0f, refractiveIndex = 0.0f;
String ambientTextureName, diffuseTextureName,
specularTextureName, normalTextureName;
StringPairArray parameters;
};
struct Shape
{
String name;
Mesh mesh;
Material material;
};
OwnedArray<Shape> shapes;
private:
//==============================================================================
File sourceFile;
struct TripleIndex
{
TripleIndex() noexcept {}
bool operator< (const TripleIndex& other) const noexcept
{
if (this == &other)
return false;
if (vertexIndex != other.vertexIndex)
return vertexIndex < other.vertexIndex;
if (textureIndex != other.textureIndex)
return textureIndex < other.textureIndex;
return normalIndex < other.normalIndex;
}
int vertexIndex = -1, textureIndex = -1, normalIndex = -1;
};
struct IndexMap
{
std::map<TripleIndex, Index> map;
Index getIndexFor (TripleIndex i, Mesh& newMesh, const Mesh& srcMesh)
{
const std::map<TripleIndex, Index>::iterator it (map.find (i));
if (it != map.end())
return it->second;
auto index = (Index) newMesh.vertices.size();
if (isPositiveAndBelow (i.vertexIndex, srcMesh.vertices.size()))
newMesh.vertices.add (srcMesh.vertices.getReference (i.vertexIndex));
if (isPositiveAndBelow (i.normalIndex, srcMesh.normals.size()))
newMesh.normals.add (srcMesh.normals.getReference (i.normalIndex));
if (isPositiveAndBelow (i.textureIndex, srcMesh.textureCoords.size()))
newMesh.textureCoords.add (srcMesh.textureCoords.getReference (i.textureIndex));
map[i] = index;
return index;
}
};
static float parseFloat (String::CharPointerType& t)
{
t.incrementToEndOfWhitespace();
return (float) CharacterFunctions::readDoubleValue (t);
}
static Vertex parseVertex (String::CharPointerType t)
{
Vertex v;
v.x = parseFloat (t);
v.y = parseFloat (t);
v.z = parseFloat (t);
return v;
}
static TextureCoord parseTextureCoord (String::CharPointerType t)
{
TextureCoord tc;
tc.x = parseFloat (t);
tc.y = parseFloat (t);
return tc;
}
static bool matchToken (String::CharPointerType& t, const char* token)
{
auto len = (int) strlen (token);
if (CharacterFunctions::compareUpTo (CharPointer_ASCII (token), t, len) == 0)
{
auto end = t + len;
if (end.isEmpty() || end.isWhitespace())
{
t = end.findEndOfWhitespace();
return true;
}
}
return false;
}
struct Face
{
Face (String::CharPointerType t)
{
while (! t.isEmpty())
triples.add (parseTriple (t));
}
Array<TripleIndex> triples;
void addIndices (Mesh& newMesh, const Mesh& srcMesh, IndexMap& indexMap)
{
TripleIndex i0 (triples[0]), i1, i2 (triples[1]);
for (auto i = 2; i < triples.size(); ++i)
{
i1 = i2;
i2 = triples.getReference (i);
newMesh.indices.add (indexMap.getIndexFor (i0, newMesh, srcMesh));
newMesh.indices.add (indexMap.getIndexFor (i1, newMesh, srcMesh));
newMesh.indices.add (indexMap.getIndexFor (i2, newMesh, srcMesh));
}
}
static TripleIndex parseTriple (String::CharPointerType& t)
{
TripleIndex i;
t.incrementToEndOfWhitespace();
i.vertexIndex = t.getIntValue32() - 1;
t = findEndOfFaceToken (t);
if (t.isEmpty() || t.getAndAdvance() != '/')
return i;
if (*t == '/')
{
++t;
}
else
{
i.textureIndex = t.getIntValue32() - 1;
t = findEndOfFaceToken (t);
if (t.isEmpty() || t.getAndAdvance() != '/')
return i;
}
i.normalIndex = t.getIntValue32() - 1;
t = findEndOfFaceToken (t);
return i;
}
static String::CharPointerType findEndOfFaceToken (String::CharPointerType t) noexcept
{
return CharacterFunctions::findEndOfToken (t, CharPointer_ASCII ("/ \t"), String().getCharPointer());
}
};
static Shape* parseFaceGroup (const Mesh& srcMesh,
Array<Face>& faceGroup,
const Material& material,
const String& name)
{
if (faceGroup.size() == 0)
return nullptr;
std::unique_ptr<Shape> shape (new Shape());
shape->name = name;
shape->material = material;
IndexMap indexMap;
for (auto& f : faceGroup)
f.addIndices (shape->mesh, srcMesh, indexMap);
return shape.release();
}
Result parseObjFile (const StringArray& lines)
{
Mesh mesh;
Array<Face> faceGroup;
Array<Material> knownMaterials;
Material lastMaterial;
String lastName;
for (auto lineNum = 0; lineNum < lines.size(); ++lineNum)
{
auto l = lines[lineNum].getCharPointer().findEndOfWhitespace();
if (matchToken (l, "v")) { mesh.vertices .add (parseVertex (l)); continue; }
if (matchToken (l, "vn")) { mesh.normals .add (parseVertex (l)); continue; }
if (matchToken (l, "vt")) { mesh.textureCoords.add (parseTextureCoord (l)); continue; }
if (matchToken (l, "f")) { faceGroup .add (Face (l)); continue; }
if (matchToken (l, "usemtl"))
{
auto name = String (l).trim();
for (auto i = knownMaterials.size(); --i >= 0;)
{
if (knownMaterials.getReference (i).name == name)
{
lastMaterial = knownMaterials.getReference (i);
break;
}
}
continue;
}
if (matchToken (l, "mtllib"))
{
auto r = parseMaterial (knownMaterials, String (l).trim());
continue;
}
if (matchToken (l, "g") || matchToken (l, "o"))
{
if (auto* shape = parseFaceGroup (mesh, faceGroup, lastMaterial, lastName))
shapes.add (shape);
faceGroup.clear();
lastName = StringArray::fromTokens (l, " \t", "")[0];
continue;
}
}
if (auto* shape = parseFaceGroup (mesh, faceGroup, lastMaterial, lastName))
shapes.add (shape);
return Result::ok();
}
Result parseMaterial (Array<Material>& materials, const String& filename)
{
jassert (sourceFile.exists());
auto f = sourceFile.getSiblingFile (filename);
if (! f.exists())
return Result::fail ("Cannot open file: " + filename);
auto lines = StringArray::fromLines (f.loadFileAsString());
materials.clear();
Material material;
for (auto line : lines)
{
auto l = line.getCharPointer().findEndOfWhitespace();
if (matchToken (l, "newmtl")) { materials.add (material); material.name = String (l).trim(); continue; }
if (matchToken (l, "Ka")) { material.ambient = parseVertex (l); continue; }
if (matchToken (l, "Kd")) { material.diffuse = parseVertex (l); continue; }
if (matchToken (l, "Ks")) { material.specular = parseVertex (l); continue; }
if (matchToken (l, "Kt")) { material.transmittance = parseVertex (l); continue; }
if (matchToken (l, "Ke")) { material.emission = parseVertex (l); continue; }
if (matchToken (l, "Ni")) { material.refractiveIndex = parseFloat (l); continue; }
if (matchToken (l, "Ns")) { material.shininess = parseFloat (l); continue; }
if (matchToken (l, "map_Ka")) { material.ambientTextureName = String (l).trim(); continue; }
if (matchToken (l, "map_Kd")) { material.diffuseTextureName = String (l).trim(); continue; }
if (matchToken (l, "map_Ks")) { material.specularTextureName = String (l).trim(); continue; }
if (matchToken (l, "map_Ns")) { material.normalTextureName = String (l).trim(); continue; }
auto tokens = StringArray::fromTokens (l, " \t", "");
if (tokens.size() >= 2)
material.parameters.set (tokens[0].trim(), tokens[1].trim());
}
materials.add (material);
return Result::ok();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavefrontObjFile)
};

View File

@@ -1,186 +1,186 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: AudioAppDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Simple audio application.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: AudioAppDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
class AudioAppDemo : public AudioAppComponent
{
public:
//==============================================================================
AudioAppDemo()
#ifdef JUCE_DEMO_RUNNER
: AudioAppComponent (getSharedAudioDeviceManager (0, 2))
#endif
{
setAudioChannels (0, 2);
setSize (800, 600);
}
~AudioAppDemo() override
{
shutdownAudio();
}
//==============================================================================
void prepareToPlay (int samplesPerBlockExpected, double newSampleRate) override
{
sampleRate = newSampleRate;
expectedSamplesPerBlock = samplesPerBlockExpected;
}
/* This method generates the actual audio samples.
In this example the buffer is filled with a sine wave whose frequency and
amplitude are controlled by the mouse position.
*/
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
bufferToFill.clearActiveBufferRegion();
auto originalPhase = phase;
for (auto chan = 0; chan < bufferToFill.buffer->getNumChannels(); ++chan)
{
phase = originalPhase;
auto* channelData = bufferToFill.buffer->getWritePointer (chan, bufferToFill.startSample);
for (auto i = 0; i < bufferToFill.numSamples ; ++i)
{
channelData[i] = amplitude * std::sin (phase);
// increment the phase step for the next sample
phase = std::fmod (phase + phaseDelta, MathConstants<float>::twoPi);
}
}
}
void releaseResources() override
{
// This gets automatically called when audio device parameters change
// or device is restarted.
}
//==============================================================================
void paint (Graphics& g) override
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
auto centreY = (float) getHeight() / 2.0f;
auto radius = amplitude * 200.0f;
if (radius >= 0.0f)
{
// Draw an ellipse based on the mouse position and audio volume
g.setColour (Colours::lightgreen);
g.fillEllipse (jmax (0.0f, lastMousePosition.x) - radius / 2.0f,
jmax (0.0f, lastMousePosition.y) - radius / 2.0f,
radius, radius);
}
// Draw a representative sine wave.
Path wavePath;
wavePath.startNewSubPath (0, centreY);
for (auto x = 1.0f; x < (float) getWidth(); ++x)
wavePath.lineTo (x, centreY + amplitude * (float) getHeight() * 2.0f
* std::sin (x * frequency * 0.0001f));
g.setColour (getLookAndFeel().findColour (Slider::thumbColourId));
g.strokePath (wavePath, PathStrokeType (2.0f));
}
// Mouse handling..
void mouseDown (const MouseEvent& e) override
{
mouseDrag (e);
}
void mouseDrag (const MouseEvent& e) override
{
lastMousePosition = e.position;
frequency = (float) (getHeight() - e.y) * 10.0f;
amplitude = jmin (0.9f, 0.2f * e.position.x / (float) getWidth());
phaseDelta = (float) (MathConstants<double>::twoPi * frequency / sampleRate);
repaint();
}
void mouseUp (const MouseEvent&) override
{
amplitude = 0.0f;
repaint();
}
void resized() override
{
// This is called when the component is resized.
// If you add any child components, this is where you should
// update their positions.
}
private:
//==============================================================================
float phase = 0.0f;
float phaseDelta = 0.0f;
float frequency = 5000.0f;
float amplitude = 0.2f;
double sampleRate = 0.0;
int expectedSamplesPerBlock = 0;
Point<float> lastMousePosition;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioAppDemo)
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: AudioAppDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Simple audio application.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: AudioAppDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
class AudioAppDemo : public AudioAppComponent
{
public:
//==============================================================================
AudioAppDemo()
#ifdef JUCE_DEMO_RUNNER
: AudioAppComponent (getSharedAudioDeviceManager (0, 2))
#endif
{
setAudioChannels (0, 2);
setSize (800, 600);
}
~AudioAppDemo() override
{
shutdownAudio();
}
//==============================================================================
void prepareToPlay (int samplesPerBlockExpected, double newSampleRate) override
{
sampleRate = newSampleRate;
expectedSamplesPerBlock = samplesPerBlockExpected;
}
/* This method generates the actual audio samples.
In this example the buffer is filled with a sine wave whose frequency and
amplitude are controlled by the mouse position.
*/
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
bufferToFill.clearActiveBufferRegion();
auto originalPhase = phase;
for (auto chan = 0; chan < bufferToFill.buffer->getNumChannels(); ++chan)
{
phase = originalPhase;
auto* channelData = bufferToFill.buffer->getWritePointer (chan, bufferToFill.startSample);
for (auto i = 0; i < bufferToFill.numSamples ; ++i)
{
channelData[i] = amplitude * std::sin (phase);
// increment the phase step for the next sample
phase = std::fmod (phase + phaseDelta, MathConstants<float>::twoPi);
}
}
}
void releaseResources() override
{
// This gets automatically called when audio device parameters change
// or device is restarted.
}
//==============================================================================
void paint (Graphics& g) override
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
auto centreY = (float) getHeight() / 2.0f;
auto radius = amplitude * 200.0f;
if (radius >= 0.0f)
{
// Draw an ellipse based on the mouse position and audio volume
g.setColour (Colours::lightgreen);
g.fillEllipse (jmax (0.0f, lastMousePosition.x) - radius / 2.0f,
jmax (0.0f, lastMousePosition.y) - radius / 2.0f,
radius, radius);
}
// Draw a representative sine wave.
Path wavePath;
wavePath.startNewSubPath (0, centreY);
for (auto x = 1.0f; x < (float) getWidth(); ++x)
wavePath.lineTo (x, centreY + amplitude * (float) getHeight() * 2.0f
* std::sin (x * frequency * 0.0001f));
g.setColour (getLookAndFeel().findColour (Slider::thumbColourId));
g.strokePath (wavePath, PathStrokeType (2.0f));
}
// Mouse handling..
void mouseDown (const MouseEvent& e) override
{
mouseDrag (e);
}
void mouseDrag (const MouseEvent& e) override
{
lastMousePosition = e.position;
frequency = (float) (getHeight() - e.y) * 10.0f;
amplitude = jmin (0.9f, 0.2f * e.position.x / (float) getWidth());
phaseDelta = (float) (MathConstants<double>::twoPi * frequency / sampleRate);
repaint();
}
void mouseUp (const MouseEvent&) override
{
amplitude = 0.0f;
repaint();
}
void resized() override
{
// This is called when the component is resized.
// If you add any child components, this is where you should
// update their positions.
}
private:
//==============================================================================
float phase = 0.0f;
float phaseDelta = 0.0f;
float frequency = 5000.0f;
float amplitude = 0.2f;
double sampleRate = 0.0;
int expectedSamplesPerBlock = 0;
Point<float> lastMousePosition;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioAppDemo)
};

View File

@@ -1,403 +1,403 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: AudioLatencyDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Tests the audio latency of a device.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: AudioLatencyDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/AudioLiveScrollingDisplay.h"
//==============================================================================
class LatencyTester : public AudioIODeviceCallback,
private Timer
{
public:
LatencyTester (TextEditor& editorBox)
: resultsBox (editorBox)
{}
//==============================================================================
void beginTest()
{
resultsBox.moveCaretToEnd();
resultsBox.insertTextAtCaret (newLine + newLine + "Starting test..." + newLine);
resultsBox.moveCaretToEnd();
startTimer (50);
const ScopedLock sl (lock);
createTestSound();
recordedSound.clear();
playingSampleNum = recordedSampleNum = 0;
testIsRunning = true;
}
void timerCallback() override
{
if (testIsRunning && recordedSampleNum >= recordedSound.getNumSamples())
{
testIsRunning = false;
stopTimer();
// Test has finished, so calculate the result..
auto latencySamples = calculateLatencySamples();
resultsBox.moveCaretToEnd();
resultsBox.insertTextAtCaret (getMessageDescribingResult (latencySamples));
resultsBox.moveCaretToEnd();
}
}
String getMessageDescribingResult (int latencySamples)
{
String message;
if (latencySamples >= 0)
{
message << newLine
<< "Results:" << newLine
<< latencySamples << " samples (" << String (latencySamples * 1000.0 / sampleRate, 1)
<< " milliseconds)" << newLine
<< "The audio device reports an input latency of "
<< deviceInputLatency << " samples, output latency of "
<< deviceOutputLatency << " samples." << newLine
<< "So the corrected latency = "
<< (latencySamples - deviceInputLatency - deviceOutputLatency)
<< " samples (" << String ((latencySamples - deviceInputLatency - deviceOutputLatency) * 1000.0 / sampleRate, 2)
<< " milliseconds)";
}
else
{
message << newLine
<< "Couldn't detect the test signal!!" << newLine
<< "Make sure there's no background noise that might be confusing it..";
}
return message;
}
//==============================================================================
void audioDeviceAboutToStart (AudioIODevice* device) override
{
testIsRunning = false;
playingSampleNum = recordedSampleNum = 0;
sampleRate = device->getCurrentSampleRate();
deviceInputLatency = device->getInputLatencyInSamples();
deviceOutputLatency = device->getOutputLatencyInSamples();
recordedSound.setSize (1, (int) (0.9 * sampleRate));
recordedSound.clear();
}
void audioDeviceStopped() override {}
void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
float** outputChannelData, int numOutputChannels, int numSamples) override
{
const ScopedLock sl (lock);
if (testIsRunning)
{
auto* recordingBuffer = recordedSound.getWritePointer (0);
auto* playBuffer = testSound.getReadPointer (0);
for (int i = 0; i < numSamples; ++i)
{
if (recordedSampleNum < recordedSound.getNumSamples())
{
auto inputSamp = 0.0f;
for (auto j = numInputChannels; --j >= 0;)
if (inputChannelData[j] != nullptr)
inputSamp += inputChannelData[j][i];
recordingBuffer[recordedSampleNum] = inputSamp;
}
++recordedSampleNum;
auto outputSamp = (playingSampleNum < testSound.getNumSamples()) ? playBuffer[playingSampleNum] : 0.0f;
for (auto j = numOutputChannels; --j >= 0;)
if (outputChannelData[j] != nullptr)
outputChannelData[j][i] = outputSamp;
++playingSampleNum;
}
}
else
{
// We need to clear the output buffers, in case they're full of junk..
for (int i = 0; i < numOutputChannels; ++i)
if (outputChannelData[i] != nullptr)
zeromem (outputChannelData[i], (size_t) numSamples * sizeof (float));
}
}
private:
TextEditor& resultsBox;
AudioBuffer<float> testSound, recordedSound;
Array<int> spikePositions;
CriticalSection lock;
int playingSampleNum = 0;
int recordedSampleNum = -1;
double sampleRate = 0.0;
bool testIsRunning = false;
int deviceInputLatency, deviceOutputLatency;
//==============================================================================
// create a test sound which consists of a series of randomly-spaced audio spikes..
void createTestSound()
{
auto length = ((int) sampleRate) / 4;
testSound.setSize (1, length);
testSound.clear();
Random rand;
for (int i = 0; i < length; ++i)
testSound.setSample (0, i, (rand.nextFloat() - rand.nextFloat() + rand.nextFloat() - rand.nextFloat()) * 0.06f);
spikePositions.clear();
int spikePos = 0;
int spikeDelta = 50;
while (spikePos < length - 1)
{
spikePositions.add (spikePos);
testSound.setSample (0, spikePos, 0.99f);
testSound.setSample (0, spikePos + 1, -0.99f);
spikePos += spikeDelta;
spikeDelta += spikeDelta / 6 + rand.nextInt (5);
}
}
// Searches a buffer for a set of spikes that matches those in the test sound
int findOffsetOfSpikes (const AudioBuffer<float>& buffer) const
{
auto minSpikeLevel = 5.0f;
auto smooth = 0.975;
auto* s = buffer.getReadPointer (0);
int spikeDriftAllowed = 5;
Array<int> spikesFound;
spikesFound.ensureStorageAllocated (100);
auto runningAverage = 0.0;
int lastSpike = 0;
for (int i = 0; i < buffer.getNumSamples() - 10; ++i)
{
auto samp = std::abs (s[i]);
if (samp > runningAverage * minSpikeLevel && i > lastSpike + 20)
{
lastSpike = i;
spikesFound.add (i);
}
runningAverage = runningAverage * smooth + (1.0 - smooth) * samp;
}
int bestMatch = -1;
auto bestNumMatches = spikePositions.size() / 3; // the minimum number of matches required
if (spikesFound.size() < bestNumMatches)
return -1;
for (int offsetToTest = 0; offsetToTest < buffer.getNumSamples() - 2048; ++offsetToTest)
{
int numMatchesHere = 0;
int foundIndex = 0;
for (int refIndex = 0; refIndex < spikePositions.size(); ++refIndex)
{
auto referenceSpike = spikePositions.getUnchecked (refIndex) + offsetToTest;
int spike = 0;
while ((spike = spikesFound.getUnchecked (foundIndex)) < referenceSpike - spikeDriftAllowed
&& foundIndex < spikesFound.size() - 1)
++foundIndex;
if (spike >= referenceSpike - spikeDriftAllowed && spike <= referenceSpike + spikeDriftAllowed)
++numMatchesHere;
}
if (numMatchesHere > bestNumMatches)
{
bestNumMatches = numMatchesHere;
bestMatch = offsetToTest;
if (numMatchesHere == spikePositions.size())
break;
}
}
return bestMatch;
}
int calculateLatencySamples() const
{
// Detect the sound in both our test sound and the recording of it, and measure the difference
// in their start times..
auto referenceStart = findOffsetOfSpikes (testSound);
jassert (referenceStart >= 0);
auto recordedStart = findOffsetOfSpikes (recordedSound);
return (recordedStart < 0) ? -1
: (recordedStart - referenceStart);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LatencyTester)
};
//==============================================================================
class AudioLatencyDemo : public Component
{
public:
AudioLatencyDemo()
{
setOpaque (true);
liveAudioScroller.reset (new LiveScrollingAudioDisplay());
addAndMakeVisible (liveAudioScroller.get());
addAndMakeVisible (resultsBox);
resultsBox.setMultiLine (true);
resultsBox.setReturnKeyStartsNewLine (true);
resultsBox.setReadOnly (true);
resultsBox.setScrollbarsShown (true);
resultsBox.setCaretVisible (false);
resultsBox.setPopupMenuEnabled (true);
resultsBox.setColour (TextEditor::outlineColourId, Colour (0x1c000000));
resultsBox.setColour (TextEditor::shadowColourId, Colour (0x16000000));
resultsBox.setText ("Running this test measures the round-trip latency between the audio output and input "
"devices you\'ve got selected.\n\n"
"It\'ll play a sound, then try to measure the time at which the sound arrives "
"back at the audio input. Obviously for this to work you need to have your "
"microphone somewhere near your speakers...");
addAndMakeVisible (startTestButton);
startTestButton.onClick = [this] { startTest(); };
#ifndef JUCE_DEMO_RUNNER
RuntimePermissions::request (RuntimePermissions::recordAudio,
[this] (bool granted)
{
int numInputChannels = granted ? 2 : 0;
audioDeviceManager.initialise (numInputChannels, 2, nullptr, true, {}, nullptr);
});
#endif
audioDeviceManager.addAudioCallback (liveAudioScroller.get());
setSize (500, 500);
}
~AudioLatencyDemo() override
{
audioDeviceManager.removeAudioCallback (liveAudioScroller.get());
audioDeviceManager.removeAudioCallback (latencyTester .get());
latencyTester .reset();
liveAudioScroller.reset();
}
void startTest()
{
if (latencyTester.get() == nullptr)
{
latencyTester.reset (new LatencyTester (resultsBox));
audioDeviceManager.addAudioCallback (latencyTester.get());
}
latencyTester->beginTest();
}
void paint (Graphics& g) override
{
g.fillAll (findColour (ResizableWindow::backgroundColourId));
}
void resized() override
{
auto b = getLocalBounds().reduced (5);
if (liveAudioScroller.get() != nullptr)
{
liveAudioScroller->setBounds (b.removeFromTop (b.getHeight() / 5));
b.removeFromTop (10);
}
startTestButton.setBounds (b.removeFromBottom (b.getHeight() / 10));
b.removeFromBottom (10);
resultsBox.setBounds (b);
}
private:
// if this PIP is running inside the demo runner, we'll use the shared device manager instead
#ifndef JUCE_DEMO_RUNNER
AudioDeviceManager audioDeviceManager;
#else
AudioDeviceManager& audioDeviceManager { getSharedAudioDeviceManager (1, 2) };
#endif
std::unique_ptr<LatencyTester> latencyTester;
std::unique_ptr<LiveScrollingAudioDisplay> liveAudioScroller;
TextButton startTestButton { "Test Latency" };
TextEditor resultsBox;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioLatencyDemo)
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: AudioLatencyDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Tests the audio latency of a device.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: AudioLatencyDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/AudioLiveScrollingDisplay.h"
//==============================================================================
class LatencyTester : public AudioIODeviceCallback,
private Timer
{
public:
LatencyTester (TextEditor& editorBox)
: resultsBox (editorBox)
{}
//==============================================================================
void beginTest()
{
resultsBox.moveCaretToEnd();
resultsBox.insertTextAtCaret (newLine + newLine + "Starting test..." + newLine);
resultsBox.moveCaretToEnd();
startTimer (50);
const ScopedLock sl (lock);
createTestSound();
recordedSound.clear();
playingSampleNum = recordedSampleNum = 0;
testIsRunning = true;
}
void timerCallback() override
{
if (testIsRunning && recordedSampleNum >= recordedSound.getNumSamples())
{
testIsRunning = false;
stopTimer();
// Test has finished, so calculate the result..
auto latencySamples = calculateLatencySamples();
resultsBox.moveCaretToEnd();
resultsBox.insertTextAtCaret (getMessageDescribingResult (latencySamples));
resultsBox.moveCaretToEnd();
}
}
String getMessageDescribingResult (int latencySamples)
{
String message;
if (latencySamples >= 0)
{
message << newLine
<< "Results:" << newLine
<< latencySamples << " samples (" << String (latencySamples * 1000.0 / sampleRate, 1)
<< " milliseconds)" << newLine
<< "The audio device reports an input latency of "
<< deviceInputLatency << " samples, output latency of "
<< deviceOutputLatency << " samples." << newLine
<< "So the corrected latency = "
<< (latencySamples - deviceInputLatency - deviceOutputLatency)
<< " samples (" << String ((latencySamples - deviceInputLatency - deviceOutputLatency) * 1000.0 / sampleRate, 2)
<< " milliseconds)";
}
else
{
message << newLine
<< "Couldn't detect the test signal!!" << newLine
<< "Make sure there's no background noise that might be confusing it..";
}
return message;
}
//==============================================================================
void audioDeviceAboutToStart (AudioIODevice* device) override
{
testIsRunning = false;
playingSampleNum = recordedSampleNum = 0;
sampleRate = device->getCurrentSampleRate();
deviceInputLatency = device->getInputLatencyInSamples();
deviceOutputLatency = device->getOutputLatencyInSamples();
recordedSound.setSize (1, (int) (0.9 * sampleRate));
recordedSound.clear();
}
void audioDeviceStopped() override {}
void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
float** outputChannelData, int numOutputChannels, int numSamples) override
{
const ScopedLock sl (lock);
if (testIsRunning)
{
auto* recordingBuffer = recordedSound.getWritePointer (0);
auto* playBuffer = testSound.getReadPointer (0);
for (int i = 0; i < numSamples; ++i)
{
if (recordedSampleNum < recordedSound.getNumSamples())
{
auto inputSamp = 0.0f;
for (auto j = numInputChannels; --j >= 0;)
if (inputChannelData[j] != nullptr)
inputSamp += inputChannelData[j][i];
recordingBuffer[recordedSampleNum] = inputSamp;
}
++recordedSampleNum;
auto outputSamp = (playingSampleNum < testSound.getNumSamples()) ? playBuffer[playingSampleNum] : 0.0f;
for (auto j = numOutputChannels; --j >= 0;)
if (outputChannelData[j] != nullptr)
outputChannelData[j][i] = outputSamp;
++playingSampleNum;
}
}
else
{
// We need to clear the output buffers, in case they're full of junk..
for (int i = 0; i < numOutputChannels; ++i)
if (outputChannelData[i] != nullptr)
zeromem (outputChannelData[i], (size_t) numSamples * sizeof (float));
}
}
private:
TextEditor& resultsBox;
AudioBuffer<float> testSound, recordedSound;
Array<int> spikePositions;
CriticalSection lock;
int playingSampleNum = 0;
int recordedSampleNum = -1;
double sampleRate = 0.0;
bool testIsRunning = false;
int deviceInputLatency, deviceOutputLatency;
//==============================================================================
// create a test sound which consists of a series of randomly-spaced audio spikes..
void createTestSound()
{
auto length = ((int) sampleRate) / 4;
testSound.setSize (1, length);
testSound.clear();
Random rand;
for (int i = 0; i < length; ++i)
testSound.setSample (0, i, (rand.nextFloat() - rand.nextFloat() + rand.nextFloat() - rand.nextFloat()) * 0.06f);
spikePositions.clear();
int spikePos = 0;
int spikeDelta = 50;
while (spikePos < length - 1)
{
spikePositions.add (spikePos);
testSound.setSample (0, spikePos, 0.99f);
testSound.setSample (0, spikePos + 1, -0.99f);
spikePos += spikeDelta;
spikeDelta += spikeDelta / 6 + rand.nextInt (5);
}
}
// Searches a buffer for a set of spikes that matches those in the test sound
int findOffsetOfSpikes (const AudioBuffer<float>& buffer) const
{
auto minSpikeLevel = 5.0f;
auto smooth = 0.975;
auto* s = buffer.getReadPointer (0);
int spikeDriftAllowed = 5;
Array<int> spikesFound;
spikesFound.ensureStorageAllocated (100);
auto runningAverage = 0.0;
int lastSpike = 0;
for (int i = 0; i < buffer.getNumSamples() - 10; ++i)
{
auto samp = std::abs (s[i]);
if (samp > runningAverage * minSpikeLevel && i > lastSpike + 20)
{
lastSpike = i;
spikesFound.add (i);
}
runningAverage = runningAverage * smooth + (1.0 - smooth) * samp;
}
int bestMatch = -1;
auto bestNumMatches = spikePositions.size() / 3; // the minimum number of matches required
if (spikesFound.size() < bestNumMatches)
return -1;
for (int offsetToTest = 0; offsetToTest < buffer.getNumSamples() - 2048; ++offsetToTest)
{
int numMatchesHere = 0;
int foundIndex = 0;
for (int refIndex = 0; refIndex < spikePositions.size(); ++refIndex)
{
auto referenceSpike = spikePositions.getUnchecked (refIndex) + offsetToTest;
int spike = 0;
while ((spike = spikesFound.getUnchecked (foundIndex)) < referenceSpike - spikeDriftAllowed
&& foundIndex < spikesFound.size() - 1)
++foundIndex;
if (spike >= referenceSpike - spikeDriftAllowed && spike <= referenceSpike + spikeDriftAllowed)
++numMatchesHere;
}
if (numMatchesHere > bestNumMatches)
{
bestNumMatches = numMatchesHere;
bestMatch = offsetToTest;
if (numMatchesHere == spikePositions.size())
break;
}
}
return bestMatch;
}
int calculateLatencySamples() const
{
// Detect the sound in both our test sound and the recording of it, and measure the difference
// in their start times..
auto referenceStart = findOffsetOfSpikes (testSound);
jassert (referenceStart >= 0);
auto recordedStart = findOffsetOfSpikes (recordedSound);
return (recordedStart < 0) ? -1
: (recordedStart - referenceStart);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LatencyTester)
};
//==============================================================================
class AudioLatencyDemo : public Component
{
public:
AudioLatencyDemo()
{
setOpaque (true);
liveAudioScroller.reset (new LiveScrollingAudioDisplay());
addAndMakeVisible (liveAudioScroller.get());
addAndMakeVisible (resultsBox);
resultsBox.setMultiLine (true);
resultsBox.setReturnKeyStartsNewLine (true);
resultsBox.setReadOnly (true);
resultsBox.setScrollbarsShown (true);
resultsBox.setCaretVisible (false);
resultsBox.setPopupMenuEnabled (true);
resultsBox.setColour (TextEditor::outlineColourId, Colour (0x1c000000));
resultsBox.setColour (TextEditor::shadowColourId, Colour (0x16000000));
resultsBox.setText ("Running this test measures the round-trip latency between the audio output and input "
"devices you\'ve got selected.\n\n"
"It\'ll play a sound, then try to measure the time at which the sound arrives "
"back at the audio input. Obviously for this to work you need to have your "
"microphone somewhere near your speakers...");
addAndMakeVisible (startTestButton);
startTestButton.onClick = [this] { startTest(); };
#ifndef JUCE_DEMO_RUNNER
RuntimePermissions::request (RuntimePermissions::recordAudio,
[this] (bool granted)
{
int numInputChannels = granted ? 2 : 0;
audioDeviceManager.initialise (numInputChannels, 2, nullptr, true, {}, nullptr);
});
#endif
audioDeviceManager.addAudioCallback (liveAudioScroller.get());
setSize (500, 500);
}
~AudioLatencyDemo() override
{
audioDeviceManager.removeAudioCallback (liveAudioScroller.get());
audioDeviceManager.removeAudioCallback (latencyTester .get());
latencyTester .reset();
liveAudioScroller.reset();
}
void startTest()
{
if (latencyTester.get() == nullptr)
{
latencyTester.reset (new LatencyTester (resultsBox));
audioDeviceManager.addAudioCallback (latencyTester.get());
}
latencyTester->beginTest();
}
void paint (Graphics& g) override
{
g.fillAll (findColour (ResizableWindow::backgroundColourId));
}
void resized() override
{
auto b = getLocalBounds().reduced (5);
if (liveAudioScroller.get() != nullptr)
{
liveAudioScroller->setBounds (b.removeFromTop (b.getHeight() / 5));
b.removeFromTop (10);
}
startTestButton.setBounds (b.removeFromBottom (b.getHeight() / 10));
b.removeFromBottom (10);
resultsBox.setBounds (b);
}
private:
// if this PIP is running inside the demo runner, we'll use the shared device manager instead
#ifndef JUCE_DEMO_RUNNER
AudioDeviceManager audioDeviceManager;
#else
AudioDeviceManager& audioDeviceManager { getSharedAudioDeviceManager (1, 2) };
#endif
std::unique_ptr<LatencyTester> latencyTester;
std::unique_ptr<LiveScrollingAudioDisplay> liveAudioScroller;
TextButton startTestButton { "Test Latency" };
TextEditor resultsBox;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioLatencyDemo)
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,376 +1,376 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: AudioRecordingDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Records audio to a file.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: AudioRecordingDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/AudioLiveScrollingDisplay.h"
//==============================================================================
/** A simple class that acts as an AudioIODeviceCallback and writes the
incoming audio data to a WAV file.
*/
class AudioRecorder : public AudioIODeviceCallback
{
public:
AudioRecorder (AudioThumbnail& thumbnailToUpdate)
: thumbnail (thumbnailToUpdate)
{
backgroundThread.startThread();
}
~AudioRecorder() override
{
stop();
}
//==============================================================================
void startRecording (const File& file)
{
stop();
if (sampleRate > 0)
{
// Create an OutputStream to write to our destination file...
file.deleteFile();
if (auto fileStream = std::unique_ptr<FileOutputStream> (file.createOutputStream()))
{
// Now create a WAV writer object that writes to our output stream...
WavAudioFormat wavFormat;
if (auto writer = wavFormat.createWriterFor (fileStream.get(), sampleRate, 1, 16, {}, 0))
{
fileStream.release(); // (passes responsibility for deleting the stream to the writer object that is now using it)
// Now we'll create one of these helper objects which will act as a FIFO buffer, and will
// write the data to disk on our background thread.
threadedWriter.reset (new AudioFormatWriter::ThreadedWriter (writer, backgroundThread, 32768));
// Reset our recording thumbnail
thumbnail.reset (writer->getNumChannels(), writer->getSampleRate());
nextSampleNum = 0;
// And now, swap over our active writer pointer so that the audio callback will start using it..
const ScopedLock sl (writerLock);
activeWriter = threadedWriter.get();
}
}
}
}
void stop()
{
// First, clear this pointer to stop the audio callback from using our writer object..
{
const ScopedLock sl (writerLock);
activeWriter = nullptr;
}
// Now we can delete the writer object. It's done in this order because the deletion could
// take a little time while remaining data gets flushed to disk, so it's best to avoid blocking
// the audio callback while this happens.
threadedWriter.reset();
}
bool isRecording() const
{
return activeWriter.load() != nullptr;
}
//==============================================================================
void audioDeviceAboutToStart (AudioIODevice* device) override
{
sampleRate = device->getCurrentSampleRate();
}
void audioDeviceStopped() override
{
sampleRate = 0;
}
void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
float** outputChannelData, int numOutputChannels,
int numSamples) override
{
const ScopedLock sl (writerLock);
if (activeWriter.load() != nullptr && numInputChannels >= thumbnail.getNumChannels())
{
activeWriter.load()->write (inputChannelData, numSamples);
// Create an AudioBuffer to wrap our incoming data, note that this does no allocations or copies, it simply references our input data
AudioBuffer<float> buffer (const_cast<float**> (inputChannelData), thumbnail.getNumChannels(), numSamples);
thumbnail.addBlock (nextSampleNum, buffer, 0, numSamples);
nextSampleNum += numSamples;
}
// We need to clear the output buffers, in case they're full of junk..
for (int i = 0; i < numOutputChannels; ++i)
if (outputChannelData[i] != nullptr)
FloatVectorOperations::clear (outputChannelData[i], numSamples);
}
private:
AudioThumbnail& thumbnail;
TimeSliceThread backgroundThread { "Audio Recorder Thread" }; // the thread that will write our audio data to disk
std::unique_ptr<AudioFormatWriter::ThreadedWriter> threadedWriter; // the FIFO used to buffer the incoming data
double sampleRate = 0.0;
int64 nextSampleNum = 0;
CriticalSection writerLock;
std::atomic<AudioFormatWriter::ThreadedWriter*> activeWriter { nullptr };
};
//==============================================================================
class RecordingThumbnail : public Component,
private ChangeListener
{
public:
RecordingThumbnail()
{
formatManager.registerBasicFormats();
thumbnail.addChangeListener (this);
}
~RecordingThumbnail() override
{
thumbnail.removeChangeListener (this);
}
AudioThumbnail& getAudioThumbnail() { return thumbnail; }
void setDisplayFullThumbnail (bool displayFull)
{
displayFullThumb = displayFull;
repaint();
}
void paint (Graphics& g) override
{
g.fillAll (Colours::darkgrey);
g.setColour (Colours::lightgrey);
if (thumbnail.getTotalLength() > 0.0)
{
auto endTime = displayFullThumb ? thumbnail.getTotalLength()
: jmax (30.0, thumbnail.getTotalLength());
auto thumbArea = getLocalBounds();
thumbnail.drawChannels (g, thumbArea.reduced (2), 0.0, endTime, 1.0f);
}
else
{
g.setFont (14.0f);
g.drawFittedText ("(No file recorded)", getLocalBounds(), Justification::centred, 2);
}
}
private:
AudioFormatManager formatManager;
AudioThumbnailCache thumbnailCache { 10 };
AudioThumbnail thumbnail { 512, formatManager, thumbnailCache };
bool displayFullThumb = false;
void changeListenerCallback (ChangeBroadcaster* source) override
{
if (source == &thumbnail)
repaint();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RecordingThumbnail)
};
//==============================================================================
class AudioRecordingDemo : public Component
{
public:
AudioRecordingDemo()
{
setOpaque (true);
addAndMakeVisible (liveAudioScroller);
addAndMakeVisible (explanationLabel);
explanationLabel.setFont (Font (15.0f, Font::plain));
explanationLabel.setJustificationType (Justification::topLeft);
explanationLabel.setEditable (false, false, false);
explanationLabel.setColour (TextEditor::textColourId, Colours::black);
explanationLabel.setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (recordButton);
recordButton.setColour (TextButton::buttonColourId, Colour (0xffff5c5c));
recordButton.setColour (TextButton::textColourOnId, Colours::black);
recordButton.onClick = [this]
{
if (recorder.isRecording())
stopRecording();
else
startRecording();
};
addAndMakeVisible (recordingThumbnail);
#ifndef JUCE_DEMO_RUNNER
RuntimePermissions::request (RuntimePermissions::recordAudio,
[this] (bool granted)
{
int numInputChannels = granted ? 2 : 0;
audioDeviceManager.initialise (numInputChannels, 2, nullptr, true, {}, nullptr);
});
#endif
audioDeviceManager.addAudioCallback (&liveAudioScroller);
audioDeviceManager.addAudioCallback (&recorder);
setSize (500, 500);
}
~AudioRecordingDemo() override
{
audioDeviceManager.removeAudioCallback (&recorder);
audioDeviceManager.removeAudioCallback (&liveAudioScroller);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
}
void resized() override
{
auto area = getLocalBounds();
liveAudioScroller .setBounds (area.removeFromTop (80).reduced (8));
recordingThumbnail.setBounds (area.removeFromTop (80).reduced (8));
recordButton .setBounds (area.removeFromTop (36).removeFromLeft (140).reduced (8));
explanationLabel .setBounds (area.reduced (8));
}
private:
// if this PIP is running inside the demo runner, we'll use the shared device manager instead
#ifndef JUCE_DEMO_RUNNER
AudioDeviceManager audioDeviceManager;
#else
AudioDeviceManager& audioDeviceManager { getSharedAudioDeviceManager (1, 0) };
#endif
LiveScrollingAudioDisplay liveAudioScroller;
RecordingThumbnail recordingThumbnail;
AudioRecorder recorder { recordingThumbnail.getAudioThumbnail() };
Label explanationLabel { {}, "This page demonstrates how to record a wave file from the live audio input..\n\n"
#if (JUCE_ANDROID || JUCE_IOS)
"After you are done with your recording you can share with other apps."
#else
"Pressing record will start recording a file in your \"Documents\" folder."
#endif
};
TextButton recordButton { "Record" };
File lastRecording;
void startRecording()
{
if (! RuntimePermissions::isGranted (RuntimePermissions::writeExternalStorage))
{
SafePointer<AudioRecordingDemo> safeThis (this);
RuntimePermissions::request (RuntimePermissions::writeExternalStorage,
[safeThis] (bool granted) mutable
{
if (granted)
safeThis->startRecording();
});
return;
}
#if (JUCE_ANDROID || JUCE_IOS)
auto parentDir = File::getSpecialLocation (File::tempDirectory);
#else
auto parentDir = File::getSpecialLocation (File::userDocumentsDirectory);
#endif
lastRecording = parentDir.getNonexistentChildFile ("JUCE Demo Audio Recording", ".wav");
recorder.startRecording (lastRecording);
recordButton.setButtonText ("Stop");
recordingThumbnail.setDisplayFullThumbnail (false);
}
void stopRecording()
{
recorder.stop();
#if JUCE_CONTENT_SHARING
SafePointer<AudioRecordingDemo> safeThis (this);
File fileToShare = lastRecording;
ContentSharer::getInstance()->shareFiles (Array<URL> ({URL (fileToShare)}),
[safeThis, fileToShare] (bool success, const String& error)
{
if (fileToShare.existsAsFile())
fileToShare.deleteFile();
if (! success && error.isNotEmpty())
NativeMessageBox::showAsync (MessageBoxOptions()
.withIconType (MessageBoxIconType::WarningIcon)
.withTitle ("Sharing Error")
.withMessage (error),
nullptr);
});
#endif
lastRecording = File();
recordButton.setButtonText ("Record");
recordingThumbnail.setDisplayFullThumbnail (true);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioRecordingDemo)
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: AudioRecordingDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Records audio to a file.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: AudioRecordingDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/AudioLiveScrollingDisplay.h"
//==============================================================================
/** A simple class that acts as an AudioIODeviceCallback and writes the
incoming audio data to a WAV file.
*/
class AudioRecorder : public AudioIODeviceCallback
{
public:
AudioRecorder (AudioThumbnail& thumbnailToUpdate)
: thumbnail (thumbnailToUpdate)
{
backgroundThread.startThread();
}
~AudioRecorder() override
{
stop();
}
//==============================================================================
void startRecording (const File& file)
{
stop();
if (sampleRate > 0)
{
// Create an OutputStream to write to our destination file...
file.deleteFile();
if (auto fileStream = std::unique_ptr<FileOutputStream> (file.createOutputStream()))
{
// Now create a WAV writer object that writes to our output stream...
WavAudioFormat wavFormat;
if (auto writer = wavFormat.createWriterFor (fileStream.get(), sampleRate, 1, 16, {}, 0))
{
fileStream.release(); // (passes responsibility for deleting the stream to the writer object that is now using it)
// Now we'll create one of these helper objects which will act as a FIFO buffer, and will
// write the data to disk on our background thread.
threadedWriter.reset (new AudioFormatWriter::ThreadedWriter (writer, backgroundThread, 32768));
// Reset our recording thumbnail
thumbnail.reset (writer->getNumChannels(), writer->getSampleRate());
nextSampleNum = 0;
// And now, swap over our active writer pointer so that the audio callback will start using it..
const ScopedLock sl (writerLock);
activeWriter = threadedWriter.get();
}
}
}
}
void stop()
{
// First, clear this pointer to stop the audio callback from using our writer object..
{
const ScopedLock sl (writerLock);
activeWriter = nullptr;
}
// Now we can delete the writer object. It's done in this order because the deletion could
// take a little time while remaining data gets flushed to disk, so it's best to avoid blocking
// the audio callback while this happens.
threadedWriter.reset();
}
bool isRecording() const
{
return activeWriter.load() != nullptr;
}
//==============================================================================
void audioDeviceAboutToStart (AudioIODevice* device) override
{
sampleRate = device->getCurrentSampleRate();
}
void audioDeviceStopped() override
{
sampleRate = 0;
}
void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
float** outputChannelData, int numOutputChannels,
int numSamples) override
{
const ScopedLock sl (writerLock);
if (activeWriter.load() != nullptr && numInputChannels >= thumbnail.getNumChannels())
{
activeWriter.load()->write (inputChannelData, numSamples);
// Create an AudioBuffer to wrap our incoming data, note that this does no allocations or copies, it simply references our input data
AudioBuffer<float> buffer (const_cast<float**> (inputChannelData), thumbnail.getNumChannels(), numSamples);
thumbnail.addBlock (nextSampleNum, buffer, 0, numSamples);
nextSampleNum += numSamples;
}
// We need to clear the output buffers, in case they're full of junk..
for (int i = 0; i < numOutputChannels; ++i)
if (outputChannelData[i] != nullptr)
FloatVectorOperations::clear (outputChannelData[i], numSamples);
}
private:
AudioThumbnail& thumbnail;
TimeSliceThread backgroundThread { "Audio Recorder Thread" }; // the thread that will write our audio data to disk
std::unique_ptr<AudioFormatWriter::ThreadedWriter> threadedWriter; // the FIFO used to buffer the incoming data
double sampleRate = 0.0;
int64 nextSampleNum = 0;
CriticalSection writerLock;
std::atomic<AudioFormatWriter::ThreadedWriter*> activeWriter { nullptr };
};
//==============================================================================
class RecordingThumbnail : public Component,
private ChangeListener
{
public:
RecordingThumbnail()
{
formatManager.registerBasicFormats();
thumbnail.addChangeListener (this);
}
~RecordingThumbnail() override
{
thumbnail.removeChangeListener (this);
}
AudioThumbnail& getAudioThumbnail() { return thumbnail; }
void setDisplayFullThumbnail (bool displayFull)
{
displayFullThumb = displayFull;
repaint();
}
void paint (Graphics& g) override
{
g.fillAll (Colours::darkgrey);
g.setColour (Colours::lightgrey);
if (thumbnail.getTotalLength() > 0.0)
{
auto endTime = displayFullThumb ? thumbnail.getTotalLength()
: jmax (30.0, thumbnail.getTotalLength());
auto thumbArea = getLocalBounds();
thumbnail.drawChannels (g, thumbArea.reduced (2), 0.0, endTime, 1.0f);
}
else
{
g.setFont (14.0f);
g.drawFittedText ("(No file recorded)", getLocalBounds(), Justification::centred, 2);
}
}
private:
AudioFormatManager formatManager;
AudioThumbnailCache thumbnailCache { 10 };
AudioThumbnail thumbnail { 512, formatManager, thumbnailCache };
bool displayFullThumb = false;
void changeListenerCallback (ChangeBroadcaster* source) override
{
if (source == &thumbnail)
repaint();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RecordingThumbnail)
};
//==============================================================================
class AudioRecordingDemo : public Component
{
public:
AudioRecordingDemo()
{
setOpaque (true);
addAndMakeVisible (liveAudioScroller);
addAndMakeVisible (explanationLabel);
explanationLabel.setFont (Font (15.0f, Font::plain));
explanationLabel.setJustificationType (Justification::topLeft);
explanationLabel.setEditable (false, false, false);
explanationLabel.setColour (TextEditor::textColourId, Colours::black);
explanationLabel.setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (recordButton);
recordButton.setColour (TextButton::buttonColourId, Colour (0xffff5c5c));
recordButton.setColour (TextButton::textColourOnId, Colours::black);
recordButton.onClick = [this]
{
if (recorder.isRecording())
stopRecording();
else
startRecording();
};
addAndMakeVisible (recordingThumbnail);
#ifndef JUCE_DEMO_RUNNER
RuntimePermissions::request (RuntimePermissions::recordAudio,
[this] (bool granted)
{
int numInputChannels = granted ? 2 : 0;
audioDeviceManager.initialise (numInputChannels, 2, nullptr, true, {}, nullptr);
});
#endif
audioDeviceManager.addAudioCallback (&liveAudioScroller);
audioDeviceManager.addAudioCallback (&recorder);
setSize (500, 500);
}
~AudioRecordingDemo() override
{
audioDeviceManager.removeAudioCallback (&recorder);
audioDeviceManager.removeAudioCallback (&liveAudioScroller);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
}
void resized() override
{
auto area = getLocalBounds();
liveAudioScroller .setBounds (area.removeFromTop (80).reduced (8));
recordingThumbnail.setBounds (area.removeFromTop (80).reduced (8));
recordButton .setBounds (area.removeFromTop (36).removeFromLeft (140).reduced (8));
explanationLabel .setBounds (area.reduced (8));
}
private:
// if this PIP is running inside the demo runner, we'll use the shared device manager instead
#ifndef JUCE_DEMO_RUNNER
AudioDeviceManager audioDeviceManager;
#else
AudioDeviceManager& audioDeviceManager { getSharedAudioDeviceManager (1, 0) };
#endif
LiveScrollingAudioDisplay liveAudioScroller;
RecordingThumbnail recordingThumbnail;
AudioRecorder recorder { recordingThumbnail.getAudioThumbnail() };
Label explanationLabel { {}, "This page demonstrates how to record a wave file from the live audio input..\n\n"
#if (JUCE_ANDROID || JUCE_IOS)
"After you are done with your recording you can share with other apps."
#else
"Pressing record will start recording a file in your \"Documents\" folder."
#endif
};
TextButton recordButton { "Record" };
File lastRecording;
void startRecording()
{
if (! RuntimePermissions::isGranted (RuntimePermissions::writeExternalStorage))
{
SafePointer<AudioRecordingDemo> safeThis (this);
RuntimePermissions::request (RuntimePermissions::writeExternalStorage,
[safeThis] (bool granted) mutable
{
if (granted)
safeThis->startRecording();
});
return;
}
#if (JUCE_ANDROID || JUCE_IOS)
auto parentDir = File::getSpecialLocation (File::tempDirectory);
#else
auto parentDir = File::getSpecialLocation (File::userDocumentsDirectory);
#endif
lastRecording = parentDir.getNonexistentChildFile ("JUCE Demo Audio Recording", ".wav");
recorder.startRecording (lastRecording);
recordButton.setButtonText ("Stop");
recordingThumbnail.setDisplayFullThumbnail (false);
}
void stopRecording()
{
recorder.stop();
#if JUCE_CONTENT_SHARING
SafePointer<AudioRecordingDemo> safeThis (this);
File fileToShare = lastRecording;
ContentSharer::getInstance()->shareFiles (Array<URL> ({URL (fileToShare)}),
[safeThis, fileToShare] (bool success, const String& error)
{
if (fileToShare.existsAsFile())
fileToShare.deleteFile();
if (! success && error.isNotEmpty())
NativeMessageBox::showAsync (MessageBoxOptions()
.withIconType (MessageBoxIconType::WarningIcon)
.withTitle ("Sharing Error")
.withMessage (error),
nullptr);
});
#endif
lastRecording = File();
recordButton.setButtonText ("Record");
recordingThumbnail.setDisplayFullThumbnail (true);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioRecordingDemo)
};

View File

@@ -1,173 +1,173 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: AudioSettingsDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Displays information about audio devices.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: AudioSettingsDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
class AudioSettingsDemo : public Component,
public ChangeListener
{
public:
AudioSettingsDemo()
{
setOpaque (true);
#ifndef JUCE_DEMO_RUNNER
RuntimePermissions::request (RuntimePermissions::recordAudio,
[this] (bool granted)
{
int numInputChannels = granted ? 2 : 0;
audioDeviceManager.initialise (numInputChannels, 2, nullptr, true, {}, nullptr);
});
#endif
audioSetupComp.reset (new AudioDeviceSelectorComponent (audioDeviceManager,
0, 256, 0, 256, true, true, true, false));
addAndMakeVisible (audioSetupComp.get());
addAndMakeVisible (diagnosticsBox);
diagnosticsBox.setMultiLine (true);
diagnosticsBox.setReturnKeyStartsNewLine (true);
diagnosticsBox.setReadOnly (true);
diagnosticsBox.setScrollbarsShown (true);
diagnosticsBox.setCaretVisible (false);
diagnosticsBox.setPopupMenuEnabled (true);
audioDeviceManager.addChangeListener (this);
logMessage ("Audio device diagnostics:\n");
dumpDeviceInfo();
setSize (500, 600);
}
~AudioSettingsDemo() override
{
audioDeviceManager.removeChangeListener (this);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
}
void resized() override
{
auto r = getLocalBounds().reduced (4);
audioSetupComp->setBounds (r.removeFromTop (proportionOfHeight (0.65f)));
diagnosticsBox.setBounds (r);
}
void dumpDeviceInfo()
{
logMessage ("--------------------------------------");
logMessage ("Current audio device type: " + (audioDeviceManager.getCurrentDeviceTypeObject() != nullptr
? audioDeviceManager.getCurrentDeviceTypeObject()->getTypeName()
: "<none>"));
if (AudioIODevice* device = audioDeviceManager.getCurrentAudioDevice())
{
logMessage ("Current audio device: " + device->getName().quoted());
logMessage ("Sample rate: " + String (device->getCurrentSampleRate()) + " Hz");
logMessage ("Block size: " + String (device->getCurrentBufferSizeSamples()) + " samples");
logMessage ("Output Latency: " + String (device->getOutputLatencyInSamples()) + " samples");
logMessage ("Input Latency: " + String (device->getInputLatencyInSamples()) + " samples");
logMessage ("Bit depth: " + String (device->getCurrentBitDepth()));
logMessage ("Input channel names: " + device->getInputChannelNames().joinIntoString (", "));
logMessage ("Active input channels: " + getListOfActiveBits (device->getActiveInputChannels()));
logMessage ("Output channel names: " + device->getOutputChannelNames().joinIntoString (", "));
logMessage ("Active output channels: " + getListOfActiveBits (device->getActiveOutputChannels()));
}
else
{
logMessage ("No audio device open");
}
}
void logMessage (const String& m)
{
diagnosticsBox.moveCaretToEnd();
diagnosticsBox.insertTextAtCaret (m + newLine);
}
private:
// if this PIP is running inside the demo runner, we'll use the shared device manager instead
#ifndef JUCE_DEMO_RUNNER
AudioDeviceManager audioDeviceManager;
#else
AudioDeviceManager& audioDeviceManager { getSharedAudioDeviceManager() };
#endif
std::unique_ptr<AudioDeviceSelectorComponent> audioSetupComp;
TextEditor diagnosticsBox;
void changeListenerCallback (ChangeBroadcaster*) override
{
dumpDeviceInfo();
}
void lookAndFeelChanged() override
{
diagnosticsBox.applyFontToAllText (diagnosticsBox.getFont());
}
static String getListOfActiveBits (const BigInteger& b)
{
StringArray bits;
for (int i = 0; i <= b.getHighestBit(); ++i)
if (b[i])
bits.add (String (i));
return bits.joinIntoString (", ");
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSettingsDemo)
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: AudioSettingsDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Displays information about audio devices.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: AudioSettingsDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
//==============================================================================
class AudioSettingsDemo : public Component,
public ChangeListener
{
public:
AudioSettingsDemo()
{
setOpaque (true);
#ifndef JUCE_DEMO_RUNNER
RuntimePermissions::request (RuntimePermissions::recordAudio,
[this] (bool granted)
{
int numInputChannels = granted ? 2 : 0;
audioDeviceManager.initialise (numInputChannels, 2, nullptr, true, {}, nullptr);
});
#endif
audioSetupComp.reset (new AudioDeviceSelectorComponent (audioDeviceManager,
0, 256, 0, 256, true, true, true, false));
addAndMakeVisible (audioSetupComp.get());
addAndMakeVisible (diagnosticsBox);
diagnosticsBox.setMultiLine (true);
diagnosticsBox.setReturnKeyStartsNewLine (true);
diagnosticsBox.setReadOnly (true);
diagnosticsBox.setScrollbarsShown (true);
diagnosticsBox.setCaretVisible (false);
diagnosticsBox.setPopupMenuEnabled (true);
audioDeviceManager.addChangeListener (this);
logMessage ("Audio device diagnostics:\n");
dumpDeviceInfo();
setSize (500, 600);
}
~AudioSettingsDemo() override
{
audioDeviceManager.removeChangeListener (this);
}
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
}
void resized() override
{
auto r = getLocalBounds().reduced (4);
audioSetupComp->setBounds (r.removeFromTop (proportionOfHeight (0.65f)));
diagnosticsBox.setBounds (r);
}
void dumpDeviceInfo()
{
logMessage ("--------------------------------------");
logMessage ("Current audio device type: " + (audioDeviceManager.getCurrentDeviceTypeObject() != nullptr
? audioDeviceManager.getCurrentDeviceTypeObject()->getTypeName()
: "<none>"));
if (AudioIODevice* device = audioDeviceManager.getCurrentAudioDevice())
{
logMessage ("Current audio device: " + device->getName().quoted());
logMessage ("Sample rate: " + String (device->getCurrentSampleRate()) + " Hz");
logMessage ("Block size: " + String (device->getCurrentBufferSizeSamples()) + " samples");
logMessage ("Output Latency: " + String (device->getOutputLatencyInSamples()) + " samples");
logMessage ("Input Latency: " + String (device->getInputLatencyInSamples()) + " samples");
logMessage ("Bit depth: " + String (device->getCurrentBitDepth()));
logMessage ("Input channel names: " + device->getInputChannelNames().joinIntoString (", "));
logMessage ("Active input channels: " + getListOfActiveBits (device->getActiveInputChannels()));
logMessage ("Output channel names: " + device->getOutputChannelNames().joinIntoString (", "));
logMessage ("Active output channels: " + getListOfActiveBits (device->getActiveOutputChannels()));
}
else
{
logMessage ("No audio device open");
}
}
void logMessage (const String& m)
{
diagnosticsBox.moveCaretToEnd();
diagnosticsBox.insertTextAtCaret (m + newLine);
}
private:
// if this PIP is running inside the demo runner, we'll use the shared device manager instead
#ifndef JUCE_DEMO_RUNNER
AudioDeviceManager audioDeviceManager;
#else
AudioDeviceManager& audioDeviceManager { getSharedAudioDeviceManager() };
#endif
std::unique_ptr<AudioDeviceSelectorComponent> audioSetupComp;
TextEditor diagnosticsBox;
void changeListenerCallback (ChangeBroadcaster*) override
{
dumpDeviceInfo();
}
void lookAndFeelChanged() override
{
diagnosticsBox.applyFontToAllText (diagnosticsBox.getFont());
}
static String getListOfActiveBits (const BigInteger& b)
{
StringArray bits;
for (int i = 0; i <= b.getHighestBit(); ++i)
if (b[i])
bits.add (String (i));
return bits.joinIntoString (", ");
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSettingsDemo)
};

View File

@@ -1,322 +1,322 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: AudioSynthesiserDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Simple synthesiser application.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: AudioSynthesiserDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/AudioLiveScrollingDisplay.h"
//==============================================================================
/** Our demo synth sound is just a basic sine wave.. */
struct SineWaveSound : public SynthesiserSound
{
SineWaveSound() {}
bool appliesToNote (int /*midiNoteNumber*/) override { return true; }
bool appliesToChannel (int /*midiChannel*/) override { return true; }
};
//==============================================================================
/** Our demo synth voice just plays a sine wave.. */
struct SineWaveVoice : public SynthesiserVoice
{
SineWaveVoice() {}
bool canPlaySound (SynthesiserSound* sound) override
{
return dynamic_cast<SineWaveSound*> (sound) != nullptr;
}
void startNote (int midiNoteNumber, float velocity,
SynthesiserSound*, int /*currentPitchWheelPosition*/) override
{
currentAngle = 0.0;
level = velocity * 0.15;
tailOff = 0.0;
auto cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
auto cyclesPerSample = cyclesPerSecond / getSampleRate();
angleDelta = cyclesPerSample * MathConstants<double>::twoPi;
}
void stopNote (float /*velocity*/, bool allowTailOff) override
{
if (allowTailOff)
{
// start a tail-off by setting this flag. The render callback will pick up on
// this and do a fade out, calling clearCurrentNote() when it's finished.
if (tailOff == 0.0) // we only need to begin a tail-off if it's not already doing so - the
tailOff = 1.0; // stopNote method could be called more than once.
}
else
{
// we're being told to stop playing immediately, so reset everything..
clearCurrentNote();
angleDelta = 0.0;
}
}
void pitchWheelMoved (int /*newValue*/) override {}
void controllerMoved (int /*controllerNumber*/, int /*newValue*/) override {}
void renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override
{
if (angleDelta != 0.0)
{
if (tailOff > 0.0)
{
while (--numSamples >= 0)
{
auto currentSample = (float) (std::sin (currentAngle) * level * tailOff);
for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample (i, startSample, currentSample);
currentAngle += angleDelta;
++startSample;
tailOff *= 0.99;
if (tailOff <= 0.005)
{
clearCurrentNote();
angleDelta = 0.0;
break;
}
}
}
else
{
while (--numSamples >= 0)
{
auto currentSample = (float) (std::sin (currentAngle) * level);
for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample (i, startSample, currentSample);
currentAngle += angleDelta;
++startSample;
}
}
}
}
using SynthesiserVoice::renderNextBlock;
private:
double currentAngle = 0.0, angleDelta = 0.0, level = 0.0, tailOff = 0.0;
};
//==============================================================================
// This is an audio source that streams the output of our demo synth.
struct SynthAudioSource : public AudioSource
{
SynthAudioSource (MidiKeyboardState& keyState) : keyboardState (keyState)
{
// Add some voices to our synth, to play the sounds..
for (auto i = 0; i < 4; ++i)
{
synth.addVoice (new SineWaveVoice()); // These voices will play our custom sine-wave sounds..
synth.addVoice (new SamplerVoice()); // and these ones play the sampled sounds
}
// ..and add a sound for them to play...
setUsingSineWaveSound();
}
void setUsingSineWaveSound()
{
synth.clearSounds();
synth.addSound (new SineWaveSound());
}
void setUsingSampledSound()
{
WavAudioFormat wavFormat;
std::unique_ptr<AudioFormatReader> audioReader (wavFormat.createReaderFor (createAssetInputStream ("cello.wav").release(), true));
BigInteger allNotes;
allNotes.setRange (0, 128, true);
synth.clearSounds();
synth.addSound (new SamplerSound ("demo sound",
*audioReader,
allNotes,
74, // root midi note
0.1, // attack time
0.1, // release time
10.0 // maximum sample length
));
}
void prepareToPlay (int /*samplesPerBlockExpected*/, double sampleRate) override
{
midiCollector.reset (sampleRate);
synth.setCurrentPlaybackSampleRate (sampleRate);
}
void releaseResources() override {}
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
// the synth always adds its output to the audio buffer, so we have to clear it
// first..
bufferToFill.clearActiveBufferRegion();
// fill a midi buffer with incoming messages from the midi input.
MidiBuffer incomingMidi;
midiCollector.removeNextBlockOfMessages (incomingMidi, bufferToFill.numSamples);
// pass these messages to the keyboard state so that it can update the component
// to show on-screen which keys are being pressed on the physical midi keyboard.
// This call will also add midi messages to the buffer which were generated by
// the mouse-clicking on the on-screen keyboard.
keyboardState.processNextMidiBuffer (incomingMidi, 0, bufferToFill.numSamples, true);
// and now get the synth to process the midi events and generate its output.
synth.renderNextBlock (*bufferToFill.buffer, incomingMidi, 0, bufferToFill.numSamples);
}
//==============================================================================
// this collects real-time midi messages from the midi input device, and
// turns them into blocks that we can process in our audio callback
MidiMessageCollector midiCollector;
// this represents the state of which keys on our on-screen keyboard are held
// down. When the mouse is clicked on the keyboard component, this object also
// generates midi messages for this, which we can pass on to our synth.
MidiKeyboardState& keyboardState;
// the synth itself!
Synthesiser synth;
};
//==============================================================================
class AudioSynthesiserDemo : public Component
{
public:
AudioSynthesiserDemo()
{
addAndMakeVisible (keyboardComponent);
addAndMakeVisible (sineButton);
sineButton.setRadioGroupId (321);
sineButton.setToggleState (true, dontSendNotification);
sineButton.onClick = [this] { synthAudioSource.setUsingSineWaveSound(); };
addAndMakeVisible (sampledButton);
sampledButton.setRadioGroupId (321);
sampledButton.onClick = [this] { synthAudioSource.setUsingSampledSound(); };
addAndMakeVisible (liveAudioDisplayComp);
audioDeviceManager.addAudioCallback (&liveAudioDisplayComp);
audioSourcePlayer.setSource (&synthAudioSource);
#ifndef JUCE_DEMO_RUNNER
RuntimePermissions::request (RuntimePermissions::recordAudio,
[this] (bool granted)
{
int numInputChannels = granted ? 2 : 0;
audioDeviceManager.initialise (numInputChannels, 2, nullptr, true, {}, nullptr);
});
#endif
audioDeviceManager.addAudioCallback (&audioSourcePlayer);
audioDeviceManager.addMidiInputDeviceCallback ({}, &(synthAudioSource.midiCollector));
setOpaque (true);
setSize (640, 480);
}
~AudioSynthesiserDemo() override
{
audioSourcePlayer.setSource (nullptr);
audioDeviceManager.removeMidiInputDeviceCallback ({}, &(synthAudioSource.midiCollector));
audioDeviceManager.removeAudioCallback (&audioSourcePlayer);
audioDeviceManager.removeAudioCallback (&liveAudioDisplayComp);
}
//==============================================================================
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
}
void resized() override
{
keyboardComponent .setBounds (8, 96, getWidth() - 16, 64);
sineButton .setBounds (16, 176, 150, 24);
sampledButton .setBounds (16, 200, 150, 24);
liveAudioDisplayComp.setBounds (8, 8, getWidth() - 16, 64);
}
private:
// if this PIP is running inside the demo runner, we'll use the shared device manager instead
#ifndef JUCE_DEMO_RUNNER
AudioDeviceManager audioDeviceManager;
#else
AudioDeviceManager& audioDeviceManager { getSharedAudioDeviceManager (0, 2) };
#endif
MidiKeyboardState keyboardState;
AudioSourcePlayer audioSourcePlayer;
SynthAudioSource synthAudioSource { keyboardState };
MidiKeyboardComponent keyboardComponent { keyboardState, MidiKeyboardComponent::horizontalKeyboard};
ToggleButton sineButton { "Use sine wave" };
ToggleButton sampledButton { "Use sampled sound" };
LiveScrollingAudioDisplay liveAudioDisplayComp;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSynthesiserDemo)
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: AudioSynthesiserDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Simple synthesiser application.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: AudioSynthesiserDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/AudioLiveScrollingDisplay.h"
//==============================================================================
/** Our demo synth sound is just a basic sine wave.. */
struct SineWaveSound : public SynthesiserSound
{
SineWaveSound() {}
bool appliesToNote (int /*midiNoteNumber*/) override { return true; }
bool appliesToChannel (int /*midiChannel*/) override { return true; }
};
//==============================================================================
/** Our demo synth voice just plays a sine wave.. */
struct SineWaveVoice : public SynthesiserVoice
{
SineWaveVoice() {}
bool canPlaySound (SynthesiserSound* sound) override
{
return dynamic_cast<SineWaveSound*> (sound) != nullptr;
}
void startNote (int midiNoteNumber, float velocity,
SynthesiserSound*, int /*currentPitchWheelPosition*/) override
{
currentAngle = 0.0;
level = velocity * 0.15;
tailOff = 0.0;
auto cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
auto cyclesPerSample = cyclesPerSecond / getSampleRate();
angleDelta = cyclesPerSample * MathConstants<double>::twoPi;
}
void stopNote (float /*velocity*/, bool allowTailOff) override
{
if (allowTailOff)
{
// start a tail-off by setting this flag. The render callback will pick up on
// this and do a fade out, calling clearCurrentNote() when it's finished.
if (tailOff == 0.0) // we only need to begin a tail-off if it's not already doing so - the
tailOff = 1.0; // stopNote method could be called more than once.
}
else
{
// we're being told to stop playing immediately, so reset everything..
clearCurrentNote();
angleDelta = 0.0;
}
}
void pitchWheelMoved (int /*newValue*/) override {}
void controllerMoved (int /*controllerNumber*/, int /*newValue*/) override {}
void renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override
{
if (angleDelta != 0.0)
{
if (tailOff > 0.0)
{
while (--numSamples >= 0)
{
auto currentSample = (float) (std::sin (currentAngle) * level * tailOff);
for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample (i, startSample, currentSample);
currentAngle += angleDelta;
++startSample;
tailOff *= 0.99;
if (tailOff <= 0.005)
{
clearCurrentNote();
angleDelta = 0.0;
break;
}
}
}
else
{
while (--numSamples >= 0)
{
auto currentSample = (float) (std::sin (currentAngle) * level);
for (auto i = outputBuffer.getNumChannels(); --i >= 0;)
outputBuffer.addSample (i, startSample, currentSample);
currentAngle += angleDelta;
++startSample;
}
}
}
}
using SynthesiserVoice::renderNextBlock;
private:
double currentAngle = 0.0, angleDelta = 0.0, level = 0.0, tailOff = 0.0;
};
//==============================================================================
// This is an audio source that streams the output of our demo synth.
struct SynthAudioSource : public AudioSource
{
SynthAudioSource (MidiKeyboardState& keyState) : keyboardState (keyState)
{
// Add some voices to our synth, to play the sounds..
for (auto i = 0; i < 4; ++i)
{
synth.addVoice (new SineWaveVoice()); // These voices will play our custom sine-wave sounds..
synth.addVoice (new SamplerVoice()); // and these ones play the sampled sounds
}
// ..and add a sound for them to play...
setUsingSineWaveSound();
}
void setUsingSineWaveSound()
{
synth.clearSounds();
synth.addSound (new SineWaveSound());
}
void setUsingSampledSound()
{
WavAudioFormat wavFormat;
std::unique_ptr<AudioFormatReader> audioReader (wavFormat.createReaderFor (createAssetInputStream ("cello.wav").release(), true));
BigInteger allNotes;
allNotes.setRange (0, 128, true);
synth.clearSounds();
synth.addSound (new SamplerSound ("demo sound",
*audioReader,
allNotes,
74, // root midi note
0.1, // attack time
0.1, // release time
10.0 // maximum sample length
));
}
void prepareToPlay (int /*samplesPerBlockExpected*/, double sampleRate) override
{
midiCollector.reset (sampleRate);
synth.setCurrentPlaybackSampleRate (sampleRate);
}
void releaseResources() override {}
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
// the synth always adds its output to the audio buffer, so we have to clear it
// first..
bufferToFill.clearActiveBufferRegion();
// fill a midi buffer with incoming messages from the midi input.
MidiBuffer incomingMidi;
midiCollector.removeNextBlockOfMessages (incomingMidi, bufferToFill.numSamples);
// pass these messages to the keyboard state so that it can update the component
// to show on-screen which keys are being pressed on the physical midi keyboard.
// This call will also add midi messages to the buffer which were generated by
// the mouse-clicking on the on-screen keyboard.
keyboardState.processNextMidiBuffer (incomingMidi, 0, bufferToFill.numSamples, true);
// and now get the synth to process the midi events and generate its output.
synth.renderNextBlock (*bufferToFill.buffer, incomingMidi, 0, bufferToFill.numSamples);
}
//==============================================================================
// this collects real-time midi messages from the midi input device, and
// turns them into blocks that we can process in our audio callback
MidiMessageCollector midiCollector;
// this represents the state of which keys on our on-screen keyboard are held
// down. When the mouse is clicked on the keyboard component, this object also
// generates midi messages for this, which we can pass on to our synth.
MidiKeyboardState& keyboardState;
// the synth itself!
Synthesiser synth;
};
//==============================================================================
class AudioSynthesiserDemo : public Component
{
public:
AudioSynthesiserDemo()
{
addAndMakeVisible (keyboardComponent);
addAndMakeVisible (sineButton);
sineButton.setRadioGroupId (321);
sineButton.setToggleState (true, dontSendNotification);
sineButton.onClick = [this] { synthAudioSource.setUsingSineWaveSound(); };
addAndMakeVisible (sampledButton);
sampledButton.setRadioGroupId (321);
sampledButton.onClick = [this] { synthAudioSource.setUsingSampledSound(); };
addAndMakeVisible (liveAudioDisplayComp);
audioDeviceManager.addAudioCallback (&liveAudioDisplayComp);
audioSourcePlayer.setSource (&synthAudioSource);
#ifndef JUCE_DEMO_RUNNER
RuntimePermissions::request (RuntimePermissions::recordAudio,
[this] (bool granted)
{
int numInputChannels = granted ? 2 : 0;
audioDeviceManager.initialise (numInputChannels, 2, nullptr, true, {}, nullptr);
});
#endif
audioDeviceManager.addAudioCallback (&audioSourcePlayer);
audioDeviceManager.addMidiInputDeviceCallback ({}, &(synthAudioSource.midiCollector));
setOpaque (true);
setSize (640, 480);
}
~AudioSynthesiserDemo() override
{
audioSourcePlayer.setSource (nullptr);
audioDeviceManager.removeMidiInputDeviceCallback ({}, &(synthAudioSource.midiCollector));
audioDeviceManager.removeAudioCallback (&audioSourcePlayer);
audioDeviceManager.removeAudioCallback (&liveAudioDisplayComp);
}
//==============================================================================
void paint (Graphics& g) override
{
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
}
void resized() override
{
keyboardComponent .setBounds (8, 96, getWidth() - 16, 64);
sineButton .setBounds (16, 176, 150, 24);
sampledButton .setBounds (16, 200, 150, 24);
liveAudioDisplayComp.setBounds (8, 8, getWidth() - 16, 64);
}
private:
// if this PIP is running inside the demo runner, we'll use the shared device manager instead
#ifndef JUCE_DEMO_RUNNER
AudioDeviceManager audioDeviceManager;
#else
AudioDeviceManager& audioDeviceManager { getSharedAudioDeviceManager (0, 2) };
#endif
MidiKeyboardState keyboardState;
AudioSourcePlayer audioSourcePlayer;
SynthAudioSource synthAudioSource { keyboardState };
MidiKeyboardComponent keyboardComponent { keyboardState, MidiKeyboardComponent::horizontalKeyboard};
ToggleButton sineButton { "Use sine wave" };
ToggleButton sampledButton { "Use sampled sound" };
LiveScrollingAudioDisplay liveAudioDisplayComp;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSynthesiserDemo)
};

View File

@@ -1,15 +1,15 @@
# ==============================================================================
#
# This file is part of the JUCE library.
# Copyright (c) 2020 - Raw Material Software Limited
# Copyright (c) 2022 - Raw Material Software Limited
#
# JUCE is an open source library subject to commercial or open-source
# licensing.
#
# By using JUCE, you agree to the terms of both the JUCE 6 End-User License
# Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
# By using JUCE, you agree to the terms of both the JUCE 7 End-User License
# Agreement and JUCE Privacy Policy.
#
# End User License Agreement: www.juce.com/juce-6-licence
# End User License Agreement: www.juce.com/juce-7-licence
# Privacy Policy: www.juce.com/juce-privacy-policy
#
# Or: You may also use this code under the terms of the GPL v3 (see

File diff suppressed because it is too large Load Diff

View File

@@ -1,483 +1,484 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: MidiDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Handles incoming and outcoming midi messages.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: MidiDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
struct MidiDeviceListEntry : ReferenceCountedObject
{
MidiDeviceListEntry (MidiDeviceInfo info) : deviceInfo (info) {}
MidiDeviceInfo deviceInfo;
std::unique_ptr<MidiInput> inDevice;
std::unique_ptr<MidiOutput> outDevice;
using Ptr = ReferenceCountedObjectPtr<MidiDeviceListEntry>;
};
//==============================================================================
class MidiDemo : public Component,
private Timer,
private MidiKeyboardState::Listener,
private MidiInputCallback,
private AsyncUpdater
{
public:
//==============================================================================
MidiDemo()
: midiKeyboard (keyboardState, MidiKeyboardComponent::horizontalKeyboard),
midiInputSelector (new MidiDeviceListBox ("Midi Input Selector", *this, true)),
midiOutputSelector (new MidiDeviceListBox ("Midi Output Selector", *this, false))
{
addLabelAndSetStyle (midiInputLabel);
addLabelAndSetStyle (midiOutputLabel);
addLabelAndSetStyle (incomingMidiLabel);
addLabelAndSetStyle (outgoingMidiLabel);
midiKeyboard.setName ("MIDI Keyboard");
addAndMakeVisible (midiKeyboard);
midiMonitor.setMultiLine (true);
midiMonitor.setReturnKeyStartsNewLine (false);
midiMonitor.setReadOnly (true);
midiMonitor.setScrollbarsShown (true);
midiMonitor.setCaretVisible (false);
midiMonitor.setPopupMenuEnabled (false);
midiMonitor.setText ({});
addAndMakeVisible (midiMonitor);
if (! BluetoothMidiDevicePairingDialogue::isAvailable())
pairButton.setEnabled (false);
addAndMakeVisible (pairButton);
pairButton.onClick = []
{
RuntimePermissions::request (RuntimePermissions::bluetoothMidi,
[] (bool wasGranted)
{
if (wasGranted)
BluetoothMidiDevicePairingDialogue::open();
});
};
keyboardState.addListener (this);
addAndMakeVisible (midiInputSelector .get());
addAndMakeVisible (midiOutputSelector.get());
setSize (732, 520);
startTimer (500);
}
~MidiDemo() override
{
stopTimer();
midiInputs .clear();
midiOutputs.clear();
keyboardState.removeListener (this);
midiInputSelector .reset();
midiOutputSelector.reset();
}
//==============================================================================
void timerCallback() override
{
updateDeviceList (true);
updateDeviceList (false);
}
void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override
{
MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity));
m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
sendToOutputs (m);
}
void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override
{
MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber, velocity));
m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
sendToOutputs (m);
}
void paint (Graphics&) override {}
void resized() override
{
auto margin = 10;
midiInputLabel.setBounds (margin, margin,
(getWidth() / 2) - (2 * margin), 24);
midiOutputLabel.setBounds ((getWidth() / 2) + margin, margin,
(getWidth() / 2) - (2 * margin), 24);
midiInputSelector->setBounds (margin, (2 * margin) + 24,
(getWidth() / 2) - (2 * margin),
(getHeight() / 2) - ((4 * margin) + 24 + 24));
midiOutputSelector->setBounds ((getWidth() / 2) + margin, (2 * margin) + 24,
(getWidth() / 2) - (2 * margin),
(getHeight() / 2) - ((4 * margin) + 24 + 24));
pairButton.setBounds (margin, (getHeight() / 2) - (margin + 24),
getWidth() - (2 * margin), 24);
outgoingMidiLabel.setBounds (margin, getHeight() / 2, getWidth() - (2 * margin), 24);
midiKeyboard.setBounds (margin, (getHeight() / 2) + (24 + margin), getWidth() - (2 * margin), 64);
incomingMidiLabel.setBounds (margin, (getHeight() / 2) + (24 + (2 * margin) + 64),
getWidth() - (2 * margin), 24);
auto y = (getHeight() / 2) + ((2 * 24) + (3 * margin) + 64);
midiMonitor.setBounds (margin, y,
getWidth() - (2 * margin), getHeight() - y - margin);
}
void openDevice (bool isInput, int index)
{
if (isInput)
{
jassert (midiInputs[index]->inDevice.get() == nullptr);
midiInputs[index]->inDevice = MidiInput::openDevice (midiInputs[index]->deviceInfo.identifier, this);
if (midiInputs[index]->inDevice.get() == nullptr)
{
DBG ("MidiDemo::openDevice: open input device for index = " << index << " failed!");
return;
}
midiInputs[index]->inDevice->start();
}
else
{
jassert (midiOutputs[index]->outDevice.get() == nullptr);
midiOutputs[index]->outDevice = MidiOutput::openDevice (midiOutputs[index]->deviceInfo.identifier);
if (midiOutputs[index]->outDevice.get() == nullptr)
{
DBG ("MidiDemo::openDevice: open output device for index = " << index << " failed!");
}
}
}
void closeDevice (bool isInput, int index)
{
if (isInput)
{
jassert (midiInputs[index]->inDevice.get() != nullptr);
midiInputs[index]->inDevice->stop();
midiInputs[index]->inDevice.reset();
}
else
{
jassert (midiOutputs[index]->outDevice.get() != nullptr);
midiOutputs[index]->outDevice.reset();
}
}
int getNumMidiInputs() const noexcept
{
return midiInputs.size();
}
int getNumMidiOutputs() const noexcept
{
return midiOutputs.size();
}
ReferenceCountedObjectPtr<MidiDeviceListEntry> getMidiDevice (int index, bool isInput) const noexcept
{
return isInput ? midiInputs[index] : midiOutputs[index];
}
private:
//==============================================================================
struct MidiDeviceListBox : public ListBox,
private ListBoxModel
{
MidiDeviceListBox (const String& name,
MidiDemo& contentComponent,
bool isInputDeviceList)
: ListBox (name, this),
parent (contentComponent),
isInput (isInputDeviceList)
{
setOutlineThickness (1);
setMultipleSelectionEnabled (true);
setClickingTogglesRowSelection (true);
}
//==============================================================================
int getNumRows() override
{
return isInput ? parent.getNumMidiInputs()
: parent.getNumMidiOutputs();
}
void paintListBoxItem (int rowNumber, Graphics& g,
int width, int height, bool rowIsSelected) override
{
auto textColour = getLookAndFeel().findColour (ListBox::textColourId);
if (rowIsSelected)
g.fillAll (textColour.interpolatedWith (getLookAndFeel().findColour (ListBox::backgroundColourId), 0.5));
g.setColour (textColour);
g.setFont ((float) height * 0.7f);
if (isInput)
{
if (rowNumber < parent.getNumMidiInputs())
g.drawText (parent.getMidiDevice (rowNumber, true)->deviceInfo.name,
5, 0, width, height,
Justification::centredLeft, true);
}
else
{
if (rowNumber < parent.getNumMidiOutputs())
g.drawText (parent.getMidiDevice (rowNumber, false)->deviceInfo.name,
5, 0, width, height,
Justification::centredLeft, true);
}
}
//==============================================================================
void selectedRowsChanged (int) override
{
auto newSelectedItems = getSelectedRows();
if (newSelectedItems != lastSelectedItems)
{
for (auto i = 0; i < lastSelectedItems.size(); ++i)
{
if (! newSelectedItems.contains (lastSelectedItems[i]))
parent.closeDevice (isInput, lastSelectedItems[i]);
}
for (auto i = 0; i < newSelectedItems.size(); ++i)
{
if (! lastSelectedItems.contains (newSelectedItems[i]))
parent.openDevice (isInput, newSelectedItems[i]);
}
lastSelectedItems = newSelectedItems;
}
}
//==============================================================================
void syncSelectedItemsWithDeviceList (const ReferenceCountedArray<MidiDeviceListEntry>& midiDevices)
{
SparseSet<int> selectedRows;
for (auto i = 0; i < midiDevices.size(); ++i)
if (midiDevices[i]->inDevice.get() != nullptr || midiDevices[i]->outDevice.get() != nullptr)
selectedRows.addRange (Range<int> (i, i + 1));
lastSelectedItems = selectedRows;
updateContent();
setSelectedRows (selectedRows, dontSendNotification);
}
private:
//==============================================================================
MidiDemo& parent;
bool isInput;
SparseSet<int> lastSelectedItems;
};
//==============================================================================
void handleIncomingMidiMessage (MidiInput* /*source*/, const MidiMessage& message) override
{
// This is called on the MIDI thread
const ScopedLock sl (midiMonitorLock);
incomingMessages.add (message);
triggerAsyncUpdate();
}
void handleAsyncUpdate() override
{
// This is called on the message loop
Array<MidiMessage> messages;
{
const ScopedLock sl (midiMonitorLock);
messages.swapWith (incomingMessages);
}
String messageText;
for (auto& m : messages)
messageText << m.getDescription() << "\n";
midiMonitor.insertTextAtCaret (messageText);
}
void sendToOutputs (const MidiMessage& msg)
{
for (auto midiOutput : midiOutputs)
if (midiOutput->outDevice.get() != nullptr)
midiOutput->outDevice->sendMessageNow (msg);
}
//==============================================================================
bool hasDeviceListChanged (const Array<MidiDeviceInfo>& availableDevices, bool isInputDevice)
{
ReferenceCountedArray<MidiDeviceListEntry>& midiDevices = isInputDevice ? midiInputs
: midiOutputs;
if (availableDevices.size() != midiDevices.size())
return true;
for (auto i = 0; i < availableDevices.size(); ++i)
if (availableDevices[i] != midiDevices[i]->deviceInfo)
return true;
return false;
}
ReferenceCountedObjectPtr<MidiDeviceListEntry> findDevice (MidiDeviceInfo device, bool isInputDevice) const
{
const ReferenceCountedArray<MidiDeviceListEntry>& midiDevices = isInputDevice ? midiInputs
: midiOutputs;
for (auto& d : midiDevices)
if (d->deviceInfo == device)
return d;
return nullptr;
}
void closeUnpluggedDevices (const Array<MidiDeviceInfo>& currentlyPluggedInDevices, bool isInputDevice)
{
ReferenceCountedArray<MidiDeviceListEntry>& midiDevices = isInputDevice ? midiInputs
: midiOutputs;
for (auto i = midiDevices.size(); --i >= 0;)
{
auto& d = *midiDevices[i];
if (! currentlyPluggedInDevices.contains (d.deviceInfo))
{
if (isInputDevice ? d.inDevice .get() != nullptr
: d.outDevice.get() != nullptr)
closeDevice (isInputDevice, i);
midiDevices.remove (i);
}
}
}
void updateDeviceList (bool isInputDeviceList)
{
auto availableDevices = isInputDeviceList ? MidiInput::getAvailableDevices()
: MidiOutput::getAvailableDevices();
if (hasDeviceListChanged (availableDevices, isInputDeviceList))
{
ReferenceCountedArray<MidiDeviceListEntry>& midiDevices
= isInputDeviceList ? midiInputs : midiOutputs;
closeUnpluggedDevices (availableDevices, isInputDeviceList);
ReferenceCountedArray<MidiDeviceListEntry> newDeviceList;
// add all currently plugged-in devices to the device list
for (auto& newDevice : availableDevices)
{
MidiDeviceListEntry::Ptr entry = findDevice (newDevice, isInputDeviceList);
if (entry == nullptr)
entry = new MidiDeviceListEntry (newDevice);
newDeviceList.add (entry);
}
// actually update the device list
midiDevices = newDeviceList;
// update the selection status of the combo-box
if (auto* midiSelector = isInputDeviceList ? midiInputSelector.get() : midiOutputSelector.get())
midiSelector->syncSelectedItemsWithDeviceList (midiDevices);
}
}
//==============================================================================
void addLabelAndSetStyle (Label& label)
{
label.setFont (Font (15.00f, Font::plain));
label.setJustificationType (Justification::centredLeft);
label.setEditable (false, false, false);
label.setColour (TextEditor::textColourId, Colours::black);
label.setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (label);
}
//==============================================================================
Label midiInputLabel { "Midi Input Label", "MIDI Input:" };
Label midiOutputLabel { "Midi Output Label", "MIDI Output:" };
Label incomingMidiLabel { "Incoming Midi Label", "Received MIDI messages:" };
Label outgoingMidiLabel { "Outgoing Midi Label", "Play the keyboard to send MIDI messages..." };
MidiKeyboardState keyboardState;
MidiKeyboardComponent midiKeyboard;
TextEditor midiMonitor { "MIDI Monitor" };
TextButton pairButton { "MIDI Bluetooth devices..." };
std::unique_ptr<MidiDeviceListBox> midiInputSelector, midiOutputSelector;
ReferenceCountedArray<MidiDeviceListEntry> midiInputs, midiOutputs;
CriticalSection midiMonitorLock;
Array<MidiMessage> incomingMessages;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiDemo)
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: MidiDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Handles incoming and outcoming midi messages.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: MidiDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
struct MidiDeviceListEntry : ReferenceCountedObject
{
MidiDeviceListEntry (MidiDeviceInfo info) : deviceInfo (info) {}
MidiDeviceInfo deviceInfo;
std::unique_ptr<MidiInput> inDevice;
std::unique_ptr<MidiOutput> outDevice;
using Ptr = ReferenceCountedObjectPtr<MidiDeviceListEntry>;
};
//==============================================================================
class MidiDemo : public Component,
private Timer,
private MidiKeyboardState::Listener,
private MidiInputCallback,
private AsyncUpdater
{
public:
//==============================================================================
MidiDemo()
: midiKeyboard (keyboardState, MidiKeyboardComponent::horizontalKeyboard),
midiInputSelector (new MidiDeviceListBox ("Midi Input Selector", *this, true)),
midiOutputSelector (new MidiDeviceListBox ("Midi Output Selector", *this, false))
{
addLabelAndSetStyle (midiInputLabel);
addLabelAndSetStyle (midiOutputLabel);
addLabelAndSetStyle (incomingMidiLabel);
addLabelAndSetStyle (outgoingMidiLabel);
midiKeyboard.setName ("MIDI Keyboard");
addAndMakeVisible (midiKeyboard);
midiMonitor.setMultiLine (true);
midiMonitor.setReturnKeyStartsNewLine (false);
midiMonitor.setReadOnly (true);
midiMonitor.setScrollbarsShown (true);
midiMonitor.setCaretVisible (false);
midiMonitor.setPopupMenuEnabled (false);
midiMonitor.setText ({});
addAndMakeVisible (midiMonitor);
if (! BluetoothMidiDevicePairingDialogue::isAvailable())
pairButton.setEnabled (false);
addAndMakeVisible (pairButton);
pairButton.onClick = []
{
RuntimePermissions::request (RuntimePermissions::bluetoothMidi,
[] (bool wasGranted)
{
if (wasGranted)
BluetoothMidiDevicePairingDialogue::open();
});
};
keyboardState.addListener (this);
addAndMakeVisible (midiInputSelector .get());
addAndMakeVisible (midiOutputSelector.get());
setSize (732, 520);
startTimer (500);
}
~MidiDemo() override
{
stopTimer();
midiInputs .clear();
midiOutputs.clear();
keyboardState.removeListener (this);
midiInputSelector .reset();
midiOutputSelector.reset();
}
//==============================================================================
void timerCallback() override
{
updateDeviceList (true);
updateDeviceList (false);
}
void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override
{
MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity));
m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
sendToOutputs (m);
}
void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override
{
MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber, velocity));
m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001);
sendToOutputs (m);
}
void paint (Graphics&) override {}
void resized() override
{
auto margin = 10;
midiInputLabel.setBounds (margin, margin,
(getWidth() / 2) - (2 * margin), 24);
midiOutputLabel.setBounds ((getWidth() / 2) + margin, margin,
(getWidth() / 2) - (2 * margin), 24);
midiInputSelector->setBounds (margin, (2 * margin) + 24,
(getWidth() / 2) - (2 * margin),
(getHeight() / 2) - ((4 * margin) + 24 + 24));
midiOutputSelector->setBounds ((getWidth() / 2) + margin, (2 * margin) + 24,
(getWidth() / 2) - (2 * margin),
(getHeight() / 2) - ((4 * margin) + 24 + 24));
pairButton.setBounds (margin, (getHeight() / 2) - (margin + 24),
getWidth() - (2 * margin), 24);
outgoingMidiLabel.setBounds (margin, getHeight() / 2, getWidth() - (2 * margin), 24);
midiKeyboard.setBounds (margin, (getHeight() / 2) + (24 + margin), getWidth() - (2 * margin), 64);
incomingMidiLabel.setBounds (margin, (getHeight() / 2) + (24 + (2 * margin) + 64),
getWidth() - (2 * margin), 24);
auto y = (getHeight() / 2) + ((2 * 24) + (3 * margin) + 64);
midiMonitor.setBounds (margin, y,
getWidth() - (2 * margin), getHeight() - y - margin);
}
void openDevice (bool isInput, int index)
{
if (isInput)
{
jassert (midiInputs[index]->inDevice.get() == nullptr);
midiInputs[index]->inDevice = MidiInput::openDevice (midiInputs[index]->deviceInfo.identifier, this);
if (midiInputs[index]->inDevice.get() == nullptr)
{
DBG ("MidiDemo::openDevice: open input device for index = " << index << " failed!");
return;
}
midiInputs[index]->inDevice->start();
}
else
{
jassert (midiOutputs[index]->outDevice.get() == nullptr);
midiOutputs[index]->outDevice = MidiOutput::openDevice (midiOutputs[index]->deviceInfo.identifier);
if (midiOutputs[index]->outDevice.get() == nullptr)
{
DBG ("MidiDemo::openDevice: open output device for index = " << index << " failed!");
}
}
}
void closeDevice (bool isInput, int index)
{
if (isInput)
{
jassert (midiInputs[index]->inDevice.get() != nullptr);
midiInputs[index]->inDevice->stop();
midiInputs[index]->inDevice.reset();
}
else
{
jassert (midiOutputs[index]->outDevice.get() != nullptr);
midiOutputs[index]->outDevice.reset();
}
}
int getNumMidiInputs() const noexcept
{
return midiInputs.size();
}
int getNumMidiOutputs() const noexcept
{
return midiOutputs.size();
}
ReferenceCountedObjectPtr<MidiDeviceListEntry> getMidiDevice (int index, bool isInput) const noexcept
{
return isInput ? midiInputs[index] : midiOutputs[index];
}
private:
//==============================================================================
struct MidiDeviceListBox : private ListBoxModel,
public ListBox
{
MidiDeviceListBox (const String& name,
MidiDemo& contentComponent,
bool isInputDeviceList)
: ListBox (name),
parent (contentComponent),
isInput (isInputDeviceList)
{
setModel (this);
setOutlineThickness (1);
setMultipleSelectionEnabled (true);
setClickingTogglesRowSelection (true);
}
//==============================================================================
int getNumRows() override
{
return isInput ? parent.getNumMidiInputs()
: parent.getNumMidiOutputs();
}
void paintListBoxItem (int rowNumber, Graphics& g,
int width, int height, bool rowIsSelected) override
{
auto textColour = getLookAndFeel().findColour (ListBox::textColourId);
if (rowIsSelected)
g.fillAll (textColour.interpolatedWith (getLookAndFeel().findColour (ListBox::backgroundColourId), 0.5));
g.setColour (textColour);
g.setFont ((float) height * 0.7f);
if (isInput)
{
if (rowNumber < parent.getNumMidiInputs())
g.drawText (parent.getMidiDevice (rowNumber, true)->deviceInfo.name,
5, 0, width, height,
Justification::centredLeft, true);
}
else
{
if (rowNumber < parent.getNumMidiOutputs())
g.drawText (parent.getMidiDevice (rowNumber, false)->deviceInfo.name,
5, 0, width, height,
Justification::centredLeft, true);
}
}
//==============================================================================
void selectedRowsChanged (int) override
{
auto newSelectedItems = getSelectedRows();
if (newSelectedItems != lastSelectedItems)
{
for (auto i = 0; i < lastSelectedItems.size(); ++i)
{
if (! newSelectedItems.contains (lastSelectedItems[i]))
parent.closeDevice (isInput, lastSelectedItems[i]);
}
for (auto i = 0; i < newSelectedItems.size(); ++i)
{
if (! lastSelectedItems.contains (newSelectedItems[i]))
parent.openDevice (isInput, newSelectedItems[i]);
}
lastSelectedItems = newSelectedItems;
}
}
//==============================================================================
void syncSelectedItemsWithDeviceList (const ReferenceCountedArray<MidiDeviceListEntry>& midiDevices)
{
SparseSet<int> selectedRows;
for (auto i = 0; i < midiDevices.size(); ++i)
if (midiDevices[i]->inDevice.get() != nullptr || midiDevices[i]->outDevice.get() != nullptr)
selectedRows.addRange (Range<int> (i, i + 1));
lastSelectedItems = selectedRows;
updateContent();
setSelectedRows (selectedRows, dontSendNotification);
}
private:
//==============================================================================
MidiDemo& parent;
bool isInput;
SparseSet<int> lastSelectedItems;
};
//==============================================================================
void handleIncomingMidiMessage (MidiInput* /*source*/, const MidiMessage& message) override
{
// This is called on the MIDI thread
const ScopedLock sl (midiMonitorLock);
incomingMessages.add (message);
triggerAsyncUpdate();
}
void handleAsyncUpdate() override
{
// This is called on the message loop
Array<MidiMessage> messages;
{
const ScopedLock sl (midiMonitorLock);
messages.swapWith (incomingMessages);
}
String messageText;
for (auto& m : messages)
messageText << m.getDescription() << "\n";
midiMonitor.insertTextAtCaret (messageText);
}
void sendToOutputs (const MidiMessage& msg)
{
for (auto midiOutput : midiOutputs)
if (midiOutput->outDevice.get() != nullptr)
midiOutput->outDevice->sendMessageNow (msg);
}
//==============================================================================
bool hasDeviceListChanged (const Array<MidiDeviceInfo>& availableDevices, bool isInputDevice)
{
ReferenceCountedArray<MidiDeviceListEntry>& midiDevices = isInputDevice ? midiInputs
: midiOutputs;
if (availableDevices.size() != midiDevices.size())
return true;
for (auto i = 0; i < availableDevices.size(); ++i)
if (availableDevices[i] != midiDevices[i]->deviceInfo)
return true;
return false;
}
ReferenceCountedObjectPtr<MidiDeviceListEntry> findDevice (MidiDeviceInfo device, bool isInputDevice) const
{
const ReferenceCountedArray<MidiDeviceListEntry>& midiDevices = isInputDevice ? midiInputs
: midiOutputs;
for (auto& d : midiDevices)
if (d->deviceInfo == device)
return d;
return nullptr;
}
void closeUnpluggedDevices (const Array<MidiDeviceInfo>& currentlyPluggedInDevices, bool isInputDevice)
{
ReferenceCountedArray<MidiDeviceListEntry>& midiDevices = isInputDevice ? midiInputs
: midiOutputs;
for (auto i = midiDevices.size(); --i >= 0;)
{
auto& d = *midiDevices[i];
if (! currentlyPluggedInDevices.contains (d.deviceInfo))
{
if (isInputDevice ? d.inDevice .get() != nullptr
: d.outDevice.get() != nullptr)
closeDevice (isInputDevice, i);
midiDevices.remove (i);
}
}
}
void updateDeviceList (bool isInputDeviceList)
{
auto availableDevices = isInputDeviceList ? MidiInput::getAvailableDevices()
: MidiOutput::getAvailableDevices();
if (hasDeviceListChanged (availableDevices, isInputDeviceList))
{
ReferenceCountedArray<MidiDeviceListEntry>& midiDevices
= isInputDeviceList ? midiInputs : midiOutputs;
closeUnpluggedDevices (availableDevices, isInputDeviceList);
ReferenceCountedArray<MidiDeviceListEntry> newDeviceList;
// add all currently plugged-in devices to the device list
for (auto& newDevice : availableDevices)
{
MidiDeviceListEntry::Ptr entry = findDevice (newDevice, isInputDeviceList);
if (entry == nullptr)
entry = new MidiDeviceListEntry (newDevice);
newDeviceList.add (entry);
}
// actually update the device list
midiDevices = newDeviceList;
// update the selection status of the combo-box
if (auto* midiSelector = isInputDeviceList ? midiInputSelector.get() : midiOutputSelector.get())
midiSelector->syncSelectedItemsWithDeviceList (midiDevices);
}
}
//==============================================================================
void addLabelAndSetStyle (Label& label)
{
label.setFont (Font (15.00f, Font::plain));
label.setJustificationType (Justification::centredLeft);
label.setEditable (false, false, false);
label.setColour (TextEditor::textColourId, Colours::black);
label.setColour (TextEditor::backgroundColourId, Colour (0x00000000));
addAndMakeVisible (label);
}
//==============================================================================
Label midiInputLabel { "Midi Input Label", "MIDI Input:" };
Label midiOutputLabel { "Midi Output Label", "MIDI Output:" };
Label incomingMidiLabel { "Incoming Midi Label", "Received MIDI messages:" };
Label outgoingMidiLabel { "Outgoing Midi Label", "Play the keyboard to send MIDI messages..." };
MidiKeyboardState keyboardState;
MidiKeyboardComponent midiKeyboard;
TextEditor midiMonitor { "MIDI Monitor" };
TextButton pairButton { "MIDI Bluetooth devices..." };
std::unique_ptr<MidiDeviceListBox> midiInputSelector, midiOutputSelector;
ReferenceCountedArray<MidiDeviceListEntry> midiInputs, midiOutputs;
CriticalSection midiMonitorLock;
Array<MidiMessage> incomingMessages;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiDemo)
};

View File

@@ -1,389 +1,389 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: PluckedStringsDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Simulation of a plucked string sound.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: PluckedStringsDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
/**
A very basic generator of a simulated plucked string sound, implementing
the Karplus-Strong algorithm.
Not performance-optimised!
*/
class StringSynthesiser
{
public:
//==============================================================================
/** Constructor.
@param sampleRate The audio sample rate to use.
@param frequencyInHz The fundamental frequency of the simulated string in
Hertz.
*/
StringSynthesiser (double sampleRate, double frequencyInHz)
{
doPluckForNextBuffer.set (false);
prepareSynthesiserState (sampleRate, frequencyInHz);
}
//==============================================================================
/** Excite the simulated string by plucking it at a given position.
@param pluckPosition The position of the plucking, relative to the length
of the string. Must be between 0 and 1.
*/
void stringPlucked (float pluckPosition)
{
jassert (pluckPosition >= 0.0 && pluckPosition <= 1.0);
// we choose a very simple approach to communicate with the audio thread:
// simply tell the synth to perform the plucking excitation at the beginning
// of the next buffer (= when generateAndAddData is called the next time).
if (doPluckForNextBuffer.compareAndSetBool (1, 0))
{
// plucking in the middle gives the largest amplitude;
// plucking at the very ends will do nothing.
amplitude = std::sin (MathConstants<float>::pi * pluckPosition);
}
}
//==============================================================================
/** Generate next chunk of mono audio output and add it into a buffer.
@param outBuffer Buffer to fill (one channel only). New sound will be
added to existing content of the buffer (instead of
replacing it).
@param numSamples Number of samples to generate (make sure that outBuffer
has enough space).
*/
void generateAndAddData (float* outBuffer, int numSamples)
{
if (doPluckForNextBuffer.compareAndSetBool (0, 1))
exciteInternalBuffer();
// cycle through the delay line and apply a simple averaging filter
for (auto i = 0; i < numSamples; ++i)
{
auto nextPos = (pos + 1) % delayLine.size();
delayLine[nextPos] = (float) (decay * 0.5 * (delayLine[nextPos] + delayLine[pos]));
outBuffer[i] += delayLine[pos];
pos = nextPos;
}
}
private:
//==============================================================================
void prepareSynthesiserState (double sampleRate, double frequencyInHz)
{
auto delayLineLength = (size_t) roundToInt (sampleRate / frequencyInHz);
// we need a minimum delay line length to get a reasonable synthesis.
// if you hit this assert, increase sample rate or decrease frequency!
jassert (delayLineLength > 50);
delayLine.resize (delayLineLength);
std::fill (delayLine.begin(), delayLine.end(), 0.0f);
excitationSample.resize (delayLineLength);
// as the excitation sample we use random noise between -1 and 1
// (as a simple approximation to a plucking excitation)
std::generate (excitationSample.begin(),
excitationSample.end(),
[] { return (Random::getSystemRandom().nextFloat() * 2.0f) - 1.0f; } );
}
void exciteInternalBuffer()
{
// fill the buffer with the precomputed excitation sound (scaled with amplitude)
jassert (delayLine.size() >= excitationSample.size());
std::transform (excitationSample.begin(),
excitationSample.end(),
delayLine.begin(),
[this] (double sample) { return static_cast<float> (amplitude * sample); } );
}
//==============================================================================
const double decay = 0.998;
double amplitude = 0.0;
Atomic<int> doPluckForNextBuffer;
std::vector<float> excitationSample, delayLine;
size_t pos = 0;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StringSynthesiser)
};
//==============================================================================
/*
This component represents a horizontal vibrating musical string of fixed height
and variable length. The string can be excited by calling stringPlucked().
*/
class StringComponent : public Component,
private Timer
{
public:
StringComponent (int lengthInPixels, Colour stringColour)
: length (lengthInPixels), colour (stringColour)
{
// ignore mouse-clicks so that our parent can get them instead.
setInterceptsMouseClicks (false, false);
setSize (length, height);
startTimerHz (60);
}
//==============================================================================
void stringPlucked (float pluckPositionRelative)
{
amplitude = maxAmplitude * std::sin (pluckPositionRelative * MathConstants<float>::pi);
phase = MathConstants<float>::pi;
}
//==============================================================================
void paint (Graphics& g) override
{
g.setColour (colour);
g.strokePath (generateStringPath(), PathStrokeType (2.0f));
}
Path generateStringPath() const
{
auto y = (float) height / 2.0f;
Path stringPath;
stringPath.startNewSubPath (0, y);
stringPath.quadraticTo ((float) length / 2.0f, y + (std::sin (phase) * amplitude), (float) length, y);
return stringPath;
}
//==============================================================================
void timerCallback() override
{
updateAmplitude();
updatePhase();
repaint();
}
void updateAmplitude()
{
// this determines the decay of the visible string vibration.
amplitude *= 0.99f;
}
void updatePhase()
{
// this determines the visible vibration frequency.
// just an arbitrary number chosen to look OK:
auto phaseStep = 400.0f / (float) length;
phase += phaseStep;
if (phase >= MathConstants<float>::twoPi)
phase -= MathConstants<float>::twoPi;
}
private:
//==============================================================================
int length;
Colour colour;
int height = 20;
float amplitude = 0.0f;
const float maxAmplitude = 12.0f;
float phase = 0.0f;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StringComponent)
};
//==============================================================================
class PluckedStringsDemo : public AudioAppComponent
{
public:
PluckedStringsDemo()
#ifdef JUCE_DEMO_RUNNER
: AudioAppComponent (getSharedAudioDeviceManager (0, 2))
#endif
{
createStringComponents();
setSize (800, 560);
// specify the number of input and output channels that we want to open
auto audioDevice = deviceManager.getCurrentAudioDevice();
auto numInputChannels = (audioDevice != nullptr ? audioDevice->getActiveInputChannels() .countNumberOfSetBits() : 0);
auto numOutputChannels = jmax (audioDevice != nullptr ? audioDevice->getActiveOutputChannels().countNumberOfSetBits() : 2, 2);
setAudioChannels (numInputChannels, numOutputChannels);
}
~PluckedStringsDemo() override
{
shutdownAudio();
}
//==============================================================================
void prepareToPlay (int /*samplesPerBlockExpected*/, double sampleRate) override
{
generateStringSynths (sampleRate);
}
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
bufferToFill.clearActiveBufferRegion();
for (auto channel = 0; channel < bufferToFill.buffer->getNumChannels(); ++channel)
{
auto* channelData = bufferToFill.buffer->getWritePointer (channel, bufferToFill.startSample);
if (channel == 0)
{
for (auto synth : stringSynths)
synth->generateAndAddData (channelData, bufferToFill.numSamples);
}
else
{
memcpy (channelData,
bufferToFill.buffer->getReadPointer (0),
((size_t) bufferToFill.numSamples) * sizeof (float));
}
}
}
void releaseResources() override
{
stringSynths.clear();
}
//==============================================================================
void paint (Graphics&) override {}
void resized() override
{
auto xPos = 20;
auto yPos = 20;
auto yDistance = 50;
for (auto stringLine : stringLines)
{
stringLine->setTopLeftPosition (xPos, yPos);
yPos += yDistance;
addAndMakeVisible (stringLine);
}
}
private:
void mouseDown (const MouseEvent& e) override
{
mouseDrag (e);
}
void mouseDrag (const MouseEvent& e) override
{
for (auto i = 0; i < stringLines.size(); ++i)
{
auto* stringLine = stringLines.getUnchecked (i);
if (stringLine->getBounds().contains (e.getPosition()))
{
auto position = (e.position.x - (float) stringLine->getX()) / (float) stringLine->getWidth();
stringLine->stringPlucked (position);
stringSynths.getUnchecked (i)->stringPlucked (position);
}
}
}
//==============================================================================
struct StringParameters
{
StringParameters (int midiNote)
: frequencyInHz (MidiMessage::getMidiNoteInHertz (midiNote)),
lengthInPixels ((int) (760 / (frequencyInHz / MidiMessage::getMidiNoteInHertz (42))))
{}
double frequencyInHz;
int lengthInPixels;
};
static Array<StringParameters> getDefaultStringParameters()
{
return Array<StringParameters> (42, 44, 46, 49, 51, 54, 56, 58, 61, 63, 66, 68, 70);
}
void createStringComponents()
{
for (auto stringParams : getDefaultStringParameters())
{
stringLines.add (new StringComponent (stringParams.lengthInPixels,
Colour::fromHSV (Random().nextFloat(), 0.6f, 0.9f, 1.0f)));
}
}
void generateStringSynths (double sampleRate)
{
stringSynths.clear();
for (auto stringParams : getDefaultStringParameters())
{
stringSynths.add (new StringSynthesiser (sampleRate, stringParams.frequencyInHz));
}
}
//==============================================================================
OwnedArray<StringComponent> stringLines;
OwnedArray<StringSynthesiser> stringSynths;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluckedStringsDemo)
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: PluckedStringsDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Simulation of a plucked string sound.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: PluckedStringsDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
/**
A very basic generator of a simulated plucked string sound, implementing
the Karplus-Strong algorithm.
Not performance-optimised!
*/
class StringSynthesiser
{
public:
//==============================================================================
/** Constructor.
@param sampleRate The audio sample rate to use.
@param frequencyInHz The fundamental frequency of the simulated string in
Hertz.
*/
StringSynthesiser (double sampleRate, double frequencyInHz)
{
doPluckForNextBuffer.set (false);
prepareSynthesiserState (sampleRate, frequencyInHz);
}
//==============================================================================
/** Excite the simulated string by plucking it at a given position.
@param pluckPosition The position of the plucking, relative to the length
of the string. Must be between 0 and 1.
*/
void stringPlucked (float pluckPosition)
{
jassert (pluckPosition >= 0.0 && pluckPosition <= 1.0);
// we choose a very simple approach to communicate with the audio thread:
// simply tell the synth to perform the plucking excitation at the beginning
// of the next buffer (= when generateAndAddData is called the next time).
if (doPluckForNextBuffer.compareAndSetBool (1, 0))
{
// plucking in the middle gives the largest amplitude;
// plucking at the very ends will do nothing.
amplitude = std::sin (MathConstants<float>::pi * pluckPosition);
}
}
//==============================================================================
/** Generate next chunk of mono audio output and add it into a buffer.
@param outBuffer Buffer to fill (one channel only). New sound will be
added to existing content of the buffer (instead of
replacing it).
@param numSamples Number of samples to generate (make sure that outBuffer
has enough space).
*/
void generateAndAddData (float* outBuffer, int numSamples)
{
if (doPluckForNextBuffer.compareAndSetBool (0, 1))
exciteInternalBuffer();
// cycle through the delay line and apply a simple averaging filter
for (auto i = 0; i < numSamples; ++i)
{
auto nextPos = (pos + 1) % delayLine.size();
delayLine[nextPos] = (float) (decay * 0.5 * (delayLine[nextPos] + delayLine[pos]));
outBuffer[i] += delayLine[pos];
pos = nextPos;
}
}
private:
//==============================================================================
void prepareSynthesiserState (double sampleRate, double frequencyInHz)
{
auto delayLineLength = (size_t) roundToInt (sampleRate / frequencyInHz);
// we need a minimum delay line length to get a reasonable synthesis.
// if you hit this assert, increase sample rate or decrease frequency!
jassert (delayLineLength > 50);
delayLine.resize (delayLineLength);
std::fill (delayLine.begin(), delayLine.end(), 0.0f);
excitationSample.resize (delayLineLength);
// as the excitation sample we use random noise between -1 and 1
// (as a simple approximation to a plucking excitation)
std::generate (excitationSample.begin(),
excitationSample.end(),
[] { return (Random::getSystemRandom().nextFloat() * 2.0f) - 1.0f; } );
}
void exciteInternalBuffer()
{
// fill the buffer with the precomputed excitation sound (scaled with amplitude)
jassert (delayLine.size() >= excitationSample.size());
std::transform (excitationSample.begin(),
excitationSample.end(),
delayLine.begin(),
[this] (double sample) { return static_cast<float> (amplitude * sample); } );
}
//==============================================================================
const double decay = 0.998;
double amplitude = 0.0;
Atomic<int> doPluckForNextBuffer;
std::vector<float> excitationSample, delayLine;
size_t pos = 0;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StringSynthesiser)
};
//==============================================================================
/*
This component represents a horizontal vibrating musical string of fixed height
and variable length. The string can be excited by calling stringPlucked().
*/
class StringComponent : public Component,
private Timer
{
public:
StringComponent (int lengthInPixels, Colour stringColour)
: length (lengthInPixels), colour (stringColour)
{
// ignore mouse-clicks so that our parent can get them instead.
setInterceptsMouseClicks (false, false);
setSize (length, height);
startTimerHz (60);
}
//==============================================================================
void stringPlucked (float pluckPositionRelative)
{
amplitude = maxAmplitude * std::sin (pluckPositionRelative * MathConstants<float>::pi);
phase = MathConstants<float>::pi;
}
//==============================================================================
void paint (Graphics& g) override
{
g.setColour (colour);
g.strokePath (generateStringPath(), PathStrokeType (2.0f));
}
Path generateStringPath() const
{
auto y = (float) height / 2.0f;
Path stringPath;
stringPath.startNewSubPath (0, y);
stringPath.quadraticTo ((float) length / 2.0f, y + (std::sin (phase) * amplitude), (float) length, y);
return stringPath;
}
//==============================================================================
void timerCallback() override
{
updateAmplitude();
updatePhase();
repaint();
}
void updateAmplitude()
{
// this determines the decay of the visible string vibration.
amplitude *= 0.99f;
}
void updatePhase()
{
// this determines the visible vibration frequency.
// just an arbitrary number chosen to look OK:
auto phaseStep = 400.0f / (float) length;
phase += phaseStep;
if (phase >= MathConstants<float>::twoPi)
phase -= MathConstants<float>::twoPi;
}
private:
//==============================================================================
int length;
Colour colour;
int height = 20;
float amplitude = 0.0f;
const float maxAmplitude = 12.0f;
float phase = 0.0f;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StringComponent)
};
//==============================================================================
class PluckedStringsDemo : public AudioAppComponent
{
public:
PluckedStringsDemo()
#ifdef JUCE_DEMO_RUNNER
: AudioAppComponent (getSharedAudioDeviceManager (0, 2))
#endif
{
createStringComponents();
setSize (800, 560);
// specify the number of input and output channels that we want to open
auto audioDevice = deviceManager.getCurrentAudioDevice();
auto numInputChannels = (audioDevice != nullptr ? audioDevice->getActiveInputChannels() .countNumberOfSetBits() : 0);
auto numOutputChannels = jmax (audioDevice != nullptr ? audioDevice->getActiveOutputChannels().countNumberOfSetBits() : 2, 2);
setAudioChannels (numInputChannels, numOutputChannels);
}
~PluckedStringsDemo() override
{
shutdownAudio();
}
//==============================================================================
void prepareToPlay (int /*samplesPerBlockExpected*/, double sampleRate) override
{
generateStringSynths (sampleRate);
}
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
bufferToFill.clearActiveBufferRegion();
for (auto channel = 0; channel < bufferToFill.buffer->getNumChannels(); ++channel)
{
auto* channelData = bufferToFill.buffer->getWritePointer (channel, bufferToFill.startSample);
if (channel == 0)
{
for (auto synth : stringSynths)
synth->generateAndAddData (channelData, bufferToFill.numSamples);
}
else
{
memcpy (channelData,
bufferToFill.buffer->getReadPointer (0),
((size_t) bufferToFill.numSamples) * sizeof (float));
}
}
}
void releaseResources() override
{
stringSynths.clear();
}
//==============================================================================
void paint (Graphics&) override {}
void resized() override
{
auto xPos = 20;
auto yPos = 20;
auto yDistance = 50;
for (auto stringLine : stringLines)
{
stringLine->setTopLeftPosition (xPos, yPos);
yPos += yDistance;
addAndMakeVisible (stringLine);
}
}
private:
void mouseDown (const MouseEvent& e) override
{
mouseDrag (e);
}
void mouseDrag (const MouseEvent& e) override
{
for (auto i = 0; i < stringLines.size(); ++i)
{
auto* stringLine = stringLines.getUnchecked (i);
if (stringLine->getBounds().contains (e.getPosition()))
{
auto position = (e.position.x - (float) stringLine->getX()) / (float) stringLine->getWidth();
stringLine->stringPlucked (position);
stringSynths.getUnchecked (i)->stringPlucked (position);
}
}
}
//==============================================================================
struct StringParameters
{
StringParameters (int midiNote)
: frequencyInHz (MidiMessage::getMidiNoteInHertz (midiNote)),
lengthInPixels ((int) (760 / (frequencyInHz / MidiMessage::getMidiNoteInHertz (42))))
{}
double frequencyInHz;
int lengthInPixels;
};
static Array<StringParameters> getDefaultStringParameters()
{
return Array<StringParameters> (42, 44, 46, 49, 51, 54, 56, 58, 61, 63, 66, 68, 70);
}
void createStringComponents()
{
for (auto stringParams : getDefaultStringParameters())
{
stringLines.add (new StringComponent (stringParams.lengthInPixels,
Colour::fromHSV (Random().nextFloat(), 0.6f, 0.9f, 1.0f)));
}
}
void generateStringSynths (double sampleRate)
{
stringSynths.clear();
for (auto stringParams : getDefaultStringParameters())
{
stringSynths.add (new StringSynthesiser (sampleRate, stringParams.frequencyInHz));
}
}
//==============================================================================
OwnedArray<StringComponent> stringLines;
OwnedArray<StringSynthesiser> stringSynths;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluckedStringsDemo)
};

View File

@@ -1,189 +1,189 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: SimpleFFTDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Simple FFT application.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: SimpleFFTDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
class SimpleFFTDemo : public AudioAppComponent,
private Timer
{
public:
SimpleFFTDemo() :
#ifdef JUCE_DEMO_RUNNER
AudioAppComponent (getSharedAudioDeviceManager (1, 0)),
#endif
forwardFFT (fftOrder),
spectrogramImage (Image::RGB, 512, 512, true)
{
setOpaque (true);
#ifndef JUCE_DEMO_RUNNER
RuntimePermissions::request (RuntimePermissions::recordAudio,
[this] (bool granted)
{
int numInputChannels = granted ? 2 : 0;
setAudioChannels (numInputChannels, 2);
});
#else
setAudioChannels (2, 2);
#endif
startTimerHz (60);
setSize (700, 500);
}
~SimpleFFTDemo() override
{
shutdownAudio();
}
//==============================================================================
void prepareToPlay (int /*samplesPerBlockExpected*/, double /*newSampleRate*/) override
{
// (nothing to do here)
}
void releaseResources() override
{
// (nothing to do here)
}
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
if (bufferToFill.buffer->getNumChannels() > 0)
{
const auto* channelData = bufferToFill.buffer->getReadPointer (0, bufferToFill.startSample);
for (auto i = 0; i < bufferToFill.numSamples; ++i)
pushNextSampleIntoFifo (channelData[i]);
bufferToFill.clearActiveBufferRegion();
}
}
//==============================================================================
void paint (Graphics& g) override
{
g.fillAll (Colours::black);
g.setOpacity (1.0f);
g.drawImage (spectrogramImage, getLocalBounds().toFloat());
}
void timerCallback() override
{
if (nextFFTBlockReady)
{
drawNextLineOfSpectrogram();
nextFFTBlockReady = false;
repaint();
}
}
void pushNextSampleIntoFifo (float sample) noexcept
{
// if the fifo contains enough data, set a flag to say
// that the next line should now be rendered..
if (fifoIndex == fftSize)
{
if (! nextFFTBlockReady)
{
zeromem (fftData, sizeof (fftData));
memcpy (fftData, fifo, sizeof (fifo));
nextFFTBlockReady = true;
}
fifoIndex = 0;
}
fifo[fifoIndex++] = sample;
}
void drawNextLineOfSpectrogram()
{
auto rightHandEdge = spectrogramImage.getWidth() - 1;
auto imageHeight = spectrogramImage.getHeight();
// first, shuffle our image leftwards by 1 pixel..
spectrogramImage.moveImageSection (0, 0, 1, 0, rightHandEdge, imageHeight);
// then render our FFT data..
forwardFFT.performFrequencyOnlyForwardTransform (fftData);
// find the range of values produced, so we can scale our rendering to
// show up the detail clearly
auto maxLevel = FloatVectorOperations::findMinAndMax (fftData, fftSize / 2);
for (auto y = 1; y < imageHeight; ++y)
{
auto skewedProportionY = 1.0f - std::exp (std::log ((float) y / (float) imageHeight) * 0.2f);
auto fftDataIndex = jlimit (0, fftSize / 2, (int) (skewedProportionY * (int) fftSize / 2));
auto level = jmap (fftData[fftDataIndex], 0.0f, jmax (maxLevel.getEnd(), 1e-5f), 0.0f, 1.0f);
spectrogramImage.setPixelAt (rightHandEdge, y, Colour::fromHSV (level, 1.0f, level, 1.0f));
}
}
enum
{
fftOrder = 10,
fftSize = 1 << fftOrder
};
private:
dsp::FFT forwardFFT;
Image spectrogramImage;
float fifo [fftSize];
float fftData [2 * fftSize];
int fifoIndex = 0;
bool nextFFTBlockReady = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimpleFFTDemo)
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: SimpleFFTDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Simple FFT application.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: SimpleFFTDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
//==============================================================================
class SimpleFFTDemo : public AudioAppComponent,
private Timer
{
public:
SimpleFFTDemo() :
#ifdef JUCE_DEMO_RUNNER
AudioAppComponent (getSharedAudioDeviceManager (1, 0)),
#endif
forwardFFT (fftOrder),
spectrogramImage (Image::RGB, 512, 512, true)
{
setOpaque (true);
#ifndef JUCE_DEMO_RUNNER
RuntimePermissions::request (RuntimePermissions::recordAudio,
[this] (bool granted)
{
int numInputChannels = granted ? 2 : 0;
setAudioChannels (numInputChannels, 2);
});
#else
setAudioChannels (2, 2);
#endif
startTimerHz (60);
setSize (700, 500);
}
~SimpleFFTDemo() override
{
shutdownAudio();
}
//==============================================================================
void prepareToPlay (int /*samplesPerBlockExpected*/, double /*newSampleRate*/) override
{
// (nothing to do here)
}
void releaseResources() override
{
// (nothing to do here)
}
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
if (bufferToFill.buffer->getNumChannels() > 0)
{
const auto* channelData = bufferToFill.buffer->getReadPointer (0, bufferToFill.startSample);
for (auto i = 0; i < bufferToFill.numSamples; ++i)
pushNextSampleIntoFifo (channelData[i]);
bufferToFill.clearActiveBufferRegion();
}
}
//==============================================================================
void paint (Graphics& g) override
{
g.fillAll (Colours::black);
g.setOpacity (1.0f);
g.drawImage (spectrogramImage, getLocalBounds().toFloat());
}
void timerCallback() override
{
if (nextFFTBlockReady)
{
drawNextLineOfSpectrogram();
nextFFTBlockReady = false;
repaint();
}
}
void pushNextSampleIntoFifo (float sample) noexcept
{
// if the fifo contains enough data, set a flag to say
// that the next line should now be rendered..
if (fifoIndex == fftSize)
{
if (! nextFFTBlockReady)
{
zeromem (fftData, sizeof (fftData));
memcpy (fftData, fifo, sizeof (fifo));
nextFFTBlockReady = true;
}
fifoIndex = 0;
}
fifo[fifoIndex++] = sample;
}
void drawNextLineOfSpectrogram()
{
auto rightHandEdge = spectrogramImage.getWidth() - 1;
auto imageHeight = spectrogramImage.getHeight();
// first, shuffle our image leftwards by 1 pixel..
spectrogramImage.moveImageSection (0, 0, 1, 0, rightHandEdge, imageHeight);
// then render our FFT data..
forwardFFT.performFrequencyOnlyForwardTransform (fftData);
// find the range of values produced, so we can scale our rendering to
// show up the detail clearly
auto maxLevel = FloatVectorOperations::findMinAndMax (fftData, fftSize / 2);
for (auto y = 1; y < imageHeight; ++y)
{
auto skewedProportionY = 1.0f - std::exp (std::log ((float) y / (float) imageHeight) * 0.2f);
auto fftDataIndex = jlimit (0, fftSize / 2, (int) (skewedProportionY * (int) fftSize / 2));
auto level = jmap (fftData[fftDataIndex], 0.0f, jmax (maxLevel.getEnd(), 1e-5f), 0.0f, 1.0f);
spectrogramImage.setPixelAt (rightHandEdge, y, Colour::fromHSV (level, 1.0f, level, 1.0f));
}
}
enum
{
fftOrder = 10,
fftSize = 1 << fftOrder
};
private:
dsp::FFT forwardFFT;
Image spectrogramImage;
float fifo [fftSize];
float fftData [2 * fftSize];
int fifoIndex = 0;
bool nextFFTBlockReady = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimpleFFTDemo)
};

View File

@@ -1,33 +1,33 @@
#include "PluginProcessor.h"
#include "PluginEditor.h"
//==============================================================================
AudioPluginAudioProcessorEditor::AudioPluginAudioProcessorEditor (AudioPluginAudioProcessor& p)
: AudioProcessorEditor (&p), processorRef (p)
{
juce::ignoreUnused (processorRef);
// Make sure that before the constructor has finished, you've set the
// editor's size to whatever you need it to be.
setSize (400, 300);
}
AudioPluginAudioProcessorEditor::~AudioPluginAudioProcessorEditor()
{
}
//==============================================================================
void AudioPluginAudioProcessorEditor::paint (juce::Graphics& g)
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
g.setColour (juce::Colours::white);
g.setFont (15.0f);
g.drawFittedText ("Hello World!", getLocalBounds(), juce::Justification::centred, 1);
}
void AudioPluginAudioProcessorEditor::resized()
{
// This is generally where you'll want to lay out the positions of any
// subcomponents in your editor..
}
#include "PluginProcessor.h"
#include "PluginEditor.h"
//==============================================================================
AudioPluginAudioProcessorEditor::AudioPluginAudioProcessorEditor (AudioPluginAudioProcessor& p)
: AudioProcessorEditor (&p), processorRef (p)
{
juce::ignoreUnused (processorRef);
// Make sure that before the constructor has finished, you've set the
// editor's size to whatever you need it to be.
setSize (400, 300);
}
AudioPluginAudioProcessorEditor::~AudioPluginAudioProcessorEditor()
{
}
//==============================================================================
void AudioPluginAudioProcessorEditor::paint (juce::Graphics& g)
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
g.setColour (juce::Colours::white);
g.setFont (15.0f);
g.drawFittedText ("Hello World!", getLocalBounds(), juce::Justification::centred, 1);
}
void AudioPluginAudioProcessorEditor::resized()
{
// This is generally where you'll want to lay out the positions of any
// subcomponents in your editor..
}

View File

@@ -1,22 +1,22 @@
#pragma once
#include "PluginProcessor.h"
//==============================================================================
class AudioPluginAudioProcessorEditor : public juce::AudioProcessorEditor
{
public:
explicit AudioPluginAudioProcessorEditor (AudioPluginAudioProcessor&);
~AudioPluginAudioProcessorEditor() override;
//==============================================================================
void paint (juce::Graphics&) override;
void resized() override;
private:
// This reference is provided as a quick way for your editor to
// access the processor object that created it.
AudioPluginAudioProcessor& processorRef;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginAudioProcessorEditor)
};
#pragma once
#include "PluginProcessor.h"
//==============================================================================
class AudioPluginAudioProcessorEditor : public juce::AudioProcessorEditor
{
public:
explicit AudioPluginAudioProcessorEditor (AudioPluginAudioProcessor&);
~AudioPluginAudioProcessorEditor() override;
//==============================================================================
void paint (juce::Graphics&) override;
void resized() override;
private:
// This reference is provided as a quick way for your editor to
// access the processor object that created it.
AudioPluginAudioProcessor& processorRef;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginAudioProcessorEditor)
};

View File

@@ -1,188 +1,188 @@
#include "PluginProcessor.h"
#include "PluginEditor.h"
//==============================================================================
AudioPluginAudioProcessor::AudioPluginAudioProcessor()
: AudioProcessor (BusesProperties()
#if ! JucePlugin_IsMidiEffect
#if ! JucePlugin_IsSynth
.withInput ("Input", juce::AudioChannelSet::stereo(), true)
#endif
.withOutput ("Output", juce::AudioChannelSet::stereo(), true)
#endif
)
{
}
AudioPluginAudioProcessor::~AudioPluginAudioProcessor()
{
}
//==============================================================================
const juce::String AudioPluginAudioProcessor::getName() const
{
return JucePlugin_Name;
}
bool AudioPluginAudioProcessor::acceptsMidi() const
{
#if JucePlugin_WantsMidiInput
return true;
#else
return false;
#endif
}
bool AudioPluginAudioProcessor::producesMidi() const
{
#if JucePlugin_ProducesMidiOutput
return true;
#else
return false;
#endif
}
bool AudioPluginAudioProcessor::isMidiEffect() const
{
#if JucePlugin_IsMidiEffect
return true;
#else
return false;
#endif
}
double AudioPluginAudioProcessor::getTailLengthSeconds() const
{
return 0.0;
}
int AudioPluginAudioProcessor::getNumPrograms()
{
return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs,
// so this should be at least 1, even if you're not really implementing programs.
}
int AudioPluginAudioProcessor::getCurrentProgram()
{
return 0;
}
void AudioPluginAudioProcessor::setCurrentProgram (int index)
{
juce::ignoreUnused (index);
}
const juce::String AudioPluginAudioProcessor::getProgramName (int index)
{
juce::ignoreUnused (index);
return {};
}
void AudioPluginAudioProcessor::changeProgramName (int index, const juce::String& newName)
{
juce::ignoreUnused (index, newName);
}
//==============================================================================
void AudioPluginAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
// Use this method as the place to do any pre-playback
// initialisation that you need..
juce::ignoreUnused (sampleRate, samplesPerBlock);
}
void AudioPluginAudioProcessor::releaseResources()
{
// When playback stops, you can use this as an opportunity to free up any
// spare memory, etc.
}
bool AudioPluginAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
{
#if JucePlugin_IsMidiEffect
juce::ignoreUnused (layouts);
return true;
#else
// This is the place where you check if the layout is supported.
// In this template code we only support mono or stereo.
// Some plugin hosts, such as certain GarageBand versions, will only
// load plugins that support stereo bus layouts.
if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono()
&& layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo())
return false;
// This checks if the input layout matches the output layout
#if ! JucePlugin_IsSynth
if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
return false;
#endif
return true;
#endif
}
void AudioPluginAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer,
juce::MidiBuffer& midiMessages)
{
juce::ignoreUnused (midiMessages);
juce::ScopedNoDenormals noDenormals;
auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();
// In case we have more outputs than inputs, this code clears any output
// channels that didn't contain input data, (because these aren't
// guaranteed to be empty - they may contain garbage).
// This is here to avoid people getting screaming feedback
// when they first compile a plugin, but obviously you don't need to keep
// this code if your algorithm always overwrites all the output channels.
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear (i, 0, buffer.getNumSamples());
// This is the place where you'd normally do the guts of your plugin's
// audio processing...
// Make sure to reset the state if your inner loop is processing
// the samples and the outer loop is handling the channels.
// Alternatively, you can process the samples with the channels
// interleaved by keeping the same state.
for (int channel = 0; channel < totalNumInputChannels; ++channel)
{
auto* channelData = buffer.getWritePointer (channel);
juce::ignoreUnused (channelData);
// ..do something to the data...
}
}
//==============================================================================
bool AudioPluginAudioProcessor::hasEditor() const
{
return true; // (change this to false if you choose to not supply an editor)
}
juce::AudioProcessorEditor* AudioPluginAudioProcessor::createEditor()
{
return new AudioPluginAudioProcessorEditor (*this);
}
//==============================================================================
void AudioPluginAudioProcessor::getStateInformation (juce::MemoryBlock& destData)
{
// You should use this method to store your parameters in the memory block.
// You could do that either as raw data, or use the XML or ValueTree classes
// as intermediaries to make it easy to save and load complex data.
juce::ignoreUnused (destData);
}
void AudioPluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
// You should use this method to restore your parameters from this memory block,
// whose contents will have been created by the getStateInformation() call.
juce::ignoreUnused (data, sizeInBytes);
}
//==============================================================================
// This creates new instances of the plugin..
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new AudioPluginAudioProcessor();
}
#include "PluginProcessor.h"
#include "PluginEditor.h"
//==============================================================================
AudioPluginAudioProcessor::AudioPluginAudioProcessor()
: AudioProcessor (BusesProperties()
#if ! JucePlugin_IsMidiEffect
#if ! JucePlugin_IsSynth
.withInput ("Input", juce::AudioChannelSet::stereo(), true)
#endif
.withOutput ("Output", juce::AudioChannelSet::stereo(), true)
#endif
)
{
}
AudioPluginAudioProcessor::~AudioPluginAudioProcessor()
{
}
//==============================================================================
const juce::String AudioPluginAudioProcessor::getName() const
{
return JucePlugin_Name;
}
bool AudioPluginAudioProcessor::acceptsMidi() const
{
#if JucePlugin_WantsMidiInput
return true;
#else
return false;
#endif
}
bool AudioPluginAudioProcessor::producesMidi() const
{
#if JucePlugin_ProducesMidiOutput
return true;
#else
return false;
#endif
}
bool AudioPluginAudioProcessor::isMidiEffect() const
{
#if JucePlugin_IsMidiEffect
return true;
#else
return false;
#endif
}
double AudioPluginAudioProcessor::getTailLengthSeconds() const
{
return 0.0;
}
int AudioPluginAudioProcessor::getNumPrograms()
{
return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs,
// so this should be at least 1, even if you're not really implementing programs.
}
int AudioPluginAudioProcessor::getCurrentProgram()
{
return 0;
}
void AudioPluginAudioProcessor::setCurrentProgram (int index)
{
juce::ignoreUnused (index);
}
const juce::String AudioPluginAudioProcessor::getProgramName (int index)
{
juce::ignoreUnused (index);
return {};
}
void AudioPluginAudioProcessor::changeProgramName (int index, const juce::String& newName)
{
juce::ignoreUnused (index, newName);
}
//==============================================================================
void AudioPluginAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
// Use this method as the place to do any pre-playback
// initialisation that you need..
juce::ignoreUnused (sampleRate, samplesPerBlock);
}
void AudioPluginAudioProcessor::releaseResources()
{
// When playback stops, you can use this as an opportunity to free up any
// spare memory, etc.
}
bool AudioPluginAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
{
#if JucePlugin_IsMidiEffect
juce::ignoreUnused (layouts);
return true;
#else
// This is the place where you check if the layout is supported.
// In this template code we only support mono or stereo.
// Some plugin hosts, such as certain GarageBand versions, will only
// load plugins that support stereo bus layouts.
if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono()
&& layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo())
return false;
// This checks if the input layout matches the output layout
#if ! JucePlugin_IsSynth
if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
return false;
#endif
return true;
#endif
}
void AudioPluginAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer,
juce::MidiBuffer& midiMessages)
{
juce::ignoreUnused (midiMessages);
juce::ScopedNoDenormals noDenormals;
auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();
// In case we have more outputs than inputs, this code clears any output
// channels that didn't contain input data, (because these aren't
// guaranteed to be empty - they may contain garbage).
// This is here to avoid people getting screaming feedback
// when they first compile a plugin, but obviously you don't need to keep
// this code if your algorithm always overwrites all the output channels.
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear (i, 0, buffer.getNumSamples());
// This is the place where you'd normally do the guts of your plugin's
// audio processing...
// Make sure to reset the state if your inner loop is processing
// the samples and the outer loop is handling the channels.
// Alternatively, you can process the samples with the channels
// interleaved by keeping the same state.
for (int channel = 0; channel < totalNumInputChannels; ++channel)
{
auto* channelData = buffer.getWritePointer (channel);
juce::ignoreUnused (channelData);
// ..do something to the data...
}
}
//==============================================================================
bool AudioPluginAudioProcessor::hasEditor() const
{
return true; // (change this to false if you choose to not supply an editor)
}
juce::AudioProcessorEditor* AudioPluginAudioProcessor::createEditor()
{
return new AudioPluginAudioProcessorEditor (*this);
}
//==============================================================================
void AudioPluginAudioProcessor::getStateInformation (juce::MemoryBlock& destData)
{
// You should use this method to store your parameters in the memory block.
// You could do that either as raw data, or use the XML or ValueTree classes
// as intermediaries to make it easy to save and load complex data.
juce::ignoreUnused (destData);
}
void AudioPluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
// You should use this method to restore your parameters from this memory block,
// whose contents will have been created by the getStateInformation() call.
juce::ignoreUnused (data, sizeInBytes);
}
//==============================================================================
// This creates new instances of the plugin..
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new AudioPluginAudioProcessor();
}

View File

@@ -1,48 +1,48 @@
#pragma once
#include <juce_audio_processors/juce_audio_processors.h>
//==============================================================================
class AudioPluginAudioProcessor : public juce::AudioProcessor
{
public:
//==============================================================================
AudioPluginAudioProcessor();
~AudioPluginAudioProcessor() override;
//==============================================================================
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
void releaseResources() override;
bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
using AudioProcessor::processBlock;
//==============================================================================
juce::AudioProcessorEditor* createEditor() override;
bool hasEditor() const override;
//==============================================================================
const juce::String getName() const override;
bool acceptsMidi() const override;
bool producesMidi() const override;
bool isMidiEffect() const override;
double getTailLengthSeconds() const override;
//==============================================================================
int getNumPrograms() override;
int getCurrentProgram() override;
void setCurrentProgram (int index) override;
const juce::String getProgramName (int index) override;
void changeProgramName (int index, const juce::String& newName) override;
//==============================================================================
void getStateInformation (juce::MemoryBlock& destData) override;
void setStateInformation (const void* data, int sizeInBytes) override;
private:
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginAudioProcessor)
};
#pragma once
#include <juce_audio_processors/juce_audio_processors.h>
//==============================================================================
class AudioPluginAudioProcessor : public juce::AudioProcessor
{
public:
//==============================================================================
AudioPluginAudioProcessor();
~AudioPluginAudioProcessor() override;
//==============================================================================
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
void releaseResources() override;
bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
using AudioProcessor::processBlock;
//==============================================================================
juce::AudioProcessorEditor* createEditor() override;
bool hasEditor() const override;
//==============================================================================
const juce::String getName() const override;
bool acceptsMidi() const override;
bool producesMidi() const override;
bool isMidiEffect() const override;
double getTailLengthSeconds() const override;
//==============================================================================
int getNumPrograms() override;
int getCurrentProgram() override;
void setCurrentProgram (int index) override;
const juce::String getProgramName (int index) override;
void changeProgramName (int index, const juce::String& newName) override;
//==============================================================================
void getStateInformation (juce::MemoryBlock& destData) override;
void setStateInformation (const void* data, int sizeInBytes) override;
private:
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginAudioProcessor)
};

View File

@@ -1,15 +1,15 @@
# ==============================================================================
#
# This file is part of the JUCE library.
# Copyright (c) 2020 - Raw Material Software Limited
# Copyright (c) 2022 - Raw Material Software Limited
#
# JUCE is an open source library subject to commercial or open-source
# licensing.
#
# By using JUCE, you agree to the terms of both the JUCE 6 End-User License
# Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
# By using JUCE, you agree to the terms of both the JUCE 7 End-User License
# Agreement and JUCE Privacy Policy.
#
# End User License Agreement: www.juce.com/juce-6-licence
# End User License Agreement: www.juce.com/juce-7-licence
# Privacy Policy: www.juce.com/juce-privacy-policy
#
# Or: You may also use this code under the terms of the GPL v3 (see

View File

@@ -1,10 +1,10 @@
#include <juce_core/juce_core.h>
int main (int argc, char* argv[])
{
// Your code goes here!
juce::ignoreUnused (argc, argv);
return 0;
}
#include <juce_core/juce_core.h>
int main (int argc, char* argv[])
{
// Your code goes here!
juce::ignoreUnused (argc, argv);
return 0;
}

View File

@@ -1,101 +1,101 @@
#include "MainComponent.h"
//==============================================================================
class GuiAppApplication : public juce::JUCEApplication
{
public:
//==============================================================================
GuiAppApplication() {}
// We inject these as compile definitions from the CMakeLists.txt
// If you've enabled the juce header with `juce_generate_juce_header(<thisTarget>)`
// you could `#include <JuceHeader.h>` and use `ProjectInfo::projectName` etc. instead.
const juce::String getApplicationName() override { return JUCE_APPLICATION_NAME_STRING; }
const juce::String getApplicationVersion() override { return JUCE_APPLICATION_VERSION_STRING; }
bool moreThanOneInstanceAllowed() override { return true; }
//==============================================================================
void initialise (const juce::String& commandLine) override
{
// This method is where you should put your application's initialisation code..
juce::ignoreUnused (commandLine);
mainWindow.reset (new MainWindow (getApplicationName()));
}
void shutdown() override
{
// Add your application's shutdown code here..
mainWindow = nullptr; // (deletes our window)
}
//==============================================================================
void systemRequestedQuit() override
{
// This is called when the app is being asked to quit: you can ignore this
// request and let the app carry on running, or call quit() to allow the app to close.
quit();
}
void anotherInstanceStarted (const juce::String& commandLine) override
{
// When another instance of the app is launched while this one is running,
// this method is invoked, and the commandLine parameter tells you what
// the other instance's command-line arguments were.
juce::ignoreUnused (commandLine);
}
//==============================================================================
/*
This class implements the desktop window that contains an instance of
our MainComponent class.
*/
class MainWindow : public juce::DocumentWindow
{
public:
explicit MainWindow (juce::String name)
: DocumentWindow (name,
juce::Desktop::getInstance().getDefaultLookAndFeel()
.findColour (ResizableWindow::backgroundColourId),
DocumentWindow::allButtons)
{
setUsingNativeTitleBar (true);
setContentOwned (new MainComponent(), true);
#if JUCE_IOS || JUCE_ANDROID
setFullScreen (true);
#else
setResizable (true, true);
centreWithSize (getWidth(), getHeight());
#endif
setVisible (true);
}
void closeButtonPressed() override
{
// This is called when the user tries to close this window. Here, we'll just
// ask the app to quit when this happens, but you can change this to do
// whatever you need.
JUCEApplication::getInstance()->systemRequestedQuit();
}
/* Note: Be careful if you override any DocumentWindow methods - the base
class uses a lot of them, so by overriding you might break its functionality.
It's best to do all your work in your content component instead, but if
you really have to override any DocumentWindow methods, make sure your
subclass also calls the superclass's method.
*/
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
};
private:
std::unique_ptr<MainWindow> mainWindow;
};
//==============================================================================
// This macro generates the main() routine that launches the app.
START_JUCE_APPLICATION (GuiAppApplication)
#include "MainComponent.h"
//==============================================================================
class GuiAppApplication : public juce::JUCEApplication
{
public:
//==============================================================================
GuiAppApplication() {}
// We inject these as compile definitions from the CMakeLists.txt
// If you've enabled the juce header with `juce_generate_juce_header(<thisTarget>)`
// you could `#include <JuceHeader.h>` and use `ProjectInfo::projectName` etc. instead.
const juce::String getApplicationName() override { return JUCE_APPLICATION_NAME_STRING; }
const juce::String getApplicationVersion() override { return JUCE_APPLICATION_VERSION_STRING; }
bool moreThanOneInstanceAllowed() override { return true; }
//==============================================================================
void initialise (const juce::String& commandLine) override
{
// This method is where you should put your application's initialisation code..
juce::ignoreUnused (commandLine);
mainWindow.reset (new MainWindow (getApplicationName()));
}
void shutdown() override
{
// Add your application's shutdown code here..
mainWindow = nullptr; // (deletes our window)
}
//==============================================================================
void systemRequestedQuit() override
{
// This is called when the app is being asked to quit: you can ignore this
// request and let the app carry on running, or call quit() to allow the app to close.
quit();
}
void anotherInstanceStarted (const juce::String& commandLine) override
{
// When another instance of the app is launched while this one is running,
// this method is invoked, and the commandLine parameter tells you what
// the other instance's command-line arguments were.
juce::ignoreUnused (commandLine);
}
//==============================================================================
/*
This class implements the desktop window that contains an instance of
our MainComponent class.
*/
class MainWindow : public juce::DocumentWindow
{
public:
explicit MainWindow (juce::String name)
: DocumentWindow (name,
juce::Desktop::getInstance().getDefaultLookAndFeel()
.findColour (ResizableWindow::backgroundColourId),
DocumentWindow::allButtons)
{
setUsingNativeTitleBar (true);
setContentOwned (new MainComponent(), true);
#if JUCE_IOS || JUCE_ANDROID
setFullScreen (true);
#else
setResizable (true, true);
centreWithSize (getWidth(), getHeight());
#endif
setVisible (true);
}
void closeButtonPressed() override
{
// This is called when the user tries to close this window. Here, we'll just
// ask the app to quit when this happens, but you can change this to do
// whatever you need.
JUCEApplication::getInstance()->systemRequestedQuit();
}
/* Note: Be careful if you override any DocumentWindow methods - the base
class uses a lot of them, so by overriding you might break its functionality.
It's best to do all your work in your content component instead, but if
you really have to override any DocumentWindow methods, make sure your
subclass also calls the superclass's method.
*/
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
};
private:
std::unique_ptr<MainWindow> mainWindow;
};
//==============================================================================
// This macro generates the main() routine that launches the app.
START_JUCE_APPLICATION (GuiAppApplication)

View File

@@ -1,25 +1,25 @@
#include "MainComponent.h"
//==============================================================================
MainComponent::MainComponent()
{
setSize (600, 400);
}
//==============================================================================
void MainComponent::paint (juce::Graphics& g)
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
g.setFont (juce::Font (16.0f));
g.setColour (juce::Colours::white);
g.drawText ("Hello World!", getLocalBounds(), juce::Justification::centred, true);
}
void MainComponent::resized()
{
// This is called when the MainComponent is resized.
// If you add any child components, this is where you should
// update their positions.
}
#include "MainComponent.h"
//==============================================================================
MainComponent::MainComponent()
{
setSize (600, 400);
}
//==============================================================================
void MainComponent::paint (juce::Graphics& g)
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
g.setFont (juce::Font (16.0f));
g.setColour (juce::Colours::white);
g.drawText ("Hello World!", getLocalBounds(), juce::Justification::centred, true);
}
void MainComponent::resized()
{
// This is called when the MainComponent is resized.
// If you add any child components, this is where you should
// update their positions.
}

View File

@@ -1,29 +1,29 @@
#pragma once
// CMake builds don't use an AppConfig.h, so it's safe to include juce module headers
// directly. If you need to remain compatible with Projucer-generated builds, and
// have called `juce_generate_juce_header(<thisTarget>)` in your CMakeLists.txt,
// you could `#include <JuceHeader.h>` here instead, to make all your module headers visible.
#include <juce_gui_extra/juce_gui_extra.h>
//==============================================================================
/*
This component lives inside our window, and this is where you should put all
your controls and content.
*/
class MainComponent : public juce::Component
{
public:
//==============================================================================
MainComponent();
//==============================================================================
void paint (juce::Graphics&) override;
void resized() override;
private:
//==============================================================================
// Your private member variables go here...
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
#pragma once
// CMake builds don't use an AppConfig.h, so it's safe to include juce module headers
// directly. If you need to remain compatible with Projucer-generated builds, and
// have called `juce_generate_juce_header(<thisTarget>)` in your CMakeLists.txt,
// you could `#include <JuceHeader.h>` here instead, to make all your module headers visible.
#include <juce_gui_extra/juce_gui_extra.h>
//==============================================================================
/*
This component lives inside our window, and this is where you should put all
your controls and content.
*/
class MainComponent : public juce::Component
{
public:
//==============================================================================
MainComponent();
//==============================================================================
void paint (juce::Graphics&) override;
void resized() override;
private:
//==============================================================================
// Your private member variables go here...
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};

View File

@@ -1,15 +1,15 @@
# ==============================================================================
#
# This file is part of the JUCE library.
# Copyright (c) 2020 - Raw Material Software Limited
# Copyright (c) 2022 - Raw Material Software Limited
#
# JUCE is an open source library subject to commercial or open-source
# licensing.
#
# By using JUCE, you agree to the terms of both the JUCE 6 End-User License
# Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
# By using JUCE, you agree to the terms of both the JUCE 7 End-User License
# Agreement and JUCE Privacy Policy.
#
# End User License Agreement: www.juce.com/juce-6-licence
# End User License Agreement: www.juce.com/juce-7-licence
# Privacy Policy: www.juce.com/juce-privacy-policy
#
# Or: You may also use this code under the terms of the GPL v3 (see
@@ -41,6 +41,18 @@ function(_juce_add_pips)
"${CMAKE_CURRENT_SOURCE_DIR}/PushNotificationsDemo.h")
endif()
if(NOT (TARGET juce_ara_sdk
AND (CMAKE_SYSTEM_NAME STREQUAL "Windows"
OR CMAKE_SYSTEM_NAME STREQUAL "Darwin"
OR CMAKE_SYSTEM_NAME STREQUAL "Linux")))
list(REMOVE_ITEM headers
"${CMAKE_CURRENT_SOURCE_DIR}/ARAPluginDemo.h")
endif()
if(NOT TARGET juce_vst2_sdk)
list(REMOVE_ITEM headers "${CMAKE_CURRENT_SOURCE_DIR}/ReaperEmbeddedViewPluginDemo.h")
endif()
foreach(header IN ITEMS ${headers})
juce_add_pip(${header} added_target)
target_link_libraries(${added_target} PUBLIC

View File

@@ -1,15 +1,15 @@
# ==============================================================================
#
# This file is part of the JUCE library.
# Copyright (c) 2020 - Raw Material Software Limited
# Copyright (c) 2022 - Raw Material Software Limited
#
# JUCE is an open source library subject to commercial or open-source
# licensing.
#
# By using JUCE, you agree to the terms of both the JUCE 6 End-User License
# Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
# By using JUCE, you agree to the terms of both the JUCE 7 End-User License
# Agreement and JUCE Privacy Policy.
#
# End User License Agreement: www.juce.com/juce-6-licence
# End User License Agreement: www.juce.com/juce-7-licence
# Privacy Policy: www.juce.com/juce-privacy-policy
#
# Or: You may also use this code under the terms of the GPL v3 (see

View File

@@ -1,203 +1,203 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: ConvolutionDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Convolution demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: ConvolutionDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct ConvolutionDemoDSP
{
void prepare (const ProcessSpec& spec)
{
sampleRate = spec.sampleRate;
convolution.prepare (spec);
updateParameters();
}
void process (ProcessContextReplacing<float> context)
{
context.isBypassed = bypass;
// Load a new IR if there's one available. Note that this doesn't lock or allocate!
bufferTransfer.get ([this] (BufferWithSampleRate& buf)
{
convolution.loadImpulseResponse (std::move (buf.buffer),
buf.sampleRate,
Convolution::Stereo::yes,
Convolution::Trim::yes,
Convolution::Normalise::yes);
});
convolution.process (context);
}
void reset()
{
convolution.reset();
}
void updateParameters()
{
auto* cabinetTypeParameter = dynamic_cast<ChoiceParameter*> (parameters[0]);
if (cabinetTypeParameter == nullptr)
{
jassertfalse;
return;
}
if (cabinetTypeParameter->getCurrentSelectedID() == 1)
{
bypass = true;
}
else
{
bypass = false;
auto selectedType = cabinetTypeParameter->getCurrentSelectedID();
auto assetName = (selectedType == 2 ? "guitar_amp.wav" : "cassette_recorder.wav");
auto assetInputStream = createAssetInputStream (assetName);
if (assetInputStream == nullptr)
{
jassertfalse;
return;
}
AudioFormatManager manager;
manager.registerBasicFormats();
std::unique_ptr<AudioFormatReader> reader { manager.createReaderFor (std::move (assetInputStream)) };
if (reader == nullptr)
{
jassertfalse;
return;
}
AudioBuffer<float> buffer (static_cast<int> (reader->numChannels),
static_cast<int> (reader->lengthInSamples));
reader->read (buffer.getArrayOfWritePointers(), buffer.getNumChannels(), 0, buffer.getNumSamples());
bufferTransfer.set (BufferWithSampleRate { std::move (buffer), reader->sampleRate });
}
}
//==============================================================================
struct BufferWithSampleRate
{
BufferWithSampleRate() = default;
BufferWithSampleRate (AudioBuffer<float>&& bufferIn, double sampleRateIn)
: buffer (std::move (bufferIn)), sampleRate (sampleRateIn) {}
AudioBuffer<float> buffer;
double sampleRate = 0.0;
};
class BufferTransfer
{
public:
void set (BufferWithSampleRate&& p)
{
const SpinLock::ScopedLockType lock (mutex);
buffer = std::move (p);
newBuffer = true;
}
// Call `fn` passing the new buffer, if there's one available
template <typename Fn>
void get (Fn&& fn)
{
const SpinLock::ScopedTryLockType lock (mutex);
if (lock.isLocked() && newBuffer)
{
fn (buffer);
newBuffer = false;
}
}
private:
BufferWithSampleRate buffer;
bool newBuffer = false;
SpinLock mutex;
};
double sampleRate = 0.0;
bool bypass = false;
MemoryBlock currentCabinetData;
Convolution convolution;
BufferTransfer bufferTransfer;
ChoiceParameter cabinetParam { { "Bypass", "Guitar amplifier 8''", "Cassette recorder" }, 1, "Cabinet Type" };
std::vector<DSPDemoParameterBase*> parameters { &cabinetParam };
};
struct ConvolutionDemo : public Component
{
ConvolutionDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<ConvolutionDemoDSP> fileReaderComponent;
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: ConvolutionDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Convolution demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: ConvolutionDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct ConvolutionDemoDSP
{
void prepare (const ProcessSpec& spec)
{
sampleRate = spec.sampleRate;
convolution.prepare (spec);
updateParameters();
}
void process (ProcessContextReplacing<float> context)
{
context.isBypassed = bypass;
// Load a new IR if there's one available. Note that this doesn't lock or allocate!
bufferTransfer.get ([this] (BufferWithSampleRate& buf)
{
convolution.loadImpulseResponse (std::move (buf.buffer),
buf.sampleRate,
Convolution::Stereo::yes,
Convolution::Trim::yes,
Convolution::Normalise::yes);
});
convolution.process (context);
}
void reset()
{
convolution.reset();
}
void updateParameters()
{
auto* cabinetTypeParameter = dynamic_cast<ChoiceParameter*> (parameters[0]);
if (cabinetTypeParameter == nullptr)
{
jassertfalse;
return;
}
if (cabinetTypeParameter->getCurrentSelectedID() == 1)
{
bypass = true;
}
else
{
bypass = false;
auto selectedType = cabinetTypeParameter->getCurrentSelectedID();
auto assetName = (selectedType == 2 ? "guitar_amp.wav" : "cassette_recorder.wav");
auto assetInputStream = createAssetInputStream (assetName);
if (assetInputStream == nullptr)
{
jassertfalse;
return;
}
AudioFormatManager manager;
manager.registerBasicFormats();
std::unique_ptr<AudioFormatReader> reader { manager.createReaderFor (std::move (assetInputStream)) };
if (reader == nullptr)
{
jassertfalse;
return;
}
AudioBuffer<float> buffer (static_cast<int> (reader->numChannels),
static_cast<int> (reader->lengthInSamples));
reader->read (buffer.getArrayOfWritePointers(), buffer.getNumChannels(), 0, buffer.getNumSamples());
bufferTransfer.set (BufferWithSampleRate { std::move (buffer), reader->sampleRate });
}
}
//==============================================================================
struct BufferWithSampleRate
{
BufferWithSampleRate() = default;
BufferWithSampleRate (AudioBuffer<float>&& bufferIn, double sampleRateIn)
: buffer (std::move (bufferIn)), sampleRate (sampleRateIn) {}
AudioBuffer<float> buffer;
double sampleRate = 0.0;
};
class BufferTransfer
{
public:
void set (BufferWithSampleRate&& p)
{
const SpinLock::ScopedLockType lock (mutex);
buffer = std::move (p);
newBuffer = true;
}
// Call `fn` passing the new buffer, if there's one available
template <typename Fn>
void get (Fn&& fn)
{
const SpinLock::ScopedTryLockType lock (mutex);
if (lock.isLocked() && newBuffer)
{
fn (buffer);
newBuffer = false;
}
}
private:
BufferWithSampleRate buffer;
bool newBuffer = false;
SpinLock mutex;
};
double sampleRate = 0.0;
bool bypass = false;
MemoryBlock currentCabinetData;
Convolution convolution;
BufferTransfer bufferTransfer;
ChoiceParameter cabinetParam { { "Bypass", "Guitar amplifier 8''", "Cassette recorder" }, 1, "Cabinet Type" };
std::vector<DSPDemoParameterBase*> parameters { &cabinetParam };
};
struct ConvolutionDemo : public Component
{
ConvolutionDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<ConvolutionDemoDSP> fileReaderComponent;
};

View File

@@ -1,115 +1,115 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: FIRFilterDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: FIR filter demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: FIRFilterDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct FIRFilterDemoDSP
{
void prepare (const ProcessSpec& spec)
{
sampleRate = spec.sampleRate;
fir.state = FilterDesign<float>::designFIRLowpassWindowMethod (440.0f, sampleRate, 21,
WindowingFunction<float>::blackman);
fir.prepare (spec);
}
void process (const ProcessContextReplacing<float>& context)
{
fir.process (context);
}
void reset()
{
fir.reset();
}
void updateParameters()
{
if (sampleRate != 0.0)
{
auto cutoff = static_cast<float> (cutoffParam.getCurrentValue());
auto windowingMethod = static_cast<WindowingFunction<float>::WindowingMethod> (typeParam.getCurrentSelectedID() - 1);
*fir.state = *FilterDesign<float>::designFIRLowpassWindowMethod (cutoff, sampleRate, 21, windowingMethod);
}
}
//==============================================================================
ProcessorDuplicator<FIR::Filter<float>, FIR::Coefficients<float>> fir;
double sampleRate = 0.0;
SliderParameter cutoffParam { { 20.0, 20000.0 }, 0.4, 440.0f, "Cutoff", "Hz" };
ChoiceParameter typeParam { { "Rectangular", "Triangular", "Hann", "Hamming", "Blackman", "Blackman-Harris", "Flat Top", "Kaiser" },
5, "Windowing Function" };
std::vector<DSPDemoParameterBase*> parameters { &cutoffParam, &typeParam };
};
struct FIRFilterDemo : public Component
{
FIRFilterDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<FIRFilterDemoDSP> fileReaderComponent;
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: FIRFilterDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: FIR filter demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: FIRFilterDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct FIRFilterDemoDSP
{
void prepare (const ProcessSpec& spec)
{
sampleRate = spec.sampleRate;
fir.state = FilterDesign<float>::designFIRLowpassWindowMethod (440.0f, sampleRate, 21,
WindowingFunction<float>::blackman);
fir.prepare (spec);
}
void process (const ProcessContextReplacing<float>& context)
{
fir.process (context);
}
void reset()
{
fir.reset();
}
void updateParameters()
{
if (sampleRate != 0.0)
{
auto cutoff = static_cast<float> (cutoffParam.getCurrentValue());
auto windowingMethod = static_cast<WindowingFunction<float>::WindowingMethod> (typeParam.getCurrentSelectedID() - 1);
*fir.state = *FilterDesign<float>::designFIRLowpassWindowMethod (cutoff, sampleRate, 21, windowingMethod);
}
}
//==============================================================================
ProcessorDuplicator<FIR::Filter<float>, FIR::Coefficients<float>> fir;
double sampleRate = 0.0;
SliderParameter cutoffParam { { 20.0, 20000.0 }, 0.4, 440.0f, "Cutoff", "Hz" };
ChoiceParameter typeParam { { "Rectangular", "Triangular", "Hann", "Hamming", "Blackman", "Blackman-Harris", "Flat Top", "Kaiser" },
5, "Windowing Function" };
std::vector<DSPDemoParameterBase*> parameters { &cutoffParam, &typeParam };
};
struct FIRFilterDemo : public Component
{
FIRFilterDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<FIRFilterDemoDSP> fileReaderComponent;
};

View File

@@ -1,100 +1,100 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: GainDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Gain demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: GainDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct GainDemoDSP
{
void prepare (const ProcessSpec&)
{
gain.setGainDecibels (-6.0f);
}
void process (const ProcessContextReplacing<float>& context)
{
gain.process (context);
}
void reset()
{
gain.reset();
}
void updateParameters()
{
gain.setGainDecibels (static_cast<float> (gainParam.getCurrentValue()));
}
//==============================================================================
Gain<float> gain;
SliderParameter gainParam { { -100.0, 20.0 }, 3.0, -6.0, "Gain", "dB" };
std::vector<DSPDemoParameterBase*> parameters { &gainParam };
};
struct GainDemo : public Component
{
GainDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<GainDemoDSP> fileReaderComponent;
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: GainDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Gain demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: GainDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct GainDemoDSP
{
void prepare (const ProcessSpec&)
{
gain.setGainDecibels (-6.0f);
}
void process (const ProcessContextReplacing<float>& context)
{
gain.process (context);
}
void reset()
{
gain.reset();
}
void updateParameters()
{
gain.setGainDecibels (static_cast<float> (gainParam.getCurrentValue()));
}
//==============================================================================
Gain<float> gain;
SliderParameter gainParam { { -100.0, 20.0 }, 3.0, -6.0, "Gain", "dB" };
std::vector<DSPDemoParameterBase*> parameters { &gainParam };
};
struct GainDemo : public Component
{
GainDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<GainDemoDSP> fileReaderComponent;
};

View File

@@ -1,119 +1,119 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: IIRFilterDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: IIR filter demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: IIRFilterDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct IIRFilterDemoDSP
{
void prepare (const ProcessSpec& spec)
{
sampleRate = spec.sampleRate;
iir.state = IIR::Coefficients<float>::makeLowPass (sampleRate, 440.0);
iir.prepare (spec);
}
void process (const ProcessContextReplacing<float>& context)
{
iir.process (context);
}
void reset()
{
iir.reset();
}
void updateParameters()
{
if (sampleRate != 0.0)
{
auto cutoff = static_cast<float> (cutoffParam.getCurrentValue());
auto qVal = static_cast<float> (qParam.getCurrentValue());
switch (typeParam.getCurrentSelectedID())
{
case 1: *iir.state = IIR::ArrayCoefficients<float>::makeLowPass (sampleRate, cutoff, qVal); break;
case 2: *iir.state = IIR::ArrayCoefficients<float>::makeHighPass (sampleRate, cutoff, qVal); break;
case 3: *iir.state = IIR::ArrayCoefficients<float>::makeBandPass (sampleRate, cutoff, qVal); break;
default: break;
}
}
}
//==============================================================================
ProcessorDuplicator<IIR::Filter<float>, IIR::Coefficients<float>> iir;
ChoiceParameter typeParam { { "Low-pass", "High-pass", "Band-pass" }, 1, "Type" };
SliderParameter cutoffParam { { 20.0, 20000.0 }, 0.5, 440.0f, "Cutoff", "Hz" };
SliderParameter qParam { { 0.3, 20.0 }, 0.5, 1.0 / std::sqrt(2.0), "Q" };
std::vector<DSPDemoParameterBase*> parameters { &typeParam, &cutoffParam, &qParam };
double sampleRate = 0.0;
};
struct IIRFilterDemo : public Component
{
IIRFilterDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<IIRFilterDemoDSP> fileReaderComponent;
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: IIRFilterDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: IIR filter demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: IIRFilterDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct IIRFilterDemoDSP
{
void prepare (const ProcessSpec& spec)
{
sampleRate = spec.sampleRate;
iir.state = IIR::Coefficients<float>::makeLowPass (sampleRate, 440.0);
iir.prepare (spec);
}
void process (const ProcessContextReplacing<float>& context)
{
iir.process (context);
}
void reset()
{
iir.reset();
}
void updateParameters()
{
if (sampleRate != 0.0)
{
auto cutoff = static_cast<float> (cutoffParam.getCurrentValue());
auto qVal = static_cast<float> (qParam.getCurrentValue());
switch (typeParam.getCurrentSelectedID())
{
case 1: *iir.state = IIR::ArrayCoefficients<float>::makeLowPass (sampleRate, cutoff, qVal); break;
case 2: *iir.state = IIR::ArrayCoefficients<float>::makeHighPass (sampleRate, cutoff, qVal); break;
case 3: *iir.state = IIR::ArrayCoefficients<float>::makeBandPass (sampleRate, cutoff, qVal); break;
default: break;
}
}
}
//==============================================================================
ProcessorDuplicator<IIR::Filter<float>, IIR::Coefficients<float>> iir;
ChoiceParameter typeParam { { "Low-pass", "High-pass", "Band-pass" }, 1, "Type" };
SliderParameter cutoffParam { { 20.0, 20000.0 }, 0.5, 440.0f, "Cutoff", "Hz" };
SliderParameter qParam { { 0.3, 20.0 }, 0.5, 1.0 / std::sqrt(2.0), "Q" };
std::vector<DSPDemoParameterBase*> parameters { &typeParam, &cutoffParam, &qParam };
double sampleRate = 0.0;
};
struct IIRFilterDemo : public Component
{
IIRFilterDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<IIRFilterDemoDSP> fileReaderComponent;
};

View File

@@ -1,151 +1,151 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: OscillatorDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Oscillator demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: OscillatorDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct OscillatorDemoDSP
{
void prepare (const ProcessSpec& spec)
{
gain.setGainDecibels (-6.0f);
for (auto&& oscillator : oscillators)
{
oscillator.setFrequency (440.f);
oscillator.prepare (spec);
}
updateParameters();
tempBuffer = AudioBlock<float> (tempBufferMemory, spec.numChannels, spec.maximumBlockSize);
}
void process (const ProcessContextReplacing<float>& context)
{
tempBuffer.copyFrom (context.getInputBlock());
tempBuffer.multiplyBy (static_cast<float> (fileMix));
oscillators[currentOscillatorIdx].process (context);
context.getOutputBlock().multiplyBy (static_cast<float> (1.0 - fileMix));
context.getOutputBlock().add (tempBuffer);
gain.process (context);
}
void reset()
{
oscillators[currentOscillatorIdx].reset();
}
void updateParameters()
{
currentOscillatorIdx = jmin (numElementsInArray (oscillators),
3 * (accuracy.getCurrentSelectedID() - 1) + (typeParam.getCurrentSelectedID() - 1));
auto freq = static_cast<float> (freqParam.getCurrentValue());
for (auto&& oscillator : oscillators)
oscillator.setFrequency (freq);
gain.setGainDecibels (static_cast<float> (gainParam.getCurrentValue()));
fileMix = mixParam.getCurrentValue();
}
//==============================================================================
Oscillator<float> oscillators[6] =
{
// No Approximation
{[] (float x) { return std::sin (x); }}, // sine
{[] (float x) { return x / MathConstants<float>::pi; }}, // saw
{[] (float x) { return x < 0.0f ? -1.0f : 1.0f; }}, // square
// Approximated by a wave-table
{[] (float x) { return std::sin (x); }, 100}, // sine
{[] (float x) { return x / MathConstants<float>::pi; }, 100}, // saw
{[] (float x) { return x < 0.0f ? -1.0f : 1.0f; }, 100} // square
};
int currentOscillatorIdx = 0;
Gain<float> gain;
ChoiceParameter typeParam { {"sine", "saw", "square"}, 1, "Type" };
ChoiceParameter accuracy { {"No Approximation", "Use Wavetable"}, 1, "Accuracy" };
SliderParameter freqParam { { 20.0, 24000.0 }, 0.4, 440.0, "Frequency", "Hz" };
SliderParameter gainParam { { -100.0, 20.0 }, 3.0, -20.0, "Gain", "dB" };
SliderParameter mixParam { { 0.0, 1.0 }, 1.0, 0.0, "File mix" };
HeapBlock<char> tempBufferMemory;
AudioBlock<float> tempBuffer;
double fileMix;
std::vector<DSPDemoParameterBase*> parameters { &typeParam, &accuracy, &freqParam, &gainParam, &mixParam };
};
struct OscillatorDemo : public Component
{
OscillatorDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<OscillatorDemoDSP> fileReaderComponent;
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: OscillatorDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Oscillator demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: OscillatorDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct OscillatorDemoDSP
{
void prepare (const ProcessSpec& spec)
{
gain.setGainDecibels (-6.0f);
for (auto&& oscillator : oscillators)
{
oscillator.setFrequency (440.f);
oscillator.prepare (spec);
}
updateParameters();
tempBuffer = AudioBlock<float> (tempBufferMemory, spec.numChannels, spec.maximumBlockSize);
}
void process (const ProcessContextReplacing<float>& context)
{
tempBuffer.copyFrom (context.getInputBlock());
tempBuffer.multiplyBy (static_cast<float> (fileMix));
oscillators[currentOscillatorIdx].process (context);
context.getOutputBlock().multiplyBy (static_cast<float> (1.0 - fileMix));
context.getOutputBlock().add (tempBuffer);
gain.process (context);
}
void reset()
{
oscillators[currentOscillatorIdx].reset();
}
void updateParameters()
{
currentOscillatorIdx = jmin (numElementsInArray (oscillators),
3 * (accuracy.getCurrentSelectedID() - 1) + (typeParam.getCurrentSelectedID() - 1));
auto freq = static_cast<float> (freqParam.getCurrentValue());
for (auto&& oscillator : oscillators)
oscillator.setFrequency (freq);
gain.setGainDecibels (static_cast<float> (gainParam.getCurrentValue()));
fileMix = mixParam.getCurrentValue();
}
//==============================================================================
Oscillator<float> oscillators[6] =
{
// No Approximation
{[] (float x) { return std::sin (x); }}, // sine
{[] (float x) { return x / MathConstants<float>::pi; }}, // saw
{[] (float x) { return x < 0.0f ? -1.0f : 1.0f; }}, // square
// Approximated by a wave-table
{[] (float x) { return std::sin (x); }, 100}, // sine
{[] (float x) { return x / MathConstants<float>::pi; }, 100}, // saw
{[] (float x) { return x < 0.0f ? -1.0f : 1.0f; }, 100} // square
};
int currentOscillatorIdx = 0;
Gain<float> gain;
ChoiceParameter typeParam { {"sine", "saw", "square"}, 1, "Type" };
ChoiceParameter accuracy { {"No Approximation", "Use Wavetable"}, 1, "Accuracy" };
SliderParameter freqParam { { 20.0, 24000.0 }, 0.4, 440.0, "Frequency", "Hz" };
SliderParameter gainParam { { -100.0, 20.0 }, 3.0, -20.0, "Gain", "dB" };
SliderParameter mixParam { { 0.0, 1.0 }, 1.0, 0.0, "File mix" };
HeapBlock<char> tempBufferMemory;
AudioBlock<float> tempBuffer;
double fileMix;
std::vector<DSPDemoParameterBase*> parameters { &typeParam, &accuracy, &freqParam, &gainParam, &mixParam };
};
struct OscillatorDemo : public Component
{
OscillatorDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<OscillatorDemoDSP> fileReaderComponent;
};

View File

@@ -1,130 +1,130 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: OverdriveDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Overdrive demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: OverdriveDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct OverdriveDemoDSP
{
void prepare (const ProcessSpec& spec)
{
sampleRate = spec.sampleRate;
auto& gainUp = overdrive.get<0>();
gainUp.setGainDecibels (24);
auto& bias = overdrive.get<1>();
bias.setBias (0.4f);
auto& wavShaper = overdrive.get<2>();
wavShaper.functionToUse = std::tanh;
auto& dcFilter = overdrive.get<3>();
dcFilter.state = IIR::Coefficients<float>::makeHighPass (sampleRate, 5.0);
auto& gainDown = overdrive.get<4>();
gainDown.setGainDecibels (-18.0f);
overdrive.prepare (spec);
}
void process (const ProcessContextReplacing<float>& context)
{
overdrive.process (context);
}
void reset()
{
overdrive.reset();
}
void updateParameters()
{
if (sampleRate != 0.0)
{
overdrive.get<0>().setGainDecibels (static_cast<float> (inGainParam.getCurrentValue()));
overdrive.get<4>().setGainDecibels (static_cast<float> (outGainParam.getCurrentValue()));
}
}
//==============================================================================
using GainProcessor = Gain<float>;
using BiasProcessor = Bias<float>;
using DriveProcessor = WaveShaper<float>;
using DCFilter = ProcessorDuplicator<IIR::Filter<float>,
IIR::Coefficients<float>>;
ProcessorChain<GainProcessor, BiasProcessor, DriveProcessor, DCFilter, GainProcessor> overdrive;
SliderParameter inGainParam { { -100.0, 60.0 }, 3, 24.0, "Input Gain", "dB" };
SliderParameter outGainParam { { -100.0, 20.0 }, 3, -18.0, "Output Gain", "dB" };
std::vector<DSPDemoParameterBase*> parameters { &inGainParam, &outGainParam };
double sampleRate = 0.0;
};
struct OverdriveDemo : public Component
{
OverdriveDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<OverdriveDemoDSP> fileReaderComponent;
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: OverdriveDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Overdrive demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: OverdriveDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct OverdriveDemoDSP
{
void prepare (const ProcessSpec& spec)
{
sampleRate = spec.sampleRate;
auto& gainUp = overdrive.get<0>();
gainUp.setGainDecibels (24);
auto& bias = overdrive.get<1>();
bias.setBias (0.4f);
auto& wavShaper = overdrive.get<2>();
wavShaper.functionToUse = std::tanh;
auto& dcFilter = overdrive.get<3>();
dcFilter.state = IIR::Coefficients<float>::makeHighPass (sampleRate, 5.0);
auto& gainDown = overdrive.get<4>();
gainDown.setGainDecibels (-18.0f);
overdrive.prepare (spec);
}
void process (const ProcessContextReplacing<float>& context)
{
overdrive.process (context);
}
void reset()
{
overdrive.reset();
}
void updateParameters()
{
if (sampleRate != 0.0)
{
overdrive.get<0>().setGainDecibels (static_cast<float> (inGainParam.getCurrentValue()));
overdrive.get<4>().setGainDecibels (static_cast<float> (outGainParam.getCurrentValue()));
}
}
//==============================================================================
using GainProcessor = Gain<float>;
using BiasProcessor = Bias<float>;
using DriveProcessor = WaveShaper<float>;
using DCFilter = ProcessorDuplicator<IIR::Filter<float>,
IIR::Coefficients<float>>;
ProcessorChain<GainProcessor, BiasProcessor, DriveProcessor, DCFilter, GainProcessor> overdrive;
SliderParameter inGainParam { { -100.0, 60.0 }, 3, 24.0, "Input Gain", "dB" };
SliderParameter outGainParam { { -100.0, 20.0 }, 3, -18.0, "Output Gain", "dB" };
std::vector<DSPDemoParameterBase*> parameters { &inGainParam, &outGainParam };
double sampleRate = 0.0;
};
struct OverdriveDemo : public Component
{
OverdriveDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<OverdriveDemoDSP> fileReaderComponent;
};

View File

@@ -1,159 +1,173 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: SIMDRegisterDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: SIMD register demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: SIMDRegisterDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct SIMDRegisterDemoDSP
{
void prepare (const ProcessSpec& spec)
{
sampleRate = spec.sampleRate;
iirCoefficients = IIR::Coefficients<float>::makeLowPass (sampleRate, 440.0f);
iir.reset (new IIR::Filter<SIMDRegister<float>> (iirCoefficients));
interleaved = AudioBlock<SIMDRegister<float>> (interleavedBlockData, 1, spec.maximumBlockSize);
zero = AudioBlock<float> (zeroData, SIMDRegister<float>::size(), spec.maximumBlockSize);
zero.clear();
auto monoSpec = spec;
monoSpec.numChannels = 1;
iir->prepare (monoSpec);
}
void process (const ProcessContextReplacing<float>& context)
{
jassert (context.getInputBlock().getNumSamples() == context.getOutputBlock().getNumSamples());
jassert (context.getInputBlock().getNumChannels() == context.getOutputBlock().getNumChannels());
auto& input = context.getInputBlock();
auto& output = context.getOutputBlock();
auto n = (int) input.getNumSamples();
auto* inout = channelPointers.getData();
for (size_t ch = 0; ch < SIMDRegister<float>::size(); ++ch)
inout[ch] = (ch < input.getNumChannels() ? const_cast<float*> (input.getChannelPointer (ch)) : zero.getChannelPointer (ch));
using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::NonConst>;
using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
DstSampleType dstData (interleaved.getChannelPointer (0), (int) interleaved.getNumChannels());
SrcSampleType srcData (inout);
dstData.convertSamples (srcData, n);
iir->process (ProcessContextReplacing<SIMDRegister<float>> (interleaved));
for (size_t ch = 0; ch < input.getNumChannels(); ++ch)
inout[ch] = output.getChannelPointer (ch);
srcData.convertSamples (dstData, n);
}
void reset()
{
iir.reset();
}
void updateParameters()
{
if (sampleRate != 0.0)
{
auto cutoff = static_cast<float> (cutoffParam.getCurrentValue());
auto qVal = static_cast<float> (qParam.getCurrentValue());
switch (typeParam.getCurrentSelectedID())
{
case 1: *iirCoefficients = IIR::ArrayCoefficients<float>::makeLowPass (sampleRate, cutoff, qVal); break;
case 2: *iirCoefficients = IIR::ArrayCoefficients<float>::makeHighPass (sampleRate, cutoff, qVal); break;
case 3: *iirCoefficients = IIR::ArrayCoefficients<float>::makeBandPass (sampleRate, cutoff, qVal); break;
default: break;
}
}
}
//==============================================================================
IIR::Coefficients<float>::Ptr iirCoefficients;
std::unique_ptr<IIR::Filter<SIMDRegister<float>>> iir;
AudioBlock<SIMDRegister<float>> interleaved;
AudioBlock<float> zero;
HeapBlock<char> interleavedBlockData, zeroData;
HeapBlock<const float*> channelPointers { SIMDRegister<float>::size() };
ChoiceParameter typeParam { { "Low-pass", "High-pass", "Band-pass" }, 1, "Type" };
SliderParameter cutoffParam { { 20.0, 20000.0 }, 0.5, 440.0f, "Cutoff", "Hz" };
SliderParameter qParam { { 0.3, 20.0 }, 0.5, 0.7, "Q" };
std::vector<DSPDemoParameterBase*> parameters { &typeParam, &cutoffParam, &qParam };
double sampleRate = 0.0;
};
struct SIMDRegisterDemo : public Component
{
SIMDRegisterDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<SIMDRegisterDemoDSP> fileReaderComponent;
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: SIMDRegisterDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: SIMD register demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: SIMDRegisterDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
template <typename T>
static T* toBasePointer (SIMDRegister<T>* r) noexcept
{
return reinterpret_cast<T*> (r);
}
constexpr auto registerSize = dsp::SIMDRegister<float>::size();
//==============================================================================
struct SIMDRegisterDemoDSP
{
void prepare (const ProcessSpec& spec)
{
sampleRate = spec.sampleRate;
iirCoefficients = IIR::Coefficients<float>::makeLowPass (sampleRate, 440.0f);
iir.reset (new IIR::Filter<SIMDRegister<float>> (iirCoefficients));
interleaved = AudioBlock<SIMDRegister<float>> (interleavedBlockData, 1, spec.maximumBlockSize);
zero = AudioBlock<float> (zeroData, SIMDRegister<float>::size(), spec.maximumBlockSize);
zero.clear();
auto monoSpec = spec;
monoSpec.numChannels = 1;
iir->prepare (monoSpec);
}
template <typename SampleType>
auto prepareChannelPointers (const AudioBlock<SampleType>& block)
{
std::array<SampleType*, registerSize> result {};
for (size_t ch = 0; ch < result.size(); ++ch)
result[ch] = (ch < block.getNumChannels() ? block.getChannelPointer (ch) : zero.getChannelPointer (ch));
return result;
}
void process (const ProcessContextReplacing<float>& context)
{
jassert (context.getInputBlock().getNumSamples() == context.getOutputBlock().getNumSamples());
jassert (context.getInputBlock().getNumChannels() == context.getOutputBlock().getNumChannels());
const auto& input = context.getInputBlock();
const auto numSamples = (int) input.getNumSamples();
auto inChannels = prepareChannelPointers (input);
using Format = AudioData::Format<AudioData::Float32, AudioData::NativeEndian>;
AudioData::interleaveSamples (AudioData::NonInterleavedSource<Format> { inChannels.data(), registerSize, },
AudioData::InterleavedDest<Format> { toBasePointer (interleaved.getChannelPointer (0)), registerSize },
numSamples);
iir->process (ProcessContextReplacing<SIMDRegister<float>> (interleaved));
auto outChannels = prepareChannelPointers (context.getOutputBlock());
AudioData::deinterleaveSamples (AudioData::InterleavedSource<Format> { toBasePointer (interleaved.getChannelPointer (0)), registerSize },
AudioData::NonInterleavedDest<Format> { outChannels.data(), registerSize },
numSamples);
}
void reset()
{
iir.reset();
}
void updateParameters()
{
if (sampleRate != 0.0)
{
auto cutoff = static_cast<float> (cutoffParam.getCurrentValue());
auto qVal = static_cast<float> (qParam.getCurrentValue());
switch (typeParam.getCurrentSelectedID())
{
case 1: *iirCoefficients = IIR::ArrayCoefficients<float>::makeLowPass (sampleRate, cutoff, qVal); break;
case 2: *iirCoefficients = IIR::ArrayCoefficients<float>::makeHighPass (sampleRate, cutoff, qVal); break;
case 3: *iirCoefficients = IIR::ArrayCoefficients<float>::makeBandPass (sampleRate, cutoff, qVal); break;
default: break;
}
}
}
//==============================================================================
IIR::Coefficients<float>::Ptr iirCoefficients;
std::unique_ptr<IIR::Filter<SIMDRegister<float>>> iir;
AudioBlock<SIMDRegister<float>> interleaved;
AudioBlock<float> zero;
HeapBlock<char> interleavedBlockData, zeroData;
ChoiceParameter typeParam { { "Low-pass", "High-pass", "Band-pass" }, 1, "Type" };
SliderParameter cutoffParam { { 20.0, 20000.0 }, 0.5, 440.0f, "Cutoff", "Hz" };
SliderParameter qParam { { 0.3, 20.0 }, 0.5, 0.7, "Q" };
std::vector<DSPDemoParameterBase*> parameters { &typeParam, &cutoffParam, &qParam };
double sampleRate = 0.0;
};
struct SIMDRegisterDemo : public Component
{
SIMDRegisterDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<SIMDRegisterDemoDSP> fileReaderComponent;
};

View File

@@ -1,117 +1,117 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: StateVariableFilterDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: State variable filter demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: StateVariableFilterDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct StateVariableFilterDemoDSP
{
void prepare (const ProcessSpec& spec)
{
sampleRate = spec.sampleRate;
filter.prepare (spec);
}
void process (const ProcessContextReplacing<float>& context)
{
filter.process (context);
}
void reset()
{
filter.reset();
}
void updateParameters()
{
if (sampleRate != 0.0)
{
filter.setCutoffFrequency (static_cast<float> (cutoffParam.getCurrentValue()));
filter.setResonance (static_cast<float> (qParam.getCurrentValue()));
switch (typeParam.getCurrentSelectedID() - 1)
{
case 0: filter.setType (StateVariableTPTFilterType::lowpass); break;
case 1: filter.setType (StateVariableTPTFilterType::bandpass); break;
case 2: filter.setType (StateVariableTPTFilterType::highpass); break;
default: jassertfalse; break;
};
}
}
//==============================================================================
StateVariableTPTFilter<float> filter;
ChoiceParameter typeParam {{ "Low-pass", "Band-pass", "High-pass" }, 1, "Type" };
SliderParameter cutoffParam {{ 20.0, 20000.0 }, 0.5, 440.0f, "Cutoff", "Hz" };
SliderParameter qParam {{ 0.3, 20.0 }, 0.5, 1.0 / MathConstants<double>::sqrt2, "Resonance" };
std::vector<DSPDemoParameterBase*> parameters { &typeParam, &cutoffParam, &qParam };
double sampleRate = 0.0;
};
struct StateVariableFilterDemo : public Component
{
StateVariableFilterDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<StateVariableFilterDemoDSP> fileReaderComponent;
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: StateVariableFilterDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: State variable filter demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: StateVariableFilterDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct StateVariableFilterDemoDSP
{
void prepare (const ProcessSpec& spec)
{
sampleRate = spec.sampleRate;
filter.prepare (spec);
}
void process (const ProcessContextReplacing<float>& context)
{
filter.process (context);
}
void reset()
{
filter.reset();
}
void updateParameters()
{
if (sampleRate != 0.0)
{
filter.setCutoffFrequency (static_cast<float> (cutoffParam.getCurrentValue()));
filter.setResonance (static_cast<float> (qParam.getCurrentValue()));
switch (typeParam.getCurrentSelectedID() - 1)
{
case 0: filter.setType (StateVariableTPTFilterType::lowpass); break;
case 1: filter.setType (StateVariableTPTFilterType::bandpass); break;
case 2: filter.setType (StateVariableTPTFilterType::highpass); break;
default: jassertfalse; break;
};
}
}
//==============================================================================
StateVariableTPTFilter<float> filter;
ChoiceParameter typeParam {{ "Low-pass", "Band-pass", "High-pass" }, 1, "Type" };
SliderParameter cutoffParam {{ 20.0, 20000.0 }, 0.5, 440.0f, "Cutoff", "Hz" };
SliderParameter qParam {{ 0.3, 20.0 }, 0.5, 1.0 / MathConstants<double>::sqrt2, "Resonance" };
std::vector<DSPDemoParameterBase*> parameters { &typeParam, &cutoffParam, &qParam };
double sampleRate = 0.0;
};
struct StateVariableFilterDemo : public Component
{
StateVariableFilterDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<StateVariableFilterDemoDSP> fileReaderComponent;
};

View File

@@ -1,99 +1,99 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: WaveShaperTanhDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Wave shaper tanh demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2019, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: WaveShaperTanhDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct WaveShaperTanhDemoDSP
{
void prepare (const ProcessSpec&) {}
void process (const ProcessContextReplacing<float>& context)
{
shapers[currentShaperIdx].process (context);
}
void reset()
{
for (auto&& shaper : shapers)
shaper.reset();
}
void updateParameters()
{
currentShaperIdx = jmin (numElementsInArray (shapers), accuracy.getCurrentSelectedID() - 1);
}
//==============================================================================
WaveShaper<float> shapers[2] { { std::tanh }, { FastMathApproximations::tanh } };
int currentShaperIdx = 0;
ChoiceParameter accuracy {{ "No Approximation", "Use fast-math approximation" }, 1, "Accuracy" };
std::vector<DSPDemoParameterBase*> parameters { &accuracy }; // no params for this demo
};
struct WaveShaperTanhDemo : public Component
{
WaveShaperTanhDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<WaveShaperTanhDemoDSP> fileReaderComponent;
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this PIP. A PIP is a short snippet
of code that can be read by the Projucer and used to generate a JUCE project.
BEGIN_JUCE_PIP_METADATA
name: WaveShaperTanhDemo
version: 1.0.0
vendor: JUCE
website: http://juce.com
description: Wave shaper tanh demo using the DSP module.
dependencies: juce_audio_basics, juce_audio_devices, juce_audio_formats,
juce_audio_processors, juce_audio_utils, juce_core,
juce_data_structures, juce_dsp, juce_events, juce_graphics,
juce_gui_basics, juce_gui_extra
exporters: xcode_mac, vs2022, linux_make
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
type: Component
mainClass: WaveShaperTanhDemo
useLocalCopy: 1
END_JUCE_PIP_METADATA
*******************************************************************************/
#pragma once
#include "../Assets/DemoUtilities.h"
#include "../Assets/DSPDemos_Common.h"
using namespace dsp;
//==============================================================================
struct WaveShaperTanhDemoDSP
{
void prepare (const ProcessSpec&) {}
void process (const ProcessContextReplacing<float>& context)
{
shapers[currentShaperIdx].process (context);
}
void reset()
{
for (auto&& shaper : shapers)
shaper.reset();
}
void updateParameters()
{
currentShaperIdx = jmin (numElementsInArray (shapers), accuracy.getCurrentSelectedID() - 1);
}
//==============================================================================
WaveShaper<float> shapers[2] { { std::tanh }, { FastMathApproximations::tanh } };
int currentShaperIdx = 0;
ChoiceParameter accuracy {{ "No Approximation", "Use fast-math approximation" }, 1, "Accuracy" };
std::vector<DSPDemoParameterBase*> parameters { &accuracy }; // no params for this demo
};
struct WaveShaperTanhDemo : public Component
{
WaveShaperTanhDemo()
{
addAndMakeVisible (fileReaderComponent);
setSize (750, 500);
}
void resized() override
{
fileReaderComponent.setBounds (getLocalBounds());
}
AudioFileReaderComponent<WaveShaperTanhDemoDSP> fileReaderComponent;
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
compileSdkVersion 31
externalNativeBuild {
cmake {
path "CMakeLists.txt"
@@ -20,7 +20,7 @@ android {
defaultConfig {
applicationId "com.rmsl.jucedemorunner"
minSdkVersion 23
targetSdkVersion 29
targetSdkVersion 31
externalNativeBuild {
cmake {
arguments "-DANDROID_TOOLCHAIN=clang", "-DANDROID_PLATFORM=android-23", "-DANDROID_STL=c++_static", "-DANDROID_CPP_FEATURES=exceptions rtti", "-DANDROID_ARM_MODE=arm", "-DANDROID_ARM_NEON=TRUE", "-DCMAKE_CXX_STANDARD=14", "-DCMAKE_CXX_EXTENSIONS=OFF"

View File

@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="6.1.2"
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="7.0.2"
package="com.rmsl.jucedemorunner">
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:anyDensity="true"
android:xlargeScreens="true"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
@@ -16,7 +17,8 @@
<uses-feature android:glEsVersion="0x00030000" android:required="true"/>
<application android:label="@string/app_name" android:name="com.rmsl.juce.JuceApp" android:icon="@drawable/icon" android:hardwareAccelerated="false">
<activity android:name="com.rmsl.juce.JuceActivity" android:label="@string/app_name" android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true">
android:screenOrientation="unspecified" android:launchMode="singleTask" android:hardwareAccelerated="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>

View File

@@ -1,72 +1,72 @@
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2020 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
/* This component scrolls a continuous waveform showing the audio that's
coming into whatever audio inputs this object is connected to.
*/
class LiveScrollingAudioDisplay : public AudioVisualiserComponent,
public AudioIODeviceCallback
{
public:
LiveScrollingAudioDisplay() : AudioVisualiserComponent (1)
{
setSamplesPerBlock (256);
setBufferSize (1024);
}
//==============================================================================
void audioDeviceAboutToStart (AudioIODevice*) override
{
clear();
}
void audioDeviceStopped() override
{
clear();
}
void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
float** outputChannelData, int numOutputChannels,
int numberOfSamples) override
{
for (int i = 0; i < numberOfSamples; ++i)
{
float inputSample = 0;
for (int chan = 0; chan < numInputChannels; ++chan)
if (const float* inputChannel = inputChannelData[chan])
inputSample += inputChannel[i]; // find the sum of all the channels
inputSample *= 10.0f; // boost the level to make it more easily visible.
pushSample (&inputSample, 1);
}
// We need to clear the output buffers before returning, in case they're full of junk..
for (int j = 0; j < numOutputChannels; ++j)
if (float* outputChannel = outputChannelData[j])
zeromem (outputChannel, (size_t) numberOfSamples * sizeof (float));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LiveScrollingAudioDisplay)
};
/*
==============================================================================
This file is part of the JUCE examples.
Copyright (c) 2022 - Raw Material Software Limited
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
/* This component scrolls a continuous waveform showing the audio that's
coming into whatever audio inputs this object is connected to.
*/
class LiveScrollingAudioDisplay : public AudioVisualiserComponent,
public AudioIODeviceCallback
{
public:
LiveScrollingAudioDisplay() : AudioVisualiserComponent (1)
{
setSamplesPerBlock (256);
setBufferSize (1024);
}
//==============================================================================
void audioDeviceAboutToStart (AudioIODevice*) override
{
clear();
}
void audioDeviceStopped() override
{
clear();
}
void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels,
float** outputChannelData, int numOutputChannels,
int numberOfSamples) override
{
for (int i = 0; i < numberOfSamples; ++i)
{
float inputSample = 0;
for (int chan = 0; chan < numInputChannels; ++chan)
if (const float* inputChannel = inputChannelData[chan])
inputSample += inputChannel[i]; // find the sum of all the channels
inputSample *= 10.0f; // boost the level to make it more easily visible.
pushSample (&inputSample, 1);
}
// We need to clear the output buffers before returning, in case they're full of junk..
for (int j = 0; j < numOutputChannels; ++j)
if (float* outputChannel = outputChannelData[j])
zeromem (outputChannel, (size_t) numberOfSamples * sizeof (float));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LiveScrollingAudioDisplay)
};

View File

@@ -1,51 +1,51 @@
#ifndef AddPair_H
#define AddPair_H
class AddPair : public Test
{
public:
AddPair()
{
m_world->SetGravity(b2Vec2(0.0f,0.0f));
{
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = 0.1f;
float minX = -6.0f;
float maxX = 0.0f;
float minY = 4.0f;
float maxY = 6.0f;
for (int i = 0; i < 400; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = b2Vec2(RandomFloat(minX,maxX),RandomFloat(minY,maxY));
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 0.01f);
}
}
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-40.0f,5.0f);
bd.bullet = true;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
body->SetLinearVelocity(b2Vec2(150.0f, 0.0f));
}
}
static Test* Create()
{
return new AddPair;
}
};
#endif
#ifndef AddPair_H
#define AddPair_H
class AddPair : public Test
{
public:
AddPair()
{
m_world->SetGravity(b2Vec2(0.0f,0.0f));
{
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = 0.1f;
float minX = -6.0f;
float maxX = 0.0f;
float minY = 4.0f;
float maxY = 6.0f;
for (int i = 0; i < 400; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = b2Vec2(RandomFloat(minX,maxX),RandomFloat(minY,maxY));
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 0.01f);
}
}
{
b2PolygonShape shape;
shape.SetAsBox(1.5f, 1.5f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-40.0f,5.0f);
bd.bullet = true;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
body->SetLinearVelocity(b2Vec2(150.0f, 0.0f));
}
}
static Test* Create()
{
return new AddPair;
}
};
#endif

View File

@@ -1,183 +1,183 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef APPLY_FORCE_H
#define APPLY_FORCE_H
class ApplyForce : public Test
{
public:
ApplyForce()
{
m_world->SetGravity(b2Vec2(0.0f, 0.0f));
const float32 k_restitution = 0.4f;
b2Body* ground;
{
b2BodyDef bd;
bd.position.Set(0.0f, 20.0f);
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
b2FixtureDef sd;
sd.shape = &shape;
sd.density = 0.0f;
sd.restitution = k_restitution;
// Left vertical
shape.Set(b2Vec2(-20.0f, -20.0f), b2Vec2(-20.0f, 20.0f));
ground->CreateFixture(&sd);
// Right vertical
shape.Set(b2Vec2(20.0f, -20.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Top horizontal
shape.Set(b2Vec2(-20.0f, 20.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Bottom horizontal
shape.Set(b2Vec2(-20.0f, -20.0f), b2Vec2(20.0f, -20.0f));
ground->CreateFixture(&sd);
}
{
b2Transform xf1;
xf1.q.Set(0.3524f * b2_pi);
xf1.p = xf1.q.GetXAxis();
b2Vec2 vertices[3];
vertices[0] = b2Mul(xf1, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf1, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf1, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly1;
poly1.Set(vertices, 3);
b2FixtureDef sd1;
sd1.shape = &poly1;
sd1.density = 4.0f;
b2Transform xf2;
xf2.q.Set(-0.3524f * b2_pi);
xf2.p = -xf2.q.GetXAxis();
vertices[0] = b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf2, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf2, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly2;
poly2.Set(vertices, 3);
b2FixtureDef sd2;
sd2.shape = &poly2;
sd2.density = 2.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.angularDamping = 5.0f;
bd.linearDamping = 0.1f;
bd.position.Set(0.0f, 2.0f);
bd.angle = b2_pi;
bd.allowSleep = false;
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&sd1);
m_body->CreateFixture(&sd2);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.3f;
for (int i = 0; i < 10; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 5.0f + 1.54f * i);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
float32 gravity = 10.0f;
float32 I = body->GetInertia();
float32 mass = body->GetMass();
// For a circle: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m)
float32 radius = b2Sqrt(2.0f * I / mass);
b2FrictionJointDef jd;
jd.localAnchorA.SetZero();
jd.localAnchorB.SetZero();
jd.bodyA = ground;
jd.bodyB = body;
jd.collideConnected = true;
jd.maxForce = mass * gravity;
jd.maxTorque = mass * radius * gravity;
m_world->CreateJoint(&jd);
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'w':
{
b2Vec2 f = m_body->GetWorldVector(b2Vec2(0.0f, -200.0f));
b2Vec2 p = m_body->GetWorldPoint(b2Vec2(0.0f, 2.0f));
m_body->ApplyForce(f, p);
}
break;
case 'a':
{
m_body->ApplyTorque(50.0f);
}
break;
case 'd':
{
m_body->ApplyTorque(-50.0f);
}
break;
default:
break;
}
}
static Test* Create()
{
return new ApplyForce;
}
b2Body* m_body;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef APPLY_FORCE_H
#define APPLY_FORCE_H
class ApplyForce : public Test
{
public:
ApplyForce()
{
m_world->SetGravity(b2Vec2(0.0f, 0.0f));
const float32 k_restitution = 0.4f;
b2Body* ground;
{
b2BodyDef bd;
bd.position.Set(0.0f, 20.0f);
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
b2FixtureDef sd;
sd.shape = &shape;
sd.density = 0.0f;
sd.restitution = k_restitution;
// Left vertical
shape.Set(b2Vec2(-20.0f, -20.0f), b2Vec2(-20.0f, 20.0f));
ground->CreateFixture(&sd);
// Right vertical
shape.Set(b2Vec2(20.0f, -20.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Top horizontal
shape.Set(b2Vec2(-20.0f, 20.0f), b2Vec2(20.0f, 20.0f));
ground->CreateFixture(&sd);
// Bottom horizontal
shape.Set(b2Vec2(-20.0f, -20.0f), b2Vec2(20.0f, -20.0f));
ground->CreateFixture(&sd);
}
{
b2Transform xf1;
xf1.q.Set(0.3524f * b2_pi);
xf1.p = xf1.q.GetXAxis();
b2Vec2 vertices[3];
vertices[0] = b2Mul(xf1, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf1, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf1, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly1;
poly1.Set(vertices, 3);
b2FixtureDef sd1;
sd1.shape = &poly1;
sd1.density = 4.0f;
b2Transform xf2;
xf2.q.Set(-0.3524f * b2_pi);
xf2.p = -xf2.q.GetXAxis();
vertices[0] = b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf2, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf2, b2Vec2(0.0f, 0.5f));
b2PolygonShape poly2;
poly2.Set(vertices, 3);
b2FixtureDef sd2;
sd2.shape = &poly2;
sd2.density = 2.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.angularDamping = 5.0f;
bd.linearDamping = 0.1f;
bd.position.Set(0.0f, 2.0f);
bd.angle = b2_pi;
bd.allowSleep = false;
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&sd1);
m_body->CreateFixture(&sd2);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.3f;
for (int i = 0; i < 10; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 5.0f + 1.54f * i);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
float32 gravity = 10.0f;
float32 I = body->GetInertia();
float32 mass = body->GetMass();
// For a circle: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m)
float32 radius = b2Sqrt(2.0f * I / mass);
b2FrictionJointDef jd;
jd.localAnchorA.SetZero();
jd.localAnchorB.SetZero();
jd.bodyA = ground;
jd.bodyB = body;
jd.collideConnected = true;
jd.maxForce = mass * gravity;
jd.maxTorque = mass * radius * gravity;
m_world->CreateJoint(&jd);
}
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'w':
{
b2Vec2 f = m_body->GetWorldVector(b2Vec2(0.0f, -200.0f));
b2Vec2 p = m_body->GetWorldPoint(b2Vec2(0.0f, 2.0f));
m_body->ApplyForce(f, p);
}
break;
case 'a':
{
m_body->ApplyTorque(50.0f);
}
break;
case 'd':
{
m_body->ApplyTorque(-50.0f);
}
break;
default:
break;
}
}
static Test* Create()
{
return new ApplyForce;
}
b2Body* m_body;
};
#endif

View File

@@ -1,159 +1,159 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BODY_TYPES_H
#define BODY_TYPES_H
class BodyTypes : public Test
{
public:
BodyTypes()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
b2FixtureDef fd;
fd.shape = &shape;
ground->CreateFixture(&fd);
}
// Define attachment
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 3.0f);
m_attachment = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 2.0f);
m_attachment->CreateFixture(&shape, 2.0f);
}
// Define platform
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-4.0f, 5.0f);
m_platform = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 4.0f, b2Vec2(4.0f, 0.0f), 0.5f * b2_pi);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.6f;
fd.density = 2.0f;
m_platform->CreateFixture(&fd);
b2RevoluteJointDef rjd;
rjd.Initialize(m_attachment, m_platform, b2Vec2(0.0f, 5.0f));
rjd.maxMotorTorque = 50.0f;
rjd.enableMotor = true;
m_world->CreateJoint(&rjd);
b2PrismaticJointDef pjd;
pjd.Initialize(ground, m_platform, b2Vec2(0.0f, 5.0f), b2Vec2(1.0f, 0.0f));
pjd.maxMotorForce = 1000.0f;
pjd.enableMotor = true;
pjd.lowerTranslation = -10.0f;
pjd.upperTranslation = 10.0f;
pjd.enableLimit = true;
m_world->CreateJoint(&pjd);
m_speed = 3.0f;
}
// Create a payload
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 8.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.75f, 0.75f);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.6f;
fd.density = 2.0f;
body->CreateFixture(&fd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'd':
m_platform->SetType(b2_dynamicBody);
break;
case 's':
m_platform->SetType(b2_staticBody);
break;
case 'k':
m_platform->SetType(b2_kinematicBody);
m_platform->SetLinearVelocity(b2Vec2(-m_speed, 0.0f));
m_platform->SetAngularVelocity(0.0f);
break;
}
}
void Step(Settings* settings)
{
// Drive the kinematic body.
if (m_platform->GetType() == b2_kinematicBody)
{
b2Vec2 p = m_platform->GetTransform().p;
b2Vec2 v = m_platform->GetLinearVelocity();
if ((p.x < -10.0f && v.x < 0.0f) ||
(p.x > 10.0f && v.x > 0.0f))
{
v.x = -v.x;
m_platform->SetLinearVelocity(v);
}
}
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (d) dynamic, (s) static, (k) kinematic");
m_textLine += 15;
}
static Test* Create()
{
return new BodyTypes;
}
b2Body* m_attachment;
b2Body* m_platform;
float32 m_speed;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BODY_TYPES_H
#define BODY_TYPES_H
class BodyTypes : public Test
{
public:
BodyTypes()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
b2FixtureDef fd;
fd.shape = &shape;
ground->CreateFixture(&fd);
}
// Define attachment
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 3.0f);
m_attachment = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 2.0f);
m_attachment->CreateFixture(&shape, 2.0f);
}
// Define platform
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-4.0f, 5.0f);
m_platform = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 4.0f, b2Vec2(4.0f, 0.0f), 0.5f * b2_pi);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.6f;
fd.density = 2.0f;
m_platform->CreateFixture(&fd);
b2RevoluteJointDef rjd;
rjd.Initialize(m_attachment, m_platform, b2Vec2(0.0f, 5.0f));
rjd.maxMotorTorque = 50.0f;
rjd.enableMotor = true;
m_world->CreateJoint(&rjd);
b2PrismaticJointDef pjd;
pjd.Initialize(ground, m_platform, b2Vec2(0.0f, 5.0f), b2Vec2(1.0f, 0.0f));
pjd.maxMotorForce = 1000.0f;
pjd.enableMotor = true;
pjd.lowerTranslation = -10.0f;
pjd.upperTranslation = 10.0f;
pjd.enableLimit = true;
m_world->CreateJoint(&pjd);
m_speed = 3.0f;
}
// Create a payload
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 8.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.75f, 0.75f);
b2FixtureDef fd;
fd.shape = &shape;
fd.friction = 0.6f;
fd.density = 2.0f;
body->CreateFixture(&fd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'd':
m_platform->SetType(b2_dynamicBody);
break;
case 's':
m_platform->SetType(b2_staticBody);
break;
case 'k':
m_platform->SetType(b2_kinematicBody);
m_platform->SetLinearVelocity(b2Vec2(-m_speed, 0.0f));
m_platform->SetAngularVelocity(0.0f);
break;
}
}
void Step(Settings* settings)
{
// Drive the kinematic body.
if (m_platform->GetType() == b2_kinematicBody)
{
b2Vec2 p = m_platform->GetTransform().p;
b2Vec2 v = m_platform->GetLinearVelocity();
if ((p.x < -10.0f && v.x < 0.0f) ||
(p.x > 10.0f && v.x > 0.0f))
{
v.x = -v.x;
m_platform->SetLinearVelocity(v);
}
}
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "Keys: (d) dynamic, (s) static, (k) kinematic");
m_textLine += 15;
}
static Test* Create()
{
return new BodyTypes;
}
b2Body* m_attachment;
b2Body* m_platform;
float32 m_speed;
};
#endif

View File

@@ -1,155 +1,155 @@
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BREAKABLE_TEST_H
#define BREAKABLE_TEST_H
// This is used to test sensor shapes.
class Breakable : public Test
{
public:
enum
{
e_count = 7
};
Breakable()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Breakable dynamic body
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 40.0f);
bd.angle = 0.25f * b2_pi;
m_body1 = m_world->CreateBody(&bd);
m_shape1.SetAsBox(0.5f, 0.5f, b2Vec2(-0.5f, 0.0f), 0.0f);
m_piece1 = m_body1->CreateFixture(&m_shape1, 1.0f);
m_shape2.SetAsBox(0.5f, 0.5f, b2Vec2(0.5f, 0.0f), 0.0f);
m_piece2 = m_body1->CreateFixture(&m_shape2, 1.0f);
}
m_break = false;
m_broke = false;
}
void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
{
if (m_broke)
{
// The body already broke.
return;
}
// Should the body break?
int32 count = contact->GetManifold()->pointCount;
float32 maxImpulse = 0.0f;
for (int32 i = 0; i < count; ++i)
{
maxImpulse = b2Max(maxImpulse, impulse->normalImpulses[i]);
}
if (maxImpulse > 40.0f)
{
// Flag the body for breaking.
m_break = true;
}
}
void Break()
{
// Create two bodies from one.
b2Body* body1 = m_piece1->GetBody();
b2Vec2 center = body1->GetWorldCenter();
body1->DestroyFixture(m_piece2);
m_piece2 = NULL;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = body1->GetPosition();
bd.angle = body1->GetAngle();
b2Body* body2 = m_world->CreateBody(&bd);
m_piece2 = body2->CreateFixture(&m_shape2, 1.0f);
// Compute consistent velocities for new bodies based on
// cached velocity.
b2Vec2 center1 = body1->GetWorldCenter();
b2Vec2 center2 = body2->GetWorldCenter();
b2Vec2 velocity1 = m_velocity + b2Cross(m_angularVelocity, center1 - center);
b2Vec2 velocity2 = m_velocity + b2Cross(m_angularVelocity, center2 - center);
body1->SetAngularVelocity(m_angularVelocity);
body1->SetLinearVelocity(velocity1);
body2->SetAngularVelocity(m_angularVelocity);
body2->SetLinearVelocity(velocity2);
}
void Step(Settings* settings)
{
if (m_break)
{
Break();
m_broke = true;
m_break = false;
}
// Cache velocities to improve movement on breakage.
if (m_broke == false)
{
m_velocity = m_body1->GetLinearVelocity();
m_angularVelocity = m_body1->GetAngularVelocity();
}
Test::Step(settings);
}
static Test* Create()
{
return new Breakable;
}
b2Body* m_body1;
b2Vec2 m_velocity;
float32 m_angularVelocity;
b2PolygonShape m_shape1;
b2PolygonShape m_shape2;
b2Fixture* m_piece1;
b2Fixture* m_piece2;
bool m_broke;
bool m_break;
};
#endif
/*
* Copyright (c) 2008-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BREAKABLE_TEST_H
#define BREAKABLE_TEST_H
// This is used to test sensor shapes.
class Breakable : public Test
{
public:
enum
{
e_count = 7
};
Breakable()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Breakable dynamic body
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 40.0f);
bd.angle = 0.25f * b2_pi;
m_body1 = m_world->CreateBody(&bd);
m_shape1.SetAsBox(0.5f, 0.5f, b2Vec2(-0.5f, 0.0f), 0.0f);
m_piece1 = m_body1->CreateFixture(&m_shape1, 1.0f);
m_shape2.SetAsBox(0.5f, 0.5f, b2Vec2(0.5f, 0.0f), 0.0f);
m_piece2 = m_body1->CreateFixture(&m_shape2, 1.0f);
}
m_break = false;
m_broke = false;
}
void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse)
{
if (m_broke)
{
// The body already broke.
return;
}
// Should the body break?
int32 count = contact->GetManifold()->pointCount;
float32 maxImpulse = 0.0f;
for (int32 i = 0; i < count; ++i)
{
maxImpulse = b2Max(maxImpulse, impulse->normalImpulses[i]);
}
if (maxImpulse > 40.0f)
{
// Flag the body for breaking.
m_break = true;
}
}
void Break()
{
// Create two bodies from one.
b2Body* body1 = m_piece1->GetBody();
b2Vec2 center = body1->GetWorldCenter();
body1->DestroyFixture(m_piece2);
m_piece2 = NULL;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = body1->GetPosition();
bd.angle = body1->GetAngle();
b2Body* body2 = m_world->CreateBody(&bd);
m_piece2 = body2->CreateFixture(&m_shape2, 1.0f);
// Compute consistent velocities for new bodies based on
// cached velocity.
b2Vec2 center1 = body1->GetWorldCenter();
b2Vec2 center2 = body2->GetWorldCenter();
b2Vec2 velocity1 = m_velocity + b2Cross(m_angularVelocity, center1 - center);
b2Vec2 velocity2 = m_velocity + b2Cross(m_angularVelocity, center2 - center);
body1->SetAngularVelocity(m_angularVelocity);
body1->SetLinearVelocity(velocity1);
body2->SetAngularVelocity(m_angularVelocity);
body2->SetLinearVelocity(velocity2);
}
void Step(Settings* settings)
{
if (m_break)
{
Break();
m_broke = true;
m_break = false;
}
// Cache velocities to improve movement on breakage.
if (m_broke == false)
{
m_velocity = m_body1->GetLinearVelocity();
m_angularVelocity = m_body1->GetAngularVelocity();
}
Test::Step(settings);
}
static Test* Create()
{
return new Breakable;
}
b2Body* m_body1;
b2Vec2 m_velocity;
float32 m_angularVelocity;
b2PolygonShape m_shape1;
b2PolygonShape m_shape2;
b2Fixture* m_piece1;
b2Fixture* m_piece2;
bool m_broke;
bool m_break;
};
#endif

View File

@@ -1,125 +1,125 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BRIDGE_H
#define BRIDGE_H
class Bridge : public Test
{
public:
enum
{
e_count = 30
};
Bridge()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
b2RevoluteJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
if (i == (e_count >> 1))
{
m_middle = body;
}
prevBody = body;
}
b2Vec2 anchor(-15.0f + 1.0f * e_count, 5.0f);
jd.Initialize(prevBody, ground, anchor);
m_world->CreateJoint(&jd);
}
for (int32 i = 0; i < 2; ++i)
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
b2PolygonShape shape;
shape.Set(vertices, 3);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-8.0f + 8.0f * i, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
for (int32 i = 0; i < 3; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 6.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
static Test* Create()
{
return new Bridge;
}
b2Body* m_middle;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BRIDGE_H
#define BRIDGE_H
class Bridge : public Test
{
public:
enum
{
e_count = 30
};
Bridge()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
b2RevoluteJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
if (i == (e_count >> 1))
{
m_middle = body;
}
prevBody = body;
}
b2Vec2 anchor(-15.0f + 1.0f * e_count, 5.0f);
jd.Initialize(prevBody, ground, anchor);
m_world->CreateJoint(&jd);
}
for (int32 i = 0; i < 2; ++i)
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
b2PolygonShape shape;
shape.Set(vertices, 3);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-8.0f + 8.0f * i, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
for (int32 i = 0; i < 3; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 6.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
static Test* Create()
{
return new Bridge;
}
b2Body* m_middle;
};
#endif

View File

@@ -1,136 +1,136 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BULLET_TEST_H
#define BULLET_TEST_H
class BulletTest : public Test
{
public:
BulletTest()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
body->CreateFixture(&edge, 0.0f);
b2PolygonShape shape;
shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
body->CreateFixture(&shape, 0.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 4.0f);
b2PolygonShape box;
box.SetAsBox(2.0f, 0.1f);
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&box, 1.0f);
box.SetAsBox(0.25f, 0.25f);
//m_x = RandomFloat(-1.0f, 1.0f);
m_x = 0.20352793f;
bd.position.Set(m_x, 10.0f);
bd.bullet = true;
m_bullet = m_world->CreateBody(&bd);
m_bullet->CreateFixture(&box, 100.0f);
m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
}
}
void Launch()
{
m_body->SetTransform(b2Vec2(0.0f, 4.0f), 0.0f);
m_body->SetLinearVelocity(b2Vec2_zero);
m_body->SetAngularVelocity(0.0f);
m_x = RandomFloat(-1.0f, 1.0f);
m_bullet->SetTransform(b2Vec2(m_x, 10.0f), 0.0f);
m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
m_bullet->SetAngularVelocity(0.0f);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
b2_gjkCalls = 0;
b2_gjkIters = 0;
b2_gjkMaxIters = 0;
b2_toiCalls = 0;
b2_toiIters = 0;
b2_toiMaxIters = 0;
b2_toiRootIters = 0;
b2_toiMaxRootIters = 0;
}
void Step(Settings* settings)
{
Test::Step(settings);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern int32 b2_toiCalls, b2_toiIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
if (b2_gjkCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
b2_gjkCalls, b2_gjkIters / float32(b2_gjkCalls), b2_gjkMaxIters);
m_textLine += 15;
}
if (b2_toiCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave toi iters = %3.1f, max toi iters = %d",
b2_toiCalls, b2_toiIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "ave toi root iters = %3.1f, max toi root iters = %d",
b2_toiRootIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
}
if (m_stepCount % 60 == 0)
{
Launch();
}
}
static Test* Create()
{
return new BulletTest;
}
b2Body* m_body;
b2Body* m_bullet;
float32 m_x;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BULLET_TEST_H
#define BULLET_TEST_H
class BulletTest : public Test
{
public:
BulletTest()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
body->CreateFixture(&edge, 0.0f);
b2PolygonShape shape;
shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
body->CreateFixture(&shape, 0.0f);
}
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 4.0f);
b2PolygonShape box;
box.SetAsBox(2.0f, 0.1f);
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&box, 1.0f);
box.SetAsBox(0.25f, 0.25f);
//m_x = RandomFloat(-1.0f, 1.0f);
m_x = 0.20352793f;
bd.position.Set(m_x, 10.0f);
bd.bullet = true;
m_bullet = m_world->CreateBody(&bd);
m_bullet->CreateFixture(&box, 100.0f);
m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
}
}
void Launch()
{
m_body->SetTransform(b2Vec2(0.0f, 4.0f), 0.0f);
m_body->SetLinearVelocity(b2Vec2_zero);
m_body->SetAngularVelocity(0.0f);
m_x = RandomFloat(-1.0f, 1.0f);
m_bullet->SetTransform(b2Vec2(m_x, 10.0f), 0.0f);
m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f));
m_bullet->SetAngularVelocity(0.0f);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
b2_gjkCalls = 0;
b2_gjkIters = 0;
b2_gjkMaxIters = 0;
b2_toiCalls = 0;
b2_toiIters = 0;
b2_toiMaxIters = 0;
b2_toiRootIters = 0;
b2_toiMaxRootIters = 0;
}
void Step(Settings* settings)
{
Test::Step(settings);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
extern int32 b2_toiCalls, b2_toiIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
if (b2_gjkCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
b2_gjkCalls, b2_gjkIters / float32(b2_gjkCalls), b2_gjkMaxIters);
m_textLine += 15;
}
if (b2_toiCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave toi iters = %3.1f, max toi iters = %d",
b2_toiCalls, b2_toiIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "ave toi root iters = %3.1f, max toi root iters = %d",
b2_toiRootIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
}
if (m_stepCount % 60 == 0)
{
Launch();
}
}
static Test* Create()
{
return new BulletTest;
}
b2Body* m_body;
b2Body* m_bullet;
float32 m_x;
};
#endif

View File

@@ -1,211 +1,211 @@
/*
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CANTILEVER_H
#define CANTILEVER_H
// It is difficult to make a cantilever made of links completely rigid with weld joints.
// You will have to use a high number of iterations to make them stiff.
// So why not go ahead and use soft weld joints? They behave like a revolute
// joint with a rotational spring.
class Cantilever : public Test
{
public:
enum
{
e_count = 8
};
Cantilever()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
jd.frequencyHz = 5.0f;
jd.dampingRatio = 0.7f;
b2Body* prevBody = ground;
for (int32 i = 0; i < 3; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.0f + 2.0f * i, 15.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 2.0f * i, 15.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-4.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
if (i > 0)
{
b2Vec2 anchor(-5.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
}
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
jd.frequencyHz = 8.0f;
jd.dampingRatio = 0.7f;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(5.5f + 1.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
if (i > 0)
{
b2Vec2 anchor(5.0f + 1.0f * i, 10.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
}
prevBody = body;
}
}
for (int32 i = 0; i < 2; ++i)
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
b2PolygonShape shape;
shape.Set(vertices, 3);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-8.0f + 8.0f * i, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
for (int32 i = 0; i < 2; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 6.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
static Test* Create()
{
return new Cantilever;
}
b2Body* m_middle;
};
#endif
/*
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CANTILEVER_H
#define CANTILEVER_H
// It is difficult to make a cantilever made of links completely rigid with weld joints.
// You will have to use a high number of iterations to make them stiff.
// So why not go ahead and use soft weld joints? They behave like a revolute
// joint with a rotational spring.
class Cantilever : public Test
{
public:
enum
{
e_count = 8
};
Cantilever()
{
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
jd.frequencyHz = 5.0f;
jd.dampingRatio = 0.7f;
b2Body* prevBody = ground;
for (int32 i = 0; i < 3; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-14.0f + 2.0f * i, 15.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(-15.0f + 2.0f * i, 15.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-4.5f + 1.0f * i, 5.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
if (i > 0)
{
b2Vec2 anchor(-5.0f + 1.0f * i, 5.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
}
prevBody = body;
}
}
{
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
b2WeldJointDef jd;
jd.frequencyHz = 8.0f;
jd.dampingRatio = 0.7f;
b2Body* prevBody = ground;
for (int32 i = 0; i < e_count; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(5.5f + 1.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
if (i > 0)
{
b2Vec2 anchor(5.0f + 1.0f * i, 10.0f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
}
prevBody = body;
}
}
for (int32 i = 0; i < 2; ++i)
{
b2Vec2 vertices[3];
vertices[0].Set(-0.5f, 0.0f);
vertices[1].Set(0.5f, 0.0f);
vertices[2].Set(0.0f, 1.5f);
b2PolygonShape shape;
shape.Set(vertices, 3);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-8.0f + 8.0f * i, 12.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
for (int32 i = 0; i < 2; ++i)
{
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-6.0f + 6.0f * i, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
static Test* Create()
{
return new Cantilever;
}
b2Body* m_middle;
};
#endif

View File

@@ -1,286 +1,286 @@
/*
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CAR_H
#define CAR_H
// This is a fun demo that shows off the wheel joint
class Car : public Test
{
public:
Car()
{
m_hz = 4.0f;
m_zeta = 0.7f;
m_speed = 50.0f;
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 0.0f;
fd.friction = 0.6f;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&fd);
float32 hs[10] = {0.25f, 1.0f, 4.0f, 0.0f, 0.0f, -1.0f, -2.0f, -2.0f, -1.25f, 0.0f};
float32 x = 20.0f, y1 = 0.0f, dx = 5.0f;
for (int32 i = 0; i < 10; ++i)
{
float32 y2 = hs[i];
shape.Set(b2Vec2(x, y1), b2Vec2(x + dx, y2));
ground->CreateFixture(&fd);
y1 = y2;
x += dx;
}
for (int32 i = 0; i < 10; ++i)
{
float32 y2 = hs[i];
shape.Set(b2Vec2(x, y1), b2Vec2(x + dx, y2));
ground->CreateFixture(&fd);
y1 = y2;
x += dx;
}
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 80.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 40.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 10.0f, 5.0f));
ground->CreateFixture(&fd);
x += 20.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 40.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x, 20.0f));
ground->CreateFixture(&fd);
}
// Teeter
{
b2BodyDef bd;
bd.position.Set(140.0f, 1.0f);
bd.type = b2_dynamicBody;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape box;
box.SetAsBox(10.0f, 0.25f);
body->CreateFixture(&box, 1.0f);
b2RevoluteJointDef jd;
jd.Initialize(ground, body, body->GetPosition());
jd.lowerAngle = -8.0f * b2_pi / 180.0f;
jd.upperAngle = 8.0f * b2_pi / 180.0f;
jd.enableLimit = true;
m_world->CreateJoint(&jd);
body->ApplyAngularImpulse(100.0f);
}
// Bridge
{
int32 N = 20;
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.6f;
b2RevoluteJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < N; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(161.0f + 2.0f * i, -0.125f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(160.0f + 2.0f * i, -0.125f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
b2Vec2 anchor(160.0f + 2.0f * N, -0.125f);
jd.Initialize(prevBody, ground, anchor);
m_world->CreateJoint(&jd);
}
// Boxes
{
b2PolygonShape box;
box.SetAsBox(0.5f, 0.5f);
b2Body* body = NULL;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(230.0f, 0.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 1.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 2.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 3.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 4.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
}
// Car
{
b2PolygonShape chassis;
b2Vec2 vertices[8];
vertices[0].Set(-1.5f, -0.5f);
vertices[1].Set(1.5f, -0.5f);
vertices[2].Set(1.5f, 0.0f);
vertices[3].Set(0.0f, 0.9f);
vertices[4].Set(-1.15f, 0.9f);
vertices[5].Set(-1.5f, 0.2f);
chassis.Set(vertices, 6);
b2CircleShape circle;
circle.m_radius = 0.4f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 1.0f);
m_car = m_world->CreateBody(&bd);
m_car->CreateFixture(&chassis, 1.0f);
b2FixtureDef fd;
fd.shape = &circle;
fd.density = 1.0f;
fd.friction = 0.9f;
bd.position.Set(-1.0f, 0.35f);
m_wheel1 = m_world->CreateBody(&bd);
m_wheel1->CreateFixture(&fd);
bd.position.Set(1.0f, 0.4f);
m_wheel2 = m_world->CreateBody(&bd);
m_wheel2->CreateFixture(&fd);
b2WheelJointDef jd;
b2Vec2 axis(0.0f, 1.0f);
jd.Initialize(m_car, m_wheel1, m_wheel1->GetPosition(), axis);
jd.motorSpeed = 0.0f;
jd.maxMotorTorque = 20.0f;
jd.enableMotor = true;
jd.frequencyHz = m_hz;
jd.dampingRatio = m_zeta;
m_spring1 = (b2WheelJoint*)m_world->CreateJoint(&jd);
jd.Initialize(m_car, m_wheel2, m_wheel2->GetPosition(), axis);
jd.motorSpeed = 0.0f;
jd.maxMotorTorque = 10.0f;
jd.enableMotor = false;
jd.frequencyHz = m_hz;
jd.dampingRatio = m_zeta;
m_spring2 = (b2WheelJoint*)m_world->CreateJoint(&jd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_spring1->SetMotorSpeed(m_speed);
break;
case 's':
m_spring1->SetMotorSpeed(0.0f);
break;
case 'd':
m_spring1->SetMotorSpeed(-m_speed);
break;
case 'q':
m_hz = b2Max(0.0f, m_hz - 1.0f);
m_spring1->SetSpringFrequencyHz(m_hz);
m_spring2->SetSpringFrequencyHz(m_hz);
break;
case 'e':
m_hz += 1.0f;
m_spring1->SetSpringFrequencyHz(m_hz);
m_spring2->SetSpringFrequencyHz(m_hz);
break;
}
}
void Step(Settings* settings)
{
m_debugDraw.DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, hz down = q, hz up = e");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "frequency = %g hz, damping ratio = %g", m_hz, m_zeta);
m_textLine += 15;
settings->viewCenter.x = m_car->GetPosition().x;
Test::Step(settings);
}
static Test* Create()
{
return new Car;
}
b2Body* m_car;
b2Body* m_wheel1;
b2Body* m_wheel2;
float32 m_hz;
float32 m_zeta;
float32 m_speed;
b2WheelJoint* m_spring1;
b2WheelJoint* m_spring2;
};
#endif
/*
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CAR_H
#define CAR_H
// This is a fun demo that shows off the wheel joint
class Car : public Test
{
public:
Car()
{
m_hz = 4.0f;
m_zeta = 0.7f;
m_speed = 50.0f;
b2Body* ground = NULL;
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 0.0f;
fd.friction = 0.6f;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&fd);
float32 hs[10] = {0.25f, 1.0f, 4.0f, 0.0f, 0.0f, -1.0f, -2.0f, -2.0f, -1.25f, 0.0f};
float32 x = 20.0f, y1 = 0.0f, dx = 5.0f;
for (int32 i = 0; i < 10; ++i)
{
float32 y2 = hs[i];
shape.Set(b2Vec2(x, y1), b2Vec2(x + dx, y2));
ground->CreateFixture(&fd);
y1 = y2;
x += dx;
}
for (int32 i = 0; i < 10; ++i)
{
float32 y2 = hs[i];
shape.Set(b2Vec2(x, y1), b2Vec2(x + dx, y2));
ground->CreateFixture(&fd);
y1 = y2;
x += dx;
}
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 80.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 40.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 10.0f, 5.0f));
ground->CreateFixture(&fd);
x += 20.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f));
ground->CreateFixture(&fd);
x += 40.0f;
shape.Set(b2Vec2(x, 0.0f), b2Vec2(x, 20.0f));
ground->CreateFixture(&fd);
}
// Teeter
{
b2BodyDef bd;
bd.position.Set(140.0f, 1.0f);
bd.type = b2_dynamicBody;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape box;
box.SetAsBox(10.0f, 0.25f);
body->CreateFixture(&box, 1.0f);
b2RevoluteJointDef jd;
jd.Initialize(ground, body, body->GetPosition());
jd.lowerAngle = -8.0f * b2_pi / 180.0f;
jd.upperAngle = 8.0f * b2_pi / 180.0f;
jd.enableLimit = true;
m_world->CreateJoint(&jd);
body->ApplyAngularImpulse(100.0f);
}
// Bridge
{
int32 N = 20;
b2PolygonShape shape;
shape.SetAsBox(1.0f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.6f;
b2RevoluteJointDef jd;
b2Body* prevBody = ground;
for (int32 i = 0; i < N; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(161.0f + 2.0f * i, -0.125f);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(160.0f + 2.0f * i, -0.125f);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
b2Vec2 anchor(160.0f + 2.0f * N, -0.125f);
jd.Initialize(prevBody, ground, anchor);
m_world->CreateJoint(&jd);
}
// Boxes
{
b2PolygonShape box;
box.SetAsBox(0.5f, 0.5f);
b2Body* body = NULL;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(230.0f, 0.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 1.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 2.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 3.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
bd.position.Set(230.0f, 4.5f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&box, 0.5f);
}
// Car
{
b2PolygonShape chassis;
b2Vec2 vertices[8];
vertices[0].Set(-1.5f, -0.5f);
vertices[1].Set(1.5f, -0.5f);
vertices[2].Set(1.5f, 0.0f);
vertices[3].Set(0.0f, 0.9f);
vertices[4].Set(-1.15f, 0.9f);
vertices[5].Set(-1.5f, 0.2f);
chassis.Set(vertices, 6);
b2CircleShape circle;
circle.m_radius = 0.4f;
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 1.0f);
m_car = m_world->CreateBody(&bd);
m_car->CreateFixture(&chassis, 1.0f);
b2FixtureDef fd;
fd.shape = &circle;
fd.density = 1.0f;
fd.friction = 0.9f;
bd.position.Set(-1.0f, 0.35f);
m_wheel1 = m_world->CreateBody(&bd);
m_wheel1->CreateFixture(&fd);
bd.position.Set(1.0f, 0.4f);
m_wheel2 = m_world->CreateBody(&bd);
m_wheel2->CreateFixture(&fd);
b2WheelJointDef jd;
b2Vec2 axis(0.0f, 1.0f);
jd.Initialize(m_car, m_wheel1, m_wheel1->GetPosition(), axis);
jd.motorSpeed = 0.0f;
jd.maxMotorTorque = 20.0f;
jd.enableMotor = true;
jd.frequencyHz = m_hz;
jd.dampingRatio = m_zeta;
m_spring1 = (b2WheelJoint*)m_world->CreateJoint(&jd);
jd.Initialize(m_car, m_wheel2, m_wheel2->GetPosition(), axis);
jd.motorSpeed = 0.0f;
jd.maxMotorTorque = 10.0f;
jd.enableMotor = false;
jd.frequencyHz = m_hz;
jd.dampingRatio = m_zeta;
m_spring2 = (b2WheelJoint*)m_world->CreateJoint(&jd);
}
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'a':
m_spring1->SetMotorSpeed(m_speed);
break;
case 's':
m_spring1->SetMotorSpeed(0.0f);
break;
case 'd':
m_spring1->SetMotorSpeed(-m_speed);
break;
case 'q':
m_hz = b2Max(0.0f, m_hz - 1.0f);
m_spring1->SetSpringFrequencyHz(m_hz);
m_spring2->SetSpringFrequencyHz(m_hz);
break;
case 'e':
m_hz += 1.0f;
m_spring1->SetSpringFrequencyHz(m_hz);
m_spring2->SetSpringFrequencyHz(m_hz);
break;
}
}
void Step(Settings* settings)
{
m_debugDraw.DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, hz down = q, hz up = e");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "frequency = %g hz, damping ratio = %g", m_hz, m_zeta);
m_textLine += 15;
settings->viewCenter.x = m_car->GetPosition().x;
Test::Step(settings);
}
static Test* Create()
{
return new Car;
}
b2Body* m_car;
b2Body* m_wheel1;
b2Body* m_wheel2;
float32 m_hz;
float32 m_zeta;
float32 m_speed;
b2WheelJoint* m_spring1;
b2WheelJoint* m_spring2;
};
#endif

View File

@@ -1,74 +1,74 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CHAIN_H
#define CHAIN_H
class Chain : public Test
{
public:
Chain()
{
b2Body* ground = {};
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.6f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
b2RevoluteJointDef jd;
jd.collideConnected = false;
const float32 y = 25.0f;
b2Body* prevBody = ground;
for (int i = 0; i < 30; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.5f + i, y);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(float32(i), y);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
}
static Test* Create()
{
return new Chain;
}
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CHAIN_H
#define CHAIN_H
class Chain : public Test
{
public:
Chain()
{
b2Body* ground = {};
{
b2BodyDef bd;
ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
{
b2PolygonShape shape;
shape.SetAsBox(0.6f, 0.125f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 0.2f;
b2RevoluteJointDef jd;
jd.collideConnected = false;
const float32 y = 25.0f;
b2Body* prevBody = ground;
for (int i = 0; i < 30; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.5f + i, y);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
b2Vec2 anchor(float32(i), y);
jd.Initialize(prevBody, body, anchor);
m_world->CreateJoint(&jd);
prevBody = body;
}
}
}
static Test* Create()
{
return new Chain;
}
};
#endif

View File

@@ -1,253 +1,253 @@
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CHARACTER_COLLISION_H
#define CHARACTER_COLLISION_H
/// This is a test of typical character collision scenarios. This does not
/// show how you should implement a character in your application.
/// Instead this is used to test smooth collision on edge chains.
class CharacterCollision : public Test
{
public:
CharacterCollision()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Collinear edges with no adjacency information.
// This shows the problematic case where a box shape can hit
// an internal vertex.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-8.0f, 1.0f), b2Vec2(-6.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(-6.0f, 1.0f), b2Vec2(-4.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(-4.0f, 1.0f), b2Vec2(-2.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Chain shape
{
b2BodyDef bd;
bd.angle = 0.25f * b2_pi;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[4];
vs[0].Set(5.0f, 7.0f);
vs[1].Set(6.0f, 8.0f);
vs[2].Set(7.0f, 8.0f);
vs[3].Set(8.0f, 7.0f);
b2ChainShape shape;
shape.CreateChain(vs, 4);
ground->CreateFixture(&shape, 0.0f);
}
// Square tiles. This shows that adjacency shapes may
// have non-smooth collision. There is no solution
// to this problem.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(1.0f, 1.0f, b2Vec2(4.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
shape.SetAsBox(1.0f, 1.0f, b2Vec2(6.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
shape.SetAsBox(1.0f, 1.0f, b2Vec2(8.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
}
// Square made from an edge loop. Collision should be smooth.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[4];
vs[0].Set(-1.0f, 3.0f);
vs[1].Set(1.0f, 3.0f);
vs[2].Set(1.0f, 5.0f);
vs[3].Set(-1.0f, 5.0f);
b2ChainShape shape;
shape.CreateLoop(vs, 4);
ground->CreateFixture(&shape, 0.0f);
}
// Edge loop. Collision should be smooth.
{
b2BodyDef bd;
bd.position.Set(-10.0f, 4.0f);
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[10];
vs[0].Set(0.0f, 0.0f);
vs[1].Set(6.0f, 0.0f);
vs[2].Set(6.0f, 2.0f);
vs[3].Set(4.0f, 1.0f);
vs[4].Set(2.0f, 2.0f);
vs[5].Set(0.0f, 2.0f);
vs[6].Set(-2.0f, 2.0f);
vs[7].Set(-4.0f, 3.0f);
vs[8].Set(-6.0f, 2.0f);
vs[9].Set(-6.0f, 0.0f);
b2ChainShape shape;
shape.CreateLoop(vs, 10);
ground->CreateFixture(&shape, 0.0f);
}
// Square character 1
{
b2BodyDef bd;
bd.position.Set(-3.0f, 8.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Square character 2
{
b2BodyDef bd;
bd.position.Set(-5.0f, 5.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.25f, 0.25f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Hexagon character
{
b2BodyDef bd;
bd.position.Set(-5.0f, 8.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
float32 angle = 0.0f;
float32 delta = b2_pi / 3.0f;
b2Vec2 vertices[6];
for (int32 i = 0; i < 6; ++i)
{
vertices[i].Set(0.5f * cosf(angle), 0.5f * sinf(angle));
angle += delta;
}
b2PolygonShape shape;
shape.Set(vertices, 6);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(3.0f, 5.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(-7.0f, 6.0f);
bd.type = b2_dynamicBody;
bd.allowSleep = false;
m_character = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.25f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 1.0f;
m_character->CreateFixture(&fd);
}
}
void Step(Settings* settings)
{
b2Vec2 v = m_character->GetLinearVelocity();
v.x = -5.0f;
m_character->SetLinearVelocity(v);
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "This tests various character collision shapes.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Limitation: square and hexagon can snag on aligned boxes.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Feature: edge chains have smooth collision inside and out.");
m_textLine += 15;
}
static Test* Create()
{
return new CharacterCollision;
}
b2Body* m_character;
};
#endif
/*
* Copyright (c) 2006-2010 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CHARACTER_COLLISION_H
#define CHARACTER_COLLISION_H
/// This is a test of typical character collision scenarios. This does not
/// show how you should implement a character in your application.
/// Instead this is used to test smooth collision on edge chains.
class CharacterCollision : public Test
{
public:
CharacterCollision()
{
// Ground body
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Collinear edges with no adjacency information.
// This shows the problematic case where a box shape can hit
// an internal vertex.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(-8.0f, 1.0f), b2Vec2(-6.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(-6.0f, 1.0f), b2Vec2(-4.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
shape.Set(b2Vec2(-4.0f, 1.0f), b2Vec2(-2.0f, 1.0f));
ground->CreateFixture(&shape, 0.0f);
}
// Chain shape
{
b2BodyDef bd;
bd.angle = 0.25f * b2_pi;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[4];
vs[0].Set(5.0f, 7.0f);
vs[1].Set(6.0f, 8.0f);
vs[2].Set(7.0f, 8.0f);
vs[3].Set(8.0f, 7.0f);
b2ChainShape shape;
shape.CreateChain(vs, 4);
ground->CreateFixture(&shape, 0.0f);
}
// Square tiles. This shows that adjacency shapes may
// have non-smooth collision. There is no solution
// to this problem.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(1.0f, 1.0f, b2Vec2(4.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
shape.SetAsBox(1.0f, 1.0f, b2Vec2(6.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
shape.SetAsBox(1.0f, 1.0f, b2Vec2(8.0f, 3.0f), 0.0f);
ground->CreateFixture(&shape, 0.0f);
}
// Square made from an edge loop. Collision should be smooth.
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[4];
vs[0].Set(-1.0f, 3.0f);
vs[1].Set(1.0f, 3.0f);
vs[2].Set(1.0f, 5.0f);
vs[3].Set(-1.0f, 5.0f);
b2ChainShape shape;
shape.CreateLoop(vs, 4);
ground->CreateFixture(&shape, 0.0f);
}
// Edge loop. Collision should be smooth.
{
b2BodyDef bd;
bd.position.Set(-10.0f, 4.0f);
b2Body* ground = m_world->CreateBody(&bd);
b2Vec2 vs[10];
vs[0].Set(0.0f, 0.0f);
vs[1].Set(6.0f, 0.0f);
vs[2].Set(6.0f, 2.0f);
vs[3].Set(4.0f, 1.0f);
vs[4].Set(2.0f, 2.0f);
vs[5].Set(0.0f, 2.0f);
vs[6].Set(-2.0f, 2.0f);
vs[7].Set(-4.0f, 3.0f);
vs[8].Set(-6.0f, 2.0f);
vs[9].Set(-6.0f, 0.0f);
b2ChainShape shape;
shape.CreateLoop(vs, 10);
ground->CreateFixture(&shape, 0.0f);
}
// Square character 1
{
b2BodyDef bd;
bd.position.Set(-3.0f, 8.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.5f, 0.5f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Square character 2
{
b2BodyDef bd;
bd.position.Set(-5.0f, 5.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape shape;
shape.SetAsBox(0.25f, 0.25f);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Hexagon character
{
b2BodyDef bd;
bd.position.Set(-5.0f, 8.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
float32 angle = 0.0f;
float32 delta = b2_pi / 3.0f;
b2Vec2 vertices[6];
for (int32 i = 0; i < 6; ++i)
{
vertices[i].Set(0.5f * cosf(angle), 0.5f * sinf(angle));
angle += delta;
}
b2PolygonShape shape;
shape.Set(vertices, 6);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(3.0f, 5.0f);
bd.type = b2_dynamicBody;
bd.fixedRotation = true;
bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.5f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
body->CreateFixture(&fd);
}
// Circle character
{
b2BodyDef bd;
bd.position.Set(-7.0f, 6.0f);
bd.type = b2_dynamicBody;
bd.allowSleep = false;
m_character = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_radius = 0.25f;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 20.0f;
fd.friction = 1.0f;
m_character->CreateFixture(&fd);
}
}
void Step(Settings* settings)
{
b2Vec2 v = m_character->GetLinearVelocity();
v.x = -5.0f;
m_character->SetLinearVelocity(v);
Test::Step(settings);
m_debugDraw.DrawString(5, m_textLine, "This tests various character collision shapes.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Limitation: square and hexagon can snag on aligned boxes.");
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Feature: edge chains have smooth collision inside and out.");
m_textLine += 15;
}
static Test* Create()
{
return new CharacterCollision;
}
b2Body* m_character;
};
#endif

View File

@@ -1,176 +1,176 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COLLISION_FILTERING_H
#define COLLISION_FILTERING_H
// This is a test of collision filtering.
// There is a triangle, a box, and a circle.
// There are 6 shapes. 3 large and 3 small.
// The 3 small ones always collide.
// The 3 large ones never collide.
// The boxes don't collide with triangles (except if both are small).
const int16 k_smallGroup = 1;
const int16 k_largeGroup = -1;
const uint16 k_defaultCategory = 0x0001;
const uint16 k_triangleCategory = 0x0002;
const uint16 k_boxCategory = 0x0004;
const uint16 k_circleCategory = 0x0008;
const uint16 k_triangleMask = 0xFFFF;
const uint16 k_boxMask = 0xFFFF ^ k_triangleCategory;
const uint16 k_circleMask = 0xFFFF;
class CollisionFiltering : public Test
{
public:
CollisionFiltering()
{
// Ground body
{
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2FixtureDef sd;
sd.shape = &shape;
sd.friction = 0.3f;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&sd);
}
// Small triangle
b2Vec2 vertices[3];
vertices[0].Set(-1.0f, 0.0f);
vertices[1].Set(1.0f, 0.0f);
vertices[2].Set(0.0f, 2.0f);
b2PolygonShape polygon;
polygon.Set(vertices, 3);
b2FixtureDef triangleShapeDef;
triangleShapeDef.shape = &polygon;
triangleShapeDef.density = 1.0f;
triangleShapeDef.filter.groupIndex = k_smallGroup;
triangleShapeDef.filter.categoryBits = k_triangleCategory;
triangleShapeDef.filter.maskBits = k_triangleMask;
b2BodyDef triangleBodyDef;
triangleBodyDef.type = b2_dynamicBody;
triangleBodyDef.position.Set(-5.0f, 2.0f);
b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
body1->CreateFixture(&triangleShapeDef);
// Large triangle (recycle definitions)
vertices[0] *= 2.0f;
vertices[1] *= 2.0f;
vertices[2] *= 2.0f;
polygon.Set(vertices, 3);
triangleShapeDef.filter.groupIndex = k_largeGroup;
triangleBodyDef.position.Set(-5.0f, 6.0f);
triangleBodyDef.fixedRotation = true; // look at me!
b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
body2->CreateFixture(&triangleShapeDef);
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-5.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape p;
p.SetAsBox(0.5f, 1.0f);
body->CreateFixture(&p, 1.0f);
b2PrismaticJointDef jd;
jd.bodyA = body2;
jd.bodyB = body;
jd.enableLimit = true;
jd.localAnchorA.Set(0.0f, 4.0f);
jd.localAnchorB.SetZero();
jd.localAxisA.Set(0.0f, 1.0f);
jd.lowerTranslation = -1.0f;
jd.upperTranslation = 1.0f;
m_world->CreateJoint(&jd);
}
// Small box
polygon.SetAsBox(1.0f, 0.5f);
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &polygon;
boxShapeDef.density = 1.0f;
boxShapeDef.restitution = 0.1f;
boxShapeDef.filter.groupIndex = k_smallGroup;
boxShapeDef.filter.categoryBits = k_boxCategory;
boxShapeDef.filter.maskBits = k_boxMask;
b2BodyDef boxBodyDef;
boxBodyDef.type = b2_dynamicBody;
boxBodyDef.position.Set(0.0f, 2.0f);
b2Body* body3 = m_world->CreateBody(&boxBodyDef);
body3->CreateFixture(&boxShapeDef);
// Large box (recycle definitions)
polygon.SetAsBox(2.0f, 1.0f);
boxShapeDef.filter.groupIndex = k_largeGroup;
boxBodyDef.position.Set(0.0f, 6.0f);
b2Body* body4 = m_world->CreateBody(&boxBodyDef);
body4->CreateFixture(&boxShapeDef);
// Small circle
b2CircleShape circle;
circle.m_radius = 1.0f;
b2FixtureDef circleShapeDef;
circleShapeDef.shape = &circle;
circleShapeDef.density = 1.0f;
circleShapeDef.filter.groupIndex = k_smallGroup;
circleShapeDef.filter.categoryBits = k_circleCategory;
circleShapeDef.filter.maskBits = k_circleMask;
b2BodyDef circleBodyDef;
circleBodyDef.type = b2_dynamicBody;
circleBodyDef.position.Set(5.0f, 2.0f);
b2Body* body5 = m_world->CreateBody(&circleBodyDef);
body5->CreateFixture(&circleShapeDef);
// Large circle
circle.m_radius *= 2.0f;
circleShapeDef.filter.groupIndex = k_largeGroup;
circleBodyDef.position.Set(5.0f, 6.0f);
b2Body* body6 = m_world->CreateBody(&circleBodyDef);
body6->CreateFixture(&circleShapeDef);
}
static Test* Create()
{
return new CollisionFiltering;
}
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COLLISION_FILTERING_H
#define COLLISION_FILTERING_H
// This is a test of collision filtering.
// There is a triangle, a box, and a circle.
// There are 6 shapes. 3 large and 3 small.
// The 3 small ones always collide.
// The 3 large ones never collide.
// The boxes don't collide with triangles (except if both are small).
const int16 k_smallGroup = 1;
const int16 k_largeGroup = -1;
const uint16 k_defaultCategory = 0x0001;
const uint16 k_triangleCategory = 0x0002;
const uint16 k_boxCategory = 0x0004;
const uint16 k_circleCategory = 0x0008;
const uint16 k_triangleMask = 0xFFFF;
const uint16 k_boxMask = 0xFFFF ^ k_triangleCategory;
const uint16 k_circleMask = 0xFFFF;
class CollisionFiltering : public Test
{
public:
CollisionFiltering()
{
// Ground body
{
b2EdgeShape shape;
shape.Set(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f));
b2FixtureDef sd;
sd.shape = &shape;
sd.friction = 0.3f;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&sd);
}
// Small triangle
b2Vec2 vertices[3];
vertices[0].Set(-1.0f, 0.0f);
vertices[1].Set(1.0f, 0.0f);
vertices[2].Set(0.0f, 2.0f);
b2PolygonShape polygon;
polygon.Set(vertices, 3);
b2FixtureDef triangleShapeDef;
triangleShapeDef.shape = &polygon;
triangleShapeDef.density = 1.0f;
triangleShapeDef.filter.groupIndex = k_smallGroup;
triangleShapeDef.filter.categoryBits = k_triangleCategory;
triangleShapeDef.filter.maskBits = k_triangleMask;
b2BodyDef triangleBodyDef;
triangleBodyDef.type = b2_dynamicBody;
triangleBodyDef.position.Set(-5.0f, 2.0f);
b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
body1->CreateFixture(&triangleShapeDef);
// Large triangle (recycle definitions)
vertices[0] *= 2.0f;
vertices[1] *= 2.0f;
vertices[2] *= 2.0f;
polygon.Set(vertices, 3);
triangleShapeDef.filter.groupIndex = k_largeGroup;
triangleBodyDef.position.Set(-5.0f, 6.0f);
triangleBodyDef.fixedRotation = true; // look at me!
b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
body2->CreateFixture(&triangleShapeDef);
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-5.0f, 10.0f);
b2Body* body = m_world->CreateBody(&bd);
b2PolygonShape p;
p.SetAsBox(0.5f, 1.0f);
body->CreateFixture(&p, 1.0f);
b2PrismaticJointDef jd;
jd.bodyA = body2;
jd.bodyB = body;
jd.enableLimit = true;
jd.localAnchorA.Set(0.0f, 4.0f);
jd.localAnchorB.SetZero();
jd.localAxisA.Set(0.0f, 1.0f);
jd.lowerTranslation = -1.0f;
jd.upperTranslation = 1.0f;
m_world->CreateJoint(&jd);
}
// Small box
polygon.SetAsBox(1.0f, 0.5f);
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &polygon;
boxShapeDef.density = 1.0f;
boxShapeDef.restitution = 0.1f;
boxShapeDef.filter.groupIndex = k_smallGroup;
boxShapeDef.filter.categoryBits = k_boxCategory;
boxShapeDef.filter.maskBits = k_boxMask;
b2BodyDef boxBodyDef;
boxBodyDef.type = b2_dynamicBody;
boxBodyDef.position.Set(0.0f, 2.0f);
b2Body* body3 = m_world->CreateBody(&boxBodyDef);
body3->CreateFixture(&boxShapeDef);
// Large box (recycle definitions)
polygon.SetAsBox(2.0f, 1.0f);
boxShapeDef.filter.groupIndex = k_largeGroup;
boxBodyDef.position.Set(0.0f, 6.0f);
b2Body* body4 = m_world->CreateBody(&boxBodyDef);
body4->CreateFixture(&boxShapeDef);
// Small circle
b2CircleShape circle;
circle.m_radius = 1.0f;
b2FixtureDef circleShapeDef;
circleShapeDef.shape = &circle;
circleShapeDef.density = 1.0f;
circleShapeDef.filter.groupIndex = k_smallGroup;
circleShapeDef.filter.categoryBits = k_circleCategory;
circleShapeDef.filter.maskBits = k_circleMask;
b2BodyDef circleBodyDef;
circleBodyDef.type = b2_dynamicBody;
circleBodyDef.position.Set(5.0f, 2.0f);
b2Body* body5 = m_world->CreateBody(&circleBodyDef);
body5->CreateFixture(&circleShapeDef);
// Large circle
circle.m_radius *= 2.0f;
circleShapeDef.filter.groupIndex = k_largeGroup;
circleBodyDef.position.Set(5.0f, 6.0f);
b2Body* body6 = m_world->CreateBody(&circleBodyDef);
body6->CreateFixture(&circleShapeDef);
}
static Test* Create()
{
return new CollisionFiltering;
}
};
#endif

View File

@@ -1,188 +1,188 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COLLISION_PROCESSING_H
#define COLLISION_PROCESSING_H
#include <algorithm>
// This test shows collision processing and tests
// deferred body destruction.
class CollisionProcessing : public Test
{
public:
CollisionProcessing()
{
// Ground body
{
b2EdgeShape shape;
shape.Set(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f));
b2FixtureDef sd;
sd.shape = &shape;;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&sd);
}
float32 xLo = -5.0f, xHi = 5.0f;
float32 yLo = 2.0f, yHi = 35.0f;
// Small triangle
b2Vec2 vertices[3];
vertices[0].Set(-1.0f, 0.0f);
vertices[1].Set(1.0f, 0.0f);
vertices[2].Set(0.0f, 2.0f);
b2PolygonShape polygon;
polygon.Set(vertices, 3);
b2FixtureDef triangleShapeDef;
triangleShapeDef.shape = &polygon;
triangleShapeDef.density = 1.0f;
b2BodyDef triangleBodyDef;
triangleBodyDef.type = b2_dynamicBody;
triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
body1->CreateFixture(&triangleShapeDef);
// Large triangle (recycle definitions)
vertices[0] *= 2.0f;
vertices[1] *= 2.0f;
vertices[2] *= 2.0f;
polygon.Set(vertices, 3);
triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
body2->CreateFixture(&triangleShapeDef);
// Small box
polygon.SetAsBox(1.0f, 0.5f);
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &polygon;
boxShapeDef.density = 1.0f;
b2BodyDef boxBodyDef;
boxBodyDef.type = b2_dynamicBody;
boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body3 = m_world->CreateBody(&boxBodyDef);
body3->CreateFixture(&boxShapeDef);
// Large box (recycle definitions)
polygon.SetAsBox(2.0f, 1.0f);
boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body4 = m_world->CreateBody(&boxBodyDef);
body4->CreateFixture(&boxShapeDef);
// Small circle
b2CircleShape circle;
circle.m_radius = 1.0f;
b2FixtureDef circleShapeDef;
circleShapeDef.shape = &circle;
circleShapeDef.density = 1.0f;
b2BodyDef circleBodyDef;
circleBodyDef.type = b2_dynamicBody;
circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body5 = m_world->CreateBody(&circleBodyDef);
body5->CreateFixture(&circleShapeDef);
// Large circle
circle.m_radius *= 2.0f;
circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body6 = m_world->CreateBody(&circleBodyDef);
body6->CreateFixture(&circleShapeDef);
}
void Step(Settings* settings)
{
Test::Step(settings);
// We are going to destroy some bodies according to contact
// points. We must buffer the bodies that should be destroyed
// because they may belong to multiple contact points.
const int32 k_maxNuke = 6;
b2Body* nuke[k_maxNuke];
int32 nukeCount = 0;
// Traverse the contact results. Destroy bodies that
// are touching heavier bodies.
for (int32 i = 0; i < m_pointCount; ++i)
{
ContactPoint* point = m_points + i;
b2Body* body1 = point->fixtureA->GetBody();
b2Body* body2 = point->fixtureB->GetBody();
float32 mass1 = body1->GetMass();
float32 mass2 = body2->GetMass();
if (mass1 > 0.0f && mass2 > 0.0f)
{
if (mass2 > mass1)
{
nuke[nukeCount++] = body1;
}
else
{
nuke[nukeCount++] = body2;
}
if (nukeCount == k_maxNuke)
{
break;
}
}
}
// Sort the nuke array to group duplicates.
std::sort(nuke, nuke + nukeCount);
// Destroy the bodies, skipping duplicates.
int32 i = 0;
while (i < nukeCount)
{
b2Body* b = nuke[i++];
while (i < nukeCount && nuke[i] == b)
{
++i;
}
if (b != m_bomb)
{
m_world->DestroyBody(b);
}
}
}
static Test* Create()
{
return new CollisionProcessing;
}
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COLLISION_PROCESSING_H
#define COLLISION_PROCESSING_H
#include <algorithm>
// This test shows collision processing and tests
// deferred body destruction.
class CollisionProcessing : public Test
{
public:
CollisionProcessing()
{
// Ground body
{
b2EdgeShape shape;
shape.Set(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f));
b2FixtureDef sd;
sd.shape = &shape;;
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
ground->CreateFixture(&sd);
}
float32 xLo = -5.0f, xHi = 5.0f;
float32 yLo = 2.0f, yHi = 35.0f;
// Small triangle
b2Vec2 vertices[3];
vertices[0].Set(-1.0f, 0.0f);
vertices[1].Set(1.0f, 0.0f);
vertices[2].Set(0.0f, 2.0f);
b2PolygonShape polygon;
polygon.Set(vertices, 3);
b2FixtureDef triangleShapeDef;
triangleShapeDef.shape = &polygon;
triangleShapeDef.density = 1.0f;
b2BodyDef triangleBodyDef;
triangleBodyDef.type = b2_dynamicBody;
triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body1 = m_world->CreateBody(&triangleBodyDef);
body1->CreateFixture(&triangleShapeDef);
// Large triangle (recycle definitions)
vertices[0] *= 2.0f;
vertices[1] *= 2.0f;
vertices[2] *= 2.0f;
polygon.Set(vertices, 3);
triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body2 = m_world->CreateBody(&triangleBodyDef);
body2->CreateFixture(&triangleShapeDef);
// Small box
polygon.SetAsBox(1.0f, 0.5f);
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &polygon;
boxShapeDef.density = 1.0f;
b2BodyDef boxBodyDef;
boxBodyDef.type = b2_dynamicBody;
boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body3 = m_world->CreateBody(&boxBodyDef);
body3->CreateFixture(&boxShapeDef);
// Large box (recycle definitions)
polygon.SetAsBox(2.0f, 1.0f);
boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body4 = m_world->CreateBody(&boxBodyDef);
body4->CreateFixture(&boxShapeDef);
// Small circle
b2CircleShape circle;
circle.m_radius = 1.0f;
b2FixtureDef circleShapeDef;
circleShapeDef.shape = &circle;
circleShapeDef.density = 1.0f;
b2BodyDef circleBodyDef;
circleBodyDef.type = b2_dynamicBody;
circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body5 = m_world->CreateBody(&circleBodyDef);
body5->CreateFixture(&circleShapeDef);
// Large circle
circle.m_radius *= 2.0f;
circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi));
b2Body* body6 = m_world->CreateBody(&circleBodyDef);
body6->CreateFixture(&circleShapeDef);
}
void Step(Settings* settings)
{
Test::Step(settings);
// We are going to destroy some bodies according to contact
// points. We must buffer the bodies that should be destroyed
// because they may belong to multiple contact points.
const int32 k_maxNuke = 6;
b2Body* nuke[k_maxNuke];
int32 nukeCount = 0;
// Traverse the contact results. Destroy bodies that
// are touching heavier bodies.
for (int32 i = 0; i < m_pointCount; ++i)
{
ContactPoint* point = m_points + i;
b2Body* body1 = point->fixtureA->GetBody();
b2Body* body2 = point->fixtureB->GetBody();
float32 mass1 = body1->GetMass();
float32 mass2 = body2->GetMass();
if (mass1 > 0.0f && mass2 > 0.0f)
{
if (mass2 > mass1)
{
nuke[nukeCount++] = body1;
}
else
{
nuke[nukeCount++] = body2;
}
if (nukeCount == k_maxNuke)
{
break;
}
}
}
// Sort the nuke array to group duplicates.
std::sort(nuke, nuke + nukeCount);
// Destroy the bodies, skipping duplicates.
int32 i = 0;
while (i < nukeCount)
{
b2Body* b = nuke[i++];
while (i < nukeCount && nuke[i] == b)
{
++i;
}
if (b != m_bomb)
{
m_world->DestroyBody(b);
}
}
}
static Test* Create()
{
return new CollisionProcessing;
}
};
#endif

View File

@@ -1,143 +1,143 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COMPOUND_SHAPES_H
#define COMPOUND_SHAPES_H
// TODO_ERIN test joints on compounds.
class CompoundShapes : public Test
{
public:
CompoundShapes()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f));
body->CreateFixture(&shape, 0.0f);
}
{
b2CircleShape circle1;
circle1.m_radius = 0.5f;
circle1.m_p.Set(-0.5f, 0.5f);
b2CircleShape circle2;
circle2.m_radius = 0.5f;
circle2.m_p.Set(0.5f, 0.5f);
for (int i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x + 5.0f, 1.05f + 2.5f * i);
bd.angle = RandomFloat(-b2_pi, b2_pi);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&circle1, 2.0f);
body->CreateFixture(&circle2, 0.0f);
}
}
{
b2PolygonShape polygon1;
polygon1.SetAsBox(0.25f, 0.5f);
b2PolygonShape polygon2;
polygon2.SetAsBox(0.25f, 0.5f, b2Vec2(0.0f, -0.5f), 0.5f * b2_pi);
for (int i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x - 5.0f, 1.05f + 2.5f * i);
bd.angle = RandomFloat(-b2_pi, b2_pi);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&polygon1, 2.0f);
body->CreateFixture(&polygon2, 2.0f);
}
}
{
b2Transform xf1;
xf1.q.Set(0.3524f * b2_pi);
xf1.p = xf1.q.GetXAxis();
b2Vec2 vertices[3];
b2PolygonShape triangle1;
vertices[0] = b2Mul(xf1, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf1, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf1, b2Vec2(0.0f, 0.5f));
triangle1.Set(vertices, 3);
b2Transform xf2;
xf2.q.Set(-0.3524f * b2_pi);
xf2.p = -xf2.q.GetXAxis();
b2PolygonShape triangle2;
vertices[0] = b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf2, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf2, b2Vec2(0.0f, 0.5f));
triangle2.Set(vertices, 3);
for (int32 i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x, 2.05f + 2.5f * i);
bd.angle = 0.0f;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&triangle1, 2.0f);
body->CreateFixture(&triangle2, 2.0f);
}
}
{
b2PolygonShape bottom;
bottom.SetAsBox( 1.5f, 0.15f );
b2PolygonShape left;
left.SetAsBox(0.15f, 2.7f, b2Vec2(-1.45f, 2.35f), 0.2f);
b2PolygonShape right;
right.SetAsBox(0.15f, 2.7f, b2Vec2(1.45f, 2.35f), -0.2f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set( 0.0f, 2.0f );
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&bottom, 4.0f);
body->CreateFixture(&left, 4.0f);
body->CreateFixture(&right, 4.0f);
}
}
static Test* Create()
{
return new CompoundShapes;
}
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef COMPOUND_SHAPES_H
#define COMPOUND_SHAPES_H
// TODO_ERIN test joints on compounds.
class CompoundShapes : public Test
{
public:
CompoundShapes()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape shape;
shape.Set(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f));
body->CreateFixture(&shape, 0.0f);
}
{
b2CircleShape circle1;
circle1.m_radius = 0.5f;
circle1.m_p.Set(-0.5f, 0.5f);
b2CircleShape circle2;
circle2.m_radius = 0.5f;
circle2.m_p.Set(0.5f, 0.5f);
for (int i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x + 5.0f, 1.05f + 2.5f * i);
bd.angle = RandomFloat(-b2_pi, b2_pi);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&circle1, 2.0f);
body->CreateFixture(&circle2, 0.0f);
}
}
{
b2PolygonShape polygon1;
polygon1.SetAsBox(0.25f, 0.5f);
b2PolygonShape polygon2;
polygon2.SetAsBox(0.25f, 0.5f, b2Vec2(0.0f, -0.5f), 0.5f * b2_pi);
for (int i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x - 5.0f, 1.05f + 2.5f * i);
bd.angle = RandomFloat(-b2_pi, b2_pi);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&polygon1, 2.0f);
body->CreateFixture(&polygon2, 2.0f);
}
}
{
b2Transform xf1;
xf1.q.Set(0.3524f * b2_pi);
xf1.p = xf1.q.GetXAxis();
b2Vec2 vertices[3];
b2PolygonShape triangle1;
vertices[0] = b2Mul(xf1, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf1, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf1, b2Vec2(0.0f, 0.5f));
triangle1.Set(vertices, 3);
b2Transform xf2;
xf2.q.Set(-0.3524f * b2_pi);
xf2.p = -xf2.q.GetXAxis();
b2PolygonShape triangle2;
vertices[0] = b2Mul(xf2, b2Vec2(-1.0f, 0.0f));
vertices[1] = b2Mul(xf2, b2Vec2(1.0f, 0.0f));
vertices[2] = b2Mul(xf2, b2Vec2(0.0f, 0.5f));
triangle2.Set(vertices, 3);
for (int32 i = 0; i < 10; ++i)
{
float32 x = RandomFloat(-0.1f, 0.1f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(x, 2.05f + 2.5f * i);
bd.angle = 0.0f;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&triangle1, 2.0f);
body->CreateFixture(&triangle2, 2.0f);
}
}
{
b2PolygonShape bottom;
bottom.SetAsBox( 1.5f, 0.15f );
b2PolygonShape left;
left.SetAsBox(0.15f, 2.7f, b2Vec2(-1.45f, 2.35f), 0.2f);
b2PolygonShape right;
right.SetAsBox(0.15f, 2.7f, b2Vec2(1.45f, 2.35f), -0.2f);
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set( 0.0f, 2.0f );
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&bottom, 4.0f);
body->CreateFixture(&left, 4.0f);
body->CreateFixture(&right, 4.0f);
}
}
static Test* Create()
{
return new CompoundShapes;
}
};
#endif

View File

@@ -1,167 +1,167 @@
/*
* Copyright (c) 2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CONFINED_H
#define CONFINED_H
class Confined : public Test
{
public:
enum
{
e_columnCount = 0,
e_rowCount = 0
};
Confined()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
// Floor
shape.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
// Left wall
shape.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(-10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
// Right wall
shape.Set(b2Vec2(10.0f, 0.0f), b2Vec2(10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
// Roof
shape.Set(b2Vec2(-10.0f, 20.0f), b2Vec2(10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
}
float32 radius = 0.5f;
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = radius;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.1f;
for (int32 j = 0; j < e_columnCount; ++j)
{
for (int i = 0; i < e_rowCount; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + (2.1f * j + 1.0f + 0.01f * i) * radius, (2.0f * i + 1.0f) * radius);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
m_world->SetGravity(b2Vec2(0.0f, 0.0f));
}
void CreateCircle()
{
float32 radius = 2.0f;
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = radius;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.0f;
b2Vec2 p(RandomFloat(), 3.0f + RandomFloat());
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = p;
//bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'c':
CreateCircle();
break;
}
}
void Step(Settings* settings)
{
bool sleeping = true;
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() != b2_dynamicBody)
{
continue;
}
if (b->IsAwake())
{
sleeping = false;
}
}
if (m_stepCount == 180)
{
m_stepCount += 0;
}
//if (sleeping)
//{
// CreateCircle();
//}
Test::Step(settings);
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() != b2_dynamicBody)
{
continue;
}
b2Vec2 p = b->GetPosition();
if (p.x <= -10.0f || 10.0f <= p.x || p.y <= 0.0f || 20.0f <= p.y)
{
p.x += 0.0;
}
}
m_debugDraw.DrawString(5, m_textLine, "Press 'c' to create a circle.");
m_textLine += 15;
}
static Test* Create()
{
return new Confined;
}
};
#endif
/*
* Copyright (c) 2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CONFINED_H
#define CONFINED_H
class Confined : public Test
{
public:
enum
{
e_columnCount = 0,
e_rowCount = 0
};
Confined()
{
{
b2BodyDef bd;
b2Body* ground = m_world->CreateBody(&bd);
b2EdgeShape shape;
// Floor
shape.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
ground->CreateFixture(&shape, 0.0f);
// Left wall
shape.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(-10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
// Right wall
shape.Set(b2Vec2(10.0f, 0.0f), b2Vec2(10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
// Roof
shape.Set(b2Vec2(-10.0f, 20.0f), b2Vec2(10.0f, 20.0f));
ground->CreateFixture(&shape, 0.0f);
}
float32 radius = 0.5f;
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = radius;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.1f;
for (int32 j = 0; j < e_columnCount; ++j)
{
for (int i = 0; i < e_rowCount; ++i)
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(-10.0f + (2.1f * j + 1.0f + 0.01f * i) * radius, (2.0f * i + 1.0f) * radius);
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
}
m_world->SetGravity(b2Vec2(0.0f, 0.0f));
}
void CreateCircle()
{
float32 radius = 2.0f;
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = radius;
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.0f;
b2Vec2 p(RandomFloat(), 3.0f + RandomFloat());
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position = p;
//bd.allowSleep = false;
b2Body* body = m_world->CreateBody(&bd);
body->CreateFixture(&fd);
}
void Keyboard(unsigned char key)
{
switch (key)
{
case 'c':
CreateCircle();
break;
}
}
void Step(Settings* settings)
{
bool sleeping = true;
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() != b2_dynamicBody)
{
continue;
}
if (b->IsAwake())
{
sleeping = false;
}
}
if (m_stepCount == 180)
{
m_stepCount += 0;
}
//if (sleeping)
//{
// CreateCircle();
//}
Test::Step(settings);
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() != b2_dynamicBody)
{
continue;
}
b2Vec2 p = b->GetPosition();
if (p.x <= -10.0f || 10.0f <= p.x || p.y <= 0.0f || 20.0f <= p.y)
{
p.x += 0.0;
}
}
m_debugDraw.DrawString(5, m_textLine, "Press 'c' to create a circle.");
m_textLine += 15;
}
static Test* Create()
{
return new Confined;
}
};
#endif

View File

@@ -1,137 +1,137 @@
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CONTINUOUS_TEST_H
#define CONTINUOUS_TEST_H
class ContinuousTest : public Test
{
public:
ContinuousTest()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
body->CreateFixture(&edge, 0.0f);
b2PolygonShape shape;
shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
body->CreateFixture(&shape, 0.0f);
}
#if 1
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 20.0f);
//bd.angle = 0.1f;
b2PolygonShape shape;
shape.SetAsBox(2.0f, 0.1f);
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&shape, 1.0f);
m_angularVelocity = RandomFloat(-50.0f, 50.0f);
//m_angularVelocity = 46.661274f;
m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
m_body->SetAngularVelocity(m_angularVelocity);
}
#else
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 2.0f);
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = 0.5f;
body->CreateFixture(&shape, 1.0f);
bd.bullet = true;
bd.position.Set(0.0f, 10.0f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
}
#endif
}
void Launch()
{
m_body->SetTransform(b2Vec2(0.0f, 20.0f), 0.0f);
m_angularVelocity = RandomFloat(-50.0f, 50.0f);
m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
m_body->SetAngularVelocity(m_angularVelocity);
}
void Step(Settings* settings)
{
if (m_stepCount == 12)
{
m_stepCount += 0;
}
Test::Step(settings);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
if (b2_gjkCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
b2_gjkCalls, b2_gjkIters / float32(b2_gjkCalls), b2_gjkMaxIters);
m_textLine += 15;
}
extern int32 b2_toiCalls, b2_toiIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
if (b2_toiCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave toi iters = %3.1f, max toi iters = %d",
b2_toiCalls, b2_toiIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "ave toi root iters = %3.1f, max toi root iters = %d",
b2_toiRootIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
}
if (m_stepCount % 60 == 0)
{
//Launch();
}
}
static Test* Create()
{
return new ContinuousTest;
}
b2Body* m_body;
float32 m_angularVelocity;
};
#endif
/*
* Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef CONTINUOUS_TEST_H
#define CONTINUOUS_TEST_H
class ContinuousTest : public Test
{
public:
ContinuousTest()
{
{
b2BodyDef bd;
bd.position.Set(0.0f, 0.0f);
b2Body* body = m_world->CreateBody(&bd);
b2EdgeShape edge;
edge.Set(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f));
body->CreateFixture(&edge, 0.0f);
b2PolygonShape shape;
shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f);
body->CreateFixture(&shape, 0.0f);
}
#if 1
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 20.0f);
//bd.angle = 0.1f;
b2PolygonShape shape;
shape.SetAsBox(2.0f, 0.1f);
m_body = m_world->CreateBody(&bd);
m_body->CreateFixture(&shape, 1.0f);
m_angularVelocity = RandomFloat(-50.0f, 50.0f);
//m_angularVelocity = 46.661274f;
m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
m_body->SetAngularVelocity(m_angularVelocity);
}
#else
{
b2BodyDef bd;
bd.type = b2_dynamicBody;
bd.position.Set(0.0f, 2.0f);
b2Body* body = m_world->CreateBody(&bd);
b2CircleShape shape;
shape.m_p.SetZero();
shape.m_radius = 0.5f;
body->CreateFixture(&shape, 1.0f);
bd.bullet = true;
bd.position.Set(0.0f, 10.0f);
body = m_world->CreateBody(&bd);
body->CreateFixture(&shape, 1.0f);
body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
}
#endif
}
void Launch()
{
m_body->SetTransform(b2Vec2(0.0f, 20.0f), 0.0f);
m_angularVelocity = RandomFloat(-50.0f, 50.0f);
m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f));
m_body->SetAngularVelocity(m_angularVelocity);
}
void Step(Settings* settings)
{
if (m_stepCount == 12)
{
m_stepCount += 0;
}
Test::Step(settings);
extern int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
if (b2_gjkCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d",
b2_gjkCalls, b2_gjkIters / float32(b2_gjkCalls), b2_gjkMaxIters);
m_textLine += 15;
}
extern int32 b2_toiCalls, b2_toiIters;
extern int32 b2_toiRootIters, b2_toiMaxRootIters;
if (b2_toiCalls > 0)
{
m_debugDraw.DrawString(5, m_textLine, "toi calls = %d, ave toi iters = %3.1f, max toi iters = %d",
b2_toiCalls, b2_toiIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "ave toi root iters = %3.1f, max toi root iters = %d",
b2_toiRootIters / float32(b2_toiCalls), b2_toiMaxRootIters);
m_textLine += 15;
}
if (m_stepCount % 60 == 0)
{
//Launch();
}
}
static Test* Create()
{
return new ContinuousTest;
}
b2Body* m_body;
float32 m_angularVelocity;
};
#endif

Some files were not shown because too many files have changed in this diff Show More