XGEN

В демо модель астероида (сфера) падает на ладшафт сгенерированный по карте высот.

Сборка производилась в gentoo c Qt 4.7.2. Для сборки вам нужно будет собрать Qt Quick 3D, подробности тут.

Так же необходимо подправить в файле qt3dbulletdemo.pro пути для переменных окружения INCLUDEPATH и LIBS.

screenshot qt3dbulletdemo

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