Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
3D in Pythonista?
-
Someone a while back posted the start of a wolfenstien style game:
https://omz-forums.appspot.com/pythonista/post/5217276491988992
The drawing was reasonably fast, though this was for a pretty small level.
-
If you can draw 2D you can always do 3D drawing. Unless you mean 3D printing 3D drawing is done by projecting the 3D onto 2D and drawing the result. After all computer/cell phones only have 2D screens. That being said there is a lot of math involved and it is nice to be able to use a library to handle it for you. Now that NumPy has been ported it shouldn't be hard to port one of the 3D graphics packages.
-
Additional note: What is hard in 3D graphics isn't the geometry, it is rendering, i.e. coloring the surfaces. Dooms biggest trick was to require all objects/walls to be perpendicular to the viewer. That simplified the rendering requirements substantially. Essentially every object in doom was a 2D shape with an image drawn on it. You scale to represent distance.
-
Are there any 3D libraries out there that could be made to work with numpy/PIL?
-
Take a look at SceneKit. While there is no python wrapper for it, you could use
objc_util
to accomplish that. It would probably make your life easier if you were to make a python wrapper first. To load that framework usefrom objc_util import * ObjcClass('NSBundle').bundleWithPath_('System/Library/Frameworks/SceneKit.framework').load()
-
If you want to play with SceneKit, here's a starting point (requires beta #160023):
Note: You can use gestures (pan/pinch) to control the camera.
from objc_util import * import ui import math load_framework('SceneKit') SCNView, SCNScene, SCNBox, SCNText, SCNNode, SCNLight, SCNAction, UIFont = map(ObjCClass, ['SCNView', 'SCNScene', 'SCNBox', 'SCNText', 'SCNNode', 'SCNLight', 'SCNAction', 'UIFont']) class SCNVector3 (Structure): _fields_ = [('x', c_float), ('y', c_float), ('z', c_float)] @on_main_thread def demo(): main_view = ui.View() main_view_objc = ObjCInstance(main_view) scene_view = SCNView.alloc().initWithFrame_options_(((0, 0),(400, 400)), None).autorelease() scene_view.setAutoresizingMask_(18) scene_view.setAllowsCameraControl_(True) scene = SCNScene.scene() root_node = scene.rootNode() text_mesh = SCNText.textWithString_extrusionDepth_('Pythonista', 6.0) text_mesh.setFlatness_(0.2) text_mesh.setChamferRadius_(0.4) text_mesh.setFont_(UIFont.fontWithName_size_('HelveticaNeue-Bold', 18)) bbox_min, bbox_max = SCNVector3(), SCNVector3() text_mesh.getBoundingBoxMin_max_(byref(bbox_min), byref(bbox_max), restype=None, argtypes=[POINTER(SCNVector3), POINTER(SCNVector3)]) text_width = bbox_max.x - bbox_min.x text_node = SCNNode.nodeWithGeometry_(text_mesh) text_node.setCastsShadow_(True) text_container = SCNNode.node() text_container.addChildNode_(text_node) text_container.setPosition_((0, 40, 0)) text_node.setPosition_((-text_width/2, 0, 0)) box = SCNBox.boxWithWidth_height_length_chamferRadius_(100, 4, 100, 1) box_node = SCNNode.nodeWithGeometry_(box) root_node.addChildNode_(box_node) rotate_action = SCNAction.repeatActionForever_(SCNAction.rotateByX_y_z_duration_(0, math.pi*2, math.pi*2, 10)) text_container.runAction_(rotate_action) root_node.addChildNode_(text_container) light_node = SCNNode.node() light_node.setPosition_((0, 100, 10)) light_node.setRotation_((1, 0, 0, -math.pi/2)) light = SCNLight.light() light.setType_('spot') light.setCastsShadow_(True) light.setColor_(UIColor.cyanColor().CGColor()) light_node.setLight_(light) root_node.addChildNode_(light_node) scene_view.setScene_(scene) main_view_objc.addSubview_(scene_view) main_view.name = 'SceneKit Demo' main_view.present() demo()
-
Apologies for bumping a 2-year-old thread. When I try to run the above script in Python 3.5 I get an error at the
map
command saying:no Objective-C class named 'b'SCNView'' found
Is this some 2.7 -> 3.5 issue? What is the extraneous
'b
in the error message referring to?thanks in advance.
-
The b'bytes string' is https://docs.python.org/3/reference/lexical_analysis.html#grammar-token-bytesprefix
SCNView is https://developer.apple.com/reference/scenekit/scnview
But I know nothing more.
-
Try adding
load_framework('SceneKit')
before the line with the error. -
Thanks Omz . I tried also the basic code from following tutorial using your template and it is working fine.
https://code.tutsplus.com/tutorials/an-introduction-to-scenekit-fundamentals--cms-23847from objc_util import * import ui import math load_framework('SceneKit') SCNView, SCNScene, SCNBox, SCNText, SCNNode, SCNLight, SCNCamera, SCNAction, UIFont = map(ObjCClass, ['SCNView', 'SCNScene', 'SCNBox', 'SCNText', 'SCNNode', 'SCNLight', 'SCNCamera', 'SCNAction', 'UIFont']) class SCNVector3 (Structure): _fields_ = [('x', c_float), ('y', c_float), ('z', c_float)] ''' https://code.tutsplus.com/tutorials/an-introduction-to-scenekit-fundamentals--cms-23847 ''' ''' override func viewDidLoad() { super.viewDidLoad() let sceneView = SCNView(frame: self.view.frame) self.view.addSubview(sceneView) let scene = SCNScene() sceneView.scene = scene let camera = SCNCamera() let cameraNode = SCNNode() cameraNode.camera = camera cameraNode.position = SCNVector3(x: 0.0, y: 0.0, z: 3.0) let light = SCNLight() light.type = SCNLightTypeOmni let lightNode = SCNNode() lightNode.light = light lightNode.position = SCNVector3(x: 1.5, y: 1.5, z: 1.5) let cubeGeometry = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0) let cubeNode = SCNNode(geometry: cubeGeometry) scene.rootNode.addChildNode(lightNode) scene.rootNode.addChildNode(cameraNode) scene.rootNode.addChildNode(cubeNode) } ''' @on_main_thread def demo(): main_view = ui.View() main_view_objc = ObjCInstance(main_view) scene_view = SCNView.alloc().initWithFrame_options_(((0, 0),(400, 400)), None).autorelease() scene_view.setAutoresizingMask_(18) scene_view.setAllowsCameraControl_(True) main_view_objc.addSubview_(scene_view) main_view.name = 'SceneKit Demo' scene = SCNScene.scene() scene_view.setScene_(scene) root_node = scene.rootNode() camera = SCNCamera.camera() camera_node = SCNNode.node() camera_node.setCamera(camera) camera_node.setPosition((0.0, 0.0, 3.0)) light = SCNLight.light() light.setType_('omni') light_node = SCNNode.node() light_node.setLight_(light) light_node.setPosition((1.5, 1.5, 1.5)) cube_geometry = SCNBox.boxWithWidth_height_length_chamferRadius_(1, 1, 1, 0) cube_node = SCNNode.nodeWithGeometry_(cube_geometry) root_node.addChildNode_(light_node) root_node.addChildNode_(camera_node) root_node.addChildNode_(cube_node) main_view.present() demo()
Full example here
https://gist.github.com/balachandrana/644840a223b636ecd84d14a595dad33b -
@omz thanks, it works now, that's awesome. I've used SceneKit quite a lot with Swift, but I'm still new to Python/ Pythonista. One question, how come you implement
SCNVector3
as a Python class, rather than importing it with the other ObjC classes? Just trying to understand the implications of working with ObjC libraries in Pythonista. thanks. -
SCNVector3
is a C struct, not an Objective-C class. Objective-C classes carry lots of information at runtime (their name, superclass, protocols, methods, etc.), and all Objective-C objects contain a pointer to their class. All of this information can be looked up via APIs provided by the Objective-C runtime library. That's what makesobjc_util
possible.C structs, on the other hand, carry no runtime info at all. If you look at some struct data, you cannot see what its type is. For all you know, it might not even be a struct. It could also be an
int
, or an array of bytes. There is also no way to see what struct types there are, or what fields a certain struct type has.So to be able to work with struct data, you first need to describe its type. In
ctypes
, you do that by creating a subclass of thectypes.Structure
class. Your subclass's_fields_
attribute lists the name and type of every field in the struct. Once you've done that, you can allocate instances of your struct in memory, and you can tellctypes
that a function takes or returns struct data of that type.In this specific case, I don't think the declaration of
SCNVector3
is actually needed. In the above code, you can see that all places where aSCNVector3
would be used in Swift, the Python/objc_util
version simply uses a tuple. This is only possible because Objective-C is involved. All Objective-C methods have a type signature, which describes the types of their return value and all arguments, including structs. This allowsobjc_util
to get some basic info about a method's struct arguments, which is enough to convert a Python tuple into correctly formated C struct data. This only works with Objective-C method arguments though, and not anywhere else. For plain C functions and variables, you still need to declare the struct type by hand. -
Thank you for your answer @dgelessus , really interesting.
-
@JadedTuna
I am new to this
Have you got a better Handel on how to do this
All I want to do is just using 3 Python learn to how create a 2d flat grid in 3d space
What are the step by steps to do this kind of thing ? -
Hi,
I got, from a swift project, some SceneKit assets (files with ‘.scn’ extension). I put them in a ###.scnassets directory and try to load them using the SCNScene.sceneNamed_ method.
This call fails, i got None as result.
Do you know if I need to make a kind of initialization for the SceneKit resources mechanism ?Thanks for your help...