1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-25 11:52:40 +01:00

Re-implement object rendering using a depth-sorted approach.

This moves the object rendering logic into ObjectRenderer. This makes
GameRenderer a bit smaller.

There are some rendering logic decisions that haven't been brought
back yet since they may be better placed elsewhere.
This commit is contained in:
Daniel Evans 2016-04-19 01:20:54 +01:00
parent 1d476270c0
commit 3e9b0c64e4
15 changed files with 892 additions and 458 deletions

View File

@ -45,7 +45,7 @@ struct ObjectData : public ObjectInformation
std::string modelName;
std::string textureName;
size_t numClumps;
uint8_t numClumps;
float drawDistance[3];
int32_t flags;
bool LOD;

View File

@ -31,8 +31,8 @@ public:
void changeModel(std::shared_ptr<ObjectData> incoming);
virtual glm::vec3 getPosition() const;
virtual glm::quat getRotation() const;
glm::vec3 getPosition() const override;
glm::quat getRotation() const override;
virtual void setRotation(const glm::quat& r);

View File

@ -86,6 +86,8 @@ class GameRenderer
/** Camera values passed to renderWorld() */
ViewCamera _camera;
ViewCamera cullingCamera;
bool cullOverride;
GLuint framebufferName;
GLuint fbTextures[2];
@ -130,38 +132,6 @@ public:
*/
void renderWorld(GameWorld* world, const ViewCamera &camera, float alpha);
/**
* @brief draws a CharacterObject and any item they are holding.
* @param pedestrian the character to render
*/
void renderPedestrian(CharacterObject* pedestrian);
/**
* @brief draws a VehicleObject and it's wheels.
* @param vehicle vehicle to render
*/
void renderVehicle(VehicleObject* vehicle);
/**
* @brief draw part of the world.
*/
void renderInstance(InstanceObject* instance);
/**
* @brief draws a pickup with it's model
* @param pickup
* @todo corona rendering, with tint.
*/
void renderPickup(PickupObject* pickup);
void renderProjectile(ProjectileObject* projectile);
void renderCutsceneObject(CutsceneObject *cutscene);
void renderWheel(Model*, const glm::mat4& matrix, const std::string& name);
void renderItem(InventoryItem* item, const glm::mat4& modelMatrix);
/**
* Renders the effects (Particles, Lighttrails etc)
*/
@ -203,6 +173,12 @@ public:
void setViewport(int w, int h);
void setCullOverride(bool override, const ViewCamera& cullCamera)
{
cullingCamera = cullCamera;
cullOverride = override;
}
MapRenderer map;
WaterRenderer water;
TextRenderer text;

View File

@ -0,0 +1,61 @@
#ifndef _RWENGINE_OBJECTRENDERER_HPP_
#define _RWENGINE_OBJECTRENDERER_HPP_
#include <glm/glm.hpp>
#include <rw/types.hpp>
#include <render/ViewCamera.hpp>
#include <render/OpenGLRenderer.hpp>
#include <objects/GameObject.hpp>
#include <engine/GameWorld.hpp>
#include <gl/DrawBuffer.hpp>
/*
Rendering Instruction contents:
Model matrix
List of subgeometries(??)
*/
typedef uint64_t RenderKey;
struct RenderInstruction
{
RenderKey sortKey;
// Ideally, this would just be an index into a buffer that contains the matrix
glm::mat4 model;
DrawBuffer* dbuff;
Renderer::DrawParameters drawInfo;
RenderInstruction(
RenderKey key,
const glm::mat4& model,
DrawBuffer* dbuff,
const Renderer::DrawParameters& dp)
: sortKey(key)
, model(model)
, dbuff(dbuff)
, drawInfo(dp)
{
}
};
typedef std::vector<RenderInstruction> RenderList;
/**
* @brief The ObjectRenderer class handles object -> renderer transformation
*
* Determines what parts of an object are within a camera frustum and exports
* a list of things to render for the object.
*/
class ObjectRenderer
{
public:
/**
* @brief buildRenderList
*
* Exports rendering instructions for an object
*/
static void buildRenderList(GameWorld* world, GameObject* object,
const ViewCamera& camera, float renderAlpha,
RenderList& outList);
};
#endif

View File

@ -49,7 +49,7 @@ public:
}
}
bool intersects(glm::vec3 center, float radius)
bool intersects(glm::vec3 center, float radius) const
{
float d;
bool result = true;

View File

@ -7,8 +7,9 @@ GameState::GameState()
, gameTime(0.f),
currentProgress(0),
maxProgress(1),
maxWantedLevel(0),
scriptOnMissionFlag(nullptr),
maxWantedLevel(0)
, playerObject(0)
, scriptOnMissionFlag(nullptr),
fadeOut(true),
fadeStart(0.f),
fadeTime(0.f),

View File

@ -141,19 +141,11 @@ void InstanceObject::changeModel(std::shared_ptr<ObjectData> incoming)
glm::vec3 InstanceObject::getPosition() const
{
if( body ) {
btVector3 Pos = body->body->getWorldTransform().getOrigin();
return glm::vec3(Pos.x(), Pos.y(), Pos.z());
}
return position;
}
glm::quat InstanceObject::getRotation() const
{
if( body ) {
btQuaternion rot = body->body->getWorldTransform().getRotation();
return glm::quat(rot.w(), rot.x(), rot.y(), rot.z());
}
return rotation;
}

View File

@ -17,6 +17,7 @@
#include <data/CutsceneData.hpp>
#include <data/Skeleton.hpp>
#include <objects/CutsceneObject.hpp>
#include <render/ObjectRenderer.hpp>
#include <render/GameShaders.hpp>
#include <core/Logger.hpp>
@ -288,89 +289,49 @@ void GameRenderer::renderWorld(GameWorld* world, const ViewCamera &camera, float
renderer->clear(glm::vec4(skyBottom, 1.f));
_camera.frustum.update(proj * view);
if (cullOverride)
{
cullingCamera.frustum.update(
cullingCamera.frustum.projection() * cullingCamera.getView());
}
culled = 0;
renderer->useProgram(worldProg);
//===============================================================
// Render List Construction
//---------------------------------------------------------------
// This is sequential at the moment, it should be easy to make it
// run in parallel with a good threading system.
RenderList renderList;
// Naive optimisation, assume 10% hitrate
renderList.reserve(world->allObjects.size() * 0.1f);
// World Objects
for (auto object : world->allObjects) {
ObjectRenderer::buildRenderList(
_renderWorld,
object,
(cullOverride ? cullingCamera : _camera),
_renderAlpha,
renderList);
}
renderer->pushDebugGroup("Objects");
renderer->pushDebugGroup("Dynamic");
renderer->pushDebugGroup("RenderList");
for( auto& object : world->allObjects ) {
if(! object->visible )
{
continue;
}
// Also parallelizable
std::sort(renderList.begin(), renderList.end(),
[](const RenderInstruction&a, const RenderInstruction&b) {
return a.sortKey < b.sortKey;
});
if( object->skeleton )
{
object->skeleton->interpolate(_renderAlpha);
}
switch(object->type()) {
case GameObject::Character:
renderPedestrian(static_cast<CharacterObject*>(object));
break;
case GameObject::Vehicle:
renderVehicle(static_cast<VehicleObject*>(object));
break;
case GameObject::Instance:
if(! world->shouldBeOnGrid(object) )
{
renderInstance(static_cast<InstanceObject*>(object));
}
break;
case GameObject::Pickup:
renderPickup(static_cast<PickupObject*>(object));
break;
case GameObject::Projectile:
renderProjectile(static_cast<ProjectileObject*>(object));
break;
case GameObject::Cutscene:
renderCutsceneObject(static_cast<CutsceneObject*>(object));
break;
default: break;
}
for (RenderInstruction& ri : renderList) {
renderer->draw(ri.model, ri.dbuff, ri.drawInfo);
}
renderer->popDebugGroup();
renderer->pushDebugGroup("Static");
// Draw the static instance objects. k = % culled
int c = 0, k = 0;
for(auto& cell : world->worldGrid )
{
c++;
int y = c % WORLD_GRID_WIDTH;
int x = c / WORLD_GRID_WIDTH;
float cellhalf = WORLD_CELL_SIZE/2.f;
float radius = cell.boundingRadius;
auto worldp = glm::vec3(glm::vec2(x,y) * glm::vec2(WORLD_CELL_SIZE) - glm::vec2(WORLD_GRID_SIZE/2.f) + glm::vec2(cellhalf), 0.f);
if( _camera.frustum.intersects(worldp, radius) )
{
for( auto& inst : cell.instances )
{
renderInstance(static_cast<InstanceObject*>(inst));
}
}
else
{
k++;
}
}
renderer->popDebugGroup();
renderer->pushDebugGroup("Transparent");
// Draw anything that got queued.
for(auto it = transparentDrawQueue.begin();
it != transparentDrawQueue.end();
++it)
{
renderer->draw(it->matrix, &it->model->geometries[it->g]->dbuff, it->dp);
}
transparentDrawQueue.clear();
renderer->popDebugGroup();
profObjects = renderer->popDebugGroup();
@ -534,339 +495,6 @@ void GameRenderer::renderPostProcess()
renderer->drawArrays(glm::mat4(), &ssRectDraw, wdp);
}
void GameRenderer::renderPedestrian(CharacterObject *pedestrian)
{
glm::mat4 matrixModel = pedestrian->getTimeAdjustedTransform( _renderAlpha );
if(!pedestrian->model->resource) return;
auto root = pedestrian->model->resource->frames[0];
renderFrame(pedestrian->model->resource, root->getChildren()[0], matrixModel, pedestrian, 1.f, pedestrian->animator);
if(pedestrian->getActiveItem()) {
auto handFrame = pedestrian->model->resource->findFrame("srhand");
glm::mat4 localMatrix;
if( handFrame ) {
while( handFrame->getParent() ) {
localMatrix = pedestrian->skeleton->getMatrix(handFrame->getIndex()) * localMatrix;
handFrame = handFrame->getParent();
}
}
renderItem(pedestrian->getActiveItem(), matrixModel * localMatrix);
}
}
void GameRenderer::renderVehicle(VehicleObject *vehicle)
{
if(!vehicle->model)
{
logger->warning("Renderer", "Vehicle model " + vehicle->vehicle->modelName + " not loaded!");
}
glm::mat4 matrixModel = vehicle->getTimeAdjustedTransform( _renderAlpha );
renderModel(vehicle->model->resource, matrixModel, vehicle);
// Draw wheels n' stuff
for( size_t w = 0; w < vehicle->info->wheels.size(); ++w) {
auto woi = data->findObjectType<ObjectData>(vehicle->vehicle->wheelModelID);
if( woi ) {
Model* wheelModel = data->models["wheels"]->resource;
auto& wi = vehicle->physVehicle->getWheelInfo(w);
if( wheelModel ) {
// Construct our own matrix so we can use the local transform
vehicle->physVehicle->updateWheelTransform(w, false);
/// @todo migrate this into Vehicle physics tick so we can interpolate old -> new
glm::mat4 wheelM ( matrixModel );
auto up = -wi.m_wheelDirectionCS;
auto right = wi.m_wheelAxleCS;
auto fwd = up.cross(right);
btQuaternion steerQ(up, wi.m_steering);
btQuaternion rollQ(right, -wi.m_rotation);
btMatrix3x3 basis(
right[0], fwd[0], up[0],
right[1], fwd[1], up[1],
right[2], fwd[2], up[2]
);
btTransform t;
t.setBasis(btMatrix3x3(steerQ) * btMatrix3x3(rollQ) * basis);
t.setOrigin(wi.m_chassisConnectionPointCS + wi.m_wheelDirectionCS * wi.m_raycastInfo.m_suspensionLength);
t.getOpenGLMatrix(glm::value_ptr(wheelM));
wheelM = matrixModel * wheelM;
wheelM = glm::scale(wheelM, glm::vec3(vehicle->vehicle->wheelScale));
if(wi.m_chassisConnectionPointCS.x() < 0.f) {
wheelM = glm::scale(wheelM, glm::vec3(-1.f, 1.f, 1.f));
}
renderWheel(wheelModel, wheelM, woi->modelName);
}
}
}
}
void GameRenderer::renderInstance(InstanceObject *instance)
{
if(instance->object && instance->object->timeOn != instance->object->timeOff) {
// Update rendering flags.
if(_renderWorld->getHour() < instance->object->timeOn
&& _renderWorld->getHour() > instance->object->timeOff) {
return;
}
}
if(!instance->model->resource)
{
return;
}
auto matrixModel = instance->getTimeAdjustedTransform(_renderAlpha);
float mindist = 100000.f;
for (size_t g = 0; g < instance->model->resource->geometries.size(); g++)
{
RW::BSGeometryBounds& bounds = instance->model->resource->geometries[g]->geometryBounds;
mindist = std::min(mindist, glm::length((glm::vec3(matrixModel[3])+bounds.center) - _camera.position) - bounds.radius);
}
Model* model = nullptr;
ModelFrame* frame = nullptr;
// These are used to gracefully fade out things that are just out of view distance.
Model* fadingModel = nullptr;
ModelFrame* fadingFrame = nullptr;
float opacity = 0.f;
if( instance->object->numClumps == 1 ) {
// Object consists of a single clump.
if( mindist > instance->object->drawDistance[0] ) {
// Check for LOD instances
if ( instance->LODinstance ) {
if( mindist > instance->LODinstance->object->drawDistance[0] ) {
culled++;
return;
}
else if (instance->LODinstance->model->resource) {
model = instance->LODinstance->model->resource;
fadingModel = instance->model->resource;
opacity = (mindist) / instance->object->drawDistance[0];
}
}
else {
fadingModel = instance->model->resource;
opacity = (mindist) / instance->object->drawDistance[0];
}
}
else if (! instance->object->LOD ) {
model = instance->model->resource;
opacity = (mindist) / instance->object->drawDistance[0];
}
}
else {
if( mindist > instance->object->drawDistance[1] ) {
culled++;
return;
}
auto root = instance->model->resource->frames[0];
int lodInd = 1;
if( mindist > instance->object->drawDistance[0] ) {
lodInd = 2;
}
auto LODindex = root->getChildren().size() - lodInd;
auto f = root->getChildren()[LODindex];
model = instance->model->resource;
frame = f;
if( lodInd == 2 ) {
fadingModel = model;
fadingFrame = root->getChildren()[LODindex+1];
opacity = (mindist) / instance->object->drawDistance[0];
}
}
if( model ) {
frame = frame ? frame : model->frames[0];
renderFrame(model, frame, matrixModel * glm::inverse(frame->getTransform()), nullptr, 1.f);
}
if( fadingModel ) {
// opacity is the distance over the culling limit,
opacity = 2.0f - opacity;
if(opacity > 0.f) {
fadingFrame = fadingFrame ? fadingFrame : fadingModel->frames[0];
renderFrame(fadingModel, fadingFrame, matrixModel * glm::inverse(fadingFrame->getTransform()), nullptr, opacity);
}
}
}
void GameRenderer::renderPickup(PickupObject *pickup)
{
if( ! pickup->isEnabled() ) return;
glm::mat4 modelMatrix = glm::translate(glm::mat4(), pickup->getPosition());
modelMatrix = glm::rotate(modelMatrix, _renderWorld->getGameTime(), glm::vec3(0.f, 0.f, 1.f));
auto odata = data->findObjectType<ObjectData>(pickup->getModelID());
Model* model = nullptr;
ModelFrame* itemModel = nullptr;
/// @todo Better determination of is this object a weapon.
if( odata->ID >= 170 && odata->ID <= 184 )
{
auto weapons = data->models["weapons"];
if( weapons && weapons->resource && odata ) {
model = weapons->resource;
itemModel = weapons->resource->findFrame(odata->modelName + "_l0");
if ( ! itemModel )
{
logger->error("Renderer", "Weapon frame " + odata->modelName + " not in model");
}
}
}
else
{
auto handle = data->models[odata->modelName];
if ( handle && handle->resource )
{
model = handle->resource;
itemModel = model->frames[model->rootFrameIdx];
}
else
{
logger->error("Renderer", "Pickup model " + odata->modelName + " not loaded");
}
}
if ( itemModel ) {
auto matrix = glm::inverse(itemModel->getTransform());
renderFrame(model, itemModel, modelMatrix * matrix, nullptr, 1.f);
}
}
void GameRenderer::renderCutsceneObject(CutsceneObject *cutscene)
{
if(!_renderWorld->state->currentCutscene) return;
if(!cutscene->model->resource)
{
return;
}
glm::mat4 matrixModel;
if( cutscene->getParentActor() ) {
matrixModel = glm::translate(matrixModel, _renderWorld->state->currentCutscene->meta.sceneOffset + glm::vec3(0.f, 0.f, 1.f));
//matrixModel = cutscene->getParentActor()->getTimeAdjustedTransform(_renderAlpha);
//matrixModel = glm::translate(matrixModel, glm::vec3(0.f, 0.f, 1.f));
glm::mat4 localMatrix;
auto boneframe = cutscene->getParentFrame();
while( boneframe ) {
localMatrix = cutscene->getParentActor()->skeleton->getMatrix(boneframe->getIndex()) * localMatrix;
boneframe = boneframe->getParent();
}
matrixModel = matrixModel * localMatrix;
}
else {
matrixModel = glm::translate(matrixModel, _renderWorld->state->currentCutscene->meta.sceneOffset + glm::vec3(0.f, 0.f, 1.f));
}
float mindist = 100000.f;
for (size_t g = 0; g < cutscene->model->resource->geometries.size(); g++)
{
RW::BSGeometryBounds& bounds = cutscene->model->resource->geometries[g]->geometryBounds;
mindist = std::min(mindist, glm::length((glm::vec3(matrixModel[3])+bounds.center) - _camera.position) - bounds.radius);
}
if( cutscene->getParentActor() ) {
glm::mat4 align;
/// @todo figure out where this 90 degree offset is coming from.
align = glm::rotate(align, glm::half_pi<float>(), {0.f, 1.f, 0.f});
renderModel(cutscene->model->resource, matrixModel * align, cutscene);
}
else {
renderModel(cutscene->model->resource, matrixModel, cutscene);
}
}
void GameRenderer::renderProjectile(ProjectileObject *projectile)
{
glm::mat4 modelMatrix = projectile->getTimeAdjustedTransform(_renderAlpha);
auto odata = data->findObjectType<ObjectData>(projectile->getProjectileInfo().weapon->modelID);
auto weapons = data->models["weapons"];
if( weapons && weapons->resource ) {
auto itemModel = weapons->resource->findFrame(odata->modelName + "_l0");
auto matrix = glm::inverse(itemModel->getTransform());
if(itemModel) {
renderFrame(weapons->resource, itemModel, modelMatrix * matrix, nullptr, 1.f);
}
else {
logger->error("Renderer", "Weapon frame " + odata->modelName + " not in model");
}
}
else {
logger->error("Renderer", "Weapon.dff not loaded");
}
}
void GameRenderer::renderWheel(Model* model, const glm::mat4 &matrix, const std::string& name)
{
for (const ModelFrame* f : model->frames)
{
const std::string& fname = f->getName();
if( fname != name ) {
continue;
}
auto firstLod = f->getChildren()[0];
for( auto& g : firstLod->getGeometries() ) {
RW::BSGeometryBounds& bounds = model->geometries[g]->geometryBounds;
if(! _camera.frustum.intersects(bounds.center + glm::vec3(matrix[3]), bounds.radius)) {
culled++;
continue;
}
renderGeometry(model, g, matrix, 1.f);
}
break;
}
}
void GameRenderer::renderItem(InventoryItem *item, const glm::mat4 &modelMatrix)
{
// srhand
if (item->getModelID() == -1) {
return; // No model for this item
}
std::shared_ptr<ObjectData> odata = data->findObjectType<ObjectData>(item->getModelID());
auto weapons = data->models["weapons"];
if( weapons && weapons->resource ) {
auto itemModel = weapons->resource->findFrame(odata->modelName + "_l0");
auto matrix = glm::inverse(itemModel->getTransform());
if(itemModel) {
renderFrame(weapons->resource, itemModel, modelMatrix * matrix, nullptr, 1.f);
}
else {
logger->error("Renderer", "Weapon frame " + odata->modelName + " not in model");
}
}
else {
logger->error("Renderer", "Weapon model not loaded");
}
}
void GameRenderer::renderGeometry(Model* model, size_t g, const glm::mat4& modelMatrix, float opacity, GameObject* object)
{
for(size_t sg = 0; sg < model->geometries[g]->subgeom.size(); ++sg)

View File

@ -0,0 +1,616 @@
#include <render/ObjectRenderer.hpp>
#include <data/Skeleton.hpp>
#include <data/Model.hpp>
#include <engine/GameData.hpp>
#include <engine/GameState.hpp>
#include <data/CutsceneData.hpp>
#include <glm/gtc/type_ptr.hpp>
// Objects that we know how to turn into renderlist entries
#include <objects/InstanceObject.hpp>
#include <objects/VehicleObject.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/ProjectileObject.hpp>
#include <objects/PickupObject.hpp>
#include <objects/CutsceneObject.hpp>
#include <items/InventoryItem.hpp>
RenderKey createKey(bool transparent, float normalizedDepth, Renderer::Textures& textures)
{
return ((transparent?0x1:0x0) << 31)
| uint32_t(0x7FFFFF * (transparent? 1.f - normalizedDepth : normalizedDepth)) << 8
| uint8_t(0xFF & (textures.size() > 0 ? textures[0] : 0)) << 0;
}
void renderGeometry(GameWorld* world,
Model* model, size_t g,
const glm::mat4& modelMatrix,
float opacity,
GameObject* object,
const ViewCamera& camera,
RenderList& outList)
{
for(size_t sg = 0; sg < model->geometries[g]->subgeom.size(); ++sg)
{
Model::SubGeometry& subgeom = model->geometries[g]->subgeom[sg];
bool isTransparent = false;
Renderer::DrawParameters dp;
dp.colour = {255, 255, 255, 255};
dp.count = subgeom.numIndices;
dp.start = subgeom.start;
dp.textures = {0};
if (model->geometries[g]->materials.size() > subgeom.material) {
Model::Material& mat = model->geometries[g]->materials[subgeom.material];
if(mat.textures.size() > 0 ) {
auto tex = mat.textures[0].texture;
if( ! tex )
{
auto& tC = mat.textures[0].name;
auto& tA = mat.textures[0].alphaName;
tex = world->data->findTexture(tC, tA);
if( ! tex )
{
//logger->warning("Renderer", "Missing texture: " + tC + " " + tA);
}
mat.textures[0].texture = tex;
}
if( tex )
{
if( tex->isTransparent() ) {
isTransparent = true;
}
dp.textures = {tex->getName()};
}
}
if( (model->geometries[g]->flags & RW::BSGeometry::ModuleMaterialColor) == RW::BSGeometry::ModuleMaterialColor) {
dp.colour = mat.colour;
if( object && object->type() == GameObject::Vehicle ) {
auto vehicle = static_cast<VehicleObject*>(object);
if( dp.colour.r == 60 && dp.colour.g == 255 && dp.colour.b == 0 ) {
dp.colour = glm::u8vec4(vehicle->colourPrimary, 255);
}
else if( dp.colour.r == 255 && dp.colour.g == 0 && dp.colour.b == 175 ) {
dp.colour = glm::u8vec4(vehicle->colourSecondary, 255);
}
}
}
dp.colour.a *= opacity;
if( dp.colour.a < 255 ) {
isTransparent = true;
}
dp.diffuse = mat.diffuseIntensity;
dp.ambient = mat.ambientIntensity;
}
glm::vec3 position(modelMatrix[3]);
float distance = glm::length(camera.position - position);
float depth = (distance - camera.frustum.near) / (camera.frustum.far - camera.frustum.near);
outList.emplace_back(
createKey(isTransparent, depth * depth, dp.textures),
modelMatrix,
&model->geometries[g]->dbuff,
dp
);
}
}
bool renderFrame(GameWorld* world,
Model* m,
ModelFrame* f,
const glm::mat4& matrix,
GameObject* object,
float opacity,
const ViewCamera& camera,
RenderList& outList)
{
auto localmatrix = matrix;
bool vis = true;
if(object && object->skeleton) {
// Skeleton is loaded with the correct matrix via Animator.
localmatrix *= object->skeleton->getMatrix(f);
vis = object->skeleton->getData(f->getIndex()).enabled;
}
else {
localmatrix *= f->getTransform();
}
if( vis ) {
for(size_t g : f->getGeometries()) {
if( !object || !object->animator )
{
RW::BSGeometryBounds& bounds = m->geometries[g]->geometryBounds;
glm::vec3 boundpos = bounds.center + glm::vec3(localmatrix[3]);
if(! camera.frustum.intersects(boundpos, bounds.radius)) {
continue;
}
}
renderGeometry(world, m, g, localmatrix, opacity, object, camera, outList);
}
}
for(ModelFrame* c : f->getChildren()) {
renderFrame(world, m, c, localmatrix, object, opacity, camera, outList);
}
return true;
}
void renderItem(GameWorld* world,
InventoryItem *item,
const glm::mat4 &modelMatrix,
const ViewCamera& camera,
RenderList& outList)
{
// srhand
if (item->getModelID() == -1) {
return; // No model for this item
}
std::shared_ptr<ObjectData> odata = world->data->findObjectType<ObjectData>(item->getModelID());
auto weapons = world->data->models["weapons"];
if( weapons && weapons->resource ) {
auto itemModel = weapons->resource->findFrame(odata->modelName + "_l0");
auto matrix = glm::inverse(itemModel->getTransform());
if(itemModel) {
renderFrame(world,
weapons->resource,
itemModel,
modelMatrix * matrix,
nullptr,
1.f,
camera,
outList);
}
}
}
void renderInstance(GameWorld* world,
InstanceObject *instance,
const ViewCamera& camera,
float renderAlpha,
std::vector<RenderInstruction>& outList)
{
if(!instance->model->resource)
{
return;
}
auto matrixModel = instance->getTimeAdjustedTransform(renderAlpha);
float mindist = glm::length(instance->getPosition()-camera.position) - instance->model->resource->getBoundingRadius();
Model* model = nullptr;
ModelFrame* frame = nullptr;
// These are used to gracefully fade out things that are just out of view distance.
Model* fadingModel = nullptr;
ModelFrame* fadingFrame = nullptr;
float opacity = 0.f;
if( instance->object->numClumps == 1 ) {
// Object consists of a single clump.
if( mindist > instance->object->drawDistance[0] ) {
// Check for LOD instances
if ( instance->LODinstance ) {
if( mindist > instance->LODinstance->object->drawDistance[0] ) {
return;
}
else if (instance->LODinstance->model->resource) {
model = instance->LODinstance->model->resource;
fadingModel = instance->model->resource;
opacity = (mindist) / instance->object->drawDistance[0];
}
}
else {
fadingModel = instance->model->resource;
opacity = (mindist) / instance->object->drawDistance[0];
}
}
else if (! instance->object->LOD ) {
model = instance->model->resource;
opacity = (mindist) / instance->object->drawDistance[0];
}
}
else {
if( mindist > instance->object->drawDistance[1] ) {
return;
}
auto root = instance->model->resource->frames[0];
int lodInd = 1;
if( mindist > instance->object->drawDistance[0] ) {
lodInd = 2;
}
auto LODindex = root->getChildren().size() - lodInd;
auto f = root->getChildren()[LODindex];
model = instance->model->resource;
frame = f;
if( lodInd == 2 ) {
fadingModel = model;
fadingFrame = root->getChildren()[LODindex+1];
opacity = (mindist) / instance->object->drawDistance[0];
}
}
if( model ) {
frame = frame ? frame : model->frames[0];
renderFrame(world,
model,
frame,
matrixModel * glm::inverse(frame->getTransform()),
instance,
1.f,
camera,
outList);
}
if( fadingModel ) {
// opacity is the distance over the culling limit,
opacity = 2.0f - opacity;
if(opacity > 0.f) {
fadingFrame = fadingFrame ? fadingFrame : fadingModel->frames[0];
renderFrame(world,
fadingModel,
fadingFrame, matrixModel * glm::inverse(fadingFrame->getTransform()),
instance,
opacity,
camera,
outList);
}
}
}
void renderCharacter(GameWorld* world,
CharacterObject *pedestrian,
const ViewCamera& camera,
float renderAlpha,
RenderList& outList)
{
glm::mat4 matrixModel = pedestrian->getTimeAdjustedTransform( renderAlpha );
if(!pedestrian->model->resource) return;
auto root = pedestrian->model->resource->frames[0];
renderFrame(world,
pedestrian->model->resource,
root->getChildren()[0],
matrixModel,
pedestrian,
1.f,
camera,
outList);
if(pedestrian->getActiveItem()) {
auto handFrame = pedestrian->model->resource->findFrame("srhand");
glm::mat4 localMatrix;
if( handFrame ) {
while( handFrame->getParent() ) {
localMatrix = pedestrian->skeleton->getMatrix(handFrame->getIndex()) * localMatrix;
handFrame = handFrame->getParent();
}
}
renderItem(world,
pedestrian->getActiveItem(),
matrixModel * localMatrix,
camera,
outList);
}
}
void renderWheel(
GameWorld* world,
VehicleObject* vehicle,
Model* model,
const glm::mat4 &matrix,
const std::string& name,
const ViewCamera& camera,
RenderList& outList)
{
for (const ModelFrame* f : model->frames)
{
const std::string& fname = f->getName();
if( fname != name ) {
continue;
}
auto firstLod = f->getChildren()[0];
for( auto& g : firstLod->getGeometries() ) {
RW::BSGeometryBounds& bounds = model->geometries[g]->geometryBounds;
if(! camera.frustum.intersects(bounds.center + glm::vec3(matrix[3]), bounds.radius)) {
continue;
}
renderGeometry(world, model, g, matrix, 1.f, vehicle, camera, outList);
}
break;
}
}
void renderVehicle(GameWorld* world,
VehicleObject *vehicle,
const ViewCamera& camera,
float renderAlpha,
RenderList& outList)
{
RW_CHECK(vehicle->model, "Vehicle model is null");
if(!vehicle->model) {
return;
}
glm::mat4 matrixModel = vehicle->getTimeAdjustedTransform( renderAlpha );
renderFrame(world,
vehicle->model->resource,
vehicle->model->resource->frames[0],
matrixModel,
vehicle,
1.f,
camera,
outList);
// Draw wheels n' stuff
for( size_t w = 0; w < vehicle->info->wheels.size(); ++w) {
auto woi = world->data->findObjectType<ObjectData>(vehicle->vehicle->wheelModelID);
if( woi ) {
Model* wheelModel = world->data->models["wheels"]->resource;
auto& wi = vehicle->physVehicle->getWheelInfo(w);
if( wheelModel ) {
// Construct our own matrix so we can use the local transform
vehicle->physVehicle->updateWheelTransform(w, false);
/// @todo migrate this into Vehicle physics tick so we can interpolate old -> new
glm::mat4 wheelM ( matrixModel );
auto up = -wi.m_wheelDirectionCS;
auto right = wi.m_wheelAxleCS;
auto fwd = up.cross(right);
btQuaternion steerQ(up, wi.m_steering);
btQuaternion rollQ(right, -wi.m_rotation);
btMatrix3x3 basis(
right[0], fwd[0], up[0],
right[1], fwd[1], up[1],
right[2], fwd[2], up[2]
);
btTransform t;
t.setBasis(btMatrix3x3(steerQ) * btMatrix3x3(rollQ) * basis);
t.setOrigin(wi.m_chassisConnectionPointCS + wi.m_wheelDirectionCS * wi.m_raycastInfo.m_suspensionLength);
t.getOpenGLMatrix(glm::value_ptr(wheelM));
wheelM = matrixModel * wheelM;
wheelM = glm::scale(wheelM, glm::vec3(vehicle->vehicle->wheelScale));
if(wi.m_chassisConnectionPointCS.x() < 0.f) {
wheelM = glm::scale(wheelM, glm::vec3(-1.f, 1.f, 1.f));
}
renderWheel(world,
vehicle,
wheelModel,
wheelM,
woi->modelName,
camera,
outList);
}
}
}
}
void renderPickup(GameWorld* world,
PickupObject *pickup,
const ViewCamera& camera,
float renderAlpha,
RenderList& outList)
{
if( ! pickup->isEnabled() ) return;
glm::mat4 modelMatrix = glm::translate(glm::mat4(), pickup->getPosition());
modelMatrix = glm::rotate(modelMatrix, world->getGameTime(), glm::vec3(0.f, 0.f, 1.f));
auto odata = world->data->findObjectType<ObjectData>(pickup->getModelID());
Model* model = nullptr;
ModelFrame* itemModel = nullptr;
/// @todo Better determination of is this object a weapon.
if( odata->ID >= 170 && odata->ID <= 184 )
{
auto weapons = world->data->models["weapons"];
if( weapons && weapons->resource && odata ) {
model = weapons->resource;
itemModel = weapons->resource->findFrame(odata->modelName + "_l0");
RW_CHECK(itemModel, "Weapon Frame not present int weapon model");
if ( ! itemModel )
{
return;
}
}
}
else
{
auto handle = world->data->models[odata->modelName];
RW_CHECK( handle && handle->resource, "Pickup has no model");
if ( handle && handle->resource )
{
model = handle->resource;
itemModel = model->frames[model->rootFrameIdx];
}
}
if ( itemModel ) {
auto matrix = glm::inverse(itemModel->getTransform());
renderFrame(world,
model,
itemModel,
modelMatrix * matrix,
pickup,
1.f,
camera,
outList);
}
}
void renderCutsceneObject(GameWorld* world,
CutsceneObject *cutscene,
const ViewCamera& camera,
float renderAlpha,
RenderList& outList)
{
if(!world->state->currentCutscene) return;
if(!cutscene->model->resource)
{
return;
}
glm::mat4 matrixModel;
if( cutscene->getParentActor() ) {
matrixModel = glm::translate(matrixModel, world->state->currentCutscene->meta.sceneOffset + glm::vec3(0.f, 0.f, 1.f));
//matrixModel = cutscene->getParentActor()->getTimeAdjustedTransform(_renderAlpha);
//matrixModel = glm::translate(matrixModel, glm::vec3(0.f, 0.f, 1.f));
glm::mat4 localMatrix;
auto boneframe = cutscene->getParentFrame();
while( boneframe ) {
localMatrix = cutscene->getParentActor()->skeleton->getMatrix(boneframe->getIndex()) * localMatrix;
boneframe = boneframe->getParent();
}
matrixModel = matrixModel * localMatrix;
}
else {
matrixModel = glm::translate(matrixModel, world->state->currentCutscene->meta.sceneOffset + glm::vec3(0.f, 0.f, 1.f));
}
auto model = cutscene->model->resource;
if( cutscene->getParentActor() ) {
glm::mat4 align;
/// @todo figure out where this 90 degree offset is coming from.
align = glm::rotate(align, glm::half_pi<float>(), {0.f, 1.f, 0.f});
renderFrame(world,
model,
model->frames[0],
matrixModel * align,
cutscene,
1.f,
camera,
outList);
}
else {
renderFrame(world,
model,
model->frames[0],
matrixModel,
cutscene,
1.f,
camera,
outList);
}
}
void renderProjectile(GameWorld* world,
ProjectileObject *projectile,
const ViewCamera& camera,
float renderAlpha,
RenderList& outList)
{
glm::mat4 modelMatrix = projectile->getTimeAdjustedTransform(renderAlpha);
auto odata = world->data->findObjectType<ObjectData>(projectile->getProjectileInfo().weapon->modelID);
auto weapons = world->data->models["weapons"];
RW_CHECK(weapons, "Weapons model not loaded");
if( weapons && weapons->resource ) {
auto itemModel = weapons->resource->findFrame(odata->modelName + "_l0");
auto matrix = glm::inverse(itemModel->getTransform());
RW_CHECK(itemModel, "Weapon frame not in model");
if(itemModel) {
renderFrame(world,
weapons->resource,
itemModel,
modelMatrix * matrix,
projectile,
1.f,
camera,
outList);
}
}
}
void ObjectRenderer::buildRenderList(GameWorld* world,
GameObject* object,
const ViewCamera& camera,
float renderAlpha,
RenderList& outList)
{
if( object->skeleton )
{
object->skeleton->interpolate(renderAlpha);
}
// Right now specialized on each object type
switch(object->type()) {
case GameObject::Instance:
renderInstance(world,
static_cast<InstanceObject*>(object),
camera,
renderAlpha,
outList);
break;
case GameObject::Character:
renderCharacter(world,
static_cast<CharacterObject*>(object),
camera,
renderAlpha,
outList);
break;;
case GameObject::Vehicle:
renderVehicle(world,
static_cast<VehicleObject*>(object),
camera,
renderAlpha,
outList);
break;;
case GameObject::Pickup:
renderPickup(world,
static_cast<PickupObject*>(object),
camera,
renderAlpha,
outList);
break;
case GameObject::Projectile:
renderProjectile(world,
static_cast<ProjectileObject*>(object),
camera,
renderAlpha,
outList);
break;
case GameObject::Cutscene:
renderCutsceneObject(world,
static_cast<CutsceneObject*>(object),
camera,
renderAlpha,
outList);
break;
default:
break;
}
}

View File

@ -170,6 +170,11 @@ OpenGLRenderer::OpenGLRenderer()
swap();
GLint maxUBOSize;
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUBOSize);
std::cout << "Max UBO Size: " << maxUBOSize << std::endl;
std::cout << "Max batch size: " << (maxUBOSize/sizeof(ObjectUniformData)) << std::endl;
glGenQueries(1, &debugQuery);
}

112
rwgame/benchmarkstate.cpp Normal file
View File

@ -0,0 +1,112 @@
#include "benchmarkstate.hpp"
#include "RWGame.hpp"
#include <engine/GameState.hpp>
BenchmarkState::BenchmarkState(RWGame* game, const std::string& benchfile)
: State(game)
, benchfile(benchfile)
, benchmarkTime(0.f)
, frameCounter(0)
{
}
void BenchmarkState::enter()
{
std::ifstream benchstream(benchfile);
unsigned int clockHour;
unsigned int clockMinute;
benchstream >> clockHour;
benchstream.seekg(1,std::ios_base::cur);
benchstream >> clockMinute;
game->getWorld()->state->basic.gameHour = clockHour;
game->getWorld()->state->basic.gameMinute = clockMinute;
float time = 0.f;
glm::vec3 tmpPos;
while (benchstream)
{
TrackPoint point;
benchstream >> point.time;
if (!benchstream) break;
benchstream >> point.position.x;
if (!benchstream) break;
benchstream >> point.position.y;
if (!benchstream) break;
benchstream >> point.position.z;
if (!benchstream) break;
benchstream >> point.angle.x;
if (!benchstream) break;
benchstream >> point.angle.y;
if (!benchstream) break;
benchstream >> point.angle.z;
if (!benchstream) break;
benchstream >> point.angle.w;
if (!benchstream) break;
if (track.size() == 0) {
tmpPos = point.position;
}
float pointDist = glm::distance(tmpPos, point.position);
tmpPos = point.position;
point.time = time + pointDist / 50.f;
time = point.time;
duration = std::max(duration, point.time);
track.push_back(point);
}
std::cout << "Loaded " << track.size() << " points" << std::endl;
}
void BenchmarkState::exit()
{
std::cout << "Results =============\n"
<< "Benchmark: " << benchfile << "\n"
<< "Frames: " << frameCounter << "\n"
<< "Duration: " << duration << " seconds\n"
<< "Avg FPS: " << (frameCounter/duration) << std::endl;
}
void BenchmarkState::tick(float dt)
{
if (track.size() > 0)
{
TrackPoint& a = track.front();
TrackPoint& b = track.back();
for (TrackPoint& p : track)
{
if (benchmarkTime < p.time)
{
b = p;
break;
}
a = p;
}
if (benchmarkTime > duration) {
StateManager::get().exit();
}
if (b.time != a.time)
{
float alpha = (benchmarkTime - a.time) / (b.time - a.time);
trackCam.position = glm::mix(a.position, b.position, alpha);
trackCam.rotation = glm::slerp(a.angle, b.angle, alpha);
}
benchmarkTime += dt;
}
}
void BenchmarkState::draw(GameRenderer* r)
{
frameCounter++;
State::draw(r);
}
void BenchmarkState::handleEvent(const sf::Event &e)
{
State::handleEvent(e);
}
const ViewCamera &BenchmarkState::getCamera()
{
return trackCam;
}

36
rwgame/benchmarkstate.hpp Normal file
View File

@ -0,0 +1,36 @@
#ifndef _RWGAME_BENCHMARKSTATE_HPP_
#define _RWGAME_BENCHMARKSTATE_HPP_
#include "State.hpp"
class BenchmarkState : public State
{
struct TrackPoint {
float time;
glm::vec3 position;
glm::quat angle;
};
std::vector<TrackPoint> track;
ViewCamera trackCam;
std::string benchfile;
float benchmarkTime;
float duration;
uint32_t frameCounter;
public:
BenchmarkState(RWGame* game, const std::string& benchfile);
virtual void enter();
virtual void exit();
virtual void tick(float dt);
virtual void draw(GameRenderer* r);
virtual void handleEvent(const sf::Event& event);
const ViewCamera& getCamera();
};
#endif

View File

@ -42,13 +42,12 @@ Model::~Model()
}
}
float Model::getBoundingRadius() const
void Model::recalculateMetrics()
{
float mindist = std::numeric_limits<float>::min();
boundingRadius = std::numeric_limits<float>::min();
for (size_t g = 0; g < geometries.size(); g++)
{
RW::BSGeometryBounds& bounds = geometries[g]->geometryBounds;
mindist = std::max(mindist, glm::length(bounds.center) + bounds.radius);
boundingRadius = std::max(boundingRadius, glm::length(bounds.center) + bounds.radius);
}
return mindist;
}

View File

@ -165,7 +165,12 @@ public:
~Model();
float getBoundingRadius() const;
void recalculateMetrics();
float getBoundingRadius() const { return boundingRadius; }
private:
float boundingRadius;
};
typedef ResourceHandle<Model>::Ref ModelRef;

View File

@ -465,5 +465,8 @@ Model* LoaderDFF::loadFromMemory(FileHandle file)
}
}
// Ensure the model has cached metrics
model->recalculateMetrics();
return model;
}