Skip to content

Commit

Permalink
UPBGE: Fix object render settings for batching groups. (#861)
Browse files Browse the repository at this point in the history
Previously object render settings such as light layer or object color
were not managed for batching groups.

For attribute like layers we can choose to make an union of all objects
layers, but in case of color there's no meaning of union.

To solve this issue one object should be used as reference for the group.
The attribute KX_BatchGroup.referenceObject is defined and initialized
to the first merged object when creating a batch group.
Later the user can change it to an other object of the same group.

Internally this object is set in KX_BatchGroup and RAS_BatchGroup,
KX_BatchGroup see a KX_GameObject and RAS_BatchGroup only the RAS_MeshUser
of this object. This mesh user is retrieved in RAS_DisplayArrayBucket::RunBatchingNode
from the first mesh slot mesh user and passed to RAS_IMaerial::ActivateMeshUser
which aims the update of material uniforms from object settings.
  • Loading branch information
panzergame authored and youle31 committed May 26, 2019
1 parent c876c51 commit 014bb4c
Show file tree
Hide file tree
Showing 16 changed files with 129 additions and 21 deletions.
9 changes: 9 additions & 0 deletions doc/python_api/rst/bge_types/bge.types.KX_BatchGroup.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,21 @@ base class --- :class:`EXP_Value`
batchGroup = types.KX_BatchGroup([scene.objects["Cube"], scene.objects["Plane"]])
.. warning::

Rendering settings unique to objects such as :data:`KX_GameObject.layer` and :data:`KX_GameObject.color` are shared when using batch groups.
These settings are taken from object :attr:`referenceObject`.

.. attribute:: objects

The list of the objects merged. (read only)

:type: :class:`EXP_ListValue` of :class:`KX_GameObject`

.. attribute:: referenceObject

The object used for object rendering settings (layer, color...).

.. method:: merge(objects)

Merge meshes using the transformation of the objects using them.
Expand Down
3 changes: 1 addition & 2 deletions source/gameengine/Ketsji/BL_BlenderShader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,12 @@ void BL_BlenderShader::UpdateLights(RAS_Rasterizer *rasty)
GPU_material_update_lamps(m_gpuMat, rasty->GetViewMatrix().Data(), rasty->GetViewInvMatrix().Data());
}

