Skip to content

feat(SOLSYS-36): shared Blender body textures, clouds + atmosphere (Earth first) - #40

Merged
ThomasAFink merged 3 commits into
mainfrom
feat/SOLSYS-36-blender-body-textures
Aug 7, 2026
Merged

feat(SOLSYS-36): shared Blender body textures, clouds + atmosphere (Earth first)#40
ThomasAFink merged 3 commits into
mainfrom
feat/SOLSYS-36-blender-body-textures

Conversation

@ThomasAFink

@ThomasAFink ThomasAFink commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Summary

  • Shared texture layout: data/textures/bodies/<bodyId>/ for planets, moons, and asteroids later
  • body_appearance.py registry maps catalog names → packs (Earth shipped; others are drop-in)
  • Earth color + clouds maps; optional cloud mix in the Blender material
  • Optional per-body atmosphere fresnel limb haze (BodyAtmosphere): Earth on; asteroids stay off
  • NASA Blue Marble + Live Cloud Maps attribution in data/textures/README.md
  • Retuned light/dark EEVEE lighting (day/night, matte specular, exposure)
  • Re-rendered earth_flyby_{light,dark}.gif

Closes #36

Test plan

  • python -m unittest discover -s tests -v
  • ruff check / format --check
  • render.py blender --body Earth --flyby --theme all --frames 72
  • Spot-check textured + clouds + atmosphere GIFs

Summary by CodeRabbit

  • New Features

    • Added shared body-appearance texture packs for Blender renders.
    • Enhanced Earth close-ups with surface textures, clouds, atmosphere, and improved lighting.
    • Added support for catalog-based appearance selection and optional texture maps.
    • Added Earth texture assets and documented sourcing, layout, and replacement requirements.
  • Documentation

    • Updated Blender pipeline, flyby, output, and roadmap documentation to describe close-up rendering and appearance packs.
  • Tests

    • Added coverage for texture resolution, atmosphere settings, appearance data, and unknown catalog entries.

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.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The 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.

Changes

Blender body appearance pipeline

Layer / File(s) Summary
Appearance packs and public API
animate/scenes/blender/body_appearance.py, animate/scenes/blender/__init__.py
Adds appearance dataclasses, texture discovery, atmosphere serialization, Earth registration, catalog lookup, and public exports.
Job appearance integration
animate/scenes/blender/flyby_scene.py, output/animate/blender/earth_flyby_*_job.json
Adds serialized appearance data to generated jobs and checked-in Earth jobs.
Textured and atmospheric rendering
animate/scenes/blender/render_flyby.py
Adds spherical UVs, texture and cloud materials, atmosphere shells, theme-specific worlds and lighting, and view settings.
Assets, documentation, and tests
data/textures/README.md, README.md, animate/scenes/blender/README.md, tests/test_blender_pipeline.py
Documents texture packs and Earth attribution, updates close-up descriptions, and validates appearance resolution and job output.

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
Loading

Possibly related PRs

  • ThomasAFink/SOLSYS#32: Provides the Blender flyby scaffold extended by this appearance and atmosphere pipeline.
  • ThomasAFink/SOLSYS#35: Provides the Earth Blender flyby pipeline extended with shared appearances, textures, and atmospheres.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The code and documentation address the registry, Earth textures, clouds, atmosphere, CLI wiring, and lighting, but required PNG and GIF assets are excluded from review. Review the excluded PNG and GIF files, filtered by !/*.png and !/*.gif, to verify texture assets and regenerated gallery outputs.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: shared Blender body textures, clouds, and atmosphere starting with Earth.
Out of Scope Changes check ✅ Passed The documented code, renderer, job, test, asset, and README changes directly support the linked issue objectives.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/SOLSYS-36-blender-body-textures

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…lose-ups

Fresnel shell is opt-in per body so airless packs stay clean; GIFs re-rendered with texture + atmosphere.
@ThomasAFink ThomasAFink changed the title feat(SOLSYS-36): shared Blender body textures (Earth first) feat(SOLSYS-36): shared Blender body textures + atmosphere (Earth first) Aug 7, 2026
Ship a real cloud-only mask, fresnel atmosphere, and light/dark EEVEE
exposure so textured Earth GIFs stay readable without chalk/glare.
@ThomasAFink ThomasAFink changed the title feat(SOLSYS-36): shared Blender body textures + atmosphere (Earth first) feat(SOLSYS-36): shared Blender body textures, clouds + atmosphere (Earth first) Aug 7, 2026
@ThomasAFink
ThomasAFink merged commit 208f96b into main Aug 7, 2026
4 of 5 checks passed
@ThomasAFink
ThomasAFink deleted the feat/SOLSYS-36-blender-body-textures branch August 7, 2026 22:43

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
tests/test_blender_pipeline.py (1)

91-116: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add 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_pack does not exercise a registered airless body or job serialization. Add an airless BodyAppearance fixture, or a registered Moon/asteroid pack, and assert that the serialized appearance omits clouds and keeps atmosphere.enabled false.

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 value

The 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 the max(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 ShaderNodeMapRange limited 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

📥 Commits

Reviewing files that changed from the base of the PR and between 477e323 and e392310.

⛔ Files ignored due to path filters (4)
  • data/textures/bodies/earth/clouds.png is excluded by !**/*.png
  • data/textures/bodies/earth/color.png is excluded by !**/*.png
  • output/animate/blender/earth_flyby_dark.gif is excluded by !**/*.gif
  • output/animate/blender/earth_flyby_light.gif is excluded by !**/*.gif
📒 Files selected for processing (10)
  • README.md
  • animate/scenes/blender/README.md
  • animate/scenes/blender/__init__.py
  • animate/scenes/blender/body_appearance.py
  • animate/scenes/blender/flyby_scene.py
  • animate/scenes/blender/render_flyby.py
  • data/textures/README.md
  • output/animate/blender/earth_flyby_dark_job.json
  • output/animate/blender/earth_flyby_light_job.json
  • tests/test_blender_pipeline.py

Comment on lines +70 to +81
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ 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 to REPO_ROOT instead of str(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 set outputDirectory to a repository-relative path.
  • output/animate/blender/earth_flyby_light_job.json#L668-L677: regenerate this job after the fix, and set outputDirectory to a repository-relative path.
📍 Affects 4 files
  • animate/scenes/blender/body_appearance.py#L70-L81 (this comment)
  • animate/scenes/blender/render_flyby.py#L373-L401
  • output/animate/blender/earth_flyby_dark_job.json#L668-L677
  • output/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.

Comment on lines +118 to +130
# 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']

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.

Comment on lines +136 to +153
# 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']

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 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 -200

Repository: 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:


🌐 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:


🌐 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:


🌐 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:


🌐 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:


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.

Comment thread data/textures/README.md
Comment on lines +36 to +39
- **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/))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 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"
fi

Repository: 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:


🌐 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.

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.

Blender planet surface textures + atmosphere for close-ups

1 participant