-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSceneKitExample.m
345 lines (273 loc) · 12.6 KB
/
SceneKitExample.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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#import "SceneKitExample.h"
#import <SceneKit/SceneKit.h>
#import <Metal/Metal.h>
#import <MapboxMaps/MapboxMaps.h>
#import <MapboxCoreMaps/MapboxCoreMaps.h>
#import <MapboxMapObjC/MapboxMapObjC.h>
#import "MapboxMaps-Swift.h"
#import "ExampleProtocol.h"
typedef void (^RenderingWillEndHandler)(void);
@interface SceneKitExampleCustomLayerHost : NSObject<MBMCustomLayerHost>
@property (readonly) CLLocationCoordinate2D modelOrigin;
@property SCNRenderer* renderer;
@property SCNScene* scene;
@property SCNNode* modelNode;
@property SCNNode* cameraNode;
@property SCNNode* textNode;
@property BOOL useCPUOcclusion;
@property (readonly) RenderingWillEndHandler renderingWillEndHandler;
- (instancetype)initWithModelOrigin: (CLLocationCoordinate2D) modelOrigin
renderingWillEndHandler: (RenderingWillEndHandler) renderingWillEndHandler;
@end
@interface SceneKitExample () <ExampleProtocol>
@property (readonly) CLLocationCoordinate2D modelOrigin;
@property MapView* mapView;
- (void) addModelAndTerrain;
@end
@implementation SceneKitExample
- (void)viewDidLoad {
[super viewDidLoad];
_modelOrigin = CLLocationCoordinate2DMake(-35.39847, 148.9819);
TMBCameraOptions* cameraOptions = [[TMBCameraOptions alloc] initWithCenter:_modelOrigin padding:UIEdgeInsetsMake(0, 0, 0, 0) anchor:CGPointMake(0, 0) zoom:18 bearing:180 pitch:60];
MapInitOptions* options = [MapInitOptionsFactory createWithMapOptions:nil cameraOptions:cameraOptions styleURI:nil styleJSON:nil antialiasingSampleCount:1];
MapView* mapView = [MapViewFactory createWithFrame:self.view.bounds
options:options];
mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
__weak SceneKitExample *weakSelf = self;
[[mapView mapboxMap] onStyleLoaded:^(id _Nonnull) {
[weakSelf addModelAndTerrain];
if ([weakSelf respondsToSelector:@selector(finish)]) {
[weakSelf finish];
}
}];
self.mapView = mapView;
[self.view addSubview:mapView];
}
- (TMBSkyLayer *) createSkyLayer {
TMBSkyLayer* layer = [[TMBSkyLayer alloc] initWithId:@"sky-layer"];
layer.skyType = [TMBValue skyType:TMBSkyType.atmosphere];
layer.skyAtmosphereSun = [TMBValue constant: @[@0.0, @0.0]];
layer.skyAtmosphereSunIntensity = [TMBValue constant: @15.0];
return layer;
}
- (void) addModelAndTerrain {
__weak SceneKitExample *weakSelf = self;
SceneKitExampleCustomLayerHost* layerHost = [[SceneKitExampleCustomLayerHost alloc] initWithModelOrigin:self.modelOrigin
renderingWillEndHandler:^{
if ([weakSelf respondsToSelector:@selector(finish)]) {
[weakSelf finish];
}
}];
[[self.mapView mapboxMap]
addCustomLayerWithId:@"Custom"
layerHost:layerHost
layerPosition:[TMBLayerPosition belowLayerId:@"waterway-label"]
completion:^(NSError * _Nullable error) {
if (error) {
NSLog(@"%@", error);
}
}];
NSString* sourceId = @"mapbox-dem";
TMBRasterDemSource* rasterDemSource = [[TMBRasterDemSource alloc] initWithId:sourceId];
rasterDemSource.url = @"mapbox://mapbox.mapbox-terrain-dem-v1";
rasterDemSource.tileSize = @514;
rasterDemSource.maxzoom = @14.0;
// Add a `RasterDEMSource`. This will be used to create and add a terrain layer.
[[self.mapView mapboxMap] addSource:rasterDemSource dataId:sourceId completion:^(NSError * _Nullable error) {
if (error) {
NSLog(@"%@", error);
}
}];
TMBTerrain* terrain = [[TMBTerrain alloc] initWithSourceId:sourceId];
TMBValue* value = [[TMBValue alloc] initWithConstant:@1.5];
terrain.exaggeration = value;
[[self.mapView mapboxMap] setTerrain:terrain completion:^(NSError * _Nullable error) {
if (error) {
NSLog(@"%@", error);
NSLog(@"Failed to add a terrain layer to the map's style.");
}
}];
// Re-use terrain source for hillshade
NSDictionary* properties = @{
@"id": @"terrain_hillshade",
@"type": @"hillshade",
@"source": @"mapbox-dem",
@"hillshade-illumination-anchor": @"map"
};
// TODO addLayerWithProperties
// [[self.mapView mapboxMap]
// addLayerWithProperties: properties
// layerPosition: [TMBLayerPosition belowLayerId: @"water"]
// completion:^(NSError * _Nullable error) {
// if (error) {
// NSLog(@"%@", error);
// }
// }];
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
@implementation SceneKitExampleCustomLayerHost
- (instancetype)initWithModelOrigin: (CLLocationCoordinate2D) modelOrigin
renderingWillEndHandler: (RenderingWillEndHandler) renderingWillEndHandler {
self = [super init];
_modelOrigin = modelOrigin;
_renderingWillEndHandler = renderingWillEndHandler;
return self;
}
- (void)renderingWillStart:(id<MTLDevice>)metalDevice
colorPixelFormat:(NSUInteger)colorPixelFormat
depthStencilPixelFormat:(NSUInteger)depthStencilPixelFormat {
self.renderer = [SCNRenderer rendererWithDevice:metalDevice
options:nil];
self.scene = [SCNScene new];
self.renderer.scene = self.scene;
self.modelNode = [[SCNScene sceneNamed:@"34M_17"].rootNode clone];
[self.scene.rootNode addChildNode: self.modelNode];
self.cameraNode = [[SCNNode alloc] init];
SCNCamera* camera = [[SCNCamera alloc] init];
self.cameraNode.camera = camera;
camera.usesOrthographicProjection = false;
[self.scene.rootNode addChildNode:self.cameraNode];
self.renderer.pointOfView = self.cameraNode;
[self setupLight];
if (@available(iOS 13.0, *)) {
self.renderer.usesReverseZ = false;
} else {
// Fallback on earlier versions, disable depth in render()
self.useCPUOcclusion = true;
}
}
- (void) setupLight {
// Ambient light
SCNNode* ambientLight = [SCNNode new];
ambientLight.light = [SCNLight new];
ambientLight.light.type = SCNLightTypeAmbient;
ambientLight.light.color = [UIColor colorWithWhite:0.4 alpha:1.0];
[self.modelNode addChildNode: ambientLight];
SCNNode* lightNode = [SCNNode new];
lightNode.light = [SCNLight new];
lightNode.light.type = SCNLightTypeDirectional;
lightNode.light.orthographicScale = 30;
lightNode.light.color = [UIColor colorWithWhite: 0.8 alpha: 1.0];
lightNode.position = SCNVector3Make(-50, 100, 100);
lightNode.light.zNear = 1;
lightNode.light.zFar = 1000;
lightNode.light.intensity = 2000;
[lightNode lookAt: self.modelNode.worldPosition];
[self.modelNode addChildNode: lightNode];
SCNNode* pointNode = [SCNNode new];
pointNode.light = [SCNLight new];
pointNode.light.type = SCNLightTypeOmni;
pointNode.light.intensity = 3000;
pointNode.position = SCNVector3Make(0, 25, 0);
[self.modelNode addChildNode: pointNode];
}
- (simd_double4x4) makeTranslationMatrixWithX: (double) x
y: (double) y
z: (double) z {
simd_double4x4 matrix = matrix_identity_double4x4;
matrix.columns[3][0] = x;
matrix.columns[3][1] = y;
matrix.columns[3][2] = z;
return matrix;
}
- (simd_double4x4) makeScaleMatrixWithX: (double) x
y: (double) y
z: (double) z {
simd_double4x4 matrix = matrix_identity_double4x4;
matrix.columns[0][0] = x;
matrix.columns[1][1] = y;
matrix.columns[2][2] = z;
return matrix;
}
- (void)renderingWillEnd {
if (self.renderingWillEndHandler) {
self.renderingWillEndHandler();
}
}
- (void)render:(nonnull MBMCustomLayerRenderParameters *)parameters mtlCommandBuffer:(nonnull id<MTLCommandBuffer>)mtlCommandBuffer mtlRenderPassDescriptor:(nonnull MTLRenderPassDescriptor *)mtlRenderPassDescriptor {
id<MTLTexture> colorTexture = mtlRenderPassDescriptor.colorAttachments[0].texture;
if (!colorTexture) {
return;
}
NSArray<NSNumber *>* m = parameters.projectionMatrix;
// It is essential to use double precision for computation below: using simd instead
// of SceneKit matrix operations.
simd_double4x4 transformSimd = matrix_identity_double4x4;
transformSimd.columns[0][0] = m[0].doubleValue;
transformSimd.columns[0][1] = m[1].doubleValue;
transformSimd.columns[0][2] = m[2].doubleValue;
transformSimd.columns[0][3] = m[3].doubleValue;
transformSimd.columns[1][0] = m[4].doubleValue;
transformSimd.columns[1][1] = m[5].doubleValue;
transformSimd.columns[1][2] = m[6].doubleValue;
transformSimd.columns[1][3] = m[7].doubleValue;
transformSimd.columns[2][0] = m[8].doubleValue;
transformSimd.columns[2][1] = m[9].doubleValue;
transformSimd.columns[2][2] = m[10].doubleValue;
transformSimd.columns[2][3] = m[11].doubleValue;
transformSimd.columns[3][0] = m[12].doubleValue;
transformSimd.columns[3][1] = m[13].doubleValue;
transformSimd.columns[3][2] = m[14].doubleValue;
transformSimd.columns[3][3] = m[15].doubleValue;
// Model is using metric unit system: scale x and y from meters to mercator and keep z is in meters.
double metersPerPoint = [TMBProjection metersPerPointFor:self.modelOrigin.latitude zoom:parameters.zoom];
double meterInMercatorCoordinateUnits = 1.0 / metersPerPoint;
simd_double4x4 modelScale = [self makeScaleMatrixWithX:meterInMercatorCoordinateUnits
y:-meterInMercatorCoordinateUnits
z:1];
MBMMercatorCoordinate* origin = [TMBProjection project:self.modelOrigin zoomScale:pow(2, parameters.zoom)];
double elevation = 0.0;
if (parameters.elevationData) {
NSNumber* elevationData = [parameters.elevationData getElevationForCoordinate:self.modelOrigin];
if (elevationData) {
elevation = elevationData.doubleValue;
}
}
simd_double4x4 translateModel = [self makeTranslationMatrixWithX: origin.x
y: origin.y
z: elevation];
simd_double4x4 transform = simd_mul(simd_mul(transformSimd, translateModel), modelScale);
SCNMatrix4 scnMat;
scnMat.m11 = transform.columns[0][0];
scnMat.m12 = transform.columns[0][1];
scnMat.m13 = transform.columns[0][2];
scnMat.m14 = transform.columns[0][3];
scnMat.m21 = transform.columns[1][0];
scnMat.m22 = transform.columns[1][1];
scnMat.m23 = transform.columns[1][2];
scnMat.m24 = transform.columns[1][3];
scnMat.m31 = transform.columns[2][0];
scnMat.m32 = transform.columns[2][1];
scnMat.m33 = transform.columns[2][2];
scnMat.m34 = transform.columns[2][3];
scnMat.m41 = transform.columns[3][0];
scnMat.m42 = transform.columns[3][1];
scnMat.m43 = transform.columns[3][2];
scnMat.m44 = transform.columns[3][3];
self.cameraNode.camera.projectionTransform = scnMat;
// flush automatic SceneKit transaction as SceneKit animation is not running and
// there's need to use transform matrix in this frame (not to have it used with delay).
[SCNTransaction flush];
if (self.useCPUOcclusion) {
mtlRenderPassDescriptor.depthAttachment = nil;
mtlRenderPassDescriptor.stencilAttachment = nil;
// Example uses depth buffer to occlude model when e.g. behind the hill.
// If depth buffer (SCNRenderer.usesReverseZ = false) is not available, or if wished to
// to indicate that model is occluded or e.g. implement fade out / fade in model occlusion,
// the example here needs to provide CPU side occlusion implementation, too.
// TODO: this is blocked on https://github.com/mapbox/mapbox-maps-ios/issues/155
}
CGRect viewPort = CGRectMake(0, 0, colorTexture.width, colorTexture.height);
[self.renderer renderWithViewport: viewPort
commandBuffer: mtlCommandBuffer
passDescriptor: mtlRenderPassDescriptor];
}
@end