void cCritterViewer::setViewpoint(cVector toviewer, cVector lookatpoint, BOOL trytoseewholeworld) { //First do some default setup stuff _fieldofviewangle = cCritterViewer::STARTFIELDOFVIEWANGLE; setSpeed(0.0); #ifndef THREEDVECTORS //not THREEDVECTORS means the 2D case. _attitude = cMatrix(cVector2(1.0, 0.0), cVector2(0.0, 1.0), _position); #else //THREEDVECTORS _attitude = cMatrix(-cVector::ZAXIS, -cVector::XAXIS, cVector::YAXIS, cVector::ZEROVECTOR); /* To get a reasonable default orientation, we arrange the viewer axes so that: viewer x axis = world -z axis, viewer y axis = world -x axis, viewer z axis = world y axis. We pick this orientation so that if the viewer moves "forward" (along its tangent vector) it moves towards the world. (We correct the mismatch between the coordinate systems in the cCritterViewer::loadViewMatrix method, which has a long comment about this.) Note that we will adjust _position (fourth column) later in this call with a moveTo, also we may rotate the _attitude a bit. */ if (!_perspective) //Ortho view, simply move up. { _proportionofworldtoshow = 1.0; //Show all of a flat world. moveTo(lookatpoint + cCritterViewer::ORTHOZOFFSET * cVector::ZAXIS); // Get above the world _maxspeed = _maxspeedstandard = 0.5 * cCritterViewer::ORTHOZOFFSET; //Mimic perspective case. } else //_perspective { if (toviewer.isPracticallyZero()) //Not usable, so pick a real direction. toviewer = cVector::ZAXIS; //Default is straight up. if (trytoseewholeworld) /* Treat toviewer as a direction, and back off in that direction enough to see the whole world */ { toviewer.normalize(); //Make it a unit vector. _proportionofworldtoshow = cCritterViewer::PROPORTIONOFWORLDTOSHOW; //Trying to show all of a world when flying around it, often leaves too big a space around it. Real furthestcornerdistance = pgame()->border().maxDistanceToCorner(lookatpoint); Real tanangle = tan(_fieldofviewangle/2.0); /* We work with half the fov in this calculation, the tanangle will be the ratio of visible distance to distance above the world, that is, tanangle = dr/dz, where Our dr is _proportionofworldtoshow * furthestcornerdistance, and our dz is the unknown seeallz height we need to back off to. Swap tangangle and dz to get the next formula. */ ASSERT(tanangle); Real seeallz = _proportionofworldtoshow * furthestcornerdistance / tanangle; moveTo(lookatpoint + seeallz * toviewer); } else /*Not trytoseewholeworld. In this case we don't normalize toviewer, instead we treat it as a displacment from the lookatpoint. */ moveTo(lookatpoint + toviewer); lookAt(lookatpoint); _maxspeed = _maxspeedstandard = 0.5 * (position()-lookatpoint).magnitude(); /* Define the speed like this so it typically takes two seconds (1/0.5) to fly in to lookatpoint. */ _lastgoodplayeroffset = position() - pgame()->pplayer()->position(); } #endif //THREEDVECTORS case }
cRealBox2 cCritterViewer::orthoViewRect() const { Real dx = _proportionofworldtoshow * pgame()->border().xsize(); Real dy = _proportionofworldtoshow * pgame()->border().ysize(); ASSERT(dy); Real tempaspect = dx/dy; if (tempaspect > _aspect) //dy is too small, make it bigger. dy = dx / _aspect; if (tempaspect < _aspect) //dx is too small, make it bigger. dx = _aspect * dy; return cRealBox2(dx, dy); /* This constructor makes a box centred at the origin. We don't center at positon, because once we do the loadViewMatrix translation, the origin is effectively at the critter location. Earlier I mistakenly had return cRealBox2(_position, dx, dy), and my ortho images were always off by a factor of two. */ }
int load_all_games() { all_games.clear(); std::string sql = "SELECT `id`,`tech_type`,\ `dir`, `exe`, `update_url`, `help_url`,\ `game_name`, `thumbnail`, `solution`, `no_embed`, `catalog` \ FROM `setting_game_list`"; Query q(*db_); if (!q.get_result(sql)) return error_sql_execute_error; while (q.fetch_row()) { gamei_ptr pgame(new game_info); pgame->id_ = q.getval(); pgame->type_ = q.getval(); COPY_STR(pgame->dir_, q.getstr()); COPY_STR(pgame->exe_, q.getstr()); COPY_STR(pgame->update_url_, q.getstr()); COPY_STR(pgame->help_url_, q.getstr()); COPY_STR(pgame->game_name_, q.getstr()); COPY_STR(pgame->thumbnail_, q.getstr()); COPY_STR(pgame->solution_, q.getstr()); pgame->no_embed_ = q.getval(); COPY_STR(pgame->catalog_, q.getstr()); all_games.insert(std::make_pair(pgame->id_, pgame)); } q.free_result(); sql = "SELECT `gameid`, `roomid`, `room_name`,\ `room_desc`, `thumbnail`, `srvip`, `srvport`, `require` \ FROM `setting_game_room_info`"; if (!q.get_result(sql)) return error_sql_execute_error; while (q.fetch_row()) { game_room_inf inf; inf.game_id_ = q.getval(); inf.room_id_ = q.getval(); COPY_STR(inf.room_name_, q.getstr()); COPY_STR(inf.room_desc_, q.getstr()); COPY_STR(inf.thumbnail_, q.getstr()); COPY_STR(inf.ip_, q.getstr()); inf.port_ = q.getval(); COPY_STR(inf.require_, q.getstr()); if (all_games.find(inf.game_id_) != all_games.end()){ all_games[inf.game_id_]->rooms_.push_back(inf); } } q.free_result(); return error_success; }
cCritterViewer::cCritterViewer(CPopView *pview): _pownerview(pview), _proportionofworldtoshow(cCritterViewer::PROPORTIONOFWORLDTOSHOW), _aspect(4.0/3.0), //Start with standard screen aspect. _fieldofviewangle(cCritterViewer::STARTFIELDOFVIEWANGLE), _trackplayer(FALSE), /* Depending on the game, you might want to start with either FALSE or TRUE for _trackplayer. In general the place where your game can make adjustments to the critterviewer is in the cGame::initializeView method. */ _perspective(FALSE), //Will be reset by setViewer, _foveaproportion(cCritterViewer::FOVEAPROPORTION) { resetAndMatchMoveBox(pgame()); }
void cCritterViewer::update(CPopView *pactiveview, Real dt) { cCritter::update(pactiveview, dt); setZClipPlanes((cRealBox3(pgame()->border())).outerBox(cSprite::MAXPRISMDZ)); /* This call updates _znear and _zfar. It explicity requires a cRealBox3 input, and we do cast of pgame()->border() BEFORE we compute the outer box. */ if (_trackplayer && pgame()->visibleplayer() && !(plistener() && plistener()->GetRuntimeClass() == RUNTIME_CLASS(cListenerViewerRide))) /* The meaning of the visibleplayer() condition is that it doesn't make sense to track the player if it's not an onscreen player. The reason for the listener condition is that you don't want to stare at the player when riding it. */ /* I should explain that the goal here is to not bother turning when the player is moving around in the middle of the veiw area, and only to turn when he's near the edge, but to have the turning when he's near the edge be smoooth. The use of the 0.85 foveaproportion parameter means that you react before the player gets right up to the edge. The reactproportion factor in lookAtProportional and moveToProportional is delicate and should probably be adjusted according to the current player speed relative to the visible window. The issue is that (a) if I make reactproportion too small, like 0.01, then the viewer doesn't turn (or move) fast enough to catch up with the player and keep it in view, but (b) if I make reactpropotion too big, like 0.5, then the turning or moving is such an abrupt jump that the visual effect is jerky. The goal is to do turns that are just big enough to not look jerky, but to have the turns be big enough so you aren't turning more often than you really have to. Another downside of a toosmall reactproportion, by the way, is that it can be computationally expensive to react. The way we finally solved this is to do a while loop to turn just far enough, moving just a little at a time so as to not overshoot. */ { if (isVisible(pgame()->pplayer()->position()))// Uses _foveaproportion _lastgoodplayeroffset = position() - pgame()->pplayer()->position(); /*I'm not sure about constantly changing _lastgoodplayeroffset. On the one hand, the offset I set in setViewpoint was a standard good one, so why not keep it. On the other, if I want to move my viewpoint around then I do want to be able to get a new value here. It seems ok for now.*/ else //not visible, so do somehting about it. { int loopcount = 0; /* Never have a while loop without a loopcount to make sure you don't spin inside the while forever under some unexpected situation like at startup. */ cVector lookat = pgame()->pplayer()->position(); cVector viewerpos = lookat + _lastgoodplayeroffset; if (pgame()->worldShape() == cGame::SHAPE_XSCROLLER) { lookat = cVector(pgame()->pplayer()->position().x(), pgame()->border().midy(), pgame()->pplayer()->position().z()); viewerpos = cVector(lookat.x(), position().y(), position().z()); } if (pgame()->worldShape() == cGame::SHAPE_YSCROLLER) { lookat = cVector(pgame()->border().midx(), pgame()->pplayer()->position().y(), pgame()->pplayer()->position().z()); viewerpos = cVector(position().x(), lookat.y(), position().z()); } if (_perspective) while (!isVisible(lookat) && loopcount < 100)// Uses _foveaproportion { #ifdef TURNMYHEADTOTRACKPLAYER lookAtProportional(lookat, cCritterViewer::TURNPROPORTION); #else //Don't TURNMYHEADTOTRACKPLAYER, instead move with the player. moveToProportional(viewerpos, cCritterViewer::TURNPROPORTION); #endif //TURNMYHEADTOTRACKPLAYER loopcount++; } else //ortho case while( !isVisible(lookat) && loopcount < 100)// Uses _foveaproportion { moveToProportional(lookat + 10.0*pgame()->pplayer()->binormal(), cCritterViewer::TURNPROPORTION); loopcount++; } } } //Possibly ride the player. if (plistener()->IsKindOf(RUNTIME_CLASS(cListenerViewerRide))) { cCritter *pplayer = pgame()->pplayer(); cVector offset = ((cListenerViewerRide*)plistener())->offset(); moveTo(pplayer->position() + offset.x()*pplayer->attitudeTangent() + offset.y()*pplayer->attitudeNormal() + offset.z()*pplayer->attitudeBinormal()); #ifdef COLLIDEVIEWER cRealBox skeleton = pplayer->moveBox(); if (skeleton.zsize()<0.5) skeleton.setZRange(0.0, offset.z()); if (skeleton.ysize()<0.5) skeleton.setYRange(0.0, offset.z()); skeleton.clamp(_position); for (int i=0; i<pgame()->pbiota()->count(); i++) { cCritter* pother = pgame()->pbiota()->GetAt(i); if (pother->IsKindOf(RUNTIME_CLASS(cCritterWall))) pother->collide(this); } /* colliding with the wall may have twisted the viwer's orientation, so align it once again. */ #endif //COLLIDEVIEWER setAttitude(pplayer->attitude()); /* Before we call lookAt, make sure your attitude matches the player. For one thing, you may have gotten twisted around in the COLLIDEVIEWER code. */ lookAt(pplayer->position() + cListenerViewerRide::PLAYERLOOKAHEAD * pplayer->radius() * pplayer->attitudeTangent()); /* This has the effect that as offset gets large you change your looking direction see right in front of the player. The multiplier cCritterViewer::PLAYERLOOKAHEAD is tweaked to work well with the default cCritterViewer::OFFSET. */ } }
void cCritterViewer::setDefaultView() { pgame()->initializeViewpoint(this); }