Skip to content

Commit

Permalink
Renderer|Client: Environment cube map textures
Browse files Browse the repository at this point in the history
Added render::Environment and an instance of it owned by RenderSystem.
RenderSystem now deletes subsystems in reverse order compared to
construction, to ensure appropriate dependency order.

Added a new asset type "texture.reflect.(map-id)" for specifying which
reflection cube maps to use in which map.

IssueID #2105
  • Loading branch information
skyjake committed Dec 29, 2015
1 parent abd61c8 commit 460327f
Show file tree
Hide file tree
Showing 8 changed files with 316 additions and 58 deletions.
54 changes: 54 additions & 0 deletions doomsday/apps/client/include/render/environ.h
@@ -0,0 +1,54 @@
/** @file environ.h Environment rendering.
*
* @authors Copyright (c) 2015 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* GPL: http://www.gnu.org/licenses/gpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details. You should have received a copy of the GNU
* General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

#ifndef DENG_CLIENT_RENDER_ENVIRON_H
#define DENG_CLIENT_RENDER_ENVIRON_H

#include <de/GLTexture>

class BspLeaf;

namespace render {

/**
* Environmental rendering and texture maps.
*/
class Environment
{
public:
Environment();

de::GLTexture const &defaultReflection() const;

/**
* Determines the reflection cube map suitable for an object at a particular
* position in the current map.
*
* @param sector Sector.
*
* @return Reflection cube map.
*/
de::GLTexture const &reflectionInBspLeaf(BspLeaf const *bspLeaf) const;

private:
DENG2_PRIVATE(d)
};

} // namespace render

#endif // DENG_CLIENT_RENDER_ENVIRON_H
13 changes: 6 additions & 7 deletions doomsday/apps/client/include/render/rendersystem.h
Expand Up @@ -35,6 +35,7 @@
class AngleClipper;
class ModelRenderer;
class SkyDrawable;
namespace render { class Environment; }