void BL_BlenderShader::Update(RAS_MeshSlot *ms, RAS_Rasterizer *rasty)
void BL_BlenderShader::Update(RAS_MeshUser *meshUser, RAS_Rasterizer *rasty)
{
if (!GPU_material_bound(m_gpuMat)) {
return;
}

RAS_MeshUser *meshUser = ms->m_meshUser;
const float (&obcol)[4] = meshUser->GetColor().Data();

GPU_material_bind_uniforms(m_gpuMat, meshUser->GetMatrix().Data(), rasty->GetViewMatrix().Data(),
Expand Down
2 changes: 1 addition & 1 deletion source/gameengine/Ketsji/BL_BlenderShader.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class BL_BlenderShader
RAS_InstancingBuffer::Attrib GetInstancingAttribs() const;

void UpdateLights(RAS_Rasterizer *rasty);
void Update(RAS_MeshSlot *ms, RAS_Rasterizer *rasty);
void Update(RAS_MeshUser *meshUser, RAS_Rasterizer *rasty);

/// Return true if the shader uses a special vertex shader for geometry instancing.
bool UseInstancing() const;
Expand Down
6 changes: 3 additions & 3 deletions source/gameengine/Ketsji/BL_Shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,17 @@ void BL_Shader::BindProg()
RAS_Shader::BindProg();
}

void BL_Shader::Update(RAS_Rasterizer *rasty, RAS_MeshSlot *ms)
void BL_Shader::Update(RAS_Rasterizer *rasty, RAS_MeshUser *meshUser)
{
#ifdef WITH_PYTHON
if (PyList_GET_SIZE(m_callbacks[CALLBACKS_OBJECT]) > 0) {
KX_GameObject *gameobj = KX_GameObject::GetClientObject((KX_ClientObjectInfo *)ms->m_meshUser->GetClientObject());
KX_GameObject *gameobj = KX_GameObject::GetClientObject((KX_ClientObjectInfo *)meshUser->GetClientObject());
PyObject *args[] = {gameobj->GetProxy()};
EXP_RunPythonCallBackList(m_callbacks[CALLBACKS_OBJECT], args, 0, ARRAY_SIZE(args));
}
#endif // WITH_PYTHON

RAS_Shader::Update(rasty, mt::mat4(ms->m_meshUser->GetMatrix()));
RAS_Shader::Update(rasty, meshUser->GetMatrix());
}

#ifdef WITH_PYTHON
Expand Down
4 changes: 2 additions & 2 deletions source/gameengine/Ketsji/BL_Shader.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ class BL_Shader : public EXP_Value, public virtual RAS_Shader

void BindProg();

/** Update the uniform shader for the current rendered mesh slot.
/** Update the uniform shader for the current rendered mesh user (= object).
* The python callbacks are executed in this function and at the end
* RAS_Shader::Update(rasty, mat) is called.
*/
void Update(RAS_Rasterizer *rasty, RAS_MeshSlot *ms);
void Update(RAS_Rasterizer *rasty, RAS_MeshUser *meshUser);

// Python interface
#ifdef WITH_PYTHON
Expand Down
75 changes: 71 additions & 4 deletions source/gameengine/Ketsji/KX_BatchGroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "CM_Message.h"

KX_BatchGroup::KX_BatchGroup()
:m_referenceObject(nullptr)
{
m_objects = new EXP_ListValue<KX_GameObject>();
// The objects are not owned by the batching group, so not released on list releasing.
Expand All @@ -53,6 +54,31 @@ EXP_ListValue<KX_GameObject> *KX_BatchGroup::GetObjects() const
return m_objects;
}

KX_GameObject *KX_BatchGroup::GetReferenceObject() const
{
return m_referenceObject;
}

bool KX_BatchGroup::SetReferenceObject(KX_GameObject *object)
{
if (object) {
RAS_MeshUser *meshUser = object->GetMeshUser();
if (!meshUser) {
CM_Error("object \"" << object->GetName() << "\" doesn't contain a mesh");
return false;
}
if (meshUser->GetBatchGroup() != this) {
CM_Error("object \"" << object->GetName() << "\" is not a part of this batch group");
return false;
}
}

m_referenceObject = object;
SetReferenceMeshUser(object ? object->GetMeshUser() : nullptr);

return true;
}

void KX_BatchGroup::MergeObjects(const std::vector<KX_GameObject *>& objects)
{
for (KX_GameObject *gameobj : objects) {
Expand Down Expand Up @@ -94,12 +120,22 @@ void KX_BatchGroup::SplitObjects(const std::vector<KX_GameObject *>& objects)

if (SplitMeshUser(meshUser)) {
m_objects->RemoveValue(gameobj);
if (gameobj == m_referenceObject) {
SetReferenceObject(nullptr);
}
}
else {
CM_Error("failed split object \"" << gameobj->GetName() << "\"");
}
}

// Update the reference mesh user if it was splitted.
if (!m_objects->Empty() && !m_referenceObject) {
// Use the first object as reference for layer and color.
KX_GameObject *firstObject = m_objects->GetFront();
SetReferenceObject(firstObject);
}

RemoveMeshUser();
}

Expand Down Expand Up @@ -133,12 +169,18 @@ static PyObject *py_new(PyTypeObject *type, PyObject *args, PyObject *kwds)

KX_BatchGroup *batchGroup = new KX_BatchGroup();
batchGroup->MergeObjects(objects);
if (batchGroup->GetObjects()->Empty()) {

EXP_ListValue<KX_GameObject> *mergedObjects = batchGroup->GetObjects();
if (mergedObjects->Empty()) {
PyErr_SetString(PyExc_SystemError, "KX_BatchGroup(objects): none objects were merged.");
delete batchGroup;
return nullptr;
}

// Use the first object as reference for layer and color.
KX_GameObject *firstObject = mergedObjects->GetFront();
batchGroup->SetReferenceObject(firstObject);

return batchGroup->GetProxy();
}

Expand Down Expand Up @@ -173,6 +215,7 @@ PyMethodDef KX_BatchGroup::Methods[] = {

PyAttributeDef KX_BatchGroup::Attributes[] = {
EXP_PYATTRIBUTE_RO_FUNCTION("objects", KX_BatchGroup, pyattr_get_objects),
EXP_PYATTRIBUTE_RW_FUNCTION("referenceObject", KX_BatchGroup, pyattr_get_referenceObject, pyattr_set_referenceObject),
EXP_PYATTRIBUTE_NULL // Sentinel
};

Expand All @@ -182,6 +225,30 @@ PyObject *KX_BatchGroup::pyattr_get_objects(EXP_PyObjectPlus *self_v, const EXP_
return self->GetObjects()->GetProxy();
}

PyObject *KX_BatchGroup::pyattr_get_referenceObject(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef)
{
KX_BatchGroup *self = static_cast<KX_BatchGroup *>(self_v);
return self->GetReferenceObject()->GetProxy();
}

int KX_BatchGroup::pyattr_set_referenceObject(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
KX_BatchGroup *self = static_cast<KX_BatchGroup *>(self_v);

KX_GameObject *object;
if (!ConvertPythonToGameObject(KX_GetActiveScene()->GetLogicManager(),
value, &object, false, "KX_BatchGroup.referenceObject"))
{
return PY_SET_ATTR_FAIL;
}

if (self->SetReferenceObject(object)) {
return PY_SET_ATTR_SUCCESS;
}

return PY_SET_ATTR_FAIL;
}

EXP_PYMETHODDEF_DOC(KX_BatchGroup, merge, "merge(objects)")
{
PyObject *pylist;
Expand Down Expand Up @@ -209,7 +276,7 @@ EXP_PYMETHODDEF_DOC(KX_BatchGroup, merge, "merge(objects)")

MergeObjects(objects);

Py_RETURN_NONE;;
Py_RETURN_NONE;
}

EXP_PYMETHODDEF_DOC(KX_BatchGroup, split, "split(objects)")
Expand Down Expand Up @@ -239,14 +306,14 @@ EXP_PYMETHODDEF_DOC(KX_BatchGroup, split, "split(objects)")

SplitObjects(objects);

Py_RETURN_NONE;;
Py_RETURN_NONE;
}

EXP_PYMETHODDEF_DOC(KX_BatchGroup, destruct, "destruct()")
{
Destruct();

Py_RETURN_NONE;;
Py_RETURN_NONE;
}

#endif // WITH_PYTHON
8 changes: 8 additions & 0 deletions source/gameengine/Ketsji/KX_BatchGroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class KX_BatchGroup : public EXP_Value, public RAS_BatchGroup
private:
/// The objects currently merged in the batch group.
EXP_ListValue<KX_GameObject> *m_objects;
/// Reference object used to retrieve layer and color.
KX_GameObject *m_referenceObject;

public:
KX_BatchGroup();
Expand All @@ -49,6 +51,10 @@ class KX_BatchGroup : public EXP_Value, public RAS_BatchGroup

EXP_ListValue<KX_GameObject> *GetObjects() const;

KX_GameObject *GetReferenceObject() const;
/// Set reference object with error checking. Return false on error.
bool SetReferenceObject(KX_GameObject *object);

/** Merge a list of objects using their mesh user and transformation.
* \param objects The list of objects to merge.
*/
Expand All @@ -62,6 +68,8 @@ class KX_BatchGroup : public EXP_Value, public RAS_BatchGroup
#ifdef WITH_PYTHON

static PyObject *pyattr_get_objects(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef);
static PyObject *pyattr_get_referenceObject(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef);
static int pyattr_set_referenceObject(EXP_PyObjectPlus *self_v, const EXP_PYATTRIBUTE_DEF *attrdef, PyObject *value);

EXP_PYMETHOD_DOC(KX_BatchGroup, merge);
EXP_PYMETHOD_DOC(KX_BatchGroup, split);
Expand Down
6 changes: 3 additions & 3 deletions source/gameengine/Ketsji/KX_BlenderMaterial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,16 +363,16 @@ bool KX_BlenderMaterial::UsesLighting() const
}
}

