├── .gitignore ├── GeoTools4iOS.xcodeproj └── project.pbxproj ├── GeoTools4iOS ├── AppDelegate.swift ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── GameViewController.swift ├── GeometryBuilder.swift ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── Quad.swift ├── SCNUtils.swift └── textures │ └── brickTexture │ ├── diffuse.jpg │ ├── normal.jpg │ └── specular.jpg ├── GeoTools4iOSTests ├── GeoTools4iOSTests.swift └── Info.plist ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build/ 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | *.xcworkspace 12 | !default.xcworkspace 13 | xcuserdata 14 | profile 15 | *.moved-aside 16 | DerivedData 17 | .idea/ 18 | # Pods - for those of you who use CocoaPods 19 | Pods 20 | -------------------------------------------------------------------------------- /GeoTools4iOS.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2162B4691AF7343D005C9FA2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2162B4681AF7343D005C9FA2 /* AppDelegate.swift */; }; 11 | 2162B46B1AF7343D005C9FA2 /* art.scnassets in Resources */ = {isa = PBXBuildFile; fileRef = 2162B46A1AF7343D005C9FA2 /* art.scnassets */; }; 12 | 2162B46D1AF7343D005C9FA2 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2162B46C1AF7343D005C9FA2 /* GameViewController.swift */; }; 13 | 2162B4701AF7343D005C9FA2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2162B46E1AF7343D005C9FA2 /* Main.storyboard */; }; 14 | 2162B4721AF7343D005C9FA2 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2162B4711AF7343D005C9FA2 /* Images.xcassets */; }; 15 | 2162B4751AF7343D005C9FA2 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2162B4731AF7343D005C9FA2 /* LaunchScreen.xib */; }; 16 | 2162B4811AF7343D005C9FA2 /* GeoTools4iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2162B4801AF7343D005C9FA2 /* GeoTools4iOSTests.swift */; }; 17 | 2162B48B1AF734B1005C9FA2 /* SCNUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2162B48A1AF734B1005C9FA2 /* SCNUtils.swift */; }; 18 | 2162B48D1AF734DE005C9FA2 /* Quad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2162B48C1AF734DE005C9FA2 /* Quad.swift */; }; 19 | 2162B48F1AF734FD005C9FA2 /* GeometryBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2162B48E1AF734FD005C9FA2 /* GeometryBuilder.swift */; }; 20 | 2162B4911AF73641005C9FA2 /* textures in Resources */ = {isa = PBXBuildFile; fileRef = 2162B4901AF73641005C9FA2 /* textures */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 2162B47B1AF7343D005C9FA2 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 2162B45B1AF7343D005C9FA2 /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 2162B4621AF7343D005C9FA2; 29 | remoteInfo = GeoTools4iOS; 30 | }; 31 | /* End PBXContainerItemProxy section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 2162B4631AF7343D005C9FA2 /* GeoTools4iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GeoTools4iOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 2162B4671AF7343D005C9FA2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | 2162B4681AF7343D005C9FA2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 2162B46A1AF7343D005C9FA2 /* art.scnassets */ = {isa = PBXFileReference; lastKnownFileType = wrapper.scnassets; path = art.scnassets; sourceTree = ""; }; 38 | 2162B46C1AF7343D005C9FA2 /* GameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = ""; }; 39 | 2162B46F1AF7343D005C9FA2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 40 | 2162B4711AF7343D005C9FA2 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 41 | 2162B4741AF7343D005C9FA2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 42 | 2162B47A1AF7343D005C9FA2 /* GeoTools4iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GeoTools4iOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 2162B47F1AF7343D005C9FA2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | 2162B4801AF7343D005C9FA2 /* GeoTools4iOSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoTools4iOSTests.swift; sourceTree = ""; }; 45 | 2162B48A1AF734B1005C9FA2 /* SCNUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SCNUtils.swift; sourceTree = ""; }; 46 | 2162B48C1AF734DE005C9FA2 /* Quad.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Quad.swift; sourceTree = ""; }; 47 | 2162B48E1AF734FD005C9FA2 /* GeometryBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeometryBuilder.swift; sourceTree = ""; }; 48 | 2162B4901AF73641005C9FA2 /* textures */ = {isa = PBXFileReference; lastKnownFileType = folder; path = textures; sourceTree = ""; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | 2162B4601AF7343D005C9FA2 /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | 2162B4771AF7343D005C9FA2 /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | /* End PBXFrameworksBuildPhase section */ 67 | 68 | /* Begin PBXGroup section */ 69 | 2162B45A1AF7343D005C9FA2 = { 70 | isa = PBXGroup; 71 | children = ( 72 | 2162B4651AF7343D005C9FA2 /* GeoTools4iOS */, 73 | 2162B47D1AF7343D005C9FA2 /* GeoTools4iOSTests */, 74 | 2162B4641AF7343D005C9FA2 /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 2162B4641AF7343D005C9FA2 /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 2162B4631AF7343D005C9FA2 /* GeoTools4iOS.app */, 82 | 2162B47A1AF7343D005C9FA2 /* GeoTools4iOSTests.xctest */, 83 | ); 84 | name = Products; 85 | sourceTree = ""; 86 | }; 87 | 2162B4651AF7343D005C9FA2 /* GeoTools4iOS */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 2162B4681AF7343D005C9FA2 /* AppDelegate.swift */, 91 | 2162B46A1AF7343D005C9FA2 /* art.scnassets */, 92 | 2162B46C1AF7343D005C9FA2 /* GameViewController.swift */, 93 | 2162B46E1AF7343D005C9FA2 /* Main.storyboard */, 94 | 2162B4711AF7343D005C9FA2 /* Images.xcassets */, 95 | 2162B4731AF7343D005C9FA2 /* LaunchScreen.xib */, 96 | 2162B4661AF7343D005C9FA2 /* Supporting Files */, 97 | 2162B48A1AF734B1005C9FA2 /* SCNUtils.swift */, 98 | 2162B48C1AF734DE005C9FA2 /* Quad.swift */, 99 | 2162B48E1AF734FD005C9FA2 /* GeometryBuilder.swift */, 100 | ); 101 | path = GeoTools4iOS; 102 | sourceTree = ""; 103 | }; 104 | 2162B4661AF7343D005C9FA2 /* Supporting Files */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 2162B4901AF73641005C9FA2 /* textures */, 108 | 2162B4671AF7343D005C9FA2 /* Info.plist */, 109 | ); 110 | name = "Supporting Files"; 111 | sourceTree = ""; 112 | }; 113 | 2162B47D1AF7343D005C9FA2 /* GeoTools4iOSTests */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 2162B4801AF7343D005C9FA2 /* GeoTools4iOSTests.swift */, 117 | 2162B47E1AF7343D005C9FA2 /* Supporting Files */, 118 | ); 119 | path = GeoTools4iOSTests; 120 | sourceTree = ""; 121 | }; 122 | 2162B47E1AF7343D005C9FA2 /* Supporting Files */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 2162B47F1AF7343D005C9FA2 /* Info.plist */, 126 | ); 127 | name = "Supporting Files"; 128 | sourceTree = ""; 129 | }; 130 | /* End PBXGroup section */ 131 | 132 | /* Begin PBXNativeTarget section */ 133 | 2162B4621AF7343D005C9FA2 /* GeoTools4iOS */ = { 134 | isa = PBXNativeTarget; 135 | buildConfigurationList = 2162B4841AF7343D005C9FA2 /* Build configuration list for PBXNativeTarget "GeoTools4iOS" */; 136 | buildPhases = ( 137 | 2162B45F1AF7343D005C9FA2 /* Sources */, 138 | 2162B4601AF7343D005C9FA2 /* Frameworks */, 139 | 2162B4611AF7343D005C9FA2 /* Resources */, 140 | ); 141 | buildRules = ( 142 | ); 143 | dependencies = ( 144 | ); 145 | name = GeoTools4iOS; 146 | productName = GeoTools4iOS; 147 | productReference = 2162B4631AF7343D005C9FA2 /* GeoTools4iOS.app */; 148 | productType = "com.apple.product-type.application"; 149 | }; 150 | 2162B4791AF7343D005C9FA2 /* GeoTools4iOSTests */ = { 151 | isa = PBXNativeTarget; 152 | buildConfigurationList = 2162B4871AF7343D005C9FA2 /* Build configuration list for PBXNativeTarget "GeoTools4iOSTests" */; 153 | buildPhases = ( 154 | 2162B4761AF7343D005C9FA2 /* Sources */, 155 | 2162B4771AF7343D005C9FA2 /* Frameworks */, 156 | 2162B4781AF7343D005C9FA2 /* Resources */, 157 | ); 158 | buildRules = ( 159 | ); 160 | dependencies = ( 161 | 2162B47C1AF7343D005C9FA2 /* PBXTargetDependency */, 162 | ); 163 | name = GeoTools4iOSTests; 164 | productName = GeoTools4iOSTests; 165 | productReference = 2162B47A1AF7343D005C9FA2 /* GeoTools4iOSTests.xctest */; 166 | productType = "com.apple.product-type.bundle.unit-test"; 167 | }; 168 | /* End PBXNativeTarget section */ 169 | 170 | /* Begin PBXProject section */ 171 | 2162B45B1AF7343D005C9FA2 /* Project object */ = { 172 | isa = PBXProject; 173 | attributes = { 174 | LastUpgradeCheck = 0630; 175 | ORGANIZATIONNAME = bluejava; 176 | TargetAttributes = { 177 | 2162B4621AF7343D005C9FA2 = { 178 | CreatedOnToolsVersion = 6.3.1; 179 | }; 180 | 2162B4791AF7343D005C9FA2 = { 181 | CreatedOnToolsVersion = 6.3.1; 182 | TestTargetID = 2162B4621AF7343D005C9FA2; 183 | }; 184 | }; 185 | }; 186 | buildConfigurationList = 2162B45E1AF7343D005C9FA2 /* Build configuration list for PBXProject "GeoTools4iOS" */; 187 | compatibilityVersion = "Xcode 3.2"; 188 | developmentRegion = English; 189 | hasScannedForEncodings = 0; 190 | knownRegions = ( 191 | en, 192 | Base, 193 | ); 194 | mainGroup = 2162B45A1AF7343D005C9FA2; 195 | productRefGroup = 2162B4641AF7343D005C9FA2 /* Products */; 196 | projectDirPath = ""; 197 | projectRoot = ""; 198 | targets = ( 199 | 2162B4621AF7343D005C9FA2 /* GeoTools4iOS */, 200 | 2162B4791AF7343D005C9FA2 /* GeoTools4iOSTests */, 201 | ); 202 | }; 203 | /* End PBXProject section */ 204 | 205 | /* Begin PBXResourcesBuildPhase section */ 206 | 2162B4611AF7343D005C9FA2 /* Resources */ = { 207 | isa = PBXResourcesBuildPhase; 208 | buildActionMask = 2147483647; 209 | files = ( 210 | 2162B4751AF7343D005C9FA2 /* LaunchScreen.xib in Resources */, 211 | 2162B4911AF73641005C9FA2 /* textures in Resources */, 212 | 2162B4721AF7343D005C9FA2 /* Images.xcassets in Resources */, 213 | 2162B46B1AF7343D005C9FA2 /* art.scnassets in Resources */, 214 | 2162B4701AF7343D005C9FA2 /* Main.storyboard in Resources */, 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | 2162B4781AF7343D005C9FA2 /* Resources */ = { 219 | isa = PBXResourcesBuildPhase; 220 | buildActionMask = 2147483647; 221 | files = ( 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | /* End PBXResourcesBuildPhase section */ 226 | 227 | /* Begin PBXSourcesBuildPhase section */ 228 | 2162B45F1AF7343D005C9FA2 /* Sources */ = { 229 | isa = PBXSourcesBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | 2162B48D1AF734DE005C9FA2 /* Quad.swift in Sources */, 233 | 2162B46D1AF7343D005C9FA2 /* GameViewController.swift in Sources */, 234 | 2162B4691AF7343D005C9FA2 /* AppDelegate.swift in Sources */, 235 | 2162B48F1AF734FD005C9FA2 /* GeometryBuilder.swift in Sources */, 236 | 2162B48B1AF734B1005C9FA2 /* SCNUtils.swift in Sources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | 2162B4761AF7343D005C9FA2 /* Sources */ = { 241 | isa = PBXSourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | 2162B4811AF7343D005C9FA2 /* GeoTools4iOSTests.swift in Sources */, 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | }; 248 | /* End PBXSourcesBuildPhase section */ 249 | 250 | /* Begin PBXTargetDependency section */ 251 | 2162B47C1AF7343D005C9FA2 /* PBXTargetDependency */ = { 252 | isa = PBXTargetDependency; 253 | target = 2162B4621AF7343D005C9FA2 /* GeoTools4iOS */; 254 | targetProxy = 2162B47B1AF7343D005C9FA2 /* PBXContainerItemProxy */; 255 | }; 256 | /* End PBXTargetDependency section */ 257 | 258 | /* Begin PBXVariantGroup section */ 259 | 2162B46E1AF7343D005C9FA2 /* Main.storyboard */ = { 260 | isa = PBXVariantGroup; 261 | children = ( 262 | 2162B46F1AF7343D005C9FA2 /* Base */, 263 | ); 264 | name = Main.storyboard; 265 | sourceTree = ""; 266 | }; 267 | 2162B4731AF7343D005C9FA2 /* LaunchScreen.xib */ = { 268 | isa = PBXVariantGroup; 269 | children = ( 270 | 2162B4741AF7343D005C9FA2 /* Base */, 271 | ); 272 | name = LaunchScreen.xib; 273 | sourceTree = ""; 274 | }; 275 | /* End PBXVariantGroup section */ 276 | 277 | /* Begin XCBuildConfiguration section */ 278 | 2162B4821AF7343D005C9FA2 /* Debug */ = { 279 | isa = XCBuildConfiguration; 280 | buildSettings = { 281 | ALWAYS_SEARCH_USER_PATHS = NO; 282 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 283 | CLANG_CXX_LIBRARY = "libc++"; 284 | CLANG_ENABLE_MODULES = YES; 285 | CLANG_ENABLE_OBJC_ARC = YES; 286 | CLANG_WARN_BOOL_CONVERSION = YES; 287 | CLANG_WARN_CONSTANT_CONVERSION = YES; 288 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 289 | CLANG_WARN_EMPTY_BODY = YES; 290 | CLANG_WARN_ENUM_CONVERSION = YES; 291 | CLANG_WARN_INT_CONVERSION = YES; 292 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 293 | CLANG_WARN_UNREACHABLE_CODE = YES; 294 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 295 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 296 | COPY_PHASE_STRIP = NO; 297 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 298 | ENABLE_STRICT_OBJC_MSGSEND = YES; 299 | GCC_C_LANGUAGE_STANDARD = gnu99; 300 | GCC_DYNAMIC_NO_PIC = NO; 301 | GCC_NO_COMMON_BLOCKS = YES; 302 | GCC_OPTIMIZATION_LEVEL = 0; 303 | GCC_PREPROCESSOR_DEFINITIONS = ( 304 | "DEBUG=1", 305 | "$(inherited)", 306 | ); 307 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 308 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 309 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 310 | GCC_WARN_UNDECLARED_SELECTOR = YES; 311 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 312 | GCC_WARN_UNUSED_FUNCTION = YES; 313 | GCC_WARN_UNUSED_VARIABLE = YES; 314 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 315 | MTL_ENABLE_DEBUG_INFO = YES; 316 | ONLY_ACTIVE_ARCH = YES; 317 | SDKROOT = iphoneos; 318 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 319 | TARGETED_DEVICE_FAMILY = "1,2"; 320 | }; 321 | name = Debug; 322 | }; 323 | 2162B4831AF7343D005C9FA2 /* Release */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 328 | CLANG_CXX_LIBRARY = "libc++"; 329 | CLANG_ENABLE_MODULES = YES; 330 | CLANG_ENABLE_OBJC_ARC = YES; 331 | CLANG_WARN_BOOL_CONVERSION = YES; 332 | CLANG_WARN_CONSTANT_CONVERSION = YES; 333 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 334 | CLANG_WARN_EMPTY_BODY = YES; 335 | CLANG_WARN_ENUM_CONVERSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 338 | CLANG_WARN_UNREACHABLE_CODE = YES; 339 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 340 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 341 | COPY_PHASE_STRIP = NO; 342 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 343 | ENABLE_NS_ASSERTIONS = NO; 344 | ENABLE_STRICT_OBJC_MSGSEND = YES; 345 | GCC_C_LANGUAGE_STANDARD = gnu99; 346 | GCC_NO_COMMON_BLOCKS = YES; 347 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 348 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 349 | GCC_WARN_UNDECLARED_SELECTOR = YES; 350 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 351 | GCC_WARN_UNUSED_FUNCTION = YES; 352 | GCC_WARN_UNUSED_VARIABLE = YES; 353 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 354 | MTL_ENABLE_DEBUG_INFO = NO; 355 | SDKROOT = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | VALIDATE_PRODUCT = YES; 358 | }; 359 | name = Release; 360 | }; 361 | 2162B4851AF7343D005C9FA2 /* Debug */ = { 362 | isa = XCBuildConfiguration; 363 | buildSettings = { 364 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 365 | INFOPLIST_FILE = GeoTools4iOS/Info.plist; 366 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 367 | PRODUCT_NAME = "$(TARGET_NAME)"; 368 | }; 369 | name = Debug; 370 | }; 371 | 2162B4861AF7343D005C9FA2 /* Release */ = { 372 | isa = XCBuildConfiguration; 373 | buildSettings = { 374 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 375 | INFOPLIST_FILE = GeoTools4iOS/Info.plist; 376 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 377 | PRODUCT_NAME = "$(TARGET_NAME)"; 378 | }; 379 | name = Release; 380 | }; 381 | 2162B4881AF7343D005C9FA2 /* Debug */ = { 382 | isa = XCBuildConfiguration; 383 | buildSettings = { 384 | BUNDLE_LOADER = "$(TEST_HOST)"; 385 | FRAMEWORK_SEARCH_PATHS = ( 386 | "$(SDKROOT)/Developer/Library/Frameworks", 387 | "$(inherited)", 388 | ); 389 | GCC_PREPROCESSOR_DEFINITIONS = ( 390 | "DEBUG=1", 391 | "$(inherited)", 392 | ); 393 | INFOPLIST_FILE = GeoTools4iOSTests/Info.plist; 394 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 395 | PRODUCT_NAME = "$(TARGET_NAME)"; 396 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GeoTools4iOS.app/GeoTools4iOS"; 397 | }; 398 | name = Debug; 399 | }; 400 | 2162B4891AF7343D005C9FA2 /* Release */ = { 401 | isa = XCBuildConfiguration; 402 | buildSettings = { 403 | BUNDLE_LOADER = "$(TEST_HOST)"; 404 | FRAMEWORK_SEARCH_PATHS = ( 405 | "$(SDKROOT)/Developer/Library/Frameworks", 406 | "$(inherited)", 407 | ); 408 | INFOPLIST_FILE = GeoTools4iOSTests/Info.plist; 409 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 410 | PRODUCT_NAME = "$(TARGET_NAME)"; 411 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GeoTools4iOS.app/GeoTools4iOS"; 412 | }; 413 | name = Release; 414 | }; 415 | /* End XCBuildConfiguration section */ 416 | 417 | /* Begin XCConfigurationList section */ 418 | 2162B45E1AF7343D005C9FA2 /* Build configuration list for PBXProject "GeoTools4iOS" */ = { 419 | isa = XCConfigurationList; 420 | buildConfigurations = ( 421 | 2162B4821AF7343D005C9FA2 /* Debug */, 422 | 2162B4831AF7343D005C9FA2 /* Release */, 423 | ); 424 | defaultConfigurationIsVisible = 0; 425 | defaultConfigurationName = Release; 426 | }; 427 | 2162B4841AF7343D005C9FA2 /* Build configuration list for PBXNativeTarget "GeoTools4iOS" */ = { 428 | isa = XCConfigurationList; 429 | buildConfigurations = ( 430 | 2162B4851AF7343D005C9FA2 /* Debug */, 431 | 2162B4861AF7343D005C9FA2 /* Release */, 432 | ); 433 | defaultConfigurationIsVisible = 0; 434 | }; 435 | 2162B4871AF7343D005C9FA2 /* Build configuration list for PBXNativeTarget "GeoTools4iOSTests" */ = { 436 | isa = XCConfigurationList; 437 | buildConfigurations = ( 438 | 2162B4881AF7343D005C9FA2 /* Debug */, 439 | 2162B4891AF7343D005C9FA2 /* Release */, 440 | ); 441 | defaultConfigurationIsVisible = 0; 442 | }; 443 | /* End XCConfigurationList section */ 444 | }; 445 | rootObject = 2162B45B1AF7343D005C9FA2 /* Project object */; 446 | } 447 | -------------------------------------------------------------------------------- /GeoTools4iOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // GeoTools4iOS 4 | // 5 | // Created by Glenn Crownover on 5/4/15. 6 | // Copyright (c) 2015 bluejava. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /GeoTools4iOS/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /GeoTools4iOS/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /GeoTools4iOS/GameViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GameViewController.swift 3 | // GeoTools4iOS 4 | // 5 | // Created by Glenn Crownover on 5/4/15. 6 | // Copyright (c) 2015 bluejava. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import QuartzCore 11 | import SceneKit 12 | 13 | class GameViewController: UIViewController { 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | /* First, some boilerplate SceneKit setup - lights, camera, (action comes later) */ 19 | 20 | // create a new scene 21 | var scene = SCNScene() 22 | 23 | var rootNode = scene.rootNode 24 | 25 | // create and add a camera to the scene 26 | let cameraNode = SCNNode() 27 | cameraNode.camera = SCNCamera() 28 | rootNode.addChildNode(cameraNode) 29 | 30 | // place the camera 31 | cameraNode.position = SCNVector3(x: 0, y: 0, z: 15) 32 | 33 | // create and add a light to the scene 34 | let lightNode = SCNNode() 35 | lightNode.light = SCNLight() 36 | lightNode.light!.type = SCNLightTypeOmni 37 | lightNode.position = SCNVector3(x: 0, y: 10, z: 10) 38 | rootNode.addChildNode(lightNode) 39 | 40 | // create and add an ambient light to the scene 41 | let ambientLightNode = SCNNode() 42 | ambientLightNode.light = SCNLight() 43 | ambientLightNode.light!.type = SCNLightTypeAmbient 44 | ambientLightNode.light!.color = UIColor.darkGrayColor() 45 | rootNode.addChildNode(ambientLightNode) 46 | 47 | /* 48 | Here are the actual custom geometry test functions 49 | */ 50 | 51 | textureTileExample(scene.rootNode) // A parallelogram with stretched texture 52 | textureTileExampleNonPar(scene.rootNode) // A non-parallel quadrilateral shape with tiled texture 53 | textureTileExample3d(scene.rootNode) // A 3d custom shape with tiled texture 54 | 55 | /* Now, some more boilerplate… */ 56 | 57 | // retrieve the SCNView 58 | let scnView = self.view as! SCNView 59 | 60 | // set the scene to the view 61 | scnView.scene = scene 62 | 63 | // allows the user to manipulate the camera 64 | scnView.allowsCameraControl = true 65 | 66 | // show statistics such as fps and timing information 67 | scnView.showsStatistics = true 68 | 69 | // configure the view 70 | scnView.backgroundColor = UIColor.whiteColor() 71 | } 72 | 73 | 74 | // Always keep in mind the orientation of the verticies when looking at the face from the "front" 75 | // With single-sided faces, we only see from the front side - so this is important. 76 | // v1 --------------v0 77 | // | __/ | 78 | // | face __/ | 79 | // | 1 __/ | 80 | // | __/ face | 81 | // | __/ 2 | 82 | // v2 ------------- v3 83 | // Two triangular faces are created from the 4 vertices - think of drawing the letter C when considering the order 84 | // to enter your vertices - top right, then top left, then bottom left, then bottom right - But of course, this is 85 | // relative only to your field of view - not the global coordinate system - "bottom" for your shape may be "up" in 86 | // the world view! 87 | 88 | // This function creates a quadrilateral shape with non-parallel sides. Note how the 89 | // texture originates at v2 and tiles to the right, up and to the left seamlessly. 90 | func textureTileExample(pnode: SCNNode) 91 | { 92 | // First, we create the 4 vertices for our custom geometry - note, they share a plane, but are otherwise irregular 93 | var v0 = SCNVector3(x: 6, y: 6, z: 0) 94 | var v1 = SCNVector3(x: 0, y: 6, z: 0) 95 | var v2 = SCNVector3(x: 0, y: 0, z: 0) 96 | var v3 = SCNVector3(x: 6, y: 0, z: 0) 97 | 98 | // Now we create the GeometryBuilder - which allows us to add quads to make up a custom shape 99 | var geobuild = GeometryBuilder(uvMode: GeometryBuilder.UVModeType.StretchToFitXY) 100 | 101 | geobuild.addQuad(Quad(v0: v0,v1: v1,v2: v2,v3: v3)) // only the one quad for us today, thanks! 102 | var geo = geobuild.getGeometry() // And here we get an SCNGeometry instance from our new shape 103 | 104 | // Lets setup the diffuse, normal and specular maps - located in a subdirectory 105 | geo.materials = [ SCNUtils.getMat("diffuse.jpg", normalFilename: "normal.jpg", specularFilename: "specular.jpg", directory: "textures/brickTexture") ] 106 | 107 | // Now we simply create the node, position it, and add to our parent! 108 | var node = SCNNode(geometry: geo) 109 | node.position = SCNVector3(x: 5, y: 2, z: 0) 110 | 111 | pnode.addChildNode(node) 112 | } 113 | 114 | func textureTileExampleFlipped(pnode: SCNNode) 115 | { 116 | // First, we create the 4 vertices for our custom geometry - note, they share a plane, but are otherwise irregular 117 | var v0 = SCNVector3(x: 6, y: 0, z: 0) 118 | var v1 = SCNVector3(x: 0, y: 0, z: 0) 119 | var v2 = SCNVector3(x: 0, y: 6, z: 0) 120 | var v3 = SCNVector3(x: 6, y: 6, z: 0) 121 | 122 | // Now we create the GeometryBuilder - which allows us to add quads to make up a custom shape 123 | var geobuild = GeometryBuilder(uvMode: GeometryBuilder.UVModeType.StretchToFitXY) 124 | 125 | geobuild.addQuad(Quad(v0: v0,v1: v1,v2: v2,v3: v3)) // only the one quad for us today, thanks! 126 | var geo = geobuild.getGeometry() // And here we get an SCNGeometry instance from our new shape 127 | 128 | // Lets setup the diffuse, normal and specular maps - located in a subdirectory 129 | geo.materials = [ SCNUtils.getMat("diffuse.jpg", normalFilename: "normal.jpg", specularFilename: "specular.jpg", directory: "textures/brickTexture") ] 130 | 131 | // Now we simply create the node, position it, and add to our parent! 132 | var node = SCNNode(geometry: geo) 133 | node.position = SCNVector3(x: 5, y: 2, z: 0) 134 | 135 | pnode.addChildNode(node) 136 | } 137 | 138 | 139 | // This function creates a quadrilateral shape with parallel sides to demonstrate 140 | // a stretchedToFit texture mapping. Of course, since it is non-square, the texture is 141 | // skewed. 142 | func textureTileExampleNonPar(pnode: SCNNode) 143 | { 144 | var v0 = SCNVector3(x: 6, y: 6.0, z: 6) 145 | var v1 = SCNVector3(x: 1, y: 4, z: 1) 146 | var v2 = SCNVector3(x: 2, y: 0, z: 2) 147 | var v3 = SCNVector3(x: 5, y: -2, z: 5) 148 | 149 | var geobuild = GeometryBuilder(uvMode: GeometryBuilder.UVModeType.StretchToFitXY) 150 | geobuild.addQuad(Quad(v0: v0,v1: v1,v2: v2,v3: v3)) // simple 151 | var geo = geobuild.getGeometry() 152 | 153 | geo.materials = [ SCNUtils.getMat("diffuse.jpg", normalFilename: "normal.jpg", specularFilename: "specular.jpg", directory: "textures/brickTexture") ] 154 | 155 | var node = SCNNode(geometry: geo) 156 | node.position = SCNVector3(x: 5, y: -6, z: 0) 157 | 158 | pnode.addChildNode(node) 159 | } 160 | 161 | func testQ3D(pnode: SCNNode) 162 | { 163 | var node = buildQuad3D([ 164 | SCNVector3(x: 4,y: 4,z: -4), 165 | SCNVector3(x: 0,y: 4,z: -4), 166 | SCNVector3(x: 0,y: 4,z: 0), 167 | SCNVector3(x: 4,y: 4,z: 0), 168 | 169 | SCNVector3(x: 4,y: 1,z: 0), 170 | SCNVector3(x: 0,y: 1,z: 0), 171 | SCNVector3(x: 0,y: 1,z: -4), 172 | SCNVector3(x: 4,y: 1,z: -4), 173 | ]) 174 | 175 | // node.geometry?.materials = [ SCNUtils.getMat("diffuse.jpg", normalFilename: "normal.jpg", specularFilename: "specular.jpg", directory: "3d textures/brickTexture") ] 176 | node.geometry?.firstMaterial?.diffuse.contents = UIColor.purpleColor() 177 | 178 | node.position = SCNVector3(x: 4, y: 4.0, z: -4) 179 | 180 | pnode.addChildNode(node) 181 | 182 | // var xx = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.1)) 183 | // xx.geometry?.firstMaterial!.diffuse.contents = UIColor.redColor() 184 | // xx.position.y = 8 185 | // pnode.addChildNode(xx) 186 | } 187 | 188 | 189 | func buildQuad3D(v: [SCNVector3]) -> SCNNode 190 | { 191 | var geobuild = GeometryBuilder(uvMode: .SizeToWorldUnitsXY) 192 | 193 | geobuild.addQuad(Quad(v0: v[3],v1: v[2],v2: v[5],v3: v[4])) // front 194 | geobuild.addQuad(Quad(v0: v[2],v1: v[1],v2: v[6],v3: v[5])) // left 195 | geobuild.addQuad(Quad(v0: v[0],v1: v[3],v2: v[4],v3: v[7])) // right 196 | geobuild.addQuad(Quad(v0: v[1],v1: v[0],v2: v[7],v3: v[6])) // back 197 | geobuild.addQuad(Quad(v0: v[0],v1: v[1],v2: v[2],v3: v[3])) // top 198 | geobuild.addQuad(Quad(v0: v[4],v1: v[5],v2: v[6],v3: v[7])) // bottom 199 | 200 | var geo = geobuild.getGeometry() 201 | 202 | return SCNNode(geometry: geo) 203 | } 204 | 205 | // And finally, here is a full 3d object with six sides. We only create the 8 vertices of the shape once, 206 | // but they are replicated for each quad and then for each face as they have their own normals, texture coordinates, etc. 207 | // But it sure makes our job easy at this point - just enter your vertices, build your quads and generate the shape! 208 | func textureTileExample3d(pnode: SCNNode) 209 | { 210 | var f0 = SCNVector3(x: 6, y: 6.0, z: 2) 211 | var f1 = SCNVector3(x: 1, y: 4, z: 2) 212 | var f2 = SCNVector3(x: 2, y: 0, z: 2) 213 | var f3 = SCNVector3(x: 5, y: -2, z: 2) 214 | 215 | var b0 = SCNVector3(x: 6, y: 6.0, z: 0) 216 | var b1 = SCNVector3(x: 1, y: 4, z: 0) 217 | var b2 = SCNVector3(x: 2, y: 0, z: 0) 218 | var b3 = SCNVector3(x: 5, y: -2, z: 0) 219 | 220 | // Note: This uvMode will consider 1 by 1 coordinate units to coorespond with one full texture. 221 | // This works great for drawing large irregularly shaped objects made with tile-able textures. 222 | // The textures tile across each face without stretching or skewing regardless of size. 223 | var geobuild = GeometryBuilder(uvMode: .SizeToWorldUnitsXY) 224 | geobuild.addQuad(Quad(v0: f0,v1: f1,v2: f2,v3: f3)) // front 225 | geobuild.addQuad(Quad(v0: b1,v1: b0,v2: b3,v3: b2)) // back 226 | geobuild.addQuad(Quad(v0: b0,v1: b1,v2: f1,v3: f0)) // top 227 | geobuild.addQuad(Quad(v0: f1,v1: b1,v2: b2,v3: f2)) // left 228 | geobuild.addQuad(Quad(v0: b0,v1: f0,v2: f3,v3: b3)) // right 229 | geobuild.addQuad(Quad(v0: f3,v1: f2,v2: b2,v3: b3)) // bottom 230 | 231 | var geo = geobuild.getGeometry() 232 | 233 | geo.materials = [ SCNUtils.getMat("diffuse.jpg", normalFilename: "normal.jpg", specularFilename: "specular.jpg", directory: "textures/brickTexture") ] 234 | 235 | var node = SCNNode(geometry: geo) 236 | node.position = SCNVector3(x: -5, y: 2, z: 0) 237 | 238 | pnode.addChildNode(node) 239 | } 240 | 241 | override func shouldAutorotate() -> Bool { 242 | return true 243 | } 244 | 245 | override func prefersStatusBarHidden() -> Bool { 246 | return true 247 | } 248 | 249 | override func supportedInterfaceOrientations() -> Int { 250 | if UIDevice.currentDevice().userInterfaceIdiom == .Phone { 251 | return Int(UIInterfaceOrientationMask.AllButUpsideDown.rawValue) 252 | } else { 253 | return Int(UIInterfaceOrientationMask.All.rawValue) 254 | } 255 | } 256 | 257 | override func didReceiveMemoryWarning() { 258 | super.didReceiveMemoryWarning() 259 | // Release any cached data, images, etc that aren't in use. 260 | } 261 | 262 | } 263 | -------------------------------------------------------------------------------- /GeoTools4iOS/GeometryBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeometryBuilder.swift 3 | // GeoTools4iOS 4 | // 5 | // Created by Glenn Crownover on 5/4/15. 6 | // Copyright (c) 2015 bluejava. All rights reserved. 7 | // 8 | 9 | // This class helps in the building of custom geometry 10 | 11 | import SceneKit 12 | 13 | class GeometryBuilder 14 | { 15 | var quads: [Quad] 16 | var textureSize: CGPoint 17 | 18 | enum UVModeType { case StretchToFitXY, StretchToFitX, StretchToFitY, SizeToWorldUnitsXY, SizeToWorldUnitsX} 19 | var uvMode = UVModeType.StretchToFitXY 20 | 21 | init(uvMode: UVModeType = .StretchToFitXY) 22 | { 23 | self.uvMode = uvMode 24 | 25 | quads = [] 26 | textureSize = CGPoint(x: 1.0,y: 1.0) // Number of world units represents the textures are mapped to geometry as one full image per unit square 27 | } 28 | 29 | // Add a quad to the geometry - list verticies in counter-clockwise order when looking from the 30 | // "outside" of the square 31 | func addQuad(quad: Quad) 32 | { 33 | quads.append(quad) 34 | } 35 | 36 | func getGeometry() -> SCNGeometry 37 | { 38 | // This one structure holds the position, normal and UV Texture Mapping for a single vertice 39 | // - This is called "Interleaving Vertex Data" as explained on the SCNGeometrySource Reference Doc 40 | struct Vertex 41 | { 42 | var position: Float3 43 | var normal: Float3 44 | var tcoord: Float2 45 | } 46 | 47 | func debugVertex(label: String, v: Vertex) 48 | { 49 | println("\(label).position: \(v.position.x),\(v.position.y),\(v.position.z)") 50 | println("\(label).normal: \(v.normal.x),\(v.normal.y),\(v.normal.z)") 51 | println("\(label).tcoord: \(v.tcoord.s),\(v.tcoord.t)") 52 | } 53 | 54 | var verts: [Vertex] = [] 55 | var faceIndices: [CInt] = [] 56 | 57 | // Walk through the quads, adding 4 vertices, 2 faces and 4 normals per quad 58 | // v1 --------------v0 59 | // | __/ | 60 | // | face __/ | 61 | // | 1 __/ | 62 | // | __/ face | 63 | // | __/ 2 | 64 | // v2 ------------- v3 65 | for quad in quads 66 | { 67 | // first, calculate normals for each vertice (compute seperately for face1 and face2 - common edge gets avg) 68 | var nvf1 = SCNUtils.getNormal(quad.v0, v1: quad.v1, v2: quad.v2) 69 | var nvf2 = SCNUtils.getNormal(quad.v0, v1: quad.v2, v2: quad.v3) 70 | 71 | // next, the texture coordinates 72 | var uv0: Float2 73 | var uv1: Float2 74 | var uv2: Float2 75 | var uv3: Float2 76 | 77 | switch uvMode 78 | { 79 | // The longest sides dictate the texture tiling, then it is stretched (if nec) across 80 | case .SizeToWorldUnitsX: 81 | var longestUEdgeLength = max( (quad.v1-quad.v0).length(), (quad.v2-quad.v3).length() ) 82 | var longestVEdgeLength = max( (quad.v1-quad.v2).length(), (quad.v0-quad.v3).length() ) 83 | uv0 = Float2(s: longestUEdgeLength,t: longestVEdgeLength) 84 | uv1 = Float2(s: 0,t: longestVEdgeLength) 85 | uv2 = Float2(s: 0,t: 0) 86 | uv3 = Float2(s: longestUEdgeLength, t:0) 87 | case .SizeToWorldUnitsXY: 88 | // For this uvMode, we allign the texture to the "upper left corner" (v1) and tile 89 | // it to the "right" and "down" (and "up") based on the coordinate units and the 90 | // texture/units ratio 91 | 92 | let v2v0 = quad.v0 - quad.v2 // v2 to v0 edge 93 | let v2v1 = quad.v1 - quad.v2 // v2 to v1 edge 94 | let v2v3 = quad.v3 - quad.v2 // v2 to v3 edge 95 | 96 | let v2v0Mag = v2v0.length() // length of v2 to v0 edge 97 | let v2v1Mag = v2v1.length() // length of v2 to v1 edge 98 | let v2v3Mag = v2v3.length() // length of v2 to v3 edge 99 | 100 | let v0angle = v2v3.angle(v2v0) // angle of v2v0 edge against v2v3 edge 101 | let v1angle = v2v3.angle(v2v1) // angle of v2v1 edge against v2v3 edge 102 | 103 | // now its just some simple trig - yay! 104 | uv0 = Float2(s: cos(v0angle) * v2v0Mag, t: sin(v0angle)*v2v0Mag) 105 | uv1 = Float2(s: cos(v1angle) * v2v1Mag, t: sin(v1angle)*v2v1Mag) 106 | uv2 = Float2(s: 0,t: 0) 107 | uv3 = Float2(s: v2v3Mag, t: 0) 108 | 109 | case .StretchToFitXY: 110 | uv0 = Float2(s: 1,t: 1) 111 | uv1 = Float2(s: 0,t: 1) 112 | uv2 = Float2(s: 0,t: 0) 113 | uv3 = Float2(s: 1,t: 0) 114 | default: 115 | println("Unknown uv mode \(uvMode)") // no uv mapping for you! 116 | uv0 = Float2(s: 1,t: 1) 117 | uv1 = Float2(s: 0,t: 1) 118 | uv2 = Float2(s: 0,t: 0) 119 | uv3 = Float2(s: 1,t: 0) 120 | } 121 | 122 | var v0norm = nvf1 + nvf2 123 | var v2norm = nvf1 + nvf2 124 | 125 | var v0 = Vertex(position: Quad.vector3ToFloat3(quad.v0), normal: Quad.vector3ToFloat3(v0norm.normalize()!), tcoord: uv0) 126 | var v1 = Vertex(position: Quad.vector3ToFloat3(quad.v1), normal: Quad.vector3ToFloat3(nvf1.normalize()!), tcoord: uv1) 127 | var v2 = Vertex(position: Quad.vector3ToFloat3(quad.v2), normal: Quad.vector3ToFloat3(v2norm.normalize()!), tcoord: uv2) 128 | var v3 = Vertex(position: Quad.vector3ToFloat3(quad.v3), normal: Quad.vector3ToFloat3(nvf2.normalize()!), tcoord: uv3) 129 | 130 | debugVertex("v0", v0) 131 | debugVertex("v1", v1) 132 | debugVertex("v2", v2) 133 | debugVertex("v3", v3) 134 | 135 | verts.append(v0) 136 | verts.append(v1) 137 | verts.append(v2) 138 | verts.append(v3) 139 | 140 | // add face 1 141 | faceIndices.append(CInt(verts.count-4)) // v0 142 | faceIndices.append(CInt(verts.count-3)) // v1 143 | faceIndices.append(CInt(verts.count-2)) // v2 144 | 145 | // add face 2 146 | faceIndices.append(CInt(verts.count-4)) // v0 147 | faceIndices.append(CInt(verts.count-2)) // v2 148 | faceIndices.append(CInt(verts.count-1)) // v3 149 | } 150 | 151 | // Define our sources 152 | 153 | let data = NSData(bytes: verts, length: verts.count * sizeof(Vertex)) 154 | var vertexSource = SCNGeometrySource( 155 | data: data, 156 | semantic: SCNGeometrySourceSemanticVertex, 157 | vectorCount: verts.count, 158 | floatComponents: true, 159 | componentsPerVector: 3, 160 | bytesPerComponent: sizeof(GLfloat), 161 | dataOffset: 0, // position is first member in Vertex 162 | dataStride: sizeof(Vertex)) 163 | 164 | let normalSource = SCNGeometrySource( 165 | data: data, 166 | semantic: SCNGeometrySourceSemanticNormal, 167 | vectorCount: verts.count, 168 | floatComponents: true, 169 | componentsPerVector: 3, 170 | bytesPerComponent: sizeof(GLfloat), 171 | dataOffset: sizeof(Float3), // one Float3 before normal in Vertex 172 | dataStride: sizeof(Vertex)) 173 | 174 | let tcoordSource = SCNGeometrySource( 175 | data: data, 176 | semantic: SCNGeometrySourceSemanticTexcoord, 177 | vectorCount: verts.count, 178 | floatComponents: true, 179 | componentsPerVector: 2, 180 | bytesPerComponent: sizeof(GLfloat), 181 | dataOffset: 2 * sizeof(Float3), // 2 Float3s before tcoord in Vertex 182 | dataStride: sizeof(Vertex)) 183 | 184 | // Define elements Data 185 | var indexData = NSData(bytes: faceIndices, length: sizeof(CInt) * faceIndices.count) 186 | var element = SCNGeometryElement(data: indexData, primitiveType: .Triangles, primitiveCount: faceIndices.count / 3, bytesPerIndex: sizeof(CInt)) 187 | 188 | var geometry = SCNGeometry(sources: [vertexSource, normalSource, tcoordSource], elements: [element]) 189 | 190 | return geometry 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /GeoTools4iOS/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /GeoTools4iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.bluejava.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarHidden 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationLandscapeLeft 38 | UIInterfaceOrientationLandscapeRight 39 | 40 | UISupportedInterfaceOrientations~ipad 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationPortraitUpsideDown 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /GeoTools4iOS/Quad.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Quad.swift 3 | // GeoTools4iOS 4 | // 5 | // Created by Glenn Crownover on 5/4/15. 6 | // Copyright (c) 2015 bluejava. All rights reserved. 7 | // 8 | 9 | // A four vertice quad - must either be planar, or if non-planar, note that the 10 | // shared edge will be v0->v2 11 | // 12 | // v1 --------v0 13 | // | _/ | 14 | // | _/ | 15 | // | _/ | 16 | // | _/ | 17 | // | / | 18 | // v2 ------- v3 19 | 20 | import SceneKit 21 | 22 | class Quad 23 | { 24 | let v0: SCNVector3 25 | let v1: SCNVector3 26 | let v2: SCNVector3 27 | let v3: SCNVector3 28 | 29 | init(v0: SCNVector3, v1: SCNVector3, v2: SCNVector3, v3: SCNVector3) 30 | { 31 | self.v0 = v0 32 | self.v1 = v1 33 | self.v2 = v2 34 | self.v3 = v3 35 | } 36 | 37 | class func vector3ToFloat3(vector3: SCNVector3) -> Float3 38 | { 39 | return Float3(x: vector3.x, y: vector3.y, z: vector3.z) 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /GeoTools4iOS/SCNUtils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SCNUtils.swift 3 | // GeoTools4iOS 4 | // 5 | // Created by Glenn Crownover on 5/4/15. 6 | // Copyright (c) 2015 bluejava. All rights reserved. 7 | // 8 | 9 | import SceneKit 10 | 11 | class SCNUtils 12 | { 13 | class func getNodeFromDAE(name: String) -> SCNNode? 14 | { 15 | var rnode = SCNNode() 16 | let nscene = SCNScene(named: name) 17 | 18 | if let nodeArray = nscene?.rootNode.childNodes 19 | { 20 | for cn in nodeArray { 21 | rnode.addChildNode(cn as! SCNNode) 22 | } 23 | return rnode 24 | } 25 | 26 | println("DAE File not found: \(name)!!") 27 | 28 | return nil 29 | } 30 | 31 | class func getStaticNodeFromDAE(name: String) -> SCNNode? 32 | { 33 | if let node = getNodeFromDAE(name) 34 | { 35 | // debugNode(node) 36 | node.physicsBody = SCNPhysicsBody(type: .Static, shape: SCNPhysicsShape(node: node, options: [ SCNPhysicsShapeTypeKey: SCNPhysicsShapeTypeConcavePolyhedron])) 37 | return node 38 | } 39 | 40 | return nil 41 | } 42 | 43 | class func debugNode(node: SCNNode) 44 | { 45 | println("node: \(node.name)") 46 | for cn in node.childNodes 47 | { 48 | debugNode(cn as! SCNNode) 49 | } 50 | } 51 | 52 | class func getMat(textureFilename: String, ureps: Float = 1.0, vreps: Float = 1.0, directory: String? = nil, 53 | normalFilename: String? = nil, specularFilename: String? = nil) -> SCNMaterial 54 | { 55 | var nsb = NSBundle.mainBundle().pathForResource(textureFilename, ofType: nil, inDirectory: directory) 56 | let im = UIImage(contentsOfFile: nsb!) 57 | 58 | let mat = SCNMaterial() 59 | mat.diffuse.contents = im 60 | 61 | if(normalFilename != nil) 62 | { 63 | mat.normal.contents = UIImage(contentsOfFile: NSBundle.mainBundle().pathForResource(normalFilename, ofType: nil, inDirectory: directory)!) 64 | } 65 | 66 | if(specularFilename != nil) 67 | { 68 | mat.specular.contents = UIImage(contentsOfFile: NSBundle.mainBundle().pathForResource(specularFilename, ofType: nil, inDirectory: directory)!) 69 | } 70 | 71 | repeatMat(mat, wRepeat: ureps,hRepeat: vreps) 72 | 73 | return mat 74 | } 75 | 76 | class func repeatMat(mat: SCNMaterial, wRepeat: Float, hRepeat: Float) 77 | { 78 | mat.diffuse.contentsTransform = SCNMatrix4MakeScale(wRepeat, hRepeat, 1.0) 79 | mat.diffuse.wrapS = .Repeat 80 | mat.diffuse.wrapT = .Repeat 81 | 82 | mat.normal.wrapS = .Repeat 83 | mat.normal.wrapT = .Repeat 84 | 85 | mat.specular.wrapS = .Repeat 86 | mat.specular.wrapT = .Repeat 87 | } 88 | 89 | // Return the normal against the plane defined by the 3 vertices, specified in 90 | // counter-clockwise order. 91 | // note, this is an un-normalized normal. (ha.. wtf? yah, thats right) 92 | class func getNormal(v0: SCNVector3, v1: SCNVector3, v2: SCNVector3) -> SCNVector3 93 | { 94 | // there are three edges defined by these 3 vertices, but we only need 2 to define the plane 95 | var edgev0v1 = v1 - v0 96 | var edgev1v2 = v2 - v1 97 | 98 | // Assume the verts are expressed in counter-clockwise order to determine normal 99 | return edgev0v1.cross(edgev1v2) 100 | } 101 | } 102 | 103 | // The following SCNVector3 extension comes from https://github.com/devindazzle/SCNVector3Extensions - with some changes by me 104 | 105 | extension CGPoint 106 | { 107 | init(x: Float, y: Float) 108 | { 109 | self.init(x: CGFloat(x), y: CGFloat(y)) 110 | } 111 | } 112 | 113 | extension SCNVector3 114 | { 115 | /** 116 | * Negates the vector described by SCNVector3 and returns 117 | * the result as a new SCNVector3. 118 | */ 119 | func negate() -> SCNVector3 { 120 | return self * -1 121 | } 122 | 123 | /** 124 | * Negates the vector described by SCNVector3 125 | */ 126 | mutating func negated() -> SCNVector3 { 127 | self = negate() 128 | return self 129 | } 130 | 131 | /** 132 | * Returns the length (magnitude) of the vector described by the SCNVector3 133 | */ 134 | func length() -> Float { 135 | return sqrt(x*x + y*y + z*z) 136 | } 137 | 138 | /** 139 | * Normalizes the vector described by the SCNVector3 to length 1.0 and returns 140 | * the result as a new SCNVector3. 141 | */ 142 | func normalized() -> SCNVector3? { 143 | 144 | var len = length() 145 | if(len > 0) 146 | { 147 | return self / length() 148 | } 149 | else 150 | { 151 | return nil 152 | } 153 | } 154 | 155 | /** 156 | * Normalizes the vector described by the SCNVector3 to length 1.0. 157 | */ 158 | mutating func normalize() -> SCNVector3? { 159 | if let vn = normalized() 160 | { 161 | self = vn 162 | return self 163 | } 164 | return nil 165 | } 166 | 167 | /** 168 | * Calculates the distance between two SCNVector3. Pythagoras! 169 | */ 170 | func distance(vector: SCNVector3) -> Float { 171 | return (self - vector).length() 172 | } 173 | 174 | /** 175 | * Calculates the dot product between two SCNVector3. 176 | */ 177 | func dot(vector: SCNVector3) -> Float { 178 | return x * vector.x + y * vector.y + z * vector.z 179 | } 180 | 181 | /** 182 | * Calculates the cross product between two SCNVector3. 183 | */ 184 | func cross(vector: SCNVector3) -> SCNVector3 { 185 | return SCNVector3Make(y * vector.z - z * vector.y, z * vector.x - x * vector.z, x * vector.y - y * vector.x) 186 | } 187 | 188 | func toString() -> String 189 | { 190 | return "SCNVector3(x:\(x), y:\(y), z:\(z)" 191 | } 192 | 193 | // Return the angle between this vector and the specified vector v 194 | func angle(v: SCNVector3) -> Float 195 | { 196 | // angle between 3d vectors P and Q is equal to the arc cos of their dot products over the product of 197 | // their magnitudes (lengths). 198 | // theta = arccos( (P • Q) / (|P||Q|) ) 199 | let dp = dot(v) // dot product 200 | let magProduct = length() * v.length() // product of lengths (magnitudes) 201 | return acos(dp / magProduct) // DONE 202 | } 203 | 204 | mutating func constrain(min: SCNVector3, max: SCNVector3) -> SCNVector3 { 205 | if(x < min.x) { self.x = min.x } 206 | if(x > max.x) { self.x = max.x } 207 | 208 | if(y < min.y) { self.y = min.y } 209 | if(y > max.y) { self.y = max.y } 210 | 211 | if(z < min.z) { self.z = min.z } 212 | if(z > max.z) { self.z = max.z } 213 | 214 | return self 215 | } 216 | } 217 | 218 | /** 219 | * Adds two SCNVector3 vectors and returns the result as a new SCNVector3. 220 | */ 221 | func + (left: SCNVector3, right: SCNVector3) -> SCNVector3 { 222 | return SCNVector3Make(left.x + right.x, left.y + right.y, left.z + right.z) 223 | } 224 | 225 | /** 226 | * Increments a SCNVector3 with the value of another. 227 | */ 228 | func += (inout left: SCNVector3, right: SCNVector3) { 229 | left = left + right 230 | } 231 | 232 | /** 233 | * Subtracts two SCNVector3 vectors and returns the result as a new SCNVector3. 234 | */ 235 | func - (left: SCNVector3, right: SCNVector3) -> SCNVector3 { 236 | return SCNVector3Make(left.x - right.x, left.y - right.y, left.z - right.z) 237 | } 238 | 239 | /** 240 | * Decrements a SCNVector3 with the value of another. 241 | */ 242 | func -= (inout left: SCNVector3, right: SCNVector3) { 243 | left = left - right 244 | } 245 | 246 | /** 247 | * Multiplies two SCNVector3 vectors and returns the result as a new SCNVector3. 248 | */ 249 | func * (left: SCNVector3, right: SCNVector3) -> SCNVector3 { 250 | return SCNVector3Make(left.x * right.x, left.y * right.y, left.z * right.z) 251 | } 252 | 253 | /** 254 | * Multiplies a SCNVector3 with another. 255 | */ 256 | func *= (inout left: SCNVector3, right: SCNVector3) { 257 | left = left * right 258 | } 259 | 260 | /** 261 | * Multiplies the x, y and z fields of a SCNVector3 with the same scalar value and 262 | * returns the result as a new SCNVector3. 263 | */ 264 | func * (vector: SCNVector3, scalar: Float) -> SCNVector3 { 265 | return SCNVector3Make(vector.x * scalar, vector.y * scalar, vector.z * scalar) 266 | } 267 | 268 | /** 269 | * Multiplies the x and y fields of a SCNVector3 with the same scalar value. 270 | */ 271 | func *= (inout vector: SCNVector3, scalar: Float) { 272 | vector = vector * scalar 273 | } 274 | 275 | /** 276 | * Divides two SCNVector3 vectors abd returns the result as a new SCNVector3 277 | */ 278 | func / (left: SCNVector3, right: SCNVector3) -> SCNVector3 { 279 | return SCNVector3Make(left.x / right.x, left.y / right.y, left.z / right.z) 280 | } 281 | 282 | /** 283 | * Divides a SCNVector3 by another. 284 | */ 285 | func /= (inout left: SCNVector3, right: SCNVector3) { 286 | left = left / right 287 | } 288 | 289 | /** 290 | * Divides the x, y and z fields of a SCNVector3 by the same scalar value and 291 | * returns the result as a new SCNVector3. 292 | */ 293 | func / (vector: SCNVector3, scalar: Float) -> SCNVector3 { 294 | return SCNVector3Make(vector.x / scalar, vector.y / scalar, vector.z / scalar) 295 | } 296 | 297 | /** 298 | * Divides the x, y and z of a SCNVector3 by the same scalar value. 299 | */ 300 | func /= (inout vector: SCNVector3, scalar: Float) { 301 | vector = vector / scalar 302 | } 303 | 304 | /** 305 | * Calculates the SCNVector from lerping between two SCNVector3 vectors 306 | */ 307 | func SCNVector3Lerp(vectorStart: SCNVector3, vectorEnd: SCNVector3, t: Float) -> SCNVector3 { 308 | return SCNVector3Make(vectorStart.x + ((vectorEnd.x - vectorStart.x) * t), vectorStart.y + ((vectorEnd.y - vectorStart.y) * t), vectorStart.z + ((vectorEnd.z - vectorStart.z) * t)) 309 | } 310 | 311 | /** 312 | * Project the vector, vectorToProject, onto the vector, projectionVector. 313 | */ 314 | func SCNVector3Project(vectorToProject: SCNVector3, projectionVector: SCNVector3) -> SCNVector3 { 315 | let scale: Float = projectionVector.dot(vectorToProject) / projectionVector.dot(projectionVector) 316 | let v: SCNVector3 = projectionVector * scale 317 | return v 318 | } 319 | 320 | // Define a couple structures that hold GLFloats (3 and 2) 321 | struct Float3 { var x, y, z: GLfloat } 322 | struct Float2 { var s, t: GLfloat } 323 | -------------------------------------------------------------------------------- /GeoTools4iOS/textures/brickTexture/diffuse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluejava/GeoTools4iOS/3c24a93431899cee86cf06e7bfaa45d1796c6714/GeoTools4iOS/textures/brickTexture/diffuse.jpg -------------------------------------------------------------------------------- /GeoTools4iOS/textures/brickTexture/normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluejava/GeoTools4iOS/3c24a93431899cee86cf06e7bfaa45d1796c6714/GeoTools4iOS/textures/brickTexture/normal.jpg -------------------------------------------------------------------------------- /GeoTools4iOS/textures/brickTexture/specular.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bluejava/GeoTools4iOS/3c24a93431899cee86cf06e7bfaa45d1796c6714/GeoTools4iOS/textures/brickTexture/specular.jpg -------------------------------------------------------------------------------- /GeoTools4iOSTests/GeoTools4iOSTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeoTools4iOSTests.swift 3 | // GeoTools4iOSTests 4 | // 5 | // Created by Glenn Crownover on 5/4/15. 6 | // Copyright (c) 2015 bluejava. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | 12 | class GeoTools4iOSTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /GeoTools4iOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.bluejava.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Glenn Crownover 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GeoTools4iOS 2 | 3 | **A Swift Library to Aid in the Creation of Custom Geometry for SceneKit (iOS)** 4 | 5 | **Note:** This is the iOS version of this same set of tools for OS X called [GeoTools](https://github.com/bluejava/GeoTools) Having separate projects is necessary to allow for the changes between Cocoa and Cocoa Touch - but also a change was required in the defining of geometry due to a bug in the iOS implementation of SceneKit which caused texture mapping to break. 6 | 7 | What follows is exactly the same as the README for [GeoTools](https://github.com/bluejava/GeoTools): 8 | 9 | ------ 10 | 11 | Use this very small library (currently just 3 classes) to easily build custom geometry in your Swift-based SceneKit projects for OS X (iOS version soon to follow). It uses the concept of "Quads" rather than "Triangles" to build up your geometry, making it easier to visualize, create and texture map shapes. It also offers support for tiling your textures across "odd" shapes without skewing. 12 | 13 | ![Custom Geometry Example](http://www.bluejava.com/int/images/GeoTools01.jpg "Custom Geometry Example") 14 | 15 | *The example above is a custom 4-sided geometry with a tiled texture.* 16 | 17 | ## Project Goals 18 | 19 | My goal was to make it as simple as possible to build complex custom geometries with minimal code. I also wanted to enable tiling texture mapping that is sensitive to geometry 20 | 21 | ## How to Use 22 | Building custom geometry entails creating a `GeometryBuilder` instance - adding `Quad` objects to it to form your shape, then calling `getGeometry()` on it to obtain your geometry. I based it on Quads (though internally the mesh is built using Triangles) - as it is easier to think of custom shapes as built up from quads. 23 | 24 | ``` 25 | v1 --------------v0 26 | | __/ | 27 | | __/ | 28 | | __/ | 29 | | __/ | 30 | | __/ | 31 | v2 ------------- v3 32 | ``` 33 | 34 | A `Quad` is made up of four vertices on a plane which make up a quadrilateral shape. Think of vertices as labeled v0 through v3 - starting with v0 at the upper right corner - v1 at upper left - v2 at bottom left - and v3 at lower right, as illustrated above. The opposing edges need not be parallel of course, allowing for shapes such as the textured brick one above. 35 | 36 | **Note:** The order of defining your vertices is important. The order determines the "normal" of the face which dictates which side of the face is visible. My code assumes a counter-clockwise order of vertices as illustrated above. I always think of drawing the letter *C* while staring towards the visible side of the face. Also keep in mind that the upper/lower/left/right referred to here is not related to the world geometry, but only to the face you are defining. 37 | 38 | ### Code Example: 39 | 40 | ```Swift 41 | // First, define the four vertices of a Quad 42 | var v0 = SCNVector3(x: 6, y: 6.0, z: 6) 43 | var v1 = SCNVector3(x: 1, y: 4, z: 1) 44 | var v2 = SCNVector3(x: 2, y: 0, z: 2) 45 | var v3 = SCNVector3(x: 5, y: -2, z: 5) 46 | 47 | // Instantiate a GeometryBuilder and add a Quad 48 | var geobuild = GeometryBuilder() 49 | geobuild.addQuad(Quad(v0: v0,v1: v1,v2: v2,v3: v3)) 50 | 51 | // And here is our new Geometry 52 | var geo = geobuild.getGeometry() 53 | 54 | // Now we can simply create a node using that geometry 55 | var node = SCNNode(geometry: geo) 56 | ``` 57 | 58 | That's it - the above code will create a node with the quadrilateral shaped plane visible from one side only. Of course you can add materials and textures to the geometry just like any other. 59 | 60 | ## Texture Mapping 61 | Internally, texture mapping is done by defining where in your texture image each of the 3 vertices that make up the Triangle with 0 referring to left or bottom of the image, and 1.0 referring to the right or top edge of your image. To tile a texture such as the bricks shown above without skewing requires some calculation for non-aligned boundaries. The library does this for you (if requested) by considering each unit of world space to be one edge of the texture. 62 | 63 | Control texture mapping by passing the optional uvMode into the `GeometryBuilder`. Currently recognized options are `StretchToFitXY` and `SizeToWorldUnitsXY`. 64 | 65 | ### StretchToFitXY 66 | 67 | ```Swift 68 | var geobuild = GeometryBuilder(uvMode: GeometryBuilder.UVModeType.StretchToFitXY) 69 | ``` 70 | 71 | This tells the GeometryBuilder to stretch the texture to each corner of the `Quad`. This can work well when your shape is square or your texture was created for that specific shape - but can often have undesireable effects when it does not: 72 | 73 | ![Custom Geometry Example](http://www.bluejava.com/int/images/GeoTools02.jpg "Custom Geometry Example") 74 | 75 | Note the skewing in the parallelogram and the angled discontinuity occuring at the diagonal edge in the non-parallelogram shape. 76 | 77 | ### SizeToWorldUnitsXY 78 | 79 | ```Swift 80 | var geobuild = GeometryBuilder(uvMode: GeometryBuilder.UVModeType.SizeToWorldUnitsXY) 81 | ``` 82 | 83 | This option considers your texture to be a single unit in "width" and "height" (U and V), then tiles across your shape without skewing or distortion. This is very useful when you have a material pattern (such as fabric, brick, stones, cement, steel, etc.) and wish to tile it across a complex shape realistically: 84 | 85 | ![SizeToWorldUnitsXY Example](http://www.bluejava.com/int/images/GeoTools03.jpg "SizeToWorldUnitsXY Example") 86 | 87 | The bottom left corner is alligned with the *V2* vertice in your `Quad` and then it tiles up, to the right, and to the left seamlessly, with each one unit square getting one complete tile of the texture. 88 | 89 | 90 | ### About this project 91 | 92 | I am working on a project (a game) that requires custom mesh geometry using Swift and SceneKit. When I began looking into how to do this I found the documentation somewhat incomplete, and I didn't find any complete examples which also covered texture mapping and/or dealt with some of the idiosyncrasies of doing it in Swift. So once I figured it out and got things working I thought maybe others could benefit from this as well. 93 | 94 | I actually needed this functionality for iOS, but was working through this issue on OS X (for convenience/efficiency sake). Once I had it working on OS X I moved it to my iOS project, and there were problems with the textures. I believe this is a bug in the iOS implementation of SceneKit for texture mapping. I have since solved that problem and will publish that as a separate project here. --------------------------------------------------------------------------------