この記事は、Pythonista3 Advent Calendar 2022 の18日目の記事です。
一方的な偏った目線で、Pythonista3 を紹介していきます。
ほぼ毎日iPhone(Pythonista3)で、コーディングをしている者です。よろしくお願いします。
以下、私の2022年12月時点の環境です。
--- SYSTEM INFORMATION ---
* Pythonista 3.3 (330025), Default interpreter 3.6.1
* iOS 16.1.1, model iPhone12,1, resolution (portrait) 828.0 x 1792.0 @ 2.0
他の環境(iPad や端末の種類、iOS のバージョン違い)では、意図としない挙動(エラーになる)なる場合もあります。ご了承ください。
ちなみに、model iPhone12,1
は、iPhone11 です。
この記事でわかること
- 画像データや、シーンデータなど、外部データの取り込み方
- 取り込み後の、SceneKit 上での操作方法
- さまざまな効果を使い、出力結果をにぎやかにする
サンプルデータ
前回は、SceneKit のみでScene を作りました。
プリミティブな球体やボックスなど、標準的に用意されているもの以外ももちろん、3DCG の世界へ呼び出すことができます。
他のデータを取り込むことにより、より豊かな3DCG 生活を楽しめるわけです。
検索をすれば、いろいろなデータを見つけることができますが、今回はApple が公開しているデータを使ってみましょう。
iPhone だとなかなか見辛いレイアウトなんですけどね。。。
データダウンロード先
-
Fox 2: SceneKit WWDC 2017 sample code
- 今回はこちらをメインに使っていきます
- Max ちゃんかわいい
- 75.2MB ありました
他のおすすめな、データたちです。
-
SceneKit State of the Union Demo
- SceneKit でよくみる、飛行機(?)のデータがある
-
- 地球のテクスチャ
- 3D でよくみるティーポットみたいなの
- データのサンプル数が多い
iPhone に取り込み、Pythonista3 に取り込む
データはPC からiPhone に移しても問題ありませんが、iPhone のみで完結も可能です。
Fox 2: SceneKit WWDC 2017 sample code
-
Download Sample Code
をタップして.zip
をダウンロード- ダウンロードフォルダで問題ありません
-
(↓)
アイコンでダウンロード完了がわかります
-
ファイルApp
で解凍-
.zip
をタップすると解凍が始まります
-
-
ファイルApp
から、Pythonista3 へデータ移動-
選択
で解凍したファイルを選ぶ
-
- 共有アクションで
Run Pythonista Script
を選択
-
Import File
で、Pythonista3 に取り込まれます
サンプルデータ探訪
WWDC 2017 の SceneKit サンプル Fox 2 を調べる その1 - Apple Engine
Fox2SceneKitWWDC2017samplecode ディレクトリツリー
.
├── Assets
│ ├── Art.scnassets
│ │ ├── character
│ │ │ ├── jump_dust.scn
│ │ │ ├── max.scn
│ │ │ ├── max_AO.png
│ │ │ ├── max_diffuse.png
│ │ │ ├── max_diffuseB.png
│ │ │ ├── max_diffuseC.png
│ │ │ ├── max_diffuseD.png
│ │ │ ├── max_idle.scn
│ │ │ ├── max_jump.scn
│ │ │ ├── max_roughness.png
│ │ │ ├── max_specular.png
│ │ │ ├── max_spin.scn
│ │ │ ├── max_walk.scn
│ │ │ └── smoke.png
│ │ ├── collision.scn
│ │ ├── enemy
│ │ │ ├── baddy_bad_AO.png
│ │ │ ├── baddy_bad_DIFFUSE.png
│ │ │ ├── baddy_bad_ILLUMINATION.png
│ │ │ ├── baddy_bad_NORMAL.png
│ │ │ ├── baddy_fearful_AO.png
│ │ │ ├── baddy_fearful_DIFFUSE.png
│ │ │ ├── baddy_fearful_ILLUMINATION.png
│ │ │ ├── baddy_fearful_NORMAL.png
│ │ │ ├── baddy_fearful_ROUGHNESS.png
│ │ │ ├── dot.png
│ │ │ ├── enemies.scn
│ │ │ ├── enemy1.scn
│ │ │ ├── enemy2.scn
│ │ │ ├── enemy_explosion.scn
│ │ │ ├── fireMask.png
│ │ │ ├── flare.png
│ │ │ └── gradient.png
│ │ ├── level_scene.scn
│ │ ├── particles
│ │ │ ├── burn.scn
│ │ │ ├── circle.png
│ │ │ ├── collect-big.scnp
│ │ │ ├── collect.scnp
│ │ │ ├── dot.png
│ │ │ ├── enemy_explosion.scn
│ │ │ ├── fire.png
│ │ │ ├── flare.png
│ │ │ ├── glow01.png
│ │ │ ├── glow_ellipse.png
│ │ │ ├── key_apparition.scn
│ │ │ ├── particles_spin.scn
│ │ │ └── unlock_door.scn
│ │ ├── scene.gks
│ │ ├── scene.scn
│ │ ├── textures
│ │ │ ├── Background_sky.png
│ │ │ ├── circle.png
│ │ │ ├── cubeMap4096.png
│ │ │ ├── depart-vieuxVolcan_AO.png
│ │ │ ├── depart-vieuxVolcan_lightmap.exr
│ │ │ ├── depart-vieuxVolcan_lightmap.png
│ │ │ ├── dot.png
│ │ │ ├── env_star.png
│ │ │ ├── fernSpiral_diffuse.png
│ │ │ ├── fernSpiral_illumination.png
│ │ │ ├── fernSpiral_roughness.png
│ │ │ ├── fern_diffuse.png
│ │ │ ├── fern_illumination.png
│ │ │ ├── fern_roughness.png
│ │ │ ├── fire.png
│ │ │ ├── flowmapPanda.png
│ │ │ ├── gem.png
│ │ │ ├── gradient_fire.png
│ │ │ ├── lavaDry_Med_diffuse.png
│ │ │ ├── lavaDry_Med_diffuse.psd
│ │ │ ├── lavaDry_diffuse.png
│ │ │ ├── lavaDry_roughness.png
│ │ │ ├── lava_cold2.png
│ │ │ ├── lava_hot.png
│ │ │ ├── lava_medium2.png
│ │ │ ├── lava_splash01.png
│ │ │ ├── lavalBubble.png
│ │ │ ├── mobilePT_diffuse.png
│ │ │ ├── mobilePlateformBig_diffuse.png
│ │ │ ├── mobilePlateformBig_roughness.png
│ │ │ ├── mobilePlateformBig_specular.png
│ │ │ ├── noisemap.png
│ │ │ ├── planks_albedo.png
│ │ │ ├── planks_metalness.png
│ │ │ ├── planks_normal.png
│ │ │ ├── planks_roughness.png
│ │ │ ├── plantes_AO.png
│ │ │ ├── plantes_lightmap.exr
│ │ │ ├── plantes_lightmap.png
│ │ │ ├── ripples.png
│ │ │ ├── rocks_border_diffuse.png
│ │ │ ├── rocks_border_normal.png
│ │ │ ├── rocks_border_roughness.png
│ │ │ ├── rocks_top_diffuse.png
│ │ │ ├── rocks_top_normal.png
│ │ │ ├── rocks_top_roughness.png
│ │ │ ├── sand_diffuse.png
│ │ │ ├── sand_normal.png
│ │ │ ├── sand_roughness.png
│ │ │ ├── sky_cube.exr
│ │ │ ├── sky_cube.png
│ │ │ ├── smoke_panda.png
│ │ │ ├── tile_blue_diffuse.png
│ │ │ ├── tile_blue_roughness.png
│ │ │ ├── tile_diffuse.png
│ │ │ ├── tile_roughness.png
│ │ │ ├── topLava_diffuse.png
│ │ │ ├── topLava_normal.png
│ │ │ ├── topLava_roughness.png
│ │ │ ├── wall_diffuse.png
│ │ │ └── wall_roughness.png
│ │ └── tile.png
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── icon-1024.png
│ │ │ ├── icon-29.png
│ │ │ ├── icon-29@2x.png
│ │ │ ├── icon-29@3x.png
│ │ │ ├── icon-40.png
│ │ │ ├── icon-40@2x.png
│ │ │ ├── icon-40@3x.png
│ │ │ ├── icon-60@2x.png
│ │ │ ├── icon-60@3x.png
│ │ │ ├── icon-76.png
│ │ │ ├── icon-76@2x.png
│ │ │ └── icon-83.5@2x.png
│ │ ├── Contents.json
│ │ ├── MacAppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon_Max-1024.png
│ │ │ ├── Icon_Max-128.png
│ │ │ ├── Icon_Max-16.png
│ │ │ ├── Icon_Max-256.png
│ │ │ ├── Icon_Max-32.png
│ │ │ ├── Icon_Max-512.png
│ │ │ └── Icon_Max-64.png
│ │ ├── tvOS App Icon & Top Shelf Image.brandassets
│ │ │ ├── App Icon - Large.imagestack
│ │ │ │ ├── Back.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ │ ├── Contents.json
│ │ │ │ │ │ └── tvos-layer1.png
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Background.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ │ ├── Contents.json
│ │ │ │ │ │ └── tvos-layer0.png
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Contents.json
│ │ │ │ ├── Front.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ │ ├── Contents.json
│ │ │ │ │ │ └── tvos-layer3.png
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Middle.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ │ ├── Contents.json
│ │ │ │ │ │ └── tvos-layer2.png
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Near.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── tvos-layer4.png
│ │ │ │ └── Contents.json
│ │ │ ├── App Icon - Small.imagestack
│ │ │ │ ├── Back.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ │ ├── Contents.json
│ │ │ │ │ │ └── tvos-layer1.png
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Background.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ │ ├── Contents.json
│ │ │ │ │ │ └── tvos-layer0.png
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Contents.json
│ │ │ │ ├── Front.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ │ ├── Contents.json
│ │ │ │ │ │ └── tvos-layer3.png
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Middle.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ │ ├── Contents.json
│ │ │ │ │ │ └── tvos-layer2.png
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Near.imagestacklayer
│ │ │ │ ├── Content.imageset
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── tvos-layer4.png
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ ├── Top Shelf Image Wide.imageset
│ │ │ │ └── Contents.json
│ │ │ └── Top Shelf Image.imageset
│ │ │ ├── Contents.json
│ │ │ └── top-shelf.png
│ │ └── tvOS LaunchImage.launchimage
│ │ └── Contents.json
│ ├── Overlays
│ │ ├── MaxIcon.png
│ │ ├── collectableBIG_empty.png
│ │ ├── collectableBIG_full.png
│ │ ├── congratulations.png
│ │ ├── congratulations_pandaMax.png
│ │ ├── dpad.png
│ │ ├── key_empty.png
│ │ └── key_full.png
│ └── audio
│ ├── Explosion1.m4a
│ ├── Explosion2.m4a
│ ├── Music_victory.mp3
│ ├── Step_rock_00.mp3
│ ├── Step_rock_01.mp3
│ ├── Step_rock_02.mp3
│ ├── Step_rock_03.mp3
│ ├── Step_rock_04.mp3
│ ├── Step_rock_05.mp3
│ ├── Step_rock_06.mp3
│ ├── Step_rock_07.mp3
│ ├── Step_rock_08.mp3
│ ├── Step_rock_09.mp3
│ ├── aah_extinction.mp3
│ ├── ambience.mp3
│ ├── attack.mp3
│ ├── collect.mp3
│ ├── collectBig.mp3
│ ├── hit.mp3
│ ├── hitEnemy.wav
│ ├── jump.m4a
│ ├── ouch_firehit.mp3
│ ├── panda_catch_fire.mp3
│ ├── unlockTheDoor.m4a
│ └── volcano.mp3
├── LICENSE.txt
├── Objective-C
│ ├── fox2 Shared
│ │ ├── AAPLCharacter.h
│ │ ├── AAPLCharacter.m
│ │ ├── AAPLGameController.h
│ │ ├── AAPLGameController.m
│ │ ├── AAPLOverlay.h
│ │ ├── AAPLOverlay.m
│ │ ├── GPKComponents
│ │ │ ├── AAPLBaseComponent.h
│ │ │ ├── AAPLBaseComponent.m
│ │ │ ├── AAPLChaserComponent.h
│ │ │ ├── AAPLChaserComponent.m
│ │ │ ├── AAPLPlayerComponent.h
│ │ │ ├── AAPLPlayerComponent.m
│ │ │ ├── AAPLScaredComponent.h
│ │ │ └── AAPLScaredComponent.m
│ │ └── UI
│ │ ├── AAPLButton.h
│ │ ├── AAPLButton.m
│ │ ├── AAPLMenu.h
│ │ ├── AAPLMenu.m
│ │ ├── AAPLSlider.h
│ │ └── AAPLSlider.m
│ ├── fox2 iOS
│ │ ├── AAPLButtonOverlay.h
│ │ ├── AAPLButtonOverlay.m
│ │ ├── AAPLControlOverlay.h
│ │ ├── AAPLControlOverlay.m
│ │ ├── AAPLGameViewController.h
│ │ ├── AAPLGameViewController.m
│ │ ├── AAPLPadOverlay.h
│ │ ├── AAPLPadOverlay.m
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Base.lproj
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── main.m
│ ├── fox2 macOS
│ │ ├── AAPLGameViewController.h
│ │ ├── AAPLGameViewController.m
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Base.lproj
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── main.m
│ ├── fox2 tvOS
│ │ ├── AAPLGameViewController.h
│ │ ├── AAPLGameViewController.m
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Base.lproj
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── main.m
│ └── fox2.xcodeproj
│ └── project.pbxproj
├── README.md
└── Swift
├── Shared
│ ├── BaseComponent.swift
│ ├── Character.swift
│ ├── ChaserComponent.swift
│ ├── GameController.swift
│ ├── Overlay.swift
│ ├── PlayerComponent.swift
│ ├── ScaredComponent.swift
│ ├── SimdExtensions.swift
│ └── UI
│ ├── Button.swift
│ ├── Menu.swift
│ └── Slider.swift
├── fox2-swift.xcodeproj
│ └── project.pbxproj
├── iOS
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── ButtonOverlay.swift
│ ├── ControlOverlay.swift
│ ├── GameViewController.swift
│ ├── Info.plist
│ └── PadOverlay.swift
├── macOS
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ └── Main.storyboard
│ ├── GameViewController.swift
│ └── Info.plist
└── tvOS
├── AppDelegate.swift
├── Base.lproj
│ └── Main.storyboard
├── GameViewController.swift
└── Info.plist
58 directories, 281 files
今回使うデータは、Assets/Art.scnassets/
のcharacter
と、textures/Background_sky.png
です。
.
├── Assets
│ ├── Art.scnassets
│ │ ├── character
│ │ │ ├── jump_dust.scn
│ │ │ ├── max.scn
│ │ │ ├── max_AO.png
│ │ │ ├── max_diffuse.png
│ │ │ ├── max_diffuseB.png
│ │ │ ├── max_diffuseC.png
│ │ │ ├── max_diffuseD.png
│ │ │ ├── max_idle.scn
│ │ │ ├── max_jump.scn
│ │ │ ├── max_roughness.png
│ │ │ ├── max_specular.png
│ │ │ ├── max_spin.scn
│ │ │ ├── max_walk.scn
│ │ │ └── smoke.png
│ │ ├── textures
│ │ │ ├── Background_sky.png
実行するファイルのディレクトリにassets
とフォルダを作り、上記データを格納しておきます。
.
├── assets
│ ├── character
│ │ ├── jump_dust.scn
│ │ ├── max.scn
│ │ ├── max_AO.png
│ │ ├── max_diffuse.png
│ │ ├── max_diffuseB.png
│ │ ├── max_diffuseC.png
│ │ ├── max_diffuseD.png
│ │ ├── max_idle.scn
│ │ ├── max_jump.scn
│ │ ├── max_roughness.png
│ │ ├── max_specular.png
│ │ ├── max_spin.scn
│ │ ├── max_walk.scn
│ │ └── smoke.png
│ └── textures
│ └── Background_sky.png
└── 実行するファイル.py
これにて準備は完了です。実装していきましょう。
実装
from objc_util import load_framework, ObjCClass, on_main_thread
from objc_util import UIColor, UIImage, NSData, nsurl
import ui
import pdbg
load_framework('SceneKit')
SCNScene = ObjCClass('SCNScene')
SCNView = ObjCClass('SCNView')
SCNNode = ObjCClass('SCNNode')
SCNLight = ObjCClass('SCNLight')
SCNCamera = ObjCClass('SCNCamera')
SCNTorus = ObjCClass('SCNTorus')
class GameScene:
def __init__(self):
self.scene: SCNScene
self.setUpScene()
def setUpScene(self):
# --- import
foxmax_URL = nsurl('./assets/character/max.scn')
bkSky_URL = NSData.dataWithContentsOfURL_(
nsurl('./assets/textures/Background_sky.png'))
tex_bks = UIImage.alloc().initWithData_(bkSky_URL)
# --- SCNScene
scene = SCNScene.sceneWithURL_options_(foxmax_URL, None)
scene.background().contents = tex_bks
scene.lightingEnvironment().contents = tex_bks
scene.lightingEnvironment().intensity = 1.24
scene_rootNode_addChildNode_ = scene.rootNode().addChildNode_
foxmaxNode = scene.rootNode().objectInChildNodesAtIndex_(0)
torus = SCNTorus.torusWithRingRadius_pipeRadius_(0.075, 0.02)
torus.ringSegmentCount = 8
torus.pipeSegmentCount = 8
torus.material().lightingModelName = 'SCNLightingModelPhysicallyBased'
torus.firstMaterial().reflective().contents = 0.9
torus.firstMaterial().diffuse().contents = UIColor.yellowColor()
torusNode = SCNNode.nodeWithGeometry_(torus)
torusNode.position = (0.0, 0.55, 0.0)
scene_rootNode_addChildNode_(torusNode)
# --- SCNLight
lightNode = SCNNode.node()
lightNode.light = SCNLight.light()
lightNode.castsShadow = True
lightNode.position = (0.0, 10.0, 10.0)
scene_rootNode_addChildNode_(lightNode)
# --- SCNCamera
camera = SCNCamera.camera()
camera.wantsHDR = True
camera.bloomBlurRadius = 18.0
camera.bloomIntensity = 1.0
camera.bloomThreshold = 1.0
camera.colorFringeIntensity = 4.0
camera.colorFringeStrength = 4.0
camera.motionBlurIntensity = 6
camera.xFov = 35.0
camera.yFov = 35.0
cameraNode = SCNNode.node()
cameraNode.camera = camera
cameraNode.position = (0.0, 0.25, 2.0)
scene_rootNode_addChildNode_(cameraNode)
self.scene = scene
class View(ui.View):
def __init__(self, *args, **kwargs):
ui.View.__init__(self, *args, **kwargs)
self.name = ''
self.bg_color = 'maroon'
self.scene: GameScene
self.scnView: SCNView
self.viewDidLoad()
self.objc_instance.addSubview_(self.scnView)
#@on_main_thread
def viewDidLoad(self):
scene = GameScene()
# --- SCNView
_frame = ((0, 0), (100, 100))
scnView = SCNView.alloc().initWithFrame_(_frame)
scnView.setAutoresizingMask_((1 << 1) | (1 << 4))
scnView.backgroundColor = UIColor.blackColor()
scnView.allowsCameraControl = True
scnView.showsStatistics = True
'''
OptionNone = 0
ShowPhysicsShapes = (1 << 0)
ShowBoundingBoxes = (1 << 1)
ShowLightInfluences = (1 << 2)
ShowLightExtents = (1 << 3)
ShowPhysicsFields = (1 << 4)
ShowWireframe = (1 << 5)
RenderAsWireframe = (1 << 6)
ShowSkeletons = (1 << 7)
ShowCreases = (1 << 8)
ShowConstraints = (1 << 9)
ShowCameras = (1 << 10)
'''
_debugOptions = ((1 << 0) | (1 << 1) | (1 << 4) | (1 << 5) | (1 << 10))
scnView.debugOptions = _debugOptions
scnView.autorelease()
scnView.scene = scene.scene
self.scene = scene
self.scnView = scnView
def touch_began(self, touch):
pass
if __name__ == '__main__':
view = View()
view.present(style='fullscreen', orientations=['portrait'])
立ち上がり時のカクツキにドキッとしますが、それ以降は問題なく動きます。
高性能なiPhone をモリモリと使っている気分でいいですよね。
データインポート
from objc_util import UIImage, NSData, nsurl
# --- import
foxmax_URL = nsurl('./assets/character/max.scn')
scene = SCNScene.sceneWithURL_options_(foxmax_URL, None)
bkSky_URL = NSData.dataWithContentsOfURL_(
nsurl('./assets/textures/Background_sky.png'))
tex_bks = UIImage.alloc().initWithData_(bkSky_URL)
Swift やObjective-C では、標準的過ぎて解説が少ない代表例が、インポート関係です。
今回は、ファイルパスをobjc_util
で読める形式にしてから読み込みをさせています。
他には、MDLAsset
を使ったデータ読み込みの方法もあります。
SCNScene
より、.scn
を読み込ませているので、max.scn
に依存したScene となっています。
今回は、触れませんでしたがmax
ちゃんの内部を見るには:
foxmaxNode = scene.rootNode().objectInChildNodesAtIndex_(0)
これで、Node へアクセスできます。
WWDC 2017 の SceneKit サンプル Fox 2 を調べる その3 - Apple Engine
カメラエフェクト
camera.wantsHDR = True
camera.bloomBlurRadius = 18.0
camera.bloomIntensity = 1.0
camera.bloomThreshold = 1.0
camera.colorFringeIntensity = 4.0
camera.colorFringeStrength = 4.0
camera.motionBlurIntensity = 6
ゴリゴリにエフェクトを付けてしまい、見た目が悪いですがさまざまななエフェクトがあります。
機能詳細は毎度のごとくの参照先ですが、比較的簡単な設定で効果がすごいのでパラメータを操作するだけでも楽しいです。
iOS で SceneKit を試す(Swift 3) その38 - Scene Editor カメラの基本設定 - Apple Engine
キューブマップや他の3DCG データ
さりげなく処理をさせていましたが、textures/Backgtound_sky.png
にて、キューブマップを設定しています。
ジオメトリの質感が一気に変わりリアル感が増すので、キューブマップもいろいろと差し替えて変化を楽しむのもいいですね。
scene.background().contents = tex_bks
scene.lightingEnvironment().contents = tex_bks
scene.lightingEnvironment().intensity = 1.24
iOS で SceneKit を試す(Swift 3) その82 - キューブマップを設定する - Apple Engine
以下は、過去に縄文土器のデータとNASA のスターマップをキューブマップにしたキャプチャです。
ワイのiPhoneが宇宙や#NASA #jomon #jomonosp #縄文オープンソースプロジェクト #Pythonista https://t.co/2Z5DPsG0oF pic.twitter.com/MYtRkKJ5kU
— pome-ta (@pome_ta93) September 21, 2020
縄文オープンソースプロジェクト | 縄文文化発信サポーターズ
縄文土器は、.stl
データで、41.9MB でした。。。パケ死に以外は心配せずに、問題なく読み込んで描画してくれます。
おそら、きれい
— pome-ta (@pome_ta93) August 18, 2020
な、火焔型土器さん#jomon #jomonosp #縄文オープンソースプロジェクト#Pythonista3 #Python pic.twitter.com/GXHh8nEfSY
3DCG 関係はさまざまな形式でデータがあり、配布されているので。SNS 等で知ったデータをすぐにiPhone で確認できてしまう。というのは面白いですね。
パーティクル
3DCG で絵力といえば、パーティクル表現なのではないかと、勝手に思っています。
SceneKit にももちろんパーティクル機能があるので、一部の機能のみですが最後に紹介をして終えたいと思います。
iOS で SceneKit を試す(Swift 3) その78 - パーティクルシステムのパラメーターをみてみる - Apple Engine
from objc_util import load_framework, ObjCClass, on_main_thread
from objc_util import UIColor, UIImage, NSData, nsurl
import ui
import pdbg
load_framework('SceneKit')
SCNScene = ObjCClass('SCNScene')
SCNParticleSystem = ObjCClass('SCNParticleSystem')
SCNSphere = ObjCClass('SCNSphere')
SCNNode = ObjCClass('SCNNode')
SCNAction = ObjCClass('SCNAction')
SCNLight = ObjCClass('SCNLight')
SCNCamera = ObjCClass('SCNCamera')
SCNView = ObjCClass('SCNView')
class GameScene:
def __init__(self):
self.scene: SCNScene
self.setUpScene()
def setUpScene(self):
# --- import
bkSky_URL = NSData.dataWithContentsOfURL_(
nsurl('./assets/textures/Background_sky.png'))
tex_bks = UIImage.alloc().initWithData_(bkSky_URL)
# --- SCNScene
scene = SCNScene.scene()
scene.background().contents = tex_bks
scene.lightingEnvironment().contents = tex_bks
scene.lightingEnvironment().intensity = 1.24
scene_rootNode_addChildNode_ = scene.rootNode().addChildNode_
emitterBall = SCNSphere.sphereWithRadius_(0.25)
emitterBall.geodesic = True
emitterBall.segmentCount = 32
# SCNParticleSystem
particleSys = SCNParticleSystem.particleSystem()
particleSys.emitterShape = emitterBall
particleSys.birthRate = 1024
particleSys.birthRateVariation = 128
particleSys.birthDirection = 2
particleSys.particleColor = UIColor.blueColor()
particleSys.particleColorVariation = (0.1, 0.4, 0.6, 0.5)
particleSys.particleAngle = 0.1
particleSys.particleAngleVariation = 8
particleSys.particleVelocity = 0.1
particleSys.particleVelocityVariation = 0.2
particleSys.particleSize = 0.001
particleSys.particleSizeVariation = 0.005
particleSys.emissionDuration = 0.001
particleSys.emissionDurationVariation = 0.1
particleSys.particleLifeSpan = 1
particleSys.particleLifeSpanVariation = 8
particleSys.particleAngularVelocity = 0.01
particleSys.particleAngularVelocityVariation = 8
particleSys.idleDuration = 0.4
particleSys.idleDurationVariation = 0.8
particleSys.particleIntensity = 2
particleSys.particleIntensityVariation = 4
particleSys.isLightingEnabled = True
particleNode = SCNNode.node()
particleNode.addParticleSystem(particleSys)
scene_rootNode_addChildNode_(particleNode)
# --- SCNCamera
camera = SCNCamera.camera()
camera.wantsHDR = True
camera.wantsExposureAdaptation = True
camera.exposureAdaptationBrighteningSpeedFactor = 0.02
camera.exposureAdaptationDarkeningSpeedFactor = 0.1
camera.minimumExposure = -15
camera.maximumExposure = 15
camera.bloomIntensity = 2.0
camera.bloomThreshold = 0.6
camera.bloomBlurRadius = 18
camera.colorFringeIntensity = 4.0
camera.colorFringeStrength = 4.0
camera.motionBlurIntensity = 6
camera.xFov = 35.0
camera.yFov = 35.0
camera.zNear = 2
camera.zFar = 100
cameraNode = SCNNode.node()
cameraNode.camera = camera
cameraNode.position = (0.0, 1.0, 2.0)
cameraNode.eulerAngles = (-0.5, 0, 0)
dollyNode = SCNNode.node()
dollyNode.addChildNode_(cameraNode)
dollyNode.runAction_(
SCNAction.repeatActionForever_(
SCNAction.rotateByX_y_z_duration_(0.0, 2.0, 0.1, 8.0)))
scene_rootNode_addChildNode_(dollyNode)
self.scene = scene
class View(ui.View):
def __init__(self, *args, **kwargs):
ui.View.__init__(self, *args, **kwargs)
self.name = ''
self.bg_color = 'maroon'
self.scene: GameScene
self.scnView: SCNView
self.viewDidLoad()
self.objc_instance.addSubview_(self.scnView)
#@on_main_thread
def viewDidLoad(self):
scene = GameScene()
# --- SCNView
_frame = ((0, 0), (100, 100))
scnView = SCNView.alloc().initWithFrame_(_frame)
scnView.setAutoresizingMask_((1 << 1) | (1 << 4))
scnView.backgroundColor = UIColor.blackColor()
scnView.allowsCameraControl = True
scnView.showsStatistics = True
'''
OptionNone = 0
ShowPhysicsShapes = (1 << 0)
ShowBoundingBoxes = (1 << 1)
ShowLightInfluences = (1 << 2)
ShowLightExtents = (1 << 3)
ShowPhysicsFields = (1 << 4)
ShowWireframe = (1 << 5)
RenderAsWireframe = (1 << 6)
ShowSkeletons = (1 << 7)
ShowCreases = (1 << 8)
ShowConstraints = (1 << 9)
ShowCameras = (1 << 10)
'''
_debugOptions = ((1 << 0) | (1 << 1) | (1 << 3) | (1 << 5) | (1 << 10))
scnView.debugOptions = _debugOptions
scnView.autorelease()
scnView.scene = scene.scene
self.scene = scene
self.scnView = scnView
def touch_began(self, touch):
pass
if __name__ == '__main__':
view = View()
view.present(style='fullscreen', orientations=['portrait'])
パラメータが多過ぎて何が何だか😅 な状態ですが、、、、
ここまで、読んでいただきありがとうございました。
せんでん
Discord
Pythonista3 の日本語コミュニティーがあります。みなさん優しくて、わからないところも親身に教えてくれるのでこの機会に覗いてみてください。
書籍
iPhone/iPad でプログラミングする最強の本。
その他
- サンプルコード
Pythonista3 Advent Calendar 2022 でのコードをまとめているリポジトリがあります。
コードのエラーや変なところや改善点など。ご指摘やPR お待ちしておりますー
なんしかガチャガチャしていますが、お気兼ねなくお声がけくださいませー
やれるか、やれないか。ではなく、やるんだけども、紹介説明することは尽きないと思うけど、締め切り守れるか?って話よ!(クズ)
— pome-ta (@pome_ta93) November 4, 2022
Pythonista3 Advent Calendar 2022 https://t.co/JKUxA525Pt #Qiita
- GitHub
基本的にGitHub にコードをあげているので、何にハマって何を実装しているのか観測できると思います。