Created
January 7, 2026 19:23
-
-
Save Martin-Pitt/863987b3d46f3d8ffb8fe2d6d70f8044 to your computer and use it in GitHub Desktop.
Blender Script to export a 31x31 subdivided grid plane to a SL megaprim sculpty
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import bpy | |
| import numpy as np | |
| bl_info = { | |
| "name": "Second Life Mega Sculpty Exporter", | |
| "blender": (4, 5, 0), | |
| "category": "Import-Export", | |
| "version": (1, 0, 0), | |
| "description": "Export 31x31 grid as a Second Life 1024x1024x256 megaprim sculpty PNG" | |
| } | |
| class EXPORT_OT_sculpty(bpy.types.Operator): | |
| bl_idname = "export_scene.sculpty" | |
| bl_label = "Export Sculpty" | |
| bl_options = {'REGISTER', 'UNDO'} | |
| filepath: bpy.props.StringProperty(subtype="FILE_PATH") | |
| def execute(self, context): | |
| obj = context.active_object | |
| if not obj or obj.type != 'MESH': | |
| self.report({'ERROR'}, "Select a mesh object") | |
| return {'CANCELLED'} | |
| mesh = obj.data | |
| if len(mesh.vertices) != 1024: | |
| self.report({'ERROR'}, "Mesh must have exactly 1024 vertices") | |
| return {'CANCELLED'} | |
| # Create Blender image (64x64) | |
| img = bpy.data.images.new("sculpty", width=64, height=64) | |
| # Prepare pixel data (RGBA format, 4 values per pixel) | |
| pixels = [0.0] * (64 * 64 * 4) | |
| # Map vertices to Second Life megaprim bounding box (1024x1024x256) | |
| for idx, vertex in enumerate(mesh.vertices): | |
| py = idx // 32 | |
| px = idx % 32 | |
| # Normalize to 0-1 range within megaprim bounds | |
| x_norm = (vertex.co.x + 512) / 1024 # X: -512 to +512 | |
| y_norm = (vertex.co.y + 512) / 1024 # Y: -512 to +512 | |
| z_norm = (vertex.co.z) / 256 # Z: 0 to +256 | |
| # Clamp to 0-1 range | |
| x_norm = max(0, min(1, x_norm)) | |
| y_norm = max(0, min(1, y_norm)) | |
| z_norm = max(0, min(1, z_norm)) | |
| # Map to 2x2 pixels in 64x64 image | |
| for dy in range(2): | |
| for dx in range(2): | |
| pixel_y = py * 2 + dy | |
| pixel_x = px * 2 + dx | |
| pixel_idx = (pixel_y * 64 + pixel_x) * 4 | |
| pixels[pixel_idx] = x_norm # R (X position) | |
| pixels[pixel_idx + 1] = y_norm # G (Y position) | |
| pixels[pixel_idx + 2] = z_norm # B (Z position) | |
| pixels[pixel_idx + 3] = 1.0 # A (opacity) | |
| img.pixels[:] = pixels | |
| img.save_render(self.filepath) | |
| # Clean up | |
| bpy.data.images.remove(img) | |
| self.report({'INFO'}, f"Sculpty saved to {self.filepath}") | |
| return {'FINISHED'} | |
| def invoke(self, context, event): | |
| context.window_manager.fileselect_add(self) | |
| return {'RUNNING_MODAL'} | |
| def menu_export(self, context): | |
| self.layout.operator(EXPORT_OT_sculpty.bl_idname, text="Second Life Mega Sculpty (.png)") | |
| def register(): | |
| bpy.utils.register_class(EXPORT_OT_sculpty) | |
| bpy.types.TOPBAR_MT_file_export.append(menu_export) | |
| def unregister(): | |
| bpy.utils.unregister_class(EXPORT_OT_sculpty) | |
| bpy.types.TOPBAR_MT_file_export.remove(menu_export) | |
| if __name__ == "__main__": | |
| register() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment