3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "main.h" // for g_settings
28 const s32 BOBFRAMES = 0x1000000; // must be a power of two
30 Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control):
35 m_draw_control(draw_control),
36 m_viewing_range_min(5.0),
37 m_viewing_range_max(5.0),
39 m_camera_position(0,0,0),
40 m_camera_direction(0,0,0),
46 m_wanted_frametime(0.0),
51 m_frametime_counter(0),
52 m_time_per_range(30. / 50), // a sane default of 30ms per 50 nodes of range
54 m_view_bobbing_anim(0),
55 m_view_bobbing_state(0),
56 m_view_bobbing_speed(0)
58 //dstream<<__FUNCTION_NAME<<std::endl;
60 // note: making the camera node a child of the player node
61 // would lead to unexpected behaviour, so we don't do that.
62 m_playernode = smgr->addEmptySceneNode(smgr->getRootSceneNode());
63 m_headnode = smgr->addEmptySceneNode(m_playernode);
64 m_cameranode = smgr->addCameraSceneNode(smgr->getRootSceneNode());
65 m_cameranode->bindTargetAndRotation(true);
66 m_wieldnode = new ExtrudedSpriteSceneNode(smgr->getRootSceneNode(), smgr, -1, v3f(0, 120, 10), v3f(0, 0, 0), v3f(100, 100, 100));
67 //m_wieldnode = new ExtrudedSpriteSceneNode(smgr->getRootSceneNode(), smgr, -1);
76 bool Camera::successfullyCreated(std::wstring& error_message)
78 if (m_playernode == NULL)
80 error_message = L"Failed to create the player scene node";
83 if (m_headnode == NULL)
85 error_message = L"Failed to create the head scene node";
88 if (m_cameranode == NULL)
90 error_message = L"Failed to create the camera scene node";
96 void Camera::step(f32 dtime)
98 if (m_view_bobbing_state != 0)
100 s32 offset = MYMAX(dtime * m_view_bobbing_speed * 0.035 * BOBFRAMES, 1);
101 if (m_view_bobbing_state == 2)
103 // Animation is getting turned off
104 s32 subanim = (m_view_bobbing_anim & (BOBFRAMES/2-1));
105 if (subanim < BOBFRAMES/4)
106 offset = -1 * MYMIN(offset, subanim);
108 offset = MYMIN(offset, BOBFRAMES/2 - subanim);
110 m_view_bobbing_anim = (m_view_bobbing_anim + offset) & (BOBFRAMES-1);
114 void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize)
116 // Set player node transformation
117 m_playernode->setPosition(player->getPosition());
118 m_playernode->setRotation(v3f(0, -1 * player->getYaw(), 0));
119 m_playernode->updateAbsolutePosition();
121 // Set head node transformation
122 v3f eye_offset = player->getEyePosition() - player->getPosition();
123 m_headnode->setPosition(eye_offset);
124 m_headnode->setRotation(v3f(player->getPitch(), 0, 0));
125 m_headnode->updateAbsolutePosition();
127 // Compute relative camera position and target
128 v3f rel_cam_pos = v3f(0,0,0);
129 v3f rel_cam_target = v3f(0,0,1);
130 v3f rel_cam_up = v3f(0,1,0);
132 s32 bobframe = m_view_bobbing_anim & (BOBFRAMES/2-1);
135 f32 bobfrac = (f32) bobframe / (BOBFRAMES/2);
136 f32 bobdir = (m_view_bobbing_anim < (BOBFRAMES/2)) ? 1.0 : -1.0;
140 f32 bobtmp = sin(pow(bobfrac, bobknob) * PI);
143 bobdir * sin(bobfrac * PI),
144 0.8 * bobtmp * bobtmp,
147 rel_cam_pos += 0.03 * bobvec;
148 rel_cam_target += 0.045 * bobvec;
149 rel_cam_up.rotateXYBy(0.03 * bobdir * bobtmp * PI);
151 f32 angle_deg = 1 * bobdir * sin(bobfrac * PI);
152 f32 angle_rad = angle_deg * PI / 180;
156 r * (cos(angle_rad) - 1),
159 //rel_cam_target += off;
160 rel_cam_up.rotateXYBy(angle_deg);
165 // Compute absolute camera position and target
166 m_headnode->getAbsoluteTransformation().transformVect(m_camera_position, rel_cam_pos);
167 m_headnode->getAbsoluteTransformation().transformVect(m_camera_direction, rel_cam_target);
168 m_camera_direction -= m_camera_position;
171 m_headnode->getAbsoluteTransformation().transformVect(abs_cam_up, rel_cam_pos + rel_cam_up);
172 abs_cam_up -= m_camera_position;
174 // Set camera node transformation
175 m_cameranode->setPosition(m_camera_position);
176 m_cameranode->setUpVector(abs_cam_up);
177 m_cameranode->setTarget(m_camera_position + m_camera_direction);
179 // FOV and and aspect ratio
180 m_aspect = (f32)screensize.X / (f32) screensize.Y;
181 m_fov_x = 2 * atan(0.5 * m_aspect * tan(m_fov_y));
182 m_cameranode->setAspectRatio(m_aspect);
183 m_cameranode->setFOV(m_fov_y);
184 // Just so big a value that everything rendered is visible
185 // Some more allowance that m_viewing_range_max * BS because of active objects etc.
186 m_cameranode->setFarValue(m_viewing_range_max * BS * 10);
188 // Render distance feedback loop
189 updateViewingRange(frametime);
191 // If the player seems to be walking on solid ground,
192 // view bobbing is enabled and free_move is off,
193 // start (or continue) the view bobbing animation.
194 v3f speed = player->getSpeed();
195 if ((hypot(speed.X, speed.Z) > BS) &&
196 (player->touching_ground) &&
197 (g_settings.getBool("view_bobbing") == true) &&
198 (g_settings.getBool("free_move") == false))
201 m_view_bobbing_state = 1;
202 m_view_bobbing_speed = MYMIN(speed.getLength(), 40);
204 else if (m_view_bobbing_state == 1)
207 m_view_bobbing_state = 2;
208 m_view_bobbing_speed = 100;
210 else if (m_view_bobbing_state == 2 && bobframe == 0)
212 // Stop animation completed
213 m_view_bobbing_state = 0;
217 void Camera::updateViewingRange(f32 frametime_in)
219 if (m_draw_control.range_all)
222 m_added_frametime += frametime_in;
225 // Actually this counter kind of sucks because frametime is busytime
226 m_frametime_counter -= frametime_in;
227 if (m_frametime_counter > 0)
229 m_frametime_counter = 0.2;
231 /*dstream<<__FUNCTION_NAME
232 <<": Collected "<<m_added_frames<<" frames, total of "
233 <<m_added_frametime<<"s."<<std::endl;
235 dstream<<"m_draw_control.blocks_drawn="
236 <<m_draw_control.blocks_drawn
237 <<", m_draw_control.blocks_would_have_drawn="
238 <<m_draw_control.blocks_would_have_drawn
241 m_draw_control.wanted_min_range = m_viewing_range_min;
242 m_draw_control.wanted_max_blocks = (1.5*m_draw_control.blocks_would_have_drawn)+1;
243 if (m_draw_control.wanted_max_blocks < 10)
244 m_draw_control.wanted_max_blocks = 10;
246 f32 block_draw_ratio = 1.0;
247 if (m_draw_control.blocks_would_have_drawn != 0)
249 block_draw_ratio = (f32)m_draw_control.blocks_drawn
250 / (f32)m_draw_control.blocks_would_have_drawn;
253 // Calculate the average frametime in the case that all wanted
254 // blocks had been drawn
255 f32 frametime = m_added_frametime / m_added_frames / block_draw_ratio;
257 m_added_frametime = 0.0;
260 f32 wanted_frametime_change = m_wanted_frametime - frametime;
261 //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl;
263 // If needed frametime change is small, just return
264 if (fabs(wanted_frametime_change) < m_wanted_frametime*0.4)
266 //dstream<<"ignoring small wanted_frametime_change"<<std::endl;
270 f32 range = m_draw_control.wanted_range;
271 f32 new_range = range;
273 f32 d_range = range - m_range_old;
274 f32 d_frametime = frametime - m_frametime_old;
277 m_time_per_range = d_frametime / d_range;
280 // The minimum allowed calculated frametime-range derivative:
281 // Practically this sets the maximum speed of changing the range.
282 // The lower this value, the higher the maximum changing speed.
283 // A low value here results in wobbly range (0.001)
284 // A high value here results in slow changing range (0.0025)
285 // SUGG: This could be dynamically adjusted so that when
286 // the camera is turning, this is lower
287 //f32 min_time_per_range = 0.0015;
288 f32 min_time_per_range = 0.0010;
289 //f32 min_time_per_range = 0.05 / range;
290 if(m_time_per_range < min_time_per_range)
292 m_time_per_range = min_time_per_range;
293 //dstream<<"m_time_per_range="<<m_time_per_range<<" (min)"<<std::endl;
297 //dstream<<"m_time_per_range="<<m_time_per_range<<std::endl;
300 f32 wanted_range_change = wanted_frametime_change / m_time_per_range;
301 // Dampen the change a bit to kill oscillations
302 //wanted_range_change *= 0.9;
303 //wanted_range_change *= 0.75;
304 wanted_range_change *= 0.5;
305 //dstream<<"wanted_range_change="<<wanted_range_change<<std::endl;
307 // If needed range change is very small, just return
308 if(fabs(wanted_range_change) < 0.001)
310 //dstream<<"ignoring small wanted_range_change"<<std::endl;
314 new_range += wanted_range_change;
316 //f32 new_range_unclamped = new_range;
317 new_range = MYMAX(new_range, m_viewing_range_min);
318 new_range = MYMIN(new_range, m_viewing_range_max);
319 /*dstream<<"new_range="<<new_range_unclamped
320 <<", clamped to "<<new_range<<std::endl;*/
322 m_draw_control.wanted_range = new_range;
324 m_range_old = new_range;
325 m_frametime_old = frametime;
328 void Camera::updateSettings()
330 m_viewing_range_min = g_settings.getS16("viewing_range_nodes_min");
331 m_viewing_range_min = MYMAX(5.0, m_viewing_range_min);
333 m_viewing_range_max = g_settings.getS16("viewing_range_nodes_max");
334 m_viewing_range_max = MYMAX(m_viewing_range_min, m_viewing_range_max);
336 f32 fov_degrees = g_settings.getFloat("fov");
337 fov_degrees = MYMAX(fov_degrees, 10.0);
338 fov_degrees = MYMIN(fov_degrees, 170.0);
339 m_fov_y = fov_degrees * PI / 180.0;
341 f32 wanted_fps = g_settings.getFloat("wanted_fps");
342 wanted_fps = MYMAX(wanted_fps, 1.0);
343 m_wanted_frametime = 1.0 / wanted_fps;
346 void Camera::wield(InventoryItem* item)
350 dstream << "wield item: " << item->getName() << std::endl;
351 m_wieldnode->setSprite(item->getImageRaw());
352 m_wieldnode->setVisible(true);
356 dstream << "wield item: none" << std::endl;
357 m_wieldnode->setVisible(false);
361 void Camera::setDigging(bool digging)
367 ExtrudedSpriteSceneNode::ExtrudedSpriteSceneNode(
368 scene::ISceneNode* parent,
369 scene::ISceneManager* mgr,
375 ISceneNode(parent, mgr, id, position, rotation, scale)
377 m_meshnode = mgr->addMeshSceneNode(NULL, this, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true);
383 ExtrudedSpriteSceneNode::~ExtrudedSpriteSceneNode()
385 removeChild(m_meshnode);
390 void ExtrudedSpriteSceneNode::setSprite(video::ITexture* texture)
394 m_meshnode->setVisible(false);
398 io::path name = getExtrudedName(texture);
399 scene::IMeshCache* cache = SceneManager->getMeshCache();
400 scene::IAnimatedMesh* mesh = cache->getMeshByName(name);
403 // Extruded texture has been found in cache.
404 m_meshnode->setMesh(mesh);
408 // Texture was not yet extruded, do it now and save in cache
409 mesh = extrude(texture);
412 dstream << "Warning: failed to extrude sprite" << std::endl;
413 m_meshnode->setVisible(false);
416 cache->addMesh(name, mesh);
417 m_meshnode->setMesh(mesh);
421 m_meshnode->setScale(v3f(1, 1, m_thickness));
422 m_meshnode->getMaterial(0).setTexture(0, texture);
423 m_meshnode->getMaterial(0).setFlag(video::EMF_LIGHTING, false);
424 m_meshnode->getMaterial(0).setFlag(video::EMF_BILINEAR_FILTER, false);
425 m_meshnode->getMaterial(0).MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
426 m_meshnode->setVisible(true);
430 void ExtrudedSpriteSceneNode::setCube(video::ITexture* texture)
434 m_meshnode->setVisible(false);
438 if (m_cubemesh == NULL)
439 m_cubemesh = SceneManager->getGeometryCreator()->createCubeMesh(v3f(1));
441 m_meshnode->setMesh(m_cubemesh);
442 m_meshnode->setScale(v3f(1));
443 m_meshnode->getMaterial(0).setTexture(0, texture);
444 m_meshnode->getMaterial(0).setFlag(video::EMF_LIGHTING, false);
445 m_meshnode->getMaterial(0).setFlag(video::EMF_BILINEAR_FILTER, false);
446 m_meshnode->getMaterial(0).MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
447 m_meshnode->setVisible(true);
451 void ExtrudedSpriteSceneNode::removeSpriteFromCache(video::ITexture* texture)
453 scene::IMeshCache* cache = SceneManager->getMeshCache();
454 scene::IAnimatedMesh* mesh = cache->getMeshByName(getExtrudedName(texture));
456 cache->removeMesh(mesh);
459 void ExtrudedSpriteSceneNode::setSpriteThickness(f32 thickness)
461 m_thickness = thickness;
463 m_meshnode->setScale(v3f(1, 1, thickness));
466 const core::aabbox3d<f32>& ExtrudedSpriteSceneNode::getBoundingBox() const
468 return m_meshnode->getBoundingBox();
471 void ExtrudedSpriteSceneNode::OnRegisterSceneNode()
474 SceneManager->registerNodeForRendering(this);
475 ISceneNode::OnRegisterSceneNode();
478 void ExtrudedSpriteSceneNode::render()
483 io::path ExtrudedSpriteSceneNode::getExtrudedName(video::ITexture* texture)
485 io::path path = texture->getName();
486 path.append("/[extruded]");
490 scene::IAnimatedMesh* ExtrudedSpriteSceneNode::extrudeARGB(u32 width, u32 height, u8* data)
492 const s32 argb_wstep = 4 * width;
493 const s32 alpha_threshold = 1;
495 scene::IMeshBuffer* buf = new scene::SMeshBuffer();
496 video::SColor c(255,255,255,255);
500 video::S3DVertex vertices[8] =
502 video::S3DVertex(-0.5,-0.5,-0.5, 0,0,-1, c, 0,1),
503 video::S3DVertex(-0.5,0.5,-0.5, 0,0,-1, c, 0,0),
504 video::S3DVertex(0.5,0.5,-0.5, 0,0,-1, c, 1,0),
505 video::S3DVertex(0.5,-0.5,-0.5, 0,0,-1, c, 1,1),
506 video::S3DVertex(0.5,-0.5,0.5, 0,0,1, c, 1,1),
507 video::S3DVertex(0.5,0.5,0.5, 0,0,1, c, 1,0),
508 video::S3DVertex(-0.5,0.5,0.5, 0,0,1, c, 0,0),
509 video::S3DVertex(-0.5,-0.5,0.5, 0,0,1, c, 0,1),
511 u16 indices[12] = {0,1,2,2,3,0,4,5,6,6,7,4};
512 buf->append(vertices, 8, indices, 12);
516 // (add faces where a solid pixel is next to a transparent one)
517 u8* solidity = new u8[(width+2) * (height+2)];
518 u32 wstep = width + 2;
519 for (u32 y = 0; y < height + 2; ++y)
521 u8* scanline = solidity + y * wstep;
522 if (y == 0 || y == height + 1)
524 for (u32 x = 0; x < width + 2; ++x)
530 u8* argb_scanline = data + (y - 1) * argb_wstep;
531 for (u32 x = 0; x < width; ++x)
532 scanline[x+1] = (argb_scanline[x*4+3] >= alpha_threshold);
533 scanline[width + 1] = 0;
537 // without this, there would be occasional "holes" in the mesh
540 for (u32 y = 0; y <= height; ++y)
542 u8* scanline = solidity + y * wstep + 1;
543 for (u32 x = 0; x <= width; ++x)
545 if (scanline[x] && !scanline[x + wstep])
548 while (scanline[xx] && !scanline[xx + wstep])
550 f32 vx1 = (x - eps) / (f32) width - 0.5;
551 f32 vx2 = (xx + eps) / (f32) width - 0.5;
552 f32 vy = 0.5 - (y - eps) / (f32) height;
553 f32 tx1 = x / (f32) width;
554 f32 tx2 = xx / (f32) width;
555 f32 ty = (y - 0.5) / (f32) height;
556 video::S3DVertex vertices[8] =
558 video::S3DVertex(vx1,vy,-0.5, 0,-1,0, c, tx1,ty),
559 video::S3DVertex(vx2,vy,-0.5, 0,-1,0, c, tx2,ty),
560 video::S3DVertex(vx2,vy,0.5, 0,-1,0, c, tx2,ty),
561 video::S3DVertex(vx1,vy,0.5, 0,-1,0, c, tx1,ty),
563 u16 indices[6] = {0,1,2,2,3,0};
564 buf->append(vertices, 4, indices, 6);
567 if (!scanline[x] && scanline[x + wstep])
570 while (!scanline[xx] && scanline[xx + wstep])
572 f32 vx1 = (x - eps) / (f32) width - 0.5;
573 f32 vx2 = (xx + eps) / (f32) width - 0.5;
574 f32 vy = 0.5 - (y + eps) / (f32) height;
575 f32 tx1 = x / (f32) width;
576 f32 tx2 = xx / (f32) width;
577 f32 ty = (y + 0.5) / (f32) height;
578 video::S3DVertex vertices[8] =
580 video::S3DVertex(vx1,vy,-0.5, 0,1,0, c, tx1,ty),
581 video::S3DVertex(vx1,vy,0.5, 0,1,0, c, tx1,ty),
582 video::S3DVertex(vx2,vy,0.5, 0,1,0, c, tx2,ty),
583 video::S3DVertex(vx2,vy,-0.5, 0,1,0, c, tx2,ty),
585 u16 indices[6] = {0,1,2,2,3,0};
586 buf->append(vertices, 4, indices, 6);
592 for (u32 x = 0; x <= width; ++x)
594 u8* scancol = solidity + x + wstep;
595 for (u32 y = 0; y <= height; ++y)
597 if (scancol[y * wstep] && !scancol[y * wstep + 1])
600 while (scancol[yy * wstep] && !scancol[yy * wstep + 1])
602 f32 vx = (x - eps) / (f32) width - 0.5;
603 f32 vy1 = 0.5 - (y - eps) / (f32) height;
604 f32 vy2 = 0.5 - (yy + eps) / (f32) height;
605 f32 tx = (x - 0.5) / (f32) width;
606 f32 ty1 = y / (f32) height;
607 f32 ty2 = yy / (f32) height;
608 video::S3DVertex vertices[8] =
610 video::S3DVertex(vx,vy1,-0.5, 1,0,0, c, tx,ty1),
611 video::S3DVertex(vx,vy1,0.5, 1,0,0, c, tx,ty1),
612 video::S3DVertex(vx,vy2,0.5, 1,0,0, c, tx,ty2),
613 video::S3DVertex(vx,vy2,-0.5, 1,0,0, c, tx,ty2),
615 u16 indices[6] = {0,1,2,2,3,0};
616 buf->append(vertices, 4, indices, 6);
619 if (!scancol[y * wstep] && scancol[y * wstep + 1])
622 while (!scancol[yy * wstep] && scancol[yy * wstep + 1])
624 f32 vx = (x + eps) / (f32) width - 0.5;
625 f32 vy1 = 0.5 - (y - eps) / (f32) height;
626 f32 vy2 = 0.5 - (yy + eps) / (f32) height;
627 f32 tx = (x + 0.5) / (f32) width;
628 f32 ty1 = y / (f32) height;
629 f32 ty2 = yy / (f32) height;
630 video::S3DVertex vertices[8] =
632 video::S3DVertex(vx,vy1,-0.5, -1,0,0, c, tx,ty1),
633 video::S3DVertex(vx,vy2,-0.5, -1,0,0, c, tx,ty2),
634 video::S3DVertex(vx,vy2,0.5, -1,0,0, c, tx,ty2),
635 video::S3DVertex(vx,vy1,0.5, -1,0,0, c, tx,ty1),
637 u16 indices[6] = {0,1,2,2,3,0};
638 buf->append(vertices, 4, indices, 6);
645 scene::SMesh* mesh = new scene::SMesh();
646 mesh->addMeshBuffer(buf);
648 mesh->recalculateBoundingBox();
649 scene::SAnimatedMesh* anim_mesh = new scene::SAnimatedMesh(mesh);
654 scene::IAnimatedMesh* ExtrudedSpriteSceneNode::extrude(video::ITexture* texture)
656 scene::IAnimatedMesh* mesh = NULL;
657 core::dimension2d<u32> size = texture->getSize();
658 video::ECOLOR_FORMAT format = texture->getColorFormat();
659 if (format == video::ECF_A8R8G8B8)
661 // Texture is in the correct color format, we can pass it
662 // to extrudeARGB right away.
663 void* data = texture->lock(true);
666 mesh = extrudeARGB(size.Width, size.Height, (u8*) data);
671 video::IVideoDriver* driver = SceneManager->getVideoDriver();
673 video::IImage* img1 = driver->createImageFromData(format, size, texture->lock(true));
677 // img1 is in the texture's color format, convert to 8-bit ARGB
678 video::IImage* img2 = driver->createImage(video::ECF_A8R8G8B8, size);
684 mesh = extrudeARGB(size.Width, size.Height, (u8*) img2->lock());