./source/Editor.cpp

       1  #include <glPortal/Editor.hpp>
          
          namespace glPortal {
          
       5  Editor::Editor(  Window &win,   World &w )
           : window(  win ),   world(  w ) {
          }
          
       9  void Editor::render(   ) {
          
          }
          

./source/Game.cpp

       1  #include <glPortal/Game.hpp>
          #include <glPortal/GameState.hpp>
          
          #include <iostream>
          #include <radix/component/Trigger.hpp>
          #include <radix/component/Player.hpp>
          
          using namespace radix;
          
          namespace glPortal {
          
      12  Game::Game(   ) {
           windowTitle = "GlPortal";
           defaultMap = "/maps/n1.xml";
          }
          
      17  void Game::initHook(   ) {
           initFunctionStack(   );
           gameController = std::make_unique<GameController>(  this );
           initRenderers(   );
           addRenderers(   );
          }
          
      24  void Game::initFunctionStack(   ) {
           world.stateFunctionStack.push(  &GameState::handleRunning );
           world.stateFunctionStack.push(  &GameState::handleSplash );
          }
          
      29  void Game::processInput(   ) {
           gameController->processInput(   );
          }
          
      33  void Game::update(   ) {
           BaseGame::update(   );
           dtime = (  currentTime-lastRender )/1000.;
          }
          
      38  void Game::initRenderers(   ) {
           World& worldReference = static_cast<glPortal::World&>(  world );
           radix::Renderer& rendererReference = *renderer.get(   );
           gameRenderer =
           std::make_unique<GameRenderer>(  worldReference,   rendererReference,   camera.get(   ),   &dtime );
           uiRenderer =
           std::make_unique<UiRenderer>(  worldReference,   rendererReference );
           }
          
          
      48  void Game::addRenderers(   ) {
           renderer->addRenderer(  *gameRenderer );
           renderer->addRenderer(  *uiRenderer );
          }
          
          } /* namespace glPortal */

./source/GameController.cpp

       1  #include <glPortal/GameController.hpp>
          #include <glPortal/Game.hpp>
          
          #include <radix/env/Environment.hpp>
          #include <radix/component/Player.hpp>
          #include <glPortal/GameState.hpp>
          
          namespace glPortal {
          
      10  GameController::GameController(  Game *game ) {
           this->game = game;
           this->world = static_cast<World*>(  game->getWorld(   ) );
           this->gameState = std::make_unique<radix::GameState>(   );
           GameState::init(  *game );
           initObservers(   );
          }
          
      18  void GameController::initObservers(   ) {
           this->closeWindowHolder = game->getWorld(   )->event.addObserver(  
           radix::InputSource::WindowCloseEvent::Type,   [this](  const radix::Event& event ) {
           this->game->close(   );
           } );
          }
          
      25  void GameController::processInput(   ) {
           game->getWindow(   ).processEvents(   );
           if (  game->getWindow(   ).isKeyDown(  SDL_SCANCODE_Q ) ) {
           game->close(   );
           }
          
           gameState->handleInput(  *game );
          }
          
          } /* namespace glPortal */

./source/GameState.cpp

       1  #include <glPortal/GameState.hpp>
          #include <glPortal/Game.hpp>
          
          #include <radix/data/screen/XmlScreenLoader.hpp>
          #include <radix/env/Environment.hpp>
          #include <radix/component/Player.hpp>
          
          namespace glPortal {
          
          radix::EventDispatcher::CallbackHolder GameState::splashCallbackHolder;
          radix::EventDispatcher::CallbackHolder GameState::winCallbackHolder;
          
      13  void GameState::init(  radix::BaseGame &game ) {
           winCallbackHolder = game.getWorld(   )->event.addObserver
           (  
           radix::GameState::WinEvent::Type,   [&game](  const radix::Event &event ) {
           game.getWorld(   )->stateFunctionStack.push(  &GameState::handleGameOverScreen );
           }
            );
          
           winCallbackHolder.setStatic(   );
          }
          
      24  void GameState::handleRunning(  radix::BaseGame &game ) {
           radix::Player &player = game.getWorld(   )->getPlayer(   ).getComponent<radix::Player>(   );
           player.frozen = false;
          
           game.getWorld(   )->event.removeObserver(  splashCallbackHolder );
          
           splashCallbackHolder = game.getWorld(   )->event.addObserver
           (  
           radix::InputSource::KeyReleasedEvent::Type,   [&player,   &game](  const radix::Event& event ) {
           const int key = (  (  radix::InputSource::KeyReleasedEvent & ) event ).key;
           if (  key == SDL_SCANCODE_ESCAPE ) {
           player.frozen = true;
           game.getWorld(   )->stateFunctionStack.push(  &GameState::handlePaused );
           }
           } );
          
           if (  !splashCallbackHolder.getStatic(   ) ) {
           splashCallbackHolder.setStatic(   );
           }
          }
          
      45  void GameState::handlePaused(  radix::BaseGame &game ) {
           radix::Screen &screen = radix::XmlScreenLoader::getScreen(  radix::Environment::getDataDir(   )
           + "/screens/title.xml" );
           game.getGameWorld(   )->addScreen(  screen );
           radix::Player &player = game.getWorld(   )->getPlayer(   ).getComponent<radix::Player>(   );
          
           game.getWorld(   )->event.removeObserver(  splashCallbackHolder );
          
           splashCallbackHolder = game.getWorld(   )->event.addObserver
           (  
           radix::InputSource::KeyReleasedEvent::Type,   [&player,   &game] (  const radix::Event& event ) {
           const int key = (  (  radix::InputSource::KeyReleasedEvent & ) event ).key;
           if (  key == SDL_SCANCODE_ESCAPE ) {
           player.frozen = false;
           game.getWorld(   )->stateFunctionStack.pop(   );
           }
           } );
          }
          
      64  void GameState::handleSplash(  radix::BaseGame &game ) {
           radix::Player &player = game.getWorld(   )->getPlayer(   ).getComponent<radix::Player>(   );
           player.frozen = true;
           radix::Screen &screen =
           radix::XmlScreenLoader::getScreen(  radix::Environment::getDataDir(   ) + "/screens/test.xml" );
           game.getGameWorld(   )->addScreen(  screen );
           if (  game.getWindow(   ).isKeyDown(  SDL_SCANCODE_RETURN ) ) {
           game.getWorld(   )->stateFunctionStack.pop(   );
           }
          }
          
      75  void GameState::handleMenu(  radix::BaseGame &game ) { }
          
      77  void GameState::handleGameOverScreen(  radix::BaseGame &game ) {
           radix::Player &player = game.getWorld(   )->getPlayer(   ).getComponent<radix::Player>(   );
           player.frozen = true;
           radix::Screen &screen =
           radix::XmlScreenLoader::getScreen(  radix::Environment::getDataDir(   ) + "/screens/end.xml" );
           game.getGameWorld(   )->addScreen(  screen );
          }
          
      85  void GameState::handleWinScreen(  radix::BaseGame &game ) { }
          
          } /* namespace glPortal */

./source/Main.cpp

       1  #include <glPortal/Game.hpp>
          
          #include <radix/env/Environment.hpp>
          #include <radix/env/ArgumentsParser.hpp>
          #include <radix/core/diag/Throwables.hpp>
          
          using namespace glPortal;
          
       9  int main(  const int argc,   char *argv[] ) {
           radix::Util::Init(   );
           radix::ArgumentsParser::setEnvironmentFromArgs(  argc,   argv );
           try {
           Game game;
           game.setup(   );
           while (  game.isRunning(   ) ) {
           game.processInput(   );
           game.update(   );
           game.render(   );
           }
           game.cleanUp(   );
           } catch (  radix::Exception::Error &err ) {
           radix::Util::Log(  radix::Error,   err.source(   ) ) << err.what(   );
           }
          
           return 0;
          }

./source/Portal.cpp

       1  #include <glPortal/Portal.hpp>
          
          #include <algorithm>
          #include <bullet/BulletDynamics/Dynamics/btRigidBody.h>
          #include <bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
          
          #include <radix/data/model/MeshLoader.hpp>
          #include <radix/core/math/Math.hpp>
          #include <radix/Entity.hpp>
          #include <radix/component/Transform.hpp>
          #include <radix/physics/Uncollider.hpp>
          #include <radix/system/PhysicsSystem.hpp>
          #include <radix/World.hpp>
          
          using namespace radix;
          
          namespace glPortal {
          
          const int Portal::PORTAL_RANGE = 1000;
          const Vector3f Portal::BLUE_COLOR = Vector3f(  0.33,   0.57,   1 );
          const Vector3f Portal::ORANGE_COLOR = Vector3f(  1,   0.76,   0.33 );
          const double Portal::NOISE_FADE_DELAY = .300;
          const double Portal::OPEN_ANIM_DURATION = .250;
          const float Portal::SURFACE_OFFSET = 0.01f;
          
      26  Portal::Portal(  Entity &ent ) : radix::Component(  ent ),   openSince(  0 ),   open(  false ) {
           uncolliderMotionState.reset(  new btDefaultMotionState );
           wrapper.vertShape.reset(  new btBoxShape(  btVector3(  0.1,   1,   0.5 ) ) );
           wrapper.horzShape.reset(  new btBoxShape(  btVector3(  .5,   0.1,   0.5 ) ) );
           // TODO Handle collision subtraction better
           Uncollider::volumes.emplace_back(  uncollider.get(   ) );
          }
          
      34  Portal::~Portal(   ) {
           Uncollider::volumes.remove(  uncollider.get(   ) );
          }
          
      38  Vector3f Portal::getDirection(   ) const {
           return direction;
          }
          
      42  void Portal::placeWrapperPiece(  const Vector3f &p,   const Quaternion &o,   const Vector3f &s,  
      43   const std::unique_ptr<btCollisionShape> &shape,   Wrapper::Side &side,   const Vector3f &offset ) {
           side.motionState.reset(  new btDefaultMotionState );
           side.motionState->setWorldTransform(  
           btTransform(  btQuaternion(  0,   0,   0,   1 ),   p ) *
           btTransform(  o ) *
           btTransform(  btQuaternion(  0,   0,   0,   1 ),   offset ) );
           btRigidBody::btRigidBodyConstructionInfo ci(  0,   side.motionState.get(   ),  
           shape.get(   ),   btVector3(  0,   0,   0 ) );
           if (  side.body ) {
           entity.manager.world.systems.get<PhysicsSystem>(   ).getPhysicsWorld(   ).removeRigidBody(  side.body.get(   ) );
           }
           side.body.reset(  new btRigidBody(  ci ) );
           entity.manager.world.systems.get<PhysicsSystem>(   ).getPhysicsWorld(   ).addRigidBody(  side.body.get(   ) );
          }
          
      58  void Portal::placeOnWall(  const Vector3f &launchPos,   const Vector3f &point,   const Vector3f &normal ) {
           //Determine on what side the portal is
           //Side 0: -x,   Side 1: x,   Side 2: -z,   Side 3: z,   Side 4: -y,   Side 5: y
           /*float dist = 1000000;
           int side = 0;
          
           float *distances;
           distances = PortalHelper::getDistancesForPoint(  point,   wall );
          
           for (  int i = 0; i < 6; i++ ) {
           if (  distances[i] < dist ) {
           side = i;
           dist = distances[i];
           }
           }*/
          
           Transform &t = entity.getComponent<Transform>(   );
           Quaternion orientation;
           Vector3f position(  point );
           Vector3f scale(  1,   2,   1 );
           direction = normalize(  normal );
           if (  normal.fuzzyEqual(  Vector3f::FORWARD ) ) {
           // Edge case,   since the portal has a "default normal" of 0,   0,   -1
           // the below quaternion-from-normal formula would be ambiguous
           // and possibly fail
           orientation = Quaternion(   );
           } else if (  normal.fuzzyEqual(  Vector3f(  0,   0,   1 ) ) ) {
           // Same as above,   other side
           orientation = Quaternion(  0,   sin(  M_PI/2 ),   0,   cos(  M_PI/2 ) );
           } else if (  normal.fuzzyEqual(  Vector3f(  0,   1,   0 ) ) ) {
           // Floor
           float yRot = std::atan2(  point.x-launchPos.x,   point.z-launchPos.z );
           orientation.fromAero(  0,   rad(  90 ),   -yRot );
           } else if (  normal.fuzzyEqual(  Vector3f(  0,   -1,   0 ) ) ) {
           // Ceiling
           float yRot = std::atan2(  point.x-launchPos.x,   point.z-launchPos.z );
           orientation.fromAero(  0,   rad(  -90 ),   yRot );
           } else {
           // http://www.gamedev.net/topic/566295-normal-to-a-quaternion/
           const Vector3f from(  0,   0,   -1 );
           Vector3f H = normalize(  from + normal );
           orientation.w = dot(  from,   H );
           orientation.x = from.y*H.z - from.z*H.y;
           orientation.y = from.z*H.x - from.x*H.z;
           orientation.z = from.x*H.y - from.y*H.x;
           }
          
           // Enable the light
           // entity.getComponent<LightSource>(   ).enabled = true;
          
           placeWrapperPiece(  position,   orientation,   scale,  
           wrapper.horzShape,   wrapper.bottom,   Vector3f(  0,   -1.076,   0.501 ) );
           placeWrapperPiece(  position,   orientation,   scale,  
           wrapper.horzShape,   wrapper.top,   Vector3f(  0,   1.076,   0.501 ) );
           placeWrapperPiece(  position,   orientation,   scale,  
           wrapper.vertShape,   wrapper.right,   Vector3f(  0.576,   0,   0.501 ) );
           placeWrapperPiece(  position,   orientation,   scale,  
           wrapper.vertShape,   wrapper.left,   Vector3f(  -0.576,   0,   0.501 ) );
          
           uncolliderMotionState->setWorldTransform(  
           btTransform(  btQuaternion(  0,   0,   0,   1 ),   position ) *
           btTransform(  orientation ) *
           btTransform(  btQuaternion(  0,   0,   0,   1 ),   btVector3(  0,   0,   0.499 ) ) );
           uncolliderShape.reset(  new btBoxShape(  scale/2 ) );
           btRigidBody::btRigidBodyConstructionInfo ci(  0,   uncolliderMotionState.get(   ),  
           uncolliderShape.get(   ),   btVector3(  0,   0,   0 ) );
           uncollider.reset(  new btRigidBody(  ci ) );
          
           open = true;
           position += (  getDirection(   ) * SURFACE_OFFSET );
           overlayMesh = MeshLoader::getMesh(  "Plane.obj" );
           stencilMesh = MeshLoader::getMesh(  "PortalStencil.obj" );
          
           t.setPosition(  position );
           t.setOrientation(  orientation );
           t.setScale(  scale );
          }
          
     136  Vector3f Portal::getScaleMult(   ) const {
           double delta = entity.manager.world.getTime(   )-openSince;
           if (  delta > OPEN_ANIM_DURATION ) {
           return Vector3f(  1,   1,   1 );
           }
           float s = delta;
          
           // Linear:
           // std::min(  (  float )delta/OPEN_ANIM_DURATION,   1.0f );
          
           // Quadratic in/out:
           s /= OPEN_ANIM_DURATION / 2;
           if (  s < 1 ) {
           s = 0.5f*s*s;
           } else {
           s--;
           s = -0.5f * (  s*(  s-2 ) - 1 );
           }
          
           return Vector3f(  s,   s,   s );
          }
          
          } /* namespace glPortal */

./source/Version.hpp

       1  #define GAME_VERSION "development"

./source/World.cpp

       1  #include <glPortal/World.hpp>
          
          using namespace radix;
          
          namespace glPortal {
          
       7  World::World(  InputSource &input ) :
           radix::World(  input ) {
          }
          
      11  World::~World(   ) = default;
          
          } /* namespace glPortal */

./source/WorldHelper.cpp

       1  #include <glPortal/WorldHelper.hpp>
          #include <glPortal/Portal.hpp>
          
          #include <bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
          
          #include <radix/data/texture/TextureLoader.hpp>
          #include <radix/component/MeshDrawable.hpp>
          #include <radix/system/PhysicsSystem.hpp>
          #include <radix/component/LightSource.hpp>
          
          using namespace radix;
          
          namespace glPortal {
          
      15  void WorldHelper::shootPortal(  int button,   World &world ) {
           Vector3f cameraDir = Math::toDirection(  world.camera.getOrientation(   ) );
           btVector3 btFrom = world.camera.getPosition(   );
           btVector3 btTo = btFrom + cameraDir*10000;
           btCollisionWorld::ClosestRayResultCallback res(  btFrom,   btTo );
          
           PhysicsSystem &phys = world.systems.get<PhysicsSystem>(   );
           phys.getPhysicsWorld(   ).rayTest(  btFrom,   btTo,   res );
          
           if (  res.hasHit(   ) ) {
           const Entity *pEnt = reinterpret_cast<Entity*>(  res.m_collisionObject->getUserPointer(   ) );
           // All RigidBodies should have their pointer set,   but check anyway
           if (  pEnt ) {
           const Entity &ent = *pEnt;
           // TODO: material in separate Component,   + 1 mat per face
           if (  ent.hasComponent<MeshDrawable>(   ) and
           ent.getComponent<MeshDrawable>(   ).material.portalable ) {
           EntityPair &pPair = getPortalPair(  0,   world );
           Vector3f ipos(  res.m_hitPointWorld );
           Entity &pEnt = (  button == 1 ) ? *pPair.first : *pPair.second;
           Portal &portal = pEnt.getComponent<Portal>(   );
           portal.openSince = world.getTime(   );
           portal.maskTex.diffuse = TextureLoader::getTexture(  "portalmask.png" );
           portal.placeOnWall(  world.camera.getPosition(   ),   ipos,   res.m_hitNormalWorld );
           LightSource &pLight = pEnt.getComponent<LightSource>(   );
          
           if (  button == 1 ) {
           portal.overlayTex.diffuse = TextureLoader::getTexture(  "blueportal.png" );
           portal.color = pLight.color = Portal::BLUE_COLOR;
           } else {
           portal.overlayTex.diffuse = TextureLoader::getTexture(  "orangeportal.png" );
           portal.color = pLight.color = Portal::ORANGE_COLOR;
           }
           }
           }
           }
          }
          
      53  EntityPair& WorldHelper::getPortalPair(  int pair,   World &world ) {
           return world.entityPairs.at(  "portalPairs" ).at(  pair );
          }
          
      57  void WorldHelper::closePortals(  World &world ) {
           EntityPair &pPair = getPortalPair(  0,   world );
          
           pPair.first->getComponent<Portal>(   ).open = false;
           pPair.second->getComponent<Portal>(   ).open = false;
          }
          
          } /* namespace glPortal */

./source/renderer/GameRenderer.cpp

       1  #include <glPortal/renderer/GameRenderer.hpp>
          #include <glPortal/World.hpp>
          
          #include <epoxy/gl.h>
          
          #include <radix/renderer/Renderer.hpp>
          #include <radix/Viewport.hpp>
          #include <radix/component/ViewFrame.hpp>
          #include <radix/component/MeshDrawable.hpp>
          #include <radix/data/shader/ShaderLoader.hpp>
          #include <radix/data/model/MeshLoader.hpp>
          #include <radix/data/material/MaterialLoader.hpp>
          
          using namespace radix;
          
          namespace glPortal {
          
      18  GameRenderer::GameRenderer(  glPortal::World& w,   radix::Renderer& ren,   radix::Camera* cam,   double* ptime ) :
           SubRenderer(  w,   ren ),  
           camera(  cam ),  
           dtime(  ptime ){
          }
          
      24  void GameRenderer::render(   ) {
           time += *dtime;
           renderer.getViewport(   )->getSize(  &viewportWidth,   &viewportHeight );
          
           glDepthMask(  GL_TRUE );
           glEnable(  GL_DEPTH_TEST );
           glEnable(  GL_CULL_FACE );
           glCullFace(  GL_BACK );
          
           glClearColor(  0,   0,   0,   1.0 );
          
           glEnable(  GL_BLEND );
           glBlendFunc(  GL_SRC_ALPHA,   GL_ONE_MINUS_SRC_ALPHA );
           glClear(  GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
          
           camera->setPerspective(   );
           camera->setAspect(  (  float ) viewportWidth / viewportHeight );
          
           renderContext->projStack.resize(  1 );
           camera->getProjMatrix(  renderContext->projStack.back(   ) );
          
           renderContext->viewStack.resize(  1 );
           camera->getViewMatrix(  renderContext->viewStack.back(   ) );
          
           renderContext->invViewStack.resize(  1 );
           camera->getInvViewMatrix(  renderContext->invViewStack.back(   ) );
          
           renderContext->viewFramesStack.clear(   );
          
           renderContext->projDirty = renderContext->viewDirty = true;
          
           renderScene(  *renderContext );
          }
          
      58  void GameRenderer::renderScene(  RenderContext &rc ) {
           if (  rc.viewFramesStack.size(   ) > rc.viewStackMaxDepth ) {
           return;
           }
           RectangleI scissor;
           if (  rc.viewFramesStack.size(   ) > 0 ) {
           const RenderContext::ViewFrameInfo &vfi = rc.getViewFrame(   );
           // Don't render further if computed clip rect is zero-sized
           if (  not renderer.clipViewFrame(  rc,   vfi.first,   vfi.second,   scissor ) ) {
           return;
           }
           }
          
           glClear(  GL_DEPTH_BUFFER_BIT );
          
           renderViewFrames(  rc );
          
           if (  rc.viewFramesStack.size(   ) > 0 ) {
           glScissor(  scissor.x,   scissor.y,   scissor.w,   scissor.h );
           renderViewFrameStencil(  rc );
           }
          
           renderEntities(  rc );
          }
          
      83  void GameRenderer::renderViewFrames(  RenderContext &rc ) {
           GLboolean save_stencil_test;
           glGetBooleanv(  GL_STENCIL_TEST,   &save_stencil_test );
           GLboolean save_scissor_test;
           glGetBooleanv(  GL_SCISSOR_TEST,   &save_scissor_test );
          
           glEnable(  GL_STENCIL_TEST );
           glEnable(  GL_SCISSOR_TEST );
           for (  Entity &e : world.entityManager ) {
           if (  e.hasComponent<ViewFrame>(   ) ) {
           const Transform &t = e.getComponent<Transform>(   );
           Matrix4f inMat; t.getModelMtx(  inMat );
           const ViewFrame &vf = e.getComponent<ViewFrame>(   );
           Matrix4f outMat;
           outMat.translate(  vf.view.getPosition(   ) );
           outMat.rotate(  vf.view.getOrientation(   ) );
           Matrix4f frameView = renderer.getFrameView(  rc.getView(   ),   inMat,   outMat );
           rc.pushViewFrame(  RenderContext::ViewFrameInfo(  vf.mesh,   t ) );
           rc.pushView(  frameView );
           renderScene(  rc );
           rc.popView(   );
           rc.popViewFrame(   );
           }
           }
           if (  not save_stencil_test ) {
           glDisable(  GL_STENCIL_TEST );
           }
           if (  not save_scissor_test ) {
           glDisable(  GL_SCISSOR_TEST );
           }
          
           // Draw portal in the depth buffer so they are not overwritten
           glClear(  GL_DEPTH_BUFFER_BIT );
          
           GLboolean save_color_mask[4];
           GLboolean save_depth_mask;
           glGetBooleanv(  GL_COLOR_WRITEMASK,   save_color_mask );
           glGetBooleanv(  GL_DEPTH_WRITEMASK,   &save_depth_mask );
           glColorMask(  GL_FALSE,   GL_FALSE,   GL_FALSE,   GL_FALSE );
           glDepthMask(  GL_TRUE );
           Shader shader = ShaderLoader::getShader(  "whitefill.frag" );
           Matrix4f modelMtx;
           for (  size_t i = 0; i < rc.viewFramesStack.size(   ); i++ ) {
           renderer.renderMesh(  rc,   shader,   modelMtx,   rc.viewFramesStack[i].first,   nullptr );
           }
           shader.release(   );
           glColorMask(  save_color_mask[0],   save_color_mask[1],   save_color_mask[2],   save_color_mask[3] );
           glDepthMask(  save_depth_mask );
          }
          
     133  void GameRenderer::renderViewFrameStencil(  RenderContext &rc ) {
           GLboolean save_color_mask[4];
           GLboolean save_depth_mask;
           glGetBooleanv(  GL_COLOR_WRITEMASK,   save_color_mask );
           glGetBooleanv(  GL_DEPTH_WRITEMASK,   &save_depth_mask );
          
           glColorMask(  GL_FALSE,   GL_FALSE,   GL_FALSE,   GL_FALSE );
           glDepthMask(  GL_FALSE );
           glStencilFunc(  GL_NEVER,   0,   0xFF );
           glStencilOp(  GL_INCR,   GL_KEEP,   GL_KEEP ); // draw 1s on test fail (  always )
           glClear(  GL_STENCIL_BUFFER_BIT ); // needs mask=0xFF
          
           rc.pushView(  rc.viewStack[0] );
           Shader shader = ShaderLoader::getShader(  "whitefill.frag" );
           Matrix4f modelMtx; rc.viewFramesStack.back(   ).second.getModelMtx(  modelMtx );
           renderer.renderMesh(  rc,   shader,   modelMtx,   rc.viewFramesStack.back(   ).first,   nullptr );
           rc.popView(   );
          
           for (  size_t i = 1; i < rc.viewStack.size(   ) - 1; i++ ) { // -1 to ignore last view
           // Increment intersection for current portal
           glStencilFunc(  GL_EQUAL,   0,   0xFF );
           glStencilOp(  GL_INCR,   GL_KEEP,   GL_KEEP ); // draw 1s on test fail (  always )
           renderer.renderMesh(  rc,   shader,   modelMtx,   rc.viewFramesStack.back(   ).first,   nullptr );
          
           // Decremental outer portal -> only sub-portal intersection remains
           glStencilFunc(  GL_NEVER,   0,   0xFF );
           glStencilOp(  GL_DECR,   GL_KEEP,   GL_KEEP ); // draw 1s on test fail (  always )
           rc.pushView(  rc.viewStack[i-1] );
           renderer.renderMesh(  rc,   shader,   modelMtx,   rc.viewFramesStack.back(   ).first,   nullptr );
           }
           shader.release(   );
          
           //glColorMask(  GL_TRUE,   GL_TRUE,   GL_FALSE,   GL_TRUE ); // blue-ish filter if drawing on white or grey
           glColorMask(  GL_TRUE,   GL_TRUE,   GL_TRUE,   GL_TRUE );
           glDepthMask(  GL_TRUE );
           glStencilOp(  GL_KEEP,   GL_KEEP,   GL_KEEP );
           /* Fill 1 or more */
           glStencilFunc(  GL_LEQUAL,   1,   0xFF );
           glColorMask(  save_color_mask[0],   save_color_mask[1],   save_color_mask[2],   save_color_mask[3] );
           glDepthMask(  save_depth_mask );
          }
          
     175  void GameRenderer::renderEntities(  RenderContext &rc ) {
           for (  Entity &e : world.entityManager ) {
           if (  e.hasComponent<MeshDrawable>(   ) ) {
           renderEntity(  rc,   e );
           }
           }
          }
          
     183  void GameRenderer::renderEntity(  RenderContext &rc,   const Entity &e ) {
           MeshDrawable &drawable = e.getComponent<MeshDrawable>(   );
           Matrix4f mtx;
           e.getComponent<Transform>(   ).getModelMtx(  mtx );
          
           if (  drawable.material.fancyname.compare(  "Metal tiles .5x" ) == 0 ) {
           Shader &metal = ShaderLoader::getShader(  "metal.frag" );
           renderer.renderMesh(  rc,   metal,   mtx,   drawable.mesh,   drawable.material );
           metal.release(   );
           } else {
           Shader &diffuse = ShaderLoader::getShader(  "diffuse.frag" );
           renderer.renderMesh(  rc,   diffuse,   mtx,   drawable.mesh,   drawable.material );
           diffuse.release(   );
           }
          }
          
     199  void GameRenderer::renderPlayer(  RenderContext &rc ) {
           const Transform &t = world.getPlayer(   ).getComponent<Transform>(   );
           Matrix4f mtx;
           mtx.translate(  t.getPosition(   ) + Vector3f(  0,   -.5f,   0 ) );
           mtx.rotate(  t.getOrientation(   ) );
           mtx.scale(  Vector3f(  1.3f,   1.3f,   1.3f ) );
           const Mesh &dummy = MeshLoader::getMesh(  "HumanToken.obj" );
           const Material &mat = MaterialLoader::fromTexture(  "HumanToken.png" );
          
           renderer.renderMesh(  rc,   ShaderLoader::getShader(  "diffuse.frag" ),   mtx,   dummy,   mat );
          }
          
     211  void GameRenderer::setCameraInPortal(  const Camera &cam,   Camera &dest,  
     212   const Entity &portal,   const Entity &otherPortal ) {
           Transform &p1T = portal.getComponent<Transform>(   );
           Matrix4f p1mat;
           p1mat.translate(  p1T.getPosition(   ) );
           p1mat.rotate(  p1T.getOrientation(   ) );
           Transform &p2T = otherPortal.getComponent<Transform>(   );
           Matrix4f p2mat;
           p2mat.translate(  p2T.getPosition(   ) );
           p2mat.rotate(  p2T.getOrientation(   ) );
           Matrix4f rotate180; rotate180.rotate(  rad(  180 ),   0,   1,   0 );
           Matrix4f view; cam.getViewMatrix(  view );
           Matrix4f destView = view * p1mat * rotate180 * inverse(  p2mat );
          
           dest.setPerspective(   );
           dest.setAspect(  cam.getAspect(   ) );
           dest.setFovy(  cam.getFovy(   ) );
           dest.setZNear(  (  p1T.getPosition(   ) - cam.getPosition(   ) ).length(   ) );
           dest.setViewMatrix(  destView );
          }
          
          } /* namespace glPortal */

./source/renderer/TerminalRenderer.cpp

       1  #include <glPortal/renderer/TerminalRenderer.hpp>
          #include <glPortal/component/Terminal.hpp>
          
          #include <epoxy/gl.h>
          
          #include <radix/renderer/Renderer.hpp>
          #include <radix/data/model/MeshLoader.hpp>
          #include <radix/data/shader/ShaderLoader.hpp>
          #include <radix/Viewport.hpp>
          
          using namespace radix;
          
          namespace glPortal {
          
      15  void TerminalRenderer::render(  RenderContext &rc,   World &world ) {
           int vpWidth,   vpHeight;
           Renderer &renderer = rc.renderer;
           renderer.getViewport(   )->getSize(  &vpWidth,   &vpHeight );
           Camera camera;
           camera.setOrthographic(   );
           camera.setBounds(  0,   vpWidth,   0,   vpHeight );
           rc.pushCamera(  camera );
           Matrix4f widget;
           widget.translate(  Vector3f(  vpWidth/2,   vpHeight-100,   -5 ) );
           widget.scale(  Vector3f(  vpWidth,   200,   1 ) );
           const Mesh &mesh = MeshLoader::getMesh(  "GUIElement.obj" );
           Shader &sh = ShaderLoader::getShader(  "color.frag" );
           Terminal &term = world.entityAliases.at(  "activeTerminal" )->getComponent<Terminal>(   );
           Vector4f screenBackgroundColor = term.backgroundColor;
           sh.bind(   );
           glUniform4f(  sh.uni(  "color" ),  
           screenBackgroundColor.x,  
           screenBackgroundColor.y,  
           screenBackgroundColor.z,  
           screenBackgroundColor.w );
           renderer.renderMesh(  rc,   sh,   widget,   mesh,   nullptr );
           Text terminalText;
           terminalText.font = "Pacaya";
           terminalText.size = 1.0f;
           terminalText.content = world.input.getCharBuffer(   );
           terminalText.color = term.textColor;
           terminalText.position = Vector3f(  0,   vpHeight-30,   -1 );
           renderer.renderText(  rc,   terminalText );
           sh.release(   );
           rc.popCamera(   );
          }
          
          } /* namespace glPortal */

./source/renderer/UiRenderer.cpp

       1  #include <glPortal/renderer/UiRenderer.hpp>
          #include <glPortal/Game.hpp>
          
          #include <epoxy/gl.h>
          
          #include <radix/data/model/MeshLoader.hpp>
          #include <radix/data/shader/ShaderLoader.hpp>
          #include <radix/data/material/MaterialLoader.hpp>
          
          #include "../Version.hpp"
          
          using namespace radix;
          
          namespace glPortal {
          
      16  UiRenderer::UiRenderer(  World &w,   radix::Renderer &ren ) :
           SubRenderer(  w,   ren ) {
          
          }
          
      21  void UiRenderer::render(   ) {
           glDepthMask(  GL_FALSE );
          
           renderer.getViewport(   )->getSize(  &viewportWidth,   &viewportHeight );
          
           initCamera(   );
          
           // Hand
           renderImage(  Vector3f(  viewportWidth - 400,   250,   -50 ),   Vector3f(  960,   540,   1 ),   "hand.png" );
          
           // Crosshair
           renderImage(  Vector3f(  viewportWidth / 2,   viewportHeight / 2,   -10 ),   Vector3f(  80,   80,   1 ),   "Reticle.png" );
          
           Text glPortalTitle;
           glPortalTitle.font = "Pacaya";
           glPortalTitle.size = 1.5f;
           glPortalTitle.content = "GlPortal";
           glPortalTitle.color = Vector4f(  1,   1,   1,   1 );
           glPortalTitle.position = Vector3f(  25,   viewportHeight - 95,   -20 );
           renderer.renderText(  *renderContext.get(   ),   glPortalTitle );
          
           Text fpsCounter;
           fpsCounter.font = "Pacaya";
           fpsCounter.size = 0.5f;
           fpsCounter.content = std::string(  "FPS: " ) + std::to_string(  Game::fps.getFps(   ) );
           fpsCounter.color = Vector4f(  1,   1,   1,   1 );
           fpsCounter.position = Vector3f(  10,   viewportHeight - 25,   -20 );
           renderer.renderText(  *renderContext.get(   ),   fpsCounter );
          
           Text version;
           version.font = "Pacaya";
           version.size = 0.5f;
           version.content = std::string(  "Early testing build: " ) + GAME_VERSION;
           version.color = Vector4f(  1,   1,   1,   1 );
           version.position = Vector3f(  10,   viewportHeight - 45,   -20 );
           renderer.renderText(  *renderContext.get(   ),   version );
          
           renderContext->popCamera(   );
           glDepthMask(  GL_TRUE );
          }
          
      62  void UiRenderer::renderImage(  radix::Vector3f position,   radix::Vector3f scale,   std::string path ) {
           Matrix4f matrix;
           matrix.translate(  position );
           matrix.scale(  scale );
          
           const Mesh &mesh = MeshLoader::getMesh(  "GUIElement.obj" );
           const Material &material = MaterialLoader::fromTexture(  path );
          
           Shader &sh = ShaderLoader::getShader(  "unshaded.frag" );
           renderer.renderMesh(  *renderContext.get(   ),   sh,   matrix,   mesh,   material );
           sh.release(   );
          }
          
          } /* namespace glPortal */

./source/system/PortalSystem.cpp

       1  #include <glPortal/system/PortalSystem.hpp>
          #include <glPortal/Portal.hpp>
          
          #include <radix/component/Player.hpp>
          #include <radix/physics/PhysicsHelper.hpp>
          #include <radix/World.hpp>
          
          using namespace radix;
          
          namespace glPortal {
          
      12  PortalSystem::PortalSystem(  World &w ) :
           System(  w ) {
          }
          
      16  PortalSystem::~PortalSystem(   ) {
          }
          
      19  void PortalSystem::update(  float dtime ) {
           (  void ) dtime;
           for (  Entity &e : world.entityManager ) {
           if (  e.hasComponent<Player>(   ) ) {
           Transform &t = e.getComponent<Transform>(   );
           const Vector3f &pos = t.getPosition(   );
           Player &plr = e.getComponent<Player>(   );
           for (  const EntityPair &pp : world.entityPairs.at(  "portals" ) ) {
           btVector3 min,   max;
           Portal &pA = pp.first->getComponent<Portal>(   ),  
           &pB = pp.second->getComponent<Portal>(   );
          
           // No uncollider? No portal link.
           if (  not pA.uncollider or not pB.uncollider ) {
           continue;
           }
          
           pA.uncolliderShape->getAabb(  
           pA.uncollider->getWorldTransform(   ),  
           min,   max );
           // Is player within portal A uncollider (  AABB then real check ) ?
           if (  PhysicsHelper::pointInAABB(  pos,   min,   max ) and
           PhysicsHelper::pointInVolume(  pos,   *pA.uncollider ) ) {
           // Behind portal A
           const Transform
           &pAT = pp.first->getComponent<Transform>(   ),  
           &pBT = pp.second->getComponent<Transform>(   );
           Vector3f normalA = pA.getDirection(   );
           Vector3f AP = pos - pAT.getPosition(   );
           if (  dot(  AP,   normalA ) < 0 ) {
           Matrix4f rotate180; rotate180.rotate(  rad(  180 ),   0,   1,   0 );
           Matrix4f view = inverse(  plr.getBaseHeadOrientation(   ).toMatrix(   ) );
           view.translate(  -t.getPosition(   ) );
           Matrix4f iv = inverse(   view *
           Matrix4f(  pAT.getPosition(   ),   pAT.getOrientation(   ) ) *
           rotate180 *
           inverse(  Matrix4f(  pBT.getPosition(   ),   pBT.getOrientation(   ) ) )  );
           t.setPosition(  iv.getPosition(   ) );
           plr.headAngle = iv.getRotation(   ).toAero(   );
           }
           }
           pB.uncolliderShape->getAabb(  
           pB.uncollider->getWorldTransform(   ),  
           min,   max );
           // Is player within portal B uncollider ?
           if (  PhysicsHelper::pointInAABB(  pos,   min,   max ) and
           PhysicsHelper::pointInVolume(  pos,   *pB.uncollider ) ) {
           // Behind portal B
           const Transform
           &pAT = pp.first->getComponent<Transform>(   ),  
           &pBT = pp.second->getComponent<Transform>(   );
           Vector3f normalB = pB.getDirection(   );
           Vector3f BP = pos - pBT.getPosition(   );
           if (  dot(  BP,   normalB ) < 0 ) {
           Matrix4f rotate180; rotate180.rotate(  rad(  180 ),   0,   1,   0 );
           Matrix4f view = inverse(  plr.getBaseHeadOrientation(   ).toMatrix(   ) );
           view.translate(  -t.getPosition(   ) );
           Matrix4f iv = inverse(   view *
           Matrix4f(  pBT.getPosition(   ),   pBT.getOrientation(   ) ) *
           rotate180 *
           inverse(  Matrix4f(  pAT.getPosition(   ),   pAT.getOrientation(   ) ) )  );
           t.setPosition(  Vector3f(  iv[12],   iv[13],   iv[14] ) );
           plr.headAngle = iv.getRotation(   ).toAero(   );
           }
           }
           }
           }
           }
          }
          
          } /* namespace glPortal */