aboutsummaryrefslogtreecommitdiff
path: root/src/mesh.cpp
blob: fd35a3a062d6b348fa58b7eb202de48d2d222409 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "mesh.h"
#include "log.h"
#include <cassert>
#include <iostream>
#include <IAnimatedMesh.h>
#include <SAnimatedMesh.h>
#include <ICameraSceneNode.h>

// In Irrlicht 1.8 the signature of ITexture::lock was changed from
// (bool, u32) to (E_TEXTURE_LOCK_MODE, u32).
#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7
#define MY_ETLM_READ_ONLY true
#else
#define MY_ETLM_READ_ONLY video::ETLM_READ_ONLY
#endif

scene::IAnimatedMesh* createCubeMesh(v3f scale)
{
	video::SColor c(255,255,255,255);
	video::S3DVertex vertices[24] =
	{
		// Up
		video::S3DVertex(-0.5,+0.5,-0.5, 0,1,0, c, 0,1),
		video::S3DVertex(-0.5,+0.5,+0.5, 0,1,0, c, 0,0),
		video::S3DVertex(+0.5,+0.5,+0.5, 0,1,0, c, 1,0),
		video::S3DVertex(+0.5,+0.5,-0.5, 0,1,0, c, 1,1),
		// Down
		video::S3DVertex(-0.5,-0.5,-0.5, 0,-1,0, c, 0,0),
		video::S3DVertex(+0.5,-0.5,-0.5, 0,-1,0, c, 1,0),
		video::S3DVertex(+0.5,-0.5,+0.5, 0,-1,0, c, 1,1),
		video::S3DVertex(-0.5,-0.5,+0.5, 0,-1,0, c, 0,1),
		// Right
		video::S3DVertex(+0.5,-0.5,-0.5, 1,0,0, c, 0,1),
		video::S3DVertex(+0.5,+0.5,-0.5, 1,0,0, c, 0,0),
		video::S3DVertex(+0.5,+0.5,+0.5, 1,0,0, c, 1,0),
		video::S3DVertex(+0.5,-0.5,+0.5, 1,0,0, c, 1,1),
		// Left
		video::S3DVertex(-0.5,-0.5,-0.5, -1,0,0, c, 1,1),
		video::S3DVertex(-0.5,-0.5,+0.5, -1,0,0, c, 0,1),
		video::S3DVertex(-0.5,+0.5,+0.5, -1,0,0, c, 0,0),
		video::S3DVertex(-0.5,+0.5,-0.5, -1,0,0, c, 1,0),
		// Back
		video::S3DVertex(-0.5,-0.5,+0.5, 0,0,1, c, 1,1),
		video::S3DVertex(+0.5,-0.5,+0.5, 0,0,1, c, 0,1),
		video::S3DVertex(+0.5,+0.5,+0.5, 0,0,1, c, 0,0),
		video::S3DVertex(-0.5,+0.5,+0.5, 0,0,1, c, 1,0),
		// Front
		video::S3DVertex(-0.5,-0.5,-0.5, 0,0,-1, c, 0,1),
		video::S3DVertex(-0.5,+0.5,-0.5, 0,0,-1, c, 0,0),
		video::S3DVertex(+0.5,+0.5,-0.5, 0,0,-1, c, 1,0),
		video::S3DVertex(+0.5,-0.5,-0.5, 0,0,-1, c, 1,1),
	};

	u16 indices[6] = {0,1,2,2,3,0};

	scene::SMesh *mesh = new scene::SMesh();
	for (u32 i=0; i<6; ++i)
	{
		scene::IMeshBuffer *buf = new scene::SMeshBuffer();
		buf->append(vertices + 4 * i, 4, indices, 6);
		// Set default material
		buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
		buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
		buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
		// Add mesh buffer to mesh
		mesh->addMeshBuffer(buf);
		buf->drop();
	}

	scene::SAnimatedMesh *anim_mesh = new scene::SAnimatedMesh(mesh);
	mesh->drop();
	scaleMesh(anim_mesh, scale);  // also recalculates bounding box
	return anim_mesh;
}

static scene::IAnimatedMesh* extrudeARGB(u32 twidth, u32 theight, u8 *data)
{
	const s32 argb_wstep = 4 * twidth;
	const s32 alpha_threshold = 1;

	scene::IMeshBuffer *buf = new scene::SMeshBuffer();
	video::SColor c(255,255,255,255);

	// Front and back
	{
		video::S3DVertex vertices[8] =
		{
			video::S3DVertex(-0.5,-0.5,-0.5, 0,0,-1, c, 0,1),
			video::S3DVertex(-0.5,+0.5,-0.5, 0,0,-1, c, 0,0),
			video::S3DVertex(+0.5,+0.5,-0.5, 0,0,-1, c, 1,0),
			video::S3DVertex(+0.5,-0.5,-0.5, 0,0,-1, c, 1,1),
			video::S3DVertex(+0.5,-0.5,+0.5, 0,0,+1, c, 1,1),
			video::S3DVertex(+0.5,+0.5,+0.5, 0,0,+1, c, 1,0),
			video::S3DVertex(-0.5,+0.5,+0.5, 0,0,+1, c, 0,0),
			video::S3DVertex(-0.5,-0.5,+0.5, 0,0,+1, c, 0,1),
		};
		u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
		buf->append(vertices, 8, indices, 12);
	}

	// "Interior"
	// (add faces where a solid pixel is next to a transparent one)
	u8 *solidity = new u8[(twidth+2) * (theight+2)];
	u32 wstep = twidth + 2;
	for (u32 y = 0; y < theight + 2; ++y)
	{
		u8 *scanline = solidity + y * wstep;
		if (y == 0 || y == theight + 1)
		{
			for (u32 x = 0; x < twidth + 2; ++x)
				scanline[x] = 0;
		}
		else
		{
			scanline[0] = 0;
			u8 *argb_scanline = data + (y - 1) * argb_wstep;
			for (u32 x = 0; x < twidth; ++x)
				scanline[x+1] = (argb_scanline[x*4+3] >= alpha_threshold);
			scanline[twidth + 1] = 0;
		}
	}

	// without this, there would be occasional "holes" in the mesh
	f32 eps = 0.01;

	for (u32 y = 0; y <= theight; ++y)
	{
		u8 *scanline = solidity + y * wstep + 1;
		for (u32 x = 0; x <= twidth; ++x)
		{
			if (scanline[x] && !scanline[x + wstep])
			{
				u32 xx = x + 1;
				while (scanline[xx] && !scanline[xx + wstep])
					++xx;
				f32 vx1 = (x - eps) / (f32) twidth - 0.5;
				f32 vx2 = (xx + eps) / (f32) twidth - 0.5;
				f32 vy = 0.5 - (y - eps) / (f32) theight;
				f32 tx1 = x / (f32) twidth;
				f32 tx2 = xx / (f32) twidth;
				f32 ty = (y - 0.5) / (f32) theight;
				video::S3DVertex vertices[8] =
				{
					video::S3DVertex(vx1,vy,-0.5, 0,-1,0, c, tx1,ty),
					video::S3DVertex(vx2,vy,-0.5, 0,-1,0, c, tx2,ty),
					video::S3DVertex(vx2,vy,+0.5, 0,-1,0, c, tx2,ty),
					video::S3DVertex(vx1,vy,+0.5, 0,-1,0, c, tx1,ty),
				};
				u16 indices[6] = {0,1,2,2,3,0};
				buf->append(vertices, 4, indices, 6);
				x = xx - 1;
			}
			if (!scanline[x] && scanline[x + wstep])
			{
				u32 xx = x + 1;
				while (!scanline[xx] && scanline[xx + wstep])
					++xx;
				f32 vx1 = (x - eps) / (f32) twidth - 0.5;
				f32 vx2 = (xx + eps) / (f32) twidth - 0.5;
				f32 vy = 0.5 - (y + eps) / (f32) theight;
				f32 tx1 = x / (f32) twidth;
				f32 tx2 = xx / (f32) twidth;
				f32 ty = (y + 0.5) / (f32) theight;
				video::S3DVertex vertices[8] =
				{
					video::S3DVertex(vx1,vy,-0.5, 0,1,0, c, tx1,ty),
					video::S3DVertex(vx1,vy,+0.5, 0,1,0, c, tx1,ty),
					video::S3DVertex(vx2,vy,+0.5, 0,1,0, c, tx2,ty),
					video::S3DVertex(vx2,vy,-0.5, 0,1,0, c, tx2,ty),
				};
				u16 indices[6] = {0,1,2,2,3,0};
				buf->append(vertices, 4, indices, 6);
				x = xx - 1;
			}
		}
	}

	for (u32 x = 0; x <= twidth; ++x)
	{
		u8 *scancol = solidity + x + wstep;
		for (u32 y = 0; y <= theight; ++y)
		{
			if (scancol[y * wstep] && !scancol[y * wstep + 1])
			{
				u32 yy = y + 1;
				while (scancol[yy * wstep] && !scancol[yy * wstep + 1])
					++yy;
				f32 vx = (x - eps) / (f32) twidth - 0.5;
				f32 vy1 = 0.5 - (y - eps) / (f32) theight;
				f32 vy2 = 0.5 - (yy + eps) / (f32) theight;
				f32 tx = (x - 0.5) / (f32) twidth;
				f32 ty1 = y / (f32) theight;
				f32 ty2 = yy / (f32) theight;
				video::S3DVertex vertices[8] =
				{
					video::S3DVertex(vx,vy1,-0.5, 1,0,0, c, tx,ty1),
					video::S3DVertex(vx,vy1,+0.5, 1,0,0, c, tx,ty1),
					video::S3DVertex(vx,vy2,+0.5, 1,0,0, c, tx,ty2),
					video::S3DVertex(vx,vy2,-0.5, 1,0,0, c, tx,ty2),
				};
				u16 indices[6] = {0,1,2,2,3,0};
				buf->append(vertices, 4, indices, 6);
				y = yy - 1;
			}
			if (!scancol[y * wstep] && scancol[y * wstep + 1])
			{
				u32 yy = y + 1;
				while (!scancol[yy * wstep] && scancol[yy * wstep + 1])
					++yy;
				f32 vx = (x + eps) / (f32) twidth - 0.5;
				f32 vy1 = 0.5 - (y - eps) / (f32) theight;
				f32 vy2 = 0.5 - (yy + eps) / (f32) theight;
				f32 tx = (x + 0.5) / (f32) twidth;
				f32 ty1 = y / (f32) theight;
				f32 ty2 = yy / (f32) theight;
				video::S3DVertex vertices[8] =
				{
					video::S3DVertex(vx,vy1,-0.5, -1,0,0, c, tx,ty1),
					video::S3DVertex(vx,vy2,-0.5, -1,0,0, c, tx,ty2),
					video::S3DVertex(vx,vy2,+0.5, -1,0,0, c, tx,ty2),
					video::S3DVertex(vx,vy1,+0.5, -1,0,0, c, tx,ty1),
				};
				u16 indices[6] = {0,1,2,2,3,0};
				buf->append(vertices, 4, indices, 6);
				y = yy - 1;
			}
		}
	}

	delete[] solidity;

	// Add to mesh
	scene::SMesh *mesh = new scene::SMesh();
	mesh->addMeshBuffer(buf);
	buf->drop();
	scene::SAnimatedMesh *anim_mesh = new scene::SAnimatedMesh(mesh);
	mesh->drop();
	return anim_mesh;
}

