feat(SOLSYS-36): shared Blender body textures, clouds + atmosphere (Earth first) - #40
Conversation
Introduce data/textures/bodies packs and body_appearance registry for planets/moons/asteroids, wire NASA Blue Marble into close-up renders, and refresh Earth light/dark GIFs.
📝 WalkthroughWalkthroughThe Blender pipeline now supports shared body appearance packs. Earth close-ups use surface and cloud textures, configurable materials, and an optional Fresnel atmosphere shell. Job generation, rendering, package exports, documentation, checked-in jobs, and tests were updated. ChangesBlender body appearance pipeline
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant buildFlybyJob
participant appearanceForCatalogName
participant render_flyby
participant BlenderPlanet
participant AtmosphereShell
CLI->>buildFlybyJob: request Earth close-up
buildFlybyJob->>appearanceForCatalogName: resolve Earth appearance
appearanceForCatalogName-->>buildFlybyJob: return texture and atmosphere data
buildFlybyJob->>render_flyby: provide serialized job
render_flyby->>BlenderPlanet: apply UVs, textures, clouds, and materials
render_flyby->>AtmosphereShell: add configured Fresnel shell
BlenderPlanet-->>CLI: render close-up frames
Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…lose-ups Fresnel shell is opt-in per body so airless packs stay clean; GIFs re-rendered with texture + atmosphere.
Ship a real cloud-only mask, fresnel atmosphere, and light/dark EEVEE exposure so textured Earth GIFs stay readable without chalk/glare.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
tests/test_blender_pipeline.py (1)
91-116: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd a regression test for an airless body.
The new assertions cover only Earth, where clouds and atmosphere are enabled.
test_unknown_catalog_name_has_no_packdoes not exercise a registered airless body or job serialization. Add an airlessBodyAppearancefixture, or a registered Moon/asteroid pack, and assert that the serialized appearance omits clouds and keepsatmosphere.enabledfalse.Also applies to: 150-154
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_blender_pipeline.py` around lines 91 - 116, Extend BodyAppearanceTests with a registered airless body fixture or catalog pack, then add a test covering appearanceForCatalogName and toJobDict. Assert the serialized appearance omits the clouds texture and has atmosphere.enabled set to false, while preserving the existing Earth and unknown-catalog tests.animate/scenes/blender/render_flyby.py (1)
206-215: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueThe specular map overrides both the scalar roughness and the light-theme clamp.
Lines 214-215 link the inverted specular map directly to
Roughness. That link replaces the value set at line 179 and discards themax(roughness, 0.60)clamp applied for the light theme at line 175. The ocean glare that the clamp prevents returns for any pack that ships a specular map.No pack ships a specular map today, so the path is currently inactive. Consider mixing the map with the scalar roughness instead of replacing it, for example through a
ShaderNodeMapRangelimited to the theme range.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@animate/scenes/blender/render_flyby.py` around lines 206 - 215, Update the specular-map handling in the material setup around _loadImageTexture so it no longer links the inverted map directly to principled.inputs['Roughness']. Combine the map with the already computed scalar roughness while preserving the light-theme max(roughness, 0.60) clamp, using a node setup constrained to the existing theme range.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@animate/scenes/blender/body_appearance.py`:
- Around line 70-81: The root cause is absolute-path serialization in
BodyAppearance.toJobDict, which makes committed jobs machine-specific. In
animate/scenes/blender/body_appearance.py lines 70-81, serialize texture map
paths relative to REPO_ROOT; in animate/scenes/blender/render_flyby.py lines
373-401, resolve each texture value against the renderer repository root before
_applyBodyMaterial while preserving already-absolute paths; regenerate
output/animate/blender/earth_flyby_dark_job.json lines 668-677 and
output/animate/blender/earth_flyby_light_job.json lines 668-677 with
repository-relative texture paths and outputDirectory values.
In `@animate/scenes/blender/render_flyby.py`:
- Around line 118-130: Update the factor selection around cloudsNode and rawFac
to inspect the loaded image’s actual channels rather than checking whether the
ShaderNodeTexImage outputs contain an Alpha socket. Use the Alpha output only
when the image has an alpha channel; otherwise route the Color output for
luminance conversion, preserving the existing attenuation through scale.
- Around line 136-153: Update the mix-node creation flow so Blender support is
determined before calling nodeTree.nodes.new: select ShaderNodeMix only when
that node type is available, otherwise select ShaderNodeMixRGB, then create and
configure the selected node using the appropriate socket mappings. Remove the
current post-creation hasattr(mix, 'data_type') guard while preserving the
existing ShaderNodeMix and MixRGB output behavior.
In `@data/textures/README.md`:
- Around line 36-39: Update the Earth cloud-map documentation around the Clouds,
Source, Credit, and License entries to identify EUMETSAT-derived imagery and
include the exact attribution “Contains modified EUMETSAT data.” Keep any
applicable NASA or base-imagery credit separate, and remove the blanket NASA
image-use-policy licensing unless it specifically applies to this cloud asset.
---
Nitpick comments:
In `@animate/scenes/blender/render_flyby.py`:
- Around line 206-215: Update the specular-map handling in the material setup
around _loadImageTexture so it no longer links the inverted map directly to
principled.inputs['Roughness']. Combine the map with the already computed scalar
roughness while preserving the light-theme max(roughness, 0.60) clamp, using a
node setup constrained to the existing theme range.
In `@tests/test_blender_pipeline.py`:
- Around line 91-116: Extend BodyAppearanceTests with a registered airless body
fixture or catalog pack, then add a test covering appearanceForCatalogName and
toJobDict. Assert the serialized appearance omits the clouds texture and has
atmosphere.enabled set to false, while preserving the existing Earth and
unknown-catalog tests.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ca173216-7d2a-48e3-b2b5-9ae02fe8312d
⛔ Files ignored due to path filters (4)
data/textures/bodies/earth/clouds.pngis excluded by!**/*.pngdata/textures/bodies/earth/color.pngis excluded by!**/*.pngoutput/animate/blender/earth_flyby_dark.gifis excluded by!**/*.gifoutput/animate/blender/earth_flyby_light.gifis excluded by!**/*.gif
📒 Files selected for processing (10)
README.mdanimate/scenes/blender/README.mdanimate/scenes/blender/__init__.pyanimate/scenes/blender/body_appearance.pyanimate/scenes/blender/flyby_scene.pyanimate/scenes/blender/render_flyby.pydata/textures/README.mdoutput/animate/blender/earth_flyby_dark_job.jsonoutput/animate/blender/earth_flyby_light_job.jsontests/test_blender_pipeline.py
| def toJobDict(self) -> dict: | ||
| maps = {key: str(path) for key, path in self.textures.existingMaps().items()} | ||
| payload = { | ||
| 'bodyId': self.bodyId, | ||
| 'kind': self.kind, | ||
| 'textures': maps, | ||
| 'roughness': self.roughness, | ||
| 'specular': self.specular, | ||
| } | ||
| if self.atmosphere.enabled: | ||
| payload['atmosphere'] = self.atmosphere.toJobDict() | ||
| return payload |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Texture paths are serialized as machine-absolute paths and committed. BodyAppearance.toJobDict writes fully resolved absolute paths into the job payload. The two checked-in Earth jobs therefore record the author's home directory and a stale temporary output directory, and the renderer loads those strings verbatim. Any other contributor who renders from the committed jobs gets an untextured body and an invalid output path. The single root cause is absolute-path serialization; every site below follows from it.
animate/scenes/blender/body_appearance.py#L70-L81: emit each map path relative toREPO_ROOTinstead ofstr(path).animate/scenes/blender/render_flyby.py#L373-L401: join each texture value with the renderer's repository root before passing the appearance dict to_applyBodyMaterial, and keep absolute values working for backward compatibility.output/animate/blender/earth_flyby_dark_job.json#L668-L677: regenerate this job after the fix, and setoutputDirectoryto a repository-relative path.output/animate/blender/earth_flyby_light_job.json#L668-L677: regenerate this job after the fix, and setoutputDirectoryto a repository-relative path.
📍 Affects 4 files
animate/scenes/blender/body_appearance.py#L70-L81(this comment)animate/scenes/blender/render_flyby.py#L373-L401output/animate/blender/earth_flyby_dark_job.json#L668-L677output/animate/blender/earth_flyby_light_job.json#L668-L677
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@animate/scenes/blender/body_appearance.py` around lines 70 - 81, The root
cause is absolute-path serialization in BodyAppearance.toJobDict, which makes
committed jobs machine-specific. In animate/scenes/blender/body_appearance.py
lines 70-81, serialize texture map paths relative to REPO_ROOT; in
animate/scenes/blender/render_flyby.py lines 373-401, resolve each texture value
against the renderer repository root before _applyBodyMaterial while preserving
already-absolute paths; regenerate
output/animate/blender/earth_flyby_dark_job.json lines 668-677 and
output/animate/blender/earth_flyby_light_job.json lines 668-677 with
repository-relative texture paths and outputDirectory values.
| # Prefer alpha when present; otherwise use luminance of the color map. | ||
| rawFac = ( | ||
| cloudsNode.outputs['Alpha'] | ||
| if 'Alpha' in cloudsNode.outputs | ||
| else cloudsNode.outputs['Color'] | ||
| ) | ||
| # Extra global attenuation in-shader (texture alpha already thinned). | ||
| scale = nodeTree.nodes.new('ShaderNodeMath') | ||
| scale.operation = 'MULTIPLY' | ||
| scale.location = (-460, 40) | ||
| scale.inputs[1].default_value = 0.75 | ||
| nodeTree.links.new(rawFac, scale.inputs[0]) | ||
| facSocket = scale.outputs['Value'] |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
The alpha fallback never runs, because ShaderNodeTexImage always exposes an Alpha output.
The comment states the intent: use alpha when present, otherwise use the color luminance. The check 'Alpha' in cloudsNode.outputs tests the socket collection, not the image channels. ShaderNodeTexImage declares both Color and Alpha sockets for every image, so the condition is always true.
If a cloud map has no alpha channel, the Alpha output returns 1.0 for every pixel. The mix factor then becomes 0.75 everywhere, and the body renders as a uniform tinted sphere with no surface detail. The current Earth asset carries a thinned alpha channel, so the defect is latent. Any future pack that ships an opaque cloud map hits it.
Test the loaded image instead.
🐛 Proposed fix to select the factor from the image channels
- rawFac = (
- cloudsNode.outputs['Alpha']
- if 'Alpha' in cloudsNode.outputs
- else cloudsNode.outputs['Color']
- )
+ hasAlpha = bool(getattr(cloudsNode.image, 'alpha_mode', 'NONE') != 'NONE') and (
+ getattr(cloudsNode.image, 'depth', 24) in (32, 64, 128)
+ )
+ rawFac = cloudsNode.outputs['Alpha'] if hasAlpha else cloudsNode.outputs['Color']🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@animate/scenes/blender/render_flyby.py` around lines 118 - 130, Update the
factor selection around cloudsNode and rawFac to inspect the loaded image’s
actual channels rather than checking whether the ShaderNodeTexImage outputs
contain an Alpha socket. Use the Alpha output only when the image has an alpha
channel; otherwise route the Color output for luminance conversion, preserving
the existing attenuation through scale.
| # Blender 4+/5: ShaderNodeMix (RGBA). Older: MixRGB. | ||
| mix = nodeTree.nodes.new('ShaderNodeMix') | ||
| if hasattr(mix, 'data_type'): | ||
| mix.data_type = 'RGBA' | ||
| mix.blend_type = 'MIX' | ||
| mix.location = (-300, 160) | ||
| nodeTree.links.new(facSocket, mix.inputs['Factor']) | ||
| nodeTree.links.new(surfaceColorSocket, mix.inputs['A']) | ||
| nodeTree.links.new(cloudTint.outputs[0], mix.inputs['B']) | ||
| return mix.outputs['Result'] | ||
| nodeTree.nodes.remove(mix) | ||
| mixRgb = nodeTree.nodes.new('ShaderNodeMixRGB') | ||
| mixRgb.blend_type = 'MIX' | ||
| mixRgb.location = (-300, 160) | ||
| nodeTree.links.new(facSocket, mixRgb.inputs['Fac']) | ||
| nodeTree.links.new(surfaceColorSocket, mixRgb.inputs['Color1']) | ||
| nodeTree.links.new(cloudTint.outputs[0], mixRgb.inputs['Color2']) | ||
| return mixRgb.outputs['Color'] |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== candidate file =="
git ls-files | rg '(^|/)render_flyby\.py$' || true
echo "== file section =="
sed -n '120,160p' animate/scenes/blender/render_flyby.py
echo "== surrounding context =="
sed -n '80,170p' animate/scenes/blender/render_flyby.py
echo "== bpy imports/usages and constraints =="
sed -n '1,80p' animate/scenes/blender/render_flyby.py
rg -n "blender|BLENDER|MIN_VERSION|requires|dependencies|requirements|ShaderNodeMix|ShaderNodeMixRGB|nodes\.new" animate/scenes/blender/render_flyby.py animate -g '*.py' -g '*.md' -g '*.txt' -g '*.toml' -g '*.yaml' -g '*.yml' | head -200Repository: ThomasAFink/SOLSYS
Length of output: 17032
🌐 Web query:
Blender Python API nodes TreeNodes.new unknown node type RuntimeError
💡 Result:
A RuntimeError when using nodes.new in the Blender Python API is commonly caused by providing an incorrect node type string or attempting to add a node in an invalid context [1][2][3]. When using nodes.new(type=...), you must provide the bl_idname of the node, not its display name or general type [3][4]. For example, use 'ShaderNodeMath' instead of 'Math' [5]. You can find the correct bl_idname for any node by hovering over it in the Blender UI while "Python Tooltips" is enabled in your Preferences (Interface > Developer Extras). Other common causes include: 1. Invalid Node Context: Some nodes can only exist in specific node trees (e.g., trying to add a Compositor node to a Shader node tree will fail) [2]. 2. Initialization Failures: Custom nodes may raise a RuntimeError if their internal init method fails [6]. Ensure that your custom node registration is complete and that all required properties or sockets are handled correctly [7]. 3. Operation State: Attempting to modify node data (like adding nodes) during a draw call (e.g., inside a Panel's draw method) will trigger a RuntimeError because the data is protected while drawing [1]. Node modifications must be performed through operators or in an appropriate execution context, not directly within UI drawing functions [1]. If you are working with Node Groups, remember that a Node Group is a data block (a NodeTree), not a node itself [8]. To add a Node Group, you must instantiate a group node (e.g., 'ShaderNodeGroup') and assign your specific node tree to its node_tree property [5][8]. For further debugging, verify the bl_idname of the node you are trying to create and ensure your script is executing in an allowed context [2][3].
Citations:
- 1: https://blenderartists.org/t/error-when-trying-to-add-custom-node/1413528
- 2: https://projects.staging.blender.org/blender/blender/commit/4aac251b421b4576b06635d969f802db1d9a3d84
- 3: https://docs.blender.org/api/current/bpy.types.Nodes.html
- 4: https://docs.blender.org/api/5.0/bpy.types.Nodes.html
- 5: https://blender.stackexchange.com/questions/5413/how-to-connect-nodes-to-node-group-inputs-and-outputs-in-python
- 6: https://blender.stackexchange.com/questions/331324/unable-to-create-custom-nodes-in-blender-4-4-beta
- 7: https://github.com/blender/blender/blob/main/scripts/templates_py/custom_nodes.py
- 8: https://blenderartists.org/t/python-add-custom-nodegroup/1205661
🌐 Web query:
Blender Python API Nodes.new unknown node bl_idname RuntimeError exact
💡 Result:
A RuntimeError: Error: Node type... undefined when using nodes.new(type=...) in the Blender Python API typically occurs because the provided string is not the correct bl_idname for the desired node [1][2]. To resolve this issue, ensure you are using the precise bl_idname string [3]. Node names (what you see in the UI) are often different from their internal bl_idname identifiers [3]. Common causes and solutions: 1. Use the correct bl_idname: Many nodes have specific internal names (e.g., Geometry Nodes often start with GeometryNode... or CompositorNode...) [2][4]. You can find the correct bl_idname by searching the Blender source code or by inspecting the bl_idname property of existing nodes in the Python console [2][5]. For example, the point node is GeometryNodePoints, not Points [2]. 2. Context and Compatibility: A node may not be available in the current node tree type (e.g., trying to add a Compositor node into a Shader tree) [6]. Blender nodes are restricted to specific tree types, and adding an incompatible node will result in an error [6]. 3. Node Groups: If you are trying to add a custom node group, you cannot use nodes.new(type="YourGroupName"). Instead, you must first create a standard NodeGroup node and then assign its node_tree property to your custom group [1][7]: group_node = nodes.new(type='ShaderNodeGroup') # Or GeometryNodeGroup, etc. group_node.node_tree = bpy.data.node_groups['YourGroupName'] 4. Verification: If you are unsure of the correct string, you can iterate through existing nodes in your scene to see their bl_idname: for node in node_tree.nodes: print(f"Name: {node.name}, bl_idname: {node.bl_idname}") Always verify that your node tree is properly initialized and active before adding nodes [8][7].
Citations:
- 1: https://blender.stackexchange.com/questions/308479/python-script-for-geometry-node
- 2: https://blender.stackexchange.com/questions/280199/how-can-i-add-a-point-or-instance-to-points-node-with-python
- 3: https://docs.blender.org/api/current/bpy.types.Nodes.html
- 4: https://gist.github.com/derek-rein/ef1282f394a65182fbbe630590bbe518
- 5: https://docs.blender.org/api/current/bpy.types.Node.html?highlight=node
- 6: https://blender.stackexchange.com/questions/114407/import-node-into-custom-nodetree-python
- 7: https://devtalk.blender.org/t/context-is-incorrect-after-new-scene-created/11898
- 8: https://blender.stackexchange.com/questions/33189/python-script-attribute-error-while-using-node-editor
🌐 Web query:
BlenderShaderNodeMix introduced Blender 4.0 4.1 4.2 ShaderNodeMix
💡 Result:
The ShaderNodeMix node was not introduced in Blender 4.0, 4.1, or 4.2; it was introduced in Blender 3.4 [1][2][3]. In Blender 3.4, the Mix node underwent a significant update in the Shader and Geometry node editors to become a more versatile, unified node that replaced the older Mix RGB node [1][2][4]. Files saved using this updated node structure are not forward compatible with versions of Blender prior to 3.4 [1][2]. Since its introduction in 3.4, ShaderNodeMix has continued to be the standard node for mixing values, colors, and vectors, with its functionality and API parameters (such as data_type, blend_type, and factor_mode) remaining consistent through the 4.0, 4.1, and 4.2 releases [5][6][3]. While Blender 4.x releases have included various general updates to the Node Editor interface, they did not introduce the ShaderNodeMix node itself [7].
Citations:
- 1: https://docs.blender.org/manual/en/3.5/render/shader_nodes/converter/mix.html
- 2: https://docs.blender.org/manual/en/3.4/render/shader_nodes/color/mix.html
- 3: https://docs.blender.org/manual/en/4.0/render/shader_nodes/converter/mix.html
- 4: https://docs.blender.org/manual/en/4.1/modeling/geometry_nodes/utilities/color/mix_rgb.html
- 5: https://docs.blender.org/manual/en/4.3/render/shader_nodes/converter/mix.html
- 6: https://docs.blender.org/api/4.1/bpy.types.ShaderNodeMix.html
- 7: https://developer.blender.org/docs/release_notes/4.0/node_editor/
🌐 Web query:
"Error: Node type '...' undefined" "RuntimeError" Blender Python nodes.new
💡 Result:
The RuntimeError: Error: Node type '...' undefined when using nodes.new in Blender Python typically occurs because you are providing the wrong identifier for the node type [1][2]. To resolve this error, follow these guidelines: 1. Use the correct bl_idname: The string passed to nodes.new(type=...) must match the node's internal bl_idname, not its human-readable name or its internal type enumeration [1]. For example, use 'CompositorNodeIDMask' instead of 'ID_MASK' [1]. 2. Find the correct identifier: You can find the correct bl_idname by looking at the Blender Python API documentation or by inspecting an existing node in the Python console: - To check an existing node's type: If you have a node object (e.g., node = bpy.context.active_node), you can check its identifier by printing node.bl_idname [1]. 3. Common Patterns: - Shader Nodes: Typically prefixed with 'ShaderNode', e.g., 'ShaderNodeBsdfDiffuse' [1]. - Compositor Nodes: Typically prefixed with 'CompositorNode', e.g., 'CompositorNodeIDMask' [1]. - Geometry Nodes: Often prefixed with 'GeometryNode', e.g., 'GeometryNodePoints' [3]. 4. Handling Node Groups: You cannot simply pass the name of a node group to nodes.new to create an instance of it [4]. Instead, you must: - Create a 'GeometryNodeGroup' (or equivalent group node) first [4]. - Assign the node group data to the new node's node_tree property [4]. Example of instantiating a custom node group: instance = nodes.new(type='GeometryNodeGroup') instance.node_tree = bpy.data.node_groups['YourGroupName'] If you are migrating older scripts, note that Blender periodically updates its API, and node identifiers may change or be unified with other node types (such as the compositor migration where some nodes were replaced by their Shader node counterparts) [2]. Always ensure your script is using the identifiers compatible with your current version of Blender [5].
Citations:
- 1: https://blender.stackexchange.com/questions/34832/what-are-the-valid-types-for-the-type-parameter-in-bpy-context-scene-node-tree-n
- 2: https://developer.blender.org/docs/release_notes/5.0/migration/compositor_migration/
- 3: https://blender.stackexchange.com/questions/280199/how-can-i-add-a-point-or-instance-to-points-node-with-python
- 4: https://blender.stackexchange.com/questions/308479/python-script-for-geometry-node
- 5: https://blenderartists.org/t/new-add-on-easily-save-render-passes-and-in-each-render-layer-to-individual-images/572917
🌐 Web query:
Blender developer talks runtime error node type undefined nodes.new
💡 Result:
The Runtime Error: Node type [type] undefined that occurs when calling nodes.new(type) in Blender Python is almost always caused by passing the wrong string identifier to the method [1][2][3]. In the Blender Python API, the string passed to nodes.new(type) must match the node's bl_idname (e.g., 'ShaderNodeBsdfPrincipled' or 'GeometryNodeMeshToPoints'), not its display name or its internal node.type property [1][4][5]. Key troubleshooting points: 1. Use bl_idname: Do not use the human-readable label or the value found in node.type (which is often an enum like 'BSDF_PRINCIPLED' or 'MESH_TO_POINTS'). You must use the full, internal identifier [1][6][7]. 2. Identify the correct string: - You can find the bl_idname of an existing node by inspecting it in the Python console: print(node.bl_idname) [1]. - If you do not have the node yet, you can list all valid bl_idname strings for nodes in a specific tree by iterating over the registered subclasses, for example: [cls.bl_idname for cls in bpy.types.ShaderNode.subclasses] [7]. 3. Context matters: Ensure the node type you are trying to add is compatible with the node tree type you are currently editing. For example, trying to add a Shader node directly to a Geometry Node tree may cause errors or invalid behaviors [7]. 4. Node Groups: If you are trying to add a custom Node Group, you cannot simply add it by name string [3]. You must first add a 'ShaderNodeGroup' or 'GeometryNodeGroup' and then assign the node_tree property to your existing group data [3]. If you continue to see this error for standard nodes, it may indicate a corrupted installation or an issue with an addon that has failed to properly register its node types [8]. Verify that you are using a standard, up-to-date version of Blender [8].
Citations:
- 1: https://blender.stackexchange.com/questions/34832/what-are-the-valid-types-for-the-type-parameter-in-bpy-context-scene-node-tree-n
- 2: https://blender.stackexchange.com/questions/280199/how-can-i-add-a-point-or-instance-to-points-node-with-python
- 3: https://blender.stackexchange.com/questions/308479/python-script-for-geometry-node
- 4: https://docs.blender.org/api/blender_python_api_2_69_release/bpy.types.Nodes.html?highlight=nodes.new
- 5: https://docs.blender.org/api/current/bpy.types.Nodes.html
- 6: http://web.purplefrog.com/~thoth/blender/python-cookbook/all-nodes-cycles-materials.html
- 7: https://blender.stackexchange.com/questions/249911/how-to-discover-the-name-of-a-geometry-node-for-use-in-scripts
- 8: Error: Node type <node> undefined AIGODLIKE/ComfyUI-BlenderAI-node#129
Guard the MixRGB fallback before creating ShaderNodeMix.
nodeTree.nodes.new('ShaderNodeMix') raises before the hasattr(mix, 'data_type') check on Blender versions that do not register that node type, so the ShaderNodeMixRGB fallback never runs. Select ShaderNodeMix or ShaderNodeMixRGB before calling nodes.new.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@animate/scenes/blender/render_flyby.py` around lines 136 - 153, Update the
mix-node creation flow so Blender support is determined before calling
nodeTree.nodes.new: select ShaderNodeMix only when that node type is available,
otherwise select ShaderNodeMixRGB, then create and configure the selected node
using the appropriate socket mappings. Remove the current post-creation
hasattr(mix, 'data_type') guard while preserving the existing ShaderNodeMix and
MixRGB output behavior.
| - **Clouds:** `bodies/earth/clouds.png` (2048×1024 RGBA; thinned coverage mask) | ||
| - **Source:** [Live Cloud Maps](https://clouds.matteason.co.uk/) 2048×1024 `clouds-alpha.png` (NASA/NOAA satellite cloud data; static pack for reproducible renders) | ||
| - **Credit:** Matt Eason / Live Cloud Maps; underlying imagery from NASA & NOAA sources | ||
| - **License:** NASA media generally in the U.S. public domain ([NASA image use policy](https://www.nasa.gov/nasa-brand-center/images-and-media/)) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Repository files matching README.md under data/textures or similar:"
git ls-files | rg '(^|/)(README\.md|.*cloud.*|.*texture.*)$' | sed -n '1,120p'
echo
echo "Target README excerpt:"
if [ -f data/textures/README.md ]; then
cat -n data/textures/README.md | sed -n '1,80p'
else
echo "data/textures/README.md not found"
fiRepository: ThomasAFink/SOLSYS
Length of output: 2937
🌐 Web query:
Live Cloud Maps matteason clouds-alpha.png EUMETSAT NASA NOAA attribution Contains modified EUMETSAT data
💡 Result:
The file clouds-alpha.png is part of the Live Cloud Maps project maintained by Matt Eason, which provides high-resolution, near-real-time global cloud imagery [1][2]. This project derives its cloud texture data from EUMETSAT satellite imagery and combines it with base Earth imagery provided by NASA [1][3]. Because the imagery is a transformed product based on EUMETSAT data, the project requires that users include the following attribution [1][3]: Contains modified EUMETSAT data Additionally, since the base Earth imagery is provided by NASA and is considered a work of the US government, it is in the public domain [1][4]. It is standard practice to acknowledge both sources when using these maps, as seen in various implementations and project repositories [3][5]. The Live Cloud Maps themselves are often released under the CC0 1.0 Universal (Public Domain) license, though the requirement to attribute the underlying EUMETSAT data persists based on EUMETSAT's own licensing policies for modified or transformed data [1][3][6][7].
Citations:
- 1: https://github.com/matteason/live-cloud-maps/
- 2: https://clouds.matteason.co.uk/
- 3: https://github.com/Serial-Studio/Serial-Studio/blob/99919d07/app/rcc/messages/Acknowledgements.txt
- 4: https://github.com/matteason/daily-cloud-maps
- 5: https://github.com/Serial-Studio/Serial-Studio/blob/f7dfd94e/app/src/UI/Widgets/GPS.cpp
- 6: https://www-cdn.eumetsat.int/files/2025-02/45173%20-%20Data_Policy%281442019%20V1%29.pdf
- 7: https://user.eumetsat.int/s3/eup-strapi-media/LESS_THAN_1_H_Manufacturer_October_2023_version_website_4089ac0ab2.pdf
🌐 Web query:
site:github.com/matteason/live-cloud-maps clouds-alpha.png license attribution EUMETSAT NASA NOAA
💡 Result:
The clouds-alpha.png file is an image asset derived from data provided by EUMETSAT [1]. Under the licensing terms of the project that generates these maps, you are required to provide the following attribution to EUMETSAT: "Contains modified EUMETSAT data" [1]. While the project also incorporates imagery from NASA (which is generally in the public domain as a work of the U.S. government), the specific requirement for EUMETSAT data must be explicitly acknowledged as outlined in their data licensing policy [1]. The creator of the repository has released their own code and images under the CC0 1.0 Universal public domain dedication, meaning no further attribution to the repository creator is strictly required, though it is appreciated [1].
Citations:
Fix the Earth cloud-map attribution and license.
clouds-alpha.png is a Live Cloud Maps product derived from EUMETSAT imagery. Update lines 36-39 to identify EUMETSAT as the source and include the required attribution Contains modified EUMETSAT data, while keeping any applicable NASA/base-imagery credit separate. Do not treat this asset under NASA’s image-use policy unless the cloud data itself qualifies.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@data/textures/README.md` around lines 36 - 39, Update the Earth cloud-map
documentation around the Clouds, Source, Credit, and License entries to identify
EUMETSAT-derived imagery and include the exact attribution “Contains modified
EUMETSAT data.” Keep any applicable NASA or base-imagery credit separate, and
remove the blanket NASA image-use-policy licensing unless it specifically
applies to this cloud asset.
Summary
data/textures/bodies/<bodyId>/for planets, moons, and asteroids laterbody_appearance.pyregistry maps catalog names → packs (Earth shipped; others are drop-in)BodyAtmosphere): Earth on; asteroids stay offdata/textures/README.mdearth_flyby_{light,dark}.gifCloses #36
Test plan
python -m unittest discover -s tests -vruff check/format --checkrender.py blender --body Earth --flyby --theme all --frames 72Summary by CodeRabbit
New Features
Documentation
Tests