void KX_BlenderMaterial::ActivateMeshSlot(RAS_MeshSlot *ms, RAS_Rasterizer *rasty, const mt::mat3x4& camtrans)
void KX_BlenderMaterial::ActivateMeshUser(RAS_MeshUser *meshUser, RAS_Rasterizer *rasty, const mt::mat3x4& camtrans)
{
if (m_shader && m_shader->Ok()) {
m_shader->Update(rasty, ms);
m_shader->Update(rasty, meshUser);
m_shader->ApplyShader();
// Update OpenGL lighting builtins.
rasty->ProcessLighting(UsesLighting(), camtrans);
}
else if (m_blenderShader) {
m_blenderShader->Update(ms, rasty);
m_blenderShader->Update(meshUser, rasty);

/* we do blend modes here, because they can change per object
* with the same material due to obcolor/obalpha */
Expand Down
2 changes: 1 addition & 1 deletion source/gameengine/Ketsji/KX_BlenderMaterial.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class KX_BlenderMaterial : public EXP_Value, public BL_Resource, public RAS_IMat
virtual void Activate(RAS_Rasterizer *rasty);
virtual void Desactivate(RAS_Rasterizer *rasty);
virtual void ActivateInstancing(RAS_Rasterizer *rasty, RAS_InstancingBuffer *buffer);
virtual void ActivateMeshSlot(RAS_MeshSlot *ms, RAS_Rasterizer *rasty, const mt::mat3x4& camtrans);
virtual void ActivateMeshUser(RAS_MeshUser *meshUser, RAS_Rasterizer *rasty, const mt::mat3x4& camtrans);

void UpdateTextures();
void ApplyTextures();
Expand Down
2 changes: 1 addition & 1 deletion source/gameengine/Ketsji/KX_TextMaterial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ void KX_TextMaterial::DesactivateInstancing()
{
}

void KX_TextMaterial::ActivateMeshSlot(RAS_MeshSlot *ms, RAS_Rasterizer *rasty, const mt::mat3x4& camtrans)
void KX_TextMaterial::ActivateMeshUser(RAS_MeshUser *meshUser, RAS_Rasterizer *rasty, const mt::mat3x4& camtrans)
{
}

Expand Down
2 changes: 1 addition & 1 deletion source/gameengine/Ketsji/KX_TextMaterial.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class KX_TextMaterial : public RAS_IMaterial
virtual void Desactivate(RAS_Rasterizer *rasty);
virtual void ActivateInstancing(RAS_Rasterizer *rasty, RAS_InstancingBuffer *buffer);
virtual void DesactivateInstancing();
virtual void ActivateMeshSlot(RAS_MeshSlot *ms, RAS_Rasterizer *rasty, const mt::mat3x4& camtrans);
virtual void ActivateMeshUser(RAS_MeshUser *meshUser, RAS_Rasterizer *rasty, const mt::mat3x4& camtrans);

virtual const std::string GetTextureName() const;
virtual Material *GetBlenderMaterial() const;
Expand Down
13 changes: 12 additions & 1 deletion source/gameengine/Rasterizer/RAS_BatchGroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ RAS_BatchGroup::Batch::Batch()
}

RAS_BatchGroup::RAS_BatchGroup()
:m_users(0)
:m_users(0),
m_referenceMeshUser(nullptr)
{
}

Expand Down Expand Up @@ -70,6 +71,16 @@ RAS_BatchGroup *RAS_BatchGroup::RemoveMeshUser()
return this;
}