scene::IAnimatedMesh* createExtrudedMesh(video::ITexture *texture,
		video::IVideoDriver *driver, v3f scale)
{
	scene::IAnimatedMesh *mesh = NULL;
	core::dimension2d<u32> size = texture->getSize();
	video::ECOLOR_FORMAT format = texture->getColorFormat();
	if (format == video::ECF_A8R8G8B8)
	{
		// Texture is in the correct color format, we can pass it
		// to extrudeARGB right away.
		void *data = texture->lock(MY_ETLM_READ_ONLY);
		if (data == NULL)
			return NULL;
		mesh = extrudeARGB(size.Width, size.Height, (u8*) data);
		texture->unlock();
	}
	else
	{
		video::IImage *img1 = driver->createImageFromData(format, size, texture->lock(MY_ETLM_READ_ONLY));
		if (img1 == NULL)
			return NULL;

		// img1 is in the texture's color format, convert to 8-bit ARGB
		video::IImage *img2 = driver->createImage(video::ECF_A8R8G8B8, size);
		if (img2 != NULL)
		{
			img1->copyTo(img2);

			mesh = extrudeARGB(size.Width, size.Height, (u8*) img2->lock());
			img2->unlock();
			img2->drop();
		}
		img1->drop();
	}

	// Set default material
	mesh->getMeshBuffer(0)->getMaterial().setTexture(0, texture);
	mesh->getMeshBuffer(0)->getMaterial().setFlag(video::EMF_LIGHTING, false);
	mesh->getMeshBuffer(0)->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
	mesh->getMeshBuffer(0)->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;

	scaleMesh(mesh, scale);  // also recalculates bounding box
	return mesh;
}

void scaleMesh(scene::IMesh *mesh, v3f scale)
{
	if(mesh == NULL)
		return;

	core::aabbox3d<f32> bbox;
	bbox.reset(0,0,0);

	u16 mc = mesh->getMeshBufferCount();
	for(u16 j=0; j<mc; j++)
	{
		scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
		video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
		u16 vc = buf->getVertexCount();
		for(u16 i=0; i<vc; i++)
		{
			vertices[i].Pos *= scale;
		}
		buf->recalculateBoundingBox();

		// calculate total bounding box
		if(j == 0)
			bbox = buf->getBoundingBox();
		else
			bbox.addInternalBox(buf->getBoundingBox());
	}
	mesh->setBoundingBox(bbox);
}

void translateMesh(scene::IMesh *mesh, v3f vec)
{
	if(mesh == NULL)
		return;

	core::aabbox3d<f32> bbox;
	bbox.reset(0,0,0);

	u16 mc = mesh->getMeshBufferCount();
	for(u16 j=0; j<mc; j++)
	{
		scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
		video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
		u16 vc = buf->getVertexCount();
		for(u16 i=0; i<vc; i++)
		{
			vertices[i].Pos += vec;
		}
		buf->recalculateBoundingBox();

		// calculate total bounding box
		if(j == 0)
			bbox = buf->getBoundingBox();
		else
			bbox.addInternalBox(buf->getBoundingBox());
	}
	mesh->setBoundingBox(bbox);
}

void setMeshColor(scene::IMesh *mesh, const video::SColor &color)
{
	if(mesh == NULL)
		return;
	
	u16 mc = mesh->getMeshBufferCount();
	for(u16 j=0; j<mc; j++)
	{
		scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
		video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
		u16 vc = buf->getVertexCount();
		for(u16 i=0; i<vc; i++)
		{
			vertices[i].Color = color;
		}
	}
}

void setMeshColorByNormalXYZ(scene::IMesh *mesh,
		const video::SColor &colorX,
		const video::SColor &colorY,
		const video::SColor &colorZ)
{
	if(mesh == NULL)
		return;
	
	u16 mc = mesh->getMeshBufferCount();
	for(u16 j=0; j<mc; j++)
	{
		scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
		video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
		u16 vc = buf->getVertexCount();
		for(u16 i=0; i<vc; i++)
		{
			f32 x = fabs(vertices[i].Normal.X);
			f32 y = fabs(vertices[i].Normal.Y);
			f32 z = fabs(vertices[i].Normal.Z);
			if(x >= y && x >= z)
				vertices[i].Color = colorX;
			else if(y >= z)
				vertices[i].Color = colorY;
			else
				vertices[i].Color = colorZ;

		}
	}
}