/**
* Renderer subsystems, draw lists, etc... @ingroup render
Expand All @@ -47,21 +48,19 @@ class RenderSystem : public de::System
// System.
void timeChanged(de::Clock const &);

SettingsRegister &settings();
SettingsRegister &appearanceSettings();

void glInit();
void glDeinit();

de::GLShaderBank &shaders();
de::ImageBank &images();
de::GLUniform const &uMapTime() const;

SettingsRegister &settings();
SettingsRegister &appearanceSettings();

AngleClipper &angleClipper() const;

render::Environment &environment();
ModelRenderer &modelRenderer();

SkyDrawable &sky();
AngleClipper &angleClipper() const;

/**
* Provides access to the central map geometry buffer.
Expand Down
18 changes: 9 additions & 9 deletions doomsday/apps/client/src/clientapp.cpp
Expand Up @@ -235,14 +235,14 @@ DENG2_PIMPL(ClientApp)
}

updater.reset();
delete world;
//delete infineSys;
delete inputSys;
delete resourceSys;
delete winSys;
delete svLink;
delete rendSys;
delete audioSys;
delete resourceSys;
delete inputSys;
delete rendSys;
delete world;
delete svLink;
delete menuBar;
clientAppSingleton = 0;
}
Expand Down Expand Up @@ -443,6 +443,10 @@ void ClientApp::initialize()
}
#endif

// Create the world system.
d->world = new ClientServerWorld;
addSystem(*d->world);

// Create the render system.
d->rendSys = new RenderSystem;
addSystem(*d->rendSys);
Expand Down Expand Up @@ -477,10 +481,6 @@ void ClientApp::initialize()
//d->infineSys = new InFineSystem;
//addSystem(*d->infineSys);

// Create the world system.
d->world = new ClientServerWorld;
addSystem(*d->world);

// Finally, run the bootstrap script.
scriptSystem().importModule("bootstrap");

Expand Down
205 changes: 205 additions & 0 deletions doomsday/apps/client/src/render/environ.cpp
@@ -0,0 +1,205 @@
/** @file environ.cpp Environment rendering.
*
* @authors Copyright (c) 2015 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* GPL: http://www.gnu.org/licenses/gpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details. You should have received a copy of the GNU
* General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

#include "render/environ.h"
#include "world/map.h"
#include "world/bspleaf.h"
#include "world/convexsubspace.h"
#include "clientapp.h"

#include <de/ImageFile>
#include <de/filesys/AssetObserver>
#include <doomsday/world/world.h>

using namespace de;

static String const DEF_PATH ("path");
static String const DEF_INTERIOR_PATH("interior.path");
static String const DEF_EXTERIOR_PATH("exterior.path");

namespace render {

DENG2_PIMPL(Environment)
, DENG2_OBSERVES(filesys::AssetObserver, Availability)
, DENG2_OBSERVES(World, MapChange)
{
enum { Interior, Exterior };

filesys::AssetObserver observer { "texture\\.reflect\\..*" };
LoopCallback mainCall;

struct EnvMaps
{
Path interior;
Path exterior;
};
QHash<String, EnvMaps> maps;

/// Currently loaded reflection textures.
GLTexture reflectionTextures[2];

Instance(Public *i) : Base(i)
{
observer.audienceForAvailability() += this;
World::get().audienceForMapChange() += this;

// Reflection cube maps use mipmapping for blurred reflections.
for(auto &tex : reflectionTextures)
{
tex.setMinFilter(gl::Linear, gl::MipLinear);
tex.setWrap(gl::ClampToEdge, gl::ClampToEdge);
}
}

~Instance()
{
World::get().audienceForMapChange() -= this;

release();
}

void assetAvailabilityChanged(String const &identifier,
filesys::AssetObserver::Event event)
{
LOG_RES_MSG("Texture asset \"%s\" is now %s")
<< identifier
<< (event == filesys::AssetObserver::Added? "available" :
"unavailable");
// Register available reflection maps.
String const mapId = identifier.substr(16).toLower();
switch(event)
{
case filesys::AssetObserver::Added:
addMapsFromAsset(mapId, App::asset(identifier));
break;

case filesys::AssetObserver::Removed:
maps.remove(mapId);
break;
}
}

void addMapsFromAsset(String const &mapId, Package::Asset const &asset)
{
EnvMaps env;

if(asset.has(DEF_PATH))
{
env.interior = env.exterior = asset.absolutePath(DEF_PATH);
}
if(asset.has(DEF_INTERIOR_PATH))
{
env.interior = asset.absolutePath(DEF_INTERIOR_PATH);
}
if(asset.has(DEF_EXTERIOR_PATH))
{
env.exterior = asset.absolutePath(DEF_EXTERIOR_PATH);
}

// All done.
maps.insert(mapId, env);
}

void release()
{
for(auto &tex : reflectionTextures)
{
tex.clear();
}
}

void loadCubeMap(GLTexture &tex, String const &path)
{
try
{
ImageFile const &imgFile = App::rootFolder().locate<ImageFile const>(path);

LOG_GL_MSG("Loading reflection cube map %s") << imgFile.description();

Image img = imgFile.image();
Image::Size const size(img.width() / 6, img.height());
tex.setImage(gl::NegativeX, img.subImage(Rectanglei(0*size.x, 0, size.x, size.y)));
tex.setImage(gl::PositiveZ, img.subImage(Rectanglei(1*size.x, 0, size.x, size.y)));
tex.setImage(gl::PositiveX, img.subImage(Rectanglei(2*size.x, 0, size.x, size.y)));
tex.setImage(gl::NegativeZ, img.subImage(Rectanglei(3*size.x, 0, size.x, size.y)));
tex.setImage(gl::NegativeY, img.subImage(Rectanglei(4*size.x, 0, size.x, size.y)));
tex.setImage(gl::PositiveY, img.subImage(Rectanglei(5*size.x, 0, size.x, size.y)));
tex.generateMipmap();
}
catch(Error const &er)
{
LOG_GL_WARNING("Failed to load reflection cube map from path \"%s\": %s")
<< path << er.asText();
}
}

void worldMapChanged()
{
mainCall.enqueue([this] () { loadTexturesForCurrentMap(); });
}

void loadTexturesForCurrentMap()
{
DENG2_ASSERT_IN_MAIN_THREAD();

release();

String const mapId = ClientApp::world().map().id().toLower();

// Check which reflection maps are available for the new map.
auto found = maps.constFind(mapId);
if(found != maps.constEnd())
{
EnvMaps const &env = found.value();
DENG2_ASSERT(env.interior.isEmpty() && env.exterior.isEmpty());

if(!env.exterior.isEmpty())
{
loadCubeMap(reflectionTextures[Exterior], env.exterior);
}

loadCubeMap(reflectionTextures[Interior], env.interior);
}
}
};

Environment::Environment()
: d(new Instance(this))
{}

GLTexture const &Environment::defaultReflection() const
{
return d->reflectionTextures[Instance::Interior];
}

GLTexture const &Environment::reflectionInBspLeaf(BspLeaf const *bspLeaf) const
{
if(!bspLeaf || !bspLeaf->hasSubspace())
{
return defaultReflection();
}

Sector const &sector = bspLeaf->subspace().sector();
if(sector.hasSkyMaskedPlane())
{
return d->reflectionTextures[Instance::Exterior];
}
return d->reflectionTextures[Instance::Interior];
}

} // namespace render

0 comments on commit 460327f

Please sign in to comment.