Skip to content

Commit

Permalink
Move some stuff from GLLItem to Swift
Browse files Browse the repository at this point in the history
CoreData with Swift is terrible (all the ordered sets). SwiftData is not an option (I need macOS 13 and also subclasses). Fun, fun, fun, so let's defer it for now.
  • Loading branch information
cochrane committed May 5, 2024
1 parent d2950b4 commit c760277
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 131 deletions.
4 changes: 4 additions & 0 deletions GLLara.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@
526E0FAB1C169E3500F198BF /* testStaticTRUDiffuse.png in Resources */ = {isa = PBXBuildFile; fileRef = 526E0F9A1C169E3500F198BF /* testStaticTRUDiffuse.png */; };
526E0FAC1C169E3500F198BF /* testStaticTRUDiffuseLightmap.png in Resources */ = {isa = PBXBuildFile; fileRef = 526E0F9B1C169E3500F198BF /* testStaticTRUDiffuseLightmap.png */; };
5272709C2BE77A7D00EE52B5 /* GLLTexture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5272709A2BE600C300EE52B5 /* GLLTexture.swift */; };
527270A32BE7E0F100EE52B5 /* GLLItem+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 527270A12BE7E0F100EE52B5 /* GLLItem+Extensions.swift */; };
5274446427FCC9C100E5A3FD /* GLLModelMesh.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5274446327FCC9C100E5A3FD /* GLLModelMesh.swift */; };
5274446627FD64F000E5A3FD /* GLLModelMeshObj.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5274446527FD64F000E5A3FD /* GLLModelMeshObj.swift */; };
5274446827FD6F7F00E5A3FD /* GLLVertexAttribAccessorSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5274446727FD6F7F00E5A3FD /* GLLVertexAttribAccessorSet.swift */; };
Expand Down Expand Up @@ -669,6 +670,7 @@
526E0F9A1C169E3500F198BF /* testStaticTRUDiffuse.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = testStaticTRUDiffuse.png; sourceTree = "<group>"; };
526E0F9B1C169E3500F198BF /* testStaticTRUDiffuseLightmap.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = testStaticTRUDiffuseLightmap.png; sourceTree = "<group>"; };
5272709A2BE600C300EE52B5 /* GLLTexture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GLLTexture.swift; sourceTree = "<group>"; };
527270A12BE7E0F100EE52B5 /* GLLItem+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GLLItem+Extensions.swift"; sourceTree = "<group>"; };
5274446327FCC9C100E5A3FD /* GLLModelMesh.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GLLModelMesh.swift; sourceTree = "<group>"; };
5274446527FD64F000E5A3FD /* GLLModelMeshObj.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GLLModelMeshObj.swift; sourceTree = "<group>"; };
5274446727FD6F7F00E5A3FD /* GLLVertexAttribAccessorSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GLLVertexAttribAccessorSet.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1345,6 +1347,7 @@
5296941F15F4112E00DF2FA3 /* Scene members */ = {
isa = PBXGroup;
children = (
527270A12BE7E0F100EE52B5 /* GLLItem+Extensions.swift */,
52B6C53A2BE5645B005E53CE /* GLLItemMeshTexture.swift */,
52AF115415FCD4A000DF2565 /* GLLCameraTarget.h */,
52AF115515FCD4A000DF2565 /* GLLCameraTarget.m */,
Expand Down Expand Up @@ -1907,6 +1910,7 @@
52B6C53C2BE56696005E53CE /* GLLItemMeshTexture.swift in Sources */,
52C9F6201600CCFC003272E1 /* GLLFloatRenderParameter.m in Sources */,
52C9F6231600CCFC003272E1 /* GLLColorRenderParameter.m in Sources */,
527270A32BE7E0F100EE52B5 /* GLLItem+Extensions.swift in Sources */,
52D3D72916029BD9006CB743 /* GLLRenderAccessoryViewController.m in Sources */,
52CE280C1605FE98005A86E9 /* GLLLogarithmicValueTransformer.m in Sources */,
5275F6821607293000978779 /* GLLItemController.m in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion GLLara/GLLDocument.m
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ - (GLLItem *)addImagePlane:(NSURL *)url error:(NSError *__autoreleasing*)error {
// Set up shader and texture
// Note: Texture set up via URL; it will do the lookup to create the instance on its own.
newItem.model = model;
GLLItemMesh *mesh = [newItem itemMeshForModelMesh:model.meshes[0]];
GLLItemMesh *mesh = [newItem itemMeshFor:model.meshes[0]];
GLLItemMeshTexture *onlyTexture = mesh.textures.anyObject;
onlyTexture.textureURL = url;
newItem.displayName = url.lastPathComponent;
Expand Down
96 changes: 96 additions & 0 deletions GLLara/GLLItem+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//
// GLLItem+Extensions.swift
// GLLara
//
// Created by Torsten Kammer on 05.05.24.
// Copyright © 2024 Torsten Kammer. All rights reserved.
//
//

import Foundation

extension GLLItem {
@objc var rootBones: [GLLItemBone] {
return bones.filter({
($0 as! GLLItemBone).parent == nil
}).map { $0 as! GLLItemBone }
}

@objc func itemMesh(for modelMesh: GLLModelMesh) -> GLLItemMesh? {
return meshes![modelMesh.meshIndex] as? GLLItemMesh
}

@objc func bone(name: String) -> GLLItemBone? {
return bones.first(where: { ($0 as! GLLItemBone).bone.name == name }) as? GLLItemBone
}

@objc var rootItem: GLLItem {
if let parent {
return parent.rootItem
}
return self
}

@objc var hasOptionalParts: Bool {
return meshes?.first(where: { ($0 as! GLLItemMesh).mesh.optionalPartNames.count > 0 }) != nil
}

@objc func loadPose(url: URL) throws {
let text = try String(contentsOf: url)
try loadPose(description: text)
}

@objc func loadPose(description: String) throws {
let lines = description.components(separatedBy: CharacterSet.newlines)
if description.firstIndex(of: ":") == nil {
// Old-style loading: Same number of lines as bones, sequentally stored, no names.
if (lines.count != bones.count) {
throw NSError(domain: "poses", code: 1, userInfo:[
NSLocalizedDescriptionKey : NSLocalizedString("Pose file does not contain the right amount of bones", comment: "error loading pose old-style"),
NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("Poses in the old format have to contain exactly as many items as bones. Try using a newer pose.", comment: "error loading pose old-style")]);
}

for i in 0 ..< lines.count {
let scanner = Scanner(string: lines[i])
if let x = scanner.scanFloat() {
(bones[i] as! GLLItemBone).rotationX = x
}
if let y = scanner.scanFloat() {
(bones[i] as! GLLItemBone).rotationY = y
}
if let z = scanner.scanFloat() {
(bones[i] as! GLLItemBone).rotationZ = z
}
}
} else {
for line in lines {
let scanner = Scanner(string: line)
guard let name = scanner.scanUpToString(":") else {
continue
}
_ = scanner.scanString(":")
guard let bone = bones.first(where: { ($0 as! GLLItemBone).bone.name == name }) as? GLLItemBone else {
continue
}
if let x = scanner.scanFloat() {
bone.rotationX = x * Float.pi / 180.0
}
if let y = scanner.scanFloat() {
bone.rotationY = y * Float.pi / 180.0
}
if let z = scanner.scanFloat() {
bone.rotationZ = z * Float.pi / 180.0
}
if let x = scanner.scanFloat() {
bone.positionX = x
}
if let y = scanner.scanFloat() {
bone.positionY = y
}
if let z = scanner.scanFloat() {
bone.positionZ = z
}
}
}
}
}
14 changes: 0 additions & 14 deletions GLLara/GLLItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,23 +65,9 @@ typedef NS_ENUM(int16_t, GLLItemChannelAssignment)

@property (nonatomic, retain) GLLModel *model;

@property (nonatomic, retain, readonly) NSArray<GLLItemBone *> *rootBones;

- (GLLItemMesh *)itemMeshForModelMesh:(GLLModelMesh *)mesh;

@property (nonatomic, readonly) mat_float16 modelTransform;

@property (nonatomic, readonly) GLLItem *rootItem;

// Whether some meshes together form optional parts.
@property (nonatomic, readonly) BOOL hasOptionalParts;

// Poses
- (BOOL)loadPoseFrom:(NSURL *)poseUrl error:(NSError *__autoreleasing*)error;
- (BOOL)loadPose:(NSString *)poseDescription error:(NSError *__autoreleasing*)error;

// Bones
- (GLLItemBone *)boneForName:(NSString *)name;
- (NSOrderedSet<GLLItemBone *> *)combinedBones;
- (NSOrderedSet<GLLItemBone *> *)combinedUsedBones;

Expand Down
111 changes: 1 addition & 110 deletions GLLara/GLLItem.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
@interface GLLItem ()
{
NSOrderedSet* cachedCombinedBones;
id childItemsObserver;
}

- (void)_standardSetValue:(id)value forKey:(NSString *)key;
Expand Down Expand Up @@ -204,7 +203,7 @@ - (void)setModel:(GLLModel *)model
[bones removeAllObjects];
for (NSUInteger i = 0; i < model.bones.count; i++)
{
GLLItemBone *parentsBone = [self.parent boneForName:[model.bones[i] name]];
GLLItemBone *parentsBone = [self.parent boneWithName:[model.bones[i] name]];
if (parentsBone)
[bones addObject:parentsBone];
else
Expand Down Expand Up @@ -259,25 +258,6 @@ - (void)setModel:(GLLModel *)model

#pragma mark - Derived

- (NSArray<GLLItemBone *> *)rootBones
{
NSIndexSet *indices = [self.bones indexesOfObjectsPassingTest:^BOOL(GLLItemBone *bone, NSUInteger idx, BOOL *stop) {
return !bone.parent;
}];
return [self.bones objectsAtIndexes:indices];
}

- (GLLItemMesh *)itemMeshForModelMesh:(GLLModelMesh *)mesh;
{
return self.meshes[mesh.meshIndex];
}

- (GLLItemBone *)boneForName:(NSString *)name;
{
return [self.bones firstObjectMatching:^BOOL(GLLItemBone *bone) {
return [bone.bone.name isEqual:name];
}];
}
- (NSOrderedSet<GLLItemBone *> *)combinedBones;
{
if (cachedCombinedBones) {
Expand Down Expand Up @@ -305,22 +285,6 @@ - (GLLItemBone *)boneForName:(NSString *)name;
return combinedBones;
}

- (GLLItem *)rootItem
{
if (self.parent)
return self.parent.rootItem;
else
return self;
}

- (BOOL)hasOptionalParts {
for (GLLItemMesh *mesh in self.meshes) {
if (mesh.mesh.optionalPartNames.count > 0)
return YES;
}
return NO;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqual:@"childItems"] && object == self) {
cachedCombinedBones = nil;
Expand All @@ -329,79 +293,6 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
}
}

#pragma mark - Poses I/O

- (BOOL)loadPoseFrom:(NSURL *)poseUrl error:(NSError *__autoreleasing*)error; {
NSString *poseDescription = [NSString stringWithContentsOfURL:poseUrl usedEncoding:NULL error:error];

if (!poseDescription) {
return NO;
}

return [self loadPose:poseDescription error:error];
}

- (BOOL)loadPose:(NSString *)poseDescription error:(NSError *__autoreleasing*)error
{
NSArray *lines = [poseDescription componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
if ([poseDescription rangeOfString:@":"].location == NSNotFound)
{
// Old-style loading: Same number of lines as bones, sequentally stored, no names.
if (lines.count != self.bones.count)
{
if (error)
*error = [NSError errorWithDomain:@"poses" code:1 userInfo:@{
NSLocalizedDescriptionKey : NSLocalizedString(@"Pose file does not contain the right amount of bones", @"error loading pose old-style"),
NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"Poses in the old format have to contain exactly as many items as bones. Try using a newer pose.", @"error loading pose old-style")}];
return NO;
}

for (NSUInteger i = 0; i < lines.count; i++)
{
NSScanner *scanner = [NSScanner scannerWithString:lines[i]];
float x = 0, y = 0, z = 0;
if ([scanner scanFloat:&x])
[self.bones[i] setRotationX:x];
if ([scanner scanFloat:&y])
[self.bones[i] setRotationY:y];
if ([scanner scanFloat:&z])
[self.bones[i] setRotationZ:z];
}
}
else
{
for (NSString *line in lines)
{
if (line.length == 0) continue; // May insert empty lines due to Windows line endings.

NSScanner *scanner = [NSScanner scannerWithString:line];
NSString *name;
[scanner scanUpToString:@":" intoString:&name];
[scanner scanString:@":" intoString:NULL];

NSIndexSet *indices = [self.bones indexesOfObjectsPassingTest:^BOOL(GLLItemBone *bone, NSUInteger idx, BOOL *stop) {
return [bone.bone.name isEqual:name];
}];
if (indices.count == 0)
{
NSLog(@"Skipping unknown bone %@", name);
continue;
}
GLLItemBone *transform = self.bones[indices.firstIndex];

float x = 0, y = 0, z = 0;
if ([scanner scanFloat:&x]) transform.rotationX = x * M_PI / 180.0;
if ([scanner scanFloat:&y]) transform.rotationY = y * M_PI / 180.0;
if ([scanner scanFloat:&z]) transform.rotationZ = z * M_PI / 180.0;

if ([scanner scanFloat:&x]) transform.positionX = x;
if ([scanner scanFloat:&y]) transform.positionY = y;
if ([scanner scanFloat:&z]) transform.positionZ = z;
}
}
return YES;
}

#pragma mark - Private methods

- (void)_standardSetValue:(id)value forKey:(NSString *)key;
Expand Down
1 change: 1 addition & 0 deletions GLLara/GLLItemController.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#import "GLLOptionalPartController.h"
#import "GLLSubItemController.h"
#import "NSArray+Map.h"
#import "GLLara-Swift.h"

@interface GLLItemController ()
// How many controllers for specific topics (as opposed to child items) there
Expand Down
2 changes: 1 addition & 1 deletion GLLara/GLLItemDragDestination.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import UniformTypeIdentifiers
guard let item = itemForPose else {
throw NSError(domain: "Pasteboard", code: 0)
}
try item.loadPose(from: url)
try item.loadPose(url: url)
} else {
try document.addModel(at: url)
}
Expand Down
2 changes: 1 addition & 1 deletion GLLara/GLLItemDrawer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class GLLItemDrawer {
try throwingRunAndBlock {
let drawData = try await sceneDrawer.resourceManager.drawDataAsync(model: item.model)
for meshData in drawData.meshDrawData {
let meshState = try GLLItemMeshState(itemDrawer: self, meshData: meshData, itemMesh: item.itemMesh(for: meshData.modelMesh))
let meshState = try GLLItemMeshState(itemDrawer: self, meshData: meshData, itemMesh: item.itemMesh(for: meshData.modelMesh)!)
self.meshStates.append(meshState)
}
await withTaskGroup(of: Void.self) { taskGroup in
Expand Down
2 changes: 1 addition & 1 deletion GLLara/GLLItemViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ - (void)loadPose:(id)sender
self.undoManager.actionName = NSLocalizedString(@"Load pose", @"load pose undo action name");
for (GLLItem *item in self.selectedItems)
{
if (![item loadPose:file error:&error])
if (![item loadPoseWithDescription:file error:&error])
{
[self.view.window presentError:error];
return;
Expand Down
2 changes: 1 addition & 1 deletion GLLara/GLLSkeletonDrawer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class GLLSkeletonDrawer: NSObject {
selection.removeAll()
selectionObservers.removeAll()
for bone in newValue {
let root = bone.item.root!
let root = bone.item.rootItem
if let existing = selection[root] {
selection[root] = existing + [bone]
} else {
Expand Down
4 changes: 2 additions & 2 deletions GLLara/GLLView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ import Combine
} else {
// If root bone: Either next root bone or next model
let item = lastSelectedBone.item!
let rootBones = item.rootBones!
let rootBones = item.rootBones
if let index = rootBones.firstIndex(of: lastSelectedBone) {
let nextIndex = (index + 1) % rootBones.count
return rootBones[nextIndex]
Expand Down Expand Up @@ -414,7 +414,7 @@ import Combine
} else {
// If root bone: Either next root bone or next model
let item = firstSelectedBone.item!
let rootBones = item.rootBones!
let rootBones = item.rootBones
if let index = rootBones.firstIndex(of: firstSelectedBone) {
let nextIndex = (index + rootBones.count - 1) % rootBones.count
return item.rootBones[nextIndex]
Expand Down

0 comments on commit c760277

Please sign in to comment.