diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 34e3614716..beada5df43 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -4,22 +4,22 @@ Application - + Export Voxels - + Sparse Voxel Octree Files (*.svo) - + Open Script - + JavaScript Files (*.js) @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file @@ -132,28 +132,28 @@ QObject - - + + Import Voxels - + Loading ... - + Place voxels - + <b>Import</b> %1 as voxels - + Cancel diff --git a/interface/resources/shaders/model_shadow.frag b/interface/resources/shaders/model_shadow.frag new file mode 100644 index 0000000000..bcb597b13c --- /dev/null +++ b/interface/resources/shaders/model_shadow.frag @@ -0,0 +1,14 @@ +#version 120 + +// +// model_shadow.frag +// fragment shader +// +// Created by Andrzej Kapolka on 3/24/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +void main(void) { + // fixed color for now (we may eventually want to use texture alpha) + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); +} diff --git a/interface/resources/shaders/model_shadow.vert b/interface/resources/shaders/model_shadow.vert new file mode 100644 index 0000000000..ae7e871887 --- /dev/null +++ b/interface/resources/shaders/model_shadow.vert @@ -0,0 +1,14 @@ +#version 120 + +// +// model_shadow.vert +// vertex shader +// +// Created by Andrzej Kapolka on 3/24/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +void main(void) { + // just use standard pipeline transform + gl_Position = ftransform(); +} diff --git a/interface/resources/shaders/skin_model_shadow.vert b/interface/resources/shaders/skin_model_shadow.vert new file mode 100644 index 0000000000..b9ef05ad8a --- /dev/null +++ b/interface/resources/shaders/skin_model_shadow.vert @@ -0,0 +1,27 @@ +#version 120 + +// +// skin_model_shadow.vert +// vertex shader +// +// Created by Andrzej Kapolka on 3/24/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +const int MAX_CLUSTERS = 128; +const int INDICES_PER_VERTEX = 4; + +uniform mat4 clusterMatrices[MAX_CLUSTERS]; + +attribute vec4 clusterIndices; +attribute vec4 clusterWeights; + +void main(void) { + vec4 position = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[int(clusterIndices[i])]; + float clusterWeight = clusterWeights[i]; + position += clusterMatrix * gl_Vertex * clusterWeight; + } + gl_Position = gl_ModelViewProjectionMatrix * position; +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 966f4eb2ea..2ca4ef74cd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2167,21 +2167,22 @@ void Application::updateShadowMap() { glViewport(0, 0, fbo->width(), fbo->height()); glm::vec3 lightDirection = -getSunDirection(); - glm::quat rotation = glm::inverse(rotationBetween(IDENTITY_FRONT, lightDirection)); - glm::vec3 translation = glm::vec3(); + glm::quat rotation = rotationBetween(IDENTITY_FRONT, lightDirection); + glm::quat inverseRotation = glm::inverse(rotation); float nearScale = 0.0f; const float MAX_SHADOW_DISTANCE = 2.0f; - float farScale = (MAX_SHADOW_DISTANCE - _viewFrustum.getNearClip()) / (_viewFrustum.getFarClip() - _viewFrustum.getNearClip()); + float farScale = (MAX_SHADOW_DISTANCE - _viewFrustum.getNearClip()) / + (_viewFrustum.getFarClip() - _viewFrustum.getNearClip()); loadViewFrustum(_myCamera, _viewFrustum); glm::vec3 points[] = { - rotation * (glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale) + translation), - rotation * (glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale) + translation), - rotation * (glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale) + translation), - rotation * (glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale) + translation), - rotation * (glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale) + translation), - rotation * (glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale) + translation), - rotation * (glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale) + translation), - rotation * (glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale) + translation) }; + inverseRotation * (glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale)), + inverseRotation * (glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale)), + inverseRotation * (glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale)), + inverseRotation * (glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale)), + inverseRotation * (glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale)), + inverseRotation * (glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale)), + inverseRotation * (glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale)), + inverseRotation * (glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale)) }; glm::vec3 minima(FLT_MAX, FLT_MAX, FLT_MAX), maxima(-FLT_MAX, -FLT_MAX, -FLT_MAX); for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) { minima = glm::min(minima, points[i]); @@ -2194,9 +2195,20 @@ void Application::updateShadowMap() { // save the combined matrix for rendering _shadowMatrix = glm::transpose(glm::translate(glm::vec3(0.5f, 0.5f, 0.5f)) * glm::scale(glm::vec3(0.5f, 0.5f, 0.5f)) * - glm::ortho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z) * - glm::mat4_cast(rotation) * glm::translate(translation)); + glm::ortho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z) * glm::mat4_cast(inverseRotation)); + // update the shadow view frustum + _shadowViewFrustum.setPosition(rotation * ((minima + maxima) * 0.5f)); + _shadowViewFrustum.setOrientation(rotation); + _shadowViewFrustum.setOrthographic(true); + _shadowViewFrustum.setWidth(maxima.x - minima.x); + _shadowViewFrustum.setHeight(maxima.y - minima.y); + _shadowViewFrustum.setNearClip(minima.z); + _shadowViewFrustum.setFarClip(maxima.z); + _shadowViewFrustum.setEyeOffsetPosition(glm::vec3()); + _shadowViewFrustum.setEyeOffsetOrientation(glm::quat()); + _shadowViewFrustum.calculate(); + glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); @@ -2205,16 +2217,14 @@ void Application::updateShadowMap() { glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); - glm::vec3 axis = glm::axis(rotation); - glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + glm::vec3 axis = glm::axis(inverseRotation); + glRotatef(glm::degrees(glm::angle(inverseRotation)), axis.x, axis.y, axis.z); // store view matrix without translation, which we'll use for precision-sensitive objects glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&_untranslatedViewMatrix); - _viewMatrixTranslation = translation; + _viewMatrixTranslation = glm::vec3(); - glTranslatef(translation.x, translation.y, translation.z); - - _avatarManager.renderAvatars(true); + _avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE); _particles.render(); glPopMatrix(); @@ -2392,7 +2402,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { } bool mirrorMode = (whichCamera.getInterpolatedMode() == CAMERA_MODE_MIRROR); - _avatarManager.renderAvatars(mirrorMode, selfAvatarOnly); + _avatarManager.renderAvatars(mirrorMode ? Avatar::MIRROR_RENDER_MODE : Avatar::NORMAL_RENDER_MODE, selfAvatarOnly); if (!selfAvatarOnly) { // Render the world box diff --git a/interface/src/Application.h b/interface/src/Application.h index 10a7a6375b..caeea529af 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -155,6 +155,7 @@ public: Audio* getAudio() { return &_audio; } Camera* getCamera() { return &_myCamera; } ViewFrustum* getViewFrustum() { return &_viewFrustum; } + ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; } VoxelSystem* getVoxels() { return &_voxels; } VoxelTree* getVoxelTree() { return _voxels.getTree(); } ParticleTreeRenderer* getParticles() { return &_particles; } @@ -390,6 +391,7 @@ private: ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels, particles) + ViewFrustum _shadowViewFrustum; quint64 _lastQueriedTime; Oscilloscope _audioScope; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 75a8386ea9..7e5a777484 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -189,10 +189,12 @@ static TextRenderer* textRenderer(TextRendererType type) { return displayNameRenderer; } -void Avatar::render(const glm::vec3& cameraPosition, bool forShadowMap) { +void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { // simple frustum check float boundingRadius = getBillboardSize(); - if (Application::getInstance()->getViewFrustum()->sphereInFrustum(cameraPosition, boundingRadius) == ViewFrustum::OUTSIDE) { + ViewFrustum* frustum = (renderMode == Avatar::SHADOW_RENDER_MODE) ? + Application::getInstance()->getShadowViewFrustum() : Application::getInstance()->getViewFrustum(); + if (frustum->sphereInFrustum(_position, boundingRadius) == ViewFrustum::OUTSIDE) { return; } @@ -202,11 +204,11 @@ void Avatar::render(const glm::vec3& cameraPosition, bool forShadowMap) { { // glow when moving far away const float GLOW_DISTANCE = 20.0f; - Glower glower(_moving && distanceToTarget > GLOW_DISTANCE && !forShadowMap ? 1.0f : 0.0f); + Glower glower(_moving && distanceToTarget > GLOW_DISTANCE && renderMode == NORMAL_RENDER_MODE ? 1.0f : 0.0f); // render body if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { - renderBody(forShadowMap); + renderBody(renderMode); } if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionProxies)) { _skeletonModel.renderCollisionProxies(0.7f); @@ -230,7 +232,8 @@ void Avatar::render(const glm::vec3& cameraPosition, bool forShadowMap) { float angle = abs(angleBetween(toTarget + delta, toTarget - delta)); float sphereRadius = getHead()->getAverageLoudness() * SPHERE_LOUDNESS_SCALING; - if (!forShadowMap && (sphereRadius > MIN_SPHERE_SIZE) && (angle < MAX_SPHERE_ANGLE) && (angle > MIN_SPHERE_ANGLE)) { + if (renderMode == NORMAL_RENDER_MODE && (sphereRadius > MIN_SPHERE_SIZE) && + (angle < MAX_SPHERE_ANGLE) && (angle > MIN_SPHERE_ANGLE)) { glColor4f(SPHERE_COLOR[0], SPHERE_COLOR[1], SPHERE_COLOR[2], 1.f - angle / MAX_SPHERE_ANGLE); glPushMatrix(); glTranslatef(_position.x, _position.y, _position.z); @@ -242,8 +245,8 @@ void Avatar::render(const glm::vec3& cameraPosition, bool forShadowMap) { } const float DISPLAYNAME_DISTANCE = 10.0f; - setShowDisplayName(!forShadowMap && distanceToTarget < DISPLAYNAME_DISTANCE); - if (forShadowMap) { + setShowDisplayName(renderMode == NORMAL_RENDER_MODE && distanceToTarget < DISPLAYNAME_DISTANCE); + if (renderMode != NORMAL_RENDER_MODE) { return; } renderDisplayName(); @@ -306,17 +309,16 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { return glm::angleAxis(angle * proportion, axis); } -void Avatar::renderBody(bool forShadowMap) { +void Avatar::renderBody(RenderMode renderMode) { if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { // render the billboard until both models are loaded - if (forShadowMap) { - return; + if (renderMode != SHADOW_RENDER_MODE) { + renderBillboard(); } - renderBillboard(); return; } - _skeletonModel.render(1.0f); - getHead()->render(1.0f); + _skeletonModel.render(1.0f, renderMode == SHADOW_RENDER_MODE); + getHead()->render(1.0f, renderMode == SHADOW_RENDER_MODE); getHand()->render(false); } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 638bff6e32..25600e0943 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -74,7 +74,10 @@ public: void init(); void simulate(float deltaTime); - virtual void render(const glm::vec3& cameraPosition, bool forShadowMap); + + enum RenderMode { NORMAL_RENDER_MODE, SHADOW_RENDER_MODE, MIRROR_RENDER_MODE }; + + virtual void render(const glm::vec3& cameraPosition, RenderMode renderMode = NORMAL_RENDER_MODE); //setters void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); } @@ -181,7 +184,7 @@ protected: float getPelvisToHeadLength() const; void renderDisplayName(); - virtual void renderBody(bool forShadowMap); + virtual void renderBody(RenderMode renderMode); private: diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 29b23e1f5b..9147a08dbd 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -72,7 +72,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { simulateAvatarFades(deltaTime); } -void AvatarManager::renderAvatars(bool forShadowMapOrMirror, bool selfAvatarOnly) { +void AvatarManager::renderAvatars(Avatar::RenderMode renderMode, bool selfAvatarOnly) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::renderAvatars()"); bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors); @@ -85,13 +85,13 @@ void AvatarManager::renderAvatars(bool forShadowMapOrMirror, bool selfAvatarOnly if (!avatar->isInitialized()) { continue; } - avatar->render(cameraPosition, forShadowMapOrMirror); + avatar->render(cameraPosition, renderMode); avatar->setDisplayingLookatVectors(renderLookAtVectors); } - renderAvatarFades(cameraPosition, forShadowMapOrMirror); + renderAvatarFades(cameraPosition, renderMode); } else { // just render myAvatar - _myAvatar->render(cameraPosition, forShadowMapOrMirror); + _myAvatar->render(cameraPosition, renderMode); _myAvatar->setDisplayingLookatVectors(renderLookAtVectors); } } @@ -114,14 +114,14 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { } } -void AvatarManager::renderAvatarFades(const glm::vec3& cameraPosition, bool forShadowMap) { +void AvatarManager::renderAvatarFades(const glm::vec3& cameraPosition, Avatar::RenderMode renderMode) { // render avatar fades - Glower glower(forShadowMap ? 0.0f : 1.0f); + Glower glower(renderMode == Avatar::NORMAL_RENDER_MODE ? 1.0f : 0.0f); foreach(const AvatarSharedPointer& fadingAvatar, _avatarFades) { Avatar* avatar = static_cast(fadingAvatar.data()); if (avatar != static_cast(_myAvatar.data())) { - avatar->render(cameraPosition, forShadowMap); + avatar->render(cameraPosition, renderMode); } } } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 455153b92a..06494f309c 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -29,7 +29,7 @@ public: MyAvatar* getMyAvatar() { return _myAvatar.data(); } void updateOtherAvatars(float deltaTime); - void renderAvatars(bool forShadowMapOrMirror = false, bool selfAvatarOnly = false); + void renderAvatars(Avatar::RenderMode renderMode, bool selfAvatarOnly = false); void clearOtherAvatars(); @@ -45,7 +45,7 @@ private: void processKillAvatar(const QByteArray& datagram); void simulateAvatarFades(float deltaTime); - void renderAvatarFades(const glm::vec3& cameraPosition, bool forShadowMap); + void renderAvatarFades(const glm::vec3& cameraPosition, Avatar::RenderMode renderMode); // virtual override AvatarHash::iterator erase(const AvatarHash::iterator& iterator); diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index db6c3fe98d..19faa0da42 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -45,13 +45,6 @@ void FaceModel::simulate(float deltaTime) { Model::simulate(deltaTime, true, newJointStates); } -bool FaceModel::render(float alpha) { - if (!Model::render(alpha)) { - return false; - } - return true; -} - void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { // get the rotation axes in joint space and use them to adjust the rotation glm::mat3 axes = glm::mat3_cast(_rotation); diff --git a/interface/src/avatar/FaceModel.h b/interface/src/avatar/FaceModel.h index d0f0f6baef..acf2d2baf4 100644 --- a/interface/src/avatar/FaceModel.h +++ b/interface/src/avatar/FaceModel.h @@ -22,7 +22,6 @@ public: FaceModel(Head* owningHead); void simulate(float deltaTime); - bool render(float alpha); protected: diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 44001a2015..4a81df8b74 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -168,8 +168,8 @@ void Head::simulate(float deltaTime, bool isMine, bool billboard) { _eyePosition = calculateAverageEyePosition(); } -void Head::render(float alpha) { - if (_faceModel.render(alpha) && _renderLookatVectors) { +void Head::render(float alpha, bool forShadowMap) { + if (_faceModel.render(alpha, forShadowMap) && _renderLookatVectors) { renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition); } } diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index a9ea9b4cc6..60730c8724 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -37,7 +37,7 @@ public: void init(); void reset(); void simulate(float deltaTime, bool isMine, bool billboard = false); - void render(float alpha); + void render(float alpha, bool forShadowMap); void setScale(float scale); void setPosition(glm::vec3 position) { _position = position; } void setGravity(glm::vec3 gravity) { _gravity = gravity; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5a1cefaa87..19d15fb803 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -450,12 +450,12 @@ void MyAvatar::renderDebugBodyPoints() { } // virtual -void MyAvatar::render(const glm::vec3& cameraPosition, bool forShadowMapOrMirror) { +void MyAvatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { // don't render if we've been asked to disable local rendering if (!_shouldRender) { return; // exit early } - Avatar::render(cameraPosition, forShadowMapOrMirror); + Avatar::render(cameraPosition, renderMode); } void MyAvatar::renderHeadMouse() const { @@ -638,20 +638,20 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _billboardValid = false; } -void MyAvatar::renderBody(bool forceRenderHead) { +void MyAvatar::renderBody(RenderMode renderMode) { if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { return; // wait until both models are loaded } // Render the body's voxels and head - _skeletonModel.render(1.0f); + _skeletonModel.render(1.0f, renderMode == SHADOW_RENDER_MODE); // Render head so long as the camera isn't inside it const float RENDER_HEAD_CUTOFF_DISTANCE = 0.40f; Camera* myCamera = Application::getInstance()->getCamera(); - if (forceRenderHead || (glm::length(myCamera->getPosition() - getHead()->calculateAverageEyePosition()) > + if (renderMode != NORMAL_RENDER_MODE || (glm::length(myCamera->getPosition() - getHead()->calculateAverageEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale)) { - getHead()->render(1.0f); + getHead()->render(1.0f, renderMode == SHADOW_RENDER_MODE); } getHand()->render(true); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 241286a721..cbb625aa2f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -35,8 +35,8 @@ public: void simulate(float deltaTime); void updateFromGyros(float deltaTime); - void render(const glm::vec3& cameraPosition, bool forShadowMapOrMirror = false); - void renderBody(bool forceRenderHead); + void render(const glm::vec3& cameraPosition, RenderMode renderMode = NORMAL_RENDER_MODE); + void renderBody(RenderMode renderMode); void renderDebugBodyPoints(); void renderHeadMouse() const; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 7173cb0b84..9e4740df15 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -62,17 +62,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } } -bool SkeletonModel::render(float alpha) { - - if (_jointStates.isEmpty()) { - return false; - } - - Model::render(alpha); - - return true; -} - void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) const { if (jointIndex == -1) { return; diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 0bcbcef2ea..514b5daf1c 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -23,7 +23,6 @@ public: SkeletonModel(Avatar* owningAvatar); void simulate(float deltaTime, bool fullUpdate = true); - bool render(float alpha); /// \param jointIndex index of hand joint /// \param shapes[out] list in which is stored pointers to hand shapes diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 16b5c167d9..690b19ee5e 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -43,11 +43,14 @@ Model::~Model() { ProgramObject Model::_program; ProgramObject Model::_normalMapProgram; +ProgramObject Model::_shadowProgram; ProgramObject Model::_skinProgram; ProgramObject Model::_skinNormalMapProgram; +ProgramObject Model::_skinShadowProgram; int Model::_normalMapTangentLocation; Model::SkinLocations Model::_skinLocations; Model::SkinLocations Model::_skinNormalMapLocations; +Model::SkinLocations Model::_skinShadowLocations; void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations) { program.bind(); @@ -93,6 +96,11 @@ void Model::init() { _normalMapTangentLocation = _normalMapProgram.attributeLocation("tangent"); _normalMapProgram.release(); + _shadowProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_shadow.vert"); + _shadowProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + + "shaders/model_shadow.frag"); + _shadowProgram.link(); + _skinProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/skin_model.vert"); _skinProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() @@ -108,6 +116,14 @@ void Model::init() { _skinNormalMapProgram.link(); initSkinProgram(_skinNormalMapProgram, _skinNormalMapLocations); + + _skinShadowProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/skin_model_shadow.vert"); + _skinShadowProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/model_shadow.frag"); + _skinShadowProgram.link(); + + initSkinProgram(_skinShadowProgram, _skinShadowLocations); } } @@ -167,7 +183,7 @@ void Model::simulate(float deltaTime, bool fullUpdate) { simulate(deltaTime, fullUpdate, updateGeometry()); } -bool Model::render(float alpha) { +bool Model::render(float alpha, bool forShadowMap) { // render the attachments foreach (Model* attachment, _attachments) { attachment->render(alpha); @@ -198,13 +214,13 @@ bool Model::render(float alpha) { glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.5f * alpha); - renderMeshes(alpha, false); + renderMeshes(alpha, forShadowMap, false); glDisable(GL_ALPHA_TEST); // render translucent meshes afterwards, with back face culling - renderMeshes(alpha, true); + renderMeshes(alpha, forShadowMap, true); glDisable(GL_CULL_FACE); @@ -960,7 +976,7 @@ void Model::deleteGeometry() { } } -void Model::renderMeshes(float alpha, bool translucent) { +void Model::renderMeshes(float alpha, bool forShadowMap, bool translucent) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& networkMeshes = _geometry->getMeshes(); @@ -985,7 +1001,12 @@ void Model::renderMeshes(float alpha, bool translucent) { ProgramObject* program = &_program; ProgramObject* skinProgram = &_skinProgram; SkinLocations* skinLocations = &_skinLocations; - if (!mesh.tangents.isEmpty()) { + if (forShadowMap) { + program = &_shadowProgram; + skinProgram = &_skinShadowProgram; + skinLocations = &_skinShadowLocations; + + } else if (!mesh.tangents.isEmpty()) { program = &_normalMapProgram; skinProgram = &_skinNormalMapProgram; skinLocations = &_skinNormalMapLocations; @@ -1018,7 +1039,7 @@ void Model::renderMeshes(float alpha, bool translucent) { } if (mesh.blendshapes.isEmpty()) { - if (!mesh.tangents.isEmpty()) { + if (!(mesh.tangents.isEmpty() || forShadowMap)) { activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3); activeProgram->enableAttributeArray(tangentLocation); } @@ -1028,7 +1049,7 @@ void Model::renderMeshes(float alpha, bool translucent) { (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); } else { - if (!mesh.tangents.isEmpty()) { + if (!(mesh.tangents.isEmpty() || forShadowMap)) { activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, 0, 3); activeProgram->enableAttributeArray(tangentLocation); } @@ -1057,31 +1078,33 @@ void Model::renderMeshes(float alpha, bool translucent) { continue; } // apply material properties - glm::vec4 diffuse = glm::vec4(part.diffuseColor, alpha); - glm::vec4 specular = glm::vec4(part.specularColor, alpha); - glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse); - glMaterialfv(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse); - glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); - glMaterialf(GL_FRONT, GL_SHININESS, part.shininess); - - Texture* diffuseMap = networkPart.diffuseTexture.data(); - if (mesh.isEye) { - if (diffuseMap) { + if (forShadowMap) { + glBindTexture(GL_TEXTURE_2D, 0); + + } else { + glm::vec4 diffuse = glm::vec4(part.diffuseColor, alpha); + glm::vec4 specular = glm::vec4(part.specularColor, alpha); + glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse); + glMaterialfv(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse); + glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); + glMaterialf(GL_FRONT, GL_SHININESS, part.shininess); + + Texture* diffuseMap = networkPart.diffuseTexture.data(); + if (mesh.isEye && diffuseMap) { diffuseMap = (_dilatedTextures[i][j] = static_cast(diffuseMap)->getDilatedTexture(_pupilDilation)).data(); } + glBindTexture(GL_TEXTURE_2D, !diffuseMap ? + Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID()); + + if (!mesh.tangents.isEmpty()) { + glActiveTexture(GL_TEXTURE1); + Texture* normalMap = networkPart.normalTexture.data(); + glBindTexture(GL_TEXTURE_2D, !normalMap ? + Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID()); + glActiveTexture(GL_TEXTURE0); + } } - glBindTexture(GL_TEXTURE_2D, !diffuseMap ? - Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID()); - - if (!mesh.tangents.isEmpty()) { - glActiveTexture(GL_TEXTURE1); - Texture* normalMap = networkPart.normalTexture.data(); - glBindTexture(GL_TEXTURE_2D, !normalMap ? - Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID()); - glActiveTexture(GL_TEXTURE0); - } - glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset); offset += part.quadIndices.size() * sizeof(int); glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, part.triangleIndices.size(), @@ -1096,7 +1119,7 @@ void Model::renderMeshes(float alpha, bool translucent) { glDisableClientState(GL_TEXTURE_COORD_ARRAY); } - if (!mesh.tangents.isEmpty()) { + if (!(mesh.tangents.isEmpty() || forShadowMap)) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index f08a6b9fc2..b4f71f14d3 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -58,7 +58,7 @@ public: void createCollisionShapes(); void updateShapePositions(); void simulate(float deltaTime, bool fullUpdate = true); - bool render(float alpha); + bool render(float alpha = 1.0f, bool forShadowMap = false); /// Sets the URL of the model to render. /// \param fallback the URL of a fallback model to render if the requested model fails to load @@ -261,7 +261,7 @@ private: void applyNextGeometry(); void deleteGeometry(); - void renderMeshes(float alpha, bool translucent); + void renderMeshes(float alpha, bool forShadowMap, bool translucent); QSharedPointer _baseGeometry; ///< reference required to prevent collection of base QSharedPointer _nextBaseGeometry; @@ -283,8 +283,10 @@ private: static ProgramObject _program; static ProgramObject _normalMapProgram; + static ProgramObject _shadowProgram; static ProgramObject _skinProgram; static ProgramObject _skinNormalMapProgram; + static ProgramObject _skinShadowProgram; static int _normalMapTangentLocation; @@ -298,6 +300,7 @@ private: static SkinLocations _skinLocations; static SkinLocations _skinNormalMapLocations; + static SkinLocations _skinShadowLocations; static void initSkinProgram(ProgramObject& program, SkinLocations& locations); static QVector createJointStates(const FBXGeometry& geometry); diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index da03aad697..fa6873b093 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -30,6 +30,9 @@ ViewFrustum::ViewFrustum() : _direction(IDENTITY_FRONT), _up(IDENTITY_UP), _right(IDENTITY_RIGHT), + _orthographic(false), + _width(1.0f), + _height(1.0f), _fieldOfView(0.0), _aspectRatio(1.0f), _nearClip(0.1f), @@ -62,6 +65,11 @@ void ViewFrustum::setOrientation(const glm::quat& orientationAsQuaternion) { // http://www.lighthouse3d.com/tutorials/view-frustum-culling/view-frustums-shape/ // void ViewFrustum::calculate() { + if (_orthographic) { + calculateOrthographic(); + return; + } + // compute the off-axis frustum parameters as we would for glFrustum float left, right, bottom, top, nearVal, farVal; glm::vec4 nearClipPlane, farClipPlane; @@ -133,6 +141,49 @@ void ViewFrustum::calculate() { _keyholeBoundingBox = AABox(corner,(_keyholeRadius * 2.0f)); } +void ViewFrustum::calculateOrthographic() { + float halfWidth = _width * 0.5f; + float halfHeight = _height * 0.5f; + + // find the corners of the view box in world space + glm::mat4 worldMatrix = glm::translate(_position) * glm::mat4(glm::mat3(_right, _up, -_direction)) * + glm::translate(_eyeOffsetPosition) * glm::mat4_cast(_eyeOffsetOrientation); + _farTopLeft = glm::vec3(worldMatrix * glm::vec4(-halfWidth, halfHeight, -_farClip, 1.0f)); + _farTopRight = glm::vec3(worldMatrix * glm::vec4(halfWidth, halfHeight, -_farClip, 1.0f)); + _farBottomLeft = glm::vec3(worldMatrix * glm::vec4(-halfWidth, -halfHeight, -_farClip, 1.0f)); + _farBottomRight = glm::vec3(worldMatrix * glm::vec4(halfWidth, -halfHeight, -_farClip, 1.0f)); + _nearTopLeft = glm::vec3(worldMatrix * glm::vec4(-halfWidth, halfHeight, -_nearClip, 1.0f)); + _nearTopRight = glm::vec3(worldMatrix * glm::vec4(halfWidth, halfHeight, -_nearClip, 1.0f)); + _nearBottomLeft = glm::vec3(worldMatrix * glm::vec4(-halfWidth, -halfHeight, -_nearClip, 1.0f)); + _nearBottomRight = glm::vec3(worldMatrix * glm::vec4(halfWidth, -halfHeight, -_nearClip, 1.0f)); + + // compute the offset position and axes in world space + _offsetPosition = glm::vec3(worldMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + _offsetDirection = glm::vec3(worldMatrix * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f)); + _offsetUp = glm::vec3(worldMatrix * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f)); + _offsetRight = glm::vec3(worldMatrix * glm::vec4(1.0f, 0.0f, 0.0f, 0.0f)); + + _planes[TOP_PLANE].set3Points(_nearTopRight, _nearTopLeft, _farTopLeft); + _planes[BOTTOM_PLANE].set3Points(_nearBottomLeft, _nearBottomRight, _farBottomRight); + _planes[LEFT_PLANE].set3Points(_nearBottomLeft, _farBottomLeft, _farTopLeft); + _planes[RIGHT_PLANE].set3Points(_farBottomRight, _nearBottomRight, _nearTopRight); + _planes[NEAR_PLANE].set3Points(_nearBottomRight, _nearBottomLeft, _nearTopLeft); + _planes[FAR_PLANE].set3Points(_farBottomLeft, _farBottomRight, _farTopRight); + + // Also calculate our projection matrix in case people want to project points... + // Projection matrix : Field of View, ratio, display range : near to far + glm::mat4 projection = glm::ortho(-halfWidth, halfWidth, -halfHeight, halfHeight, _nearClip, _farClip); + glm::vec3 lookAt = _position + _direction; + glm::mat4 view = glm::lookAt(_position, lookAt, _up); + + // Our ModelViewProjection : multiplication of our 3 matrices (note: model is identity, so we can drop it) + _ourModelViewProjectionMatrix = projection * view; // Remember, matrix multiplication is the other way around + + // Set up our keyhole bounding box... + glm::vec3 corner = _position - _keyholeRadius; + _keyholeBoundingBox = AABox(corner, (_keyholeRadius * 2.0f)); +} + //enum { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE }; const char* ViewFrustum::debugPlaneName (int plane) const { switch (plane) { diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index a0b3a851aa..7a1c3b49ba 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -41,6 +41,9 @@ public: const glm::vec3& getRight() const { return _right; } // setters for lens attributes + void setOrthographic(bool orthographic) { _orthographic = orthographic; } + void setWidth(float width) { _width = width; } + void setHeight(float height) { _height = height; } void setFieldOfView(float f) { _fieldOfView = f; } void setAspectRatio(float a) { _aspectRatio = a; } void setNearClip(float n) { _nearClip = n; } @@ -50,6 +53,9 @@ public: void setEyeOffsetOrientation(const glm::quat& o) { _eyeOffsetOrientation = o; } // getters for lens attributes + bool isOrthographic() const { return _orthographic; } + float getWidth() const { return _width; } + float getHeight() const { return _height; } float getFieldOfView() const { return _fieldOfView; } float getAspectRatio() const { return _aspectRatio; } float getNearClip() const { return _nearClip; } @@ -114,6 +120,8 @@ private: ViewFrustum::location sphereInKeyhole(const glm::vec3& center, float radius) const; ViewFrustum::location boxInKeyhole(const AABox& box) const; + void calculateOrthographic(); + // camera location/orientation attributes glm::vec3 _position; // the position in TREE_SCALE glm::vec3 _positionVoxelScale; // the position in voxel scale @@ -125,6 +133,9 @@ private: glm::vec3 _right; // Lens attributes + bool _orthographic; + float _width; + float _height; float _fieldOfView; // degrees float _aspectRatio; float _nearClip;