Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Utility for lightmap preparation #48

Open
wants to merge 23 commits into
base: master
Choose a base branch
from

Conversation

rawnsley
Copy link
Contributor

Lightmaps are a pain to bake because you have to remember to always do the following:

  1. Select all the objects that should be light mapped
  2. Make sure the selected UV layer is correct for every mesh
  3. Make sure the lightmap image texture is active in the material graph

Miss any one of these steps and your bake will fail, probably silently and after a long time.

This PR adds a new Hubs 'Panel' to the Render Properties that has a button that finds all the MOZ_lightmap components and traverses the scene graph making sure everything is selected and active. It also does some strict error checking, so a positive result means you are ready to bake, but anything inconsistent (such as a missing image or missing UV layer) will throw an error.

I've tried to match the same style as the other Hubs panels, but the actual scene logic was a bit verbose so I put it into it's own file.

Blender doesn't seem to have a concept of "parents" for things like meshes, which doesn't make much sense to me, but we just use full search in those instances. Performance isn't an issue, but it does make the code a bit ugly.

@netpro2k
Copy link
Contributor

netpro2k commented Oct 20, 2021

Blender doesn't seem to have a concept of "parents" for things like meshes, which doesn't make much sense to me

This relationship is expressed in Objects. Each Object can have a "Data", things like Mesh, Light, and Camera are all "Data" and can be referenced by multiple Objects.

@rawnsley
Copy link
Contributor Author

Blender doesn't seem to have a concept of "parents" for things like meshes, which doesn't make much sense to me

This relationship is expressed in Objects. Each Object can have a "Data", things like Mesh, Light, and Camera are all "Data" and can be referenced by multiple Objects.

I was expecting some sort of parent property that would let me go up the hierarchy easily like we can with the HTML DOM. It might need to be a list of parents I guess, but I can't think of a good reason they didn't include it.

@rawnsley rawnsley marked this pull request as draft October 21, 2021 13:58
@rawnsley
Copy link
Contributor Author

I forgot to make this PR draft originally.

@rawnsley
Copy link
Contributor Author

In developing this script I discovered a complication in the Blender baking process. The behaviour appears to be that Blender starts with the selected objects and performs a bake on every materials that object uses, targeting the active image texture or just the first one if there isn't anything selected. This is why it's important to activate the right one before baking.

The consequence of this is that you can't have mixed-material objects where one is light mapped and another is not. This sounds like an obscure use case, but it happened in the first real Blender file I ran the script on. In this scene there was a spear with a wood shaft and a stone head. The stone material was also used by some large rocks in the scene that needed a lightmap. Usually this is best practice material sharing and lightmaping the tip of the spear isn't going to cause any harm (a few lost pixels in the lightmap), but because that same object uses the wood material the bake will end up corrupting one of the input textures.

I've added a sanity check when the script runs to warn the user in this mixed-material scenario.

This Blender project demonstrates the problems of mixed materials and baking irrespective of this add-on.

@rawnsley
Copy link
Contributor Author

rawnsley commented Nov 5, 2021

I've added some functionality to cope with the mixed material situation that allows you to add dummy image textures (based on a suggestion by Jim). This works well, but obviously complicates the plug-in a bit, but to some extent it's just reflecting the complexities of Blender baking. By adding things things to the scene file it does more than just select things, which is a shame because I prefer add-ons that "first do no harm". To try and counter this I included a Remove all button that tidies up the mess it makes.

Instructions

Basic Use

This Blender file contains a scene with a single lightmapped material that is shared by two meshes and two more materials that are applied to two different meshes:

Screenshot 2021-11-05 at 10 47 41

Pressing the Select all lightmap elements for baking button selects:

  • the objects with lightmapped materials (this tells Bake what to process)
  • the lightmap UV set (read from the material graph)
  • the faces within the mesh of those objects that are assigned the lightmapped material (ready for UV packing)
  • the lightmap image texture from the material itself (this tells Bake what image to write to)

Technically things are made "active" rather than "selected", but this is a Blender API distinction that doesn't need concern the user.

In this state the UV's are in ready to be unwrapped for lightmapping either though the built in "Lightmap pack" (which isn't great) or whatever method the user sees fit.

Screenshot 2021-11-05 at 10 48 01

Once the UVs are set they won't need to be changed unless the models change. Then the user only needs to select the proper baking method (typically Diffuse with Direct and Indirect contributions) and press the Bake button.

Multiple Lightmaps

It's also possible to have multiple lightmaps defined across multiple objects. We have found this setup useful where either a single texture doesn't provide enough pixels or you want to split indoor and outdoor lightmaps to allow for finer grained scaling.

This Blender file contains a scene where one material uses Lightmap1 and two materials share Lightmap2.

Screenshot 2021-11-05 at 11 06 39

When multiple lightmaps are found, the panel lists them independently for selection to allow for UV packing. The main button selects all the lightmapped materials as before so all the lightmap images can be baked at once.

Cross-Material Lightmaps

Object meshes can have different materials assigned to different faces as can be seen in this Blender file, where the top faces of two of the cubes share the lightmapped material:

Screenshot 2021-11-05 at 11 09 19

Pressing the Select all lightmap elements for baking button will select all the objects in the scene as before, but it will also throw an error message because if you baked at this point the ColorTexture material would be corrupted as Blender looks for the first image texture it can find if none is active. Note that the Red material is safe because it has no image textures; Blender will inform the user about this during baking, but it won't cause any problems.

The new panel includes a Decoy Textures section with an Add to Selected button. When you press this button it will add an image texture with the label HUBS_DECOY_IMAGE_TEXTURE to any material that is deemed "at risk" - i.e. is selected, has an image texture, but has no lightmap target. Pressing Remove All will do just that to all materials whether the objects are selected or not.

This situation is actually quite common in production scenes. One real-world example is where we had a spear mesh with two materials: wood and stone. The stone material was already being used for the interior of a cave and had a lightmap, but the wood material did not. Baking without dummy textures corrupts the wood image textures.

@rawnsley rawnsley marked this pull request as ready for review November 5, 2021 11:34
@keianhzo
Copy link
Contributor

keianhzo commented Jul 25, 2022

@rawnsley Thanks for this. I think this would be a great first Hubs add-on tool to have. I feel the baking pain and having something that saves you a few clicks and headaches is always welcome. My initial obvious comment is that we would need to update this to the latest add architecture. It shouldn't be much effort as this is quite independent element of the plugin so it should just be a matter of moving things around.

We currently have a components module for all hubs component related. We can probably create a tools module in the add-on root to host this and future tools and bundle all the PR files inside its own render module under tools.

Other than that and pending a deeper code review I think we can start with the current UI proposal. Maybe we could put the lightmapping UI inside a Ligthmap box (similarly to what we do in the components list) to separate it from future tools UI but we can do that later when we have more tools.

Regarding the decoy textures. I wasn't able to reproduce the texture corruption using your multi-material example file above and Blender 3.1.2. Maybe this is something that has been fixed in the latest Blender versions?

Let us know if you are willing to update this branch yourself or you prefer us fork and do it ourselves.

@rawnsley
Copy link
Contributor Author

@keianhzo Thanks for the feedback. Agreed on all of that - I'll take a look at refactoring my branch to eliminate the conflicts and confirm if the decoy textures are still required. It will be a good opportunity to familiarise myself with all of the good work you've been doing recently.

Just to be clear: are you thinking of keeping non-core tools like this in a new GitHub repository or just keeping them in a tools folder at the root of this repository?

@keianhzo
Copy link
Contributor

I mean in addons\io_hubs_addon\tools\render. I think they will be useful as part of the add-on itself. The render panel where you have placed it seems like a good initial location to me.

@rawnsley
Copy link
Contributor Author

@keianhzo I've (finally) got around to pulling in the current master branch. I've retested on Blender 3.6 and this tool is working as before.

I can confirm that texture corruption is still a problem in the "Cross-Material Lightmaps" scenario outlined above. Without using this tool: if you select all the objects in the scene and start a bake, the image for the ColorTexture material is overwritten erroneously.

@keianhzo keianhzo added this to the 1.3.0 milestone Aug 7, 2023
@keianhzo keianhzo modified the milestones: 1.3.0, 1.4.0 Dec 18, 2023
Copy link
Contributor

@Exairnous Exairnous left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rawnsley Sorry it took me so long to review, thank you very much for this! Overall this looks really good and I think it'll be of great use to people. The error checking is great! There are a couple things that I would like to discuss and/or I think could use a few tweaks, though.

  • I noticed that there isn't any support for allowing a mapping node between the UV map and the image texture. I don't think support for this is needed, but thought I should mention it just to double check with you.
  • Why are the light map faces being selected? It seems like this tool is mainly for baking once everything has been set up?
  • Rather than trying to force everything into object mode, I think it would be better to use the poll on the operator to limit it to only run when in object mode.
  • It looks like the selection and hidden status for all faces of all objects (regardless of whether they are light mapped or not) is being modified with little to no warning to the user. This should be limited to only objects with light maps on them.
  • I think materials with 0 users should be skipped.
  • object is a reserved keyword in Python (as much as anything is reserved in Python), so we've been trying to stay away from it, ob or obj has predominantly been used instead.
  • Blender supports materials being linked to Objects as well as to Meshes and this appears to export to glTF (although it looks like they store them on meshes in the glTF file). If you do something like the following for selectUVMaps instead of looping through the meshes and calling selectObjectFromMesh, it should account for objects and be a bit faster (I think):
for ob in bpy.context.scene.objects:
    if ob.type == "MESH":
        for materialSlot in ob.material_slots:
            if materialSlot.material:
                if materialSlot.material.name == material.name:
                    ob.hide_set(False)
                    ob.select_set(True)
                    mesh = ob.data
                    etc.

Comment on lines +154 to +155
for materialSlot in object.material_slots:
material = materialSlot.material
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A material slot isn't guaranteed to have a material in it, so an if material check is needed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants