├── README.md ├── Terrain.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── matthewwaller.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist └── Terrain ├── AppDelegate.swift ├── Assets.xcassets ├── AccentColor.colorset │ └── Contents.json ├── AppIcon.appiconset │ └── Contents.json ├── Contents.json └── grid.imageset │ ├── Contents.json │ └── grid_transparent.png ├── ContentView.swift └── Preview Content └── Preview Assets.xcassets └── Contents.json /README.md: -------------------------------------------------------------------------------- 1 | Quick and dirty, just a small example of procedurally generating terrain using RealityKit. 2 | 3 | Converted from Scenekit here: https://github.com/rogerboesch/SceneKitTutorial/ 4 | -------------------------------------------------------------------------------- /Terrain.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DADA79392BFCD2F90096F987 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DADA79382BFCD2F90096F987 /* AppDelegate.swift */; }; 11 | DADA793B2BFCD2F90096F987 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DADA793A2BFCD2F90096F987 /* ContentView.swift */; }; 12 | DADA793D2BFCD2FC0096F987 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DADA793C2BFCD2FC0096F987 /* Assets.xcassets */; }; 13 | DADA79402BFCD2FC0096F987 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DADA793F2BFCD2FC0096F987 /* Preview Assets.xcassets */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXFileReference section */ 17 | DADA79352BFCD2F90096F987 /* Terrain.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Terrain.app; sourceTree = BUILT_PRODUCTS_DIR; }; 18 | DADA79382BFCD2F90096F987 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 19 | DADA793A2BFCD2F90096F987 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 20 | DADA793C2BFCD2FC0096F987 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 21 | DADA793F2BFCD2FC0096F987 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 22 | /* End PBXFileReference section */ 23 | 24 | /* Begin PBXFrameworksBuildPhase section */ 25 | DADA79322BFCD2F90096F987 /* Frameworks */ = { 26 | isa = PBXFrameworksBuildPhase; 27 | buildActionMask = 2147483647; 28 | files = ( 29 | ); 30 | runOnlyForDeploymentPostprocessing = 0; 31 | }; 32 | /* End PBXFrameworksBuildPhase section */ 33 | 34 | /* Begin PBXGroup section */ 35 | DADA792C2BFCD2F90096F987 = { 36 | isa = PBXGroup; 37 | children = ( 38 | DADA79372BFCD2F90096F987 /* Terrain */, 39 | DADA79362BFCD2F90096F987 /* Products */, 40 | ); 41 | sourceTree = ""; 42 | }; 43 | DADA79362BFCD2F90096F987 /* Products */ = { 44 | isa = PBXGroup; 45 | children = ( 46 | DADA79352BFCD2F90096F987 /* Terrain.app */, 47 | ); 48 | name = Products; 49 | sourceTree = ""; 50 | }; 51 | DADA79372BFCD2F90096F987 /* Terrain */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | DADA79382BFCD2F90096F987 /* AppDelegate.swift */, 55 | DADA793A2BFCD2F90096F987 /* ContentView.swift */, 56 | DADA793C2BFCD2FC0096F987 /* Assets.xcassets */, 57 | DADA793E2BFCD2FC0096F987 /* Preview Content */, 58 | ); 59 | path = Terrain; 60 | sourceTree = ""; 61 | }; 62 | DADA793E2BFCD2FC0096F987 /* Preview Content */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | DADA793F2BFCD2FC0096F987 /* Preview Assets.xcassets */, 66 | ); 67 | path = "Preview Content"; 68 | sourceTree = ""; 69 | }; 70 | /* End PBXGroup section */ 71 | 72 | /* Begin PBXNativeTarget section */ 73 | DADA79342BFCD2F90096F987 /* Terrain */ = { 74 | isa = PBXNativeTarget; 75 | buildConfigurationList = DADA79432BFCD2FC0096F987 /* Build configuration list for PBXNativeTarget "Terrain" */; 76 | buildPhases = ( 77 | DADA79312BFCD2F90096F987 /* Sources */, 78 | DADA79322BFCD2F90096F987 /* Frameworks */, 79 | DADA79332BFCD2F90096F987 /* Resources */, 80 | ); 81 | buildRules = ( 82 | ); 83 | dependencies = ( 84 | ); 85 | name = Terrain; 86 | productName = Terrain; 87 | productReference = DADA79352BFCD2F90096F987 /* Terrain.app */; 88 | productType = "com.apple.product-type.application"; 89 | }; 90 | /* End PBXNativeTarget section */ 91 | 92 | /* Begin PBXProject section */ 93 | DADA792D2BFCD2F90096F987 /* Project object */ = { 94 | isa = PBXProject; 95 | attributes = { 96 | BuildIndependentTargetsInParallel = 1; 97 | LastSwiftUpdateCheck = 1540; 98 | LastUpgradeCheck = 1540; 99 | TargetAttributes = { 100 | DADA79342BFCD2F90096F987 = { 101 | CreatedOnToolsVersion = 15.4; 102 | }; 103 | }; 104 | }; 105 | buildConfigurationList = DADA79302BFCD2F90096F987 /* Build configuration list for PBXProject "Terrain" */; 106 | compatibilityVersion = "Xcode 14.0"; 107 | developmentRegion = en; 108 | hasScannedForEncodings = 0; 109 | knownRegions = ( 110 | en, 111 | Base, 112 | ); 113 | mainGroup = DADA792C2BFCD2F90096F987; 114 | productRefGroup = DADA79362BFCD2F90096F987 /* Products */; 115 | projectDirPath = ""; 116 | projectRoot = ""; 117 | targets = ( 118 | DADA79342BFCD2F90096F987 /* Terrain */, 119 | ); 120 | }; 121 | /* End PBXProject section */ 122 | 123 | /* Begin PBXResourcesBuildPhase section */ 124 | DADA79332BFCD2F90096F987 /* Resources */ = { 125 | isa = PBXResourcesBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | DADA79402BFCD2FC0096F987 /* Preview Assets.xcassets in Resources */, 129 | DADA793D2BFCD2FC0096F987 /* Assets.xcassets in Resources */, 130 | ); 131 | runOnlyForDeploymentPostprocessing = 0; 132 | }; 133 | /* End PBXResourcesBuildPhase section */ 134 | 135 | /* Begin PBXSourcesBuildPhase section */ 136 | DADA79312BFCD2F90096F987 /* Sources */ = { 137 | isa = PBXSourcesBuildPhase; 138 | buildActionMask = 2147483647; 139 | files = ( 140 | DADA793B2BFCD2F90096F987 /* ContentView.swift in Sources */, 141 | DADA79392BFCD2F90096F987 /* AppDelegate.swift in Sources */, 142 | ); 143 | runOnlyForDeploymentPostprocessing = 0; 144 | }; 145 | /* End PBXSourcesBuildPhase section */ 146 | 147 | /* Begin XCBuildConfiguration section */ 148 | DADA79412BFCD2FC0096F987 /* Debug */ = { 149 | isa = XCBuildConfiguration; 150 | buildSettings = { 151 | ALWAYS_SEARCH_USER_PATHS = NO; 152 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 153 | CLANG_ANALYZER_NONNULL = YES; 154 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 155 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 156 | CLANG_ENABLE_MODULES = YES; 157 | CLANG_ENABLE_OBJC_ARC = YES; 158 | CLANG_ENABLE_OBJC_WEAK = YES; 159 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 160 | CLANG_WARN_BOOL_CONVERSION = YES; 161 | CLANG_WARN_COMMA = YES; 162 | CLANG_WARN_CONSTANT_CONVERSION = YES; 163 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 164 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 165 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 166 | CLANG_WARN_EMPTY_BODY = YES; 167 | CLANG_WARN_ENUM_CONVERSION = YES; 168 | CLANG_WARN_INFINITE_RECURSION = YES; 169 | CLANG_WARN_INT_CONVERSION = YES; 170 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 171 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 172 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 173 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 174 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 175 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 176 | CLANG_WARN_STRICT_PROTOTYPES = YES; 177 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 178 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 179 | CLANG_WARN_UNREACHABLE_CODE = YES; 180 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 181 | COPY_PHASE_STRIP = NO; 182 | DEBUG_INFORMATION_FORMAT = dwarf; 183 | ENABLE_STRICT_OBJC_MSGSEND = YES; 184 | ENABLE_TESTABILITY = YES; 185 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 186 | GCC_C_LANGUAGE_STANDARD = gnu17; 187 | GCC_DYNAMIC_NO_PIC = NO; 188 | GCC_NO_COMMON_BLOCKS = YES; 189 | GCC_OPTIMIZATION_LEVEL = 0; 190 | GCC_PREPROCESSOR_DEFINITIONS = ( 191 | "DEBUG=1", 192 | "$(inherited)", 193 | ); 194 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 195 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 196 | GCC_WARN_UNDECLARED_SELECTOR = YES; 197 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 198 | GCC_WARN_UNUSED_FUNCTION = YES; 199 | GCC_WARN_UNUSED_VARIABLE = YES; 200 | IPHONEOS_DEPLOYMENT_TARGET = 17.5; 201 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 202 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 203 | MTL_FAST_MATH = YES; 204 | ONLY_ACTIVE_ARCH = YES; 205 | SDKROOT = iphoneos; 206 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; 207 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 208 | }; 209 | name = Debug; 210 | }; 211 | DADA79422BFCD2FC0096F987 /* Release */ = { 212 | isa = XCBuildConfiguration; 213 | buildSettings = { 214 | ALWAYS_SEARCH_USER_PATHS = NO; 215 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 216 | CLANG_ANALYZER_NONNULL = YES; 217 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 218 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 219 | CLANG_ENABLE_MODULES = YES; 220 | CLANG_ENABLE_OBJC_ARC = YES; 221 | CLANG_ENABLE_OBJC_WEAK = YES; 222 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 223 | CLANG_WARN_BOOL_CONVERSION = YES; 224 | CLANG_WARN_COMMA = YES; 225 | CLANG_WARN_CONSTANT_CONVERSION = YES; 226 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 227 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 228 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 229 | CLANG_WARN_EMPTY_BODY = YES; 230 | CLANG_WARN_ENUM_CONVERSION = YES; 231 | CLANG_WARN_INFINITE_RECURSION = YES; 232 | CLANG_WARN_INT_CONVERSION = YES; 233 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 234 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 235 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 236 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 237 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 238 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 239 | CLANG_WARN_STRICT_PROTOTYPES = YES; 240 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 241 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 242 | CLANG_WARN_UNREACHABLE_CODE = YES; 243 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 244 | COPY_PHASE_STRIP = NO; 245 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 246 | ENABLE_NS_ASSERTIONS = NO; 247 | ENABLE_STRICT_OBJC_MSGSEND = YES; 248 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 249 | GCC_C_LANGUAGE_STANDARD = gnu17; 250 | GCC_NO_COMMON_BLOCKS = YES; 251 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 252 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 253 | GCC_WARN_UNDECLARED_SELECTOR = YES; 254 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 255 | GCC_WARN_UNUSED_FUNCTION = YES; 256 | GCC_WARN_UNUSED_VARIABLE = YES; 257 | IPHONEOS_DEPLOYMENT_TARGET = 17.5; 258 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 259 | MTL_ENABLE_DEBUG_INFO = NO; 260 | MTL_FAST_MATH = YES; 261 | SDKROOT = iphoneos; 262 | SWIFT_COMPILATION_MODE = wholemodule; 263 | VALIDATE_PRODUCT = YES; 264 | }; 265 | name = Release; 266 | }; 267 | DADA79442BFCD2FC0096F987 /* Debug */ = { 268 | isa = XCBuildConfiguration; 269 | buildSettings = { 270 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 271 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 272 | CODE_SIGN_STYLE = Automatic; 273 | CURRENT_PROJECT_VERSION = 1; 274 | DEVELOPMENT_ASSET_PATHS = "\"Terrain/Preview Content\""; 275 | DEVELOPMENT_TEAM = QBL739225X; 276 | ENABLE_PREVIEWS = YES; 277 | GENERATE_INFOPLIST_FILE = YES; 278 | INFOPLIST_KEY_NSCameraUsageDescription = ""; 279 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 280 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 281 | INFOPLIST_KEY_UIRequiredDeviceCapabilities = arkit; 282 | INFOPLIST_KEY_UIStatusBarHidden = YES; 283 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 284 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 285 | LD_RUNPATH_SEARCH_PATHS = ( 286 | "$(inherited)", 287 | "@executable_path/Frameworks", 288 | ); 289 | MARKETING_VERSION = 1.0; 290 | PRODUCT_BUNDLE_IDENTIFIER = com.Cephalopod.Terrain; 291 | PRODUCT_NAME = "$(TARGET_NAME)"; 292 | SWIFT_EMIT_LOC_STRINGS = YES; 293 | SWIFT_VERSION = 5.0; 294 | TARGETED_DEVICE_FAMILY = "1,2"; 295 | }; 296 | name = Debug; 297 | }; 298 | DADA79452BFCD2FC0096F987 /* Release */ = { 299 | isa = XCBuildConfiguration; 300 | buildSettings = { 301 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 302 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 303 | CODE_SIGN_STYLE = Automatic; 304 | CURRENT_PROJECT_VERSION = 1; 305 | DEVELOPMENT_ASSET_PATHS = "\"Terrain/Preview Content\""; 306 | DEVELOPMENT_TEAM = QBL739225X; 307 | ENABLE_PREVIEWS = YES; 308 | GENERATE_INFOPLIST_FILE = YES; 309 | INFOPLIST_KEY_NSCameraUsageDescription = ""; 310 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 311 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 312 | INFOPLIST_KEY_UIRequiredDeviceCapabilities = arkit; 313 | INFOPLIST_KEY_UIStatusBarHidden = YES; 314 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 315 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 316 | LD_RUNPATH_SEARCH_PATHS = ( 317 | "$(inherited)", 318 | "@executable_path/Frameworks", 319 | ); 320 | MARKETING_VERSION = 1.0; 321 | PRODUCT_BUNDLE_IDENTIFIER = com.Cephalopod.Terrain; 322 | PRODUCT_NAME = "$(TARGET_NAME)"; 323 | SWIFT_EMIT_LOC_STRINGS = YES; 324 | SWIFT_VERSION = 5.0; 325 | TARGETED_DEVICE_FAMILY = "1,2"; 326 | }; 327 | name = Release; 328 | }; 329 | /* End XCBuildConfiguration section */ 330 | 331 | /* Begin XCConfigurationList section */ 332 | DADA79302BFCD2F90096F987 /* Build configuration list for PBXProject "Terrain" */ = { 333 | isa = XCConfigurationList; 334 | buildConfigurations = ( 335 | DADA79412BFCD2FC0096F987 /* Debug */, 336 | DADA79422BFCD2FC0096F987 /* Release */, 337 | ); 338 | defaultConfigurationIsVisible = 0; 339 | defaultConfigurationName = Release; 340 | }; 341 | DADA79432BFCD2FC0096F987 /* Build configuration list for PBXNativeTarget "Terrain" */ = { 342 | isa = XCConfigurationList; 343 | buildConfigurations = ( 344 | DADA79442BFCD2FC0096F987 /* Debug */, 345 | DADA79452BFCD2FC0096F987 /* Release */, 346 | ); 347 | defaultConfigurationIsVisible = 0; 348 | defaultConfigurationName = Release; 349 | }; 350 | /* End XCConfigurationList section */ 351 | }; 352 | rootObject = DADA792D2BFCD2F90096F987 /* Project object */; 353 | } 354 | -------------------------------------------------------------------------------- /Terrain.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Terrain.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Terrain.xcodeproj/xcuserdata/matthewwaller.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Terrain.xcodeproj/xcuserdata/matthewwaller.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Terrain.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Terrain/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Terrain 4 | // 5 | // Created by Matthew Waller on 5/21/24. 6 | // 7 | 8 | import UIKit 9 | import SwiftUI 10 | 11 | @main 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | 19 | // Create the SwiftUI view that provides the window contents. 20 | let contentView = ContentView() 21 | 22 | // Use a UIHostingController as window root view controller. 23 | let window = UIWindow(frame: UIScreen.main.bounds) 24 | window.rootViewController = UIHostingController(rootView: contentView) 25 | self.window = window 26 | window.makeKeyAndVisible() 27 | return true 28 | } 29 | 30 | func applicationWillResignActive(_ application: UIApplication) { 31 | // 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. 32 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 33 | } 34 | 35 | func applicationDidEnterBackground(_ application: UIApplication) { 36 | // 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. 37 | } 38 | 39 | func applicationWillEnterForeground(_ application: UIApplication) { 40 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 41 | } 42 | 43 | func applicationDidBecomeActive(_ application: UIApplication) { 44 | // 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. 45 | } 46 | 47 | 48 | } 49 | 50 | -------------------------------------------------------------------------------- /Terrain/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Terrain/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Terrain/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Terrain/Assets.xcassets/grid.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "grid_transparent.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Terrain/Assets.xcassets/grid.imageset/grid_transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatthewWaller/Terrain/951b14d165fa4dc3b06bc8594a90b806613956b9/Terrain/Assets.xcassets/grid.imageset/grid_transparent.png -------------------------------------------------------------------------------- /Terrain/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Terrain 4 | // 5 | // Created by Matthew Waller on 5/21/24. 6 | // 7 | 8 | import SwiftUI 9 | import RealityKit 10 | 11 | struct ContentView : View { 12 | var body: some View { 13 | ARViewContainer().edgesIgnoringSafeArea(.all) 14 | } 15 | } 16 | 17 | struct ARViewContainer: UIViewRepresentable { 18 | 19 | func makeUIView(context: Context) -> ARView { 20 | 21 | let arView = ARView(frame: .zero, cameraMode: .nonAR, automaticallyConfigureSession: true) 22 | 23 | let anchor = AnchorEntity(world: .init(-2, -3, -32)) 24 | 25 | let terrain = RBTerrain() 26 | 27 | let generator = RBPerlinNoiseGenerator(seed: nil) 28 | terrain.formula = {(x: Int32, y: Int32) in 29 | return generator.valueFor(x: x, y: y) 30 | } 31 | 32 | terrain.create(withImage: UIImage(named: "grid")!) 33 | // terrain.create(withColor: .orange) 34 | anchor.children.append(terrain) 35 | 36 | // Add the horizontal plane anchor to the scene 37 | arView.scene.anchors.append(anchor) 38 | terrain.generateCollisionShapes(recursive: true) 39 | arView.installGestures([.all], for: terrain) 40 | return arView 41 | 42 | } 43 | 44 | func updateUIView(_ uiView: ARView, context: Context) {} 45 | 46 | } 47 | 48 | #Preview { 49 | ContentView() 50 | } 51 | 52 | import Foundation 53 | import RealityKit 54 | import UIKit 55 | import simd 56 | 57 | typealias RBTerrainFormula = ((Int32, Int32) -> (Double)) 58 | 59 | class RBTerrain: Entity, HasModel, HasCollision { 60 | private var _heightScale = 256 61 | private var _terrainWidth = 32 62 | private var _terrainLength = 32 63 | private var _texture: UIImage? 64 | private var _color = UIColor.white 65 | 66 | var formula: RBTerrainFormula? 67 | 68 | var length: Int { 69 | return _terrainLength 70 | } 71 | 72 | var width: Int { 73 | return _terrainWidth 74 | } 75 | 76 | var texture: UIImage? { 77 | get { 78 | return _texture 79 | } 80 | set(value) { 81 | _texture = value 82 | updateMaterial() 83 | } 84 | } 85 | 86 | var color: UIColor { 87 | get { 88 | return _color 89 | } 90 | set(value) { 91 | _color = value 92 | updateMaterial() 93 | } 94 | } 95 | 96 | func valueFor(x: Int32, y: Int32) -> Double { 97 | return formula?(x, y) ?? 0.0 98 | } 99 | 100 | private func updateMaterial() { 101 | guard let modelEntity = self.children.first as? ModelEntity else { return } 102 | 103 | if let texture = _texture?.cgImage { 104 | var material = SimpleMaterial() 105 | 106 | let textureMade: TextureResource = try! .generate(from: texture, options: .init(semantic: nil)) 107 | let baseColor = MaterialParameters.Texture(textureMade) 108 | 109 | material.color = .init(tint: .white, texture: baseColor) 110 | modelEntity.model?.materials = [material] 111 | } else { 112 | let material = UnlitMaterial.init(color: _color) 113 | modelEntity.model?.materials = [material] 114 | self.children[0] = modelEntity 115 | } 116 | } 117 | 118 | private func createGeometry() -> MeshResource { 119 | var vertices: [SIMD3] = [] 120 | var indices: [UInt32] = [] 121 | 122 | let w = Float(_terrainWidth) 123 | let h = Float(_terrainLength) 124 | let scale = Float(_heightScale) 125 | 126 | for y in 0..(Float(x), topLeftZ, Float(y + 1)) 134 | let topRight = SIMD3(Float(x + 1), topRightZ, Float(y + 1)) 135 | let bottomLeft = SIMD3(Float(x), bottomLeftZ, Float(y)) 136 | let bottomRight = SIMD3(Float(x + 1), bottomRightZ, Float(y)) 137 | 138 | vertices.append(contentsOf: [bottomLeft, topLeft, topRight, bottomRight]) 139 | 140 | let index = UInt32(vertices.count) 141 | indices.append(contentsOf: [index - 4, index - 3, index - 2, index - 4, index - 2, index - 1]) 142 | } 143 | } 144 | 145 | var meshDescriptor = MeshDescriptor() 146 | meshDescriptor.positions = MeshBuffer(vertices) 147 | meshDescriptor.primitives = .triangles(indices) 148 | 149 | return try! MeshResource.generate(from: [meshDescriptor]) 150 | } 151 | 152 | func create(withImage image: UIImage?) { 153 | let geometry = createGeometry() 154 | let modelEntity = ModelEntity(mesh: geometry) 155 | self.addChild(modelEntity) 156 | 157 | if let image = image { 158 | self.texture = image 159 | } else { 160 | self.color = UIColor.green 161 | } 162 | } 163 | 164 | func create(withColor color: UIColor) { 165 | let geometry = createGeometry() 166 | let modelEntity = ModelEntity(mesh: geometry) 167 | self.addChild(modelEntity) 168 | 169 | self.color = color 170 | } 171 | 172 | init(width: Int, length: Int, scale: Int) { 173 | super.init() 174 | 175 | _terrainWidth = width 176 | _terrainLength = length 177 | _heightScale = scale 178 | } 179 | 180 | required init() { 181 | super.init() 182 | } 183 | } 184 | 185 | // 186 | // RBPerlinNoiseGenerator.swift 187 | // Perlin noise generator (used for terrain class) 188 | // 189 | // Created by Roger Boesch on 12/07/16. 190 | // Based on Obj-C code created by Steven Troughton-Smith on 24/12/11. 191 | // 192 | 193 | import UIKit 194 | 195 | class RBPerlinNoiseGenerator { 196 | private static let noiseX = 1619 197 | private static let noiseY = 31337 198 | private static let noiseSeed = 1013 199 | 200 | private var _seed: Int = 1 201 | 202 | // ------------------------------------------------------------------------- 203 | 204 | private func interpolate(a: Double, b: Double, x: Double) ->Double { 205 | let ft: Double = x * Double.pi 206 | let f: Double = (1.0-cos(ft)) * 0.5 207 | 208 | return a*(1.0-f)+b*f 209 | } 210 | 211 | // ------------------------------------------------------------------------- 212 | 213 | private func findNoise(x: Double, y: Double) ->Double { 214 | var n = (RBPerlinNoiseGenerator.noiseX*Int(x) + 215 | RBPerlinNoiseGenerator.noiseY*Int(y) + 216 | RBPerlinNoiseGenerator.noiseSeed * _seed) & 0x7fffffff 217 | 218 | n = (n >> 13) ^ n 219 | n = (n &* (n &* n &* 60493 + 19990303) + 1376312589) & 0x7fffffff 220 | 221 | return 1.0 - Double(n)/1073741824 222 | } 223 | 224 | // ------------------------------------------------------------------------- 225 | 226 | private func noise(x: Double, y: Double) ->Double { 227 | let floorX: Double = Double(Int(x)) 228 | let floorY: Double = Double(Int(y)) 229 | 230 | let s = findNoise(x:floorX, y:floorY) 231 | let t = findNoise(x:floorX+1, y:floorY) 232 | let u = findNoise(x:floorX, y:floorY+1) 233 | let v = findNoise(x:floorX+1, y:floorY+1) 234 | 235 | let i1 = interpolate(a:s, b:t, x:x-floorX) 236 | let i2 = interpolate(a:u, b:v, x:x-floorX) 237 | 238 | return interpolate(a:i1, b:i2, x:y-floorY) 239 | } 240 | 241 | // ------------------------------------------------------------------------- 242 | // MARK: - Calculate a noise value for x,y 243 | 244 | func valueFor(x: Int32, y: Int32) ->Double { 245 | let octaves = 2 246 | let p: Double = 1/2 247 | let zoom: Double = 6 248 | var getnoise: Double = 0 249 | 250 | for a in 0.. 255) { 260 | value = 255 261 | } 262 | else if (value < 0) { 263 | value = 0 264 | } 265 | 266 | return value 267 | } 268 | 269 | // ------------------------------------------------------------------------- 270 | // MARK: - Initialisation 271 | 272 | init(seed: Int? = nil) { 273 | if (seed == nil) { 274 | _seed = Int(arc4random()) % Int(INT32_MAX) 275 | } 276 | else { 277 | _seed = seed! 278 | } 279 | } 280 | 281 | // ------------------------------------------------------------------------- 282 | 283 | } 284 | -------------------------------------------------------------------------------- /Terrain/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } --------------------------------------------------------------------------------