-
-
Notifications
You must be signed in to change notification settings - Fork 183
Implementing pass index
BFBGE and UPBGE were not supporting all the information from the Object Info
node :
This wiki is describing the modifications step by step of the sources to make this node works in game.
In this node Location
is the object position, this one is properly updated. Object Index
is the Pass Index
in the object Relations
panel. Material Index
is the Pass Index
property in material Render Pipeline Options
(for node materials) or Options
(for simple materials).
For this last panel the option isn't exposed but exists. To show it in the UI the python script located in release/scripts/startup/bl_ui/properties_material.py must be modified. The class MATERIAL_PT_game_options
is managing the properties in the Options
panel when the game engine is selected.
class MATERIAL_PT_game_options(MaterialButtonsPanel, Panel):
bl_label = "Options"
COMPAT_ENGINES = {'BLENDER_GAME'}
@classmethod
def poll(cls, context):
mat = context.material
engine = context.scene.render.engine
return check_material(mat) and (mat.type in {'SURFACE', 'WIRE'}) and (engine in cls.COMPAT_ENGINES)
def draw(self, context):
layout = self.layout
base_mat = context.material
mat = active_node_mat(base_mat)
split = layout.split()
col = split.column()
if simple_material(base_mat):
col.prop(mat, "invert_z")
sub = col.row()
sub.prop(mat, "offset_z")
sub.active = mat.use_transparency and mat.transparency_method == 'Z_TRANSPARENCY'
sub = col.column(align=True)
sub.label(text="Light Group:")
sub.prop(mat, "light_group", text="")
row = sub.row(align=True)
row.active = bool(mat.light_group)
row.prop(mat, "use_light_group_exclusive", text="Exclusive")
row.prop(mat, "use_light_group_local", text="Local")
col = split.column()
col.prop(mat, "use_mist")
col.prop(mat, "use_vertex_color_paint")
col.prop(mat, "use_vertex_color_light")
col.prop(mat, "use_object_color")
col.prop(mat, "use_instancing")
All the python script in bl_ui/ are using a common syntax to expose properties:
widget.prop(obj, "name")
The widget can be either a column, a row, a sub panel etc…, depending on the placement desired. obj
is the object used to take the property from and "name"
the property name, in consideration obj.name
must exist.
Our property is Material.pass_index
and we want it under Geometry Instancing
property aligned with the same column. Then the line col.prop(mat, "pass_index")
is added at the end of the draw
function and the panel looks:
Next we have to expose these variables to python game API, it's sometimes not needed or useful, but here the user can use the pass indices to dynamically change the property of a material. First of all we identify the classes concerned by these indices: KX_GameObject
and KX_BlenderMaterial
.
These both class have a .h and .cpp file of the same name into the directory source/gameengine/Ketsji. In the two classes the variable m_passIndex
is implemented:
class KX_BlenderMaterial/KX_GameObject : …
{
…
private:
…
short m_passIndex;
…
};
Next the python API declaration for KX_BlenderMaterial.passIndex
and KX_GameObject.passIndex
follows the guide https://github.com/UPBGE/blender/wiki/Python-Proxy. The line:
EXP_PYATTRIBUTE_SHORT_RW("passIndex", 0, SHRT_MAX, false, KX_GameObject/KX_BlenderMaterial, m_passIndex),
is added into PyAttributeDef KX_GameObject::Attributes[]
and PyAttributeDef KX_BlenderMaterial::Attributes[]
attributes arrays. This line register and attributes of type short
using the variable passIndex
in class KX_GameObject
or KX_BlenderMaterial
with a range of 0 to SHRT_MAX (maximum value for a short
) without clamping but error throw.
These value should be initialized at game start based on blender data, for the materials the conversion is proceeded inside KX_BlenderMaterial
constructor for object the function BL_GameObjectFromBlenderObject
in BL_BLenderDataConversion.cpp
is responsible.
As said the material constructor copy the value from blender data, this one is the struct Material
and the variable index
. The line
KX_BlenderMaterial::KX_BlenderMaterial(Material *mat, const std::string& name)
:…,
m_passIndex(mat->index)
{
…
}
is added into the men-initializer of KX_BlenderMaterial
.
Concerning KX_GameObject
, the class supports trivial getter and setter for pass index:
class KX_GameObject : …
{
…
public:
…
void SetPassIndex(short index);
short GetPassIndex() const;
…
};
This setter is used in BL_GameObjectFromBlenderObject
:
gameobj->SetPassIndex(ob->index);
At this modification stage we have complete user interface to initialize the values from blender and modify them from python. The next step is passing these values to the rendering process.
Before typing anything, we have to understand how the rendering is basically working. The render is proceeded per materials, then per meshes part using this material, then per objects using this meshes part.
So the material is bind at the first level, then all meshes part are bind and unbind and between, each object using this mesh part is updating material uniforms such as the object color and transform before a rendering of the mesh geometry.
What we want is to update similarly to object color the object index. One class is used to store all the data relative to the object and shared by the meshes parts: RAS_MeshUser
. This is holding the transformation, color, layer, front face setting etc… This is the correct place to add the object pass index.
The short
variable m_passIndex
is added same for the trivial getter and setter:
class RAS_MeshUser : …
{
private:
…
short m_passIndex;
…
public:
…
short GetPassIndex() const;
…
void SetPassIndex(short index);
…
};
After this we have to forward the object pass index from the mesh user to uniforms. The function GPU_material_bind_uniforms
is updating the uniforms depending on objects of a material and is called into BL_BlenderShader::Update
for every object mesh parts. In consideration this function has access to the mesh user and so object data.
Last argument of GPU_material_bind_uniforms
is an optional array of three floats: [objects pass index, material pass index, random].
The first float is the value returned by RAS_MeshUser::GetPassIndex()
. The second float is an argument passed to BL_BlenderShader::Update
, as the caller is KX_BlenderMaterial::ActivateMeshSlot
it's possible to pass m_passIndex
to new argument short matPassIndex
of BL_BlenderShader::Update
.
Finally the random value is handled in way that mimic blender, blender is using the hash of the object name hash. As names are unique per object (most of the time) we can assume that a random unique per object can be used (but not equivalent at each game run as the only way to achieve it is by using the object name and replicated object can keep the same name, breaking the per object random) base on the hash of the client object pointer using the mesh user. This client object is get from RAS_MeshUser::GetClientObject()
and warranted to be unique for each game object.
The function updating uniforms is:
void BL_BlenderShader::Update(RAS_MeshSlot *ms, short matPassIndex, RAS_Rasterizer *rasty)
{
…
RAS_MeshUser *meshUser = ms->m_meshUser;
const float (&obcol)[4] = meshUser->GetColor().Data();
float objectInfo[3];
if (GPU_get_material_builtins(m_gpuMat) & GPU_OBJECT_INFO) {
objectInfo[0] = meshUser->GetPassIndex();
objectInfo[1] = matPassIndex;
objectInfo[2] = BLI_hash_int_2d((uintptr_t)meshUser->GetClientObject(), 0) / ((float)0xFFFFFFFF);
}
GPU_material_bind_uniforms(m_gpuMat, meshUser->GetMatrix().Data(), rasty->GetViewMatrix().Data(),
obcol, meshUser->GetLayer(), 1.0f, nullptr, objectInfo);
…
}
To optimize the process, materials not using object info have bit GPU_OBJECT_INFO
of GPU_get_material_builtins(m_gpuMat)
to zero and they skip the object info update.
After doing all these modifications, we must check them by the accessing ways : UI and python API, leave a good commit message, open a pull request and have a nice day !