video::ITexture *generateTextureFromMesh(scene::IMesh *mesh,
		IrrlichtDevice *device,
		core::dimension2d<u32> dim,
		std::string texture_name,
		v3f camera_position,
		v3f camera_lookat,
		core::CMatrix4<f32> camera_projection_matrix,
		video::SColorf ambient_light,
		v3f light_position,
		video::SColorf light_color,
		f32 light_radius)
{
	video::IVideoDriver *driver = device->getVideoDriver();
	if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
	{
		static bool warned = false;
		if(!warned)
		{
			errorstream<<"generateTextureFromMesh(): EVDF_RENDER_TO_TARGET"
					" not supported."<<std::endl;
			warned = true;
		}
		return NULL;
	}

	// Create render target texture
	video::ITexture *rtt = driver->addRenderTargetTexture(
			dim, texture_name.c_str(), video::ECF_A8R8G8B8);
	if(rtt == NULL)
	{
		errorstream<<"generateTextureFromMesh(): addRenderTargetTexture"
				" returned NULL."<<std::endl;
		return NULL;
	}

	// Set render target
	driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0));

	// Get a scene manager
	scene::ISceneManager *smgr_main = device->getSceneManager();
	assert(smgr_main);
	scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
	assert(smgr);

	scene::IMeshSceneNode* meshnode = smgr->addMeshSceneNode(mesh, NULL, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true);
	meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
	meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
	meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, true);

	scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
			camera_position, camera_lookat);
	// second parameter of setProjectionMatrix (isOrthogonal) is ignored
	camera->setProjectionMatrix(camera_projection_matrix, false);

	smgr->setAmbientLight(ambient_light);
	smgr->addLightSceneNode(0, light_position, light_color, light_radius);

	// Render scene
	driver->beginScene(true, true, video::SColor(0,0,0,0));
	smgr->drawAll();
	driver->endScene();

	// NOTE: The scene nodes should not be dropped, otherwise
	//       smgr->drop() segfaults
	/*cube->drop();
	camera->drop();
	light->drop();*/
	// Drop scene manager
	smgr->drop();

	// Unset render target
	driver->setRenderTarget(0, false, true, 0);

	return rtt;
}
n class="hl kwc">irr::EET_KEY_INPUT_EVENT) { if(event.KeyInput.PressedDown) { keyIsDown.set(event.KeyInput); keyWasDown.set(event.KeyInput); } else { keyIsDown.unset(event.KeyInput); } } if(event.EventType == irr::EET_MOUSE_INPUT_EVENT) { if(noMenuActive() == false) { left_active = false; middle_active = false; right_active = false; } else { left_active = event.MouseInput.isLeftPressed(); middle_active = event.MouseInput.isMiddlePressed(); right_active = event.MouseInput.isRightPressed(); if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { leftclicked = true; } if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) { rightclicked = true; } if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { leftreleased = true; } if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) { rightreleased = true; } if(event.MouseInput.Event == EMIE_MOUSE_WHEEL) { mouse_wheel += event.MouseInput.Wheel; } } } return false; } bool IsKeyDown(const KeyPress &keyCode) const { return keyIsDown[keyCode]; } // Checks whether a key was down and resets the state bool WasKeyDown(const KeyPress &keyCode) { bool b = keyWasDown[keyCode]; if (b) keyWasDown.unset(keyCode); return b; } s32 getMouseWheel() { s32 a = mouse_wheel; mouse_wheel = 0; return a; } void clearInput() { keyIsDown.clear(); keyWasDown.clear(); leftclicked = false; rightclicked = false; leftreleased = false; rightreleased = false; left_active = false; middle_active = false; right_active = false; mouse_wheel = 0; } MyEventReceiver() { clearInput(); } bool leftclicked; bool rightclicked; bool leftreleased; bool rightreleased; bool left_active; bool middle_active; bool right_active; s32 mouse_wheel; private: IrrlichtDevice *m_device; // The current state of keys KeyList keyIsDown; // Whether a key has been pressed or not KeyList keyWasDown; }; /* Separated input handler */ class RealInputHandler : public InputHandler { public: RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver): m_device(device), m_receiver(receiver) { } virtual bool isKeyDown(const KeyPress &keyCode) { return m_receiver->IsKeyDown(keyCode); } virtual bool wasKeyDown(const KeyPress &keyCode) { return m_receiver->WasKeyDown(keyCode); } virtual v2s32 getMousePos() { return m_device->getCursorControl()->getPosition(); } virtual void setMousePos(s32 x, s32 y) { m_device->getCursorControl()->setPosition(x, y); } virtual bool getLeftState() { return m_receiver->left_active; } virtual bool getRightState() { return m_receiver->right_active; } virtual bool getLeftClicked() { return m_receiver->leftclicked; } virtual bool getRightClicked() { return m_receiver->rightclicked; } virtual void resetLeftClicked() { m_receiver->leftclicked = false; } virtual void resetRightClicked() { m_receiver->rightclicked = false; } virtual bool getLeftReleased() { return m_receiver->leftreleased; } virtual bool getRightReleased() { return m_receiver->rightreleased; } virtual void resetLeftReleased() { m_receiver->leftreleased = false; } virtual void resetRightReleased() { m_receiver->rightreleased = false; } virtual s32 getMouseWheel() { return m_receiver->getMouseWheel(); } void clear() { m_receiver->clearInput(); } private: IrrlichtDevice *m_device; MyEventReceiver *m_receiver; }; class RandomInputHandler : public InputHandler { public: RandomInputHandler() { leftdown = false; rightdown = false; leftclicked = false; rightclicked = false; leftreleased = false; rightreleased = false; keydown.clear(); } virtual bool isKeyDown(const KeyPress &keyCode) { return keydown[keyCode]; } virtual bool wasKeyDown(const KeyPress &keyCode) { return false; } virtual v2s32 getMousePos() { return mousepos; } virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x,y); } virtual bool getLeftState() { return leftdown; } virtual bool getRightState() { return rightdown; } virtual bool getLeftClicked() { return leftclicked; } virtual bool getRightClicked() { return rightclicked; } virtual void resetLeftClicked() { leftclicked = false; } virtual void resetRightClicked() { rightclicked = false; } virtual bool getLeftReleased() { return leftreleased; } virtual bool getRightReleased() { return rightreleased; } virtual void resetLeftReleased() { leftreleased = false; } virtual void resetRightReleased() { rightreleased = false; } virtual s32 getMouseWheel() { return 0; } virtual void step(float dtime) { { static float counter1 = 0; counter1 -= dtime; if(counter1 < 0.0) { counter1 = 0.1*Rand(1, 40); keydown.toggle(getKeySetting("keymap_jump")); } } { static float counter1 = 0; counter1 -= dtime; if(counter1 < 0.0) { counter1 = 0.1*Rand(1, 40); keydown.toggle(getKeySetting("keymap_special1")); } } { static float counter1 = 0; counter1 -= dtime; if(counter1 < 0.0) { counter1 = 0.1*Rand(1, 40); keydown.toggle(getKeySetting("keymap_forward")); } } { static float counter1 = 0; counter1 -= dtime; if(counter1 < 0.0) { counter1 = 0.1*Rand(1, 40); keydown.toggle(getKeySetting("keymap_left")); } } { static float counter1 = 0; counter1 -= dtime; if(counter1 < 0.0) { counter1 = 0.1*Rand(1, 20); mousespeed = v2s32(Rand(-20,20), Rand(-15,20)); } } { static float counter1 = 0; counter1 -= dtime; if(counter1 < 0.0) { counter1 = 0.1*Rand(1, 30); leftdown = !leftdown; if(leftdown) leftclicked = true; if(!leftdown) leftreleased = true; } } { static float counter1 = 0; counter1 -= dtime; if(counter1 < 0.0) { counter1 = 0.1*Rand(1, 15); rightdown = !rightdown; if(rightdown) rightclicked = true; if(!rightdown) rightreleased = true; } } mousepos += mousespeed; } s32 Rand(s32 min, s32 max) { return (myrand()%(max-min+1))+min; } private: KeyList keydown; v2s32 mousepos; v2s32 mousespeed; bool leftdown; bool rightdown; bool leftclicked; bool rightclicked; bool leftreleased; bool rightreleased; }; struct MenuTextures { std::string current_gameid; bool global_textures; video::ITexture *background; video::ITexture *overlay; video::ITexture *header; video::ITexture *footer; MenuTextures(): global_textures(false), background(NULL), overlay(NULL), header(NULL), footer(NULL) {} static video::ITexture* getMenuTexture(const std::string &tname, video::IVideoDriver* driver, const SubgameSpec *spec) { if(spec){ std::string path; // eg. minetest_menu_background.png (for texture packs) std::string pack_tname = spec->id + "_menu_" + tname + ".png"; path = getTexturePath(pack_tname); if(path != "") return driver->getTexture(path.c_str()); // eg. games/minetest_game/menu/background.png path = getImagePath(spec->path + DIR_DELIM + "menu" + DIR_DELIM + tname + ".png"); if(path != "") return driver->getTexture(path.c_str()); } else { std::string path; // eg. menu_background.png std::string pack_tname = "menu_" + tname + ".png"; path = getTexturePath(pack_tname); if(path != "") return driver->getTexture(path.c_str()); } return NULL; } void update(video::IVideoDriver* driver, const SubgameSpec *spec, int tab) { if(tab == TAB_SINGLEPLAYER){ if(spec->id == current_gameid) return; current_gameid = spec->id; global_textures = false; background = getMenuTexture("background", driver, spec); overlay = getMenuTexture("overlay", driver, spec); header = getMenuTexture("header", driver, spec); footer = getMenuTexture("footer", driver, spec); } else { if(global_textures) return; current_gameid = ""; global_textures = true; background = getMenuTexture("background", driver, NULL); overlay = getMenuTexture("overlay", driver, NULL); header = getMenuTexture("header", driver, NULL); footer = getMenuTexture("footer", driver, NULL); } } }; void drawMenuBackground(video::IVideoDriver* driver, const MenuTextures &menutextures) { v2u32 screensize = driver->getScreenSize(); video::ITexture *texture = menutextures.background; /* If no texture, draw background of solid color */ if(!texture){ video::SColor color(255,80,58,37); core::rect<s32> rect(0, 0, screensize.X, screensize.Y); driver->draw2DRectangle(color, rect, NULL); return; } /* Draw background texture */ v2u32 sourcesize = texture->getSize(); driver->draw2DImage(texture, core::rect<s32>(0, 0, screensize.X, screensize.Y), core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y), NULL, NULL, true); } void drawMenuOverlay(video::IVideoDriver* driver, const MenuTextures &menutextures) { v2u32 screensize = driver->getScreenSize(); video::ITexture *texture = menutextures.overlay; /* If no texture, draw nothing */ if(!texture) return; /* Draw overlay texture */ v2u32 sourcesize = texture->getSize(); driver->draw2DImage(texture, core::rect<s32>(0, 0, screensize.X, screensize.Y), core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y), NULL, NULL, true); } void drawMenuHeader(video::IVideoDriver* driver, const MenuTextures &menutextures) { core::dimension2d<u32> screensize = driver->getScreenSize(); video::ITexture *texture = menutextures.header; /* If no texture, draw nothing */ if(!texture) return; f32 mult = (((f32)screensize.Width / 2)) / ((f32)texture->getOriginalSize().Width); v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult, ((f32)texture->getOriginalSize().Height) * mult); // Don't draw the header is there isn't enough room s32 free_space = (((s32)screensize.Height)-320)/2; if (free_space > splashsize.Y) { core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y); splashrect += v2s32((screensize.Width/2)-(splashsize.X/2), ((free_space/2)-splashsize.Y/2)+10); video::SColor bgcolor(255,50,50,50); driver->draw2DImage(texture, splashrect, core::rect<s32>(core::position2d<s32>(0,0), core::dimension2di(texture->getSize())), NULL, NULL, true); } } void drawMenuFooter(video::IVideoDriver* driver, const MenuTextures &menutextures) { core::dimension2d<u32> screensize = driver->getScreenSize(); video::ITexture *texture = menutextures.footer; /* If no texture, draw nothing */ if(!texture) return; f32 mult = (((f32)screensize.Width)) / ((f32)texture->getOriginalSize().Width); v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult, ((f32)texture->getOriginalSize().Height) * mult); // Don't draw the footer if there isn't enough room s32 free_space = (((s32)screensize.Height)-320)/2; if (free_space > footersize.Y) { core::rect<s32> rect(0,0,footersize.X,footersize.Y); rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y); rect -= v2s32(footersize.X/2, 0); driver->draw2DImage(texture, rect, core::rect<s32>(core::position2d<s32>(0,0), core::dimension2di(texture->getSize())), NULL, NULL, true); } } static const SubgameSpec* getMenuGame(const MainMenuData &menudata) { for(size_t i=0; i<menudata.games.size(); i++){ if(menudata.games[i].id == menudata.selected_game){ return &menudata.games[i]; } } return NULL; } #endif // !SERVER // These are defined global so that they're not optimized too much. // Can't change them to volatile. s16 temp16; f32 tempf; v3f tempv3f1; v3f tempv3f2; std::string tempstring; std::string tempstring2; void SpeedTests() { { infostream<<"The following test should take around 20ms."<<std::endl; TimeTaker timer("Testing std::string speed"); const u32 jj = 10000; for(u32 j=0; j<jj; j++) { tempstring = ""; tempstring2 = ""; const u32 ii = 10; for(u32 i=0; i<ii; i++){ tempstring2 += "asd"; } for(u32 i=0; i<ii+1; i++){ tempstring += "asd"; if(tempstring == tempstring2) break; } } } infostream<<"All of the following tests should take around 100ms each." <<std::endl; { TimeTaker timer("Testing floating-point conversion speed"); tempf = 0.001; for(u32 i=0; i<4000000; i++){ temp16 += tempf; tempf += 0.001; } } { TimeTaker timer("Testing floating-point vector speed"); tempv3f1 = v3f(1,2,3); tempv3f2 = v3f(4,5,6); for(u32 i=0; i<10000000; i++){ tempf += tempv3f1.dotProduct(tempv3f2); tempv3f2 += v3f(7,8,9); } } { TimeTaker timer("Testing std::map speed"); std::map<v2s16, f32> map1; tempf = -324; const s16 ii=300; for(s16 y=0; y<ii; y++){ for(s16 x=0; x<ii; x++){ map1[v2s16(x,y)] = tempf; tempf += 1; } } for(s16 y=ii-1; y>=0; y--){ for(s16 x=0; x<ii; x++){ tempf = map1[v2s16(x,y)]; } } } { infostream<<"Around 5000/ms should do well here."<<std::endl; TimeTaker timer("Testing mutex speed"); JMutex m; m.Init(); u32 n = 0; u32 i = 0; do{ n += 10000; for(; i<n; i++){ m.Lock(); m.Unlock(); } } // Do at least 10ms while(timer.getTimerTime() < 10); u32 dtime = timer.stop(); u32 per_ms = n / dtime; infostream<<"Done. "<<dtime<<"ms, " <<per_ms<<"/ms"<<std::endl; } } static void print_worldspecs(const std::vector<WorldSpec> &worldspecs, std::ostream &os) { for(u32 i=0; i<worldspecs.size(); i++){ std::string name = worldspecs[i].name; std::string path = worldspecs[i].path; if(name.find(" ") != std::string::npos) name = std::string("'") + name + "'"; path = std::string("'") + path + "'"; name = padStringRight(name, 14); os<<" "<<name<<" "<<path<<std::endl; } } int main(int argc, char *argv[]) { int retval = 0; /* Initialization */ log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION); log_add_output_all_levs(&main_dstream_no_stderr_log_out); log_register_thread("main"); // This enables internatonal characters input if( setlocale(LC_ALL, "") == NULL ) { fprintf( stderr, "%s: warning: could not set default locale\n", argv[0] ); } // Set locale. This is for forcing '.' as the decimal point. try { std::locale::global(std::locale(std::locale(""), "C", std::locale::numeric)); setlocale(LC_NUMERIC, "C"); } catch (const std::exception& ex) { errorstream<<"Could not set numeric locale to C"<<std::endl; } /* Parse command line */ // List all allowed options std::map<std::string, ValueSpec> allowed_options; allowed_options.insert(std::make_pair("help", ValueSpec(VALUETYPE_FLAG, _("Show allowed options")))); allowed_options.insert(std::make_pair("config", ValueSpec(VALUETYPE_STRING, _("Load configuration from specified file")))); allowed_options.insert(std::make_pair("port", ValueSpec(VALUETYPE_STRING, _("Set network port (UDP)")))); allowed_options.insert(std::make_pair("disable-unittests", ValueSpec(VALUETYPE_FLAG, _("Disable unit tests")))); allowed_options.insert(std::make_pair("enable-unittests", ValueSpec(VALUETYPE_FLAG, _("Enable unit tests")))); allowed_options.insert(std::make_pair("map-dir", ValueSpec(VALUETYPE_STRING, _("Same as --world (deprecated)")))); allowed_options.insert(std::make_pair("world", ValueSpec(VALUETYPE_STRING, _("Set world path (implies local game) ('list' lists all)")))); allowed_options.insert(std::make_pair("worldname", ValueSpec(VALUETYPE_STRING, _("Set world by name (implies local game)")))); allowed_options.insert(std::make_pair("info", ValueSpec(VALUETYPE_FLAG, _("Print more information to console")))); allowed_options.insert(std::make_pair("verbose", ValueSpec(VALUETYPE_FLAG, _("Print even more information to console")))); allowed_options.insert(std::make_pair("trace", ValueSpec(VALUETYPE_FLAG, _("Print enormous amounts of information to log and console")))); allowed_options.insert(std::make_pair("logfile", ValueSpec(VALUETYPE_STRING, _("Set logfile path ('' = no logging)")))); allowed_options.insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING, _("Set gameid (\"--gameid list\" prints available ones)")))); #ifndef SERVER allowed_options.insert(std::make_pair("videomodes", ValueSpec(VALUETYPE_FLAG, _("Show available video modes")))); allowed_options.insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG, _("Run speed tests")))); allowed_options.insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING, _("Address to connect to. ('' = local game)")))); allowed_options.insert(std::make_pair("random-input", ValueSpec(VALUETYPE_FLAG, _("Enable random user input, for testing")))); allowed_options.insert(std::make_pair("server", ValueSpec(VALUETYPE_FLAG, _("Run dedicated server")))); allowed_options.insert(std::make_pair("name", ValueSpec(VALUETYPE_STRING, _("Set player name")))); allowed_options.insert(std::make_pair("password", ValueSpec(VALUETYPE_STRING, _("Set password")))); allowed_options.insert(std::make_pair("go", ValueSpec(VALUETYPE_FLAG, _("Disable main menu")))); #endif Settings cmd_args; bool ret = cmd_args.parseCommandLine(argc, argv, allowed_options); if(ret == false || cmd_args.getFlag("help") || cmd_args.exists("nonopt1")) { dstream<<_("Allowed options:")<<std::endl; for(std::map<std::string, ValueSpec>::iterator i = allowed_options.begin(); i != allowed_options.end(); ++i) { std::ostringstream os1(std::ios::binary); os1<<" --"<<i->first; if(i->second.type == VALUETYPE_FLAG) {} else os1<<_(" <value>"); dstream<<padStringRight(os1.str(), 24); if(i->second.help != NULL) dstream<<i->second.help; dstream<<std::endl; } return cmd_args.getFlag("help") ? 0 : 1; } /* Low-level initialization */ // If trace is enabled, enable logging of certain things if(cmd_args.getFlag("trace")){ dstream<<_("Enabling trace level debug output")<<std::endl; log_trace_level_enabled = true; dout_con_ptr = &verbosestream; // this is somewhat old crap socket_enable_debug_output = true; // socket doesn't use log.h } // In certain cases, output info level on stderr if(cmd_args.getFlag("info") || cmd_args.getFlag("verbose") || cmd_args.getFlag("trace") || cmd_args.getFlag("speedtests")) log_add_output(&main_stderr_log_out, LMT_INFO); // In certain cases, output verbose level on stderr if(cmd_args.getFlag("verbose") || cmd_args.getFlag("trace")) log_add_output(&main_stderr_log_out, LMT_VERBOSE); porting::signal_handler_init(); bool &kill = *porting::signal_handler_killstatus(); porting::initializePaths(); // Create user data directory fs::CreateDir(porting::path_user); init_gettext((porting::path_share + DIR_DELIM + "locale").c_str()); infostream<<"path_share = "<<porting::path_share<<std::endl; infostream<<"path_user = "<<porting::path_user<<std::endl; // Initialize debug stacks debug_stacks_init(); DSTACK(__FUNCTION_NAME); // Debug handler BEGIN_DEBUG_EXCEPTION_HANDLER // List gameids if requested if(cmd_args.exists("gameid") && cmd_args.get("gameid") == "list") { std::set<std::string> gameids = getAvailableGameIds(); for(std::set<std::string>::const_iterator i = gameids.begin(); i != gameids.end(); i++) dstream<<(*i)<<std::endl; return 0; } // List worlds if requested if(cmd_args.exists("world") && cmd_args.get("world") == "list"){ dstream<<_("Available worlds:")<<std::endl; std::vector<WorldSpec> worldspecs = getAvailableWorlds(); print_worldspecs(worldspecs, dstream); return 0; } // Print startup message infostream<<PROJECT_NAME<< " "<<_("with")<<" SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST <<", "<<BUILD_INFO <<std::endl; /* Basic initialization */ // Initialize default settings set_default_settings(g_settings); // Initialize sockets sockets_init(); atexit(sockets_cleanup); /* Read config file */ // Path of configuration file in use std::string configpath = ""; if(cmd_args.exists("config")) { bool r = g_settings->readConfigFile(cmd_args.get("config").c_str()); if(r == false) { errorstream<<"Could not read configuration from \"" <<cmd_args.get("config")<<"\""<<std::endl; return 1; } configpath = cmd_args.get("config"); } else { std::vector<std::string> filenames; filenames.push_back(porting::path_user + DIR_DELIM + "minetest.conf"); // Legacy configuration file location filenames.push_back(porting::path_user + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf"); #if RUN_IN_PLACE // Try also from a lower level (to aid having the same configuration // for many RUN_IN_PLACE installs) filenames.push_back(porting::path_user + DIR_DELIM + ".." + DIR_DELIM + ".." + DIR_DELIM + "minetest.conf"); #endif for(u32 i=0; i<filenames.size(); i++) { bool r = g_settings->readConfigFile(filenames[i].c_str()); if(r) { configpath = filenames[i]; break; } } // If no path found, use the first one (menu creates the file) if(configpath == "") configpath = filenames[0]; } // Initialize debug streams #define DEBUGFILE "debug.txt" #if RUN_IN_PLACE std::string logfile = DEBUGFILE; #else std::string logfile = porting::path_user+DIR_DELIM+DEBUGFILE; #endif if(cmd_args.exists("logfile")) logfile = cmd_args.get("logfile"); log_remove_output(&main_dstream_no_stderr_log_out); int loglevel = g_settings->getS32("debug_log_level"); if (loglevel == 0) //no logging logfile = ""; else if (loglevel > 0 && loglevel <= LMT_NUM_VALUES) log_add_output_maxlev(&main_dstream_no_stderr_log_out, (LogMessageLevel)(loglevel - 1)); if(logfile != "") debugstreams_init(false, logfile.c_str()); else debugstreams_init(false, NULL); infostream<<"logfile = "<<logfile<<std::endl; // Initialize random seed srand(time(0)); mysrand(time(0)); /* Run unit tests */ if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false) || cmd_args.getFlag("enable-unittests") == true) { run_tests(); } /* Game parameters */ // Port u16 port = 30000; if(cmd_args.exists("port")) port = cmd_args.getU16("port"); else if(g_settings->exists("port")) port = g_settings->getU16("port"); if(port == 0) port = 30000; // World directory std::string commanded_world = ""; if(cmd_args.exists("world")) commanded_world = cmd_args.get("world"); else if(cmd_args.exists("map-dir")) commanded_world = cmd_args.get("map-dir"); else if(cmd_args.exists("nonopt0")) // First nameless argument commanded_world = cmd_args.get("nonopt0"); else if(g_settings->exists("map-dir")) commanded_world = g_settings->get("map-dir"); // World name std::string commanded_worldname = ""; if(cmd_args.exists("worldname")) commanded_worldname = cmd_args.get("worldname"); // Strip world.mt from commanded_world { std::string worldmt = "world.mt"; if(commanded_world.size() > worldmt.size() && commanded_world.substr(commanded_world.size()-worldmt.size()) == worldmt){ dstream<<_("Supplied world.mt file - stripping it off.")<<std::endl; commanded_world = commanded_world.substr( 0, commanded_world.size()-worldmt.size()); } } // If a world name was specified, convert it to a path if(commanded_worldname != ""){ // Get information about available worlds std::vector<WorldSpec> worldspecs = getAvailableWorlds(); bool found = false; for(u32 i=0; i<worldspecs.size(); i++){ std::string name = worldspecs[i].name; if(name == commanded_worldname){ if(commanded_world != ""){ dstream<<_("--worldname takes precedence over previously " "selected world.")<<std::endl; } commanded_world = worldspecs[i].path; found = true; break; } } if(!found){ dstream<<_("World")<<" '"<<commanded_worldname<<_("' not " "available. Available worlds:")<<std::endl; print_worldspecs(worldspecs, dstream); return 1; } } // Gamespec SubgameSpec commanded_gamespec; if(cmd_args.exists("gameid")){ std::string gameid = cmd_args.get("gameid"); commanded_gamespec = findSubgame(gameid); if(!commanded_gamespec.isValid()){ errorstream<<"Game \""<<gameid<<"\" not found"<<std::endl; return 1; } } /* Run dedicated server if asked to or no other option */ #ifdef SERVER bool run_dedicated_server = true; #else bool run_dedicated_server = cmd_args.getFlag("server"); #endif g_settings->set("server_dedicated", run_dedicated_server ? "true" : "false"); if(run_dedicated_server) { DSTACK("Dedicated server branch"); // Create time getter if built with Irrlicht #ifndef SERVER g_timegetter = new SimpleTimeGetter(); #endif // World directory std::string world_path; verbosestream<<_("Determining world path")<<std::endl; bool is_legacy_world = false; // If a world was commanded, use it if(commanded_world != ""){ world_path = commanded_world; infostream<<"Using commanded world path ["<<world_path<<"]" <<std::endl; } // No world was specified; try to select it automatically else { // Get information about available worlds std::vector<WorldSpec> worldspecs = getAvailableWorlds(); // If a world name was specified, select it if(commanded_worldname != ""){ world_path = ""; for(u32 i=0; i<worldspecs.size(); i++){ std::string name = worldspecs[i].name; if(name == commanded_worldname){ world_path = worldspecs[i].path; break; } } if(world_path == ""){ dstream<<_("World")<<" '"<<commanded_worldname<<"' "<<_("not " "available. Available worlds:")<<std::endl; print_worldspecs(worldspecs, dstream); return 1; } } // If there is only a single world, use it if(worldspecs.size() == 1){ world_path = worldspecs[0].path; dstream<<_("Automatically selecting world at")<<" [" <<world_path<<"]"<<std::endl; // If there are multiple worlds, list them } else if(worldspecs.size() > 1){ dstream<<_("Multiple worlds are available.")<<std::endl; dstream<<_("Please select one using --worldname <name>" " or --world <path>")<<std::endl; print_worldspecs(worldspecs, dstream); return 1; // If there are no worlds, automatically create a new one } else { // This is the ultimate default world path world_path = porting::path_user + DIR_DELIM + "worlds" + DIR_DELIM + "world"; infostream<<"Creating default world at [" <<world_path<<"]"<<std::endl; } } if(world_path == ""){ errorstream<<"No world path specified or found."<<std::endl; return 1; } verbosestream<<_("Using world path")<<" ["<<world_path<<"]"<<std::endl; // We need a gamespec. SubgameSpec gamespec; verbosestream<<_("Determining gameid/gamespec")<<std::endl; // If world doesn't exist if(!getWorldExists(world_path)) { // Try to take gamespec from command line if(commanded_gamespec.isValid()){ gamespec = commanded_gamespec; infostream<<"Using commanded gameid ["<<gamespec.id<<"]"<<std::endl; } // Otherwise we will be using "minetest" else{ gamespec = findSubgame(g_settings->get("default_game")); infostream<<"Using default gameid ["<<gamespec.id<<"]"<<std::endl; } } // World exists else { std::string world_gameid = getWorldGameId(world_path, is_legacy_world); // If commanded to use a gameid, do so if(commanded_gamespec.isValid()){ gamespec = commanded_gamespec; if(commanded_gamespec.id != world_gameid){ errorstream<<"WARNING: Using commanded gameid [" <<gamespec.id<<"]"<<" instead of world gameid [" <<world_gameid<<"]"<<std::endl; } } else{ // If world contains an embedded game, use it; // Otherwise find world from local system. gamespec = findWorldSubgame(world_path); infostream<<"Using world gameid ["<<gamespec.id<<"]"<<std::endl; } } if(!gamespec.isValid()){ errorstream<<"Subgame ["<<gamespec.id<<"] could not be found." <<std::endl; return 1; } verbosestream<<_("Using gameid")<<" ["<<gamespec.id<<"]"<<std::endl; // Create server Server server(world_path, configpath, gamespec, false); server.start(port); // Run server dedicated_server_loop(server, kill); return 0; } #ifndef SERVER // Exclude from dedicated server build /* More parameters */ std::string address = g_settings->get("address"); if(commanded_world != "") address = ""; else if(cmd_args.exists("address")) address = cmd_args.get("address"); std::string playername = g_settings->get("name"); if(cmd_args.exists("name")) playername = cmd_args.get("name"); bool skip_main_menu = cmd_args.getFlag("go"); /* Device initialization */ // Resolution selection bool fullscreen = g_settings->getBool("fullscreen"); u16 screenW = g_settings->getU16("screenW"); u16 screenH = g_settings->getU16("screenH"); // bpp, fsaa, vsync bool vsync = g_settings->getBool("vsync"); u16 bits = g_settings->getU16("fullscreen_bpp"); u16 fsaa = g_settings->getU16("fsaa"); // Determine driver video::E_DRIVER_TYPE driverType; std::string driverstring = g_settings->get("video_driver"); if(driverstring == "null") driverType = video::EDT_NULL; else if(driverstring == "software") driverType = video::EDT_SOFTWARE; else if(driverstring == "burningsvideo") driverType = video::EDT_BURNINGSVIDEO; else if(driverstring == "direct3d8") driverType = video::EDT_DIRECT3D8; else if(driverstring == "direct3d9") driverType = video::EDT_DIRECT3D9; else if(driverstring == "opengl") driverType = video::EDT_OPENGL; #ifdef _IRR_COMPILE_WITH_OGLES1_ else if(driverstring == "ogles1") driverType = video::EDT_OGLES1; #endif #ifdef _IRR_COMPILE_WITH_OGLES2_ else if(driverstring == "ogles2") driverType = video::EDT_OGLES2; #endif else { errorstream<<"WARNING: Invalid video_driver specified; defaulting " "to opengl"<<std::endl; driverType = video::EDT_OPENGL; } /* List video modes if requested */ MyEventReceiver receiver; if(cmd_args.getFlag("videomodes")){ IrrlichtDevice *nulldevice; SIrrlichtCreationParameters params = SIrrlichtCreationParameters(); params.DriverType = video::EDT_NULL; params.WindowSize = core::dimension2d<u32>(640, 480); params.Bits = 24; params.AntiAlias = fsaa; params.Fullscreen = false; params.Stencilbuffer = false; params.Vsync = vsync; params.EventReceiver = &receiver; nulldevice = createDeviceEx(params); if(nulldevice == 0) return 1; dstream<<_("Available video modes (WxHxD):")<<std::endl; video::IVideoModeList *videomode_list = nulldevice->getVideoModeList(); if(videomode_list == 0){ nulldevice->drop(); return 1; } s32 videomode_count = videomode_list->getVideoModeCount(); core::dimension2d<u32> videomode_res; s32 videomode_depth; for (s32 i = 0; i < videomode_count; ++i){ videomode_res = videomode_list->getVideoModeResolution(i); videomode_depth = videomode_list->getVideoModeDepth(i); dstream<<videomode_res.Width<<"x"<<videomode_res.Height <<"x"<<videomode_depth<<std::endl; } dstream<<_("Active video mode (WxHxD):")<<std::endl; videomode_res = videomode_list->getDesktopResolution(); videomode_depth = videomode_list->getDesktopDepth(); dstream<<videomode_res.Width<<"x"<<videomode_res.Height <<"x"<<videomode_depth<<std::endl; nulldevice->drop(); return 0; } /* Create device and exit if creation failed */ IrrlichtDevice *device; SIrrlichtCreationParameters params = SIrrlichtCreationParameters(); params.DriverType = driverType; params.WindowSize = core::dimension2d<u32>(screenW, screenH); params.Bits = bits; params.AntiAlias = fsaa; params.Fullscreen = fullscreen; params.Stencilbuffer = false; params.Vsync = vsync; params.EventReceiver = &receiver; device = createDeviceEx(params); if (device == 0) return 1; // could not create selected driver. /* Continue initialization */ video::IVideoDriver* driver = device->getVideoDriver(); /* This changes the minimum allowed number of vertices in a VBO. Default is 500. */ //driver->setMinHardwareBufferVertexCount(50); // Create time getter g_timegetter = new IrrlichtTimeGetter(device); // Create game callback for menus g_gamecallback = new MainGameCallback(device); /* Speed tests (done after irrlicht is loaded to get timer) */ if(cmd_args.getFlag("speedtests")) { dstream<<"Running speed tests"<<std::endl; SpeedTests(); device->drop(); return 0; } device->setResizable(true); bool random_input = g_settings->getBool("random_input") || cmd_args.getFlag("random-input"); InputHandler *input = NULL; if(random_input) input = new RandomInputHandler(); else input = new RealInputHandler(device, &receiver); scene::ISceneManager* smgr = device->getSceneManager(); guienv = device->getGUIEnvironment(); gui::IGUISkin* skin = guienv->getSkin(); #if USE_FREETYPE std::string font_path = g_settings->get("font_path"); u16 font_size = g_settings->getU16("font_size"); gui::IGUIFont *font = gui::CGUITTFont::createTTFont(guienv, font_path.c_str(), font_size); #else gui::IGUIFont* font = guienv->getFont(getTexturePath("fontlucida.png").c_str()); #endif if(font) skin->setFont(font); else errorstream<<"WARNING: Font file was not found." " Using default font."<<std::endl; // If font was not found, this will get us one font = skin->getFont(); assert(font); u32 text_height = font->getDimension(L"Hello, world!").Height; infostream<<"text_height="<<text_height<<std::endl; //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0)); skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255)); //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0)); //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0)); skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0)); skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0)); skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255,70,100,50)); skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255,255,255,255)); #if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2 // Irrlicht 1.8 input colours skin->setColor(gui::EGDC_EDITABLE, video::SColor(255,128,128,128)); skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255,96,134,49)); #endif // Create the menu clouds if (!g_menucloudsmgr) g_menucloudsmgr = smgr->createNewSceneManager(); if (!g_menuclouds) g_menuclouds = new Clouds(g_menucloudsmgr->getRootSceneNode(), g_menucloudsmgr, -1, rand(), 100); g_menuclouds->update(v2f(0, 0), video::SColor(255,200,200,255)); scene::ICameraSceneNode* camera; camera = g_menucloudsmgr->addCameraSceneNode(0, v3f(0,0,0), v3f(0, 60, 100)); camera->setFarValue(10000); /* GUI stuff */ ChatBackend chat_backend; /* If an error occurs, this is set to something and the menu-game loop is restarted. It is then displayed before the menu. */ std::wstring error_message = L""; // The password entered during the menu screen, std::string password; bool first_loop = true; /* Menu-game loop */ while(device->run() && kill == false) { // Set the window caption wchar_t* text = wgettext("Main Menu"); device->setWindowCaption((std::wstring(L"Minetest [")+text+L"]").c_str()); delete[] text; // This is used for catching disconnects try { /* Clear everything from the GUIEnvironment */ guienv->clear(); /* We need some kind of a root node to be able to add custom gui elements directly on the screen. Otherwise they won't be automatically drawn. */ guiroot = guienv->addStaticText(L"", core::rect<s32>(0, 0, 10000, 10000)); SubgameSpec gamespec; WorldSpec worldspec; bool simple_singleplayer_mode = false; // These are set up based on the menu and other things std::string current_playername = "inv£lid"; std::string current_password = ""; std::string current_address = "does-not-exist"; int current_port = 0; /* Out-of-game menu loop. Loop quits when menu returns proper parameters. */ while(kill == false) { // If skip_main_menu, only go through here once if(skip_main_menu && !first_loop){ kill = true; break; } first_loop = false; // Cursor can be non-visible when coming from the game device->getCursorControl()->setVisible(true); // Some stuff are left to scene manager when coming from the game // (map at least?) smgr->clear(); // Initialize menu data MainMenuData menudata; if(g_settings->exists("selected_mainmenu_tab")) menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab"); if(g_settings->exists("selected_serverlist")) menudata.selected_serverlist = g_settings->getS32("selected_serverlist"); if(g_settings->exists("selected_mainmenu_game")){ menudata.selected_game = g_settings->get("selected_mainmenu_game"); menudata.selected_game_name = findSubgame(menudata.selected_game).name; } menudata.address = narrow_to_wide(address); menudata.name = narrow_to_wide(playername); menudata.port = narrow_to_wide(itos(port)); if(cmd_args.exists("password")) menudata.password = narrow_to_wide(cmd_args.get("password")); menudata.fancy_trees = g_settings->getBool("new_style_leaves"); menudata.smooth_lighting = g_settings->getBool("smooth_lighting"); menudata.clouds_3d = g_settings->getBool("enable_3d_clouds"); menudata.opaque_water = g_settings->getBool("opaque_water"); menudata.mip_map = g_settings->getBool("mip_map"); menudata.anisotropic_filter = g_settings->getBool("anisotropic_filter"); menudata.bilinear_filter = g_settings->getBool("bilinear_filter"); menudata.trilinear_filter = g_settings->getBool("trilinear_filter"); menudata.enable_shaders = g_settings->getS32("enable_shaders"); menudata.preload_item_visuals = g_settings->getBool("preload_item_visuals"); menudata.enable_particles = g_settings->getBool("enable_particles"); menudata.liquid_finite = g_settings->getBool("liquid_finite"); driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, menudata.mip_map); menudata.creative_mode = g_settings->getBool("creative_mode"); menudata.enable_damage = g_settings->getBool("enable_damage"); menudata.enable_public = g_settings->getBool("server_announce"); // Default to selecting nothing menudata.selected_world = -1; // Get world listing for the menu std::vector<WorldSpec> worldspecs = getAvailableWorlds(); // If there is only one world, select it if(worldspecs.size() == 1){ menudata.selected_world = 0; } // Otherwise try to select according to selected_world_path else if(g_settings->exists("selected_world_path")){ std::string trypath = g_settings->get("selected_world_path"); for(u32 i=0; i<worldspecs.size(); i++){ if(worldspecs[i].path == trypath){ menudata.selected_world = i; break; } } } // If a world was commanded, append and select it if(commanded_world != ""){ std::string gameid = getWorldGameId(commanded_world, true); std::string name = _("[--world parameter]"); if(gameid == ""){ gameid = g_settings->get("default_game"); name += " [new]"; } WorldSpec spec(commanded_world, name, gameid); worldspecs.push_back(spec); menudata.selected_world = worldspecs.size()-1; } // Copy worldspecs to menu menudata.worlds = worldspecs; // Get game listing menudata.games = getAvailableGames(); // If selected game doesn't exist, take first from list if(findSubgame(menudata.selected_game).id == "" && !menudata.games.empty()){ menudata.selected_game = menudata.games[0].id; } const SubgameSpec *menugame = getMenuGame(menudata); MenuTextures menutextures; menutextures.update(driver, menugame, menudata.selected_tab); if(skip_main_menu == false) { video::IVideoDriver* driver = device->getVideoDriver(); float fps_max = g_settings->getFloat("fps_max"); infostream<<"Waiting for other menus"<<std::endl; while(device->run() && kill == false) { if(noMenuActive()) break; driver->beginScene(true, true, video::SColor(255,128,128,128)); drawMenuBackground(driver, menutextures); guienv->drawAll(); driver->endScene(); // On some computers framerate doesn't seem to be // automatically limited sleep_ms(25); } infostream<<"Waited for other menus"<<std::endl; GUIMainMenu *menu = new GUIMainMenu(guienv, guiroot, -1, &g_menumgr, &menudata, g_gamecallback); menu->allowFocusRemoval(true); if(error_message != L"") { verbosestream<<"error_message = " <<wide_to_narrow(error_message)<<std::endl; GUIMessageMenu *menu2 = new GUIMessageMenu(guienv, guiroot, -1, &g_menumgr, error_message.c_str()); menu2->drop(); error_message = L""; } // Time is in milliseconds, for clouds u32 lasttime = device->getTimer()->getTime(); MenuMusicFetcher soundfetcher; ISoundManager *sound = NULL; #if USE_SOUND sound = createOpenALSoundManager(&soundfetcher); #endif if(!sound) sound = &dummySoundManager; SimpleSoundSpec spec; spec.name = "main_menu"; spec.gain = 1; s32 handle = sound->playSound(spec, true); infostream<<"Created main menu"<<std::endl; while(device->run() && kill == false) { if(menu->getStatus() == true) break; // Game can be selected in the menu menugame = getMenuGame(menudata); menutextures.update(driver, menugame, menu->getTab()); // Clouds for the main menu bool cloud_menu_background = g_settings->getBool("menu_clouds"); if(menugame){ // If game has regular background and no overlay, don't use clouds if(cloud_menu_background && menutextures.background && !menutextures.overlay){ cloud_menu_background = false; } // If game game has overlay and no regular background, always draw clouds else if(menutextures.overlay && !menutextures.background){ cloud_menu_background = true; } } // Time calc for the clouds f32 dtime=0; // in seconds if (cloud_menu_background) { u32 time = device->getTimer()->getTime(); if(time > lasttime) dtime = (time - lasttime) / 1000.0; else dtime = 0; lasttime = time; } //driver->beginScene(true, true, video::SColor(255,0,0,0)); driver->beginScene(true, true, video::SColor(255,140,186,250)); if (cloud_menu_background) { // *3 otherwise the clouds would move very slowly g_menuclouds->step(dtime*3); g_menuclouds->render(); g_menucloudsmgr->drawAll(); drawMenuOverlay(driver, menutextures); drawMenuHeader(driver, menutextures); drawMenuFooter(driver, menutextures); } else { drawMenuBackground(driver, menutextures); drawMenuHeader(driver, menutextures); drawMenuFooter(driver, menutextures); } guienv->drawAll(); driver->endScene(); // On some computers framerate doesn't seem to be // automatically limited if (cloud_menu_background) { // Time of frame without fps limit float busytime; u32 busytime_u32; // not using getRealTime is necessary for wine u32 time = device->getTimer()->getTime(); if(time > lasttime) busytime_u32 = time - lasttime; else busytime_u32 = 0; busytime = busytime_u32 / 1000.0; // FPS limiter u32 frametime_min = 1000./fps_max; if(busytime_u32 < frametime_min) { u32 sleeptime = frametime_min - busytime_u32; device->sleep(sleeptime); } } else { sleep_ms(25); } } sound->stopSound(handle); if(sound != &dummySoundManager){ delete sound; sound = NULL; } // Save controls status menu->readInput(&menudata); infostream<<"Dropping main menu"<<std::endl; menu->drop(); } playername = wide_to_narrow(menudata.name); if (playername == "") playername = std::string("Guest") + itos(myrand_range(1000,9999)); password = translatePassword(playername, menudata.password); //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl; address = wide_to_narrow(menudata.address); int newport = stoi(wide_to_narrow(menudata.port)); if(newport != 0) port = newport; simple_singleplayer_mode = menudata.simple_singleplayer_mode; // Save settings g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab); g_settings->setS32("selected_serverlist", menudata.selected_serverlist); g_settings->set("selected_mainmenu_game", menudata.selected_game); g_settings->set("new_style_leaves", itos(menudata.fancy_trees)); g_settings->set("smooth_lighting", itos(menudata.smooth_lighting)); g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d)); g_settings->set("opaque_water", itos(menudata.opaque_water)); g_settings->set("mip_map", itos(menudata.mip_map)); g_settings->set("anisotropic_filter", itos(menudata.anisotropic_filter)); g_settings->set("bilinear_filter", itos(menudata.bilinear_filter)); g_settings->set("trilinear_filter", itos(menudata.trilinear_filter)); g_settings->setS32("enable_shaders", menudata.enable_shaders); g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals)); g_settings->set("enable_particles", itos(menudata.enable_particles)); g_settings->set("liquid_finite", itos(menudata.liquid_finite)); g_settings->set("creative_mode", itos(menudata.creative_mode)); g_settings->set("enable_damage", itos(menudata.enable_damage)); g_settings->set("server_announce", itos(menudata.enable_public)); g_settings->set("name", playername); g_settings->set("address", address); g_settings->set("port", itos(port)); if(menudata.selected_world != -1) g_settings->set("selected_world_path", worldspecs[menudata.selected_world].path); // Break out of menu-game loop to shut down cleanly if(device->run() == false || kill == true) break; current_playername = playername; current_password = password; current_address = address; current_port = port; // If using simple singleplayer mode, override if(simple_singleplayer_mode){ current_playername = "singleplayer"; current_password = ""; current_address = ""; current_port = 30011; } else if (address != "") { ServerListSpec server; server["name"] = menudata.servername; server["address"] = wide_to_narrow(menudata.address); server["port"] = wide_to_narrow(menudata.port); server["description"] = menudata.serverdescription; ServerList::insert(server); } // Set world path to selected one if(menudata.selected_world != -1){ worldspec = worldspecs[menudata.selected_world]; infostream<<"Selected world: "<<worldspec.name <<" ["<<worldspec.path<<"]"<<std::endl; } // Only refresh if so requested if(menudata.only_refresh){ infostream<<"Refreshing menu"<<std::endl; continue; } // Create new world if requested if(menudata.create_world_name != L"") { std::string path = porting::path_user + DIR_DELIM "worlds" + DIR_DELIM + wide_to_narrow(menudata.create_world_name); // Create world if it doesn't exist if(!initializeWorld(path, menudata.create_world_gameid)){ error_message = wgettext("Failed to initialize world"); errorstream<<wide_to_narrow(error_message)<<std::endl; continue; } g_settings->set("selected_world_path", path); g_settings->set("selected_mainmenu_game", menudata.create_world_gameid); continue; } // If local game if(current_address == "") { if(menudata.selected_world == -1){ error_message = wgettext("No world selected and no address " "provided. Nothing to do."); errorstream<<wide_to_narrow(error_message)<<std::endl; continue; } // Load gamespec for required game gamespec = findWorldSubgame(worldspec.path); if(!gamespec.isValid() && !commanded_gamespec.isValid()){ error_message = wgettext("Could not find or load game \"") + narrow_to_wide(worldspec.gameid) + L"\""; errorstream<<wide_to_narrow(error_message)<<std::endl; continue; } if(commanded_gamespec.isValid() && commanded_gamespec.id != worldspec.gameid){ errorstream<<"WARNING: Overriding gamespec from \"" <<worldspec.gameid<<"\" to \"" <<commanded_gamespec.id<<"\""<<std::endl; gamespec = commanded_gamespec; } if(!gamespec.isValid()){ error_message = wgettext("Invalid gamespec."); error_message += L" (world_gameid=" +narrow_to_wide(worldspec.gameid)+L")"; errorstream<<wide_to_narrow(error_message)<<std::endl; continue; } } // Continue to game break; } // Break out of menu-game loop to shut down cleanly if(device->run() == false || kill == true) break; /* Run game */ the_game( kill, random_input, input, device, font, worldspec.path, current_playername, current_password, current_address, current_port, error_message, configpath, chat_backend, gamespec, simple_singleplayer_mode ); smgr->clear(); } //try catch(con::PeerNotFoundException &e) { error_message = wgettext("Connection error (timed out?)"); errorstream<<wide_to_narrow(error_message)<<std::endl; } #ifdef NDEBUG catch(std::exception &e) { std::string narrow_message = "Some exception: \""; narrow_message += e.what(); narrow_message += "\""; errorstream<<narrow_message<<std::endl; error_message = narrow_to_wide(narrow_message); } #endif // If no main menu, show error and exit if(skip_main_menu) { if(error_message != L""){ verbosestream<<"error_message = " <<wide_to_narrow(error_message)<<std::endl; retval = 1; } break; } } // Menu-game loop g_menuclouds->drop(); g_menucloudsmgr->drop(); delete input; /* In the end, delete the Irrlicht device. */ device->drop(); #if USE_FREETYPE font->drop(); #endif #endif // !SERVER // Update configuration file if(configpath != "") g_settings->updateConfigFile(configpath.c_str()); // Print modified quicktune values { bool header_printed = false; std::vector<std::string> names = getQuicktuneNames(); for(u32 i=0; i<names.size(); i++){ QuicktuneValue val = getQuicktuneValue(names[i]); if(!val.modified) continue; if(!header_printed){ dstream<<"Modified quicktune values:"<<std::endl; header_printed = true; } dstream<<names[i]<<" = "<<val.getString()<<std::endl; } } END_DEBUG_EXCEPTION_HANDLER(errorstream) debugstreams_deinit(); return retval; } //END