В демо модель астероида (сфера) падает на ладшафт сгенерированный по карте высот.
Сборка производилась в gentoo c Qt 4.7.2. Для сборки вам нужно будет собрать Qt Quick 3D, подробности тут.
Так же необходимо подправить в файле qt3dbulletdemo.pro пути для переменных окружения INCLUDEPATH и LIBS.

1/********************************************************************************
2* *
3* Части заголовочного файла glviewer.h *
4* *
5********************************************************************************/
6// Подключаем заголовки Qt/3D.
7#include
8#include
9#include
10
11// Подключаем заголовки Bullet.
12#include
13#include
14
15// Это класс вывода отладки используемый Bullet, его я комментировать не буду,
16// там и так все просто.
17#include "debugdrawer.h"
18
19...
20
21class GLViewer : public QGLView
22{
23
24...
25
26private:
27// Это карта высот с вычисленными высотами.
28 QVector<float> heightMapRaw;
29
30// Основной узел графа графической сцены.
31 QGLSceneNode *scene;
32
33// Параметры источника света.
34 QGLLightParameters *lightParameters;
35
36// Отрисовщик отладки
37 DebugDrawer *debuger;
38
39// Экземпляры основных частей движка физики.
40 btBroadphaseInterface *broadphase;
41 btDefaultCollisionConfiguration *collisionConfiguration;
42 btCollisionDispatcher *dispatcher;
43 btSequentialImpulseConstraintSolver *solver;
44
45// Собственно мир физики.
46 btDiscreteDynamicsWorld *dynamicsWorld;
47
48// Форма представлия ландшафта в физическом движке, генерируется по карте высот.
49 btHeightfieldTerrainShape *landscapeShape;
50
51// Состояние трасформаций ландшафта в физическом движке.
52 btDefaultMotionState* landscapeMotionState;
53
54// Объект твердого тела для ладшафта
55 btRigidBody *landscapeRigidBody;
56
57// Узел с графическим представлением астероида.
58 QGLSceneNode *ballNode;
59
60// Аналогично ладшафту
61 btSphereShape *ballShape;
62 btDefaultMotionState* ballMotionState;
63 btRigidBody *ballRigidBody;
64...
65
66};
67
68
69
70
71/********************************************************************************
72* *
73* Исходный код glviewer.cpp *
74* *
75********************************************************************************/
76
77#include
78
79#include "glviewer.h"
80#include "callbacktriangle.h"
81
82GLViewer::GLViewer(QWidget *parent) :
83 QGLView(parent)
84{
85// Использовать отладочную отрисовку физики.
86 enabledDebuger = false;
87
88// Сила тяжести мира
89 btScalar g = 9.80665;
90
91// Инициализация движка физики.
92 broadphase = new btDbvtBroadphase();
93 collisionConfiguration = new btDefaultCollisionConfiguration();
94 dispatcher = new btCollisionDispatcher(collisionConfiguration);
95 solver = new btSequentialImpulseConstraintSolver();
96 dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,
97 broadphase,
98 solver,
99 collisionConfiguration
100 );
101 dynamicsWorld->setGravity(btVector3(0, -g, 0));
102
103// Подключение отрисовщика отладки.
104 debuger = new DebugDrawer();
105 dynamicsWorld->setDebugDrawer(debuger);
106
107// Режим отладки.
108 debuger->setDebugMode(65535);
109
110// Создаем главный узел сцены.
111 scene = new QGLSceneNode;
112
113// Создаем параметры главного источника сцены.
114 lightParameters = new QGLLightParameters();
115
116// Иницилизируем переменные статуса включения наших объектов.
117 enabledBall = false;
118 enabledLandscape = false;
119
120// Создаем астероид
121 CreateBall();
122
123// Создаем ландшафт
124 CreateLandscape();
125
126// Запускаем таймер анимации
127 idTimer = startTimer(1);
128}
129
130
131GLViewer::~GLViewer()
132{
133// Удаляем таймер анимации.
134 killTimer(idTimer);
135
136// Удаляем сцену со всеми объектами.
137 delete scene;
138
139// Удаляем параметры основного источника света.
140 delete lightParameters;
141
142// Если был создан астероид удаляем его объекты физики.
143 if(enabledBall)
144 {
145 delete ballMotionState;
146 delete ballRigidBody;
147 delete ballShape;
148 }
149
150// Если был создан ландшафт удаляем его объекты физики.
151 if(enabledLandscape)
152 {
153 delete landscapeMotionState;
154 delete landscapeRigidBody;
155 delete landscapeShape;
156 }
157
158// Удаляем основные объекты физики.
159 delete dynamicsWorld;
160 delete solver;
161 delete dispatcher;
162 delete collisionConfiguration;
163 delete broadphase;
164 delete debuger;
165}
166
167void GLViewer::CreateBall()
168{
169// Основные значеня для астероида. Масса, радиус, начальная позиция, инерция, цвет.
170 float mass = 1000.0;
171 float radius = 200.0;
172 btVector3 startPosition(0, 1500.0, 0);
173 btVector3 inertia(0, 0, 0);
174 QColor color(Qt::red);
175
176// Создаем сферу описывающую астероид в движке физики.
177 ballShape = new btSphereShape(radius);
178
179// Вычисляем инерцию объекта.
180 ballShape->calculateLocalInertia(mass, inertia);
181
182// Создаем объект состояния движения астероида, инициализируем заданной позицией.
183 ballMotionState = new btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, 1.0),
184 startPosition)
185 );
186
187// Создаем структуру группирующую информацию об объекте.
188 btRigidBody::btRigidBodyConstructionInfo ballRigidBodyInfo(mass,
189 ballMotionState,
190 ballShape
191 );
192// На основе вычисленной и собранной информации создаем модель твердого тела астероида.
193 ballRigidBody = new btRigidBody(ballRigidBodyInfo);
194
195// Создаем сборщика графического представления астероида.
196 QGLBuilder ball;
197
198// Вычисляем диаметр и создаем сферу.
199 ball << QGLSphere(radius * 2.0);
200
201// Строем узел со сферой.
202 ballNode = ball.finalizedSceneNode();
203
204// Создаем материал для астероида (удаляетсяавтоматически при удалении сцены),
205// устанавливаем цвет.
206 QGLMaterial *material = new QGLMaterial;
207 material->setDiffuseColor(color);
208
209// Применяем к узлу.
210 ballNode->setMaterial(material);
211
212// Добавляем в сцену графическое представление астероида.
213 scene->addNode(ballNode);
214
215// Добавляем астероид в мир моделирования физики.
216 dynamicsWorld->addRigidBody(ballRigidBody);
217
218// Статус использования астероида устанавливаем в истинну.
219 enabledBall = true;
220}
221
222void GLViewer::CreateLandscape()
223{
224// Создаем ландшафт. Используем карту высот сгенерированную в граф. редакторе.
225 QImage heightMap(":/files/hm.png");
226
227// Устанавливаем шаг сетки и множитель высоты.
228 qreal scaleMesh = 10.0;
229 qreal scaleHeight = 3.0;
230
231// Цвет ландшафта.
232 QColor color(Qt::gray);
233
234// Вычисляем минимальные и максимальные координаты ладшафта по X и Z.
235 qreal minimumX = - scaleMesh * heightMap.width() / 2.0;
236 qreal maximumX = - minimumX;
237
238 qreal minimumZ = - scaleMesh * heightMap.height() / 2.0;
239 qreal maximumZ = - minimumZ;
240
241// Для нахождения минимума и максимума Y (высота) инициализируем переменные.
242 qreal minimumY = scaleHeight * QColor(heightMap.pixel(0, 0)).red();
243 qreal maximumY = scaleHeight * QColor(heightMap.pixel(0, 0)).red();
244
245// Создаем карту высоты с вычисленной высотой.
246 for(int y = 0; y < heightMap.height(); y++)
247 for(int x = 0; x < heightMap.width(); x++)
248 {
249 int height = scaleHeight * QColor(heightMap.pixel(x, y)).red();
250
251// Заодно ищем минимум и максимум по Y.
252 if(minimumY > height) minimumY = height;
253 if(maximumY < height) maximumY = height;
254
255// Добавляем высоту в карту.
256 heightMapRaw.append(height);
257 }
258
259// Создаем физическое представление ландшафта по карте высот.
260 landscapeShape = new btHeightfieldTerrainShape(
261 heightMap.width(), heightMap.height(),
262 heightMapRaw.data(),
263 1.0, // при PHY_FLOAT игнорируется
264 minimumY,
265 maximumY,
266 1,
267 PHY_FLOAT,
268 true
269 );
270
271// Устанавливаем шаг сетки, высоту (Y) не трогаем, так как уже все посчитанно.
272 landscapeShape->setLocalScaling(btVector3(scaleMesh, 1.0, scaleMesh));
273
274// Создаем объект обратного вызова для создания треугольника.
275 CallBackTriangle callBackTriangle;
276
277// Запускаем процесс прохода по всей карте высот.
278// В обратном вызове добавляем вершины в массив, для создания сетки ландшафта.
279 landscapeShape->processAllTriangles(
280 &callBackTriangle,
281 btVector3(minimumX, minimumY, minimumZ),
282 btVector3(maximumX, maximumY, maximumZ)
283 );
284
285// Создаем физическое представление ландшафта.
286 landscapeMotionState = new btDefaultMotionState(btTransform(
287 btQuaternion(0, 0, 0, 1.0),
288 btVector3(0, 0, 0)
289 )
290 );
291 btRigidBody::btRigidBodyConstructionInfo rigidBodyInfo(0,
292 landscapeMotionState,
293 landscapeShape
294 );
295 landscapeRigidBody = new btRigidBody(rigidBodyInfo);
296
297// Создаем сборщик ландшафта в граф. представлении.
298 QGLBuilder landscape;
299
300// Добавляем вычисленные треугольники.
301 landscape.addTriangles(callBackTriangle.GetData());
302
303// Создаем узел.
304 QGLSceneNode *landscapeNode = landscape.finalizedSceneNode();
305
306// Добавляем в сцену.
307 scene->addNode(landscapeNode);
308
309// Создаем материал устанавливаем цвет и добавляем в узел.
310 QGLMaterial *material = new QGLMaterial;
311 material->setDiffuseColor(color);
312 landscapeNode->setMaterial(material);
313
314// Добавляем физическое представления в мир физики.
315 dynamicsWorld->addRigidBody(landscapeRigidBody);
316
317 enabledLandscape = true;
318}
319
320void GLViewer::initializeGL(QGLPainter *painter)
321{
322// Инициализируем cвет.
323 lightParameters->setPosition(QVector3D(0, 300.0, 0));
324 lightParameters->setDiffuseColor(Qt::white);
325
326// Включаем его.
327 painter->setMainLight(lightParameters);
328
329// По умолчанию используем материалы для вычисления эффектов.
330 painter->setStandardEffect(QGL::LitMaterial);
331
332// Задаем позицию камеры.
333 camera()->setEye(QVector3D(2000.0, 1500.0, 500.0));
334}
335
336void GLViewer::paintGL(QGLPainter *painter)
337{
338// Расчитываем проекцию.
339 QMatrix4x4 projection;
340 projection.perspective(60.0, (qreal)width()/height(), 1.0, 6000.0);
341 painter->projectionMatrix() = projection;
342
343// Запускаем шаг вычисления физики.
344 dynamicsWorld->stepSimulation(1.0/60, 10);
345
346// Если есть астероид.
347 if(enabledBall)
348 {
349// То получаем текущую позицию в мире физике.
350 btTransform transform;
351 ballRigidBody->getMotionState()->getWorldTransform(transform);
352
353// И применяем ее к графической модели.
354 ballNode->setPosition(QVector3D(
355 transform.getOrigin().getX(),
356 transform.getOrigin().getY(),
357 transform.getOrigin().getZ()
358 )
359 );
360 }
361
362// Рисуем сцену со всеми объектами.
363 scene->draw(painter);
364
365// Если включен отладчик физики.
366 if(enabledDebuger)
367 {
368// Включаем смешивание цветов, для рассчета прозрачности.
369 glEnable(GL_BLEND);
370
371// Задаем на чем рисовать отладчику.
372 debuger->SetPainter(painter);
373
374// Запускаем прорисовку отладки.
375 dynamicsWorld->debugDrawWorld();
376
377// Отключаем смешивание.
378 glDisable(GL_BLEND);
379 }
380}
381
382void GLViewer::timerEvent(QTimerEvent *event)
383{
384 Q_UNUSED(event)
385 updateGL();
386}
387
388
389
390/********************************************************************************
391* *
392* Можно заметить из кода, что для анимации модели астероида используется *
393* только изменение позиции, поворот вокруг осей не используется, так как *
394* сфера одноцветная. *
395* *
396********************************************************************************/
397
