Hi all, I’m working in the Atomic Game Engine project, implementing in-editor lightmapping. I’ve checked and the core code I’m hitting is unchanged from Urho3D’s, so I thought it would make more sense to ask about it here
Context
To give some context to my question, I’m currently at the point of applying a generated lightmap to objects by cloning their material and setting UV scale and offset accordingly.
I’ve added a second set of UV transform uniforms in Uniform.hlsl:
uniform float4 cUOffset;
uniform float4 cVOffset;
uniform float4 cUOffsetLM;
uniform float4 cVOffsetLM;
uniform float4x3 cZone;
And added a transform function to be used in lightmap shaders (currently testing with LitSolid) in Transforms.hlsl:
float2 GetTexCoord2(float2 iTexCoord)
{
return float2(dot(iTexCoord, cUOffsetLM.xy) + cUOffsetLM.w, dot(iTexCoord, cVOffsetLM.xy) + cVOffsetLM.w);
};
In Material.h/cpp, I added methods similar to SetUVTransform but corresponding to the second set of UVs, moving the transform calculation into a separate method and calling it from both:
void Material::SetUVTransform2(const Vector2& offset, float rotation, const Vector2& repeat)
{
Matrix3x4 transform = CalculateUVTransform(offset, rotation, repeat);
SetShaderParameter("UOffsetLM", Vector4(transform.m00_, transform.m01_, transform.m02_, transform.m03_));
SetShaderParameter("VOffsetLM", Vector4(transform.m10_, transform.m11_, transform.m12_, transform.m13_));
}
Matrix3x4 Material::CalculateUVTransform(const Vector2& offset, float rotation, const Vector2& repeat)
{
Matrix3x4 transform(Matrix3x4::IDENTITY);
transform.m00_ = repeat.x_;
transform.m11_ = repeat.y_;
transform.m03_ = -0.5f * transform.m00_ + 0.5f;
transform.m13_ = -0.5f * transform.m11_ + 0.5f;
Matrix3x4 rotationMatrix(Matrix3x4::IDENTITY);
rotationMatrix.m00_ = Cos(rotation);
rotationMatrix.m01_ = Sin(rotation);
rotationMatrix.m10_ = -rotationMatrix.m01_;
rotationMatrix.m11_ = rotationMatrix.m00_;
rotationMatrix.m03_ = 0.5f - 0.5f * (rotationMatrix.m00_ + rotationMatrix.m01_);
rotationMatrix.m13_ = 0.5f - 0.5f * (rotationMatrix.m10_ + rotationMatrix.m11_);
transform = rotationMatrix * transform;
Matrix3x4 offsetMatrix = Matrix3x4::IDENTITY;
offsetMatrix.m03_ = offset.x_;
offsetMatrix.m13_ = offset.y_;
return offsetMatrix * transform;
}
The scale and offset are calculated in the lightmapper and applied to each model (where rect corresponds to where the model’s LM is in the full LM atlas):
IntRect& rect = rects[i];
Vector2 scale((float)rect.Width() / atlasWidth, (float)rect.Height() / atlasHeight);
Vector2 offset((float)rect.left_ / atlasWidth, (float)rect.top_ / atlasHeight);
LMStaticModel* model = generators[i]->GetModel();
model->SetLightmapTexure(atlasTexture);
model->SetLightmapUVTransform(offset, 0, scale);
LMStaticModel::SetLightmapTexure and LMStaticModel::SetLightmapUVTransform just set these values on the cloned material.
The actual question, at last
This is my current lightmap atlas test case (automatically generated, but with numbers added for my sanity afterwards):
As an example mapping the second, smaller cube in the atlas I would expect an offset of approx (0.6, 0.0) and a scale/repeat of approx (0.3, 0.3). Setting these values manually on the material’s U/VOffsetLM I get the expected result:
However setting them through SetUVTransform2 (which uses the same transform calculation as SetUVTransform), the calculated values are not what I would expect, and the visual result is way off:
So what is going on here? Is my understanding of UVs completely wrong (not impossible at all) or is that CalculateUVTransform wrong. It’s been in Material.cpp as-is since before the Atomic fork, but I see Material::SetUVTransform isn’t actually used anywhere so it’s possible the calculation is wrong and it’s never been noticed.