├── .gitignore ├── .gitmodules ├── Backend-Microservices └── README.md ├── EduSphere.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcuserdata │ └── mingchungxia.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── EduSphere ├── ContentView.swift ├── EduSphereApp.swift ├── ImageBasedLighting.skybox │ └── ImageBasedLight.exr ├── Info.plist ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Resources │ ├── Assets.xcassets │ │ ├── AppIcon.solidimagestack │ │ │ ├── Back.solidimagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── logo_background.png │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Front.solidimagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── logo_front_layer.png │ │ │ │ └── Contents.json │ │ │ └── Middle.solidimagestacklayer │ │ │ │ ├── Content.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── logo_middle_layer.png │ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── EduSphere_logo.imageset │ │ │ ├── Contents.json │ │ │ └── EduSphere_logo.png │ │ └── Robot.imageset │ │ │ ├── Contents.json │ │ │ └── Robot 4.png │ ├── Chair.usdz │ ├── ChurchScene.jpg │ ├── DockScene.jpg │ ├── LivingRoomScene.jpg │ ├── ParkScene.jpg │ └── StarfieldScene.jpg └── Source │ ├── Features │ ├── About │ │ ├── Models │ │ │ └── AboutInfo.swift │ │ └── Views │ │ │ └── AboutView.swift │ ├── Chatbot │ │ ├── ViewModels │ │ │ └── ChatbotViewModel.swift │ │ └── Views │ │ │ └── ChatbotView.swift │ ├── Common │ │ ├── Managers │ │ │ └── LanguageManager.swift │ │ ├── Models │ │ │ └── Language.swift │ │ └── Views │ │ │ └── ImmersiveView.swift │ ├── Learn │ │ ├── Models │ │ │ ├── ChoiceModel.swift │ │ │ └── Mod3DModel.swift │ │ ├── ViewModels │ │ │ └── LearnViewModel.swift │ │ └── Views │ │ │ └── LearnView.swift │ └── Practice │ │ ├── Models │ │ └── ImmersionModel.swift │ │ ├── ViewModels │ │ └── PracticeViewModel.swift │ │ └── Views │ │ └── PracticeView.swift │ └── SupportingFiles │ ├── WordCollection.swift │ └── WordDecoder.swift ├── LICENSE ├── Packages └── RealityKitContent │ ├── .swiftpm │ └── xcode │ │ └── xcuserdata │ │ └── mingchungxia.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist │ ├── Package.realitycomposerpro │ ├── ProjectData │ │ └── main.json │ └── WorkspaceData │ │ ├── SceneMetadataList.json │ │ └── Settings.rcprojectdata │ ├── Package.swift │ ├── README.md │ └── Sources │ └── RealityKitContent │ ├── RealityKitContent.rkassets │ ├── Immersive.usda │ ├── Scene.usda │ └── _PlainMaterial.usda │ └── RealityKitContent.swift └── README.md /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingchungx/EduSphere-VisionOS/940cf7bced762a1b1d3855b2b0ac77c3432bb4ae/.gitignore -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Backend-Microservices/EduSphere-Flask"] 2 | path = Backend-Microservices/EduSphere-Flask 3 | url = https://github.com/LukeWang2/EduSphere-Flask 4 | [submodule "Backend-Microservices/EduSphere"] 5 | path = Backend-Microservices/EduSphere 6 | url = https://github.com/evinli/EduSphere 7 | -------------------------------------------------------------------------------- /Backend-Microservices/README.md: -------------------------------------------------------------------------------- 1 | # Backend-Microservices 2 | 3 | ## Description 4 | 5 | This directory contains the references for the microservices used on the backend for the EduSphere VisionOS application. 6 | 7 | -------------------------------------------------------------------------------- /EduSphere.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 56; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | BD3DC1F52AB633EF008E3DB0 /* ChoiceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD3DC1F42AB633EF008E3DB0 /* ChoiceModel.swift */; }; 11 | BD3DC1F72AB63868008E3DB0 /* Mod3DModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD3DC1F62AB63868008E3DB0 /* Mod3DModel.swift */; }; 12 | BD3DC1FD2AB63D1F008E3DB0 /* ChatbotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD3DC1FC2AB63D1F008E3DB0 /* ChatbotView.swift */; }; 13 | BD3DC2012AB65FF8008E3DB0 /* WordCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD3DC2002AB65FF8008E3DB0 /* WordCollection.swift */; }; 14 | BD3DC2032AB660D3008E3DB0 /* WordDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD3DC2022AB660D3008E3DB0 /* WordDecoder.swift */; }; 15 | BD3DC2052AB67C97008E3DB0 /* ImmersionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD3DC2042AB67C97008E3DB0 /* ImmersionModel.swift */; }; 16 | BD3DC20D2AB67E76008E3DB0 /* ParkScene.jpg in Resources */ = {isa = PBXBuildFile; fileRef = BD3DC20C2AB67E75008E3DB0 /* ParkScene.jpg */; }; 17 | BD3DC20F2AB68D69008E3DB0 /* ChatbotViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD3DC20E2AB68D69008E3DB0 /* ChatbotViewModel.swift */; }; 18 | BD3DC2152AB6EC3E008E3DB0 /* PracticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD3DC2142AB6EC3E008E3DB0 /* PracticeView.swift */; }; 19 | BD3DC2172AB6EC48008E3DB0 /* PracticeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD3DC2162AB6EC48008E3DB0 /* PracticeViewModel.swift */; }; 20 | BD3DC2192AB70717008E3DB0 /* DockScene.jpg in Resources */ = {isa = PBXBuildFile; fileRef = BD3DC2182AB70717008E3DB0 /* DockScene.jpg */; }; 21 | BD3DC21B2AB70783008E3DB0 /* ChurchScene.jpg in Resources */ = {isa = PBXBuildFile; fileRef = BD3DC21A2AB70783008E3DB0 /* ChurchScene.jpg */; }; 22 | BD3DC21D2AB707D9008E3DB0 /* LivingRoomScene.jpg in Resources */ = {isa = PBXBuildFile; fileRef = BD3DC21C2AB707D8008E3DB0 /* LivingRoomScene.jpg */; }; 23 | BDD07EC92AB5F490001A3CAD /* RealityKitContent in Frameworks */ = {isa = PBXBuildFile; productRef = BDD07EC82AB5F490001A3CAD /* RealityKitContent */; }; 24 | BDD07ECB2AB5F490001A3CAD /* EduSphereApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD07ECA2AB5F490001A3CAD /* EduSphereApp.swift */; }; 25 | BDD07ECD2AB5F490001A3CAD /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD07ECC2AB5F490001A3CAD /* ContentView.swift */; }; 26 | BDD07ECF2AB5F490001A3CAD /* ImmersiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD07ECE2AB5F490001A3CAD /* ImmersiveView.swift */; }; 27 | BDD07ED12AB5F490001A3CAD /* ImageBasedLighting.skybox in Resources */ = {isa = PBXBuildFile; fileRef = BDD07ED02AB5F490001A3CAD /* ImageBasedLighting.skybox */; }; 28 | BDD07ED32AB5F492001A3CAD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BDD07ED22AB5F492001A3CAD /* Assets.xcassets */; }; 29 | BDD07ED62AB5F492001A3CAD /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BDD07ED52AB5F492001A3CAD /* Preview Assets.xcassets */; }; 30 | BDD07EF02AB5F531001A3CAD /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD07EEF2AB5F531001A3CAD /* AboutView.swift */; }; 31 | BDD07EF22AB5F537001A3CAD /* LearnView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD07EF12AB5F537001A3CAD /* LearnView.swift */; }; 32 | BDD07EF52AB5F953001A3CAD /* StarfieldScene.jpg in Resources */ = {isa = PBXBuildFile; fileRef = BDD07EF42AB5F953001A3CAD /* StarfieldScene.jpg */; }; 33 | BDD07EF72AB5FA17001A3CAD /* Chair.usdz in Resources */ = {isa = PBXBuildFile; fileRef = BDD07EF62AB5FA17001A3CAD /* Chair.usdz */; }; 34 | BDD07EF92AB5FBE7001A3CAD /* LearnViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD07EF82AB5FBE7001A3CAD /* LearnViewModel.swift */; }; 35 | BDD07EFC2AB61132001A3CAD /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = BDD07EFB2AB61132001A3CAD /* Alamofire */; }; 36 | BDD07EFE2AB61245001A3CAD /* AboutInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD07EFD2AB61245001A3CAD /* AboutInfo.swift */; }; 37 | BDD07F002AB6159C001A3CAD /* LanguageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD07EFF2AB6159C001A3CAD /* LanguageManager.swift */; }; 38 | BDD07F022AB6183C001A3CAD /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD07F012AB6183C001A3CAD /* Language.swift */; }; 39 | /* End PBXBuildFile section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | BD3DC1F42AB633EF008E3DB0 /* ChoiceModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChoiceModel.swift; sourceTree = ""; }; 43 | BD3DC1F62AB63868008E3DB0 /* Mod3DModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mod3DModel.swift; sourceTree = ""; }; 44 | BD3DC1FC2AB63D1F008E3DB0 /* ChatbotView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatbotView.swift; sourceTree = ""; }; 45 | BD3DC1FE2AB643AC008E3DB0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 46 | BD3DC2002AB65FF8008E3DB0 /* WordCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordCollection.swift; sourceTree = ""; }; 47 | BD3DC2022AB660D3008E3DB0 /* WordDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordDecoder.swift; sourceTree = ""; }; 48 | BD3DC2042AB67C97008E3DB0 /* ImmersionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImmersionModel.swift; sourceTree = ""; }; 49 | BD3DC20C2AB67E75008E3DB0 /* ParkScene.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = ParkScene.jpg; sourceTree = ""; }; 50 | BD3DC20E2AB68D69008E3DB0 /* ChatbotViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatbotViewModel.swift; sourceTree = ""; }; 51 | BD3DC2142AB6EC3E008E3DB0 /* PracticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PracticeView.swift; sourceTree = ""; }; 52 | BD3DC2162AB6EC48008E3DB0 /* PracticeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PracticeViewModel.swift; sourceTree = ""; }; 53 | BD3DC2182AB70717008E3DB0 /* DockScene.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = DockScene.jpg; sourceTree = ""; }; 54 | BD3DC21A2AB70783008E3DB0 /* ChurchScene.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = ChurchScene.jpg; sourceTree = ""; }; 55 | BD3DC21C2AB707D8008E3DB0 /* LivingRoomScene.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = LivingRoomScene.jpg; sourceTree = ""; }; 56 | BDD07EC32AB5F490001A3CAD /* EduSphere.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EduSphere.app; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | BDD07EC72AB5F490001A3CAD /* RealityKitContent */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = RealityKitContent; sourceTree = ""; }; 58 | BDD07ECA2AB5F490001A3CAD /* EduSphereApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EduSphereApp.swift; sourceTree = ""; }; 59 | BDD07ECC2AB5F490001A3CAD /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 60 | BDD07ECE2AB5F490001A3CAD /* ImmersiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImmersiveView.swift; sourceTree = ""; }; 61 | BDD07ED02AB5F490001A3CAD /* ImageBasedLighting.skybox */ = {isa = PBXFileReference; lastKnownFileType = file.skybox; path = ImageBasedLighting.skybox; sourceTree = ""; }; 62 | BDD07ED22AB5F492001A3CAD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 63 | BDD07ED52AB5F492001A3CAD /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 64 | BDD07EEF2AB5F531001A3CAD /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; 65 | BDD07EF12AB5F537001A3CAD /* LearnView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LearnView.swift; sourceTree = ""; }; 66 | BDD07EF42AB5F953001A3CAD /* StarfieldScene.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = StarfieldScene.jpg; sourceTree = ""; }; 67 | BDD07EF62AB5FA17001A3CAD /* Chair.usdz */ = {isa = PBXFileReference; lastKnownFileType = file.usdz; path = Chair.usdz; sourceTree = ""; }; 68 | BDD07EF82AB5FBE7001A3CAD /* LearnViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LearnViewModel.swift; sourceTree = ""; }; 69 | BDD07EFD2AB61245001A3CAD /* AboutInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutInfo.swift; sourceTree = ""; }; 70 | BDD07EFF2AB6159C001A3CAD /* LanguageManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageManager.swift; sourceTree = ""; }; 71 | BDD07F012AB6183C001A3CAD /* Language.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Language.swift; sourceTree = ""; }; 72 | /* End PBXFileReference section */ 73 | 74 | /* Begin PBXFrameworksBuildPhase section */ 75 | BDD07EC02AB5F490001A3CAD /* Frameworks */ = { 76 | isa = PBXFrameworksBuildPhase; 77 | buildActionMask = 2147483647; 78 | files = ( 79 | BDD07EC92AB5F490001A3CAD /* RealityKitContent in Frameworks */, 80 | BDD07EFC2AB61132001A3CAD /* Alamofire in Frameworks */, 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | /* End PBXFrameworksBuildPhase section */ 85 | 86 | /* Begin PBXGroup section */ 87 | BD3DC1F82AB63CE9008E3DB0 /* Chatbot */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | BD3DC1FB2AB63CFA008E3DB0 /* Views */, 91 | BD3DC1FA2AB63CF5008E3DB0 /* ViewModels */, 92 | BD3DC1F92AB63CF1008E3DB0 /* Models */, 93 | ); 94 | path = Chatbot; 95 | sourceTree = ""; 96 | }; 97 | BD3DC1F92AB63CF1008E3DB0 /* Models */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | ); 101 | path = Models; 102 | sourceTree = ""; 103 | }; 104 | BD3DC1FA2AB63CF5008E3DB0 /* ViewModels */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | BD3DC20E2AB68D69008E3DB0 /* ChatbotViewModel.swift */, 108 | ); 109 | path = ViewModels; 110 | sourceTree = ""; 111 | }; 112 | BD3DC1FB2AB63CFA008E3DB0 /* Views */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | BD3DC1FC2AB63D1F008E3DB0 /* ChatbotView.swift */, 116 | ); 117 | path = Views; 118 | sourceTree = ""; 119 | }; 120 | BD3DC1FF2AB65FE9008E3DB0 /* SupportingFiles */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | BD3DC2002AB65FF8008E3DB0 /* WordCollection.swift */, 124 | BD3DC2022AB660D3008E3DB0 /* WordDecoder.swift */, 125 | ); 126 | path = SupportingFiles; 127 | sourceTree = ""; 128 | }; 129 | BD3DC2102AB6EC0E008E3DB0 /* Practice */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | BD3DC2132AB6EC35008E3DB0 /* Views */, 133 | BD3DC2122AB6EC2F008E3DB0 /* ViewModels */, 134 | BD3DC2112AB6EC2C008E3DB0 /* Models */, 135 | ); 136 | path = Practice; 137 | sourceTree = ""; 138 | }; 139 | BD3DC2112AB6EC2C008E3DB0 /* Models */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | BD3DC2042AB67C97008E3DB0 /* ImmersionModel.swift */, 143 | ); 144 | path = Models; 145 | sourceTree = ""; 146 | }; 147 | BD3DC2122AB6EC2F008E3DB0 /* ViewModels */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | BD3DC2162AB6EC48008E3DB0 /* PracticeViewModel.swift */, 151 | ); 152 | path = ViewModels; 153 | sourceTree = ""; 154 | }; 155 | BD3DC2132AB6EC35008E3DB0 /* Views */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | BD3DC2142AB6EC3E008E3DB0 /* PracticeView.swift */, 159 | ); 160 | path = Views; 161 | sourceTree = ""; 162 | }; 163 | BDD07EBA2AB5F490001A3CAD = { 164 | isa = PBXGroup; 165 | children = ( 166 | BDD07EC52AB5F490001A3CAD /* EduSphere */, 167 | BDD07EC62AB5F490001A3CAD /* Packages */, 168 | BDD07EC42AB5F490001A3CAD /* Products */, 169 | ); 170 | sourceTree = ""; 171 | }; 172 | BDD07EC42AB5F490001A3CAD /* Products */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | BDD07EC32AB5F490001A3CAD /* EduSphere.app */, 176 | ); 177 | name = Products; 178 | sourceTree = ""; 179 | }; 180 | BDD07EC52AB5F490001A3CAD /* EduSphere */ = { 181 | isa = PBXGroup; 182 | children = ( 183 | BD3DC1FE2AB643AC008E3DB0 /* Info.plist */, 184 | BDD07ECA2AB5F490001A3CAD /* EduSphereApp.swift */, 185 | BDD07ECC2AB5F490001A3CAD /* ContentView.swift */, 186 | BDD07EF32AB5F93A001A3CAD /* Resources */, 187 | BDD07EDC2AB5F4A7001A3CAD /* Source */, 188 | BDD07ED02AB5F490001A3CAD /* ImageBasedLighting.skybox */, 189 | BDD07ED42AB5F492001A3CAD /* Preview Content */, 190 | ); 191 | path = EduSphere; 192 | sourceTree = ""; 193 | }; 194 | BDD07EC62AB5F490001A3CAD /* Packages */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | BDD07EC72AB5F490001A3CAD /* RealityKitContent */, 198 | ); 199 | path = Packages; 200 | sourceTree = ""; 201 | }; 202 | BDD07ED42AB5F492001A3CAD /* Preview Content */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | BDD07ED52AB5F492001A3CAD /* Preview Assets.xcassets */, 206 | ); 207 | path = "Preview Content"; 208 | sourceTree = ""; 209 | }; 210 | BDD07EDC2AB5F4A7001A3CAD /* Source */ = { 211 | isa = PBXGroup; 212 | children = ( 213 | BD3DC1FF2AB65FE9008E3DB0 /* SupportingFiles */, 214 | BDD07EDD2AB5F4AB001A3CAD /* Features */, 215 | ); 216 | path = Source; 217 | sourceTree = ""; 218 | }; 219 | BDD07EDD2AB5F4AB001A3CAD /* Features */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | BDD07EDE2AB5F4B2001A3CAD /* Common */, 223 | BD3DC2102AB6EC0E008E3DB0 /* Practice */, 224 | BD3DC1F82AB63CE9008E3DB0 /* Chatbot */, 225 | BDD07EE12AB5F4CB001A3CAD /* Learn */, 226 | BDD07EE02AB5F4C8001A3CAD /* About */, 227 | ); 228 | path = Features; 229 | sourceTree = ""; 230 | }; 231 | BDD07EDE2AB5F4B2001A3CAD /* Common */ = { 232 | isa = PBXGroup; 233 | children = ( 234 | BDD07EE52AB5F4EB001A3CAD /* Managers */, 235 | BDD07EE42AB5F4E7001A3CAD /* Views */, 236 | BDD07EE32AB5F4E4001A3CAD /* ViewModels */, 237 | BDD07EE22AB5F4E0001A3CAD /* Models */, 238 | ); 239 | path = Common; 240 | sourceTree = ""; 241 | }; 242 | BDD07EE02AB5F4C8001A3CAD /* About */ = { 243 | isa = PBXGroup; 244 | children = ( 245 | BDD07EEB2AB5F501001A3CAD /* Views */, 246 | BDD07EEA2AB5F4FE001A3CAD /* ViewModels */, 247 | BDD07EE92AB5F4FB001A3CAD /* Models */, 248 | ); 249 | path = About; 250 | sourceTree = ""; 251 | }; 252 | BDD07EE12AB5F4CB001A3CAD /* Learn */ = { 253 | isa = PBXGroup; 254 | children = ( 255 | BDD07EE82AB5F4F7001A3CAD /* Views */, 256 | BDD07EE72AB5F4F3001A3CAD /* ViewModels */, 257 | BDD07EE62AB5F4EF001A3CAD /* Models */, 258 | ); 259 | path = Learn; 260 | sourceTree = ""; 261 | }; 262 | BDD07EE22AB5F4E0001A3CAD /* Models */ = { 263 | isa = PBXGroup; 264 | children = ( 265 | BDD07F012AB6183C001A3CAD /* Language.swift */, 266 | ); 267 | path = Models; 268 | sourceTree = ""; 269 | }; 270 | BDD07EE32AB5F4E4001A3CAD /* ViewModels */ = { 271 | isa = PBXGroup; 272 | children = ( 273 | ); 274 | path = ViewModels; 275 | sourceTree = ""; 276 | }; 277 | BDD07EE42AB5F4E7001A3CAD /* Views */ = { 278 | isa = PBXGroup; 279 | children = ( 280 | BDD07ECE2AB5F490001A3CAD /* ImmersiveView.swift */, 281 | ); 282 | path = Views; 283 | sourceTree = ""; 284 | }; 285 | BDD07EE52AB5F4EB001A3CAD /* Managers */ = { 286 | isa = PBXGroup; 287 | children = ( 288 | BDD07EFF2AB6159C001A3CAD /* LanguageManager.swift */, 289 | ); 290 | path = Managers; 291 | sourceTree = ""; 292 | }; 293 | BDD07EE62AB5F4EF001A3CAD /* Models */ = { 294 | isa = PBXGroup; 295 | children = ( 296 | BD3DC1F42AB633EF008E3DB0 /* ChoiceModel.swift */, 297 | BD3DC1F62AB63868008E3DB0 /* Mod3DModel.swift */, 298 | ); 299 | path = Models; 300 | sourceTree = ""; 301 | }; 302 | BDD07EE72AB5F4F3001A3CAD /* ViewModels */ = { 303 | isa = PBXGroup; 304 | children = ( 305 | BDD07EF82AB5FBE7001A3CAD /* LearnViewModel.swift */, 306 | ); 307 | path = ViewModels; 308 | sourceTree = ""; 309 | }; 310 | BDD07EE82AB5F4F7001A3CAD /* Views */ = { 311 | isa = PBXGroup; 312 | children = ( 313 | BDD07EF12AB5F537001A3CAD /* LearnView.swift */, 314 | ); 315 | path = Views; 316 | sourceTree = ""; 317 | }; 318 | BDD07EE92AB5F4FB001A3CAD /* Models */ = { 319 | isa = PBXGroup; 320 | children = ( 321 | BDD07EFD2AB61245001A3CAD /* AboutInfo.swift */, 322 | ); 323 | path = Models; 324 | sourceTree = ""; 325 | }; 326 | BDD07EEA2AB5F4FE001A3CAD /* ViewModels */ = { 327 | isa = PBXGroup; 328 | children = ( 329 | ); 330 | path = ViewModels; 331 | sourceTree = ""; 332 | }; 333 | BDD07EEB2AB5F501001A3CAD /* Views */ = { 334 | isa = PBXGroup; 335 | children = ( 336 | BDD07EEF2AB5F531001A3CAD /* AboutView.swift */, 337 | ); 338 | path = Views; 339 | sourceTree = ""; 340 | }; 341 | BDD07EF32AB5F93A001A3CAD /* Resources */ = { 342 | isa = PBXGroup; 343 | children = ( 344 | BDD07EF62AB5FA17001A3CAD /* Chair.usdz */, 345 | BDD07EF42AB5F953001A3CAD /* StarfieldScene.jpg */, 346 | BD3DC2182AB70717008E3DB0 /* DockScene.jpg */, 347 | BD3DC21A2AB70783008E3DB0 /* ChurchScene.jpg */, 348 | BD3DC20C2AB67E75008E3DB0 /* ParkScene.jpg */, 349 | BD3DC21C2AB707D8008E3DB0 /* LivingRoomScene.jpg */, 350 | BDD07ED22AB5F492001A3CAD /* Assets.xcassets */, 351 | ); 352 | path = Resources; 353 | sourceTree = ""; 354 | }; 355 | /* End PBXGroup section */ 356 | 357 | /* Begin PBXNativeTarget section */ 358 | BDD07EC22AB5F490001A3CAD /* EduSphere */ = { 359 | isa = PBXNativeTarget; 360 | buildConfigurationList = BDD07ED92AB5F492001A3CAD /* Build configuration list for PBXNativeTarget "EduSphere" */; 361 | buildPhases = ( 362 | BDD07EBF2AB5F490001A3CAD /* Sources */, 363 | BDD07EC02AB5F490001A3CAD /* Frameworks */, 364 | BDD07EC12AB5F490001A3CAD /* Resources */, 365 | ); 366 | buildRules = ( 367 | ); 368 | dependencies = ( 369 | ); 370 | name = EduSphere; 371 | packageProductDependencies = ( 372 | BDD07EC82AB5F490001A3CAD /* RealityKitContent */, 373 | BDD07EFB2AB61132001A3CAD /* Alamofire */, 374 | ); 375 | productName = EduSphere; 376 | productReference = BDD07EC32AB5F490001A3CAD /* EduSphere.app */; 377 | productType = "com.apple.product-type.application"; 378 | }; 379 | /* End PBXNativeTarget section */ 380 | 381 | /* Begin PBXProject section */ 382 | BDD07EBB2AB5F490001A3CAD /* Project object */ = { 383 | isa = PBXProject; 384 | attributes = { 385 | BuildIndependentTargetsInParallel = 1; 386 | LastSwiftUpdateCheck = 1500; 387 | LastUpgradeCheck = 1500; 388 | TargetAttributes = { 389 | BDD07EC22AB5F490001A3CAD = { 390 | CreatedOnToolsVersion = 15.0; 391 | }; 392 | }; 393 | }; 394 | buildConfigurationList = BDD07EBE2AB5F490001A3CAD /* Build configuration list for PBXProject "EduSphere" */; 395 | compatibilityVersion = "Xcode 14.0"; 396 | developmentRegion = en; 397 | hasScannedForEncodings = 0; 398 | knownRegions = ( 399 | en, 400 | Base, 401 | ); 402 | mainGroup = BDD07EBA2AB5F490001A3CAD; 403 | packageReferences = ( 404 | BDD07EFA2AB61132001A3CAD /* XCRemoteSwiftPackageReference "Alamofire" */, 405 | ); 406 | productRefGroup = BDD07EC42AB5F490001A3CAD /* Products */; 407 | projectDirPath = ""; 408 | projectRoot = ""; 409 | targets = ( 410 | BDD07EC22AB5F490001A3CAD /* EduSphere */, 411 | ); 412 | }; 413 | /* End PBXProject section */ 414 | 415 | /* Begin PBXResourcesBuildPhase section */ 416 | BDD07EC12AB5F490001A3CAD /* Resources */ = { 417 | isa = PBXResourcesBuildPhase; 418 | buildActionMask = 2147483647; 419 | files = ( 420 | BD3DC2192AB70717008E3DB0 /* DockScene.jpg in Resources */, 421 | BDD07ED32AB5F492001A3CAD /* Assets.xcassets in Resources */, 422 | BD3DC20D2AB67E76008E3DB0 /* ParkScene.jpg in Resources */, 423 | BD3DC21B2AB70783008E3DB0 /* ChurchScene.jpg in Resources */, 424 | BDD07EF72AB5FA17001A3CAD /* Chair.usdz in Resources */, 425 | BDD07ED62AB5F492001A3CAD /* Preview Assets.xcassets in Resources */, 426 | BDD07EF52AB5F953001A3CAD /* StarfieldScene.jpg in Resources */, 427 | BD3DC21D2AB707D9008E3DB0 /* LivingRoomScene.jpg in Resources */, 428 | BDD07ED12AB5F490001A3CAD /* ImageBasedLighting.skybox in Resources */, 429 | ); 430 | runOnlyForDeploymentPostprocessing = 0; 431 | }; 432 | /* End PBXResourcesBuildPhase section */ 433 | 434 | /* Begin PBXSourcesBuildPhase section */ 435 | BDD07EBF2AB5F490001A3CAD /* Sources */ = { 436 | isa = PBXSourcesBuildPhase; 437 | buildActionMask = 2147483647; 438 | files = ( 439 | BDD07EF22AB5F537001A3CAD /* LearnView.swift in Sources */, 440 | BD3DC1F72AB63868008E3DB0 /* Mod3DModel.swift in Sources */, 441 | BD3DC20F2AB68D69008E3DB0 /* ChatbotViewModel.swift in Sources */, 442 | BD3DC2152AB6EC3E008E3DB0 /* PracticeView.swift in Sources */, 443 | BDD07EF02AB5F531001A3CAD /* AboutView.swift in Sources */, 444 | BDD07ECD2AB5F490001A3CAD /* ContentView.swift in Sources */, 445 | BDD07F022AB6183C001A3CAD /* Language.swift in Sources */, 446 | BD3DC2052AB67C97008E3DB0 /* ImmersionModel.swift in Sources */, 447 | BD3DC1F52AB633EF008E3DB0 /* ChoiceModel.swift in Sources */, 448 | BDD07EF92AB5FBE7001A3CAD /* LearnViewModel.swift in Sources */, 449 | BDD07ECB2AB5F490001A3CAD /* EduSphereApp.swift in Sources */, 450 | BD3DC1FD2AB63D1F008E3DB0 /* ChatbotView.swift in Sources */, 451 | BDD07ECF2AB5F490001A3CAD /* ImmersiveView.swift in Sources */, 452 | BD3DC2172AB6EC48008E3DB0 /* PracticeViewModel.swift in Sources */, 453 | BDD07EFE2AB61245001A3CAD /* AboutInfo.swift in Sources */, 454 | BDD07F002AB6159C001A3CAD /* LanguageManager.swift in Sources */, 455 | BD3DC2032AB660D3008E3DB0 /* WordDecoder.swift in Sources */, 456 | BD3DC2012AB65FF8008E3DB0 /* WordCollection.swift in Sources */, 457 | ); 458 | runOnlyForDeploymentPostprocessing = 0; 459 | }; 460 | /* End PBXSourcesBuildPhase section */ 461 | 462 | /* Begin XCBuildConfiguration section */ 463 | BDD07ED72AB5F492001A3CAD /* Debug */ = { 464 | isa = XCBuildConfiguration; 465 | buildSettings = { 466 | ALWAYS_SEARCH_USER_PATHS = NO; 467 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 468 | CLANG_ANALYZER_NONNULL = YES; 469 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 470 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 471 | CLANG_ENABLE_MODULES = YES; 472 | CLANG_ENABLE_OBJC_ARC = YES; 473 | CLANG_ENABLE_OBJC_WEAK = YES; 474 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 475 | CLANG_WARN_BOOL_CONVERSION = YES; 476 | CLANG_WARN_COMMA = YES; 477 | CLANG_WARN_CONSTANT_CONVERSION = YES; 478 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 479 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 480 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 481 | CLANG_WARN_EMPTY_BODY = YES; 482 | CLANG_WARN_ENUM_CONVERSION = YES; 483 | CLANG_WARN_INFINITE_RECURSION = YES; 484 | CLANG_WARN_INT_CONVERSION = YES; 485 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 486 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 487 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 488 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 489 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 490 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 491 | CLANG_WARN_STRICT_PROTOTYPES = YES; 492 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 493 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 494 | CLANG_WARN_UNREACHABLE_CODE = YES; 495 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 496 | COPY_PHASE_STRIP = NO; 497 | DEBUG_INFORMATION_FORMAT = dwarf; 498 | ENABLE_STRICT_OBJC_MSGSEND = YES; 499 | ENABLE_TESTABILITY = YES; 500 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 501 | GCC_C_LANGUAGE_STANDARD = gnu17; 502 | GCC_DYNAMIC_NO_PIC = NO; 503 | GCC_NO_COMMON_BLOCKS = YES; 504 | GCC_OPTIMIZATION_LEVEL = 0; 505 | GCC_PREPROCESSOR_DEFINITIONS = ( 506 | "DEBUG=1", 507 | "$(inherited)", 508 | ); 509 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 510 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 511 | GCC_WARN_UNDECLARED_SELECTOR = YES; 512 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 513 | GCC_WARN_UNUSED_FUNCTION = YES; 514 | GCC_WARN_UNUSED_VARIABLE = YES; 515 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 516 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 517 | MTL_FAST_MATH = YES; 518 | ONLY_ACTIVE_ARCH = YES; 519 | SDKROOT = xros; 520 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; 521 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 522 | XROS_DEPLOYMENT_TARGET = 1.0; 523 | }; 524 | name = Debug; 525 | }; 526 | BDD07ED82AB5F492001A3CAD /* Release */ = { 527 | isa = XCBuildConfiguration; 528 | buildSettings = { 529 | ALWAYS_SEARCH_USER_PATHS = NO; 530 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 531 | CLANG_ANALYZER_NONNULL = YES; 532 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 533 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 534 | CLANG_ENABLE_MODULES = YES; 535 | CLANG_ENABLE_OBJC_ARC = YES; 536 | CLANG_ENABLE_OBJC_WEAK = YES; 537 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 538 | CLANG_WARN_BOOL_CONVERSION = YES; 539 | CLANG_WARN_COMMA = YES; 540 | CLANG_WARN_CONSTANT_CONVERSION = YES; 541 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 542 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 543 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 544 | CLANG_WARN_EMPTY_BODY = YES; 545 | CLANG_WARN_ENUM_CONVERSION = YES; 546 | CLANG_WARN_INFINITE_RECURSION = YES; 547 | CLANG_WARN_INT_CONVERSION = YES; 548 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 549 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 550 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 551 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 552 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 553 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 554 | CLANG_WARN_STRICT_PROTOTYPES = YES; 555 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 556 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 557 | CLANG_WARN_UNREACHABLE_CODE = YES; 558 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 559 | COPY_PHASE_STRIP = NO; 560 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 561 | ENABLE_NS_ASSERTIONS = NO; 562 | ENABLE_STRICT_OBJC_MSGSEND = YES; 563 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 564 | GCC_C_LANGUAGE_STANDARD = gnu17; 565 | GCC_NO_COMMON_BLOCKS = YES; 566 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 567 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 568 | GCC_WARN_UNDECLARED_SELECTOR = YES; 569 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 570 | GCC_WARN_UNUSED_FUNCTION = YES; 571 | GCC_WARN_UNUSED_VARIABLE = YES; 572 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 573 | MTL_ENABLE_DEBUG_INFO = NO; 574 | MTL_FAST_MATH = YES; 575 | SDKROOT = xros; 576 | SWIFT_COMPILATION_MODE = wholemodule; 577 | VALIDATE_PRODUCT = YES; 578 | XROS_DEPLOYMENT_TARGET = 1.0; 579 | }; 580 | name = Release; 581 | }; 582 | BDD07EDA2AB5F492001A3CAD /* Debug */ = { 583 | isa = XCBuildConfiguration; 584 | buildSettings = { 585 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 586 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 587 | CODE_SIGN_STYLE = Automatic; 588 | CURRENT_PROJECT_VERSION = 1; 589 | DEVELOPMENT_ASSET_PATHS = "\"EduSphere/Preview Content\""; 590 | DEVELOPMENT_TEAM = PLD5587GKY; 591 | ENABLE_PREVIEWS = YES; 592 | GENERATE_INFOPLIST_FILE = YES; 593 | INFOPLIST_FILE = EduSphere/Info.plist; 594 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 595 | LD_RUNPATH_SEARCH_PATHS = ( 596 | "$(inherited)", 597 | "@executable_path/Frameworks", 598 | ); 599 | MARKETING_VERSION = 1.0; 600 | PRODUCT_BUNDLE_IDENTIFIER = mingchungxia.EduSphere; 601 | PRODUCT_NAME = "$(TARGET_NAME)"; 602 | SUPPORTED_PLATFORMS = "xros xrsimulator"; 603 | SWIFT_EMIT_LOC_STRINGS = YES; 604 | SWIFT_VERSION = 5.0; 605 | TARGETED_DEVICE_FAMILY = "1,2,7"; 606 | }; 607 | name = Debug; 608 | }; 609 | BDD07EDB2AB5F492001A3CAD /* Release */ = { 610 | isa = XCBuildConfiguration; 611 | buildSettings = { 612 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 613 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 614 | CODE_SIGN_STYLE = Automatic; 615 | CURRENT_PROJECT_VERSION = 1; 616 | DEVELOPMENT_ASSET_PATHS = "\"EduSphere/Preview Content\""; 617 | DEVELOPMENT_TEAM = PLD5587GKY; 618 | ENABLE_PREVIEWS = YES; 619 | GENERATE_INFOPLIST_FILE = YES; 620 | INFOPLIST_FILE = EduSphere/Info.plist; 621 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 622 | LD_RUNPATH_SEARCH_PATHS = ( 623 | "$(inherited)", 624 | "@executable_path/Frameworks", 625 | ); 626 | MARKETING_VERSION = 1.0; 627 | PRODUCT_BUNDLE_IDENTIFIER = mingchungxia.EduSphere; 628 | PRODUCT_NAME = "$(TARGET_NAME)"; 629 | SUPPORTED_PLATFORMS = "xros xrsimulator"; 630 | SWIFT_EMIT_LOC_STRINGS = YES; 631 | SWIFT_VERSION = 5.0; 632 | TARGETED_DEVICE_FAMILY = "1,2,7"; 633 | }; 634 | name = Release; 635 | }; 636 | /* End XCBuildConfiguration section */ 637 | 638 | /* Begin XCConfigurationList section */ 639 | BDD07EBE2AB5F490001A3CAD /* Build configuration list for PBXProject "EduSphere" */ = { 640 | isa = XCConfigurationList; 641 | buildConfigurations = ( 642 | BDD07ED72AB5F492001A3CAD /* Debug */, 643 | BDD07ED82AB5F492001A3CAD /* Release */, 644 | ); 645 | defaultConfigurationIsVisible = 0; 646 | defaultConfigurationName = Release; 647 | }; 648 | BDD07ED92AB5F492001A3CAD /* Build configuration list for PBXNativeTarget "EduSphere" */ = { 649 | isa = XCConfigurationList; 650 | buildConfigurations = ( 651 | BDD07EDA2AB5F492001A3CAD /* Debug */, 652 | BDD07EDB2AB5F492001A3CAD /* Release */, 653 | ); 654 | defaultConfigurationIsVisible = 0; 655 | defaultConfigurationName = Release; 656 | }; 657 | /* End XCConfigurationList section */ 658 | 659 | /* Begin XCRemoteSwiftPackageReference section */ 660 | BDD07EFA2AB61132001A3CAD /* XCRemoteSwiftPackageReference "Alamofire" */ = { 661 | isa = XCRemoteSwiftPackageReference; 662 | repositoryURL = "https://github.com/Alamofire/Alamofire.git"; 663 | requirement = { 664 | kind = upToNextMajorVersion; 665 | minimumVersion = 5.8.0; 666 | }; 667 | }; 668 | /* End XCRemoteSwiftPackageReference section */ 669 | 670 | /* Begin XCSwiftPackageProductDependency section */ 671 | BDD07EC82AB5F490001A3CAD /* RealityKitContent */ = { 672 | isa = XCSwiftPackageProductDependency; 673 | productName = RealityKitContent; 674 | }; 675 | BDD07EFB2AB61132001A3CAD /* Alamofire */ = { 676 | isa = XCSwiftPackageProductDependency; 677 | package = BDD07EFA2AB61132001A3CAD /* XCRemoteSwiftPackageReference "Alamofire" */; 678 | productName = Alamofire; 679 | }; 680 | /* End XCSwiftPackageProductDependency section */ 681 | }; 682 | rootObject = BDD07EBB2AB5F490001A3CAD /* Project object */; 683 | } 684 | -------------------------------------------------------------------------------- /EduSphere.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /EduSphere.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /EduSphere.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "alamofire", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/Alamofire/Alamofire.git", 7 | "state" : { 8 | "revision" : "b2fa556e4e48cbf06cf8c63def138c98f4b811fa", 9 | "version" : "5.8.0" 10 | } 11 | } 12 | ], 13 | "version" : 2 14 | } 15 | -------------------------------------------------------------------------------- /EduSphere.xcodeproj/xcuserdata/mingchungxia.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | EduSphere.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /EduSphere/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import SwiftUI 9 | import RealityKit 10 | import RealityKitContent 11 | 12 | struct ContentView: View { 13 | @ObservedObject var languageManager = LanguageManager.shared 14 | 15 | var body: some View { 16 | content 17 | .glassBackgroundEffect() 18 | } 19 | 20 | var content: some View { 21 | VStack { 22 | header 23 | Divider() 24 | .padding() 25 | navigator 26 | } 27 | } 28 | 29 | var header: some View { 30 | HStack { 31 | Text("EduSphere") 32 | .font(.title) 33 | .fontWeight(.bold) 34 | .padding() 35 | Spacer() 36 | languagePicker 37 | } 38 | .padding(.horizontal) 39 | .padding(.top) 40 | } 41 | 42 | var languagePicker: some View { 43 | Picker("Language", selection: $languageManager.language) { 44 | ForEach(Language.languages, id: \.self) { language in 45 | Text(language.uppercased()) 46 | } 47 | } 48 | } 49 | 50 | var navigator: some View { 51 | TabView { 52 | AboutView() 53 | .tabItem { 54 | Label("About", systemImage: "info.circle") 55 | } 56 | 57 | LearnView() 58 | .tabItem { 59 | Label("Learn", systemImage: "graduationcap") 60 | } 61 | 62 | PracticeView() 63 | .tabItem { 64 | Label("Practice", systemImage: "camera.viewfinder") 65 | } 66 | 67 | ChatbotView() 68 | .tabItem { 69 | Label("Chatbot", systemImage: "text.bubble") 70 | } 71 | } 72 | } 73 | } 74 | 75 | #Preview(windowStyle: .automatic) { 76 | ContentView() 77 | } 78 | -------------------------------------------------------------------------------- /EduSphere/EduSphereApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EduSphereApp.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct EduSphereApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | 17 | ImmersiveSpace(id: "ImmersiveSpace") { 18 | ImmersiveView() 19 | } 20 | .immersionStyle(selection: .constant(.full), in: .full) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /EduSphere/ImageBasedLighting.skybox/ImageBasedLight.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingchungx/EduSphere-VisionOS/940cf7bced762a1b1d3855b2b0ac77c3432bb4ae/EduSphere/ImageBasedLighting.skybox/ImageBasedLight.exr -------------------------------------------------------------------------------- /EduSphere/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | NSAppTransportSecurity 8 | 9 | NSAllowsArbitraryLoads 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /EduSphere/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo_background.png", 5 | "idiom" : "reality", 6 | "scale" : "2x" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/logo_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingchungx/EduSphere-VisionOS/940cf7bced762a1b1d3855b2b0ac77c3432bb4ae/EduSphere/Resources/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/logo_background.png -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/AppIcon.solidimagestack/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | }, 6 | "layers" : [ 7 | { 8 | "filename" : "Front.solidimagestacklayer" 9 | }, 10 | { 11 | "filename" : "Middle.solidimagestacklayer" 12 | }, 13 | { 14 | "filename" : "Back.solidimagestacklayer" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo_front_layer.png", 5 | "idiom" : "reality", 6 | "scale" : "2x" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/logo_front_layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingchungx/EduSphere-VisionOS/940cf7bced762a1b1d3855b2b0ac77c3432bb4ae/EduSphere/Resources/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/logo_front_layer.png -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo_middle_layer.png", 5 | "idiom" : "reality", 6 | "scale" : "2x" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/logo_middle_layer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingchungx/EduSphere-VisionOS/940cf7bced762a1b1d3855b2b0ac77c3432bb4ae/EduSphere/Resources/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Content.imageset/logo_middle_layer.png -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/AppIcon.solidimagestack/Middle.solidimagestacklayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/EduSphere_logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "EduSphere_logo.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 | -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/EduSphere_logo.imageset/EduSphere_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingchungx/EduSphere-VisionOS/940cf7bced762a1b1d3855b2b0ac77c3432bb4ae/EduSphere/Resources/Assets.xcassets/EduSphere_logo.imageset/EduSphere_logo.png -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/Robot.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Robot 4.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 | -------------------------------------------------------------------------------- /EduSphere/Resources/Assets.xcassets/Robot.imageset/Robot 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingchungx/EduSphere-VisionOS/940cf7bced762a1b1d3855b2b0ac77c3432bb4ae/EduSphere/Resources/Assets.xcassets/Robot.imageset/Robot 4.png -------------------------------------------------------------------------------- /EduSphere/Resources/Chair.usdz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingchungx/EduSphere-VisionOS/940cf7bced762a1b1d3855b2b0ac77c3432bb4ae/EduSphere/Resources/Chair.usdz -------------------------------------------------------------------------------- /EduSphere/Resources/ChurchScene.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingchungx/EduSphere-VisionOS/940cf7bced762a1b1d3855b2b0ac77c3432bb4ae/EduSphere/Resources/ChurchScene.jpg -------------------------------------------------------------------------------- /EduSphere/Resources/DockScene.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingchungx/EduSphere-VisionOS/940cf7bced762a1b1d3855b2b0ac77c3432bb4ae/EduSphere/Resources/DockScene.jpg -------------------------------------------------------------------------------- /EduSphere/Resources/LivingRoomScene.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingchungx/EduSphere-VisionOS/940cf7bced762a1b1d3855b2b0ac77c3432bb4ae/EduSphere/Resources/LivingRoomScene.jpg -------------------------------------------------------------------------------- /EduSphere/Resources/ParkScene.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingchungx/EduSphere-VisionOS/940cf7bced762a1b1d3855b2b0ac77c3432bb4ae/EduSphere/Resources/ParkScene.jpg -------------------------------------------------------------------------------- /EduSphere/Resources/StarfieldScene.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingchungx/EduSphere-VisionOS/940cf7bced762a1b1d3855b2b0ac77c3432bb4ae/EduSphere/Resources/StarfieldScene.jpg -------------------------------------------------------------------------------- /EduSphere/Source/Features/About/Models/AboutInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutInfo.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import Foundation 9 | 10 | struct AboutInfo: Identifiable { 11 | let id = UUID() 12 | } 13 | 14 | extension AboutInfo { 15 | static let header: String = { 16 | "About" 17 | }() 18 | static let body: String = { 19 | "EduSphere is an interactive language learning app designed to provide a unique language learning experience. EduSphere includes many games: Multiple choice and a Chatbot. \n\nMultiple choice presents a 3d model of an object and three options to choose from. Be careful, too many wrong choices will end the game! \n\nThe chatbot allows the ability to practice writing any language with real time responses. \n\nChange the language of your game in the top right selector." 20 | }() 21 | } 22 | -------------------------------------------------------------------------------- /EduSphere/Source/Features/About/Views/AboutView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutView.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct AboutView: View { 11 | var body: some View { 12 | content 13 | } 14 | 15 | var content: some View { 16 | HStack(spacing: 100) { 17 | sideImage 18 | text 19 | } 20 | } 21 | 22 | var sideImage: some View { 23 | Image("EduSphere_logo") 24 | .resizable() 25 | .scaledToFit() 26 | .frame(width: 300, height: 300) 27 | .padding(.leading, 200) 28 | } 29 | 30 | var text: some View { 31 | VStack(alignment: .leading, spacing: 20) { 32 | Text(AboutInfo.header) 33 | .font(.title2) 34 | .fontWeight(.bold) 35 | .padding(.vertical) 36 | Text(AboutInfo.body) 37 | } 38 | .padding(.trailing, 200) 39 | } 40 | } 41 | 42 | #Preview { 43 | AboutView() 44 | } 45 | -------------------------------------------------------------------------------- /EduSphere/Source/Features/Chatbot/ViewModels/ChatbotViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatbotViewModel.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | 11 | @MainActor 12 | final class ChatbotViewModel: ObservableObject { 13 | @Published var messages: [String] = [] 14 | 15 | func respond(msg: String) async { 16 | let endpoint = "http://127.0.0.1:5000/chatbot-query" 17 | let parameters: Parameters = [ 18 | "msg": msg 19 | ] 20 | self.messages.append(msg) 21 | AF.request(endpoint, parameters: parameters).responseData { response in 22 | switch response.result { 23 | case .success(let data): 24 | /* 25 | // Process data on success 26 | if let string = String(data: data, encoding: .utf8) { 27 | self.messages.append(string.capitalized) 28 | } 29 | */ 30 | // Process data on success 31 | if let json = try? JSONSerialization.jsonObject(with: data, options: []), let jsonDict = json as? [String: Any] { 32 | let reply = jsonDict["reply"] as? String 33 | 34 | // Access and use the extracted components as needed 35 | print("Reply:", reply ?? "") 36 | 37 | self.messages.append(reply ?? "") 38 | } 39 | case .failure(_): 40 | // Ignore error case 41 | debugPrint("Failed") 42 | } 43 | } 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /EduSphere/Source/Features/Chatbot/Views/ChatbotView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatbotView.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ChatbotView: View { 11 | @State private var text: String = "" 12 | 13 | @ObservedObject private var chatbotViewModel = ChatbotViewModel() 14 | 15 | var body: some View { 16 | ScrollView { 17 | VStack(alignment: .leading) { 18 | messages 19 | keyboard 20 | } 21 | } 22 | } 23 | 24 | var messages: some View { 25 | ForEach(chatbotViewModel.messages.indices, id: \.self) { index in 26 | alternatingView(for: index) 27 | } 28 | } 29 | 30 | @ViewBuilder 31 | func alternatingView(for index: Int) -> some View { 32 | Group { 33 | if index % 2 == 0 { 34 | userMessage(query: chatbotViewModel.messages[index]) 35 | } else { 36 | botMessage(response: chatbotViewModel.messages[index]) 37 | } 38 | } 39 | } 40 | 41 | var keyboard: some View { 42 | HStack { 43 | TextField("Send a message...", text: $text) 44 | .foregroundStyle(Color.white) 45 | Button { 46 | Task { 47 | await chatbotViewModel.respond(msg: text) 48 | text = "" 49 | } 50 | } label: { 51 | Image(systemName: "paperplane.fill") 52 | } 53 | } 54 | .padding() 55 | .background( 56 | .regularMaterial, 57 | in: RoundedRectangle(cornerRadius: 20) 58 | ) 59 | .padding() 60 | .shadow(radius: 3) 61 | } 62 | 63 | @ViewBuilder 64 | func botMessage(response: String) -> some View { 65 | HStack { 66 | Image("Robot") 67 | .resizable() 68 | .scaledToFill() 69 | .frame(width: 100, height: 100) 70 | .shadow(radius: 3) 71 | Text(response) 72 | } 73 | .padding() 74 | .background( 75 | .ultraThinMaterial, 76 | in: RoundedRectangle(cornerRadius: 20) 77 | ) 78 | .padding() 79 | } 80 | 81 | @ViewBuilder 82 | func userMessage(query: String) -> some View { 83 | HStack { 84 | Image(systemName: "person.fill") 85 | .resizable() 86 | .scaledToFill() 87 | .frame(width: 70, height: 70) 88 | .shadow(radius: 3) 89 | Text(query) 90 | } 91 | .padding() 92 | .background( 93 | .ultraThinMaterial, 94 | in: RoundedRectangle(cornerRadius: 20) 95 | ) 96 | .padding() 97 | } 98 | } 99 | 100 | #Preview { 101 | ChatbotView() 102 | } 103 | -------------------------------------------------------------------------------- /EduSphere/Source/Features/Common/Managers/LanguageManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LanguageManager.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import Foundation 9 | 10 | final class LanguageManager: ObservableObject { 11 | static let shared = LanguageManager() 12 | 13 | @Published var language: String = "english" 14 | 15 | init() { 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /EduSphere/Source/Features/Common/Models/Language.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Language.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Language: Identifiable { 11 | let id = UUID() 12 | } 13 | 14 | extension Language { 15 | static let languages: [String] = { 16 | ["afrikaans", 17 | "albanian", 18 | "amharic", 19 | "arabic", 20 | "armenian", 21 | "azerbaijani", 22 | "basque", 23 | "belarusian", 24 | "bengali", 25 | "bosnian", 26 | "bulgarian", 27 | "catalan", 28 | "cebuano", 29 | "chichewa", 30 | "chinese (simplified)", 31 | "chinese (traditional)", 32 | "corsican", "croatian", 33 | "czech", 34 | "danish", 35 | "dutch", 36 | "english", 37 | "esperanto", 38 | "estonian", 39 | "filipino", 40 | "finnish", 41 | "french", 42 | "frisian", 43 | "galician", 44 | "georgian", 45 | "german", 46 | "greek", 47 | "gujarati", 48 | "haitian creole", 49 | "hausa", 50 | "hawaiian", 51 | "hebrew", 52 | "hindi", 53 | "hmong", 54 | "hungarian", 55 | "icelandic", 56 | "igbo", 57 | "indonesian", 58 | "irish", 59 | "italian", 60 | "japanese", 61 | "javanese", 62 | "kannada", 63 | "kazakh", 64 | "khmer", 65 | "korean", 66 | "kurdish (kurmanji)", 67 | "kyrgyz", 68 | "lao", 69 | "latin", 70 | "latvian", 71 | "lithuanian", 72 | "luxembourgish", 73 | "macedonian", 74 | "malagasy", 75 | "malay", 76 | "malayalam", 77 | "maltese", 78 | "maori", 79 | "marathi", 80 | "mongolian", 81 | "myanmar (burmese)", 82 | "nepali", 83 | "norwegian", 84 | "odia", 85 | "pashto", 86 | "persian", 87 | "polish", 88 | "portuguese", 89 | "punjabi", 90 | "romanian", 91 | "russian", 92 | "samoan", 93 | "scots gaelic", 94 | "serbian", 95 | "sesotho", 96 | "shona", 97 | "sindhi", 98 | "sinhala", 99 | "slovak", 100 | "slovenian", 101 | "somali", 102 | "spanish", 103 | "sundanese", 104 | "swahili", 105 | "swedish", 106 | "tajik", 107 | "tamil", 108 | "telugu", 109 | "thai", 110 | "turkish", 111 | "ukrainian", 112 | "urdu", 113 | "uyghur", 114 | "uzbek", 115 | "vietnamese", 116 | "welsh", 117 | "xhosa", 118 | "yiddish", 119 | "yoruba", 120 | "zulu"] 121 | }() 122 | } 123 | -------------------------------------------------------------------------------- /EduSphere/Source/Features/Common/Views/ImmersiveView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImmersiveView.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import SwiftUI 9 | import RealityKit 10 | import RealityKitContent 11 | 12 | struct ImmersiveView: View { 13 | var body: some View { 14 | RealityView { content in 15 | // Create a material with a star field on it. 16 | guard let url = Bundle.main.url(forResource: Immersion.state, withExtension: "jpg"), 17 | let resource = try? await TextureResource(contentsOf: url) else { 18 | // If the asset isn't available, something is wrong with the app. 19 | fatalError("Unable to load texture.") 20 | } 21 | var material = UnlitMaterial() 22 | material.color = .init(texture: .init(resource)) 23 | 24 | // Attach the material to a large sphere. 25 | let entity = Entity() 26 | entity.components.set(ModelComponent( 27 | mesh: .generateSphere(radius: 1000), 28 | materials: [material] 29 | )) 30 | 31 | // Ensure the texture image points inward at the viewer. 32 | entity.scale *= .init(x: -1, y: 1, z: 1) 33 | 34 | content.add(entity) 35 | } 36 | } 37 | } 38 | 39 | #Preview { 40 | ImmersiveView() 41 | .previewLayout(.sizeThatFits) 42 | } 43 | -------------------------------------------------------------------------------- /EduSphere/Source/Features/Learn/Models/ChoiceModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChoiceModel.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct Choice: Identifiable { 12 | let id = UUID() 13 | var text: String 14 | var correct: Bool 15 | var color: Color 16 | } 17 | -------------------------------------------------------------------------------- /EduSphere/Source/Features/Learn/Models/Mod3DModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mod3DModel.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Mod3D { 11 | var name: String 12 | var url: String // convertible to URL 13 | } 14 | -------------------------------------------------------------------------------- /EduSphere/Source/Features/Learn/ViewModels/LearnViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LearnViewModel.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | import SwiftUI 11 | 12 | @MainActor 13 | final class LearnViewModel: ObservableObject { 14 | @Published var mod3D: Mod3D = Mod3D( 15 | name: "PLACEHOLDER", 16 | url: "https://storage.googleapis.com/edusphere/low_poly/arcade_low.usdz" 17 | ) 18 | 19 | @Published var choices: [Choice] = [ 20 | Choice(text: "", correct: false, color: Color.red), 21 | Choice(text: "", correct: false, color: Color.blue), 22 | Choice(text: "", correct: false, color: Color.yellow) 23 | ] 24 | 25 | @Published var immersiveScene: Immersion = Immersion( 26 | missingWord: "space", 27 | assetName: "StarfieldScene", 28 | sentence: "I am in ___." 29 | ) 30 | 31 | private let start = 0 32 | private let end = 176 33 | 34 | func fetchRandomMod3D(src: String, dest: String) async throws { 35 | let endpoint = "http://127.0.0.1:8000/api/item-from-id" 36 | let id = Int.random(in: start...end) 37 | let parameters: Parameters = ["id": id] 38 | debugPrint("Fetching id: \(id)") 39 | 40 | // Get the Mod3D from CockroachDB backend 41 | // Make GET request 42 | AF.request(endpoint, parameters: parameters).responseData { response in 43 | switch response.result { 44 | case .success(let data): 45 | // Process data on success 46 | if let json = try? JSONSerialization.jsonObject(with: data, options: []), let jsonDict = json as? [String: Any] { 47 | let name = jsonDict["name"] as? String 48 | let url = jsonDict["url"] as? String 49 | 50 | // Access and use the extracted components as needed 51 | print("Name:", name ?? "") 52 | print("URL:", url ?? "") 53 | 54 | self.mod3D = Mod3D(name: name!, url: url!) 55 | 56 | Task { 57 | try? await self.fetchChoices(word: name ?? "PLACEHOLDER", src: src, dest: dest) 58 | } 59 | } 60 | case .failure(_): 61 | // Ignore error case 62 | self.mod3D = Mod3D( 63 | name: "PLACEHOLDER", 64 | url: "https://storage.googleapis.com/edusphere/low_poly/arcade_low.usdz" 65 | ) 66 | } 67 | } 68 | } 69 | 70 | private func fetchChoices(word: String, src: String, dest: String) async throws { 71 | // src is the source language, dest is the destination language 72 | let endpoint = "http://127.0.0.1:5000/translate" 73 | let randomWords = getRandomWords() 74 | let parameters1: Parameters = [ 75 | "word": randomWords.0, 76 | "src": src, 77 | "dest": dest 78 | ] 79 | let parameters2: Parameters = [ 80 | "word": randomWords.1, 81 | "src": src, 82 | "dest": dest 83 | ] 84 | 85 | // parameters3 is always the correct one 86 | let parameters3: Parameters = [ 87 | "word": word, 88 | "src": src, 89 | "dest": dest 90 | ] 91 | 92 | debugPrint("Attempting to translate \(word) from \(src) to \(dest)") 93 | 94 | // Fetch choices from API 95 | AF.request(endpoint, parameters: parameters1).responseData { response in 96 | switch response.result { 97 | case .success(let data): 98 | // Process data on success 99 | if let json = try? JSONSerialization.jsonObject(with: data, options: []), let jsonDict = json as? [String: Any] { 100 | let translation = jsonDict["translation"] as? String 101 | 102 | // Access and use the extracted components as needed 103 | print("Translation:", translation ?? "") 104 | 105 | self.choices[0] = Choice(text: translation?.capitalized ?? "", correct: false, color: Color.red) 106 | } 107 | case .failure(_): 108 | // Ignore error case 109 | self.choices = [ 110 | Choice(text: "PLACEHOLDER 1", correct: false, color: Color.red), 111 | Choice(text: "PLACEHOLDER 2", correct: true, color: Color.blue), 112 | Choice(text: "PLACEHOLDER 3", correct: false, color: Color.yellow) 113 | ] 114 | } 115 | } 116 | 117 | AF.request(endpoint, parameters: parameters2).responseData { response in 118 | switch response.result { 119 | case .success(let data): 120 | // Process data on success 121 | if let json = try? JSONSerialization.jsonObject(with: data, options: []), let jsonDict = json as? [String: Any] { 122 | let translation = jsonDict["translation"] as? String 123 | 124 | // Access and use the extracted components as needed 125 | print("Translation:", translation ?? "") 126 | 127 | self.choices[1] = Choice(text: translation?.capitalized ?? "", correct: false, color: Color.blue) 128 | } 129 | case .failure(_): 130 | // Ignore error case 131 | self.choices = [ 132 | Choice(text: "PLACEHOLDER 1", correct: false, color: Color.red), 133 | Choice(text: "PLACEHOLDER 2", correct: true, color: Color.blue), 134 | Choice(text: "PLACEHOLDER 3", correct: false, color: Color.yellow) 135 | ] 136 | } 137 | } 138 | 139 | AF.request(endpoint, parameters: parameters3).responseData { response in 140 | switch response.result { 141 | case .success(let data): 142 | // Process data on success 143 | if let json = try? JSONSerialization.jsonObject(with: data, options: []), let jsonDict = json as? [String: Any] { 144 | let translation = jsonDict["translation"] as? String 145 | 146 | // Access and use the extracted components as needed 147 | print("Translation:", translation ?? "") 148 | 149 | self.choices[2] = Choice(text: translation?.capitalized ?? "", correct: true, color: Color.yellow) 150 | } 151 | case .failure(_): 152 | // Ignore error case 153 | self.choices = [ 154 | Choice(text: "PLACEHOLDER 1", correct: false, color: Color.red), 155 | Choice(text: "PLACEHOLDER 2", correct: true, color: Color.blue), 156 | Choice(text: "PLACEHOLDER 3", correct: false, color: Color.yellow) 157 | ] 158 | } 159 | } 160 | } 161 | 162 | private func getRandomWords() -> (String, String) { 163 | let n = WordCollection.random.count 164 | let wordIndices = (Int.random(in: 0...n-1), Int.random(in: 0...n-1)) 165 | return (WordCollection.random[wordIndices.0], WordCollection.random[wordIndices.1]) 166 | } 167 | 168 | private func returnTranslation(text: String, src: String, dest: String) async -> String { 169 | let endpoint = "http://127.0.0.1:5000/translate" 170 | let parameters: Parameters = [ 171 | "word": text, 172 | "src": src, 173 | "dest": dest 174 | ] 175 | 176 | return await withCheckedContinuation { continuation in 177 | AF.request(endpoint, parameters: parameters).responseData { response in 178 | switch response.result { 179 | case .success(let data): 180 | // Process data on success 181 | if let string = String(data: data, encoding: .utf8) { 182 | continuation.resume(returning: string.capitalized) 183 | } 184 | case .failure(_): 185 | // Ignore error case 186 | continuation.resume(returning: "") 187 | } 188 | } 189 | } 190 | } 191 | 192 | func getImmersiveScene(src: String, dest: String) async { 193 | let sceneId = Int.random(in: 0...4) // MARK: Change to 10 194 | let immersion = Immersion.immersions[sceneId] 195 | 196 | // Get the translations 197 | let translation = await self.returnTranslation(text: immersion.sentence, src: src, dest: dest) 198 | 199 | let missingWord = await self.returnTranslation(text: immersion.missingWord, src: src, dest: dest) 200 | 201 | self.immersiveScene = Immersion( 202 | missingWord: missingWord, 203 | assetName: immersion.assetName, 204 | sentence: translation 205 | ) 206 | } 207 | 208 | func checkCorrectBlank(text: String) -> Bool { 209 | return text == self.immersiveScene.missingWord 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /EduSphere/Source/Features/Learn/Views/LearnView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LearnView.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import SwiftUI 9 | import RealityKit 10 | 11 | // MARK: Main 12 | struct LearnView: View { 13 | // State Variables 14 | @State private var highestScore: Int = 0 15 | @State private var score: Int = 0 16 | @State private var hearts: Int = 5 17 | @State private var showingGame: Bool = false 18 | @State private var loading: Bool = false 19 | @State private var text: String = "" 20 | @State private var gameType: String = "MC" 21 | 22 | // ObservedObjects 23 | @ObservedObject private var learnViewModel = LearnViewModel() 24 | @ObservedObject private var languageManager = LanguageManager.shared 25 | 26 | var body: some View { 27 | content 28 | } 29 | 30 | var content: some View { 31 | VStack { 32 | if !showingGame { 33 | edusphereLogo 34 | largeTitle 35 | description 36 | playButton 37 | } else { 38 | game 39 | } 40 | Spacer() 41 | if !showingGame { 42 | footer 43 | .padding(.vertical) 44 | Spacer() 45 | } 46 | } 47 | } 48 | 49 | var edusphereLogo: some View { 50 | Image("EduSphere_logo") 51 | .resizable() 52 | .scaledToFit() 53 | .frame(width: 200, height: 200) 54 | } 55 | 56 | var largeTitle: some View { 57 | Text("Learn") 58 | .font(.extraLargeTitle) 59 | .fontWeight(.semibold) 60 | } 61 | 62 | var description: some View { 63 | Text("See everyday 3D objects and learn to name them! However... 5 strikes and you're out!") 64 | .padding() 65 | } 66 | 67 | var playButton: some View { 68 | Button { 69 | resetView() 70 | showingGame = true 71 | } label: { 72 | Image(systemName: "arrowtriangle.forward.fill") 73 | .resizable() 74 | .scaledToFit() 75 | .padding(50) 76 | .frame(width: 200, height: 200) 77 | } 78 | } 79 | 80 | var footer: some View { 81 | HStack { 82 | Spacer() 83 | Text("Highest Score: \(highestScore)") 84 | .font(.callout) 85 | .fontWeight(.semibold) 86 | .opacity(0.7) 87 | } 88 | .padding(.horizontal) 89 | } 90 | } 91 | 92 | // MARK: Game 93 | extension LearnView { 94 | var game: some View { 95 | VStack { 96 | gameHeader 97 | Spacer() 98 | if loading { 99 | ProgressView() 100 | } else { 101 | multipleChoice 102 | } 103 | Spacer() 104 | footer 105 | Spacer() 106 | } 107 | } 108 | 109 | // Game style 1: fill in the blank 110 | var fillInTheBlank: some View { 111 | Group { 112 | Text(learnViewModel.immersiveScene.sentence) 113 | TextField("Missing Word", text: $text) 114 | .keyboardType(.default) 115 | .onSubmit { 116 | Task { 117 | if learnViewModel.checkCorrectBlank(text: text) { 118 | score += 1 119 | // showingGame = false 120 | chooseGameType() 121 | } else { 122 | hearts -= 1 123 | } 124 | } 125 | } 126 | } 127 | .onAppear { 128 | Task { 129 | await updateFIB() 130 | } 131 | } 132 | } 133 | 134 | // Game style 2: multiple choice 135 | var multipleChoice: some View { 136 | Group { 137 | translationChoices 138 | Spacer() 139 | itemModel 140 | } 141 | .onAppear { 142 | Task { 143 | await updateMC() 144 | } 145 | } 146 | } 147 | 148 | var gameHeader: some View { 149 | HStack { 150 | HStack { 151 | Image(systemName: "arrow.left") 152 | Button { 153 | showingGame = false 154 | } label: { 155 | Text("Quit") 156 | } 157 | } 158 | Spacer() 159 | VStack(alignment: .trailing) { 160 | HStack { 161 | heart(1) 162 | heart(2) 163 | heart(3) 164 | heart(4) 165 | heart(5) 166 | } 167 | .padding(.vertical) 168 | Text("Score: \(score)") 169 | } 170 | } 171 | .padding(.horizontal) 172 | .padding(.bottom) 173 | } 174 | 175 | var itemModel: some View { 176 | // Asynchronous Model Retrieval 177 | Model3D(url: URL(string: learnViewModel.mod3D.url)!) { model in 178 | model 179 | .resizable() 180 | .scaledToFit() 181 | .frame(width: 300, height: 300) 182 | } placeholder: { 183 | ProgressView() 184 | } 185 | } 186 | 187 | var translationChoices: some View { 188 | HStack(spacing: 70) { 189 | ForEach(learnViewModel.choices.shuffled()) { c in 190 | if !c.text.isEmpty { 191 | choice( 192 | text: c.text, 193 | correct: c.correct, 194 | color: c.color 195 | ) 196 | } 197 | } 198 | } 199 | .padding() 200 | .onChange(of: hearts) { 201 | if hearts <= 0 { 202 | resetView() 203 | } 204 | } 205 | } 206 | 207 | @ViewBuilder 208 | func heart(_ lowerBound: Int) -> some View { 209 | Image(systemName: hearts >= lowerBound ? "heart.fill" : "heart") 210 | .foregroundStyle(Color.red) 211 | .shadow(radius: 2) 212 | } 213 | 214 | @ViewBuilder 215 | func choice(text: String, correct: Bool, color: Color) -> some View { 216 | Button { 217 | Task { 218 | if correct { 219 | score += 1 220 | await updateMC() 221 | // chooseGameType() 222 | } else { 223 | hearts -= 1 224 | } 225 | } 226 | } label: { 227 | Text(text) 228 | } 229 | /* 230 | .background( 231 | color, 232 | in: RoundedRectangle(cornerRadius: 20) 233 | ) 234 | */ 235 | } 236 | } 237 | 238 | // MARK: Functions 239 | extension LearnView { 240 | // Updates multiple choice 241 | func updateMC() async { 242 | loading.toggle() 243 | await fetchRandomMod3D() 244 | // await fetchChoices() 245 | debugPrint("Updated Game View") 246 | loading.toggle() 247 | } 248 | 249 | // Updates fill in the bank 250 | func updateFIB() async { 251 | loading.toggle() 252 | showingGame = true 253 | 254 | await learnViewModel.getImmersiveScene(src: "english", dest: languageManager.language.lowercased()) 255 | Immersion.state = learnViewModel.immersiveScene.assetName 256 | 257 | loading.toggle() 258 | } 259 | 260 | func resetView() { 261 | highestScore = max(highestScore, score) 262 | score = 0 263 | hearts = 5 264 | showingGame = false 265 | } 266 | 267 | func fetchRandomMod3D() async { 268 | try? await learnViewModel.fetchRandomMod3D( 269 | src: "english", 270 | dest: languageManager.language.lowercased() 271 | ) 272 | } 273 | 274 | private func chooseGameType() { 275 | let ran = Int.random(in: 1...2) 276 | if (ran == 1) { 277 | gameType = "MC" 278 | } else { 279 | gameType = "FIB" 280 | } 281 | debugPrint("Next game: \(gameType)") 282 | } 283 | } 284 | 285 | // MARK: Preview 286 | #Preview { 287 | LearnView() 288 | } 289 | -------------------------------------------------------------------------------- /EduSphere/Source/Features/Practice/Models/ImmersionModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImmersionModel.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import Foundation 9 | 10 | // Hard coded random immersive scenery for fill in the blank games 11 | struct Immersion: Identifiable { 12 | let id = UUID() 13 | var missingWord: String 14 | var assetName: String 15 | var sentence: String 16 | } 17 | 18 | extension Immersion { 19 | static var state: String = { 20 | "StarfieldScene" 21 | }() 22 | 23 | static let immersions: [Immersion] = {[ 24 | Immersion(missingWord: "space", assetName: "StarfieldScene", sentence: "I am floating in ___."), // I am in SPACE. 25 | Immersion(missingWord: "dock", assetName: "DockScene", sentence: "I am on a ___, next to the water."), // I am on a DOCK. 26 | Immersion(missingWord: "church", assetName: "ChurchScene", sentence: "Be quiet, we are in a ___."), // Be quiet, we are in a CHURCH. 27 | Immersion(missingWord: "park", assetName: "ParkScene", sentence: "The weather is nice at the ___."), // The weather is nice at the PARK. 28 | Immersion(missingWord: "living room", assetName: "LivingRoomScene", sentence: "The ___ is very comfortable."), // The LIVING ROOM is very comfortable. 29 | ]}() 30 | } 31 | -------------------------------------------------------------------------------- /EduSphere/Source/Features/Practice/ViewModels/PracticeViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PracticeViewModel.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-17. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | 11 | @MainActor 12 | final class PracticeViewModel: ObservableObject { 13 | @Published var currentImmersion: Immersion = Immersion.immersions[0] 14 | @Published var currentIndex: Int = 0 15 | 16 | func initalizeImmersion(src: String, dest: String) async { 17 | // This is called onAppear 18 | // We have this function so that our first displayed immersion is translated 19 | let first = Immersion.immersions[0] 20 | await translateImmersion(immersion: first, src: src, dest: dest) 21 | self.currentImmersion.assetName = first.assetName 22 | } 23 | 24 | func setNextImmersion(src: String, dest: String) async { 25 | let nextIndex = getNextPosition(index: self.currentIndex) 26 | self.currentIndex = nextIndex 27 | await translateImmersion( 28 | immersion: Immersion.immersions[nextIndex], 29 | src: src, 30 | dest: dest 31 | ) 32 | self.currentImmersion.assetName = Immersion.immersions[nextIndex].assetName 33 | } 34 | 35 | func translateImmersion(immersion: Immersion, src: String, dest: String) async { 36 | self.currentImmersion.missingWord = await returnTranslation(text: immersion.missingWord, src: src, dest: dest) 37 | self.currentImmersion.sentence = await returnTranslation(text: immersion.sentence, src: src, dest: dest) 38 | } 39 | 40 | private func returnTranslation(text: String, src: String, dest: String) async -> String { 41 | let endpoint = "http://127.0.0.1:5000/translate" 42 | let parameters: Parameters = [ 43 | "word": text, 44 | "src": src, 45 | "dest": dest 46 | ] 47 | 48 | return await withCheckedContinuation { continuation in 49 | AF.request(endpoint, parameters: parameters).responseData { response in 50 | switch response.result { 51 | case .success(let data): 52 | // Process data on success 53 | if let json = try? JSONSerialization.jsonObject(with: data, options: []), let jsonDict = json as? [String: Any] { 54 | let translation = jsonDict["translation"] as? String 55 | 56 | // Access and use the extracted components as needed 57 | print("Translation:", translation ?? "") 58 | 59 | continuation.resume(returning: translation ?? "") 60 | } 61 | case .failure(_): 62 | // Ignore error case 63 | continuation.resume(returning: "") 64 | } 65 | } 66 | } 67 | } 68 | 69 | func checkCorrect(input: String, ans: String) -> Bool { 70 | return input.lowercased() == ans.lowercased() 71 | } 72 | 73 | func getNextPosition(index: Int) -> Int { 74 | // Assumes non-zero length Immersion.immersions 75 | if index == Immersion.immersions.count - 1 { 76 | return 0 77 | } else { 78 | return index + 1 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /EduSphere/Source/Features/Practice/Views/PracticeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PracticeView.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-17. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct PracticeView: View { 11 | // State Variables 12 | @State private var text: String = "" 13 | @State private var showImmersiveSpace: Bool = false 14 | @State private var immersiveSpaceIsShown: Bool = false 15 | @State private var loadingNext: Bool = false 16 | 17 | // ObservedObjects 18 | @ObservedObject private var practiceViewModel = PracticeViewModel() 19 | @ObservedObject private var languageManager = LanguageManager.shared 20 | 21 | // Environment Variables 22 | @Environment(\.openImmersiveSpace) var openImmersiveSpace 23 | @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace 24 | 25 | var body: some View { 26 | content 27 | .onChange(of: showImmersiveSpace) { _, newValue in 28 | Task { 29 | if newValue { 30 | switch await openImmersiveSpace(id: "ImmersiveSpace") { 31 | case .opened: 32 | immersiveSpaceIsShown = true 33 | case .error, .userCancelled: 34 | fallthrough 35 | @unknown default: 36 | immersiveSpaceIsShown = false 37 | showImmersiveSpace = false 38 | } 39 | } else if immersiveSpaceIsShown { 40 | await dismissImmersiveSpace() 41 | immersiveSpaceIsShown = false 42 | } 43 | } 44 | } 45 | } 46 | 47 | var content: some View { 48 | VStack { 49 | if showImmersiveSpace { 50 | // Game 51 | gameHeader 52 | Spacer() 53 | fillInTheBank(text: practiceViewModel.currentImmersion.sentence) 54 | textfield 55 | Spacer() 56 | } else if !showImmersiveSpace && loadingNext { 57 | Spacer() 58 | ProgressView() 59 | Spacer() 60 | } else { 61 | edusphereLogo 62 | largeTitle 63 | description 64 | playButton 65 | Spacer() 66 | HStack { 67 | Text("Spacer") 68 | .font(.callout) 69 | .fontWeight(.semibold) 70 | .opacity(0.7) 71 | } 72 | .padding() 73 | .opacity(0) 74 | } 75 | } 76 | } 77 | 78 | var edusphereLogo: some View { 79 | Image("EduSphere_logo") 80 | .resizable() 81 | .scaledToFit() 82 | .frame(width: 200, height: 200) 83 | } 84 | 85 | var largeTitle: some View { 86 | Text("Practice") 87 | .font(.extraLargeTitle) 88 | .fontWeight(.semibold) 89 | } 90 | 91 | var description: some View { 92 | Text("Go into virtual reality and explore your surrounds, discovering new words to describe your location!") 93 | .padding() 94 | } 95 | 96 | var playButton: some View { 97 | Button { 98 | Task { 99 | await practiceViewModel.initalizeImmersion(src: "english", dest: languageManager.language.lowercased()) 100 | showImmersiveSpace = true 101 | } 102 | } label: { 103 | Image(systemName: "arrowtriangle.forward.fill") 104 | .resizable() 105 | .scaledToFit() 106 | .padding(50) 107 | .frame(width: 200, height: 200) 108 | } 109 | } 110 | } 111 | 112 | // MARK: Game 113 | extension PracticeView { 114 | var gameHeader: some View { 115 | HStack { 116 | Image(systemName: "arrow.left") 117 | Button { 118 | showImmersiveSpace = false 119 | } label: { 120 | Text("Quit") 121 | } 122 | Spacer() 123 | Button { 124 | Task { 125 | await showNext() 126 | } 127 | } label: { 128 | Image(systemName: "arrow.counterclockwise") 129 | } 130 | } 131 | .padding(.horizontal) 132 | .padding(.vertical) 133 | } 134 | 135 | @ViewBuilder 136 | func fillInTheBank(text: String) -> some View { 137 | Text(text) 138 | .font(.largeTitle) 139 | .fontWeight(.semibold) 140 | .padding() 141 | } 142 | 143 | var textfield: some View { 144 | HStack { 145 | TextField("Fill in the blank...", text: $text) 146 | .foregroundStyle(Color.white) 147 | .onSubmit { 148 | Task { 149 | await submitAns() 150 | } 151 | } 152 | Button { 153 | Task { 154 | await submitAns() 155 | } 156 | } label: { 157 | Image(systemName: "rectangle.and.pencil.and.ellipsis") 158 | } 159 | } 160 | .padding() 161 | .background( 162 | .regularMaterial, 163 | in: RoundedRectangle(cornerRadius: 20) 164 | ) 165 | .padding() 166 | .shadow(radius: 3) 167 | } 168 | } 169 | 170 | // MARK: Functions 171 | extension PracticeView { 172 | func showNext() async { 173 | showImmersiveSpace = false 174 | loadingNext = true 175 | await practiceViewModel.setNextImmersion( 176 | src: "english", 177 | dest: languageManager.language.lowercased() 178 | ) 179 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { 180 | text = "" 181 | Immersion.state = practiceViewModel.currentImmersion.assetName 182 | debugPrint(Immersion.state) 183 | loadingNext = false 184 | showImmersiveSpace = true 185 | } 186 | } 187 | 188 | private func submitAns() async { 189 | if practiceViewModel.checkCorrect( 190 | input: text, 191 | ans: practiceViewModel.currentImmersion.missingWord 192 | ) { 193 | await showNext() 194 | } else { 195 | // Alter user it was wrong 196 | } 197 | text = "" 198 | } 199 | } 200 | 201 | #Preview { 202 | PracticeView() 203 | } 204 | -------------------------------------------------------------------------------- /EduSphere/Source/SupportingFiles/WordCollection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WordCollection.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import Foundation 9 | 10 | struct WordCollection { 11 | static let random = { 12 | ["that", "with", "they", "have", "this", "from", "some", "what", "there", "other", "were", "your", "when", "word", "said", "each", "which", "their", "time", "will", "about", "many", "then", "them", "would", "write", "like", "these", "long", "make", "thing", "look", "more", "could", "come", "sound", "most", "number", "over", "know", "water", "than", "call", "first", "people", "down", "side", "been", "find", "work", "part", "take", "place", "made", "live", "where", "after", "back", "little", "only", "round", "year", "came", "show", "every", "good", "give", "under", "name", "very", "through", "just", "form", "much", "great", "think", "help", "line", "before", "turn", "cause", "same", "mean", "differ", "move", "right", "does", "tell", "sentence", "three", "want", "well", "also", "play", "small", "home", "read", "hand", "port", "large", "spell", "even", "land", "here", "must", "high", "such", "follow", "change", "went", "light", "kind", "need", "house", "picture", "again", "animal", "point", "mother", "world", "near", "build", "self", "earth", "father", "head", "stand", "page", "should", "country", "found", "answer", "school", "grow", "study", "still", "learn", "plant", "cover", "food", "four", "thought", "keep", "never", "last", "door", "between", "city", "tree", "cross", "since", "hard", "start", "might", "story", "draw", "left", "late", "while", "press", "close", "night", "real", "life", "stop", "open", "seem", "together", "next", "white", "children", "begin", "walk", "example", "ease", "paper", "often", "always", "music", "those", "both", "mark", "book", "letter", "until", "mile", "river", "feet", "care", "second", "group", "carry", "took", "rain", "room", "friend", "began", "idea", "fish", "mountain", "north", "once", "base", "hear", "horse", "sure", "watch", "color", "face", "wood", "main", "enough", "plain", "girl", "usual", "young", "ready", "above", "ever", "list", "though", "feel", "talk", "bird", "soon", "body", "family", "direct", "pose", "leave", "song", "measure", "state", "product", "black", "short", "numeral", "class", "wind", "question", "happen", "complete", "ship", "area", "half", "rock", "order", "fire", "south", "problem", "piece", "told", "knew", "pass", "farm", "whole", "king", "size", "heard", "best", "hour", "better", "TRUE", "during", "hundred", "remember", "step", "early", "hold", "west", "ground", "interest", "reach", "fast", "five", "sing", "listen", "table", "travel", "less", "morning", "simple", "several", "vowel", "toward", "against", "pattern", "slow", "center", "love", "person", "money", "serve", "appear", "road", "science", "rule", "govern", "pull", "cold", "notice", "voice", "fall", "power", "town", "fine", "certain", "unit", "lead", "dark", "machine", "note", "wait", "plan", "figure", "star", "noun", "field", "rest", "correct", "able", "pound", "done", "beauty", "drive", "stood", "contain", "front", "teach", "week", "final", "gave", "green", "quick", "develop", "sleep", "warm", "free", "minute", "strong", "special", "mind", "behind", "clear", "tail", "produce", "fact", "street", "inch", "nothing", "course", "stay", "wheel", "full", "force", "blue", "object", "decide", "surface", "deep", "moon", "island", "foot", "busy", "test", "record", "boat", "common", "gold", "possible", "plane", "wonder", "laugh", "thousand", "check", "game", "shape", "miss", "brought", "heat", "snow", "bring", "perhaps", "fill", "east", "weight", "language", "among"] 13 | }() 14 | } 15 | -------------------------------------------------------------------------------- /EduSphere/Source/SupportingFiles/WordDecoder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WordDecoder.swift 3 | // EduSphere 4 | // 5 | // Created by Mingchung Xia on 2023-09-16. 6 | // 7 | 8 | import Foundation 9 | /* 10 | func decodeUnicodeString(_ unicodeString: String) -> String? { 11 | let scanner = Scanner(string: unicodeString) 12 | var result: UInt32 = 0 13 | 14 | return String(UnicodeScalar(result)!) 15 | } 16 | */ 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Mingchung Xia 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 | -------------------------------------------------------------------------------- /Packages/RealityKitContent/.swiftpm/xcode/xcuserdata/mingchungxia.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | RealityKitContent.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json: -------------------------------------------------------------------------------- 1 | { 2 | "pathsToIds" : { 3 | "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/GridMaterial.usda" : "CB766F92-EE55-4A63-9401-E7B8C009764D", 4 | "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Immersive.usda" : "65F6F990-A780-4474-B78B-572E0E4E273D", 5 | "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Scene.usda" : "0A9B4653-B11E-4D6A-850E-C6FCB621626C", 6 | "\/RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Untitled Scene.usda" : "D560BB77-AAF3-4BDE-B7C4-989332A4688B", 7 | "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/_GridMaterial.usda" : "CC9FD5C7-161F-48CA-B2B9-61DD597CBD66", 8 | "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/_PlainMaterial.usda" : "1510F6C7-214D-4800-9C6C-F3964D5FC3E3", 9 | "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/GridMaterial.usda" : "66168B71-AB05-424E-8B6C-D33D6E61B08F", 10 | "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Immersive.usda" : "AF09ED6F-1707-48FD-8720-65B998362C09", 11 | "RealityKitContent\/Sources\/RealityKitContent\/RealityKitContent.rkassets\/Scene.usda" : "D66134B1-3681-4A8E-AFE5-29F257229F3B" 12 | } 13 | } -------------------------------------------------------------------------------- /Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/SceneMetadataList.json: -------------------------------------------------------------------------------- 1 | { 2 | "0A9B4653-B11E-4D6A-850E-C6FCB621626C" : { 3 | "cameraTransform" : [ 4 | 1, 5 | 0, 6 | 0, 7 | 0, 8 | 0, 9 | 0.86602545, 10 | -0.49999994, 11 | 0, 12 | 0, 13 | 0.49999994, 14 | 0.86602545, 15 | 0, 16 | 0.0035969093, 17 | 0.35542378, 18 | 0.62919164, 19 | 1 20 | ], 21 | "objectMetadataList" : [ 22 | [ 23 | "0A9B4653-B11E-4D6A-850E-C6FCB621626C", 24 | "Root" 25 | ], 26 | { 27 | "isExpanded" : true, 28 | "isLocked" : false 29 | } 30 | ] 31 | }, 32 | "65F6F990-A780-4474-B78B-572E0E4E273D" : { 33 | "cameraTransform" : [ 34 | 1, 35 | 0, 36 | -0, 37 | 0, 38 | -0, 39 | 0.86602545, 40 | -0.49999988, 41 | 0, 42 | 0, 43 | 0.49999988, 44 | 0.86602545, 45 | 0, 46 | 1.1972517e-08, 47 | 2.6179132, 48 | 0.43191218, 49 | 1 50 | ], 51 | "objectMetadataList" : [ 52 | [ 53 | "65F6F990-A780-4474-B78B-572E0E4E273D", 54 | "Root" 55 | ], 56 | { 57 | "isExpanded" : true, 58 | "isLocked" : false 59 | } 60 | ] 61 | }, 62 | "1510F6C7-214D-4800-9C6C-F3964D5FC3E3" : { 63 | "cameraTransform" : [ 64 | 0.99999994, 65 | 0, 66 | -0, 67 | 0, 68 | -0, 69 | 0.8660254, 70 | -0.49999994, 71 | 0, 72 | 0, 73 | 0.49999994, 74 | 0.8660254, 75 | 0, 76 | 0, 77 | 0.27093536, 78 | 0.46927384, 79 | 1 80 | ], 81 | "objectMetadataList" : [ 82 | [ 83 | "1510F6C7-214D-4800-9C6C-F3964D5FC3E3", 84 | "Root" 85 | ], 86 | { 87 | "isExpanded" : true, 88 | "isLocked" : false 89 | } 90 | ] 91 | }, 92 | "66168B71-AB05-424E-8B6C-D33D6E61B08F" : { 93 | "cameraTransform" : [ 94 | 1, 95 | 0, 96 | -0, 97 | 0, 98 | -0, 99 | 0.8660254, 100 | -0.5, 101 | 0, 102 | 0, 103 | 0.5, 104 | 0.8660254, 105 | 0, 106 | 0, 107 | 0.23875366, 108 | 0.4135335, 109 | 1 110 | ], 111 | "objectMetadataList" : [ 112 | [ 113 | "66168B71-AB05-424E-8B6C-D33D6E61B08F", 114 | "Root" 115 | ], 116 | { 117 | "isExpanded" : true, 118 | "isLocked" : false 119 | } 120 | ] 121 | }, 122 | "AF09ED6F-1707-48FD-8720-65B998362C09" : { 123 | "cameraTransform" : [ 124 | 1, 125 | 0, 126 | -0, 127 | 0, 128 | -0, 129 | 0.7071069, 130 | -0.7071067, 131 | 0, 132 | 0, 133 | 0.7071067, 134 | 0.7071069, 135 | 0, 136 | 0, 137 | 2.8836339, 138 | -0.107588194, 139 | 1 140 | ], 141 | "objectMetadataList" : [ 142 | [ 143 | "AF09ED6F-1707-48FD-8720-65B998362C09", 144 | "Root", 145 | "Sphere_Left" 146 | ], 147 | { 148 | "isExpanded" : true, 149 | "isLocked" : false 150 | }, 151 | [ 152 | "AF09ED6F-1707-48FD-8720-65B998362C09", 153 | "Root", 154 | "Sphere_Right" 155 | ], 156 | { 157 | "isExpanded" : true, 158 | "isLocked" : false 159 | }, 160 | [ 161 | "AF09ED6F-1707-48FD-8720-65B998362C09", 162 | "Root" 163 | ], 164 | { 165 | "isExpanded" : true, 166 | "isLocked" : false 167 | } 168 | ] 169 | }, 170 | "CB766F92-EE55-4A63-9401-E7B8C009764D" : { 171 | "cameraTransform" : [ 172 | 1, 173 | 0, 174 | -0, 175 | 0, 176 | -0, 177 | 0.8660253, 178 | -0.5000001, 179 | 0, 180 | 0, 181 | 0.5000001, 182 | 0.8660253, 183 | 0, 184 | 0, 185 | 0.27093494, 186 | 0.4692731, 187 | 1 188 | ], 189 | "objectMetadataList" : [ 190 | [ 191 | "CB766F92-EE55-4A63-9401-E7B8C009764D", 192 | "Root" 193 | ], 194 | { 195 | "isExpanded" : true, 196 | "isLocked" : false 197 | }, 198 | [ 199 | "CB766F92-EE55-4A63-9401-E7B8C009764D", 200 | "Root", 201 | "GridMaterial" 202 | ], 203 | { 204 | "isExpanded" : true, 205 | "isLocked" : false 206 | } 207 | ] 208 | }, 209 | "D560BB77-AAF3-4BDE-B7C4-989332A4688B" : { 210 | "cameraTransform" : [ 211 | 1, 212 | 0, 213 | -0, 214 | 0, 215 | -0, 216 | 0.8660253, 217 | -0.5000001, 218 | 0, 219 | 0, 220 | 0.5000001, 221 | 0.8660253, 222 | 0, 223 | 0, 224 | 0.27093494, 225 | 0.4692731, 226 | 1 227 | ], 228 | "objectMetadataList" : [ 229 | 230 | ] 231 | }, 232 | "D66134B1-3681-4A8E-AFE5-29F257229F3B" : { 233 | "cameraTransform" : [ 234 | 1, 235 | 0, 236 | -0, 237 | 0, 238 | -0, 239 | 0.7071069, 240 | -0.7071067, 241 | 0, 242 | 0, 243 | 0.7071067, 244 | 0.7071069, 245 | 0, 246 | 0, 247 | 0.26894823, 248 | 0.26934713, 249 | 1 250 | ], 251 | "objectMetadataList" : [ 252 | [ 253 | "D66134B1-3681-4A8E-AFE5-29F257229F3B", 254 | "Root" 255 | ], 256 | { 257 | "isExpanded" : true, 258 | "isLocked" : false 259 | }, 260 | [ 261 | "D66134B1-3681-4A8E-AFE5-29F257229F3B", 262 | "Root", 263 | "GridMaterial", 264 | "GridMaterial" 265 | ], 266 | { 267 | "isExpanded" : true, 268 | "isLocked" : false 269 | } 270 | ] 271 | } 272 | } -------------------------------------------------------------------------------- /Packages/RealityKitContent/Package.realitycomposerpro/WorkspaceData/Settings.rcprojectdata: -------------------------------------------------------------------------------- 1 | { 2 | "cameraPresets" : { 3 | 4 | }, 5 | "secondaryToolbarData" : { 6 | "isGridVisible" : true, 7 | "sceneReverbPreset" : -1 8 | }, 9 | "unitDefaults" : { 10 | "°" : "°", 11 | "kg" : "g", 12 | "m" : "cm", 13 | "m\/s" : "m\/s", 14 | "m\/s²" : "m\/s²", 15 | "s" : "s" 16 | } 17 | } -------------------------------------------------------------------------------- /Packages/RealityKitContent/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.9 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "RealityKitContent", 8 | products: [ 9 | // Products define the executables and libraries a package produces, and make them visible to other packages. 10 | .library( 11 | name: "RealityKitContent", 12 | targets: ["RealityKitContent"]), 13 | ], 14 | dependencies: [ 15 | // Dependencies declare other packages that this package depends on. 16 | // .package(url: /* package url */, from: "1.0.0"), 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 20 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 21 | .target( 22 | name: "RealityKitContent", 23 | dependencies: []), 24 | ] 25 | ) -------------------------------------------------------------------------------- /Packages/RealityKitContent/README.md: -------------------------------------------------------------------------------- 1 | # RealityKitContent 2 | 3 | A description of this package. -------------------------------------------------------------------------------- /Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Immersive.usda: -------------------------------------------------------------------------------- 1 | #usda 1.0 2 | ( 3 | defaultPrim = "Root" 4 | metersPerUnit = 1 5 | upAxis = "Y" 6 | ) 7 | 8 | def Xform "Root" 9 | { 10 | reorder nameChildren = ["Sphere_Left", "Sphere_Right", "_PlainMaterial"] 11 | def Sphere "Sphere_Right" ( 12 | active = true 13 | prepend apiSchemas = ["MaterialBindingAPI"] 14 | ) 15 | { 16 | rel material:binding = ( 17 | bindMaterialAs = "weakerThanDescendants" 18 | ) 19 | double radius = 0.1 20 | quatf xformOp:orient = (1, 0, 0, 0) 21 | float3 xformOp:scale = (1, 1, 1) 22 | float3 xformOp:translate = (0.5, 1.5, -1.5) 23 | uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] 24 | } 25 | 26 | def Sphere "Sphere_Left" ( 27 | active = true 28 | prepend apiSchemas = ["MaterialBindingAPI"] 29 | ) 30 | { 31 | rel material:binding = ( 32 | bindMaterialAs = "weakerThanDescendants" 33 | ) 34 | double radius = 0.1 35 | quatf xformOp:orient = (1, 0, 0, 0) 36 | float3 xformOp:scale = (1, 1, 1) 37 | float3 xformOp:translate = (-0.5, 1.5, -1.5) 38 | uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] 39 | } 40 | 41 | def "_PlainMaterial" ( 42 | active = true 43 | prepend references = @_PlainMaterial.usda@ 44 | ) 45 | { 46 | float3 xformOp:scale = (1, 1, 1) 47 | uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/Scene.usda: -------------------------------------------------------------------------------- 1 | #usda 1.0 2 | ( 3 | defaultPrim = "Root" 4 | metersPerUnit = 1 5 | upAxis = "Y" 6 | ) 7 | 8 | def Xform "Root" 9 | { 10 | reorder nameChildren = ["Sphere", "_GridMaterial", "_PlainMaterial"] 11 | rel material:binding = None ( 12 | bindMaterialAs = "weakerThanDescendants" 13 | ) 14 | 15 | def Sphere "Sphere" ( 16 | active = true 17 | prepend apiSchemas = ["MaterialBindingAPI"] 18 | ) 19 | { 20 | rel material:binding = ( 21 | bindMaterialAs = "weakerThanDescendants" 22 | ) 23 | double radius = 0.05 24 | quatf xformOp:orient = (1, 0, 0, 0) 25 | float3 xformOp:scale = (1, 1, 1) 26 | float3 xformOp:translate = (0, 0, 0.0004) 27 | uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] 28 | 29 | def RealityKitComponent "Collider" 30 | { 31 | uint group = 1 32 | uniform token info:id = "RealityKit.Collider" 33 | uint mask = 4294967295 34 | token type = "Default" 35 | 36 | def RealityKitStruct "Shape" 37 | { 38 | float3 extent = (0.2, 0.2, 0.2) 39 | float radius = 0.05 40 | token shapeType = "Sphere" 41 | } 42 | } 43 | 44 | def RealityKitComponent "InputTarget" 45 | { 46 | uniform token info:id = "RealityKit.InputTarget" 47 | } 48 | } 49 | 50 | def "_PlainMaterial" ( 51 | active = true 52 | prepend references = @_PlainMaterial.usda@ 53 | ) 54 | { 55 | float3 xformOp:scale = (1, 1, 1) 56 | uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets/_PlainMaterial.usda: -------------------------------------------------------------------------------- 1 | #usda 1.0 2 | ( 3 | defaultPrim = "Root" 4 | metersPerUnit = 1 5 | upAxis = "Y" 6 | ) 7 | 8 | def Xform "Root" 9 | { 10 | def Material "Material" 11 | { 12 | token outputs:mtlx:surface 13 | token outputs:realitykit:vertex 14 | prepend token outputs:surface.connect = 15 | float2 ui:nodegraph:realitykit:subgraphOutputs:pos = (358.25, 99.5) 16 | float2 ui:nodegraph:realitykit:subgraphOutputs:size = (181.5, 99) 17 | 18 | def Shader "MaterialXPreviewSurface" 19 | { 20 | uniform token info:id = "ND_UsdPreviewSurface_surfaceshader" 21 | token outputs:out 22 | float2 ui:nodegraph:node:pos = (103.75, 99.5) 23 | float2 ui:nodegraph:node:size = (207.5, 199) 24 | } 25 | 26 | def Shader "UsdPreviewSurface" 27 | { 28 | uniform token info:id = "UsdPreviewSurface" 29 | color3f inputs:diffuseColor = (0.89737034, 0.89737034, 0.89737034) ( 30 | colorSpace = "Input - Texture - sRGB - sRGB" 31 | ) 32 | float inputs:metallic = 0.15 33 | token outputs:surface 34 | } 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Bundle for the RealityKitContent project 4 | public let realityKitContentBundle = Bundle.module 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EduSphere-VisionOS 2 | 3 | ## Description 4 | 5 | EduSphere is a VisionOS application built for the Apple Vision Pro, built in 36 hours at Hack The North 2023. EduSphere is an interactive AR/VR language learning app including a 3D rendering game and a chatbot. 6 | 7 | The backend services used for this project are linked as submodules under the Backend-Microservices directory. 8 | 9 | All technologies used in this fullstack app are: Swift, SwiftUI, VisionOS, RealityKit, Python, Flask, Cohere, Google Cloud Platform, CockroachDB, SQL, Alamofire, Nomad, Reality Converters, Adobe-XD, MVVM 10 | 11 | ![about](https://github.com/mingchungx/EduSphere-VisionOS/assets/65872029/25c59405-50e8-4696-a27a-04c48d9d7d82) 12 | ![play](https://github.com/mingchungx/EduSphere-VisionOS/assets/65872029/a1c7e5cc-b795-49a8-b919-02a403b8bdc9) 13 | ![game](https://github.com/mingchungx/EduSphere-VisionOS/assets/65872029/4a932ea0-e7e2-4321-b688-8bcd8985dc9d) 14 | ![chatbot](https://github.com/mingchungx/EduSphere-VisionOS/assets/65872029/fc7e955a-4fb2-4630-92c8-6d884675cdb0) 15 | 16 | ## Installation and Usage 17 | 18 | EduSphere is free to clone and use. Feel free to download and explore the source code by following the commands below: 19 | 20 | ``` 21 | git clone https://github.com/mingchungx/EduSphere-VisionOS.git 22 | ``` 23 | 24 | ## Authors 25 | 26 | Mingchung Xia (University of Waterloo) 27 | 28 | Luke Wang (McGill University) 29 | 30 | Ruixin Dai (Sheridan College) 31 | 32 | Evin Li (The University of British Columbia) 33 | 34 | --------------------------------------------------------------------------------