$search
00001 /* 00002 Challenge - Virtual Robot Challenge System 00003 Copyright (C) 1999--2008: 00004 Stephane Magnenat <stephane at magnenat dot net> 00005 (http://stephane.magnenat.net) 00006 3D models 00007 Copyright (C) 2008: 00008 Basilio Noris 00009 Aseba - an event-based framework for distributed robot control 00010 Copyright (C) 2007--2012: 00011 Stephane Magnenat <stephane at magnenat dot net> 00012 (http://stephane.magnenat.net) 00013 and other contributors, see authors.txt for details 00014 00015 This program is free software: you can redistribute it and/or modify 00016 it under the terms of the GNU Lesser General Public License as published 00017 by the Free Software Foundation, version 3 of the License. 00018 00019 This program is distributed in the hope that it will be useful, 00020 but WITHOUT ANY WARRANTY; without even the implied warranty of 00021 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00022 GNU Lesser General Public License for more details. 00023 00024 You should have received a copy of the GNU Lesser General Public License 00025 along with this program. If not, see <http://www.gnu.org/licenses/>. 00026 */ 00027 00028 #ifndef ASEBA_ASSERT 00029 #define ASEBA_ASSERT 00030 #endif 00031 00032 #include "../../vm/vm.h" 00033 #include "../../vm/natives.h" 00034 #include "../../common/productids.h" 00035 #include "../../common/consts.h" 00036 #include "../../transport/buffer/vm-buffer.h" 00037 #include <dashel/dashel.h> 00038 #include <enki/PhysicalEngine.h> 00039 #include <enki/robots/e-puck/EPuck.h> 00040 #include <iostream> 00041 #include <QtGui> 00042 #include <QtDebug> 00043 #include "challenge.h" 00044 #include <challenge.moc> 00045 #include <string.h> 00046 00047 #define SIMPLIFIED_EPUCK 00048 00049 static void initTexturesResources() 00050 { 00051 Q_INIT_RESOURCE(challenge_textures); 00052 } 00053 00055 template<typename Derived, typename Base> 00056 inline Derived polymorphic_downcast(Base base) 00057 { 00058 Derived derived = dynamic_cast<Derived>(base); 00059 assert(derived); 00060 return derived; 00061 } 00062 00063 // Definition of the aseba glue 00064 00065 namespace Enki 00066 { 00067 class AsebaFeedableEPuck; 00068 } 00069 00070 // map for aseba glue code 00071 typedef QMap<AsebaVMState*, Enki::AsebaFeedableEPuck*> VmEPuckMap; 00072 static VmEPuckMap asebaEPuckMap; 00073 00074 static AsebaNativeFunctionPointer nativeFunctions[] = 00075 { 00076 ASEBA_NATIVES_STD_FUNCTIONS, 00077 }; 00078 00079 static const AsebaNativeFunctionDescription* nativeFunctionsDescriptions[] = 00080 { 00081 ASEBA_NATIVES_STD_DESCRIPTIONS, 00082 0 00083 }; 00084 00085 // changed by selection dialog 00086 static QString localName; 00087 00088 extern "C" AsebaVMDescription vmDescription_en; 00089 extern "C" AsebaVMDescription vmDescription_fr; 00090 00091 namespace Enki 00092 { 00093 #define EPUCK_FEEDER_INITIAL_ENERGY 10 00094 #define EPUCK_FEEDER_THRESHOLD_HIDE 2 00095 #define EPUCK_FEEDER_THRESHOLD_SHOW 4 00096 #define EPUCK_FEEDER_RADIUS 5 00097 #define EPUCK_FEEDER_RADIUS_DEAD 6 00098 #define EPUCK_FEEDER_RANGE 10 00099 00100 #define EPUCK_FEEDER_COLOR_ACTIVE Color::blue 00101 #define EPUCK_FEEDER_COLOR_INACTIVE Color::red 00102 #define EPUCK_FEEDER_COLOR_DEAD Color::gray 00103 00104 #define EPUCK_FEEDER_D_ENERGY 4 00105 #define EPUCK_FEEDER_RECHARGE_RATE 0.5 00106 #define EPUCK_FEEDER_MAX_ENERGY 100 00107 00108 #define EPUCK_FEEDER_LIFE_SPAN 60 00109 #define EPUCK_FEEDER_DEATH_SPAN 10 00110 00111 #define EPUCK_INITIAL_ENERGY 10 00112 #define EPUCK_ENERGY_CONSUMPTION_RATE 1 00113 00114 #define DEATH_ANIMATION_STEPS 30 00115 #define PORT_BASE ASEBA_DEFAULT_PORT 00116 00117 extern GLint GenFeederBase(); 00118 extern GLint GenFeederCharge0(); 00119 extern GLint GenFeederCharge1(); 00120 extern GLint GenFeederCharge2(); 00121 extern GLint GenFeederCharge3(); 00122 extern GLint GenFeederRing(); 00123 00124 class FeedableEPuck: public EPuck 00125 { 00126 public: 00127 double energy; 00128 double score; 00129 QString name; 00130 int diedAnimation; 00131 00132 public: 00133 FeedableEPuck() : EPuck(CAPABILITY_BASIC_SENSORS | CAPABILITY_CAMERA), energy(EPUCK_INITIAL_ENERGY), score(0), diedAnimation(-1) { } 00134 00135 virtual void controlStep(double dt) 00136 { 00137 EPuck::controlStep(dt); 00138 00139 energy -= dt * EPUCK_ENERGY_CONSUMPTION_RATE; 00140 score += dt; 00141 if (energy < 0) 00142 { 00143 score /= 2; 00144 energy = EPUCK_INITIAL_ENERGY; 00145 diedAnimation = DEATH_ANIMATION_STEPS; 00146 } 00147 else if (diedAnimation >= 0) 00148 diedAnimation--; 00149 } 00150 }; 00151 00152 class AsebaFeedableEPuck : public FeedableEPuck, public Dashel::Hub 00153 { 00154 public: 00155 Dashel::Stream* stream; 00156 AsebaVMState vm; 00157 std::valarray<unsigned short> bytecode; 00158 std::valarray<signed short> stack; 00159 struct Variables 00160 { 00161 sint16 productId; // product id 00162 sint16 speedL; // left motor speed 00163 sint16 speedR; // right motor speed 00164 sint16 colorR; // body red [0..100] % 00165 sint16 colorG; // body green [0..100] % 00166 sint16 colorB; // body blue [0..100] % 00167 #ifdef SIMPLIFIED_EPUCK 00168 sint16 dist_A[8]; // proximity sensors in dist [cm] as variables, normal e-puck order 00169 sint16 dist_B[8]; // proximity sensors in dist [cm] as array, normal e-puck order 00170 #else 00171 sint16 prox[8]; // proximity sensors, normal e-puck order 00172 #endif 00173 #ifdef SIMPLIFIED_EPUCK 00174 sint16 camR_A[3]; // camera red as variables (left, middle, right) [0..100] % 00175 sint16 camR_B[3]; // camera red as array (left, middle, right) [0..100] % 00176 sint16 camG_A[3]; // camera green as variables (left, middle, right) [0..100] % 00177 sint16 camG_B[3]; // camera green as array (left, middle, right) [0..100] % 00178 sint16 camB_A[3]; // camera blue as variables (left, middle, right) [0..100] % 00179 sint16 camB_B[3]; // camera blue as array (left, middle, right) [0..100] % 00180 #else 00181 sint16 camR[60]; // camera red (left, middle, right) [0..100] % 00182 sint16 camG[60]; // camera green (left, middle, right) [0..100] % 00183 sint16 camB[60]; // camera blue (left, middle, right) [0..100] % 00184 #endif 00185 sint16 energy; 00186 sint16 user[1024]; 00187 } variables; 00188 int port; 00189 00190 uint16 lastMessageSource; 00191 std::valarray<uint8> lastMessageData; 00192 00193 public: 00194 AsebaFeedableEPuck(int id) : 00195 stream(0) 00196 { 00197 asebaEPuckMap[&vm] = this; 00198 00199 vm.nodeId = 1; 00200 00201 bytecode.resize(512); 00202 vm.bytecode = &bytecode[0]; 00203 vm.bytecodeSize = bytecode.size(); 00204 00205 stack.resize(64); 00206 vm.stack = &stack[0]; 00207 vm.stackSize = stack.size(); 00208 00209 vm.variables = reinterpret_cast<sint16 *>(&variables); 00210 vm.variablesSize = sizeof(variables) / sizeof(sint16); 00211 00212 port = PORT_BASE+id; 00213 try 00214 { 00215 Dashel::Hub::connect(QString("tcpin:port=%1").arg(port).toStdString()); 00216 } 00217 catch (Dashel::DashelException e) 00218 { 00219 QMessageBox::critical(0, QApplication::tr("Aseba Challenge"), QApplication::tr("Cannot create listening port %0: %1").arg(port).arg(e.what())); 00220 abort(); 00221 } 00222 00223 AsebaVMInit(&vm); 00224 00225 variables.productId = ASEBA_PID_CHALLENGE; 00226 variables.colorG = 100; 00227 } 00228 00229 virtual ~AsebaFeedableEPuck() 00230 { 00231 asebaEPuckMap.remove(&vm); 00232 } 00233 00234 public: 00235 void connectionCreated(Dashel::Stream *stream) 00236 { 00237 std::string targetName = stream->getTargetName(); 00238 if (targetName.substr(0, targetName.find_first_of(':')) == "tcp") 00239 { 00240 qDebug() << this << " : New client connected."; 00241 if (this->stream) 00242 { 00243 closeStream(this->stream); 00244 qDebug() << this << " : Disconnected old client."; 00245 } 00246 this->stream = stream; 00247 } 00248 } 00249 00250 void incomingData(Dashel::Stream *stream) 00251 { 00252 uint16 temp; 00253 uint16 len; 00254 00255 stream->read(&temp, 2); 00256 len = bswap16(temp); 00257 stream->read(&temp, 2); 00258 lastMessageSource = bswap16(temp); 00259 lastMessageData.resize(len+2); 00260 stream->read(&lastMessageData[0], lastMessageData.size()); 00261 00262 if (bswap16(*(uint16*)&lastMessageData[0]) >= 0xA000) 00263 AsebaProcessIncomingEvents(&vm); 00264 else 00265 qDebug() << this << " : Non debug event dropped."; 00266 } 00267 00268 void connectionClosed(Dashel::Stream *stream, bool abnormal) 00269 { 00270 if (stream == this->stream) 00271 { 00272 this->stream = 0; 00273 // clear breakpoints 00274 vm.breakpointsCount = 0; 00275 } 00276 if (abnormal) 00277 qDebug() << this << " : Client has disconnected unexpectedly."; 00278 else 00279 qDebug() << this << " : Client has disconnected properly."; 00280 } 00281 00282 double toDoubleClamp(sint16 val, double mul, double min, double max) 00283 { 00284 double v = static_cast<double>(val) * mul; 00285 if (v > max) 00286 v = max; 00287 else if (v < min) 00288 v = min; 00289 return v; 00290 } 00291 00292 void controlStep(double dt) 00293 { 00294 // set physical variables 00295 leftSpeed = toDoubleClamp(variables.speedL, 1, -13, 13); 00296 rightSpeed = toDoubleClamp(variables.speedR, 1, -13, 13); 00297 Color c; 00298 c.setR(toDoubleClamp(variables.colorR, 0.01, 0, 1)); 00299 c.setG(toDoubleClamp(variables.colorG, 0.01, 0, 1)); 00300 c.setB(toDoubleClamp(variables.colorB, 0.01, 0, 1)); 00301 setColor(c); 00302 00303 // do motion 00304 FeedableEPuck::controlStep(dt); 00305 00306 // get physical variables 00307 #ifdef SIMPLIFIED_EPUCK 00308 variables.dist_A[0] = static_cast<sint16>(infraredSensor0.getDist()); 00309 variables.dist_A[1] = static_cast<sint16>(infraredSensor1.getDist()); 00310 variables.dist_A[2] = static_cast<sint16>(infraredSensor2.getDist()); 00311 variables.dist_A[3] = static_cast<sint16>(infraredSensor3.getDist()); 00312 variables.dist_A[4] = static_cast<sint16>(infraredSensor4.getDist()); 00313 variables.dist_A[5] = static_cast<sint16>(infraredSensor5.getDist()); 00314 variables.dist_A[6] = static_cast<sint16>(infraredSensor6.getDist()); 00315 variables.dist_A[7] = static_cast<sint16>(infraredSensor7.getDist()); 00316 for (size_t i = 0; i < 8; ++i) 00317 variables.dist_B[i] = variables.dist_A[i]; 00318 #else 00319 variables.prox[0] = static_cast<sint16>(infraredSensor0.finalValue); 00320 variables.prox[1] = static_cast<sint16>(infraredSensor1.finalValue); 00321 variables.prox[2] = static_cast<sint16>(infraredSensor2.finalValue); 00322 variables.prox[3] = static_cast<sint16>(infraredSensor3.finalValue); 00323 variables.prox[4] = static_cast<sint16>(infraredSensor4.finalValue); 00324 variables.prox[5] = static_cast<sint16>(infraredSensor5.finalValue); 00325 variables.prox[6] = static_cast<sint16>(infraredSensor6.finalValue); 00326 variables.prox[7] = static_cast<sint16>(infraredSensor7.finalValue); 00327 #endif 00328 00329 #ifdef SIMPLIFIED_EPUCK 00330 for (size_t i = 0; i < 3; i++) 00331 { 00332 double sumR = 0; 00333 double sumG = 0; 00334 double sumB = 0; 00335 for (size_t j = 0; j < 20; j++) 00336 { 00337 size_t index = 59 - (i * 20 + j); 00338 sumR += camera.image[index].r(); 00339 sumG += camera.image[index].g(); 00340 sumB += camera.image[index].b(); 00341 } 00342 variables.camR_A[i] = variables.camR_B[i] = static_cast<sint16>(sumR * 100. / 20.); 00343 variables.camG_A[i] = variables.camG_B[i] = static_cast<sint16>(sumG * 100. / 20.); 00344 variables.camB_A[i] = variables.camB_B[i] = static_cast<sint16>(sumB * 100. / 20.); 00345 } 00346 #else 00347 for (size_t i = 0; i < 60; i++) 00348 { 00349 variables.camR[i] = static_cast<sint16>(camera.image[i].r() * 100.); 00350 variables.camG[i] = static_cast<sint16>(camera.image[i].g() * 100.); 00351 variables.camB[i] = static_cast<sint16>(camera.image[i].b() * 100.); 00352 } 00353 #endif 00354 00355 variables.energy = static_cast<sint16>(energy); 00356 00357 // do a network step 00358 Hub::step(); 00359 00360 // run VM 00361 AsebaVMRun(&vm, 65535); 00362 00363 // reschedule a periodic event if we are not in step by step 00364 if (AsebaMaskIsClear(vm.flags, ASEBA_VM_STEP_BY_STEP_MASK) || AsebaMaskIsClear(vm.flags, ASEBA_VM_EVENT_ACTIVE_MASK)) 00365 AsebaVMSetupEvent(&vm, ASEBA_EVENT_LOCAL_EVENTS_START); 00366 } 00367 }; 00368 00369 class EPuckFeeding : public LocalInteraction 00370 { 00371 public: 00372 double energy; 00373 double age; 00374 bool alive; 00375 00376 public : 00377 EPuckFeeding(Robot *owner, double age) : energy(EPUCK_FEEDER_INITIAL_ENERGY), age(age) 00378 { 00379 r = EPUCK_FEEDER_RANGE; 00380 this->owner = owner; 00381 alive = true; 00382 } 00383 00384 void objectStep(double dt, World *w, PhysicalObject *po) 00385 { 00386 if (alive) 00387 { 00388 FeedableEPuck *epuck = dynamic_cast<FeedableEPuck *>(po); 00389 if (epuck && energy > 0) 00390 { 00391 double dEnergy = dt * EPUCK_FEEDER_D_ENERGY; 00392 epuck->energy += dEnergy; 00393 energy -= dEnergy; 00394 if (energy < EPUCK_FEEDER_THRESHOLD_HIDE) 00395 owner->setColor(EPUCK_FEEDER_COLOR_INACTIVE); 00396 } 00397 } 00398 } 00399 00400 void finalize(double dt, World *w) 00401 { 00402 age += dt; 00403 if (alive) 00404 { 00405 if ((energy < EPUCK_FEEDER_THRESHOLD_SHOW) && (energy+dt >= EPUCK_FEEDER_THRESHOLD_SHOW)) 00406 owner->setColor(EPUCK_FEEDER_COLOR_ACTIVE); 00407 energy += EPUCK_FEEDER_RECHARGE_RATE * dt; 00408 if (energy > EPUCK_FEEDER_MAX_ENERGY) 00409 energy = EPUCK_FEEDER_MAX_ENERGY; 00410 00411 if (age > EPUCK_FEEDER_LIFE_SPAN) 00412 { 00413 alive = false; 00414 age = 0; 00415 owner->setColor(EPUCK_FEEDER_COLOR_DEAD); 00416 owner->setCylindric(EPUCK_FEEDER_RADIUS_DEAD, owner->getHeight(), owner->getMass()); 00417 } 00418 } 00419 else 00420 { 00421 if (age > EPUCK_FEEDER_DEATH_SPAN) 00422 { 00423 alive = true; 00424 age = 0; 00425 owner->setColor(EPUCK_FEEDER_COLOR_ACTIVE); 00426 owner->setCylindric(EPUCK_FEEDER_RADIUS, owner->getHeight(), owner->getMass()); 00427 } 00428 } 00429 } 00430 }; 00431 00432 class EPuckFeeder : public Robot 00433 { 00434 public: 00435 EPuckFeeding feeding; 00436 00437 public: 00438 EPuckFeeder(double age) : feeding(this, age) 00439 { 00440 setCylindric(EPUCK_FEEDER_RADIUS, 5, -1); 00441 addLocalInteraction(&feeding); 00442 setColor(EPUCK_FEEDER_COLOR_ACTIVE); 00443 } 00444 }; 00445 00446 class FeederModel : public ViewerWidget::CustomRobotModel 00447 { 00448 public: 00449 FeederModel(ViewerWidget* viewer) 00450 { 00451 textures.resize(2); 00452 textures[0] = viewer->bindTexture(QPixmap(QString(":/textures/feeder.png")), GL_TEXTURE_2D); 00453 textures[1] = viewer->bindTexture(QPixmap(QString(":/textures/feederr.png")), GL_TEXTURE_2D, GL_LUMINANCE8); 00454 lists.resize(6); 00455 lists[0] = GenFeederBase(); 00456 lists[1] = GenFeederCharge0(); 00457 lists[2] = GenFeederCharge1(); 00458 lists[3] = GenFeederCharge2(); 00459 lists[4] = GenFeederCharge3(); 00460 lists[5] = GenFeederRing(); 00461 } 00462 00463 void cleanup(ViewerWidget* viewer) 00464 { 00465 for (int i = 0; i < textures.size(); i++) 00466 viewer->deleteTexture(textures[i]); 00467 for (int i = 0; i < lists.size(); i++) 00468 glDeleteLists(lists[i], 1); 00469 } 00470 00471 virtual void draw(PhysicalObject* object) const 00472 { 00473 EPuckFeeder* feeder = polymorphic_downcast<EPuckFeeder*>(object); 00474 double age = feeder->feeding.age; 00475 bool alive = feeder->feeding.alive; 00476 00477 glEnable(GL_TEXTURE_2D); 00478 glBindTexture(GL_TEXTURE_2D, textures[0]); 00479 00480 glPushMatrix(); 00481 double disp; 00482 if (age < M_PI/2) 00483 { 00484 //glTranslated(0, 0, -0.2); 00485 if (alive) 00486 disp = -1 + sin(age); 00487 else 00488 disp = -sin(age); 00489 } 00490 else 00491 { 00492 if (alive) 00493 disp = 0; 00494 else 00495 disp = -1; 00496 } 00497 00498 glTranslated(0, 0, 4.3*disp-0.2); 00499 00500 // body 00501 glColor3d(1, 1, 1); 00502 glCallList(lists[0]); 00503 00504 // ring 00505 glColor3d(0.6+object->getColor().components[0]-0.3*object->getColor().components[1]-0.3*object->getColor().components[2], 0.6+object->getColor().components[1]-0.3*object->getColor().components[0]-0.3*object->getColor().components[2], 0.6+object->getColor().components[2]-0.3*object->getColor().components[0]-0.3*object->getColor().components[1]); 00506 glCallList(lists[5]); 00507 00508 // food 00509 glColor3d(0.3, 0.3, 1); 00510 int foodAmount = (int)((feeder->feeding.energy * 5) / (EPUCK_FEEDER_MAX_ENERGY + 0.001)); 00511 assert(foodAmount <= 4); 00512 for (int i = 0; i < foodAmount; i++) 00513 glCallList(lists[1+i]); 00514 00515 glPopMatrix(); 00516 00517 // shadow 00518 glColor3d(1, 1, 1); 00519 glBindTexture(GL_TEXTURE_2D, textures[1]); 00520 glDisable(GL_LIGHTING); 00521 glEnable(GL_BLEND); 00522 glBlendFunc(GL_ZERO, GL_SRC_COLOR); 00523 00524 // bottom shadow 00525 glPushMatrix(); 00526 // disable writing of z-buffer 00527 glDepthMask( GL_FALSE ); 00528 glEnable(GL_POLYGON_OFFSET_FILL); 00529 //glScaled(1-disp*0.1, 1-disp*0.1, 1); 00530 glBegin(GL_QUADS); 00531 glTexCoord2f(1.f, 0.f); 00532 glVertex2f(-7.5f, -7.5f); 00533 glTexCoord2f(1.f, 1.f); 00534 glVertex2f(7.5f, -7.5f); 00535 glTexCoord2f(0.f, 1.f); 00536 glVertex2f(7.5f, 7.5f); 00537 glTexCoord2f(0.f, 0.f); 00538 glVertex2f(-7.5f, 7.5f); 00539 glEnd(); 00540 glDisable(GL_POLYGON_OFFSET_FILL); 00541 glDepthMask( GL_TRUE ); 00542 glPopMatrix(); 00543 00544 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 00545 glDisable(GL_BLEND); 00546 glEnable(GL_LIGHTING); 00547 00548 glDisable(GL_TEXTURE_2D); 00549 } 00550 00551 virtual void drawSpecial(PhysicalObject* object, int param) const 00552 { 00553 /*glEnable(GL_BLEND); 00554 glBlendFunc(GL_ONE, GL_ONE); 00555 glDisable(GL_TEXTURE_2D); 00556 glCallList(lists[0]); 00557 glDisable(GL_BLEND);*/ 00558 } 00559 }; 00560 00561 // Challenge Viewer 00562 00563 ChallengeViewer::ChallengeViewer(World* world, int ePuckCount) : ViewerWidget(world), ePuckCount(ePuckCount) 00564 { 00565 savingVideo = false; 00566 autoCamera = false; 00567 initTexturesResources(); 00568 00569 int res = QFontDatabase::addApplicationFont(":/fonts/SF Old Republic SC.ttf"); 00570 Q_ASSERT(res != -1); 00571 Q_UNUSED(res); 00572 //qDebug() << QFontDatabase::applicationFontFamilies(res); 00573 titleFont = QFont("SF Old Republic SC", 20); 00574 entryFont = QFont("SF Old Republic SC", 23); 00575 labelFont = QFont("SF Old Republic SC", 16); 00576 00577 setMouseTracking(true); 00578 setAttribute(Qt::WA_OpaquePaintEvent); 00579 setAttribute(Qt::WA_NoSystemBackground); 00580 setAutoFillBackground(false); 00581 //resize(780, 560); 00582 } 00583 00584 void ChallengeViewer::addNewRobot() 00585 { 00586 bool ok; 00587 QString eventName = QInputDialog::getText(this, tr("Add a new robot"), tr("Robot name:"), QLineEdit::Normal, "", &ok); 00588 if (ok && !eventName.isEmpty()) 00589 { 00590 // TODO change ePuckCount to port 00591 Enki::AsebaFeedableEPuck* epuck = new Enki::AsebaFeedableEPuck(ePuckCount++); 00592 epuck->pos.x = Enki::random.getRange(120)+10; 00593 epuck->pos.y = Enki::random.getRange(120)+10; 00594 epuck->name = eventName; 00595 world->addObject(epuck); 00596 } 00597 } 00598 00599 void ChallengeViewer::removeRobot() 00600 { 00601 std::set<AsebaFeedableEPuck *> toFree; 00602 // TODO: for now, remove all robots; later, show a gui box to choose which robot to remove 00603 for (World::ObjectsIterator it = world->objects.begin(); it != world->objects.end(); ++it) 00604 { 00605 AsebaFeedableEPuck *epuck = dynamic_cast<AsebaFeedableEPuck*>(*it); 00606 if (epuck) 00607 toFree.insert(epuck); 00608 } 00609 00610 for (std::set<AsebaFeedableEPuck *>::iterator it = toFree.begin(); it != toFree.end(); ++it) 00611 { 00612 world->removeObject(*it); 00613 delete *it; 00614 } 00615 ePuckCount = 0; 00616 } 00617 00618 void ChallengeViewer::autoCameraStateChanged(bool state) 00619 { 00620 autoCamera = state; 00621 } 00622 00623 void ChallengeViewer::timerEvent(QTimerEvent * event) 00624 { 00625 if (autoCamera) 00626 { 00627 altitude = 70; 00628 yaw += 0.002; 00629 pos = QPointF(-world->w/2 + 120*sin(yaw+M_PI/2), -world->h/2 + 120*cos(yaw+M_PI/2)); 00630 if (yaw > 2*M_PI) 00631 yaw -= 2*M_PI; 00632 pitch = M_PI/7 ; 00633 } 00634 ViewerWidget::timerEvent(event); 00635 } 00636 /* 00637 void ChallengeViewer::mouseMoveEvent ( QMouseEvent * event ) 00638 { 00639 #ifndef Q_WS_MAC 00640 00641 bool isInButtonArea = event->y() < addRobotButton->y() + addRobotButton->height() + 10; 00642 if (hideButtons->isChecked()) 00643 { 00644 if (isInButtonArea && !addRobotButton->isVisible()) 00645 { 00646 menuFrame->show(); 00647 } 00648 if (!isInButtonArea && addRobotButton->isVisible()) 00649 { 00650 menuFrame->hide(); 00651 } 00652 } 00653 00654 #endif // Q_WS_MAC 00655 00656 ViewerWidget::mouseMoveEvent(event); 00657 } 00658 */ 00659 void ChallengeViewer::keyPressEvent ( QKeyEvent * event ) 00660 { 00661 if (event->key() == Qt::Key_V) 00662 savingVideo = true; 00663 else 00664 ViewerWidget::keyPressEvent(event); 00665 } 00666 00667 void ChallengeViewer::keyReleaseEvent ( QKeyEvent * event ) 00668 { 00669 if (event->key() == Qt::Key_V) 00670 savingVideo = false; 00671 else 00672 ViewerWidget::keyReleaseEvent (event); 00673 } 00674 00675 void ChallengeViewer::drawQuad2D(double x, double y, double w, double ar) 00676 { 00677 double thisAr = (double)width() / (double)height(); 00678 double h = (w * thisAr) / ar; 00679 glBegin(GL_QUADS); 00680 glTexCoord2d(0, 1); 00681 glVertex2d(x, y); 00682 glTexCoord2d(1, 1); 00683 glVertex2d(x+w, y); 00684 glTexCoord2d(1, 0); 00685 glVertex2d(x+w, y+h); 00686 glTexCoord2d(0, 0); 00687 glVertex2d(x, y+h); 00688 glEnd(); 00689 } 00690 00691 void ChallengeViewer::initializeGL() 00692 { 00693 ViewerWidget::initializeGL(); 00694 } 00695 00696 void ChallengeViewer::renderObjectsTypesHook() 00697 { 00698 // render vrcs specific static types 00699 managedObjects[&typeid(EPuckFeeder)] = new FeederModel(this); 00700 managedObjectsAliases[&typeid(AsebaFeedableEPuck)] = &typeid(EPuck); 00701 } 00702 00703 void ChallengeViewer::displayObjectHook(PhysicalObject *object) 00704 { 00705 FeedableEPuck *epuck = dynamic_cast<FeedableEPuck*>(object); 00706 if ((epuck) && (epuck->diedAnimation >= 0)) 00707 { 00708 ViewerUserData *userData = dynamic_cast<ViewerUserData *>(epuck->userData); 00709 assert(userData); 00710 00711 double dist = (double)(DEATH_ANIMATION_STEPS - epuck->diedAnimation); 00712 double coeff = (double)(epuck->diedAnimation) / DEATH_ANIMATION_STEPS; 00713 glColor3d(0.2*coeff, 0.2*coeff, 0.2*coeff); 00714 glTranslated(0, 0, 2. * dist); 00715 userData->drawSpecial(object); 00716 } 00717 /*FeedableEPuck *epuck = dynamic_cast<FeedableEPuck*>(object); 00718 { 00719 }*/ 00720 } 00721 00722 void ChallengeViewer::sceneCompletedHook() 00723 { 00724 // create a map with names and scores 00725 qglColor(Qt::black); 00726 QMultiMap<int, QStringList> scores; 00727 for (World::ObjectsIterator it = world->objects.begin(); it != world->objects.end(); ++it) 00728 { 00729 AsebaFeedableEPuck *epuck = dynamic_cast<AsebaFeedableEPuck*>(*it); 00730 if (epuck) 00731 { 00732 QStringList entry; 00733 entry << epuck->name << QString::number(epuck->port) << QString::number((int)epuck->energy) << QString::number((int)epuck->score); 00734 scores.insert((int)epuck->score, entry); 00735 renderText(epuck->pos.x, epuck->pos.y, 10, epuck->name, labelFont); 00736 } 00737 } 00738 00739 // build score texture 00740 QImage scoreBoard(512, 256, QImage::Format_ARGB32); 00741 scoreBoard.setDotsPerMeterX(2350); 00742 scoreBoard.setDotsPerMeterY(2350); 00743 QPainter painter(&scoreBoard); 00744 //painter.fillRect(scoreBoard.rect(), QColor(224,224,255,196)); 00745 painter.fillRect(scoreBoard.rect(), QColor(224,255,224,196)); 00746 00747 // draw lines 00748 painter.setBrush(Qt::NoBrush); 00749 QPen pen(Qt::black); 00750 pen.setWidth(2); 00751 painter.setPen(pen); 00752 painter.drawRect(scoreBoard.rect()); 00753 pen.setWidth(1); 00754 painter.setPen(pen); 00755 painter.drawLine(22, 34, 504, 34); 00756 painter.drawLine(312, 12, 312, 247); 00757 painter.drawLine(312, 240, 504, 240); 00758 00759 // draw title 00760 painter.setFont(titleFont); 00761 painter.drawText(35, 28, "name"); 00762 painter.drawText(200, 28, "port"); 00763 painter.drawText(324, 28, "energy"); 00764 painter.drawText(430, 28, "points"); 00765 00766 // display entries 00767 QMapIterator<int, QStringList> it(scores); 00768 00769 it.toBack(); 00770 int pos = 61; 00771 while (it.hasPrevious()) 00772 { 00773 it.previous(); 00774 painter.drawText(200, pos, it.value().at(1)); 00775 pos += 24; 00776 } 00777 00778 it.toBack(); 00779 painter.setFont(entryFont); 00780 pos = 61; 00781 while (it.hasPrevious()) 00782 { 00783 it.previous(); 00784 painter.drawText(35, pos, it.value().at(0)); 00785 painter.drawText(335, pos, it.value().at(2)); 00786 painter.drawText(445, pos, it.value().at(3)); 00787 pos += 24; 00788 } 00789 00790 glDisable(GL_LIGHTING); 00791 glEnable(GL_TEXTURE_2D); 00792 GLuint tex = bindTexture(scoreBoard, GL_TEXTURE_2D); 00793 glEnable(GL_BLEND); 00794 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 00795 00796 glCullFace(GL_FRONT); 00797 glColor4d(1, 1, 1, 0.75); 00798 for (int i = 0; i < 4; i++) 00799 { 00800 glPushMatrix(); 00801 glTranslated(world->w/2, world->h/2, 50); 00802 glRotated(90*i, 0, 0, 1); 00803 glBegin(GL_QUADS); 00804 glTexCoord2d(0, 0); 00805 glVertex3d(-20, -20, 0); 00806 glTexCoord2d(1, 0); 00807 glVertex3d(20, -20, 0); 00808 glTexCoord2d(1, 1); 00809 glVertex3d(20, -20, 20); 00810 glTexCoord2d(0, 1); 00811 glVertex3d(-20, -20, 20); 00812 glEnd(); 00813 glPopMatrix(); 00814 } 00815 00816 glCullFace(GL_BACK); 00817 glColor3d(1, 1, 1); 00818 for (int i = 0; i < 4; i++) 00819 { 00820 glPushMatrix(); 00821 glTranslated(world->w/2, world->h/2, 50); 00822 glRotated(90*i, 0, 0, 1); 00823 glBegin(GL_QUADS); 00824 glTexCoord2d(0, 0); 00825 glVertex3d(-20, -20, 0); 00826 glTexCoord2d(1, 0); 00827 glVertex3d(20, -20, 0); 00828 glTexCoord2d(1, 1); 00829 glVertex3d(20, -20, 20); 00830 glTexCoord2d(0, 1); 00831 glVertex3d(-20, -20, 20); 00832 glEnd(); 00833 glPopMatrix(); 00834 } 00835 00836 deleteTexture(tex); 00837 00838 glDisable(GL_TEXTURE_2D); 00839 glColor4d(7./8.,7./8.,1,0.75); 00840 glPushMatrix(); 00841 glTranslated(world->w/2, world->h/2, 50); 00842 glBegin(GL_QUADS); 00843 glVertex3d(-20,-20,20); 00844 glVertex3d(20,-20,20); 00845 glVertex3d(20,20,20); 00846 glVertex3d(-20,20,20); 00847 00848 glVertex3d(-20,20,0); 00849 glVertex3d(20,20,0); 00850 glVertex3d(20,-20,0); 00851 glVertex3d(-20,-20,0); 00852 glEnd(); 00853 glPopMatrix(); 00854 00855 // save image 00856 static int imageCounter = 0; 00857 if (savingVideo) 00858 grabFrameBuffer().save(QString("frame%0.bmp").arg(imageCounter++), "BMP"); 00859 } 00860 00861 00862 ChallengeApplication::ChallengeApplication(World* world, int ePuckCount) : 00863 viewer(world, ePuckCount) 00864 { 00865 // help viewer 00866 helpViewer = new QTextBrowser(); 00867 helpViewer->setReadOnly(true); 00868 helpViewer->resize(600, 500); 00869 // help files generated by txt2tags, xhtml mode, with TOC 00870 if (localName.left(2) == "fr") 00871 helpViewer->setSource(QString("qrc:/doc/challenge.fr.html")); 00872 else if (localName.left(2) == "es") 00873 helpViewer->setSource(QString("qrc:/doc/challenge.es.html")); 00874 else 00875 helpViewer->setSource(QString("qrc:/doc/challenge.en.html")); 00876 helpViewer->moveCursor(QTextCursor::Start); 00877 helpViewer->setWindowTitle(tr("Aseba Challenge Help")); 00878 00879 connect(this, SIGNAL(windowClosed()), helpViewer, SLOT(close())); 00880 00881 // main windows layout 00882 QVBoxLayout *vLayout = new QVBoxLayout; 00883 QHBoxLayout *hLayout = new QHBoxLayout; 00884 00885 hLayout->addStretch(); 00886 00887 // construction of menu frame 00888 QFrame* menuFrame = new QFrame(); 00889 //menuFrame->setFrameStyle(QFrame::Box | QFrame::Plain); 00890 menuFrame->setFrameStyle(QFrame::NoFrame | QFrame::Plain); 00891 QHBoxLayout *frameLayout = new QHBoxLayout; 00892 QPushButton* addRobotButton = new QPushButton(tr("Add a new robot")); 00893 frameLayout->addWidget(addRobotButton); 00894 QPushButton* delRobotButton = new QPushButton(tr("Remove all robots")); 00895 frameLayout->addWidget(delRobotButton); 00896 QCheckBox* autoCamera = new QCheckBox(tr("Auto camera")); 00897 frameLayout->addWidget(autoCamera); 00898 QCheckBox* fullScreen = new QCheckBox(tr("Full screen")); 00899 frameLayout->addWidget(fullScreen); 00900 //hideButtons = new QCheckBox(tr("Auto hide")); 00901 //frameLayout->addWidget(hideButtons); 00902 QPushButton* helpButton = new QPushButton(tr("Help")); 00903 frameLayout->addWidget(helpButton); 00904 QPushButton* quitButton = new QPushButton(tr("Quit")); 00905 frameLayout->addWidget(quitButton); 00906 menuFrame->setLayout(frameLayout); 00907 00908 // menuFrame->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); 00909 viewer.setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); 00910 resize(780, 560); 00911 00912 // construction of the screen layout 00913 hLayout->addWidget(menuFrame); 00914 hLayout->addStretch(); 00915 00916 vLayout->addLayout(hLayout); 00917 vLayout->addWidget(&viewer); 00918 vLayout->setContentsMargins(0,4,0,0); 00919 setLayout(vLayout); 00920 00921 connect(addRobotButton, SIGNAL(clicked()), &viewer, SLOT(addNewRobot())); 00922 connect(delRobotButton, SIGNAL(clicked()), &viewer, SLOT(removeRobot())); 00923 connect(autoCamera, SIGNAL(toggled(bool)), &viewer, SLOT(autoCameraStateChanged(bool))); 00924 connect(fullScreen, SIGNAL(toggled(bool)), SLOT(fullScreenStateChanged(bool))); 00925 connect(helpButton, SIGNAL(clicked()), helpViewer, SLOT(show())); 00926 connect(quitButton, SIGNAL(clicked()), SLOT(close())); 00927 00928 autoCamera->setCheckState(Qt::Checked); 00929 00930 /* 00931 #else // Q_WS_MAC 00932 00933 QMenuBar *menuBar = new QMenuBar(0); 00934 QMenu *menu = menuBar->addMenu(tr("Simulator control")); 00935 menu->addAction(tr("Add a new robot"), this, SLOT(addNewRobot())); 00936 menu->addAction(tr("Remove all robots"), this, SLOT(removeRobot())); 00937 menu->addSeparator(); 00938 autoCamera = new QAction(tr("Auto camera"), 0); 00939 autoCamera->setCheckable(true); 00940 menu->addAction(autoCamera); 00941 menu->addSeparator(); 00942 menu->addAction(tr("Help"), helpViewer, SLOT(show())); 00943 00944 #endif // Q_WS_MAC 00945 */ 00946 } 00947 00948 void ChallengeApplication::fullScreenStateChanged(bool fullScreen) 00949 { 00950 if (fullScreen) 00951 showFullScreen(); 00952 else 00953 showNormal(); 00954 } 00955 00956 void ChallengeApplication::closeEvent ( QCloseEvent * event ) 00957 { 00958 if (event->isAccepted()) 00959 emit windowClosed(); 00960 } 00961 } 00962 00963 // Implementation of aseba glue code 00964 00965 extern "C" void AsebaPutVmToSleep(AsebaVMState *vm) 00966 { 00967 } 00968 00969 extern "C" void AsebaSendBuffer(AsebaVMState *vm, const uint8* data, uint16 length) 00970 { 00971 Dashel::Stream* stream = asebaEPuckMap[vm]->stream; 00972 assert(stream); 00973 00974 try 00975 { 00976 uint16 temp; 00977 temp = bswap16(length - 2); 00978 stream->write(&temp, 2); 00979 temp = bswap16(vm->nodeId); 00980 stream->write(&temp, 2); 00981 stream->write(data, length); 00982 stream->flush(); 00983 } 00984 catch (Dashel::DashelException e) 00985 { 00986 std::cerr << "Cannot write to socket: " << stream->getFailReason() << std::endl; 00987 } 00988 } 00989 00990 extern "C" uint16 AsebaGetBuffer(AsebaVMState *vm, uint8* data, uint16 maxLength, uint16* source) 00991 { 00992 if (asebaEPuckMap[vm]->lastMessageData.size()) 00993 { 00994 *source = asebaEPuckMap[vm]->lastMessageSource; 00995 memcpy(data, &asebaEPuckMap[vm]->lastMessageData[0], asebaEPuckMap[vm]->lastMessageData.size()); 00996 } 00997 return asebaEPuckMap[vm]->lastMessageData.size(); 00998 } 00999 01000 extern "C" const AsebaVMDescription* AsebaGetVMDescription(AsebaVMState *vm) 01001 { 01002 if (localName == "fr") 01003 return &vmDescription_fr; 01004 else 01005 return &vmDescription_en; 01006 } 01007 01008 static const AsebaLocalEventDescription localEvents[] = { { "timer", "periodic timer at 50 Hz" }, { NULL, NULL }}; 01009 01010 extern "C" const AsebaLocalEventDescription * AsebaGetLocalEventsDescriptions(AsebaVMState *vm) 01011 { 01012 return localEvents; 01013 } 01014 01015 extern "C" const AsebaNativeFunctionDescription * const * AsebaGetNativeFunctionsDescriptions(AsebaVMState *vm) 01016 { 01017 return nativeFunctionsDescriptions; 01018 } 01019 01020 extern "C" void AsebaNativeFunction(AsebaVMState *vm, uint16 id) 01021 { 01022 nativeFunctions[id](vm); 01023 } 01024 01025 extern "C" void AsebaWriteBytecode(AsebaVMState *vm) 01026 { 01027 } 01028 01029 extern "C" void AsebaResetIntoBootloader(AsebaVMState *vm) 01030 { 01031 } 01032 01033 extern "C" void AsebaAssert(AsebaVMState *vm, AsebaAssertReason reason) 01034 { 01035 std::cerr << "\nFatal error: "; 01036 switch (vm->nodeId) 01037 { 01038 case 1: std::cerr << "left motor module"; break; 01039 case 2: std::cerr << "right motor module"; break; 01040 case 3: std::cerr << "proximity sensors module"; break; 01041 case 4: std::cerr << "distance sensors module"; break; 01042 default: std::cerr << "unknown module"; break; 01043 } 01044 std::cerr << " has produced exception: "; 01045 switch (reason) 01046 { 01047 case ASEBA_ASSERT_UNKNOWN: std::cerr << "undefined"; break; 01048 case ASEBA_ASSERT_UNKNOWN_UNARY_OPERATOR: std::cerr << "unknown unary operator"; break; 01049 case ASEBA_ASSERT_UNKNOWN_BINARY_OPERATOR: std::cerr << "unknown binary operator"; break; 01050 case ASEBA_ASSERT_UNKNOWN_BYTECODE: std::cerr << "unknown bytecode"; break; 01051 case ASEBA_ASSERT_STACK_OVERFLOW: std::cerr << "stack overflow"; break; 01052 case ASEBA_ASSERT_STACK_UNDERFLOW: std::cerr << "stack underflow"; break; 01053 case ASEBA_ASSERT_OUT_OF_VARIABLES_BOUNDS: std::cerr << "out of variables bounds"; break; 01054 case ASEBA_ASSERT_OUT_OF_BYTECODE_BOUNDS: std::cerr << "out of bytecode bounds"; break; 01055 case ASEBA_ASSERT_STEP_OUT_OF_RUN: std::cerr << "step out of run"; break; 01056 case ASEBA_ASSERT_BREAKPOINT_OUT_OF_BYTECODE_BOUNDS: std::cerr << "breakpoint out of bytecode bounds"; break; 01057 case ASEBA_ASSERT_EMIT_BUFFER_TOO_LONG: std::cerr << "tried to emit a buffer too long"; break; 01058 default: std::cerr << "unknown exception"; break; 01059 } 01060 std::cerr << ".\npc = " << vm->pc << ", sp = " << vm->sp; 01061 std::cerr << "\nResetting VM" << std::endl; 01062 assert(false); 01063 AsebaVMInit(vm); 01064 } 01065 01066 01067 LanguageSelectionDialog::LanguageSelectionDialog() 01068 { 01069 QVBoxLayout* layout = new QVBoxLayout(this); 01070 01071 QLabel* text = new QLabel(tr("Please choose your language")); 01072 layout->addWidget(text); 01073 01074 languageSelectionBox = new QComboBox(this); 01075 languageSelectionBox->addItem(QString::fromUtf8("English"), "en"); 01076 languageSelectionBox->addItem(QString::fromUtf8("Français"), "fr"); 01077 languageSelectionBox->addItem(QString::fromUtf8("German"), "de"); 01078 languageSelectionBox->addItem(QString::fromUtf8("Español"), "es"); 01079 //qDebug() << "locale is " << QLocale::system().name(); 01080 for (int i = 0; i < languageSelectionBox->count(); ++i) 01081 { 01082 if (QLocale::system().name().startsWith(languageSelectionBox->itemData(i).toString())) 01083 { 01084 languageSelectionBox->setCurrentIndex(i); 01085 break; 01086 } 01087 } 01088 layout->addWidget(languageSelectionBox); 01089 01090 QPushButton* okButton = new QPushButton(QIcon(":/images/ok.png"), tr("Ok")); 01091 connect(okButton, SIGNAL(clicked(bool)), SLOT(accept())); 01092 layout->addWidget(okButton); 01093 01094 setWindowTitle(tr("Language selection")); 01095 } 01096 01097 01098 int main(int argc, char *argv[]) 01099 { 01100 QApplication app(argc, argv); 01101 01102 // Translation support 01103 QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); 01104 01105 QTranslator qtTranslator; 01106 qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); 01107 app.installTranslator(&qtTranslator); 01108 01109 //qDebug() << QLocale::system().name(); 01110 QTranslator translator; 01111 translator.load(QString(":/asebachallenge_") + QLocale::system().name()); 01112 app.installTranslator(&translator); 01113 01114 // choose the language 01115 { 01116 LanguageSelectionDialog languageSelectionDialog; 01117 languageSelectionDialog.show(); 01118 languageSelectionDialog.exec(); 01119 01120 localName = languageSelectionDialog.languageSelectionBox->itemData(languageSelectionDialog.languageSelectionBox->currentIndex()).toString(); 01121 qtTranslator.load(QString("qt_") + localName, QLibraryInfo::location(QLibraryInfo::TranslationsPath)); 01122 translator.load(QString(":/asebachallenge_") + localName); 01123 } 01124 01125 // Create the world 01126 Enki::World world(140, 140, Enki::Color(0.4, 0.4, 0.4)); 01127 01128 // Add feeders 01129 Enki::EPuckFeeder* feeders[4]; 01130 01131 feeders[0] = new Enki::EPuckFeeder(0); 01132 feeders[0]->pos.x = 40; 01133 feeders[0]->pos.y = 40; 01134 world.addObject(feeders[0]); 01135 01136 feeders[1] = new Enki::EPuckFeeder(15); 01137 feeders[1]->pos.x = 100; 01138 feeders[1]->pos.y = 40; 01139 world.addObject(feeders[1]); 01140 01141 feeders[2] = new Enki::EPuckFeeder(45); 01142 feeders[2]->pos.x = 40; 01143 feeders[2]->pos.y = 100; 01144 world.addObject(feeders[2]); 01145 01146 feeders[3] = new Enki::EPuckFeeder(30); 01147 feeders[3]->pos.x = 100; 01148 feeders[3]->pos.y = 100; 01149 world.addObject(feeders[3]); 01150 01151 // Add e-puck 01152 int ePuckCount = 0; 01153 for (int i = 1; i < argc; i++) 01154 { 01155 Enki::AsebaFeedableEPuck* epuck = new Enki::AsebaFeedableEPuck(i-1); 01156 epuck->pos.x = Enki::random.getRange(120)+10; 01157 epuck->pos.y = Enki::random.getRange(120)+10; 01158 epuck->name = argv[i]; 01159 world.addObject(epuck); 01160 ePuckCount++; 01161 } 01162 01163 // Create viewer 01164 Enki::ChallengeApplication viewer(&world, ePuckCount); 01165 01166 // Show and run 01167 viewer.setWindowTitle("ASEBA Challenge - Stephane Magnenat (code) - Basilio Noris (gfx)"); 01168 viewer.show(); 01169 01170 return app.exec(); 01171 }