mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-07 03:12:36 +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:
parent
1d476270c0
commit
3e9b0c64e4
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -86,6 +86,8 @@ class GameRenderer
|
||||
|
||||
/** Camera values passed to renderWorld() */
|
||||
ViewCamera _camera;
|
||||
ViewCamera cullingCamera;
|
||||
bool cullOverride;
|
||||
|
||||
GLuint framebufferName;
|
||||
GLuint fbTextures[2];
|
||||
@ -129,38 +131,6 @@ public:
|
||||
* - draws the skybox
|
||||
*/
|
||||
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)
|
||||
@ -202,6 +172,12 @@ public:
|
||||
}
|
||||
|
||||
void setViewport(int w, int h);
|
||||
|
||||
void setCullOverride(bool override, const ViewCamera& cullCamera)
|
||||
{
|
||||
cullingCamera = cullCamera;
|
||||
cullOverride = override;
|
||||
}
|
||||
|
||||
MapRenderer map;
|
||||
WaterRenderer water;
|
||||
|
61
rwengine/include/render/ObjectRenderer.hpp
Normal file
61
rwengine/include/render/ObjectRenderer.hpp
Normal 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
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
// Also parallelizable
|
||||
std::sort(renderList.begin(), renderList.end(),
|
||||
[](const RenderInstruction&a, const RenderInstruction&b) {
|
||||
return a.sortKey < b.sortKey;
|
||||
});
|
||||
|
||||
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)
|
||||
|
616
rwengine/src/render/ObjectRenderer.cpp
Normal file
616
rwengine/src/render/ObjectRenderer.cpp
Normal 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;
|
||||
}
|
||||
}
|
@ -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
112
rwgame/benchmarkstate.cpp
Normal 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
36
rwgame/benchmarkstate.hpp
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -165,7 +165,12 @@ public:
|
||||
|
||||
~Model();
|
||||
|
||||
float getBoundingRadius() const;
|
||||
void recalculateMetrics();
|
||||
|
||||
float getBoundingRadius() const { return boundingRadius; }
|
||||
|
||||
private:
|
||||
float boundingRadius;
|
||||
};
|
||||
|
||||
typedef ResourceHandle<Model>::Ref ModelRef;
|
||||
|
@ -465,5 +465,8 @@ Model* LoaderDFF::loadFromMemory(FileHandle file)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the model has cached metrics
|
||||
model->recalculateMetrics();
|
||||
|
||||
return model;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user