-
Notifications
You must be signed in to change notification settings - Fork 6
/
GLLModelBone.m
161 lines (127 loc) · 7.44 KB
/
GLLModelBone.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//
// GLLModelBone.m
// GLLara
//
// Created by Torsten Kammer on 01.09.12.
// Copyright (c) 2012 Torsten Kammer. All rights reserved.
//
#import "GLLModelBone.h"
#import "GLLASCIIScanner.h"
#import "GLLModel.h"
#import "simd_matrix.h"
#import "TRInDataStream.h"
#import "TROutDataStream.h"
@implementation GLLModelBone
- (instancetype)initWithModel:(GLLModel *)model;
{
if (!(self = [super init])) return nil;
_model = model;
_parentIndex = UINT16_MAX;
_children = @[];
_positionX = 0;
_positionY = 0;
_positionZ = 0;
_positionMatrix = simd_mat_identity();
_inversePositionMatrix = simd_mat_identity();
_name = NSLocalizedString(@"Root bone", @"Only bone in a boneless format");
return self;
}
- (instancetype)initFromSequentialData:(id)stream partOfModel:(GLLModel *)model atIndex:(NSUInteger)index error:(NSError *__autoreleasing*)error;
{
if (!(self = [super init])) return nil;
if (![stream isValid])
{
if (error)
*error = [NSError errorWithDomain:GLLModelLoadingErrorDomain code:GLLModelLoadingError_PrematureEndOfFile userInfo:@{
NSLocalizedDescriptionKey : NSLocalizedString(@"The file is missing some data.", @"Premature end of file error"),
NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The file breaks off in the middle of the bones section. Maybe it is damaged?", @"Premature end of file error")
}];
return nil;
}
_model = model;
_name = [stream readPascalString];
_parentIndex = [stream readUint16];
_positionX = [stream readFloat32];
_positionY = [stream readFloat32];
_positionZ = [stream readFloat32];
if (_parentIndex == index) {
if ([_name hasPrefix:@"unused"]) {
// Apparently that's a thing that people do. Create unused bones with themselves set as parent. Why, though? What's wrong with them?
NSLog(@"Bone %lu (named \"%@\") has itself as parent. Unused, so set as root bone. Why do people do that?", index, _name);
_parentIndex = UINT16_MAX;
} else {
if (error)
*error = [NSError errorWithDomain:GLLModelLoadingErrorDomain code:GLLModelLoadingError_CircularReference userInfo:@{
NSLocalizedDescriptionKey : [NSString stringWithFormat:NSLocalizedString(@"Bone \"%@\" has itself as an ancestor.", @"Found a circle in the bone relationships."), self.name],
NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The bones would form an infinite loop.", @"Found a circle in a bone relationship")}];
return nil;
}
}
if (![stream isValid])
{
if (error)
*error = [NSError errorWithDomain:GLLModelLoadingErrorDomain code:GLLModelLoadingError_PrematureEndOfFile userInfo:@{
NSLocalizedDescriptionKey : NSLocalizedString(@"The file is missing some data.", @"Premature end of file error"),
NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The file breaks off in the middle of the bones section. Maybe it is damaged?", @"Premature end of file error")
}];
return nil;
}
_positionMatrix = simd_mat_positional(simd_make(_positionX, _positionY, _positionZ, 1.0f));
_inversePositionMatrix = simd_mat_positional(simd_make(-_positionX, -_positionY, -_positionZ, 1.0f));
return self;
}
- (GLLModelBone *)parent
{
if (self.parentIndex >= self.model.bones.count) return nil;
return self.model.bones[self.parentIndex];
}
#pragma mark - Finishing loading
- (BOOL)findParentsAndChildrenError:(NSError *__autoreleasing*)error;
{
if (self.parentIndex != UINT16_MAX && self.parentIndex >= self.model.bones.count)
{
if (error)
*error = [NSError errorWithDomain:GLLModelLoadingErrorDomain code:GLLModelLoadingError_IndexOutOfRange userInfo:@{
NSLocalizedDescriptionKey : [NSString stringWithFormat:NSLocalizedString(@"Parent of bone \"%@\" does not exist.", @"The parent index of this bone is invalid."), self.name],
NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"All bones have to have a parent that exists or no parent at all.", @"The parent index of this bone is invalid.")
}];
return NO;
}
GLLModelBone *parent = self.parent;
NSMutableSet *encounteredBones = [NSMutableSet setWithObject:self];
while (parent != nil)
{
if ([encounteredBones containsObject:parent])
{
if (error)
*error = [NSError errorWithDomain:GLLModelLoadingErrorDomain code:GLLModelLoadingError_CircularReference userInfo:@{
NSLocalizedDescriptionKey : [NSString stringWithFormat:NSLocalizedString(@"Bone \"%@\" has itself as an ancestor.", @"Found a circle in the bone relationships."), self.name],
NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The bones would form an infinite loop.", @"Found a circle in a bone relationship")}];
return NO;
}
[encounteredBones addObject:parent];
parent = parent.parent;
}
_children = [self.model.bones filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"parent == %@", self]];
return YES;
}
#pragma mark - Export
- (NSString *)writeASCII;
{
NSMutableString *result = [NSMutableString string];
[result appendFormat:@"%@\n", self.name];
[result appendFormat:@"%d\n", self.parentIndex != NSNotFound ? (int) self.parentIndex : -1];
[result appendFormat:@"%f %f %f\n", self.positionX, self.positionY, self.positionZ];
return [result copy];
}
- (NSData *)writeBinary;
{
TROutDataStream *stream = [[TROutDataStream alloc] init];
[stream appendPascalString:self.name];
[stream appendUint16:(uint16_t) self.parentIndex];
[stream appendFloat32:self.positionX];
[stream appendFloat32:self.positionY];
[stream appendFloat32:self.positionZ];
return stream.data;
}
@end