RAS_MeshUser *RAS_BatchGroup::GetReferenceMeshUser() const
{
return m_referenceMeshUser;
}

void RAS_BatchGroup::SetReferenceMeshUser(RAS_MeshUser *meshUser)
{
m_referenceMeshUser = meshUser;
}

bool RAS_BatchGroup::MergeMeshSlot(RAS_BatchGroup::Batch& batch, RAS_MeshSlot& slot, const mt::mat4& mat)
{
RAS_DisplayArrayBucket *origArrayBucket = slot.m_displayArrayBucket;
Expand Down
6 changes: 6 additions & 0 deletions source/gameengine/Rasterizer/RAS_BatchGroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class RAS_BatchGroup
private:
/// The reference counter.
short m_users;
/// Reference object used to retrieve layer and color.
RAS_MeshUser *m_referenceMeshUser;

/// A batch contained the merged display array for all the display array used for a given material.
class Batch
Expand Down Expand Up @@ -80,6 +82,10 @@ class RAS_BatchGroup
/// Notice the batch group that it is unused by one less mesh user.
RAS_BatchGroup *RemoveMeshUser();

RAS_MeshUser *GetReferenceMeshUser() const;
/// Change reference mesh user without error check.
void SetReferenceMeshUser(RAS_MeshUser *meshUser);

/** Merge the display array of the mesh slots contained in the mesh user.
* \param meshUser The mesh user to merge mesh slots from.
* \param mat The object matrix to use in display array merging. It's not the matrix from
Expand Down
8 changes: 8 additions & 0 deletions source/gameengine/Rasterizer/RAS_DisplayArrayBucket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
#include "RAS_MaterialBucket.h"
#include "RAS_IMaterial.h"
#include "RAS_Mesh.h"
#include "RAS_MeshUser.h"
#include "RAS_Deformer.h"
#include "RAS_BatchGroup.h"
#include "RAS_Rasterizer.h"
#include "RAS_InstancingBuffer.h"
#include "RAS_BucketManager.h"
Expand Down Expand Up @@ -321,6 +323,7 @@ void RAS_DisplayArrayBucket::RunBatchingNode(const RAS_DisplayArrayNodeTuple& tu
RAS_ManagerNodeData *managerData = tuple.m_managerData;
RAS_MaterialNodeData *materialData = tuple.m_materialData;

RAS_IMaterial *material = materialData->m_material;
const unsigned int nummeshslots = m_activeMeshSlots.size();

// We must use a int instead of unsigned size to match GLsizei type.
Expand Down Expand Up @@ -368,6 +371,11 @@ void RAS_DisplayArrayBucket::RunBatchingNode(const RAS_DisplayArrayNodeTuple& tu
* To be sure we don't use the old face wise we force it to true. */
rasty->SetFrontFace(true);

// Retrieve batch group from first active mesh slot.
RAS_BatchGroup *group = m_activeMeshSlots.front()->m_meshUser->GetBatchGroup();
// Use batch group reference mesh user for layer and object color.
material->ActivateMeshUser(group->GetReferenceMeshUser(), rasty, managerData->m_trans);

RAS_AttributeArrayStorage *attribStorage = m_nodeData.m_attribStorage;
attribStorage->BindPrimitives();

Expand Down
2 changes: 1 addition & 1 deletion source/gameengine/Rasterizer/RAS_IMaterial.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class RAS_IMaterial : public CM_UpdateServer<RAS_IMaterial>
virtual void Activate(RAS_Rasterizer *rasty) = 0;
virtual void Desactivate(RAS_Rasterizer *rasty) = 0;
virtual void ActivateInstancing(RAS_Rasterizer *rasty, RAS_InstancingBuffer *buffer) = 0;
virtual void ActivateMeshSlot(RAS_MeshSlot *ms, RAS_Rasterizer *rasty, const mt::mat3x4& camtrans) = 0;
virtual void ActivateMeshUser(RAS_MeshUser *meshUser, RAS_Rasterizer *rasty, const mt::mat3x4& camtrans) = 0;

bool IsAlpha() const;
bool IsAlphaDepth() const;
Expand Down
2 changes: 1 addition & 1 deletion source/gameengine/Rasterizer/RAS_MeshSlot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ void RAS_MeshSlot::RunNode(const RAS_MeshSlotNodeTuple& tuple)
RAS_DisplayArrayStorage *storage = displayArrayData->m_arrayStorage;

if (!managerData->m_shaderOverride) {
materialData->m_material->ActivateMeshSlot(this, rasty, managerData->m_trans);
materialData->m_material->ActivateMeshUser(m_meshUser, rasty, managerData->m_trans);

if (materialData->m_zsort && storage) {
displayArrayData->m_array->SortPolygons(
Expand Down

0 comments on commit 014bb4c

Please sign in to comment.