How to specify two-sided material for RealityKit / ARView? - ios

I'm trying to load a model and texture in RealityKit (set up in an ARView instance), but I can't seem to figure out how to specify the material should be two-sided.
I have the model loaded up as a ModelEntity, the texture loaded up as a TextureResource. The model and texture are loading up, but are rending one-sided. Since the model is open (i.e., back faces are visible), there are gaps on how it is rendered.
So far, I have,
let entity: ModelEntity = try .loadModel(named: "model.obj")
var material = SimpleMaterial()
material.baseColor = try .texture(.load(named: "texture.png"))
entity.model?.materials = [material]
I was hoping to find a property such as
material.twoSided = true
but so far, I have not found the equivalent thing in RealityKit.
Anyone know how to set two-sided materials in RealityKit?

There doesn't seem to be any way to do this programmatically at the moment via the RealityKit APIs.
Can you change your model definition so it doesn't do back face culling? For example in a USDZ file I am importing it defines one part of the mesh as:
def Mesh "Plane_1"
{
uniform bool doubleSided = 1
You might be able to convert your obj file to a used file using usdzconvert first (https://developer.apple.com/download/more/?=USDPython) then edit the file manually, then import that in to your scene.
It might also be depending on how the model is setup that you can pass in more than one material to the materials array that are applied to different parts of the model, you can see how many materials the model expects:
entity.model?.mesh.expectedMaterialCount

As others already answered you can't do it programmatically.
You can however do it manually for each model via the inspection panel.
See the image below. Near the bottom, you have "Double Sided" checkbox.

I don't think there is a way to do this. RealityKit is still in early days. Material support in RealityKit is very limited right now. I think there are plans to change this in iOS 14 or beyond. There are comments in the documentation that describe features that don't yet exist such as Material protocol says "Describes a material (colors, shaders, textures, etc.) that define the look of a mesh part." There currently is no way to define custom shaders. If you look at the RealityKit framework bundle, there are shader graph definitions and new material features that are not yet exposed in the public API. I suspect there will be a shader graph editor, support for custom shaders, and double-sided materials coming.

It can be done, but it takes a change of perspective: instead of making a double sided material, you create a double sided mesh. This is accomplished by taking every part in every model and creating a double with the normals inverted (and the triangles reversed). Using the code below, the solution to the stated question becomes:
do {
let entity: ModelEntity = try .loadModel(named: "model.obj")
if let model = entity.model {
try model.mesh.addInvertedNormals()
// or alternatively, since this model isn't onscreen yet:
// model.mesh = try model.mesh.addingInvertedNormals()
var material = SimpleMaterial()
material.baseColor = try .texture(.load(named: "texture.png"))
model.materials = [material]
entity.model = model
}
} catch {}
And the entity will now display the material on both sides. Here is the code to do it:
import Foundation
import RealityKit
public extension MeshResource {
// call this to create a 2-sided mesh that will then be displayed
func addingInvertedNormals() throws -> MeshResource {
return try MeshResource.generate(from: contents.addingInvertedNormals())
}
// call this on a mesh that is already displayed to make it 2 sided
func addInvertedNormals() throws {
try replace(with: contents.addingInvertedNormals())
}
static func generateTwoSidedPlane(width: Float, depth: Float, cornerRadius: Float = 0) -> MeshResource {
let plane = generatePlane(width: width, depth: depth, cornerRadius: cornerRadius)
let twoSided = try? plane.addingInvertedNormals()
return twoSided ?? plane
}
}
public extension MeshResource.Contents {
func addingInvertedNormals() -> MeshResource.Contents {
var newContents = self
newContents.models = .init(models.map { $0.addingInvertedNormals() })
return newContents
}
}
public extension MeshResource.Model {
func partsWithNormalsInverted() -> [MeshResource.Part] {
return parts.map { $0.normalsInverted() }.compactMap { $0 }
}
func addingParts(additionalParts: [MeshResource.Part]) -> MeshResource.Model {
let newParts = parts.map { $0 } + additionalParts
var newModel = self
newModel.parts = .init(newParts)
return newModel
}
func addingInvertedNormals() -> MeshResource.Model {
return addingParts(additionalParts: partsWithNormalsInverted())
}
}
public extension MeshResource.Part {
func normalsInverted() -> MeshResource.Part? {
if let normals, let triangleIndices {
let newNormals = normals.map { $0 * -1.0 }
var newPart = self
newPart.normals = .init(newNormals)
// ordering of points in the triangles must be reversed,
// or the inversion of the normal has no effect
newPart.triangleIndices = .init(triangleIndices.reversed())
// id must be unique, or others with that id will be discarded
newPart.id = id + " with inverted normals"
return newPart
} else {
print("No normals to invert, returning nil")
return nil
}
}
}
So, calling addingInvertedNormals() creates a mesh that will show the same material on both sides. I have used this to create a 2-sided grid plane.
With a little extra work (left as an exercise!), you could give the created parts different material indexes, and show different materials on each side.

What you describe is called culling. Check MTLCullMode for example. From there you can jump to various points where you can set culling mode (you are interested in no culling).

Related

Complex ModelComponent on RealityKit Entity

I'm trying to use RealityKits Entity Component system to it's fullest extent but I'm having trouble fitting together a few pieces. Specifically around the HasModel Component.
In Reality Composer I make a simple model composed of three basic objects. I export it as a .USDZ file and drop it into my Xcode project.
I then load the model from disk like this:
guard let basicLabelFileURL = Bundle.main.url(forResource: "label", withExtension: "usdz") else {
fatalError("Could not find label file")
}
let basicLabel = try ModelEntity.loadModel(contentsOf: basicLabelFileURL)
I then have custom Entity called LabelEntity
class LabelEntity: Entity, HasAnchoring, HasModel {
required public init() {
super.init()
}
public init(entity: Entity) {
super.init()
self.model = ??? entity?
}
}
Which gets Initialized with the model from disk.
let newLabelEntity = LabelEntity(entity: basicLabel)
As you can see I wan't to make the model loaded from disk the ModelComponent of my Custom Entity. However the ModelComponent initalizer only accepts a single mesh and then array of material.
Where is my knowledge gap? How can I make a custom Entity using the ModelComponent from a complex hierarchy of meshes (other models)?

CIFilter does not answer the kCIAttributeDescription of a custom filter

I have created a custom CIFilter and registered it in the CIFilter class with the class func registerName method. The custom filter is created and works as expected.
However CIFilter does not answer the kCIAttributeDescription that was registered.
CIFilter.localizedDescription(forFilterName: ciFilterName) does not answer the string used in the registerName method.
If the custom filter is actually instantiated it does contain the attributeDescription.
All the Apple filters in the system do answer the localizedDescription without creating the filter. The user gets information about the filter to aid in the process of filter selection.
How can a custom filter achieve the same behavior?
Here is the registration code
CIFilter.registerName(kPRandom, constructor: PGLFilterConstructor(),
classAttributes: [
kCIAttributeFilterDisplayName : "Random Filters",
kCIAttributeFilterCategories :
[ kCICategoryStillImage,
kCICategoryTransition],
kCIAttributeDescription : "Swipe on stack Make command to add random filters to the stack. Select photos for random input with Parms Pick command"
]
)
p.s the custom filter is for use in the iPad "Wills Filter Tool" app on the App Store. Exploring the world of filters is pretty amazing so the Random filter generator turns out to be a good learning and experimental tool in applying filters.
p.p.s Swift playground that demos the issue
import UIKit
import CoreImage
// demonstrates that registering a custom filter does
// not register the kCIAttributeDescription
let kPRandom = "Random Filters"
let kCIDissolveFilter = "CIDissolveTransition"
class PGLFilterConstructor: CIFilterConstructor {
//MARK: CIFilterConstructor protocol
func filter(withName: String) -> CIFilter? {
return CIFilter(name: withName)
}
}
class PGLRandomFilterAction: CIFilter {
// filter that answers the input as the outputImage
// a pass thru filter
#objc dynamic var inputImage: CIImage?
class func register() {
CIFilter.registerName(kPRandom, constructor: PGLFilterConstructor(), classAttributes:
[
kCIAttributeFilterDisplayName : "Random Filters",
kCIAttributeFilterCategories :
[ kCICategoryStillImage,
kCICategoryTransition],
kCIAttributeDescription : "Swipe on stack Make command to add random filters to the stack. Select photos for random input with Parms Pick command"
]
)
}
override var outputImage: CIImage? {
get {
return inputImage }
}
}
PGLRandomFilterAction.register()
// added to CIFilter
let randomDescription = CIFilter.localizedDescription(forFilterName: kPRandom)
let dissolveDescription = CIFilter.localizedDescription(forFilterName: kCIDissolveFilter)
// randomDescription contains the filter name. Should be the string
// set in the kCIAttributeDescription line
// dissolveDescription contains the expected description
It's true that my PGLFilterConstructor is not descended from NSObject. Adding that and the #objc to the demo script does not correct the issue however.
It appears that localizedDescription is an overlooked behavior in the CIFilter area for custom registered filters. It's minor compared to all the other work going on in CoreImage so I don't think it's helpful to submit a bug report on the behavior.
So the solution is a work around.
The app customizes the UI controls based on the CIFilter.attributes with a wrapper class. Customization occurs in wrapper subclasses as appropriate to the type of filter.
For this issue add more customization in the wrapper subclasses.
In the superclass wrapper (PGLSourceFilter) return the standard description
class func localizedDescription(filterName: String) -> String {
// custom subclasses should override
guard let standardDescription = CIFilter.localizedDescription(forFilterName: filterName)
else { return filterName }
return standardDescription
}
Then the wrapper subclass can correct for the missing description
override class func localizedDescription(filterName: String) -> String {
// custom subclasses should override
return "Swipe 'Make' to add random filters. Select photos for random input on Parms 'Pick' command"
}

Binding model and view: how to observe object properties

I have a view structured like a form that creates a model object. I am trying to bind the form elements (UIControl) to the model properties, so that the views auto-update when their corresponding model property is changed, and the model update when the controls are changed (two way binding). The model can change without the view knowing because multiple views can be linked to one same model property.
Approach 1: Plain Swift
My problem is the following: to observe changes to the model properties, I tried to use KVO in Swift, and specifically the observe(_:changeHandler:) method.
class Binding<View: NSObject, Object: NSObject, ValueType> {
weak var object: Object?
weak var view: View?
var objectToViewObservation: NSKeyValueObservation?
var viewToObjectObservation: NSKeyValueObservation?
private var objectKeyPath: WritableKeyPath<Object, ValueType>
private var viewKeyPath: WritableKeyPath<View, ValueType>
init(betweenObject objectKeyPath: WritableKeyPath<Object, ValueType>,
andView viewKeyPath: WritableKeyPath<View, ValueType>) {
self.objectKeyPath = objectKeyPath
self.viewKeyPath = viewKeyPath
}
override func bind(_ object: Object, with view: View) {
super.bind(object, with: view)
self.object = object
self.view = view
// initial value from object to view
self.view![keyPath: viewKeyPath] = self.object![keyPath: objectKeyPath]
// object --> view
objectToViewObservation = object.observe(objectKeyPath) { _, change in
guard var view = self.view else {
// view doesn't exist anymore
self.objectToViewObservation = nil
return
}
guard let value = change.newValue else { return }
view[keyPath: self.viewKeyPath] = value
}
// view --> object
viewToObjectObservation = view.observe(viewKeyPath) { _, change in
guard var object = self.object else {
// object doesn't exist anymore
self.viewToObjectObservation = nil
return
}
guard let value = change.newValue else { return }
object[keyPath: self.objectKeyPath] = value
}
}
}
However some of the properties of my model have types CustomEnum, CustomClass, Bool?, and ClosedRange<Int>, and to use observe I had to mark them as #objc dynamic, which yielded the error:
Property cannot be marked #objc because its type cannot be represented in Objective-C
Approach 2: Using RxSwift rx.observe
I turned to RxSwift and the rx.observe method thinking I could work around this problem, but the same thing happened (at runtime this time).
// In some generic bridge class between the view and the model
func bind(to object: SomeObjectType) {
object.rx
.observe(SomeType.self, "someProperty")
.flatMap { Observable.from(optional: $0) }
.bind(to: self.controlProperty)
.disposed(by: disposeBag)
}
Approach 3: Using RxSwift BehaviorRelays?
This is my first experience with RxSwift, and I know I should be using BehaviorRelay for my model, however I don't want to change all my model properties as my model object is working with other framework. I could try to implement a bridge then, to transform model properties into BehaviorRelay, but I would come across the same problem: how to listen for model changes.
In this question, there were no answer as to how to listen for property changes without refactoring all model properties to RxSwift's Variable (currently deprecated).
Approach 4: Using didSet Swift property observer?
The didSet and willSet property observers in plain Swift would allow listening for changes, however this would require to mark all the properties in the model with these observers, which I find quite inconvenient, since my model object has a lot of properties. If there is a way to add these observers at runtime, this would solve my problem.
I believe that what I am trying to achieve is quite common, having a set of views that modify a model object, however I can't find a way to properly link the model to the view, so that both auto-update when needed.
Basically, I'm looking for an answer to one of the following questions:
Is there something I overlooked, is there a better way to achieve what I want?
or How to overcome the "Property cannot be marked #objc" problem?
or How to bridge my model object to BehaviorRelay without changing my model?
or How to add didSet observers at runtime?
You said:
I believe that what I am trying to achieve is quite common, having a set of views that modify a model object, however I can't find a way to properly link the model to the view, so that both auto-update when needed.
Actually it's not at all common. One idea you don't mention is to wrap your entire model into a Behavior Relay. Then the set of views can modify your model object.
Each of your views, in turn, can observe the model in the behavior relay and update accordingly. This is the basis of, for example, the Redux pattern.
You could also use your approach #3 and use property wrappers to make the code a bit cleaner:
#propertyWrapper
struct RxPublished<Value> {
private let relay: BehaviorRelay<Value>
public init(wrappedValue: Value) {
self.relay = BehaviorRelay(value: wrappedValue)
}
var wrappedValue: Value {
get { relay.value }
set { relay.accept(newValue) }
}
var projectedValue: Observable<Value> {
relay.asObservable()
}
}
But understand that the whole reason you are having this problem is not due to Rx itself, but rather due to the fact that you are trying to mix paradigms. You are increasing the complexity of your code. Hopefully, it's just a temporary increase during a refactoring.
Old Answer
You said you want to make it "so that the views auto-update when their corresponding model property is changed, and the model update when the controls are changed (two way binding)."
IMO, that way of thinking about the problem is incorrect. Better would be to examine each output independently of all other outputs and deal with it directly. In order to explain what I mean, I will use the example of converting °F to °C and back...
This sounds like a great reason to use 2-way binding but let's see?
// the chain of observables represents a view model
celsiusTextField.rx.text // • this is the input view
.orEmpty // • these next two convert
.compactMap { Double($0) } // the view into an input model
.map { $0 * 9 / 5 + 32 } // • this is the model
.map { "\($0)" } // • this converts the model into a view
.bind(to: fahrenheitTextField) // • this is the output view
.disposed(by: disposeBag)
fahrenheitTextField.rx.text
.orEmpty
.compactMap { Double($0) }
.map { ($0 - 32) * 5 / 9 }
.map { "\($0)" }
.bind(to: celsiusTextField.rx.text)
.disposed(by: disposeBag)
The above code handles the two-way communication between the text fields without two-way binding. It does this by using two separate view models (The view model is the code between the text Observable and the text Observer as described in the comments.)
We can see a lot of duplication. We can DRY it up a bit:
extension ControlProperty where PropertyType == String? {
func viewModel(model: #escaping (Double) -> Double) -> Observable<String> {
orEmpty
.compactMap { Double($0) }
.map(model)
.map { "\($0)" }
}
}
You may prefer a different error handling strategy than what I used above. I was striving for simplicity since this is an example.
The key though is that each observable chain should be centered on a particular effect. It should combine all the causes that contribute to that effect, implement some sort of logic on the inputs, and then emit the needed output for that effect. If you do this to each output individually you will find that you don't need two-way binding at all.

How to approach a task: Inquiry with Choices and Responses (Engine)

ANSWERED! I posted an answer with code that I modified from Arbitur. Thanks to Phillip as well for the key advice that lead me to redirect my searches.
TLDR:
is there a way to run a function from a string? Like RunFunction("myfunction_" + number + "()"), or, is a way to hardcode like myfunction_\&number() ?
I'm Justin and this is my first post. I've worked hard the past 2 months to not ask a question, but I'm finally breaking bad. I'll try to be brief but I'm terribly wordy (and this long intro doesn't help). Appreciation in advance for your patience.
Basically, I'm making an engine that transforms simple words and phrases into Swift code; the goal of this is to have something to quickly make Text games, educational apps, and anything involving a non-linear set of questions / answers that change based on how you respond.
Now, I'm working on what I thought would be an easy part: storing the actual text for the Labels and Buttons (I'm working in Xcode / iOS atm). I decided to model it like film-makers / novelists do: a movie is composed of Scenes which are composed of Clips. You can also think of this as Chapters / Paragraphs
So (not actual code):
App = (Scene 1 -> Scene 2 -> Scene 3, etc)
Scene 1 = (Clip 1 -> Clip 2 -> Clip 3, etc)
Scene 2 = Clip 2 -> Clip 5 -> Clip 1, based on responses
....And so on.
The way I think (and program), I tend to avoid Classes as much as possible, outside of being strictly data or strictly function structures.
So, my first solution was to create a struct (or class) with nested functions:
struct SceneList {
//fScene calls, and passes the clip# from the button click
func fPlayClip(clipNUM: Int)
{
//Do logic stuff, then play the correct clip
}
//Called from button clicks on main screen; passes in clip#
func fScene1(play_clip clipNumber: Int){
//sub functions(S1)
func fClip1(){ print("I'm scene 1, clip 1")}
func fClip2(){ print ("I'm scene 1, clip 2")}
fPlayClip(clipNumber)
}
func fScene2(play_clip clipNumber: Int){
//sub functions(S2)
func fClip1(){ print("Yo, scene 2, clip 1")}
func fClip2(){ print ("Yo, scene 2, clip 2")}
fPlayClip(clipNumber)
}
}
Unfortunately, this design failed because there is no way for me to call sub-functions from fPlayClip(), so I took another approach:
//////////////////////////
/////SceneList.swift//////
//////////////////////////
struct Scene1{
func fClip1(){ print("I'm scene 1, clip 1")}
func fClip2(){ print ("I'm scene 1, clip 2")}}
struct Scene2{
func fClip1(){ print("Yo, scene 2, clip 1")}
func fClip2(){ print ("Yo, scene 2, clip 2")}}
//////////////////////////
////////Main.swift////////
//////////////////////////
// Implemention of the structs / clips.
// (playScene is called in button click method.)
func playScene(/*button prams go here*/){
// Switch(){ case desired: // Solve for X,
var oScenePlayer = SceneX() // where X is the desired scene
// Switch(){ case desired: // Solve for Z,
oScenePlayer.fClipZ() // where Z is the desired clip
}
Again, this failed because I couldn't use just one object, [oScenePlayer], because each struct was a different type.
I then went around for a while trying to figure out a way to destroy the object, then recreate it, but I couldn't do that. Then, I looked around for a way to reassign one object to another class type, but couldn't. Then, I peered into the extensions, protocols, generic types, etc, trying to figure out a way to extend / inherent to get this to work, but failed.
So now, my final two /working/ solutions, are to just create a new object on scene changes, and wait for ARC or whatever to destroy the old one; or, for me to go back to the first example, and simply embed [fPlayScene] into every function:
//SOLUTION 1:
struct SceneList {
func fScene1(play_clip clipNumber: Int){
//sub functions(S1)
func fClip1(){ print("I'm scene 1, clip 1")}
func fClip2(){ print ("I'm scene 1, clip 2")}
//Do logic stuff below, solving for Y
case desired:
fClipY()
}
}
//SOLUTION 2:
//////////////////////////
/////SceneList.swift//////
//////////////////////////
struct Scene1{
func fClip1(){ print("I'm scene 1, clip 1")}
func fClip2(){ print ("I'm scene 1, clip 2")}}
}
//And so on...
//////////////////////////
////////Main.swift////////
//////////////////////////
//////////////////////////
// Inside the globalish area:
let oScene1: Scene1,
oScene2: Scene2
//And so on...
var global_next_scene = 1
var global_next_clip = 1
/////////////////////////////////
// Inside the button call method:
func playScene(next_scene: Int, next_clip: Int){
switch(next_scene){ //Find the scene
case 1:
oScene1 = Scene1()
switch(next_clip){ //Play the clip
case 1:
oScene1.fClip1()
}
}
Basically, I feel that I'm using WAY too many switch statements, and in way too many places, (there could be hundreds of scenes and thousands of clips), when something as simple as a RunFunction("fClip" + next_clip + "()") would work, but I don't know of any 'execute a swift command from a string' functions :[, or something where I could hardcode like fClip\&next_clip\() which I think was possible in c++ 15 years ago when I last programmed anything
I came up with a few other crazy ideas to implement this, I even thought about OOP (shudder) embedded classes and having member variables hold instances of the subclasses, but with my current knowledge / resources I can't find a simpler way to do this than the last 2 snippets.
All of the structs, member functions, switch statements, etc, are going to be auto-generated via my engine--so it's not that I'm looking for a faster way to do this, it just seems like an inefficient / taxing way to do it.
Any guidance would be appreciated. Thanks much in advance, and I'm surprised it's taken me this long to need to ask a question here ^-^()
Peace and blessings
It seems to me that you're mixing what is naturally data into your code. That is, you're creating functions that know which element they're responsible for...and that's what leads to all the duplication you're wondering about.
Perhaps there are parts of the problem I don't understand, but why not have a structure such as:
class Clip {
func play() {
print("Playing \(self)")
}
}
class Scene {
var clips = [Clip]()
func play() {
print("Playing \(self)")
for aClip in clips {
aClip.play()
}
}
func playClip(number: Int) {
if number >= 0 && number < clips.count {
clips[number].play()
}
}
}
class SceneList {
var scenes = [Scene]()
func play() {
print("Playing \(self)")
for aScene in scenes {
aScene.play()
}
}
func playScene(number: Int) {
if number >= 0 && number < scenes.count {
scenes[number].play()
}
}
}
var list = SceneList()
for _ in 0..<2 {
var scene = Scene()
list.scenes.append(scene)
for _ in 0..<3 {
scene.clips.append(Clip())
}
}
list.play()
Add whatever other properties and logic you like but the main point is to turn those numbered functions into generic functions with data references.
Phillip's answer inspired me to look more into arrays and separating data from functions, and indeed what I was looking for was available closures--I didn't know that you could call a closure simply from stating (unwrapping) a variable :) What a neat language!
This works perfectly, and now I don't need any classes or structs ^-^() (all the better!!)
//initializer
var clips_for_scene = [
1: { print("initialized") }
]
//SceneList
var scenes = [
1: { clips_for_scene = [
1: { print("scene 1, clip 1") },
2: { print("s1, c2") }]},
2: { clips_for_scene = [
1: { print("scene 2, clip 1") },
2: { print("s2, c2") }]}]
func playClip(which_scene: Int, _ which_clip:Int){
scenes[which_scene]?()
clips_for_scene[which_clip]?()
}
playClip(1, 1)
playClip(1, 2)
playClip(2, 1)
playClip(2, 2)
//output:
//scene 1, clip 1
//s1, c2
//scene 2, clip 1
//s2, c2
Thanks to Arbitur as well, as their answer on a separate question:
https://stackoverflow.com/a/30286375/6593818

Preloading sprite kit textures

So I was reading the apple documentation for best sprite kit practices. I came across this:
For example, if your game uses the same textures for all its gameplay, you might create a special loading class that runs once at startup. You perform the work of loading the textures once, and then leave them in memory. If a scene object is deleted and recreated to restart gameplay, the textures do not need to be reloaded.
And this would significantly help performance in my application. Can someone point me in the right direction to how I would go about achieving this?
I presume I would call a function to load up texture's in my View Controller? And then access that texture atlas?
The thing is, do you really want to cache the resources like that? Can't say I ever found a need for something of that nature. Anyways, if doing that somehow helps with your app's performance, then you can make a TextureManager class which would be a singleton (create separate file for TextureManager class), like this:
class TextureManager{
private var textures = [String:SKTexture]()
static let sharedInstance = TextureManager()
private init(){}
func getTexture(withName name:String)->SKTexture?{ return textures[name] }
func addTexture(withName name:String, texture :SKTexture){
if textures[name] == nil {
textures[name] = texture
}
}
func addTextures(texturesDictionary:[String:SKTexture]) {
for (name, texture) in texturesDictionary {
addTexture(withName: name, texture: texture)
}
}
func removeTexture(withName name:String)->Bool {
if textures[name] != nil {
textures[name] = nil
return true
}
return false
}
}
Here you are using dictionary and associate each texture with its name. Pretty simple concept. If there isn't a texture with the same name in a dictionary, then add it. Just beware of premature optimization.
The usage:
//I used didMoveToView in this example, but more appropriate would be to use something before this method is called, like viewDidLoad, or doing this inside off app delegate.
override func didMoveToView(view: SKView) {
let atlas = SKTextureAtlas(named: "game")
let texture = atlas.textureNamed("someTexture1")
let dictionary = [
"someTexture2": atlas.textureNamed("someTexture2"),
"someTexture3": atlas.textureNamed("someTexture3"),
"someTexture4": atlas.textureNamed("someTexture4"),
]
TextureManager.sharedInstance.addTexture(withName: "someTexture", texture: texture)
TextureManager.sharedInstance.addTextures(dictionary)
}
As I said, you have to put TextureManager implementation in a separate file, to make it real singleton. Otherwise, if you define it in GameScene for example, you will be able to call that private init, and then TextureManager will not be a real singleton.
So, with this code you can create some textures at the very beginning of the app lifecycle, like it is said in the docs:
For example, if your game uses the same textures for all its gameplay,
you might create a special loading class that runs once at startup.
and fill the dictionary with them. Later on, whenever you need a texture, you will not use atlas.textureNamed() method, but rather load it from a dictionary property of a TextureManager class. Also, when transitioning between scenes, that dictionary will survive scene's deinits, and will persist while app is alive.

Resources